From 905e5880717816e276bdeacd9e158dfcea696731 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:53:33 +0200 Subject: [PATCH 1/3] build: build SDCC 4.6.0 with two mcs51 fixes in the tree Released SDCC 4.5.0 and 4.6.0 miscompile some of our code in the mcs51 register allocator (SDCC bugs 4153 and 4154, fixed in trunk r17025 and r17039). The first make now downloads the 4.6.0 sources, checks their sha256, applies the two fixes from sdcc/ and installs the compiler into toolchain/, named after a checksum of sdcc/, so a changed patch builds a new one. make CC=sdcc still uses an installed compiler. --- .dockerignore | 1 + .gitignore | 1 + Makefile | 15 ++++++++++++--- README.md | 11 ++++++++--- sdcc/0001-mcs51-positionRegs.patch | 15 +++++++++++++++ sdcc/0002-mcs51-spillThis.patch | 24 ++++++++++++++++++++++++ sdcc/build.sh | 26 ++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 sdcc/0001-mcs51-positionRegs.patch create mode 100644 sdcc/0002-mcs51-spillThis.patch create mode 100755 sdcc/build.sh diff --git a/.dockerignore b/.dockerignore index f3bb7aeb..fb0cbbbf 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ .gitattributes .github/ output/ +toolchain/ installer/output/ *.bin html_data.c diff --git a/.gitignore b/.gitignore index 9f544835..e3793b25 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .idea/ output/ output-main/ +toolchain/ html_data.c html_data.h version.h diff --git a/Makefile b/Makefile index 5754ee08..90fbcebf 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,11 @@ DEFAULT_CONFIG_LOCATION = 454656 CONFIG_LOCATION = 458752 HTML_LOCATION = 262144 +SDCC_PREFIX = toolchain/sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1) ifeq ($(origin CC),default) -CC = sdcc +SDCC = $(SDCC_PREFIX)/bin/sdcc +CC = $(SDCC) +ASM = $(SDCC_PREFIX)/bin/sdas8051 endif CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip ASM ?= sdas8051 @@ -72,7 +75,7 @@ all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(SUBDIRS) $(BUILDDIR)/rt bank_size: tools/bank_occupancy.sh "$(MACHINE)" -create_build_dir: +create_build_dir: $(SDCC) mkdir -p "$(BUILDDIR)" mkdir -p "$(BUILDDIR)/uip" mkdir -p "$(BUILDDIR)/httpd" @@ -175,6 +178,12 @@ clean: $(SUBDIRSCLEAN) distclean: $(SUBDIRSCLEAN) -rm -f html_data.c html_data.h $(VERSION_HEADER) -rm -rf $(BUILDDIR) + -rm -rf toolchain + +ifneq ($(SDCC),) +$(SDCC): + sh sdcc/build.sh $(abspath $(SDCC_PREFIX)) +endif $(SUBDIRSCLEAN): $(MAKE) -C $(@:clean=) clean @@ -223,7 +232,7 @@ MACHINE_DEFS := $(sort $(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$( MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS)) .PHONY: machine_check -machine_check: +machine_check: $(SDCC) @mkdir -p $(BUILDDIR)/tmp @set -eo pipefail; \ for MACHINE in $(MACHINE_DEFS); \ diff --git a/README.md b/README.md index 22d9df5d..d7d664d4 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,17 @@ device simulator is provided, which runs entirely under Linux as a local webserv ## (0) Compiling Requirements -Install the following particular build requisites (Debian 12/13), note that Ubuntu 24.04 -still has an older version of sdcc, but you will need sdcc version 4.5 for the code to compile: +Install the following particular build requisites (Debian 12/13): ``` -sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev +sudo apt install make gcc g++ bison flex libboost-dev patch bzip2 curl xxd python-is-python3 libjson-c-dev zlib1g-dev ``` +The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds it into +`toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0 +and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/) +and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since +r17039. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`. +
If using Docker (click to expand) diff --git a/sdcc/0001-mcs51-positionRegs.patch b/sdcc/0001-mcs51-positionRegs.patch new file mode 100644 index 00000000..e71fda96 --- /dev/null +++ b/sdcc/0001-mcs51-positionRegs.patch @@ -0,0 +1,15 @@ +https://sourceforge.net/p/sdcc/bugs/4153/ + +Index: src/mcs51/ralloc.c +=================================================================== +--- src/mcs51/ralloc.c (revision 17011) ++++ src/mcs51/ralloc.c (working copy) +@@ -1208,7 +1208,7 @@ + int change = 0; + + /* if the result has been spilt then cannot share */ +- if (opsym->isspilt) ++ if (result->isspilt || opsym->isspilt) + return 0; + + for (;;) diff --git a/sdcc/0002-mcs51-spillThis.patch b/sdcc/0002-mcs51-spillThis.patch new file mode 100644 index 00000000..0f9c8d40 --- /dev/null +++ b/sdcc/0002-mcs51-spillThis.patch @@ -0,0 +1,24 @@ +https://sourceforge.net/p/sdcc/bugs/4154/ + +Index: src/mcs51/ralloc.c +=================================================================== +--- src/mcs51/ralloc.c (revision 17011) ++++ src/mcs51/ralloc.c (working copy) +@@ -641,6 +641,7 @@ + spillThis (symbol * sym) + { + int i; ++ bool ownsRegs = bitVectBitValue (_G.regAssigned, sym->key); + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ +@@ -658,7 +659,8 @@ + { + if (sym->regs[i]) + { +- freeReg (sym->regs[i]); ++ if (ownsRegs) ++ freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } diff --git a/sdcc/build.sh b/sdcc/build.sh new file mode 100755 index 00000000..a312316e --- /dev/null +++ b/sdcc/build.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -eu +V=4.6.0 +SHA=5fd6a93e5997ce01756868fe35e441095cfb637894a80c262514a634094973b6 +PREFIX=$1 +D=$(cd "$(dirname "$0")" && pwd) +W=$(mktemp -d) +trap 'rm -rf "$W"' EXIT +curl -fL -o "$W/sdcc.tar.bz2" "https://downloads.sourceforge.net/project/sdcc/sdcc/$V/sdcc-src-$V.tar.bz2" +if command -v sha256sum >/dev/null; then + echo "$SHA $W/sdcc.tar.bz2" | sha256sum -c - +else + echo "$SHA $W/sdcc.tar.bz2" | shasum -a 256 -c - +fi +tar -C "$W" -xjf "$W/sdcc.tar.bz2" +cd "$W/sdcc-$V" +for p in "$D"/*.patch; do + patch -p0 < "$p" +done +off="" +for p in z80 z180 r2k r2ka r3ka r4k r5k r6k sm83 tlcs90 ez80 ez80_z80 z80n r800 ds390 ds400 pic14 pic16 hc08 s08 stm8 pdk13 pdk14 pdk15 mos6502 mos65c02 huc6280 f8 f8l; do + off="$off --disable-$p-port" +done +./configure --prefix="$PREFIX" $off --disable-sdcdb --disable-ucsim --disable-non-free +make -j"$(getconf _NPROCESSORS_ONLN)" +make install From 0181276ec695fea4a06651dbfa20af4356b8fd10 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:53:33 +0200 Subject: [PATCH 2/3] ci: build the in-tree SDCC and keep it between runs The workflow and the Dockerfile install what building SDCC needs instead of the distribution's sdcc, and the workflow caches toolchain/ keyed on the contents of sdcc/. --- .github/workflows/build.yml | 12 ++++++++++-- Dockerfile | 9 ++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7097ef48..7d85b74b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,12 @@ jobs: - name: Install dependencies run: | apt update - apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev -y + apt install make gcc g++ bison flex libboost-dev patch bzip2 curl ca-certificates xxd python-is-python3 libjson-c-dev zlib1g-dev -y + - name: Cache the patched SDCC + uses: actions/cache@v4 + with: + path: branch/toolchain + key: sdcc-${{ hashFiles('branch/sdcc/*') }} - name: Run the host-side unit tests run: cd branch && make -C test - name: Check that every machine definition can be compiled @@ -41,7 +46,10 @@ jobs: - name: Make project with the health instrumentation run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1 - name: Make main - run: cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map + run: | + ln -s ../branch/toolchain main/toolchain + PATH="$(echo "$PWD"/branch/toolchain/sdcc-*/bin):$PATH" + cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map - name: Calculate bank growth run: | sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV" diff --git a/Dockerfile b/Dockerfile index 8942883f..c0d24e9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,14 @@ FROM debian:13-slim RUN apt-get update && apt-get install -y \ make \ gcc \ - sdcc \ + g++ \ + bison \ + flex \ + libboost-dev \ + patch \ + bzip2 \ + curl \ + ca-certificates \ xxd \ python3 \ libjson-c-dev \ From 9cc859ad3084d1d6625c6ec7fec7bcdc9130f210 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Tue, 20 Oct 2026 23:37:11 +0200 Subject: [PATCH 3/3] build: carry the sdas fix for SDCC bug 4070 The 8051 assembler sign-extends every value to 24 bits, so the top byte of a __sfr32 address list like 0xa4a5a6a7 comes out as 0xff. The patch keeps 32-bit arithmetic while leaving the 24-bit T line addresses alone, so the relocations are unchanged. Proposed upstream in the bug thread. With it SFR_DATA_U32 can be used again, which saves about 240 bytes of code in REG_SET. --- README.md | 4 +- sdcc/0003-sdas-mcs51-sfr32-valmasks.patch | 130 ++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 sdcc/0003-sdas-mcs51-sfr32-valmasks.patch diff --git a/README.md b/README.md index d7d664d4..25c0a02d 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,9 @@ The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds i `toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0 and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/) and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since -r17039. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`. +r17039. The third patch is for [#4070](https://sourceforge.net/p/sdcc/bugs/4070/), where the +assembler drops the top byte of a `__sfr32` address list; it is proposed upstream and lets +`SFR_DATA_U32` be used again. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`.
If using Docker (click to expand) diff --git a/sdcc/0003-sdas-mcs51-sfr32-valmasks.patch b/sdcc/0003-sdas-mcs51-sfr32-valmasks.patch new file mode 100644 index 00000000..fa9c66ae --- /dev/null +++ b/sdcc/0003-sdas-mcs51-sfr32-valmasks.patch @@ -0,0 +1,130 @@ +https://sourceforge.net/p/sdcc/bugs/4070/ + +Index: sdas/asxxsrc/asexpr.c +=================================================================== +--- sdas/asxxsrc/asexpr.c ++++ sdas/asxxsrc/asexpr.c +@@ -863,18 +863,59 @@ + return((n & s_mask) ? (n | ~v_mask) : (n & v_mask)); + } + +-/*)Function void exprmasks(esp) ++/*)Function void exprmasks(n) + * + * int n T Line Bytes in Address + * + * The function exprmasks() configures the assembler +- * for 16, 24, or 32-Bit Data/Addresses. ++ * for 16, 24, or 32-Bit Data/Addresses. The arithmetic ++ * precision follows the address width unless a port ++ * calls valmasks() afterwards. + * + * local variables: + * none + * + * global variables: + * int a_bytes T Line Bytes in Address ++ * ++ * functions called: ++ * void valmasks() asexpr.c ++ * ++ * side effects: ++ * The address width and arithmetic precision ++ * parameters are set. ++ */ ++ ++void ++exprmasks(int n) ++{ ++ switch(n) { ++ default: ++ n = 2; ++ case 2: ++ case 3: ++ case 4: ++ break; ++ } ++ a_bytes = n; ++ valmasks(n); ++} ++ ++/*)Function void valmasks(n) ++ * ++ * int n Bytes of arithmetic precision ++ * ++ * The function valmasks() configures the 16, 24, or ++ * 32-Bit arithmetic precision independently of the ++ * T Line address width. A port whose addresses fit ++ * in 24 bits but whose symbols may hold 32-bit values ++ * (the 8051 __sfr32 address lists, for example) uses ++ * exprmasks(3) followed by valmasks(4). ++ * ++ * local variables: ++ * none ++ * ++ * global variables: + * a_uint a_mask Address mask + * a_uint s_mask Sign mask + * a_uint v_mask Value mask +@@ -885,16 +926,13 @@ + * side effects: + * The arithmetic precision parameters are set. + */ +- ++ + void +-exprmasks(int n) ++valmasks(int n) + { +- a_bytes = n; +- + #ifdef LONGINT +- switch(a_bytes) { ++ switch(n) { + default: +- a_bytes = 2; + case 2: + a_mask = (a_uint) 0x0000FFFFl; + s_mask = (a_uint) 0x00008000l; +@@ -914,9 +952,8 @@ + break; + } + #else +- switch(a_bytes) { ++ switch(n) { + default: +- a_bytes = 2; + case 2: + a_mask = (a_uint) 0x0000FFFF; + s_mask = (a_uint) 0x00008000; +Index: sdas/asxxsrc/asxxxx.h +=================================================================== +--- sdas/asxxsrc/asxxxx.h ++++ sdas/asxxsrc/asxxxx.h +@@ -1354,6 +1354,7 @@ + extern int digit(int c, int r); + extern int is_digit(int c, int r); + extern void exprmasks(int n); ++extern void valmasks(int n); + extern void expr(struct expr *esp, int n); + extern void binop(int c, struct expr *esp, struct expr *re); + extern int is_abs(struct expr *esp); +Index: sdas/as8051/i51mch.c +=================================================================== +--- sdas/as8051/i51mch.c ++++ sdas/as8051/i51mch.c +@@ -782,9 +782,12 @@ + hilo = 1; + + /* +- * Address Space ++ * Address Space: 24-bit addresses in the T lines, ++ * 32-bit arithmetic so that __sfr32 address lists ++ * like 0xa4a5a6a7 keep their most significant byte. + */ +- exprmasks(3); ++ exprmasks(3); ++ valmasks(4); + + /* + * First time only: