diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a6b7c4b24..f2b5a000f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -5,7 +5,7 @@ ARG VARIANT FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends binutils-arm-none-eabi gcc-arm-none-eabi libpng-dev build-essential clang-format-13 xorg-dev libsdl2-dev gcc-mingw-w64 libarchive-tools + && apt-get -y install --no-install-recommends binutils-arm-none-eabi gcc-arm-none-eabi libpng-dev build-essential clang-format-13 xorg-dev libsdl2-dev g++-mingw-w64 gcc-mingw-w64 libarchive-tools WORKDIR /deps RUN git clone https://github.com/pret/agbcc.git && cd agbcc && ./build.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9455fbe23..5f165da63 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,39 +2,39 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: pull_request_target: jobs: format: - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ubuntu-22.04 steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout if: ${{ github.event_name == 'push' }} uses: actions/checkout@master - + - name: Install tools run: | sudo apt update && sudo apt install clang-format-13 - + - name: Check formatting run: | make check_format build-gba: - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ubuntu-22.04 name: Build GBA (USA) steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} @@ -42,7 +42,7 @@ jobs: - name: Checkout if: ${{ github.event_name == 'push' }} uses: actions/checkout@master - + - name: Checkout gh-pages if: ${{ github.event_name == 'push' }} uses: actions/checkout@master @@ -120,17 +120,17 @@ jobs: cwd: "./gh-pages" add: "reports maps" message: ${{ env.REPORTS_COMMIT_MSG }} - + build-gba-variants: - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ubuntu-22.04 name: Build GBA (${{ matrix.variant }}) strategy: - matrix: + matrix: variant: ["europe", "japan", "japan_vc", "usa_beta"] steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} @@ -138,7 +138,7 @@ jobs: - name: Checkout if: ${{ github.event_name == 'push' }} uses: actions/checkout@master - + - name: Checkout gh-pages if: ${{ github.event_name == 'push' }} uses: actions/checkout@master @@ -167,15 +167,15 @@ jobs: ports: strategy: - matrix: - platform: ["sdl", "sdl_win32", "win32"] + matrix: + platform: ["sdl", "sdl_win32", "win32", "ps2"] os: ["ubuntu-22.04", "macos-15"] - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ${{ matrix.os }} name: Build ${{ matrix.platform }} (${{ matrix.os }}) steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} @@ -187,32 +187,46 @@ jobs: - name: Install tools (Linux) if: ${{ matrix.os == 'ubuntu-22.04' }} run: | - sudo apt update && sudo apt install xorg-dev libsdl2-dev gcc-mingw-w64 libarchive-tools + sudo apt update && sudo apt install xorg-dev libsdl2-dev g++-mingw-w64 gcc-mingw-w64 libarchive-tools mkisofs - name: Install Tools (Macos) if: ${{ matrix.os == 'macos-15' }} run: | - brew install libpng sdl2 mingw-w64 arm-none-eabi-gcc - + brew install libpng sdl2 mingw-w64 arm-none-eabi-gcc cdrtools + - name: Install SDL for win32 if: ${{ matrix.platform == 'sdl_win32' }} run: | make SDL2.dll - + + # TODO: Don't install latest SDK, use a stable version + - name: Install PS2DEV + if: ${{ matrix.platform == 'ps2' }} + run: | + PS2DEV="$HOME/ps2dev" + echo "PS2DEV=$HOME/ps2dev" >> "$GITHUB_ENV" + mkdir -p $PS2DEV + echo "PS2SDK=$PS2DEV/ps2sdk" >> "$GITHUB_ENV" + echo "GSKIT=$PS2DEV/gsKit" >> "$GITHUB_ENV" + echo "$PS2DEV/bin" >> "$GITHUB_PATH" + echo "$PS2DEV/ee/bin" >> "$GITHUB_PATH" + curl -o ps2dev-latest.tar.gz -LC - https://github.com/ps2dev/ps2dev/releases/download/latest/ps2dev-$(if [[ "$OSTYPE" == "darwin"* ]]; then echo macos; else echo ubuntu; fi)-latest.tar.gz + tar -xf ps2dev-latest.tar.gz --strip-components 1 -C $PS2DEV + - name: ${{ matrix.platform }} run: | make -j${nproc} ${{ matrix.platform }} test: strategy: - matrix: + matrix: platform: ["sdl"] - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ubuntu-22.04 name: TAS Test ${{ matrix.platform }} steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} @@ -223,17 +237,17 @@ jobs: - name: Install tools run: | - sudo apt update && sudo apt install xorg-dev libsdl2-dev gcc-mingw-w64 libarchive-tools - + sudo apt update && sudo apt install xorg-dev libsdl2-dev g++-mingw-w64 gcc-mingw-w64 libarchive-tools + - name: Install SDL for win32 if: ${{ matrix.platform == 'sdl_win32' }} run: | make SDL2.dll - + - name: Build ${{ matrix.platform }} run: | make -j${nproc} ${{ matrix.platform }} TAS_TESTING=1 - + - name: Run TAS timeout-minutes: 1 env: @@ -242,12 +256,12 @@ jobs: ./sa2.${{ matrix.platform }} level_editor: - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}} runs-on: ubuntu-22.04 name: Level editor (BriBaSA) steps: - name: Checkout PR - if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}} uses: actions/checkout@master with: ref: ${{ github.event.pull_request.head.sha }} @@ -259,7 +273,7 @@ jobs: - name: Install deps run: | sudo apt update && sudo apt install xorg-dev libsdl2-dev - + - name: Build run: | make -j${nproc} bribasa diff --git a/.gitignore b/.gitignore index 60cc4e932..cf418a746 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,12 @@ libagbsyscall/*.s *.exe *.dll *.sdl +*.iso + +# PSP build outputs +EBOOT.PBP +PARAM.SFO +sa2_debug.log # third party deps /ext diff --git a/INSTALL.md b/INSTALL.md index 8315370fc..4feb4b25f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,7 +14,7 @@ Install WSL (Ubuntu). Once installed, open this project in the WSL terminal and #### On Linux (including WSL) ``` sudo apt update -sudo apt install build-essential binutils-arm-none-eabi gcc-arm-none-eabi libpng-dev xorg-dev libsdl2-dev gcc-mingw-w64 libarchive-tools +sudo apt install build-essential binutils-arm-none-eabi gcc-arm-none-eabi libpng-dev xorg-dev libsdl2-dev g++-mingw-w64 gcc-mingw-w64 libarchive-tools ``` #### On MacOS diff --git a/Makefile b/Makefile index b972750e0..a7429b953 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,14 @@ else ifeq ($(CPU_ARCH),i386) TOOLCHAIN := /usr/x86_64-w64-mingw32/ PREFIX := x86_64-w64-mingw32- endif +# PSP +else ifeq ($(PLATFORM),psp) + PSPDEV ?= $(HOME)/pspdev + PSPSDK := $(PSPDEV)/psp/sdk + export PATH := $(PSPDEV)/bin:$(PATH) + PREFIX := psp- +else ifeq ($(PLATFORM),ps2) + PREFIX := mips64r5900el-ps2-elf- else # Native ifneq ($(PLATFORM),sdl) @@ -74,6 +82,7 @@ CC1 := tools/agbcc/bin/agbcc$(EXE) CC1_OLD := tools/agbcc/bin/old_agbcc$(EXE) else CC1 := $(PREFIX)gcc$(EXE) +CXX := $(PREFIX)g++$(EXE) CC1_OLD := $(CC1) endif @@ -120,6 +129,14 @@ else ifeq ($(PLATFORM),sdl) ROM := $(BUILD_NAME).sdl ELF := $(ROM).elf MAP := $(ROM).map +else ifeq ($(PLATFORM),psp) +ROM := EBOOT.PBP +ELF := $(BUILD_NAME).psp.elf +MAP := $(BUILD_NAME).psp.map +else ifeq ($(PLATFORM),ps2) +ROM := $(BUILD_NAME).$(PLATFORM).iso +ELF := $(ROM:.iso=.elf) +MAP := $(ROM:.iso=.map) else ROM := $(BUILD_NAME).$(PLATFORM).exe ELF := $(ROM:.exe=.elf) @@ -156,16 +173,28 @@ TILESETS_SUBDIR = graphics/tilesets/ ifeq ($(PLATFORM),gba) C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/*") else ifeq ($(PLATFORM),sdl) -C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*") +C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") +else ifeq ($(PLATFORM),psp) +C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") +else ifeq ($(PLATFORM),ps2) +C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*" -not -path "*/platform/pret_sdl/*") else ifeq ($(PLATFORM),sdl_win32) -C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*") +C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") else ifeq ($(PLATFORM),win32) -C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/pret_sdl/*") +C_SRCS := $(shell find $(C_SUBDIR) -name "*.c" -not -path "*/platform/pret_sdl/*" -not -path "*/platform/ps2/*") else C_SRCS := $(shell find $(C_SUBDIR) -name "*.c") endif C_OBJS := $(patsubst $(C_SUBDIR)/%.c,$(C_BUILDDIR)/%.o,$(C_SRCS)) +ifeq ($(PLATFORM),gba) +CXX_SRCS := $(shell find $(C_SUBDIR) -name "*.cc" -not -path "*/platform/*") +else +CXX_SRCS := $(shell find $(C_SUBDIR) -name "*.cc") +endif + +CXX_OBJS := $(patsubst $(C_SUBDIR)/%.cc,$(C_BUILDDIR)/%.o,$(CXX_SRCS)) + # Platform not included as we only need the headers for decomp scratches C_HEADERS := $(shell find $(INCLUDE_DIRS) -name "*.h" -not -path "*/platform/*") @@ -189,7 +218,7 @@ MID_OBJS := $(patsubst $(MID_SUBDIR)/%.mid,$(MID_BUILDDIR)/%.o,$(MID_SRCS)) SOUND_ASM_SRCS := $(wildcard $(SOUND_ASM_SUBDIR)/*.s) SOUND_ASM_OBJS := $(patsubst $(SOUND_ASM_SUBDIR)/%.s,$(SOUND_ASM_BUILDDIR)/%.o,$(SOUND_ASM_SRCS)) -OBJS := $(C_OBJS) $(ASM_OBJS) $(C_ASM_OBJS) $(DATA_ASM_OBJS) $(SONG_OBJS) $(MID_OBJS) +OBJS := $(C_OBJS) $(CXX_OBJS) $(ASM_OBJS) $(C_ASM_OBJS) $(DATA_ASM_OBJS) $(SONG_OBJS) $(MID_OBJS) OBJS_REL := $(patsubst $(OBJ_DIR)/%,%,$(OBJS)) FORMAT_SRC_PATHS := $(shell find . -name "*.c" ! -path '*/src/data/*' ! -path '*/build/*' ! -path '*/ext/*') @@ -225,6 +254,12 @@ else ifeq ($(PLATFORM),sdl) CC1FLAGS += -Wno-parentheses-equality -Wno-unused-value CPPFLAGS += -D TITLE_BAR=$(BUILD_NAME).$(PLATFORM) -D PLATFORM_GBA=0 -D PLATFORM_SDL=1 -D PLATFORM_WIN32=0 $(shell sdl2-config --cflags) + else ifeq ($(PLATFORM),psp) + CC1FLAGS += -G0 + CPPFLAGS += -D PLATFORM_GBA=0 -D PLATFORM_SDL=1 -D PLATFORM_WIN32=0 -D SDL_MAIN_HANDLED -I$(PSPDEV)/psp/include/SDL2 -I$(PSPDEV)/psp/include -I$(PSPSDK)/include -D_PSP_FW_VERSION=600 + else ifeq ($(PLATFORM),ps2) + CC1FLAGS += -G0 -Wno-parentheses-equality -Wno-unused-value -ffast-math + CPPFLAGS += -D PLATFORM_GBA=0 -D PLATFORM_SDL=0 -D PLATFORM_WIN32=0 -D_EE -D__PS2__ -I$(PS2SDK)/common/include -I$(PS2SDK)/ee/include -I$(PS2DEV)/gsKit/include -I$(PS2SDK)/ports/include else ifeq ($(PLATFORM),sdl_win32) CPPFLAGS += -D TITLE_BAR=$(BUILD_NAME).$(PLATFORM) -D PLATFORM_GBA=0 -D PLATFORM_SDL=1 -D PLATFORM_WIN32=0 $(SDL_MINGW_FLAGS) else ifeq ($(PLATFORM),win32) @@ -241,24 +276,18 @@ else endif endif -ifeq ($(PLATFORM),gba) - ASFLAGS += -mcpu=arm7tdmi -mthumb-interwork - CC1FLAGS += -mthumb-interwork -else - ifeq ($(PLATFORM), sdl) - # for modern we are using a modern compiler - # so instead of CPP we can use gcc -E to "preprocess only" - CPP := $(CC1) -E - endif - # Allow file input through stdin on modern GCC and set it to "compile only" - CC1FLAGS += -x c -S -endif - ifeq ($(DEBUG),1) CC1FLAGS += -g3 -O0 CPPFLAGS += -D DEBUG=1 else - CC1FLAGS += -O2 + ifeq ($(PLATFORM),psp) + # -O3 for PSP (Allegrex MIPS, small D-cache) + CC1FLAGS += -O3 -funroll-loops -fomit-frame-pointer + else ifeq ($(PLATFORM),ps2) + CC1FLAGS += -O3 -fomit-frame-pointer + else + CC1FLAGS += -O2 + endif CPPFLAGS += -D DEBUG=0 endif @@ -285,6 +314,26 @@ else CPPFLAGS += -D ENABLE_DECOMP_CREDITS=1 endif +CXXFLAGS := $(CC1FLAGS) $(CPPFLAGS) -fno-rtti -fno-exceptions -std=c++11 + +ifeq ($(PLATFORM),gba) + ASFLAGS += -mcpu=arm7tdmi -mthumb-interwork + CC1FLAGS += -mthumb-interwork +else + ifeq ($(PLATFORM), sdl) + # for modern we are using a modern compiler + # so instead of CPP we can use gcc -E to "preprocess only" + CPP := $(CC1) -E + else ifeq ($(PLATFORM), psp) + CPP := $(CC1) -E + else ifeq ($(PLATFORM), ps2) + ASFLAGS += -msingle-float + endif + # Allow file input through stdin on modern gcc/g++ and set it to "compile only" + CC1FLAGS += -x c -S + CXXFLAGS += -x c++ -S +endif + ### LINKER FLAGS ### # GBA @@ -297,7 +346,7 @@ else ifeq ($(PLATFORM),sdl) else MAP_FLAG := -Xlinker -Map= endif -# Win32 +# Win32, PSP, PS2 else MAP_FLAG := -Xlinker -Map= endif @@ -307,6 +356,10 @@ ifeq ($(PLATFORM),gba) LIBS := $(ROOT_DIR)/tools/agbcc/lib/libgcc.a $(ROOT_DIR)/tools/agbcc/lib/libc.a $(LIBABGSYSCALL_LIBS) else ifeq ($(PLATFORM),sdl) LIBS := $(shell sdl2-config --cflags --libs) +else ifeq ($(PLATFORM),psp) + LIBS := -L$(PSPDEV)/psp/lib -L$(PSPSDK)/lib -lSDL2 -lm -lGL -lpspvram -lpspaudio -lpspvfpu -lpspdisplay -lpspgu -lpspge -lpsphprm -lpspctrl -lpsppower -lpspdebug -lpspnet -lpspnet_apctl -Wl,-zmax-page-size=128 +else ifeq ($(PLATFORM),ps2) + LIBS := -T$(PS2SDK)/ee/startup/linkfile -L$(PS2SDK)/common/lib -L$(PS2SDK)/ee/lib -L$(PS2DEV)/gsKit/lib -L$(PS2SDK)/ports/lib -lgskit -ldmakit -lps2_drivers -lmc -lpatches -Wl,-zmax-page-size=128 else ifeq ($(PLATFORM),sdl_win32) LIBS := -mwin32 -lkernel32 -lwinmm -lmingw32 -lxinput $(SDL_MINGW_LIBS) else ifeq ($(PLATFORM), win32) @@ -316,7 +369,7 @@ endif #### MAIN TARGETS #### # these commands will run regardless of deps being completed -.PHONY: clean tools tidy clean-tools $(TOOLDIRS) libagbsyscall +.PHONY: clean tools tidy clean-tools $(TOOLDIRS) libagbsyscall ps2 # Ensure required directories exist $(shell mkdir -p $(C_BUILDDIR) $(ASM_BUILDDIR) $(DATA_ASM_BUILDDIR) $(SOUND_ASM_BUILDDIR) $(SONG_BUILDDIR) $(MID_BUILDDIR)) @@ -397,7 +450,8 @@ clean-tools: tidy: $(RM) -r build/* $(RM) SDL2.dll - $(RM) $(BUILD_NAME)*.exe $(BUILD_NAME)*.elf $(BUILD_NAME)*.map $(BUILD_NAME)*.sdl $(BUILD_NAME)*.gba + $(RM) $(BUILD_NAME)*.exe $(BUILD_NAME)*.elf $(BUILD_NAME)*.map $(BUILD_NAME)*.sdl $(BUILD_NAME)*.gba $(BUILD_NAME)*.iso + $(RM) EBOOT.PBP PARAM.SFO usa_beta: ; @$(MAKE) GAME_REGION=USA GAME_VARIANT=BETA @@ -409,6 +463,10 @@ europe: ; @$(MAKE) GAME_REGION=EUROPE sdl: ; @$(MAKE) PLATFORM=sdl +psp: ; @$(MAKE) PLATFORM=psp + +ps2: ; @$(MAKE) PLATFORM=ps2 + tas_sdl: ; @$(MAKE) sdl TAS_TESTING=1 sdl_win32: @@ -459,7 +517,7 @@ data/mb_chao_garden_japan.gba.lz: data/mb_chao_garden_japan.gba %.bin: %.aif ; $(AIF) $< $@ -$(ELF): $(OBJS) libagbsyscall +$(ELF): $(OBJS) ifeq ($(PLATFORM),gba) @echo "$(LD) -T $(LDSCRIPT) $(MAP_FLAG) $(MAP) -o $@" @$(CPP) -P $(CPPFLAGS) $(LDSCRIPT) > $(OBJ_DIR)/$(LDSCRIPT) @@ -470,15 +528,34 @@ else @cd $(OBJ_DIR) && $(CC1) $(MAP_FLAG)$(ROOT_DIR)/$(MAP) $(OBJS_REL) $(LIBS) -o $(ROOT_DIR)/$@ endif -$(ROM): $(ELF) + ifeq ($(PLATFORM),gba) +$(ROM): $(ELF) libagbsyscall $(OBJCOPY) -O binary --pad-to 0x8400000 $< $@ $(FIX) $@ -p -t"$(TITLE)" -c$(GAME_CODE) -m$(MAKER_CODE) -r$(GAME_REVISION) --silent -else ifeq ($(PLATFORM),sdl) +else ifeq ($(PLATFORM),win32) +$(ROM): $(ELF) libagbsyscall + $(OBJCOPY) -O pei-x86-64 $< $@ +else +$(ROM): $(ELF) +ifeq ($(PLATFORM),sdl) cp $< $@ +else ifeq ($(PLATFORM),psp) + @echo Creating $(ROM) from $(ELF) + @psp-fixup-imports $< + @mksfoex 'Sonic Advance 2' PARAM.SFO + @psp-strip $< -o $(BUILD_NAME).psp_strip.elf + @pack-pbp $@ PARAM.SFO NULL NULL NULL NULL NULL $(BUILD_NAME).psp_strip.elf NULL + @-rm -f $(BUILD_NAME).psp_strip.elf +else ifeq ($(PLATFORM),ps2) + @echo Creating $(ROM) from $(ELF) + @cp -r ps2/ntsc $(OBJ_DIR)/iso + @cp $< $(OBJ_DIR)/iso/$(PS2_GAME_CODE) + @mkisofs -o $(ROM) $(OBJ_DIR)/iso/ else $(OBJCOPY) -O pei-x86-64 $< $@ endif +endif # Build c sources, and ensure alignment $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c @@ -491,11 +568,21 @@ ifeq ($(PLATFORM), gba) endif @$(AS) $(ASFLAGS) $(C_BUILDDIR)/$*.s -o $@ +$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.cc + @echo "$(CXX) -o $@ $<" + @$(shell mkdir -p $(shell dirname '$(C_BUILDDIR)/$*.o')) + @$(CXX) $(CXXFLAGS) -o $(C_BUILDDIR)/$*.s $< + @$(AS) $(ASFLAGS) $(C_BUILDDIR)/$*.s -o $@ + # Scan the src dependencies to determine if any dependent files have changed $(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.c @$(shell mkdir -p $(shell dirname '$(C_BUILDDIR)/$*.d')) $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) $< +$(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.cc + @$(shell mkdir -p $(shell dirname '$(C_BUILDDIR)/$*.d')) + $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) $< + # rule for sources from the src dir (parts of libraries) $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s @echo "$(AS) -o $@ $<" @@ -515,6 +602,7 @@ $(DATA_ASM_BUILDDIR)/%.d: $(DATA_ASM_SUBDIR)/%.s ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(C_SRCS:.c=.d)) +-include $(addprefix $(OBJ_DIR)/,$(CXX_SRCS:.cc=.d)) -include $(addprefix $(OBJ_DIR)/,$(DATA_ASM_SRCS:.s=.d)) endif diff --git a/README.md b/README.md index 382027ade..e980f96c4 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ It can also build: * **sa2.sdl** `make sdl` (Linux/MacOS SDL 64bit port) * **sa2.sdl_win32.exe** `make sdl_win32` (Windows SDL 64bit port) * :construction: **sa2.win32.exe** `make win32` (Win32 native port, not functional) +* **sa2.ps2.iso** `make ps2` (PlayStation 2 port, requires [PS2DEV](https://github.com/ps2dev/ps2dev)) +* **EBOOT.PBP** `make psp` (PlayStation Portable port, requires [PSPDEV](https://github.com/pspdev/pspdev)) ## Current state diff --git a/asm/macros/portable.inc b/asm/macros/portable.inc index b389fb26a..fd66638b8 100644 --- a/asm/macros/portable.inc +++ b/asm/macros/portable.inc @@ -10,6 +10,8 @@ .macro mPtr value #if defined(__aarch64__) || defined(__x86_64__) .quad \value +#elif defined(__mips__) + .4byte \value #else .int \value #endif diff --git a/config.mk b/config.mk index 5f687443a..15affde18 100644 --- a/config.mk +++ b/config.mk @@ -63,6 +63,7 @@ MAKER_CODE := 78 BUILD_NAME := sa2 TITLE := SONICADVANC2 GAME_CODE := A2N +PS2_GAME_CODE := SLUS_054.02 # Revision diff --git a/include/config.h b/include/config.h index 15b9df4ec..357b56580 100644 --- a/include/config.h +++ b/include/config.h @@ -42,11 +42,14 @@ #define RENDERER_SOFTWARE 0 #define RENDERER_OPENGL 1 #define RENDERER_COUNT 2 + +#ifndef RENDERER #if PLATFORM_WIN32 && !PLATFORM_SDL // TODO: Only win32 for now #define RENDERER RENDERER_OPENGL #else #define RENDERER RENDERER_SOFTWARE #endif +#endif #endif // GUARD_SA2_CONFIG_H diff --git a/include/gba/defines.h b/include/gba/defines.h index d11003525..9f31a2dab 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -39,8 +39,18 @@ #define OAM_ENTRY_COUNT 128 #if PORTABLE // NOTE: Used in gba/types.h, so they have to be defined before the #include +#if defined(__PSP__) +// PSP: Use GBA-native resolution, SDL scales to 480x272 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 160 +#elif defined(__PS2__) +// Runs at 60fps with the "fast draw" +#define DISPLAY_WIDTH 320 +#define DISPLAY_HEIGHT 180 +#else #define DISPLAY_WIDTH 426 #define DISPLAY_HEIGHT 240 +#endif // NOTE: We shouldn't consider WIDESCREEN_HACK a permanent thing. // This hack should best be removed once there's a "native" platform layer. diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h index 265142303..d1ab08dbb 100644 --- a/include/gba/io_reg.h +++ b/include/gba/io_reg.h @@ -5,6 +5,27 @@ #include #endif +// these are not hardware registers on ports +// so there is no need to make re-read them +#if PORTABLE +typedef uint8_t reg_u8; +typedef uint16_t reg_u16; +typedef uint32_t reg_u32; +typedef int16_t reg_s16; +typedef int32_t reg_s32; +typedef uint64_t reg_u64; +typedef winreg_t reg_wint; +#else +typedef volatile uint8_t reg_u8; +typedef volatile uint16_t reg_u16; +typedef volatile uint32_t reg_u32; +typedef volatile int16_t reg_s16; +typedef volatile int32_t reg_s32; +typedef volatile uint64_t reg_u64; +typedef volatile winreg_t reg_wint; +#endif + + #define IO_SIZE 0x400 #if !PORTABLE #define REG_BASE 0x4000000 // I/O register base address @@ -369,155 +390,155 @@ extern unsigned char REG_BASE[IO_SIZE]; // I/O registers -#define REG_DISPCNT (*(vu16 *)REG_ADDR_DISPCNT) -#define REG_DISPSTAT (*(vu16 *)REG_ADDR_DISPSTAT) -#define REG_VCOUNT (*(vu16 *)REG_ADDR_VCOUNT) -#define REG_BG0CNT (*(vu16 *)REG_ADDR_BG0CNT) -#define REG_BG1CNT (*(vu16 *)REG_ADDR_BG1CNT) -#define REG_BG2CNT (*(vu16 *)REG_ADDR_BG2CNT) -#define REG_BG3CNT (*(vu16 *)REG_ADDR_BG3CNT) -#define REG_BG0HOFS (*(vu16 *)REG_ADDR_BG0HOFS) -#define REG_BG0VOFS (*(vu16 *)REG_ADDR_BG0VOFS) -#define REG_BG1HOFS (*(vu16 *)REG_ADDR_BG1HOFS) -#define REG_BG1VOFS (*(vu16 *)REG_ADDR_BG1VOFS) -#define REG_BG2HOFS (*(vu16 *)REG_ADDR_BG2HOFS) -#define REG_BG2VOFS (*(vu16 *)REG_ADDR_BG2VOFS) -#define REG_BG3HOFS (*(vu16 *)REG_ADDR_BG3HOFS) -#define REG_BG3VOFS (*(vu16 *)REG_ADDR_BG3VOFS) -#define REG_BG2PA (*(vu16 *)REG_ADDR_BG2PA) -#define REG_BG2PB (*(vu16 *)REG_ADDR_BG2PB) -#define REG_BG2PC (*(vu16 *)REG_ADDR_BG2PC) -#define REG_BG2PD (*(vu16 *)REG_ADDR_BG2PD) -#define REG_BG2X (*(vu32 *)REG_ADDR_BG2X) -#define REG_BG2X_L (*(vu16 *)REG_ADDR_BG2X_L) -#define REG_BG2X_H (*(vu16 *)REG_ADDR_BG2X_H) -#define REG_BG2Y (*(vu32 *)REG_ADDR_BG2Y) -#define REG_BG2Y_L (*(vu16 *)REG_ADDR_BG2Y_L) -#define REG_BG2Y_H (*(vu16 *)REG_ADDR_BG2Y_H) -#define REG_BG3PA (*(vu16 *)REG_ADDR_BG3PA) -#define REG_BG3PB (*(vu16 *)REG_ADDR_BG3PB) -#define REG_BG3PC (*(vu16 *)REG_ADDR_BG3PC) -#define REG_BG3PD (*(vu16 *)REG_ADDR_BG3PD) -#define REG_BG3X (*(vu32 *)REG_ADDR_BG3X) -#define REG_BG3X_L (*(vu16 *)REG_ADDR_BG3X_L) -#define REG_BG3X_H (*(vu16 *)REG_ADDR_BG3X_H) -#define REG_BG3Y (*(vu32 *)REG_ADDR_BG3Y) -#define REG_BG3Y_L (*(vu16 *)REG_ADDR_BG3Y_L) -#define REG_BG3Y_H (*(vu16 *)REG_ADDR_BG3Y_H) -#define REG_WIN0H (*(volatile winreg_t *)REG_ADDR_WIN0H) -#define REG_WIN1H (*(volatile winreg_t *)REG_ADDR_WIN1H) -#define REG_WIN0V (*(volatile winreg_t *)REG_ADDR_WIN0V) -#define REG_WIN1V (*(volatile winreg_t *)REG_ADDR_WIN1V) -#define REG_WININ (*(volatile winreg_t *)REG_ADDR_WININ) -#define REG_WINOUT (*(volatile winreg_t *)REG_ADDR_WINOUT) -#define REG_MOSAIC (*(vu16 *)REG_ADDR_MOSAIC) -#define REG_BLDCNT (*(vu16 *)REG_ADDR_BLDCNT) -#define REG_BLDALPHA (*(vu16 *)REG_ADDR_BLDALPHA) -#define REG_BLDY (*(vu16 *)REG_ADDR_BLDY) - -#define REG_SOUND1CNT_L (*(vu16 *)REG_ADDR_SOUND1CNT_L) -#define REG_NR10 (*(vu8 *)REG_ADDR_NR10) -#define REG_SOUND1CNT_H (*(vu16 *)REG_ADDR_SOUND1CNT_H) -#define REG_NR11 (*(vu8 *)REG_ADDR_NR11) -#define REG_NR12 (*(vu8 *)REG_ADDR_NR12) -#define REG_SOUND1CNT_X (*(vu16 *)REG_ADDR_SOUND1CNT_X) -#define REG_NR13 (*(vu8 *)REG_ADDR_NR13) -#define REG_NR14 (*(vu8 *)REG_ADDR_NR14) -#define REG_SOUND2CNT_L (*(vu16 *)REG_ADDR_SOUND2CNT_L) -#define REG_NR21 (*(vu8 *)REG_ADDR_NR21) -#define REG_NR22 (*(vu8 *)REG_ADDR_NR22) -#define REG_SOUND2CNT_H (*(vu16 *)REG_ADDR_SOUND2CNT_H) -#define REG_NR23 (*(vu8 *)REG_ADDR_NR23) -#define REG_NR24 (*(vu8 *)REG_ADDR_NR24) -#define REG_SOUND3CNT_L (*(vu16 *)REG_ADDR_SOUND3CNT_L) -#define REG_NR30 (*(vu8 *)REG_ADDR_NR30) -#define REG_SOUND3CNT_H (*(vu16 *)REG_ADDR_SOUND3CNT_H) -#define REG_NR31 (*(vu8 *)REG_ADDR_NR31) -#define REG_NR32 (*(vu8 *)REG_ADDR_NR32) -#define REG_SOUND3CNT_X (*(vu16 *)REG_ADDR_SOUND3CNT_X) -#define REG_NR33 (*(vu8 *)REG_ADDR_NR33) -#define REG_NR34 (*(vu8 *)REG_ADDR_NR34) -#define REG_SOUND4CNT_L (*(vu16 *)REG_ADDR_SOUND4CNT_L) -#define REG_NR41 (*(vu8 *)REG_ADDR_NR41) -#define REG_NR42 (*(vu8 *)REG_ADDR_NR42) -#define REG_SOUND4CNT_H (*(vu16 *)REG_ADDR_SOUND4CNT_H) -#define REG_NR43 (*(vu8 *)REG_ADDR_NR43) -#define REG_NR44 (*(vu8 *)REG_ADDR_NR44) -#define REG_SOUNDCNT_L (*(vu16 *)REG_ADDR_SOUNDCNT_L) -#define REG_NR50 (*(vu8 *)REG_ADDR_NR50) -#define REG_NR51 (*(vu8 *)REG_ADDR_NR51) -#define REG_SOUNDCNT_H (*(vu16 *)REG_ADDR_SOUNDCNT_H) -#define REG_SOUNDCNT_X (*(vu16 *)REG_ADDR_SOUNDCNT_X) -#define REG_NR52 (*(vu8 *)REG_ADDR_NR52) -#define REG_SOUNDBIAS (*(vu16 *)REG_ADDR_SOUNDBIAS) -#define REG_SOUNDBIAS_L (*(vu8 *)REG_ADDR_SOUNDBIAS_L) -#define REG_SOUNDBIAS_H (*(vu8 *)REG_ADDR_SOUNDBIAS_H) -#define REG_WAVE_RAM0 (*(vu32 *)REG_ADDR_WAVE_RAM0) -#define REG_WAVE_RAM1 (*(vu32 *)REG_ADDR_WAVE_RAM1) -#define REG_WAVE_RAM2 (*(vu32 *)REG_ADDR_WAVE_RAM2) -#define REG_WAVE_RAM3 (*(vu32 *)REG_ADDR_WAVE_RAM3) -#define REG_FIFO_A (*(vu32 *)REG_ADDR_FIFO_A) -#define REG_FIFO_B (*(vu32 *)REG_ADDR_FIFO_B) +#define REG_DISPCNT (*(reg_u16 *)REG_ADDR_DISPCNT) +#define REG_DISPSTAT (*(reg_u16 *)REG_ADDR_DISPSTAT) +#define REG_VCOUNT (*(reg_u16 *)REG_ADDR_VCOUNT) +#define REG_BG0CNT (*(reg_u16 *)REG_ADDR_BG0CNT) +#define REG_BG1CNT (*(reg_u16 *)REG_ADDR_BG1CNT) +#define REG_BG2CNT (*(reg_u16 *)REG_ADDR_BG2CNT) +#define REG_BG3CNT (*(reg_u16 *)REG_ADDR_BG3CNT) +#define REG_BG0HOFS (*(reg_u16 *)REG_ADDR_BG0HOFS) +#define REG_BG0VOFS (*(reg_u16 *)REG_ADDR_BG0VOFS) +#define REG_BG1HOFS (*(reg_u16 *)REG_ADDR_BG1HOFS) +#define REG_BG1VOFS (*(reg_u16 *)REG_ADDR_BG1VOFS) +#define REG_BG2HOFS (*(reg_u16 *)REG_ADDR_BG2HOFS) +#define REG_BG2VOFS (*(reg_u16 *)REG_ADDR_BG2VOFS) +#define REG_BG3HOFS (*(reg_u16 *)REG_ADDR_BG3HOFS) +#define REG_BG3VOFS (*(reg_u16 *)REG_ADDR_BG3VOFS) +#define REG_BG2PA (*(reg_u16 *)REG_ADDR_BG2PA) +#define REG_BG2PB (*(reg_u16 *)REG_ADDR_BG2PB) +#define REG_BG2PC (*(reg_u16 *)REG_ADDR_BG2PC) +#define REG_BG2PD (*(reg_u16 *)REG_ADDR_BG2PD) +#define REG_BG2X (*(reg_u32 *)REG_ADDR_BG2X) +#define REG_BG2X_L (*(reg_u16 *)REG_ADDR_BG2X_L) +#define REG_BG2X_H (*(reg_u16 *)REG_ADDR_BG2X_H) +#define REG_BG2Y (*(reg_u32 *)REG_ADDR_BG2Y) +#define REG_BG2Y_L (*(reg_u16 *)REG_ADDR_BG2Y_L) +#define REG_BG2Y_H (*(reg_u16 *)REG_ADDR_BG2Y_H) +#define REG_BG3PA (*(reg_u16 *)REG_ADDR_BG3PA) +#define REG_BG3PB (*(reg_u16 *)REG_ADDR_BG3PB) +#define REG_BG3PC (*(reg_u16 *)REG_ADDR_BG3PC) +#define REG_BG3PD (*(reg_u16 *)REG_ADDR_BG3PD) +#define REG_BG3X (*(reg_u32 *)REG_ADDR_BG3X) +#define REG_BG3X_L (*(reg_u16 *)REG_ADDR_BG3X_L) +#define REG_BG3X_H (*(reg_u16 *)REG_ADDR_BG3X_H) +#define REG_BG3Y (*(reg_u32 *)REG_ADDR_BG3Y) +#define REG_BG3Y_L (*(reg_u16 *)REG_ADDR_BG3Y_L) +#define REG_BG3Y_H (*(reg_u16 *)REG_ADDR_BG3Y_H) +#define REG_WIN0H (*(reg_wint *)REG_ADDR_WIN0H) +#define REG_WIN1H (*(reg_wint *)REG_ADDR_WIN1H) +#define REG_WIN0V (*(reg_wint *)REG_ADDR_WIN0V) +#define REG_WIN1V (*(reg_wint *)REG_ADDR_WIN1V) +#define REG_WININ (*(reg_wint *)REG_ADDR_WININ) +#define REG_WINOUT (*(reg_wint *)REG_ADDR_WINOUT) +#define REG_MOSAIC (*(reg_u16 *)REG_ADDR_MOSAIC) +#define REG_BLDCNT (*(reg_u16 *)REG_ADDR_BLDCNT) +#define REG_BLDALPHA (*(reg_u16 *)REG_ADDR_BLDALPHA) +#define REG_BLDY (*(reg_u16 *)REG_ADDR_BLDY) + +#define REG_SOUND1CNT_L (*(reg_u16 *)REG_ADDR_SOUND1CNT_L) +#define REG_NR10 (*(reg_u8 *)REG_ADDR_NR10) +#define REG_SOUND1CNT_H (*(reg_u16 *)REG_ADDR_SOUND1CNT_H) +#define REG_NR11 (*(reg_u8 *)REG_ADDR_NR11) +#define REG_NR12 (*(reg_u8 *)REG_ADDR_NR12) +#define REG_SOUND1CNT_X (*(reg_u16 *)REG_ADDR_SOUND1CNT_X) +#define REG_NR13 (*(reg_u8 *)REG_ADDR_NR13) +#define REG_NR14 (*(reg_u8 *)REG_ADDR_NR14) +#define REG_SOUND2CNT_L (*(reg_u16 *)REG_ADDR_SOUND2CNT_L) +#define REG_NR21 (*(reg_u8 *)REG_ADDR_NR21) +#define REG_NR22 (*(reg_u8 *)REG_ADDR_NR22) +#define REG_SOUND2CNT_H (*(reg_u16 *)REG_ADDR_SOUND2CNT_H) +#define REG_NR23 (*(reg_u8 *)REG_ADDR_NR23) +#define REG_NR24 (*(reg_u8 *)REG_ADDR_NR24) +#define REG_SOUND3CNT_L (*(reg_u16 *)REG_ADDR_SOUND3CNT_L) +#define REG_NR30 (*(reg_u8 *)REG_ADDR_NR30) +#define REG_SOUND3CNT_H (*(reg_u16 *)REG_ADDR_SOUND3CNT_H) +#define REG_NR31 (*(reg_u8 *)REG_ADDR_NR31) +#define REG_NR32 (*(reg_u8 *)REG_ADDR_NR32) +#define REG_SOUND3CNT_X (*(reg_u16 *)REG_ADDR_SOUND3CNT_X) +#define REG_NR33 (*(reg_u8 *)REG_ADDR_NR33) +#define REG_NR34 (*(reg_u8 *)REG_ADDR_NR34) +#define REG_SOUND4CNT_L (*(reg_u16 *)REG_ADDR_SOUND4CNT_L) +#define REG_NR41 (*(reg_u8 *)REG_ADDR_NR41) +#define REG_NR42 (*(reg_u8 *)REG_ADDR_NR42) +#define REG_SOUND4CNT_H (*(reg_u16 *)REG_ADDR_SOUND4CNT_H) +#define REG_NR43 (*(reg_u8 *)REG_ADDR_NR43) +#define REG_NR44 (*(reg_u8 *)REG_ADDR_NR44) +#define REG_SOUNDCNT_L (*(reg_u16 *)REG_ADDR_SOUNDCNT_L) +#define REG_NR50 (*(reg_u8 *)REG_ADDR_NR50) +#define REG_NR51 (*(reg_u8 *)REG_ADDR_NR51) +#define REG_SOUNDCNT_H (*(reg_u16 *)REG_ADDR_SOUNDCNT_H) +#define REG_SOUNDCNT_X (*(reg_u16 *)REG_ADDR_SOUNDCNT_X) +#define REG_NR52 (*(reg_u8 *)REG_ADDR_NR52) +#define REG_SOUNDBIAS (*(reg_u16 *)REG_ADDR_SOUNDBIAS) +#define REG_SOUNDBIAS_L (*(reg_u8 *)REG_ADDR_SOUNDBIAS_L) +#define REG_SOUNDBIAS_H (*(reg_u8 *)REG_ADDR_SOUNDBIAS_H) +#define REG_WAVE_RAM0 (*(reg_u32 *)REG_ADDR_WAVE_RAM0) +#define REG_WAVE_RAM1 (*(reg_u32 *)REG_ADDR_WAVE_RAM1) +#define REG_WAVE_RAM2 (*(reg_u32 *)REG_ADDR_WAVE_RAM2) +#define REG_WAVE_RAM3 (*(reg_u32 *)REG_ADDR_WAVE_RAM3) +#define REG_FIFO_A (*(reg_u32 *)REG_ADDR_FIFO_A) +#define REG_FIFO_B (*(reg_u32 *)REG_ADDR_FIFO_B) #define REG_DMA0SAD (*(volatile uintptr_t *)REG_ADDR_DMA0SAD) #define REG_DMA0DAD (*(volatile uintptr_t *)REG_ADDR_DMA0DAD) -#define REG_DMA0CNT (*(vu32 *)REG_ADDR_DMA0CNT) -#define REG_DMA0CNT_L (*(vu16 *)REG_ADDR_DMA0CNT_L) -#define REG_DMA0CNT_H (*(vu16 *)REG_ADDR_DMA0CNT_H) +#define REG_DMA0CNT (*(reg_u32 *)REG_ADDR_DMA0CNT) +#define REG_DMA0CNT_L (*(reg_u16 *)REG_ADDR_DMA0CNT_L) +#define REG_DMA0CNT_H (*(reg_u16 *)REG_ADDR_DMA0CNT_H) #define REG_DMA1SAD (*(volatile uintptr_t *)REG_ADDR_DMA1SAD) #define REG_DMA1DAD (*(volatile uintptr_t *)REG_ADDR_DMA1DAD) -#define REG_DMA1CNT (*(vu32 *)REG_ADDR_DMA1CNT) -#define REG_DMA1CNT_L (*(vu16 *)REG_ADDR_DMA1CNT_L) -#define REG_DMA1CNT_H (*(vu16 *)REG_ADDR_DMA1CNT_H) +#define REG_DMA1CNT (*(reg_u32 *)REG_ADDR_DMA1CNT) +#define REG_DMA1CNT_L (*(reg_u16 *)REG_ADDR_DMA1CNT_L) +#define REG_DMA1CNT_H (*(reg_u16 *)REG_ADDR_DMA1CNT_H) #define REG_DMA2SAD (*(volatile uintptr_t *)REG_ADDR_DMA2SAD) #define REG_DMA2DAD (*(volatile uintptr_t *)REG_ADDR_DMA2DAD) -#define REG_DMA2CNT (*(vu32 *)REG_ADDR_DMA2CNT) -#define REG_DMA2CNT_L (*(vu16 *)REG_ADDR_DMA2CNT_L) -#define REG_DMA2CNT_H (*(vu16 *)REG_ADDR_DMA2CNT_H) +#define REG_DMA2CNT (*(reg_u32 *)REG_ADDR_DMA2CNT) +#define REG_DMA2CNT_L (*(reg_u16 *)REG_ADDR_DMA2CNT_L) +#define REG_DMA2CNT_H (*(reg_u16 *)REG_ADDR_DMA2CNT_H) #define REG_DMA3SAD (*(volatile uintptr_t *)REG_ADDR_DMA3SAD) #define REG_DMA3DAD (*(volatile uintptr_t *)REG_ADDR_DMA3DAD) -#define REG_DMA3CNT (*(vu32 *)REG_ADDR_DMA3CNT) -#define REG_DMA3CNT_L (*(vu16 *)REG_ADDR_DMA3CNT_L) -#define REG_DMA3CNT_H (*(vu16 *)REG_ADDR_DMA3CNT_H) - -#define REG_TMCNT(n) (*(vu16 *)(REG_ADDR_TMCNT + ((n) * 4))) -#define REG_TM0CNT (*(vu32 *)REG_ADDR_TM0CNT) -#define REG_TM0CNT_L (*(vu16 *)REG_ADDR_TM0CNT_L) -#define REG_TM0CNT_H (*(vu16 *)REG_ADDR_TM0CNT_H) -#define REG_TM1CNT (*(vu32 *)REG_ADDR_TM1CNT) -#define REG_TM1CNT_L (*(vu16 *)REG_ADDR_TM1CNT_L) -#define REG_TM1CNT_H (*(vu16 *)REG_ADDR_TM1CNT_H) -#define REG_TM2CNT (*(vu32 *)REG_ADDR_TM2CNT) -#define REG_TM2CNT_L (*(vu16 *)REG_ADDR_TM2CNT_L) -#define REG_TM2CNT_H (*(vu16 *)REG_ADDR_TM2CNT_H) -#define REG_TM3CNT (*(vu32 *)REG_ADDR_TM3CNT) -#define REG_TM3CNT_L (*(vu16 *)REG_ADDR_TM3CNT_L) -#define REG_TM3CNT_H (*(vu16 *)REG_ADDR_TM3CNT_H) - -#define REG_SIOCNT (*(vu16 *)REG_ADDR_SIOCNT) -#define REG_SIOCNT_32 (*(vu32 *)REG_ADDR_SIOCNT) -#define REG_SIODATA8 (*(vu16 *)REG_ADDR_SIODATA8) -#define REG_SIODATA32 (*(vu32 *)REG_ADDR_SIODATA32) -#define REG_SIOMLT_SEND (*(vu16 *)REG_ADDR_SIOMLT_SEND) -#define REG_SIOMLT_RECV (*(vu64 *)REG_ADDR_SIOMLT_RECV) -#define REG_SIOMULTI0 (*(vu16 *)REG_ADDR_SIOMULTI0) -#define REG_SIOMULTI1 (*(vu16 *)REG_ADDR_SIOMULTI1) -#define REG_SIOMULTI2 (*(vu16 *)REG_ADDR_SIOMULTI2) -#define REG_SIOMULTI3 (*(vu16 *)REG_ADDR_SIOMULTI3) - -#define REG_KEYINPUT (*(vu16 *)REG_ADDR_KEYINPUT) -#define REG_KEYCNT (*(vu16 *)REG_ADDR_KEYCNT) - -#define REG_RCNT (*(vu16 *)REG_ADDR_RCNT) - -#define REG_IME (*(vu16 *)REG_ADDR_IME) -#define REG_IE (*(vu16 *)REG_ADDR_IE) -#define REG_IF (*(vu16 *)REG_ADDR_IF) - -#define REG_WAITCNT (*(vu16 *)REG_ADDR_WAITCNT) +#define REG_DMA3CNT (*(reg_u32 *)REG_ADDR_DMA3CNT) +#define REG_DMA3CNT_L (*(reg_u16 *)REG_ADDR_DMA3CNT_L) +#define REG_DMA3CNT_H (*(reg_u16 *)REG_ADDR_DMA3CNT_H) + +#define REG_TMCNT(n) (*(reg_u16 *)(REG_ADDR_TMCNT + ((n) * 4))) +#define REG_TM0CNT (*(reg_u32 *)REG_ADDR_TM0CNT) +#define REG_TM0CNT_L (*(reg_u16 *)REG_ADDR_TM0CNT_L) +#define REG_TM0CNT_H (*(reg_u16 *)REG_ADDR_TM0CNT_H) +#define REG_TM1CNT (*(reg_u32 *)REG_ADDR_TM1CNT) +#define REG_TM1CNT_L (*(reg_u16 *)REG_ADDR_TM1CNT_L) +#define REG_TM1CNT_H (*(reg_u16 *)REG_ADDR_TM1CNT_H) +#define REG_TM2CNT (*(reg_u32 *)REG_ADDR_TM2CNT) +#define REG_TM2CNT_L (*(reg_u16 *)REG_ADDR_TM2CNT_L) +#define REG_TM2CNT_H (*(reg_u16 *)REG_ADDR_TM2CNT_H) +#define REG_TM3CNT (*(reg_u32 *)REG_ADDR_TM3CNT) +#define REG_TM3CNT_L (*(reg_u16 *)REG_ADDR_TM3CNT_L) +#define REG_TM3CNT_H (*(reg_u16 *)REG_ADDR_TM3CNT_H) + +#define REG_SIOCNT (*(reg_u16 *)REG_ADDR_SIOCNT) +#define REG_SIOCNT_32 (*(reg_u32 *)REG_ADDR_SIOCNT) +#define REG_SIODATA8 (*(reg_u16 *)REG_ADDR_SIODATA8) +#define REG_SIODATA32 (*(reg_u32 *)REG_ADDR_SIODATA32) +#define REG_SIOMLT_SEND (*(reg_u16 *)REG_ADDR_SIOMLT_SEND) +#define REG_SIOMLT_RECV (*(reg_u64 *)REG_ADDR_SIOMLT_RECV) +#define REG_SIOMULTI0 (*(reg_u16 *)REG_ADDR_SIOMULTI0) +#define REG_SIOMULTI1 (*(reg_u16 *)REG_ADDR_SIOMULTI1) +#define REG_SIOMULTI2 (*(reg_u16 *)REG_ADDR_SIOMULTI2) +#define REG_SIOMULTI3 (*(reg_u16 *)REG_ADDR_SIOMULTI3) + +#define REG_KEYINPUT (*(reg_u16 *)REG_ADDR_KEYINPUT) +#define REG_KEYCNT (*(reg_u16 *)REG_ADDR_KEYCNT) + +#define REG_RCNT (*(reg_u16 *)REG_ADDR_RCNT) + +#define REG_IME (*(reg_u16 *)REG_ADDR_IME) +#define REG_IE (*(reg_u16 *)REG_ADDR_IE) +#define REG_IF (*(reg_u16 *)REG_ADDR_IF) + +#define REG_WAITCNT (*(reg_u16 *)REG_ADDR_WAITCNT) // I/O register fields diff --git a/include/gba/types.h b/include/gba/types.h index 72e721ad7..d22c4a684 100644 --- a/include/gba/types.h +++ b/include/gba/types.h @@ -20,6 +20,9 @@ typedef struct __attribute__((packed)) name struct_body name; #endif +#ifdef __PS2__ +#include +#else typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; @@ -28,6 +31,7 @@ typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; +#endif #if (GAME == GAME_SA1) typedef u8 MetatileIndexType; @@ -38,12 +42,12 @@ typedef u16 MetatileIndexType; // If the DISPLAY_HEIGHT was >255, scanline effects would break, // so we have to make this variable bigger. // (u16 should be plenty for screen coordinates, right?) -#if !defined(DISPLAY_HEIGHT) -#error DISPLAY_HEIGHT not defined. +#if !defined(WIDESCREEN_HACK) && !PLATFORM_GBA +#error WIDESCREEN_HACK not defined. #endif /// TODO: Technically this should only be #if (DISPLAY_HEIGHT > 255), // we should probably replace uses of int_vcount with a different type where a high DISPLAY_WIDTH necessitates u16. -#if ((DISPLAY_WIDTH > 255) || (DISPLAY_HEIGHT > 255)) +#if !PLATFORM_GBA && WIDESCREEN_HACK typedef u16 int_vcount; #else typedef u8 int_vcount; diff --git a/include/lib/m4a/m4a_internal.h b/include/lib/m4a/m4a_internal.h index 7755591c6..cf90c7f8e 100644 --- a/include/lib/m4a/m4a_internal.h +++ b/include/lib/m4a/m4a_internal.h @@ -106,6 +106,13 @@ #define MAX_LINES 0 #endif +typedef s32 fixed8_24; +#define float_to_fp8_24(value) (fixed8_24)((value)*16777216.0f) +#define u32_to_fp8_24(value) ((value) << 24) +#define fp8_24_to_u32(value) ((value) >> 24) +#define fp8_24_to_float(value) (float)((value) / 16777216.0f) +#define fp8_24_fractional_part(value) ((value)&0xFFFFFF) + struct MP2KTrack; struct MP2KPlayerState; @@ -185,7 +192,7 @@ struct MixerSource { u8 padding5; u32 ct; - float fw; + fixed8_24 fw; u32 freq; } sound; @@ -243,7 +250,7 @@ struct SoundMixerState { #if PLATFORM_GBA s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2]; #else - float pcmBuffer[PCM_DMA_BUF_SIZE * 2]; + fixed8_24 pcmBuffer[PCM_DMA_BUF_SIZE * 2]; #endif }; diff --git a/include/platform/platform.h b/include/platform/platform.h index 323b195c0..728ceee8d 100644 --- a/include/platform/platform.h +++ b/include/platform/platform.h @@ -21,7 +21,7 @@ extern void Platform_RLFree(unsigned char *dest); extern void Platform_LZDecompressUnsafe(unsigned char *src, unsigned char *dest); extern void Platform_RLDecompressUnsafe(unsigned char *src, unsigned char *dest); -extern void Platform_QueueAudio(const void *data, u32 numBytes); +extern void Platform_QueueAudio(const s16 *data, u32 numBytes); extern void Platform_ProcessBackgroundsCopyQueue(void); // TODO: Re-enable once #include-ing global.h/core.h/sprite.h does not result in compilation errors. // void Platform_TransformSprite(Sprite *s, SpriteTransform *transform); diff --git a/include/platform/shared/audio/cgb_audio.h b/include/platform/shared/audio/cgb_audio.h index 65dd77a91..8312e496f 100644 --- a/include/platform/shared/audio/cgb_audio.h +++ b/include/platform/shared/audio/cgb_audio.h @@ -18,9 +18,9 @@ struct AudioCGB { u8 EnvCounterI[4]; bool8 EnvDir[4]; bool8 DAC[4]; - float WAVRAM[32]; + fixed8_24 WAVRAM[32]; u16 ch4LFSR[2]; - float outBuffer[PCM_DMA_BUF_SIZE * 2]; + fixed8_24 outBuffer[PCM_DMA_BUF_SIZE * 2]; }; void cgb_audio_init(u32 rate); @@ -31,6 +31,6 @@ void cgb_set_length(u8 channel, u8 length); void cgb_set_envelope(u8 channel, u8 envelope); void cgb_trigger_note(u8 channel); void cgb_audio_generate(u16 samplesPerFrame); -float *cgb_get_buffer(); +fixed8_24 *cgb_get_buffer(); #endif diff --git a/include/platform/shared/audio/cgb_tables.h b/include/platform/shared/audio/cgb_tables.h index 0966471ae..3e5b56b38 100644 --- a/include/platform/shared/audio/cgb_tables.h +++ b/include/platform/shared/audio/cgb_tables.h @@ -1,2100 +1,2361 @@ #ifndef CGB_TABLES_H #define CGB_TABLES_H -const int16_t PU0[32] - = { 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +#include "platform/shared/audio/cgb_audio.h" -const int16_t PU1[32] - = { 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +#define SAMPLE_RATE 48000.0 +#define FREQUENCY_RATE (SAMPLE_RATE / 32.0) -const int16_t PU2[32] = { 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 }; +const fixed8_24 PU0[32] + = { float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15) }; -const int16_t PU3[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }; +const fixed8_24 PU1[32] + = { float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15) }; + +const fixed8_24 PU2[32] + = { float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15) }; + +const fixed8_24 PU3[32] + = { float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), float_to_fp8_24(1.0 / 15), + float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15), float_to_fp8_24(-1.0 / 15) }; int16_t WAV[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1 }; -const float freqTable[2048] = { 32, - 32.0156326331216, - 32.0312805474096, - 32.0469437652812, - 32.0626223091976, - 32.0783162016642, - 32.0940254652302, - 32.109750122489, - 32.1254901960784, - 32.1412457086807, - 32.1570166830226, - 32.1728031418753, - 32.188605108055, - 32.2044226044226, - 32.220255653884, - 32.2361042793901, - 32.251968503937, - 32.2678483505662, - 32.2837438423645, - 32.2996550024643, - 32.3155818540434, - 32.3315244203256, - 32.3474827245805, - 32.3634567901235, - 32.3794466403162, - 32.3954522985665, - 32.4114737883284, - 32.4275111331024, - 32.4435643564356, - 32.4596334819217, - 32.4757185332012, - 32.4918195339613, - 32.5079365079365, - 32.5240694789082, - 32.5402184707051, - 32.5563835072032, - 32.572564612326, - 32.5887618100448, - 32.6049751243781, - 32.6212045793927, - 32.6374501992032, - 32.6537120079721, - 32.6699900299103, - 32.6862842892768, - 32.7025948103792, - 32.7189216175736, - 32.7352647352647, - 32.751624187906, - 32.768, - 32.784392196098, - 32.8008008008008, - 32.8172258387581, - 32.8336673346693, - 32.8501253132832, - 32.8665997993982, - 32.8830908178625, - 32.8995983935743, - 32.9161225514817, - 32.9326633165829, - 32.9492207139266, - 32.9657947686117, - 32.9823855057876, - 32.9989929506546, - 33.0156171284635, - 33.0322580645161, - 33.0489157841654, - 33.0655903128153, - 33.0822816759213, - 33.0989898989899, - 33.1157150075796, - 33.1324570273003, - 33.1492159838139, - 33.165991902834, - 33.1827848101266, - 33.1995947315096, - 33.2164216928535, - 33.2332657200811, - 33.2501268391679, - 33.2670050761421, - 33.2839004570848, - 33.3008130081301, - 33.3177427554652, - 33.3346897253306, - 33.3516539440204, - 33.3686354378819, - 33.3856342333164, - 33.4026503567788, - 33.4196838347782, - 33.4367346938776, - 33.4538029606942, - 33.4708886618999, - 33.4879918242207, - 33.5051124744376, - 33.5222506393862, - 33.539406345957, - 33.5565796210957, - 33.5737704918033, - 33.5909789851358, - 33.6082051282051, - 33.6254489481786, - 33.6427104722793, - 33.6599897277863, - 33.6772867420349, - 33.6946015424164, - 33.7119341563786, - 33.7292846114256, - 33.7466529351184, - 33.7640391550747, - 33.7814432989691, - 33.7988653945333, - 33.8163054695562, - 33.8337635518844, - 33.8512396694215, - 33.8687338501292, - 33.8862461220269, - 33.9037765131919, - 33.9213250517598, - 33.9388917659244, - 33.9564766839378, - 33.9740798341109, - 33.9917012448133, - 34.0093409444733, - 34.0269989615784, - 34.0446753246753, - 34.0623700623701, - 34.0800832033281, - 34.0978147762747, - 34.1155648099948, - 34.1333333333333, - 34.1511203751954, - 34.1689259645464, - 34.1867501304121, - 34.2045929018789, - 34.222454308094, - 34.2403343782654, - 34.2582331416623, - 34.2761506276151, - 34.2940868655154, - 34.3120418848168, - 34.330015715034, - 34.3480083857442, - 34.3660199265863, - 34.3840503672613, - 34.4020997375328, - 34.4201680672269, - 34.4382553862323, - 34.4563617245005, - 34.4744871120463, - 34.4926315789474, - 34.5107951553449, - 34.5289778714436, - 34.5471797575119, - 34.5654008438819, - 34.5836411609499, - 34.6019007391763, - 34.6201796090861, - 34.6384778012685, - 34.6567953463776, - 34.6751322751323, - 34.6934886183166, - 34.7118644067797, - 34.7302596714361, - 34.7486744432662, - 34.7671087533156, - 34.7855626326964, - 34.8040361125863, - 34.8225292242295, - 34.8410419989367, - 34.8595744680851, - 34.8781266631187, - 34.8966986155485, - 34.9152903569526, - 34.9339019189765, - 34.9525333333333, - 34.9711846318036, - 34.989855846236, - 35.008547008547, - 35.0272581507215, - 35.0459893048128, - 35.0647405029427, - 35.0835117773019, - 35.10230316015, - 35.1211146838156, - 35.1399463806971, - 35.1587982832618, - 35.1776704240472, - 35.1965628356606, - 35.2154755507792, - 35.2344086021505, - 35.2533620225928, - 35.2723358449946, - 35.2913301023156, - 35.3103448275862, - 35.3293800539084, - 35.3484358144552, - 35.3675121424717, - 35.3866090712743, - 35.4057266342518, - 35.4248648648649, - 35.4440237966468, - 35.4632034632035, - 35.4824038982133, - 35.501625135428, - 35.5208672086721, - 35.5401301518438, - 35.5594139989148, - 35.5787187839305, - 35.5980445410103, - 35.6173913043478, - 35.636759108211, - 35.6561479869423, - 35.6755579749592, - 35.6949891067538, - 35.7144414168937, - 35.7339149400218, - 35.7534097108565, - 35.7729257641921, - 35.792463134899, - 35.8120218579235, - 35.8316019682887, - 35.8512035010941, - 35.8708264915161, - 35.8904709748083, - 35.9101369863014, - 35.9298245614035, - 35.9495337356007, - 35.9692645444566, - 35.9890170236134, - 36.0087912087912, - 36.0285871357889, - 36.048404840484, - 36.0682443588332, - 36.0881057268722, - 36.1079889807162, - 36.1278941565601, - 36.1478212906784, - 36.167770419426, - 36.187741579238, - 36.2077348066298, - 36.2277501381979, - 36.2477876106195, - 36.267847260653, - 36.2879291251384, - 36.3080332409972, - 36.3281596452328, - 36.3483083749307, - 36.3684794672586, - 36.388672959467, - 36.4088888888889, - 36.4291272929405, - 36.4493882091212, - 36.4696716750139, - 36.4899777282851, - 36.5103064066852, - 36.5306577480491, - 36.5510317902956, - 36.5714285714286, - 36.5918481295366, - 36.6122905027933, - 36.6327557294578, - 36.6532438478747, - 36.6737548964745, - 36.6942889137738, - 36.7148459383753, - 36.7354260089686, - 36.7560291643298, - 36.7766554433221, - 36.7973048848961, - 36.8179775280899, - 36.8386734120292, - 36.859392575928, - 36.8801350590884, - 36.9009009009009, - 36.9216901408451, - 36.9425028184893, - 36.9633389734913, - 36.9841986455982, - 37.0050818746471, - 37.025988700565, - 37.0469191633691, - 37.0678733031674, - 37.0888511601585, - 37.1098527746319, - 37.1308781869688, - 37.1519274376417, - 37.173000567215, - 37.1940976163451, - 37.2152186257808, - 37.2363636363636, - 37.2575326890279, - 37.2787258248009, - 37.2999430848036, - 37.3211845102506, - 37.3424501424501, - 37.363740022805, - 37.3850541928123, - 37.4063926940639, - 37.4277555682467, - 37.4491428571429, - 37.4705546026301, - 37.4919908466819, - 37.5134516313681, - 37.5349369988545, - 37.556446991404, - 37.5779816513761, - 37.5995410212278, - 37.6211251435132, - 37.6427340608845, - 37.664367816092, - 37.6860264519839, - 37.7077100115075, - 37.7294185377087, - 37.7511520737327, - 37.7729106628242, - 37.7946943483276, - 37.8165031736872, - 37.838337182448, - 37.8601964182553, - 37.8820809248555, - 37.903990746096, - 37.9259259259259, - 37.9478865083961, - 37.9698725376593, - 37.991884057971, - 38.0139211136891, - 38.0359837492745, - 38.0580720092915, - 38.0801859384079, - 38.1023255813953, - 38.1244909831297, - 38.1466821885914, - 38.1688992428655, - 38.1911421911422, - 38.2134110787172, - 38.2357059509918, - 38.2580268534734, - 38.2803738317757, - 38.3027469316189, - 38.3251461988304, - 38.3475716793446, - 38.3700234192037, - 38.3925014645577, - 38.4150058616647, - 38.4375366568915, - 38.4600938967136, - 38.4826776277158, - 38.5052878965922, - 38.527924750147, - 38.5505882352941, - 38.5732783990583, - 38.5959952885748, - 38.6187389510902, - 38.6415094339623, - 38.6643067846608, - 38.6871310507674, - 38.7099822799764, - 38.7328605200946, - 38.755765819042, - 38.7786982248521, - 38.801657785672, - 38.824644549763, - 38.8476585655009, - 38.870699881376, - 38.8937685459941, - 38.916864608076, - 38.9399881164587, - 38.9631391200951, - 38.9863176680547, - 39.0095238095238, - 39.0327575938058, - 39.0560190703218, - 39.0793082886106, - 39.1026252983294, - 39.1259701492537, - 39.1493428912784, - 39.1727435744172, - 39.1961722488038, - 39.2196289646918, - 39.2431137724551, - 39.2666267225884, - 39.2901678657074, - 39.3137372525495, - 39.3373349339736, - 39.360960960961, - 39.3846153846154, - 39.4082982561636, - 39.4320096269555, - 39.4557495484648, - 39.4795180722892, - 39.5033152501507, - 39.5271411338963, - 39.5509957754979, - 39.5748792270531, - 39.5987915407855, - 39.6227327690447, - 39.6467029643073, - 39.6707021791768, - 39.694730466384, - 39.7187878787879, - 39.7428744693754, - 39.7669902912621, - 39.7911353976928, - 39.8153098420413, - 39.8395136778115, - 39.8637469586375, - 39.8880097382836, - 39.9123020706455, - 39.9366240097502, - 39.9609756097561, - 39.9853569249542, - 40.009768009768, - 40.0342089187538, - 40.0586797066015, - 40.0831804281346, - 40.1077111383109, - 40.1322718922229, - 40.156862745098, - 40.1814837522992, - 40.2061349693252, - 40.2308164518109, - 40.2555282555283, - 40.280270436386, - 40.3050430504305, - 40.3298461538462, - 40.3546798029557, - 40.3795440542206, - 40.4044389642417, - 40.4293645897594, - 40.4543209876543, - 40.4793082149475, - 40.504326328801, - 40.5293753865182, - 40.5544554455446, - 40.5795665634675, - 40.6047087980174, - 40.6298822070676, - 40.6550868486352, - 40.6803227808814, - 40.7055900621118, - 40.7308887507769, - 40.7562189054726, - 40.7815805849409, - 40.8069738480697, - 40.8323987538941, - 40.857855361596, - 40.8833437305053, - 40.9088639200999, - 40.9344159900062, - 40.96, - 40.9856160100063, - 41.0112640801001, - 41.0369442705072, - 41.062656641604, - 41.0884012539185, - 41.1141781681305, - 41.1399874450722, - 41.1658291457287, - 41.1917033312382, - 41.2176100628931, - 41.2435494021397, - 41.2695214105793, - 41.2955261499685, - 41.3215636822194, - 41.3476340694006, - 41.3737373737374, - 41.3998736576121, - 41.4260429835651, - 41.4522454142948, - 41.4784810126582, - 41.504749841672, - 41.531051964512, - 41.5573874445149, - 41.5837563451777, - 41.6101587301587, - 41.6365946632783, - 41.6630642085188, - 41.6895674300254, - 41.7161043921069, - 41.7426751592357, - 41.7692797960484, - 41.7959183673469, - 41.8225909380983, - 41.8492975734355, - 41.8760383386582, - 41.9028132992327, - 41.9296225207934, - 41.9564660691421, - 41.9833440102498, - 42.0102564102564, - 42.0372033354715, - 42.0641848523748, - 42.0912010276172, - 42.1182519280206, - 42.1453376205788, - 42.1724581724582, - 42.1996136509981, - 42.2268041237113, - 42.254029658285, - 42.2812903225807, - 42.3085861846352, - 42.3359173126615, - 42.3632837750485, - 42.3906856403622, - 42.4181229773463, - 42.4455958549223, - 42.4731043421905, - 42.5006485084306, - 42.5282284231019, - 42.5558441558442, - 42.5834957764782, - 42.6111833550065, - 42.6389069616135, - 42.6666666666667, - 42.6944625407166, - 42.722294654498, - 42.7501630789302, - 42.7780678851175, - 42.8060091443501, - 42.8339869281046, - 42.8620013080445, - 42.890052356021, - 42.9181401440733, - 42.9462647444299, - 42.9744262295082, - 43.002624671916, - 43.0308601444517, - 43.0591327201051, - 43.0874424720579, - 43.1157894736842, - 43.1441737985517, - 43.1725955204216, - 43.2010547132498, - 43.2295514511873, - 43.2580858085809, - 43.2866578599736, - 43.3152676801057, - 43.3439153439153, - 43.3726009265387, - 43.4013245033113, - 43.4300861497681, - 43.4588859416446, - 43.4877239548772, - 43.5166002656043, - 43.5455149501661, - 43.5744680851064, - 43.6034597471723, - 43.6324900133156, - 43.6615589606929, - 43.6906666666667, - 43.7198132088059, - 43.7489986648865, - 43.7782231128925, - 43.807486631016, - 43.8367892976589, - 43.8661311914324, - 43.8955123911587, - 43.9249329758713, - 43.9543930248156, - 43.9838926174497, - 44.0134318334453, - 44.0430107526882, - 44.0726294552791, - 44.1022880215343, - 44.1319865319865, - 44.1617250673855, - 44.1915037086986, - 44.221322537112, - 44.2511816340311, - 44.2810810810811, - 44.3110209601082, - 44.34100135318, - 44.3710223425863, - 44.4010840108401, - 44.431186440678, - 44.4613297150611, - 44.4915139171758, - 44.5217391304348, - 44.5520054384772, - 44.5823129251701, - 44.6126616746086, - 44.6430517711172, - 44.6734832992502, - 44.7039563437926, - 44.7344709897611, - 44.7650273224044, - 44.7956254272044, - 44.8262653898769, - 44.8569472963724, - 44.8876712328767, - 44.9184372858122, - 44.9492455418381, - 44.9800960878518, - 45.010989010989, - 45.0419243986254, - 45.0729023383769, - 45.1039229181005, - 45.1349862258953, - 45.1660923501034, - 45.1972413793103, - 45.2284334023465, - 45.2596685082873, - 45.2909467864547, - 45.3222683264177, - 45.3536332179931, - 45.3850415512465, - 45.4164934164934, - 45.4479889042996, - 45.4795281054823, - 45.5111111111111, - 45.5427380125087, - 45.5744089012517, - 45.6061238691719, - 45.6378830083566, - 45.6696864111498, - 45.7015341701534, - 45.7334263782275, - 45.7653631284916, - 45.7973445143256, - 45.8293706293706, - 45.8614415675297, - 45.8935574229692, - 45.9257182901191, - 45.9579242636746, - 45.9901754385965, - 46.0224719101124, - 46.0548137737175, - 46.0872011251758, - 46.1196340605208, - 46.1521126760563, - 46.184637068358, - 46.2172073342736, - 46.2498235709245, - 46.2824858757062, - 46.3151943462898, - 46.3479490806223, - 46.3807501769285, - 46.4135977337111, - 46.446491849752, - 46.4794326241135, - 46.5124201561391, - 46.5454545454545, - 46.5785358919687, - 46.6116642958748, - 46.6448398576513, - 46.6780626780627, - 46.7113328581611, - 46.7446504992867, - 46.7780157030692, - 46.8114285714286, - 46.8448892065761, - 46.8783977110157, - 46.9119541875447, - 46.945558739255, - 46.9792114695341, - 47.012912482066, - 47.0466618808327, - 47.0804597701149, - 47.1143062544932, - 47.1482014388489, - 47.1821454283657, - 47.2161383285303, - 47.2501802451334, - 47.2842712842713, - 47.3184115523466, - 47.3526011560694, - 47.3868402024584, - 47.4211287988423, - 47.4554670528602, - 47.4898550724638, - 47.5242929659173, - 47.5587808417997, - 47.5933188090051, - 47.6279069767442, - 47.6625454545455, - 47.6972343522562, - 47.7319737800437, - 47.7667638483965, - 47.8016046681255, - 47.836496350365, - 47.8714390065741, - 47.906432748538, - 47.9414776883687, - 47.9765739385066, - 48.0117216117216, - 48.0469208211144, - 48.0821716801174, - 48.1174743024963, - 48.1528288023512, - 48.1882352941176, - 48.2236938925681, - 48.259204712813, - 48.2947678703021, - 48.330383480826, - 48.3660516605166, - 48.4017725258493, - 48.4375461936438, - 48.4733727810651, - 48.5092524056255, - 48.5451851851852, - 48.581171237954, - 48.6172106824926, - 48.6533036377134, - 48.6894502228826, - 48.7256505576208, - 48.7619047619048, - 48.7982129560685, - 48.8345752608048, - 48.8709917971663, - 48.9074626865672, - 48.9439880507842, - 48.9805680119582, - 49.0172026925954, - 49.0538922155689, - 49.0906367041199, - 49.1274362818591, - 49.1642910727682, - 49.2012012012012, - 49.2381667918858, - 49.2751879699248, - 49.3122648607976, - 49.3493975903615, - 49.3865862848531, - 49.4238310708899, - 49.4611320754717, - 49.4984894259819, - 49.535903250189, - 49.5733736762481, - 49.6109008327025, - 49.6484848484849, - 49.6861258529189, - 49.7238239757208, - 49.7615793470008, - 49.7993920972644, - 49.8372623574145, - 49.8751902587519, - 49.9131759329779, - 49.9512195121951, - 49.9893211289092, - 50.0274809160305, - 50.0656990068755, - 50.1039755351682, - 50.1423106350421, - 50.1807044410413, - 50.2191570881226, - 50.2576687116564, - 50.296239447429, - 50.3348694316436, - 50.3735588009224, - 50.4123076923077, - 50.451116243264, - 50.4899845916795, - 50.5289128758674, - 50.5679012345679, - 50.6069498069498, - 50.6460587326121, - 50.6852281515855, - 50.7244582043344, - 50.7637490317583, - 50.8031007751938, - 50.8425135764158, - 50.8819875776397, - 50.9215229215229, - 50.9611197511664, - 51.0007782101167, - 51.0404984423676, - 51.0802805923617, - 51.1201248049922, - 51.160031225605, - 51.2, - 51.2400312744331, - 51.2801251956182, - 51.3202819107283, - 51.3605015673981, - 51.4007843137255, - 51.4411302982732, - 51.4815396700707, - 51.5220125786164, - 51.5625491738788, - 51.6031496062992, - 51.6438140267928, - 51.6845425867508, - 51.7253354380426, - 51.7661927330174, - 51.8071146245059, - 51.8481012658228, - 51.889152810768, - 51.9302694136292, - 51.9714512291832, - 52.0126984126984, - 52.0540111199365, - 52.0953895071542, - 52.1368337311058, - 52.1783439490446, - 52.2199203187251, - 52.2615629984051, - 52.3032721468476, - 52.3450479233227, - 52.3868904876099, - 52.4288, - 52.470776621297, - 52.5128205128205, - 52.5549318364074, - 52.5971107544141, - 52.6393574297189, - 52.6816720257235, - 52.7240547063556, - 52.7665056360709, - 52.809024979855, - 52.8516129032258, - 52.8942695722357, - 52.9369951534734, - 52.9797898140663, - 53.0226537216829, - 53.0655870445344, - 53.1085899513776, - 53.1516626115166, - 53.1948051948052, - 53.2380178716491, - 53.2813008130081, - 53.3246541903987, - 53.3680781758958, - 53.4115729421353, - 53.4551386623165, - 53.4987755102041, - 53.5424836601307, - 53.5862632869992, - 53.6301145662848, - 53.6740376740377, - 53.7180327868852, - 53.7621000820345, - 53.8062397372742, - 53.8504519309778, - 53.8947368421053, - 53.9390946502058, - 53.9835255354201, - 54.0280296784831, - 54.0726072607261, - 54.1172584640793, - 54.1619834710744, - 54.206782464847, - 54.2516556291391, - 54.2966031483016, - 54.3416252072969, - 54.3867219917012, - 54.4318936877076, - 54.477140482128, - 54.522462562396, - 54.5678601165695, - 54.6133333333333, - 54.6588824020017, - 54.7045075125209, - 54.750208855472, - 54.7959866220736, - 54.8418410041841, - 54.8877721943049, - 54.9337803855826, - 54.9798657718121, - 55.0260285474391, - 55.072268907563, - 55.1185870479394, - 55.1649831649832, - 55.2114574557709, - 55.2580101180438, - 55.304641350211, - 55.3513513513514, - 55.3981403212172, - 55.4450084602369, - 55.4919559695174, - 55.5389830508475, - 55.5860899067006, - 55.6332767402377, - 55.6805437553101, - 55.7278911564626, - 55.7753191489362, - 55.8228279386712, - 55.8704177323103, - 55.9180887372014, - 55.9658411614005, - 56.0136752136752, - 56.0615911035073, - 56.1095890410959, - 56.1576692373608, - 56.2058319039451, - 56.2540772532189, - 56.3024054982818, - 56.3508168529665, - 56.3993115318417, - 56.4478897502153, - 56.4965517241379, - 56.5452976704055, - 56.594127806563, - 56.6430423509075, - 56.6920415224914, - 56.7411255411255, - 56.790294627383, - 56.8395490026019, - 56.8888888888889, - 56.9383145091225, - 56.9878260869565, - 57.0374238468233, - 57.0871080139373, - 57.1368788142982, - 57.1867364746946, - 57.2366812227074, - 57.2867132867133, - 57.336832895888, - 57.3870402802102, - 57.4373356704645, - 57.4877192982456, - 57.5381913959614, - 57.5887521968366, - 57.6394019349165, - 57.6901408450704, - 57.7409691629956, - 57.7918871252205, - 57.8428949691086, - 57.8939929328622, - 57.9451812555261, - 57.9964601769912, - 58.0478299379982, - 58.0992907801418, - 58.150842945874, - 58.202486678508, - 58.2542222222222, - 58.3060498220641, - 58.3579697239537, - 58.4099821746881, - 58.4620874219447, - 58.5142857142857, - 58.5665773011618, - 58.6189624329159, - 58.6714413607878, - 58.7240143369176, - 58.7766816143498, - 58.8294434470377, - 58.8823000898473, - 58.9352517985612, - 58.988298829883, - 59.0414414414414, - 59.0946798917944, - 59.1480144404332, - 59.2014453477868, - 59.254972875226, - 59.3085972850679, - 59.3623188405797, - 59.4161378059837, - 59.470054446461, - 59.5240690281562, - 59.5781818181818, - 59.6323930846224, - 59.6867030965392, - 59.7411121239745, - 59.7956204379562, - 59.8502283105023, - 59.9049360146252, - 59.9597438243367, - 60.014652014652, - 60.0696608615949, - 60.1247706422018, - 60.1799816345271, - 60.2352941176471, - 60.2907083716651, - 60.3462246777164, - 60.4018433179724, - 60.4575645756458, - 60.5133887349954, - 60.5693160813309, - 60.6253469010176, - 60.6814814814815, - 60.7377201112141, - 60.7940630797774, - 60.8505106778087, - 60.907063197026, - 60.9637209302326, - 61.0204841713222, - 61.0773532152843, - 61.134328358209, - 61.1914098972923, - 61.2485981308411, - 61.3058933582788, - 61.3632958801498, - 61.4208059981256, - 61.4784240150094, - 61.5361502347418, - 61.593984962406, - 61.6519285042333, - 61.7099811676083, - 61.7681432610745, - 61.8264150943396, - 61.8847969782814, - 61.9432892249527, - 62.0018921475875, - 62.0606060606061, - 62.1194312796209, - 62.1783681214421, - 62.2374169040836, - 62.2965779467681, - 62.3558515699334, - 62.4152380952381, - 62.4747378455672, - 62.5343511450382, - 62.5940783190067, - 62.6539196940727, - 62.7138755980861, - 62.7739463601533, - 62.8341323106424, - 62.89443378119, - 62.954851104707, - 63.0153846153846, - 63.0760346487007, - 63.1368015414258, - 63.1976856316297, - 63.2586872586873, - 63.319806763285, - 63.3810444874275, - 63.4424007744434, - 63.5038759689923, - 63.5654704170708, - 63.6271844660194, - 63.6890184645287, - 63.7509727626459, - 63.8130477117819, - 63.8752436647174, - 63.9375609756098, - 64, - 64.0625610948192, - 64.1252446183953, - 64.1880509304603, - 64.2509803921569, - 64.3140333660451, - 64.37721021611, - 64.440511307768, - 64.503937007874, - 64.5674876847291, - 64.6311637080868, - 64.6949654491609, - 64.7588932806324, - 64.8229475766568, - 64.8871287128713, - 64.9514370664024, - 65.015873015873, - 65.0804369414101, - 65.1451292246521, - 65.2099502487562, - 65.2749003984064, - 65.3399800598205, - 65.4051896207585, - 65.4705294705295, - 65.536, - 65.6016016016016, - 65.6673346693387, - 65.7331995987964, - 65.7991967871486, - 65.8653266331658, - 65.9315895372234, - 65.9979859013092, - 66.0645161290323, - 66.1311806256307, - 66.1979797979798, - 66.2649140546006, - 66.331983805668, - 66.3991894630193, - 66.4665314401623, - 66.5340101522843, - 66.6016260162602, - 66.6693794506612, - 66.7372708757637, - 66.8053007135576, - 66.8734693877551, - 66.9417773237998, - 67.0102249488753, - 67.078812691914, - 67.1475409836066, - 67.2164102564103, - 67.2854209445585, - 67.3545734840699, - 67.4238683127572, - 67.4933058702369, - 67.5628865979382, - 67.6326109391125, - 67.702479338843, - 67.7724922440538, - 67.8426501035197, - 67.9129533678757, - 67.9834024896266, - 68.0539979231568, - 68.1247401247401, - 68.1956295525494, - 68.2666666666667, - 68.3378519290928, - 68.4091858037578, - 68.4806687565308, - 68.5523012552301, - 68.6240837696335, - 68.6960167714885, - 68.7681007345226, - 68.8403361344538, - 68.9127234490011, - 68.9852631578947, - 69.0579557428872, - 69.1308016877637, - 69.2038014783527, - 69.276955602537, - 69.3502645502646, - 69.4237288135593, - 69.4973488865324, - 69.5711252653928, - 69.6450584484591, - 69.7191489361702, - 69.7933972310969, - 69.8678038379531, - 69.9423692636073, - 70.017094017094, - 70.0919786096257, - 70.1670235546039, - 70.2422293676313, - 70.3175965665236, - 70.3931256713212, - 70.4688172043011, - 70.5446716899892, - 70.6206896551724, - 70.6968716289105, - 70.7732181425486, - 70.8497297297297, - 70.9264069264069, - 71.0032502708559, - 71.0802603036876, - 71.157437567861, - 71.2347826086957, - 71.3122959738847, - 71.3899782135076, - 71.4678298800436, - 71.5458515283843, - 71.624043715847, - 71.7024070021882, - 71.7809419496166, - 71.859649122807, - 71.9385290889133, - 72.0175824175824, - 72.0968096809681, - 72.1762114537445, - 72.2557883131202, - 72.3355408388521, - 72.4154696132597, - 72.4955752212389, - 72.5758582502769, - 72.6563192904656, - 72.7369589345172, - 72.8177777777778, - 72.8987764182425, - 72.9799554565702, - 73.0613154960981, - 73.1428571428571, - 73.2245810055866, - 73.3064876957494, - 73.3885778275476, - 73.4708520179372, - 73.5533108866442, - 73.6359550561798, - 73.718785151856, - 73.8018018018018, - 73.8850056369786, - 73.9683972911964, - 74.0519774011299, - 74.1357466063348, - 74.2197055492639, - 74.3038548752835, - 74.3881952326901, - 74.4727272727273, - 74.5574516496018, - 74.6423690205011, - 74.72748004561, - 74.8127853881279, - 74.8982857142857, - 74.9839816933639, - 75.0698739977091, - 75.1559633027523, - 75.2422502870264, - 75.3287356321839, - 75.415420023015, - 75.5023041474654, - 75.5893886966551, - 75.6766743648961, - 75.764161849711, - 75.8518518518518, - 75.9397450753187, - 76.0278422273782, - 76.116144018583, - 76.2046511627907, - 76.2933643771828, - 76.3822843822844, - 76.4714119019837, - 76.5607476635514, - 76.6502923976608, - 76.7400468384075, - 76.8300117233294, - 76.9201877934272, - 77.0105757931845, - 77.1011764705882, - 77.1919905771496, - 77.2830188679245, - 77.3742621015348, - 77.4657210401891, - 77.5573964497041, - 77.6492890995261, - 77.7413997627521, - 77.833729216152, - 77.9262782401902, - 78.0190476190476, - 78.1120381406436, - 78.2052505966587, - 78.2986857825568, - 78.3923444976077, - 78.4862275449102, - 78.5803357314149, - 78.6746698679472, - 78.7692307692308, - 78.864019253911, - 78.9590361445783, - 79.0542822677925, - 79.1497584541063, - 79.2454655380895, - 79.3414043583535, - 79.4375757575758, - 79.5339805825243, - 79.6306196840826, - 79.7274939172749, - 79.8246041412911, - 79.9219512195122, - 80.019536019536, - 80.1173594132029, - 80.2154222766218, - 80.3137254901961, - 80.4122699386503, - 80.5110565110565, - 80.610086100861, - 80.7093596059113, - 80.8088779284834, - 80.9086419753086, - 81.008652657602, - 81.1089108910891, - 81.2094175960347, - 81.3101736972705, - 81.4111801242236, - 81.5124378109453, - 81.6139476961395, - 81.715710723192, - 81.8177278401998, - 81.92, - 82.0225281602002, - 82.125313283208, - 82.228356336261, - 82.3316582914573, - 82.4352201257862, - 82.5390428211587, - 82.6431273644388, - 82.7474747474748, - 82.8520859671302, - 82.9569620253165, - 83.0621039290241, - 83.1675126903553, - 83.2731893265565, - 83.3791348600509, - 83.4853503184713, - 83.5918367346939, - 83.698595146871, - 83.8056265984655, - 83.9129321382842, - 84.0205128205128, - 84.1283697047497, - 84.2365038560411, - 84.3449163449163, - 84.4536082474227, - 84.5625806451613, - 84.671834625323, - 84.7813712807245, - 84.8911917098446, - 85.0012970168612, - 85.1116883116883, - 85.222366710013, - 85.3333333333333, - 85.4445893089961, - 85.556135770235, - 85.6679738562091, - 85.7801047120419, - 85.8925294888598, - 86.005249343832, - 86.1182654402102, - 86.2315789473684, - 86.3451910408432, - 86.4591029023747, - 86.5733157199472, - 86.6878306878307, - 86.8026490066225, - 86.9177718832891, - 87.0332005312085, - 87.1489361702128, - 87.2649800266312, - 87.3813333333333, - 87.497997329773, - 87.6149732620321, - 87.7322623828648, - 87.8498659517426, - 87.9677852348993, - 88.0860215053763, - 88.2045760430686, - 88.3234501347709, - 88.442645074224, - 88.5621621621622, - 88.68200270636, - 88.8021680216802, - 88.9226594301221, - 89.0434782608696, - 89.1646258503401, - 89.2861035422343, - 89.4079126875853, - 89.5300546448087, - 89.6525307797538, - 89.7753424657534, - 89.8984910836763, - 90.021978021978, - 90.1458046767538, - 90.2699724517906, - 90.3944827586207, - 90.5193370165746, - 90.6445366528354, - 90.7700831024931, - 90.8959778085992, - 91.0222222222222, - 91.1488178025035, - 91.2757660167131, - 91.4030683403068, - 91.5307262569833, - 91.6587412587413, - 91.7871148459384, - 91.9158485273492, - 92.0449438202247, - 92.1744022503516, - 92.3042253521127, - 92.4344146685472, - 92.5649717514124, - 92.6958981612447, - 92.8271954674221, - 92.958865248227, - 93.0909090909091, - 93.2233285917497, - 93.3561253561254, - 93.4893009985735, - 93.6228571428572, - 93.7567954220315, - 93.89111747851, - 94.025824964132, - 94.1609195402299, - 94.2964028776978, - 94.4322766570605, - 94.5685425685426, - 94.7052023121387, - 94.8422575976845, - 94.9797101449275, - 95.1175616835994, - 95.2558139534884, - 95.3944687045124, - 95.533527696793, - 95.6729927007299, - 95.812865497076, - 95.9531478770132, - 96.0938416422287, - 96.2349486049927, - 96.3764705882353, - 96.5184094256259, - 96.6607669616519, - 96.8035450516987, - 96.9467455621302, - 97.0903703703704, - 97.2344213649852, - 97.3789004457652, - 97.5238095238095, - 97.6691505216095, - 97.8149253731343, - 97.9611360239163, - 98.1077844311377, - 98.2548725637181, - 98.4024024024024, - 98.5503759398496, - 98.6987951807229, - 98.8476621417798, - 98.9969788519637, - 99.1467473524962, - 99.2969696969697, - 99.4476479514416, - 99.5987841945289, - 99.7503805175038, - 99.9024390243902, - 100.054961832061, - 100.207951070336, - 100.361408882083, - 100.515337423313, - 100.669738863287, - 100.824615384615, - 100.979969183359, - 101.135802469136, - 101.292117465224, - 101.448916408669, - 101.606201550388, - 101.76397515528, - 101.922239502333, - 102.080996884735, - 102.240249609984, - 102.4, - 102.560250391236, - 102.721003134796, - 102.882260596546, - 103.044025157233, - 103.206299212598, - 103.369085173502, - 103.532385466035, - 103.696202531646, - 103.860538827258, - 104.025396825397, - 104.190779014308, - 104.356687898089, - 104.52312599681, - 104.690095846645, - 104.8576, - 105.025641025641, - 105.194221508828, - 105.363344051447, - 105.533011272142, - 105.703225806452, - 105.873990306947, - 106.045307443366, - 106.217179902755, - 106.38961038961, - 106.562601626016, - 106.736156351792, - 106.910277324633, - 107.084967320261, - 107.26022913257, - 107.436065573771, - 107.612479474548, - 107.789473684211, - 107.96705107084, - 108.145214521452, - 108.323966942149, - 108.503311258278, - 108.683250414594, - 108.863787375415, - 109.044925124792, - 109.226666666667, - 109.409015025042, - 109.591973244147, - 109.77554438861, - 109.959731543624, - 110.144537815126, - 110.329966329966, - 110.516020236088, - 110.702702702703, - 110.890016920474, - 111.077966101695, - 111.266553480475, - 111.455782312925, - 111.645655877342, - 111.836177474403, - 112.02735042735, - 112.219178082192, - 112.41166380789, - 112.604810996564, - 112.798623063683, - 112.993103448276, - 113.188255613126, - 113.384083044983, - 113.580589254766, - 113.777777777778, - 113.975652173913, - 114.174216027875, - 114.373472949389, - 114.573426573427, - 114.77408056042, - 114.975438596491, - 115.177504393673, - 115.380281690141, - 115.583774250441, - 115.787985865724, - 115.992920353982, - 116.198581560284, - 116.404973357016, - 116.612099644128, - 116.819964349376, - 117.028571428571, - 117.237924865832, - 117.448028673835, - 117.658886894075, - 117.870503597122, - 118.082882882883, - 118.296028880866, - 118.509945750452, - 118.724637681159, - 118.940108892922, - 119.156363636364, - 119.373406193078, - 119.591240875912, - 119.80987202925, - 120.029304029304, - 120.249541284404, - 120.470588235294, - 120.692449355433, - 120.915129151292, - 121.138632162662, - 121.362962962963, - 121.588126159555, - 121.814126394052, - 122.040968342644, - 122.268656716418, - 122.497196261682, - 122.7265917603, - 122.956848030019, - 123.187969924812, - 123.419962335217, - 123.652830188679, - 123.886578449906, - 124.121212121212, - 124.356736242884, - 124.593155893536, - 124.830476190476, - 125.068702290076, - 125.307839388145, - 125.547892720307, - 125.78886756238, - 126.030769230769, - 126.273603082852, - 126.517374517375, - 126.762088974855, - 127.007751937985, - 127.254368932039, - 127.501945525292, - 127.750487329435, - 128, - 128.250489236791, - 128.501960784314, - 128.75442043222, - 129.007874015748, - 129.262327416174, - 129.517786561265, - 129.774257425743, - 130.031746031746, - 130.290258449304, - 130.549800796813, - 130.810379241517, - 131.072, - 131.334669338677, - 131.598393574297, - 131.863179074447, - 132.129032258065, - 132.39595959596, - 132.663967611336, - 132.933062880325, - 133.20325203252, - 133.474541751527, - 133.74693877551, - 134.020449897751, - 134.295081967213, - 134.570841889117, - 134.847736625514, - 135.125773195876, - 135.404958677686, - 135.685300207039, - 135.966804979253, - 136.24948024948, - 136.533333333333, - 136.818371607516, - 137.10460251046, - 137.392033542977, - 137.680672268908, - 137.970526315789, - 138.261603375527, - 138.553911205074, - 138.847457627119, - 139.142250530786, - 139.43829787234, - 139.735607675906, - 140.034188034188, - 140.334047109208, - 140.635193133047, - 140.937634408602, - 141.241379310345, - 141.546436285097, - 141.852813852814, - 142.160520607375, - 142.469565217391, - 142.779956427015, - 143.091703056769, - 143.404814004376, - 143.719298245614, - 144.035164835165, - 144.352422907489, - 144.671081677704, - 144.991150442478, - 145.312638580931, - 145.635555555556, - 145.95991091314, - 146.285714285714, - 146.612975391499, - 146.941704035874, - 147.27191011236, - 147.603603603604, - 147.936794582393, - 148.27149321267, - 148.607709750567, - 148.945454545455, - 149.284738041002, - 149.625570776256, - 149.967963386728, - 150.311926605505, - 150.657471264368, - 151.004608294931, - 151.353348729792, - 151.703703703704, - 152.055684454756, - 152.409302325581, - 152.764568764569, - 153.121495327103, - 153.480093676815, - 153.840375586854, - 154.202352941176, - 154.566037735849, - 154.931442080378, - 155.298578199052, - 155.667458432304, - 156.038095238095, - 156.410501193317, - 156.784688995215, - 157.16067146283, - 157.538461538462, - 157.918072289157, - 158.299516908213, - 158.682808716707, - 159.067961165049, - 159.45498783455, - 159.843902439024, - 160.234718826406, - 160.627450980392, - 161.022113022113, - 161.418719211823, - 161.817283950617, - 162.217821782178, - 162.620347394541, - 163.024875621891, - 163.431421446384, - 163.84, - 164.250626566416, - 164.663316582915, - 165.078085642317, - 165.49494949495, - 165.913924050633, - 166.335025380711, - 166.758269720102, - 167.183673469388, - 167.611253196931, - 168.041025641026, - 168.473007712082, - 168.907216494845, - 169.343669250646, - 169.782383419689, - 170.223376623377, - 170.666666666667, - 171.11227154047, - 171.560209424084, - 172.010498687664, - 172.463157894737, - 172.918205804749, - 173.375661375661, - 173.835543766578, - 174.297872340426, - 174.762666666667, - 175.229946524064, - 175.699731903485, - 176.172043010753, - 176.646900269542, - 177.124324324324, - 177.60433604336, - 178.086956521739, - 178.572207084469, - 179.060109289618, - 179.550684931507, - 180.043956043956, - 180.539944903581, - 181.038674033149, - 181.540166204986, - 182.044444444444, - 182.551532033426, - 183.061452513966, - 183.574229691877, - 184.089887640449, - 184.608450704225, - 185.129943502825, - 185.654390934844, - 186.181818181818, - 186.712250712251, - 187.245714285714, - 187.78223495702, - 188.32183908046, - 188.864553314121, - 189.410404624277, - 189.959420289855, - 190.511627906977, - 191.067055393586, - 191.625730994152, - 192.187683284457, - 192.752941176471, - 193.321533923304, - 193.89349112426, - 194.46884272997, - 195.047619047619, - 195.629850746269, - 196.215568862275, - 196.804804804805, - 197.397590361446, - 197.993957703928, - 198.593939393939, - 199.197568389058, - 199.804878048781, - 200.415902140673, - 201.030674846626, - 201.649230769231, - 202.271604938272, - 202.897832817337, - 203.527950310559, - 204.16199376947, - 204.8, - 205.442006269592, - 206.088050314465, - 206.738170347003, - 207.392405063291, - 208.050793650794, - 208.713375796178, - 209.380191693291, - 210.051282051282, - 210.726688102894, - 211.406451612903, - 212.090614886731, - 212.779220779221, - 213.472312703583, - 214.169934640523, - 214.872131147541, - 215.578947368421, - 216.290429042904, - 217.006622516556, - 217.727574750831, - 218.453333333333, - 219.183946488294, - 219.919463087248, - 220.659932659933, - 221.405405405405, - 222.15593220339, - 222.91156462585, - 223.672354948805, - 224.438356164384, - 225.209621993127, - 225.986206896552, - 226.768166089965, - 227.555555555556, - 228.348432055749, - 229.146853146853, - 229.950877192982, - 230.760563380282, - 231.575971731449, - 232.397163120567, - 233.224199288256, - 234.057142857143, - 234.89605734767, - 235.741007194245, - 236.592057761733, - 237.449275362319, - 238.312727272727, - 239.182481751825, - 240.058608058608, - 240.941176470588, - 241.830258302583, - 242.725925925926, - 243.628252788104, - 244.537313432836, - 245.453183520599, - 246.375939849624, - 247.305660377358, - 248.242424242424, - 249.186311787072, - 250.137404580153, - 251.095785440613, - 252.061538461538, - 253.034749034749, - 254.015503875969, - 255.003891050584, - 256, - 257.003921568627, - 258.015748031496, - 259.03557312253, - 260.063492063492, - 261.099601593625, - 262.144, - 263.196787148594, - 264.258064516129, - 265.327935222672, - 266.406504065041, - 267.49387755102, - 268.590163934426, - 269.695473251029, - 270.809917355372, - 271.933609958506, - 273.066666666667, - 274.20920502092, - 275.361344537815, - 276.523206751055, - 277.694915254237, - 278.876595744681, - 280.068376068376, - 281.270386266094, - 282.48275862069, - 283.705627705628, - 284.939130434783, - 286.183406113537, - 287.438596491228, - 288.704845814978, - 289.982300884956, - 291.271111111111, - 292.571428571429, - 293.883408071749, - 295.207207207207, - 296.542986425339, - 297.890909090909, - 299.251141552511, - 300.623853211009, - 302.009216589862, - 303.407407407407, - 304.818604651163, - 306.242990654206, - 307.680751173709, - 309.132075471698, - 310.597156398104, - 312.07619047619, - 313.569377990431, - 315.076923076923, - 316.599033816425, - 318.135922330097, - 319.687804878049, - 321.254901960784, - 322.837438423645, - 324.435643564356, - 326.049751243781, - 327.68, - 329.326633165829, - 330.989898989899, - 332.670050761421, - 334.367346938775, - 336.082051282051, - 337.814432989691, - 339.564766839378, - 341.333333333333, - 343.120418848168, - 344.926315789474, - 346.751322751323, - 348.595744680851, - 350.459893048128, - 352.344086021505, - 354.248648648649, - 356.173913043478, - 358.120218579235, - 360.087912087912, - 362.077348066298, - 364.088888888889, - 366.122905027933, - 368.179775280899, - 370.25988700565, - 372.363636363636, - 374.491428571429, - 376.64367816092, - 378.820809248555, - 381.023255813953, - 383.251461988304, - 385.505882352941, - 387.786982248521, - 390.095238095238, - 392.431137724551, - 394.795180722892, - 397.187878787879, - 399.609756097561, - 402.061349693252, - 404.543209876543, - 407.055900621118, - 409.6, - 412.176100628931, - 414.784810126582, - 417.426751592357, - 420.102564102564, - 422.812903225807, - 425.558441558442, - 428.339869281046, - 431.157894736842, - 434.013245033113, - 436.906666666667, - 439.838926174497, - 442.810810810811, - 445.823129251701, - 448.876712328767, - 451.972413793103, - 455.111111111111, - 458.293706293706, - 461.521126760563, - 464.794326241135, - 468.114285714286, - 471.482014388489, - 474.898550724638, - 478.36496350365, - 481.882352941176, - 485.451851851852, - 489.074626865672, - 492.751879699248, - 496.484848484849, - 500.274809160305, - 504.123076923077, - 508.031007751938, - 512, - 516.031496062992, - 520.126984126984, - 524.288, - 528.516129032258, - 532.813008130081, - 537.180327868852, - 541.619834710744, - 546.133333333333, - 550.72268907563, - 555.389830508475, - 560.136752136752, - 564.965517241379, - 569.878260869565, - 574.877192982456, - 579.964601769912, - 585.142857142857, - 590.414414414414, - 595.781818181818, - 601.247706422018, - 606.814814814815, - 612.485981308411, - 618.264150943396, - 624.152380952381, - 630.153846153846, - 636.271844660194, - 642.509803921569, - 648.871287128713, - 655.36, - 661.979797979798, - 668.734693877551, - 675.628865979381, - 682.666666666667, - 689.852631578947, - 697.191489361702, - 704.688172043011, - 712.347826086956, - 720.175824175824, - 728.177777777778, - 736.359550561798, - 744.727272727273, - 753.287356321839, - 762.046511627907, - 771.011764705882, - 780.190476190476, - 789.590361445783, - 799.219512195122, - 809.086419753086, - 819.2, - 829.569620253165, - 840.205128205128, - 851.116883116883, - 862.315789473684, - 873.813333333333, - 885.621621621622, - 897.753424657534, - 910.222222222222, - 923.042253521127, - 936.228571428571, - 949.797101449275, - 963.764705882353, - 978.149253731343, - 992.969696969697, - 1008.24615384615, - 1024, - 1040.25396825397, - 1057.03225806452, - 1074.36065573771, - 1092.26666666667, - 1110.77966101695, - 1129.93103448276, - 1149.75438596491, - 1170.28571428571, - 1191.56363636364, - 1213.62962962963, - 1236.52830188679, - 1260.30769230769, - 1285.01960784314, - 1310.72, - 1337.4693877551, - 1365.33333333333, - 1394.3829787234, - 1424.69565217391, - 1456.35555555556, - 1489.45454545455, - 1524.09302325581, - 1560.38095238095, - 1598.43902439024, - 1638.4, - 1680.41025641026, - 1724.63157894737, - 1771.24324324324, - 1820.44444444444, - 1872.45714285714, - 1927.52941176471, - 1985.93939393939, - 2048, - 2114.06451612903, - 2184.53333333333, - 2259.86206896552, - 2340.57142857143, - 2427.25925925926, - 2520.61538461538, - 2621.44, - 2730.66666666667, - 2849.39130434783, - 2978.90909090909, - 3120.7619047619, - 3276.8, - 3449.26315789474, - 3640.88888888889, - 3855.05882352941, - 4096, - 4369.06666666667, - 4681.14285714286, - 5041.23076923077, - 5461.33333333333, - 5957.81818181818, - 6553.6, - 7281.77777777778, - 8192, - 9362.28571428571, - 10922.6666666667, - 13107.2, - 16384, - 21845.3333333333, - 32768, - 65536 }; +const fixed8_24 freqTable[2048] = { + float_to_fp8_24(32 / FREQUENCY_RATE), + float_to_fp8_24(32.0156326331216 / FREQUENCY_RATE), + float_to_fp8_24(32.0312805474096 / FREQUENCY_RATE), + float_to_fp8_24(32.0469437652812 / FREQUENCY_RATE), + float_to_fp8_24(32.0626223091976 / FREQUENCY_RATE), + float_to_fp8_24(32.0783162016642 / FREQUENCY_RATE), + float_to_fp8_24(32.0940254652302 / FREQUENCY_RATE), + float_to_fp8_24(32.109750122489 / FREQUENCY_RATE), + float_to_fp8_24(32.1254901960784 / FREQUENCY_RATE), + float_to_fp8_24(32.1412457086807 / FREQUENCY_RATE), + float_to_fp8_24(32.1570166830226 / FREQUENCY_RATE), + float_to_fp8_24(32.1728031418753 / FREQUENCY_RATE), + float_to_fp8_24(32.188605108055 / FREQUENCY_RATE), + float_to_fp8_24(32.2044226044226 / FREQUENCY_RATE), + float_to_fp8_24(32.220255653884 / FREQUENCY_RATE), + float_to_fp8_24(32.2361042793901 / FREQUENCY_RATE), + float_to_fp8_24(32.251968503937 / FREQUENCY_RATE), + float_to_fp8_24(32.2678483505662 / FREQUENCY_RATE), + float_to_fp8_24(32.2837438423645 / FREQUENCY_RATE), + float_to_fp8_24(32.2996550024643 / FREQUENCY_RATE), + float_to_fp8_24(32.3155818540434 / FREQUENCY_RATE), + float_to_fp8_24(32.3315244203256 / FREQUENCY_RATE), + float_to_fp8_24(32.3474827245805 / FREQUENCY_RATE), + float_to_fp8_24(32.3634567901235 / FREQUENCY_RATE), + float_to_fp8_24(32.3794466403162 / FREQUENCY_RATE), + float_to_fp8_24(32.3954522985665 / FREQUENCY_RATE), + float_to_fp8_24(32.4114737883284 / FREQUENCY_RATE), + float_to_fp8_24(32.4275111331024 / FREQUENCY_RATE), + float_to_fp8_24(32.4435643564356 / FREQUENCY_RATE), + float_to_fp8_24(32.4596334819217 / FREQUENCY_RATE), + float_to_fp8_24(32.4757185332012 / FREQUENCY_RATE), + float_to_fp8_24(32.4918195339613 / FREQUENCY_RATE), + float_to_fp8_24(32.5079365079365 / FREQUENCY_RATE), + float_to_fp8_24(32.5240694789082 / FREQUENCY_RATE), + float_to_fp8_24(32.5402184707051 / FREQUENCY_RATE), + float_to_fp8_24(32.5563835072032 / FREQUENCY_RATE), + float_to_fp8_24(32.572564612326 / FREQUENCY_RATE), + float_to_fp8_24(32.5887618100448 / FREQUENCY_RATE), + float_to_fp8_24(32.6049751243781 / FREQUENCY_RATE), + float_to_fp8_24(32.6212045793927 / FREQUENCY_RATE), + float_to_fp8_24(32.6374501992032 / FREQUENCY_RATE), + float_to_fp8_24(32.6537120079721 / FREQUENCY_RATE), + float_to_fp8_24(32.6699900299103 / FREQUENCY_RATE), + float_to_fp8_24(32.6862842892768 / FREQUENCY_RATE), + float_to_fp8_24(32.7025948103792 / FREQUENCY_RATE), + float_to_fp8_24(32.7189216175736 / FREQUENCY_RATE), + float_to_fp8_24(32.7352647352647 / FREQUENCY_RATE), + float_to_fp8_24(32.751624187906 / FREQUENCY_RATE), + float_to_fp8_24(32.768 / FREQUENCY_RATE), + float_to_fp8_24(32.784392196098 / FREQUENCY_RATE), + float_to_fp8_24(32.8008008008008 / FREQUENCY_RATE), + float_to_fp8_24(32.8172258387581 / FREQUENCY_RATE), + float_to_fp8_24(32.8336673346693 / FREQUENCY_RATE), + float_to_fp8_24(32.8501253132832 / FREQUENCY_RATE), + float_to_fp8_24(32.8665997993982 / FREQUENCY_RATE), + float_to_fp8_24(32.8830908178625 / FREQUENCY_RATE), + float_to_fp8_24(32.8995983935743 / FREQUENCY_RATE), + float_to_fp8_24(32.9161225514817 / FREQUENCY_RATE), + float_to_fp8_24(32.9326633165829 / FREQUENCY_RATE), + float_to_fp8_24(32.9492207139266 / FREQUENCY_RATE), + float_to_fp8_24(32.9657947686117 / FREQUENCY_RATE), + float_to_fp8_24(32.9823855057876 / FREQUENCY_RATE), + float_to_fp8_24(32.9989929506546 / FREQUENCY_RATE), + float_to_fp8_24(33.0156171284635 / FREQUENCY_RATE), + float_to_fp8_24(33.0322580645161 / FREQUENCY_RATE), + float_to_fp8_24(33.0489157841654 / FREQUENCY_RATE), + float_to_fp8_24(33.0655903128153 / FREQUENCY_RATE), + float_to_fp8_24(33.0822816759213 / FREQUENCY_RATE), + float_to_fp8_24(33.0989898989899 / FREQUENCY_RATE), + float_to_fp8_24(33.1157150075796 / FREQUENCY_RATE), + float_to_fp8_24(33.1324570273003 / FREQUENCY_RATE), + float_to_fp8_24(33.1492159838139 / FREQUENCY_RATE), + float_to_fp8_24(33.165991902834 / FREQUENCY_RATE), + float_to_fp8_24(33.1827848101266 / FREQUENCY_RATE), + float_to_fp8_24(33.1995947315096 / FREQUENCY_RATE), + float_to_fp8_24(33.2164216928535 / FREQUENCY_RATE), + float_to_fp8_24(33.2332657200811 / FREQUENCY_RATE), + float_to_fp8_24(33.2501268391679 / FREQUENCY_RATE), + float_to_fp8_24(33.2670050761421 / FREQUENCY_RATE), + float_to_fp8_24(33.2839004570848 / FREQUENCY_RATE), + float_to_fp8_24(33.3008130081301 / FREQUENCY_RATE), + float_to_fp8_24(33.3177427554652 / FREQUENCY_RATE), + float_to_fp8_24(33.3346897253306 / FREQUENCY_RATE), + float_to_fp8_24(33.3516539440204 / FREQUENCY_RATE), + float_to_fp8_24(33.3686354378819 / FREQUENCY_RATE), + float_to_fp8_24(33.3856342333164 / FREQUENCY_RATE), + float_to_fp8_24(33.4026503567788 / FREQUENCY_RATE), + float_to_fp8_24(33.4196838347782 / FREQUENCY_RATE), + float_to_fp8_24(33.4367346938776 / FREQUENCY_RATE), + float_to_fp8_24(33.4538029606942 / FREQUENCY_RATE), + float_to_fp8_24(33.4708886618999 / FREQUENCY_RATE), + float_to_fp8_24(33.4879918242207 / FREQUENCY_RATE), + float_to_fp8_24(33.5051124744376 / FREQUENCY_RATE), + float_to_fp8_24(33.5222506393862 / FREQUENCY_RATE), + float_to_fp8_24(33.539406345957 / FREQUENCY_RATE), + float_to_fp8_24(33.5565796210957 / FREQUENCY_RATE), + float_to_fp8_24(33.5737704918033 / FREQUENCY_RATE), + float_to_fp8_24(33.5909789851358 / FREQUENCY_RATE), + float_to_fp8_24(33.6082051282051 / FREQUENCY_RATE), + float_to_fp8_24(33.6254489481786 / FREQUENCY_RATE), + float_to_fp8_24(33.6427104722793 / FREQUENCY_RATE), + float_to_fp8_24(33.6599897277863 / FREQUENCY_RATE), + float_to_fp8_24(33.6772867420349 / FREQUENCY_RATE), + float_to_fp8_24(33.6946015424164 / FREQUENCY_RATE), + float_to_fp8_24(33.7119341563786 / FREQUENCY_RATE), + float_to_fp8_24(33.7292846114256 / FREQUENCY_RATE), + float_to_fp8_24(33.7466529351184 / FREQUENCY_RATE), + float_to_fp8_24(33.7640391550747 / FREQUENCY_RATE), + float_to_fp8_24(33.7814432989691 / FREQUENCY_RATE), + float_to_fp8_24(33.7988653945333 / FREQUENCY_RATE), + float_to_fp8_24(33.8163054695562 / FREQUENCY_RATE), + float_to_fp8_24(33.8337635518844 / FREQUENCY_RATE), + float_to_fp8_24(33.8512396694215 / FREQUENCY_RATE), + float_to_fp8_24(33.8687338501292 / FREQUENCY_RATE), + float_to_fp8_24(33.8862461220269 / FREQUENCY_RATE), + float_to_fp8_24(33.9037765131919 / FREQUENCY_RATE), + float_to_fp8_24(33.9213250517598 / FREQUENCY_RATE), + float_to_fp8_24(33.9388917659244 / FREQUENCY_RATE), + float_to_fp8_24(33.9564766839378 / FREQUENCY_RATE), + float_to_fp8_24(33.9740798341109 / FREQUENCY_RATE), + float_to_fp8_24(33.9917012448133 / FREQUENCY_RATE), + float_to_fp8_24(34.0093409444733 / FREQUENCY_RATE), + float_to_fp8_24(34.0269989615784 / FREQUENCY_RATE), + float_to_fp8_24(34.0446753246753 / FREQUENCY_RATE), + float_to_fp8_24(34.0623700623701 / FREQUENCY_RATE), + float_to_fp8_24(34.0800832033281 / FREQUENCY_RATE), + float_to_fp8_24(34.0978147762747 / FREQUENCY_RATE), + float_to_fp8_24(34.1155648099948 / FREQUENCY_RATE), + float_to_fp8_24(34.1333333333333 / FREQUENCY_RATE), + float_to_fp8_24(34.1511203751954 / FREQUENCY_RATE), + float_to_fp8_24(34.1689259645464 / FREQUENCY_RATE), + float_to_fp8_24(34.1867501304121 / FREQUENCY_RATE), + float_to_fp8_24(34.2045929018789 / FREQUENCY_RATE), + float_to_fp8_24(34.222454308094 / FREQUENCY_RATE), + float_to_fp8_24(34.2403343782654 / FREQUENCY_RATE), + float_to_fp8_24(34.2582331416623 / FREQUENCY_RATE), + float_to_fp8_24(34.2761506276151 / FREQUENCY_RATE), + float_to_fp8_24(34.2940868655154 / FREQUENCY_RATE), + float_to_fp8_24(34.3120418848168 / FREQUENCY_RATE), + float_to_fp8_24(34.330015715034 / FREQUENCY_RATE), + float_to_fp8_24(34.3480083857442 / FREQUENCY_RATE), + float_to_fp8_24(34.3660199265863 / FREQUENCY_RATE), + float_to_fp8_24(34.3840503672613 / FREQUENCY_RATE), + float_to_fp8_24(34.4020997375328 / FREQUENCY_RATE), + float_to_fp8_24(34.4201680672269 / FREQUENCY_RATE), + float_to_fp8_24(34.4382553862323 / FREQUENCY_RATE), + float_to_fp8_24(34.4563617245005 / FREQUENCY_RATE), + float_to_fp8_24(34.4744871120463 / FREQUENCY_RATE), + float_to_fp8_24(34.4926315789474 / FREQUENCY_RATE), + float_to_fp8_24(34.5107951553449 / FREQUENCY_RATE), + float_to_fp8_24(34.5289778714436 / FREQUENCY_RATE), + float_to_fp8_24(34.5471797575119 / FREQUENCY_RATE), + float_to_fp8_24(34.5654008438819 / FREQUENCY_RATE), + float_to_fp8_24(34.5836411609499 / FREQUENCY_RATE), + float_to_fp8_24(34.6019007391763 / FREQUENCY_RATE), + float_to_fp8_24(34.6201796090861 / FREQUENCY_RATE), + float_to_fp8_24(34.6384778012685 / FREQUENCY_RATE), + float_to_fp8_24(34.6567953463776 / FREQUENCY_RATE), + float_to_fp8_24(34.6751322751323 / FREQUENCY_RATE), + float_to_fp8_24(34.6934886183166 / FREQUENCY_RATE), + float_to_fp8_24(34.7118644067797 / FREQUENCY_RATE), + float_to_fp8_24(34.7302596714361 / FREQUENCY_RATE), + float_to_fp8_24(34.7486744432662 / FREQUENCY_RATE), + float_to_fp8_24(34.7671087533156 / FREQUENCY_RATE), + float_to_fp8_24(34.7855626326964 / FREQUENCY_RATE), + float_to_fp8_24(34.8040361125863 / FREQUENCY_RATE), + float_to_fp8_24(34.8225292242295 / FREQUENCY_RATE), + float_to_fp8_24(34.8410419989367 / FREQUENCY_RATE), + float_to_fp8_24(34.8595744680851 / FREQUENCY_RATE), + float_to_fp8_24(34.8781266631187 / FREQUENCY_RATE), + float_to_fp8_24(34.8966986155485 / FREQUENCY_RATE), + float_to_fp8_24(34.9152903569526 / FREQUENCY_RATE), + float_to_fp8_24(34.9339019189765 / FREQUENCY_RATE), + float_to_fp8_24(34.9525333333333 / FREQUENCY_RATE), + float_to_fp8_24(34.9711846318036 / FREQUENCY_RATE), + float_to_fp8_24(34.989855846236 / FREQUENCY_RATE), + float_to_fp8_24(35.008547008547 / FREQUENCY_RATE), + float_to_fp8_24(35.0272581507215 / FREQUENCY_RATE), + float_to_fp8_24(35.0459893048128 / FREQUENCY_RATE), + float_to_fp8_24(35.0647405029427 / FREQUENCY_RATE), + float_to_fp8_24(35.0835117773019 / FREQUENCY_RATE), + float_to_fp8_24(35.10230316015 / FREQUENCY_RATE), + float_to_fp8_24(35.1211146838156 / FREQUENCY_RATE), + float_to_fp8_24(35.1399463806971 / FREQUENCY_RATE), + float_to_fp8_24(35.1587982832618 / FREQUENCY_RATE), + float_to_fp8_24(35.1776704240472 / FREQUENCY_RATE), + float_to_fp8_24(35.1965628356606 / FREQUENCY_RATE), + float_to_fp8_24(35.2154755507792 / FREQUENCY_RATE), + float_to_fp8_24(35.2344086021505 / FREQUENCY_RATE), + float_to_fp8_24(35.2533620225928 / FREQUENCY_RATE), + float_to_fp8_24(35.2723358449946 / FREQUENCY_RATE), + float_to_fp8_24(35.2913301023156 / FREQUENCY_RATE), + float_to_fp8_24(35.3103448275862 / FREQUENCY_RATE), + float_to_fp8_24(35.3293800539084 / FREQUENCY_RATE), + float_to_fp8_24(35.3484358144552 / FREQUENCY_RATE), + float_to_fp8_24(35.3675121424717 / FREQUENCY_RATE), + float_to_fp8_24(35.3866090712743 / FREQUENCY_RATE), + float_to_fp8_24(35.4057266342518 / FREQUENCY_RATE), + float_to_fp8_24(35.4248648648649 / FREQUENCY_RATE), + float_to_fp8_24(35.4440237966468 / FREQUENCY_RATE), + float_to_fp8_24(35.4632034632035 / FREQUENCY_RATE), + float_to_fp8_24(35.4824038982133 / FREQUENCY_RATE), + float_to_fp8_24(35.501625135428 / FREQUENCY_RATE), + float_to_fp8_24(35.5208672086721 / FREQUENCY_RATE), + float_to_fp8_24(35.5401301518438 / FREQUENCY_RATE), + float_to_fp8_24(35.5594139989148 / FREQUENCY_RATE), + float_to_fp8_24(35.5787187839305 / FREQUENCY_RATE), + float_to_fp8_24(35.5980445410103 / FREQUENCY_RATE), + float_to_fp8_24(35.6173913043478 / FREQUENCY_RATE), + float_to_fp8_24(35.636759108211 / FREQUENCY_RATE), + float_to_fp8_24(35.6561479869423 / FREQUENCY_RATE), + float_to_fp8_24(35.6755579749592 / FREQUENCY_RATE), + float_to_fp8_24(35.6949891067538 / FREQUENCY_RATE), + float_to_fp8_24(35.7144414168937 / FREQUENCY_RATE), + float_to_fp8_24(35.7339149400218 / FREQUENCY_RATE), + float_to_fp8_24(35.7534097108565 / FREQUENCY_RATE), + float_to_fp8_24(35.7729257641921 / FREQUENCY_RATE), + float_to_fp8_24(35.792463134899 / FREQUENCY_RATE), + float_to_fp8_24(35.8120218579235 / FREQUENCY_RATE), + float_to_fp8_24(35.8316019682887 / FREQUENCY_RATE), + float_to_fp8_24(35.8512035010941 / FREQUENCY_RATE), + float_to_fp8_24(35.8708264915161 / FREQUENCY_RATE), + float_to_fp8_24(35.8904709748083 / FREQUENCY_RATE), + float_to_fp8_24(35.9101369863014 / FREQUENCY_RATE), + float_to_fp8_24(35.9298245614035 / FREQUENCY_RATE), + float_to_fp8_24(35.9495337356007 / FREQUENCY_RATE), + float_to_fp8_24(35.9692645444566 / FREQUENCY_RATE), + float_to_fp8_24(35.9890170236134 / FREQUENCY_RATE), + float_to_fp8_24(36.0087912087912 / FREQUENCY_RATE), + float_to_fp8_24(36.0285871357889 / FREQUENCY_RATE), + float_to_fp8_24(36.048404840484 / FREQUENCY_RATE), + float_to_fp8_24(36.0682443588332 / FREQUENCY_RATE), + float_to_fp8_24(36.0881057268722 / FREQUENCY_RATE), + float_to_fp8_24(36.1079889807162 / FREQUENCY_RATE), + float_to_fp8_24(36.1278941565601 / FREQUENCY_RATE), + float_to_fp8_24(36.1478212906784 / FREQUENCY_RATE), + float_to_fp8_24(36.167770419426 / FREQUENCY_RATE), + float_to_fp8_24(36.187741579238 / FREQUENCY_RATE), + float_to_fp8_24(36.2077348066298 / FREQUENCY_RATE), + float_to_fp8_24(36.2277501381979 / FREQUENCY_RATE), + float_to_fp8_24(36.2477876106195 / FREQUENCY_RATE), + float_to_fp8_24(36.267847260653 / FREQUENCY_RATE), + float_to_fp8_24(36.2879291251384 / FREQUENCY_RATE), + float_to_fp8_24(36.3080332409972 / FREQUENCY_RATE), + float_to_fp8_24(36.3281596452328 / FREQUENCY_RATE), + float_to_fp8_24(36.3483083749307 / FREQUENCY_RATE), + float_to_fp8_24(36.3684794672586 / FREQUENCY_RATE), + float_to_fp8_24(36.388672959467 / FREQUENCY_RATE), + float_to_fp8_24(36.4088888888889 / FREQUENCY_RATE), + float_to_fp8_24(36.4291272929405 / FREQUENCY_RATE), + float_to_fp8_24(36.4493882091212 / FREQUENCY_RATE), + float_to_fp8_24(36.4696716750139 / FREQUENCY_RATE), + float_to_fp8_24(36.4899777282851 / FREQUENCY_RATE), + float_to_fp8_24(36.5103064066852 / FREQUENCY_RATE), + float_to_fp8_24(36.5306577480491 / FREQUENCY_RATE), + float_to_fp8_24(36.5510317902956 / FREQUENCY_RATE), + float_to_fp8_24(36.5714285714286 / FREQUENCY_RATE), + float_to_fp8_24(36.5918481295366 / FREQUENCY_RATE), + float_to_fp8_24(36.6122905027933 / FREQUENCY_RATE), + float_to_fp8_24(36.6327557294578 / FREQUENCY_RATE), + float_to_fp8_24(36.6532438478747 / FREQUENCY_RATE), + float_to_fp8_24(36.6737548964745 / FREQUENCY_RATE), + float_to_fp8_24(36.6942889137738 / FREQUENCY_RATE), + float_to_fp8_24(36.7148459383753 / FREQUENCY_RATE), + float_to_fp8_24(36.7354260089686 / FREQUENCY_RATE), + float_to_fp8_24(36.7560291643298 / FREQUENCY_RATE), + float_to_fp8_24(36.7766554433221 / FREQUENCY_RATE), + float_to_fp8_24(36.7973048848961 / FREQUENCY_RATE), + float_to_fp8_24(36.8179775280899 / FREQUENCY_RATE), + float_to_fp8_24(36.8386734120292 / FREQUENCY_RATE), + float_to_fp8_24(36.859392575928 / FREQUENCY_RATE), + float_to_fp8_24(36.8801350590884 / FREQUENCY_RATE), + float_to_fp8_24(36.9009009009009 / FREQUENCY_RATE), + float_to_fp8_24(36.9216901408451 / FREQUENCY_RATE), + float_to_fp8_24(36.9425028184893 / FREQUENCY_RATE), + float_to_fp8_24(36.9633389734913 / FREQUENCY_RATE), + float_to_fp8_24(36.9841986455982 / FREQUENCY_RATE), + float_to_fp8_24(37.0050818746471 / FREQUENCY_RATE), + float_to_fp8_24(37.025988700565 / FREQUENCY_RATE), + float_to_fp8_24(37.0469191633691 / FREQUENCY_RATE), + float_to_fp8_24(37.0678733031674 / FREQUENCY_RATE), + float_to_fp8_24(37.0888511601585 / FREQUENCY_RATE), + float_to_fp8_24(37.1098527746319 / FREQUENCY_RATE), + float_to_fp8_24(37.1308781869688 / FREQUENCY_RATE), + float_to_fp8_24(37.1519274376417 / FREQUENCY_RATE), + float_to_fp8_24(37.173000567215 / FREQUENCY_RATE), + float_to_fp8_24(37.1940976163451 / FREQUENCY_RATE), + float_to_fp8_24(37.2152186257808 / FREQUENCY_RATE), + float_to_fp8_24(37.2363636363636 / FREQUENCY_RATE), + float_to_fp8_24(37.2575326890279 / FREQUENCY_RATE), + float_to_fp8_24(37.2787258248009 / FREQUENCY_RATE), + float_to_fp8_24(37.2999430848036 / FREQUENCY_RATE), + float_to_fp8_24(37.3211845102506 / FREQUENCY_RATE), + float_to_fp8_24(37.3424501424501 / FREQUENCY_RATE), + float_to_fp8_24(37.363740022805 / FREQUENCY_RATE), + float_to_fp8_24(37.3850541928123 / FREQUENCY_RATE), + float_to_fp8_24(37.4063926940639 / FREQUENCY_RATE), + float_to_fp8_24(37.4277555682467 / FREQUENCY_RATE), + float_to_fp8_24(37.4491428571429 / FREQUENCY_RATE), + float_to_fp8_24(37.4705546026301 / FREQUENCY_RATE), + float_to_fp8_24(37.4919908466819 / FREQUENCY_RATE), + float_to_fp8_24(37.5134516313681 / FREQUENCY_RATE), + float_to_fp8_24(37.5349369988545 / FREQUENCY_RATE), + float_to_fp8_24(37.556446991404 / FREQUENCY_RATE), + float_to_fp8_24(37.5779816513761 / FREQUENCY_RATE), + float_to_fp8_24(37.5995410212278 / FREQUENCY_RATE), + float_to_fp8_24(37.6211251435132 / FREQUENCY_RATE), + float_to_fp8_24(37.6427340608845 / FREQUENCY_RATE), + float_to_fp8_24(37.664367816092 / FREQUENCY_RATE), + float_to_fp8_24(37.6860264519839 / FREQUENCY_RATE), + float_to_fp8_24(37.7077100115075 / FREQUENCY_RATE), + float_to_fp8_24(37.7294185377087 / FREQUENCY_RATE), + float_to_fp8_24(37.7511520737327 / FREQUENCY_RATE), + float_to_fp8_24(37.7729106628242 / FREQUENCY_RATE), + float_to_fp8_24(37.7946943483276 / FREQUENCY_RATE), + float_to_fp8_24(37.8165031736872 / FREQUENCY_RATE), + float_to_fp8_24(37.838337182448 / FREQUENCY_RATE), + float_to_fp8_24(37.8601964182553 / FREQUENCY_RATE), + float_to_fp8_24(37.8820809248555 / FREQUENCY_RATE), + float_to_fp8_24(37.903990746096 / FREQUENCY_RATE), + float_to_fp8_24(37.9259259259259 / FREQUENCY_RATE), + float_to_fp8_24(37.9478865083961 / FREQUENCY_RATE), + float_to_fp8_24(37.9698725376593 / FREQUENCY_RATE), + float_to_fp8_24(37.991884057971 / FREQUENCY_RATE), + float_to_fp8_24(38.0139211136891 / FREQUENCY_RATE), + float_to_fp8_24(38.0359837492745 / FREQUENCY_RATE), + float_to_fp8_24(38.0580720092915 / FREQUENCY_RATE), + float_to_fp8_24(38.0801859384079 / FREQUENCY_RATE), + float_to_fp8_24(38.1023255813953 / FREQUENCY_RATE), + float_to_fp8_24(38.1244909831297 / FREQUENCY_RATE), + float_to_fp8_24(38.1466821885914 / FREQUENCY_RATE), + float_to_fp8_24(38.1688992428655 / FREQUENCY_RATE), + float_to_fp8_24(38.1911421911422 / FREQUENCY_RATE), + float_to_fp8_24(38.2134110787172 / FREQUENCY_RATE), + float_to_fp8_24(38.2357059509918 / FREQUENCY_RATE), + float_to_fp8_24(38.2580268534734 / FREQUENCY_RATE), + float_to_fp8_24(38.2803738317757 / FREQUENCY_RATE), + float_to_fp8_24(38.3027469316189 / FREQUENCY_RATE), + float_to_fp8_24(38.3251461988304 / FREQUENCY_RATE), + float_to_fp8_24(38.3475716793446 / FREQUENCY_RATE), + float_to_fp8_24(38.3700234192037 / FREQUENCY_RATE), + float_to_fp8_24(38.3925014645577 / FREQUENCY_RATE), + float_to_fp8_24(38.4150058616647 / FREQUENCY_RATE), + float_to_fp8_24(38.4375366568915 / FREQUENCY_RATE), + float_to_fp8_24(38.4600938967136 / FREQUENCY_RATE), + float_to_fp8_24(38.4826776277158 / FREQUENCY_RATE), + float_to_fp8_24(38.5052878965922 / FREQUENCY_RATE), + float_to_fp8_24(38.527924750147 / FREQUENCY_RATE), + float_to_fp8_24(38.5505882352941 / FREQUENCY_RATE), + float_to_fp8_24(38.5732783990583 / FREQUENCY_RATE), + float_to_fp8_24(38.5959952885748 / FREQUENCY_RATE), + float_to_fp8_24(38.6187389510902 / FREQUENCY_RATE), + float_to_fp8_24(38.6415094339623 / FREQUENCY_RATE), + float_to_fp8_24(38.6643067846608 / FREQUENCY_RATE), + float_to_fp8_24(38.6871310507674 / FREQUENCY_RATE), + float_to_fp8_24(38.7099822799764 / FREQUENCY_RATE), + float_to_fp8_24(38.7328605200946 / FREQUENCY_RATE), + float_to_fp8_24(38.755765819042 / FREQUENCY_RATE), + float_to_fp8_24(38.7786982248521 / FREQUENCY_RATE), + float_to_fp8_24(38.801657785672 / FREQUENCY_RATE), + float_to_fp8_24(38.824644549763 / FREQUENCY_RATE), + float_to_fp8_24(38.8476585655009 / FREQUENCY_RATE), + float_to_fp8_24(38.870699881376 / FREQUENCY_RATE), + float_to_fp8_24(38.8937685459941 / FREQUENCY_RATE), + float_to_fp8_24(38.916864608076 / FREQUENCY_RATE), + float_to_fp8_24(38.9399881164587 / FREQUENCY_RATE), + float_to_fp8_24(38.9631391200951 / FREQUENCY_RATE), + float_to_fp8_24(38.9863176680547 / FREQUENCY_RATE), + float_to_fp8_24(39.0095238095238 / FREQUENCY_RATE), + float_to_fp8_24(39.0327575938058 / FREQUENCY_RATE), + float_to_fp8_24(39.0560190703218 / FREQUENCY_RATE), + float_to_fp8_24(39.0793082886106 / FREQUENCY_RATE), + float_to_fp8_24(39.1026252983294 / FREQUENCY_RATE), + float_to_fp8_24(39.1259701492537 / FREQUENCY_RATE), + float_to_fp8_24(39.1493428912784 / FREQUENCY_RATE), + float_to_fp8_24(39.1727435744172 / FREQUENCY_RATE), + float_to_fp8_24(39.1961722488038 / FREQUENCY_RATE), + float_to_fp8_24(39.2196289646918 / FREQUENCY_RATE), + float_to_fp8_24(39.2431137724551 / FREQUENCY_RATE), + float_to_fp8_24(39.2666267225884 / FREQUENCY_RATE), + float_to_fp8_24(39.2901678657074 / FREQUENCY_RATE), + float_to_fp8_24(39.3137372525495 / FREQUENCY_RATE), + float_to_fp8_24(39.3373349339736 / FREQUENCY_RATE), + float_to_fp8_24(39.360960960961 / FREQUENCY_RATE), + float_to_fp8_24(39.3846153846154 / FREQUENCY_RATE), + float_to_fp8_24(39.4082982561636 / FREQUENCY_RATE), + float_to_fp8_24(39.4320096269555 / FREQUENCY_RATE), + float_to_fp8_24(39.4557495484648 / FREQUENCY_RATE), + float_to_fp8_24(39.4795180722892 / FREQUENCY_RATE), + float_to_fp8_24(39.5033152501507 / FREQUENCY_RATE), + float_to_fp8_24(39.5271411338963 / FREQUENCY_RATE), + float_to_fp8_24(39.5509957754979 / FREQUENCY_RATE), + float_to_fp8_24(39.5748792270531 / FREQUENCY_RATE), + float_to_fp8_24(39.5987915407855 / FREQUENCY_RATE), + float_to_fp8_24(39.6227327690447 / FREQUENCY_RATE), + float_to_fp8_24(39.6467029643073 / FREQUENCY_RATE), + float_to_fp8_24(39.6707021791768 / FREQUENCY_RATE), + float_to_fp8_24(39.694730466384 / FREQUENCY_RATE), + float_to_fp8_24(39.7187878787879 / FREQUENCY_RATE), + float_to_fp8_24(39.7428744693754 / FREQUENCY_RATE), + float_to_fp8_24(39.7669902912621 / FREQUENCY_RATE), + float_to_fp8_24(39.7911353976928 / FREQUENCY_RATE), + float_to_fp8_24(39.8153098420413 / FREQUENCY_RATE), + float_to_fp8_24(39.8395136778115 / FREQUENCY_RATE), + float_to_fp8_24(39.8637469586375 / FREQUENCY_RATE), + float_to_fp8_24(39.8880097382836 / FREQUENCY_RATE), + float_to_fp8_24(39.9123020706455 / FREQUENCY_RATE), + float_to_fp8_24(39.9366240097502 / FREQUENCY_RATE), + float_to_fp8_24(39.9609756097561 / FREQUENCY_RATE), + float_to_fp8_24(39.9853569249542 / FREQUENCY_RATE), + float_to_fp8_24(40.009768009768 / FREQUENCY_RATE), + float_to_fp8_24(40.0342089187538 / FREQUENCY_RATE), + float_to_fp8_24(40.0586797066015 / FREQUENCY_RATE), + float_to_fp8_24(40.0831804281346 / FREQUENCY_RATE), + float_to_fp8_24(40.1077111383109 / FREQUENCY_RATE), + float_to_fp8_24(40.1322718922229 / FREQUENCY_RATE), + float_to_fp8_24(40.156862745098 / FREQUENCY_RATE), + float_to_fp8_24(40.1814837522992 / FREQUENCY_RATE), + float_to_fp8_24(40.2061349693252 / FREQUENCY_RATE), + float_to_fp8_24(40.2308164518109 / FREQUENCY_RATE), + float_to_fp8_24(40.2555282555283 / FREQUENCY_RATE), + float_to_fp8_24(40.280270436386 / FREQUENCY_RATE), + float_to_fp8_24(40.3050430504305 / FREQUENCY_RATE), + float_to_fp8_24(40.3298461538462 / FREQUENCY_RATE), + float_to_fp8_24(40.3546798029557 / FREQUENCY_RATE), + float_to_fp8_24(40.3795440542206 / FREQUENCY_RATE), + float_to_fp8_24(40.4044389642417 / FREQUENCY_RATE), + float_to_fp8_24(40.4293645897594 / FREQUENCY_RATE), + float_to_fp8_24(40.4543209876543 / FREQUENCY_RATE), + float_to_fp8_24(40.4793082149475 / FREQUENCY_RATE), + float_to_fp8_24(40.504326328801 / FREQUENCY_RATE), + float_to_fp8_24(40.5293753865182 / FREQUENCY_RATE), + float_to_fp8_24(40.5544554455446 / FREQUENCY_RATE), + float_to_fp8_24(40.5795665634675 / FREQUENCY_RATE), + float_to_fp8_24(40.6047087980174 / FREQUENCY_RATE), + float_to_fp8_24(40.6298822070676 / FREQUENCY_RATE), + float_to_fp8_24(40.6550868486352 / FREQUENCY_RATE), + float_to_fp8_24(40.6803227808814 / FREQUENCY_RATE), + float_to_fp8_24(40.7055900621118 / FREQUENCY_RATE), + float_to_fp8_24(40.7308887507769 / FREQUENCY_RATE), + float_to_fp8_24(40.7562189054726 / FREQUENCY_RATE), + float_to_fp8_24(40.7815805849409 / FREQUENCY_RATE), + float_to_fp8_24(40.8069738480697 / FREQUENCY_RATE), + float_to_fp8_24(40.8323987538941 / FREQUENCY_RATE), + float_to_fp8_24(40.857855361596 / FREQUENCY_RATE), + float_to_fp8_24(40.8833437305053 / FREQUENCY_RATE), + float_to_fp8_24(40.9088639200999 / FREQUENCY_RATE), + float_to_fp8_24(40.9344159900062 / FREQUENCY_RATE), + float_to_fp8_24(40.96 / FREQUENCY_RATE), + float_to_fp8_24(40.9856160100063 / FREQUENCY_RATE), + float_to_fp8_24(41.0112640801001 / FREQUENCY_RATE), + float_to_fp8_24(41.0369442705072 / FREQUENCY_RATE), + float_to_fp8_24(41.062656641604 / FREQUENCY_RATE), + float_to_fp8_24(41.0884012539185 / FREQUENCY_RATE), + float_to_fp8_24(41.1141781681305 / FREQUENCY_RATE), + float_to_fp8_24(41.1399874450722 / FREQUENCY_RATE), + float_to_fp8_24(41.1658291457287 / FREQUENCY_RATE), + float_to_fp8_24(41.1917033312382 / FREQUENCY_RATE), + float_to_fp8_24(41.2176100628931 / FREQUENCY_RATE), + float_to_fp8_24(41.2435494021397 / FREQUENCY_RATE), + float_to_fp8_24(41.2695214105793 / FREQUENCY_RATE), + float_to_fp8_24(41.2955261499685 / FREQUENCY_RATE), + float_to_fp8_24(41.3215636822194 / FREQUENCY_RATE), + float_to_fp8_24(41.3476340694006 / FREQUENCY_RATE), + float_to_fp8_24(41.3737373737374 / FREQUENCY_RATE), + float_to_fp8_24(41.3998736576121 / FREQUENCY_RATE), + float_to_fp8_24(41.4260429835651 / FREQUENCY_RATE), + float_to_fp8_24(41.4522454142948 / FREQUENCY_RATE), + float_to_fp8_24(41.4784810126582 / FREQUENCY_RATE), + float_to_fp8_24(41.504749841672 / FREQUENCY_RATE), + float_to_fp8_24(41.531051964512 / FREQUENCY_RATE), + float_to_fp8_24(41.5573874445149 / FREQUENCY_RATE), + float_to_fp8_24(41.5837563451777 / FREQUENCY_RATE), + float_to_fp8_24(41.6101587301587 / FREQUENCY_RATE), + float_to_fp8_24(41.6365946632783 / FREQUENCY_RATE), + float_to_fp8_24(41.6630642085188 / FREQUENCY_RATE), + float_to_fp8_24(41.6895674300254 / FREQUENCY_RATE), + float_to_fp8_24(41.7161043921069 / FREQUENCY_RATE), + float_to_fp8_24(41.7426751592357 / FREQUENCY_RATE), + float_to_fp8_24(41.7692797960484 / FREQUENCY_RATE), + float_to_fp8_24(41.7959183673469 / FREQUENCY_RATE), + float_to_fp8_24(41.8225909380983 / FREQUENCY_RATE), + float_to_fp8_24(41.8492975734355 / FREQUENCY_RATE), + float_to_fp8_24(41.8760383386582 / FREQUENCY_RATE), + float_to_fp8_24(41.9028132992327 / FREQUENCY_RATE), + float_to_fp8_24(41.9296225207934 / FREQUENCY_RATE), + float_to_fp8_24(41.9564660691421 / FREQUENCY_RATE), + float_to_fp8_24(41.9833440102498 / FREQUENCY_RATE), + float_to_fp8_24(42.0102564102564 / FREQUENCY_RATE), + float_to_fp8_24(42.0372033354715 / FREQUENCY_RATE), + float_to_fp8_24(42.0641848523748 / FREQUENCY_RATE), + float_to_fp8_24(42.0912010276172 / FREQUENCY_RATE), + float_to_fp8_24(42.1182519280206 / FREQUENCY_RATE), + float_to_fp8_24(42.1453376205788 / FREQUENCY_RATE), + float_to_fp8_24(42.1724581724582 / FREQUENCY_RATE), + float_to_fp8_24(42.1996136509981 / FREQUENCY_RATE), + float_to_fp8_24(42.2268041237113 / FREQUENCY_RATE), + float_to_fp8_24(42.254029658285 / FREQUENCY_RATE), + float_to_fp8_24(42.2812903225807 / FREQUENCY_RATE), + float_to_fp8_24(42.3085861846352 / FREQUENCY_RATE), + float_to_fp8_24(42.3359173126615 / FREQUENCY_RATE), + float_to_fp8_24(42.3632837750485 / FREQUENCY_RATE), + float_to_fp8_24(42.3906856403622 / FREQUENCY_RATE), + float_to_fp8_24(42.4181229773463 / FREQUENCY_RATE), + float_to_fp8_24(42.4455958549223 / FREQUENCY_RATE), + float_to_fp8_24(42.4731043421905 / FREQUENCY_RATE), + float_to_fp8_24(42.5006485084306 / FREQUENCY_RATE), + float_to_fp8_24(42.5282284231019 / FREQUENCY_RATE), + float_to_fp8_24(42.5558441558442 / FREQUENCY_RATE), + float_to_fp8_24(42.5834957764782 / FREQUENCY_RATE), + float_to_fp8_24(42.6111833550065 / FREQUENCY_RATE), + float_to_fp8_24(42.6389069616135 / FREQUENCY_RATE), + float_to_fp8_24(42.6666666666667 / FREQUENCY_RATE), + float_to_fp8_24(42.6944625407166 / FREQUENCY_RATE), + float_to_fp8_24(42.722294654498 / FREQUENCY_RATE), + float_to_fp8_24(42.7501630789302 / FREQUENCY_RATE), + float_to_fp8_24(42.7780678851175 / FREQUENCY_RATE), + float_to_fp8_24(42.8060091443501 / FREQUENCY_RATE), + float_to_fp8_24(42.8339869281046 / FREQUENCY_RATE), + float_to_fp8_24(42.8620013080445 / FREQUENCY_RATE), + float_to_fp8_24(42.890052356021 / FREQUENCY_RATE), + float_to_fp8_24(42.9181401440733 / FREQUENCY_RATE), + float_to_fp8_24(42.9462647444299 / FREQUENCY_RATE), + float_to_fp8_24(42.9744262295082 / FREQUENCY_RATE), + float_to_fp8_24(43.002624671916 / FREQUENCY_RATE), + float_to_fp8_24(43.0308601444517 / FREQUENCY_RATE), + float_to_fp8_24(43.0591327201051 / FREQUENCY_RATE), + float_to_fp8_24(43.0874424720579 / FREQUENCY_RATE), + float_to_fp8_24(43.1157894736842 / FREQUENCY_RATE), + float_to_fp8_24(43.1441737985517 / FREQUENCY_RATE), + float_to_fp8_24(43.1725955204216 / FREQUENCY_RATE), + float_to_fp8_24(43.2010547132498 / FREQUENCY_RATE), + float_to_fp8_24(43.2295514511873 / FREQUENCY_RATE), + float_to_fp8_24(43.2580858085809 / FREQUENCY_RATE), + float_to_fp8_24(43.2866578599736 / FREQUENCY_RATE), + float_to_fp8_24(43.3152676801057 / FREQUENCY_RATE), + float_to_fp8_24(43.3439153439153 / FREQUENCY_RATE), + float_to_fp8_24(43.3726009265387 / FREQUENCY_RATE), + float_to_fp8_24(43.4013245033113 / FREQUENCY_RATE), + float_to_fp8_24(43.4300861497681 / FREQUENCY_RATE), + float_to_fp8_24(43.4588859416446 / FREQUENCY_RATE), + float_to_fp8_24(43.4877239548772 / FREQUENCY_RATE), + float_to_fp8_24(43.5166002656043 / FREQUENCY_RATE), + float_to_fp8_24(43.5455149501661 / FREQUENCY_RATE), + float_to_fp8_24(43.5744680851064 / FREQUENCY_RATE), + float_to_fp8_24(43.6034597471723 / FREQUENCY_RATE), + float_to_fp8_24(43.6324900133156 / FREQUENCY_RATE), + float_to_fp8_24(43.6615589606929 / FREQUENCY_RATE), + float_to_fp8_24(43.6906666666667 / FREQUENCY_RATE), + float_to_fp8_24(43.7198132088059 / FREQUENCY_RATE), + float_to_fp8_24(43.7489986648865 / FREQUENCY_RATE), + float_to_fp8_24(43.7782231128925 / FREQUENCY_RATE), + float_to_fp8_24(43.807486631016 / FREQUENCY_RATE), + float_to_fp8_24(43.8367892976589 / FREQUENCY_RATE), + float_to_fp8_24(43.8661311914324 / FREQUENCY_RATE), + float_to_fp8_24(43.8955123911587 / FREQUENCY_RATE), + float_to_fp8_24(43.9249329758713 / FREQUENCY_RATE), + float_to_fp8_24(43.9543930248156 / FREQUENCY_RATE), + float_to_fp8_24(43.9838926174497 / FREQUENCY_RATE), + float_to_fp8_24(44.0134318334453 / FREQUENCY_RATE), + float_to_fp8_24(44.0430107526882 / FREQUENCY_RATE), + float_to_fp8_24(44.0726294552791 / FREQUENCY_RATE), + float_to_fp8_24(44.1022880215343 / FREQUENCY_RATE), + float_to_fp8_24(44.1319865319865 / FREQUENCY_RATE), + float_to_fp8_24(44.1617250673855 / FREQUENCY_RATE), + float_to_fp8_24(44.1915037086986 / FREQUENCY_RATE), + float_to_fp8_24(44.221322537112 / FREQUENCY_RATE), + float_to_fp8_24(44.2511816340311 / FREQUENCY_RATE), + float_to_fp8_24(44.2810810810811 / FREQUENCY_RATE), + float_to_fp8_24(44.3110209601082 / FREQUENCY_RATE), + float_to_fp8_24(44.34100135318 / FREQUENCY_RATE), + float_to_fp8_24(44.3710223425863 / FREQUENCY_RATE), + float_to_fp8_24(44.4010840108401 / FREQUENCY_RATE), + float_to_fp8_24(44.431186440678 / FREQUENCY_RATE), + float_to_fp8_24(44.4613297150611 / FREQUENCY_RATE), + float_to_fp8_24(44.4915139171758 / FREQUENCY_RATE), + float_to_fp8_24(44.5217391304348 / FREQUENCY_RATE), + float_to_fp8_24(44.5520054384772 / FREQUENCY_RATE), + float_to_fp8_24(44.5823129251701 / FREQUENCY_RATE), + float_to_fp8_24(44.6126616746086 / FREQUENCY_RATE), + float_to_fp8_24(44.6430517711172 / FREQUENCY_RATE), + float_to_fp8_24(44.6734832992502 / FREQUENCY_RATE), + float_to_fp8_24(44.7039563437926 / FREQUENCY_RATE), + float_to_fp8_24(44.7344709897611 / FREQUENCY_RATE), + float_to_fp8_24(44.7650273224044 / FREQUENCY_RATE), + float_to_fp8_24(44.7956254272044 / FREQUENCY_RATE), + float_to_fp8_24(44.8262653898769 / FREQUENCY_RATE), + float_to_fp8_24(44.8569472963724 / FREQUENCY_RATE), + float_to_fp8_24(44.8876712328767 / FREQUENCY_RATE), + float_to_fp8_24(44.9184372858122 / FREQUENCY_RATE), + float_to_fp8_24(44.9492455418381 / FREQUENCY_RATE), + float_to_fp8_24(44.9800960878518 / FREQUENCY_RATE), + float_to_fp8_24(45.010989010989 / FREQUENCY_RATE), + float_to_fp8_24(45.0419243986254 / FREQUENCY_RATE), + float_to_fp8_24(45.0729023383769 / FREQUENCY_RATE), + float_to_fp8_24(45.1039229181005 / FREQUENCY_RATE), + float_to_fp8_24(45.1349862258953 / FREQUENCY_RATE), + float_to_fp8_24(45.1660923501034 / FREQUENCY_RATE), + float_to_fp8_24(45.1972413793103 / FREQUENCY_RATE), + float_to_fp8_24(45.2284334023465 / FREQUENCY_RATE), + float_to_fp8_24(45.2596685082873 / FREQUENCY_RATE), + float_to_fp8_24(45.2909467864547 / FREQUENCY_RATE), + float_to_fp8_24(45.3222683264177 / FREQUENCY_RATE), + float_to_fp8_24(45.3536332179931 / FREQUENCY_RATE), + float_to_fp8_24(45.3850415512465 / FREQUENCY_RATE), + float_to_fp8_24(45.4164934164934 / FREQUENCY_RATE), + float_to_fp8_24(45.4479889042996 / FREQUENCY_RATE), + float_to_fp8_24(45.4795281054823 / FREQUENCY_RATE), + float_to_fp8_24(45.5111111111111 / FREQUENCY_RATE), + float_to_fp8_24(45.5427380125087 / FREQUENCY_RATE), + float_to_fp8_24(45.5744089012517 / FREQUENCY_RATE), + float_to_fp8_24(45.6061238691719 / FREQUENCY_RATE), + float_to_fp8_24(45.6378830083566 / FREQUENCY_RATE), + float_to_fp8_24(45.6696864111498 / FREQUENCY_RATE), + float_to_fp8_24(45.7015341701534 / FREQUENCY_RATE), + float_to_fp8_24(45.7334263782275 / FREQUENCY_RATE), + float_to_fp8_24(45.7653631284916 / FREQUENCY_RATE), + float_to_fp8_24(45.7973445143256 / FREQUENCY_RATE), + float_to_fp8_24(45.8293706293706 / FREQUENCY_RATE), + float_to_fp8_24(45.8614415675297 / FREQUENCY_RATE), + float_to_fp8_24(45.8935574229692 / FREQUENCY_RATE), + float_to_fp8_24(45.9257182901191 / FREQUENCY_RATE), + float_to_fp8_24(45.9579242636746 / FREQUENCY_RATE), + float_to_fp8_24(45.9901754385965 / FREQUENCY_RATE), + float_to_fp8_24(46.0224719101124 / FREQUENCY_RATE), + float_to_fp8_24(46.0548137737175 / FREQUENCY_RATE), + float_to_fp8_24(46.0872011251758 / FREQUENCY_RATE), + float_to_fp8_24(46.1196340605208 / FREQUENCY_RATE), + float_to_fp8_24(46.1521126760563 / FREQUENCY_RATE), + float_to_fp8_24(46.184637068358 / FREQUENCY_RATE), + float_to_fp8_24(46.2172073342736 / FREQUENCY_RATE), + float_to_fp8_24(46.2498235709245 / FREQUENCY_RATE), + float_to_fp8_24(46.2824858757062 / FREQUENCY_RATE), + float_to_fp8_24(46.3151943462898 / FREQUENCY_RATE), + float_to_fp8_24(46.3479490806223 / FREQUENCY_RATE), + float_to_fp8_24(46.3807501769285 / FREQUENCY_RATE), + float_to_fp8_24(46.4135977337111 / FREQUENCY_RATE), + float_to_fp8_24(46.446491849752 / FREQUENCY_RATE), + float_to_fp8_24(46.4794326241135 / FREQUENCY_RATE), + float_to_fp8_24(46.5124201561391 / FREQUENCY_RATE), + float_to_fp8_24(46.5454545454545 / FREQUENCY_RATE), + float_to_fp8_24(46.5785358919687 / FREQUENCY_RATE), + float_to_fp8_24(46.6116642958748 / FREQUENCY_RATE), + float_to_fp8_24(46.6448398576513 / FREQUENCY_RATE), + float_to_fp8_24(46.6780626780627 / FREQUENCY_RATE), + float_to_fp8_24(46.7113328581611 / FREQUENCY_RATE), + float_to_fp8_24(46.7446504992867 / FREQUENCY_RATE), + float_to_fp8_24(46.7780157030692 / FREQUENCY_RATE), + float_to_fp8_24(46.8114285714286 / FREQUENCY_RATE), + float_to_fp8_24(46.8448892065761 / FREQUENCY_RATE), + float_to_fp8_24(46.8783977110157 / FREQUENCY_RATE), + float_to_fp8_24(46.9119541875447 / FREQUENCY_RATE), + float_to_fp8_24(46.945558739255 / FREQUENCY_RATE), + float_to_fp8_24(46.9792114695341 / FREQUENCY_RATE), + float_to_fp8_24(47.012912482066 / FREQUENCY_RATE), + float_to_fp8_24(47.0466618808327 / FREQUENCY_RATE), + float_to_fp8_24(47.0804597701149 / FREQUENCY_RATE), + float_to_fp8_24(47.1143062544932 / FREQUENCY_RATE), + float_to_fp8_24(47.1482014388489 / FREQUENCY_RATE), + float_to_fp8_24(47.1821454283657 / FREQUENCY_RATE), + float_to_fp8_24(47.2161383285303 / FREQUENCY_RATE), + float_to_fp8_24(47.2501802451334 / FREQUENCY_RATE), + float_to_fp8_24(47.2842712842713 / FREQUENCY_RATE), + float_to_fp8_24(47.3184115523466 / FREQUENCY_RATE), + float_to_fp8_24(47.3526011560694 / FREQUENCY_RATE), + float_to_fp8_24(47.3868402024584 / FREQUENCY_RATE), + float_to_fp8_24(47.4211287988423 / FREQUENCY_RATE), + float_to_fp8_24(47.4554670528602 / FREQUENCY_RATE), + float_to_fp8_24(47.4898550724638 / FREQUENCY_RATE), + float_to_fp8_24(47.5242929659173 / FREQUENCY_RATE), + float_to_fp8_24(47.5587808417997 / FREQUENCY_RATE), + float_to_fp8_24(47.5933188090051 / FREQUENCY_RATE), + float_to_fp8_24(47.6279069767442 / FREQUENCY_RATE), + float_to_fp8_24(47.6625454545455 / FREQUENCY_RATE), + float_to_fp8_24(47.6972343522562 / FREQUENCY_RATE), + float_to_fp8_24(47.7319737800437 / FREQUENCY_RATE), + float_to_fp8_24(47.7667638483965 / FREQUENCY_RATE), + float_to_fp8_24(47.8016046681255 / FREQUENCY_RATE), + float_to_fp8_24(47.836496350365 / FREQUENCY_RATE), + float_to_fp8_24(47.8714390065741 / FREQUENCY_RATE), + float_to_fp8_24(47.906432748538 / FREQUENCY_RATE), + float_to_fp8_24(47.9414776883687 / FREQUENCY_RATE), + float_to_fp8_24(47.9765739385066 / FREQUENCY_RATE), + float_to_fp8_24(48.0117216117216 / FREQUENCY_RATE), + float_to_fp8_24(48.0469208211144 / FREQUENCY_RATE), + float_to_fp8_24(48.0821716801174 / FREQUENCY_RATE), + float_to_fp8_24(48.1174743024963 / FREQUENCY_RATE), + float_to_fp8_24(48.1528288023512 / FREQUENCY_RATE), + float_to_fp8_24(48.1882352941176 / FREQUENCY_RATE), + float_to_fp8_24(48.2236938925681 / FREQUENCY_RATE), + float_to_fp8_24(48.259204712813 / FREQUENCY_RATE), + float_to_fp8_24(48.2947678703021 / FREQUENCY_RATE), + float_to_fp8_24(48.330383480826 / FREQUENCY_RATE), + float_to_fp8_24(48.3660516605166 / FREQUENCY_RATE), + float_to_fp8_24(48.4017725258493 / FREQUENCY_RATE), + float_to_fp8_24(48.4375461936438 / FREQUENCY_RATE), + float_to_fp8_24(48.4733727810651 / FREQUENCY_RATE), + float_to_fp8_24(48.5092524056255 / FREQUENCY_RATE), + float_to_fp8_24(48.5451851851852 / FREQUENCY_RATE), + float_to_fp8_24(48.581171237954 / FREQUENCY_RATE), + float_to_fp8_24(48.6172106824926 / FREQUENCY_RATE), + float_to_fp8_24(48.6533036377134 / FREQUENCY_RATE), + float_to_fp8_24(48.6894502228826 / FREQUENCY_RATE), + float_to_fp8_24(48.7256505576208 / FREQUENCY_RATE), + float_to_fp8_24(48.7619047619048 / FREQUENCY_RATE), + float_to_fp8_24(48.7982129560685 / FREQUENCY_RATE), + float_to_fp8_24(48.8345752608048 / FREQUENCY_RATE), + float_to_fp8_24(48.8709917971663 / FREQUENCY_RATE), + float_to_fp8_24(48.9074626865672 / FREQUENCY_RATE), + float_to_fp8_24(48.9439880507842 / FREQUENCY_RATE), + float_to_fp8_24(48.9805680119582 / FREQUENCY_RATE), + float_to_fp8_24(49.0172026925954 / FREQUENCY_RATE), + float_to_fp8_24(49.0538922155689 / FREQUENCY_RATE), + float_to_fp8_24(49.0906367041199 / FREQUENCY_RATE), + float_to_fp8_24(49.1274362818591 / FREQUENCY_RATE), + float_to_fp8_24(49.1642910727682 / FREQUENCY_RATE), + float_to_fp8_24(49.2012012012012 / FREQUENCY_RATE), + float_to_fp8_24(49.2381667918858 / FREQUENCY_RATE), + float_to_fp8_24(49.2751879699248 / FREQUENCY_RATE), + float_to_fp8_24(49.3122648607976 / FREQUENCY_RATE), + float_to_fp8_24(49.3493975903615 / FREQUENCY_RATE), + float_to_fp8_24(49.3865862848531 / FREQUENCY_RATE), + float_to_fp8_24(49.4238310708899 / FREQUENCY_RATE), + float_to_fp8_24(49.4611320754717 / FREQUENCY_RATE), + float_to_fp8_24(49.4984894259819 / FREQUENCY_RATE), + float_to_fp8_24(49.535903250189 / FREQUENCY_RATE), + float_to_fp8_24(49.5733736762481 / FREQUENCY_RATE), + float_to_fp8_24(49.6109008327025 / FREQUENCY_RATE), + float_to_fp8_24(49.6484848484849 / FREQUENCY_RATE), + float_to_fp8_24(49.6861258529189 / FREQUENCY_RATE), + float_to_fp8_24(49.7238239757208 / FREQUENCY_RATE), + float_to_fp8_24(49.7615793470008 / FREQUENCY_RATE), + float_to_fp8_24(49.7993920972644 / FREQUENCY_RATE), + float_to_fp8_24(49.8372623574145 / FREQUENCY_RATE), + float_to_fp8_24(49.8751902587519 / FREQUENCY_RATE), + float_to_fp8_24(49.9131759329779 / FREQUENCY_RATE), + float_to_fp8_24(49.9512195121951 / FREQUENCY_RATE), + float_to_fp8_24(49.9893211289092 / FREQUENCY_RATE), + float_to_fp8_24(50.0274809160305 / FREQUENCY_RATE), + float_to_fp8_24(50.0656990068755 / FREQUENCY_RATE), + float_to_fp8_24(50.1039755351682 / FREQUENCY_RATE), + float_to_fp8_24(50.1423106350421 / FREQUENCY_RATE), + float_to_fp8_24(50.1807044410413 / FREQUENCY_RATE), + float_to_fp8_24(50.2191570881226 / FREQUENCY_RATE), + float_to_fp8_24(50.2576687116564 / FREQUENCY_RATE), + float_to_fp8_24(50.296239447429 / FREQUENCY_RATE), + float_to_fp8_24(50.3348694316436 / FREQUENCY_RATE), + float_to_fp8_24(50.3735588009224 / FREQUENCY_RATE), + float_to_fp8_24(50.4123076923077 / FREQUENCY_RATE), + float_to_fp8_24(50.451116243264 / FREQUENCY_RATE), + float_to_fp8_24(50.4899845916795 / FREQUENCY_RATE), + float_to_fp8_24(50.5289128758674 / FREQUENCY_RATE), + float_to_fp8_24(50.5679012345679 / FREQUENCY_RATE), + float_to_fp8_24(50.6069498069498 / FREQUENCY_RATE), + float_to_fp8_24(50.6460587326121 / FREQUENCY_RATE), + float_to_fp8_24(50.6852281515855 / FREQUENCY_RATE), + float_to_fp8_24(50.7244582043344 / FREQUENCY_RATE), + float_to_fp8_24(50.7637490317583 / FREQUENCY_RATE), + float_to_fp8_24(50.8031007751938 / FREQUENCY_RATE), + float_to_fp8_24(50.8425135764158 / FREQUENCY_RATE), + float_to_fp8_24(50.8819875776397 / FREQUENCY_RATE), + float_to_fp8_24(50.9215229215229 / FREQUENCY_RATE), + float_to_fp8_24(50.9611197511664 / FREQUENCY_RATE), + float_to_fp8_24(51.0007782101167 / FREQUENCY_RATE), + float_to_fp8_24(51.0404984423676 / FREQUENCY_RATE), + float_to_fp8_24(51.0802805923617 / FREQUENCY_RATE), + float_to_fp8_24(51.1201248049922 / FREQUENCY_RATE), + float_to_fp8_24(51.160031225605 / FREQUENCY_RATE), + float_to_fp8_24(51.2 / FREQUENCY_RATE), + float_to_fp8_24(51.2400312744331 / FREQUENCY_RATE), + float_to_fp8_24(51.2801251956182 / FREQUENCY_RATE), + float_to_fp8_24(51.3202819107283 / FREQUENCY_RATE), + float_to_fp8_24(51.3605015673981 / FREQUENCY_RATE), + float_to_fp8_24(51.4007843137255 / FREQUENCY_RATE), + float_to_fp8_24(51.4411302982732 / FREQUENCY_RATE), + float_to_fp8_24(51.4815396700707 / FREQUENCY_RATE), + float_to_fp8_24(51.5220125786164 / FREQUENCY_RATE), + float_to_fp8_24(51.5625491738788 / FREQUENCY_RATE), + float_to_fp8_24(51.6031496062992 / FREQUENCY_RATE), + float_to_fp8_24(51.6438140267928 / FREQUENCY_RATE), + float_to_fp8_24(51.6845425867508 / FREQUENCY_RATE), + float_to_fp8_24(51.7253354380426 / FREQUENCY_RATE), + float_to_fp8_24(51.7661927330174 / FREQUENCY_RATE), + float_to_fp8_24(51.8071146245059 / FREQUENCY_RATE), + float_to_fp8_24(51.8481012658228 / FREQUENCY_RATE), + float_to_fp8_24(51.889152810768 / FREQUENCY_RATE), + float_to_fp8_24(51.9302694136292 / FREQUENCY_RATE), + float_to_fp8_24(51.9714512291832 / FREQUENCY_RATE), + float_to_fp8_24(52.0126984126984 / FREQUENCY_RATE), + float_to_fp8_24(52.0540111199365 / FREQUENCY_RATE), + float_to_fp8_24(52.0953895071542 / FREQUENCY_RATE), + float_to_fp8_24(52.1368337311058 / FREQUENCY_RATE), + float_to_fp8_24(52.1783439490446 / FREQUENCY_RATE), + float_to_fp8_24(52.2199203187251 / FREQUENCY_RATE), + float_to_fp8_24(52.2615629984051 / FREQUENCY_RATE), + float_to_fp8_24(52.3032721468476 / FREQUENCY_RATE), + float_to_fp8_24(52.3450479233227 / FREQUENCY_RATE), + float_to_fp8_24(52.3868904876099 / FREQUENCY_RATE), + float_to_fp8_24(52.4288 / FREQUENCY_RATE), + float_to_fp8_24(52.470776621297 / FREQUENCY_RATE), + float_to_fp8_24(52.5128205128205 / FREQUENCY_RATE), + float_to_fp8_24(52.5549318364074 / FREQUENCY_RATE), + float_to_fp8_24(52.5971107544141 / FREQUENCY_RATE), + float_to_fp8_24(52.6393574297189 / FREQUENCY_RATE), + float_to_fp8_24(52.6816720257235 / FREQUENCY_RATE), + float_to_fp8_24(52.7240547063556 / FREQUENCY_RATE), + float_to_fp8_24(52.7665056360709 / FREQUENCY_RATE), + float_to_fp8_24(52.809024979855 / FREQUENCY_RATE), + float_to_fp8_24(52.8516129032258 / FREQUENCY_RATE), + float_to_fp8_24(52.8942695722357 / FREQUENCY_RATE), + float_to_fp8_24(52.9369951534734 / FREQUENCY_RATE), + float_to_fp8_24(52.9797898140663 / FREQUENCY_RATE), + float_to_fp8_24(53.0226537216829 / FREQUENCY_RATE), + float_to_fp8_24(53.0655870445344 / FREQUENCY_RATE), + float_to_fp8_24(53.1085899513776 / FREQUENCY_RATE), + float_to_fp8_24(53.1516626115166 / FREQUENCY_RATE), + float_to_fp8_24(53.1948051948052 / FREQUENCY_RATE), + float_to_fp8_24(53.2380178716491 / FREQUENCY_RATE), + float_to_fp8_24(53.2813008130081 / FREQUENCY_RATE), + float_to_fp8_24(53.3246541903987 / FREQUENCY_RATE), + float_to_fp8_24(53.3680781758958 / FREQUENCY_RATE), + float_to_fp8_24(53.4115729421353 / FREQUENCY_RATE), + float_to_fp8_24(53.4551386623165 / FREQUENCY_RATE), + float_to_fp8_24(53.4987755102041 / FREQUENCY_RATE), + float_to_fp8_24(53.5424836601307 / FREQUENCY_RATE), + float_to_fp8_24(53.5862632869992 / FREQUENCY_RATE), + float_to_fp8_24(53.6301145662848 / FREQUENCY_RATE), + float_to_fp8_24(53.6740376740377 / FREQUENCY_RATE), + float_to_fp8_24(53.7180327868852 / FREQUENCY_RATE), + float_to_fp8_24(53.7621000820345 / FREQUENCY_RATE), + float_to_fp8_24(53.8062397372742 / FREQUENCY_RATE), + float_to_fp8_24(53.8504519309778 / FREQUENCY_RATE), + float_to_fp8_24(53.8947368421053 / FREQUENCY_RATE), + float_to_fp8_24(53.9390946502058 / FREQUENCY_RATE), + float_to_fp8_24(53.9835255354201 / FREQUENCY_RATE), + float_to_fp8_24(54.0280296784831 / FREQUENCY_RATE), + float_to_fp8_24(54.0726072607261 / FREQUENCY_RATE), + float_to_fp8_24(54.1172584640793 / FREQUENCY_RATE), + float_to_fp8_24(54.1619834710744 / FREQUENCY_RATE), + float_to_fp8_24(54.206782464847 / FREQUENCY_RATE), + float_to_fp8_24(54.2516556291391 / FREQUENCY_RATE), + float_to_fp8_24(54.2966031483016 / FREQUENCY_RATE), + float_to_fp8_24(54.3416252072969 / FREQUENCY_RATE), + float_to_fp8_24(54.3867219917012 / FREQUENCY_RATE), + float_to_fp8_24(54.4318936877076 / FREQUENCY_RATE), + float_to_fp8_24(54.477140482128 / FREQUENCY_RATE), + float_to_fp8_24(54.522462562396 / FREQUENCY_RATE), + float_to_fp8_24(54.5678601165695 / FREQUENCY_RATE), + float_to_fp8_24(54.6133333333333 / FREQUENCY_RATE), + float_to_fp8_24(54.6588824020017 / FREQUENCY_RATE), + float_to_fp8_24(54.7045075125209 / FREQUENCY_RATE), + float_to_fp8_24(54.750208855472 / FREQUENCY_RATE), + float_to_fp8_24(54.7959866220736 / FREQUENCY_RATE), + float_to_fp8_24(54.8418410041841 / FREQUENCY_RATE), + float_to_fp8_24(54.8877721943049 / FREQUENCY_RATE), + float_to_fp8_24(54.9337803855826 / FREQUENCY_RATE), + float_to_fp8_24(54.9798657718121 / FREQUENCY_RATE), + float_to_fp8_24(55.0260285474391 / FREQUENCY_RATE), + float_to_fp8_24(55.072268907563 / FREQUENCY_RATE), + float_to_fp8_24(55.1185870479394 / FREQUENCY_RATE), + float_to_fp8_24(55.1649831649832 / FREQUENCY_RATE), + float_to_fp8_24(55.2114574557709 / FREQUENCY_RATE), + float_to_fp8_24(55.2580101180438 / FREQUENCY_RATE), + float_to_fp8_24(55.304641350211 / FREQUENCY_RATE), + float_to_fp8_24(55.3513513513514 / FREQUENCY_RATE), + float_to_fp8_24(55.3981403212172 / FREQUENCY_RATE), + float_to_fp8_24(55.4450084602369 / FREQUENCY_RATE), + float_to_fp8_24(55.4919559695174 / FREQUENCY_RATE), + float_to_fp8_24(55.5389830508475 / FREQUENCY_RATE), + float_to_fp8_24(55.5860899067006 / FREQUENCY_RATE), + float_to_fp8_24(55.6332767402377 / FREQUENCY_RATE), + float_to_fp8_24(55.6805437553101 / FREQUENCY_RATE), + float_to_fp8_24(55.7278911564626 / FREQUENCY_RATE), + float_to_fp8_24(55.7753191489362 / FREQUENCY_RATE), + float_to_fp8_24(55.8228279386712 / FREQUENCY_RATE), + float_to_fp8_24(55.8704177323103 / FREQUENCY_RATE), + float_to_fp8_24(55.9180887372014 / FREQUENCY_RATE), + float_to_fp8_24(55.9658411614005 / FREQUENCY_RATE), + float_to_fp8_24(56.0136752136752 / FREQUENCY_RATE), + float_to_fp8_24(56.0615911035073 / FREQUENCY_RATE), + float_to_fp8_24(56.1095890410959 / FREQUENCY_RATE), + float_to_fp8_24(56.1576692373608 / FREQUENCY_RATE), + float_to_fp8_24(56.2058319039451 / FREQUENCY_RATE), + float_to_fp8_24(56.2540772532189 / FREQUENCY_RATE), + float_to_fp8_24(56.3024054982818 / FREQUENCY_RATE), + float_to_fp8_24(56.3508168529665 / FREQUENCY_RATE), + float_to_fp8_24(56.3993115318417 / FREQUENCY_RATE), + float_to_fp8_24(56.4478897502153 / FREQUENCY_RATE), + float_to_fp8_24(56.4965517241379 / FREQUENCY_RATE), + float_to_fp8_24(56.5452976704055 / FREQUENCY_RATE), + float_to_fp8_24(56.594127806563 / FREQUENCY_RATE), + float_to_fp8_24(56.6430423509075 / FREQUENCY_RATE), + float_to_fp8_24(56.6920415224914 / FREQUENCY_RATE), + float_to_fp8_24(56.7411255411255 / FREQUENCY_RATE), + float_to_fp8_24(56.790294627383 / FREQUENCY_RATE), + float_to_fp8_24(56.8395490026019 / FREQUENCY_RATE), + float_to_fp8_24(56.8888888888889 / FREQUENCY_RATE), + float_to_fp8_24(56.9383145091225 / FREQUENCY_RATE), + float_to_fp8_24(56.9878260869565 / FREQUENCY_RATE), + float_to_fp8_24(57.0374238468233 / FREQUENCY_RATE), + float_to_fp8_24(57.0871080139373 / FREQUENCY_RATE), + float_to_fp8_24(57.1368788142982 / FREQUENCY_RATE), + float_to_fp8_24(57.1867364746946 / FREQUENCY_RATE), + float_to_fp8_24(57.2366812227074 / FREQUENCY_RATE), + float_to_fp8_24(57.2867132867133 / FREQUENCY_RATE), + float_to_fp8_24(57.336832895888 / FREQUENCY_RATE), + float_to_fp8_24(57.3870402802102 / FREQUENCY_RATE), + float_to_fp8_24(57.4373356704645 / FREQUENCY_RATE), + float_to_fp8_24(57.4877192982456 / FREQUENCY_RATE), + float_to_fp8_24(57.5381913959614 / FREQUENCY_RATE), + float_to_fp8_24(57.5887521968366 / FREQUENCY_RATE), + float_to_fp8_24(57.6394019349165 / FREQUENCY_RATE), + float_to_fp8_24(57.6901408450704 / FREQUENCY_RATE), + float_to_fp8_24(57.7409691629956 / FREQUENCY_RATE), + float_to_fp8_24(57.7918871252205 / FREQUENCY_RATE), + float_to_fp8_24(57.8428949691086 / FREQUENCY_RATE), + float_to_fp8_24(57.8939929328622 / FREQUENCY_RATE), + float_to_fp8_24(57.9451812555261 / FREQUENCY_RATE), + float_to_fp8_24(57.9964601769912 / FREQUENCY_RATE), + float_to_fp8_24(58.0478299379982 / FREQUENCY_RATE), + float_to_fp8_24(58.0992907801418 / FREQUENCY_RATE), + float_to_fp8_24(58.150842945874 / FREQUENCY_RATE), + float_to_fp8_24(58.202486678508 / FREQUENCY_RATE), + float_to_fp8_24(58.2542222222222 / FREQUENCY_RATE), + float_to_fp8_24(58.3060498220641 / FREQUENCY_RATE), + float_to_fp8_24(58.3579697239537 / FREQUENCY_RATE), + float_to_fp8_24(58.4099821746881 / FREQUENCY_RATE), + float_to_fp8_24(58.4620874219447 / FREQUENCY_RATE), + float_to_fp8_24(58.5142857142857 / FREQUENCY_RATE), + float_to_fp8_24(58.5665773011618 / FREQUENCY_RATE), + float_to_fp8_24(58.6189624329159 / FREQUENCY_RATE), + float_to_fp8_24(58.6714413607878 / FREQUENCY_RATE), + float_to_fp8_24(58.7240143369176 / FREQUENCY_RATE), + float_to_fp8_24(58.7766816143498 / FREQUENCY_RATE), + float_to_fp8_24(58.8294434470377 / FREQUENCY_RATE), + float_to_fp8_24(58.8823000898473 / FREQUENCY_RATE), + float_to_fp8_24(58.9352517985612 / FREQUENCY_RATE), + float_to_fp8_24(58.988298829883 / FREQUENCY_RATE), + float_to_fp8_24(59.0414414414414 / FREQUENCY_RATE), + float_to_fp8_24(59.0946798917944 / FREQUENCY_RATE), + float_to_fp8_24(59.1480144404332 / FREQUENCY_RATE), + float_to_fp8_24(59.2014453477868 / FREQUENCY_RATE), + float_to_fp8_24(59.254972875226 / FREQUENCY_RATE), + float_to_fp8_24(59.3085972850679 / FREQUENCY_RATE), + float_to_fp8_24(59.3623188405797 / FREQUENCY_RATE), + float_to_fp8_24(59.4161378059837 / FREQUENCY_RATE), + float_to_fp8_24(59.470054446461 / FREQUENCY_RATE), + float_to_fp8_24(59.5240690281562 / FREQUENCY_RATE), + float_to_fp8_24(59.5781818181818 / FREQUENCY_RATE), + float_to_fp8_24(59.6323930846224 / FREQUENCY_RATE), + float_to_fp8_24(59.6867030965392 / FREQUENCY_RATE), + float_to_fp8_24(59.7411121239745 / FREQUENCY_RATE), + float_to_fp8_24(59.7956204379562 / FREQUENCY_RATE), + float_to_fp8_24(59.8502283105023 / FREQUENCY_RATE), + float_to_fp8_24(59.9049360146252 / FREQUENCY_RATE), + float_to_fp8_24(59.9597438243367 / FREQUENCY_RATE), + float_to_fp8_24(60.014652014652 / FREQUENCY_RATE), + float_to_fp8_24(60.0696608615949 / FREQUENCY_RATE), + float_to_fp8_24(60.1247706422018 / FREQUENCY_RATE), + float_to_fp8_24(60.1799816345271 / FREQUENCY_RATE), + float_to_fp8_24(60.2352941176471 / FREQUENCY_RATE), + float_to_fp8_24(60.2907083716651 / FREQUENCY_RATE), + float_to_fp8_24(60.3462246777164 / FREQUENCY_RATE), + float_to_fp8_24(60.4018433179724 / FREQUENCY_RATE), + float_to_fp8_24(60.4575645756458 / FREQUENCY_RATE), + float_to_fp8_24(60.5133887349954 / FREQUENCY_RATE), + float_to_fp8_24(60.5693160813309 / FREQUENCY_RATE), + float_to_fp8_24(60.6253469010176 / FREQUENCY_RATE), + float_to_fp8_24(60.6814814814815 / FREQUENCY_RATE), + float_to_fp8_24(60.7377201112141 / FREQUENCY_RATE), + float_to_fp8_24(60.7940630797774 / FREQUENCY_RATE), + float_to_fp8_24(60.8505106778087 / FREQUENCY_RATE), + float_to_fp8_24(60.907063197026 / FREQUENCY_RATE), + float_to_fp8_24(60.9637209302326 / FREQUENCY_RATE), + float_to_fp8_24(61.0204841713222 / FREQUENCY_RATE), + float_to_fp8_24(61.0773532152843 / FREQUENCY_RATE), + float_to_fp8_24(61.134328358209 / FREQUENCY_RATE), + float_to_fp8_24(61.1914098972923 / FREQUENCY_RATE), + float_to_fp8_24(61.2485981308411 / FREQUENCY_RATE), + float_to_fp8_24(61.3058933582788 / FREQUENCY_RATE), + float_to_fp8_24(61.3632958801498 / FREQUENCY_RATE), + float_to_fp8_24(61.4208059981256 / FREQUENCY_RATE), + float_to_fp8_24(61.4784240150094 / FREQUENCY_RATE), + float_to_fp8_24(61.5361502347418 / FREQUENCY_RATE), + float_to_fp8_24(61.593984962406 / FREQUENCY_RATE), + float_to_fp8_24(61.6519285042333 / FREQUENCY_RATE), + float_to_fp8_24(61.7099811676083 / FREQUENCY_RATE), + float_to_fp8_24(61.7681432610745 / FREQUENCY_RATE), + float_to_fp8_24(61.8264150943396 / FREQUENCY_RATE), + float_to_fp8_24(61.8847969782814 / FREQUENCY_RATE), + float_to_fp8_24(61.9432892249527 / FREQUENCY_RATE), + float_to_fp8_24(62.0018921475875 / FREQUENCY_RATE), + float_to_fp8_24(62.0606060606061 / FREQUENCY_RATE), + float_to_fp8_24(62.1194312796209 / FREQUENCY_RATE), + float_to_fp8_24(62.1783681214421 / FREQUENCY_RATE), + float_to_fp8_24(62.2374169040836 / FREQUENCY_RATE), + float_to_fp8_24(62.2965779467681 / FREQUENCY_RATE), + float_to_fp8_24(62.3558515699334 / FREQUENCY_RATE), + float_to_fp8_24(62.4152380952381 / FREQUENCY_RATE), + float_to_fp8_24(62.4747378455672 / FREQUENCY_RATE), + float_to_fp8_24(62.5343511450382 / FREQUENCY_RATE), + float_to_fp8_24(62.5940783190067 / FREQUENCY_RATE), + float_to_fp8_24(62.6539196940727 / FREQUENCY_RATE), + float_to_fp8_24(62.7138755980861 / FREQUENCY_RATE), + float_to_fp8_24(62.7739463601533 / FREQUENCY_RATE), + float_to_fp8_24(62.8341323106424 / FREQUENCY_RATE), + float_to_fp8_24(62.89443378119 / FREQUENCY_RATE), + float_to_fp8_24(62.954851104707 / FREQUENCY_RATE), + float_to_fp8_24(63.0153846153846 / FREQUENCY_RATE), + float_to_fp8_24(63.0760346487007 / FREQUENCY_RATE), + float_to_fp8_24(63.1368015414258 / FREQUENCY_RATE), + float_to_fp8_24(63.1976856316297 / FREQUENCY_RATE), + float_to_fp8_24(63.2586872586873 / FREQUENCY_RATE), + float_to_fp8_24(63.319806763285 / FREQUENCY_RATE), + float_to_fp8_24(63.3810444874275 / FREQUENCY_RATE), + float_to_fp8_24(63.4424007744434 / FREQUENCY_RATE), + float_to_fp8_24(63.5038759689923 / FREQUENCY_RATE), + float_to_fp8_24(63.5654704170708 / FREQUENCY_RATE), + float_to_fp8_24(63.6271844660194 / FREQUENCY_RATE), + float_to_fp8_24(63.6890184645287 / FREQUENCY_RATE), + float_to_fp8_24(63.7509727626459 / FREQUENCY_RATE), + float_to_fp8_24(63.8130477117819 / FREQUENCY_RATE), + float_to_fp8_24(63.8752436647174 / FREQUENCY_RATE), + float_to_fp8_24(63.9375609756098 / FREQUENCY_RATE), + float_to_fp8_24(64 / FREQUENCY_RATE), + float_to_fp8_24(64.0625610948192 / FREQUENCY_RATE), + float_to_fp8_24(64.1252446183953 / FREQUENCY_RATE), + float_to_fp8_24(64.1880509304603 / FREQUENCY_RATE), + float_to_fp8_24(64.2509803921569 / FREQUENCY_RATE), + float_to_fp8_24(64.3140333660451 / FREQUENCY_RATE), + float_to_fp8_24(64.37721021611 / FREQUENCY_RATE), + float_to_fp8_24(64.440511307768 / FREQUENCY_RATE), + float_to_fp8_24(64.503937007874 / FREQUENCY_RATE), + float_to_fp8_24(64.5674876847291 / FREQUENCY_RATE), + float_to_fp8_24(64.6311637080868 / FREQUENCY_RATE), + float_to_fp8_24(64.6949654491609 / FREQUENCY_RATE), + float_to_fp8_24(64.7588932806324 / FREQUENCY_RATE), + float_to_fp8_24(64.8229475766568 / FREQUENCY_RATE), + float_to_fp8_24(64.8871287128713 / FREQUENCY_RATE), + float_to_fp8_24(64.9514370664024 / FREQUENCY_RATE), + float_to_fp8_24(65.015873015873 / FREQUENCY_RATE), + float_to_fp8_24(65.0804369414101 / FREQUENCY_RATE), + float_to_fp8_24(65.1451292246521 / FREQUENCY_RATE), + float_to_fp8_24(65.2099502487562 / FREQUENCY_RATE), + float_to_fp8_24(65.2749003984064 / FREQUENCY_RATE), + float_to_fp8_24(65.3399800598205 / FREQUENCY_RATE), + float_to_fp8_24(65.4051896207585 / FREQUENCY_RATE), + float_to_fp8_24(65.4705294705295 / FREQUENCY_RATE), + float_to_fp8_24(65.536 / FREQUENCY_RATE), + float_to_fp8_24(65.6016016016016 / FREQUENCY_RATE), + float_to_fp8_24(65.6673346693387 / FREQUENCY_RATE), + float_to_fp8_24(65.7331995987964 / FREQUENCY_RATE), + float_to_fp8_24(65.7991967871486 / FREQUENCY_RATE), + float_to_fp8_24(65.8653266331658 / FREQUENCY_RATE), + float_to_fp8_24(65.9315895372234 / FREQUENCY_RATE), + float_to_fp8_24(65.9979859013092 / FREQUENCY_RATE), + float_to_fp8_24(66.0645161290323 / FREQUENCY_RATE), + float_to_fp8_24(66.1311806256307 / FREQUENCY_RATE), + float_to_fp8_24(66.1979797979798 / FREQUENCY_RATE), + float_to_fp8_24(66.2649140546006 / FREQUENCY_RATE), + float_to_fp8_24(66.331983805668 / FREQUENCY_RATE), + float_to_fp8_24(66.3991894630193 / FREQUENCY_RATE), + float_to_fp8_24(66.4665314401623 / FREQUENCY_RATE), + float_to_fp8_24(66.5340101522843 / FREQUENCY_RATE), + float_to_fp8_24(66.6016260162602 / FREQUENCY_RATE), + float_to_fp8_24(66.6693794506612 / FREQUENCY_RATE), + float_to_fp8_24(66.7372708757637 / FREQUENCY_RATE), + float_to_fp8_24(66.8053007135576 / FREQUENCY_RATE), + float_to_fp8_24(66.8734693877551 / FREQUENCY_RATE), + float_to_fp8_24(66.9417773237998 / FREQUENCY_RATE), + float_to_fp8_24(67.0102249488753 / FREQUENCY_RATE), + float_to_fp8_24(67.078812691914 / FREQUENCY_RATE), + float_to_fp8_24(67.1475409836066 / FREQUENCY_RATE), + float_to_fp8_24(67.2164102564103 / FREQUENCY_RATE), + float_to_fp8_24(67.2854209445585 / FREQUENCY_RATE), + float_to_fp8_24(67.3545734840699 / FREQUENCY_RATE), + float_to_fp8_24(67.4238683127572 / FREQUENCY_RATE), + float_to_fp8_24(67.4933058702369 / FREQUENCY_RATE), + float_to_fp8_24(67.5628865979382 / FREQUENCY_RATE), + float_to_fp8_24(67.6326109391125 / FREQUENCY_RATE), + float_to_fp8_24(67.702479338843 / FREQUENCY_RATE), + float_to_fp8_24(67.7724922440538 / FREQUENCY_RATE), + float_to_fp8_24(67.8426501035197 / FREQUENCY_RATE), + float_to_fp8_24(67.9129533678757 / FREQUENCY_RATE), + float_to_fp8_24(67.9834024896266 / FREQUENCY_RATE), + float_to_fp8_24(68.0539979231568 / FREQUENCY_RATE), + float_to_fp8_24(68.1247401247401 / FREQUENCY_RATE), + float_to_fp8_24(68.1956295525494 / FREQUENCY_RATE), + float_to_fp8_24(68.2666666666667 / FREQUENCY_RATE), + float_to_fp8_24(68.3378519290928 / FREQUENCY_RATE), + float_to_fp8_24(68.4091858037578 / FREQUENCY_RATE), + float_to_fp8_24(68.4806687565308 / FREQUENCY_RATE), + float_to_fp8_24(68.5523012552301 / FREQUENCY_RATE), + float_to_fp8_24(68.6240837696335 / FREQUENCY_RATE), + float_to_fp8_24(68.6960167714885 / FREQUENCY_RATE), + float_to_fp8_24(68.7681007345226 / FREQUENCY_RATE), + float_to_fp8_24(68.8403361344538 / FREQUENCY_RATE), + float_to_fp8_24(68.9127234490011 / FREQUENCY_RATE), + float_to_fp8_24(68.9852631578947 / FREQUENCY_RATE), + float_to_fp8_24(69.0579557428872 / FREQUENCY_RATE), + float_to_fp8_24(69.1308016877637 / FREQUENCY_RATE), + float_to_fp8_24(69.2038014783527 / FREQUENCY_RATE), + float_to_fp8_24(69.276955602537 / FREQUENCY_RATE), + float_to_fp8_24(69.3502645502646 / FREQUENCY_RATE), + float_to_fp8_24(69.4237288135593 / FREQUENCY_RATE), + float_to_fp8_24(69.4973488865324 / FREQUENCY_RATE), + float_to_fp8_24(69.5711252653928 / FREQUENCY_RATE), + float_to_fp8_24(69.6450584484591 / FREQUENCY_RATE), + float_to_fp8_24(69.7191489361702 / FREQUENCY_RATE), + float_to_fp8_24(69.7933972310969 / FREQUENCY_RATE), + float_to_fp8_24(69.8678038379531 / FREQUENCY_RATE), + float_to_fp8_24(69.9423692636073 / FREQUENCY_RATE), + float_to_fp8_24(70.017094017094 / FREQUENCY_RATE), + float_to_fp8_24(70.0919786096257 / FREQUENCY_RATE), + float_to_fp8_24(70.1670235546039 / FREQUENCY_RATE), + float_to_fp8_24(70.2422293676313 / FREQUENCY_RATE), + float_to_fp8_24(70.3175965665236 / FREQUENCY_RATE), + float_to_fp8_24(70.3931256713212 / FREQUENCY_RATE), + float_to_fp8_24(70.4688172043011 / FREQUENCY_RATE), + float_to_fp8_24(70.5446716899892 / FREQUENCY_RATE), + float_to_fp8_24(70.6206896551724 / FREQUENCY_RATE), + float_to_fp8_24(70.6968716289105 / FREQUENCY_RATE), + float_to_fp8_24(70.7732181425486 / FREQUENCY_RATE), + float_to_fp8_24(70.8497297297297 / FREQUENCY_RATE), + float_to_fp8_24(70.9264069264069 / FREQUENCY_RATE), + float_to_fp8_24(71.0032502708559 / FREQUENCY_RATE), + float_to_fp8_24(71.0802603036876 / FREQUENCY_RATE), + float_to_fp8_24(71.157437567861 / FREQUENCY_RATE), + float_to_fp8_24(71.2347826086957 / FREQUENCY_RATE), + float_to_fp8_24(71.3122959738847 / FREQUENCY_RATE), + float_to_fp8_24(71.3899782135076 / FREQUENCY_RATE), + float_to_fp8_24(71.4678298800436 / FREQUENCY_RATE), + float_to_fp8_24(71.5458515283843 / FREQUENCY_RATE), + float_to_fp8_24(71.624043715847 / FREQUENCY_RATE), + float_to_fp8_24(71.7024070021882 / FREQUENCY_RATE), + float_to_fp8_24(71.7809419496166 / FREQUENCY_RATE), + float_to_fp8_24(71.859649122807 / FREQUENCY_RATE), + float_to_fp8_24(71.9385290889133 / FREQUENCY_RATE), + float_to_fp8_24(72.0175824175824 / FREQUENCY_RATE), + float_to_fp8_24(72.0968096809681 / FREQUENCY_RATE), + float_to_fp8_24(72.1762114537445 / FREQUENCY_RATE), + float_to_fp8_24(72.2557883131202 / FREQUENCY_RATE), + float_to_fp8_24(72.3355408388521 / FREQUENCY_RATE), + float_to_fp8_24(72.4154696132597 / FREQUENCY_RATE), + float_to_fp8_24(72.4955752212389 / FREQUENCY_RATE), + float_to_fp8_24(72.5758582502769 / FREQUENCY_RATE), + float_to_fp8_24(72.6563192904656 / FREQUENCY_RATE), + float_to_fp8_24(72.7369589345172 / FREQUENCY_RATE), + float_to_fp8_24(72.8177777777778 / FREQUENCY_RATE), + float_to_fp8_24(72.8987764182425 / FREQUENCY_RATE), + float_to_fp8_24(72.9799554565702 / FREQUENCY_RATE), + float_to_fp8_24(73.0613154960981 / FREQUENCY_RATE), + float_to_fp8_24(73.1428571428571 / FREQUENCY_RATE), + float_to_fp8_24(73.2245810055866 / FREQUENCY_RATE), + float_to_fp8_24(73.3064876957494 / FREQUENCY_RATE), + float_to_fp8_24(73.3885778275476 / FREQUENCY_RATE), + float_to_fp8_24(73.4708520179372 / FREQUENCY_RATE), + float_to_fp8_24(73.5533108866442 / FREQUENCY_RATE), + float_to_fp8_24(73.6359550561798 / FREQUENCY_RATE), + float_to_fp8_24(73.718785151856 / FREQUENCY_RATE), + float_to_fp8_24(73.8018018018018 / FREQUENCY_RATE), + float_to_fp8_24(73.8850056369786 / FREQUENCY_RATE), + float_to_fp8_24(73.9683972911964 / FREQUENCY_RATE), + float_to_fp8_24(74.0519774011299 / FREQUENCY_RATE), + float_to_fp8_24(74.1357466063348 / FREQUENCY_RATE), + float_to_fp8_24(74.2197055492639 / FREQUENCY_RATE), + float_to_fp8_24(74.3038548752835 / FREQUENCY_RATE), + float_to_fp8_24(74.3881952326901 / FREQUENCY_RATE), + float_to_fp8_24(74.4727272727273 / FREQUENCY_RATE), + float_to_fp8_24(74.5574516496018 / FREQUENCY_RATE), + float_to_fp8_24(74.6423690205011 / FREQUENCY_RATE), + float_to_fp8_24(74.72748004561 / FREQUENCY_RATE), + float_to_fp8_24(74.8127853881279 / FREQUENCY_RATE), + float_to_fp8_24(74.8982857142857 / FREQUENCY_RATE), + float_to_fp8_24(74.9839816933639 / FREQUENCY_RATE), + float_to_fp8_24(75.0698739977091 / FREQUENCY_RATE), + float_to_fp8_24(75.1559633027523 / FREQUENCY_RATE), + float_to_fp8_24(75.2422502870264 / FREQUENCY_RATE), + float_to_fp8_24(75.3287356321839 / FREQUENCY_RATE), + float_to_fp8_24(75.415420023015 / FREQUENCY_RATE), + float_to_fp8_24(75.5023041474654 / FREQUENCY_RATE), + float_to_fp8_24(75.5893886966551 / FREQUENCY_RATE), + float_to_fp8_24(75.6766743648961 / FREQUENCY_RATE), + float_to_fp8_24(75.764161849711 / FREQUENCY_RATE), + float_to_fp8_24(75.8518518518518 / FREQUENCY_RATE), + float_to_fp8_24(75.9397450753187 / FREQUENCY_RATE), + float_to_fp8_24(76.0278422273782 / FREQUENCY_RATE), + float_to_fp8_24(76.116144018583 / FREQUENCY_RATE), + float_to_fp8_24(76.2046511627907 / FREQUENCY_RATE), + float_to_fp8_24(76.2933643771828 / FREQUENCY_RATE), + float_to_fp8_24(76.3822843822844 / FREQUENCY_RATE), + float_to_fp8_24(76.4714119019837 / FREQUENCY_RATE), + float_to_fp8_24(76.5607476635514 / FREQUENCY_RATE), + float_to_fp8_24(76.6502923976608 / FREQUENCY_RATE), + float_to_fp8_24(76.7400468384075 / FREQUENCY_RATE), + float_to_fp8_24(76.8300117233294 / FREQUENCY_RATE), + float_to_fp8_24(76.9201877934272 / FREQUENCY_RATE), + float_to_fp8_24(77.0105757931845 / FREQUENCY_RATE), + float_to_fp8_24(77.1011764705882 / FREQUENCY_RATE), + float_to_fp8_24(77.1919905771496 / FREQUENCY_RATE), + float_to_fp8_24(77.2830188679245 / FREQUENCY_RATE), + float_to_fp8_24(77.3742621015348 / FREQUENCY_RATE), + float_to_fp8_24(77.4657210401891 / FREQUENCY_RATE), + float_to_fp8_24(77.5573964497041 / FREQUENCY_RATE), + float_to_fp8_24(77.6492890995261 / FREQUENCY_RATE), + float_to_fp8_24(77.7413997627521 / FREQUENCY_RATE), + float_to_fp8_24(77.833729216152 / FREQUENCY_RATE), + float_to_fp8_24(77.9262782401902 / FREQUENCY_RATE), + float_to_fp8_24(78.0190476190476 / FREQUENCY_RATE), + float_to_fp8_24(78.1120381406436 / FREQUENCY_RATE), + float_to_fp8_24(78.2052505966587 / FREQUENCY_RATE), + float_to_fp8_24(78.2986857825568 / FREQUENCY_RATE), + float_to_fp8_24(78.3923444976077 / FREQUENCY_RATE), + float_to_fp8_24(78.4862275449102 / FREQUENCY_RATE), + float_to_fp8_24(78.5803357314149 / FREQUENCY_RATE), + float_to_fp8_24(78.6746698679472 / FREQUENCY_RATE), + float_to_fp8_24(78.7692307692308 / FREQUENCY_RATE), + float_to_fp8_24(78.864019253911 / FREQUENCY_RATE), + float_to_fp8_24(78.9590361445783 / FREQUENCY_RATE), + float_to_fp8_24(79.0542822677925 / FREQUENCY_RATE), + float_to_fp8_24(79.1497584541063 / FREQUENCY_RATE), + float_to_fp8_24(79.2454655380895 / FREQUENCY_RATE), + float_to_fp8_24(79.3414043583535 / FREQUENCY_RATE), + float_to_fp8_24(79.4375757575758 / FREQUENCY_RATE), + float_to_fp8_24(79.5339805825243 / FREQUENCY_RATE), + float_to_fp8_24(79.6306196840826 / FREQUENCY_RATE), + float_to_fp8_24(79.7274939172749 / FREQUENCY_RATE), + float_to_fp8_24(79.8246041412911 / FREQUENCY_RATE), + float_to_fp8_24(79.9219512195122 / FREQUENCY_RATE), + float_to_fp8_24(80.019536019536 / FREQUENCY_RATE), + float_to_fp8_24(80.1173594132029 / FREQUENCY_RATE), + float_to_fp8_24(80.2154222766218 / FREQUENCY_RATE), + float_to_fp8_24(80.3137254901961 / FREQUENCY_RATE), + float_to_fp8_24(80.4122699386503 / FREQUENCY_RATE), + float_to_fp8_24(80.5110565110565 / FREQUENCY_RATE), + float_to_fp8_24(80.610086100861 / FREQUENCY_RATE), + float_to_fp8_24(80.7093596059113 / FREQUENCY_RATE), + float_to_fp8_24(80.8088779284834 / FREQUENCY_RATE), + float_to_fp8_24(80.9086419753086 / FREQUENCY_RATE), + float_to_fp8_24(81.008652657602 / FREQUENCY_RATE), + float_to_fp8_24(81.1089108910891 / FREQUENCY_RATE), + float_to_fp8_24(81.2094175960347 / FREQUENCY_RATE), + float_to_fp8_24(81.3101736972705 / FREQUENCY_RATE), + float_to_fp8_24(81.4111801242236 / FREQUENCY_RATE), + float_to_fp8_24(81.5124378109453 / FREQUENCY_RATE), + float_to_fp8_24(81.6139476961395 / FREQUENCY_RATE), + float_to_fp8_24(81.715710723192 / FREQUENCY_RATE), + float_to_fp8_24(81.8177278401998 / FREQUENCY_RATE), + float_to_fp8_24(81.92 / FREQUENCY_RATE), + float_to_fp8_24(82.0225281602002 / FREQUENCY_RATE), + float_to_fp8_24(82.125313283208 / FREQUENCY_RATE), + float_to_fp8_24(82.228356336261 / FREQUENCY_RATE), + float_to_fp8_24(82.3316582914573 / FREQUENCY_RATE), + float_to_fp8_24(82.4352201257862 / FREQUENCY_RATE), + float_to_fp8_24(82.5390428211587 / FREQUENCY_RATE), + float_to_fp8_24(82.6431273644388 / FREQUENCY_RATE), + float_to_fp8_24(82.7474747474748 / FREQUENCY_RATE), + float_to_fp8_24(82.8520859671302 / FREQUENCY_RATE), + float_to_fp8_24(82.9569620253165 / FREQUENCY_RATE), + float_to_fp8_24(83.0621039290241 / FREQUENCY_RATE), + float_to_fp8_24(83.1675126903553 / FREQUENCY_RATE), + float_to_fp8_24(83.2731893265565 / FREQUENCY_RATE), + float_to_fp8_24(83.3791348600509 / FREQUENCY_RATE), + float_to_fp8_24(83.4853503184713 / FREQUENCY_RATE), + float_to_fp8_24(83.5918367346939 / FREQUENCY_RATE), + float_to_fp8_24(83.698595146871 / FREQUENCY_RATE), + float_to_fp8_24(83.8056265984655 / FREQUENCY_RATE), + float_to_fp8_24(83.9129321382842 / FREQUENCY_RATE), + float_to_fp8_24(84.0205128205128 / FREQUENCY_RATE), + float_to_fp8_24(84.1283697047497 / FREQUENCY_RATE), + float_to_fp8_24(84.2365038560411 / FREQUENCY_RATE), + float_to_fp8_24(84.3449163449163 / FREQUENCY_RATE), + float_to_fp8_24(84.4536082474227 / FREQUENCY_RATE), + float_to_fp8_24(84.5625806451613 / FREQUENCY_RATE), + float_to_fp8_24(84.671834625323 / FREQUENCY_RATE), + float_to_fp8_24(84.7813712807245 / FREQUENCY_RATE), + float_to_fp8_24(84.8911917098446 / FREQUENCY_RATE), + float_to_fp8_24(85.0012970168612 / FREQUENCY_RATE), + float_to_fp8_24(85.1116883116883 / FREQUENCY_RATE), + float_to_fp8_24(85.222366710013 / FREQUENCY_RATE), + float_to_fp8_24(85.3333333333333 / FREQUENCY_RATE), + float_to_fp8_24(85.4445893089961 / FREQUENCY_RATE), + float_to_fp8_24(85.556135770235 / FREQUENCY_RATE), + float_to_fp8_24(85.6679738562091 / FREQUENCY_RATE), + float_to_fp8_24(85.7801047120419 / FREQUENCY_RATE), + float_to_fp8_24(85.8925294888598 / FREQUENCY_RATE), + float_to_fp8_24(86.005249343832 / FREQUENCY_RATE), + float_to_fp8_24(86.1182654402102 / FREQUENCY_RATE), + float_to_fp8_24(86.2315789473684 / FREQUENCY_RATE), + float_to_fp8_24(86.3451910408432 / FREQUENCY_RATE), + float_to_fp8_24(86.4591029023747 / FREQUENCY_RATE), + float_to_fp8_24(86.5733157199472 / FREQUENCY_RATE), + float_to_fp8_24(86.6878306878307 / FREQUENCY_RATE), + float_to_fp8_24(86.8026490066225 / FREQUENCY_RATE), + float_to_fp8_24(86.9177718832891 / FREQUENCY_RATE), + float_to_fp8_24(87.0332005312085 / FREQUENCY_RATE), + float_to_fp8_24(87.1489361702128 / FREQUENCY_RATE), + float_to_fp8_24(87.2649800266312 / FREQUENCY_RATE), + float_to_fp8_24(87.3813333333333 / FREQUENCY_RATE), + float_to_fp8_24(87.497997329773 / FREQUENCY_RATE), + float_to_fp8_24(87.6149732620321 / FREQUENCY_RATE), + float_to_fp8_24(87.7322623828648 / FREQUENCY_RATE), + float_to_fp8_24(87.8498659517426 / FREQUENCY_RATE), + float_to_fp8_24(87.9677852348993 / FREQUENCY_RATE), + float_to_fp8_24(88.0860215053763 / FREQUENCY_RATE), + float_to_fp8_24(88.2045760430686 / FREQUENCY_RATE), + float_to_fp8_24(88.3234501347709 / FREQUENCY_RATE), + float_to_fp8_24(88.442645074224 / FREQUENCY_RATE), + float_to_fp8_24(88.5621621621622 / FREQUENCY_RATE), + float_to_fp8_24(88.68200270636 / FREQUENCY_RATE), + float_to_fp8_24(88.8021680216802 / FREQUENCY_RATE), + float_to_fp8_24(88.9226594301221 / FREQUENCY_RATE), + float_to_fp8_24(89.0434782608696 / FREQUENCY_RATE), + float_to_fp8_24(89.1646258503401 / FREQUENCY_RATE), + float_to_fp8_24(89.2861035422343 / FREQUENCY_RATE), + float_to_fp8_24(89.4079126875853 / FREQUENCY_RATE), + float_to_fp8_24(89.5300546448087 / FREQUENCY_RATE), + float_to_fp8_24(89.6525307797538 / FREQUENCY_RATE), + float_to_fp8_24(89.7753424657534 / FREQUENCY_RATE), + float_to_fp8_24(89.8984910836763 / FREQUENCY_RATE), + float_to_fp8_24(90.021978021978 / FREQUENCY_RATE), + float_to_fp8_24(90.1458046767538 / FREQUENCY_RATE), + float_to_fp8_24(90.2699724517906 / FREQUENCY_RATE), + float_to_fp8_24(90.3944827586207 / FREQUENCY_RATE), + float_to_fp8_24(90.5193370165746 / FREQUENCY_RATE), + float_to_fp8_24(90.6445366528354 / FREQUENCY_RATE), + float_to_fp8_24(90.7700831024931 / FREQUENCY_RATE), + float_to_fp8_24(90.8959778085992 / FREQUENCY_RATE), + float_to_fp8_24(91.0222222222222 / FREQUENCY_RATE), + float_to_fp8_24(91.1488178025035 / FREQUENCY_RATE), + float_to_fp8_24(91.2757660167131 / FREQUENCY_RATE), + float_to_fp8_24(91.4030683403068 / FREQUENCY_RATE), + float_to_fp8_24(91.5307262569833 / FREQUENCY_RATE), + float_to_fp8_24(91.6587412587413 / FREQUENCY_RATE), + float_to_fp8_24(91.7871148459384 / FREQUENCY_RATE), + float_to_fp8_24(91.9158485273492 / FREQUENCY_RATE), + float_to_fp8_24(92.0449438202247 / FREQUENCY_RATE), + float_to_fp8_24(92.1744022503516 / FREQUENCY_RATE), + float_to_fp8_24(92.3042253521127 / FREQUENCY_RATE), + float_to_fp8_24(92.4344146685472 / FREQUENCY_RATE), + float_to_fp8_24(92.5649717514124 / FREQUENCY_RATE), + float_to_fp8_24(92.6958981612447 / FREQUENCY_RATE), + float_to_fp8_24(92.8271954674221 / FREQUENCY_RATE), + float_to_fp8_24(92.958865248227 / FREQUENCY_RATE), + float_to_fp8_24(93.0909090909091 / FREQUENCY_RATE), + float_to_fp8_24(93.2233285917497 / FREQUENCY_RATE), + float_to_fp8_24(93.3561253561254 / FREQUENCY_RATE), + float_to_fp8_24(93.4893009985735 / FREQUENCY_RATE), + float_to_fp8_24(93.6228571428572 / FREQUENCY_RATE), + float_to_fp8_24(93.7567954220315 / FREQUENCY_RATE), + float_to_fp8_24(93.89111747851 / FREQUENCY_RATE), + float_to_fp8_24(94.025824964132 / FREQUENCY_RATE), + float_to_fp8_24(94.1609195402299 / FREQUENCY_RATE), + float_to_fp8_24(94.2964028776978 / FREQUENCY_RATE), + float_to_fp8_24(94.4322766570605 / FREQUENCY_RATE), + float_to_fp8_24(94.5685425685426 / FREQUENCY_RATE), + float_to_fp8_24(94.7052023121387 / FREQUENCY_RATE), + float_to_fp8_24(94.8422575976845 / FREQUENCY_RATE), + float_to_fp8_24(94.9797101449275 / FREQUENCY_RATE), + float_to_fp8_24(95.1175616835994 / FREQUENCY_RATE), + float_to_fp8_24(95.2558139534884 / FREQUENCY_RATE), + float_to_fp8_24(95.3944687045124 / FREQUENCY_RATE), + float_to_fp8_24(95.533527696793 / FREQUENCY_RATE), + float_to_fp8_24(95.6729927007299 / FREQUENCY_RATE), + float_to_fp8_24(95.812865497076 / FREQUENCY_RATE), + float_to_fp8_24(95.9531478770132 / FREQUENCY_RATE), + float_to_fp8_24(96.0938416422287 / FREQUENCY_RATE), + float_to_fp8_24(96.2349486049927 / FREQUENCY_RATE), + float_to_fp8_24(96.3764705882353 / FREQUENCY_RATE), + float_to_fp8_24(96.5184094256259 / FREQUENCY_RATE), + float_to_fp8_24(96.6607669616519 / FREQUENCY_RATE), + float_to_fp8_24(96.8035450516987 / FREQUENCY_RATE), + float_to_fp8_24(96.9467455621302 / FREQUENCY_RATE), + float_to_fp8_24(97.0903703703704 / FREQUENCY_RATE), + float_to_fp8_24(97.2344213649852 / FREQUENCY_RATE), + float_to_fp8_24(97.3789004457652 / FREQUENCY_RATE), + float_to_fp8_24(97.5238095238095 / FREQUENCY_RATE), + float_to_fp8_24(97.6691505216095 / FREQUENCY_RATE), + float_to_fp8_24(97.8149253731343 / FREQUENCY_RATE), + float_to_fp8_24(97.9611360239163 / FREQUENCY_RATE), + float_to_fp8_24(98.1077844311377 / FREQUENCY_RATE), + float_to_fp8_24(98.2548725637181 / FREQUENCY_RATE), + float_to_fp8_24(98.4024024024024 / FREQUENCY_RATE), + float_to_fp8_24(98.5503759398496 / FREQUENCY_RATE), + float_to_fp8_24(98.6987951807229 / FREQUENCY_RATE), + float_to_fp8_24(98.8476621417798 / FREQUENCY_RATE), + float_to_fp8_24(98.9969788519637 / FREQUENCY_RATE), + float_to_fp8_24(99.1467473524962 / FREQUENCY_RATE), + float_to_fp8_24(99.2969696969697 / FREQUENCY_RATE), + float_to_fp8_24(99.4476479514416 / FREQUENCY_RATE), + float_to_fp8_24(99.5987841945289 / FREQUENCY_RATE), + float_to_fp8_24(99.7503805175038 / FREQUENCY_RATE), + float_to_fp8_24(99.9024390243902 / FREQUENCY_RATE), + float_to_fp8_24(100.054961832061 / FREQUENCY_RATE), + float_to_fp8_24(100.207951070336 / FREQUENCY_RATE), + float_to_fp8_24(100.361408882083 / FREQUENCY_RATE), + float_to_fp8_24(100.515337423313 / FREQUENCY_RATE), + float_to_fp8_24(100.669738863287 / FREQUENCY_RATE), + float_to_fp8_24(100.824615384615 / FREQUENCY_RATE), + float_to_fp8_24(100.979969183359 / FREQUENCY_RATE), + float_to_fp8_24(101.135802469136 / FREQUENCY_RATE), + float_to_fp8_24(101.292117465224 / FREQUENCY_RATE), + float_to_fp8_24(101.448916408669 / FREQUENCY_RATE), + float_to_fp8_24(101.606201550388 / FREQUENCY_RATE), + float_to_fp8_24(101.76397515528 / FREQUENCY_RATE), + float_to_fp8_24(101.922239502333 / FREQUENCY_RATE), + float_to_fp8_24(102.080996884735 / FREQUENCY_RATE), + float_to_fp8_24(102.240249609984 / FREQUENCY_RATE), + float_to_fp8_24(102.4 / FREQUENCY_RATE), + float_to_fp8_24(102.560250391236 / FREQUENCY_RATE), + float_to_fp8_24(102.721003134796 / FREQUENCY_RATE), + float_to_fp8_24(102.882260596546 / FREQUENCY_RATE), + float_to_fp8_24(103.044025157233 / FREQUENCY_RATE), + float_to_fp8_24(103.206299212598 / FREQUENCY_RATE), + float_to_fp8_24(103.369085173502 / FREQUENCY_RATE), + float_to_fp8_24(103.532385466035 / FREQUENCY_RATE), + float_to_fp8_24(103.696202531646 / FREQUENCY_RATE), + float_to_fp8_24(103.860538827258 / FREQUENCY_RATE), + float_to_fp8_24(104.025396825397 / FREQUENCY_RATE), + float_to_fp8_24(104.190779014308 / FREQUENCY_RATE), + float_to_fp8_24(104.356687898089 / FREQUENCY_RATE), + float_to_fp8_24(104.52312599681 / FREQUENCY_RATE), + float_to_fp8_24(104.690095846645 / FREQUENCY_RATE), + float_to_fp8_24(104.8576 / FREQUENCY_RATE), + float_to_fp8_24(105.025641025641 / FREQUENCY_RATE), + float_to_fp8_24(105.194221508828 / FREQUENCY_RATE), + float_to_fp8_24(105.363344051447 / FREQUENCY_RATE), + float_to_fp8_24(105.533011272142 / FREQUENCY_RATE), + float_to_fp8_24(105.703225806452 / FREQUENCY_RATE), + float_to_fp8_24(105.873990306947 / FREQUENCY_RATE), + float_to_fp8_24(106.045307443366 / FREQUENCY_RATE), + float_to_fp8_24(106.217179902755 / FREQUENCY_RATE), + float_to_fp8_24(106.38961038961 / FREQUENCY_RATE), + float_to_fp8_24(106.562601626016 / FREQUENCY_RATE), + float_to_fp8_24(106.736156351792 / FREQUENCY_RATE), + float_to_fp8_24(106.910277324633 / FREQUENCY_RATE), + float_to_fp8_24(107.084967320261 / FREQUENCY_RATE), + float_to_fp8_24(107.26022913257 / FREQUENCY_RATE), + float_to_fp8_24(107.436065573771 / FREQUENCY_RATE), + float_to_fp8_24(107.612479474548 / FREQUENCY_RATE), + float_to_fp8_24(107.789473684211 / FREQUENCY_RATE), + float_to_fp8_24(107.96705107084 / FREQUENCY_RATE), + float_to_fp8_24(108.145214521452 / FREQUENCY_RATE), + float_to_fp8_24(108.323966942149 / FREQUENCY_RATE), + float_to_fp8_24(108.503311258278 / FREQUENCY_RATE), + float_to_fp8_24(108.683250414594 / FREQUENCY_RATE), + float_to_fp8_24(108.863787375415 / FREQUENCY_RATE), + float_to_fp8_24(109.044925124792 / FREQUENCY_RATE), + float_to_fp8_24(109.226666666667 / FREQUENCY_RATE), + float_to_fp8_24(109.409015025042 / FREQUENCY_RATE), + float_to_fp8_24(109.591973244147 / FREQUENCY_RATE), + float_to_fp8_24(109.77554438861 / FREQUENCY_RATE), + float_to_fp8_24(109.959731543624 / FREQUENCY_RATE), + float_to_fp8_24(110.144537815126 / FREQUENCY_RATE), + float_to_fp8_24(110.329966329966 / FREQUENCY_RATE), + float_to_fp8_24(110.516020236088 / FREQUENCY_RATE), + float_to_fp8_24(110.702702702703 / FREQUENCY_RATE), + float_to_fp8_24(110.890016920474 / FREQUENCY_RATE), + float_to_fp8_24(111.077966101695 / FREQUENCY_RATE), + float_to_fp8_24(111.266553480475 / FREQUENCY_RATE), + float_to_fp8_24(111.455782312925 / FREQUENCY_RATE), + float_to_fp8_24(111.645655877342 / FREQUENCY_RATE), + float_to_fp8_24(111.836177474403 / FREQUENCY_RATE), + float_to_fp8_24(112.02735042735 / FREQUENCY_RATE), + float_to_fp8_24(112.219178082192 / FREQUENCY_RATE), + float_to_fp8_24(112.41166380789 / FREQUENCY_RATE), + float_to_fp8_24(112.604810996564 / FREQUENCY_RATE), + float_to_fp8_24(112.798623063683 / FREQUENCY_RATE), + float_to_fp8_24(112.993103448276 / FREQUENCY_RATE), + float_to_fp8_24(113.188255613126 / FREQUENCY_RATE), + float_to_fp8_24(113.384083044983 / FREQUENCY_RATE), + float_to_fp8_24(113.580589254766 / FREQUENCY_RATE), + float_to_fp8_24(113.777777777778 / FREQUENCY_RATE), + float_to_fp8_24(113.975652173913 / FREQUENCY_RATE), + float_to_fp8_24(114.174216027875 / FREQUENCY_RATE), + float_to_fp8_24(114.373472949389 / FREQUENCY_RATE), + float_to_fp8_24(114.573426573427 / FREQUENCY_RATE), + float_to_fp8_24(114.77408056042 / FREQUENCY_RATE), + float_to_fp8_24(114.975438596491 / FREQUENCY_RATE), + float_to_fp8_24(115.177504393673 / FREQUENCY_RATE), + float_to_fp8_24(115.380281690141 / FREQUENCY_RATE), + float_to_fp8_24(115.583774250441 / FREQUENCY_RATE), + float_to_fp8_24(115.787985865724 / FREQUENCY_RATE), + float_to_fp8_24(115.992920353982 / FREQUENCY_RATE), + float_to_fp8_24(116.198581560284 / FREQUENCY_RATE), + float_to_fp8_24(116.404973357016 / FREQUENCY_RATE), + float_to_fp8_24(116.612099644128 / FREQUENCY_RATE), + float_to_fp8_24(116.819964349376 / FREQUENCY_RATE), + float_to_fp8_24(117.028571428571 / FREQUENCY_RATE), + float_to_fp8_24(117.237924865832 / FREQUENCY_RATE), + float_to_fp8_24(117.448028673835 / FREQUENCY_RATE), + float_to_fp8_24(117.658886894075 / FREQUENCY_RATE), + float_to_fp8_24(117.870503597122 / FREQUENCY_RATE), + float_to_fp8_24(118.082882882883 / FREQUENCY_RATE), + float_to_fp8_24(118.296028880866 / FREQUENCY_RATE), + float_to_fp8_24(118.509945750452 / FREQUENCY_RATE), + float_to_fp8_24(118.724637681159 / FREQUENCY_RATE), + float_to_fp8_24(118.940108892922 / FREQUENCY_RATE), + float_to_fp8_24(119.156363636364 / FREQUENCY_RATE), + float_to_fp8_24(119.373406193078 / FREQUENCY_RATE), + float_to_fp8_24(119.591240875912 / FREQUENCY_RATE), + float_to_fp8_24(119.80987202925 / FREQUENCY_RATE), + float_to_fp8_24(120.029304029304 / FREQUENCY_RATE), + float_to_fp8_24(120.249541284404 / FREQUENCY_RATE), + float_to_fp8_24(120.470588235294 / FREQUENCY_RATE), + float_to_fp8_24(120.692449355433 / FREQUENCY_RATE), + float_to_fp8_24(120.915129151292 / FREQUENCY_RATE), + float_to_fp8_24(121.138632162662 / FREQUENCY_RATE), + float_to_fp8_24(121.362962962963 / FREQUENCY_RATE), + float_to_fp8_24(121.588126159555 / FREQUENCY_RATE), + float_to_fp8_24(121.814126394052 / FREQUENCY_RATE), + float_to_fp8_24(122.040968342644 / FREQUENCY_RATE), + float_to_fp8_24(122.268656716418 / FREQUENCY_RATE), + float_to_fp8_24(122.497196261682 / FREQUENCY_RATE), + float_to_fp8_24(122.7265917603 / FREQUENCY_RATE), + float_to_fp8_24(122.956848030019 / FREQUENCY_RATE), + float_to_fp8_24(123.187969924812 / FREQUENCY_RATE), + float_to_fp8_24(123.419962335217 / FREQUENCY_RATE), + float_to_fp8_24(123.652830188679 / FREQUENCY_RATE), + float_to_fp8_24(123.886578449906 / FREQUENCY_RATE), + float_to_fp8_24(124.121212121212 / FREQUENCY_RATE), + float_to_fp8_24(124.356736242884 / FREQUENCY_RATE), + float_to_fp8_24(124.593155893536 / FREQUENCY_RATE), + float_to_fp8_24(124.830476190476 / FREQUENCY_RATE), + float_to_fp8_24(125.068702290076 / FREQUENCY_RATE), + float_to_fp8_24(125.307839388145 / FREQUENCY_RATE), + float_to_fp8_24(125.547892720307 / FREQUENCY_RATE), + float_to_fp8_24(125.78886756238 / FREQUENCY_RATE), + float_to_fp8_24(126.030769230769 / FREQUENCY_RATE), + float_to_fp8_24(126.273603082852 / FREQUENCY_RATE), + float_to_fp8_24(126.517374517375 / FREQUENCY_RATE), + float_to_fp8_24(126.762088974855 / FREQUENCY_RATE), + float_to_fp8_24(127.007751937985 / FREQUENCY_RATE), + float_to_fp8_24(127.254368932039 / FREQUENCY_RATE), + float_to_fp8_24(127.501945525292 / FREQUENCY_RATE), + float_to_fp8_24(127.750487329435 / FREQUENCY_RATE), + float_to_fp8_24(128 / FREQUENCY_RATE), + float_to_fp8_24(128.250489236791 / FREQUENCY_RATE), + float_to_fp8_24(128.501960784314 / FREQUENCY_RATE), + float_to_fp8_24(128.75442043222 / FREQUENCY_RATE), + float_to_fp8_24(129.007874015748 / FREQUENCY_RATE), + float_to_fp8_24(129.262327416174 / FREQUENCY_RATE), + float_to_fp8_24(129.517786561265 / FREQUENCY_RATE), + float_to_fp8_24(129.774257425743 / FREQUENCY_RATE), + float_to_fp8_24(130.031746031746 / FREQUENCY_RATE), + float_to_fp8_24(130.290258449304 / FREQUENCY_RATE), + float_to_fp8_24(130.549800796813 / FREQUENCY_RATE), + float_to_fp8_24(130.810379241517 / FREQUENCY_RATE), + float_to_fp8_24(131.072 / FREQUENCY_RATE), + float_to_fp8_24(131.334669338677 / FREQUENCY_RATE), + float_to_fp8_24(131.598393574297 / FREQUENCY_RATE), + float_to_fp8_24(131.863179074447 / FREQUENCY_RATE), + float_to_fp8_24(132.129032258065 / FREQUENCY_RATE), + float_to_fp8_24(132.39595959596 / FREQUENCY_RATE), + float_to_fp8_24(132.663967611336 / FREQUENCY_RATE), + float_to_fp8_24(132.933062880325 / FREQUENCY_RATE), + float_to_fp8_24(133.20325203252 / FREQUENCY_RATE), + float_to_fp8_24(133.474541751527 / FREQUENCY_RATE), + float_to_fp8_24(133.74693877551 / FREQUENCY_RATE), + float_to_fp8_24(134.020449897751 / FREQUENCY_RATE), + float_to_fp8_24(134.295081967213 / FREQUENCY_RATE), + float_to_fp8_24(134.570841889117 / FREQUENCY_RATE), + float_to_fp8_24(134.847736625514 / FREQUENCY_RATE), + float_to_fp8_24(135.125773195876 / FREQUENCY_RATE), + float_to_fp8_24(135.404958677686 / FREQUENCY_RATE), + float_to_fp8_24(135.685300207039 / FREQUENCY_RATE), + float_to_fp8_24(135.966804979253 / FREQUENCY_RATE), + float_to_fp8_24(136.24948024948 / FREQUENCY_RATE), + float_to_fp8_24(136.533333333333 / FREQUENCY_RATE), + float_to_fp8_24(136.818371607516 / FREQUENCY_RATE), + float_to_fp8_24(137.10460251046 / FREQUENCY_RATE), + float_to_fp8_24(137.392033542977 / FREQUENCY_RATE), + float_to_fp8_24(137.680672268908 / FREQUENCY_RATE), + float_to_fp8_24(137.970526315789 / FREQUENCY_RATE), + float_to_fp8_24(138.261603375527 / FREQUENCY_RATE), + float_to_fp8_24(138.553911205074 / FREQUENCY_RATE), + float_to_fp8_24(138.847457627119 / FREQUENCY_RATE), + float_to_fp8_24(139.142250530786 / FREQUENCY_RATE), + float_to_fp8_24(139.43829787234 / FREQUENCY_RATE), + float_to_fp8_24(139.735607675906 / FREQUENCY_RATE), + float_to_fp8_24(140.034188034188 / FREQUENCY_RATE), + float_to_fp8_24(140.334047109208 / FREQUENCY_RATE), + float_to_fp8_24(140.635193133047 / FREQUENCY_RATE), + float_to_fp8_24(140.937634408602 / FREQUENCY_RATE), + float_to_fp8_24(141.241379310345 / FREQUENCY_RATE), + float_to_fp8_24(141.546436285097 / FREQUENCY_RATE), + float_to_fp8_24(141.852813852814 / FREQUENCY_RATE), + float_to_fp8_24(142.160520607375 / FREQUENCY_RATE), + float_to_fp8_24(142.469565217391 / FREQUENCY_RATE), + float_to_fp8_24(142.779956427015 / FREQUENCY_RATE), + float_to_fp8_24(143.091703056769 / FREQUENCY_RATE), + float_to_fp8_24(143.404814004376 / FREQUENCY_RATE), + float_to_fp8_24(143.719298245614 / FREQUENCY_RATE), + float_to_fp8_24(144.035164835165 / FREQUENCY_RATE), + float_to_fp8_24(144.352422907489 / FREQUENCY_RATE), + float_to_fp8_24(144.671081677704 / FREQUENCY_RATE), + float_to_fp8_24(144.991150442478 / FREQUENCY_RATE), + float_to_fp8_24(145.312638580931 / FREQUENCY_RATE), + float_to_fp8_24(145.635555555556 / FREQUENCY_RATE), + float_to_fp8_24(145.95991091314 / FREQUENCY_RATE), + float_to_fp8_24(146.285714285714 / FREQUENCY_RATE), + float_to_fp8_24(146.612975391499 / FREQUENCY_RATE), + float_to_fp8_24(146.941704035874 / FREQUENCY_RATE), + float_to_fp8_24(147.27191011236 / FREQUENCY_RATE), + float_to_fp8_24(147.603603603604 / FREQUENCY_RATE), + float_to_fp8_24(147.936794582393 / FREQUENCY_RATE), + float_to_fp8_24(148.27149321267 / FREQUENCY_RATE), + float_to_fp8_24(148.607709750567 / FREQUENCY_RATE), + float_to_fp8_24(148.945454545455 / FREQUENCY_RATE), + float_to_fp8_24(149.284738041002 / FREQUENCY_RATE), + float_to_fp8_24(149.625570776256 / FREQUENCY_RATE), + float_to_fp8_24(149.967963386728 / FREQUENCY_RATE), + float_to_fp8_24(150.311926605505 / FREQUENCY_RATE), + float_to_fp8_24(150.657471264368 / FREQUENCY_RATE), + float_to_fp8_24(151.004608294931 / FREQUENCY_RATE), + float_to_fp8_24(151.353348729792 / FREQUENCY_RATE), + float_to_fp8_24(151.703703703704 / FREQUENCY_RATE), + float_to_fp8_24(152.055684454756 / FREQUENCY_RATE), + float_to_fp8_24(152.409302325581 / FREQUENCY_RATE), + float_to_fp8_24(152.764568764569 / FREQUENCY_RATE), + float_to_fp8_24(153.121495327103 / FREQUENCY_RATE), + float_to_fp8_24(153.480093676815 / FREQUENCY_RATE), + float_to_fp8_24(153.840375586854 / FREQUENCY_RATE), + float_to_fp8_24(154.202352941176 / FREQUENCY_RATE), + float_to_fp8_24(154.566037735849 / FREQUENCY_RATE), + float_to_fp8_24(154.931442080378 / FREQUENCY_RATE), + float_to_fp8_24(155.298578199052 / FREQUENCY_RATE), + float_to_fp8_24(155.667458432304 / FREQUENCY_RATE), + float_to_fp8_24(156.038095238095 / FREQUENCY_RATE), + float_to_fp8_24(156.410501193317 / FREQUENCY_RATE), + float_to_fp8_24(156.784688995215 / FREQUENCY_RATE), + float_to_fp8_24(157.16067146283 / FREQUENCY_RATE), + float_to_fp8_24(157.538461538462 / FREQUENCY_RATE), + float_to_fp8_24(157.918072289157 / FREQUENCY_RATE), + float_to_fp8_24(158.299516908213 / FREQUENCY_RATE), + float_to_fp8_24(158.682808716707 / FREQUENCY_RATE), + float_to_fp8_24(159.067961165049 / FREQUENCY_RATE), + float_to_fp8_24(159.45498783455 / FREQUENCY_RATE), + float_to_fp8_24(159.843902439024 / FREQUENCY_RATE), + float_to_fp8_24(160.234718826406 / FREQUENCY_RATE), + float_to_fp8_24(160.627450980392 / FREQUENCY_RATE), + float_to_fp8_24(161.022113022113 / FREQUENCY_RATE), + float_to_fp8_24(161.418719211823 / FREQUENCY_RATE), + float_to_fp8_24(161.817283950617 / FREQUENCY_RATE), + float_to_fp8_24(162.217821782178 / FREQUENCY_RATE), + float_to_fp8_24(162.620347394541 / FREQUENCY_RATE), + float_to_fp8_24(163.024875621891 / FREQUENCY_RATE), + float_to_fp8_24(163.431421446384 / FREQUENCY_RATE), + float_to_fp8_24(163.84 / FREQUENCY_RATE), + float_to_fp8_24(164.250626566416 / FREQUENCY_RATE), + float_to_fp8_24(164.663316582915 / FREQUENCY_RATE), + float_to_fp8_24(165.078085642317 / FREQUENCY_RATE), + float_to_fp8_24(165.49494949495 / FREQUENCY_RATE), + float_to_fp8_24(165.913924050633 / FREQUENCY_RATE), + float_to_fp8_24(166.335025380711 / FREQUENCY_RATE), + float_to_fp8_24(166.758269720102 / FREQUENCY_RATE), + float_to_fp8_24(167.183673469388 / FREQUENCY_RATE), + float_to_fp8_24(167.611253196931 / FREQUENCY_RATE), + float_to_fp8_24(168.041025641026 / FREQUENCY_RATE), + float_to_fp8_24(168.473007712082 / FREQUENCY_RATE), + float_to_fp8_24(168.907216494845 / FREQUENCY_RATE), + float_to_fp8_24(169.343669250646 / FREQUENCY_RATE), + float_to_fp8_24(169.782383419689 / FREQUENCY_RATE), + float_to_fp8_24(170.223376623377 / FREQUENCY_RATE), + float_to_fp8_24(170.666666666667 / FREQUENCY_RATE), + float_to_fp8_24(171.11227154047 / FREQUENCY_RATE), + float_to_fp8_24(171.560209424084 / FREQUENCY_RATE), + float_to_fp8_24(172.010498687664 / FREQUENCY_RATE), + float_to_fp8_24(172.463157894737 / FREQUENCY_RATE), + float_to_fp8_24(172.918205804749 / FREQUENCY_RATE), + float_to_fp8_24(173.375661375661 / FREQUENCY_RATE), + float_to_fp8_24(173.835543766578 / FREQUENCY_RATE), + float_to_fp8_24(174.297872340426 / FREQUENCY_RATE), + float_to_fp8_24(174.762666666667 / FREQUENCY_RATE), + float_to_fp8_24(175.229946524064 / FREQUENCY_RATE), + float_to_fp8_24(175.699731903485 / FREQUENCY_RATE), + float_to_fp8_24(176.172043010753 / FREQUENCY_RATE), + float_to_fp8_24(176.646900269542 / FREQUENCY_RATE), + float_to_fp8_24(177.124324324324 / FREQUENCY_RATE), + float_to_fp8_24(177.60433604336 / FREQUENCY_RATE), + float_to_fp8_24(178.086956521739 / FREQUENCY_RATE), + float_to_fp8_24(178.572207084469 / FREQUENCY_RATE), + float_to_fp8_24(179.060109289618 / FREQUENCY_RATE), + float_to_fp8_24(179.550684931507 / FREQUENCY_RATE), + float_to_fp8_24(180.043956043956 / FREQUENCY_RATE), + float_to_fp8_24(180.539944903581 / FREQUENCY_RATE), + float_to_fp8_24(181.038674033149 / FREQUENCY_RATE), + float_to_fp8_24(181.540166204986 / FREQUENCY_RATE), + float_to_fp8_24(182.044444444444 / FREQUENCY_RATE), + float_to_fp8_24(182.551532033426 / FREQUENCY_RATE), + float_to_fp8_24(183.061452513966 / FREQUENCY_RATE), + float_to_fp8_24(183.574229691877 / FREQUENCY_RATE), + float_to_fp8_24(184.089887640449 / FREQUENCY_RATE), + float_to_fp8_24(184.608450704225 / FREQUENCY_RATE), + float_to_fp8_24(185.129943502825 / FREQUENCY_RATE), + float_to_fp8_24(185.654390934844 / FREQUENCY_RATE), + float_to_fp8_24(186.181818181818 / FREQUENCY_RATE), + float_to_fp8_24(186.712250712251 / FREQUENCY_RATE), + float_to_fp8_24(187.245714285714 / FREQUENCY_RATE), + float_to_fp8_24(187.78223495702 / FREQUENCY_RATE), + float_to_fp8_24(188.32183908046 / FREQUENCY_RATE), + float_to_fp8_24(188.864553314121 / FREQUENCY_RATE), + float_to_fp8_24(189.410404624277 / FREQUENCY_RATE), + float_to_fp8_24(189.959420289855 / FREQUENCY_RATE), + float_to_fp8_24(190.511627906977 / FREQUENCY_RATE), + float_to_fp8_24(191.067055393586 / FREQUENCY_RATE), + float_to_fp8_24(191.625730994152 / FREQUENCY_RATE), + float_to_fp8_24(192.187683284457 / FREQUENCY_RATE), + float_to_fp8_24(192.752941176471 / FREQUENCY_RATE), + float_to_fp8_24(193.321533923304 / FREQUENCY_RATE), + float_to_fp8_24(193.89349112426 / FREQUENCY_RATE), + float_to_fp8_24(194.46884272997 / FREQUENCY_RATE), + float_to_fp8_24(195.047619047619 / FREQUENCY_RATE), + float_to_fp8_24(195.629850746269 / FREQUENCY_RATE), + float_to_fp8_24(196.215568862275 / FREQUENCY_RATE), + float_to_fp8_24(196.804804804805 / FREQUENCY_RATE), + float_to_fp8_24(197.397590361446 / FREQUENCY_RATE), + float_to_fp8_24(197.993957703928 / FREQUENCY_RATE), + float_to_fp8_24(198.593939393939 / FREQUENCY_RATE), + float_to_fp8_24(199.197568389058 / FREQUENCY_RATE), + float_to_fp8_24(199.804878048781 / FREQUENCY_RATE), + float_to_fp8_24(200.415902140673 / FREQUENCY_RATE), + float_to_fp8_24(201.030674846626 / FREQUENCY_RATE), + float_to_fp8_24(201.649230769231 / FREQUENCY_RATE), + float_to_fp8_24(202.271604938272 / FREQUENCY_RATE), + float_to_fp8_24(202.897832817337 / FREQUENCY_RATE), + float_to_fp8_24(203.527950310559 / FREQUENCY_RATE), + float_to_fp8_24(204.16199376947 / FREQUENCY_RATE), + float_to_fp8_24(204.8 / FREQUENCY_RATE), + float_to_fp8_24(205.442006269592 / FREQUENCY_RATE), + float_to_fp8_24(206.088050314465 / FREQUENCY_RATE), + float_to_fp8_24(206.738170347003 / FREQUENCY_RATE), + float_to_fp8_24(207.392405063291 / FREQUENCY_RATE), + float_to_fp8_24(208.050793650794 / FREQUENCY_RATE), + float_to_fp8_24(208.713375796178 / FREQUENCY_RATE), + float_to_fp8_24(209.380191693291 / FREQUENCY_RATE), + float_to_fp8_24(210.051282051282 / FREQUENCY_RATE), + float_to_fp8_24(210.726688102894 / FREQUENCY_RATE), + float_to_fp8_24(211.406451612903 / FREQUENCY_RATE), + float_to_fp8_24(212.090614886731 / FREQUENCY_RATE), + float_to_fp8_24(212.779220779221 / FREQUENCY_RATE), + float_to_fp8_24(213.472312703583 / FREQUENCY_RATE), + float_to_fp8_24(214.169934640523 / FREQUENCY_RATE), + float_to_fp8_24(214.872131147541 / FREQUENCY_RATE), + float_to_fp8_24(215.578947368421 / FREQUENCY_RATE), + float_to_fp8_24(216.290429042904 / FREQUENCY_RATE), + float_to_fp8_24(217.006622516556 / FREQUENCY_RATE), + float_to_fp8_24(217.727574750831 / FREQUENCY_RATE), + float_to_fp8_24(218.453333333333 / FREQUENCY_RATE), + float_to_fp8_24(219.183946488294 / FREQUENCY_RATE), + float_to_fp8_24(219.919463087248 / FREQUENCY_RATE), + float_to_fp8_24(220.659932659933 / FREQUENCY_RATE), + float_to_fp8_24(221.405405405405 / FREQUENCY_RATE), + float_to_fp8_24(222.15593220339 / FREQUENCY_RATE), + float_to_fp8_24(222.91156462585 / FREQUENCY_RATE), + float_to_fp8_24(223.672354948805 / FREQUENCY_RATE), + float_to_fp8_24(224.438356164384 / FREQUENCY_RATE), + float_to_fp8_24(225.209621993127 / FREQUENCY_RATE), + float_to_fp8_24(225.986206896552 / FREQUENCY_RATE), + float_to_fp8_24(226.768166089965 / FREQUENCY_RATE), + float_to_fp8_24(227.555555555556 / FREQUENCY_RATE), + float_to_fp8_24(228.348432055749 / FREQUENCY_RATE), + float_to_fp8_24(229.146853146853 / FREQUENCY_RATE), + float_to_fp8_24(229.950877192982 / FREQUENCY_RATE), + float_to_fp8_24(230.760563380282 / FREQUENCY_RATE), + float_to_fp8_24(231.575971731449 / FREQUENCY_RATE), + float_to_fp8_24(232.397163120567 / FREQUENCY_RATE), + float_to_fp8_24(233.224199288256 / FREQUENCY_RATE), + float_to_fp8_24(234.057142857143 / FREQUENCY_RATE), + float_to_fp8_24(234.89605734767 / FREQUENCY_RATE), + float_to_fp8_24(235.741007194245 / FREQUENCY_RATE), + float_to_fp8_24(236.592057761733 / FREQUENCY_RATE), + float_to_fp8_24(237.449275362319 / FREQUENCY_RATE), + float_to_fp8_24(238.312727272727 / FREQUENCY_RATE), + float_to_fp8_24(239.182481751825 / FREQUENCY_RATE), + float_to_fp8_24(240.058608058608 / FREQUENCY_RATE), + float_to_fp8_24(240.941176470588 / FREQUENCY_RATE), + float_to_fp8_24(241.830258302583 / FREQUENCY_RATE), + float_to_fp8_24(242.725925925926 / FREQUENCY_RATE), + float_to_fp8_24(243.628252788104 / FREQUENCY_RATE), + float_to_fp8_24(244.537313432836 / FREQUENCY_RATE), + float_to_fp8_24(245.453183520599 / FREQUENCY_RATE), + float_to_fp8_24(246.375939849624 / FREQUENCY_RATE), + float_to_fp8_24(247.305660377358 / FREQUENCY_RATE), + float_to_fp8_24(248.242424242424 / FREQUENCY_RATE), + float_to_fp8_24(249.186311787072 / FREQUENCY_RATE), + float_to_fp8_24(250.137404580153 / FREQUENCY_RATE), + float_to_fp8_24(251.095785440613 / FREQUENCY_RATE), + float_to_fp8_24(252.061538461538 / FREQUENCY_RATE), + float_to_fp8_24(253.034749034749 / FREQUENCY_RATE), + float_to_fp8_24(254.015503875969 / FREQUENCY_RATE), + float_to_fp8_24(255.003891050584 / FREQUENCY_RATE), + float_to_fp8_24(256 / FREQUENCY_RATE), + float_to_fp8_24(257.003921568627 / FREQUENCY_RATE), + float_to_fp8_24(258.015748031496 / FREQUENCY_RATE), + float_to_fp8_24(259.03557312253 / FREQUENCY_RATE), + float_to_fp8_24(260.063492063492 / FREQUENCY_RATE), + float_to_fp8_24(261.099601593625 / FREQUENCY_RATE), + float_to_fp8_24(262.144 / FREQUENCY_RATE), + float_to_fp8_24(263.196787148594 / FREQUENCY_RATE), + float_to_fp8_24(264.258064516129 / FREQUENCY_RATE), + float_to_fp8_24(265.327935222672 / FREQUENCY_RATE), + float_to_fp8_24(266.406504065041 / FREQUENCY_RATE), + float_to_fp8_24(267.49387755102 / FREQUENCY_RATE), + float_to_fp8_24(268.590163934426 / FREQUENCY_RATE), + float_to_fp8_24(269.695473251029 / FREQUENCY_RATE), + float_to_fp8_24(270.809917355372 / FREQUENCY_RATE), + float_to_fp8_24(271.933609958506 / FREQUENCY_RATE), + float_to_fp8_24(273.066666666667 / FREQUENCY_RATE), + float_to_fp8_24(274.20920502092 / FREQUENCY_RATE), + float_to_fp8_24(275.361344537815 / FREQUENCY_RATE), + float_to_fp8_24(276.523206751055 / FREQUENCY_RATE), + float_to_fp8_24(277.694915254237 / FREQUENCY_RATE), + float_to_fp8_24(278.876595744681 / FREQUENCY_RATE), + float_to_fp8_24(280.068376068376 / FREQUENCY_RATE), + float_to_fp8_24(281.270386266094 / FREQUENCY_RATE), + float_to_fp8_24(282.48275862069 / FREQUENCY_RATE), + float_to_fp8_24(283.705627705628 / FREQUENCY_RATE), + float_to_fp8_24(284.939130434783 / FREQUENCY_RATE), + float_to_fp8_24(286.183406113537 / FREQUENCY_RATE), + float_to_fp8_24(287.438596491228 / FREQUENCY_RATE), + float_to_fp8_24(288.704845814978 / FREQUENCY_RATE), + float_to_fp8_24(289.982300884956 / FREQUENCY_RATE), + float_to_fp8_24(291.271111111111 / FREQUENCY_RATE), + float_to_fp8_24(292.571428571429 / FREQUENCY_RATE), + float_to_fp8_24(293.883408071749 / FREQUENCY_RATE), + float_to_fp8_24(295.207207207207 / FREQUENCY_RATE), + float_to_fp8_24(296.542986425339 / FREQUENCY_RATE), + float_to_fp8_24(297.890909090909 / FREQUENCY_RATE), + float_to_fp8_24(299.251141552511 / FREQUENCY_RATE), + float_to_fp8_24(300.623853211009 / FREQUENCY_RATE), + float_to_fp8_24(302.009216589862 / FREQUENCY_RATE), + float_to_fp8_24(303.407407407407 / FREQUENCY_RATE), + float_to_fp8_24(304.818604651163 / FREQUENCY_RATE), + float_to_fp8_24(306.242990654206 / FREQUENCY_RATE), + float_to_fp8_24(307.680751173709 / FREQUENCY_RATE), + float_to_fp8_24(309.132075471698 / FREQUENCY_RATE), + float_to_fp8_24(310.597156398104 / FREQUENCY_RATE), + float_to_fp8_24(312.07619047619 / FREQUENCY_RATE), + float_to_fp8_24(313.569377990431 / FREQUENCY_RATE), + float_to_fp8_24(315.076923076923 / FREQUENCY_RATE), + float_to_fp8_24(316.599033816425 / FREQUENCY_RATE), + float_to_fp8_24(318.135922330097 / FREQUENCY_RATE), + float_to_fp8_24(319.687804878049 / FREQUENCY_RATE), + float_to_fp8_24(321.254901960784 / FREQUENCY_RATE), + float_to_fp8_24(322.837438423645 / FREQUENCY_RATE), + float_to_fp8_24(324.435643564356 / FREQUENCY_RATE), + float_to_fp8_24(326.049751243781 / FREQUENCY_RATE), + float_to_fp8_24(327.68 / FREQUENCY_RATE), + float_to_fp8_24(329.326633165829 / FREQUENCY_RATE), + float_to_fp8_24(330.989898989899 / FREQUENCY_RATE), + float_to_fp8_24(332.670050761421 / FREQUENCY_RATE), + float_to_fp8_24(334.367346938775 / FREQUENCY_RATE), + float_to_fp8_24(336.082051282051 / FREQUENCY_RATE), + float_to_fp8_24(337.814432989691 / FREQUENCY_RATE), + float_to_fp8_24(339.564766839378 / FREQUENCY_RATE), + float_to_fp8_24(341.333333333333 / FREQUENCY_RATE), + float_to_fp8_24(343.120418848168 / FREQUENCY_RATE), + float_to_fp8_24(344.926315789474 / FREQUENCY_RATE), + float_to_fp8_24(346.751322751323 / FREQUENCY_RATE), + float_to_fp8_24(348.595744680851 / FREQUENCY_RATE), + float_to_fp8_24(350.459893048128 / FREQUENCY_RATE), + float_to_fp8_24(352.344086021505 / FREQUENCY_RATE), + float_to_fp8_24(354.248648648649 / FREQUENCY_RATE), + float_to_fp8_24(356.173913043478 / FREQUENCY_RATE), + float_to_fp8_24(358.120218579235 / FREQUENCY_RATE), + float_to_fp8_24(360.087912087912 / FREQUENCY_RATE), + float_to_fp8_24(362.077348066298 / FREQUENCY_RATE), + float_to_fp8_24(364.088888888889 / FREQUENCY_RATE), + float_to_fp8_24(366.122905027933 / FREQUENCY_RATE), + float_to_fp8_24(368.179775280899 / FREQUENCY_RATE), + float_to_fp8_24(370.25988700565 / FREQUENCY_RATE), + float_to_fp8_24(372.363636363636 / FREQUENCY_RATE), + float_to_fp8_24(374.491428571429 / FREQUENCY_RATE), + float_to_fp8_24(376.64367816092 / FREQUENCY_RATE), + float_to_fp8_24(378.820809248555 / FREQUENCY_RATE), + float_to_fp8_24(381.023255813953 / FREQUENCY_RATE), + float_to_fp8_24(383.251461988304 / FREQUENCY_RATE), + float_to_fp8_24(385.505882352941 / FREQUENCY_RATE), + float_to_fp8_24(387.786982248521 / FREQUENCY_RATE), + float_to_fp8_24(390.095238095238 / FREQUENCY_RATE), + float_to_fp8_24(392.431137724551 / FREQUENCY_RATE), + float_to_fp8_24(394.795180722892 / FREQUENCY_RATE), + float_to_fp8_24(397.187878787879 / FREQUENCY_RATE), + float_to_fp8_24(399.609756097561 / FREQUENCY_RATE), + float_to_fp8_24(402.061349693252 / FREQUENCY_RATE), + float_to_fp8_24(404.543209876543 / FREQUENCY_RATE), + float_to_fp8_24(407.055900621118 / FREQUENCY_RATE), + float_to_fp8_24(409.6 / FREQUENCY_RATE), + float_to_fp8_24(412.176100628931 / FREQUENCY_RATE), + float_to_fp8_24(414.784810126582 / FREQUENCY_RATE), + float_to_fp8_24(417.426751592357 / FREQUENCY_RATE), + float_to_fp8_24(420.102564102564 / FREQUENCY_RATE), + float_to_fp8_24(422.812903225807 / FREQUENCY_RATE), + float_to_fp8_24(425.558441558442 / FREQUENCY_RATE), + float_to_fp8_24(428.339869281046 / FREQUENCY_RATE), + float_to_fp8_24(431.157894736842 / FREQUENCY_RATE), + float_to_fp8_24(434.013245033113 / FREQUENCY_RATE), + float_to_fp8_24(436.906666666667 / FREQUENCY_RATE), + float_to_fp8_24(439.838926174497 / FREQUENCY_RATE), + float_to_fp8_24(442.810810810811 / FREQUENCY_RATE), + float_to_fp8_24(445.823129251701 / FREQUENCY_RATE), + float_to_fp8_24(448.876712328767 / FREQUENCY_RATE), + float_to_fp8_24(451.972413793103 / FREQUENCY_RATE), + float_to_fp8_24(455.111111111111 / FREQUENCY_RATE), + float_to_fp8_24(458.293706293706 / FREQUENCY_RATE), + float_to_fp8_24(461.521126760563 / FREQUENCY_RATE), + float_to_fp8_24(464.794326241135 / FREQUENCY_RATE), + float_to_fp8_24(468.114285714286 / FREQUENCY_RATE), + float_to_fp8_24(471.482014388489 / FREQUENCY_RATE), + float_to_fp8_24(474.898550724638 / FREQUENCY_RATE), + float_to_fp8_24(478.36496350365 / FREQUENCY_RATE), + float_to_fp8_24(481.882352941176 / FREQUENCY_RATE), + float_to_fp8_24(485.451851851852 / FREQUENCY_RATE), + float_to_fp8_24(489.074626865672 / FREQUENCY_RATE), + float_to_fp8_24(492.751879699248 / FREQUENCY_RATE), + float_to_fp8_24(496.484848484849 / FREQUENCY_RATE), + float_to_fp8_24(500.274809160305 / FREQUENCY_RATE), + float_to_fp8_24(504.123076923077 / FREQUENCY_RATE), + float_to_fp8_24(508.031007751938 / FREQUENCY_RATE), + float_to_fp8_24(512 / FREQUENCY_RATE), + float_to_fp8_24(516.031496062992 / FREQUENCY_RATE), + float_to_fp8_24(520.126984126984 / FREQUENCY_RATE), + float_to_fp8_24(524.288 / FREQUENCY_RATE), + float_to_fp8_24(528.516129032258 / FREQUENCY_RATE), + float_to_fp8_24(532.813008130081 / FREQUENCY_RATE), + float_to_fp8_24(537.180327868852 / FREQUENCY_RATE), + float_to_fp8_24(541.619834710744 / FREQUENCY_RATE), + float_to_fp8_24(546.133333333333 / FREQUENCY_RATE), + float_to_fp8_24(550.72268907563 / FREQUENCY_RATE), + float_to_fp8_24(555.389830508475 / FREQUENCY_RATE), + float_to_fp8_24(560.136752136752 / FREQUENCY_RATE), + float_to_fp8_24(564.965517241379 / FREQUENCY_RATE), + float_to_fp8_24(569.878260869565 / FREQUENCY_RATE), + float_to_fp8_24(574.877192982456 / FREQUENCY_RATE), + float_to_fp8_24(579.964601769912 / FREQUENCY_RATE), + float_to_fp8_24(585.142857142857 / FREQUENCY_RATE), + float_to_fp8_24(590.414414414414 / FREQUENCY_RATE), + float_to_fp8_24(595.781818181818 / FREQUENCY_RATE), + float_to_fp8_24(601.247706422018 / FREQUENCY_RATE), + float_to_fp8_24(606.814814814815 / FREQUENCY_RATE), + float_to_fp8_24(612.485981308411 / FREQUENCY_RATE), + float_to_fp8_24(618.264150943396 / FREQUENCY_RATE), + float_to_fp8_24(624.152380952381 / FREQUENCY_RATE), + float_to_fp8_24(630.153846153846 / FREQUENCY_RATE), + float_to_fp8_24(636.271844660194 / FREQUENCY_RATE), + float_to_fp8_24(642.509803921569 / FREQUENCY_RATE), + float_to_fp8_24(648.871287128713 / FREQUENCY_RATE), + float_to_fp8_24(655.36 / FREQUENCY_RATE), + float_to_fp8_24(661.979797979798 / FREQUENCY_RATE), + float_to_fp8_24(668.734693877551 / FREQUENCY_RATE), + float_to_fp8_24(675.628865979381 / FREQUENCY_RATE), + float_to_fp8_24(682.666666666667 / FREQUENCY_RATE), + float_to_fp8_24(689.852631578947 / FREQUENCY_RATE), + float_to_fp8_24(697.191489361702 / FREQUENCY_RATE), + float_to_fp8_24(704.688172043011 / FREQUENCY_RATE), + float_to_fp8_24(712.347826086956 / FREQUENCY_RATE), + float_to_fp8_24(720.175824175824 / FREQUENCY_RATE), + float_to_fp8_24(728.177777777778 / FREQUENCY_RATE), + float_to_fp8_24(736.359550561798 / FREQUENCY_RATE), + float_to_fp8_24(744.727272727273 / FREQUENCY_RATE), + float_to_fp8_24(753.287356321839 / FREQUENCY_RATE), + float_to_fp8_24(762.046511627907 / FREQUENCY_RATE), + float_to_fp8_24(771.011764705882 / FREQUENCY_RATE), + float_to_fp8_24(780.190476190476 / FREQUENCY_RATE), + float_to_fp8_24(789.590361445783 / FREQUENCY_RATE), + float_to_fp8_24(799.219512195122 / FREQUENCY_RATE), + float_to_fp8_24(809.086419753086 / FREQUENCY_RATE), + float_to_fp8_24(819.2 / FREQUENCY_RATE), + float_to_fp8_24(829.569620253165 / FREQUENCY_RATE), + float_to_fp8_24(840.205128205128 / FREQUENCY_RATE), + float_to_fp8_24(851.116883116883 / FREQUENCY_RATE), + float_to_fp8_24(862.315789473684 / FREQUENCY_RATE), + float_to_fp8_24(873.813333333333 / FREQUENCY_RATE), + float_to_fp8_24(885.621621621622 / FREQUENCY_RATE), + float_to_fp8_24(897.753424657534 / FREQUENCY_RATE), + float_to_fp8_24(910.222222222222 / FREQUENCY_RATE), + float_to_fp8_24(923.042253521127 / FREQUENCY_RATE), + float_to_fp8_24(936.228571428571 / FREQUENCY_RATE), + float_to_fp8_24(949.797101449275 / FREQUENCY_RATE), + float_to_fp8_24(963.764705882353 / FREQUENCY_RATE), + float_to_fp8_24(978.149253731343 / FREQUENCY_RATE), + float_to_fp8_24(992.969696969697 / FREQUENCY_RATE), + float_to_fp8_24(1008.24615384615 / FREQUENCY_RATE), + float_to_fp8_24(1024 / FREQUENCY_RATE), + float_to_fp8_24(1040.25396825397 / FREQUENCY_RATE), + float_to_fp8_24(1057.03225806452 / FREQUENCY_RATE), + float_to_fp8_24(1074.36065573771 / FREQUENCY_RATE), + float_to_fp8_24(1092.26666666667 / FREQUENCY_RATE), + float_to_fp8_24(1110.77966101695 / FREQUENCY_RATE), + float_to_fp8_24(1129.93103448276 / FREQUENCY_RATE), + float_to_fp8_24(1149.75438596491 / FREQUENCY_RATE), + float_to_fp8_24(1170.28571428571 / FREQUENCY_RATE), + float_to_fp8_24(1191.56363636364 / FREQUENCY_RATE), + float_to_fp8_24(1213.62962962963 / FREQUENCY_RATE), + float_to_fp8_24(1236.52830188679 / FREQUENCY_RATE), + float_to_fp8_24(1260.30769230769 / FREQUENCY_RATE), + float_to_fp8_24(1285.01960784314 / FREQUENCY_RATE), + float_to_fp8_24(1310.72 / FREQUENCY_RATE), + float_to_fp8_24(1337.4693877551 / FREQUENCY_RATE), + float_to_fp8_24(1365.33333333333 / FREQUENCY_RATE), + float_to_fp8_24(1394.3829787234 / FREQUENCY_RATE), + float_to_fp8_24(1424.69565217391 / FREQUENCY_RATE), + float_to_fp8_24(1456.35555555556 / FREQUENCY_RATE), + float_to_fp8_24(1489.45454545455 / FREQUENCY_RATE), + float_to_fp8_24(1524.09302325581 / FREQUENCY_RATE), + float_to_fp8_24(1560.38095238095 / FREQUENCY_RATE), + float_to_fp8_24(1598.43902439024 / FREQUENCY_RATE), + float_to_fp8_24(1638.4 / FREQUENCY_RATE), + float_to_fp8_24(1680.41025641026 / FREQUENCY_RATE), + float_to_fp8_24(1724.63157894737 / FREQUENCY_RATE), + float_to_fp8_24(1771.24324324324 / FREQUENCY_RATE), + float_to_fp8_24(1820.44444444444 / FREQUENCY_RATE), + float_to_fp8_24(1872.45714285714 / FREQUENCY_RATE), + float_to_fp8_24(1927.52941176471 / FREQUENCY_RATE), + float_to_fp8_24(1985.93939393939 / FREQUENCY_RATE), + float_to_fp8_24(2048 / FREQUENCY_RATE), + float_to_fp8_24(2114.06451612903 / FREQUENCY_RATE), + float_to_fp8_24(2184.53333333333 / FREQUENCY_RATE), + float_to_fp8_24(2259.86206896552 / FREQUENCY_RATE), + float_to_fp8_24(2340.57142857143 / FREQUENCY_RATE), + float_to_fp8_24(2427.25925925926 / FREQUENCY_RATE), + float_to_fp8_24(2520.61538461538 / FREQUENCY_RATE), + float_to_fp8_24(2621.44 / FREQUENCY_RATE), + float_to_fp8_24(2730.66666666667 / FREQUENCY_RATE), + float_to_fp8_24(2849.39130434783 / FREQUENCY_RATE), + float_to_fp8_24(2978.90909090909 / FREQUENCY_RATE), + float_to_fp8_24(3120.7619047619 / FREQUENCY_RATE), + float_to_fp8_24(3276.8 / FREQUENCY_RATE), + float_to_fp8_24(3449.26315789474 / FREQUENCY_RATE), + float_to_fp8_24(3640.88888888889 / FREQUENCY_RATE), + float_to_fp8_24(3855.05882352941 / FREQUENCY_RATE), + float_to_fp8_24(4096 / FREQUENCY_RATE), + float_to_fp8_24(4369.06666666667 / FREQUENCY_RATE), + float_to_fp8_24(4681.14285714286 / FREQUENCY_RATE), + float_to_fp8_24(5041.23076923077 / FREQUENCY_RATE), + float_to_fp8_24(5461.33333333333 / FREQUENCY_RATE), + float_to_fp8_24(5957.81818181818 / FREQUENCY_RATE), + float_to_fp8_24(6553.6 / FREQUENCY_RATE), + float_to_fp8_24(7281.77777777778 / FREQUENCY_RATE), + float_to_fp8_24(8192 / FREQUENCY_RATE), + float_to_fp8_24(9362.28571428571 / FREQUENCY_RATE), + float_to_fp8_24(10922.6666666667 / FREQUENCY_RATE), + float_to_fp8_24(13107.2 / FREQUENCY_RATE), + float_to_fp8_24(16384 / FREQUENCY_RATE), + float_to_fp8_24(21845.3333333333 / FREQUENCY_RATE), + float_to_fp8_24(32768 / FREQUENCY_RATE), + float_to_fp8_24(65536 / FREQUENCY_RATE), +}; -const float freqTableNSE[256] = { - 524288, 262144, 131072, 87381.3333333333, 65536, 52428.8, 43690.6666666667, 37449.1428571429, - 524288, 262144, 131072, 87381.3333333333, 65536, 52428.8, 43690.6666666667, 37449.1428571429, - 262144, 131072, 65536, 43690.6666666667, 32768, 26214.4, 21845.3333333333, 18724.5714285714, - 262144, 131072, 65536, 43690.6666666667, 32768, 26214.4, 21845.3333333333, 18724.5714285714, - 131072, 65536, 32768, 21845.3333333333, 16384, 13107.2, 10922.6666666667, 9362.28571428571, - 131072, 65536, 32768, 21845.3333333333, 16384, 13107.2, 10922.6666666667, 9362.28571428571, - 65536, 32768, 16384, 10922.6666666667, 8192, 6553.6, 5461.33333333333, 4681.14285714286, - 65536, 32768, 16384, 10922.6666666667, 8192, 6553.6, 5461.33333333333, 4681.14285714286, - 32768, 16384, 8192, 5461.33333333333, 4096, 3276.8, 2730.66666666667, 2340.57142857143, - 32768, 16384, 8192, 5461.33333333333, 4096, 3276.8, 2730.66666666667, 2340.57142857143, - 16384, 8192, 4096, 2730.66666666667, 2048, 1638.4, 1365.33333333333, 1170.28571428571, - 16384, 8192, 4096, 2730.66666666667, 2048, 1638.4, 1365.33333333333, 1170.28571428571, - 8192, 4096, 2048, 1365.33333333333, 1024, 819.2, 682.666666666667, 585.142857142857, - 8192, 4096, 2048, 1365.33333333333, 1024, 819.2, 682.666666666667, 585.142857142857, - 4096, 2048, 1024, 682.666666666667, 512, 409.6, 341.333333333333, 292.571428571429, - 4096, 2048, 1024, 682.666666666667, 512, 409.6, 341.333333333333, 292.571428571429, - 2048, 1024, 512, 341.333333333333, 256, 204.8, 170.666666666667, 146.285714285714, - 2048, 1024, 512, 341.333333333333, 256, 204.8, 170.666666666667, 146.285714285714, - 1024, 512, 256, 170.666666666667, 128, 102.4, 85.3333333333333, 73.1428571428571, - 1024, 512, 256, 170.666666666667, 128, 102.4, 85.3333333333333, 73.1428571428571, - 512, 256, 128, 85.3333333333333, 64, 51.2, 42.6666666666667, 36.5714285714286, - 512, 256, 128, 85.3333333333333, 64, 51.2, 42.6666666666667, 36.5714285714286, - 256, 128, 64, 42.6666666666667, 32, 25.6, 21.3333333333333, 18.2857142857143, - 256, 128, 64, 42.6666666666667, 32, 25.6, 21.3333333333333, 18.2857142857143, - 128, 64, 32, 21.3333333333333, 16, 12.8, 10.6666666666667, 9.14285714285714, - 128, 64, 32, 21.3333333333333, 16, 12.8, 10.6666666666667, 9.14285714285714, - 64, 32, 16, 10.6666666666667, 8, 6.4, 5.33333333333333, 4.57142857142857, - 64, 32, 16, 10.6666666666667, 8, 6.4, 5.33333333333333, 4.57142857142857, - 32, 16, 8, 5.33333333333333, 4, 3.2, 2.66666666666667, 2.28571428571429, - 32, 16, 8, 5.33333333333333, 4, 3.2, 2.66666666666667, 2.28571428571429, - 16, 8, 4, 2.66666666666667, 2, 1.6, 1.33333333333333, 1.14285714285714, - 16, 8, 4, 2.66666666666667, 2, 1.6, 1.33333333333333, 1.14285714285714, +const fixed8_24 freqTableNSE[256] = { + float_to_fp8_24(524288 / SAMPLE_RATE), + float_to_fp8_24(262144 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(87381.3333333333 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(52428.8 / SAMPLE_RATE), + float_to_fp8_24(43690.6666666667 / SAMPLE_RATE), + float_to_fp8_24(37449.1428571429 / SAMPLE_RATE), + float_to_fp8_24(524288 / SAMPLE_RATE), + float_to_fp8_24(262144 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(87381.3333333333 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(52428.8 / SAMPLE_RATE), + float_to_fp8_24(43690.6666666667 / SAMPLE_RATE), + float_to_fp8_24(37449.1428571429 / SAMPLE_RATE), + float_to_fp8_24(262144 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(43690.6666666667 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(26214.4 / SAMPLE_RATE), + float_to_fp8_24(21845.3333333333 / SAMPLE_RATE), + float_to_fp8_24(18724.5714285714 / SAMPLE_RATE), + float_to_fp8_24(262144 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(43690.6666666667 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(26214.4 / SAMPLE_RATE), + float_to_fp8_24(21845.3333333333 / SAMPLE_RATE), + float_to_fp8_24(18724.5714285714 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(21845.3333333333 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(13107.2 / SAMPLE_RATE), + float_to_fp8_24(10922.6666666667 / SAMPLE_RATE), + float_to_fp8_24(9362.28571428571 / SAMPLE_RATE), + float_to_fp8_24(131072 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(21845.3333333333 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(13107.2 / SAMPLE_RATE), + float_to_fp8_24(10922.6666666667 / SAMPLE_RATE), + float_to_fp8_24(9362.28571428571 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(10922.6666666667 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(6553.6 / SAMPLE_RATE), + float_to_fp8_24(5461.33333333333 / SAMPLE_RATE), + float_to_fp8_24(4681.14285714286 / SAMPLE_RATE), + float_to_fp8_24(65536 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(10922.6666666667 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(6553.6 / SAMPLE_RATE), + float_to_fp8_24(5461.33333333333 / SAMPLE_RATE), + float_to_fp8_24(4681.14285714286 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(5461.33333333333 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(3276.8 / SAMPLE_RATE), + float_to_fp8_24(2730.66666666667 / SAMPLE_RATE), + float_to_fp8_24(2340.57142857143 / SAMPLE_RATE), + float_to_fp8_24(32768 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(5461.33333333333 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(3276.8 / SAMPLE_RATE), + float_to_fp8_24(2730.66666666667 / SAMPLE_RATE), + float_to_fp8_24(2340.57142857143 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2730.66666666667 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1638.4 / SAMPLE_RATE), + float_to_fp8_24(1365.33333333333 / SAMPLE_RATE), + float_to_fp8_24(1170.28571428571 / SAMPLE_RATE), + float_to_fp8_24(16384 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2730.66666666667 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1638.4 / SAMPLE_RATE), + float_to_fp8_24(1365.33333333333 / SAMPLE_RATE), + float_to_fp8_24(1170.28571428571 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1365.33333333333 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(819.2 / SAMPLE_RATE), + float_to_fp8_24(682.666666666667 / SAMPLE_RATE), + float_to_fp8_24(585.142857142857 / SAMPLE_RATE), + float_to_fp8_24(8192 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1365.33333333333 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(819.2 / SAMPLE_RATE), + float_to_fp8_24(682.666666666667 / SAMPLE_RATE), + float_to_fp8_24(585.142857142857 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(682.666666666667 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(409.6 / SAMPLE_RATE), + float_to_fp8_24(341.333333333333 / SAMPLE_RATE), + float_to_fp8_24(292.571428571429 / SAMPLE_RATE), + float_to_fp8_24(4096 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(682.666666666667 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(409.6 / SAMPLE_RATE), + float_to_fp8_24(341.333333333333 / SAMPLE_RATE), + float_to_fp8_24(292.571428571429 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(341.333333333333 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(204.8 / SAMPLE_RATE), + float_to_fp8_24(170.666666666667 / SAMPLE_RATE), + float_to_fp8_24(146.285714285714 / SAMPLE_RATE), + float_to_fp8_24(2048 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(341.333333333333 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(204.8 / SAMPLE_RATE), + float_to_fp8_24(170.666666666667 / SAMPLE_RATE), + float_to_fp8_24(146.285714285714 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(170.666666666667 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(102.4 / SAMPLE_RATE), + float_to_fp8_24(85.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(73.1428571428571 / SAMPLE_RATE), + float_to_fp8_24(1024 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(170.666666666667 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(102.4 / SAMPLE_RATE), + float_to_fp8_24(85.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(73.1428571428571 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(85.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(51.2 / SAMPLE_RATE), + float_to_fp8_24(42.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(36.5714285714286 / SAMPLE_RATE), + float_to_fp8_24(512 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(85.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(51.2 / SAMPLE_RATE), + float_to_fp8_24(42.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(36.5714285714286 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(42.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(25.6 / SAMPLE_RATE), + float_to_fp8_24(21.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(18.2857142857143 / SAMPLE_RATE), + float_to_fp8_24(256 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(42.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(25.6 / SAMPLE_RATE), + float_to_fp8_24(21.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(18.2857142857143 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(21.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(12.8 / SAMPLE_RATE), + float_to_fp8_24(10.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(9.14285714285714 / SAMPLE_RATE), + float_to_fp8_24(128 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(21.3333333333333 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(12.8 / SAMPLE_RATE), + float_to_fp8_24(10.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(9.14285714285714 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(10.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(6.4 / SAMPLE_RATE), + float_to_fp8_24(5.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(4.57142857142857 / SAMPLE_RATE), + float_to_fp8_24(64 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(10.6666666666667 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(6.4 / SAMPLE_RATE), + float_to_fp8_24(5.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(4.57142857142857 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(5.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(4 / SAMPLE_RATE), + float_to_fp8_24(3.2 / SAMPLE_RATE), + float_to_fp8_24(2.66666666666667 / SAMPLE_RATE), + float_to_fp8_24(2.28571428571429 / SAMPLE_RATE), + float_to_fp8_24(32 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(5.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(4 / SAMPLE_RATE), + float_to_fp8_24(3.2 / SAMPLE_RATE), + float_to_fp8_24(2.66666666666667 / SAMPLE_RATE), + float_to_fp8_24(2.28571428571429 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(4 / SAMPLE_RATE), + float_to_fp8_24(2.66666666666667 / SAMPLE_RATE), + float_to_fp8_24(2 / SAMPLE_RATE), + float_to_fp8_24(1.6 / SAMPLE_RATE), + float_to_fp8_24(1.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(1.14285714285714 / SAMPLE_RATE), + float_to_fp8_24(16 / SAMPLE_RATE), + float_to_fp8_24(8 / SAMPLE_RATE), + float_to_fp8_24(4 / SAMPLE_RATE), + float_to_fp8_24(2.66666666666667 / SAMPLE_RATE), + float_to_fp8_24(2 / SAMPLE_RATE), + float_to_fp8_24(1.6 / SAMPLE_RATE), + float_to_fp8_24(1.33333333333333 / SAMPLE_RATE), + float_to_fp8_24(1.14285714285714 / SAMPLE_RATE), }; #endif diff --git a/include/platform/shared/video/gpsp_renderer.h b/include/platform/shared/video/gpsp_renderer.h new file mode 100644 index 000000000..5c15b3f7e --- /dev/null +++ b/include/platform/shared/video/gpsp_renderer.h @@ -0,0 +1,66 @@ +#ifndef GUARD_SW_RENDERER_COMMON_H +#define GUARD_SW_RENDERER_COMMON_H + +// shared color math for the gba ppu blend unit +// used by both the normal (multi-pass) and fast (single-pass) software renderers + +#include + +// bgr555 channel extraction +#define getAlphaBit(x) (((x) >> 15) & 1) +#define getRedChannel(x) (((x) >> 0) & 0x1F) +#define getGreenChannel(x) (((x) >> 5) & 0x1F) +#define getBlueChannel(x) (((x) >> 10) & 0x1F) +#define COLOR_OPAQUE 0x8000 + +static inline uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB, unsigned int eva, unsigned int evb) +{ + unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4; + unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4; + unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | COLOR_OPAQUE; +} + +static inline uint16_t alphaBrightnessIncrease(uint16_t targetA, unsigned int evy) +{ + unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16; + unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16; + unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | COLOR_OPAQUE; +} + +static inline uint16_t alphaBrightnessDecrease(uint16_t targetA, unsigned int evy) +{ + unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16; + unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16; + unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return r | (g << 5) | (b << 10) | COLOR_OPAQUE; +} + +extern void gpsp_draw_frame(uint16_t *framebuf); + +#endif // GUARD_SW_RENDERER_COMMON_H diff --git a/libagbsyscall/Makefile b/libagbsyscall/Makefile index 654a44e4b..7f6c55693 100644 --- a/libagbsyscall/Makefile +++ b/libagbsyscall/Makefile @@ -34,6 +34,10 @@ else ifeq ($(CPU_ARCH),i386) TOOLCHAIN := /usr/x86_64-w64-mingw32/ PREFIX := x86_64-w64-mingw32- endif +else ifeq ($(PLATFORM),psp) + PSPDEV ?= $(HOME)/pspdev + export PATH := $(PSPDEV)/bin:$(PATH) + PREFIX := psp- else ifneq ($(PLATFORM),sdl) $(error Unknown CPU architecture $(CPU_ARCH)) endif # (PLATFORM == gba) diff --git a/ps2/ntsc/SYSTEM.CNF b/ps2/ntsc/SYSTEM.CNF new file mode 100644 index 000000000..9c440527d --- /dev/null +++ b/ps2/ntsc/SYSTEM.CNF @@ -0,0 +1,3 @@ +BOOT2 = cdrom0:\SLUS_054.02;1 +VER = 1.00 +VMODE = NTSC \ No newline at end of file diff --git a/src/game/special_stage/world.c b/src/game/special_stage/world.c index eb293bf74..98f7e8e71 100644 --- a/src/game/special_stage/world.c +++ b/src/game/special_stage/world.c @@ -238,19 +238,10 @@ void sub_806EA04(void) temp5 = (i - stage->cameraHeight) * temp2 * 2; *unk1884++ = (Q_16_16_TO_INT(temp) * cos) >> 0x10; // BG2PA - // HACK: in SDL we don't handle these PB and PD values properly -#if PLATFORM_SDL - *unk1884++ = 0; -#else *unk1884++ = (Q_16_16_TO_INT(temp) * sin) >> 0x10; // BG2PB -#endif *unk1884++ = (Q_16_16_TO_INT(temp) * -sin) >> 0x10; // BG2PC -#if PLATFORM_SDL - *unk1884++ = 0; -#else *unk1884++ = (Q_16_16_TO_INT(temp) * cos) >> 0x10; // BG2PD -#endif x = (Q_16_16_TO_INT(temp5) * sin) + (Q_16_16_TO_INT(temp4) * cos) + stage->q16CameraX; y = (Q_16_16_TO_INT(temp4) * -sin) + (Q_16_16_TO_INT(temp5) * cos) + stage->q16CameraY; diff --git a/src/game/stage/camera.c b/src/game/stage/camera.c index f79e15226..ebfc56a4d 100644 --- a/src/game/stage/camera.c +++ b/src/game/stage/camera.c @@ -1202,9 +1202,13 @@ void CreateStageBg_Zone4(void) gBgScrollRegs[3][0] = 0; gBgScrollRegs[3][1] = 0; +// Software renderer for these devices are too slow +// to handle these spotlights +#if !defined(__PS2__) && !defined(__PSP__) if (IS_SINGLE_PLAYER) { CreateSpotLightBeams(); } +#endif } void StageBgUpdate_Zone4Acts12(s32 cameraX, s32 cameraY) diff --git a/src/platform/pret_sdl/sdl2.c b/src/platform/pret_sdl/sdl2.c index f4ceaf79a..e645f09f8 100644 --- a/src/platform/pret_sdl/sdl2.c +++ b/src/platform/pret_sdl/sdl2.c @@ -10,6 +10,12 @@ #include #endif +#ifdef __PSP__ +#include +#include +#include +#endif + #include #include "global.h" @@ -21,6 +27,7 @@ #include "lib/agb_flash/flash_internal.h" #include "platform/shared/dma.h" #include "platform/shared/input.h" +#include "platform/shared/video/gpsp_renderer.h" #if ENABLE_AUDIO #include "platform/shared/audio/cgb_audio.h" @@ -92,6 +99,11 @@ bool paused = false; bool stepOneFrame = false; bool headless = false; +#ifdef __PSP__ +static SDL_Joystick *joystick = NULL; +static SDL_Rect pspDestRect; +#endif + double lastGameTime = 0; double curGameTime = 0; double fixedTimestep = 1.0 / 60.0; // 16.666667ms @@ -120,8 +132,50 @@ void *Platform_malloc(size_t numBytes) { return HeapAlloc(GetProcessHeap(), HEAP void Platform_free(void *ptr) { HeapFree(GetProcessHeap(), 0, ptr); } #endif +#ifdef __PSP__ +PSP_MODULE_INFO("SonicAdvance2", 0, 1, 0); +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU); +PSP_HEAP_SIZE_KB(-1024); + +unsigned int sce_newlib_stack_size = 512 * 1024; + +extern bool isRunning; + +int exitCallback(int arg1, int arg2, void *common) +{ + (void)arg1; + (void)arg2; + (void)common; + isRunning = false; + return 0; +} + +int callbackThread(SceSize args, void *argp) +{ + (void)args; + (void)argp; + int cbid = sceKernelCreateCallback("Exit Callback", exitCallback, NULL); + sceKernelRegisterExitCallback(cbid); + sceKernelSleepThreadCB(); + return 0; +} + +int setupPspCallbacks(void) +{ + int thid = sceKernelCreateThread("update_thread", callbackThread, 0x11, 0xFA0, 0, 0); + if (thid >= 0) { + sceKernelStartThread(thid, 0, 0); + } + return thid; +} +#endif + int main(int argc, char **argv) { +#ifdef __PSP__ + setupPspCallbacks(); +#endif + const char *headlessEnv = getenv("HEADLESS"); if (headlessEnv && strcmp(headlessEnv, "true") == 0) { @@ -151,8 +205,10 @@ int main(int argc, char **argv) REG_KEYINPUT = 0x3FF; if (headless) { +#if ENABLE_AUDIO // Required or it makes an infinite loop cgb_audio_init(48000); +#endif AgbMain(); return 1; } @@ -162,14 +218,24 @@ int main(int argc, char **argv) return 1; } +#ifdef __PSP__ + if (SDL_NumJoysticks() > 0) { + joystick = SDL_JoystickOpen(0); + } +#endif + #ifdef TITLE_BAR const char *title = STR(TITLE_BAR); #else const char *title = "SAT-R sa2"; #endif +#ifdef __PSP__ + sdlWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 480, 272, SDL_WINDOW_SHOWN); +#else sdlWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); +#endif if (sdlWindow == NULL) { fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; @@ -191,7 +257,15 @@ int main(int argc, char **argv) } #endif +#ifdef __PSP__ + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (sdlRenderer == NULL) + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED); + if (sdlRenderer == NULL) + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0); +#else sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC); +#endif if (sdlRenderer == NULL) { fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; @@ -208,7 +282,12 @@ int main(int argc, char **argv) SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255); SDL_RenderClear(sdlRenderer); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); +#ifdef __PSP__ + // SDL_RenderSetLogicalSize is broken on PSP, stretch to fill manually + pspDestRect = (SDL_Rect) { 0, 0, GU_SCR_WIDTH, GU_SCR_HEIGHT }; +#else SDL_RenderSetLogicalSize(sdlRenderer, DISPLAY_WIDTH, DISPLAY_HEIGHT); +#endif #if ENABLE_VRAM_VIEW SDL_SetRenderDrawColor(vramRenderer, 0, 0, 0, 255); SDL_RenderClear(vramRenderer); @@ -234,16 +313,16 @@ int main(int argc, char **argv) SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */ want.freq = 48000; - want.format = AUDIO_F32; + want.format = AUDIO_S16; want.channels = 2; want.samples = (want.freq / 60); cgb_audio_init(want.freq); - if (SDL_OpenAudio(&want, 0) < 0) + if (SDL_OpenAudio(&want, 0) < 0) { SDL_Log("Failed to open audio: %s", SDL_GetError()); - else { - if (want.format != AUDIO_F32) /* we let this one thing change. */ - SDL_Log("We didn't get Float32 audio format."); + } else { + if (want.format != AUDIO_S16) /* we let this one thing change. */ + SDL_Log("We didn't get S16 audio format."); SDL_PauseAudio(0); } #endif @@ -259,12 +338,10 @@ int main(int argc, char **argv) bool newFrameRequested = FALSE; -// Every GBA frame we process the SDL events and render the number of times -// SDL requires us to for vsync. When we need another frame we break out of -// the loop via a return +// called every gba frame. we process sdl events and render as many times +// as vsync needs, then return when a new game frame is needed. void VBlankIntrWait(void) { - // ((struct MultiSioPacket *)gMultiSioArea.nextSendBufp) #define HANDLE_VBLANK_INTRS() \ ({ \ REG_DISPSTAT |= INTR_FLAG_VBLANK; \ @@ -281,16 +358,18 @@ void VBlankIntrWait(void) } bool frameAvailable = TRUE; + bool frameDrawn = false; while (isRunning) { +#ifndef __PSP__ ProcessSDLEvents(); +#endif if (!paused || stepOneFrame) { double dt = fixedTimestep / timeScale; // TODO: Fix speedup - // Hack to emulate the behaviour of threaded sdl - // it will not add any new values to the accumulator - // when a new frame was requested within a frame cycle + // don't accumulate time if we already requested a new frame + // this frame cycle (emulates threaded sdl behavior) if (!newFrameRequested) { double deltaTime = 0; @@ -314,6 +393,7 @@ void VBlankIntrWait(void) if (frameAvailable) { VDraw(sdlTexture); frameAvailable = FALSE; + frameDrawn = true; HANDLE_VBLANK_INTRS(); @@ -329,6 +409,12 @@ void VBlankIntrWait(void) } } + // present +#ifdef __PSP__ + // manual blit since SDL_RenderSetLogicalSize doesn't work on psp + SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &pspDestRect); + SDL_RenderPresent(sdlRenderer); +#else SDL_RenderClear(sdlRenderer); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); @@ -345,6 +431,7 @@ void VBlankIntrWait(void) SDL_RenderPresent(sdlRenderer); #if ENABLE_VRAM_VIEW SDL_RenderPresent(vramRenderer); +#endif #endif } @@ -352,8 +439,11 @@ void VBlankIntrWait(void) SDL_DestroyWindow(sdlWindow); SDL_Quit(); +#ifdef __PSP__ + sceKernelExitGame(); +#endif exit(0); -#undef RUN_VBLANK_INTRS +#undef HANDLE_VBLANK_INTRS } static void ReadSaveFile(char *path) @@ -397,6 +487,8 @@ static void CloseSaveFile() } } +static u16 keys; + // Key mappings #define KEY_A_BUTTON SDLK_c #define KEY_B_BUTTON SDLK_x @@ -419,12 +511,60 @@ static void CloseSaveFile() keys |= key; \ break; -static u16 keys; +#ifdef __PSP__ +#define BTN_TRIANGLE 0 +#define BTN_CIRCLE 1 +#define BTN_CROSS 2 +#define BTN_SQUARE 3 +#define BTN_LTRIGGER 4 +#define BTN_RTRIGGER 5 +#define BTN_DOWN 6 +#define BTN_LEFT 7 +#define BTN_UP 8 +#define BTN_RIGHT 9 +#define BTN_SELECT 10 +#define BTN_START 11 + +static u16 PollJoystickButtons(void) +{ + u16 newKeys = 0; + if (joystick == NULL) + return newKeys; + + SDL_JoystickUpdate(); + + if (SDL_JoystickGetButton(joystick, BTN_CROSS)) + newKeys |= A_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_CIRCLE)) + newKeys |= B_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_SQUARE)) + newKeys |= B_BUTTON; // Square also B + if (SDL_JoystickGetButton(joystick, BTN_START)) + newKeys |= START_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_SELECT)) + newKeys |= SELECT_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_LTRIGGER)) + newKeys |= L_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_RTRIGGER)) + newKeys |= R_BUTTON; + if (SDL_JoystickGetButton(joystick, BTN_UP)) + newKeys |= DPAD_UP; + if (SDL_JoystickGetButton(joystick, BTN_DOWN)) + newKeys |= DPAD_DOWN; + if (SDL_JoystickGetButton(joystick, BTN_LEFT)) + newKeys |= DPAD_LEFT; + if (SDL_JoystickGetButton(joystick, BTN_RIGHT)) + newKeys |= DPAD_RIGHT; + + return newKeys; +} + +#endif u32 fullScreenFlags = 0; static SDL_DisplayMode sdlDispMode = { 0 }; -void Platform_QueueAudio(const void *data, uint32_t bytesCount) +void Platform_QueueAudio(const s16 *data, uint32_t bytesCount) { if (headless) { return; @@ -561,18 +701,33 @@ u16 Platform_GetKeyInput(void) return (gamepadKeys != 0) ? gamepadKeys : keys; #endif +#ifdef __PSP__ + return keys | PollJoystickButtons(); +#endif + return keys; } // BIOS function implementations are based on the VBA-M source code. -static uint32_t CPUReadMemory(const void *src) { return *(uint32_t *)src; } +// safe unaligned access for MIPS +static uint32_t CPUReadMemory(const void *src) +{ + uint32_t val; + memcpy(&val, src, sizeof(val)); + return val; +} -static void CPUWriteMemory(void *dest, uint32_t val) { *(uint32_t *)dest = val; } +static void CPUWriteMemory(void *dest, uint32_t val) { memcpy(dest, &val, sizeof(val)); } -static uint16_t CPUReadHalfWord(const void *src) { return *(uint16_t *)src; } +static uint16_t CPUReadHalfWord(const void *src) +{ + uint16_t val; + memcpy(&val, src, sizeof(val)); + return val; +} -static void CPUWriteHalfWord(void *dest, uint16_t val) { *(uint16_t *)dest = val; } +static void CPUWriteHalfWord(void *dest, uint16_t val) { memcpy(dest, &val, sizeof(val)); } static uint8_t CPUReadByte(const void *src) { return *(uint8_t *)src; } @@ -968,25 +1123,26 @@ static const uint16_t bgMapSizes[][2] = { #define applySpriteHorizontalMosaicEffect(x) (x - (x % (mosaicSpriteEffectX + 1))) #define applySpriteVerticalMosaicEffect(y) (y - (y % (mosaicSpriteEffectY + 1))) -// NOTE: This is the corrected function. static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line) { unsigned int charBaseBlock = (control >> 2) & 3; unsigned int screenBaseBlock = (control & BGCNT_SCREENBASE_MASK) >> 8; - unsigned int bitsPerPixel = ((control >> 7) & 1) ? 8 : 4; + unsigned int is8bpp = (control >> 7) & 1; // Determine background dimensions from the control register unsigned int mapWidth = bgMapSizes[control >> 14][0]; // in tiles - unsigned int mapHeight = bgMapSizes[control >> 14][1]; // in tiles - unsigned int mapPixelWidth = mapWidth * TILE_WIDTH; - unsigned int mapPixelHeight = mapHeight * TILE_WIDTH; + unsigned int mapPixelWidth = mapWidth << 3; + unsigned int mapPixelHeight = bgMapSizes[control >> 14][1] << 3; + unsigned int pixelWidthMask = mapPixelWidth - 1; + unsigned int pixelHeightMask = mapPixelHeight - 1; uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock); uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock); uint16_t *pal = (uint16_t *)PLTT; // Apply vertical mosaic effect to the entire scanline if enabled - if (control & BGCNT_MOSAIC) { + bool hasMosaic = control & BGCNT_MOSAIC; + if (hasMosaic) { lineNum = applyBGVerticalMosaicEffect(lineNum); } @@ -994,29 +1150,22 @@ static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16 hoffs &= 0x1FF; voffs &= 0x1FF; + unsigned int yy = (lineNum + voffs) & pixelHeightMask; + unsigned int mapY = yy >> 3; + unsigned int tileY = yy & 7; + unsigned int mapRowBase = mapY * mapWidth; + for (unsigned int x = 0; x < DISPLAY_WIDTH; x++) { - unsigned int xx, yy; + unsigned int xx; - // Calculate the source coordinate in the background map, applying scroll and mosaic - if (control & BGCNT_MOSAIC) { - xx = applyBGHorizontalMosaicEffect(x) + hoffs; + if (hasMosaic) { + xx = (applyBGHorizontalMosaicEffect(x) + hoffs) & pixelWidthMask; } else { - xx = x + hoffs; + xx = (x + hoffs) & pixelWidthMask; } - yy = lineNum + voffs; - - // Wrap the coordinates based on the background's actual pixel dimensions. - // This fixes issues with backgrounds that are not 256x256. - xx &= (mapPixelWidth - 1); - yy &= (mapPixelHeight - 1); - - // Convert pixel coordinates to tile coordinates - unsigned int mapX = xx / TILE_WIDTH; - unsigned int mapY = yy / TILE_WIDTH; - // Calculate the 1D index into the tilemap. This was the primary source of bugs, - // as the original code used a hardcoded map width of 32 tiles. - unsigned int mapIndex = mapY * mapWidth + mapX; + unsigned int mapX = xx >> 3; + unsigned int mapIndex = mapRowBase + mapX; uint16_t entry = bgmap[mapIndex]; unsigned int tileNum = entry & 0x3FF; @@ -1026,40 +1175,30 @@ static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16 vramPalIdBuffer[tileNum] = paletteNum; #endif - // Get the coordinate within the specific tile - unsigned int tileX = xx % TILE_WIDTH; - unsigned int tileY = yy % TILE_WIDTH; + unsigned int tx = xx & 7; + unsigned int ty = tileY; - // Handle horizontal and vertical tile flipping if (entry & (1 << 10)) - tileX = (TILE_WIDTH - 1) - tileX; // H-flip + tx = 7 - tx; if (entry & (1 << 11)) - tileY = (TILE_WIDTH - 1) - tileY; // V-flip + ty = 7 - ty; - // Calculate address of the pixel data and extract the color - if (bitsPerPixel == 4) { - uint32_t tileDataOffset = tileNum * TILE_SIZE_4BPP; - uint32_t pixelByteOffset = (tileY * TILE_WIDTH + tileX) / 2; + if (!is8bpp) { + uint32_t tileDataOffset = tileNum << 5; + uint32_t pixelByteOffset = (ty << 2) + (tx >> 1); uint8_t pixelPair = bgtiles[tileDataOffset + pixelByteOffset]; - uint8_t pixel; - if (tileX & 1) { - pixel = pixelPair >> 4; - } else { - pixel = pixelPair & 0xF; - } + uint8_t pixel = (tx & 1) ? (pixelPair >> 4) : (pixelPair & 0xF); if (pixel != 0) { - line[x] = pal[16 * paletteNum + pixel] | 0x8000; + line[x] = pal[(paletteNum << 4) + pixel] | 0x8000; } } else { // 8 bits per pixel - uint32_t tileDataOffset = tileNum * TILE_SIZE_8BPP; - uint32_t pixelByteOffset = tileY * TILE_WIDTH + tileX; + uint32_t tileDataOffset = tileNum << 6; + uint32_t pixelByteOffset = (ty << 3) + tx; uint8_t pixel = bgtiles[tileDataOffset + pixelByteOffset]; if (pixel != 0) { - // For 8bpp tiles, the palette number in the tile entry is ignored. - // The pixel value is a direct index into the 256-color palette. line[x] = pal[pixel] | 0x8000; } } @@ -1257,64 +1396,7 @@ const u8 spriteSizes[][2] = { { 32, 64 }, }; -#define getAlphaBit(x) ((x >> 15) & 1) -#define getRedChannel(x) ((x >> 0) & 0x1F) -#define getGreenChannel(x) ((x >> 5) & 0x1F) -#define getBlueChannel(x) ((x >> 10) & 0x1F) -#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x) - -static uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB) -{ - unsigned int eva = REG_BLDALPHA & 0x1F; - unsigned int evb = (REG_BLDALPHA >> 8) & 0x1F; - // shift right by 4 = division by 16 - unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4; - unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4; - unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4; - - if (r > 31) - r = 31; - if (g > 31) - g = 31; - if (b > 31) - b = 31; - - return r | (g << 5) | (b << 10) | (1 << 15); -} - -static uint16_t alphaBrightnessIncrease(uint16_t targetA) -{ - unsigned int evy = (REG_BLDY & 0x1F); - unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16; - unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16; - unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16; - - if (r > 31) - r = 31; - if (g > 31) - g = 31; - if (b > 31) - b = 31; - - return r | (g << 5) | (b << 10) | (1 << 15); -} - -static uint16_t alphaBrightnessDecrease(uint16_t targetA) -{ - unsigned int evy = (REG_BLDY & 0x1F); - unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16; - unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16; - unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16; - - if (r > 31) - r = 31; - if (g > 31) - g = 31; - if (b > 31) - b = 31; - - return r | (g << 5) | (b << 10) | (1 << 15); -} +#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x) // outputs the blended pixel in colorOutput, the prxxx are the bg priority and // subpriority, pixelpos is pixel offset in scanline @@ -1396,8 +1478,6 @@ static void DrawOamSprites(struct scanlineData *scanline, uint16_t vcount, bool bool isAffine = oam->split.affineMode & 1; bool doubleSizeOrDisabled = (oam->split.affineMode >> 1) & 1; - bool isSemiTransparent = (oam->split.objMode == 1); - bool isObjWin = (oam->split.objMode == 2); if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine { @@ -1408,31 +1488,41 @@ static void DrawOamSprites(struct scanlineData *scanline, uint16_t vcount, bool width = gOamShapesSizes[index][0]; height = gOamShapesSizes[index][1]; - int rect_width = width; - int rect_height = height; - int half_width = width / 2; int half_height = height / 2; - pixels = scanline->spriteLayers[oam->split.priority]; - int32_t x = oam->split.x; int32_t y = oam->split.y; #if !EXTENDED_OAM - // The regular, unextended values are 9 and 8 unsigned bits for x and y respectively. - // Once they have exceeded the screen's right or bottom, they get treated as signed values on original hardware. - // This is done so that, for example, a sprite at 0 on either axis that moves left or up will not suddenly disappear. - // - // With EXTENDED_OAM we are using signed 16 bit values, so we don't want to change the raw value. if (x >= DISPLAY_WIDTH) x -= 512; if (y >= DISPLAY_HEIGHT) y -= 256; #endif + if (isAffine && doubleSizeOrDisabled) { + half_width *= 2; + half_height *= 2; + } + + int spriteTop = y; + int spriteBottom = y + (half_height * 2); + if ((int)vcount < spriteTop || (int)vcount >= spriteBottom) + continue; + + int spriteLeft = x; + int spriteRight = x + (half_width * 2); + if (spriteRight < 0 || spriteLeft >= DISPLAY_WIDTH) + continue; + + bool isSemiTransparent = (oam->split.objMode == 1); + bool isObjWin = (oam->split.objMode == 2); + + int rect_width = width; + int rect_height = height; + if (isAffine) { - // TODO: there is probably a better way to do this u8 matrixNum = oam->split.matrixNum * 4; OamData *oam1 = &((OamData *)OAM)[matrixNum]; @@ -1445,26 +1535,22 @@ static void DrawOamSprites(struct scanlineData *scanline, uint16_t vcount, bool matrix[1][0] = oam3->all.affineParam; matrix[1][1] = oam4->all.affineParam; - if (doubleSizeOrDisabled) // double size for affine - { + if (doubleSizeOrDisabled) { rect_width *= 2; rect_height *= 2; - half_width *= 2; - half_height *= 2; } } else { - // Identity matrix[0][0] = 0x100; matrix[0][1] = 0; matrix[1][0] = 0; matrix[1][1] = 0x100; } + pixels = scanline->spriteLayers[oam->split.priority]; x += half_width; y += half_height; - // Does this sprite actually draw on this scanline? - if (vcount >= (y - half_height) && vcount < (y + half_height)) { + { int local_y = (oam->split.mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y; int number = oam->split.tileNum; int palette = oam->split.paletteNum; @@ -1472,96 +1558,100 @@ static void DrawOamSprites(struct scanlineData *scanline, uint16_t vcount, bool bool flipY = !isAffine && ((oam->split.matrixNum >> 4) & 1); bool is8BPP = oam->split.bpp & 1; - for (int local_x = -half_width; local_x <= half_width; local_x++) { + { uint8_t *tiledata = (uint8_t *)objtiles; - uint16_t *palette = (uint16_t *)(PLTT + (0x200 / 2)); - int local_mosaicX; - int tex_x; - int tex_y; - - unsigned int global_x = local_x + x; - - if (global_x < 0 || global_x >= DISPLAY_WIDTH) - continue; - - if (oam->split.mosaic == 1) { - // mosaic effect has to be applied to global coordinates otherwise - // the mosaic will scroll - local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x; - tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2); - tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2); - } else { - tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2); - tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2); - } + uint16_t *sprpal = (uint16_t *)(PLTT + (0x200 / 2)); + for (int local_x = -half_width; local_x <= half_width; local_x++) { + int local_mosaicX; + int tex_x; + int tex_y; - /* Check if transformed coordinates are inside bounds. */ - - if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0) - continue; - - if (flipX) - tex_x = width - tex_x - 1; - if (flipY) - tex_y = height - tex_y - 1; - - int tile_x = tex_x % 8; - int tile_y = tex_y % 8; - int block_x = tex_x / 8; - int block_y = tex_y / 8; - int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x); - uint16_t pixel = 0; - - if (!is8BPP) { - int tileDataIndex = (block_offset + oam->split.tileNum) * 32 + (tile_y * 4) + (tile_x / 2); - pixel = tiledata[tileDataIndex]; - if (tile_x & 1) - pixel >>= 4; - else - pixel &= 0xF; - palette += oam->split.paletteNum * 16; -#if ENABLE_VRAM_VIEW - vramPalIdBuffer[0x800 + (tileDataIndex / 32)] = 16 + oam->split.paletteNum; -#endif - } else { - pixel = tiledata[(block_offset * 2 + oam->split.tileNum) * 32 + (tile_y * 8) + tile_x]; - } + unsigned int global_x = local_x + x; - if (pixel != 0) { - uint16_t color = palette[pixel]; + if (global_x < 0 || global_x >= DISPLAY_WIDTH) + continue; + + if (oam->split.mosaic == 1) { + // mosaic effect has to be applied to global coordinates otherwise + // the mosaic will scroll + local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x; + tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2); + } else { + tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2); + tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2); + } + + /* Check if transformed coordinates are inside bounds. */ - // if sprite mode is 2 then write to the window mask instead - if (isObjWin) { - if (scanline->winMask[global_x] & WINMASK_WINOUT) - scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F; + if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0) continue; + + if (flipX) + tex_x = width - tex_x - 1; + if (flipY) + tex_y = height - tex_y - 1; + + int tile_x = tex_x & 7; + int tile_y = tex_y & 7; + int block_x = tex_x >> 3; + int block_y = tex_y >> 3; + int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width >> 3) : 16)) + block_x); + uint16_t pixel = 0; + + uint16_t *pixpal; + if (!is8BPP) { + int tileDataIndex = ((block_offset + oam->split.tileNum) << 5) + (tile_y << 2) + (tile_x >> 1); + pixel = tiledata[tileDataIndex]; + if (tile_x & 1) + pixel >>= 4; + else + pixel &= 0xF; + pixpal = sprpal + (oam->split.paletteNum << 4); +#if ENABLE_VRAM_VIEW + vramPalIdBuffer[0x800 + (tileDataIndex >> 5)] = 16 + oam->split.paletteNum; +#endif + } else { + pixel = tiledata[((block_offset * 2 + oam->split.tileNum) << 5) + (tile_y << 3) + tile_x]; + pixpal = sprpal; } - // this code runs if pixel is to be drawn - if (global_x < DISPLAY_WIDTH && global_x >= 0) { - // check if its enabled in the window (if window is enabled) - winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR); - - // has to be separated from the blend mode switch statement - // because of OBJ semi transparancy feature - if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent) { - uint16_t targetA = color; - uint16_t targetB = 0; - if (alphaBlendSelectTargetB(scanline, &targetB, oam->split.priority, 0, global_x, false)) { - color = alphaBlendColor(targetA, targetB); - } - } else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) { - switch (blendMode) { - case 2: - color = alphaBrightnessIncrease(color); - break; - case 3: - color = alphaBrightnessDecrease(color); - break; - } + + if (pixel != 0) { + uint16_t color = pixpal[pixel]; + + // if sprite mode is 2 then write to the window mask instead + if (isObjWin) { + if (scanline->winMask[global_x] & WINMASK_WINOUT) + scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F; + continue; } + // this code runs if pixel is to be drawn + if (global_x < DISPLAY_WIDTH && global_x >= 0) { + // check if its enabled in the window (if window is enabled) + winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR); + + // has to be separated from the blend mode switch statement + // because of OBJ semi transparancy feature + if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent) { + uint16_t targetA = color; + uint16_t targetB = 0; + if (alphaBlendSelectTargetB(scanline, &targetB, oam->split.priority, 0, global_x, false)) { + color = alphaBlendColor(targetA, targetB, REG_BLDALPHA & 0x1F, (REG_BLDALPHA >> 8) & 0x1F); + } + } else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) { + switch (blendMode) { + case 2: + color = alphaBrightnessIncrease(color, REG_BLDY & 0x1F); + break; + case 3: + color = alphaBrightnessDecrease(color, REG_BLDY & 0x1F); + break; + } + } - // write pixel to pixel framebuffer - pixels[global_x] = color | (1 << 15); + // write pixel to pixel framebuffer + pixels[global_x] = color | (1 << 15); + } } } } @@ -1574,14 +1664,19 @@ static void DrawScanline(uint16_t *pixels, uint16_t vcount) unsigned int mode = REG_DISPCNT & 3; unsigned char numOfBgs = (mode == 0 ? 4 : 3); int bgnum, prnum; - struct scanlineData scanline; + static struct scanlineData scanline; unsigned int blendMode = (REG_BLDCNT >> 6) & 3; unsigned int xpos; + unsigned int enabledBgs = (REG_DISPCNT >> 8) & 0xF; - // initialize all priority bookkeeping data - memset(scanline.layers, 0, sizeof(scanline.layers)); - memset(scanline.winMask, 0, sizeof(scanline.winMask)); - memset(scanline.spriteLayers, 0, sizeof(scanline.spriteLayers)); + // Only zero the layers that are actually enabled, + // instead of blindly zeroing all 4+4 layers (~8KB total) every scanline. + for (bgnum = 0; bgnum < numOfBgs; bgnum++) { + if (enabledBgs & (1 << bgnum)) + memset(scanline.layers[bgnum], 0, sizeof(scanline.layers[bgnum])); + } + if (REG_DISPCNT & DISPCNT_OBJ_ON) + memset(scanline.spriteLayers, 0, sizeof(scanline.spriteLayers)); memset(scanline.prioritySortedBgsCount, 0, sizeof(scanline.prioritySortedBgsCount)); for (bgnum = 0; bgnum < numOfBgs; bgnum++) { @@ -1696,63 +1791,88 @@ static void DrawScanline(uint16_t *pixels, uint16_t vcount) if (REG_DISPCNT & DISPCNT_OBJ_ON) DrawOamSprites(&scanline, vcount, windowsEnabled); - // iterate trough every priority in order - for (prnum = 3; prnum >= 0; prnum--) { - for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) { - char bgnum = scanline.prioritySortedBgs[prnum][prsub]; - // if background is enabled then draw it - if (isbgEnabled(bgnum)) { - uint16_t *src = scanline.layers[bgnum]; - // copy all pixels to framebuffer + // iterate through every priority in order + if (blendMode == 0 && !windowsEnabled) { + for (prnum = 3; prnum >= 0; prnum--) { + for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) { + char bgnum = scanline.prioritySortedBgs[prnum][prsub]; + if (isbgEnabled(bgnum)) { + uint16_t *src = scanline.layers[bgnum]; + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) { + uint16_t color = src[xpos]; + if (color & 0x8000) // alpha bit set = opaque + pixels[xpos] = color; + } + } + } + // draw sprites on current priority + if (REG_DISPCNT & DISPCNT_OBJ_ON) { + uint16_t *src = scanline.spriteLayers[prnum]; for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) { - uint16_t color = src[xpos]; - bool winEffectEnable = true; - - if (!getAlphaBit(color)) - continue; // do nothing if alpha bit is not set + if (src[xpos] & 0x8000) + pixels[xpos] = src[xpos]; + } + } + } + } else { + // FULL PATH: blending and/or windows are active + for (prnum = 3; prnum >= 0; prnum--) { + for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--) { + char bgnum = scanline.prioritySortedBgs[prnum][prsub]; + // if background is enabled then draw it + if (isbgEnabled(bgnum)) { + uint16_t *src = scanline.layers[bgnum]; + // copy all pixels to framebuffer + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) { + uint16_t color = src[xpos]; + bool winEffectEnable = true; + + if (!getAlphaBit(color)) + continue; // do nothing if alpha bit is not set + + if (windowsEnabled) { + winEffectEnable = ((scanline.winMask[xpos] & WINMASK_CLR) >> 5); + // if bg is disabled inside the window then do not draw the pixel + if (!(scanline.winMask[xpos] & 1 << bgnum)) + continue; + } - if (windowsEnabled) { - winEffectEnable = ((scanline.winMask[xpos] & WINMASK_CLR) >> 5); - // if bg is disabled inside the window then do not draw the pixel - if (!(scanline.winMask[xpos] & 1 << bgnum)) - continue; - } + // blending code + if (blendMode != 0 && REG_BLDCNT & (1 << bgnum) && winEffectEnable) { + uint16_t targetA = color; + uint16_t targetB = 0; - // blending code - if (blendMode != 0 && REG_BLDCNT & (1 << bgnum) && winEffectEnable) { - uint16_t targetA = color; - uint16_t targetB = 0; - - switch (blendMode) { - case 1: { - char isSpriteBlendingEnabled = REG_BLDCNT & BLDCNT_TGT2_OBJ ? 1 : 0; - // find targetB and blend it - if (alphaBlendSelectTargetB(&scanline, &targetB, prnum, prsub + 1, xpos, isSpriteBlendingEnabled)) { - color = alphaBlendColor(targetA, targetB); - } - } break; - case 2: - color = alphaBrightnessIncrease(targetA); - break; - case 3: - color = alphaBrightnessDecrease(targetA); - break; + switch (blendMode) { + case 1: { + char isSpriteBlendingEnabled = REG_BLDCNT & BLDCNT_TGT2_OBJ ? 1 : 0; + // find targetB and blend it + if (alphaBlendSelectTargetB(&scanline, &targetB, prnum, prsub + 1, xpos, isSpriteBlendingEnabled)) { + color = alphaBlendColor(targetA, targetB, REG_BLDALPHA & 0x1F, (REG_BLDALPHA >> 8) & 0x1F); + } + } break; + case 2: + color = alphaBrightnessIncrease(targetA, REG_BLDY & 0x1F); + break; + case 3: + color = alphaBrightnessDecrease(targetA, REG_BLDY & 0x1F); + break; + } } + // write the pixel to scanline buffer output + pixels[xpos] = color; } - // write the pixel to scanline buffer output - pixels[xpos] = color; } } - } - // draw sprites on current priority - uint16_t *src = scanline.spriteLayers[prnum]; - for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) { - if (getAlphaBit(src[xpos])) { - // check if sprite pixel draws inside window - if (windowsEnabled && !(scanline.winMask[xpos] & WINMASK_OBJ)) - continue; - // draw the pixel - pixels[xpos] = src[xpos]; + // draw sprites on current priority + uint16_t *src = scanline.spriteLayers[prnum]; + for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++) { + if (getAlphaBit(src[xpos])) { + // check if sprite pixel draws inside window + if (windowsEnabled && !(scanline.winMask[xpos] & WINMASK_OBJ)) + continue; + // draw the pixel + pixels[xpos] = src[xpos]; + } } } } @@ -1760,21 +1880,25 @@ static void DrawScanline(uint16_t *pixels, uint16_t vcount) uint16_t *memsetu16(uint16_t *dst, uint16_t fill, size_t count) { - for (int i = 0; i < count; i++) { - *dst++ = fill; + uint32_t fill32 = ((uint32_t)fill << 16) | fill; + uint32_t *dst32 = (uint32_t *)dst; + size_t pairs = count >> 1; + for (size_t i = 0; i < pairs; i++) { + dst32[i] = fill32; } - - return 0; + if (count & 1) { + dst[count - 1] = fill; + } + return dst; } static void DrawFrame(uint16_t *pixels) { int i; - int j; - static uint16_t scanlines[DISPLAY_HEIGHT][DISPLAY_WIDTH]; - unsigned int blendMode = (REG_BLDCNT >> 6) & 3; for (i = 0; i < DISPLAY_HEIGHT; i++) { + uint16_t *scanline = &pixels[i * DISPLAY_WIDTH]; + REG_VCOUNT = i; if (((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT) { REG_DISPSTAT |= INTR_FLAG_VCOUNT; @@ -1782,10 +1906,10 @@ static void DrawFrame(uint16_t *pixels) gIntrTable[INTR_INDEX_VCOUNT](); } - // Render the backdrop color before the each individual scanline. - // HBlank interrupt code could have changed it inbetween lines. - memsetu16(scanlines[i], *(uint16_t *)PLTT, DISPLAY_WIDTH); - DrawScanline(scanlines[i], i); + // Render the backdrop color before each individual scanline. + // HBlank interrupt code could have changed it in between lines. + memsetu16(scanline, *(uint16_t *)PLTT, DISPLAY_WIDTH); + DrawScanline(scanline, i); REG_DISPSTAT |= INTR_FLAG_HBLANK; @@ -1797,14 +1921,6 @@ static void DrawFrame(uint16_t *pixels) REG_DISPSTAT &= ~INTR_FLAG_HBLANK; REG_DISPSTAT &= ~INTR_FLAG_VCOUNT; } - - // Copy to screen - for (i = 0; i < DISPLAY_HEIGHT; i++) { - uint16_t *src = scanlines[i]; - for (j = 0; j < DISPLAY_WIDTH; j++) { - pixels[i * DISPLAY_WIDTH + j] = src[j]; - } - } } #if ENABLE_VRAM_VIEW @@ -1845,8 +1961,14 @@ void VramDraw(SDL_Texture *texture) void VDraw(SDL_Texture *texture) { - memset(gameImage, 0, sizeof(gameImage)); +// TODO: always use this renderer +#if TRUE + { + gpsp_draw_frame(gameImage); + } +#else DrawFrame(gameImage); +#endif SDL_UpdateTexture(texture, NULL, gameImage, DISPLAY_WIDTH * sizeof(Uint16)); REG_VCOUNT = DISPLAY_HEIGHT + 1; // prep for being in VBlank period } diff --git a/src/platform/ps2/ps2.c b/src/platform/ps2/ps2.c new file mode 100644 index 000000000..a42ebdecc --- /dev/null +++ b/src/platform/ps2/ps2.c @@ -0,0 +1,916 @@ +#include +#include + +// PS2 libs +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// Game +#include "global.h" +#include "core.h" +#include "multi_sio.h" +#include "gba/defines.h" +#include "gba/io_reg.h" +#include "gba/types.h" +#include "lib/agb_flash/flash_internal.h" + +// Emulation +#include "platform/shared/dma.h" +#include "platform/shared/audio/cgb_audio.h" +#include "platform/shared/video/gpsp_renderer.h" + +static GSGLOBAL *gsGlobal; +static GSTEXTURE screen; + +#ifndef TILE_WIDTH +#define TILE_WIDTH 8 +#endif + +extern IntrFunc gIntrTable[16]; + +ALIGNED(256) uint16_t gameImage[DISPLAY_WIDTH * (DISPLAY_HEIGHT + 1)]; + +struct VidMode { + const char *name; + s16 mode; + s16 interlace; + s16 field; + int max_width; + int max_height; + int width; + int height; + int vck; + int iPassCount; + int x_off; + int y_off; +}; + +static const struct VidMode vid_modes[] = { + { "240p", GS_MODE_NTSC, GS_NONINTERLACED, GS_FRAME, 652, 224, 320, 224, 2, 1, 0, 0 }, +#if !defined(VERSION_EU) + // NTSC + { "480i", GS_MODE_NTSC, GS_INTERLACED, GS_FIELD, 704, 480, 704, 452, 4, 1, 0, 0 }, + { "480p", GS_MODE_DTV_480P, GS_NONINTERLACED, GS_FRAME, 704, 480, 704, 452, 2, 1, 0, 0 }, +#else + // PAL + { "576i", GS_MODE_PAL, GS_INTERLACED, GS_FIELD, 704, 576, 704, 536, 4, 1, 0, 0 }, + { "576p", GS_MODE_DTV_576P, GS_NONINTERLACED, GS_FRAME, 704, 576, 704, 536, 2, 1, 0, 0 }, +#endif + // HDTV + { "720p", GS_MODE_DTV_720P, GS_NONINTERLACED, GS_FRAME, 1280, 720, 1280, 720, 1, 2, 0, 0 }, + { "1080i", GS_MODE_DTV_1080I, GS_INTERLACED, GS_FRAME, 1920, 1080, 1920, 1080, 1, 2, 0, 0 }, +}; + +static const struct VidMode *vid_mode; +static bool use_hires = false; + +static u8 padbuf[256] __attribute__((aligned(64))); +static int init_done = 0; + +static int joy_port = -1; +static int joy_slot = -1; +static int joy_id = -1; +static struct padButtonStatus joy_buttons __attribute__((aligned(64))); + +bool speedUp = false; +bool isRunning = true; +bool paused = false; +bool stepOneFrame = false; +bool headless = false; + +double lastGameTime = 0; +double curGameTime = 0; +double fixedTimestep = 1.0 / 60.0; // 16.666667ms +double timeScale = 1.0; +double accumulator = 0.0; + +static FILE *sSaveFile = NULL; + +extern void AgbMain(void); + +void VideoUpdateTexture(void); + +static void ReadSaveFile(char *path); +static void StoreSaveFile(void); +static void CloseSaveFile(void); + +u16 Platform_GetKeyInput(void); + +// Audio + +static bool AudioInit(void) +{ + if (init_audio_driver() != 0) + return false; + audsrv_set_volume(MAX_VOLUME); + + audsrv_fmt_t fmt; + + fmt.freq = 48000; + fmt.bits = 16; + fmt.channels = 2; + + if (audsrv_set_format(&fmt)) { + printf("audio_ps2: unsupported sound format\n"); + audsrv_quit(); + return false; + } + + return true; +} + +static void AudioPlay(const uint8_t *buf, size_t len) +{ + if ((audsrv_queued() / 4) < 6000) { + audsrv_play_audio(buf, len); + } +} + +// Video + +void VideoInit(void) +{ + if (vid_mode == NULL) { + vid_mode = &vid_modes[3]; // Standard def 480p + } else { + if (use_hires) { + gsKit_hires_deinit_global(gsGlobal); + } else { + gsKit_deinit_global(gsGlobal); + } + } + use_hires = (vid_mode->mode == GS_MODE_DTV_720P || vid_mode->mode == GS_MODE_DTV_1080I); + + if (use_hires) { + gsGlobal = gsKit_hires_init_global(); + } else { + gsGlobal = gsKit_init_global(); + } + + dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF); + + dmaKit_chan_init(DMA_CHANNEL_GIF); + + gsGlobal->Mode = vid_mode->mode; + gsGlobal->Width = vid_mode->width; + gsGlobal->Height = vid_mode->height; + if (gsGlobal->Mode == GS_MODE_DTV_1080I) { + gsGlobal->Height /= 2; + } + + gsGlobal->Interlace = vid_mode->interlace; + gsGlobal->Field = vid_mode->field; + gsGlobal->ZBuffering = GS_SETTING_ON; + gsGlobal->DoubleBuffering = GS_SETTING_ON; + gsGlobal->PrimAAEnable = GS_SETTING_OFF; + gsGlobal->Dithering = GS_SETTING_OFF; + gsGlobal->PSM = GS_PSM_CT16; + gsGlobal->PSMZ = GS_PSMZ_16; + + if (use_hires) { + gsKit_hires_init_screen(gsGlobal, vid_mode->iPassCount); + } else { + gsKit_init_screen(gsGlobal); + } + // hires sets the texture pointer to the wrong location. Ensure it's correct. + gsGlobal->TexturePointer = gsGlobal->CurrentPointer; + gsKit_TexManager_init(gsGlobal); + + screen.Width = DISPLAY_WIDTH; + screen.Height = DISPLAY_HEIGHT; + screen.PSM = GS_PSM_CT16; + screen.Mem = (void *)gameImage; +} + +void VideoUpdateTexture(void) +{ + gsKit_TexManager_invalidate(gsGlobal, &screen); + gsKit_TexManager_bind(gsGlobal, &screen); + + int startX = (gsGlobal->Width); + int startY = (gsGlobal->Height); + + gsKit_clear(gsGlobal, GS_SETREG_RGBAQ(0, 0, 0, 0, 0)); + + gsKit_prim_sprite_texture(gsGlobal, &screen, + 0.0f, // X1 + 0.0f, // Y2 + 0.0f, // U1 + 0.0f, // V1 + startX, // X2 + startY, // Y2 + gsGlobal->Width, // U2 + gsGlobal->Height, // V2 + 0, GS_SETREG_RGBAQ(128, 128, 128, 0, 0)); +} + +// Controller + +static inline int WaitPad(int tries) +{ + int state = padGetState(joy_port, joy_slot); + if (state == PAD_STATE_DISCONN) { + joy_id = -1; + return -1; + } + + while ((state != PAD_STATE_STABLE) && (state != PAD_STATE_FINDCTP1)) { + state = padGetState(joy_port, joy_slot); + if (--tries == 0) + break; + } + + return 0; +} + +static int DetectPad(void) +{ + int id = padInfoMode(joy_port, joy_slot, PAD_MODECURID, 0); + if (id <= 0) + return -1; + + const int ext = padInfoMode(joy_port, joy_slot, PAD_MODECUREXID, 0); + if (ext) + id = ext; + + printf("ControllerInit: detected pad type %d\n", id); + + if (id == PAD_TYPE_DIGITAL || id == PAD_TYPE_DUALSHOCK) + padSetMainMode(joy_port, joy_slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK); + + return id; +} + +static void ControllerInit(void) +{ + int ret = -1; + + // MEMORY CARD already initied SIO2MAN + ret = init_joystick_driver(false); + + if (ret != 0) { + printf("ControllerInit: failed to init joystick driver: %d\n", ret); + return; + } + + const int numports = padGetPortMax(); + // Find the first device connected + for (int port = 0; port < numports && joy_port < 0; ++port) { + if (joy_port == -1 && joy_slot == -1 && mtapPortOpen(port)) { + const int maxslots = padGetSlotMax(port); + for (int slot = 0; slot < maxslots; ++slot) { + if (joy_port == -1 && joy_slot == -1 && padPortOpen(port, slot, padbuf) >= 0) { + joy_port = port; + joy_slot = slot; + printf("ControllerInit: using pad (%d, %d)\n", port, slot); + break; + } + } + } + } + + if (joy_slot < 0 || joy_port < 0) { + printf("ControllerInit: could not open a single port\n"); + return; + } + + init_done = 1; +} + +static u16 ControllerRead(void) +{ + if (!init_done) + return 0; + + if (WaitPad(10) < 0) + return 0; // nothing received + + if (joy_id < 0) { + // pad not detected yet, do it + joy_id = DetectPad(); + if (joy_id < 0) + return 0; // still nothing + if (WaitPad(10) < 0) + return 0; + } + + if (padRead(joy_port, joy_slot, &joy_buttons)) { + return 0xffff ^ joy_buttons.btns; + } + + return 0; +} + +// PS2 Drivers +void ResetIOP() +{ + SifInitRpc(0); + while (!SifIopReset(NULL, 0)) { } // Comment this line if you want to "debug" through ps2link + while (!SifIopSync()) { } +} + +static void PrepareIOP() +{ + ResetIOP(); + SifInitRpc(0); + sbv_patch_enable_lmb(); + sbv_patch_disable_prefix_check(); +} + +static void InitPS2Drivers() +{ + init_only_boot_ps2_filesystem_driver(); + init_memcard_driver(true); +} + +static void deInitPS2Drivers() +{ + deinit_memcard_driver(true); + deinit_only_boot_ps2_filesystem_driver(); +} + +int main(int argc, char **argv) +{ + PrepareIOP(); + InitPS2Drivers(); + + // ReadSaveFile("sa2.sav"); + + // Prevent the multiplayer screen from being drawn ( see core.c:EngineInit() ) + REG_RCNT = 0x8000; + REG_KEYINPUT = 0x3FF; + + AudioInit(); + VideoInit(); + ControllerInit(); + + cgb_audio_init(48000); + + VideoUpdateTexture(); + + // Allow the game loop to take control + AgbMain(); + + return 0; +} + +// GBA callbacks +void VBlankIntrWait(void) +{ +#define HANDLE_VBLANK_INTRS() \ + ({ \ + REG_VCOUNT = DISPLAY_HEIGHT + 1; \ + REG_DISPSTAT |= INTR_FLAG_VBLANK; \ + RunDMAs(DMA_VBLANK); \ + if (REG_DISPSTAT & DISPSTAT_VBLANK_INTR) \ + gIntrTable[INTR_INDEX_VBLANK](); \ + REG_DISPSTAT &= ~INTR_FLAG_VBLANK; \ + }) + + if (headless) { + REG_VCOUNT = DISPLAY_HEIGHT + 1; + HANDLE_VBLANK_INTRS(); + return; + } + + if (isRunning) { + REG_KEYINPUT = KEYS_MASK ^ Platform_GetKeyInput(); + + gpsp_draw_frame(gameImage); + + VideoUpdateTexture(); + HANDLE_VBLANK_INTRS(); + + if (use_hires) { + gsKit_hires_sync(gsGlobal); + gsKit_hires_flip(gsGlobal); + // TODO: use once released on the SDK + // gsKit_hires_flip_ext(gsGlobal, GSFLIP_RATE_LIMIT_1); + } else { + gsKit_sync_flip(gsGlobal); + gsKit_queue_exec(gsGlobal); + } + gsKit_TexManager_nextFrame(gsGlobal); + return; + } + + CloseSaveFile(); + + deInitPS2Drivers(); + exit(0); +#undef HANDLE_VBLANK_INTRS +} + +void Platform_StoreSaveFile(void) { StoreSaveFile(); } + +s16 convertedAudio[4096]; + +void Platform_QueueAudio(const s16 *data, uint32_t bytesCount) +{ + if (headless) { + return; + } + AudioPlay((uint8_t *)data, bytesCount); +} + +u16 Platform_GetKeyInput(void) +{ + static struct { + u16 gbaBtn; + u16 sceBtn; + } binds[] = { + { A_BUTTON, PAD_CROSS }, { B_BUTTON, PAD_SQUARE }, { L_BUTTON, PAD_L2 }, { R_BUTTON, PAD_R2 }, + { L_BUTTON, PAD_L1 }, { R_BUTTON, PAD_R1 }, { START_BUTTON, PAD_START }, { DPAD_LEFT, PAD_LEFT }, + { DPAD_RIGHT, PAD_RIGHT }, { DPAD_UP, PAD_UP }, { DPAD_DOWN, PAD_DOWN }, + }; + + u16 keys = 0; + u16 btns = ControllerRead(); + + for (int i = 0; i < ARRAY_COUNT(binds); ++i) + if (btns & binds[i].sceBtn) + keys |= binds[i].gbaBtn; + + return keys; +} + +static void ReadSaveFile(char *path) +{ + // Check whether the saveFile exists, and create it if not + sSaveFile = fopen(path, "r+b"); + if (sSaveFile == NULL) { + sSaveFile = fopen(path, "w+b"); + } + + fseek(sSaveFile, 0, SEEK_END); + int fileSize = ftell(sSaveFile); + fseek(sSaveFile, 0, SEEK_SET); + + // Only read as many bytes as fit inside the buffer + // or as many bytes as are in the file + int bytesToRead = (fileSize < sizeof(FLASH_BASE)) ? fileSize : sizeof(FLASH_BASE); + + int bytesRead = fread(FLASH_BASE, 1, bytesToRead, sSaveFile); + + // Fill the buffer if the savefile was just created or smaller than the buffer itself + for (int i = bytesRead; i < sizeof(FLASH_BASE); i++) { + FLASH_BASE[i] = 0xFF; + } +} + +static void StoreSaveFile() +{ + if (sSaveFile != NULL) { + fseek(sSaveFile, 0, SEEK_SET); + fwrite(FLASH_BASE, 1, sizeof(FLASH_BASE), sSaveFile); + } +} + +static void CloseSaveFile() +{ + if (sSaveFile != NULL) { + fclose(sSaveFile); + } +} + +// BIOS function implementations are based on the VBA-M source code. +// TODO: Link these functions from Libagbsyscall + +// safe unaligned access for MIPS +static uint32_t CPUReadMemory(const void *src) +{ + uint32_t val; + memcpy(&val, src, sizeof(val)); + return val; +} + +static void CPUWriteMemory(void *dest, uint32_t val) { memcpy(dest, &val, sizeof(val)); } + +static uint16_t CPUReadHalfWord(const void *src) +{ + uint16_t val; + memcpy(&val, src, sizeof(val)); + return val; +} + +static void CPUWriteHalfWord(void *dest, uint16_t val) { memcpy(dest, &val, sizeof(val)); } + +static uint8_t CPUReadByte(const void *src) { return *(uint8_t *)src; } + +static void CPUWriteByte(void *dest, uint8_t val) { *(uint8_t *)dest = val; } + +void CpuSet(const void *src, void *dst, u32 cnt) +{ + if (dst == NULL) { + puts("Attempted to CpuSet to NULL\n"); + return; + } + + int count = cnt & 0x1FFFFF; + + const u8 *source = src; + u8 *dest = dst; + + // 32-bit ? + if ((cnt >> 26) & 1) { + // assert(((uintptr_t)src & ~3) == (uintptr_t)src); + // assert(((uintptr_t)dst & ~3) == (uintptr_t)dst); + + // needed for 32-bit mode! + // source = (u8 *)((uint32_t )source & ~3); + // dest = (u8 *)((uint32_t )dest & ~3); + + // fill ? + if ((cnt >> 24) & 1) { + uint32_t value = CPUReadMemory(source); + while (count) { + CPUWriteMemory(dest, value); + dest += 4; + count--; + } + } else { + // copy + while (count) { + CPUWriteMemory(dest, CPUReadMemory(source)); + source += 4; + dest += 4; + count--; + } + } + } else { + // No align on 16-bit fill? + // assert(((uintptr_t)src & ~1) == (uintptr_t)src); + // assert(((uintptr_t)dst & ~1) == (uintptr_t)dst); + + // 16-bit fill? + if ((cnt >> 24) & 1) { + uint16_t value = CPUReadHalfWord(source); + while (count) { + CPUWriteHalfWord(dest, value); + dest += 2; + count--; + } + } else { + // copy + while (count) { + CPUWriteHalfWord(dest, CPUReadHalfWord(source)); + source += 2; + dest += 2; + count--; + } + } + } +} + +void CpuFastSet(const void *src, void *dst, u32 cnt) +{ + if (dst == NULL) { + puts("Attempted to CpuFastSet to NULL\n"); + return; + } + + int count = cnt & 0x1FFFFF; + + const u8 *source = src; + u8 *dest = dst; + + // source = (u8 *)((uint32_t )source & ~3); + // dest = (u8 *)((uint32_t )dest & ~3); + + // fill? + if ((cnt >> 24) & 1) { + uint32_t value = CPUReadMemory(source); + while (count > 0) { + // BIOS always transfers 32 bytes at a time + for (int i = 0; i < 8; i++) { + CPUWriteMemory(dest, value); + dest += 4; + } + count -= 8; + } + } else { + // copy + while (count > 0) { + // BIOS always transfers 32 bytes at a time + for (int i = 0; i < 8; i++) { + uint32_t value = CPUReadMemory(source); + CPUWriteMemory(dest, value); + source += 4; + dest += 4; + } + count -= 8; + } + } +} + +void LZ77UnCompVram(const void *src_, void *dest_) +{ + const u8 *src = (const u8 *)src_; + u8 *dest = dest_; + int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; + int srcPos = 4; + int destPos = 0; + + for (;;) { + unsigned char flags = src[srcPos++]; + + for (int i = 0; i < 8; i++) { + if (flags & 0x80) { + int blockSize = (src[srcPos] >> 4) + 3; + int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1; + + srcPos += 2; + + int blockPos = destPos - blockDistance; + + // Some Ruby/Sapphire tilesets overflow. + if (destPos + blockSize > destSize) { + blockSize = destSize - destPos; + // fprintf(stderr, "Destination buffer overflow.\n"); + puts("Destination buffer overflow.\n"); + } + + if (blockPos < 0) + goto fail; + + for (int j = 0; j < blockSize; j++) + dest[destPos++] = dest[blockPos + j]; + } else { + if (destPos >= destSize) + goto fail; + + dest[destPos++] = src[srcPos++]; + } + + if (destPos == destSize) { + return; + } + + flags <<= 1; + } + } + +fail: + puts("Fatal error while decompressing LZ file.\n"); +} + +void LZ77UnCompWram(const void *src, void *dst) +{ + const uint8_t *source = src; + uint8_t *dest = dst; + + uint32_t header = CPUReadMemory(source); + source += 4; + + int len = header >> 8; + + while (len > 0) { + uint8_t d = CPUReadByte(source++); + + if (d) { + for (int i = 0; i < 8; i++) { + if (d & 0x80) { + uint16_t data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + uint8_t *windowOffset = dest - offset - 1; + for (int i2 = 0; i2 < length; i2++) { + CPUWriteByte(dest++, CPUReadByte(windowOffset++)); + len--; + if (len == 0) + return; + } + } else { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if (len == 0) + return; + } + d <<= 1; + } + } else { + for (int i = 0; i < 8; i++) { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if (len == 0) + return; + } + } + } +} + +void RLUnCompWram(const void *src, void *dest) +{ + int remaining = CPUReadMemory(src) >> 8; + int blockHeader; + int block; + src += 4; + while (remaining > 0) { + blockHeader = CPUReadByte(src); + src++; + if (blockHeader & 0x80) // Compressed? + { + blockHeader &= 0x7F; + blockHeader += 3; + block = CPUReadByte(src); + src++; + while (blockHeader-- && remaining) { + remaining--; + CPUWriteByte(dest, block); + dest++; + } + } else // Uncompressed + { + blockHeader++; + while (blockHeader-- && remaining) { + remaining--; + u8 byte = CPUReadByte(src); + src++; + CPUWriteByte(dest, byte); + dest++; + } + } + } +} + +void RLUnCompVram(const void *src, void *dest) +{ + int remaining = CPUReadMemory(src) >> 8; + int padding = (4 - remaining) & 0x3; + int blockHeader; + int block; + int halfWord = 0; + src += 4; + while (remaining > 0) { + blockHeader = CPUReadByte(src); + src++; + if (blockHeader & 0x80) // Compressed? + { + blockHeader &= 0x7F; + blockHeader += 3; + block = CPUReadByte(src); + src++; + while (blockHeader-- && remaining) { + remaining--; + if ((uintptr_t)dest & 1) { + halfWord |= block << 8; + CPUWriteHalfWord((void *)((uintptr_t)dest ^ 1), halfWord); + } else + halfWord = block; + dest++; + } + } else // Uncompressed + { + blockHeader++; + while (blockHeader-- && remaining) { + remaining--; + u8 byte = CPUReadByte(src); + src++; + if ((uintptr_t)dest & 1) { + halfWord |= byte << 8; + CPUWriteHalfWord((void *)((uintptr_t)dest ^ 1), halfWord); + } else + halfWord = byte; + dest++; + } + } + } + if ((uintptr_t)dest & 1) { + padding--; + dest++; + } + for (; padding > 0; padding -= 2, dest += 2) + CPUWriteHalfWord(dest, 0); +} + +const s16 sineTable[256] + = { (s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1, (s16)0x0C7C, (s16)0x0E05, + (s16)0x0F8C, (s16)0x1111, (s16)0x1294, (s16)0x1413, (s16)0x158F, (s16)0x1708, (s16)0x187D, (s16)0x19EF, (s16)0x1B5D, (s16)0x1CC6, + (s16)0x1E2B, (s16)0x1F8B, (s16)0x20E7, (s16)0x223D, (s16)0x238E, (s16)0x24DA, (s16)0x261F, (s16)0x275F, (s16)0x2899, (s16)0x29CD, + (s16)0x2AFA, (s16)0x2C21, (s16)0x2D41, (s16)0x2E5A, (s16)0x2F6B, (s16)0x3076, (s16)0x3179, (s16)0x3274, (s16)0x3367, (s16)0x3453, + (s16)0x3536, (s16)0x3612, (s16)0x36E5, (s16)0x37AF, (s16)0x3871, (s16)0x392A, (s16)0x39DA, (s16)0x3A82, (s16)0x3B20, (s16)0x3BB6, + (s16)0x3C42, (s16)0x3CC5, (s16)0x3D3E, (s16)0x3DAE, (s16)0x3E14, (s16)0x3E71, (s16)0x3EC5, (s16)0x3F0E, (s16)0x3F4E, (s16)0x3F84, + (s16)0x3FB1, (s16)0x3FD3, (s16)0x3FEC, (s16)0x3FFB, (s16)0x4000, (s16)0x3FFB, (s16)0x3FEC, (s16)0x3FD3, (s16)0x3FB1, (s16)0x3F84, + (s16)0x3F4E, (s16)0x3F0E, (s16)0x3EC5, (s16)0x3E71, (s16)0x3E14, (s16)0x3DAE, (s16)0x3D3E, (s16)0x3CC5, (s16)0x3C42, (s16)0x3BB6, + (s16)0x3B20, (s16)0x3A82, (s16)0x39DA, (s16)0x392A, (s16)0x3871, (s16)0x37AF, (s16)0x36E5, (s16)0x3612, (s16)0x3536, (s16)0x3453, + (s16)0x3367, (s16)0x3274, (s16)0x3179, (s16)0x3076, (s16)0x2F6B, (s16)0x2E5A, (s16)0x2D41, (s16)0x2C21, (s16)0x2AFA, (s16)0x29CD, + (s16)0x2899, (s16)0x275F, (s16)0x261F, (s16)0x24DA, (s16)0x238E, (s16)0x223D, (s16)0x20E7, (s16)0x1F8B, (s16)0x1E2B, (s16)0x1CC6, + (s16)0x1B5D, (s16)0x19EF, (s16)0x187D, (s16)0x1708, (s16)0x158F, (s16)0x1413, (s16)0x1294, (s16)0x1111, (s16)0x0F8C, (s16)0x0E05, + (s16)0x0C7C, (s16)0x0AF1, (s16)0x0964, (s16)0x07D5, (s16)0x0645, (s16)0x04B5, (s16)0x0323, (s16)0x0192, (s16)0x0000, (s16)0xFE6E, + (s16)0xFCDD, (s16)0xFB4B, (s16)0xF9BB, (s16)0xF82B, (s16)0xF69C, (s16)0xF50F, (s16)0xF384, (s16)0xF1FB, (s16)0xF074, (s16)0xEEEF, + (s16)0xED6C, (s16)0xEBED, (s16)0xEA71, (s16)0xE8F8, (s16)0xE783, (s16)0xE611, (s16)0xE4A3, (s16)0xE33A, (s16)0xE1D5, (s16)0xE075, + (s16)0xDF19, (s16)0xDDC3, (s16)0xDC72, (s16)0xDB26, (s16)0xD9E1, (s16)0xD8A1, (s16)0xD767, (s16)0xD633, (s16)0xD506, (s16)0xD3DF, + (s16)0xD2BF, (s16)0xD1A6, (s16)0xD095, (s16)0xCF8A, (s16)0xCE87, (s16)0xCD8C, (s16)0xCC99, (s16)0xCBAD, (s16)0xCACA, (s16)0xC9EE, + (s16)0xC91B, (s16)0xC851, (s16)0xC78F, (s16)0xC6D6, (s16)0xC626, (s16)0xC57E, (s16)0xC4E0, (s16)0xC44A, (s16)0xC3BE, (s16)0xC33B, + (s16)0xC2C2, (s16)0xC252, (s16)0xC1EC, (s16)0xC18F, (s16)0xC13B, (s16)0xC0F2, (s16)0xC0B2, (s16)0xC07C, (s16)0xC04F, (s16)0xC02D, + (s16)0xC014, (s16)0xC005, (s16)0xC000, (s16)0xC005, (s16)0xC014, (s16)0xC02D, (s16)0xC04F, (s16)0xC07C, (s16)0xC0B2, (s16)0xC0F2, + (s16)0xC13B, (s16)0xC18F, (s16)0xC1EC, (s16)0xC252, (s16)0xC2C2, (s16)0xC33B, (s16)0xC3BE, (s16)0xC44A, (s16)0xC4E0, (s16)0xC57E, + (s16)0xC626, (s16)0xC6D6, (s16)0xC78F, (s16)0xC851, (s16)0xC91B, (s16)0xC9EE, (s16)0xCACA, (s16)0xCBAD, (s16)0xCC99, (s16)0xCD8C, + (s16)0xCE87, (s16)0xCF8A, (s16)0xD095, (s16)0xD1A6, (s16)0xD2BF, (s16)0xD3DF, (s16)0xD506, (s16)0xD633, (s16)0xD767, (s16)0xD8A1, + (s16)0xD9E1, (s16)0xDB26, (s16)0xDC72, (s16)0xDDC3, (s16)0xDF19, (s16)0xE075, (s16)0xE1D5, (s16)0xE33A, (s16)0xE4A3, (s16)0xE611, + (s16)0xE783, (s16)0xE8F8, (s16)0xEA71, (s16)0xEBED, (s16)0xED6C, (s16)0xEEEF, (s16)0xF074, (s16)0xF1FB, (s16)0xF384, (s16)0xF50F, + (s16)0xF69C, (s16)0xF82B, (s16)0xF9BB, (s16)0xFB4B, (s16)0xFCDD, (s16)0xFE6E }; + +void SoftReset(u32 resetFlags) { } + +void SoftResetExram(u32 resetFlags) { } + +// Following functions taken from mGBA's source +u16 ArcTan(s16 i) +{ + s32 a = -((i * i) >> 14); + s32 b = ((0xA9 * a) >> 14) + 0x390; + b = ((b * a) >> 14) + 0x91C; + b = ((b * a) >> 14) + 0xFB6; + b = ((b * a) >> 14) + 0x16AA; + b = ((b * a) >> 14) + 0x2081; + b = ((b * a) >> 14) + 0x3651; + b = ((b * a) >> 14) + 0xA2F9; + + return (i * b) >> 16; +} + +u16 ArcTan2(s16 x, s16 y) +{ + if (!y) { + if (x >= 0) + return 0; + return 0x8000; + } + if (!x) { + if (y >= 0) + return 0x4000; + return 0xC000; + } + if (y >= 0) { + if (x >= 0) { + if (x >= y) + return ArcTan((y << 14) / x); + } else if (-x >= y) + return ArcTan((y << 14) / x) + 0x8000; + return 0x4000 - ArcTan((x << 14) / y); + } else { + if (x <= 0) { + if (-x > -y) + return ArcTan((y << 14) / x) + 0x8000; + } else if (x >= -y) + return ArcTan((y << 14) / x) + 0x10000; + return 0xC000 - ArcTan((x << 14) / y); + } +} + +u16 Sqrt(u32 num) +{ + if (!num) + return 0; + u32 lower; + u32 upper = num; + u32 bound = 1; + while (bound < upper) { + upper >>= 1; + bound <<= 1; + } + while (1) { + upper = num; + u32 accum = 0; + lower = bound; + while (1) { + u32 oldLower = lower; + if (lower <= upper >> 1) + lower <<= 1; + if (oldLower >= upper >> 1) + break; + } + while (1) { + accum <<= 1; + if (upper >= lower) { + ++accum; + upper -= lower; + } + if (lower == bound) + break; + lower >>= 1; + } + u32 oldBound = bound; + bound += accum; + bound >>= 1; + if (bound >= oldBound) { + bound = oldBound; + break; + } + } + return bound; +} + +int MultiBoot(struct MultiBootParam *mp) { return 0; } diff --git a/src/platform/shared/audio/cgb_audio.c b/src/platform/shared/audio/cgb_audio.c index 787a65f43..035d18f17 100644 --- a/src/platform/shared/audio/cgb_audio.c +++ b/src/platform/shared/audio/cgb_audio.c @@ -3,14 +3,15 @@ #include "platform/shared/audio/cgb_tables.h" static struct AudioCGB gb; -static float soundChannelPos[4]; -static const s16 *PU1Table; -static const s16 *PU2Table; +static fixed8_24 soundChannelPos[4]; +static const fixed8_24 *PU1Table; +static const fixed8_24 *PU2Table; static u32 apuFrame; static u8 apuCycle; static u32 sampleRate; static u16 lfsrMax[2]; -float ch4Samples; +fixed8_24 ch4Samples; +fixed8_24 volScale[16]; void cgb_audio_init(u32 rate) { @@ -31,7 +32,7 @@ void cgb_audio_init(u32 rate) gb.DAC[ch] = 0; soundChannelPos[ch] = 0; } - soundChannelPos[1] = 1; + soundChannelPos[1] = u32_to_fp8_24(1); PU1Table = PU0; PU2Table = PU0; sampleRate = rate; @@ -39,7 +40,9 @@ void cgb_audio_init(u32 rate) gb.ch4LFSR[1] = 0x80; lfsrMax[0] = 0x8000; lfsrMax[1] = 0x80; - ch4Samples = 0.0f; + ch4Samples = 0; + for (int i = 0; i < 16; i++) + volScale[i] = u32_to_fp8_24(i) / 15; } void cgb_set_sweep(u8 sweep) @@ -52,8 +55,8 @@ void cgb_set_sweep(u8 sweep) void cgb_set_wavram() { for (u8 wavi = 0; wavi < 0x10; wavi++) { - gb.WAVRAM[(wavi << 1)] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0xF0) >> 4) / 7.5f - 1.0f; - gb.WAVRAM[(wavi << 1) + 1] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0x0F)) / 7.5f - 1.0f; + gb.WAVRAM[(wavi << 1)] = (u32_to_fp8_24(((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0xF0) >> 4) * 2) / 15 - u32_to_fp8_24(1); + gb.WAVRAM[(wavi << 1) + 1] = (u32_to_fp8_24(((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0x0F)) * 2) / 15 - u32_to_fp8_24(1); } } @@ -103,7 +106,7 @@ void cgb_trigger_note(u8 channel) void cgb_audio_generate(u16 samplesPerFrame) { - float *outBuffer = gb.outBuffer; + fixed8_24 *outBuffer = gb.outBuffer; switch (REG_NR11 & 0xC0) { case 0x00: PU1Table = PU0; @@ -193,75 +196,80 @@ void cgb_audio_generate(u16 samplesPerFrame) } } // Sound generation loop - soundChannelPos[0] += freqTable[REG_SOUND1CNT_X & 0x7FF] / (sampleRate / 32); - soundChannelPos[1] += freqTable[REG_SOUND2CNT_H & 0x7FF] / (sampleRate / 32); - soundChannelPos[2] += freqTable[REG_SOUND3CNT_X & 0x7FF] / (sampleRate / 32); - while (soundChannelPos[0] >= 32) - soundChannelPos[0] -= 32; - while (soundChannelPos[1] >= 32) - soundChannelPos[1] -= 32; - while (soundChannelPos[2] >= 32) - soundChannelPos[2] -= 32; - float outputL = 0; - float outputR = 0; + soundChannelPos[0] += freqTable[REG_SOUND1CNT_X & (ARRAY_COUNT(freqTable) - 1)]; + soundChannelPos[1] += freqTable[REG_SOUND2CNT_H & (ARRAY_COUNT(freqTable) - 1)]; + soundChannelPos[2] += freqTable[REG_SOUND3CNT_X & (ARRAY_COUNT(freqTable) - 1)]; + + soundChannelPos[0] &= (u32_to_fp8_24(32)) - 1; + soundChannelPos[1] &= (u32_to_fp8_24(32)) - 1; + soundChannelPos[2] &= (u32_to_fp8_24(32)) - 1; + + fixed8_24 outputL = 0; + fixed8_24 outputR = 0; if (REG_NR52 & 0x80) { if ((gb.DAC[0]) && (REG_NR52 & 0x01)) { if (REG_NR51 & 0x10) - outputL += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f; + outputL += (gb.Vol[0] * PU1Table[fp8_24_to_u32(soundChannelPos[0])]); if (REG_NR51 & 0x01) - outputR += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f; + outputR += (gb.Vol[0] * PU1Table[fp8_24_to_u32(soundChannelPos[0])]); } if ((gb.DAC[1]) && (REG_NR52 & 0x02)) { if (REG_NR51 & 0x20) - outputL += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f; + outputL += (gb.Vol[1] * PU2Table[fp8_24_to_u32(soundChannelPos[1])]); if (REG_NR51 & 0x02) - outputR += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f; + outputR += (gb.Vol[1] * PU2Table[fp8_24_to_u32(soundChannelPos[1])]); } if ((REG_NR30 & 0x80) && (REG_NR52 & 0x04)) { if (REG_NR51 & 0x40) - outputL += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f; + outputL += (gb.Vol[2] * gb.WAVRAM[fp8_24_to_u32(soundChannelPos[2])] >> 2); if (REG_NR51 & 0x04) - outputR += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f; + outputR += (gb.Vol[2] * gb.WAVRAM[fp8_24_to_u32(soundChannelPos[2])] >> 2); } if ((gb.DAC[3]) && (REG_NR52 & 0x08)) { bool32 lfsrMode = ((REG_NR43 & 0x08) == 8); - ch4Samples += freqTableNSE[REG_SOUND4CNT_H & 0xFF] / sampleRate; + ch4Samples += freqTableNSE[REG_SOUND4CNT_H & (ARRAY_COUNT(freqTableNSE) - 1)]; int ch4Out = 0; if (gb.ch4LFSR[lfsrMode] & 1) { ch4Out++; } else { ch4Out--; } - int avgDiv = 1; - while (ch4Samples >= 1) { - avgDiv++; - bool8 lfsrCarry = 0; - if (gb.ch4LFSR[lfsrMode] & 2) - lfsrCarry ^= 1; - gb.ch4LFSR[lfsrMode] >>= 1; - if (gb.ch4LFSR[lfsrMode] & 2) - lfsrCarry ^= 1; - if (lfsrCarry) - gb.ch4LFSR[lfsrMode] |= lfsrMax[lfsrMode]; - if (gb.ch4LFSR[lfsrMode] & 1) { - ch4Out++; - } else { - ch4Out--; - } - ch4Samples--; + int steps = fp8_24_to_u32(ch4Samples); + ch4Samples = fp8_24_fractional_part(ch4Samples); + + u16 lfsr = gb.ch4LFSR[lfsrMode]; + u16 lfsrMask = lfsrMax[lfsrMode]; + + for (int i = 0; i < steps; i++) { + // Comments to show what the bit + // manipulation here is representing + + // if (gb.ch4LFSR[lfsrMode] & 2) + u16 lfsrCarry = (lfsr >> 1) & 1; + lfsr >>= 1; + // if (gb.ch4LFSR[lfsrMode] & 2) + lfsrCarry ^= (lfsr >> 1) & 1; + // if (lfsrCarry) gb.ch4LFSR[lfsrMode] |= lfsrMax[lfsrMode]; + lfsr |= -lfsrCarry & lfsrMask; + // if (gb.ch4LFSR[lfsrMode] & 1) + ch4Out += (lfsr & 1) ? 1 : -1; } - float sample = ch4Out; - if (avgDiv > 1) - sample /= avgDiv; + gb.ch4LFSR[lfsrMode] = lfsr; + + fixed8_24 sample = u32_to_fp8_24(ch4Out); + if (steps > 0) + sample /= (steps + 1); + + // Muliply by the sample and then shift to make 8.24 again if (REG_NR51 & 0x80) - outputL += gb.Vol[3] * sample / 15.0f; + outputL += ((s64)sample * volScale[gb.Vol[3]]) >> 24; if (REG_NR51 & 0x08) - outputR += gb.Vol[3] * sample / 15.0f; + outputR += ((s64)sample * volScale[gb.Vol[3]]) >> 24; } } - outBuffer[0] = outputL * 0.25f; - outBuffer[1] = outputR * 0.25f; + outBuffer[0] = (outputL >> 2); + outBuffer[1] = (outputR >> 2); } } -float *cgb_get_buffer() { return gb.outBuffer; } +fixed8_24 *cgb_get_buffer() { return gb.outBuffer; } diff --git a/src/platform/shared/audio/m4a_sound_mixer.c b/src/platform/shared/audio/m4a_sound_mixer.c index 49b0d65bc..d0bf1ba8b 100644 --- a/src/platform/shared/audio/m4a_sound_mixer.c +++ b/src/platform/shared/audio/m4a_sound_mixer.c @@ -5,9 +5,9 @@ #include "platform/platform.h" #include "platform/shared/audio/cgb_audio.h" -static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData *wav, float *pcmBuffer, +static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData *wav, fixed8_24 *pcmBuffer, u16 samplesPerFrame, float sampleRateReciprocal); -static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *pcmBuffer, u8 dmaCounter, +static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, fixed8_24 *pcmBuffer, u8 dmaCounter, u16 maxBufSize); static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData *wav); static void ChnVolSetAsm(struct MixerSource *chan, struct MP2KTrack *track); @@ -25,12 +25,15 @@ static void ChnVolSetAsm(struct MixerSource *chan, struct MP2KTrack *track); #define FLOOR_DIV_POW2(a, b) ((a) > 0 ? (a) / (b) : (((a) + 1 - (b)) / (b))) #endif -static float audioBuffer[PCM_DMA_BUF_SIZE]; +static s16 audioBuffer[PCM_DMA_BUF_SIZE]; static struct SoundMixerState sSoundInfo = { 0 }; struct SoundMixerState *SOUND_INFO_PTR = &sSoundInfo; void SoundMain(void) { +#if !ENABLE_AUDIO + return; +#endif struct SoundMixerState *mixer = SOUND_INFO_PTR; if (mixer->lockStatus != ID_NUMBER) { @@ -54,7 +57,7 @@ void SoundMain(void) mixer->CgbSound(); s32 samplesPerFrame = mixer->samplesPerFrame; - float *pcmBuffer = mixer->pcmBuffer; + fixed8_24 *pcmBuffer = mixer->pcmBuffer; s32 dmaCounter = mixer->dmaCounter; if (dmaCounter > 1) { @@ -65,7 +68,7 @@ void SoundMain(void) cgb_audio_generate(samplesPerFrame); } -static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *pcmBuffer, u8 dmaCounter, +static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, fixed8_24 *pcmBuffer, u8 dmaCounter, u16 maxBufSize) { u32 reverb = mixer->reverb; @@ -73,8 +76,8 @@ static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 sa // The vanilla reverb effect outputs a mono sound from four sources: // - L/R channels as they were mixer->framesPerDmaCycle frames ago // - L/R channels as they were (mixer->framesPerDmaCycle - 1) frames ago - float *tmp1 = pcmBuffer; - float *tmp2; + fixed8_24 *tmp1 = pcmBuffer; + fixed8_24 *tmp2; if (dmaCounter == 2) { tmp2 = mixer->pcmBuffer; } else { @@ -82,8 +85,8 @@ static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 sa } u16 i = 0; do { - float s = tmp1[0] + tmp1[1] + tmp2[0] + tmp2[1]; - s *= ((float)reverb / 512.0f); + fixed8_24 s = tmp1[0] + tmp1[1] + tmp2[0] + tmp2[1]; + s = (s * reverb) >> 9; tmp1[0] = tmp1[1] = s; tmp1 += 2; tmp2 += 2; @@ -92,7 +95,7 @@ static void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 sa // memset(pcmBuffer, 0, samplesPerFrame); // memset(pcmBuffer + maxBufSize, 0, samplesPerFrame); for (int i = 0; i < samplesPerFrame; i++) { - float *dst = &pcmBuffer[i * 2]; + fixed8_24 *dst = &pcmBuffer[i * 2]; dst[1] = dst[0] = 0.0f; } } @@ -225,7 +228,7 @@ static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData *wav } } -static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData *wav, float *pcmBuffer, +static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData *wav, fixed8_24 *pcmBuffer, u16 samplesPerFrame, float sampleRateReciprocal) { /*, [[[]]]) {*/ u8 v = chan->data.sound.envelopeVol * (mixer->masterVol + 1) / 16U; @@ -240,15 +243,19 @@ static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSour } s32 samplesLeftInWav = chan->data.sound.ct; s8 *current = chan->current; - signed envR = chan->data.sound.envelopeVolR; - signed envL = chan->data.sound.envelopeVolL; + + fixed8_24 envR = chan->data.sound.envelopeVolR << 9; // (* 32768) + fixed8_24 envL = chan->data.sound.envelopeVolL << 9; if (chan->type & TONEDATA_TYPE_CGB) { for (u16 i = 0; i < samplesPerFrame; i++, pcmBuffer += 2) { s8 c = *(current++); - pcmBuffer[1] += (c * envR) / 32768.0f; - pcmBuffer[0] += (c * envL) / 32768.0f; + // Creates a value between -32768 and 32768 + // So shift by 9 to make this between -1 and 1 + // in 8.24 + pcmBuffer[1] += (c * envR); + pcmBuffer[0] += (c * envL); if (--samplesLeftInWav == 0) { samplesLeftInWav = loopLen; if (loopLen != 0) { @@ -263,8 +270,8 @@ static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSour chan->data.sound.ct = samplesLeftInWav; chan->current = current; } else { - float finePos = chan->data.sound.fw; - float romSamplesPerOutputSample = chan->data.sound.freq * sampleRateReciprocal; + fixed8_24 finePos = chan->data.sound.fw; + fixed8_24 romSamplesPerOutputSample = float_to_fp8_24(chan->data.sound.freq * sampleRateReciprocal); s16 b = current[0]; s16 m = current[1] - b; @@ -273,15 +280,15 @@ static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSour for (u16 i = 0; i < samplesPerFrame; i++, pcmBuffer += 2) { // Use linear interpolation to calculate a value between the current sample in the wav // and the next sample. Also cancel out the 9.23 stuff - float sample = (finePos * m) + b; + fixed8_24 sample = ((finePos * m) + u32_to_fp8_24(b)); - pcmBuffer[1] += (sample * envR) / 32768.0f; - pcmBuffer[0] += (sample * envL) / 32768.0f; + pcmBuffer[1] += ((s64)sample * envR) >> 24; + pcmBuffer[0] += ((s64)sample * envL) >> 24; finePos += romSamplesPerOutputSample; - u32 newCoarsePos = finePos; + u32 newCoarsePos = fp8_24_to_u32(finePos); if (newCoarsePos != 0) { - finePos -= (int)finePos; + finePos = fp8_24_fractional_part(finePos); samplesLeftInWav -= newCoarsePos; if (samplesLeftInWav <= 0) { if (loopLen != 0) { @@ -905,25 +912,32 @@ void m4aSoundVSync(void) struct SoundMixerState *mixer = SOUND_INFO_PTR; if (mixer->lockStatus - ID_NUMBER <= 1) { s32 samplesPerFrame = mixer->samplesPerFrame * 2; - float *m4aBuffer = mixer->pcmBuffer; - float *cgbBuffer = cgb_get_buffer(); + fixed8_24 *m4aBuffer = mixer->pcmBuffer; + fixed8_24 *cgbBuffer = cgb_get_buffer(); s32 dmaCounter = mixer->dmaCounter; - bool8 shouldQueue = FALSE; if (dmaCounter > 1) { m4aBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1)); } for (u32 i = 0; i < samplesPerFrame; i++) { - audioBuffer[i] = m4aBuffer[i] + cgbBuffer[i]; - if (audioBuffer[i] != 0) { - shouldQueue = TRUE; + // Sample is fixed 8.24 with a value of -1 to 1 + fixed8_24 sample = (m4aBuffer[i] + cgbBuffer[i]); + // Clamp + if (sample > u32_to_fp8_24(1)) { + sample = u32_to_fp8_24(1); } + if (sample < -u32_to_fp8_24(1)) { + sample = -u32_to_fp8_24(1); + } + // 1 in 8.24 format is 1 << 24 + // 32768 is size expected for s16 audio + // 32768 = 1 << 15 + // 24 - 15 = 9 + audioBuffer[i] = sample >> 9; } - if (shouldQueue) { - Platform_QueueAudio(audioBuffer, samplesPerFrame * 4); - } + Platform_QueueAudio(audioBuffer, samplesPerFrame * sizeof(s16)); if ((s8)(--mixer->dmaCounter) <= 0) mixer->dmaCounter = mixer->framesPerDmaCycle; } diff --git a/src/platform/shared/dma.c b/src/platform/shared/dma.c index 60ad6144f..343a27cac 100644 --- a/src/platform/shared/dma.c +++ b/src/platform/shared/dma.c @@ -4,7 +4,7 @@ struct DMATransfer DMAList[DMA_COUNT] = { 0 }; -void RunDMAs(u32 type) +void RunDMAs(DmaStartTypes type) { for (int dmaNum = 0; dmaNum < DMA_COUNT; dmaNum++) { struct DMATransfer *dma = &DMAList[dmaNum]; @@ -124,7 +124,7 @@ void DmaStop(int dmaNum) void DmaWait(int dmaNum) { - vu32 *ctrlRegs = ®_DMA0CNT; + vu32 *ctrlRegs = (vu32 *)®_DMA0CNT; #if !USE_NEW_DMA while (ctrlRegs[dmaNum * 3] & (DMA_ENABLE << 16)) ; diff --git a/src/platform/shared/input.c b/src/platform/shared/input.c index e8250ed04..c3f801c8b 100644 --- a/src/platform/shared/input.c +++ b/src/platform/shared/input.c @@ -3,8 +3,8 @@ #include #include #include -#include "gba/io_reg.h" #include "gba/types.h" +#include "gba/io_reg.h" #include "platform/shared/input.h" #define STICK_THRESHOLD 0.5f diff --git a/src/platform/shared/video/gpsp_renderer.cc b/src/platform/shared/video/gpsp_renderer.cc new file mode 100644 index 000000000..255e8d18b --- /dev/null +++ b/src/platform/shared/video/gpsp_renderer.cc @@ -0,0 +1,2299 @@ +/* gameplaySP - Modified to fit the SA2 codebase (FreshOllie - 2026) + * + * Copyright (C) 2006 Exophase + * Copyright (C) 2023 David Guillen Fandos + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +extern "C" { +#include "config.h" +} + +#if RENDERER == RENDERER_SOFTWARE + +#include +#include + +extern "C" { +#include "global.h" +#include "core.h" +#include "gba/defines.h" +#include "gba/io_reg.h" +#include "gba/types.h" + +#include "platform/shared/dma.h" +#include "platform/shared/video/gpsp_renderer.h" +} + +#define eswap16(value) (value) +#define eswap32(value) (value) + +#define GBA_SCREEN_PITCH DISPLAY_WIDTH + +#define read_ioreg(regaddr) (eswap16(*(u16 *)(regaddr))) +#define read_ioreg32(regaddr) (read_ioreg(regaddr) | (read_ioreg((regaddr) + sizeof(u16)) << 16)) + +#define convert_palette(value) (value & 0x7FFF) + +u16 *gba_screen_pixels = NULL; + +#define get_screen_pixels() gba_screen_pixels +#define get_screen_pitch() GBA_SCREEN_PITCH + +#define REG_ADDR_BGxCNT(n) (REG_ADDR_BG0CNT + (n) * sizeof(u16)) +#define REG_ADDR_WINxH(n) (REG_ADDR_WIN0H + (n) * sizeof(winreg_t)) +#define REG_ADDR_WINxV(n) (REG_ADDR_WIN0V + (n) * sizeof(winreg_t)) +#define REG_ADDR_BGxHOFS(n) (REG_ADDR_BG0HOFS + ((n)*2) * sizeof(u16)) +#define REG_ADDR_BGxVOFS(n) (REG_ADDR_BG0VOFS + ((n)*2) * sizeof(u16)) +#define REG_ADDR_BGxPA(n) (REG_ADDR_BG2PA + ((n)-2) * 8 * sizeof(u16)) +#define REG_ADDR_BGxPB(n) (REG_ADDR_BG2PB + ((n)-2) * 8 * sizeof(u16)) +#define REG_ADDR_BGxPC(n) (REG_ADDR_BG2PC + ((n)-2) * 8 * sizeof(u16)) +#define REG_ADDR_BGxPD(n) (REG_ADDR_BG2PD + ((n)-2) * 8 * sizeof(u16)) + +typedef struct { + u16 pad0[OAM_DATA_COUNT_AFFINE - 1]; + u16 dx; + u16 pad1[OAM_DATA_COUNT_AFFINE - 1]; + u16 dmx; + u16 pad2[OAM_DATA_COUNT_AFFINE - 1]; + u16 dy; + u16 pad3[OAM_DATA_COUNT_AFFINE - 1]; + u16 dmy; +} t_affp; + +typedef void (*bitmap_render_function)(u32 start, u32 end, void *dest_ptr, const u16 *pal); +typedef void (*tile_render_function)(u32 layer, u32 start, u32 end, void *dest_ptr, const u16 *pal); + +typedef void (*render_function_u16)(u32 start, u32 end, u16 *scanline, u32 enable_flags); +typedef void (*render_function_u32)(u32 start, u32 end, u32 *scanline, u32 enable_flags); + +typedef void (*window_render_function)(u16 *scanline, u32 start, u32 end); + +static void render_scanline_conditional(u32 start, u32 end, u16 *scanline, u32 enable_flags = 0x3F); + +typedef struct { + bitmap_render_function blit_render; + bitmap_render_function scale_render; + bitmap_render_function affine_render; +} bitmap_layer_render_struct; + +typedef struct { + render_function_u16 fullcolor; + render_function_u16 indexed_u16; + render_function_u32 indexed_u32; + render_function_u32 stacked; +} layer_render_struct; + +// Object blending modes +#define OBJ_MOD_NORMAL 0 +#define OBJ_MOD_SEMITRAN 1 +#define OBJ_MOD_WINDOW 2 +#define OBJ_MOD_INVALID 3 + +// BLDCNT color effect modes +#define COL_EFFECT_NONE 0x0 +#define COL_EFFECT_BLEND 0x1 +#define COL_EFFECT_BRIGHT 0x2 +#define COL_EFFECT_DARK 0x3 + +// Background render modes +#define RENDER_NORMAL 0 +#define RENDER_COL16 1 +#define RENDER_COL32 2 +#define RENDER_ALPHA 3 + +// Byte lengths of complete tiles and tile rows in 4bpp and 8bpp. + +#define tile_width_4bpp 4 +#define tile_size_4bpp 32 +#define tile_width_8bpp 8 +#define tile_size_8bpp 64 + +// Sprite rendering cycles +#define REND_CYC_MAX 32768 /* Theoretical max is 17920 */ +#define REND_CYC_SCANLINE 1210 +#define REND_CYC_REDUCED 954 + +// Generate bit mask (bits 9th and 10th) with information about the pixel +// status (1st and/or 2nd target) for later blending. +static inline u16 color_flags(u32 layer) +{ + u32 bldcnt = read_ioreg(REG_ADDR_BLDCNT); + return (((bldcnt >> layer) & 0x01) | // 1st target + ((bldcnt >> (layer + 7)) & 0x02) // 2nd target + ) + << 9; +} + +static const u32 map_widths[] = { 256, 512, 256, 512 }; + +typedef enum { + FULLCOLOR, // Regular rendering, output a 16 bit color + INDXCOLOR, // Rendering to indexed color, so we can later apply dark/bright + STCKCOLOR, // Stacks two indexed pixels (+flags) to apply blending + PIXCOPY // Special mode used for sprites, to allow for obj-window drawing +} rendtype; + +s32 affine_reference_x[2]; +s32 affine_reference_y[2]; + +static inline s32 signext28(u32 value) +{ + s32 ret = (s32)(value << 4); + return ret >> 4; +} + +static u32 prev_affine[4] = { 0, 0, 0, 0 }; + +static void video_reload_counters(bool vblank) +{ + /* This happens every Vblank, and every scanline */ + u32 value; + + value = read_ioreg32(REG_ADDR_BG2X_L); + if (vblank || prev_affine[0] != value) { + affine_reference_x[0] = signext28(value); + prev_affine[0] = value; + } + + value = read_ioreg32(REG_ADDR_BG2Y_L); + if (vblank || prev_affine[1] != value) { + affine_reference_y[0] = signext28(value); + prev_affine[1] = value; + } + + value = read_ioreg32(REG_ADDR_BG3X_L); + if (vblank || prev_affine[2] != value) { + affine_reference_x[1] = signext28(value); + prev_affine[2] = value; + } + + value = read_ioreg32(REG_ADDR_BG3Y_L); + if (vblank || prev_affine[3] != value) { + affine_reference_y[1] = signext28(value); + prev_affine[3] = value; + } +} + +// Renders non-affine tiled background layer. +// Will process a full or partial tile (start and end within 0..8) and draw +// it in either 8 or 4 bpp mode. Honors vertical and horizontal flip. + +// tile contains the tile info (contains tile index, flip bits, pal info) +// hflip causes the tile pixels lookup to be reversed (from MSB to LSB +// If isbase is not set, color 0 is interpreted as transparent, otherwise +// we are drawing the base layer, so palette[0] is used (backdrop). + +template +static inline void rend_part_tile_Nbpp(u32 bg_comb, u32 px_comb, dtype *dest_ptr, u32 start, u32 end, u16 tile, const u8 *tile_base, + int vertical_pixel_flip, const u16 *paltbl) +{ + // Seek to the specified tile, using the tile number and size. + // tile_base already points to the right tile-line vertical offset + const u8 *tile_ptr = &tile_base[(tile & 0x3FF) * (is8bpp ? 64 : 32)]; + u16 bgcolor = paltbl[0]; + + // On vertical flip, apply the mirror offset + if (tile & 0x800) + tile_ptr += vertical_pixel_flip; + + if (is8bpp) { + // Each byte is a color, mapped to a palete. 8 bytes can be read as 64bit + for (u32 i = start; i < end; i++, dest_ptr++) { + // Honor hflip by selecting bytes in the correct order + u32 sel = hflip ? (7 - i) : i; + u8 pval = tile_ptr[sel]; + // Alhpa mode stacks previous value (unless rendering the first layer) + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = paltbl[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_comb; // Add combine flags + else if (rdtype == STCKCOLOR) + // Stack pixels on top of the pixel value and combine flags + *dest_ptr = pval | px_comb | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + if (rdtype == FULLCOLOR) + *dest_ptr = bgcolor; + else + *dest_ptr = 0 | bg_comb; // Add combine flags + } + } + } else { + // In 4bpp mode, the tile[15..12] bits contain the sub-palette number. + u16 tilepal = (tile >> 12) << 4; + u16 pxflg = px_comb | tilepal; + const u16 *subpal = &paltbl[tilepal]; + // Read packed pixel data, skip start pixels + u32 tilepix = eswap32(*(u32 *)tile_ptr); + if (hflip) + tilepix <<= (start * 4); + else + tilepix >>= (start * 4); + // Only 32 bits (8 pixels * 4 bits) + for (u32 i = start; i < end; i++, dest_ptr++) { + u8 pval = hflip ? tilepix >> 28 : tilepix & 0xF; + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = subpal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pxflg | pval; + else if (rdtype == STCKCOLOR) // Stack pixels + *dest_ptr = pxflg | pval | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + if (rdtype == FULLCOLOR) + *dest_ptr = bgcolor; + else + *dest_ptr = 0 | bg_comb; + } + // Advance to next packed data + if (hflip) + tilepix <<= 4; + else + tilepix >>= 4; + } + } +} + +// Same as above, but optimized for full tiles. Skip comments here. +template +static inline void render_tile_Nbpp(u32 bg_comb, u32 px_comb, dtype *dest_ptr, u16 tile, const u8 *tile_base, int vertical_pixel_flip, + const u16 *paltbl) +{ + const u8 *tile_ptr = &tile_base[(tile & 0x3FF) * (is8bpp ? 64 : 32)]; + u16 bgcolor = paltbl[0]; + + if (tile & 0x800) + tile_ptr += vertical_pixel_flip; + + if (is8bpp) { + for (u32 j = 0; j < 2; j++) { + u32 tilepix = eswap32(((u32 *)tile_ptr)[hflip ? 1 - j : j]); + if (tilepix) { + for (u32 i = 0; i < 4; i++, dest_ptr++) { + u8 pval = hflip ? (tilepix >> (24 - i * 8)) : (tilepix >> (i * 8)); + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = paltbl[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_comb; // Add combine flags + else if (rdtype == STCKCOLOR) + *dest_ptr = pval | px_comb | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } + } else { + for (u32 i = 0; i < 4; i++, dest_ptr++) + if (isbase) + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } + } else { + u32 tilepix = eswap32(*(u32 *)tile_ptr); + if (tilepix) { // We can skip it all if the row is transparent + u16 tilepal = (tile >> 12) << 4; + u16 pxflg = px_comb | tilepal; + const u16 *subpal = &paltbl[tilepal]; + for (u32 i = 0; i < 8; i++, dest_ptr++) { + u8 pval = (hflip ? (tilepix >> ((7 - i) * 4)) : (tilepix >> (i * 4))) & 0xF; + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = subpal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pxflg | pval; + else if (rdtype == STCKCOLOR) + *dest_ptr = pxflg | pval | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } + } else if (isbase) { + // In this case we simply fill the pixels with background pixels + for (u32 i = 0; i < 8; i++, dest_ptr++) + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } +} + +template +static void render_scanline_text_fast(u32 layer, u32 start, u32 end, void *scanline, const u16 *paltbl) +{ + u32 bg_control = read_ioreg(REG_ADDR_BGxCNT(layer)); + u16 vcount = read_ioreg(REG_ADDR_VCOUNT); + u32 map_size = (bg_control >> 14) & 0x03; + u32 map_width = map_widths[map_size]; + u32 hoffset = (start + read_ioreg(REG_ADDR_BGxHOFS(layer))) % 512; + u32 voffset = (vcount + read_ioreg(REG_ADDR_BGxVOFS(layer))) % 512; + stype *dest_ptr = ((stype *)scanline) + start; + + // Calculate combine masks. These store 2 bits of info: 1st and 2nd target. + // If set, the current pixel belongs to a layer that is 1st or 2nd target. + u32 bg_comb = color_flags(5), px_comb = color_flags(layer); + + // Background map data is in VRAM, at an offset specified in 2K blocks. + // (each map data block is 32x32 tiles, at 16bpp, so 2KB) + u32 base_block = (bg_control & BGCNT_SCREENBASE_MASK) >> 8; + u16 *map_base = (u16 *)BG_SCREEN_ADDR(base_block); + u16 *map_ptr, *second_ptr; + + end -= start; + + // Skip the top one/two block(s) if using the bottom half + if ((map_size & 0x02) && (voffset >= 256)) + map_base += ((map_width / 8) * 32); + + // Skip the top tiles within the block + map_base += (((voffset % 256) / 8) * (map_width / 8)); + + // we might need to render from two charblocks, store a second pointer. + second_ptr = map_ptr = map_base; + + if (map_size & 0x01) { // If background is 512 pixels wide + if (hoffset >= 256) { + // If we are rendering the right block, skip a whole charblock + hoffset -= 256; + map_ptr += ((map_width / 8) * 32); + } else { + // If we are rendering the left block, we might overrun into the right + second_ptr += ((map_width / 8) * 32); + } + } else { + hoffset %= 256; // Background is 256 pixels wide + } + + // Skip the left blocks within the block + map_ptr += hoffset / 8; + + // Render a single scanline of text tiles + u32 tilewidth = is8bpp ? tile_width_8bpp : tile_width_4bpp; + u32 vert_pix_offset = (voffset % 8) * tilewidth; + // Calculate the pixel offset between a line and its "flipped" mirror. + // The values can be {56, 40, 24, 8, -8, -24, -40, -56} + s32 vflip_off + = is8bpp ? tile_size_8bpp - 2 * vert_pix_offset - tile_width_8bpp : tile_size_4bpp - 2 * vert_pix_offset - tile_width_4bpp; + + // The tilemap base is selected via bgcnt (16KiB chunks) + u32 tilecntrl = (bg_control >> 2) & 0x03; + // Account for the base offset plus the tile vertical offset + u8 *tile_base = BG_CHAR_ADDR(tilecntrl) + vert_pix_offset; + // Number of pixels available until the end of the tile block + u32 pixel_run = map_width - hoffset; + + u32 tile_hoff = hoffset % 8; + u32 partial_hcnt = 8 - tile_hoff; + + if (tile_hoff) { + // First partial tile, only right side is visible. + u32 todraw = MIN(end, partial_hcnt); // [1..7] + u32 stop = tile_hoff + todraw; // Usually 8, unless short run. + + u16 tile = eswap16(*map_ptr++); + if (tile & 0x400) // Tile horizontal flip + rend_part_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile_hoff, stop, tile, tile_base, + vflip_off, paltbl); + else + rend_part_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile_hoff, stop, tile, tile_base, + vflip_off, paltbl); + + dest_ptr += todraw; + end -= todraw; + pixel_run -= todraw; + } + + if (!end) + return; + + // Now render full tiles + u32 todraw = MIN(end, pixel_run) / 8; + + for (u32 i = 0; i < todraw; i++, dest_ptr += 8) { + u16 tile = eswap16(*map_ptr++); + if (tile & 0x400) // Tile horizontal flip + render_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile, tile_base, vflip_off, paltbl); + else + render_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile, tile_base, vflip_off, paltbl); + } + + end -= todraw * 8; + pixel_run -= todraw * 8; + + if (!end) + return; + + // Switch to the next char block if we ran out of tiles + if (!pixel_run) + map_ptr = second_ptr; + + todraw = end / 8; + for (u32 i = 0; i < todraw; i++, dest_ptr += 8) { + u16 tile = eswap16(*map_ptr++); + if (tile & 0x400) // Tile horizontal flip + render_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile, tile_base, vflip_off, paltbl); + else + render_tile_Nbpp(bg_comb, px_comb, dest_ptr, tile, tile_base, vflip_off, paltbl); + } + + end -= todraw * 8; + + // Finalize the tile rendering the left side of it (from 0 up to "end"). + if (end) { + u16 tile = eswap16(*map_ptr++); + if (tile & 0x400) // Tile horizontal flip + rend_part_tile_Nbpp(bg_comb, px_comb, dest_ptr, 0, end, tile, tile_base, vflip_off, + paltbl); + else + rend_part_tile_Nbpp(bg_comb, px_comb, dest_ptr, 0, end, tile, tile_base, vflip_off, + paltbl); + } +} + +// A slow version of the above function that allows for mosaic effects +template +static void render_scanline_text_mosaic(u32 layer, u32 start, u32 end, void *scanline, const u16 *paltbl) +{ + u32 bg_control = read_ioreg(REG_ADDR_BGxCNT(layer)); + const u32 mosh = (read_ioreg(REG_ADDR_MOSAIC) & 0xF) + 1; + const u32 mosv = ((read_ioreg(REG_ADDR_MOSAIC) >> 4) & 0xF) + 1; + u16 vcount = read_ioreg(REG_ADDR_VCOUNT); + u32 map_size = (bg_control >> 14) & 0x03; + u32 map_width = map_widths[map_size]; + u32 hoffset = (start + read_ioreg(REG_ADDR_BGxHOFS(layer))) % 512; + u16 vmosoff = vcount - vcount % mosv; + u32 voffset = (vmosoff + read_ioreg(REG_ADDR_BGxVOFS(layer))) % 512; + stype *dest_ptr = ((stype *)scanline) + start; + + u32 bg_comb = color_flags(5), px_comb = color_flags(layer); + + u32 base_block = (bg_control & BGCNT_SCREENBASE_MASK) >> 8; + u16 *map_base = (u16 *)BG_SCREEN_ADDR(base_block); + u16 *map_ptr, *second_ptr; + + if ((map_size & 0x02) && (voffset >= 256)) + map_base += ((map_width / 8) * 32); + + map_base += (((voffset % 256) / 8) * (map_width / 8)); + + second_ptr = map_ptr = map_base; + + if (map_size & 0x01) { // If background is 512 pixels wide + if (hoffset >= 256) { + // If we are rendering the right block, skip a whole charblock + hoffset -= 256; + map_ptr += ((map_width / 8) * 32); + } else { + // If we are rendering the left block, we might overrun into the right + second_ptr += ((map_width / 8) * 32); + } + } else { + hoffset %= 256; // Background is 256 pixels wide + } + + // Skip the left blocks within the block + map_ptr += hoffset / 8; + + // Render a single scanline of text tiles + u32 tilewidth = is8bpp ? tile_width_8bpp : tile_width_4bpp; + u32 vert_pix_offset = (voffset % 8) * tilewidth; + // Calculate the pixel offset between a line and its "flipped" mirror. + // The values can be {56, 40, 24, 8, -8, -24, -40, -56} + s32 vflip_off + = is8bpp ? tile_size_8bpp - 2 * vert_pix_offset - tile_width_8bpp : tile_size_4bpp - 2 * vert_pix_offset - tile_width_4bpp; + + // The tilemap base is selected via bgcnt (16KiB chunks) + u32 tilecntrl = (bg_control >> 2) & 0x03; + // Account for the base offset plus the tile vertical offset + u8 *tile_base = BG_CHAR_ADDR(tilecntrl) + vert_pix_offset; + + u16 bgcolor = paltbl[0]; + + // Iterate pixel by pixel, loading data every N pixels to honor mosaic effect + u8 pval = 0; + for (u32 i = 0; start < end; start++, i++, dest_ptr++) { + u16 tile = eswap16(*map_ptr); + + if (!(i % mosh)) { + const u8 *tile_ptr = &tile_base[(tile & 0x3FF) * (is8bpp ? 64 : 32)]; + + bool hflip = (tile & 0x400); + if (tile & 0x800) + tile_ptr += vflip_off; + + // Load byte or nibble with pixel data. + if (is8bpp) { + if (hflip) + pval = tile_ptr[7 - hoffset % 8]; + else + pval = tile_ptr[hoffset % 8]; + } else { + if (hflip) + pval = (tile_ptr[(7 - hoffset % 8) >> 1] >> (((hoffset & 1) ^ 1) * 4)) & 0xF; + else + pval = (tile_ptr[(hoffset % 8) >> 1] >> ((hoffset & 1) * 4)) & 0xF; + } + } + + if (is8bpp) { + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = paltbl[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_comb; // Add combine flags + else if (rdtype == STCKCOLOR) + *dest_ptr = pval | px_comb | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } else { + u16 tilepal = (tile >> 12) << 4; + u16 pxflg = px_comb | tilepal; + const u16 *subpal = &paltbl[tilepal]; + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = subpal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pxflg | pval; + else if (rdtype == STCKCOLOR) + *dest_ptr = pxflg | pval | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + *dest_ptr = (rdtype == FULLCOLOR) ? bgcolor : 0 | bg_comb; + } + } + + // Need to continue from the next charblock + hoffset++; + if (hoffset % 8 == 0) + map_ptr++; + if (hoffset >= map_width) { + hoffset = 0; + map_ptr = second_ptr; + } + } +} + +template +static void render_scanline_text(u32 layer, u32 start, u32 end, void *scanline, const u16 *paltbl) +{ + // Tile mode has 4 and 8 bpp modes. + u32 bg_control = read_ioreg(REG_ADDR_BGxCNT(layer)); + bool is8bpp = (read_ioreg(REG_ADDR_BGxCNT(layer)) & 0x80); + const u32 mosamount = read_ioreg(REG_ADDR_MOSAIC) & 0xFF; + bool has_mosaic = (bg_control & 0x40) && (mosamount != 0); + + if (has_mosaic) { + if (is8bpp) + render_scanline_text_mosaic(layer, start, end, scanline, paltbl); + else + render_scanline_text_mosaic(layer, start, end, scanline, paltbl); + } else { + if (is8bpp) + render_scanline_text_fast(layer, start, end, scanline, paltbl); + else + render_scanline_text_fast(layer, start, end, scanline, paltbl); + } +} + +static inline u8 lookup_pix_8bpp(u32 px, u32 py, const u8 *tile_base, const u8 *map_base, u32 map_size) +{ + // Pitch represents the log2(number of tiles per row) (from 16 to 128) + u32 map_pitch = map_size + 4; + // Given coords (px,py) in the background space, find the tile. + u32 mapoff = (px / 8) + ((py / 8) << map_pitch); + // Each tile is 8x8, so 64 bytes each. + const u8 *tile_ptr = &tile_base[map_base[mapoff] * tile_size_8bpp]; + // Read the 8bit color within the tile. + return tile_ptr[(px % 8) + ((py % 8) * 8)]; +} + +template +static inline void rend_pix_8bpp(dsttype *dest_ptr, u8 pval, u32 bg_comb, u32 px_comb, const u16 *pal) +{ + // Alhpa mode stacks previous value (unless rendering the first layer) + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = pal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_comb; // Add combine flags + else if (rdtype == STCKCOLOR) + // Stack pixels. If base, stack the base pixel. + *dest_ptr = pval | px_comb | ((isbase ? bg_comb : *dest_ptr) << 16); + } else if (isbase) { + // Transparent pixel, but we are base layer, so render background. + if (rdtype == FULLCOLOR) + *dest_ptr = pal[0]; + else + *dest_ptr = 0 | bg_comb; // Just backdrop color and combine flags + } +} + +template static inline void render_bdrop_pixel_8bpp(dsttype *dest_ptr, u32 bg_comb, u16 bgcol) +{ + // Alhpa mode stacks previous value (unless rendering the first layer) + if (rdtype == FULLCOLOR) + *dest_ptr = bgcol; + else + *dest_ptr = 0 | bg_comb; +} + +typedef void (*affine_render_function)(u32 layer, u32 start, u32 cnt, const u8 *map_base, u32 map_size, const u8 *tile_base, void *dst_ptr, + const u16 *pal); + +// Affine background rendering logic. +// wrap extends the background infinitely, otherwise transparent/backdrop fill +// rotate indicates if there's any rotation (optimized version for no-rotation) +// mosaic applies to horizontal mosaic (vertical is adjusted via affine ref) +template +static inline void render_affine_background(u32 layer, u32 start, u32 cnt, const u8 *map_base, u32 map_size, const u8 *tile_base, + void *dst_ptr_raw, const u16 *pal) +{ + + dtype *dst_ptr = (dtype *)dst_ptr_raw; + // Backdrop and current layer combine bits. + u32 bg_comb = color_flags(5); + u32 px_comb = color_flags(layer); + + s32 dx = (s16)read_ioreg(REG_ADDR_BGxPA(layer)); + s32 dy = (s16)read_ioreg(REG_ADDR_BGxPC(layer)); + + s32 source_x = affine_reference_x[layer - 2] + (start * dx); + s32 source_y = affine_reference_y[layer - 2] + (start * dy); + + // Maps are squared, four sizes available (128x128 to 1024x1024) + u32 width_height = 128 << map_size; + + // Horizontal mosaic effect. + const u32 mosh = (mosaic ? (read_ioreg(REG_ADDR_MOSAIC)) & 0xF : 0) + 1; + + if (wrap) { + // In wrap mode the entire space is covered, since it "wraps" at the edges + u8 pval = 0; + if (rotate) { + for (u32 i = 0; cnt; i++, cnt--) { + u32 pix_x = (u32)(source_x >> 8) & (width_height - 1); + u32 pix_y = (u32)(source_y >> 8) & (width_height - 1); + + // Lookup pixel and draw it (only every Nth if mosaic is on) + if (!mosaic || !(i % mosh)) + pval = lookup_pix_8bpp(pix_x, pix_y, tile_base, map_base, map_size); + rend_pix_8bpp(dst_ptr++, pval, bg_comb, px_comb, pal); + + source_x += dx; + source_y += dy; // Move to the next pixel + } + } else { + // Y coordinate stays contant across the walk. + const u32 pix_y = (u32)(source_y >> 8) & (width_height - 1); + for (u32 i = 0; cnt; i++, cnt--) { + u32 pix_x = (u32)(source_x >> 8) & (width_height - 1); + if (!mosaic || !(i % mosh)) + pval = lookup_pix_8bpp(pix_x, pix_y, tile_base, map_base, map_size); + rend_pix_8bpp(dst_ptr++, pval, bg_comb, px_comb, pal); + source_x += dx; // Only moving in the X direction. + } + } + } else { + u16 bgcol = pal[0]; + if (rotate) { + // Draw backdrop pixels if necessary until we reach the background edge. + while (cnt) { + // Draw backdrop pixels if they lie outside of the background. + u32 pix_x = (u32)(source_x >> 8), pix_y = (u32)(source_y >> 8); + + // Stop once we find a pixel that is actually *inside* the map. + if (pix_x < width_height && pix_y < width_height) + break; + + // Draw a backdrop pixel if we are the base layer. + if (isbase) + render_bdrop_pixel_8bpp(dst_ptr, bg_comb, bgcol); + + dst_ptr++; + source_x += dx; + source_y += dy; + cnt--; + } + + // Draw background pixels by looking them up in the map + u8 pval = 0; + for (u32 i = 0; cnt; i++, cnt--) { + u32 pix_x = (u32)(source_x >> 8), pix_y = (u32)(source_y >> 8); + + // Check if we run out of background pixels, stop drawing. + if (pix_x >= width_height || pix_y >= width_height) + break; + + // Lookup pixel and draw it. + if (!mosaic || !(i % mosh)) + pval = lookup_pix_8bpp(pix_x, pix_y, tile_base, map_base, map_size); + rend_pix_8bpp(dst_ptr++, pval, bg_comb, px_comb, pal); + + // Move to the next pixel, update coords accordingly + source_x += dx; + source_y += dy; + } + } else { + // Specialized version for scaled-only backgrounds + u8 pval = 0; + const u32 pix_y = (u32)(source_y >> 8); + if (pix_y < width_height) { // Check if within Y-coord range + // Draw/find till left edge + while (cnt) { + u32 pix_x = (u32)(source_x >> 8); + if (pix_x < width_height) + break; + + if (isbase) + render_bdrop_pixel_8bpp(dst_ptr, bg_comb, bgcol); + + dst_ptr++; + source_x += dx; + cnt--; + } + // Draw actual background + for (u32 i = 0; cnt; i++, cnt--) { + u32 pix_x = (u32)(source_x >> 8); + if (pix_x >= width_height) + break; + + if (!mosaic || !(i % mosh)) + pval = lookup_pix_8bpp(pix_x, pix_y, tile_base, map_base, map_size); + rend_pix_8bpp(dst_ptr++, pval, bg_comb, px_comb, pal); + + source_x += dx; + } + } + } + + // Complete the line on the right, if we ran out over the bg edge. + // Only necessary for the base layer, otherwise we can safely finish. + if (isbase) + while (cnt--) + render_bdrop_pixel_8bpp(dst_ptr++, bg_comb, bgcol); + } +} + +// Renders affine backgrounds. These differ substantially from non-affine +// ones. Tile maps are byte arrays (instead of 16 bit), limiting the map to +// 256 different tiles (with no flip bits and just one single 256 color pal). +// Optimize for common cases: wrap/non-wrap, scaling/rotation. +template +static void render_scanline_affine(u32 layer, u32 start, u32 end, void *scanline, const u16 *pal) +{ + + u32 bg_control = read_ioreg(REG_ADDR_BGxCNT(layer)); + u32 map_size = (bg_control >> 14) & 0x03; + + // Char block base pointer + u32 base_block = (bg_control >> 8) & 0x1F; + u8 *map_base = BG_SCREEN_ADDR(base_block); + // The tilemap base is selected via bgcnt (16KiB chunks) + u32 tilecntrl = (bg_control >> 2) & 0x03; + u8 *tile_base = BG_CHAR_ADDR(tilecntrl); + + dsttype *dest_ptr = ((dsttype *)scanline) + start; + const u32 mosamount = read_ioreg(REG_ADDR_MOSAIC) & 0xFF; + + bool has_mosaic = (bg_control & 0x40) && (mosamount != 0); + bool has_rotation = read_ioreg(REG_ADDR_BGxPC(layer)) != 0; + bool has_wrap = (bg_control >> 13) & 1; + + // Number of pixels to render + u32 cnt = end - start; + + // Four specialized versions for faster rendering on specific cases like + // scaling only or non-wrapped backgrounds. + u32 fidx = (has_wrap ? 0x4 : 0) | (has_rotation ? 0x2 : 0) | (has_mosaic ? 0x1 : 0); + + static const affine_render_function rdfns[8] = { + render_affine_background, + render_affine_background, + render_affine_background, + render_affine_background, + render_affine_background, + render_affine_background, + render_affine_background, + render_affine_background, + }; + + rdfns[fidx](layer, start, cnt, map_base, map_size, tile_base, dest_ptr, pal); +} + +template +static inline void bitmap_pixel_write(buftype *dst_ptr, pixfmt val, const u16 *palptr, u16 px_attr) +{ + if (mode != 4) + *dst_ptr = convert_palette(val); // Direct color, u16 bitmap + else if (val) { + if (rdmode == FULLCOLOR) + *dst_ptr = palptr[val]; + else if (rdmode == INDXCOLOR) + *dst_ptr = val | px_attr; // Add combine flags + else if (rdmode == STCKCOLOR) + *dst_ptr = val | px_attr | ((*dst_ptr) << 16); // Stack pixels + } +} + +typedef enum { + BLIT, // The bitmap has no scaling nor rotation on the X axis + SCALED, // The bitmap features some scaling (on the X axis) but no rotation + ROTATED // Bitmap has rotation (and perhaps scaling too) +} bm_rendmode; + +// Renders a bitmap honoring the pixel mode and any affine transformations. +// There's optimized versions for bitmaps without scaling / rotation. + +template // Whether mosaic effect is used. +static inline void render_scanline_bitmap(u32 start, u32 end, void *scanline, const u16 *palptr) +{ + s32 dx = (s16)read_ioreg(REG_ADDR_BG2PA); + s32 dy = (s16)read_ioreg(REG_ADDR_BG2PC); + s32 source_x = affine_reference_x[0] + (start * dx); // Always BG2 + s32 source_y = affine_reference_y[0] + (start * dy); + + // Premature abort render optimization if bitmap out of Y coordinate. + if ((rdmode != ROTATED) && ((u32)(source_y >> 8)) >= height) + return; + + // Modes 4 and 5 feature double buffering. + bool second_frame = (mode >= 4) && (read_ioreg(REG_ADDR_DISPCNT) & 0x10); + pixfmt *src_ptr = (pixfmt *)&VRAM[second_frame ? 0xA000 : 0x0000]; + dsttype *dst_ptr = ((dsttype *)scanline) + start; + u16 px_attr = color_flags(2); // Always BG2 + + const u32 mosh = (mosaic ? (read_ioreg(REG_ADDR_MOSAIC)) & 0xF : 0) + 1; + + if (rdmode == BLIT) { + // We just blit pixels (copy) from buffer to buffer. + const u32 pixel_y = (u32)(source_y >> 8); + if (source_x < 0) { + // The bitmap starts somewhere after "start", skip those pixels. + u32 delta = (-source_x + 255) >> 8; + dst_ptr += delta; + start += delta; + source_x = 0; + } + + u32 pixel_x = (u32)(source_x >> 8); + u32 pixcnt = MIN(end - start, width - pixel_x); + pixfmt *valptr = &src_ptr[pixel_x + (pixel_y * width)]; + pixfmt val = 0; + for (u32 i = 0; pixcnt; i++, pixcnt--, valptr++) { + // Pretty much pixel copier + if (!mosaic || !(i % mosh)) + val = sizeof(pixfmt) == 2 ? eswap16(*valptr) : *valptr; + bitmap_pixel_write(dst_ptr++, val, palptr, px_attr); + } + } else if (rdmode == SCALED) { + // Similarly to above, but now we need to sample pixels instead. + const u32 pixel_y = (u32)(source_y >> 8); + + // Find the "inside" of the bitmap + while (start < end) { + u32 pixel_x = (u32)(source_x >> 8); + if (pixel_x < width) + break; + source_x += dx; + start++; + dst_ptr++; + } + + u32 cnt = end - start; + pixfmt val = 0; + for (u32 i = 0; cnt; i++, cnt--) { + u32 pixel_x = (u32)(source_x >> 8); + if (pixel_x >= width) + break; // We reached the end of the bitmap + + if (!mosaic || !(i % mosh)) { + pixfmt *valptr = &src_ptr[pixel_x + (pixel_y * width)]; + val = sizeof(pixfmt) == 2 ? eswap16(*valptr) : *valptr; + } + + bitmap_pixel_write(dst_ptr++, val, palptr, px_attr); + source_x += dx; + } + } else { + // Look for the first pixel to be drawn. + while (start < end) { + u32 pixel_x = (u32)(source_x >> 8), pixel_y = (u32)(source_y >> 8); + if (pixel_x < width && pixel_y < height) + break; + start++; + dst_ptr++; + source_x += dx; + source_y += dy; + } + + pixfmt val = 0; + for (u32 i = 0; start < end; start++) { + u32 pixel_x = (u32)(source_x >> 8), pixel_y = (u32)(source_y >> 8); + + // Check if we run out of background pixels, stop drawing. + if (pixel_x >= width || pixel_y >= height) + break; + + // Lookup pixel and draw it. + if (!mosaic || !(i % mosh)) { + pixfmt *valptr = &src_ptr[pixel_x + (pixel_y * width)]; + val = sizeof(pixfmt) == 2 ? eswap16(*valptr) : *valptr; + } + + bitmap_pixel_write(dst_ptr++, val, palptr, px_attr); + + // Move to the next pixel, update coords accordingly + source_x += dx; + source_y += dy; + } + } +} + +// Object/Sprite rendering logic + +static const u8 obj_dim_table[3][4][2] = { { { 8, 8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, + { { 16, 8 }, { 32, 8 }, { 32, 16 }, { 64, 32 } }, + { { 8, 16 }, { 8, 32 }, { 16, 32 }, { 32, 64 } } }; + +static u8 obj_priority_list[5][DISPLAY_HEIGHT][128]; +static u8 obj_priority_count[5][DISPLAY_HEIGHT]; +static u8 obj_alpha_count[DISPLAY_HEIGHT]; + +typedef struct { + s32 obj_x, obj_y; + s32 obj_w, obj_h; + const OamData *oam_data; + bool is_double; +} t_sprite; + +// Renders a tile row (8 pixels) for a regular (non-affine) object/sprite. +// tile_offset points to the VRAM offset where the data lives. +template +static inline void render_obj_part_tile_Nbpp(u32 px_comb, dsttype *dest_ptr, u32 start, u32 end, u32 tile_offset, u16 palette, + const u16 *pal) +{ + // Note that the last VRAM bank wrap around, hence the offset aliasing + const u8 *tile_ptr = OBJ_VRAM0 + (tile_offset & 0x7FFF); + u32 px_attr = px_comb | palette | 0x100; // Combine flags + high palette bit + + if (is8bpp) { + // Each byte is a color, mapped to a palete. + for (u32 i = start; i < end; i++, dest_ptr++) { + // Honor hflip by selecting bytes in the correct order + u32 sel = hflip ? (7 - i) : i; + u8 pval = tile_ptr[sel]; + // Alhpa mode stacks previous value + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = pal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_attr; // Add combine flags + else if (rdtype == STCKCOLOR) { + // Stack pixels on top of the pixel value and combine flags + // We do not stack OBJ on OBJ, rather overwrite the previous object + if (*dest_ptr & 0x100) + *dest_ptr = pval | px_attr | ((*dest_ptr) & 0xFFFF0000); + else + *dest_ptr = pval | px_attr | ((*dest_ptr) << 16); + } else if (rdtype == PIXCOPY) + *dest_ptr = dest_ptr[DISPLAY_WIDTH]; + } + } + } else { + // Only 32 bits (8 pixels * 4 bits) + for (u32 i = start; i < end; i++, dest_ptr++) { + u32 selb = hflip ? (3 - i / 2) : i / 2; + u32 seln = hflip ? ((i & 1) ^ 1) : (i & 1); + u8 pval = (tile_ptr[selb] >> (seln * 4)) & 0xF; + const u16 *subpal = &pal[palette]; + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = subpal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_attr; + else if (rdtype == STCKCOLOR) { + if (*dest_ptr & 0x100) + *dest_ptr = pval | px_attr | ((*dest_ptr) & 0xFFFF0000); + else + *dest_ptr = pval | px_attr | ((*dest_ptr) << 16); // Stack pixels + } else if (rdtype == PIXCOPY) + *dest_ptr = dest_ptr[DISPLAY_WIDTH]; + } + } + } +} + +// Same as above but optimized for full tiles +template +static inline void render_obj_tile_Nbpp(u32 px_comb, dsttype *dest_ptr, u32 tile_offset, u16 palette, const u16 *pal) +{ + const u8 *tile_ptr = &VRAM[0x10000 + (tile_offset & 0x7FFF)]; + u32 px_attr = px_comb | palette | 0x100; // Combine flags + high palette bit + + if (is8bpp) { + for (u32 j = 0; j < 2; j++) { + u32 tilepix = eswap32(((u32 *)tile_ptr)[hflip ? 1 - j : j]); + if (tilepix) { + for (u32 i = 0; i < 4; i++, dest_ptr++) { + u8 pval = hflip ? (tilepix >> (24 - i * 8)) : (tilepix >> (i * 8)); + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = pal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_attr; // Add combine flags + else if (rdtype == STCKCOLOR) { + if (*dest_ptr & 0x100) + *dest_ptr = pval | px_attr | ((*dest_ptr) & 0xFFFF0000); + else + *dest_ptr = pval | px_attr | ((*dest_ptr) << 16); + } else if (rdtype == PIXCOPY) + *dest_ptr = dest_ptr[DISPLAY_WIDTH]; + } + } + } else + dest_ptr += 4; + } + } else { + u32 tilepix = eswap32(*(u32 *)tile_ptr); + if (tilepix) { // Can skip all pixels if the row is just transparent + for (u32 i = 0; i < 8; i++, dest_ptr++) { + u8 pval = (hflip ? (tilepix >> ((7 - i) * 4)) : (tilepix >> (i * 4))) & 0xF; + const u16 *subpal = &pal[palette]; + if (pval) { + if (rdtype == FULLCOLOR) + *dest_ptr = subpal[pval]; + else if (rdtype == INDXCOLOR) + *dest_ptr = pval | px_attr; + else if (rdtype == STCKCOLOR) { // Stack background, replace sprite + if (*dest_ptr & 0x100) + *dest_ptr = pval | px_attr | ((*dest_ptr) & 0xFFFF0000); + else + *dest_ptr = pval | px_attr | ((*dest_ptr) << 16); + } else if (rdtype == PIXCOPY) + *dest_ptr = dest_ptr[DISPLAY_WIDTH]; + } + } + } + } +} + +// Renders a regular sprite (non-affine) row to screen. +// delta_x is the object X coordinate referenced from the window start. +// cnt is the maximum number of pixels to draw, honoring window, obj width, etc. +template +static void render_object(s32 delta_x, u32 cnt, stype *dst_ptr, u32 tile_offset, u32 px_comb, u16 palette, const u16 *palptr) +{ + // Tile size in bytes for each mode + const u32 tile_bsize = is8bpp ? tile_size_8bpp : tile_size_4bpp; + // Number of bytes to advance (or rewind) on the tile map + const s32 tile_size_off = hflip ? -tile_bsize : tile_bsize; + + if (delta_x < 0) { // Left part is outside of the screen/window. + u32 offx = -delta_x; // How many pixels did we skip from the object? + s32 block_off = offx / 8; + u32 tile_off = offx % 8; + + // Skip the first object tiles (skips in the flip direction) + tile_offset += block_off * tile_size_off; + + // Render a partial tile to the left + if (tile_off) { + u32 residual = 8 - tile_off; // Pixel count to complete the first tile + u32 maxpix = MIN(residual, cnt); + render_obj_part_tile_Nbpp(px_comb, dst_ptr, tile_off, tile_off + maxpix, tile_offset, palette, + palptr); + + // Move to the next tile + tile_offset += tile_size_off; + // Account for drawn pixels + cnt -= maxpix; + dst_ptr += maxpix; + } + } else { + // Render object completely from the left. Skip the empty space to the left + dst_ptr += delta_x; + } + + // Render full tiles to the scan line. + s32 num_tiles = cnt / 8; + while (num_tiles--) { + // Render full tiles + render_obj_tile_Nbpp(px_comb, dst_ptr, tile_offset, palette, palptr); + tile_offset += tile_size_off; + dst_ptr += 8; + } + + // Render any partial tile on the end + cnt = cnt % 8; + if (cnt) + render_obj_part_tile_Nbpp(px_comb, dst_ptr, 0, cnt, tile_offset, palette, palptr); +} + +// A slower version of the version above, that renders objects pixel by pixel. +// This allows proper mosaic effects whenever necessary. +template +static void render_object_mosaic(s32 delta_x, u32 cnt, stype *dst_ptr, u32 base_tile_offset, u32 mosh, u32 px_comb, u16 palette, + const u16 *pal) +{ + const u32 tile_bsize = is8bpp ? tile_size_8bpp : tile_size_4bpp; + const s32 tile_size_off = hflip ? -tile_bsize : tile_bsize; + + u32 offx = 0; + if (delta_x < 0) { // Left part is outside of the screen/window. + offx = -delta_x; // Number of skipped pixels + } else { + dst_ptr += delta_x; + } + + u32 px_attr = px_comb | palette | 0x100; // Combine flags + high palette bit + + u8 pval = 0; + for (u32 i = 0; i < cnt; i++, offx++, dst_ptr++) { + if (!(i % mosh)) { + // Load tile pixel color. + u32 tile_offset = base_tile_offset + (offx / 8) * tile_size_off; + const u8 *tile_ptr = &VRAM[0x10000 + (tile_offset & 0x7FFF)]; + + // Lookup for each mode and flip value. + if (is8bpp) { + if (hflip) + pval = tile_ptr[7 - offx % 8]; + else + pval = tile_ptr[offx % 8]; + } else { + if (hflip) + pval = (tile_ptr[(7 - offx % 8) >> 1] >> (((offx & 1) ^ 1) * 4)) & 0xF; + else + pval = (tile_ptr[(offx % 8) >> 1] >> ((offx & 1) * 4)) & 0xF; + } + } + + // Write the pixel value as required + const u16 *subpal = &pal[palette]; + if (pval) { + if (rdtype == FULLCOLOR) + *dst_ptr = is8bpp ? pal[pval] : subpal[pval]; + else if (rdtype == INDXCOLOR) + *dst_ptr = pval | px_attr; // Add combine flags + else if (rdtype == STCKCOLOR) { + if (*dst_ptr & 0x100) + *dst_ptr = pval | px_attr | ((*dst_ptr) & 0xFFFF0000); + else + *dst_ptr = pval | px_attr | ((*dst_ptr) << 16); + } else if (rdtype == PIXCOPY) + *dst_ptr = dst_ptr[DISPLAY_WIDTH]; + } + } +} + +// Renders an affine sprite row to screen. +// They support 4bpp and 8bpp modes. 1D and 2D tile mapping modes. +// Their render area is limited to their size (and optionally double size) +template +static void render_affine_object(const t_sprite *obji, const t_affp *affp, bool is_double, u32 start, u32 end, stype *dst_ptr, u32 mosv, + u32 mosh, u32 base_tile, u32 pxcomb, u16 palette, const u16 *palptr) +{ + // Tile size in bytes for each mode + const u32 tile_bsize = is8bpp ? tile_size_8bpp : tile_size_4bpp; + const u32 tile_bwidth = is8bpp ? tile_width_8bpp : tile_width_4bpp; + + // Affine params + s32 dx = (s16)eswap16(affp->dx); + s32 dy = (s16)eswap16(affp->dy); + s32 dmx = (s16)eswap16(affp->dmx); + s32 dmy = (s16)eswap16(affp->dmy); + + // Object dimensions and boundaries + u32 obj_dimw = obji->obj_w; + u32 obj_dimh = obji->obj_h; + s32 middle_x = is_double ? obji->obj_w : (obji->obj_w / 2); + s32 middle_y = is_double ? obji->obj_h : (obji->obj_h / 2); + s32 obj_width = is_double ? obji->obj_w * 2 : obji->obj_w; + s32 obj_height = is_double ? obji->obj_h * 2 : obji->obj_h; + + s32 vcount = read_ioreg(REG_ADDR_VCOUNT); + if (mosaic) + vcount -= vcount % mosv; + s32 y_delta = vcount - (obji->obj_y + middle_y); + + if (obji->obj_x < (signed)start) + middle_x -= (start - obji->obj_x); + s32 source_x = (obj_dimw << 7) + (y_delta * dmx) - (middle_x * dx); + s32 source_y = (obj_dimh << 7) + (y_delta * dmy) - (middle_x * dy); + + // Early optimization if Y-coord is out completely for this line. + // (if there's no rotation Y coord remains identical throughout the line). + if (!rotate && ((u32)(source_y >> 8)) >= (u32)obj_height) + return; + + u32 d_start = MAX((signed)start, obji->obj_x); + u32 d_end = MIN((signed)end, obji->obj_x + obj_width); + u32 cnt = d_end - d_start; + dst_ptr += d_start; + + bool obj1dmap = read_ioreg(REG_ADDR_DISPCNT) & 0x40; + const u32 tile_pitch = obj1dmap ? (obj_dimw / 8) * tile_bsize : 1024; + u32 px_attr = pxcomb | palette | 0x100; // Combine flags + high palette bit + + // Skip pixels outside of the sprite area, until we reach the sprite "inside" + while (cnt) { + u32 pixel_x = (u32)(source_x >> 8), pixel_y = (u32)(source_y >> 8); + + // Stop once we find a pixel that is actually *inside* the map. + if (pixel_x < obj_dimw && pixel_y < obj_dimh) + break; + + dst_ptr++; + source_x += dx; + if (rotate) + source_y += dy; + cnt--; + } + + // Draw sprite pixels by looking them up first. Lookup address is tricky! + u8 pixval = 0; + for (u32 i = 0; i < cnt; i++) { + u32 pixel_x = (u32)(source_x >> 8), pixel_y = (u32)(source_y >> 8); + + // Check if we run out of the sprite, then we can safely abort. + if (pixel_x >= obj_dimw || pixel_y >= obj_dimh) + return; + + // For mosaic, we "remember" the last looked up pixel. + if (!mosaic || !(i % mosh)) { + // Lookup pixel and draw it. + if (is8bpp) { + // We lookup the byte directly and render it. + const u32 tile_off = base_tile + // Character base + ((pixel_y >> 3) * tile_pitch) + // Skip vertical blocks + ((pixel_x >> 3) * tile_bsize) + // Skip horizontal blocks + ((pixel_y & 0x7) * tile_bwidth) + // Skip vertical rows to the pixel + (pixel_x & 0x7); // Skip the horizontal offset + + pixval = *(OBJ_VRAM0 + (tile_off & 0x7FFF)); // Read pixel value! + } else { + const u32 tile_off = base_tile + // Character base + ((pixel_y >> 3) * tile_pitch) + // Skip vertical blocks + ((pixel_x >> 3) * tile_bsize) + // Skip horizontal blocks + ((pixel_y & 0x7) * tile_bwidth) + // Skip vertical rows to the pixel + ((pixel_x >> 1) & 0x3); // Skip the horizontal offset + + u8 pixpair = *(OBJ_VRAM0 + (tile_off & 0x7FFF)); // Read 2 pixels @4bpp + pixval = ((pixel_x & 1) ? pixpair >> 4 : pixpair & 0xF); + } + } + + // Render the pixel value + if (pixval) { + if (rdtype == FULLCOLOR) + *dst_ptr = palptr[pixval | palette]; + else if (rdtype == INDXCOLOR) + *dst_ptr = pixval | px_attr; // Add combine flags + else if (rdtype == STCKCOLOR) { + // Stack pixels on top of the pixel value and combine flags + if (*dst_ptr & 0x100) + *dst_ptr = pixval | px_attr | ((*dst_ptr) & 0xFFFF0000); + else + *dst_ptr = pixval | px_attr | ((*dst_ptr) << 16); // Stack pixels + } else if (rdtype == PIXCOPY) + *dst_ptr = dst_ptr[DISPLAY_WIDTH]; + } + + // Move to the next pixel, update coords accordingly + dst_ptr++; + source_x += dx; + if (rotate) + source_y += dy; + } +} + +// Renders a single sprite on the current scanline. +// This function calls the affine or regular renderer depending on the sprite. +// Will calculate whether sprite has certain effects (flip, rotation ...) to +// use an optimized renderer function. +template +inline static void render_sprite(const t_sprite *obji, bool is_affine, u32 start, u32 end, stype *scanline, u32 pxcomb, const u16 *palptr) +{ + s32 vcount = read_ioreg(REG_ADDR_VCOUNT); + bool obj1dmap = read_ioreg(REG_ADDR_DISPCNT) & 0x40; + u32 tile = obji->oam_data->split.tileNum; + if (is8bpp && !obj1dmap) { + tile &= ~1; + } + u32 base_tile = tile * 32; + + const u32 mosv = (mosaic ? (read_ioreg(REG_ADDR_MOSAIC) >> 12) & 0xF : 0) + 1; + const u32 mosh = (mosaic ? (read_ioreg(REG_ADDR_MOSAIC) >> 8) & 0xF : 0) + 1; + + // Render the object scanline using the correct mode. + // (in 4bpp mode calculate the palette number) + // Objects use the higher palette part + u16 pal = (is8bpp ? 0 : (obji->oam_data->split.paletteNum << 4)); + + if (is_affine) { + u32 pnum = obji->oam_data->split.matrixNum; + const t_affp *affp_base = (t_affp *)OAM; + const t_affp *affp = &affp_base[pnum]; + + if (affp->dy == 0) // No rotation happening (just scale) + render_affine_object(obji, affp, obji->is_double, start, end, scanline, mosv, mosh, + base_tile, pxcomb, pal, palptr); + else // Full rotation and scaling + render_affine_object(obji, affp, obji->is_double, start, end, scanline, mosv, mosh, + base_tile, pxcomb, pal, palptr); + } else { + // The object could be out of the window, check and skip. + if (obji->obj_x >= (signed)end || obji->obj_x + obji->obj_w <= (signed)start) + return; + + // Non-affine objects can be flipped on both edges. + bool hflip = (obji->oam_data->split.matrixNum >> 3) & 1; + bool vflip = (obji->oam_data->split.matrixNum >> 4) & 1; + + // Calulate the vertical offset (row) to be displayed. Account for vflip. + u32 voffset = vflip ? obji->obj_y + obji->obj_h - vcount - 1 : vcount - obji->obj_y; + if (mosaic) + voffset -= voffset % mosv; + + // Calculate base tile for the object (points to the row to be drawn). + u32 tile_bsize = is8bpp ? tile_size_8bpp : tile_size_4bpp; + u32 tile_bwidth = is8bpp ? tile_width_8bpp : tile_width_4bpp; + u32 obj_pitch = obj1dmap ? (obji->obj_w / 8) * tile_bsize : 1024; + u32 hflip_off = hflip ? ((obji->obj_w / 8) - 1) * tile_bsize : 0; + + // Calculate the pointer to the tile. + const u32 tile_offset = base_tile + // Char offset + (voffset / 8) * obj_pitch + // Select tile row offset + (voffset % 8) * tile_bwidth + // Skip tile rows + hflip_off; // Account for horizontal flip + + // Make everything relative to start + s32 obj_x_offset = obji->obj_x - start; + u32 clipped_width = obj_x_offset >= 0 ? obji->obj_w : obji->obj_w + obj_x_offset; + u32 max_range = obj_x_offset >= 0 ? end - obji->obj_x : end - start; + u32 max_draw = MIN(max_range, clipped_width); + + if (mosaic && mosh > 1) { + if (hflip) + render_object_mosaic(obj_x_offset, max_draw, &scanline[start], tile_offset, mosh, pxcomb, pal, + palptr); + else + render_object_mosaic(obj_x_offset, max_draw, &scanline[start], tile_offset, mosh, pxcomb, pal, + palptr); + } else { + if (hflip) + render_object(obj_x_offset, max_draw, &scanline[start], tile_offset, pxcomb, pal, palptr); + else + render_object(obj_x_offset, max_draw, &scanline[start], tile_offset, pxcomb, pal, palptr); + } + } +} + +// Renders objects on a scanline for a given priority. +// This function assumes that order_obj has been called to prepare the objects. +template void render_scanline_objs(u32 priority, u32 start, u32 end, void *raw_ptr, const u16 *palptr) +{ + stype *scanline = (stype *)raw_ptr; + s32 vcount = read_ioreg(REG_ADDR_VCOUNT); + s32 objn; + u32 objcnt = obj_priority_count[priority][vcount]; + u8 *objlist = obj_priority_list[priority][vcount]; + + // Render all the visible objects for this priority (back to front) + for (objn = objcnt - 1; objn >= 0; objn--) { + // Objects in the list are pre-filtered and sorted in the appropriate order + u32 objoff = objlist[objn]; + const OamData *oam_data = (OamData *)&OAM[objoff * OAM_DATA_SIZE_AFFINE]; + + u16 obj_shape = oam_data->split.shape; + u16 obj_size = oam_data->split.size; + bool is_affine = oam_data->split.affineMode & 1; + bool is_trans = oam_data->split.objMode == OBJ_MOD_SEMITRAN; + s32 obj_x = oam_data->split.x; + s32 obj_y = oam_data->split.y; +#if !EXTENDED_OAM + if (obj_x > DISPLAY_WIDTH) + obj_x -= 512; +#endif + + t_sprite obji = { + .obj_x = obj_x, + .obj_y = obj_y, + .obj_w = obj_dim_table[obj_shape][obj_size][0], + .obj_h = obj_dim_table[obj_shape][obj_size][1], + .oam_data = oam_data, + .is_double = !!((oam_data->split.affineMode >> 1) & 1), + }; + + s32 obj_maxw = (is_affine && obji.is_double) ? obji.obj_w * 2 : obji.obj_w; + + // The object could be out of the window, check and skip. + if (obji.obj_x >= (signed)end || obji.obj_x + obj_maxw <= (signed)start) + continue; + + // ST-OBJs force 1st target bit (forced blending) + bool forcebld = is_trans && rdtype != FULLCOLOR; +#if !EXTENDED_OAM + if (obji.obj_y > DISPLAY_HEIGHT) + obji.obj_y -= 256; +#endif + // In PIXCOPY mode, we have already some stuff rendered (winout) and now + // we render the "win-in" area for this object. The PIXCOPY function will + // copy (merge) the two pixels depending on the result of the sprite render + // The temporary buffer is rendered on the next scanline area. + if (rdtype == PIXCOPY) { + u32 sec_start = MAX((signed)start, obji.obj_x); + u32 sec_end = MIN((signed)end, obji.obj_x + obj_maxw); + u32 obj_enable = read_ioreg(REG_ADDR_WINOUT) >> 8; + + // Render at the next scanline! + u16 *tmp_ptr = (u16 *)&scanline[GBA_SCREEN_PITCH]; + render_scanline_conditional(sec_start, sec_end, tmp_ptr, obj_enable); + } + + // Calculate combine masks. These store 2 bits of info: 1st and 2nd target. + // If set, the current pixel belongs to a layer that is 1st or 2nd target. + // For ST-objs, we set an extra bit, for later blending. + u32 pxcomb = (forcebld ? 0x800 : 0) | color_flags(4); + + bool emosaic = oam_data->split.mosaic; + bool is_8bpp = oam_data->split.bpp; + + // Some games enable mosaic but set it to size 0 (1), so ignore. + const u32 mosreg = read_ioreg(REG_ADDR_MOSAIC) & 0xFF00; + + if (emosaic && mosreg) { + if (is_8bpp) + render_sprite(&obji, is_affine, start, end, scanline, pxcomb, palptr); + else + render_sprite(&obji, is_affine, start, end, scanline, pxcomb, palptr); + } else { + if (is_8bpp) + render_sprite(&obji, is_affine, start, end, scanline, pxcomb, palptr); + else + render_sprite(&obji, is_affine, start, end, scanline, pxcomb, palptr); + } + } +} + +int sprite_limit = 0; + +// Goes through the object list in the OAM (from #127 to #0) and adds objects +// into a sorted list by priority for the current row. +// Invisible objects are discarded. ST-objects are flagged. Cycle counting is +// performed to discard excessive objects (to match HW capabilities). +static void order_obj(u32 video_mode) +{ + u32 obj_num; + u32 row; + u16 rend_cycles[DISPLAY_HEIGHT]; + + bool hblank_free = read_ioreg(REG_ADDR_DISPCNT) & 0x20; + u16 max_rend_cycles = !sprite_limit ? REND_CYC_MAX : hblank_free ? REND_CYC_REDUCED : REND_CYC_SCANLINE; + + memset(obj_priority_count, 0, sizeof(obj_priority_count)); + memset(obj_alpha_count, 0, sizeof(obj_alpha_count)); + memset(rend_cycles, 0, sizeof(rend_cycles)); + + for (obj_num = 0; obj_num < 128; obj_num++) { + const OamData *oam_data = (OamData *)&OAM[obj_num * OAM_DATA_SIZE_AFFINE]; + + // Bit 9 disables regular sprites (that is, non-affine ones). + if (oam_data->split.affineMode == 2) + continue; + + u16 obj_shape = oam_data->split.shape; + u32 obj_mode = oam_data->split.objMode; + + // Prohibited shape and mode + if ((obj_shape == 0x3) || (obj_mode == OBJ_MOD_INVALID)) + continue; + + // On bitmap modes, objs 0-511 are not usable, ingore them. + if ((video_mode >= 3) && (!(oam_data->split.tileNum & 0x200))) + continue; + + // Calculate object size (from size and shape attr bits) + u16 obj_size = oam_data->split.size; + s32 obj_height = obj_dim_table[obj_shape][obj_size][1]; + s32 obj_width = obj_dim_table[obj_shape][obj_size][0]; + s32 obj_y = oam_data->split.y; + +#if !EXTENDED_OAM + if (obj_y > DISPLAY_HEIGHT) + obj_y -= 256; +#endif + // Double size for affine sprites with double bit set + if ((oam_data->split.affineMode >> 1) & 1) { + obj_height *= 2; + obj_width *= 2; + } + + if (((obj_y + obj_height) > 0) && (obj_y < DISPLAY_HEIGHT)) { + s32 obj_x = oam_data->split.x; + +#if !EXTENDED_OAM + if (obj_x > DISPLAY_WIDTH) + obj_x -= 512; +#endif + if (((obj_x + obj_width) > 0) && (obj_x < DISPLAY_WIDTH)) { + u32 obj_priority = oam_data->split.priority; + bool is_affine = oam_data->split.affineMode & 1; + // Clip Y coord and height to the 0..159 interval + u32 starty = MAX(obj_y, 0); + u32 endy = MIN(obj_y + obj_height, DISPLAY_HEIGHT); + + // Calculate needed cycles to render the sprite + u16 cyccnt = is_affine ? (10 + obj_width * 2) : obj_width; + + switch (obj_mode) { + case OBJ_MOD_SEMITRAN: + for (row = starty; row < endy; row++) { + if (rend_cycles[row] < max_rend_cycles) { + u32 cur_cnt = obj_priority_count[obj_priority][row]; + obj_priority_list[obj_priority][row][cur_cnt] = obj_num; + obj_priority_count[obj_priority][row] = cur_cnt + 1; + rend_cycles[row] += cyccnt; + // Mark the row as having semi-transparent objects + obj_alpha_count[row] = 1; + } + } + break; + case OBJ_MOD_WINDOW: + obj_priority = 4; + /* fallthrough */ + case OBJ_MOD_NORMAL: + // Add the object to the list. + for (row = starty; row < endy; row++) { + if (rend_cycles[row] < max_rend_cycles) { + u32 cur_cnt = obj_priority_count[obj_priority][row]; + obj_priority_list[obj_priority][row][cur_cnt] = obj_num; + obj_priority_count[obj_priority][row] = cur_cnt + 1; + rend_cycles[row] += cyccnt; + } + } + break; + }; + } + } + } +} + +u32 layer_order[16]; +u32 layer_count; + +// Sorts active BG/OBJ layers and generates an ordered list of layers. +// Things are drawn back to front, so lowest priority goes first. +static void order_layers(u32 layer_flags, u32 vcnt) +{ + bool obj_enabled = (layer_flags & 0x10); + s32 priority; + + layer_count = 0; + + for (priority = 3; priority >= 0; priority--) { + bool anyobj = obj_priority_count[priority][vcnt] > 0; + s32 lnum; + + for (lnum = 3; lnum >= 0; lnum--) { + if (((layer_flags >> lnum) & 1) && ((read_ioreg(REG_ADDR_BGxCNT(lnum)) & 0x03) == priority)) { + layer_order[layer_count++] = lnum; + } + } + + if (obj_enabled && anyobj) + layer_order[layer_count++] = priority | 0x04; + } +} + +// Blending is performed by separating an RGB value into 0G0R0B (32 bit) +// Since blending factors are at most 16, mult/add operations do not overflow +// to the neighbouring color and can be performed much faster than separatedly + +// Here follow the mask value to separate/expand the color to 32 bit, +// the mask to detect overflows in the blend operation and + +#define BLND_MSK (SATR_MSK | SATG_MSK | SATB_MSK) + +#define OVFG_MSK 0x04000000 +#define OVFR_MSK 0x00008000 +#define OVFB_MSK 0x00000020 +#define SATG_MSK 0x03E00000 +#define SATR_MSK 0x00007C00 +#define SATB_MSK 0x0000001F + +typedef enum { + OBJ_BLEND, // No effects, just blend forced-blend pixels (ie. ST objects) + BLEND_ONLY, // Just alpha blending (if the pixels are 1st and 2nd target) + BLEND_BRIGHT, // Perform alpha blending if appropiate, and brighten otherwise + BLEND_DARK, // Same but with darken effecg +} blendtype; + +// Applies blending (and optional brighten/darken) effect to a bunch of +// color-indexed pixel pairs. Depending on the mode and the pixel target +// number, blending, darken/brighten or no effect will be applied. +// Bits 0-8 encode the color index (paletted colors) +// Bit 9 is set if the pixel belongs to a 1st target layer +// Bit 10 is set if the pixel belongs to a 2nd target layer +// Bit 11 is set if the pixel belongs to a ST-object +template static void merge_blend(u32 start, u32 end, u16 *dst, u32 *src) +{ + u32 bldalpha = read_ioreg(REG_ADDR_BLDALPHA); + u32 brightf = MIN(16, read_ioreg(REG_ADDR_BLDY) & 0x1F); + u32 blend_a = MIN(16, (bldalpha >> 0) & 0x1F); + u32 blend_b = MIN(16, (bldalpha >> 8) & 0x1F); + + bool can_saturate = blend_a + blend_b > 16; + + if (can_saturate) { + // If blending can result in saturation, we need to clamp output values. + while (start < end) { + u32 pixpair = src[start]; + // If ST-OBJ, force blending mode (has priority over other effects). + // If regular blending mode, blend if 1st/2nd bits are set respectively. + // Otherwise, apply other color effects if 1st bit is set. + bool force_blend = (pixpair & 0x04000800) == 0x04000800; + bool do_blend = (pixpair & 0x04000200) == 0x04000200; + if ((st_objs && force_blend) || (do_blend && bldtype == BLEND_ONLY)) { + // Top pixel is 1st target, pixel below is 2nd target. Blend! + u16 p1 = PLTT[(pixpair >> 0) & 0x1FF]; + u16 p2 = PLTT[(pixpair >> 16) & 0x1FF]; + u32 p1e = (p1 | (p1 << 16)) & BLND_MSK; + u32 p2e = (p2 | (p2 << 16)) & BLND_MSK; + u32 pfe = (((p1e * blend_a) + (p2e * blend_b)) >> 4); + + // If the overflow bit is set, saturate (set) all bits to one. + if (pfe & (OVFR_MSK | OVFG_MSK | OVFB_MSK)) { + if (pfe & OVFG_MSK) + pfe |= SATG_MSK; + if (pfe & OVFR_MSK) + pfe |= SATR_MSK; + if (pfe & OVFB_MSK) + pfe |= SATB_MSK; + } + pfe &= BLND_MSK; + dst[start++] = (pfe >> 16) | pfe; + } else if ((bldtype == BLEND_DARK || bldtype == BLEND_BRIGHT) && (pixpair & 0x200) == 0x200) { + // Top pixel is 1st-target, can still apply bright/dark effect. + u16 pidx = PLTT[pixpair & 0x1FF]; + u32 epixel = (pidx | (pidx << 16)) & BLND_MSK; + u32 pa = bldtype == BLEND_DARK ? 0 : ((BLND_MSK * brightf) >> 4) & BLND_MSK; + u32 pb = ((epixel * (16 - brightf)) >> 4) & BLND_MSK; + epixel = (pa + pb) & BLND_MSK; + dst[start++] = (epixel >> 16) | epixel; + } else { + dst[start++] = PLTT[pixpair & 0x1FF]; // No effects + } + } + } else { + while (start < end) { + u32 pixpair = src[start]; + bool do_blend = (pixpair & 0x04000200) == 0x04000200; + bool force_blend = (pixpair & 0x04000800) == 0x04000800; + if ((st_objs && force_blend) || (do_blend && bldtype == BLEND_ONLY)) { + // Top pixel is 1st target, pixel below is 2nd target. Blend! + u16 p1 = PLTT[(pixpair >> 0) & 0x1FF]; + u16 p2 = PLTT[(pixpair >> 16) & 0x1FF]; + u32 p1e = (p1 | (p1 << 16)) & BLND_MSK; + u32 p2e = (p2 | (p2 << 16)) & BLND_MSK; + u32 pfe = (((p1e * blend_a) + (p2e * blend_b)) >> 4) & BLND_MSK; + dst[start++] = (pfe >> 16) | pfe; + } else if ((bldtype == BLEND_DARK || bldtype == BLEND_BRIGHT) && (pixpair & 0x200) == 0x200) { + // Top pixel is 1st-target, can still apply bright/dark effect. + u16 pidx = PLTT[pixpair & 0x1FF]; + u32 epixel = (pidx | (pidx << 16)) & BLND_MSK; + u32 pa = bldtype == BLEND_DARK ? 0 : ((BLND_MSK * brightf) >> 4) & BLND_MSK; + u32 pb = ((epixel * (16 - brightf)) >> 4) & BLND_MSK; + epixel = (pa + pb) & BLND_MSK; + dst[start++] = (epixel >> 16) | epixel; + } else { + dst[start++] = PLTT[pixpair & 0x1FF]; // No effects + } + } + } +} + +// Applies brighten/darken effect to a bunch of color-indexed pixels. +template static void merge_brightness(u32 start, u32 end, u16 *srcdst) +{ + u32 brightness = MIN(16, read_ioreg(REG_ADDR_BLDY) & 0x1F); + + while (start < end) { + u16 spix = srcdst[start]; + u16 pixcol = PLTT[spix & 0x1FF]; + + if ((spix & 0x200) == 0x200) { + // Pixel is 1st target, can apply color effect. + u32 epixel = (pixcol | (pixcol << 16)) & BLND_MSK; + u32 pa = bldtype == BLEND_DARK ? 0 : ((BLND_MSK * brightness) >> 4) & BLND_MSK; // B/W + u32 pb = ((epixel * (16 - brightness)) >> 4) & BLND_MSK; // Pixel color + epixel = (pa + pb) & BLND_MSK; + pixcol = (epixel >> 16) | epixel; + } + + srcdst[start++] = pixcol; + } +} + +// Fills a segment using the backdrop color (in the right mode). +template void fill_line_background(u32 start, u32 end, dsttype *scanline) +{ + dsttype bgcol = PLTT[0]; + u16 bg_comb = color_flags(5); + while (start < end) + if (rdmode == FULLCOLOR) + scanline[start++] = bgcol; + else + scanline[start++] = 0 | bg_comb; +} + +// Renders the backdrop color (ie. whenever no layer is active) applying +// any effects that might still apply (usually darken/brighten). +static void render_backdrop(u32 start, u32 end, u16 *scanline) +{ + u16 bldcnt = read_ioreg(REG_ADDR_BLDCNT); + u16 pixcol = PLTT[0]; + u32 effect = (bldcnt >> 6) & 0x03; + u32 bd_1st_target = ((bldcnt >> 0x5) & 0x01); + + if (bd_1st_target && effect == COL_EFFECT_BRIGHT) { + u32 brightness = MIN(16, read_ioreg(REG_ADDR_BLDY) & 0x1F); + + // Unpack 16 bit pixel for fast blending operation + u32 epixel = (pixcol | (pixcol << 16)) & BLND_MSK; + u32 pa = ((BLND_MSK * brightness) >> 4) & BLND_MSK; // White color + u32 pb = ((epixel * (16 - brightness)) >> 4) & BLND_MSK; // Pixel color + epixel = (pa + pb) & BLND_MSK; + pixcol = (epixel >> 16) | epixel; + } else if (bd_1st_target && effect == COL_EFFECT_DARK) { + u32 brightness = MIN(16, read_ioreg(REG_ADDR_BLDY) & 0x1F); + u32 epixel = (pixcol | (pixcol << 16)) & BLND_MSK; + epixel = ((epixel * (16 - brightness)) >> 4) & BLND_MSK; // Pixel color + pixcol = (epixel >> 16) | epixel; + } + + // Fill the line with that color + while (start < end) + scanline[start++] = pixcol; +} + +// Renders all the available and enabled layers (in tiled mode). +// Walks the list of layers in visibility order and renders them in the +// specified mode (taking into consideration the first layer, etc). +template +void tile_render_layers(u32 start, u32 end, dsttype *dst_ptr, u32 enabled_layers) +{ + u32 lnum; + u32 base_done = 0; + u16 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + u16 video_mode = dispcnt & 0x07; + bool obj_enabled = (enabled_layers & 0x10); // Objects are visible + + bool objlayer_is_1st_tgt = ((read_ioreg(REG_ADDR_BLDCNT) >> 4) & 1) != 0; + bool has_trans_obj = obj_alpha_count[read_ioreg(REG_ADDR_VCOUNT)]; + + for (lnum = 0; lnum < layer_count; lnum++) { + u32 layer = layer_order[lnum]; + bool is_obj = layer & 0x4; + if (is_obj && obj_enabled) { + bool can_skip_blend = !has_trans_obj && !objlayer_is_1st_tgt; + + // If it's the first layer, make sure to fill with backdrop color. + if (!base_done) + fill_line_background(start, end, dst_ptr); + + // Optimization: skip blending mode if no blending can happen to this layer + if (objmode == STCKCOLOR && can_skip_blend) + render_scanline_objs(layer & 0x3, start, end, dst_ptr, &PLTT[0x100]); + else + render_scanline_objs(layer & 0x3, start, end, dst_ptr, &PLTT[0x100]); + + base_done = 1; + } else if (!is_obj && ((1 << layer) & enabled_layers)) { + bool layer_is_1st_tgt = ((read_ioreg(REG_ADDR_BLDCNT) >> layer) & 1) != 0; + bool can_skip_blend = !has_trans_obj && !layer_is_1st_tgt; + + bool is_affine = (video_mode >= 1) && (layer >= 2); + u32 fnidx = (base_done) | (is_affine ? 2 : 0); + + // Can optimize rendering if no blending can really happen. + // If stack mode, no blending and not base layer, we might speed up a bit + if (bgmode == STCKCOLOR && can_skip_blend) { + static const tile_render_function rdfns[4] = { + render_scanline_text, + render_scanline_text, + render_scanline_affine, + render_scanline_affine, + }; + rdfns[fnidx](layer, start, end, dst_ptr, PLTT); + } else { + static const tile_render_function rdfns[4] = { + render_scanline_text, + render_scanline_text, + render_scanline_affine, + render_scanline_affine, + }; + rdfns[fnidx](layer, start, end, dst_ptr, PLTT); + } + + base_done = 1; + } + } + + // Render background if we did not render any active layer. + if (!base_done) + fill_line_background(start, end, dst_ptr); +} + +// Renders all layers honoring color effects (blending, brighten/darken). +// It uses different rendering routines depending on the coloring effect +// requirements, speeding up common cases where no effects are used. + +// No effects use NORMAL mode (RBB565 color is written on the buffer). +// For blending, we use BLEND mode to record the two top-most pixels. +// For other effects we use COLOR16, which records an indexed color in the +// buffer (used for darken/brighten effects at later passes) or COLOR32, +// which similarly uses an indexed color for rendering but recording one +// color for the background and another one for the object layer. + +static void render_w_effects(u32 start, u32 end, u16 *scanline, u32 enable_flags, const layer_render_struct *renderers) +{ + bool effects_enabled = enable_flags & 0x20; // Window bit for effects. + bool obj_blend = obj_alpha_count[read_ioreg(REG_ADDR_VCOUNT)] > 0; + u16 bldcnt = read_ioreg(REG_ADDR_BLDCNT); + + // If the window bits disable effects, default to NONE + u32 effect_type = effects_enabled ? ((bldcnt >> 6) & 0x03) : COL_EFFECT_NONE; + + switch (effect_type) { + case COL_EFFECT_BRIGHT: { + // If no layers are 1st target, no effect will really happen. + bool some_1st_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x3F) != 0; + // If the factor is zero, it's the same as "regular" rendering. + bool non_zero_blend = (read_ioreg(REG_ADDR_BLDY) & 0x1F) != 0; + if (some_1st_tgt && non_zero_blend) { + if (obj_blend) { + u32 tmp_buf[DISPLAY_WIDTH]; + renderers->indexed_u32(start, end, tmp_buf, enable_flags); + merge_blend(start, end, scanline, tmp_buf); + } else { + renderers->indexed_u16(start, end, scanline, enable_flags); + merge_brightness(start, end, scanline); + } + return; + } + } break; + + case COL_EFFECT_DARK: { + // If no layers are 1st target, no effect will really happen. + bool some_1st_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x3F) != 0; + // If the factor is zero, it's the same as "regular" rendering. + bool non_zero_blend = (read_ioreg(REG_ADDR_BLDY) & 0x1F) != 0; + if (some_1st_tgt && non_zero_blend) { + if (obj_blend) { + u32 tmp_buf[DISPLAY_WIDTH]; + renderers->indexed_u32(start, end, tmp_buf, enable_flags); + merge_blend(start, end, scanline, tmp_buf); + } else { + renderers->indexed_u16(start, end, scanline, enable_flags); + merge_brightness(start, end, scanline); + } + return; + } + } break; + + case COL_EFFECT_BLEND: { + // If no layers are 1st or 2nd target, no effect will really happen. + bool some_1st_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x003F) != 0; + bool some_2nd_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x3F00) != 0; + // If 1st target is 100% opacity and 2nd is 0%, just render regularly. + bool non_trns_tgt = (read_ioreg(REG_ADDR_BLDALPHA) & 0x1F1F) != 0x001F; + if (some_1st_tgt && some_2nd_tgt && non_trns_tgt) { + u32 tmp_buf[DISPLAY_WIDTH]; + renderers->stacked(start, end, tmp_buf, enable_flags); + if (obj_blend) + merge_blend(start, end, scanline, tmp_buf); + else + merge_blend(start, end, scanline, tmp_buf); + return; + } + } break; + + case COL_EFFECT_NONE: + // Default case, see below. + break; + }; + + // Default rendering mode, without layer effects (except perhaps sprites). + if (obj_blend) { + u32 tmp_buf[DISPLAY_WIDTH]; + renderers->stacked(start, end, tmp_buf, enable_flags); + merge_blend(start, end, scanline, tmp_buf); + } else { + renderers->fullcolor(start, end, scanline, enable_flags); + } +} + +#define bitmap_layer_render_functions(rdmode, dsttype, mode, ttype, w, h) \ + { \ + { \ + render_scanline_bitmap, \ + render_scanline_bitmap, \ + render_scanline_bitmap, \ + }, \ + { \ + render_scanline_bitmap, \ + render_scanline_bitmap, \ + render_scanline_bitmap, \ + } \ + } + +static const bitmap_layer_render_struct idx32_bmrend[3][2] + = { bitmap_layer_render_functions(INDXCOLOR, u32, 3, u16, DISPLAY_WIDTH, DISPLAY_HEIGHT), + bitmap_layer_render_functions(INDXCOLOR, u32, 4, u8, DISPLAY_WIDTH, DISPLAY_HEIGHT), + bitmap_layer_render_functions(INDXCOLOR, u32, 5, u16, DISPLAY_HEIGHT, 128) }; + +// Render the BG and OBJ in a bitmap scanline from start to end ONLY if +// enable_flag allows that layer/OBJ. + +template +static void bitmap_render_layers(u32 start, u32 end, dsttype *scanline, u32 enable_flags) +{ + u16 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + bool has_trans_obj = obj_alpha_count[read_ioreg(REG_ADDR_VCOUNT)]; + bool objlayer_is_1st_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x10) != 0; + bool bg2_is_1st_tgt = (read_ioreg(REG_ADDR_BLDCNT) & 0x4) != 0; + + // Fill in the renderers for a layer based on the mode type, + static const bitmap_layer_render_struct renderers[3][2] + = { bitmap_layer_render_functions(bgmode, dsttype, 3, u16, DISPLAY_WIDTH, DISPLAY_HEIGHT), + bitmap_layer_render_functions(bgmode, dsttype, 4, u8, DISPLAY_WIDTH, DISPLAY_HEIGHT), + bitmap_layer_render_functions(bgmode, dsttype, 5, u16, DISPLAY_HEIGHT, 128) }; + + const u32 mosamount = read_ioreg(REG_ADDR_MOSAIC) & 0xFF; + u32 bg_control = read_ioreg(REG_ADDR_BG2CNT); + u32 mmode = ((bg_control & 0x40) && (mosamount != 0)) ? 1 : 0; + + unsigned modeidx = (dispcnt & 0x07) - 3; + const bitmap_layer_render_struct *mode_rend = &renderers[modeidx][mmode]; + const bitmap_layer_render_struct *idxm_rend = &idx32_bmrend[modeidx][mmode]; + + u32 current_layer; + u32 layer_order_pos; + + fill_line_background(start, end, scanline); + + for (layer_order_pos = 0; layer_order_pos < layer_count; layer_order_pos++) { + current_layer = layer_order[layer_order_pos]; + if (current_layer & 0x04) { + if (enable_flags & 0x10) { + bool can_skip_blend = !has_trans_obj && !objlayer_is_1st_tgt; + + // Optimization: skip blending mode if no blending can happen to this layer + if (objmode == STCKCOLOR && can_skip_blend) + render_scanline_objs(current_layer & 3, start, end, scanline, &PLTT[0x100]); + else + render_scanline_objs(current_layer & 3, start, end, scanline, &PLTT[0x100]); + } + } else { + if (enable_flags & 0x04) { + s32 dx = (s16)read_ioreg(REG_ADDR_BG2PA); + s32 dy = (s16)read_ioreg(REG_ADDR_BG2PC); + + // Optimization: Skip stack mode if there's no blending happening. + bool can_skip_blend = !has_trans_obj && !bg2_is_1st_tgt; + const bitmap_layer_render_struct *rd = (bgmode == STCKCOLOR && can_skip_blend) ? idxm_rend : mode_rend; + + if (dy) + rd->affine_render(start, end, scanline, PLTT); + else if (dx == 256) + rd->blit_render(start, end, scanline, PLTT); + else + rd->scale_render(start, end, scanline, PLTT); + } + } + } +} + +static const layer_render_struct tile_mode_renderers = { + .fullcolor = tile_render_layers, + .indexed_u16 = tile_render_layers, + .indexed_u32 = tile_render_layers, + .stacked = tile_render_layers, +}; + +static const layer_render_struct bitmap_mode_renderers = { + .fullcolor = bitmap_render_layers, + .indexed_u16 = bitmap_render_layers, + .indexed_u32 = bitmap_render_layers, + .stacked = bitmap_render_layers, +}; + +// Renders a full scanline, given an enable_flags mask (for which layers and +// effects are enabled). +static void render_scanline_conditional(u32 start, u32 end, u16 *scanline, u32 enable_flags) +{ + u16 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + u32 video_mode = dispcnt & 0x07; + + // Check if any layer is actually active. + if (layer_count && (enable_flags & 0x1F)) { + // Color effects currently only supported in indexed-color modes (tiled and mode 4) + if (video_mode < 3) + render_w_effects(start, end, scanline, enable_flags, &tile_mode_renderers); + else if (video_mode == 4) + render_w_effects(start, end, scanline, enable_flags, &bitmap_mode_renderers); + else + // TODO: Implement mode 3 & 5 color effects (at least partially, ie. ST objs) + bitmap_mode_renderers.fullcolor(start, end, scanline, enable_flags); + } else + // Render the backdrop color, since no layers are enabled/visible. + render_backdrop(start, end, scanline); +} + +// Renders the are outside of all active windows +static void render_windowout_pass(u16 *scanline, u32 start, u32 end) +{ + u32 winout = read_ioreg(REG_ADDR_WINOUT); + u32 wndout_enable = winout & 0x3F; + + render_scanline_conditional(start, end, scanline, wndout_enable); +} + +// Renders window-obj. This is a pixel-level windowing effect, based on sprites +// (objects) with a special rendering mode (the sprites are not themselves +// visible but rather "enable" other pixels to be rendered conditionally). +static void render_windowobj_pass(u16 *scanline, u32 start, u32 end) +{ + u32 winout = read_ioreg(REG_ADDR_WINOUT); + u32 wndout_enable = winout & 0x3F; + + // First we render the "window-out" segment. + render_scanline_conditional(start, end, scanline, wndout_enable); + + // Now we render the objects in "copy" mode. This renders the scanline in + // WinObj-mode to a temporary buffer and performs a "copy-mode" render. + // In this mode, we copy pixels from the temp buffer to the final buffer + // whenever an object pixel is rendered. + render_scanline_objs(4, start, end, scanline, NULL); + + // TODO: Evaluate whether it's better to render the whole line and copy, + // or render subsegments and copy as we go (depends on the pixel/obj count) +} + +// If the window Y coordinates are out of the window range we can skip +// rendering the inside of the window. +inline bool in_window_y(u32 vcount, u32 top, u32 bottom) +{ + // TODO: check if these are reversed when top-bottom are also reversed. + if (top > DISPLAY_HEIGHT + 67) // This causes the window to be invisible + return false; + if (bottom > DISPLAY_HEIGHT + 67) // This makes it all visible + return true; + + if (top > bottom) /* Reversed: if not in the "band" */ + return vcount > top || vcount <= bottom; + + return vcount >= top && vcount < bottom; +} + +// Renders window 0/1. Checks boundaries and divides the segment into +// subsegments (if necessary) rendering each one in their right mode. +// outfn is called for "out-of-window" rendering. +template static void render_window_n_pass(u16 *scanline, u32 start, u32 end) +{ + u32 vcount = read_ioreg(REG_ADDR_VCOUNT); + // Check the Y coordinates to check if they fall in the right row + u32 win_top = WIN_GET_LOWER(*(winreg_t *)(REG_ADDR_WINxV(winnum))); + u32 win_bot = WIN_GET_HIGHER(*(winreg_t *)(REG_ADDR_WINxV(winnum))); + // Check the X coordinates and generate up to three segments + // Clip the coordinates to the [start, end) range. + u32 win_lraw = WIN_GET_LOWER(*(winreg_t *)(REG_ADDR_WINxH(winnum))); + u32 win_rraw = WIN_GET_HIGHER(*(winreg_t *)(REG_ADDR_WINxH(winnum))); + u32 win_l = MAX(start, MIN(end, win_lraw)); + u32 win_r = MAX(start, MIN(end, win_rraw)); + + bool goodwin = win_lraw < win_rraw; + + if (!in_window_y(vcount, win_top, win_bot) || (win_lraw == win_rraw)) + // WindowN is completely out, just render all out. + outfn(scanline, start, end); + else { + // Render window withtin the clipped range + // Enable bits for stuff inside the window (and outside) + u32 winin = (*(winreg_t *)REG_ADDR_WININ) & 0xFFFF; + u32 wndn_enable = (winin >> (8 * winnum)) & 0x3F; + + // If the window is defined upside down, the areas are inverted. + if (goodwin) { + // Render [start, win_l) range (which is outside the window) + if (win_l != start) + outfn(scanline, start, win_l); + // Render the actual window0 pixels + render_scanline_conditional(win_l, win_r, scanline, wndn_enable); + // Render the [win_l, end] range (outside) + if (win_r != end) + outfn(scanline, win_r, end); + } else { + // Render [0, win_r) range (which is "inside" window0) + if (win_r != start) + render_scanline_conditional(start, win_r, scanline, wndn_enable); + // The actual window is now outside, render recursively + outfn(scanline, win_r, win_l); + // Render the [win_l, DISPLAY_WIDTH] range ("inside") + if (win_l != end) + render_scanline_conditional(win_l, end, scanline, wndn_enable); + } + } +} + +// Renders a full scaleline, taking into consideration windowing effects. +// Breaks the rendering step into N steps, for each windowed region. +static void render_scanline_window(u16 *scanline) +{ + u16 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + u32 win_ctrl = (dispcnt >> 13); + + // Priority decoding for windows + switch (win_ctrl) { + case 0x0: // No windows are active. + render_scanline_conditional(0, DISPLAY_WIDTH, scanline); + break; + + case 0x1: // Window 0 + render_window_n_pass(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x2: // Window 1 + render_window_n_pass(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x3: // Window 0 & 1 + render_window_n_pass, 0>(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x4: // Window Obj + render_windowobj_pass(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x5: // Window 0 & Obj + render_window_n_pass(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x6: // Window 1 & Obj + render_window_n_pass(scanline, 0, DISPLAY_WIDTH); + break; + + case 0x7: // Window 0, 1 & Obj + render_window_n_pass, 0>(scanline, 0, DISPLAY_WIDTH); + break; + } +} + +static const u8 active_layers[] = { + 0x1F, // Mode 0, Tile BG0-3 and OBJ + 0x17, // Mode 1, Tile BG0-2 and OBJ + 0x1C, // Mode 2, Tile BG2-3 and OBJ + 0x14, // Mode 3, BMP BG2 and OBJ + 0x14, // Mode 4, BMP BG2 and OBJ + 0x14, // Mode 5, BMP BG2 and OBJ + 0, // Unused + 0, +}; + +void update_scanline(void) +{ + u32 pitch = get_screen_pitch(); + u16 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + u32 vcount = read_ioreg(REG_ADDR_VCOUNT); + u16 *screen_offset = get_screen_pixels() + (vcount * pitch); + u32 video_mode = dispcnt & 0x07; + + order_layers((dispcnt >> 8) & active_layers[video_mode], vcount); + + // If the screen is in in forced blank draw pure white. + if (dispcnt & 0x80) + memset(screen_offset, 0xff, DISPLAY_WIDTH * sizeof(u16)); + else + render_scanline_window(screen_offset); + + // Mode 0 does not use any affine params at all. + if (video_mode) { + // Account for vertical mosaic effect, by correcting affine references. + const u32 bgmosv = ((read_ioreg(REG_ADDR_MOSAIC) >> 4) & 0xF) + 1; + + if (read_ioreg(REG_ADDR_BG2CNT) & 0x40) { // Mosaic enabled for this BG + if ((vcount % bgmosv) == bgmosv - 1) { // Correct after the last line + affine_reference_x[0] += (s16)read_ioreg(REG_ADDR_BG2PB) * bgmosv; + affine_reference_y[0] += (s16)read_ioreg(REG_ADDR_BG2PD) * bgmosv; + } + } else { + affine_reference_x[0] += (s16)read_ioreg(REG_ADDR_BG2PB); + affine_reference_y[0] += (s16)read_ioreg(REG_ADDR_BG2PD); + } + + if (read_ioreg(REG_ADDR_BG3CNT) & 0x40) { + if ((vcount % bgmosv) == bgmosv - 1) { + affine_reference_x[1] += (s16)read_ioreg(REG_ADDR_BG3PB) * bgmosv; + affine_reference_y[1] += (s16)read_ioreg(REG_ADDR_BG3PD) * bgmosv; + } + } else { + affine_reference_x[1] += (s16)read_ioreg(REG_ADDR_BG3PB); + affine_reference_y[1] += (s16)read_ioreg(REG_ADDR_BG3PD); + } + } +} + +void gpsp_draw_frame(u16 *framebuf) +{ + int i; + + gba_screen_pixels = framebuf; + + // assume that the oam is only updated once before the frame + // starts to be drawn + u32 dispcnt = read_ioreg(REG_ADDR_DISPCNT); + u32 video_mode = dispcnt & 0x07; + order_obj(video_mode); + + for (i = 0; i < DISPLAY_HEIGHT; i++) { + REG_VCOUNT = i; + if (((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT) { + REG_DISPSTAT |= INTR_FLAG_VCOUNT; + if (REG_DISPSTAT & DISPSTAT_VCOUNT_INTR) + gIntrTable[INTR_INDEX_VCOUNT](); + } + video_reload_counters(false); + update_scanline(); + + REG_DISPSTAT |= INTR_FLAG_HBLANK; + + RunDMAs(DMA_HBLANK); + + if (REG_DISPSTAT & DISPSTAT_HBLANK_INTR) + gIntrTable[INTR_INDEX_HBLANK](); + + REG_DISPSTAT &= ~INTR_FLAG_HBLANK; + REG_DISPSTAT &= ~INTR_FLAG_VCOUNT; + } + + video_reload_counters(true); +} + +#endif diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 21d27dbf9..6df7d1a4e 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -473,4 +473,4 @@ void *Platform_realloc(void *ptr, size_t numBytes) } void Platform_free(void *ptr) { HeapFree(GetProcessHeap(), 0, ptr); } -void Platform_QueueAudio(const u8 *data, u32 numBytes) { } +void Platform_QueueAudio(const s16 *data, u32 numBytes) { } diff --git a/src/sprite.c b/src/sprite.c index f4a7bbdea..524641702 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -9,7 +9,7 @@ #include "animation_commands.h" #include "platform/platform.h" -#if !PLATFORM_GBA && !PLATFORM_SDL +#if !PLATFORM_GBA && RENDERER != RENDERER_SOFTWARE extern void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum); extern void Platform_TransformSprite(Sprite *sprite, SpriteTransform *transform); #endif @@ -395,7 +395,7 @@ NONMATCH("asm/non_matching/engine/TransformSprite.inc", void TransformSprite(Spr // sp24 = s UnkSpriteStruct big; const SpriteOffset *dimensions = s->dimensions; -#if PORTABLE && (RENDERER != RENDERER_SOFTWARE) +#if PORTABLE && RENDERER != RENDERER_SOFTWARE Platform_TransformSprite(s, transform); return; #endif @@ -727,7 +727,7 @@ void DisplaySprite(Sprite *sprite) oam->split.paletteNum += sprite->palId; #endif -#if !PLATFORM_GBA && !PLATFORM_SDL +#if !PLATFORM_GBA && RENDERER != RENDERER_SOFTWARE // TEMP // Quick hack for getting output in OpenGL test // The whole function call should be replaced by this! diff --git a/tools/scaninc/source_file.cpp b/tools/scaninc/source_file.cpp index 9d188eb73..53e258d95 100644 --- a/tools/scaninc/source_file.cpp +++ b/tools/scaninc/source_file.cpp @@ -31,7 +31,7 @@ SourceFileType GetFileType(std::string& path) std::string extension = path.substr(pos + 1); - if (extension == "c") + if (extension == "c" || extension == "cc") return SourceFileType::Cpp; else if (extension == "s") return SourceFileType::Asm;