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