blob: b03a6abf2ab5f4965d95624f364d17a8dbbce04f [file] [log] [blame]
Martin Roth4dcd13d2016-02-24 13:53:07 -08001/*
2 * bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
3 *
4 * bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
5 * itself out of the way to address 0x90000, and jumps there.
6 *
7 * It then loads 'setup' directly after itself (0x90200), and the system
8 * at 0x10000, using BIOS interrupts.
9 *
10 * The loader has been made as simple as possible, and continuos
11 * read errors will result in a unbreakable loop. Reboot by hand. It
12 * loads pretty fast by getting whole tracks at a time whenever possible.
13 *
14 * 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.
15 */
16
17#include "defs.h"
18
19ROOT_DEV = 0
20
21.code16
22.section ".bootsect", "ax", @progbits
23_boot:
24
25
26# ld86 requires an entry symbol. This may as well be the usual one.
27.globl _main
28_main:
29 movw $BOOTSEG, %ax
30 movw %ax, %ds
31 movw $INITSEG, %ax
32 movw %ax, %es
33 movw $256, %cx
34 subw %si, %si
35 subw %di, %di
36 cld
37 rep
38 movsw
39 ljmp $INITSEG, $go - _boot
40
41go:
42 movw %cs, %ax
43 movw $(0x4000-12), %dx # 0x4000 is arbitrary value >= length of
44 # bootsect + length of setup + room for stack
45 # 12 is disk parm size
46
47# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
48# wouldn't have to worry about this if we checked the top of memory. Also
49# my BIOS can be configured to put the wini drive tables in high memory
50# instead of in the vector table. The old stack might have clobbered the
51# drive table.
52
53 movw %ax, %ds
54 movw %ax, %es
55 movw %ax, %ss # put stack at INITSEG:0x4000-12.
56 movw %dx, %sp
57
58/*
59 * Many BIOS's default disk parameter tables will not
60 * recognize multi-sector reads beyond the maximum sector number
61 * specified in the default diskette parameter tables - this may
62 * mean 7 sectors in some cases.
63 *
64 * Since single sector reads are slow and out of the question,
65 * we must take care of this by creating new parameter tables
66 * (for the first disk) in RAM. We will set the maximum sector
67 * count to 18 - the most we will encounter on an HD 1.44.
68 *
69 * High doesn't hurt. Low does.
70 *
71 * Segments are as follows: ds=es=ss=cs - INITSEG,
72 * fs = 0, gs = parameter table segment
73 */
74 pushw $0
75 popw %fs
76 movw $0x78, %bx # fs:bx is parameter table address
77 lgs %fs:(%bx),%si # gs:si is source
78
79 movw %dx, %di # es:di is destination
80 movw $6, %cx # copy 12 bytes
81 cld
82
83 rep movsw %gs:(%si), (%di)
84
85 movw %dx, %di
86 movb $18, 4(%di) # patch sector count
87
88 movw %di, %fs:(%bx)
89 movw %es, %fs:2(%bx)
90
91 movw %cs, %ax
92 movw %ax, %fs
93 movw %ax, %gs
94
95 xorb %ah, %ah # reset FDC
96 xorb %dl, %dl
97 int $0x13
98
99# load the setup-sectors directly after the bootblock.
100# Note that 'es' is already set up.
101
102load_setup:
103 xorw %dx, %dx # drive 0, head 0
104 movw $0x0002, %cx # sector 2, track 0
105 movw $0x0200, %bx # address = 512, in INITSEG
106 movw $(0x0200 + SETUPSECS), %ax # service 2, nr of sectors
107 # (assume all on head 0, track 0)
108 int $0x13 # read it
109 jnc ok_load_setup # ok - continue
110
111 pushw %ax # dump error code
112 call print_nl
113 movw %sp, %bp
114 call print_hex
115 popw %ax
116
117 xorb %dl, %dl # reset FDC
118 xorb %ah, %ah
119 int $0x13
120 jmp load_setup
121
122ok_load_setup:
123
124# Get disk drive parameters, specifically nr of sectors/track
125
126
127/* It seems that there is no BIOS call to get the number of sectors. Guess
128 * 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
129 * Otherwise guess 9
130 */
131
132 xorw %dx, %dx # drive 0, head 0
133 movw $0x0012, %cx # sector 18, track 0
134 movw $(0x200+(SETUPSECS*0x200)), %bx # address after setup (es = cs)
135 movw $0x0201, %ax # service 2, 1 sector
136 int $0x13
137 jnc got_sectors
138 movb $0x0f, %cl # sector 15
139 movw $0x0201, %ax # service 2, 1 sector
140 int $0x13
141 jnc got_sectors
142 movb $0x09, %cl
143
144got_sectors:
145 movw %cx, %cs:sectors - _boot
146 movw $INITSEG, %ax
147 movw %ax, %es
148
149# Print some inane message
150
151 movb $0x03, %ah # read cursor pos
152 xorb %bh, %bh
153 int $0x10
154
155 movw $9, %cx
156 movw $0x0007, %bx # page 0, attribute 7 (normal)
157 movw $msg1 - _boot, %bp
158 movw $0x1301, %ax # write string, move cursor
159 int $0x10
160
161# ok, we've written the message, now
162# we want to load the system (at 0x10000)
163
164 movw $TSTLOAD, %ax
165 movw %ax, %es # segment of 0x010000
166 call read_it
167 call kill_motor
168 call turnoffcursor
169 call print_nl
170
171# after that (everyting loaded), we jump to
172# the setup-routine loaded directly after
173# the bootblock:
174
175 ljmp $SETUPSEG,$0
176
177# This routine loads the system at address 0x10000, making sure
178# no 64kB boundaries are crossed. We try to load it as fast as
179# possible, loading whole tracks whenever we can.
180#
181# in: es - starting address segment (normally 0x1000)
182#
183sread: .word 1+SETUPSECS # sectors read of current track
184head: .word 0 # current head
185track: .word 0 # current track
186
187read_it:
188 movw %es, %ax
189 testw $0x0fff, %ax
190die:
191 jne die # es must be at 64kB boundary
192 xorw %bx,%bx # bx is starting address within segment
193rp_read:
194 movw %es, %ax
195 subw $TSTLOAD, %ax # have we loaded all yet?
196 cmpw syssize - _boot, %ax
197 jbe ok1_read
198 ret
199ok1_read:
200 movw %cs:sectors - _boot, %ax
201 subw sread - _boot, %ax
202 movw %ax, %cx
203 shlw $9, %cx
204 addw %bx, %cx
205 jnc ok2_read
206 je ok2_read
207 xorw %ax, %ax
208 subw %bx, %ax
209 shrw $9, %ax
210ok2_read:
211 call read_track
212 movw %ax, %cx
213 add sread - _boot, %ax
214 cmpw %cs:sectors - _boot, %ax
215 jne ok3_read
216 movw $1, %ax
217 subw head - _boot, %ax
218 jne ok4_read
219 incw track - _boot
220ok4_read:
221 movw %ax, head - _boot
222 xorw %ax, %ax
223ok3_read:
224 movw %ax, sread - _boot
225 shlw $9, %cx
226 addw %cx, %bx
227 jnc rp_read
228 movw %es, %ax
229 addb $0x10, %ah
230 movw %ax, %es
231 xorw %bx, %bx
232 jmp rp_read
233
234read_track:
235 pusha
236 pusha
237 movw $0xe2e, %ax # loading... message 2e = .
238 movw $7, %bx
239 int $0x10
240 popa
241
242 movw track - _boot, %dx
243 movw sread - _boot, %cx
244 incw %cx
245 movb %dl, %ch
246 movw head - _boot, %dx
247 movb %dl, %dh
248 andw $0x0100, %dx
249 movb $2, %ah
250
251 pushw %dx # save for error dump
252 pushw %cx
253 pushw %bx
254 pushw %ax
255
256 int $0x13
257 jc bad_rt
258 addw $8, %sp
259 popa
260 ret
261
262bad_rt:
263 pushw %ax # save error code
264 call print_all # ah = error, al = read
265
266 xorb %ah, %ah
267 xorb %dl, %dl
268 int $0x13
269
270 addw $10, %sp
271 popa
272 jmp read_track
273
274/*
275 * print_all is for debugging purposes.
276 * It will print out all of the registers. The assumption is that this is
277 * called from a routine, with a stack frame like
278 * dx
279 * cx
280 * bx
281 * ax
282 * error
283 * ret <- sp
284 *
285*/
286
287print_all:
288 movw $5, %cx # error code + 4 registers
289 movw %sp, %bp
290
291print_loop:
292 pushw %cx # save count left
293 call print_nl # nl for readability
294
295 cmpb 5, %cl # see if register name is needed
296 jae no_reg
297
298 movw $(0xe05 + 'A' - 1), %ax
299 subb %cl, %al
300 int $0x10
301 movb $'X', %al
302 int $0x10
303 movb $':', %al
304 int $0x10
305
306no_reg:
307 addw $2, %bp # next register
308 call print_hex # print it
309 popw %cx
310 loop print_loop
311 ret
312
313print_nl:
314 movw $0xe0d, %ax # CR
315 int $0x10
316 movb $0x0a, %al # LF
317 int $0x10
318 ret
319
320/*
321 * print_hex is for debugging purposes, and prints the word
322 * pointed to by ss:bp in hexadecmial.
323 */
324
325print_hex:
326 movw $4, %cx # 4 hex digits
327 movw (%bp), %dx # load word into dx
328
329print_digit:
330 rolw $4, %dx # rotate so that lowest 4 bits are used
331 movb $0xe, %ah
332 movb %dl, %al # mask off so we have only next nibble
333 andb $0xf, %al
334 addb $'0', %al # convert to 0-based digit
335 cmpb $'9', %al # check for overflow
336 jbe good_digit
337 addb $('A' - '0' - 10), %al
338
339good_digit:
340 int $0x10
341 loop print_digit
342 ret
343
344
345/*
346 * This procedure turns off the floppy drive motor, so
347 * that we enter the kernel in a known state, and
348 * don't have to worry about it later.
349 */
350kill_motor:
351 pushw %dx
352 movw $0x3f2, %dx
353 xorb %al, %al
354 outb %al, %dx
355 popw %dx
356 ret
357
358turnoffcursor:
359 movb $0x01, %ah # turn off the cursor
360 movb $0x00, %bh
361 movw $0x2000, %cx
362 int $0x10
363 ret
364
365sectors:
366 .word 0
367
368msg1:
369 .byte 13,10
370 .ascii "Loading"
371
372.org 497
373setup_sects:
374 .byte SETUPSECS
375.org 500
376syssize:
377 .word _syssize
378.org 508
379root_dev:
380 .word ROOT_DEV
381boot_flag:
382 .word 0xAA55
383_eboot: