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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
; =============================================================================
; dmesg.asm - kernel boot spew: everything the kernel knows implicitly before
; init spawns, printed dmesg-style on the LCD console (and mirrored over the
; link port, so a gbhub logs the whole boot).
;
; The info costs nothing to know:
; - the boot ROM hands the console model in A/B at entry (saved to wBootA/B)
; - the cartridge describes itself in its own header ($0147-$0149)
; - the CGB memory map is a constant of the hardware
; - subsystem shapes (proc slots, sockets, program table) are link-time facts
; - fs_init just mounted (or formatted) the disk and left the block bitmap
; in fs_bmbuf - counting free blocks is a 16-byte popcount
; =============================================================================
INCLUDE "include/gbos.inc"
SECTION "dmesg", ROM0
; -----------------------------------------------------------------------------
; kernel console output (boot-time: no process context, so KPutc is unusable)
; -----------------------------------------------------------------------------
; kputc(A = char) - LCD terminal + serial mirror. Preserves BC/DE/HL.
kputc::
push bc
push de
push hl
ld b, a
ld [rSB], a ; mirror to the link (gbhub console log)
ld a, $81
ld [rSC], a
ld a, b
call term_putc
pop hl
pop de
pop bc
ret
; kputs(HL = NUL-terminated string). Clobbers A, HL.
kputs::
.l
ld a, [hl+]
or a
ret z
call kputc
jr .l
; kputhex(A = byte) - two uppercase hex digits. Clobbers A.
kputhex::
push af
swap a
call .nib
pop af
.nib
and $0F
add "0"
cp "9" + 1
jr c, .put
add "A" - "9" - 1
.put
jp kputc
; kputdec(A = 0..255) - decimal, no leading zeros. Clobbers A, BC, DE.
kputdec::
ld c, 0 ; "printed a digit yet" flag
ld b, 100
call .digit
ld b, 10
call .digit
add "0"
jp kputc ; ones digit always prints
.digit ; A/B -> A = remainder; prints quotient digit
ld e, 0
.sub
cp b
jr c, .rem
sub b
inc e
jr .sub
.rem
ld d, a
ld a, e
or c
jr z, .skip ; leading zero
ld a, e
add "0"
call kputc
ld c, 1
.skip
ld a, d
ret
; -----------------------------------------------------------------------------
; boot_banner - kernel line + console model + cart header + memory map + proc
; shape. Called right after term_init.
; -----------------------------------------------------------------------------
boot_banner::
ld hl, .s_kernel
call kputs
; console model: boot ROM leaves A=$11 (CGB/AGB, B bit0 = AGB), $01 (DMG),
; $FF (MGB/pocket)
ld hl, .s_console
call kputs
ld a, [wBootA]
cp $11
jr nz, .not_cgb
ld a, [wBootB]
bit 0, a
jr z, .cgb
ld hl, .s_agb
jr .model
.cgb
ld hl, .s_cgb
jr .model
.not_cgb
cp $01
jr nz, .not_dmg
ld hl, .s_dmg
jr .model
.not_dmg
ld hl, .s_mgb
.model
call kputs
ld hl, .s_boota
call kputs
ld a, [wBootA]
call kputhex
ld a, ")"
call kputc
ld a, 10
call kputc
; cart: the header describes the very cartridge we run from
ld hl, .s_cart
call kputs
ld a, [$0147] ; cart type
cp $19
jr c, .rawtype
cp $1F
jr nc, .rawtype
ld hl, .s_mbc5
call kputs
jr .romsize
.rawtype
ld hl, .s_type
call kputs
ld a, [$0147]
call kputhex
.romsize
ld hl, .s_rom
call kputs
ld a, [$0148] ; ROM size code: 32K << code
cp 9
jr nc, .romq
add a ; *2: word index into the pointer table
ld e, a
ld d, 0
ld hl, .romtab
add hl, de
ld a, [hl+]
ld h, [hl]
ld l, a
jr .romp
.romq
ld hl, .s_unk
.romp
call kputs
ld hl, .s_sram
call kputs
ld a, [$0149] ; cart RAM size code
cp 6
jr nc, .ramq
add a
ld e, a
ld d, 0
ld hl, .ramtab
add hl, de
ld a, [hl+]
ld h, [hl]
ld l, a
jr .ramp
.ramq
ld hl, .s_unk
.ramp
call kputs
ld hl, .s_sram2
call kputs
; memory map: hardware constants of the CGB
ld hl, .s_mem
call kputs
; processes + programs: link-time shape of the kernel
ld hl, .s_proc
call kputs
ld hl, ProgramTableEnd - ProgramTable ; bytes; 4 per entry
ld a, l
srl h
rra
srl h
rra ; /4 (table < 1K, fits in A)
call kputdec
ld hl, .s_progs
call kputs
ret
.s_kernel: db "gbos sm83 microkernel\n", 0
.s_console: db "console: ", 0
.s_cgb: db "CGB", 0
.s_agb: db "AGB", 0
.s_dmg: db "DMG", 0
.s_mgb: db "MGB", 0
.s_boota: db " (boot a=$", 0
.s_cart: db "cart: ", 0
.s_mbc5: db "mbc5", 0
.s_type: db "type $", 0
.s_rom: db ", ", 0
.s_sram: db " rom, ", 0
.s_sram2: db " sram\n", 0
.s_unk: db "?", 0
.s_mem: db "mem: 32K wram 16K vram 127B hram\n", 0
.s_proc: db "proc: {d:MAX_PROCS} slots, ", 0
.s_progs: db " programs\n", 0
.romtab: dw .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8
.r0: db "32K",0
.r1: db "64K",0
.r2: db "128K",0
.r3: db "256K",0
.r4: db "512K",0
.r5: db "1M",0
.r6: db "2M",0
.r7: db "4M",0
.r8: db "8M",0
.ramtab: dw .m0, .m1, .m2, .m3, .m4, .m5
.m0: db "no",0
.m1: db "2K",0
.m2: db "8K",0
.m3: db "32K",0
.m4: db "128K",0
.m5: db "64K",0
; -----------------------------------------------------------------------------
; boot_net - after net_init: the link is the network.
; -----------------------------------------------------------------------------
boot_net::
ld hl, .s
call kputs
ret
.s: db "net: slip on link port, {d:MAX_SOCKS} sockets\n", 0
; -----------------------------------------------------------------------------
; boot_fs - after fs_init: mounted vs freshly formatted, and free blocks
; (popcount of clear bits in the block bitmap fs_init left in fs_bmbuf).
; -----------------------------------------------------------------------------
boot_fs::
ld hl, .s_fs
call kputs
ld a, [wBootFsFresh]
or a
jr z, .mounted
ld hl, .s_fresh
call kputs
jr .count
.mounted
ld hl, .s_mount
call kputs
.count
ld hl, fs_bmbuf
ld b, FS_NBLOCKS / 8
ld c, 0 ; free-block count
.byte
ld a, [hl+]
cpl ; set bits = free blocks
ld e, 8
.bit
rra
jr nc, .nofree
inc c
.nofree
dec e
jr nz, .bit
dec b
jr nz, .byte
ld a, c
call kputdec
ld hl, .s_of
call kputs
ld a, FS_NBLOCKS
call kputdec
ld hl, .s_blkfree
call kputs
ret
.s_fs: db "fs: gbfs v{d:FS_VERSION} ", 0
.s_mount: db "mounted, ", 0
.s_fresh: db "formatted, ", 0
.s_of: db "/", 0
.s_blkfree: db " blk free\n", 0
; -----------------------------------------------------------------------------
; boot_tty / boot_init - console shape, and the handoff line.
; -----------------------------------------------------------------------------
boot_tty::
ld hl, .s
call kputs
ret
.s: db "tty: 40x18 console, SELECT = osk\n", 0
boot_init::
ld hl, .s
call kputs
ret
.s: db "init: spawning pid 1\n", 0
|