// rt/start.s — process entry. Linux sets up the stack so that on // entry [SP] holds argc, [SP+8] starts argv (NULL-terminated array // of *u8), [SP+8+(argc+1)*8] starts envp (also NULL-terminated). // // We pull argc into DI and the argv pointer into SI, then jump to // `main`. ww programs that declare `fn main(argc: i32, argv: **u8) // i32` see them; programs declaring `fn main() i32` simply ignore // the regs. Either way, main's return value is fed to the exit // syscall. // // envp is captured into the rt_envp_slot DATAW cell before CALL main // so lib/os.getenv (via the rt_envp getter below) can walk it. We // compute envp = SP + 16 + argc*8 in AX using three doublings and // adds — w6a's syntax doesn't ship SIB-style indexed addressing // (no `LEAQ 16(SP)(DI*8), AX`), so the explicit shift-by-three is // the portable form. AX/DI/SI are scratch on entry; no callee- // saved discipline applies until we reach main. TEXT _start,$0 MOVQ (SP), DI // argc → DI (1st arg to main) LEAQ 8(SP), SI // &argv[0] → SI (2nd arg to main) MOVQ DI, AX // AX = argc ADDQ AX, AX // AX = argc * 2 ADDQ AX, AX // AX = argc * 4 ADDQ AX, AX // AX = argc * 8 ADDQ SP, AX // AX = SP + argc*8 ADDQ $16, AX // AX = SP + argc*8 + 16 = &argv[argc+1] = envp MOVQ AX, rt_envp_slot(SB) // argc/argv survive the envp calc (only AX was clobbered): stash both // so lib/os.args can rebuild the []str view after entry. Same DATAW- // slot + TEXT-getter shape as rt_envp below (task #17 fnmatch filter). MOVQ DI, rt_argc_slot(SB) MOVQ SI, rt_argv_slot(SB) CALL main(SB) MOVQ AX, DI MOVQ $60, AX SYSCALL // rt_envp — getter that returns the envp pointer captured at process // entry. Bound from lib/os via `@symbol("rt_envp") fn rtenvp() **u8;`, // matching the rt_syscall / rt_malloc / rt_abort pattern. Read-only // view of the kernel-supplied table; the bytes live for the process // lifetime. A future setenv that grows the table re-points the slot // (separate task). TEXT rt_envp,$0 MOVQ rt_envp_slot(SB), AX RET // rt_envp_slot — 8-byte writable cell holding the envp pointer. // Initialised to zero in .data; _start overwrites it before // transferring control to main. DATAW gives us the writable .data // slot (w6a has no GLOBL; same shape as lib/log's silent/default/ // global cells in lib/log/log.s). DATAW rt_envp_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00" // rt_argc — getter returning the process argc captured at entry. Bound // from lib/os as `@symbol("rt_argc") fn rtargc() i64;` (rt_envp twin). TEXT rt_argc,$0 MOVQ rt_argc_slot(SB), AX RET // rt_argv — getter returning &argv[0], a NUL-terminated table of *u8 // argument strings. Bound from lib/os as // `@symbol("rt_argv") fn rtargv() **u8;`. TEXT rt_argv,$0 MOVQ rt_argv_slot(SB), AX RET // rt_argc_slot / rt_argv_slot — 8-byte writable cells, zero in .data, // overwritten by _start before CALL main. Same shape as rt_envp_slot. DATAW rt_argc_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00" DATAW rt_argv_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00"