From 18532c2e13efbc1aa43f0b00b4459831f9414de0 Mon Sep 17 00:00:00 2001 From: aap Date: Wed, 26 Aug 2026 17:53:21 +0200 Subject: [PATCH] build the ps2 platform entirely with freesce: --freesce premake option premake5 gmake --freesce retargets the one ps2 platform at freesce (its own ee-gcc 2.9 + newlib; FREESCE/FREESCE_GCC roots, defaulting to /usr/local/freesce) -- a premake-time choice since the 2.9 and 3.2 C++ ABIs don't link. The tools/freesce wrappers strip premake's gcc-3-style dep flags. demoreel_cd_freesce.elf now links with zero SCE inputs: freesce crt chain, newlib libc/libm, and ctorgen to register the PluginList ctors gcc 2.9 leaves orphaned. Co-Authored-By: Claude Fable 5 --- premake5.lua | 40 +++++++++-- src/ps2/ps2types.h | 18 +++++ tools/demoreel/Makefile | 145 ++++++++++++++++++++++++++++++++++------ tools/freesce/ee-ar | 3 + tools/freesce/ee-g++ | 18 +++++ tools/freesce/ee-gcc | 13 ++++ 6 files changed, 212 insertions(+), 25 deletions(-) create mode 100755 tools/freesce/ee-ar create mode 100755 tools/freesce/ee-g++ create mode 100755 tools/freesce/ee-gcc diff --git a/premake5.lua b/premake5.lua index 2557039..e100f12 100755 --- a/premake5.lua +++ b/premake5.lua @@ -38,6 +38,11 @@ newoption { default = "../SDL3-3.2.22", } +newoption { + trigger = "freesce", + description = "ps2: build against freesce with its own ee-gcc 2.9, instead of the SCE SDK" +} + workspace "librw" location "build" language "C++" @@ -82,10 +87,33 @@ workspace "librw" filter { "platforms:ps2" } defines { "RW_PS2" } toolset "gcc" - gccprefix 'ee-' - buildoptions { "-nostdlib", "-fno-common" } - includedirs { "$(PS2SDK)/ee/include", "$(PS2SDK)/common/include" } optimize "Off" + if _OPTIONS["freesce"] then + -- freesce, by the xtc convention: two roots, two axes. + -- FREESCE is the SDK vintage -- a tree root with + -- ee/include and ee/lib under it, which is how an + -- install and a git worktree are both laid out -- and + -- FREESCE_GCC is the compiler root (an env var read by + -- the tools/freesce wrappers), which is not + -- SDK-versioned and so does not derive from it. Both + -- default to /usr/local/freesce. A premake-time choice + -- because the 2.9 and 3.2 C++ ABIs don't link: one + -- flavor per generated tree. The wrappers also strip + -- premake's gcc-3-style dependency flags, which the + -- 2.9 driver rejects. + gccprefix '../tools/freesce/ee-' + buildoptions { "-fno-common", "-fno-exceptions", "-mno-abicalls", "-G0" } + makesettings [[ +FREESCE ?= /usr/local/freesce +FREESCE_GCC ?= /usr/local/freesce/ee/gcc +export FREESCE_GCC +]] + includedirs { "$(FREESCE)/ee/include" } + else + gccprefix 'ee-' + buildoptions { "-nostdlib", "-fno-common" } + includedirs { "$(PS2SDK)/ee/include", "$(PS2SDK)/common/include" } + end filter { "platforms:*amd64*" } architecture "x86_64" @@ -120,10 +148,14 @@ workspace "librw" Bindir = "bin/%{cfg.platform}/%{cfg.buildcfg}" function vucode() + -- with --freesce, its own dvp-as by its root, not from PATH + local dvpas = _OPTIONS["freesce"] + and '$(or $(FREESCE_GCC),/usr/local/freesce/ee/gcc)/bin/ee-dvp-as' + or 'ee-dvp-as' filter "files:**.dsm" buildmessage 'dvp-as %{file.name}' buildcommands { - 'cpp -x assembler-with-cpp "%{file.abspath}" | ee-dvp-as -I "%{file.directory}" -o "%{cfg.objdir}/%{file.basename}.o"' + 'cpp -x assembler-with-cpp "%{file.abspath}" | ' .. dvpas .. ' -I "%{file.directory}" -o "%{cfg.objdir}/%{file.basename}.o"' } buildoutputs { '%{cfg.objdir}/%{file.basename}.o' } filter {} diff --git a/src/ps2/ps2types.h b/src/ps2/ps2types.h index 6234fa1..cdca60c 100644 --- a/src/ps2/ps2types.h +++ b/src/ps2/ps2types.h @@ -2,15 +2,33 @@ #include +#ifndef __int8_t_defined typedef char int8_t; +#endif +#ifndef __int16_t_defined typedef short int16_t; +#endif +#ifndef __int32_t_defined typedef int int32_t; +#endif +#ifndef __int64_t_defined typedef long long int64_t; +#endif +#ifndef __uint8_t_defined typedef unsigned char uint8_t; +#endif +#ifndef __uint16_t_defined typedef unsigned short uint16_t; +#endif +#ifndef __uint32_t_defined typedef unsigned int uint32_t; +#endif +#ifndef __uint64_t_defined typedef unsigned long long uint64_t; +#endif +#ifndef __uintptr_t_defined typedef unsigned int uintptr_t; +#endif typedef u_long128 uint128_t; typedef union QWord QWord; diff --git a/tools/demoreel/Makefile b/tools/demoreel/Makefile index 9274c91..12a7b61 100644 --- a/tools/demoreel/Makefile +++ b/tools/demoreel/Makefile @@ -1,51 +1,133 @@ -# PS2 build (SCE toolchain). PC builds are generated by premake. -# Build librw for ps2 first: premake5 gmake; make -C build librw config=debug_ps2 +# PS2 build, two flavors: +# - demoreel.elf / demoreel_cd.elf: the real SCE SDK toolchain and +# libraries at /usr/local/sce end to end (i386 execution works on +# this machine; see librwps2/Makefile for the pattern). +# Build librw for it first (with the SCE gcc on PATH so premake's +# ee-g++ resolves to 3.2, matching the C++ ABI of these objects): +# premake5 gmake +# PATH=/usr/local/sce/ee/gcc/bin:$PATH \ +# make -C build librw config=debug_ps2 PS2SDK=/usr/local/sce +# - demoreel_cd_freesce.elf: complete freesce build, no SCE anywhere; +# see the FREESCE_ section below. -CC=ee-gcc -CXX=ee-g++ +CC := /usr/local/sce/ee/gcc/bin/ee-gcc +CXX := /usr/local/sce/ee/gcc/bin/ee-g++ TARGET=demoreel LIBRW := ../.. -OBJDIR := obj/ps2 SRC := main.cpp proctex.cpp scene_tunnel.cpp scene_particles.cpp scene_knot.cpp -SKELSRC := skeleton.cpp ps2.cpp +SKELSRC := skeleton.cpp camera.cpp ps2.cpp SCELIBDIR := /usr/local/sce/ee/lib +GCCLIB := /usr/local/sce/ee/gcc/lib/gcc-lib/ee/3.2-ee-040921 CFLAGS := -Os -DRW_PS2 -fno-common -fno-exceptions LIBS = $(LIBRW)/lib/ps2/Debug/librw.a \ $(SCELIBDIR)/libgraph.a \ $(SCELIBDIR)/libdma.a \ - $(SCELIBDIR)/libmc.a \ $(SCELIBDIR)/libpc.a \ $(SCELIBDIR)/libpad.a \ $(SCELIBDIR)/libcdvd.a \ - $(SCELIBDIR)/libscf.a + $(SCELIBDIR)/libkernl.a -GCCLIB := /usr/local/sce/ee/gcc/lib/gcc-lib/ee/3.2-ee-030926 -CRT_BEGIN := $(OBJDIR)/crt0.o $(GCCLIB)/crti.o $(GCCLIB)/crtbegin.o +CRT_BEGIN := crt0.o $(GCCLIB)/crti.o $(GCCLIB)/crtbegin.o CRT_END := $(GCCLIB)/crtend.o $(GCCLIB)/crtn.o -OBJ := $(addprefix $(OBJDIR)/,$(SRC:.cpp=.o) $(SKELSRC:.cpp=.o)) -DEP := $(OBJ:.o=.d) - INC := -I. \ -I$(LIBRW) \ -I$(LIBRW)/skeleton \ - -I/usr/local/sce/common/include \ - -I/usr/local/sce/ee/include + -I/usr/local/sce/ee/include \ + -I/usr/local/sce/common/include -$(TARGET).elf: $(OBJDIR)/crt0.o $(OBJ) +# Two ELFs: demoreel.elf runs off the host filesystem (host: for pcsx2, +# host0: for the SCE TOOL) for quick iteration. demoreel_cd.elf reboots +# the IOP from cdrom0:\MODULES\IOPRP300.IMG like a real disc boot (see +# SKEL_CDROM in skeleton/ps2.cpp) - only CDROM builds have been known to +# run correctly, so that's the one to test on hardware/dsedb. +all: $(TARGET).elf $(TARGET)_cd.elf + +OBJDIR := obj/ps2/host +OBJ := $(addprefix $(OBJDIR)/,$(SRC:.cpp=.o) $(SKELSRC:.cpp=.o)) +DEP := $(OBJ:.o=.d) + +OBJDIR_CD := obj/ps2/cd +OBJ_CD := $(addprefix $(OBJDIR_CD)/,$(SRC:.cpp=.o) $(SKELSRC:.cpp=.o)) +DEP_CD := $(OBJ_CD:.o=.d) + +$(TARGET).elf: $(OBJ) crt0.o $(CXX) -o $@ $(CRT_BEGIN) $(OBJ) $(LIBS) $(CRT_END) -T $(SCELIBDIR)/app.cmd -L$(SCELIBDIR) -lm -nostartfiles -Wl,--gc-sections +$(TARGET)_cd.elf: $(OBJ_CD) crt0.o + $(CXX) -o $@ $(CRT_BEGIN) $(OBJ_CD) $(LIBS) $(CRT_END) -T $(SCELIBDIR)/app.cmd -L$(SCELIBDIR) -lm -nostartfiles -Wl,--gc-sections + +# CD build against freesce -- a COMPLETE freesce build: freesce's own +# ee-gcc 2.9 toolchain, freesce headers only, freesce's six libraries, +# crt0/crti/crtbegin/crtend/crtn and app.cmd. libc/libm are the +# toolchain's newlib, libgcc its own; there is no libstdc++ (2.9's +# C++ runtime bits live in libgcc). app.cmd deliberately has no +# GROUP(-lc ...) directive, so the runtime libs go inside an explicit +# link group. Nothing comes from /usr/local/sce. +# +# Two roots, because they are two different axes (the xtc convention): +# FREESCE is the SDK vintage -- headers, libraries, crt, app.cmd -- a +# tree root with ee/include and ee/lib under it, which is how an +# install and a git worktree are both laid out. FREESCE_GCC is the +# compiler root; the compiler is not SDK-versioned, so it deliberately +# does not derive from FREESCE (a source worktree has no compiler). +# +# make demoreel_cd_freesce.elf +# make demoreel_cd_freesce.elf FREESCE=~/src/freesce_24 # from a worktree +# +# librw.a for this flavor -- the SCE-vs-freesce choice is made at +# premake time, because the 2.9 and 3.2 C++ ABIs don't link, so one +# generated tree builds one flavor: +# premake5 gmake --freesce; make -C build librw config=debug_ps2 +# (a librw.a generated without --freesce is 3.2-ABI and won't link +# here; regenerate and rebuild) +FREESCE ?= /usr/local/freesce +FREESCE_GCC ?= /usr/local/freesce/ee/gcc +FREESCE_LIB := $(FREESCE)/ee/lib +# ctorgen: gcc 2.9 on the EE emits file-scope ctors as _GLOBAL_$I +# functions but registers none of them; this collects them into .ctors +# entries. Without it librw's global ctors (the PluginList statics) +# silently never run. ee/bin/ctorgen in an install, tools/ctorgen.sh +# in a worktree. +FREESCE_CTORGEN ?= $(firstword $(wildcard $(FREESCE)/ee/bin/ctorgen $(FREESCE)/tools/ctorgen.sh)) +FREESCE_CXX := $(FREESCE_GCC)/bin/ee-g++ +FREESCE_NM := $(FREESCE_GCC)/bin/ee-nm +FREESCE_CFLAGS := -Os -mno-abicalls -G0 -DRW_PS2 -fno-common -fno-exceptions +FREESCE_INC := -I. -I$(LIBRW) -I$(LIBRW)/skeleton -I$(FREESCE)/ee/include +FREESCE_LIBRW := $(LIBRW)/lib/ps2/Debug/librw.a +OBJDIR_CDF := obj/ps2/cdf +OBJ_CDF := $(addprefix $(OBJDIR_CDF)/,$(SRC:.cpp=.o) $(SKELSRC:.cpp=.o)) +FREESCE_LIBS = $(FREESCE_LIB)/libgraph.a $(FREESCE_LIB)/libdma.a $(FREESCE_LIB)/libpc.a \ + $(FREESCE_LIB)/libpad.a $(FREESCE_LIB)/libcdvd.a $(FREESCE_LIB)/libkernl.a + +$(OBJDIR_CDF)/ctorreg.s: $(OBJ_CDF) $(FREESCE_LIBRW) + NM=$(FREESCE_NM) $(FREESCE_CTORGEN) $(OBJ_CDF) $(FREESCE_LIBRW) > $@ + +$(OBJDIR_CDF)/ctorreg.o: $(OBJDIR_CDF)/ctorreg.s + $(FREESCE_CXX) -c $< -o $@ + +# -nostdlib, not -nostartfiles: everything is listed explicitly, and +# the g++ driver must not append its usual -lstdc++ (which freesce's +# gcc 2.9 doesn't have) nor a trailing -lgcc, whose __main.o would +# collide with freesce's own __main in crtbegin.o. +$(TARGET)_cd_freesce.elf: $(OBJ_CDF) $(OBJDIR_CDF)/ctorreg.o $(FREESCE_LIBRW) + $(FREESCE_CXX) -o $@ -nostdlib \ + $(FREESCE_LIB)/crt0.o $(FREESCE_LIB)/crti.o $(FREESCE_LIB)/crtbegin.o \ + $(OBJDIR_CDF)/ctorreg.o $(OBJ_CDF) $(FREESCE_LIBRW) \ + -Wl,--start-group $(FREESCE_LIBS) -lc -lm -lgcc -Wl,--end-group \ + $(FREESCE_LIB)/crtend.o $(FREESCE_LIB)/crtn.o \ + -T $(FREESCE_LIB)/app.cmd -L$(FREESCE_LIB) + run: $(TARGET).elf dsedb -r run $(TARGET).elf -$(OBJDIR)/crt0.o: - @mkdir -p $(@D) +crt0.o: $(SCELIBDIR)/crt0.s $(CC) -c -xassembler-with-cpp -o $@ $(SCELIBDIR)/crt0.s $(OBJDIR)/%.o: %.cpp @@ -56,7 +138,28 @@ $(OBJDIR)/%.o: $(LIBRW)/skeleton/%.cpp @mkdir -p $(@D) $(CXX) $(CFLAGS) $(INC) -MMD -c $< -o $@ -clean: - rm -rf $(OBJDIR) $(TARGET).elf +$(OBJDIR_CD)/%.o: %.cpp + @mkdir -p $(@D) + $(CXX) $(CFLAGS) -DSKEL_CDROM $(INC) -MMD -c $< -o $@ --include $(DEP) +$(OBJDIR_CD)/%.o: $(LIBRW)/skeleton/%.cpp + @mkdir -p $(@D) + $(CXX) $(CFLAGS) -DSKEL_CDROM $(INC) -MMD -c $< -o $@ + +# freesce-flavor CD objects: freesce compiler, freesce headers only +# (they carry the vintage's IOP_IMAGE_FILE -- IOPRP242.IMG -- in +# sifdev.h, exactly like SCE's headers carry theirs); libc headers are +# the toolchain's newlib ones. No -MMD: the 2.9 driver has no -MF and +# drops .d files in the cwd, so this flavor goes without dep tracking. +$(OBJDIR_CDF)/%.o: %.cpp + @mkdir -p $(@D) + $(FREESCE_CXX) $(FREESCE_CFLAGS) -DSKEL_CDROM $(FREESCE_INC) -c $< -o $@ + +$(OBJDIR_CDF)/%.o: $(LIBRW)/skeleton/%.cpp + @mkdir -p $(@D) + $(FREESCE_CXX) $(FREESCE_CFLAGS) -DSKEL_CDROM $(FREESCE_INC) -c $< -o $@ + +clean: + rm -rf obj/ps2 crt0.o $(TARGET).elf $(TARGET)_cd.elf $(TARGET)_cd_freesce.elf + +-include $(DEP) $(DEP_CD) diff --git a/tools/freesce/ee-ar b/tools/freesce/ee-ar new file mode 100755 index 0000000..b0b7a20 --- /dev/null +++ b/tools/freesce/ee-ar @@ -0,0 +1,3 @@ +#!/bin/sh +# premake's gccprefix applies to ar too; just forward +exec "${FREESCE_GCC:-/usr/local/freesce/ee/gcc}/bin/ee-ar" "$@" diff --git a/tools/freesce/ee-g++ b/tools/freesce/ee-g++ new file mode 100755 index 0000000..d1b4bd1 --- /dev/null +++ b/tools/freesce/ee-g++ @@ -0,0 +1,18 @@ +#!/bin/sh +# ee-gcc 2.9 wrapper for the premake gmake build (--freesce): the +# generated rules pass gcc-3-style dependency flags; the 2.9 driver +# treats -MF's argument as a second input file and dies. Strip -MD, +# -MP and -MF , then run the real driver. +# FREESCE_GCC is the compiler root (the xtc convention): the compiler +# is not SDK-versioned, so it does not derive from FREESCE. +skip= +for a in "$@"; do + shift + if [ -n "$skip" ]; then skip=; continue; fi + case "$a" in + -MD|-MP) continue ;; + -MF) skip=1; continue ;; + esac + set -- "$@" "$a" +done +exec "${FREESCE_GCC:-/usr/local/freesce/ee/gcc}/bin/ee-g++" "$@" diff --git a/tools/freesce/ee-gcc b/tools/freesce/ee-gcc new file mode 100755 index 0000000..0e6f01c --- /dev/null +++ b/tools/freesce/ee-gcc @@ -0,0 +1,13 @@ +#!/bin/sh +# see ee-g++ in this directory +skip= +for a in "$@"; do + shift + if [ -n "$skip" ]; then skip=; continue; fi + case "$a" in + -MD|-MP) continue ;; + -MF) skip=1; continue ;; + esac + set -- "$@" "$a" +done +exec "${FREESCE_GCC:-/usr/local/freesce/ee/gcc}/bin/ee-gcc" "$@"