-
Notifications
You must be signed in to change notification settings - Fork 5
/
builder-hex0-x86-stage1.hex2
241 lines (197 loc) · 6.32 KB
/
builder-hex0-x86-stage1.hex2
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
# SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
# SPDX-License-Identifier: MIT
#
# Builder-Hex0 is a small bootable machine image which acts as
# a bootloader using a hex0 compiler. It compiles hex0 code starting
# at sector 2, placing the compiled code at address 0x7E00 and then
# and then jumps to the resulting binary.
#
# hex0 is a "language" for binary encoding in hexadecimal
# with support for comments.
# Functions:
# _start
# read_sector
# read
# compile
#------------------------------------------------------------
# Memory:
# 9FC00 - FFFFF BIOS
# 7C00 - 7E00 MBR/code
# 7A00 - 7BFF sector read buffer
# < 7700 real mode stack
#------------------------------------------------------------
# ------------------------------------------------------------
# Stub Entry Point
#
# boot drive is in dl
#
:_start
# We cannot be sure the registers are initialized to zero so we
# do that first. We far jump in order to set CS.
# Use special encoding of "xor ax, ax" to appease some BIOSes.
33 C0 # xor ax, ax
8E D8 # mov ds, ax
8E C0 # mov es, ax
8E D0 # mov ss, ax
BC 00 77 # mov sp, 0x7700
FC # cld ; clear direction flag
EA $compile 00 00 # jmp far compile
#----------------------------------------
:compile
# Compile hex0 to binary
#
# input:
# ecx = logical sector to read
# dl = boot drive
# this flag is set after the first digit is seen
31 DB # xor bx,bx
BF $stage2_entry # mov di, $stage2_entry
:read_loop
E8 @read # call read
84 C0 # test al, al
74 !finish # jz finish
3C 23 # cmp al, '#'
74 !skip_comment # jz skip_comment
3C 3B # cmp al, ';'
74 !skip_comment # jz skip_comment
3C 66 # cmp al, 'f'
7F !read_loop # jg read_loop
3C 61 # cmp al, 'a'
7C !maybe_upper # jl maybe_upper
# Handle a to f
2C 57 # sub al, 'a'-10 == 87 = 0x57
EB !maybe_store # jmp maybe_store
:maybe_upper
3C 46 # cmp al, 'F'
7F !read_loop # jg read_loop
3C 41 # cmp al, 'A'
7C !maybe_digit # jl maybe_digit
# Handle A to F
2C 37 # sub al, 'A'-10 == 55 = x37
EB !maybe_store # jmp maybe_store
:maybe_digit
3C 39 # cmp al, '9'
7F !read_loop # jg read_loop
3C 30 # cmp al, '0'
7C !read_loop # jl read_loop
# Handle 0 to 9
2C 30 # sub al, '0' == x30
EB !maybe_store # jmp maybe_store
:skip_comment
E8 @read # call read
3C 0A # cmp al, '\n'
75 !skip_comment # jnz skip_comment
EB !read_loop # jmp read_loop
# only store on second digit
:maybe_store
84 DB # test bl, bl
75 !second_digit # jnz second_digit
# If on first digit, record and keep going
#:first_digit
C0 E0 04 # shl al, 4
88 C7 # mov bh, al
FE C3 # inc bl
EB !read_loop # jmp read_loop
# If on second digit, store and clear state
:second_digit
08 C7 # or bh, al
88 F8 # mov al, bh
AA # stosb
31 DB # xor bx, bx
EB !read_loop # jmp read_loop
:finish
E9 @stage2_entry # jmp stage2_entry
#------------------------------------------------------------
:read_sector
# input:
# dl = drive
#
60 # pusha
:read_one_loop
BE 01 00 # mov si, 1
89 36 $num_sectors_bios # mov word ptr [num_sectors_bios], si
B4 42 # mov ah, 0x42 ; rw mode = 42 (read LBA)
BE $addr_packet # mov si, addr_packet ; disk address packet
CD 13 # int 0x13
72 !read_error # jc read_error
8B 3E $num_sectors_bios # mov di, word ptr [num_sectors_bios] ; number of sectors actually read
85 FF # test di, di
74 !read_error # jz read_error
61 # popa
C3 # ret
# Reset disk subsystem on error
:read_error
31 C0 # xor ax, ax
CD 13 # int 0x13
EB !read_one_loop # jmp read_one_loop
#----------------------------------------
:read
53 # push bx
# get current position
BB $starting_lba # mov bx, starting_lba
8B 0F # mov cx, [bx]
8B 47 FA # mov ax, [bx-6]
#end of sector?
3D FF 01 # cmp ax, 0x01ff
74 !read_next_sector # je read_next_sector
#nextchar:
40 # inc ax
EB !getchar # jmp getchar
:read_next_sector
E8 @read_sector # call read_sector
# save new location and offset
41 # inc cx
89 0F # mov [bx], cx
31 C0 # xor ax, ax
:getchar
89 47 FA # mov [bx-6], ax
89 C3 # mov bx, ax
8A 87 00 78 # mov al, [bx+0x7800]
#finish:
5B # pop bx
C3 # ret
#-------------
# initialized data section
:addr_packet
10 00
# This 16-bit field does double duty: outside read_sector, it holds
# the last read offset, while read_sector reuses it to store the
# number of sectors to read, saving 2 bytes of seed.
:num_sectors_bios
FF 01
:dest_offset
00 78
:dest_segment
00 00
:starting_lba
01 00 00 00 00 00 00 00
# padding to fill a 512 byte sector
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
# disk ID - some BIOSes don't tolerate zeros
12 34 56 78
# writeability flag - use 5E5E for read-only
00 00
# partition table - some BIOSes require this
# Type B5 for "Boot5trap"
80 00 02 00 B5 FF FF FF 01 00 00 00 FF FF FF FF
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# This is the DOS/MBR identifier at offset 510:
55 AA
:stage2_entry