109 lines
4 KiB
ArmAsm
109 lines
4 KiB
ArmAsm
; crt0 — C runtime startup for the W65816 backend.
|
|
;
|
|
; Entry point invoked by the loader (or the OMF dispatcher). Sets up
|
|
; the processor mode the rest of the runtime expects, zeroes BSS,
|
|
; calls main, and halts on return.
|
|
;
|
|
; Conventions:
|
|
; - Native mode (E=0), 16-bit M and X (REP #$30) on entry to main.
|
|
; - DP=0, DBR=0 — assumed by the C runtime.
|
|
; - Linker-emitted symbols: __bss_start, __bss_end (16-bit addrs).
|
|
|
|
.text
|
|
|
|
.globl __start
|
|
__start:
|
|
; Disable IRQ first — the IIgs ROM hands a vsync IRQ on every frame,
|
|
; and its handler runs in 8-bit M/X mode, corrupting our state if
|
|
; we leave I clear. SEI is fine in either emulation or native
|
|
; mode and is always 1 byte / 2 cycles.
|
|
sei
|
|
; Native mode + 16-bit registers.
|
|
clc
|
|
xce
|
|
rep #0x30
|
|
; Disable IIgs peripheral interrupt sources at the chip level —
|
|
; SEI alone leaves the hardware lines asserted, and the IRQ trap
|
|
; in ROM keeps re-firing if the source isn't quiesced. STZ
|
|
; stores zero without going through A; in M=8 it stores 1 byte
|
|
; (matching the 8-bit registers), so no LDA #0 prelude is needed.
|
|
sep #0x20
|
|
stz 0xc041 ; INTEN = 0 (clear AN3/mouse/0.25s/VBL/mouse-IRQ enables)
|
|
stz 0xc023 ; VGCINT = 0 (clear external/1-sec/scan-line IRQ enables)
|
|
stz 0xc032 ; SCANINT clear
|
|
rep #0x20
|
|
|
|
; Top-of-stack at $0FFF. Native-mode S is 16-bit, so we don't need
|
|
; to stay in page 1. Soft-double frames can be ~170 bytes plus the
|
|
; usual call-chain overhead — at $01FF stack growth wraps into the
|
|
; direct page ($0000-$00FF) which holds our libcall scratch
|
|
; ($E0-$F4) and IMG slots ($D0-$DE), corrupting them. $0FFF gives
|
|
; ~3.5 KB of headroom and stays below the text base ($1000).
|
|
lda #0x0fff
|
|
tcs
|
|
|
|
; Enable Language Card 1 RAM at $D000-$DFFF for read+write.
|
|
; By default the IIgs maps that range to ROM (read-only). Two
|
|
; reads of $C083 enable RAM-bank-1, second read also enables
|
|
; writes. Without this, BSS auto-relocated past $C000 lands on
|
|
; ROM and globals never initialise (writes drop on the floor;
|
|
; reads return ROM bytes). Caught by the expression-parser
|
|
; smoke test (#92) when runtime growth pushed bss past $BFFF.
|
|
; The reads must be 8-bit (one byte at a time) — a 16-bit M
|
|
; read at $C083 would also touch $C084 (a different soft
|
|
; switch), wiping the LC enable we just set.
|
|
sep #0x20
|
|
lda 0xc083
|
|
lda 0xc083
|
|
rep #0x20
|
|
|
|
; Zero BSS. X iterates from __bss_start to __bss_end; each
|
|
; iteration writes one byte of zero at addr X (via DP=0 +
|
|
; offset 0 — which is just X). STZ in M=8 stores 1 byte and
|
|
; doesn't touch A, so we don't need the LDA #0 prelude.
|
|
rep #0x10 ; ensure X is 16-bit
|
|
ldx #__bss_start
|
|
.Lbss_loop:
|
|
cpx #__bss_end
|
|
bcs .Lbss_done ; X >= end -> done
|
|
sep #0x20 ; 8-bit M for 1-byte store
|
|
stz 0x0, x ; *(uint8_t *)X = 0 (DP=0)
|
|
rep #0x20
|
|
inx
|
|
bra .Lbss_loop
|
|
.Lbss_done:
|
|
|
|
; Run static constructors. The linker emits
|
|
; __init_array_start / __init_array_end around the .init_array
|
|
; section; each entry is a 16-bit function pointer. Walk and
|
|
; JSL each via __jsl_indir.
|
|
rep #0x30 ; native, 16-bit M and X
|
|
ldx #__init_array_start
|
|
.Linit_loop:
|
|
cpx #__init_array_end
|
|
bcs .Linit_done
|
|
; __jsl_indir does `JMP (__indirTarget)` — reads a 16-bit ptr
|
|
; from __indirTarget and JMPs there. So __indirTarget must
|
|
; hold the function pointer itself (NOT the address of the
|
|
; init_array slot). Dereference the entry: ($E0)→A.
|
|
stx 0xe0 ; entry addr -> DP scratch
|
|
ldy #0
|
|
lda (0xe0), y ; A = mem[X] (DP-indirect-Y, opcode 0xb1)
|
|
sta __indirTarget ; __indirTarget = function pointer
|
|
phx ; preserve X across the call
|
|
jsl __jsl_indir
|
|
plx
|
|
inx
|
|
inx
|
|
bra .Linit_loop
|
|
.Linit_done:
|
|
|
|
; Call main. Standard W65816 ABI: i16 first arg in A; we pass
|
|
; nothing. After return, A holds the exit code.
|
|
jsl main
|
|
|
|
; Halt via BRK $00. MAME / debuggers catch this as a clean
|
|
; program termination.
|
|
.byte 0x00, 0x00
|
|
|
|
.size __start, . - __start
|