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