toolchain: gsinit зануляет _DATA (C-семантика статиков) + sprinter-cc --max-allocs

Все четыре crt0 (default/small/minimal/banked): gsinit теперь зануляет
_DATA и _BSS через общий zero_area, затем копирует _INITIALIZER.
Явные `= 0` у глобалов/статиков больше не нужны (они жгли байты
_INITIALIZER в образе). crt0-приватные переменные, записываемые ДО
gsinit (_estex_startup_ix и др.), перенесены из _DATA в _CODE (RAM).

sprinter-cc: новая опция --max-allocs N → SDCC --max-allocs-per-node
(агрессивнее аллокация регистров, меньше/быстрее код ценой времени
компиляции).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 21:15:42 +03:00
parent e1450ba7b4
commit 961cfb786d
5 changed files with 134 additions and 66 deletions
+31 -15
View File
@@ -27,6 +27,8 @@
.globl s__INITIALIZER
.globl l__INITIALIZER
.globl s__INITIALIZED
.globl s__DATA
.globl l__DATA
.globl s__BSS
.globl l__BSS
@@ -300,10 +302,13 @@ gpn_skip:
;; =========================================================================
;; Runtime data (HOME's _DATA / _BSS)
;; =========================================================================
.area _DATA
.area _CODE
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
;; _CODE — RAM on the Sprinter, always mapped.
_estex_startup_ix::
.ds 2
.area _DATA
_argc::
.ds 2
_argv::
@@ -326,32 +331,43 @@ progname_buf:
.ds 128 ; ESTEX APPINFO app_path target
;; =========================================================================
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS.
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
;; _DATA is zeroed because C guarantees zero-valued statics — this lets
;; code drop explicit `= 0` initializers that burn _INITIALIZER bytes.
;; =========================================================================
.area _GSINIT
gsinit::
ld bc, #l__INITIALIZER
.area _CODE
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
zero_area:
ld a, b
or a, c
jr Z, gsinit_bss
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_bss:
ld bc, #l__BSS
ld a, b
or a, c
jr Z, gsinit_done
ld hl, #s__BSS
ret Z
ld (hl), #0
dec bc
ld a, b
or a, c
jr Z, gsinit_done
ret Z
ld d, h
ld e, l
inc de
ldir
ret
.area _GSINIT
gsinit::
ld hl, #s__DATA
ld bc, #l__DATA
call zero_area
ld hl, #s__BSS
ld bc, #l__BSS
call zero_area
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_done
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_done:
.area _GSFINAL
+32 -15
View File
@@ -35,6 +35,8 @@
.globl s__INITIALIZER
.globl l__INITIALIZER
.globl s__INITIALIZED
.globl s__DATA
.globl l__DATA
.globl s__BSS
.globl l__BSS
@@ -398,7 +400,9 @@ gpn_skip:
;; =========================================================================
;; Runtime data
;; =========================================================================
.area _DATA
.area _CODE
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
;; _CODE — RAM on the Sprinter, always mapped.
_estex_startup_ix::
.ds 2
_estex_file_handle::
@@ -407,6 +411,8 @@ _estex_block_id::
.ds 1
_bank_block_id::
.ds 1
.area _DATA
_argc::
.ds 2
_argv::
@@ -427,32 +433,43 @@ _w2_self_allocated::
.endif
;; =========================================================================
;; gsinit — same as crt0.s (copy INITIALIZER, zero BSS)
;; gsinit — same as crt0.s (zero _DATA + _BSS, copy INITIALIZER)
;; =========================================================================
.area _GSINIT
gsinit::
ld bc, #l__INITIALIZER
.area _CODE
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
zero_area:
ld a, b
or a, c
jr Z, gsinit_bss
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_bss:
ld bc, #l__BSS
ld a, b
or a, c
jr Z, gsinit_done
ld hl, #s__BSS
ret Z
ld (hl), #0
dec bc
ld a, b
or a, c
jr Z, gsinit_done
ret Z
ld d, h
ld e, l
inc de
ldir
ret
.area _GSINIT
gsinit::
;; --- Zero _DATA + _BSS (C guarantees zero-valued statics) ---
ld hl, #s__DATA
ld bc, #l__DATA
call zero_area
ld hl, #s__BSS
ld bc, #l__BSS
call zero_area
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_done
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_done:
.area _GSFINAL
+30 -17
View File
@@ -20,6 +20,8 @@
.globl s__INITIALIZER
.globl l__INITIALIZER
.globl s__INITIALIZED
.globl s__DATA
.globl l__DATA
.globl s__BSS
.globl l__BSS
@@ -70,40 +72,51 @@ _start::
;; =========================================================================
;; Runtime data
;; =========================================================================
.area _DATA
.area _CODE
;; Written BEFORE gsinit runs (gsinit zeroes _DATA), so these live in
;; _CODE — RAM on the Sprinter, always mapped.
_estex_startup_ix::
.ds 2
;; =========================================================================
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS.
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
;; SDCC appends per-unit init code between this label and the ret in _GSFINAL.
;; =========================================================================
.area _GSINIT
gsinit::
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
ld bc, #l__INITIALIZER
.area _CODE
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
zero_area:
ld a, b
or a, c
jr Z, gsinit_bss
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_bss:
;; --- Zero _BSS if non-empty ---
ld bc, #l__BSS
ld a, b
or a, c
jr Z, gsinit_done
ld hl, #s__BSS
ret Z
ld (hl), #0
dec bc
ld a, b
or a, c
jr Z, gsinit_done
ret Z
ld d, h
ld e, l
inc de
ldir
ret
.area _GSINIT
gsinit::
;; --- Zero _DATA + _BSS (C guarantees zero-valued statics) ---
ld hl, #s__DATA
ld bc, #l__DATA
call zero_area
ld hl, #s__BSS
ld bc, #l__BSS
call zero_area
;; --- Copy _INITIALIZER -> _INITIALIZED if non-empty ---
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_done
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_done:
.area _GSFINAL
+35 -19
View File
@@ -38,6 +38,8 @@
.globl s__INITIALIZER
.globl l__INITIALIZER
.globl s__INITIALIZED
.globl s__DATA
.globl l__DATA
.globl s__BSS
.globl l__BSS
@@ -119,11 +121,11 @@ w2_join:
ld sp, #0xBFFE
.ifdef DEBUG_RT
;; Save the self-alloc flag across gsinit (which clobbers BC/DE/HL and
;; zeroes BSS). SP is in W2 now, push is safe.
;; zeroes _DATA/_BSS). SP is in W2 now, push is safe.
push af
.endif
;; ----- Step 5: save IX prefix in its canonical W2 slot. -------------
;; ----- Step 5: save IX prefix in its canonical slot (_CODE, W1). ----
ld (_estex_startup_ix), ix
;; ----- Step 6: standard gsinit + argv flow (identical to crt0.s). --
@@ -336,10 +338,13 @@ gpn_skip:
;; Runtime data — same layout as crt0.s. Lives in DATA/BSS which the
;; linker places in W2 (--data-loc 0x8000).
;; =========================================================================
.area _DATA
.area _CODE
;; _estex_startup_ix is written BEFORE gsinit runs (gsinit zeroes _DATA),
;; so it lives in _CODE — RAM on the Sprinter, always mapped.
_estex_startup_ix::
.ds 2
.area _DATA
_argc::
.ds 2
_argv::
@@ -362,33 +367,44 @@ _w2_self_allocated::
.endif
;; =========================================================================
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS. Identical
;; to crt0.s; runs AFTER W2 is mapped so writes to _INITIALIZED/_BSS land.
;; gsinit — zero _DATA + _BSS, then copy _INITIALIZER -> _INITIALIZED.
;; Identical to crt0.s; runs AFTER W2 is mapped so the writes land.
;; _DATA is zeroed because C guarantees zero-valued statics — initializing
;; `static uint8_t x;` here lets code drop explicit `= 0` initializers
;; that would otherwise burn _INITIALIZER bytes in the EXE image.
;; =========================================================================
.area _GSINIT
gsinit::
ld bc, #l__INITIALIZER
.area _CODE
;; zero_area — fill BC bytes at HL with 0 (BC may be zero).
zero_area:
ld a, b
or a, c
jr Z, gsinit_bss
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_bss:
ld bc, #l__BSS
ld a, b
or a, c
jr Z, gsinit_done
ld hl, #s__BSS
ret Z
ld (hl), #0
dec bc
ld a, b
or a, c
jr Z, gsinit_done
ret Z
ld d, h
ld e, l
inc de
ldir
ret
.area _GSINIT
gsinit::
ld hl, #s__DATA
ld bc, #l__DATA
call zero_area
ld hl, #s__BSS
ld bc, #l__BSS
call zero_area
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_done
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_done:
.area _GSFINAL