Updated paths.
[AROS.git] / arch / all-pc / boot / grub / stage2 / asm.S
blob4dac1fee94e114c8287bd2f368b1cee6e062a4c0
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
22  * Note: These functions defined in this file may be called from C.
23  *       Be careful of that you must not modify some registers. Quote
24  *       from gcc-2.95.2/gcc/config/i386/i386.h:
25         
26    1 for registers not available across function calls.
27    These must include the FIXED_REGISTERS and also any
28    registers that can be used without being saved.
29    The latter must include the registers where values are returned
30    and the register where structure-value addresses are passed.
31    Aside from that, you can include as many other registers as you like.
33   ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
34 {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
35  */
37 #define ASM_FILE
39 #include "shared.h"
41 #ifdef STAGE1_5
42 # define        ABS(x)  ((x) - EXT_C(main) + 0x2200)
43 #else
44 # define        ABS(x)  ((x) - EXT_C(main) + 0x8200)
45 #endif
46         
47         .file   "asm.S"
49         .text
51         /* Tell GAS to generate 16-bit instructions so that this code works
52            in real mode. */
53         .code16
55 #ifndef STAGE1_5
56         /* 
57          * In stage2, do not link start.S with the rest of the source
58          * files directly, so define the start symbols here just to
59          * force ld quiet. These are not referred anyway.
60          */
61         .globl  start, _start
62 start:
63 _start:
64 #endif /* ! STAGE1_5 */
65         
66 ENTRY(main)
67         /*
68          *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
69          *  at 0x0:0x2200 in stage1.5.
70          */
71         ljmp $0, $ABS(codestart)
73         /*
74          *  Compatibility version number
75          *
76          *  These MUST be at byte offset 6 and 7 of the executable
77          *  DO NOT MOVE !!!
78          */
79         . = EXT_C(main) + 0x6
80         .byte   COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
82         /*
83          *  This is a special data area 8 bytes from the beginning.
84          */
86         . = EXT_C(main) + 0x8
88 VARIABLE(install_partition)
89         .long   0xFFFFFF
90 /* This variable is here only because of a historical reason.  */
91 VARIABLE(saved_entryno)
92         .long   0
93 VARIABLE(stage2_id)
94         .byte   STAGE2_ID
95 VARIABLE(force_lba)
96         .byte   0
97 VARIABLE(version_string)
98         .string VERSION
99 VARIABLE(config_file)
100 #ifndef STAGE1_5
101         .string "/boot/pc/grub/menu.lst"
102 #else   /* STAGE1_5 */
103         .long   0xffffffff
104         .string "/boot/pc/grub/stage2"
105 #endif  /* STAGE1_5 */
107         /*
108          *  Leave some breathing room for the config file name.
109          */
111         . = EXT_C(main) + 0x70
113 /* the real mode code continues... */
114 codestart:
115         cli             /* we're not safe here! */
117         /* set up %ds, %ss, and %es */
118         xorw    %ax, %ax
119         movw    %ax, %ds
120         movw    %ax, %ss
121         movw    %ax, %es
123 #ifndef SUPPORT_DISKLESS
124         /*
125          * Save the sector number of the second sector (i.e. this sector)
126          * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
127          */
128         ADDR32  movl    %ebp, EXT_C(install_second_sector)
129 #endif
130         
131         /* set up the real mode/BIOS stack */
132         movl    $STACKOFF, %ebp
133         movl    %ebp, %esp
135         sti             /* we're safe again */
137 #ifndef SUPPORT_DISKLESS
138         /* save boot drive reference */
139         ADDR32  movb    %dl, EXT_C(boot_drive)
141         /* reset disk system (%ah = 0) */
142         int     $0x13
143 #endif
145         /* transition to protected mode */
146         DATA32  call EXT_C(real_to_prot)
148         /* The ".code32" directive takes GAS out of 16-bit mode. */
149         .code32
151         /* clean out the bss */
153         /* set %edi to the bss starting address */
154 #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
155         movl    $__bss_start, %edi
156 #elif defined(HAVE_USCORE_EDATA_SYMBOL)
157         movl    $_edata, %edi
158 #elif defined(HAVE_EDATA_SYMBOL)
159         movl    $edata, %edi
160 #endif
162         /* set %ecx to the bss end */   
163 #if defined(HAVE_END_SYMBOL)
164         movl    $end, %ecx
165 #elif defined(HAVE_USCORE_END_SYMBOL)
166         movl    $_end, %ecx
167 #endif
169         /* compute the bss length */
170         subl    %edi, %ecx
171         
172         /* zero %al */
173         xorb    %al, %al
175         /* set the direction */
176         cld
177         
178         /* clean out */
179         rep
180         stosb
181         
182         /*
183          *  Call the start of main body of C code, which does some
184          *  of it's own initialization before transferring to "cmain".
185          */
186         call EXT_C(init_bios_info)
190  *  This call is special...  it never returns...  in fact it should simply
191  *  hang at this point!
192  */
194 ENTRY(stop)
195         call    EXT_C(prot_to_real)
197         /*
198          * This next part is sort of evil.  It takes advantage of the
199          * byte ordering on the x86 to work in either 16-bit or 32-bit
200          * mode, so think about it before changing it.
201          */
203 ENTRY(hard_stop)
204         hlt
205         jmp EXT_C(hard_stop)
207 #ifndef STAGE1_5
209  * stop_floppy()
211  * Stops the floppy drive from spinning, so that other software is
212  * jumped to with a known state.
213  */
214 ENTRY(stop_floppy)
215         pusha
216         call    EXT_C(prot_to_real)
217         .code16
218         xorb    %dl, %dl
219         int     $0x13
220         DATA32  call EXT_C(real_to_prot)
221         .code32
222         popa
223         ret
226  * grub_reboot()
228  * Reboot the system. At the moment, rely on BIOS.
229  */
230 ENTRY(grub_reboot)
231         call    EXT_C(prot_to_real)
232         .code16
233         /* cold boot */
234         movw    $0x0472, %di
235         movw    %ax, (%di)
236         ljmp    $0xFFFF, $0x0000
237         .code32
238         
240  * grub_halt(int no_apm)
242  * Halt the system, using APM if possible. If NO_APM is true, don't use
243  * APM even if it is available.
244  */
245 ENTRY(grub_halt)
246         /* get the argument */
247         movl    4(%esp), %eax
248         
249         /* see if zero */
250         testl   %eax, %eax
251         jnz     EXT_C(stop)
253         call    EXT_C(prot_to_real)
254         .code16
255         
256         /* detect APM */
257         movw    $0x5300, %ax
258         xorw    %bx, %bx
259         int     $0x15
260         jc      EXT_C(hard_stop)
261         /* don't check %bx for buggy BIOSes... */
263         /* disconnect APM first */
264         movw    $0x5304, %ax
265         xorw    %bx, %bx
266         int     $0x15
268         /* connect APM */
269         movw    $0x5301, %ax
270         xorw    %bx, %bx
271         int     $0x15
272         jc      EXT_C(hard_stop)
274         /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
275         movw    $0x530E, %ax
276         xorw    %bx, %bx
277         movw    $0x0101, %cx
278         int     $0x15
279         jc      EXT_C(hard_stop)
280         
281         /* set the power state to off */
282         movw    $0x5307, %ax
283         movw    $1, %bx
284         movw    $3, %cx
285         int     $0x15
287         /* shouldn't reach here */
288         jmp     EXT_C(hard_stop)
289         .code32
290         
292  * track_int13(int drive)
294  * Track the int13 handler to probe I/O address space.
295  */
296 ENTRY(track_int13)
297         pushl   %ebp
298         movl    %esp, %ebp
300         pushl   %ebx
301         pushl   %edi
303         /* copy the original int13 handler segment:offset */
304         movl    $0x4c, %edi
305         movl    (%edi), %eax
306         movl    %eax, track_int13_addr
307                 
308         /* replace the int1 handler */
309         movl    $0x4, %edi
310         pushl   (%edi)
311         movl    $ABS(int1_handler), %eax
312         movl    %eax, (%edi)
314         /* read the MBR to call int13 successfully */
315         movb    8(%ebp), %dl
316         
317         call    EXT_C(prot_to_real)
318         .code16
320         movw    $SCRATCHSEG, %ax
321         movw    %ax, %es
322         xorw    %bx, %bx
323         movw    $1, %cx
324         xorb    %dh, %dh
326         /* save FLAGS on the stack to emulate int13 */
327         pushfw
328         
329         /* set the TF flag */
330         /* FIXME: this can be simplified not to use AX */
331         pushfw
332         popw    %ax
333         orw     $0x100, %ax
334         pushw   %ax
335         popfw
337         movw    $0x0201, %ax
339         .byte   0x9a            /* lcall */
340 track_int13_addr:
341         .word   0               /* offset */
342         .word   0               /* segment */
344         /* TF is cleared here automatically */
345         
346         DATA32  call    EXT_C(real_to_prot)
347         .code32
349         /* restore the int1 handler */
350         movl    $0x4, %edi
351         popl    (%edi)
353         popl    %edi
354         popl    %ebx
355         popl    %ebp
356         
357         ret
361  * Check if the next instruction is I/O, and if this is true, add the
362  * port into the io map.
364  * Note: Probably this will make the execution of int13 very slow.
366  * Note2: In this implementation, all we can know is I/O-mapped I/O. It
367  * is impossible to detect memory-mapped I/O.
368  */
369 int1_handler:
370         .code16
371         
372         pushw   %bp
373         movw    %sp, %bp
374         pushw   %ds
375         pushw   %ax
376         pushw   %si
377         pushw   %dx
378         
379         /* IP */
380         movw    2(%bp), %si
381         /* CS */
382         movw    4(%bp), %ax
383         movw    %ax, %ds
385         /* examine the next instruction */
386 1:      lodsb   (%si), %al
387         /* skip this code if it is a prefix */
388         cmpb    $0x2E, %al
389         je      1b
390         cmpb    $0x36, %al
391         je      1b
392         cmpb    $0x3E, %al
393         je      1b
394         cmpb    $0x26, %al
395         je      1b
396         cmpb    $0x64, %al
397         jl      2f
398         cmpb    $0x67, %al
399         jle     1b
400 2:      cmpb    $0xF0, %al
401         jl      3f
402         cmpb    $0xF3, %al
403         jle     1b
404         
405 3:      /* check if this code is out* or in* */
407         /* ins? or outs? */
408         cmpb    $0x6C, %al
409         jl      4f
410         cmpb    $0x6F, %al
411         jle     5f
413 4:      /* in? or out? (register operand version) */
414         cmpb    $0xEC, %al
415         jl      6f
416         cmpb    $0xEF, %al
417         jle     5f
418         
419 6:      /* in? or out? (immediate operand version) */
420         cmpb    $0xE4, %al
421         jl      8f
422         cmpb    $0xE7, %al
423         jg      8f
425 7:      /* immediate has a port */
426         lodsb   (%si), %al
427         movzbw  %al, %dx
428         
429 5:      /* %dx has a port */
431         /* set %ds to zero */
432         xorw    %ax, %ax
433         movw    %ax, %ds
434                 
435         /* set %si to the io map */
436         movw    $ABS(EXT_C(io_map)), %si
438                 
439 9:      /* check if the io map already has the port */
440         lodsw   (%si), %ax
441         /* check if this is the end */
442         testw   %ax, %ax
443         jz      1f
444         /* check if this matches the port */
445         cmpw    %ax, %dx
446         jne     9b
447         /* if so, leave from this handler */
448         jmp     8f
449         
450 1:      /* check for the buffer overrun */
451         cmpw    $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
452         je      8f
453         /* add the port into the io map */
454         movw    %dx, -2(%si)
456 8:      /* restore registers */ 
457         popw    %dx
458         popw    %si
459         popw    %ax
460         popw    %ds
461         popw    %bp
463         iret
464         
465         .code32
467 ENTRY(io_map)
468         .space  (IO_MAP_SIZE + 1) * 2
469         
470         
472  * set_int15_handler(void)
474  * Set up int15_handler.
475  */
476 ENTRY(set_int15_handler)
477         pushl   %edi
478         
479         /* save the original int15 handler */
480         movl    $0x54, %edi
481         movw    (%edi), %ax
482         movw    %ax, ABS(int15_offset)
483         movw    2(%edi), %ax
484         movw    %ax, ABS(int15_segment)
486         /* save the new int15 handler */
487         movw    $ABS(int15_handler), %ax
488         movw    %ax, (%edi)
489         xorw    %ax, %ax
490         movw    %ax, 2(%edi)
492         popl    %edi
493         ret
497  * unset_int15_handler(void)
499  * Restore the original int15 handler
500  */
501 ENTRY(unset_int15_handler)
502         pushl   %edi
503         
504         /* check if int15_handler is set */
505         movl    $0x54, %edi
506         movw    $ABS(int15_handler), %ax
507         cmpw    %ax, (%edi)
508         jne     1f
509         xorw    %ax, %ax
510         cmpw    %ax, 2(%edi)
511         jne     1f
513         /* restore the original */
514         movw    ABS(int15_offset), %ax
515         movw    %ax, (%edi)
516         movw    ABS(int15_segment), %ax
517         movw    %ax, 2(%edi)
520         popl    %edi
521         ret
525  * Translate a key code to another.
527  * Note: This implementation cannot handle more than one length
528  * scancodes (such as Right Ctrl).
529  */
530         .code16
531 int15_handler:
532         /* if non-carrier, ignore it */
533         jnc     1f
534         /* check if AH=4F */
535         cmpb    $0x4F, %ah
536         jne     1f
538         /* E0 and E1 are special */
539         cmpb    $0xE1, %al
540         je      4f
541         cmpb    $0xE0, %al
542         /* this flag is actually the machine code (je or jmp) */
543 int15_skip_flag:        
544         je      4f
545         
546         pushw   %bp
547         movw    %sp, %bp
548         
549         pushw   %bx
550         pushw   %dx
551         pushw   %ds
552         pushw   %si
554         /* save bits 0-6 of %al in %dl */
555         movw    %ax, %dx
556         andb    $0x7f, %dl
557         /* save the highest bit in %bl */
558         movb    %al, %bl
559         xorb    %dl, %bl
560         /* set %ds to 0 */
561         xorw    %ax, %ax
562         movw    %ax, %ds
563         /* set %si to the key map */
564         movw    $ABS(EXT_C(bios_key_map)), %si
566         /* find the key code from the key map */
568         lodsw
569         /* check if this is the end */
570         testw   %ax, %ax
571         jz      3f
572         /* check if this matches the key code */
573         cmpb    %al, %dl
574         jne     2b
575         /* if so, perform the mapping */
576         movb    %ah, %dl
578         /* restore %ax */
579         movw    %dx, %ax
580         orb     %bl, %al
581         /* make sure that CF is set */
582         orw     $1, 6(%bp)
583         /* restore other registers */
584         popw    %si
585         popw    %ds
586         popw    %dx
587         popw    %bx
588         popw    %bp
589         iret
590         
592         /* tricky: jmp (0x74) <-> je (0xeb) */
593         xorb    $(0x74 ^ 0xeb), ABS(int15_skip_flag)
595         /* just cascade to the original */
596         /* ljmp */
597         .byte   0xea
598 int15_offset:   .word   0
599 int15_segment:  .word   0
601         .code32
603         .align  4       
604 ENTRY(bios_key_map)
605         .space  (KEY_MAP_SIZE + 1) * 2
606         
607         
609  * set_int13_handler(map)
611  * Copy MAP to the drive map and set up int13_handler.
612  */
613 ENTRY(set_int13_handler)
614         pushl   %ebp
615         movl    %esp, %ebp
617         pushl   %edi
618         pushl   %esi
620         /* copy MAP to the drive map */
621         movl    $(DRIVE_MAP_SIZE * 2), %ecx
622         movl    $ABS(drive_map), %edi
623         movl    8(%ebp), %esi
624         cld
625         rep
626         movsb
628         /* save the original int13 handler */
629         movl    $0x4c, %edi
630         movw    (%edi), %ax
631         movw    %ax, ABS(int13_offset)
632         movw    2(%edi), %ax
633         movw    %ax, ABS(int13_segment)
634         
635         /* decrease the lower memory size and set it to the BIOS memory */
636         movl    $0x413, %edi
637         decw    (%edi)
638         xorl    %eax, %eax
639         movw    (%edi), %ax
640         
641         /* compute the segment */
642         shll    $6, %eax
644         /* save the new int13 handler */
645         movl    $0x4c, %edi
646         movw    %ax, 2(%edi)
647         xorw    %cx, %cx
648         movw    %cx, (%edi)
650         /* copy int13_handler to the reserved area */
651         shll    $4, %eax
652         movl    %eax, %edi
653         movl    $ABS(int13_handler), %esi
654         movl    $(int13_handler_end - int13_handler), %ecx
655         rep
656         movsb
658         popl    %esi
659         popl    %edi
660         popl    %ebp
661         ret
663         
664 /* 
665  * Map a drive to another drive.
666  */
667         
668         .code16
669         
670 int13_handler:
671         pushw   %ax
672         pushw   %bp
673         movw    %sp, %bp
674         
675         pushw   %si
677         /* set %si to the drive map */
678         movw    $(drive_map - int13_handler), %si
679         /* find the drive number from the drive map */
680         cld
681 1:      
682         lodsw   %cs:(%si), %ax
683         /* check if this is the end */
684         testw   %ax, %ax
685         jz      2f
686         /* check if this matches the drive number */
687         cmpb    %al, %dl
688         jne     1b
689         /* if so, perform the mapping */
690         movb    %ah, %dl
692         /* restore %si */
693         popw    %si
694         /* save %ax in the stack */
695         pushw   %ax
696         /* simulate the interrupt call */
697         pushw   8(%bp)
698         /* set %ax and %bp to the original values */
699         movw    2(%bp), %ax
700         movw    (%bp), %bp
701         /* lcall */
702         .byte   0x9a
703 int13_offset:   .word   0
704 int13_segment:  .word   0
705         /* save flags */
706         pushf
707         /* restore %bp */
708         movw    %sp, %bp
709         /* save %ax */
710         pushw   %ax
711         /* set the flags in the stack to the value returned by int13 */
712         movw    (%bp), %ax
713         movw    %ax, 0xc(%bp)
714         /* check if should map the drive number */
715         movw    6(%bp), %ax
716         cmpw    $0x8, %ax
717         jne     3f
718         cmpw    $0x15, %ax
719         jne     3f
720         /* check if the mapping was performed */
721         movw    2(%bp), %ax
722         testw   %ax, %ax
723         jz      3f
724         /* perform the mapping */
725         movb    %al, %dl
727         popw    %ax
728         movw    4(%bp), %bp
729         addw    $8, %sp
730         iret
732         .align  4
733 drive_map:      .space  (DRIVE_MAP_SIZE + 1) * 2
734 int13_handler_end:
735         
736         .code32
737         
738         
740  * chain_stage1(segment, offset, part_table_addr)
742  *  This starts another stage1 loader, at segment:offset.
743  */
745 ENTRY(chain_stage1)
746         /* no need to save anything, just use %esp */
748         /* store %ESI, presuming %ES is 0 */
749         movl    0xc(%esp), %esi
751         /* store new offset */
752         movl    0x8(%esp), %eax
753         movl    %eax, offset
755         /* store new segment */
756         movw    0x4(%esp), %ax
757         movw    %ax, segment
759         /* set up to pass boot drive */
760         movb    EXT_C(boot_drive), %dl
762         call    EXT_C(prot_to_real)
763         .code16
765 #ifdef ABSOLUTE_WITHOUT_ASTERISK
766         DATA32  ADDR32  ljmp    (offset)
767 #else
768         DATA32  ADDR32  ljmp    *(offset)
769 #endif
770         .code32
771 #endif /* STAGE1_5 */
774 #ifdef STAGE1_5
776  * chain_stage2(segment, offset, second_sector)
778  *  This starts another stage2 loader, at segment:offset.  It presumes
779  *  that the other one starts with this same "asm.S" file, and passes
780  *  parameters by writing the embedded install variables.
781  */
783 ENTRY(chain_stage2)
784         /* no need to save anything, just use %esp */
786         /* store new offset */
787         movl    0x8(%esp), %eax
788         movl    %eax, offset
789         movl    %eax, %ebx
791         /* store new segment */
792         movw    0x4(%esp), %ax
793         movw    %ax, segment
794         shll    $4, %eax
796         /* generate linear address */
797         addl    %eax, %ebx
799         /* set up to pass the partition where stage2 is located in */
800         movl    EXT_C(current_partition), %eax
801         movl    %eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
803         /* set up to pass the drive where stage2 is located in */
804         movb    EXT_C(current_drive), %dl
806         /* set up to pass the second sector of stage2 */
807         movl    0xc(%esp), %ecx
809         call    EXT_C(prot_to_real)
810         .code16
812         movl    %ecx, %ebp
814 #ifdef ABSOLUTE_WITHOUT_ASTERISK
815         DATA32  ADDR32  ljmp    (offset)
816 #else
817         DATA32  ADDR32  ljmp    *(offset)
818 #endif
820         .code32
821 #endif /* STAGE1_5 */
822         
824  *  These next two routines, "real_to_prot" and "prot_to_real" are structured
825  *  in a very specific way.  Be very careful when changing them.
827  *  NOTE:  Use of either one messes up %eax and %ebp.
828  */
830 ENTRY(real_to_prot)
831         .code16
832         cli
834         /* load the GDT register */
835         DATA32  ADDR32  lgdt    gdtdesc
837         /* turn on protected mode */
838         movl    %cr0, %eax
839         orl     $CR0_PE_ON, %eax
840         movl    %eax, %cr0
842         /* jump to relocation, flush prefetch queue, and reload %cs */
843         DATA32  ljmp    $PROT_MODE_CSEG, $protcseg
845         /*
846          *  The ".code32" directive only works in GAS, the GNU assembler!
847          *  This gets out of "16-bit" mode.
848          */
849         .code32
851 protcseg:
852         /* reload other segment registers */
853         movw    $PROT_MODE_DSEG, %ax
854         movw    %ax, %ds
855         movw    %ax, %es
856         movw    %ax, %fs
857         movw    %ax, %gs
858         movw    %ax, %ss
860         /* put the return address in a known safe location */
861         movl    (%esp), %eax
862         movl    %eax, STACKOFF
864         /* get protected mode stack */
865         movl    protstack, %eax
866         movl    %eax, %esp
867         movl    %eax, %ebp
869         /* get return address onto the right stack */
870         movl    STACKOFF, %eax
871         movl    %eax, (%esp)
873         /* zero %eax */
874         xorl    %eax, %eax
876         /* return on the old (or initialized) stack! */
877         ret
880 ENTRY(prot_to_real)
881         /* just in case, set GDT */
882         lgdt    gdtdesc
884         /* save the protected mode stack */
885         movl    %esp, %eax
886         movl    %eax, protstack
888         /* get the return address */
889         movl    (%esp), %eax
890         movl    %eax, STACKOFF
892         /* set up new stack */
893         movl    $STACKOFF, %eax
894         movl    %eax, %esp
895         movl    %eax, %ebp
897         /* set up segment limits */
898         movw    $PSEUDO_RM_DSEG, %ax
899         movw    %ax, %ds
900         movw    %ax, %es
901         movw    %ax, %fs
902         movw    %ax, %gs
903         movw    %ax, %ss
905         /* this might be an extra step */
906         ljmp    $PSEUDO_RM_CSEG, $tmpcseg       /* jump to a 16 bit segment */
908 tmpcseg:
909         .code16
911         /* clear the PE bit of CR0 */
912         movl    %cr0, %eax
913         andl    $CR0_PE_OFF, %eax
914         movl    %eax, %cr0
916         /* flush prefetch queue, reload %cs */
917         DATA32  ljmp    $0, $realcseg
919 realcseg:
920         /* we are in real mode now
921          * set up the real mode segment registers : DS, SS, ES
922          */
923         /* zero %eax */
924         xorl    %eax, %eax
926         movw    %ax, %ds
927         movw    %ax, %es
928         movw    %ax, %fs
929         movw    %ax, %gs
930         movw    %ax, %ss
932         /* restore interrupts */
933         sti
935         /* return on new stack! */
936         DATA32  ret
938         .code32
942  *   int biosdisk_int13_extensions (int ax, int drive, void *dap)
944  *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
945  *   is passed for disk address packet. If an error occurs, return
946  *   non-zero, otherwise zero.
947  */
949 ENTRY(biosdisk_int13_extensions)
950         pushl   %ebp
951         movl    %esp, %ebp
953         pushl   %esi
954         pushl   %ebx
956         /* compute the address of disk_address_packet */
957         movl    0x10(%ebp), %eax
958         movw    %ax, %si
959         xorw    %ax, %ax
960         shrl    $4, %eax
961         movw    %ax, %cx        /* save the segment to cx */
963         /* drive */
964         movb    0xc(%ebp), %dl
965         /* ax */
966         movw    0x8(%ebp), %bx
967         /* enter real mode */
968         call    EXT_C(prot_to_real)
969         
970         .code16
971         movw    %bx, %ax
972         movw    %cx, %ds
973         int     $0x13           /* do the operation */
974         movb    %ah, %dl        /* save return value */
975         /* clear the data segment */
976         xorw    %ax, %ax
977         movw    %ax, %ds
978         /* back to protected mode */
979         DATA32  call    EXT_C(real_to_prot)
980         .code32
982         movb    %dl, %al        /* return value in %eax */
984         popl    %ebx
985         popl    %esi
986         popl    %ebp
988         ret
989         
991  *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
992  *                          int nsec, int segment)
994  *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
995  *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
996  *   return non-zero, otherwise zero.
997  */
999 ENTRY(biosdisk_standard)
1000         pushl   %ebp
1001         movl    %esp, %ebp
1003         pushl   %ebx
1004         pushl   %edi
1005         pushl   %esi
1007         /* set up CHS information */
1008         movl    0x10(%ebp), %eax
1009         movb    %al, %ch
1010         movb    0x18(%ebp), %al
1011         shlb    $2, %al
1012         shrw    $2, %ax
1013         movb    %al, %cl
1014         movb    0x14(%ebp), %dh
1015         /* drive */
1016         movb    0xc(%ebp), %dl
1017         /* segment */
1018         movw    0x20(%ebp), %bx
1019         /* save nsec and ah to %di */
1020         movb    0x8(%ebp), %ah
1021         movb    0x1c(%ebp), %al
1022         movw    %ax, %di
1023         /* enter real mode */
1024         call    EXT_C(prot_to_real)
1026         .code16
1027         movw    %bx, %es
1028         xorw    %bx, %bx
1029         movw    $3, %si         /* attempt at least three times */
1031 1:      
1032         movw    %di, %ax
1033         int     $0x13           /* do the operation */
1034         jnc     2f              /* check if successful */
1036         movb    %ah, %bl        /* save return value */
1037         /* if fail, reset the disk system */
1038         xorw    %ax, %ax
1039         int     $0x13
1040         
1041         decw    %si
1042         cmpw    $0, %si
1043         je      2f
1044         xorb    %bl, %bl
1045         jmp     1b              /* retry */
1046 2:      
1047         /* back to protected mode */
1048         DATA32  call    EXT_C(real_to_prot)
1049         .code32
1051         movb    %bl, %al        /* return value in %eax */
1052         
1053         popl    %esi
1054         popl    %edi
1055         popl    %ebx
1056         popl    %ebp
1058         ret
1062  *   int check_int13_extensions (int drive)
1064  *   Check if LBA is supported for DRIVE. If it is supported, then return
1065  *   the major version of extensions, otherwise zero.
1066  */
1068 ENTRY(check_int13_extensions)
1069         pushl   %ebp
1070         movl    %esp, %ebp
1072         pushl   %ebx
1074         /* drive */
1075         movb    0x8(%ebp), %dl
1076         /* enter real mode */
1077         call    EXT_C(prot_to_real)
1079         .code16
1080         movb    $0x41, %ah
1081         movw    $0x55aa, %bx
1082         int     $0x13           /* do the operation */
1083         
1084         /* check the result */
1085         jc      1f
1086         cmpw    $0xaa55, %bx
1087         jne     1f
1089         movb    %ah, %bl        /* save the major version into %bl */
1091         /* check if AH=0x42 is supported if FORCE_LBA is zero */
1092         movb    EXT_C(force_lba), %al
1093         testb   %al, %al
1094         jnz     2f
1095         andw    $1, %cx
1096         jnz     2f
1097         
1099         xorb    %bl, %bl
1101         /* back to protected mode */
1102         DATA32  call    EXT_C(real_to_prot)
1103         .code32
1105         movb    %bl, %al        /* return value in %eax */
1107         popl    %ebx
1108         popl    %ebp
1110         ret
1114  *   int get_diskinfo_standard (int drive, unsigned long *cylinders, 
1115  *                              unsigned long *heads, unsigned long *sectors)
1117  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1118  *   error occurs, then return non-zero, otherwise zero.
1119  */
1121 ENTRY(get_diskinfo_standard)
1122         pushl   %ebp
1123         movl    %esp, %ebp
1125         pushl   %ebx
1126         pushl   %edi
1128         /* drive */
1129         movb    0x8(%ebp), %dl
1130         /* enter real mode */
1131         call    EXT_C(prot_to_real)
1133         .code16
1134         movb    $0x8, %ah
1135         int     $0x13           /* do the operation */
1136         /* check if successful */
1137         testb   %ah, %ah
1138         jnz     1f
1139         /* bogus BIOSes may not return an error number */
1140         testb   $0x3f, %cl      /* 0 sectors means no disk */
1141         jnz     1f              /* if non-zero, then succeed */
1142         /* XXX 0x60 is one of the unused error numbers */
1143         movb    $0x60, %ah
1145         movb    %ah, %bl        /* save return value in %bl */
1146         /* back to protected mode */
1147         DATA32  call    EXT_C(real_to_prot)
1148         .code32
1150         /* restore %ebp */
1151         leal    0x8(%esp), %ebp
1152         
1153         /* heads */
1154         movb    %dh, %al
1155         incl    %eax            /* the number of heads is counted from zero */
1156         movl    0x10(%ebp), %edi
1157         movl    %eax, (%edi)
1159         /* sectors */
1160         xorl    %eax, %eax
1161         movb    %cl, %al
1162         andb    $0x3f, %al
1163         movl    0x14(%ebp), %edi
1164         movl    %eax, (%edi)
1166         /* cylinders */
1167         shrb    $6, %cl
1168         movb    %cl, %ah
1169         movb    %ch, %al
1170         incl    %eax            /* the number of cylinders is 
1171                                    counted from zero */
1172         movl    0xc(%ebp), %edi
1173         movl    %eax, (%edi)
1175         xorl    %eax, %eax
1176         movb    %bl, %al        /* return value in %eax */
1178         popl    %edi
1179         popl    %ebx
1180         popl    %ebp
1182         ret
1185 #if 0           
1187  *   int get_diskinfo_floppy (int drive, unsigned long *cylinders, 
1188  *                            unsigned long *heads, unsigned long *sectors)
1190  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1191  *   error occurs, then return non-zero, otherwise zero.
1192  */
1194 ENTRY(get_diskinfo_floppy)
1195         pushl   %ebp
1196         movl    %esp, %ebp
1198         pushl   %ebx
1199         pushl   %esi
1201         /* drive */
1202         movb    0x8(%ebp), %dl
1203         /* enter real mode */
1204         call    EXT_C(prot_to_real)
1206         .code16
1207         /* init probe value */
1208         movl    $probe_values-1, %esi
1210         xorw    %ax, %ax
1211         int     $0x13           /* reset floppy controller */
1213         incw    %si
1214         movb    (%si), %cl
1215         cmpb    $0, %cl         /* probe failed if zero */
1216         je      2f
1218         /* perform read */
1219         movw    $SCRATCHSEG, %ax
1220         movw    %ax, %es
1221         xorw    %bx, %bx
1222         movw    $0x0201, %ax
1223         movb    $0, %ch
1224         movb    $0, %dh
1225         int     $0x13
1227         /* FIXME: Read from floppy may fail even if the geometry is correct.
1228            So should retry at least three times.  */
1229         jc      1b              /* next value */
1230         
1231         /* succeed */
1232         jmp     2f
1233         
1234 probe_values:
1235         .byte   36, 18, 15, 9, 0
1236         
1238         /* back to protected mode */
1239         DATA32  call    EXT_C(real_to_prot)
1240         .code32
1242         /* restore %ebp */
1243         leal    0x8(%esp), %ebp
1244         
1245         /* cylinders */
1246         movl    0xc(%ebp), %eax
1247         movl    $80, %ebx
1248         movl    %ebx, (%eax)
1249         /* heads */
1250         movl    0x10(%ebp), %eax
1251         movl    $2, %ebx
1252         movl    %ebx, (%eax)
1253         /* sectors */
1254         movl    0x14(%ebp), %eax
1255         movzbl  %cl, %ebx
1256         movl    %ebx, (%eax)
1258         /* return value in %eax */
1259         xorl    %eax, %eax
1260         cmpb    $0, %cl
1261         jne     3f
1262         incl    %eax            /* %eax = 1 (non-zero) */
1264         popl    %esi
1265         popl    %ebx
1266         popl    %ebp
1268         ret
1269 #endif
1270         
1272 /* Source files are splitted, as they have different copyrights.  */
1273 #ifndef STAGE1_5
1274 # include "setjmp.S"
1275 # include "apm.S"
1276 #endif /* ! STAGE1_5 */
1277                 
1278         
1280 #ifndef STAGE1_5
1281 /* get_code_end() :  return the address of the end of the code
1282  * This is here so that it can be replaced by asmstub.c.
1283  */
1284 ENTRY(get_code_end)
1285         /* will be the end of the bss */
1286 # if defined(HAVE_END_SYMBOL)
1287         movl    $end, %eax
1288 # elif defined(HAVE_USCORE_END_SYMBOL)
1289         movl    $_end, %eax
1290 # endif
1291         shrl    $2, %eax                /* Round up to the next word. */
1292         incl    %eax
1293         shll    $2, %eax
1294         ret
1295 #endif /* ! STAGE1_5 */
1299  * get_memsize(i) :  return the memory size in KB. i == 0 for conventional
1300  *              memory, i == 1 for extended memory
1301  *      BIOS call "INT 12H" to get conventional memory size
1302  *      BIOS call "INT 15H, AH=88H" to get extended memory size
1303  *              Both have the return value in AX.
1305  */
1307 ENTRY(get_memsize)
1308         push    %ebp
1309         push    %ebx
1311         mov     0xc(%esp), %ebx
1313         call    EXT_C(prot_to_real)     /* enter real mode */
1314         .code16
1316         cmpb    $0x1, %bl
1317         DATA32  je      xext
1319         int     $0x12
1320         DATA32  jmp     xdone
1322 xext:
1323         movb    $0x88, %ah
1324         int     $0x15
1326 xdone:
1327         movw    %ax, %bx
1329         DATA32  call    EXT_C(real_to_prot)
1330         .code32
1332         movw    %bx, %ax
1333         pop     %ebx
1334         pop     %ebp
1335         ret
1338 #ifndef STAGE1_5
1342  * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is
1343  *              memory between 1M and 16M in 1K parts, upper 16 bits is
1344  *              memory above 16M in 64K parts.  If error, return -1.
1345  *      BIOS call "INT 15H, AH=E801H" to get EISA memory map,
1346  *              AX = memory between 1M and 16M in 1K parts.
1347  *              BX = memory above 16M in 64K parts.
1349  */
1351 ENTRY(get_eisamemsize)
1352         push    %ebp
1353         push    %ebx
1355         call    EXT_C(prot_to_real)     /* enter real mode */
1356         .code16
1358         movw    $0xe801, %ax
1359         int     $0x15
1361         shll    $16, %ebx
1362         movw    %ax, %bx
1364         DATA32  call    EXT_C(real_to_prot)
1365         .code32
1367         movl    $0xFFFFFFFF, %eax
1368         cmpb    $0x86, %bh
1369         je      xnoteisa
1371         movl    %ebx, %eax
1373 xnoteisa:
1374         pop     %ebx
1375         pop     %ebp
1376         ret
1380  * get_mmap_entry(addr, cont) :  address and old continuation value (zero to
1381  *              start), for the Query System Address Map BIOS call.
1383  *  Sets the first 4-byte int value of "addr" to the size returned by
1384  *  the call.  If the call fails, sets it to zero.
1386  *      Returns:  new (non-zero) continuation value, 0 if done.
1388  * NOTE: Currently hard-coded for a maximum buffer length of 1024.
1389  */
1391 ENTRY(get_mmap_entry)
1392         push    %ebp
1393         push    %ebx
1394         push    %edi
1395         push    %esi
1397         /* place address (+4) in ES:DI */
1398         movl    0x14(%esp), %eax
1399         addl    $4, %eax
1400         movl    %eax, %edi
1401         andl    $0xf, %edi
1402         shrl    $4, %eax
1403         movl    %eax, %esi
1405         /* set continuation value */
1406         movl    0x18(%esp), %ebx
1408         /* set default maximum buffer size */
1409         movl    $0x14, %ecx
1411         /* set EDX to 'SMAP' */
1412         movl    $0x534d4150, %edx
1414         call    EXT_C(prot_to_real)     /* enter real mode */
1415         .code16
1417         movw    %si, %es
1418         movl    $0xe820, %eax
1419         int     $0x15
1421         DATA32  jc      xnosmap
1423         cmpl    $0x534d4150, %eax
1424         DATA32  jne     xnosmap
1426         cmpl    $0x14, %ecx
1427         DATA32  jl      xnosmap
1429         cmpl    $0x400, %ecx
1430         DATA32  jg      xnosmap
1432         DATA32  jmp     xsmap
1434 xnosmap:
1435         movl    $0, %ecx
1437 xsmap:
1438         DATA32  call    EXT_C(real_to_prot)
1439         .code32
1441         /* write length of buffer (zero if error) into "addr" */
1442         movl    0x14(%esp), %eax
1443         movl    %ecx, (%eax)
1445         /* set return value to continuation */
1446         movl    %ebx, %eax
1448         pop     %esi
1449         pop     %edi
1450         pop     %ebx
1451         pop     %ebp
1452         ret
1455  * get_rom_config_table()
1457  * Get the linear address of a ROM configuration table. Return zero,
1458  * if fails.
1459  */
1460         
1461 ENTRY(get_rom_config_table)
1462         pushl   %ebp
1463         pushl   %ebx
1465         /* zero %ebx for simplicity */
1466         xorl    %ebx, %ebx
1467         
1468         call    EXT_C(prot_to_real)
1469         .code16
1471         movw    $0xc0, %ax
1472         int     $0x15
1474         jc      no_rom_table
1475         testb   %ah, %ah
1476         jnz     no_rom_table
1477         
1478         movw    %es, %dx
1479         jmp     found_rom_table
1480         
1481 no_rom_table:
1482         xorw    %dx, %dx
1483         xorw    %bx, %bx
1484         
1485 found_rom_table:
1486         DATA32  call    EXT_C(real_to_prot)
1487         .code32
1489         /* compute the linear address */
1490         movw    %dx, %ax
1491         shll    $4, %eax
1492         addl    %ebx, %eax
1494         popl    %ebx
1495         popl    %ebp
1496         ret
1500  * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
1502  * Get VBE controller information.
1503  */
1505 ENTRY(get_vbe_controller_info)
1506         pushl   %ebp
1507         movl    %esp, %ebp
1508         
1509         pushl   %edi
1510         pushl   %ebx
1512         /* Convert the linear address to segment:offset */
1513         movl    8(%ebp), %eax
1514         movl    %eax, %edi
1515         andl    $0x0000000f, %edi
1516         shrl    $4, %eax
1517         movl    %eax, %ebx
1519         call    EXT_C(prot_to_real)
1520         .code16
1522         movw    %bx, %es
1523         movw    $0x4F00, %ax
1524         int     $0x10
1526         movw    %ax, %bx
1527         DATA32  call    EXT_C(real_to_prot)
1528         .code32
1530         movzwl  %bx, %eax
1532         popl    %ebx
1533         popl    %edi
1534         popl    %ebp
1535         ret
1537         
1539  * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
1541  * Get VBE mode information.
1542  */
1544 ENTRY(get_vbe_mode_info)
1545         pushl   %ebp
1546         movl    %esp, %ebp
1547         
1548         pushl   %edi
1549         pushl   %ebx
1551         /* Convert the linear address to segment:offset */
1552         movl    0xc(%ebp), %eax
1553         movl    %eax, %edi
1554         andl    $0x0000000f, %edi
1555         shrl    $4, %eax
1556         movl    %eax, %ebx
1558         /* Save the mode number in %cx */
1559         movl    0x8(%ebp), %ecx
1560         
1561         call    EXT_C(prot_to_real)
1562         .code16
1564         movw    %bx, %es
1565         movw    $0x4F01, %ax
1566         int     $0x10
1568         movw    %ax, %bx
1569         DATA32  call    EXT_C(real_to_prot)
1570         .code32
1572         movzwl  %bx, %eax
1574         popl    %ebx
1575         popl    %edi
1576         popl    %ebp
1577         ret
1579         
1581  * int set_vbe_mode (int mode_number)
1583  * Set VBE mode. Don't support user-specified CRTC information.
1584  */
1586 ENTRY(set_vbe_mode)
1587         pushl   %ebp
1588         movl    %esp, %ebp
1589         
1590         pushl   %ebx
1592         /* Save the mode number in %bx */
1593         movl    0x8(%ebp), %ebx
1594         /* Clear bit D11 */
1595         andl    $0xF7FF, %ebx
1596         
1597         call    EXT_C(prot_to_real)
1598         .code16
1600         movw    $0x4F02, %ax
1601         int     $0x10
1603         movw    %ax, %bx
1604         DATA32  call    EXT_C(real_to_prot)
1605         .code32
1607         movzwl  %bx, %eax
1609         popl    %ebx
1610         popl    %ebp
1611         ret
1613                 
1615  * gateA20(int linear)
1617  * Gate address-line 20 for high memory.
1619  * This routine is probably overconservative in what it does, but so what?
1621  * It also eats any keystrokes in the keyboard buffer.  :-(
1622  */
1624 ENTRY(gateA20)
1625         /* first, try a BIOS call */
1626         pushl   %ebp
1627         movl    8(%esp), %edx
1628         
1629         call    EXT_C(prot_to_real)
1630         
1631         .code16
1632         movw    $0x2400, %ax
1633         testw   %dx, %dx
1634         jz      1f
1635         incw    %ax
1636 1:      stc
1637         int     $0x15
1638         jnc     2f
1640         /* set non-zero if failed */
1641         movb    $1, %ah
1643         /* save the status */
1644 2:      movb    %ah, %dl
1646         DATA32  call    EXT_C(real_to_prot)
1647         .code32
1649         popl    %ebp
1650         testb   %dl, %dl
1651         jnz     3f
1652         ret
1654 3:      /* use keyboard controller */
1655         pushl   %eax
1657         call    gloop1
1659         movb    $KC_CMD_WOUT, %al
1660         outb    $K_CMD
1662 gloopint1:
1663         inb     $K_STATUS
1664         andb    $K_IBUF_FUL, %al
1665         jnz     gloopint1
1667         movb    $KB_OUTPUT_MASK, %al
1668         cmpb    $0, 0x8(%esp)
1669         jz      gdoit
1671         orb     $KB_A20_ENABLE, %al
1672 gdoit:
1673         outb    $K_RDWR
1675         call    gloop1
1677         /* output a dummy command (USB keyboard hack) */
1678         movb    $0xff, %al
1679         outb    $K_CMD
1680         call    gloop1
1681         
1682         popl    %eax
1683         ret
1685 gloop1:
1686         inb     $K_STATUS
1687         andb    $K_IBUF_FUL, %al
1688         jnz     gloop1
1690 gloop2:
1691         inb     $K_STATUS
1692         andb    $K_OBUF_FUL, %al
1693         jz      gloop2ret
1694         inb     $K_RDWR
1695         jmp     gloop2
1697 gloop2ret:
1698         ret
1701 ENTRY(patch_code)       /* labels start with "pc_" */
1702         .code16
1704         mov     %cs, %ax
1705         mov     %ax, %ds
1706         mov     %ax, %es
1707         mov     %ax, %fs
1708         mov     %ax, %gs
1709         ADDR32  movl    $0, 0
1710 pc_stop:
1711         hlt
1712         DATA32  jmp     pc_stop
1713 ENTRY(patch_code_end)
1715         .code32
1719  * linux_boot()
1721  * Does some funky things (including on the stack!), then jumps to the
1722  * entry point of the Linux setup code.
1723  */
1725 VARIABLE(linux_text_len)
1726         .long   0
1727         
1728 VARIABLE(linux_data_tmp_addr)
1729         .long   0
1730         
1731 VARIABLE(linux_data_real_addr)
1732         .long   0
1733         
1734 ENTRY(linux_boot)
1735         /* don't worry about saving anything, we're committed at this point */
1736         cld     /* forward copying */
1738         /* copy kernel */
1739         movl    EXT_C(linux_text_len), %ecx
1740         addl    $3, %ecx
1741         shrl    $2, %ecx
1742         movl    $LINUX_BZIMAGE_ADDR, %esi
1743         movl    $LINUX_ZIMAGE_ADDR, %edi
1745         rep
1746         movsl
1748 ENTRY(big_linux_boot)
1749         movl    EXT_C(linux_data_real_addr), %ebx
1750         
1751         /* copy the real mode part */
1752         movl    EXT_C(linux_data_tmp_addr), %esi
1753         movl    %ebx, %edi
1754         movl    $LINUX_SETUP_MOVE_SIZE, %ecx
1755         cld
1756         rep
1757         movsb
1759         /* change %ebx to the segment address */
1760         shrl    $4, %ebx
1761         movl    %ebx, %eax
1762         addl    $0x20, %eax
1763         movl    %eax, linux_setup_seg
1764                         
1765         /* XXX new stack pointer in safe area for calling functions */
1766         movl    $0x4000, %esp
1767         call    EXT_C(stop_floppy)
1769         /* final setup for linux boot */
1771         call    EXT_C(prot_to_real)
1772         .code16
1774         /* final setup for linux boot */
1775         cli
1776         movw    %bx, %ss
1777         movw    $LINUX_SETUP_STACK, %sp
1778         
1779         movw    %bx, %ds
1780         movw    %bx, %es
1781         movw    %bx, %fs
1782         movw    %bx, %gs
1784         /* jump to start */
1785         /* ljmp */
1786         .byte   0xea
1787         .word   0
1788 linux_setup_seg:        
1789         .word   0
1790         .code32
1794  * multi_boot(int start, int mb_info)
1796  *  This starts a kernel in the manner expected of the multiboot standard.
1797  */
1799 ENTRY(multi_boot)
1800         /* no need to save anything */
1801         call    EXT_C(stop_floppy)
1803         movl    $0x2BADB002, %eax
1804         movl    0x8(%esp), %ebx
1806         /* boot kernel here (absolute address call) */
1807         call    *0x4(%esp)
1809         /* error */
1810         call    EXT_C(stop)
1812 #endif /* ! STAGE1_5 */
1813         
1815  * void console_putchar (int c)
1817  * Put the character C on the console. Because GRUB wants to write a
1818  * character with an attribute, this implementation is a bit tricky.
1819  * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
1820  * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
1821  * save the current position, restore the original position, write the
1822  * character and the attribute, and restore the current position.
1824  * The reason why this is so complicated is that there is no easy way to
1825  * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
1826  * support setting a background attribute.
1827  */
1828 ENTRY(console_putchar)
1829         movl    0x4(%esp), %edx
1830         pusha
1831 #ifdef STAGE1_5
1832         movb    $0x07, %bl
1833 #else
1834         movl    EXT_C(console_current_color), %ebx
1835 #endif
1836         
1837         call    EXT_C(prot_to_real)
1838         .code16
1839         movb    %dl, %al
1840         xorb    %bh, %bh
1842 #ifndef STAGE1_5
1843         /* use teletype output if control character */
1844         cmpb    $0x7, %al
1845         je      1f
1846         cmpb    $0x8, %al
1847         je      1f
1848         cmpb    $0xa, %al
1849         je      1f
1850         cmpb    $0xd, %al
1851         je      1f
1853         /* save the character and the attribute on the stack */
1854         pushw   %ax
1855         pushw   %bx
1856         
1857         /* get the current position */
1858         movb    $0x3, %ah
1859         int     $0x10
1861         /* check the column with the width */
1862         cmpb    $79, %dl
1863         jl      2f
1864         
1865         /* print CR and LF, if next write will exceed the width */      
1866         movw    $0x0e0d, %ax
1867         int     $0x10
1868         movb    $0x0a, %al
1869         int     $0x10
1870         
1871         /* get the current position */
1872         movb    $0x3, %ah
1873         int     $0x10
1875 2:      
1876         /* restore the character and the attribute */
1877         popw    %bx
1878         popw    %ax
1879         
1880         /* write the character with the attribute */
1881         movb    $0x9, %ah
1882         movw    $1, %cx
1883         int     $0x10
1885         /* move the cursor forward */
1886         incb    %dl
1887         movb    $0x2, %ah
1888         int     $0x10
1890         jmp     3f
1891 #endif /* ! STAGE1_5 */
1892         
1893 1:      movb    $0xe, %ah
1894         int     $0x10
1895         
1896 3:      DATA32  call    EXT_C(real_to_prot)
1897         .code32
1898         
1899         popa
1900         ret
1903 #ifndef STAGE1_5
1905 /* this table is used in translate_keycode below */
1906 translation_table:
1907         .word   KEY_LEFT, 2
1908         .word   KEY_RIGHT, 6
1909         .word   KEY_UP, 16
1910         .word   KEY_DOWN, 14
1911         .word   KEY_HOME, 1
1912         .word   KEY_END, 5
1913         .word   KEY_DC, 4
1914         .word   KEY_BACKSPACE, 8
1915         .word   KEY_PPAGE, 7
1916         .word   KEY_NPAGE, 3
1917         .word   0
1918         
1920  * translate_keycode translates the key code %dx to an ascii code.
1921  */
1922         .code16
1924 translate_keycode:
1925         pushw   %bx
1926         pushw   %si
1927         
1928         movw    $ABS(translation_table), %si
1929         
1930 1:      lodsw
1931         /* check if this is the end */
1932         testw   %ax, %ax
1933         jz      2f
1934         /* load the ascii code into %ax */
1935         movw    %ax, %bx
1936         lodsw
1937         /* check if this matches the key code */
1938         cmpw    %bx, %dx
1939         jne     1b
1940         /* translate %dx, if successful */
1941         movw    %ax, %dx
1943 2:      popw    %si
1944         popw    %bx
1945         ret
1947         .code32
1948         
1951  * remap_ascii_char remaps the ascii code %dl to another if the code is
1952  * contained in ASCII_KEY_MAP.
1953  */
1954         .code16
1955         
1956 remap_ascii_char:
1957         pushw   %si
1958         
1959         movw    $ABS(EXT_C(ascii_key_map)), %si
1961         lodsw
1962         /* check if this is the end */
1963         testw   %ax, %ax
1964         jz      2f
1965         /* check if this matches the ascii code */
1966         cmpb    %al, %dl
1967         jne     1b
1968         /* if so, perform the mapping */
1969         movb    %ah, %dl
1971         /* restore %si */
1972         popw    %si
1974         ret
1976         .code32
1978         .align  4
1979 ENTRY(ascii_key_map)
1980         .space  (KEY_MAP_SIZE + 1) * 2
1981         
1984  * int console_getkey (void)
1985  * BIOS call "INT 16H Function 00H" to read character from keyboard
1986  *      Call with       %ah = 0x0
1987  *      Return:         %ah = keyboard scan code
1988  *                      %al = ASCII character
1989  */
1991 ENTRY(console_getkey)
1992         push    %ebp
1994         call    EXT_C(prot_to_real)
1995         .code16
1997         int     $0x16
1999         movw    %ax, %dx                /* real_to_prot uses %eax */
2000         call    translate_keycode
2001         call    remap_ascii_char
2002         
2003         DATA32  call    EXT_C(real_to_prot)
2004         .code32
2006         movw    %dx, %ax
2008         pop     %ebp
2009         ret
2013  * int console_checkkey (void)
2014  *      if there is a character pending, return it; otherwise return -1
2015  * BIOS call "INT 16H Function 01H" to check whether a character is pending
2016  *      Call with       %ah = 0x1
2017  *      Return:
2018  *              If key waiting to be input:
2019  *                      %ah = keyboard scan code
2020  *                      %al = ASCII character
2021  *                      Zero flag = clear
2022  *              else
2023  *                      Zero flag = set
2024  */
2025 ENTRY(console_checkkey)
2026         push    %ebp
2027         xorl    %edx, %edx
2028         
2029         call    EXT_C(prot_to_real)     /* enter real mode */
2030         .code16
2032         movb    $0x1, %ah
2033         int     $0x16
2035         DATA32  jz      notpending
2036         
2037         movw    %ax, %dx
2038         call    translate_keycode
2039         call    remap_ascii_char
2040         DATA32  jmp     pending
2042 notpending:
2043         movl    $0xFFFFFFFF, %edx
2045 pending:
2046         DATA32  call    EXT_C(real_to_prot)
2047         .code32
2049         mov     %edx, %eax
2051         pop     %ebp
2052         ret
2054         
2056  * int console_getxy (void)
2057  * BIOS call "INT 10H Function 03h" to get cursor position
2058  *      Call with       %ah = 0x03
2059  *                      %bh = page
2060  *      Returns         %ch = starting scan line
2061  *                      %cl = ending scan line
2062  *                      %dh = row (0 is top)
2063  *                      %dl = column (0 is left)
2064  */
2067 ENTRY(console_getxy)
2068         push    %ebp
2069         push    %ebx                    /* save EBX */
2071         call    EXT_C(prot_to_real)
2072         .code16
2074         xorb    %bh, %bh                /* set page to 0 */
2075         movb    $0x3, %ah
2076         int     $0x10                   /* get cursor position */
2078         DATA32  call    EXT_C(real_to_prot)
2079         .code32
2081         movb    %dl, %ah
2082         movb    %dh, %al
2084         pop     %ebx
2085         pop     %ebp
2086         ret
2090  * void console_gotoxy(int x, int y)
2091  * BIOS call "INT 10H Function 02h" to set cursor position
2092  *      Call with       %ah = 0x02
2093  *                      %bh = page
2094  *                      %dh = row (0 is top)
2095  *                      %dl = column (0 is left)
2096  */
2099 ENTRY(console_gotoxy)
2100         push    %ebp
2101         push    %ebx                    /* save EBX */
2103         movb    0xc(%esp), %dl           /* %dl = x */
2104         movb    0x10(%esp), %dh          /* %dh = y */
2106         call    EXT_C(prot_to_real)
2107         .code16
2109         xorb    %bh, %bh                /* set page to 0 */
2110         movb    $0x2, %ah
2111         int     $0x10                   /* set cursor position */
2113         DATA32  call    EXT_C(real_to_prot)
2114         .code32
2116         pop     %ebx
2117         pop     %ebp
2118         ret
2120         
2122  * void console_cls (void)
2123  * BIOS call "INT 10H Function 09h" to write character and attribute
2124  *      Call with       %ah = 0x09
2125  *                      %al = (character)
2126  *                      %bh = (page number)
2127  *                      %bl = (attribute)
2128  *                      %cx = (number of times)
2129  */
2132 ENTRY(console_cls)
2133         push    %ebp
2134         push    %ebx                    /* save EBX */
2136         call    EXT_C(prot_to_real)
2137         .code16
2139         /* move the cursor to the beginning */
2140         movb    $0x02, %ah
2141         xorb    %bh, %bh
2142         xorw    %dx, %dx
2143         int     $0x10
2145         /* write spaces to the entire screen */
2146         movw    $0x0920, %ax
2147         movw    $0x07, %bx
2148         movw    $(80 * 25), %cx
2149         int     $0x10
2151         /* move back the cursor */
2152         movb    $0x02, %ah
2153         int     $0x10
2155         DATA32  call    EXT_C(real_to_prot)
2156         .code32
2158         pop     %ebx
2159         pop     %ebp
2160         ret
2162         
2164  * int console_setcursor (int on)
2165  * BIOS call "INT 10H Function 01h" to set cursor type
2166  *      Call with       %ah = 0x01
2167  *                      %ch = cursor starting scanline
2168  *                      %cl = cursor ending scanline
2169  */
2171 console_cursor_state:
2172         .byte   1
2173 console_cursor_shape:
2174         .word   0
2175         
2176 ENTRY(console_setcursor)
2177         push    %ebp
2178         push    %ebx
2180         /* check if the standard cursor shape has already been saved */
2181         movw    console_cursor_shape, %ax
2182         testw   %ax, %ax
2183         jne     1f
2185         call    EXT_C(prot_to_real)
2186         .code16
2188         movb    $0x03, %ah
2189         xorb    %bh, %bh
2190         int     $0x10
2192         DATA32  call    EXT_C(real_to_prot)
2193         .code32
2195         movw    %cx, console_cursor_shape
2197         /* set %cx to the designated cursor shape */
2198         movw    $0x2000, %cx
2199         movl    0xc(%esp), %ebx
2200         testl   %ebx, %ebx
2201         jz      2f
2202         movw    console_cursor_shape, %cx
2203 2:      
2204         call    EXT_C(prot_to_real)
2205         .code16
2207         movb    $0x1, %ah
2208         int     $0x10 
2210         DATA32  call    EXT_C(real_to_prot)
2211         .code32
2213         movzbl  console_cursor_state, %eax
2214         movb    %bl, console_cursor_state
2215         
2216         pop     %ebx
2217         pop     %ebp
2218         ret
2219                 
2221  * getrtsecs()
2222  *      if a seconds value can be read, read it and return it (BCD),
2223  *      otherwise return 0xFF
2224  * BIOS call "INT 1AH Function 02H" to check whether a character is pending
2225  *      Call with       %ah = 0x2
2226  *      Return:
2227  *              If RT Clock can give correct values
2228  *                      %ch = hour (BCD)
2229  *                      %cl = minutes (BCD)
2230  *                      %dh = seconds (BCD)
2231  *                      %dl = daylight savings time (00h std, 01h daylight)
2232  *                      Carry flag = clear
2233  *              else
2234  *                      Carry flag = set
2235  *                         (this indicates that the clock is updating, or
2236  *                          that it isn't running)
2237  */
2238 ENTRY(getrtsecs)
2239         push    %ebp
2241         call    EXT_C(prot_to_real)     /* enter real mode */
2242         .code16
2244         movb    $0x2, %ah
2245         int     $0x1a
2247         DATA32  jnc     gottime
2248         movb    $0xff, %dh
2250 gottime:
2251         DATA32  call    EXT_C(real_to_prot)
2252         .code32
2254         movb    %dh, %al
2256         pop     %ebp
2257         ret
2259         
2261  * currticks()
2262  *      return the real time in ticks, of which there are about
2263  *      18-20 per second
2264  */
2265 ENTRY(currticks)
2266         pushl   %ebp
2268         call    EXT_C(prot_to_real)     /* enter real mode */
2269         .code16
2271         /* %ax is already zero */
2272         int     $0x1a
2274         DATA32  call    EXT_C(real_to_prot)
2275         .code32
2277         movl    %ecx, %eax
2278         shll    $16, %eax
2279         movw    %dx, %ax
2281         popl    %ebp
2282         ret
2284 #endif /* STAGE1_5 */
2287  *  This is the area for all of the special variables.
2288  */
2290         .p2align        2       /* force 4-byte alignment */
2292 protstack:
2293         .long   PROTSTACKINIT
2295 VARIABLE(boot_drive)
2296 #ifdef SUPPORT_DISKLESS
2297         .long   NETWORK_DRIVE
2298 #else
2299         .long   0
2300 #endif
2302 VARIABLE(install_second_sector)
2303         .long   0
2304         
2305         /* an address can only be long-jumped to if it is in memory, this
2306            is used by multiple routines */
2307 offset:
2308         .long   0x8000
2309 segment:
2310         .word   0
2312 VARIABLE(apm_bios_info)
2313         .word   0       /* version */
2314         .word   0       /* cseg */
2315         .long   0       /* offset */
2316         .word   0       /* cseg_16 */
2317         .word   0       /* dseg_16 */
2318         .word   0       /* cseg_len */
2319         .word   0       /* cseg_16_len */
2320         .word   0       /* dseg_16_len */
2321         
2323  * This is the Global Descriptor Table
2325  *  An entry, a "Segment Descriptor", looks like this:
2327  * 31          24         19   16                 7           0
2328  * ------------------------------------------------------------
2329  * |             | |B| |A|       | |   |1|0|E|W|A|            |
2330  * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
2331  * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
2332  * ------------------------------------------------------------
2333  * |                             |                            |
2334  * |        BASE 15..0           |       LIMIT 15..0          |
2335  * |                             |                            |
2336  * ------------------------------------------------------------
2338  *  Note the ordering of the data items is reversed from the above
2339  *  description.
2340  */
2342         .p2align        2       /* force 4-byte alignment */
2343 gdt:
2344         .word   0, 0
2345         .byte   0, 0, 0, 0
2347         /* code segment */
2348         .word   0xFFFF, 0
2349         .byte   0, 0x9A, 0xCF, 0
2351         /* data segment */
2352         .word   0xFFFF, 0
2353         .byte   0, 0x92, 0xCF, 0
2355         /* 16 bit real mode CS */
2356         .word   0xFFFF, 0
2357         .byte   0, 0x9E, 0, 0
2359         /* 16 bit real mode DS */
2360         .word   0xFFFF, 0
2361         .byte   0, 0x92, 0, 0
2364 /* this is the GDT descriptor */
2365 gdtdesc:
2366         .word   0x27                    /* limit */
2367         .long   gdt                     /* addr */