1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
; =============================================================================
; boot.asm - cartridge header, reset vectors, kernel entry & init
; =============================================================================
INCLUDE "include/gbos.inc"
; -----------------------------------------------------------------------------
; RST vectors. We reserve rst $30 as the syscall trap.
; -----------------------------------------------------------------------------
SECTION "rst00", ROM0[$00]
ret
SECTION "rst30_syscall", ROM0[$30]
jp SyscallTrap ; user code does: ld c, SYS_x ; rst $30
; -----------------------------------------------------------------------------
; Interrupt vectors
; -----------------------------------------------------------------------------
SECTION "vblank", ROM0[$40]
jp VBlankISR ; applies the latched SCY (see hram.asm hSCY)
SECTION "stat", ROM0[$48]
reti
SECTION "timer", ROM0[$50]
jp TimerISR ; drives preemptive scheduling
SECTION "serial", ROM0[$58]
reti
SECTION "joypad", ROM0[$60]
reti
; -----------------------------------------------------------------------------
; Entry point. rgbfix writes the Nintendo logo + header over $104-$14F.
; -----------------------------------------------------------------------------
SECTION "entry", ROM0[$100]
nop
jp KernelInit
ds $150 - @ ; reserve $104-$14F for logo + header (rgbfix fills)
; -----------------------------------------------------------------------------
; Kernel init
; -----------------------------------------------------------------------------
SECTION "kinit", ROM0
KernelInit:
di
ld sp, KSTACK_TOP
push af ; boot ROM hands the console model in A/B -
push bc ; park it on the (uncleared) stack for dmesg
; CGB double-speed mode: 8.4 MHz for the whole kernel + userland. The
; PPU/LCD keep their normal rate; the timer and DIV double with the CPU,
; which TimerISR (128 Hz -> 64 Hz toggle) and sys_sleep (DIV halving)
; compensate for. P1 select lines off first, per the hardware procedure.
ldh a, [$FF4D] ; KEY1
bit 7, a ; already fast? (warm reboot)
jr nz, .fast
ld a, $30
ldh [$FF00], a ; P1: no select lines (STOP quirk guard)
ld a, 1
ldh [$FF4D], a ; KEY1.0 = prepare speed switch
stop ; switch: continues in double speed
.fast
; enable cart RAM (MBC5)
ld a, $0A
ld [MBC5_RAM_ENABLE], a
call ClearKernelRAM
pop bc
pop af
ld [wBootA], a
ld a, b
ld [wBootB], a
call CopyHramCode ; move context-switch trampoline into HRAM
call ProcInit ; wipe process table
; point wCurProc at the (zeroed) kernel PCB so disk I/O during boot restores
; RAM bank 0 after each block transfer (wKernelPCB.PROC_RAMB = 0).
ld a, LOW(wKernelPCB)
ld [wCurProc], a
ld a, HIGH(wKernelPCB)
ld [wCurProc+1], a
call pipe_init ; mark all pipes free
call term_init ; LCD console first, so the boot spew is visible
call boot_banner ; dmesg: kernel/console/cart/mem/proc lines
call net_init ; clear the socket table, set our IP (10.0.0.2)
call boot_net
call fs_init ; mount the disk (format on first boot; persists)
call boot_fs
call osk_init ; build the on-screen keyboard (window layer)
call boot_tty
call boot_init ; "init: spawning pid 1"
; --- spawn the init task (pid 1); it fork()s the rest ---
ld hl, TaskInit
ld b, 0 ; cart-RAM bank 0
ld c, 1 ; WRAM u-area bank
call ProcCreate
call SchedInit ; pick first task, set wCurProc
; system tick: hardware timer at 16384 Hz input, TMA=0 -> TIMA overflow
; IRQ at 64 Hz. TimerISR just counts (wTicks); scheduling stays
; cooperative. VBlank is enabled too, but its ISR is equally
; transparent: it only applies the latched SCY (terminal ring-scroll).
xor a
ld [rTIMA], a
ld [rTMA], a
ld a, %00000111 ; enable | 16384 Hz
ld [rTAC], a
xor a
ld [rIF], a
ld a, IEF_TIMER | IEF_VBLANK
ld [rIE], a
ei
; hand control to the first task via a context switch-in
jp SchedRunFirst
; -----------------------------------------------------------------------------
ClearKernelRAM:
; zero the kernel globals region only ($C000-$CBFF). We must NOT touch
; $CC00-$CFFF: the kernel stack (SP=$D000) grows down into it, and our
; own return address lives there while this routine runs.
ld hl, $C000
ld bc, $0C00
.loop
xor a
ld [hl+], a
dec bc
ld a, b
or c
jr nz, .loop
ret
; -----------------------------------------------------------------------------
; VBlankISR - transparent, like TimerISR: applies the latched SCY so terminal
; ring-scrolls land at frame boundaries. SCY is sampled per scanline, so a
; mid-frame write shears the picture (top at the old offset, bottom at the
; new) - exactly the sixel-view glitch this ISR exists to prevent.
; -----------------------------------------------------------------------------
SECTION "vblank_isr", ROM0
VBlankISR:
push af
ldh a, [hSCY]
ldh [$FF42], a ; rSCY
pop af
reti
|