From d660a22c85c54f05b27084947b7622921c572dcf Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Thu, 2 May 2024 16:58:02 +0200 Subject: [PATCH] asset, rdpq, toolchain: initial picolibc support --- src/asset.c | 16 ++++++++ src/rdpq/rdpq_text.c | 12 ++++++ src/system.c | 1 + tools/build-toolchain.sh | 81 +++++++++++++++++++++++++++++++--------- tools/meson-cross.txt | 19 ++++++++++ 5 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 tools/meson-cross.txt diff --git a/src/asset.c b/src/asset.c index 6a77ac2c5f..03f282f859 100644 --- a/src/asset.c +++ b/src/asset.c @@ -227,7 +227,11 @@ static fpos_t seekfn_none(void *c, fpos_t pos, int whence) return -1; } +#if defined(__PICOLIBC__) +static int readfn_none(void *c, char *buf, size_t sz) +#else static int readfn_none(void *c, char *buf, int sz) +#endif { cookie_none_t *cookie = c; assertf(!cookie->seeked, "Cannot seek in file opened via asset_fopen (it might be compressed)"); @@ -251,7 +255,11 @@ typedef struct { uint8_t state[] alignas(8); } cookie_cmp_t; +#if defined(__PICOLIBC__) +static int readfn_cmp(void *c, char *buf, size_t sz) +#else static int readfn_cmp(void *c, char *buf, int sz) +#endif { cookie_cmp_t *cookie = (cookie_cmp_t*)c; assertf(!cookie->seeked, "Cannot seek in file opened via asset_fopen (it might be compressed)"); @@ -334,7 +342,11 @@ FILE *asset_fopen(const char *fn, int *sz) cookie->pos = 0; cookie->seeked = false; if (sz) *sz = header.orig_size; +#if defined(__PICOLIBC__) && defined(TINY_STDIO) + assertf(0, "assets: opening compressed assets not yet supported on picolibc"); +#else return funopen(cookie, readfn_cmp, NULL, seekfn_cmp, closefn_cmp); +#endif } // Not compressed. Return a wrapped FILE* without the seeking capability, @@ -344,10 +356,14 @@ FILE *asset_fopen(const char *fn, int *sz) *sz = ftell(f); } fseek(f, 0, SEEK_SET); +#if defined(__PICOLIBC__) && defined(TINY_STDIO) + return f; +#else cookie_none_t *cookie = malloc(sizeof(cookie_none_t)); cookie->fp = f; cookie->seeked = false; return funopen(cookie, readfn_none, NULL, seekfn_none, closefn_none); +#endif } #endif /* N64 */ diff --git a/src/rdpq/rdpq_text.c b/src/rdpq/rdpq_text.c index 34a7d26474..e4b1de2106 100644 --- a/src/rdpq/rdpq_text.c +++ b/src/rdpq/rdpq_text.c @@ -38,6 +38,17 @@ int rdpq_text_printn(const rdpq_textparms_t *parms, uint8_t initial_font_id, flo int rdpq_text_printf(const rdpq_textparms_t *parms, uint8_t font_id, float x0, float y0, const char *utf8_fmt, ...) { +#if defined(__PICOLIBC__) && defined(TINY_STDIO) + char *buf; + + va_list va; + va_start(va, utf8_fmt); + size_t n = vasprintf(&buf, utf8_fmt, va); + va_end(va); + + int nbytes = rdpq_text_printn(parms, font_id, x0, y0, buf, n); + free(buf); +#else char buf[512]; size_t n = sizeof(buf); @@ -48,6 +59,7 @@ int rdpq_text_printf(const rdpq_textparms_t *parms, uint8_t font_id, float x0, f int nbytes = rdpq_text_printn(parms, font_id, x0, y0, buf2, n); if (buf2 != buf) free(buf2); +#endif return nbytes; } diff --git a/src/system.c b/src/system.c index 6c7b8f6d30..57b08be335 100644 --- a/src/system.c +++ b/src/system.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/tools/build-toolchain.sh b/tools/build-toolchain.sh index 021c8fb274..a9649d9d1f 100755 --- a/tools/build-toolchain.sh +++ b/tools/build-toolchain.sh @@ -22,6 +22,10 @@ N64_BUILD=${N64_BUILD:-""} N64_HOST=${N64_HOST:-""} N64_TARGET=${N64_TARGET:-mips64-elf} +# Toolchain configuration options. +N64_USE_PICOLIBC=${N64_USE_PICOLIBC:-"false"} +N64_USE_PICOLIBC_TINYSTDIO=${N64_USE_PICOLIBC_TINYSTDIO:-"false"} + # Set N64_INST before calling the script to change the default installation directory path INSTALL_PATH="${N64_INST}" # Set PATH for newlib to compile using GCC for MIPS N64 (pass 1) @@ -38,8 +42,9 @@ GCC_CONFIGURE_ARGS=() BINUTILS_V=2.42 GCC_V=13.2.0 NEWLIB_V=4.4.0.20231231 -GMP_V=6.3.0 -MPC_V=1.3.1 +PICOLIBC_V=1.8.6 +GMP_V=6.3.0 +MPC_V=1.3.1 MPFR_V=4.2.1 MAKE_V=${MAKE_V:-""} @@ -104,8 +109,13 @@ test -d "binutils-$BINUTILS_V" || tar -xzf "binutils-$BINUTILS_V.tar.gz" test -f "gcc-$GCC_V.tar.gz" || download "https://ftp.gnu.org/gnu/gcc/gcc-$GCC_V/gcc-$GCC_V.tar.gz" test -d "gcc-$GCC_V" || tar -xzf "gcc-$GCC_V.tar.gz" -test -f "newlib-$NEWLIB_V.tar.gz" || download "https://sourceware.org/pub/newlib/newlib-$NEWLIB_V.tar.gz" -test -d "newlib-$NEWLIB_V" || tar -xzf "newlib-$NEWLIB_V.tar.gz" +if [ "$N64_USE_PICOLIBC" == "true" ]; then + test -f "picolibc-$PICOLIBC_V.tar.xz" || download "https://github.com/picolibc/picolibc/releases/download/$PICOLIBC_V/picolibc-$PICOLIBC_V.tar.xz" + test -d "picolibc-$PICOLIBC_V" || tar -xf "picolibc-$PICOLIBC_V.tar.xz" +else + test -f "newlib-$NEWLIB_V.tar.gz" || download "https://sourceware.org/pub/newlib/newlib-$NEWLIB_V.tar.gz" + test -d "newlib-$NEWLIB_V" || tar -xzf "newlib-$NEWLIB_V.tar.gz" +fi if [ "$GMP_V" != "" ]; then test -f "gmp-$GMP_V.tar.bz2" || download "https://ftp.gnu.org/gnu/gmp/gmp-$GMP_V.tar.bz2" @@ -221,19 +231,56 @@ make all-target-libgcc -j "$JOBS" make install-target-libgcc || sudo make install-target-libgcc || su -c "make install-target-libgcc" popd -# Compile newlib for target. -mkdir -p newlib_compile_target -pushd newlib_compile_target -CFLAGS_FOR_TARGET="-DHAVE_ASSERT_FUNC -O2" ../"newlib-$NEWLIB_V"/configure \ - --prefix="$CROSS_PREFIX" \ - --target="$N64_TARGET" \ - --with-cpu=mips64vr4300 \ - --disable-threads \ - --disable-libssp \ - --disable-werror -make -j "$JOBS" -make install || sudo env PATH="$PATH" make install || su -c "env PATH=\"$PATH\" make install" -popd +if [ "$N64_USE_PICOLIBC" == "true" ]; then + # Compile picolibc for target. + mkdir -p picolibc_compile_target + pushd picolibc_compile_target + meson setup \ + --cross-file=../../meson-cross.txt \ + -Dmultilib=false \ + -Dpicocrt=false \ + -Dpicolib=false \ + -Dsemihost=false \ + -Dspecsdir=none \ + -Dtests=false \ + -Dtinystdio="$N64_USE_PICOLIBC_TINYSTDIO" \ + -Dfast-bufio=true \ + -Dio-long-long=true \ + -Dio-pos-args="$N64_USE_PICOLIBC_TINYSTDIO" \ + -Dio-percent-b=true \ + -Dposix-console=true \ + -Dformat-default=double \ + -Dnewlib-fseek-optimization=true \ + -Dnewlib-fvwrite-in-streamio=true \ + -Dnewlib-io-float=true \ + -Dnewlib-stdio64=false \ + -Dnewlib-unbuf-stream-opt=true \ + -Dnewlib-nano-malloc=false \ + -Dnewlib-multithread=false \ + -Dnewlib-retargetable-locking=false \ + -Dthread-local-storage=false \ + -Dprefix="$CROSS_PREFIX" \ + -Dlibdir=mips64-elf/lib \ + -Dincludedir=mips64-elf/include \ + ../"picolibc-$PICOLIBC_V" + ninja -j "$JOBS" + ninja install || sudo env PATH="$PATH" ninja install || su -c "env PATH=\"$PATH\" ninja install" + popd +else + # Compile newlib for target. + mkdir -p newlib_compile_target + pushd newlib_compile_target + CFLAGS_FOR_TARGET="-DHAVE_ASSERT_FUNC -O2" ../"newlib-$NEWLIB_V"/configure \ + --prefix="$CROSS_PREFIX" \ + --target="$N64_TARGET" \ + --with-cpu=mips64vr4300 \ + --disable-threads \ + --disable-libssp \ + --disable-werror + make -j "$JOBS" + make install || sudo env PATH="$PATH" make install || su -c "env PATH=\"$PATH\" make install" + popd +fi # For a standard cross-compiler, the only thing left is to finish compiling the target libraries # like libstd++. We can continue on the previous GCC build target. diff --git a/tools/meson-cross.txt b/tools/meson-cross.txt new file mode 100644 index 0000000000..26fee89b1b --- /dev/null +++ b/tools/meson-cross.txt @@ -0,0 +1,19 @@ +[binaries] +# Meson 0.53.2 doesn't use any cflags when doing basic compiler tests, +# so we have to add -nostdlib to the compiler configuration itself or +# early compiler tests will fail. This can be removed when picolibc +# requires at least version 0.54.2 of meson. +c = ['mips64-elf-gcc', '-nostdlib'] +ar = 'mips64-elf-ar' +as = 'mips64-elf-as' +nm = 'mips64-elf-nm' +strip = 'mips64-elf-strip' + +[host_machine] +system = 'none' +cpu_family = 'mips64' +cpu = 'mips64vr4300' +endian = 'big' + +[properties] +skip_sanity_check = true