c71e249a4e
First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.
What's in here:
bin/sprinter-cc — driver script invoking SDCC + linker + mkexe
libc/ — Sprinter-specific libc layer over ESTEX/BIOS
(conio, gfx, io, mem, stdio + headers)
runtime/ — crt0 variants (default/small/banked/minimal)
+ heap + bank trampolines
toolchain/ — mkexe (SprintEXE packer, C + tests)
examples/ — 30 demo programs (gfx, file I/O, env, time, …)
lib/Makefile — builds the libc archive (sprinter.lib)
docs/ — converted Sprinter manuals + asm reference samples
third_party/ — solid-c reference compiler dump + sdcc setup script
release_docs/ — packaging / release notes
gitignore overhaul:
• Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
on macOS APFS). Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
• Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
INPUT fixtures, not build outputs.
• Collapse the duplicated SDCC/C/Sdcc sections into one section per
concern (build outputs / vendored / OS-junk).
• Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
111 lines
3.3 KiB
ArmAsm
111 lines
3.3 KiB
ArmAsm
;; ----------------------------------------------------------------------
|
|
;; crt0_minimal.s — Sprinter ESTEX C runtime startup, no argv parsing.
|
|
;;
|
|
;; This is the opt-out variant for programs where every byte counts.
|
|
;; The DEFAULT runtime is runtime/crt0.s which also parses argv from the
|
|
;; ESTEX command line and passes argc/argv to main().
|
|
;;
|
|
;; To use this instead of the default:
|
|
;; crt0.rel: $(PROJ_ROOT)/runtime/crt0_minimal.s
|
|
;; $(SDASZ80) -o $@ $<
|
|
;;
|
|
;; Otherwise identical to crt0.s — saves IX, calls gsinit, calls main
|
|
;; with no arguments, then EXIT via ESTEX $41.
|
|
;; ----------------------------------------------------------------------
|
|
|
|
.module crt0_minimal
|
|
.globl _main
|
|
|
|
;; Linker-emitted symbols for areas (resolved at link time).
|
|
.globl s__INITIALIZER
|
|
.globl l__INITIALIZER
|
|
.globl s__INITIALIZED
|
|
.globl s__BSS
|
|
.globl l__BSS
|
|
|
|
;; =========================================================================
|
|
;; AREA ORDERING — emitted up-front so the linker walks them in this order.
|
|
;; =========================================================================
|
|
.area _HOME
|
|
.area _CODE
|
|
.area _INITIALIZER
|
|
.area _GSINIT
|
|
.area _GSFINAL
|
|
|
|
.area _DATA
|
|
.area _INITIALIZED
|
|
.area _BSEG
|
|
.area _BSS
|
|
.area _HEAP
|
|
|
|
;; =========================================================================
|
|
;; Entry point — first instruction in _CODE, hence at --code-loc (0x4100).
|
|
;; =========================================================================
|
|
.area _CODE
|
|
_start::
|
|
;; ESTEX already set SP from the EXE header, but make it explicit.
|
|
ld sp, #0xBFFE
|
|
|
|
;; Save startup prefix (IX) for argv/exit-code helpers.
|
|
ld (_estex_startup_ix), ix
|
|
|
|
;; Run SDCC global initialisers (concatenated into _GSINIT).
|
|
call gsinit
|
|
|
|
;; Hand off to user main.
|
|
call _main
|
|
|
|
;; SDCC 4.5 __sdcccall(1): int return is in DE, so low byte of main()'s
|
|
;; return is in E. (See memory/sdcc_z80_abi.md.)
|
|
;; main returned — terminate via ESTEX EXIT directly. We do NOT
|
|
;; expose a _exit symbol; programs that need exit() / atexit() must
|
|
;; link libc/io/atexit.c.
|
|
ld a, e
|
|
ld b, a
|
|
ld c, #0x41 ; ESTEX EXIT
|
|
rst #0x10
|
|
1$: halt
|
|
jr 1$
|
|
|
|
;; =========================================================================
|
|
;; Runtime data
|
|
;; =========================================================================
|
|
.area _DATA
|
|
_estex_startup_ix::
|
|
.ds 2
|
|
|
|
;; =========================================================================
|
|
;; gsinit — copy _INITIALIZER -> _INITIALIZED, then zero _BSS.
|
|
;; 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
|
|
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
|
|
ld (hl), #0
|
|
dec bc
|
|
ld a, b
|
|
or a, c
|
|
jr Z, gsinit_done
|
|
ld d, h
|
|
ld e, l
|
|
inc de
|
|
ldir
|
|
gsinit_done:
|
|
|
|
.area _GSFINAL
|
|
ret
|