-
Notifications
You must be signed in to change notification settings - Fork 1
/
bootsect.S
383 lines (325 loc) · 8.03 KB
/
bootsect.S
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/*
* bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
*
* bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
* itself out of the way to address 0x90000, and jumps there.
*
* It then loads 'setup' directly after itself (0x90200), and the system
* at 0x10000, using BIOS interrupts.
*
* The loader has been made as simple as possible, and continuos
* read errors will result in a unbreakable loop. Reboot by hand. It
* loads pretty fast by getting whole tracks at a time whenever possible.
*
* 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.
*/
#include "defs.h"
ROOT_DEV = 0
.code16
.section ".bootsect", "ax", @progbits
_boot:
# ld86 requires an entry symbol. This may as well be the usual one.
.globl _main
_main:
movw $BOOTSEG, %ax
movw %ax, %ds
movw $INITSEG, %ax
movw %ax, %es
movw $256, %cx
subw %si, %si
subw %di, %di
cld
rep
movsw
ljmp $INITSEG, $go - _boot
go:
movw %cs, %ax
movw $(0x4000-12), %dx # 0x4000 is arbitrary value >= length of
# bootsect + length of setup + room for stack
# 12 is disk parm size
# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
# wouldn't have to worry about this if we checked the top of memory. Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table. The old stack might have clobbered the
# drive table.
movw %ax, %ds
movw %ax, %es
movw %ax, %ss # put stack at INITSEG:0x4000-12.
movw %dx, %sp
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 18 - the most we will encounter on an HD 1.44.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs = parameter table segment
*/
pushw $0
popw %fs
movw $0x78, %bx # fs:bx is parameter table address
lgs %fs:(%bx),%si # gs:si is source
movw %dx, %di # es:di is destination
movw $6, %cx # copy 12 bytes
cld
rep movsw %gs:(%si), (%di)
movw %dx, %di
movb $18, 4(%di) # patch sector count
movw %di, %fs:(%bx)
movw %es, %fs:2(%bx)
movw %cs, %ax
movw %ax, %fs
movw %ax, %gs
xorb %ah, %ah # reset FDC
xorb %dl, %dl
int $0x13
# load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.
load_setup:
xorw %dx, %dx # drive 0, head 0
movw $0x0002, %cx # sector 2, track 0
movw $0x0200, %bx # address = 512, in INITSEG
movw $(0x0200 + SETUPSECS), %ax # service 2, nr of sectors
# (assume all on head 0, track 0)
int $0x13 # read it
jnc ok_load_setup # ok - continue
pushw %ax # dump error code
call print_nl
movw %sp, %bp
call print_hex
popw %ax
xorb %dl, %dl # reset FDC
xorb %ah, %ah
int $0x13
jmp load_setup
ok_load_setup:
# Get disk drive parameters, specifically nr of sectors/track
/* It seems that there is no BIOS call to get the number of sectors. Guess
* 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
* Otherwise guess 9
*/
xorw %dx, %dx # drive 0, head 0
movw $0x0012, %cx # sector 18, track 0
movw $(0x200+(SETUPSECS*0x200)), %bx # address after setup (es = cs)
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x0f, %cl # sector 15
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x09, %cl
got_sectors:
movw %cx, %cs:sectors - _boot
movw $INITSEG, %ax
movw %ax, %es
# Print some inane message
movb $0x03, %ah # read cursor pos
xorb %bh, %bh
int $0x10
movw $9, %cx
movw $0x0007, %bx # page 0, attribute 7 (normal)
movw $msg1 - _boot, %bp
movw $0x1301, %ax # write string, move cursor
int $0x10
# ok, we've written the message, now
# we want to load the system (at 0x10000)
movw $TSTLOAD, %ax
movw %ax, %es # segment of 0x010000
call read_it
call kill_motor
call turnoffcursor
call print_nl
# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:
ljmp $SETUPSEG,$0
# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
#
# in: es - starting address segment (normally 0x1000)
#
sread: .word 1+SETUPSECS # sectors read of current track
head: .word 0 # current head
track: .word 0 # current track
read_it:
movw %es, %ax
testw $0x0fff, %ax
die:
jne die # es must be at 64kB boundary
xorw %bx,%bx # bx is starting address within segment
rp_read:
movw %es, %ax
subw $TSTLOAD, %ax # have we loaded all yet?
cmpw syssize - _boot, %ax
jbe ok1_read
ret
ok1_read:
movw %cs:sectors - _boot, %ax
subw sread - _boot, %ax
movw %ax, %cx
shlw $9, %cx
addw %bx, %cx
jnc ok2_read
je ok2_read
xorw %ax, %ax
subw %bx, %ax
shrw $9, %ax
ok2_read:
call read_track
movw %ax, %cx
add sread - _boot, %ax
cmpw %cs:sectors - _boot, %ax
jne ok3_read
movw $1, %ax
subw head - _boot, %ax
jne ok4_read
incw track - _boot
ok4_read:
movw %ax, head - _boot
xorw %ax, %ax
ok3_read:
movw %ax, sread - _boot
shlw $9, %cx
addw %cx, %bx
jnc rp_read
movw %es, %ax
addb $0x10, %ah
movw %ax, %es
xorw %bx, %bx
jmp rp_read
read_track:
pusha
pusha
movw $0xe2e, %ax # loading... message 2e = .
movw $7, %bx
int $0x10
popa
movw track - _boot, %dx
movw sread - _boot, %cx
incw %cx
movb %dl, %ch
movw head - _boot, %dx
movb %dl, %dh
andw $0x0100, %dx
movb $2, %ah
pushw %dx # save for error dump
pushw %cx
pushw %bx
pushw %ax
int $0x13
jc bad_rt
addw $8, %sp
popa
ret
bad_rt:
pushw %ax # save error code
call print_all # ah = error, al = read
xorb %ah, %ah
xorb %dl, %dl
int $0x13
addw $10, %sp
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
movw $5, %cx # error code + 4 registers
movw %sp, %bp
print_loop:
pushw %cx # save count left
call print_nl # nl for readability
cmpb 5, %cl # see if register name is needed
jae no_reg
movw $(0xe05 + 'A' - 1), %ax
subb %cl, %al
int $0x10
movb $'X', %al
int $0x10
movb $':', %al
int $0x10
no_reg:
addw $2, %bp # next register
call print_hex # print it
popw %cx
loop print_loop
ret
print_nl:
movw $0xe0d, %ax # CR
int $0x10
movb $0x0a, %al # LF
int $0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
movw $4, %cx # 4 hex digits
movw (%bp), %dx # load word into dx
print_digit:
rolw $4, %dx # rotate so that lowest 4 bits are used
movb $0xe, %ah
movb %dl, %al # mask off so we have only next nibble
andb $0xf, %al
addb $'0', %al # convert to 0-based digit
cmpb $'9', %al # check for overflow
jbe good_digit
addb $('A' - '0' - 10), %al
good_digit:
int $0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
pushw %dx
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
popw %dx
ret
turnoffcursor:
movb $0x01, %ah # turn off the cursor
movb $0x00, %bh
movw $0x2000, %cx
int $0x10
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading"
.org 497
setup_sects:
.byte SETUPSECS
.org 500
syssize:
.word _syssize
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
_eboot: