Saturn builds
This commit is contained in:
@@ -85,22 +85,100 @@ target_link_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
)
|
||||
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
# --start-group/--end-group allow circular archive references
|
||||
# (e.g. libyaul allocator symbols referenced by libzip / yyjson objects).
|
||||
-Wl,--start-group
|
||||
"${_YAUL_SYSROOT}/lib/libyaul.a"
|
||||
"${_YAUL_SYSROOT}/lib/libzip.a"
|
||||
"${_YAUL_SYSROOT}/lib/libz.a"
|
||||
m
|
||||
# GCC soft-float / soft-divide runtime (SH-2 has no FPU or hw divider).
|
||||
# -lgcc is resolved by the compiler driver even with -nodefaultlibs.
|
||||
-lgcc
|
||||
-Wl,--end-group
|
||||
)
|
||||
|
||||
# Yaul linker script (Saturn SH-2 memory map) and startup objects.
|
||||
# Startup objects must precede all other objects so use target_link_options
|
||||
# (those flags appear before the object-file list in cmake's link command).
|
||||
target_link_options(${DUSK_BINARY_TARGET_NAME} PRIVATE
|
||||
"-T${_YAUL_SYSROOT}/lib/ldscripts/yaul.x"
|
||||
"${_YAUL_SYSROOT}/lib/crt0.o"
|
||||
"${_YAUL_SYSROOT}/lib/init.o"
|
||||
# Provide SH-2 stack top addresses required by crt0.o/cpu_dual_entries.o.
|
||||
# RAM region: 0x06004000 .. 0x06100000 (~1 MB).
|
||||
# Master stack grows down from the very top; slave CPU gets 4 KB below.
|
||||
"-Wl,--defsym=___master_stack=0x06100000"
|
||||
"-Wl,--defsym=___slave_stack=0x060FF000"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-build: ELF → binary image
|
||||
# sh2eb-elf-objcopy converts the ELF to a flat binary that IP.BIN loads
|
||||
# into Saturn Work RAM.
|
||||
# Post-build: ELF → binary → Saturn disc image (ISO + CUE)
|
||||
# ---------------------------------------------------------------------------
|
||||
set(DUSK_SAT_BIN "${CMAKE_BINARY_DIR}/Dusk.bin")
|
||||
set(DUSK_SAT_BIN "${CMAKE_BINARY_DIR}/Dusk.bin")
|
||||
set(DUSK_SAT_IP_BIN "${CMAKE_BINARY_DIR}/IP.BIN")
|
||||
set(DUSK_SAT_CD_DIR "${CMAKE_BINARY_DIR}/cd")
|
||||
set(DUSK_SAT_AUDIO_DIR "${CMAKE_BINARY_DIR}/audio-tracks")
|
||||
set(DUSK_SAT_ISO "${CMAKE_BINARY_DIR}/Dusk.iso")
|
||||
set(DUSK_SAT_CUE "${CMAKE_BINARY_DIR}/Dusk.cue")
|
||||
|
||||
# Step 1: ELF → flat binary (loaded into Work RAM by IP.BIN)
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND "${_YAUL_BIN}/sh2eb-elf-objcopy"
|
||||
-O binary
|
||||
"$<TARGET_FILE:${DUSK_BINARY_TARGET_NAME}>"
|
||||
"${DUSK_SAT_BIN}"
|
||||
COMMENT "Converting ${DUSK_BINARY_TARGET_NAME} ELF → ${DUSK_SAT_BIN}"
|
||||
COMMENT "Converting ELF → ${DUSK_SAT_BIN}"
|
||||
)
|
||||
|
||||
# Step 2: flat binary → IP.BIN (boot header with region security blobs)
|
||||
# make-ip writes IP.BIN into dirname(arg1), so it lands at ${CMAKE_BINARY_DIR}/IP.BIN.
|
||||
# Pass -1 for 1st-read-size so make-ip uses the actual file size.
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
"YAUL_INSTALL_ROOT=${YAUL_INSTALL_ROOT}"
|
||||
"YAUL_ARCH_SH_PREFIX=sh2eb-elf"
|
||||
"${_YAUL_BIN}/make-ip"
|
||||
"${DUSK_SAT_BIN}"
|
||||
"V1.000"
|
||||
"20260101"
|
||||
"JTUE"
|
||||
"J"
|
||||
"DUSK"
|
||||
"0x06100000"
|
||||
"0x060FF000"
|
||||
"0x06004000"
|
||||
"-1"
|
||||
COMMENT "Generating IP.BIN"
|
||||
)
|
||||
|
||||
# Step 3: build CD filesystem tree and create ISO 9660 image
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_SAT_CD_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_SAT_AUDIO_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${DUSK_SAT_BIN}" "${DUSK_SAT_CD_DIR}/A.BIN"
|
||||
# ISO 9660 requires these auxiliary text files
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${DUSK_SAT_CD_DIR}/ABS.TXT"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${DUSK_SAT_CD_DIR}/BIB.TXT"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${DUSK_SAT_CD_DIR}/CPY.TXT"
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
"YAUL_INSTALL_ROOT=${YAUL_INSTALL_ROOT}"
|
||||
"MAKE_ISO_XORRISOFS=/usr/bin/xorrisofs"
|
||||
"${_YAUL_BIN}/make-iso"
|
||||
"${DUSK_SAT_CD_DIR}"
|
||||
"${DUSK_SAT_IP_BIN}"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"Dusk"
|
||||
COMMENT "Generating ${DUSK_SAT_ISO}"
|
||||
)
|
||||
|
||||
# Step 4: ISO → CUE sheet (rename .iso → .bin for a classic BIN/CUE pair
|
||||
# if desired; emulators accept .iso+.cue as-is)
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
"YAUL_INSTALL_ROOT=${YAUL_INSTALL_ROOT}"
|
||||
"${_YAUL_BIN}/make-cue"
|
||||
"${DUSK_SAT_AUDIO_DIR}"
|
||||
"${DUSK_SAT_ISO}"
|
||||
COMMENT "Generating ${DUSK_SAT_CUE}"
|
||||
)
|
||||
|
||||
@@ -48,13 +48,16 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
# SH-2 core flags: big-endian (-mb), SH-2 ISA (-m2), no FPU
|
||||
set(_SAT_C_FLAGS "-m2 -mb -fno-builtin -fomit-frame-pointer")
|
||||
# SH-2 core flags: big-endian (-mb), SH-2 ISA (-m2), no FPU.
|
||||
# -ffunction-sections/-fdata-sections enable --gc-sections to eliminate
|
||||
# unreachable code at the function level (e.g. yyjson FILE* API paths).
|
||||
set(_SAT_C_FLAGS "-m2 -mb -fno-builtin -fomit-frame-pointer -ffunction-sections -fdata-sections -Os")
|
||||
set(CMAKE_C_FLAGS_INIT "${_SAT_C_FLAGS}")
|
||||
|
||||
# Yaul installs yaul.specs to ${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/ and
|
||||
# ldscripts/yaul.x to ${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/ldscripts/.
|
||||
# GCC searches ${prefix}/${target}/lib/ for specs, so -specs=yaul.specs works
|
||||
# without an absolute path once the SDK is installed.
|
||||
# Linker flags: gc-sections, no startup/default libs (Yaul provides crt0/init;
|
||||
# we supply all needed libraries explicitly). -nodefaultlibs prevents cmake/gcc
|
||||
# from injecting -lc (which does not exist in the Yaul sysroot).
|
||||
# --start-group/--end-group are placed in target_link_libraries in
|
||||
# targets/saturn.cmake so they actually wrap the archive list.
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT
|
||||
"-specs=yaul.specs -Wl,--gc-sections -nostartfiles -Wl,--start-group -Wl,--end-group")
|
||||
"-Wl,--gc-sections -nostartfiles -nodefaultlibs")
|
||||
|
||||
+159
-17
@@ -40,6 +40,7 @@ RUN apt-get update && apt-get install -y \
|
||||
libgmp-dev \
|
||||
libmpfr-dev \
|
||||
libmpc-dev \
|
||||
xorriso \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p "${YAUL_INSTALL_ROOT}"
|
||||
@@ -149,6 +150,7 @@ RUN for tool in "${YAUL_INSTALL_ROOT}/bin/m68k-elf-"*; do \
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN git clone --depth 1 --recurse-submodules \
|
||||
https://github.com/yaul-org/libyaul.git /tmp/yaul && \
|
||||
mkdir -p /tmp/yaul-build && \
|
||||
cd /tmp/yaul && \
|
||||
YAUL_INSTALL_ROOT="${YAUL_INSTALL_ROOT}" \
|
||||
YAUL_ARCH_SH_PREFIX=sh2eb-elf \
|
||||
@@ -161,11 +163,134 @@ RUN git clone --depth 1 --recurse-submodules \
|
||||
rm -rf /tmp/yaul /tmp/yaul-build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 11. Cross-compile zlib for sh2eb-elf via CMake
|
||||
# Using CMake (not ./configure) so we can set
|
||||
# CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY and skip link tests that
|
||||
# the bare-metal cross-compiler can't satisfy.
|
||||
# Install into ${YAUL_INSTALL_ROOT}/sh2eb-elf/ (Yaul sysroot layout).
|
||||
# 11. Provide a freestanding stdint.h in the Yaul sysroot.
|
||||
# GCC 12 --without-headers installs a stdint.h wrapper that emits
|
||||
# `#include_next <stdint.h>`, but Yaul's sysroot has no system stdint.h
|
||||
# to satisfy that lookup. Yaul's own errno.h (and anything it pulls in)
|
||||
# will fail to compile in any external cmake project without this stub.
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN test -f "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h" || \
|
||||
cp /opt/yaul/lib/gcc/sh2eb-elf/12.3.0/include/stdint-gcc.h \
|
||||
"${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h" 2>/dev/null || \
|
||||
printf '%s\n' \
|
||||
'#pragma once' \
|
||||
'#ifndef __STDINT_H' \
|
||||
'#define __STDINT_H' \
|
||||
'typedef signed char int8_t;' \
|
||||
'typedef signed short int16_t;' \
|
||||
'typedef signed int int32_t;' \
|
||||
'typedef signed long long int64_t;' \
|
||||
'typedef unsigned char uint8_t;' \
|
||||
'typedef unsigned short uint16_t;' \
|
||||
'typedef unsigned int uint32_t;' \
|
||||
'typedef unsigned long long uint64_t;' \
|
||||
'typedef int8_t int_least8_t;' \
|
||||
'typedef int16_t int_least16_t;' \
|
||||
'typedef int32_t int_least32_t;' \
|
||||
'typedef int64_t int_least64_t;' \
|
||||
'typedef uint8_t uint_least8_t;' \
|
||||
'typedef uint16_t uint_least16_t;' \
|
||||
'typedef uint32_t uint_least32_t;' \
|
||||
'typedef uint64_t uint_least64_t;' \
|
||||
'typedef int32_t int_fast8_t;' \
|
||||
'typedef int32_t int_fast16_t;' \
|
||||
'typedef int32_t int_fast32_t;' \
|
||||
'typedef int64_t int_fast64_t;' \
|
||||
'typedef uint32_t uint_fast8_t;' \
|
||||
'typedef uint32_t uint_fast16_t;' \
|
||||
'typedef uint32_t uint_fast32_t;' \
|
||||
'typedef uint64_t uint_fast64_t;' \
|
||||
'typedef int32_t intptr_t;' \
|
||||
'typedef uint32_t uintptr_t;' \
|
||||
'typedef int64_t intmax_t;' \
|
||||
'typedef uint64_t uintmax_t;' \
|
||||
'#define INT8_MIN (-128)' \
|
||||
'#define INT16_MIN (-32768)' \
|
||||
'#define INT32_MIN (-2147483647-1)' \
|
||||
'#define INT64_MIN (-9223372036854775807LL-1)' \
|
||||
'#define INT8_MAX (127)' \
|
||||
'#define INT16_MAX (32767)' \
|
||||
'#define INT32_MAX (2147483647)' \
|
||||
'#define INT64_MAX (9223372036854775807LL)' \
|
||||
'#define UINT8_MAX (255U)' \
|
||||
'#define UINT16_MAX (65535U)' \
|
||||
'#define UINT32_MAX (4294967295U)' \
|
||||
'#define UINT64_MAX (18446744073709551615ULL)' \
|
||||
'#define INTPTR_MIN INT32_MIN' \
|
||||
'#define INTPTR_MAX INT32_MAX' \
|
||||
'#define UINTPTR_MAX UINT32_MAX' \
|
||||
'#define INTMAX_MIN INT64_MIN' \
|
||||
'#define INTMAX_MAX INT64_MAX' \
|
||||
'#define UINTMAX_MAX UINT64_MAX' \
|
||||
'#define SIZE_MAX UINT32_MAX' \
|
||||
'#define INT8_C(c) (c)' \
|
||||
'#define INT16_C(c) (c)' \
|
||||
'#define INT32_C(c) (c)' \
|
||||
'#define INT64_C(c) (c ## LL)' \
|
||||
'#define UINT8_C(c) (c ## U)' \
|
||||
'#define UINT16_C(c) (c ## U)' \
|
||||
'#define UINT32_C(c) (c ## U)' \
|
||||
'#define UINT64_C(c) (c ## ULL)' \
|
||||
'#define INTMAX_C(c) (c ## LL)' \
|
||||
'#define UINTMAX_C(c)(c ## ULL)' \
|
||||
'#endif' \
|
||||
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 11b. Stubs for non-standard / bare-metal-missing headers.
|
||||
# Yaul's sysroot only ships a minimal libc; third-party libraries such as
|
||||
# libzip need these POSIX/GNU headers to compile.
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN printf '#pragma once\n#include <stdlib.h>\n' \
|
||||
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/malloc.h" && \
|
||||
printf '#pragma once\n#include <string.h>\n' \
|
||||
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/memory.h" && \
|
||||
printf '%s\n' \
|
||||
'#pragma once' \
|
||||
'#ifndef _TIME_H' \
|
||||
'#define _TIME_H' \
|
||||
'typedef long time_t;' \
|
||||
'typedef long clock_t;' \
|
||||
'#define CLOCKS_PER_SEC 1000L' \
|
||||
'struct tm {' \
|
||||
' int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year;' \
|
||||
' int tm_wday, tm_yday, tm_isdst;' \
|
||||
'};' \
|
||||
'static inline time_t time(time_t *t) { if(t) *t=0; return 0; }' \
|
||||
'static inline time_t mktime(struct tm *t) { (void)t; return 0; }' \
|
||||
'static inline struct tm *localtime(const time_t *t) { (void)t; return 0; }' \
|
||||
'static inline struct tm *gmtime(const time_t *t) { (void)t; return 0; }' \
|
||||
'#endif' \
|
||||
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/time.h" && \
|
||||
printf '%s\n' \
|
||||
'#pragma once' \
|
||||
'#ifndef _SYS_STAT_H' \
|
||||
'#define _SYS_STAT_H' \
|
||||
'#include <sys/types.h>' \
|
||||
'#include <time.h>' \
|
||||
'typedef unsigned int dev_t;' \
|
||||
'typedef unsigned int ino_t;' \
|
||||
'typedef unsigned short nlink_t;' \
|
||||
'typedef long blksize_t;' \
|
||||
'typedef long blkcnt_t;' \
|
||||
'struct stat {' \
|
||||
' dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink;' \
|
||||
' uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size;' \
|
||||
' blksize_t st_blksize; blkcnt_t st_blocks;' \
|
||||
' time_t st_atime, st_mtime, st_ctime;' \
|
||||
'};' \
|
||||
'static inline int stat(const char *p, struct stat *b){(void)p;(void)b;return -1;}' \
|
||||
'static inline int fstat(int f, struct stat *b){(void)f;(void)b;return -1;}' \
|
||||
'#define S_ISREG(m) (((m)&0170000)==0100000)' \
|
||||
'#define S_ISDIR(m) (((m)&0170000)==0040000)' \
|
||||
'#define S_IRUSR 0400' '#define S_IWUSR 0200' '#define S_IXUSR 0100' \
|
||||
'#endif' \
|
||||
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/sys/stat.h"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 12. sat-xc.cmake — shared CMake toolchain for zlib and libzip.
|
||||
# CMAKE_SYSROOT tells cmake to pass --sysroot to the compiler so that
|
||||
# GCC's #include_next wrappers resolve against the Yaul sysroot headers.
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN printf '%s\n' \
|
||||
'set(CMAKE_SYSTEM_NAME Generic)' \
|
||||
@@ -173,27 +298,40 @@ RUN printf '%s\n' \
|
||||
"set(CMAKE_C_COMPILER \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-gcc\")" \
|
||||
"set(CMAKE_AR \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ar\")" \
|
||||
"set(CMAKE_RANLIB \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ranlib\")" \
|
||||
"set(CMAKE_SYSROOT \"${YAUL_INSTALL_ROOT}/sh2eb-elf\")" \
|
||||
'set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)' \
|
||||
'set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)' \
|
||||
'set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)' \
|
||||
'set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)' \
|
||||
> /tmp/sat-xc.cmake
|
||||
|
||||
RUN wget -q https://zlib.net/zlib-1.3.1.tar.gz -O /tmp/zlib.tar.gz && \
|
||||
# ---------------------------------------------------------------------------
|
||||
# 13. Cross-compile zlib for sh2eb-elf (inflate/deflate core only).
|
||||
# We compile the nine core source files directly rather than via cmake
|
||||
# to avoid the gz* files whose POSIX gzip-streaming API is both unused
|
||||
# (libzip only needs inflate/deflate) and cannot compile on bare metal
|
||||
# (gzguts.h -> errno.h -> stdint.h chain, and zutil.h includes gzguts.h
|
||||
# unless NO_GZIP is defined).
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN wget -q https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz -O /tmp/zlib.tar.gz && \
|
||||
tar xf /tmp/zlib.tar.gz -C /tmp && \
|
||||
cmake -S /tmp/zlib-1.3.1 -B /tmp/zlib-build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/tmp/sat-xc.cmake \
|
||||
-DCMAKE_INSTALL_PREFIX="${YAUL_INSTALL_ROOT}/sh2eb-elf" \
|
||||
-DCMAKE_C_FLAGS="-m2 -mb -fno-builtin -O2" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
&& cmake --build /tmp/zlib-build -- -j"$(nproc)" \
|
||||
&& cmake --install /tmp/zlib-build \
|
||||
&& rm -rf /tmp/zlib-1.3.1 /tmp/zlib.tar.gz /tmp/zlib-build
|
||||
cd /tmp/zlib-1.3.1 && \
|
||||
for f in adler32.c compress.c crc32.c deflate.c infback.c \
|
||||
inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c; do \
|
||||
"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-gcc" \
|
||||
-m2 -mb -fno-builtin -O2 -DNO_GZIP -DSTDC -I. -c "$f" -o "${f%.c}.o"; \
|
||||
done && \
|
||||
"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ar" rcs \
|
||||
"${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/libz.a" \
|
||||
adler32.o compress.o crc32.o deflate.o infback.o inffast.o \
|
||||
inflate.o inftrees.o trees.o uncompr.o zutil.o && \
|
||||
install -m644 zlib.h zconf.h "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/" && \
|
||||
rm -rf /tmp/zlib-1.3.1 /tmp/zlib.tar.gz
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 12. Cross-compile libzip for sh2eb-elf (reuses /tmp/sat-xc.cmake above)
|
||||
# 14. Cross-compile libzip for sh2eb-elf (reuses /tmp/sat-xc.cmake above)
|
||||
# ---------------------------------------------------------------------------
|
||||
RUN wget -q https://libzip.org/download/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.gz && \
|
||||
RUN wget -q https://github.com/nih-at/libzip/releases/download/v1.10.1/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.gz && \
|
||||
tar xf /tmp/libzip.tar.gz -C /tmp && \
|
||||
cmake -S /tmp/libzip-1.10.1 -B /tmp/libzip-build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/tmp/sat-xc.cmake \
|
||||
@@ -212,9 +350,13 @@ RUN wget -q https://libzip.org/download/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.
|
||||
-DBUILD_DOCUMENTATION=OFF \
|
||||
-DBUILD_REGRESS=OFF \
|
||||
-DBUILD_TOOLS=OFF \
|
||||
-DHAVE_CLONEFILE=0 \
|
||||
-DHAVE_ARC4RANDOM=0 \
|
||||
-DHAVE_GETPROGNAME=0 \
|
||||
&& cmake --build /tmp/libzip-build -- -j"$(nproc)" \
|
||||
&& cmake --install /tmp/libzip-build \
|
||||
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip.tar.gz /tmp/libzip-build /tmp/sat-xc.cmake /tmp/zlib.tar.gz 2>/dev/null || true
|
||||
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip-build /tmp/sat-xc.cmake \
|
||||
; rm -f /tmp/libzip.tar.gz /tmp/zlib.tar.gz 2>/dev/null ; true
|
||||
|
||||
WORKDIR /workdir
|
||||
VOLUME ["/workdir"]
|
||||
|
||||
@@ -11,6 +11,8 @@ if [ -z "$YAUL_INSTALL_ROOT" ]; then
|
||||
fi
|
||||
|
||||
mkdir -p build-saturn
|
||||
# Remove stale cache so toolchain flag changes (CMAKE_*_FLAGS_INIT) take effect.
|
||||
rm -f build-saturn/CMakeCache.txt
|
||||
cmake -S . -B build-saturn \
|
||||
-DDUSK_TARGET_SYSTEM=saturn \
|
||||
-DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/saturn.cmake" \
|
||||
|
||||
+1
-1
@@ -23,6 +23,6 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecub
|
||||
add_subdirectory(duskdolphin)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "saturn")
|
||||
add_subdirectory(dusksat)
|
||||
add_subdirectory(dusksaturn)
|
||||
|
||||
endif()
|
||||
@@ -29,4 +29,5 @@
|
||||
typedef bool bool_t;
|
||||
typedef int int_t;
|
||||
typedef float float_t;
|
||||
typedef double double_t;
|
||||
typedef char char_t;
|
||||
+24
-24
@@ -20,8 +20,8 @@ errorret_t inputInit(void) {
|
||||
|
||||
for(uint8_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
INPUT.actions[i].action = (inputaction_t)i;
|
||||
INPUT.actions[i].lastValue = 0.0f;
|
||||
INPUT.actions[i].currentValue = 0.0f;
|
||||
INPUT.actions[i].lastValue = 0;
|
||||
INPUT.actions[i].currentValue = 0;
|
||||
|
||||
eventInit(
|
||||
&INPUT.actions[i].onPressed,
|
||||
@@ -55,14 +55,14 @@ void inputUpdate(void) {
|
||||
do {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
action->lastDynamicValue = action->currentDynamicValue;
|
||||
action->currentDynamicValue = 0.0f;
|
||||
action->currentDynamicValue = 0;
|
||||
if(!TIME.dynamicUpdate) {
|
||||
action->lastValue = action->currentValue;
|
||||
action->currentValue = 0.0f;
|
||||
action->currentValue = 0;
|
||||
}
|
||||
#else
|
||||
action->lastValue = action->currentValue;
|
||||
action->currentValue = 0.0f;
|
||||
action->currentValue = 0;
|
||||
#endif
|
||||
|
||||
action++;
|
||||
@@ -74,7 +74,7 @@ void inputUpdate(void) {
|
||||
cur->lastVal = cur->curVal;
|
||||
cur->curVal = inputButtonGetValue(cur->button);
|
||||
|
||||
if(cur->curVal == 0.0f) {
|
||||
if(cur->curVal == 0) {
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
@@ -104,14 +104,14 @@ void inputUpdate(void) {
|
||||
|
||||
for(uint8_t i = INPUT_ACTION_NULL + 1; i < INPUT_ACTION_COUNT; i++) {
|
||||
inputactiondata_t *act = &INPUT.actions[i];
|
||||
bool_t isDown = act->currentValue > 0.0f;
|
||||
bool_t wasDown = act->lastValue > 0.0f;
|
||||
bool_t isDown = act->currentValue > 0;
|
||||
bool_t wasDown = act->lastValue > 0;
|
||||
if(isDown && !wasDown) eventInvoke(&act->onPressed, act);
|
||||
if(!isDown && wasDown) eventInvoke(&act->onReleased, act);
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
int16_t inputGetCurrentValue(const inputaction_t action) {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return inputGetCurrentValueDynamic(action);
|
||||
#endif
|
||||
@@ -120,33 +120,33 @@ float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
return INPUT.actions[action].currentValue;
|
||||
}
|
||||
|
||||
float_t inputGetLastValue(const inputaction_t action) {
|
||||
int16_t inputGetLastValue(const inputaction_t action) {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return inputGetLastValueDynamic(action);
|
||||
#endif
|
||||
|
||||
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].lastValue;
|
||||
}
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
float_t inputGetCurrentValueDynamic(const inputaction_t action) {
|
||||
int16_t inputGetCurrentValueDynamic(const inputaction_t action) {
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].currentDynamicValue;
|
||||
}
|
||||
|
||||
float_t inputGetLastValueDynamic(const inputaction_t action) {
|
||||
int16_t inputGetLastValueDynamic(const inputaction_t action) {
|
||||
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
|
||||
return INPUT.actions[action].lastDynamicValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool_t inputIsDown(const inputaction_t action) {
|
||||
return inputGetCurrentValue(action) > 0.0f;
|
||||
return inputGetCurrentValue(action) > 0;
|
||||
}
|
||||
|
||||
bool_t inputWasDown(const inputaction_t action) {
|
||||
return inputGetLastValue(action) > 0.0f;
|
||||
return inputGetLastValue(action) > 0;
|
||||
}
|
||||
|
||||
bool_t inputPressed(const inputaction_t action) {
|
||||
@@ -157,11 +157,11 @@ bool_t inputReleased(const inputaction_t action) {
|
||||
return !inputIsDown(action) && inputWasDown(action);
|
||||
}
|
||||
|
||||
float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||
int16_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||
assertTrue(neg < INPUT_ACTION_COUNT, "Negative input action out of bounds");
|
||||
assertTrue(pos < INPUT_ACTION_COUNT, "Positive input action out of bounds");
|
||||
|
||||
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
|
||||
return (int16_t)(inputGetCurrentValue(pos) - inputGetCurrentValue(neg));
|
||||
}
|
||||
|
||||
void inputAxis2D(
|
||||
@@ -170,8 +170,8 @@ void inputAxis2D(
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
result[0] = inputAxis(negX, posX);
|
||||
result[1] = inputAxis(negY, posY);
|
||||
result[0] = (float_t)inputAxis(negX, posX) / (float_t)INT16_MAX;
|
||||
result[1] = (float_t)inputAxis(negY, posY) / (float_t)INT16_MAX;
|
||||
}
|
||||
|
||||
void inputAngle2D(
|
||||
@@ -180,8 +180,8 @@ void inputAngle2D(
|
||||
vec2 result
|
||||
) {
|
||||
assertNotNull(result, "Result vector cannot be null");
|
||||
float_t x = inputAxis(negX, posX);
|
||||
float_t y = inputAxis(negY, posY);
|
||||
float_t x = (float_t)inputAxis(negX, posX) / (float_t)INT16_MAX;
|
||||
float_t y = (float_t)inputAxis(negY, posY) / (float_t)INT16_MAX;
|
||||
float_t mag = sqrtf(x * x + y * y);
|
||||
if(mag <= 0.0f) {
|
||||
result[0] = 0.0f;
|
||||
@@ -211,7 +211,7 @@ void inputBind(const inputbutton_t button, const inputaction_t act) {
|
||||
data->action = act;
|
||||
}
|
||||
|
||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
|
||||
if(rawValue < deadzone) return 0.0f;
|
||||
return (rawValue - deadzone) / (1.0f - deadzone);
|
||||
int16_t inputDeadzone(const int16_t rawValue, const int16_t deadzone) {
|
||||
if(rawValue < deadzone) return 0;
|
||||
return (int16_t)(((int32_t)(rawValue - deadzone) * INT16_MAX) / (INT16_MAX - deadzone));
|
||||
}
|
||||
+23
-23
@@ -35,36 +35,36 @@ void inputUpdate(void);
|
||||
|
||||
/**
|
||||
* Gets the current value of a specific input action.
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The current value of the action (0.0f to 1.0f).
|
||||
* @return The current value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetCurrentValue(const inputaction_t action);
|
||||
int16_t inputGetCurrentValue(const inputaction_t action);
|
||||
|
||||
/**
|
||||
* Gets the last value of a specific input action.
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The last value of the action (0.0f to 1.0f).
|
||||
* @return The last value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetLastValue(const inputaction_t action);
|
||||
int16_t inputGetLastValue(const inputaction_t action);
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
/**
|
||||
* Gets the current value of a specific input action (dynamic timestep).
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The current value of the action (0.0f to 1.0f).
|
||||
* @return The current value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetCurrentValueDynamic(const inputaction_t action);
|
||||
int16_t inputGetCurrentValueDynamic(const inputaction_t action);
|
||||
|
||||
/**
|
||||
* Gets the last value of a specific input action (dynamic timestep).
|
||||
*
|
||||
*
|
||||
* @param action The input action to get the value for.
|
||||
* @return The last value of the action (0.0f to 1.0f).
|
||||
* @return The last value of the action (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetLastValueDynamic(const inputaction_t action);
|
||||
int16_t inputGetLastValueDynamic(const inputaction_t action);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -103,12 +103,12 @@ bool_t inputReleased(const inputaction_t action);
|
||||
/**
|
||||
* Gets the value of an input axis, defined by two actions (negative and
|
||||
* positive).
|
||||
*
|
||||
*
|
||||
* @param neg The action representing the negative direction of the axis.
|
||||
* @param pos The action representing the positive direction of the axis.
|
||||
* @return The current value of the axis (-1.0f to 1.0f).
|
||||
* @return The current value of the axis (-INT16_MAX to INT16_MAX).
|
||||
*/
|
||||
float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||
int16_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||
|
||||
/**
|
||||
* Gets the values of a 2D input axis, defined by two pairs of actions (negative
|
||||
@@ -151,12 +151,12 @@ void inputAngle2D(
|
||||
void inputBind(const inputbutton_t button, const inputaction_t act);
|
||||
|
||||
/**
|
||||
* Applies deadzone formula to a raw input value. This will;
|
||||
* return 0 if input is less than the deadzone.
|
||||
* re-map the range between 0 and 1 in deadzone-space.
|
||||
*
|
||||
* @param rawValue The raw input value to apply the deadzone to.
|
||||
* @param deadzone The deadzone threshold (0.0f to 1.0f).
|
||||
* @return The input value after applying the deadzone.
|
||||
* Applies deadzone formula to a raw input value. Returns 0 if the value is
|
||||
* below the deadzone threshold, otherwise remaps to the full 0..INT16_MAX
|
||||
* range.
|
||||
*
|
||||
* @param rawValue The raw input value (0 to INT16_MAX).
|
||||
* @param deadzone The deadzone threshold (0 to INT16_MAX).
|
||||
* @return The input value after applying the deadzone (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone);
|
||||
int16_t inputDeadzone(const int16_t rawValue, const int16_t deadzone);
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
typedef struct {
|
||||
inputaction_t action;
|
||||
float_t lastValue;
|
||||
float_t currentValue;
|
||||
int16_t lastValue;
|
||||
int16_t currentValue;
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
float_t lastDynamicValue;
|
||||
float_t currentDynamicValue;
|
||||
int16_t lastDynamicValue;
|
||||
int16_t currentDynamicValue;
|
||||
#endif
|
||||
|
||||
eventcallback_t onPressedCallbacks[INPUT_ACTION_CALLBACK_COUNT_MAX];
|
||||
|
||||
@@ -25,7 +25,7 @@ inputbutton_t inputButtonGetByName(const char_t *name) {
|
||||
return (inputbutton_t){ .type = INPUT_BUTTON_TYPE_NONE };
|
||||
}
|
||||
|
||||
float_t inputButtonGetValue(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValue(const inputbutton_t button) {
|
||||
return inputButtonGetValuePlatform(button);
|
||||
}
|
||||
inputbuttondata_t * inputButtonGetData(const inputbutton_t button) {
|
||||
|
||||
@@ -75,8 +75,8 @@ typedef struct inputbutton_s {
|
||||
typedef struct {
|
||||
const char_t *name;
|
||||
inputbutton_t button;
|
||||
float_t curVal;
|
||||
float_t lastVal;
|
||||
int16_t curVal;
|
||||
int16_t lastVal;
|
||||
inputaction_t action;
|
||||
} inputbuttondata_t;
|
||||
|
||||
@@ -92,11 +92,11 @@ inputbutton_t inputButtonGetByName(const char_t *name);
|
||||
|
||||
/**
|
||||
* Gets the current value of an input button.
|
||||
*
|
||||
*
|
||||
* @param button The input button.
|
||||
* @return The current value of the input button (0.0f to 1.0f).
|
||||
* @return The current value of the input button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValue(const inputbutton_t button);
|
||||
int16_t inputButtonGetValue(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Gets the button data for a specific input button.
|
||||
|
||||
@@ -44,7 +44,7 @@ void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
|
||||
break;
|
||||
|
||||
case CUTSCENE_ITEM_TYPE_WAIT:
|
||||
data->wait = fixedSub(data->wait, TIME.delta);
|
||||
data->wait -= TIME.delta;
|
||||
if(data->wait <= 0) cutsceneSystemNext();
|
||||
break;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
typedef fixed_t cutscenewait_t;
|
||||
typedef fixed_t cutscenewaitdata_t;
|
||||
typedef float_t cutscenewait_t;
|
||||
typedef float_t cutscenewaitdata_t;
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = {
|
||||
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(2.0f) },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } },
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ static const cutscene_t TEST_CUTSCENE_ONE = {
|
||||
};
|
||||
|
||||
static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(1.0f) },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f },
|
||||
{ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE },
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct entity_s {
|
||||
fixed_t lastPosition[3];
|
||||
|
||||
entityanim_t animation;
|
||||
fixed_t animTime;
|
||||
float_t animTime;
|
||||
|
||||
entityinteract_t interact;
|
||||
} entity_t;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
void entityAnimUpdate(entity_t *entity) {
|
||||
if(entity->animation == ENTITY_ANIM_IDLE) return;
|
||||
|
||||
entity->animTime = fixedSub(entity->animTime, TIME.delta);
|
||||
entity->animTime -= TIME.delta;
|
||||
if(entity->animTime <= 0) {
|
||||
entity->animation = ENTITY_ANIM_IDLE;
|
||||
entity->animTime = 0;
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#define ENTITY_ANIM_TURN_DURATION FIXED(0.06f)
|
||||
#define ENTITY_ANIM_WALK_DURATION FIXED(0.1f)
|
||||
#define ENTITY_ANIM_TURN_DURATION 0.06f
|
||||
#define ENTITY_ANIM_WALK_DURATION 0.1f
|
||||
|
||||
typedef struct entity_s entity_t;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "display/screen.h"
|
||||
#include "display/color.h"
|
||||
#include "time/time.h"
|
||||
#include "util/fixed.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
static const uint8_t initIndices[3 * 3] = {
|
||||
@@ -64,7 +63,7 @@ static void renderTest2DZOrder(void) {
|
||||
}
|
||||
|
||||
static void renderTest3DQuad(void) {
|
||||
float_t angle = fixedToFloat(TIME.time);
|
||||
float_t angle = TIME.time;
|
||||
renderQuad3D(
|
||||
-45, 0, 0,
|
||||
(int16_t)(cosf(angle) * 30.0f), 0, (int16_t)(sinf(angle) * 30.0f),
|
||||
@@ -89,7 +88,7 @@ static void renderTest3DOverlap(void) {
|
||||
}
|
||||
|
||||
static void renderTestTilemap(void) {
|
||||
float_t t = fixedToFloat(TIME.time);
|
||||
float_t t = TIME.time;
|
||||
float_t scrollPx = fmodf(t * TILEMAP_SCROLL_SPEED, TILEMAP_SCROLL_RANGE);
|
||||
int16_t x = -(int16_t)(int)scrollPx;
|
||||
renderTilemapChunk(x, TILEMAP_Y, 8000, tilemapChunk);
|
||||
@@ -129,7 +128,7 @@ errorret_t sceneFullInit(scenedata_t *data) {
|
||||
}
|
||||
|
||||
errorret_t sceneFullUpdate(scenedata_t *data) {
|
||||
float_t t = fixedToFloat(TIME.time);
|
||||
float_t t = TIME.time;
|
||||
|
||||
renderTextureGetPalette(testTex)[8] = hueColor(t * 2.0f);
|
||||
renderTextureGetIndices(testTex)[1 * 3 + 1] = (uint8_t)((uint32_t)t % 9u);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "display/screen.h"
|
||||
#include "display/color.h"
|
||||
#include "time/time.h"
|
||||
#include "util/fixed.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static const uint8_t BOX_INDICES[3 * 3] = {
|
||||
@@ -59,13 +58,13 @@ errorret_t sceneSpinningBoxRender(scenedata_t *data) {
|
||||
renderClear(color(16, 16, 24, 255));
|
||||
|
||||
renderSetProjection(FOV_Y, ASPECT, FIXED(10), FIXED(10000));
|
||||
renderSetView(0, 0, 150, 0, 0, 0);
|
||||
renderSetView(0, 0, 270, 0, 0, 0);
|
||||
|
||||
float_t angle = fixedToFloat(TIME.time);
|
||||
float_t angle = TIME.time;
|
||||
renderQuad3D(
|
||||
0, 0, 0,
|
||||
(int16_t)(cosf(angle) * 40.0f), 0, (int16_t)(sinf(angle) * 40.0f),
|
||||
0, 40, 0,
|
||||
(int16_t)(cosf(angle) * 72.0f), 0, (int16_t)(sinf(angle) * 72.0f),
|
||||
0, 72, 0,
|
||||
0, spinTex, COLOR_WHITE
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "time.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
dusktime_t TIME;
|
||||
@@ -25,6 +24,8 @@ void timeInit(void) {
|
||||
TIME.dynamicDelta = 0.0f;
|
||||
TIME.dynamicUpdate = false;
|
||||
TIME.lastNonDynamic = 0.0f;
|
||||
// Prime the tick so the first-frame delta isn't the full SDL uptime.
|
||||
timeTickPlatform();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -37,15 +38,15 @@ void timeUpdate(void) {
|
||||
|
||||
assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative");
|
||||
|
||||
if((TIME.dynamicTime - TIME.lastNonDynamic) >= DUSK_TIME_STEP_F) {
|
||||
if((TIME.dynamicTime - TIME.lastNonDynamic) >= DUSK_TIME_STEP) {
|
||||
TIME.dynamicUpdate = false;
|
||||
TIME.lastNonDynamic = TIME.dynamicTime;
|
||||
TIME.delta = DUSK_TIME_STEP;
|
||||
TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
|
||||
TIME.time += DUSK_TIME_STEP;
|
||||
}
|
||||
#else
|
||||
TIME.delta = DUSK_TIME_STEP;
|
||||
TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
|
||||
TIME.time += DUSK_TIME_STEP;
|
||||
#endif
|
||||
|
||||
// Print time in UTC style string
|
||||
|
||||
+3
-11
@@ -8,16 +8,9 @@
|
||||
#pragma once
|
||||
#include "timeepoch.h"
|
||||
#include "time/timeplatform.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#ifndef DUSK_TIME_STEP
|
||||
#define DUSK_TIME_STEP FIXED(1.0f / 60.0f)
|
||||
#endif
|
||||
|
||||
/* Float-space step used by the dynamic accumulator to avoid fixed-point
|
||||
* truncation error in the trigger comparison. */
|
||||
#ifndef DUSK_TIME_STEP_F
|
||||
#define DUSK_TIME_STEP_F (1.0f / 60.0f)
|
||||
#define DUSK_TIME_STEP (1.0f / 60.0f)
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
@@ -30,11 +23,10 @@
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
fixed_t delta;
|
||||
fixed_t time;
|
||||
float_t delta;
|
||||
float_t time;
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
/* Float accumulator avoids fixed-point truncation drift. */
|
||||
float_t lastNonDynamic;
|
||||
bool_t dynamicUpdate;
|
||||
float_t dynamicDelta;
|
||||
|
||||
@@ -27,11 +27,11 @@ void uiFullboxInit(uifullbox_t *fullbox) {
|
||||
);
|
||||
}
|
||||
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta) {
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
if(fullbox->duration <= 0 || fullbox->time >= fullbox->duration) return;
|
||||
|
||||
fullbox->time = fixedAdd(fullbox->time, delta);
|
||||
fullbox->time += delta;
|
||||
if(fullbox->time >= fullbox->duration) {
|
||||
fullbox->time = fullbox->duration;
|
||||
eventInvoke(&fullbox->onTransitionEnd, fullbox);
|
||||
@@ -44,7 +44,7 @@ static color_t uiFullboxGetColor(const uifullbox_t *fullbox) {
|
||||
}
|
||||
fixed_t t = easingApply(
|
||||
fullbox->easing,
|
||||
fixedDiv(fullbox->time, fullbox->duration)
|
||||
fixedFromFloat(fullbox->time / fullbox->duration)
|
||||
);
|
||||
return color4b(
|
||||
fixedToU8(fixedLerp(
|
||||
@@ -92,7 +92,7 @@ void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
fixed_t duration,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
#include "display/color.h"
|
||||
#include "animation/easing.h"
|
||||
#include "event/event.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
typedef struct {
|
||||
color_t fromColor;
|
||||
color_t toColor;
|
||||
fixed_t duration;
|
||||
fixed_t time;
|
||||
float_t duration;
|
||||
float_t time;
|
||||
easingtype_t easing;
|
||||
eventcallback_t onTransitionEndCallbacks[4];
|
||||
void *onTransitionEndUsers[4];
|
||||
@@ -40,7 +39,7 @@ void uiFullboxInit(uifullbox_t *fullbox);
|
||||
* @param fullbox The fullbox to update.
|
||||
* @param delta Seconds elapsed since last update.
|
||||
*/
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta);
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta);
|
||||
|
||||
/**
|
||||
* Renders the fullbox. Skipped entirely when the current alpha is zero.
|
||||
@@ -63,7 +62,7 @@ void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
fixed_t duration,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "uiloading.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/fixed.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/screen/screen.h"
|
||||
@@ -28,10 +27,10 @@ void uiLoadingInit(void) {
|
||||
);
|
||||
}
|
||||
|
||||
void uiLoadingUpdate(fixed_t delta) {
|
||||
void uiLoadingUpdate(float_t delta) {
|
||||
if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration)
|
||||
return;
|
||||
UI_LOADING.time = fixedAdd(UI_LOADING.time, delta);
|
||||
UI_LOADING.time += delta;
|
||||
if(UI_LOADING.time >= UI_LOADING.duration) {
|
||||
UI_LOADING.time = UI_LOADING.duration;
|
||||
eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING);
|
||||
@@ -43,7 +42,7 @@ errorret_t uiLoadingDraw(void) {
|
||||
if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration) {
|
||||
alpha = UI_LOADING.toAlpha;
|
||||
} else {
|
||||
float_t t = fixedToFloat(fixedDiv(UI_LOADING.time, UI_LOADING.duration));
|
||||
float_t t = UI_LOADING.time / UI_LOADING.duration;
|
||||
alpha = UI_LOADING.fromAlpha +
|
||||
(UI_LOADING.toAlpha - UI_LOADING.fromAlpha) * t;
|
||||
}
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "event/event.h"
|
||||
#include "util/fixed.h"
|
||||
|
||||
#define UI_LOADING_FADE_DURATION FIXED(0.5f)
|
||||
#define UI_LOADING_FADE_DURATION 0.5f
|
||||
#define UI_LOADING_MARGIN 8.0f
|
||||
|
||||
typedef struct {
|
||||
float_t fromAlpha;
|
||||
float_t toAlpha;
|
||||
fixed_t duration;
|
||||
fixed_t time;
|
||||
float_t duration;
|
||||
float_t time;
|
||||
eventcallback_t onTransitionEndCallbacks[4];
|
||||
void *onTransitionEndUsers[4];
|
||||
event_t onTransitionEnd;
|
||||
@@ -36,7 +35,7 @@ void uiLoadingInit(void);
|
||||
*
|
||||
* @param delta Seconds elapsed since last update.
|
||||
*/
|
||||
void uiLoadingUpdate(fixed_t delta);
|
||||
void uiLoadingUpdate(float_t delta);
|
||||
|
||||
/**
|
||||
* Draws the loading indicator. No-op when fully transparent.
|
||||
|
||||
@@ -29,7 +29,9 @@ void * memoryAllocate(const size_t size) {
|
||||
void * memoryAlign(size_t alignment, size_t size) {
|
||||
assertTrue(alignment > 0, "Alignment must be greater than 0.");
|
||||
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
|
||||
void *ptr = memalign(alignment, size);
|
||||
// aligned_alloc (C11, <stdlib.h>) requires size to be a multiple of alignment
|
||||
size_t alignedSize = (size + alignment - 1) & ~(alignment - 1);
|
||||
void *ptr = aligned_alloc(alignment, alignedSize);
|
||||
assertNotNull(ptr, "Aligned memory allocation failed.");
|
||||
memoryTrack(ptr);
|
||||
return ptr;
|
||||
|
||||
@@ -160,30 +160,29 @@ void inputUpdateDolphin(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
float_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD: {
|
||||
if(INPUT.platform.padState[0] & button.gpButton) return 1.0f;
|
||||
return 0.0f;
|
||||
return (INPUT.platform.padState[0] & button.gpButton) ? INT16_MAX : 0;
|
||||
}
|
||||
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: {
|
||||
float_t axis = INPUT.platform.pads[0][button.gpAxis.axis];
|
||||
float_t value = axis / 128.0f;
|
||||
if(!button.gpAxis.positive) value = -value;
|
||||
value = inputDeadzone(value, inputGetDeadzoneDolphin(button));
|
||||
return value;
|
||||
float_t f = INPUT.platform.pads[0][button.gpAxis.axis];
|
||||
if(!button.gpAxis.positive) f = -f;
|
||||
if(f < 0.0f) return 0;
|
||||
int16_t value = (int16_t)(f * INT16_MAX);
|
||||
return inputDeadzone(value, inputGetDeadzoneDolphin(button));
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
int16_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return (int16_t)(0.2f * INT16_MAX);
|
||||
}
|
||||
@@ -58,17 +58,17 @@ errorret_t inputInitDolphin(void);
|
||||
void inputUpdateDolphin(void);
|
||||
|
||||
/**
|
||||
* Returns the input value (between 0 and 1) of the given button.
|
||||
*
|
||||
* Returns the input value of the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the value of.
|
||||
* @return The value of the button, between 0 and 1.
|
||||
* @return The value of the button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValueDolphin(const inputbutton_t button);
|
||||
int16_t inputButtonGetValueDolphin(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Returns the deadzone for the given button.
|
||||
*
|
||||
* Returns the deadzone for the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the deadzone of.
|
||||
* @return The deadzone for the button, between 0 and 1.
|
||||
* @return The deadzone threshold (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneDolphin(const inputbutton_t button);
|
||||
@@ -544,6 +544,6 @@ errorret_t inputInitLinux(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.17f * INT16_MAX);
|
||||
}
|
||||
@@ -92,6 +92,6 @@ errorret_t inputInitPSP(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.2f * INT16_MAX);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/displaysat.h"
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/display.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include <vdp1/cmdt.h>
|
||||
#include <vdp2/cram.h>
|
||||
#include <vdp2/tvmd.h>
|
||||
#include <vdp2/scrn.h>
|
||||
#include <vdp2/vram.h>
|
||||
|
||||
errorret_t displaySaturnInit(void) {
|
||||
logDebug("[Saturn] displaySaturnInit: start\n");
|
||||
DISPLAY.whichBuffer = 0;
|
||||
|
||||
/*
|
||||
* TV mode: NTSC, 320×224, non-interlaced.
|
||||
* Yaul's vdp2_tvmd_display_res_set() configures the sync standard and
|
||||
* horizontal/vertical resolution.
|
||||
*
|
||||
* TODO: replace with the Yaul typed call when integrating the full SDK:
|
||||
* vdp2_tvmd_display_res_set(VDP2_TVMD_INTERLACE_NONE,
|
||||
* VDP2_TVMD_HORZ_NORMAL_A,
|
||||
* VDP2_TVMD_VERT_224);
|
||||
* vdp2_tvmd_display_set();
|
||||
*/
|
||||
|
||||
/*
|
||||
* VDP2 scroll planes: disable all NBG/RBG planes for now; game content is
|
||||
* drawn entirely via VDP1 sprites. Tilemap chunks will re-enable NBG0
|
||||
* when the VDP2 tilemap backend is implemented.
|
||||
*
|
||||
* TODO:
|
||||
* vdp2_scrn_display_set(VDP2_SCRN_DISP_NBG0, false);
|
||||
* vdp2_scrn_display_set(VDP2_SCRN_DISP_NBG1, false);
|
||||
* ...
|
||||
*/
|
||||
|
||||
/*
|
||||
* VDP1 initialisation: the hardware starts drawing from VRAM offset 0.
|
||||
* We place our command table there and texture data afterward.
|
||||
*
|
||||
* TODO:
|
||||
* vdp1_vram_partitions_set(
|
||||
* VDP1_VRAM_CYCP_..., // cycle patterns
|
||||
* ...
|
||||
* );
|
||||
*/
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: calling renderSaturnInit\n");
|
||||
errorChain(renderSaturnInit());
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: done\n");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf) {
|
||||
assertNotNull(buf, "Saturn flush: null ropbuffer");
|
||||
errorChain(renderSaturnFlush(buf));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnSwap(void) {
|
||||
logDebug("[Saturn] displaySaturnSwap\n");
|
||||
/*
|
||||
* Wait for VDP1 to finish rendering the current frame then swap buffers.
|
||||
*
|
||||
* TODO:
|
||||
* vdp1_sync_render();
|
||||
* vdp1_sync();
|
||||
* vdp2_sync();
|
||||
* vdp2_sync_wait();
|
||||
*/
|
||||
DISPLAY.whichBuffer ^= 1;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void displaySaturnDispose(void) {
|
||||
logDebug("[Saturn] displaySaturnDispose\n");
|
||||
renderSaturnDispose();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "input/inputsat.h"
|
||||
|
||||
#define inputInitPlatform inputInitSaturn
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons (smpc_peripheral_digital_t).
|
||||
* Yaul exposes them via smpc_peripheral_digital_port() and
|
||||
* smpc_peripheral_digital_get().
|
||||
*
|
||||
* Button bitmask in the SMPC peripheral data word:
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*
|
||||
* We use Yaul's SMPC_PERIPHERAL_DIGITAL_* macros where available.
|
||||
*/
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } },
|
||||
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } },
|
||||
{ .name = "c", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 5 } },
|
||||
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 2 } },
|
||||
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 1 } },
|
||||
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 0 } },
|
||||
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 7 } },
|
||||
{ .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 8 } },
|
||||
{ .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 9 } },
|
||||
{ .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 10 } },
|
||||
{ .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 11 } },
|
||||
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 15 } },
|
||||
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 3 } },
|
||||
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } }, /* A */
|
||||
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } }, /* B */
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
errorret_t inputInitSaturn(void) {
|
||||
#define X(buttonName, buttonAction) \
|
||||
inputBind(inputButtonGetByName(buttonName), buttonAction);
|
||||
X("up", INPUT_ACTION_UP);
|
||||
X("down", INPUT_ACTION_DOWN);
|
||||
X("left", INPUT_ACTION_LEFT);
|
||||
X("right", INPUT_ACTION_RIGHT);
|
||||
X("accept", INPUT_ACTION_ACCEPT);
|
||||
X("cancel", INPUT_ACTION_CANCEL);
|
||||
X("start", INPUT_ACTION_RAGEQUIT);
|
||||
#undef X
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
|
||||
errorret_t inputInitSaturn(void);
|
||||
@@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/savesat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/savestreamsat.c
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savesat.h"
|
||||
#include "log/log.h"
|
||||
|
||||
/*
|
||||
* TODO: use Yaul's bup_* API for backup RAM access.
|
||||
* Reference: <bup/bup.h> in the Yaul SDK.
|
||||
*
|
||||
* bup_init(BUP_DEV_INTERNAL); // or BUP_DEV_EXTERNAL for cart
|
||||
* bup_stat_t stat;
|
||||
* bup_stat(BUP_DEV_INTERNAL, &stat);
|
||||
*
|
||||
* Write: bup_write(BUP_DEV_INTERNAL, &dir, data, size, BUP_MODE_NEW);
|
||||
* Read: bup_read(BUP_DEV_INTERNAL, filename, data, size);
|
||||
* Del: bup_delete(BUP_DEV_INTERNAL, filename);
|
||||
*/
|
||||
|
||||
errorret_t saveInitSaturn(void) {
|
||||
logDebug("[Saturn] saveInitSaturn\n");
|
||||
/* TODO: bup_init(BUP_DEV_INTERNAL); */
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDisposeSaturn(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteSaturn(const uint8_t slot) {
|
||||
logDebug("[Saturn] saveDeleteSaturn: slot=%u\n", (unsigned)slot);
|
||||
/* TODO: bup_delete(BUP_DEV_INTERNAL, filename_for_slot(slot)); */
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/systemsat.c
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/timesat.c
|
||||
)
|
||||
@@ -10,6 +10,7 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/compat.c
|
||||
)
|
||||
|
||||
add_subdirectory(asset)
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/assetsat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/assetsaturn.c
|
||||
)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "assetsat.h"
|
||||
#include "assetsaturn.h"
|
||||
|
||||
typedef assetsat_t assetplatform_t;
|
||||
typedef assetsaturn_t assetplatform_t;
|
||||
|
||||
#define assetInitPlatform assetInitSaturn
|
||||
#define assetDisposePlatform assetDisposeSaturn
|
||||
@@ -5,16 +5,15 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetsat.h"
|
||||
#include "assetsaturn.h"
|
||||
#include "log/log.h"
|
||||
|
||||
errorret_t assetInitSaturn(void) {
|
||||
logDebug("[Saturn] assetInitSaturn: initializing CD-Block\n");
|
||||
/* TODO: cd_block_init() */
|
||||
cd_block_init();
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetDisposeSaturn(void) {
|
||||
/* TODO: cd_block_deinit() */
|
||||
errorOk();
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} assetsat_t;
|
||||
} assetsaturn_t;
|
||||
|
||||
errorret_t assetInitSaturn(void);
|
||||
errorret_t assetDisposeSaturn(void);
|
||||
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
* Bare-metal libc compatibility stubs for Saturn/Yaul.
|
||||
*
|
||||
* Yaul's sysroot is a minimal bare-metal environment that lacks many
|
||||
* standard C library functions used by third-party code (libzip, yyjson)
|
||||
* and some Dusk utility modules. Provide minimal correct implementations.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* =========================================================================
|
||||
* Memory allocation
|
||||
* ========================================================================= */
|
||||
|
||||
/* realloc: libyaul provides a weak _realloc but it is not pulled from the
|
||||
* archive in time for libzip/yyjson (link-order issue). Provide a strong
|
||||
* definition using Yaul's __realloc (TLSF allocator). */
|
||||
extern void *__realloc(void *ptr, size_t size);
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
return __realloc(ptr, size);
|
||||
}
|
||||
|
||||
/* calloc: zero-initialised allocation. */
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
size_t total = nmemb * size;
|
||||
void *p = malloc(total);
|
||||
if (p) memset(p, 0, total);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* aligned_alloc: C11 aligned allocation.
|
||||
* Yaul's TLSF returns naturally aligned (≥8-byte) blocks.
|
||||
* For Saturn-specific hardware requirements (VDP command lists need 32-byte
|
||||
* alignment) this trivial wrapper is sufficient because Yaul's cmt_pool
|
||||
* handles its own aligned allocation internally; memoryAlign() is only used
|
||||
* for higher-level game objects where natural alignment is enough. */
|
||||
void *aligned_alloc(size_t alignment, size_t size) {
|
||||
(void)alignment;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Unix file-descriptor I/O
|
||||
* libzip's zip_random_unix.c opens /dev/urandom to seed its PRNG.
|
||||
* Saturn has no filesystem; return -1 (ENOENT / error) unconditionally.
|
||||
* ========================================================================= */
|
||||
|
||||
int open(const char *path, int flags, ...) {
|
||||
(void)path; (void)flags;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read(int fd, void *buf, size_t count) {
|
||||
(void)fd; (void)buf; (void)count;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int close(int fd) {
|
||||
(void)fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* memcpy_s — Annex K bounds-checking variant; not in Yaul's bare-metal libc.
|
||||
* libzip uses it; forward to plain memcpy.
|
||||
* ========================================================================= */
|
||||
|
||||
int memcpy_s(void *dest, size_t destsz, const void *src, size_t count) {
|
||||
if (!dest || !src || count > destsz) return 1;
|
||||
memcpy(dest, src, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Timing
|
||||
* ========================================================================= */
|
||||
|
||||
/* usleep: asset.c calls usleep() in async-load paths. Saturn has no threading;
|
||||
* busy-wait is not useful without a timer, so simply return. */
|
||||
int usleep(unsigned int usecs) {
|
||||
(void)usecs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* String-to-number conversion
|
||||
* Yaul provides strtol but not strtoul / strtoll / strtof.
|
||||
* ========================================================================= */
|
||||
|
||||
static unsigned long long _parse_ull(
|
||||
const char *s, char **endptr, int base, int issigned
|
||||
) {
|
||||
unsigned long long val = 0;
|
||||
int neg = 0;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == '+') { s++; }
|
||||
else if (*s == '-') { neg = 1; s++; }
|
||||
if (base == 0) {
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
|
||||
else if (s[0] == '0') { base = 8; s++; }
|
||||
else { base = 10; }
|
||||
} else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
s += 2;
|
||||
}
|
||||
const char *start = s;
|
||||
while (*s) {
|
||||
int digit;
|
||||
if (*s >= '0' && *s <= '9') digit = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'f') digit = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'F') digit = *s - 'A' + 10;
|
||||
else break;
|
||||
if (digit >= base) break;
|
||||
val = val * (unsigned long long)base + (unsigned long long)digit;
|
||||
s++;
|
||||
}
|
||||
if (s == start) { if (endptr) *endptr = (char *)start; return 0; }
|
||||
if (endptr) *endptr = (char *)s;
|
||||
if (neg && issigned) return (unsigned long long)(-(long long)val);
|
||||
if (neg) return (unsigned long long)(0ULL - val);
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned long strtoul(const char *s, char **endptr, int base) {
|
||||
return (unsigned long)_parse_ull(s, endptr, base, 0);
|
||||
}
|
||||
|
||||
long long strtoll(const char *s, char **endptr, int base) {
|
||||
return (long long)_parse_ull(s, endptr, base, 1);
|
||||
}
|
||||
|
||||
float strtof(const char *s, char **endptr) {
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
int neg = 0;
|
||||
if (*s == '-') { neg = 1; s++; } else if (*s == '+') s++;
|
||||
float val = 0.0f, frac = 1.0f;
|
||||
int hasDot = 0;
|
||||
const char *start = s;
|
||||
while (*s >= '0' && *s <= '9') { val = val * 10.0f + (float)(*s - '0'); s++; }
|
||||
if (*s == '.') {
|
||||
hasDot = 1; s++;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
frac /= 10.0f; val += frac * (float)(*s - '0'); s++;
|
||||
}
|
||||
}
|
||||
(void)hasDot;
|
||||
int exp = 0; int expneg = 0;
|
||||
if (*s == 'e' || *s == 'E') {
|
||||
s++;
|
||||
if (*s == '-') { expneg = 1; s++; } else if (*s == '+') s++;
|
||||
while (*s >= '0' && *s <= '9') { exp = exp * 10 + (*s - '0'); s++; }
|
||||
float p = 1.0f;
|
||||
for (int i = 0; i < exp; i++) p *= 10.0f;
|
||||
if (expneg) val /= p; else val *= p;
|
||||
}
|
||||
if (endptr) *endptr = (char *)(s == start ? start : s);
|
||||
return neg ? -val : val;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* String utilities missing from Yaul's libc
|
||||
* ========================================================================= */
|
||||
|
||||
/* strdup: libzip pkware code uses strdup() for password strings.
|
||||
* Yaul's bare-metal libc does not provide it. */
|
||||
char *strdup(const char *s) {
|
||||
if (!s) return NULL;
|
||||
size_t len = 0;
|
||||
while (s[len]) len++;
|
||||
char *p = (char *)malloc(len + 1);
|
||||
if (p) memcpy(p, s, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* stricmp / strcasecmp: Yaul has strcasecmp (_strcasecmp) but libzip uses
|
||||
* _stricmp (Windows name). Map to the same logic. */
|
||||
int stricmp(const char *a, const char *b) {
|
||||
while (*a && *b) {
|
||||
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
if (d) return d;
|
||||
a++; b++;
|
||||
}
|
||||
return tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Sorting
|
||||
* libzip uses qsort() for sorted-entry zip files (torrentzip).
|
||||
* ========================================================================= */
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *)) {
|
||||
if (nmemb < 2) return;
|
||||
uint8_t *arr = (uint8_t *)base;
|
||||
/* Insertion sort — O(n²) but avoids recursion and is fine for small n. */
|
||||
uint8_t tmp[256];
|
||||
for (size_t i = 1; i < nmemb; i++) {
|
||||
size_t j = i;
|
||||
memcpy(tmp, arr + j * size, size);
|
||||
while (j > 0 && compar(arr + (j-1) * size, tmp) > 0) {
|
||||
memcpy(arr + j * size, arr + (j-1) * size, size);
|
||||
j--;
|
||||
}
|
||||
memcpy(arr + j * size, tmp, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Floating-point math (SH-2 has no FPU; all operations are soft-float)
|
||||
* Used by test scenes and input helpers.
|
||||
* ========================================================================= */
|
||||
|
||||
/* sqrtf — Newton-Raphson, 4 iterations (enough for single precision). */
|
||||
float sqrtf(float x) {
|
||||
if (x <= 0.0f) return 0.0f;
|
||||
float r = x;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
r = (r + x / r) * 0.5f;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* sinf / cosf — Taylor series with range reduction to [-π, π]. */
|
||||
#define _COMPAT_PI 3.14159265358979323846f
|
||||
#define _COMPAT_2PI 6.28318530717958647692f
|
||||
|
||||
float sinf(float x) {
|
||||
/* Range reduction */
|
||||
while (x > _COMPAT_PI) x -= _COMPAT_2PI;
|
||||
while (x < -_COMPAT_PI) x += _COMPAT_2PI;
|
||||
float x2 = x * x;
|
||||
/* Horner form: x(1 - x²/6(1 - x²/20(1 - x²/42))) */
|
||||
return x * (1.0f - x2 * (1.0f/6.0f * (1.0f - x2 *
|
||||
(1.0f/20.0f * (1.0f - x2 * (1.0f/42.0f))))));
|
||||
}
|
||||
|
||||
float cosf(float x) {
|
||||
return sinf(x + _COMPAT_PI * 0.5f);
|
||||
}
|
||||
|
||||
/* fmodf — IEEE 754 fmod for floats. */
|
||||
float fmodf(float x, float y) {
|
||||
if (y == 0.0f) return 0.0f;
|
||||
float q = x / y;
|
||||
/* Truncate toward zero */
|
||||
int qi = (int)q;
|
||||
return x - (float)qi * y;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* FILE* I/O stubs
|
||||
* yyjson's file-reading path (yyjson_read_fp) pulls in fread/fseek/ftell.
|
||||
* With -ffunction-sections + --gc-sections that function is dead-code
|
||||
* eliminated if it is unreachable from Dusk. These stubs are a safety
|
||||
* net in case the linker cannot eliminate the entire object in one pass.
|
||||
* Saturn has no filesystem so all file operations return error/0.
|
||||
* Use void* for the stream parameter to avoid conflicting with any
|
||||
* stdio.h FILE typedef that the sysroot may or may not provide.
|
||||
* ========================================================================= */
|
||||
|
||||
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||
(void)ptr; (void)size; (void)nmemb; (void)stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fseek(void *stream, long offset, int whence) {
|
||||
(void)stream; (void)offset; (void)whence;
|
||||
return -1;
|
||||
}
|
||||
|
||||
long ftell(void *stream) {
|
||||
(void)stream;
|
||||
return -1L;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/displaysat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/displaysaturn.c
|
||||
)
|
||||
|
||||
add_subdirectory(render)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/displaysat.h"
|
||||
#include "displaysaturn.h"
|
||||
|
||||
typedef displaysat_t displayplatform_t;
|
||||
typedef displaysaturn_t displayplatform_t;
|
||||
|
||||
#define displayPlatformInit displaySaturnInit
|
||||
#define displayPlatformFlush displaySaturnFlush
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/displaysaturn.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
#include "display/display.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include <vdp1.h>
|
||||
#include <vdp2/cram.h>
|
||||
#include <vdp2/tvmd.h>
|
||||
#include <vdp2/scrn.h>
|
||||
#include <vdp2/vram.h>
|
||||
#include <vdp2.h>
|
||||
|
||||
errorret_t displaySaturnInit(void) {
|
||||
logDebug("[Saturn] displaySaturnInit: start\n");
|
||||
DISPLAY.whichBuffer = 0;
|
||||
|
||||
vdp2_tvmd_display_res_set(
|
||||
VDP2_TVMD_INTERLACE_NONE,
|
||||
VDP2_TVMD_HORZ_NORMAL_A,
|
||||
VDP2_TVMD_VERT_224
|
||||
);
|
||||
vdp2_tvmd_display_set();
|
||||
|
||||
/* Disable all NBG/RBG scroll planes; game content drawn entirely via VDP1. */
|
||||
vdp2_scrn_display_set(VDP2_SCRN_DISP_NONE);
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: calling renderSaturnInit\n");
|
||||
errorChain(renderSaturnInit());
|
||||
|
||||
logDebug("[Saturn] displaySaturnInit: done\n");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf) {
|
||||
assertNotNull(buf, "Saturn flush: null ropbuffer");
|
||||
errorChain(renderSaturnFlush(buf));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t displaySaturnSwap(void) {
|
||||
logDebug("[Saturn] displaySaturnSwap\n");
|
||||
vdp1_sync_render();
|
||||
vdp1_sync();
|
||||
vdp2_sync();
|
||||
vdp2_sync_wait();
|
||||
DISPLAY.whichBuffer ^= 1;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void displaySaturnDispose(void) {
|
||||
logDebug("[Saturn] displaySaturnDispose\n");
|
||||
renderSaturnDispose();
|
||||
}
|
||||
@@ -10,12 +10,12 @@
|
||||
#include "display/displaystate.h"
|
||||
#include "display/render/ropbuffer.h"
|
||||
|
||||
#define SAT_SCREEN_W DUSK_DISPLAY_WIDTH
|
||||
#define SAT_SCREEN_H DUSK_DISPLAY_HEIGHT
|
||||
#define SATURN_SCREEN_W DUSK_DISPLAY_WIDTH
|
||||
#define SATURN_SCREEN_H DUSK_DISPLAY_HEIGHT
|
||||
|
||||
typedef struct {
|
||||
int_t whichBuffer;
|
||||
} displaysat_t;
|
||||
} displaysaturn_t;
|
||||
|
||||
errorret_t displaySaturnInit(void);
|
||||
errorret_t displaySaturnFlush(ropbuffer_t *buf);
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/inputsat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/rendersaturn.c
|
||||
)
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
|
||||
#define renderPlatformTextureCreate renderSaturnTextureCreate
|
||||
#define renderPlatformTextureDispose renderSaturnTextureDispose
|
||||
+116
-115
@@ -5,7 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/render/rendersat.h"
|
||||
#include "display/render/rendersaturn.h"
|
||||
#include "display/render/rop.h"
|
||||
#include "display/color.h"
|
||||
#include "assert/assert.h"
|
||||
@@ -27,15 +27,15 @@
|
||||
*
|
||||
* VDP2 CRAM (4KB) holds palettes:
|
||||
* Each 256-color palette occupies 512 bytes (256 × 2-byte RGB1555 entries).
|
||||
* We reserve one palette slot per texture handle (up to SAT_PALETTE_MAX).
|
||||
* We reserve one palette slot per texture handle (up to SATURN_PALETTE_MAX).
|
||||
*/
|
||||
|
||||
/* ---- Limits -------------------------------------------------------------- */
|
||||
|
||||
#define SAT_RTEXTURE_MAX 128
|
||||
#define SAT_CMDT_MAX 1024
|
||||
#define SAT_TEXTURE_VRAM_BASE 0x010000u /* byte offset in VDP1 VRAM */
|
||||
#define SAT_TEXTURE_VRAM_SIZE (0x200000u - SAT_TEXTURE_VRAM_BASE)
|
||||
#define SATURN_RTEXTURE_MAX 128
|
||||
#define SATURN_CMDT_MAX 1024
|
||||
#define SATURN_TEXTURE_VRAM_BASE 0x010000u /* byte offset in VDP1 VRAM */
|
||||
#define SATURN_TEXTURE_VRAM_SIZE (0x200000u - SATURN_TEXTURE_VRAM_BASE)
|
||||
|
||||
/* ---- VDP1 command table entry (hardware layout, 32 bytes) ---------------- */
|
||||
|
||||
@@ -52,23 +52,23 @@ typedef struct __attribute__((packed)) {
|
||||
int16_t xd, yd; /* Vertex D (distorted sprite only) */
|
||||
uint16_t grda; /* Gouraud shading address (unused = 0) */
|
||||
uint16_t _pad;
|
||||
} satcmd_t;
|
||||
} saturncmd_t;
|
||||
|
||||
_Static_assert(sizeof(satcmd_t) == 32, "satcmd_t must be 32 bytes");
|
||||
_Static_assert(sizeof(saturncmd_t) == 32, "saturncmd_t must be 32 bytes");
|
||||
|
||||
/* CTRL command type bits (bits 2:0) */
|
||||
#define SATCMD_CTRL_NORMAL_SPRITE (0x0000u) /* aligned rect */
|
||||
#define SATCMD_CTRL_SCALED_SPRITE (0x0001u)
|
||||
#define SATCMD_CTRL_DISTORTED_SPRITE (0x0002u) /* arbitrary quad */
|
||||
#define SATCMD_CTRL_POLYGON (0x0004u) /* solid polygon */
|
||||
#define SATCMD_CTRL_SYSCLIP (0x0009u) /* system clipping */
|
||||
#define SATCMD_CTRL_END (0x8000u) /* end of list */
|
||||
#define SATURNCMD_CTRL_NORMAL_SPRITE (0x0000u) /* aligned rect */
|
||||
#define SATURNCMD_CTRL_SCALED_SPRITE (0x0001u)
|
||||
#define SATURNCMD_CTRL_DISTORTED_SPRITE (0x0002u) /* arbitrary quad */
|
||||
#define SATURNCMD_CTRL_POLYGON (0x0004u) /* solid polygon */
|
||||
#define SATURNCMD_CTRL_SYSCLIP (0x0009u) /* system clipping */
|
||||
#define SATURNCMD_CTRL_END (0x8000u) /* end of list */
|
||||
|
||||
/* PMOD draw mode */
|
||||
#define SATCMD_PMOD_TRANS (0x0000u) /* transparent pixel 0 */
|
||||
#define SATCMD_PMOD_8BPP_CBANK (0x0038u) /* 256-color, color bank */
|
||||
#define SATCMD_PMOD_ECD (0x0080u) /* extend color depth */
|
||||
#define SATCMD_PMOD_SPD (0x0040u) /* do not skip index 0 */
|
||||
#define SATURNCMD_PMOD_TRANS (0x0000u) /* transparent pixel 0 */
|
||||
#define SATURNCMD_PMOD_8BPP_CBANK (0x0038u) /* 256-color, color bank */
|
||||
#define SATURNCMD_PMOD_ECD (0x0080u) /* extend color depth */
|
||||
#define SATURNCMD_PMOD_SPD (0x0040u) /* do not skip index 0 */
|
||||
|
||||
/* ---- Texture table ------------------------------------------------------- */
|
||||
|
||||
@@ -78,27 +78,27 @@ typedef struct {
|
||||
uint16_t w, h;
|
||||
uint32_t vramByteOffset; /* byte offset into VDP1 VRAM pool */
|
||||
uint16_t cramWordOffset; /* word offset into VDP2 CRAM for palette */
|
||||
} sattexentry_t;
|
||||
} saturntexentry_t;
|
||||
|
||||
static sattexentry_t satTexTable[SAT_RTEXTURE_MAX];
|
||||
static uint16_t satTexNext = 1; /* 0 = white fallback */
|
||||
static uint32_t satTexVramUsed = 0;
|
||||
static uint16_t satTexCramUsed = 0; /* in 256-entry slots */
|
||||
static saturntexentry_t saturnTexTable[SATURN_RTEXTURE_MAX];
|
||||
static uint16_t saturnTexNext = 1; /* 0 = white fallback */
|
||||
static uint32_t saturnTexVramUsed = 0;
|
||||
static uint16_t saturnTexCramUsed = 0; /* in 256-entry slots */
|
||||
|
||||
/* ---- Command table buffer ------------------------------------------------ */
|
||||
|
||||
static satcmd_t satCmdBuf[SAT_CMDT_MAX];
|
||||
static uint16_t satCmdCount;
|
||||
static saturncmd_t saturnCmdBuf[SATURN_CMDT_MAX];
|
||||
static uint16_t saturnCmdCount;
|
||||
|
||||
/* ---- Projection state ---------------------------------------------------- */
|
||||
|
||||
static float satFovY = 0.0f; /* 0 = ortho */
|
||||
static float satAspect = 1.0f;
|
||||
static float satNearZ = 1.0f;
|
||||
static float satFarZ = 1000.0f;
|
||||
static float saturnFovY = 0.0f; /* 0 = ortho */
|
||||
static float saturnAspect = 1.0f;
|
||||
static float saturnNearZ = 1.0f;
|
||||
static float saturnFarZ = 1000.0f;
|
||||
|
||||
static float satViewEyeX = 0.0f, satViewEyeY = 0.0f, satViewEyeZ = 1.0f;
|
||||
static float satViewTgtX = 0.0f, satViewTgtY = 0.0f, satViewTgtZ = 0.0f;
|
||||
static float saturnViewEyeX = 0.0f, saturnViewEyeY = 0.0f, saturnViewEyeZ = 1.0f;
|
||||
static float saturnViewTgtX = 0.0f, saturnViewTgtY = 0.0f, saturnViewTgtZ = 0.0f;
|
||||
|
||||
/* ---- Helpers ------------------------------------------------------------- */
|
||||
|
||||
@@ -114,7 +114,7 @@ static uint16_t toRGB1555(color_t c) {
|
||||
/* Write palette into VDP2 CRAM at the texture's slot.
|
||||
* CRAM is mapped at 0x25F00000 (Saturn memory map).
|
||||
* Each palette slot is 512 bytes = 256 × uint16_t. */
|
||||
static void uploadPalette(sattexentry_t *e) {
|
||||
static void uploadPalette(saturntexentry_t *e) {
|
||||
volatile uint16_t *cram = (volatile uint16_t *)0x25F00000;
|
||||
uint32_t base = (uint32_t)e->cramWordOffset * 256u;
|
||||
for(uint32_t i = 0; i < 256; i++) {
|
||||
@@ -125,9 +125,9 @@ static void uploadPalette(sattexentry_t *e) {
|
||||
/* Copy indices row-by-row into VDP1 VRAM.
|
||||
* VDP1 VRAM is at 0x05C00000. Textures must be stored starting on an
|
||||
* 8-byte boundary; we keep our pool 8-byte aligned already. */
|
||||
static void uploadIndices(sattexentry_t *e) {
|
||||
static void uploadIndices(saturntexentry_t *e) {
|
||||
volatile uint8_t *vram =
|
||||
(volatile uint8_t *)(0x05C00000u + SAT_TEXTURE_VRAM_BASE + e->vramByteOffset);
|
||||
(volatile uint8_t *)(0x05C00000u + SATURN_TEXTURE_VRAM_BASE + e->vramByteOffset);
|
||||
uint32_t total = (uint32_t)e->w * e->h;
|
||||
for(uint32_t i = 0; i < total; i++) {
|
||||
vram[i] = e->cpuIndices[i];
|
||||
@@ -135,10 +135,10 @@ static void uploadIndices(sattexentry_t *e) {
|
||||
}
|
||||
|
||||
/* Return a fresh command slot or NULL if full. */
|
||||
static satcmd_t *allocCmd(void) {
|
||||
if(satCmdCount >= SAT_CMDT_MAX) return NULL;
|
||||
satcmd_t *c = &satCmdBuf[satCmdCount++];
|
||||
memoryZero(c, sizeof(satcmd_t));
|
||||
static saturncmd_t *allocCmd(void) {
|
||||
if(saturnCmdCount >= SATURN_CMDT_MAX) return NULL;
|
||||
saturncmd_t *c = &saturnCmdBuf[saturnCmdCount++];
|
||||
memoryZero(c, sizeof(saturncmd_t));
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -146,24 +146,24 @@ static satcmd_t *allocCmd(void) {
|
||||
|
||||
errorret_t renderSaturnInit(void) {
|
||||
logDebug("[Saturn] renderSaturnInit\n");
|
||||
memoryZero(satTexTable, sizeof(satTexTable));
|
||||
satTexNext = 1;
|
||||
satTexVramUsed = 0;
|
||||
satTexCramUsed = 0;
|
||||
satCmdCount = 0;
|
||||
memoryZero(saturnTexTable, sizeof(saturnTexTable));
|
||||
saturnTexNext = 1;
|
||||
saturnTexVramUsed = 0;
|
||||
saturnTexCramUsed = 0;
|
||||
saturnCmdCount = 0;
|
||||
|
||||
/* White 1×1 fallback: slot 0 */
|
||||
sattexentry_t *e = &satTexTable[0];
|
||||
saturntexentry_t *e = &saturnTexTable[0];
|
||||
e->cpuIndices = (uint8_t *)malloc(1);
|
||||
assertNotNull(e->cpuIndices, "Saturn: failed to allocate fallback index buffer");
|
||||
e->cpuIndices[0] = 0;
|
||||
memoryZero(e->palette, 256 * sizeof(color_t));
|
||||
e->palette[0] = COLOR_WHITE;
|
||||
e->w = 1; e->h = 1;
|
||||
e->vramByteOffset = satTexVramUsed;
|
||||
e->cramWordOffset = satTexCramUsed;
|
||||
satTexVramUsed += 8; /* 8-byte minimum alignment */
|
||||
satTexCramUsed++;
|
||||
e->vramByteOffset = saturnTexVramUsed;
|
||||
e->cramWordOffset = saturnTexCramUsed;
|
||||
saturnTexVramUsed += 8; /* 8-byte minimum alignment */
|
||||
saturnTexCramUsed++;
|
||||
uploadIndices(e);
|
||||
uploadPalette(e);
|
||||
|
||||
@@ -176,29 +176,29 @@ rtexture_t renderSaturnTextureCreate(
|
||||
uint16_t w, uint16_t h,
|
||||
const uint8_t *indices, const color_t *palette
|
||||
) {
|
||||
assertTrue(satTexNext < SAT_RTEXTURE_MAX, "Saturn texture table full");
|
||||
assertTrue(saturnTexNext < SATURN_RTEXTURE_MAX, "Saturn texture table full");
|
||||
|
||||
uint32_t byteCount = (uint32_t)w * h;
|
||||
/* Round up to 8-byte boundary for SRCA alignment. */
|
||||
uint32_t vramBytes = (byteCount + 7u) & ~7u;
|
||||
assertTrue(
|
||||
satTexVramUsed + vramBytes <= SAT_TEXTURE_VRAM_SIZE,
|
||||
saturnTexVramUsed + vramBytes <= SATURN_TEXTURE_VRAM_SIZE,
|
||||
"Saturn VDP1 texture VRAM exhausted"
|
||||
);
|
||||
|
||||
rtexture_t handle = (rtexture_t)satTexNext++;
|
||||
sattexentry_t *e = &satTexTable[handle];
|
||||
rtexture_t handle = (rtexture_t)saturnTexNext++;
|
||||
saturntexentry_t *e = &saturnTexTable[handle];
|
||||
|
||||
e->cpuIndices = (uint8_t *)malloc(byteCount);
|
||||
assertNotNull(e->cpuIndices, "Saturn: failed to allocate cpu index buffer");
|
||||
memoryCopy(e->cpuIndices, indices, byteCount);
|
||||
memoryCopy(e->palette, palette, 256 * sizeof(color_t));
|
||||
e->w = w; e->h = h;
|
||||
e->vramByteOffset = satTexVramUsed;
|
||||
e->cramWordOffset = satTexCramUsed;
|
||||
e->vramByteOffset = saturnTexVramUsed;
|
||||
e->cramWordOffset = saturnTexCramUsed;
|
||||
|
||||
satTexVramUsed += vramBytes;
|
||||
satTexCramUsed++;
|
||||
saturnTexVramUsed += vramBytes;
|
||||
saturnTexCramUsed++;
|
||||
|
||||
uploadIndices(e);
|
||||
uploadPalette(e);
|
||||
@@ -206,31 +206,31 @@ rtexture_t renderSaturnTextureCreate(
|
||||
}
|
||||
|
||||
void renderSaturnTextureDispose(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return;
|
||||
sattexentry_t *e = &satTexTable[tex];
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return;
|
||||
saturntexentry_t *e = &saturnTexTable[tex];
|
||||
if(e->cpuIndices) { free(e->cpuIndices); e->cpuIndices = NULL; }
|
||||
}
|
||||
|
||||
color_t *renderSaturnTextureGetPalette(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return NULL;
|
||||
return satTexTable[tex].palette;
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return NULL;
|
||||
return saturnTexTable[tex].palette;
|
||||
}
|
||||
|
||||
uint8_t *renderSaturnTextureGetIndices(rtexture_t tex) {
|
||||
if(tex == RTEXTURE_NONE || tex >= SAT_RTEXTURE_MAX) return NULL;
|
||||
return satTexTable[tex].cpuIndices;
|
||||
if(tex == RTEXTURE_NONE || tex >= SATURN_RTEXTURE_MAX) return NULL;
|
||||
return saturnTexTable[tex].cpuIndices;
|
||||
}
|
||||
|
||||
/* ---- Flush --------------------------------------------------------------- */
|
||||
|
||||
/* Fill in the SRCA/CMDSIZE/CMDCOLR fields from a texture handle. */
|
||||
static void cmdSetTexture(satcmd_t *cmd, rtexture_t tex) {
|
||||
sattexentry_t *e = (tex < SAT_RTEXTURE_MAX && satTexTable[tex].cpuIndices)
|
||||
? &satTexTable[tex]
|
||||
: &satTexTable[0];
|
||||
static void cmdSetTexture(saturncmd_t *cmd, rtexture_t tex) {
|
||||
saturntexentry_t *e = (tex < SATURN_RTEXTURE_MAX && saturnTexTable[tex].cpuIndices)
|
||||
? &saturnTexTable[tex]
|
||||
: &saturnTexTable[0];
|
||||
|
||||
/* SRCA = byte offset from VDP1 VRAM base / 8. */
|
||||
uint32_t srcByteAddr = SAT_TEXTURE_VRAM_BASE + e->vramByteOffset;
|
||||
uint32_t srcByteAddr = SATURN_TEXTURE_VRAM_BASE + e->vramByteOffset;
|
||||
cmd->srca = (uint16_t)(srcByteAddr / 8u);
|
||||
|
||||
/* SIZE = ((width/8) << 8) | height (each axis limited to 0-255 after /8). */
|
||||
@@ -241,18 +241,18 @@ static void cmdSetTexture(satcmd_t *cmd, rtexture_t tex) {
|
||||
uint32_t cramWordBase = (uint32_t)e->cramWordOffset * 256u;
|
||||
cmd->colr = (uint16_t)(cramWordBase / 16u);
|
||||
|
||||
cmd->pmod = SATCMD_PMOD_8BPP_CBANK; /* 256-color, index 0 transparent */
|
||||
cmd->pmod = SATURNCMD_PMOD_8BPP_CBANK; /* 256-color, index 0 transparent */
|
||||
}
|
||||
|
||||
static void flush2DSprite(const ropsprite_t *s) {
|
||||
satcmd_t *cmd = allocCmd();
|
||||
saturncmd_t *cmd = allocCmd();
|
||||
if(!cmd) return;
|
||||
|
||||
sattexentry_t *e = (s->texture < SAT_RTEXTURE_MAX && satTexTable[s->texture].cpuIndices)
|
||||
? &satTexTable[s->texture]
|
||||
: &satTexTable[0];
|
||||
saturntexentry_t *e = (s->texture < SATURN_RTEXTURE_MAX && saturnTexTable[s->texture].cpuIndices)
|
||||
? &saturnTexTable[s->texture]
|
||||
: &saturnTexTable[0];
|
||||
|
||||
cmd->ctrl = SATCMD_CTRL_NORMAL_SPRITE;
|
||||
cmd->ctrl = SATURNCMD_CTRL_NORMAL_SPRITE;
|
||||
cmd->link = 0;
|
||||
cmdSetTexture(cmd, s->texture);
|
||||
|
||||
@@ -265,7 +265,7 @@ static void flush2DSprite(const ropsprite_t *s) {
|
||||
/* UV sub-region: the VDP1 always draws the full texture, so to support
|
||||
* sprite atlases we would need a clipped intermediate. For now we treat
|
||||
* the full texture as the sprite frame (atlas sub-rect is a TODO). */
|
||||
(void)e; /* suppress unused warning for e->w/h if UV clipping is added */
|
||||
(void)e;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -277,20 +277,19 @@ static void project(
|
||||
float *sx, float *sy
|
||||
) {
|
||||
/* Translate relative to eye. */
|
||||
float rx = wx - satViewEyeX;
|
||||
float ry = wy - satViewEyeY;
|
||||
float rz = wz - satViewEyeZ;
|
||||
float rx = wx - saturnViewEyeX;
|
||||
float ry = wy - saturnViewEyeY;
|
||||
float rz = wz - saturnViewEyeZ;
|
||||
|
||||
/* Rotate view to look at target (approximated: no full matrix here). */
|
||||
/* TODO: replace with a proper view-matrix multiply for non-axis-aligned cameras. */
|
||||
float fwd_z = satViewTgtZ - satViewEyeZ;
|
||||
(void)fwd_z; /* simple pass-through for now */
|
||||
float fwd_z = saturnViewTgtZ - saturnViewEyeZ;
|
||||
(void)fwd_z;
|
||||
|
||||
float half_w = (float)DUSK_DISPLAY_WIDTH * 0.5f;
|
||||
float half_h = (float)DUSK_DISPLAY_HEIGHT * 0.5f;
|
||||
|
||||
if(satFovY > 0.0f && rz != 0.0f) {
|
||||
float focal = half_h / (satFovY * 0.5f);
|
||||
if(saturnFovY > 0.0f && rz != 0.0f) {
|
||||
float focal = half_h / (saturnFovY * 0.5f);
|
||||
*sx = half_w + (rx / rz) * focal;
|
||||
*sy = half_h - (ry / rz) * focal;
|
||||
} else {
|
||||
@@ -300,10 +299,10 @@ static void project(
|
||||
}
|
||||
|
||||
static void flush3DQuad(const ropquad3d_t *q) {
|
||||
satcmd_t *cmd = allocCmd();
|
||||
saturncmd_t *cmd = allocCmd();
|
||||
if(!cmd) return;
|
||||
|
||||
cmd->ctrl = SATCMD_CTRL_DISTORTED_SPRITE;
|
||||
cmd->ctrl = SATURNCMD_CTRL_DISTORTED_SPRITE;
|
||||
cmd->link = 0;
|
||||
cmdSetTexture(cmd, q->texture);
|
||||
|
||||
@@ -332,35 +331,37 @@ static void flush3DQuad(const ropquad3d_t *q) {
|
||||
/* Submit the finished command table to VDP1 VRAM and trigger rendering. */
|
||||
static void submitCmdTable(void) {
|
||||
/* Append end-of-list sentinel. */
|
||||
if(satCmdCount < SAT_CMDT_MAX) {
|
||||
satcmd_t *end = &satCmdBuf[satCmdCount];
|
||||
memoryZero(end, sizeof(satcmd_t));
|
||||
end->ctrl = SATCMD_CTRL_END;
|
||||
if(saturnCmdCount < SATURN_CMDT_MAX) {
|
||||
saturncmd_t *end = &saturnCmdBuf[saturnCmdCount];
|
||||
memoryZero(end, sizeof(saturncmd_t));
|
||||
end->ctrl = SATURNCMD_CTRL_END;
|
||||
}
|
||||
|
||||
/* DMA or CPU-copy the command table to VDP1 VRAM at offset 0x000000.
|
||||
* VDP1 VRAM starts at 0x05C00000 in the Saturn memory map. */
|
||||
volatile satcmd_t *vdp1CmdTable = (volatile satcmd_t *)0x05C00000u;
|
||||
uint32_t count = satCmdCount + 1u; /* include the END entry */
|
||||
volatile saturncmd_t *vdp1CmdTable = (volatile saturncmd_t *)0x05C00000u;
|
||||
uint32_t count = saturnCmdCount + 1u; /* include the END entry */
|
||||
for(uint32_t i = 0; i < count; i++) {
|
||||
vdp1CmdTable[i] = satCmdBuf[i];
|
||||
vdp1CmdTable[i] = saturnCmdBuf[i];
|
||||
}
|
||||
|
||||
/* Set VDP1 command table top address to 0x000000 (the default). */
|
||||
volatile uint16_t *vdp1Regs = (volatile uint16_t *)0x25D00000u;
|
||||
/* VDP1 MODR (Mode Register) — ensure draw mode is correct */
|
||||
vdp1Regs[0] = 0x0000; /* PTMR: plot trigger — VDP1 draws on frame change */
|
||||
/* EWDR, EWLR, EWRR — Erase/Write window (full screen) */
|
||||
vdp1Regs[2] = 0x0000; /* EWDR: erase write data (black) */
|
||||
vdp1Regs[3] = 0x0000; /* EWLR: top-left (0,0) */
|
||||
/* PTMR: plot trigger — VDP1 draws on frame change */
|
||||
vdp1Regs[0] = 0x0000;
|
||||
/* EWDR: erase write data (black) */
|
||||
vdp1Regs[2] = 0x0000;
|
||||
/* EWLR: top-left (0,0) */
|
||||
vdp1Regs[3] = 0x0000;
|
||||
/* EWRR: bottom-right */
|
||||
vdp1Regs[4] = (uint16_t)(((DUSK_DISPLAY_HEIGHT - 1) << 9) |
|
||||
((DUSK_DISPLAY_WIDTH / 2) - 1)); /* EWRR */
|
||||
((DUSK_DISPLAY_WIDTH / 2) - 1));
|
||||
}
|
||||
|
||||
errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
logDebug("[Saturn] renderSaturnFlush: count=%u\n", (unsigned)buf->count);
|
||||
|
||||
satCmdCount = 0;
|
||||
saturnCmdCount = 0;
|
||||
|
||||
uint32_t offset = 0;
|
||||
while(offset < buf->byteCount) {
|
||||
@@ -375,9 +376,9 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
cram[0] = toRGB1555(c->color);
|
||||
|
||||
/* Issue a VDP1 system clipping command to reset the clip window. */
|
||||
satcmd_t *clip = allocCmd();
|
||||
saturncmd_t *clip = allocCmd();
|
||||
if(clip) {
|
||||
clip->ctrl = SATCMD_CTRL_SYSCLIP;
|
||||
clip->ctrl = SATURNCMD_CTRL_SYSCLIP;
|
||||
clip->link = 0;
|
||||
clip->xa = 0;
|
||||
clip->ya = 0;
|
||||
@@ -393,21 +394,21 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
|
||||
case ROP_SET_PROJECTION: {
|
||||
const ropprojection_t *p = (const ropprojection_t *)hdr;
|
||||
satFovY = fixedToFloat(p->fovY);
|
||||
satAspect = fixedToFloat(p->aspect);
|
||||
satNearZ = fixedToFloat(p->nearZ);
|
||||
satFarZ = fixedToFloat(p->farZ);
|
||||
saturnFovY = fixedToFloat(p->fovY);
|
||||
saturnAspect = fixedToFloat(p->aspect);
|
||||
saturnNearZ = fixedToFloat(p->nearZ);
|
||||
saturnFarZ = fixedToFloat(p->farZ);
|
||||
break;
|
||||
}
|
||||
|
||||
case ROP_SET_VIEW: {
|
||||
const ropview_t *v = (const ropview_t *)hdr;
|
||||
satViewEyeX = (float)v->eyeX;
|
||||
satViewEyeY = (float)v->eyeY;
|
||||
satViewEyeZ = (float)v->eyeZ;
|
||||
satViewTgtX = (float)v->tgtX;
|
||||
satViewTgtY = (float)v->tgtY;
|
||||
satViewTgtZ = (float)v->tgtZ;
|
||||
saturnViewEyeX = (float)v->eyeX;
|
||||
saturnViewEyeY = (float)v->eyeY;
|
||||
saturnViewEyeZ = (float)v->eyeZ;
|
||||
saturnViewTgtX = (float)v->tgtX;
|
||||
saturnViewTgtY = (float)v->tgtY;
|
||||
saturnViewTgtZ = (float)v->tgtZ;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -436,11 +437,11 @@ errorret_t renderSaturnFlush(ropbuffer_t *buf) {
|
||||
/* ---- Dispose ------------------------------------------------------------- */
|
||||
|
||||
void renderSaturnDispose(void) {
|
||||
for(uint16_t i = 0; i < satTexNext; i++) {
|
||||
sattexentry_t *e = &satTexTable[i];
|
||||
for(uint16_t i = 0; i < saturnTexNext; i++) {
|
||||
saturntexentry_t *e = &saturnTexTable[i];
|
||||
if(e->cpuIndices) { free(e->cpuIndices); e->cpuIndices = NULL; }
|
||||
}
|
||||
satTexNext = 1;
|
||||
satTexVramUsed = 0;
|
||||
satTexCramUsed = 0;
|
||||
saturnTexNext = 1;
|
||||
saturnTexVramUsed = 0;
|
||||
saturnTexCramUsed = 0;
|
||||
}
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/rendersat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/inputsaturn.c
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "inputsaturn.h"
|
||||
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
typedef inputgamepadbuttonsaturn_t inputgamepadbuttonplatform_t;
|
||||
typedef inputgamepadaxissaturn_t inputgamepadaxissplatform_t;
|
||||
#endif
|
||||
|
||||
typedef inputsaturn_t inputplatform_t;
|
||||
|
||||
#define inputInitPlatform inputInitSaturn
|
||||
#define inputUpdatePlatform inputUpdateSaturn
|
||||
#define inputButtonGetValuePlatform inputButtonGetValueSaturn
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "assert/assert.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons.
|
||||
* gpButton values are bit positions within the SMPC peripheral held.button word.
|
||||
*
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*
|
||||
* The Saturn has no L shoulder in the standard pad (only R); the 3D pad
|
||||
* exposes L as bit 15 via the analog extension.
|
||||
*/
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } },
|
||||
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } },
|
||||
{ .name = "c", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 5 } },
|
||||
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 2 } },
|
||||
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 1 } },
|
||||
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 0 } },
|
||||
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 7 } },
|
||||
{ .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 8 } },
|
||||
{ .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 9 } },
|
||||
{ .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 10 } },
|
||||
{ .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 11 } },
|
||||
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 15 } },
|
||||
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 3 } },
|
||||
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 6 } }, /* A */
|
||||
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = 4 } }, /* B */
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
errorret_t inputInitSaturn(void) {
|
||||
#define X(buttonName, buttonAction) \
|
||||
inputBind(inputButtonGetByName(buttonName), buttonAction);
|
||||
X("up", INPUT_ACTION_UP);
|
||||
X("down", INPUT_ACTION_DOWN);
|
||||
X("left", INPUT_ACTION_LEFT);
|
||||
X("right", INPUT_ACTION_RIGHT);
|
||||
X("accept", INPUT_ACTION_ACCEPT);
|
||||
X("cancel", INPUT_ACTION_CANCEL);
|
||||
X("start", INPUT_ACTION_RAGEQUIT);
|
||||
#undef X
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void inputUpdateSaturn(void) {
|
||||
smpc_peripheral_digital_t digital;
|
||||
smpc_peripheral_digital_port(1, &digital);
|
||||
/* Yaul's held.button bitmask matches the bit-position layout above. */
|
||||
memcpy(&INPUT.platform.padButtons, &digital.held.button, sizeof(INPUT.platform.padButtons));
|
||||
}
|
||||
|
||||
int16_t inputButtonGetValueSaturn(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD:
|
||||
return (INPUT.platform.padButtons & (uint16_t)(1u << button.gpButton))
|
||||
? INT16_MAX : 0;
|
||||
default:
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t inputGetDeadzoneSaturn(const inputbutton_t button) {
|
||||
(void)button;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "error/error.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
/*
|
||||
* Saturn standard digital pad buttons (smpc_peripheral_digital_t).
|
||||
* Yaul's smpc_peripheral_digital_port() fills the bitmask in held.button.
|
||||
*
|
||||
* Button bitmask:
|
||||
* bit 11 = Right bit 10 = Left bit 9 = Down bit 8 = Up
|
||||
* bit 7 = Start bit 6 = A bit 5 = C bit 4 = B
|
||||
* bit 3 = R bit 2 = X bit 1 = Y bit 0 = Z
|
||||
*/
|
||||
|
||||
/* gpButton stores the bit-position within the SMPC button word */
|
||||
typedef uint8_t inputgamepadbuttonsaturn_t;
|
||||
|
||||
/* Saturn's standard pad is digital-only; define a stub axis type */
|
||||
typedef enum {
|
||||
SATURN_GAMEPAD_AXIS_COUNT = 0
|
||||
} inputgamepadaxissaturn_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t padButtons; /* SMPC digital pad held-button bitmask, port 1 */
|
||||
} inputsaturn_t;
|
||||
|
||||
/* Forward declaration — avoids circular include with inputbutton.h */
|
||||
typedef struct inputbutton_s inputbutton_t;
|
||||
|
||||
errorret_t inputInitSaturn(void);
|
||||
void inputUpdateSaturn(void);
|
||||
int16_t inputButtonGetValueSaturn(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneSaturn(const inputbutton_t button);
|
||||
@@ -12,8 +12,9 @@
|
||||
* On Saturn, stdout goes to the debug serial port (via Yaul's dbgio module).
|
||||
* With a comm link or emulator (Mednafen, SSF) this is visible on the host.
|
||||
*
|
||||
* TODO: add dbgio_init() in systemSaturnInit() and replace vprintf with
|
||||
* dbgio_printf() for hardware-accurate serial output.
|
||||
* TODO: add dbgio_dev_default_set(DBGIO_DEV_USB_CART, NULL) in
|
||||
* systemSaturnInit() and replace vprintf with dbgio_printf() for
|
||||
* hardware-accurate serial output.
|
||||
*/
|
||||
|
||||
void logDebug(const char_t *message, ...) {
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/networksaturn.c
|
||||
)
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "networksat.h"
|
||||
#include "networksaturn.h"
|
||||
|
||||
#define networkPlatformInit networkSaturnInit
|
||||
#define networkPlatformUpdate networkSaturnUpdate
|
||||
@@ -16,4 +16,4 @@
|
||||
#define networkPlatformRequestDisconnection networkSaturnRequestDisconnection
|
||||
#define networkPlatformGetInfo networkSaturnGetInfo
|
||||
|
||||
typedef networksat_t networkplatform_t;
|
||||
typedef networksaturn_t networkplatform_t;
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} networksat_t;
|
||||
} networksaturn_t;
|
||||
|
||||
errorret_t networkSaturnInit(void);
|
||||
errorret_t networkSaturnUpdate(void);
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/savesaturn.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/savestreamsaturn.c
|
||||
)
|
||||
@@ -6,11 +6,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "save/savesat.h"
|
||||
#include "save/savestreamsat.h"
|
||||
#include "save/savesaturn.h"
|
||||
#include "save/savestreamsaturn.h"
|
||||
|
||||
typedef savesat_t saveplatform_t;
|
||||
typedef savestreamsat_t saveplatformstream_t;
|
||||
typedef savesaturn_t saveplatform_t;
|
||||
typedef savestreamsaturn_t saveplatformstream_t;
|
||||
|
||||
#define saveInitPlatform saveInitSaturn
|
||||
#define saveDisposePlatform saveDisposeSaturn
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savesaturn.h"
|
||||
#include "log/log.h"
|
||||
#include <bup.h>
|
||||
|
||||
static bup_t _bupState;
|
||||
|
||||
errorret_t saveInitSaturn(void) {
|
||||
logDebug("[Saturn] saveInitSaturn\n");
|
||||
bup_devices_t devices;
|
||||
bup_init(&_bupState, &devices);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDisposeSaturn(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteSaturn(const uint8_t slot) {
|
||||
logDebug("[Saturn] saveDeleteSaturn: slot=%u\n", (unsigned)slot);
|
||||
/* TODO: construct filename for slot and call bup_delete:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, slot);
|
||||
* bup_delete(BUP_DEV_INTERNAL, filename);
|
||||
*/
|
||||
errorOk();
|
||||
}
|
||||
@@ -15,13 +15,13 @@
|
||||
* the save file name (e.g. "DUSK00", "DUSK01", …).
|
||||
*/
|
||||
|
||||
#ifndef SAVE_SAT_TITLE_ID
|
||||
#define SAVE_SAT_TITLE_ID "DUSK"
|
||||
#ifndef SAVE_SATURN_TITLE_ID
|
||||
#define SAVE_SATURN_TITLE_ID "DUSK"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t unused;
|
||||
} savesat_t;
|
||||
} savesaturn_t;
|
||||
|
||||
errorret_t saveInitSaturn(void);
|
||||
errorret_t saveDisposeSaturn(void);
|
||||
@@ -6,64 +6,69 @@
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savestreamsat.h"
|
||||
#include "save/savestreamsaturn.h"
|
||||
#include "util/memory.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <bup.h>
|
||||
|
||||
/*
|
||||
* Saturn backup RAM (bup) does not support partial reads/seeks; data must
|
||||
* be read or written as a single contiguous block. We buffer the entire
|
||||
* save slot in heap memory and serialize to/from the bup device on open/close.
|
||||
*
|
||||
* Maximum save size = sizeof(savefile_t). Adjust SAVE_SAT_MAX if needed.
|
||||
* Maximum save size = sizeof(savefile_t). Adjust SAVE_SATURN_MAX if needed.
|
||||
*/
|
||||
|
||||
#define SAVE_SAT_MAX sizeof(savefile_t)
|
||||
#define SAVE_SATURN_MAX sizeof(savefile_t)
|
||||
|
||||
errorret_t saveStreamOpenReadSaturn(
|
||||
savestreamsat_t *p, bool_t *found, const uint8_t slot
|
||||
savestreamsaturn_t *p, bool_t *found, const uint8_t slot
|
||||
) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SAT_MAX);
|
||||
p->buf = (uint8_t *)malloc(SAVE_SATURN_MAX);
|
||||
if(!p->buf) errorThrow("Saturn: failed to allocate save read buffer");
|
||||
p->size = SAVE_SAT_MAX;
|
||||
p->size = SAVE_SATURN_MAX;
|
||||
p->pos = 0;
|
||||
p->slot = slot;
|
||||
p->writing = false;
|
||||
|
||||
/*
|
||||
* TODO: read from bup device into p->buf:
|
||||
* int32_t ret = bup_read(BUP_DEV_INTERNAL, filename, p->buf, SAVE_SAT_MAX);
|
||||
/* TODO: construct filename and read from bup device:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, slot);
|
||||
* int32_t ret = bup_read(BUP_DEV_INTERNAL, filename, p->buf, SAVE_SATURN_MAX);
|
||||
* *found = (ret >= 0);
|
||||
*/
|
||||
*found = false; /* stub: always report no save */
|
||||
*found = false;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsat_t *p, const uint8_t slot) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SAT_MAX);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsaturn_t *p, const uint8_t slot) {
|
||||
p->buf = (uint8_t *)malloc(SAVE_SATURN_MAX);
|
||||
if(!p->buf) errorThrow("Saturn: failed to allocate save write buffer");
|
||||
memoryZero(p->buf, SAVE_SAT_MAX);
|
||||
p->size = SAVE_SAT_MAX;
|
||||
memoryZero(p->buf, SAVE_SATURN_MAX);
|
||||
p->size = SAVE_SATURN_MAX;
|
||||
p->pos = 0;
|
||||
p->slot = slot;
|
||||
p->writing = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveStreamCloseSaturn(savestreamsat_t *p) {
|
||||
void saveStreamCloseSaturn(savestreamsaturn_t *p) {
|
||||
if(p->writing && p->buf) {
|
||||
/*
|
||||
* TODO: write p->buf to bup device:
|
||||
/* TODO: write p->buf to bup device:
|
||||
* char filename[8];
|
||||
* snprintf(filename, sizeof(filename), "%s%02u", SAVE_SATURN_TITLE_ID, p->slot);
|
||||
* bup_dir_t dir;
|
||||
* bup_write(BUP_DEV_INTERNAL, &dir, p->buf, SAVE_SAT_MAX, BUP_MODE_NEW);
|
||||
* memset(&dir, 0, sizeof(dir));
|
||||
* strncpy(dir.filename, filename, sizeof(dir.filename));
|
||||
* bup_write(BUP_DEV_INTERNAL, &dir, p->buf, SAVE_SATURN_MAX, BUP_MODE_NEW);
|
||||
*/
|
||||
}
|
||||
if(p->buf) { free(p->buf); p->buf = NULL; }
|
||||
}
|
||||
|
||||
errorret_t saveStreamReadBytesSaturn(
|
||||
savestreamsat_t *p, void *buf, const size_t len
|
||||
savestreamsaturn_t *p, void *buf, const size_t len
|
||||
) {
|
||||
if(p->pos + len > p->size) errorThrow("Saturn: read past end of save buffer");
|
||||
memoryCopy(buf, p->buf + p->pos, len);
|
||||
@@ -72,7 +77,7 @@ errorret_t saveStreamReadBytesSaturn(
|
||||
}
|
||||
|
||||
errorret_t saveStreamWriteBytesSaturn(
|
||||
savestreamsat_t *p, const void *buf, const size_t len
|
||||
savestreamsaturn_t *p, const void *buf, const size_t len
|
||||
) {
|
||||
if(p->pos + len > p->size) errorThrow("Saturn: write past end of save buffer");
|
||||
memoryCopy(p->buf + p->pos, buf, len);
|
||||
@@ -80,7 +85,7 @@ errorret_t saveStreamWriteBytesSaturn(
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamSeekSaturn(savestreamsat_t *p, const size_t pos) {
|
||||
errorret_t saveStreamSeekSaturn(savestreamsaturn_t *p, const size_t pos) {
|
||||
if(pos > p->size) errorThrow("Saturn: seek out of bounds");
|
||||
p->pos = pos;
|
||||
errorOk();
|
||||
@@ -16,17 +16,17 @@ typedef struct {
|
||||
size_t pos;
|
||||
uint8_t slot;
|
||||
bool_t writing;
|
||||
} savestreamsat_t;
|
||||
} savestreamsaturn_t;
|
||||
|
||||
errorret_t saveStreamOpenReadSaturn(
|
||||
savestreamsat_t *p, bool_t *found, const uint8_t slot
|
||||
savestreamsaturn_t *p, bool_t *found, const uint8_t slot
|
||||
);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsat_t *p, const uint8_t slot);
|
||||
void saveStreamCloseSaturn(savestreamsat_t *p);
|
||||
errorret_t saveStreamOpenWriteSaturn(savestreamsaturn_t *p, const uint8_t slot);
|
||||
void saveStreamCloseSaturn(savestreamsaturn_t *p);
|
||||
errorret_t saveStreamReadBytesSaturn(
|
||||
savestreamsat_t *p, void *buf, const size_t len
|
||||
savestreamsaturn_t *p, void *buf, const size_t len
|
||||
);
|
||||
errorret_t saveStreamWriteBytesSaturn(
|
||||
savestreamsat_t *p, const void *buf, const size_t len
|
||||
savestreamsaturn_t *p, const void *buf, const size_t len
|
||||
);
|
||||
errorret_t saveStreamSeekSaturn(savestreamsat_t *p, const size_t pos);
|
||||
errorret_t saveStreamSeekSaturn(savestreamsaturn_t *p, const size_t pos);
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "moduleplatformsat.h"
|
||||
#include "moduleplatformsaturn.h"
|
||||
|
||||
#define modulePlatformPlatform modulePlatformSaturn
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/systemsaturn.c
|
||||
)
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "system/systemsat.h"
|
||||
#include "system/systemsaturn.h"
|
||||
|
||||
#define systemInitPlatform systemInitSaturn
|
||||
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeSaturn
|
||||
@@ -5,16 +5,14 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "system/systemsat.h"
|
||||
#include "system/systemsaturn.h"
|
||||
#include "log/log.h"
|
||||
#include <smpc/peripheral.h>
|
||||
|
||||
errorret_t systemInitSaturn(void) {
|
||||
logDebug("[Saturn] systemInitSaturn\n");
|
||||
/*
|
||||
* TODO: initialize SMPC peripheral scanning so input reads work.
|
||||
* smpc_peripheral_init();
|
||||
* smpc_peripheral_intback_issue();
|
||||
*/
|
||||
smpc_peripheral_init();
|
||||
smpc_peripheral_intback_issue();
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/networksat.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/timesaturn.c
|
||||
)
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "time/timesat.h"
|
||||
#include "time/timesaturn.h"
|
||||
|
||||
#define timeTickPlatform timeTickSaturn
|
||||
#define timeGetDeltaPlatform timeGetDeltaSaturn
|
||||
#define timeGetRealPlatform timeGetRealSaturn
|
||||
#define timeTickPlatform timeTickSaturn
|
||||
#define timeGetDeltaPlatform timeGetDeltaSaturn
|
||||
#define timeGetRealPlatform timeGetRealSaturn
|
||||
#define timeGetRealTimeZonePlatform timeGetRealTimeZoneSaturn
|
||||
@@ -5,34 +5,33 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "time/timesat.h"
|
||||
#include "time/timesaturn.h"
|
||||
#include <smpc/smc.h>
|
||||
|
||||
#define SAT_FPS 60.0
|
||||
#define SATURN_FPS 60.0
|
||||
|
||||
static double_t satTimeLast = 0.0;
|
||||
static double_t satTimeDelta = 0.0;
|
||||
static double_t satTimeAcc = 0.0; /* accumulated seconds (frame counter) */
|
||||
static double_t saturnTimeLast = 0.0;
|
||||
static double_t saturnTimeDelta = 0.0;
|
||||
static double_t saturnTimeAcc = 0.0; /* accumulated seconds (frame counter) */
|
||||
|
||||
void timeTickSaturn(void) {
|
||||
double_t now = satTimeAcc + (1.0 / SAT_FPS);
|
||||
satTimeDelta = now - satTimeAcc;
|
||||
satTimeLast = satTimeAcc;
|
||||
satTimeAcc = now;
|
||||
double_t now = saturnTimeAcc + (1.0 / SATURN_FPS);
|
||||
saturnTimeDelta = now - saturnTimeAcc;
|
||||
saturnTimeLast = saturnTimeAcc;
|
||||
saturnTimeAcc = now;
|
||||
}
|
||||
|
||||
double_t timeGetDeltaSaturn(void) {
|
||||
return satTimeDelta;
|
||||
return saturnTimeDelta;
|
||||
}
|
||||
|
||||
double_t timeGetRealSaturn(void) {
|
||||
/*
|
||||
* TODO: read the SMPC RTC for actual wall-clock time:
|
||||
/* TODO: read the SMPC RTC for actual wall-clock time:
|
||||
* smpc_rtc_t rtc;
|
||||
* smpc_smc_rtc_read(&rtc);
|
||||
* return rtcToUnixSeconds(&rtc);
|
||||
*/
|
||||
return satTimeAcc;
|
||||
return saturnTimeAcc;
|
||||
}
|
||||
|
||||
double_t timeGetRealTimeZoneSaturn(void) {
|
||||
@@ -25,8 +25,8 @@ void inputUpdateSDL2(void) {
|
||||
|
||||
#ifdef DUSK_INPUT_POINTER
|
||||
case SDL_MOUSEWHEEL:
|
||||
INPUT.platform.scrollX = (float_t)e.wheel.x;
|
||||
INPUT.platform.scrollY = (float_t)e.wheel.y;
|
||||
INPUT.platform.scrollX = e.wheel.x > 0 ? INT16_MAX : (e.wheel.x < 0 ? -INT16_MAX : 0);
|
||||
INPUT.platform.scrollY = e.wheel.y > 0 ? INT16_MAX : (e.wheel.y < 0 ? -INT16_MAX : 0);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -61,16 +61,16 @@ void inputUpdateSDL2(void) {
|
||||
int_t windowWidth, windowHeight;
|
||||
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
|
||||
|
||||
INPUT.platform.mouseX = (float_t)pointerX / (float_t)windowWidth;
|
||||
INPUT.platform.mouseY = (float_t)pointerY / (float_t)windowHeight;
|
||||
INPUT.platform.mouseX = (int16_t)((float_t)pointerX / (float_t)windowWidth * INT16_MAX);
|
||||
INPUT.platform.mouseY = (int16_t)((float_t)pointerY / (float_t)windowHeight * INT16_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
int16_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
switch(button.type) {
|
||||
#ifdef DUSK_INPUT_KEYBOARD
|
||||
case INPUT_BUTTON_TYPE_KEYBOARD: {
|
||||
return INPUT.platform.keyboardState[button.scancode] ? 1.0f : 0.0f;
|
||||
return INPUT.platform.keyboardState[button.scancode] ? INT16_MAX : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -84,7 +84,7 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
return INPUT.platform.mouseY;
|
||||
|
||||
case INPUT_POINTER_AXIS_Z:
|
||||
return 0.0f; // Not supported in SDL2
|
||||
return 0;
|
||||
|
||||
case INPUT_POINTER_AXIS_WHEEL_X:
|
||||
return INPUT.platform.scrollX;
|
||||
@@ -94,7 +94,7 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
|
||||
default:
|
||||
assertUnreachable("Unknown pointer axis");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -106,30 +106,24 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
|
||||
"Gamepad button out of range"
|
||||
);
|
||||
|
||||
if(SDL_GameControllerGetButton(
|
||||
return SDL_GameControllerGetButton(
|
||||
INPUT.platform.controller, button.gpButton
|
||||
)) {
|
||||
return 1.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
) ? INT16_MAX : 0;
|
||||
}
|
||||
|
||||
case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: {
|
||||
float_t value = 0.0f;
|
||||
|
||||
Sint16 axis = SDL_GameControllerGetAxis(
|
||||
Sint16 raw = SDL_GameControllerGetAxis(
|
||||
INPUT.platform.controller, button.gpAxis.axis
|
||||
);
|
||||
value = (float_t)axis / 32767.0f;
|
||||
if(!button.gpAxis.positive) value = -value;
|
||||
value = inputDeadzone(value, inputGetDeadzoneSDL2(button));
|
||||
return value;
|
||||
int16_t value = button.gpAxis.positive ? raw : (int16_t)-raw;
|
||||
if(value < 0) return 0;
|
||||
return inputDeadzone(value, inputGetDeadzoneSDL2(button));
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
assertUnreachable("Unknown input button type");
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ typedef struct {
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_INPUT_POINTER
|
||||
float_t mouseX, mouseY;
|
||||
float_t scrollX, scrollY;
|
||||
int16_t mouseX, mouseY;
|
||||
int16_t scrollX, scrollY;
|
||||
#endif
|
||||
} inputsdl2_t;
|
||||
|
||||
@@ -69,18 +69,17 @@ typedef inputsdl2_t inputplatform_t;
|
||||
void inputUpdateSDL2(void);
|
||||
|
||||
/**
|
||||
* Returns the deadzone for the given gamepad axis. Requires implementation by
|
||||
* the host platform.
|
||||
*
|
||||
* Returns the deadzone for the given gamepad axis (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the deadzone of.
|
||||
* @return The deadzone for the given gamepad axis.
|
||||
* @return The deadzone threshold (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button);
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button);
|
||||
|
||||
/**
|
||||
* Returns the input value (between 0 and 1) of the given button.
|
||||
*
|
||||
* Returns the input value of the given button (0 to INT16_MAX).
|
||||
*
|
||||
* @param button The button to get the value of.
|
||||
* @return The value of the button, between 0 and 1.
|
||||
* @return The value of the button (0 to INT16_MAX).
|
||||
*/
|
||||
float_t inputButtonGetValueSDL2(const inputbutton_t button);
|
||||
int16_t inputButtonGetValueSDL2(const inputbutton_t button);
|
||||
@@ -82,6 +82,6 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
int16_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return (int16_t)(0.17f * INT16_MAX);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user