Skip to content

Commit 28eb0eb

Browse files
committed
Enable bare-metal cross-compilation support
This supports bare-metal toolchains (e.g., riscv-none-elf-) by adding conditional compilation for POSIX-specific APIs and fixing toolchain selection in the build system.
1 parent cdda0ea commit 28eb0eb

File tree

4 files changed

+305
-44
lines changed

4 files changed

+305
-44
lines changed

backend/headless.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,36 @@
44
* All rights reserved.
55
*/
66

7-
#include <errno.h>
8-
#include <fcntl.h>
9-
#include <signal.h>
7+
/* Detect POSIX environment for shared memory support
8+
* Bare-metal and embedded systems lack sys/mman.h and related POSIX APIs
9+
*/
10+
#if defined(__unix__) || defined(__unix) || defined(unix) || \
11+
(defined(__APPLE__) && defined(__MACH__)) || defined(__linux__) || \
12+
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
13+
defined(__HAIKU__)
14+
#define HAVE_POSIX_SHM 1
15+
#endif
16+
1017
#include <stdio.h>
1118
#include <stdlib.h>
1219
#include <string.h>
20+
#include <twin.h>
21+
22+
#ifdef HAVE_POSIX_SHM
23+
#include <errno.h>
24+
#include <fcntl.h>
25+
#include <signal.h>
1326
#include <sys/mman.h>
1427
#include <sys/stat.h>
1528
#include <sys/time.h>
16-
#include <twin.h>
1729
#include <unistd.h>
18-
1930
#include "headless-shm.h"
31+
#endif
32+
2033
#include "twin_private.h"
2134

35+
#ifdef HAVE_POSIX_SHM
36+
2237
typedef struct {
2338
twin_headless_shm_t *shm;
2439
twin_argb32_t *framebuffer; /* Points to current back buffer */
@@ -361,10 +376,44 @@ twin_context_t *twin_headless_init(int width, int height)
361376
return NULL;
362377
}
363378

379+
#else /* !HAVE_POSIX_SHM - Bare-metal stub implementation */
380+
/* Provides minimal no-op backend for embedded systems without POSIX support */
381+
382+
static void twin_headless_config_dummy(twin_context_t *ctx)
383+
{
384+
(void) ctx;
385+
}
386+
387+
static bool twin_headless_poll_dummy(twin_context_t *ctx)
388+
{
389+
(void) ctx;
390+
return false; /* Immediately exit */
391+
}
392+
393+
static void twin_headless_exit_dummy(twin_context_t *ctx)
394+
{
395+
free(ctx);
396+
}
397+
398+
static twin_context_t *twin_headless_init_dummy(int width, int height)
399+
{
400+
(void) width;
401+
(void) height;
402+
return NULL;
403+
}
404+
#endif /* HAVE_POSIX_SHM */
405+
364406
/* Register the headless backend */
365407
const twin_backend_t g_twin_backend = {
408+
#ifdef HAVE_POSIX_SHM
366409
.init = twin_headless_init,
367410
.configure = twin_headless_configure,
368411
.poll = twin_headless_poll,
369412
.exit = twin_headless_exit,
413+
#else
414+
.init = twin_headless_init_dummy,
415+
.configure = twin_headless_config_dummy,
416+
.poll = twin_headless_poll_dummy,
417+
.exit = twin_headless_exit_dummy,
418+
#endif
370419
};

mk/toolchain.mk

Lines changed: 122 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,115 @@
1+
# Linux Kernel-style Toolchain Configuration
2+
# ============================================
3+
#
4+
# This file implements toolchain detection and configuration following
5+
# Linux kernel conventions. Supports native and cross-compilation builds.
6+
#
7+
# Usage:
8+
# make # Native build with system compiler
9+
# make CROSS_COMPILE=arm-none-eabi- # Cross-compile for ARM bare-metal
10+
# make CC=clang # Use specific compiler
11+
# make CC=emcc # Build for WebAssembly with Emscripten
12+
#
13+
# Reference: https://www.kernel.org/doc/html/latest/kbuild/kbuild.html
14+
15+
# Compiler identification flags
116
CC_IS_CLANG :=
217
CC_IS_GCC :=
18+
CC_IS_EMCC :=
319

4-
# FIXME: Cross-compilation using Clang is not supported.
5-
ifdef CROSS_COMPILE
6-
CC := $(CROSS_COMPILE)gcc
20+
# Detect host architecture for platform-specific optimizations
21+
HOSTARCH := $(shell uname -m)
22+
23+
# Set CC based on CROSS_COMPILE or default compiler
24+
# Check if CC comes from Make's default or command line/environment
25+
ifeq ($(origin CC),default)
26+
# CC is Make's built-in default, override it
27+
ifdef CROSS_COMPILE
28+
CC := $(CROSS_COMPILE)gcc
29+
else
30+
CC := cc
31+
endif
732
endif
833

9-
override CC := $(shell which $(CC))
10-
ifndef CC
11-
$(error "Valid C compiler not found.")
34+
# Resolve CC to absolute path and validate
35+
CC_PATH := $(shell which $(CC) 2>/dev/null)
36+
ifeq ($(CC_PATH),)
37+
$(error Compiler '$(CC)' not found. Please install the toolchain or check your PATH.)
1238
endif
39+
override CC := $(CC_PATH)
1340

14-
ifneq ($(shell $(CC) --version | head -n 1 | grep clang),)
15-
CC_IS_CLANG := 1
16-
else ifneq ($(shell $(CC) --version | grep "Free Software Foundation"),)
17-
CC_IS_GCC := 1
41+
# Compiler type detection (Emscripten, Clang, GCC)
42+
CC_VERSION := $(shell $(CC) --version 2>/dev/null)
43+
ifneq ($(findstring emcc,$(CC_VERSION)),)
44+
CC_IS_EMCC := 1
45+
else ifneq ($(findstring clang,$(CC_VERSION)),)
46+
CC_IS_CLANG := 1
47+
else ifneq ($(findstring Free Software Foundation,$(CC_VERSION)),)
48+
CC_IS_GCC := 1
1849
endif
1950

20-
ifeq ("$(CC_IS_CLANG)$(CC_IS_GCC)", "")
21-
$(warning Unsupported C compiler)
51+
# Validate compiler type
52+
ifeq ($(CC_IS_CLANG)$(CC_IS_GCC)$(CC_IS_EMCC),)
53+
$(warning Unsupported C compiler: $(CC))
2254
endif
2355

56+
# C++ compiler setup
2457
ifndef CXX
25-
CXX := $(CROSS_COMPILE)g++
26-
endif
27-
ifeq ("$(CC_IS_CLANG)", "1")
28-
override CXX := $(subst clang,clang++,$(CC))
58+
ifeq ($(CC_IS_CLANG),1)
59+
override CXX := $(subst clang,clang++,$(CC))
60+
else ifeq ($(CC_IS_EMCC),1)
61+
override CXX := em++
62+
else
63+
CXX := $(CROSS_COMPILE)g++
64+
endif
2965
endif
3066

67+
# Target toolchain configuration
68+
# These tools are used for building the target binaries (potentially cross-compiled)
69+
3170
ifndef CPP
3271
CPP := $(CC) -E
3372
endif
3473

35-
ifndef LD
36-
LD := $(CROSS_COMPILE)ld
37-
endif
38-
39-
ifndef AR
40-
AR := $(CROSS_COMPILE)ar
41-
endif
42-
43-
ifndef RANLIB
44-
RANLIB := $(CROSS_COMPILE)ranlib
45-
endif
46-
47-
ifndef STRIP
48-
STRIP := $(CROSS_COMPILE)strip -sx
49-
endif
50-
51-
ifndef OBJCOPY
52-
OBJCOPY := $(CROSS_COMPILE)objcopy
53-
endif
74+
# Emscripten uses its own toolchain (emar, emranlib, etc.)
75+
# For other compilers, use CROSS_COMPILE prefix
76+
ifeq ($(CC_IS_EMCC),1)
77+
# Emscripten toolchain
78+
ifndef AR
79+
AR := emar
80+
endif
81+
ifndef RANLIB
82+
RANLIB := emranlib
83+
endif
84+
ifndef STRIP
85+
STRIP := emstrip
86+
endif
87+
# Emscripten doesn't use traditional ld/objcopy
88+
LD := emcc
89+
OBJCOPY := true
90+
else
91+
# Traditional GCC/Clang toolchain
92+
# Use $(origin) to detect if variables come from Make's built-in defaults
93+
ifeq ($(origin LD),default)
94+
LD := $(CROSS_COMPILE)ld
95+
endif
96+
ifeq ($(origin AR),default)
97+
AR := $(CROSS_COMPILE)ar
98+
endif
99+
ifeq ($(origin RANLIB),default)
100+
RANLIB := $(CROSS_COMPILE)ranlib
101+
endif
102+
ifndef STRIP
103+
STRIP := $(CROSS_COMPILE)strip -sx
104+
endif
105+
ifndef OBJCOPY
106+
OBJCOPY := $(CROSS_COMPILE)objcopy
107+
endif
108+
endif
109+
110+
# Host toolchain configuration
111+
# These tools are used for building host utilities (always native compilation)
112+
# Useful when cross-compiling: target tools build for embedded, host tools for development machine
54113

55114
ifndef HOSTCC
56115
HOSTCC := $(HOST_COMPILE)gcc
@@ -83,3 +142,31 @@ endif
83142
ifndef HOSTOBJCOPY
84143
HOSTOBJCOPY := $(HOST_COMPILE)objcopy
85144
endif
145+
146+
# Platform-specific compiler optimizations
147+
# Based on target architecture and compiler capabilities
148+
149+
# macOS-specific linker flags
150+
# Xcode 15+ generates warnings about duplicate library options
151+
# Only apply to native macOS builds (not cross-compilation)
152+
ifeq ($(shell uname -s),Darwin)
153+
ifndef CROSS_COMPILE
154+
# Check if ld supports -no_warn_duplicate_libraries
155+
LD_SUPPORTS_NO_WARN := $(shell $(CC) -Wl,-no_warn_duplicate_libraries 2>&1 | grep -q "unknown option" && echo 0 || echo 1)
156+
ifeq ($(LD_SUPPORTS_NO_WARN),1)
157+
LDFLAGS += -Wl,-no_warn_duplicate_libraries
158+
endif
159+
endif
160+
endif
161+
162+
# Toolchain information display
163+
ifdef CROSS_COMPILE
164+
$(info Cross-compiling with: $(CROSS_COMPILE))
165+
$(info Target toolchain: $(CC))
166+
endif
167+
168+
ifeq ($(CC_IS_EMCC),1)
169+
$(info WebAssembly build with Emscripten)
170+
$(info Compiler: $(CC))
171+
$(info Toolchain: emar, emranlib, emstrip)
172+
endif

src/twin_private.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,101 @@ typedef int64_t twin_xfixed_t;
140140
#define twin_dfixed_div(a, b) \
141141
(twin_dfixed_t)((((int64_t) (a)) << 8) / ((int64_t) (b)))
142142

143+
/* 64-bit fixed-point multiplication and division
144+
* These require 128-bit intermediate results for full precision.
145+
*
146+
* For 64-bit platforms with __int128_t support, use native 128-bit arithmetic.
147+
* For 32-bit platforms (RISC-V 32, ARM32, i386), use software implementation.
148+
*/
149+
#if defined(__SIZEOF_INT128__) || \
150+
(defined(__GNUC__) && !defined(__i386__) && \
151+
(defined(__x86_64__) || defined(__aarch64__)))
152+
/* Native 128-bit integer support (x86-64, AArch64, etc.) */
143153
#define twin_xfixed_mul(a, b) \
144154
(twin_xfixed_t)((((__int128_t) (a)) * ((__int128_t) (b))) >> 32)
145155
#define twin_xfixed_div(a, b) \
146156
(twin_xfixed_t)((((__int128_t) (a)) << 32) / ((__int128_t) (b)))
157+
#else
158+
/* Software implementation for 32-bit platforms
159+
* Uses 64x64 -> 128 multiplication split into 32x32 -> 64 parts
160+
*
161+
* For a * b where a and b are 64-bit:
162+
* a = ah << 32 | al
163+
* b = bh << 32 | bl
164+
* a * b = (ah*bh << 64) + (ah*bl << 32) + (al*bh << 32) + (al*bl)
165+
*
166+
* After right shift by 32:
167+
* result = (ah*bh << 32) + (ah*bl) + (al*bh) + (al*bl >> 32)
168+
*/
169+
static inline twin_xfixed_t _twin_xfixed_mul_32bit(twin_xfixed_t a,
170+
twin_xfixed_t b)
171+
{
172+
/* Handle sign */
173+
int neg = ((a < 0) != (b < 0));
174+
uint64_t ua = (a < 0) ? -a : a;
175+
uint64_t ub = (b < 0) ? -b : b;
176+
177+
/* Split into high and low 32-bit parts */
178+
uint64_t ah = ua >> 32;
179+
uint64_t al = ua & 0xFFFFFFFFULL;
180+
uint64_t bh = ub >> 32;
181+
uint64_t bl = ub & 0xFFFFFFFFULL;
182+
183+
/* Compute partial products */
184+
uint64_t p_hh = ah * bh, p_hl = ah * bl;
185+
uint64_t p_lh = al * bh, p_ll = al * bl;
186+
187+
/* Combine with appropriate shifts, handling carry correctly
188+
* Result = (p_hh << 32) + p_hl + p_lh + (p_ll >> 32)
189+
* Split middle terms to prevent overflow: add low 32 bits first,
190+
* then high 32 bits with carries
191+
*/
192+
uint64_t mid_low =
193+
(p_hl & 0xFFFFFFFFULL) + (p_lh & 0xFFFFFFFFULL) + (p_ll >> 32);
194+
uint64_t mid_high = (p_hl >> 32) + (p_lh >> 32) + (mid_low >> 32);
195+
uint64_t result =
196+
(p_hh << 32) + (mid_high << 32) + (mid_low & 0xFFFFFFFFULL);
197+
198+
return neg ? -(int64_t) result : (int64_t) result;
199+
}
200+
201+
static inline twin_xfixed_t _twin_xfixed_div_32bit(twin_xfixed_t a,
202+
twin_xfixed_t b)
203+
{
204+
/* Handle sign */
205+
int neg = ((a < 0) != (b < 0));
206+
uint64_t ua = (a < 0) ? -a : a;
207+
uint64_t ub = (b < 0) ? -b : b;
208+
209+
/* Fixed-point division: (a / b) in Q32.32 = (a << 32) / b in integer
210+
* arithmetic Since ua is 64-bit, (ua << 32) is 96-bit. Perform long
211+
* division: Split ua into 32-bit parts: ua = (ah << 32) | al Then (ua <<
212+
* 32) = (ah << 64) | (al << 32)
213+
*
214+
* Step 1: Divide high part (ah << 32) by ub to get high 32 bits of quotient
215+
* Step 2: Divide ((remainder << 32) | al) by ub to get low 32 bits
216+
*/
217+
uint64_t ah = ua >> 32; /* High 32 bits */
218+
uint64_t al = ua & 0xFFFFFFFFULL; /* Low 32 bits */
219+
220+
/* Divide (ah << 32) / ub */
221+
uint64_t ah_shifted = ah << 32;
222+
uint64_t q_h = ah_shifted / ub;
223+
uint64_t rem = ah_shifted % ub;
224+
225+
/* Divide ((rem << 32) | al) / ub */
226+
uint64_t dividend_low = (rem << 32) | al;
227+
uint64_t q_l = dividend_low / ub;
228+
229+
/* Combine quotients: result = (q_h << 32) | q_l */
230+
uint64_t result = (q_h << 32) | q_l;
231+
232+
return neg ? -(int64_t) result : (int64_t) result;
233+
}
234+
235+
#define twin_xfixed_mul(a, b) _twin_xfixed_mul_32bit(a, b)
236+
#define twin_xfixed_div(a, b) _twin_xfixed_div_32bit(a, b)
237+
#endif
147238

148239
/*
149240
* 'double' is a no-no in any shipping code, but useful during
@@ -432,6 +523,9 @@ twin_time_t _twin_timeout_delay(void);
432523

433524
void _twin_run_work(void);
434525

526+
/* Event dispatch - internal use only */
527+
bool twin_dispatch_once(twin_context_t *ctx);
528+
435529
void _twin_box_init(twin_box_t *box,
436530
twin_box_t *parent,
437531
twin_window_t *window,
@@ -514,6 +608,9 @@ typedef struct twin_backend {
514608

515609
bool (*poll)(twin_context_t *ctx);
516610

611+
/* Start the main loop with application initialization callback */
612+
void (*start)(twin_context_t *ctx, void (*init_callback)(twin_context_t *));
613+
517614
/* Device cleanup when drawing is done */
518615
void (*exit)(twin_context_t *ctx);
519616
} twin_backend_t;

0 commit comments

Comments
 (0)