blob: 60d351f72dd577fcf41f466d3618f62a23a7f58a [file] [log] [blame]
Greg Hartman76d05dc2016-11-23 15:51:27 -08001/* NOTE: this boot sector contains instructions that need at least an 80186.
2 * Yes, as86 has a bug somewhere in the valid instruction set checks.
3 *
4 */
5
6/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
7 * modified by Drew Eckhardt
8 * modified by Bruce Evans (bde)
9 *
10 * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
11 *
12 * It then loads the system at SYSSEG<<4, using BIOS interrupts.
13 *
14 * The loader has been made as simple as possible, and continuous read errors
15 * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
16 * getting whole tracks at a time whenever possible.
17 */
18
19FILE_LICENCE ( GPL2_ONLY )
20
21.equ BOOTSEG, 0x07C0 /* original address of boot-sector */
22
23.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */
24
25 .org 0
26 .arch i386
27 .text
28 .section ".prefix", "ax", @progbits
29 .code16
30
31 jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */
32go:
33 movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */
34 /* of bootsect + room for stack + 12 for */
35 /* saved disk parm block */
36
37 movw $BOOTSEG, %ax
38 movw %ax,%ds
39 movw %ax,%es
40 movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */
41 movw %di,%sp
42
43/* Many BIOS's default disk parameter tables will not recognize multi-sector
44 * reads beyond the maximum sector number specified in the default diskette
45 * parameter tables - this may mean 7 sectors in some cases.
46 *
47 * Since single sector reads are slow and out of the question, we must take care
48 * of this by creating new parameter tables (for the first disk) in RAM. We
49 * will set the maximum sector count to 36 - the most we will encounter on an
50 * ED 2.88. High doesn't hurt. Low does.
51 *
52 * Segments are as follows: ds=es=ss=cs - BOOTSEG
53 */
54
55 xorw %cx,%cx
56 movw %cx,%es /* access segment 0 */
57 movw $0x78, %bx /* 0:bx is parameter table address */
58 pushw %ds /* save ds */
59/* 0:bx is parameter table address */
60 ldsw %es:(%bx),%si /* loads ds and si */
61
62 movw %ax,%es /* ax is BOOTSECT (loaded above) */
63 movb $6, %cl /* copy 12 bytes */
64 cld
65 pushw %di /* keep a copy for later */
66 rep
67 movsw /* ds:si is source, es:di is dest */
68 popw %di
69
70 movb $36,%es:4(%di)
71
72 movw %cx,%ds /* access segment 0 */
73 xchgw %di,(%bx)
74 movw %es,%si
75 xchgw %si,2(%bx)
76 popw %ds /* restore ds */
77 movw %di, dpoff /* save old parameters */
78 movw %si, dpseg /* to restore just before finishing */
79 pushw %ds
80 popw %es /* reload es */
81
82/* Note that es is already set up. Also cx is 0 from rep movsw above. */
83
84 xorb %ah,%ah /* reset FDC */
85 xorb %dl,%dl
86 int $0x13
87
88/* Get disk drive parameters, specifically number of sectors/track.
89 *
90 * It seems that there is no BIOS call to get the number of sectors. Guess
91 * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
92 * 15 if sector 15 can be read. Otherwise guess 9.
93 */
94
95 movw $disksizes, %si /* table of sizes to try */
96
97probe_loop:
98 lodsb
99 cbtw /* extend to word */
100 movw %ax, sectors
101 cmpw $disksizes+4, %si
102 jae got_sectors /* if all else fails, try 9 */
103 xchgw %cx,%ax /* cx = track and sector */
104 xorw %dx,%dx /* drive 0, head 0 */
105 movw $0x0200, %bx /* address after boot sector */
106 /* (512 bytes from origin, es = cs) */
107 movw $0x0201, %ax /* service 2, 1 sector */
108 int $0x13
109 jc probe_loop /* try next value */
110
111got_sectors:
112 movw $msg1end-msg1, %cx
113 movw $msg1, %si
114 call print_str
115
116/* ok, we've written the Loading... message, now we want to load the system */
117
118 movw $SYSSEG, %ax
119 movw %ax,%es /* segment of SYSSEG<<4 */
120 pushw %es
121 call read_it
122
123/* This turns off the floppy drive motor, so that we enter the kernel in a
124 * known state, and don't have to worry about it later.
125 */
126 movw $0x3f2, %dx
127 xorb %al,%al
128 outb %al,%dx
129
130 call print_nl
131 pop %es /* = SYSSEG */
132
133/* Restore original disk parameters */
134 movw $0x78, %bx
135 movw dpoff, %di
136 movw dpseg, %si
137 xorw %ax,%ax
138 movw %ax,%ds
139 movw %di,(%bx)
140 movw %si,2(%bx)
141
142 /* Everything now loaded. %es = SYSSEG, so %es:0000 points to
143 * start of loaded image.
144 */
145
146 /* Jump to loaded copy */
147 ljmp $SYSSEG, $start_runtime
148
149endseg: .word SYSSEG
150 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
151 .ascii "ADDW"
152 .long endseg
153 .long 16
154 .long 0
155 .previous
156
157/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
158 * boundaries are crossed. We try to load it as fast as possible, loading whole
159 * tracks whenever we can.
160 *
161 * in: es - starting address segment (normally SYSSEG)
162 */
163read_it:
164 movw $0,sread /* load whole image including prefix */
165 movw %es,%ax
166 testw $0x0fff, %ax
167die: jne die /* es must be at 64kB boundary */
168 xorw %bx,%bx /* bx is starting address within segment */
169rp_read:
170 movw %es,%ax
171 movw %bx,%dx
172 movb $4, %cl
173 shrw %cl,%dx /* bx is always divisible by 16 */
174 addw %dx,%ax
175 cmpw endseg, %ax /* have we loaded all yet? */
176 jb ok1_read
177 ret
178ok1_read:
179 movw sectors, %ax
180 subw sread, %ax
181 movw %ax,%cx
182 shlw $9, %cx
183 addw %bx,%cx
184 jnc ok2_read
185 je ok2_read
186 xorw %ax,%ax
187 subw %bx,%ax
188 shrw $9, %ax
189ok2_read:
190 call read_track
191 movw %ax,%cx
192 addw sread, %ax
193 cmpw sectors, %ax
194 jne ok3_read
195 movw $1, %ax
196 subw head, %ax
197 jne ok4_read
198 incw track
199ok4_read:
200 movw %ax, head
201 xorw %ax,%ax
202ok3_read:
203 movw %ax, sread
204 shlw $9, %cx
205 addw %cx,%bx
206 jnc rp_read
207 movw %es,%ax
208 addb $0x10, %ah
209 movw %ax,%es
210 xorw %bx,%bx
211 jmp rp_read
212
213read_track:
214 pusha
215 pushw %ax
216 pushw %bx
217 pushw %bp /* just in case the BIOS is buggy */
218 movw $0x0e2e, %ax /* 0x2e = . */
219 movw $0x0007, %bx
220 int $0x10
221 popw %bp
222 popw %bx
223 popw %ax
224
225 movw track, %dx
226 movw sread, %cx
227 incw %cx
228 movb %dl,%ch
229 movw head, %dx
230 movb %dl,%dh
231 andw $0x0100, %dx
232 movb $2, %ah
233
234 pushw %dx /* save for error dump */
235 pushw %cx
236 pushw %bx
237 pushw %ax
238
239 int $0x13
240 jc bad_rt
241 addw $8, %sp
242 popa
243 ret
244
245bad_rt: pushw %ax /* save error code */
246 call print_all /* ah = error, al = read */
247
248 xorb %ah,%ah
249 xorb %dl,%dl
250 int $0x13
251
252 addw $10, %sp
253 popa
254 jmp read_track
255
256/* print_all is for debugging purposes. It will print out all of the registers.
257 * The assumption is that this is called from a routine, with a stack frame like
258 * dx
259 * cx
260 * bx
261 * ax
262 * error
263 * ret <- sp
264 */
265
266print_all:
267 call print_nl /* nl for readability */
268 movw $5, %cx /* error code + 4 registers */
269 movw %sp,%bp
270
271print_loop:
272 pushw %cx /* save count left */
273
274 cmpb $5, %cl
275 jae no_reg /* see if register name is needed */
276
277 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
278 movw $0xe05+0x41-1, %ax
279 subb %cl,%al
280 int $0x10
281
282 movb $0x58, %al /* 'X' */
283 int $0x10
284
285 movb $0x3A, %al /* ':' */
286 int $0x10
287
288no_reg:
289 addw $2, %bp /* next register */
290 call print_hex /* print it */
291 movb $0x20, %al /* print a space */
292 int $0x10
293 popw %cx
294 loop print_loop
295 call print_nl /* nl for readability */
296 ret
297
298print_str:
299 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
300 movb $0x0e, %ah /* write char, tty mode */
301prloop:
302 lodsb
303 int $0x10
304 loop prloop
305 ret
306
307print_nl:
308 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
309 movw $0xe0d, %ax /* CR */
310 int $0x10
311 movb $0xa, %al /* LF */
312 int $0x10
313 ret
314
315/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
316
317print_hex:
318 movw (%bp),%dx /* load word into dx */
319 movb $4, %cl
320 movb $0x0e, %ah /* write char, tty mode */
321 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
322 call print_digit
323 call print_digit
324 call print_digit
325/* fall through */
326print_digit:
327 rol %cl,%dx /* rotate so that lowest 4 bits are used */
328 movb $0x0f, %al /* mask for nybble */
329 andb %dl,%al
330 addb $0x90, %al /* convert al to ascii hex (four instructions) */
331 daa
332 adcb $0x40, %al
333 daa
334 int $0x10
335 ret
336
337sread: .word 0 /* sectors read of current track */
338head: .word 0 /* current head */
339track: .word 0 /* current track */
340
341sectors:
342 .word 0
343
344dpseg: .word 0
345dpoff: .word 0
346
347disksizes:
348 .byte 36,18,15,9
349
350msg1:
351 .ascii "Loading ROM image"
352msg1end:
353
354 .org 510, 0
355 .word 0xAA55
356
357start_runtime:
358 /* Install gPXE */
359 call install
360
361 /* Set up real-mode stack */
362 movw %bx, %ss
363 movw $_estack16, %sp
364
365 /* Jump to .text16 segment */
366 pushw %ax
367 pushw $1f
368 lret
369 .section ".text16", "awx", @progbits
3701:
371 pushl $main
372 pushw %cs
373 call prot_call
374 popl %ecx /* discard */
375
376 /* Uninstall gPXE */
377 call uninstall
378
379 /* Boot next device */
380 int $0x18
381