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