fix(c-compile): drop --code-loc 0x100 that overwrote SDCC's crt0 init (Z80)

SDCC's z80 crt0 puts the reset vector at 0x0000 (jp init) and the init stub
(set SP, call _main) at an absolute .org 0x100. Passing `--code-loc 0x100`
relocated the _CODE segment on top of that init stub, so on reset the CPU
jumped into __clock/_exit (rst 0x08 then ret with a garbage stack) and
derailed into NOP land before ever reaching main — every Z80 C program ran
but drove nothing (z80-led-chaser-c compiled yet the LEDs never moved).
Verified via a standalone chip-WASM harness: with the flag the chaser does 0
LED writes; without it, it walks the bit (8 writes). Pairs with the z80-cpu
RAM map now covering 0x8000-0xFFFF so the crt0's SP=0 stack is real RAM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Montero 2026-06-03 20:11:01 +02:00
parent 1d6961d03c
commit e79368a04a
1 changed files with 9 additions and 4 deletions

View File

@ -146,12 +146,17 @@ async def compile_c(source: str, target: CTarget) -> dict:
c_path = tmp / "program.c"
c_path.write_text(source, encoding="utf-8")
# Let SDCC link its default crt0 at 0x0000 — it places a small init
# stub there which sets SP and jumps to _main(). User code lives
# right after the stub (typically <0x80 bytes in).
# Do NOT pass --code-loc. SDCC's z80 crt0 hard-codes the reset vector
# at 0x0000 (`jp init`) and its init stub at `.org 0x100` (sets SP,
# calls _main). Forcing `--code-loc 0x100` placed the relocatable
# _CODE segment ON TOP of that absolute init stub, so reset jumped
# straight into __clock/_exit (rst 0x08 → ret with a garbage stack)
# and every Z80 C program derailed before reaching main — the LEDs
# never moved. Letting SDCC place _CODE after the crt0 header keeps
# the init stub intact. --data-loc 0x8000 matches the z80-cpu chip's
# RAM window (which spans 0x8000-0xFFFF so the crt0's SP=0 stack works).
cmd = [
sdcc, flag,
"--code-loc", "0x0100",
"--data-loc", "0x8000",
"-o", str(tmp / "program.ihx"),
str(c_path),