aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING340
-rw-r--r--Change.log68
-rw-r--r--Makefile18
-rw-r--r--ldlinux.asm2821
-rw-r--r--now.pl6
-rw-r--r--syslinux.doc255
6 files changed, 3508 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..bcb6ed52
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+ \ No newline at end of file
diff --git a/Change.log b/Change.log
new file mode 100644
index 00000000..ce737657
--- /dev/null
+++ b/Change.log
@@ -0,0 +1,68 @@
+Changes in 1.31:
+ * Now compiles under Linux, using NASM, rather than using
+ Turbo Assembler under DOS. See http://www.cryogen.com/Nasm
+ for information about NASM.
+ * Perl script to install SYSLINUX under Linux, as well as a
+ rewritten DOS installer (now is written in assembler, so we
+ don't need Turbo C.)
+
+Changes in 1.30:
+ * Added support for loading bzImage and initrd loading, and made
+ SYSLINUX new-setup-code aware (SYSLINUX 1.30 id=0x31).
+ * Added LILO-style kernel labels; see the LABEL and IMPLICIT
+ keywords in README file.
+ * Added support for colorization of intro and help screens.
+ * The vga= option is now handled correctly.
+ * Massive rewrite of large chunks of the code in order to
+ support the first two new features.
+
+Changes in 1.20:
+ * Added simple online help at the "boot:" prompt.
+ * Removed 2880K image as I no longer have access to such a
+ floppy drive. (Donations accepted!!)
+ * Decided to distribute the source in a subdirectory rather
+ than in a nested zipfile.
+
+Changes in 1.11:
+ * Removed a sanity check which would cause booting to fail on
+ Phoenix BIOS version 4.03. Apparently this BIOS is buggy.
+
+Changes in 1.10:
+ * Added configuration file SYSLINUX.CFG. This file contains all
+ configurable options, and can be edited from any OS which can
+ access an MS-DOS filesystem; there is no longer a need to run
+ SYSLINUX.EXE except to write the boot sector.
+ * Default command line now given by "default" line in config
+ file.
+ * LINUXMSG.TXT and BOOTMSG.TXT hard-coded file names replaced by
+ "display" and "prompt" lines in config file.
+ * LILO-style option appending now supported ("append" line in
+ config file).
+ * Prompt timeout is now supported ("timeout" line in config
+ file). The timeout is cancelled when anything is typed on the
+ command line.
+ * Pressing <ESC> or <Ctrl-C> at the Loading... stage now aborts
+ the kernel loading in progress and returns the user to the
+ boot: prompt.
+ * The installer now automatically sets the READONLY flag on
+ LDLINUX.SYS.
+ * Added 2880K disk image.
+
+Changes in 1.03:
+ * Fixed bug that would prevent booting from double-density
+ floppies and other DOS filesystems with multiple sectors per
+ cluster.
+ * Added 720K disk image.
+ * Changed default kernel name on disk images to LINUX.
+
+Changes in 1.02:
+ * Fixed bug that would garble the command line on recent kernels
+ with more than 4 sectors of setup code (this wasn't really a
+ *bug*; rather, a kernel change broke the code. Unfortunately
+ the Linux boot interface is still sorely undocumented).
+ * Added BOOTMSG.TXT file support (message file which does not
+ force display of the boot prompt).
+
+Changes in 1.01:
+ * Fixed bug on some (most?) 386 BIOSes would require two boot
+ attempts.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..95f6174f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+NASM = nasm
+
+all: bootsect.bin ldlinux.sys
+
+ldlinux.bin: ldlinux.asm
+ $(NASM) -f bin -dHEX_TIME="`perl now.pl`" -l ldlinux.lst -o ldlinux.bin ldlinux.asm
+
+bootsect.bin: ldlinux.bin
+ dd if=ldlinux.bin of=bootsect.bin bs=512 count=1
+
+ldlinux.sys:
+ dd if=ldlinux.bin of=ldlinux.sys bs=512 skip=1
+
+clean:
+ rm -f *.bin *.lst *.sys
+
+distclean: clean
+ rm -f *~ \#*
diff --git a/ldlinux.asm b/ldlinux.asm
new file mode 100644
index 00000000..fe1d5769
--- /dev/null
+++ b/ldlinux.asm
@@ -0,0 +1,2821 @@
+; ****************************************************************************
+;
+; LDLINUX.ASM
+;
+; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
+; functionality is good to have for installation floppies, where it may
+; be hard to find a functional Linux system to run LILO off.
+;
+; This program allows manipulation of the disk to take place entirely
+; from MS-LOSS, and can be especially useful in conjunction with the
+; umsdos filesystem.
+;
+; This file is loaded in stages; first the boot sector at offset 7C00h,
+; then the first sector (cluster, really, but we can only assume 1 sector)
+; of LDLINUX.SYS at 7E00h and finally the remainder of LDLINUX.SYS at 8000h
+;
+; Copyright (C) 1994-1998 H. Peter Anvin
+;
+; This code is free software under the terms of the GNU General Public
+; License, version 2, or at your option any later version.
+;
+; ****************************************************************************
+
+;
+; Some semi-configurable constants... change on your own risk
+;
+max_cmd_len equ 2047 ; Must be odd; 2047 is the kernel limit
+retry_count equ 6 ; How patient are we with the disk?
+
+;
+; Should be updated with every release to avoid bootsector/SYS file mismatch
+;
+%define version_str '1.31' ; Must be 4 characters long!
+;%define date '1998-01-31' ; Must be 10 characters long!
+%define date HEX_TIME ; Used while developing
+%define year '1998'
+;
+; Debgging stuff
+;
+; %define debug 1 ; Uncomment to enable debugging
+;
+; ID for SYSLINUX (reported to kernel)
+;
+syslinux_id equ 031h ; SYSLINUX (3) version 1.x (1)
+;
+; Segments used by Linux
+;
+real_mode_seg equ 9000h
+ struc real_mode_seg_t
+ resb 20h-($-$$) ; org 20h
+kern_cmd_magic resw 1 ; Magic # for command line
+kern_cmd_offset resw 1 ; Offset for kernel command line
+ resb 497-($-$$) ; org 497d
+bs_setupsecs resb 1 ; Sectors for setup code (0 -> 4)
+bs_rootflags resw 1 ; Root readonly flag
+bs_syssize resw 1
+bs_swapdev resw 1 ; Swap device (obsolete)
+bs_ramsize resw 1 ; Ramdisk flags, formerly ramdisk size
+bs_vidmode resw 1 ; Video mode
+bs_rootdev resw 1 ; Root device
+bs_bootsign resw 1 ; Boot sector signature (0AA55h)
+su_jump resb 1 ; 0EBh
+su_jump2 resb 1
+su_header resd 1 ; New setup code: header
+su_version resw 1 ; See linux/arch/i386/boot/setup.S
+su_switch resw 1
+su_setupseg resw 1
+su_startsys resw 1
+su_kver resw 1 ; Kernel version pointer
+su_loader resb 1 ; Loader ID
+su_loadflags resb 1 ; Load high flag
+su_movesize resw 1
+su_code32start resd 1 ; Start of code loaded high
+su_ramdiskat resd 1 ; Start of initial ramdisk
+su_ramdisklen equ $ ; Length of initial ramdisk
+su_ramdisklen1 resw 1
+su_ramdisklen2 resw 1
+su_bsklugeoffs resw 1
+su_bsklugeseg resw 1
+su_heapend resw 1
+ resb (8000h-12)-($-$$) ; Were bootsect.S puts it...
+linux_stack equ $
+linux_fdctab equ $
+ resb 8000h-($-$$)
+cmd_line_here equ $ ; Should be out of the way
+ endstruc
+
+setup_seg equ 9020h
+ struc setup_seg_t
+ org 0h ; as 9020:0000, not 9000:0200
+setup_entry equ $
+ endstruc
+
+;
+; Magic number of su_header field
+;
+HEADER_ID equ 'HdrS' ; HdrS (in littleendian hex)
+;
+; Flags for the su_loadflags field
+;
+LOAD_HIGH equ 01h ; Large kernel, load high
+CAN_USE_HEAP equ 80h ; Boot loader reports heap size
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for up to 16 of these, we
+; stick them at 8000:0000 and copy them down before we need them.
+;
+; Note: this structure can be added to, but must be less than 4K in size.
+;
+ struc vkernel
+vk_vname: resb 11 ; Virtual name **MUST BE FIRST!**
+ resb 3 ; Alignment filler
+vk_rname: resb 11 ; Real name
+ resb 1 ; Filler
+vk_appendlen: resw 1
+vk_append: resb max_cmd_len+1 ; Command line
+vk_size: equ $
+ endstruc
+;
+; Segment assignments in the bottom 640K
+; 0000h - main code/data segment (and BIOS segment)
+; 9000h - real_mode_seg
+;
+vk_seg equ 8000h ; This is where we stick'em
+xfer_buf_seg equ 7000h ; Bounce buffer for I/O to high mem
+
+
+;
+; For our convenience: define macros for jump-over-unconditinal jumps
+;
+%macro jmpz 1
+ jnz %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpnz 1
+ jz %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpe 1
+ jne %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpne 1
+ je %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpc 1
+ jnc %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpnc 1
+ jc %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpb 1
+ jnb %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+%macro jmpnb 1
+ jb %%skip
+ jmp %1
+%%skip:
+%endmacro
+
+;
+; Macros similar to res[bwd], but which works in the code segment (after
+; section .text)
+;
+%macro zb 1
+ times %1 db 0
+%endmacro
+
+%macro zw 1
+ times %1 dw 0
+%endmacro
+
+%macro zd 1
+ times %1 dd 0
+%endmacro
+
+; ---------------------------------------------------------------------------
+; BEGIN THE BIOS/CODE/DATA SEGMENT
+; ---------------------------------------------------------------------------
+ absolute 4*1Eh ; In the interrupt table
+fdctab equ $
+fdctab1 resw 1
+fdctab2 resw 1
+
+%ifdef debug
+ org 0100h
+..start:
+;
+; Hook for debugger stuff. This gets automatically removed when
+; generating the real thing.
+;
+; Initialize the registers for debugger operation
+;
+ cli
+ mov ax,cs
+ mov ds,ax
+ mov es,ax
+ mov ss,ax
+ mov sp,StackBuf
+ sti
+ cld
+;
+; Load the actual boot sector so we can copy the data block
+;
+ xor ax,ax ; Reset floppy
+ xor dx,dx
+ int 13h
+ mov cx,6 ; Retry count...
+debug_tryloop: push cx
+ mov bx,trackbuf
+ mov cx,0001h
+ xor dx,dx
+ mov ax,0201h
+ int 13h
+ pop cx
+ jnc debug_okay
+ loop debug_tryloop
+ int 3 ; Halt! (Breakpoint)
+debug_okay: mov si,trackbuf+0bh
+ mov di,bsBytesPerSec
+ mov cx,33h
+ rep movsb
+;
+; Save bogus "BIOS floppy block" info to the stack in case we hit kaboom
+;
+ push si
+ push si
+ push si ; Writing to the trackbuf is harmless
+;
+; Copy the BIOS data area
+;
+ push ds
+ xor ax,ax
+ mov ds,ax
+ mov si,0400h
+ mov di,si
+ mov cx,0100h
+ rep movsw
+ pop ds
+;
+;
+; A NOP where we can breakpoint, then jump into the code *after*
+; the segment register initialization section
+;
+ nop
+ jmp debugentrypt
+%endif
+
+ absolute 0484h
+BIOS_vidrows resb 1 ; Number of screen rows
+
+ absolute 0600h
+trackbuf equ $ ; Track buffer goes here
+trackbufsize equ 16384 ; Safe size of track buffer
+; trackbuf ends at 4600h
+
+ absolute 6000h ; Here we keep our BSS stuff
+StackBuf equ $ ; Start the stack here (grow down)
+VKernelBuf: resb vk_size ; "Current" vkernel
+ alignb 4
+AppendBuf resb max_cmd_len+1 ; append=
+FKeyName resb 10*16 ; File names for F-key help
+NumBuf resb 16 ; Buffer to load number
+NumBufEnd equ NumBuf+15 ; Pointer to last byte in NumBuf
+RootDir equ $ ; Location of root directory
+RootDir1 resw 1
+RootDir2 resw 1
+DataArea equ $ ; Location of data area
+DataArea1 resw 1
+DataArea2 resw 1
+FBytes equ $ ; Used by open/getc
+FBytes1 resw 1
+FBytes2 resw 1
+RootDirSize resw 1 ; Root dir size in sectors
+DirScanCtr resw 1 ; Used while searching directory
+DirBlocksLeft resw 1 ; Ditto
+EndofDirSec resw 1 ; = trackbuf+bsBytesPerSec-31
+RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
+ClustSize resw 1 ; Bytes/cluster
+SecPerClust resw 1 ; Same as bsSecPerClust, but a word
+BufSafe resw 1 ; Clusters we can load into trackbuf
+BufSafeSec resw 1 ; = how many sectors?
+BufSafeBytes resw 1 ; = how many bytes?
+EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
+HighMemSize resw 1 ; High memory (K)
+KernelClust resw 1 ; Kernel size in clusters
+KernelK resw 1 ; Kernel size in kilobytes
+InitRDClust resw 1 ; Ramdisk size in clusters
+InitRDat resw 1 ; Load address (x256)
+ClustPerMoby resw 1 ; Clusters per 64K
+FClust resw 1 ; Number of clusters in open/getc file
+FNextClust resw 1 ; Pointer to next cluster in d:o
+FPtr resw 1 ; Pointer to next char in buffer
+CmdOptPtr resw 1 ; Pointer to first option on cmd line
+KernelCNameLen resw 1 ; Length of unmangled kernel name
+InitRDCNameLen resw 1 ; Length of unmangled initrd name
+NextCharJump resw 1 ; Routine to interpret next print char
+HiLoadAddr resw 1 ; Address pointer for high load loop
+TextAttrBX equ $
+TextAttribute resb 1 ; Text attribute for message file
+TextPage resb 1 ; Active display page
+CursorDX equ $
+CursorCol resb 1 ; Cursor column for message file
+CursorRow resb 1 ; Cursor row for message file
+ScreenSize equ $
+VidCols resb 1 ; Columns on screen-1
+VidRows resb 1 ; Rows on screen-1
+RetryCount resb 1 ; Used for disk access retries
+KbdFlags resb 1 ; Check for keyboard escapes
+MNameBuf resb 11 ; Generic mangled file name buffer
+KernelName resb 11 ; Mangled name for kernel
+InitRD resb 11 ; initrd= mangled name
+KernelCName resb 13 ; Unmangled kernel name
+InitRDCName resb 13 ; Unmangled initrd name
+
+ section .text
+ org 7C00h
+bootsec equ $
+ jmp short start ; 2 bytes
+ nop ; 1 byte
+;
+; "Superblock" follows -- it's in the boot sector, so it's already
+; loaded and ready for us
+;
+bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
+bsBytesPerSec zw 1
+bsSecPerClust zb 1
+bsResSectors zw 1
+bsFATs zb 1
+bsRootDirEnts zw 1
+bsSectors zw 1
+bsMedia zb 1
+bsFATsecs zw 1
+bsSecPerTrack zw 1
+bsHeads zw 1
+bsHiddenSecs equ $
+bsHidden1 zw 1
+bsHidden2 zw 1
+bsHugeSectors equ $
+bsHugeSec1 zw 1
+bsHugeSec2 zw 1
+bsDriveNumber zb 1
+bsReserved1 zb 1
+bsBootSignature zb 1 ; 29h if the following fields exist
+bsVolumeID zd 1
+bsVolumeLabel zb 11
+bsFileSysType zb 8 ; Must be FAT12 for this version
+;
+; Note we don't check the constraints above now; we did that at install
+; time (we hope!)
+;
+
+floppy_table equ $ ; No sense in wasting memory, overwrite start
+
+start:
+ cli ; No interrupts yet, please
+ xor ax,ax
+ mov es,ax
+ mov ss,ax
+ mov sp,StackBuf ; Just below BSS
+;
+; Now sautee the BIOS floppy info block to that it will support decent-
+; size transfers; the floppy block is 11 bytes and is stored in the
+; INT 1Eh vector (brilliant waste of resources, eh?)
+;
+; Of course, if BIOSes had been properly programmed, we wouldn't have
+; had to waste precious boot sector space with this code.
+;
+ mov bx,fdctab
+ lds si,[ss:bx] ; DS:SI -> original
+ push ds ; Save on stack in case
+ push si ; we have to bail
+ push bx
+ mov cx,6 ; 12 bytes
+ mov di,floppy_table
+ push di
+ cld
+ rep movsw ; Faster to move words
+ pop di
+ mov ds,ax ; Now we can point DS to here, too
+ mov cl,[bsSecPerTrack] ; Patch the sector count
+ mov [di+4],cl
+; mov byte ptr [di+9],0Fh ; I have no clue what this does???
+ mov [bx+2],ax ; Segment 0
+ mov [bx],di ; offset floppy_block
+;
+; Ready to enable interrupts, captain
+;
+ sti
+;
+; Reset floppy system to load new parameter block
+;
+ xor dx,dx
+ int 13h ; AH = 00h already
+;
+; Now we have to do some arithmetric to figure out where things are located.
+; If Microsoft had had brains they would already have done this for us,
+; and stored it in the superblock at format time, but here we go,
+; wasting precious boot sector space again...
+;
+debugentrypt:
+ mov al,[bsFATs] ; Number of FATs
+ jmpc kaboom ; If the floppy init failed
+ ; (too far to be above the mov)
+ cbw ; Clear AH
+ mul byte [bsFATsecs] ; Get the size of the FAT area
+ add ax,[bsHidden1] ; Add hidden sectors
+ adc dx,[bsHidden2]
+ add ax,[bsResSectors] ; And reserved sectors (why two?)
+ adc dx,byte 0
+
+ mov [RootDir1],ax ; Location of root directory
+ mov [RootDir2],dx
+ mov [DataArea1],ax
+ mov [DataArea2],dx
+ push ax
+ push dx
+
+ mov ax,32 ; Size of a directory entry
+ mul word [bsRootDirEnts]
+ mov bx,[bsBytesPerSec]
+ add ax,bx ; Round up, not down
+ dec ax
+ div bx ; Now we have the size of the root dir
+ mov [RootDirSize],ax
+ mov [DirScanCtr],ax
+ add bx,trackbuf-31
+ mov [EndofDirSec],bx ; End of a single directory sector
+
+ add [DataArea1],ax ; Now we have the location of the
+ adc word [DataArea2],byte 0 ; first data cluster
+;
+; Now the fun begins. We have to search the root directory for
+; LDLINUX.SYS and load the first sector, so we have a little more
+; space to have fun with. Then we can go chasing through the FAT.
+; Joy!!
+;
+sd_nextsec: pop dx
+ pop ax
+ push ax
+ push dx
+ mov bx,trackbuf
+ call getonesec
+ mov si,trackbuf
+sd_nextentry: cmp byte [si],0 ; Directory high water mark
+jz_kaboom: jz kaboom
+ mov di,ldlinux_sys
+ mov cx,11
+ push si
+ repe cmpsb
+ pop si
+ je found_it
+ add si,byte 32 ; Distance to next
+ cmp si,[EndofDirSec]
+ jb sd_nextentry
+ add ax,byte 1
+ adc dx,byte 0
+ dec word [DirScanCtr]
+ jnz sd_nextsec
+;
+; kaboom: write a message and bail out.
+;
+kaboom:
+ mov si,bailmsg
+ call writestr ; Returns with AL = 0
+ cbw ; Sets AH = 0 (shorter than XOR)
+ int 16h ; Wait for keypress
+ mov sp,StackBuf-2*3 ; Reset stack
+ pop si ; BIOS floppy block address
+ cli
+ pop word [si] ; Restore location
+ pop word [si+2]
+ sti
+ int 19h ; And try once more to boot...
+norge: jmp short norge ; If int 19h returned... oh boy...
+
+;
+; found_it: now we compute the location of the first sector, then
+; load it and JUMP (since we're almost out of space)
+;
+found_it: pop ax
+ pop ax
+ mov al,[bsSecPerClust]
+ cbw ; We won't have 128 sec/cluster
+ mov bp,ax ; Load an entire cluster
+ mov bx,[si+26] ; First cluster
+ push bx ; Remember which cluster it was
+ dec bx ; First cluster is cluster 2
+ dec bx
+ mul bx
+ add ax,[DataArea1]
+ adc dx,[DataArea2]
+ mov bx,ldlinux_magic
+ call getlinsec
+ mov si,bs_magic
+ mov di,ldlinux_magic
+ mov cx,[magic_len]
+ repe cmpsb ; Make sure that the bootsector
+ jne kaboom
+ jmp ldlinux_ent ; matches LDLINUX.SYS
+;
+; writestr: write a null-terminated string to the console
+;
+writestr:
+wstr_1: lodsb
+ and al,al
+ jz return
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; White on black, current page
+ int 10h
+ jmp short wstr_1
+
+;
+; disk_error: decrement the retry count and bail if zero
+;
+disk_error: dec si ; SI holds the disk retry counter
+ jz kaboom
+ xchg ax,bx ; Shorter than MOV
+ pop bx
+ pop cx
+ pop dx
+ jmp short disk_try_again
+;
+; getonesec: like getlinsec, but pre-sets the count to 1
+;
+getonesec:
+ mov bp,1
+ ; Fall through to getlinsec
+
+;
+; getlinsec: load a sequence of BP floppy sector given by the linear sector
+; number in DX:AX into the buffer at ES:BX. We try to optimize
+; by loading up to a whole track at a time, but the user
+; is responsible for not crossing a 64K boundary.
+; (Yes, BP is weird for a count, but it was available...)
+;
+; On return, BX points to the first byte after the transferred
+; block.
+;
+getlinsec:
+ mov si,[bsSecPerTrack]
+ div si ; Convert linear to sector/track
+ mov cx,dx ; Save sector
+ xor dx,dx ; 32-bit track number
+ div word [bsHeads] ; Convert track to head/cyl
+ ;
+ ; Now we have AX = cyl, DX = head, CX = sector (0-based)
+ ; for the very first sector, SI = bsSecPerTrack
+ ;
+gls_nexttrack: push si
+ push bp
+ sub si,cx ; Sectors left on track
+ cmp bp,si
+ jna gls_lasttrack
+ mov bp,si ; No more than a trackful, please!
+gls_lasttrack: push ax ; Cylinder #
+ push dx ; Head #
+ push bp ; Number of sectors we're transferring
+
+ push cx
+ mov cl,6 ; Because IBM was STOOPID
+ shl ah,cl ; and thought 8 bits was enough
+ ; then thought 10 bits was enough...
+ pop cx ; Sector #
+ inc cx ; Sector numbers are 1-based
+ or cl,ah
+ mov ch,al
+ mov dh,dl
+ mov dl,[bsDriveNumber]
+ xchg ax,bp ; Sector to transfer count
+ ; (xchg shorter than mov)
+ mov ah,02h ; Read it!
+;
+; Do the disk transfer... save the registers in case we fail :(
+;
+ mov si,retry_count ; # of times to retry a disk access
+disk_try_again: push dx
+ push cx
+ push bx
+ push ax
+ push si
+ int 13h
+ pop si
+ pop bx
+ jc disk_error
+;
+; It seems the following test fails on some machines (buggy BIOS?)
+;
+; cmp al,bl ; Check that we got what we asked for
+; jne disk_error
+;
+; Disk access successful
+;
+ pop bx ; Buffer location
+ pop si ; Not needed anymore
+ pop si ; Neither is this
+ pop si ; Sector transferred count
+ mov ax,si ; Reduce sector left count
+ mul word [bsBytesPerSec] ; Figure out how much to advance ptr
+ add bx,ax ; Update buffer location
+ pop dx ; Head #
+ pop ax ; Cyl #
+ inc dx ; Next track on cyl
+ cmp dx,[bsHeads] ; Was this the last one?
+ jb gls_nonewcyl
+ inc ax ; If so, new cylinder
+ xor dx,dx ; First head on new cylinder
+gls_nonewcyl: pop bp ; Sectors left to transfer
+ xor cx,cx ; First sector on new track
+ sub bp,si ; Reduce with # of sectors just read
+ pop si
+ ja gls_nexttrack
+return: ret
+
+bailmsg db 'Boot failed: change disks and press any key', 0Dh, 0Ah, 0
+
+bs_checkpt equ $ ; Must be <= 1E5h
+
+ zb 1E3h-($-$$)
+bs_magic equ $ ; The following 32 bytes should
+ ; match ldlinux_magic
+ldlinux_sys db 'LDLINUX SYS' ; Looks like this in the root dir
+ db ' '
+bs_version db version_str
+ db ' '
+bs_date db date
+magic_len equ $-bs_magic
+
+bootsignature dw 0AA55h
+
+;
+; ===========================================================================
+; End of boot sector
+; ===========================================================================
+; Start of LDLINUX.SYS
+; ===========================================================================
+;
+; This "magic number" works well with the "type" command... :-)
+;
+ldlinux_magic db 'LDLINUX'
+missing_dot db ' '
+ db 'SYS ', version_str, ' ', date
+magic_eof db 0Dh, 0Ah, 01Ah
+
+ zb 220h-($-$$)
+ldlinux_ent:
+;
+; The boot sector left the cluster number of this first LDLINUX.SYS
+; sector on the stack. We'll need it later, so we should pop it off
+;
+ pop word [RunLinClust]
+;
+; Tell the user we got this far
+;
+ mov si,crlf
+ call writestr
+ mov byte [missing_dot],'.'
+ mov byte [magic_eof],0
+ mov si,ldlinux_magic
+ call writestr
+;
+; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
+; We can really only rely on a single sector having been loaded. Hence
+; we should load the FAT into RAM and start chasing pointers...
+;
+ mov bx,FAT ; Where it goes in memory
+ mov ax,[bsHidden1] ; Hidden sectors
+ mov dx,[bsHidden2]
+ add ax,[bsResSectors] ; plus reserved sectors = FAT
+ adc dx,0
+ mov bp,[bsFATsecs] ; Sectors/FAT
+ call getlinsec ; Load it in...
+;
+; Fine, now we have the FAT in memory. How big is a cluster, really?
+; Also figure out how many clusters will fit in an 8K buffer, and how
+; many sectors and bytes that is
+;
+ mov al,[bsSecPerClust] ; We do this in the boot
+ xor ah,ah ; sector, too, but there
+ mov [SecPerClust],ax ; wasn't space to save it
+ mul word [bsBytesPerSec]
+ mov [ClustSize],ax ; Bytes/cluster
+ mov bx,ax
+ mov ax,trackbufsize
+ xor dx,dx
+ div bx
+ mov [BufSafe],ax ; # of cluster in trackbuf
+ mul word [SecPerClust]
+ mov [BufSafeSec],ax
+ mul word [bsBytesPerSec]
+ mov [BufSafeBytes],ax
+ add ax,getcbuf ; getcbuf is same size as
+ mov [EndOfGetCBuf],ax ; trackbuf, for simplicity
+;
+; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
+; cluster again, though.
+;
+ mov bx,ldlinux_magic
+ add bx,[ClustSize]
+ mov si,[RunLinClust]
+ call nextcluster
+ xor dx,dx
+ mov ax,ldlinux_len-1 ; To be on the safe side
+ add ax,[ClustSize]
+ div word [ClustSize] ; the number of clusters
+ dec ax ; We've already read one
+ jz all_read_jmp
+ mov cx,ax
+ call getfssec
+;
+; All loaded up
+;
+all_read_jmp:
+ mov si,copyright_str
+ call writestr
+ jmp all_read
+;
+; -----------------------------------------------------------------------------
+; Subroutines that have to be in the first sector
+; -----------------------------------------------------------------------------
+;
+; getfssec: Get multiple clusters from a file, given the starting cluster.
+;
+; This routine makes sure the subtransfers do not cross a 64K boundary,
+; and will correct the situation if it does, UNLESS *sectors* cross
+; 64K boundaries.
+;
+; ES:BX -> Buffer
+; SI -> Starting cluster number (2-based)
+; CX -> Cluster count (0FFFFh = until end of file)
+;
+ ; 386 check
+getfssec:
+getfragment: xor bp,bp ; Fragment sector count
+ mov ax,si ; Get sector address
+ dec ax ; Convert to 0-based
+ dec ax
+ mul word [SecPerClust]
+ add ax,[DataArea1]
+ adc dx,[DataArea2]
+getseccnt: ; See if we can read > 1 clust
+ add bp,[SecPerClust]
+ dec cx ; Reduce clusters left to find
+ mov di,si ; Predict next cluster
+ inc di
+ call nextcluster
+ jc gfs_eof ; At EOF?
+ jcxz endfragment ; Or was it the last we wanted?
+ cmp si,di ; Is file continuous?
+ jz getseccnt ; Yes, we can get
+endfragment: clc ; Not at EOF
+gfs_eof: pushf ; Remember EOF or not
+ push si
+ push cx
+gfs_getchunk:
+ push ax
+ push dx
+ mov ax,es ; Check for 64K boundaries.
+ shl ax,1 ; This really belongs in
+ shl ax,1 ; getlinsec, but it would
+ shl ax,1 ; make it not fit in the boot
+ shl ax,1 ; sector.
+ add ax,bx
+ xor dx,dx
+ neg ax
+ jnz gfs_partseg
+ inc dx ; Full 64K segment
+gfs_partseg:
+ div word [bsBytesPerSec] ; How many sectors fit?
+ mov si,bp
+ sub si,ax ; Compute remaining sectors
+ jbe gfs_lastchunk
+ mov bp,ax
+ pop dx
+ pop ax
+ push si ; Save remaining sector count
+ push ax ; Save position
+ push dx
+ push bp ; Save sectors to transfer
+ call getlinsec
+ pop bp
+ pop dx
+ pop ax
+ add ax,bp ; Advance sector pointer
+ adc dx,0
+ pop bp ; Load remaining sector counter
+ jmp gfs_getchunk
+gfs_lastchunk: pop dx
+ pop ax
+ call getlinsec
+ pop cx
+ pop si
+ popf
+ jcxz gfs_return ; If we hit the count limit
+ jnc getfragment ; If we didn't hit EOF
+gfs_return: ret
+
+;
+; nextcluster: Advance a cluster pointer in SI to the next cluster
+; pointed at in the FAT tables (note: FAT12 assumed)
+; Sets CF on return if end of file.
+;
+nextcluster:
+ push bx
+ mov bx,si ; Multiply by 3/2
+ shr bx,1
+ pushf ; CF now set if odd
+ add si,bx
+ mov si,[FAT+si]
+ popf
+ jnc nc_even
+ shr si,1 ; Needed for odd only
+ shr si,1
+ shr si,1
+ shr si,1
+nc_even:
+ and si,0FFFh
+ cmp si,0FF0h ; Clears CF if at end of file
+ cmc ; But we want it SET...
+ pop bx
+nc_return: ret
+
+;
+; Debug routine
+;
+%ifdef debug
+safedumpregs:
+ cmp word [Debug_Magic],0D00Dh
+ jnz nc_return
+ jmp dumpregs
+%endif
+
+;
+; Data that has to be in the first sector
+;
+copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
+crlf db 0Dh, 0Ah, 0
+
+rl_checkpt equ $ ; Must be <= 400h
+
+; ----------------------------------------------------------------------------
+; End of code and data that have to be in the first sector
+; ----------------------------------------------------------------------------
+
+all_read:
+;
+; Check that no moron is trying to boot Linux on a 286 or so. According
+; to Intel, the way to check is to see if the high 4 bits of the FLAGS
+; register are either all stuck at 1 (8086/8088) or all stuck at 0
+; (286 in real mode), if not it is a 386 or higher. They didn't
+; say how to check for a 186/188, so I *hope* it falls out as a 8086
+; or 286 in this test.
+;
+; Also, provide an escape route in case it doesn't work.
+;
+check_escapes:
+ mov ah,02h ; Check keyboard flags
+ int 16h
+ mov [KbdFlags],al ; Save for boot prompt check
+ test al,04h ; Ctrl->skip 386 check
+ jnz skip_checks
+test_8086:
+ pushf ; Get flags
+ pop ax
+ and ax,0FFFh ; Clear top 4 bits
+ push ax ; Load into FLAGS
+ popf
+ pushf ; And load back
+ pop ax
+ and ax,0F000h ; Get top 4 bits
+ cmp ax,0F000h ; If set -> 8086/8088
+ je not_386
+test_286:
+ pushf ; Get flags
+ pop ax
+ or ax,0F000h ; Set top 4 bits
+ push ax
+ popf
+ pushf
+ pop ax
+ and ax,0F000h ; Get top 4 bits
+ jnz is_386 ; If not clear -> 386
+not_386:
+ mov si,err_not386
+ call writestr
+ jmp kaboom
+is_386:
+ .386 ; Now we know it's a 386
+;
+; Now check that there is at least 608K of low (DOS) memory
+; (608K = 9800h segments)
+;
+ int 12h
+ cmp ax,608
+ jae enough_ram
+ mov si,err_noram
+ call writestr
+ jmp kaboom
+enough_ram:
+skip_checks:
+;
+; Initialization that does not need to go into the any of the pre-load
+; areas
+;
+ mov al,[BIOS_vidrows]
+ and al,al
+ jnz vidrows_is_ok
+ mov al,25 ; No vidrows in BIOS, assume 25
+vidrows_is_ok: mov [VidRows],al
+ mov ah,0fh
+ int 10h ; Read video state
+ mov [TextPage],bh
+ dec ah ; Store count-1 (same as rows)
+ mov [VidCols],ah
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out.
+;
+ mov si,linuxauto_cmd ; Default command: "linux auto"
+ mov di,default_cmd
+ mov cx,linuxauto_len
+ rep movsb
+;
+; Load configuration file
+;
+ mov di,syslinux_cfg
+ call open
+ jmpz no_config_file
+parse_config:
+ call getkeyword
+ jmpc end_config_file ; Config file loaded
+ cmp ax,'de' ; DEfault
+ je pc_default
+ cmp ax,'ap' ; APpend
+ je pc_append
+ cmp ax,'ti' ; TImeout
+ jmpe pc_timeout
+ cmp ax,'pr' ; PRompt
+ jmpe pc_prompt
+ cmp ax,'di' ; DIsplay
+ jmpe pc_display
+ cmp ax,'la' ; LAbel
+ jmpe pc_label
+ cmp ax,'ke' ; KErnel
+ je pc_kernel
+ cmp ax,'im' ; IMplicit
+ jmpe pc_implicit
+ cmp al,'f' ; F-key
+ jmpe pc_fkey
+ jmp parse_config
+pc_default: mov di,default_cmd ; "default" command
+ call getline
+ mov si,auto_cmd ; add "auto"+null
+ mov cx,auto_len
+ rep movsb
+ jmp parse_config
+pc_append: cmp word [VKernelCtr],0 ; "append" command
+ ja pc_append_vk
+ mov di,AppendBuf
+ call getline
+ sub di,AppendBuf
+pc_app1: mov [AppendLen],di
+ jmp parse_config
+pc_append_vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel)
+ call getline
+ sub di,VKernelBuf+vk_append
+ cmp di,2
+ jne pc_app2
+ cmp byte [VKernelBuf+vk_append],'-'
+ jne pc_app2
+ mov di,0 ; If "append -" -> null string
+pc_app2: mov [VKernelBuf+vk_appendlen],di
+ jmp parse_config
+pc_kernel: cmp word [VKernelCtr],0 ; "kernel" command
+ jmpe parse_config ; (vkernel only)
+ mov di,trackbuf
+ push di
+ call getline
+ pop si
+ mov di,VKernelBuf+vk_rname
+ call mangle_name
+ jmp parse_config
+pc_timeout: call getint ; "timeout" command
+ jmpc parse_config
+ mov ax,0D215h ; There are approx 1.D215h
+ mul bx ; clock ticks per 1/10 s
+ add bx,dx
+ mov [KbdTimeOut],bx
+ jmp parse_config
+pc_display: mov di,trackbuf
+ push di
+ call getline ; Get filename to display
+ pop si
+ mov di,MNameBuf ; Mangled name buffer
+ push di
+ call mangle_name ; Mangle file name
+ pop di
+ call searchdir ; Search for file
+ jmpz parse_config ; File not found?
+ call get_msg_file ; Load and display file
+pc_parse_config: jmp parse_config
+pc_prompt: call getint ; "prompt" command
+ jc pc_parse_config
+ mov [ForcePrompt],bx
+ jmp short pc_parse_config
+pc_implicit: call getint ; "implicit" command
+ jc pc_parse_config
+ mov [AllowImplicit],bx
+ jmp short pc_parse_config
+pc_fkey: sub ah,'1'
+ jnb pc_fkey1
+ mov ah,9 ; F10
+pc_fkey1: xor cx,cx
+ mov cl,ah
+ push cx
+ mov ax,1
+ shl ax,cl
+ or [FKeyMap], ax ; Mark that we have this loaded
+ mov di,trackbuf
+ push di
+ call getline ; Get filename to display
+ pop si
+ pop di
+ shl di,4 ; Multiply number by 16
+ add di,FKeyName
+ call mangle_name ; Mangle file name
+ jmp short pc_parse_config
+pc_label: call commit_vk ; Commit any current vkernel
+ mov di,trackbuf ; Get virtual filename
+ push di
+ call getline
+ pop si
+ mov di,VKernelBuf+vk_vname
+ call mangle_name ; Mangle virtual name
+ inc word [VKernelCtr] ; One more vkernel
+ mov si,VKernelBuf+vk_vname ; By default, rname == vname
+ mov di,VKernelBuf+vk_rname
+ mov cx,11
+ rep movsb
+ mov si,AppendBuf ; Default append==global append
+ mov di,VKernelBuf+vk_append
+ mov cx,[AppendLen]
+ mov [VKernelBuf+vk_appendlen],cx
+ rep movsb
+ jmp short pc_parse_config
+;
+; commit_vk: Store the current VKernelBuf into buffer segment
+;
+commit_vk:
+ cmp word [VKernelCtr],0
+ je cvk_ret ; No VKernel = return
+ cmp word [VKernelCtr],16 ; Above limit?
+ ja cvk_overflow
+ mov di,[VKernelCtr]
+ dec di
+ shl di,12 ; 4K/buffer
+ mov si,VKernelBuf
+ mov cx,1024 ; = 4K bytes
+ push es
+ push word vk_seg
+ pop es
+ rep movsd ; Copy to buffer segment
+ pop es
+cvk_ret: ret
+cvk_overflow: mov word [VKernelCtr],16 ; No more than 16, please
+ ret
+
+;
+; End of configuration file
+;
+end_config_file:
+ call commit_vk ; Commit any current vkernel
+no_config_file:
+;
+; Check whether or not we are supposed to display the boot prompt.
+;
+check_for_key:
+ cmp word [ForcePrompt],0 ; Force prompt?
+ jnz enter_command
+ test byte [KbdFlags],5Bh ; Caps, Scroll, Shift, Alt
+ jmpz auto_boot ; If neither, default boot
+
+enter_command:
+ mov si,boot_prompt
+ call writestr
+
+ mov di,command_line
+;
+; get the very first character -- we can either time
+; out, or receive a character press at this time. Some dorky BIOSes stuff
+; a return in the buffer on bootup, so wipe the keyboard buffer first.
+;
+clear_buffer: mov ah,1 ; Check for pending char
+ int 16h
+ jz get_char_time
+ xor ax,ax ; Get char
+ int 16h
+ jmp clear_buffer
+get_char_time: mov cx,[KbdTimeOut]
+ and cx,cx
+ jz get_char ; Timeout == 0 -> no timeout
+ inc cx ; The first loop will happen
+ ; immediately as we don't
+ ; know the appropriate DX value
+time_loop: push cx
+tick_loop: push dx
+ mov ah,1 ; Check for pending keystroke
+ int 16h
+ jnz get_char_pop
+ xor ax,ax
+ int 1Ah ; Get time "of day"
+ pop ax
+ cmp dx,ax ; Has the timer advanced?
+ je tick_loop
+ pop cx
+ loop time_loop ; If so, decrement counter
+ jmp command_done ; Timeout!
+get_char_pop: pop eax ; Clear the stack
+get_char: xor ax,ax ; Get char
+ int 16h
+ and al,al
+ jz func_key
+ cmp al,' ' ; ASCII?
+ jb not_ascii
+ ja enter_char
+ cmp di,command_line ; Space must not be first
+ je get_char
+enter_char: cmp di,max_cmd_len+command_line ; Check there's space
+ jnb get_char
+ stosb ; Save it
+ call writechr ; Echo to screen
+ jmp get_char
+not_ascii: cmp al,0Dh ; Enter
+ je command_done
+ cmp al,08h ; Backspace
+ jne get_char
+ cmp di,command_line ; Make sure there is anything
+ je get_char ; to erase
+ dec di ; Unstore one character
+ mov si,wipe_char ; and erase it from the screen
+ call writestr
+ jmp get_char
+func_key:
+ push di
+ cmp ah,68 ; F10
+ ja get_char
+ sub ah,59 ; F1
+ jb get_char
+ mov cl,ah
+ shr ax,4 ; Convert to x16
+ mov bx,1
+ shl bx,cl
+ and bx,[FKeyMap]
+ jz get_char ; Undefined F-key
+ mov di,ax
+ add di,FKeyName
+ call searchdir
+ jz fk_nofile
+ call get_msg_file
+ jmp fk_wrcmd
+fk_nofile:
+ mov si,crlf
+ call writestr
+fk_wrcmd:
+ mov si,boot_prompt
+ call writestr
+ pop di ; Command line write pointer
+ push di
+ mov byte [di],0 ; Null-terminate command line
+ mov si,command_line
+ call writestr ; Write command line so far
+ pop di
+ jmp get_char
+auto_boot:
+ mov si,default_cmd
+ mov di,command_line
+ mov cx,(max_cmd_len+4) >> 2
+ rep movsd
+ jmp load_kernel
+command_done:
+ mov si,crlf
+ call writestr
+ cmp di,command_line ; Did we just hit return?
+ je auto_boot
+ xor al,al ; Store a final null
+ stosb
+
+load_kernel: ; Load the kernel now
+;
+; First we need to mangle the kernel name the way DOS would...
+;
+ mov si,command_line
+ mov di,KernelName
+ push si
+ push di
+ call mangle_name
+ pop di
+ pop si
+;
+; Fast-forward to first option (we start over from the beginning, since
+; mangle_name doesn't necessarily return a consistent ending state.)
+;
+clin_non_wsp: lodsb
+ cmp al,' '
+ ja clin_non_wsp
+clin_is_wsp: and al,al
+ jz clin_opt_ptr
+ lodsb
+ cmp al,' '
+ jbe clin_is_wsp
+clin_opt_ptr: dec si ; Point to first nonblank
+ mov [CmdOptPtr],si ; Save ptr to first option
+;
+; Now check if it is a "virtual kernel"
+;
+ mov cx,[VKernelCtr]
+ push ds
+ push word vk_seg
+ pop ds
+ cmp cx,0
+ je not_vk
+ xor si,si ; Point to first vkernel
+vk_check: pusha
+ mov cx,11
+ repe cmpsb ; Is this it?
+ je vk_found
+ popa
+ add si,4096 ; 4K per vkernel structure
+ loop vk_check
+not_vk: pop ds
+;
+; Not a "virtual kernel" - check that's OK and construct the command line
+;
+ cmp word [AllowImplicit],0
+ je bad_implicit
+ push es
+ push si
+ push di
+ mov di,real_mode_seg
+ mov es,di
+ mov si,AppendBuf
+ mov di,cmd_line_here
+ mov cx,AppendLen
+ rep movsb
+ mov [CmdLinePtr],di
+ pop di
+ pop si
+ pop es
+;
+; Find the kernel on disk
+;
+get_kernel: mov si,KernelName
+ mov di,KernelCName
+ call unmangle_name ; Get human form
+ sub di,KernelCName
+ mov [KernelCNameLen],di
+ mov di,KernelName ; Search on disk
+ call searchdir
+ jnz kernel_good
+bad_kernel: mov si,err_notfound ; Complain about missing kernel
+ call writestr
+ mov si,KernelCName
+ call writestr
+ mov si,crlf
+ jmp abort_load ; Ask user for clue
+;
+; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
+;
+bad_implicit: mov si,KernelName ; For the error message
+ mov di,KernelCName
+ call unmangle_name
+ jmp bad_kernel
+;
+; vk_found: We *are* using a "virtual kernel"
+;
+vk_found: popa
+ push di
+ mov di,VKernelBuf
+ mov cx,vk_size >> 2
+ rep movsd
+ push es ; Restore old DS
+ pop ds
+ push es
+ push word real_mode_seg
+ pop es
+ mov di,cmd_line_here
+ mov si,VKernelBuf+vk_append
+ mov cx,[VKernelBuf+vk_appendlen]
+ rep movsb
+ mov [CmdLinePtr],di ; Where to add rest of cmd
+ pop es
+ pop di ; DI -> KernelName
+ push di
+ mov si,VKernelBuf+vk_rname
+ mov cx,11
+ rep movsb
+ pop di
+ jmp get_kernel
+;
+; kernel_corrupt: Called if the kernel file does not seem healthy
+;
+kernel_corrupt: mov si,err_notkernel
+ jmp abort_load
+kernel_good:
+;
+; This is it! We have a name (and location on the disk)... let's load
+; that sucker!!
+;
+; A Linux kernel consists of three parts: boot sector, setup code, and
+; kernel code. The boot sector is never executed when using an external
+; booting utility, but it contains some status bytes that are necessary.
+; The boot sector and setup code together form exactly 5 sectors that
+; should be loaded at 9000:0. The subsequent code should be loaded
+; at 1000:0. For simplicity, we load the whole thing at 0F60:0, and
+; copy the latter stuff afterwards.
+;
+; NOTE: In the previous code I have avoided making any assumptions regarding
+; the size of a sector, in case this concept ever gets extended to other
+; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT
+; filesystem, of course). However, a "sector" when it comes to Linux booting
+; stuff means 512 bytes *no matter what*, so here I am using that piece
+; of knowledge.
+;
+; First check that our kernel is at least 64K and less than 8M (if it is
+; more than 8M, we need to change the logic for loading it anyway...)
+;
+load_it:
+ cmp dx,80h ; 8 megs
+ ja kernel_corrupt
+ and dx,dx
+ jz kernel_corrupt
+kernel_sane: push ax
+ push dx
+ push si
+ mov si,loading_msg
+ call cwritestr
+;
+; Now start transferring the kernel
+;
+ push word real_mode_seg
+ pop es
+
+ push ax
+ push dx
+ div word [ClustSize] ; # of clusters total
+ and dx,dx ; Round up
+ setnz dl
+ movzx dx,dl
+ add ax,dx
+ mov [KernelClust],ax
+ pop dx
+ pop ax
+ add ax,1023
+ adc dx,0
+ mov bx,1024
+ div bx ; Get number of kilobytes
+ mov [KernelK],ax
+;
+; Now, if we transfer these straight, we'll hit 64K boundaries. Hence we
+; have to see if we're loading more than 64K, and if so, load it step by
+; step.
+;
+ mov dx,1 ; 10000h
+ xor ax,ax
+ div word [ClustSize]
+ mov [ClustPerMoby],ax ; Clusters/64K
+;
+; Start by loading the bootsector/setup code, to see if we need to
+; do something funky. It should fit in the first 32K (loading 64K won't
+; work since we might have funny stuff up near the end of memory).
+; If we have larger than 32K clusters, yes, we're hosed.
+;
+ call abort_check ; Check for abort key
+ mov cx,[ClustPerMoby]
+ shr cx,1 ; Half a moby
+ sub [KernelClust],cx
+ xor bx,bx
+ pop si ; Cluster pointer on stack
+ call getfssec
+ jc kernel_corrupt ; Failure in first 32K
+ cmp word [es:bs_bootsign],0AA55h
+ jne kernel_corrupt ; Boot sec signature missing
+ cmp byte [es:su_jump], 0EBh ; Jump opcode
+ jne kernel_corrupt
+;
+; Get the BIOS' idea of what the size of high memory is
+;
+ push si ; Save our cluster pointer!
+ mov ah,88h
+ int 15h
+ cmp ax,14*1024 ; Don't trust memory >15M
+ jna hms_ok
+ mov ax,14*1024
+hms_ok: mov [HighMemSize],ax
+;
+; Construct the command line (append options have already been copied)
+;
+ mov word [es:kern_cmd_magic],0A33Fh ; Command line magic no
+ mov word [es:kern_cmd_offset],cmd_line_here
+ mov di,[CmdLinePtr]
+ mov si,boot_image ; BOOT_IMAGE=
+ mov cx,boot_image_len
+ rep movsb
+ mov si,KernelCName ; Unmangled kernel name
+ mov cx,[KernelCNameLen]
+ rep movsb
+ mov al,' ' ; Space
+ stosb
+ mov si,[CmdOptPtr] ; Options from user input
+ mov cx,(kern_cmd_len+3) >> 2
+ rep movsd
+;
+%ifdef debug
+ push ds ; DEBUG DEBUG DEBUG
+ push es
+ pop ds
+ mov si,offset cmd_line_here
+ call cwritestr
+ pop ds
+ mov si,offset crlf
+ call cwritestr
+%endif
+;
+; Scan through the command line for anything that looks like we might be
+; interested in. The original version of this code automatically assumed
+; the first option was BOOT_IMAGE=, but that is no longer certain.
+;
+ mov si,cmd_line_here
+ mov byte [initrd_flag],0
+ push es
+ pop ds
+get_next_opt: lodsb
+ and al,al
+ jmpz cmdline_end
+ cmp al,' '
+ jbe get_next_opt
+ dec si
+ mov eax,[si]
+ cmp eax,'vga='
+ je is_vga_cmd
+ cmp eax,'mem='
+ je is_mem_cmd
+ push es ; Save ES->real_mode_seg
+ push cs
+ pop es ; Set ES <- normal DS
+ mov di,initrd_cmd
+ mov cx,initrd_cmd_len
+ repe cmpsb
+ jne not_initrd
+ mov di,InitRD
+ push si ; mangle_dir mangles si
+ call mangle_name ; Mangle ramdisk name
+ pop si
+ cmp byte [InitRD],' ' ; Null filename?
+ seta byte [initrd_flag] ; Set flag if not
+not_initrd: pop es ; Restore ES->real_mode_seg
+skip_this_opt: lodsb ; Load from command line
+ cmp al,' '
+ ja skip_this_opt
+ dec si
+ jmp get_next_opt
+is_vga_cmd:
+ add si,4
+ mov eax,[si]
+ mov bx, -1
+ cmp eax, 'norm' ; vga=normal
+ je vc0
+ and eax, 0ffffffh ; 3 bytes
+ mov bx, -2
+ cmp eax, 'ext' ; vga=ext
+ je vc0
+ mov bx, -3
+ cmp eax, 'ask' ; vga=ask
+ je vc0
+ call parseint ; vga=<number>
+ jc skip_this_opt ; Not an integer
+vc0: mov [es:bs_vidmode],bx ; Set video mode
+ jmp skip_this_opt
+is_mem_cmd:
+ add si,4
+ call parseint
+ jc skip_this_opt ; Not an integer
+ shr ebx,10 ; Convert to kilobytes
+ sub ebx,1024 ; Don't count first meg
+ cmp ebx,14*1024 ; Only trust < 15M point
+ jna memcmd_fair
+ mov bx,14*1024
+memcmd_fair: mov [HighMemSize],bx
+ jmp skip_this_opt
+cmdline_end:
+ push cs ; Restore standard DS
+ pop ds
+;
+; Now check if we have a large kernel, which needs to be loaded high
+;
+ cmp dword [es:su_header],HEADER_ID ; New setup code ID
+ jmpne old_kernel ; Old kernel, load low
+ cmp word [es:su_version],0200h ; Setup code version 2.0
+ jmpb old_kernel ; Old kernel, load low
+ cmp word [es:su_version],0201h ; Version 2.01+?
+ jb new_kernel ; If 2.00, skip this step
+ mov word [es:su_heapend],linux_stack ; Set up the heap
+ or byte [es:su_loadflags],80h ; Let the kernel know we cared
+;
+; We definitely have a new-style kernel. Let the kernel know who we are,
+; and that we are clueful
+;
+new_kernel:
+ mov byte [es:su_loader],syslinux_id ; Show some ID
+;
+; Now see if we have an initial RAMdisk; if so, do requisite computation
+;
+ test byte [initrd_flag],1
+ jz nk_noinitrd
+ push es ; ES->real_mode_seg
+ push ds
+ pop es ; We need ES==DS
+ mov si,InitRD
+ mov di,InitRDCName
+ call unmangle_name ; Create human-readable name
+ sub di,InitRDCName
+ mov [InitRDCNameLen],di
+ mov di,InitRD
+ call searchdir ; Look for it in directory
+ pop es
+ jz initrd_notthere
+ mov [initrd_ptr],si ; Save cluster pointer
+ mov [es:su_ramdisklen1],ax ; Ram disk length
+ mov [es:su_ramdisklen2],dx
+ div word [ClustSize]
+ and dx,dx ; Round up
+ setnz dl
+ movzx dx,dl
+ add ax,dx
+ mov [InitRDClust],ax ; Ramdisk clusters
+ mov eax,[es:su_ramdisklen]
+ shr eax,10 ; Convert to kilobytes
+ mov dx,[HighMemSize] ; End of memory
+ add dx,1024 ; Add "low" memory
+ sub dx,ax ; Subtract size of ramdisk
+ and dx,0ffc0h ; Round down to 64K boundary
+ shl dx,2 ; Convert to 256-byte blocks
+ mov [InitRDat],dx ; Load address
+ call loadinitrd ; Load initial ramdisk
+;
+; About to load the kernel, so print the kernel signon
+;
+nk_noinitrd:
+ mov si,KernelCName ; Print kernel name part of
+ call cwritestr ; "Loading" message
+ mov si,dotdot_msg ; Print dots
+ call cwritestr
+ test byte [es:su_loadflags],LOAD_HIGH ; Is high load flag set?
+ jnz high_kernel ; Yes, load high
+ jmp low_kernel ; No, load low
+initrd_notthere:
+ mov si,err_noinitrd
+ call writestr
+ mov si,InitRDCName
+ call writestr
+ mov si,crlf
+ jmp abort_load
+;
+; If we get here, we need to load kernel high
+;
+no_high_mem: mov si,err_nohighmem ; Error routine
+ jmp abort_load
+high_kernel:
+ mov ax,[HighMemSize]
+ cmp ax,[KernelK]
+ jb no_high_mem ; Not enough high memory
+;
+; Move the stuff beyond the setup code to high memory at 100000h
+;
+ mov bx,1 ; 1 boot sector
+ add bl,[es:bs_setupsecs] ; Plus setup sectors
+ sbb bh,0
+ shl bx,1 ; Convert to 256-byte blocks
+ mov ax,1080h ; 108000h = 1M + 32K
+ sub ax,bx ; Adjust pointer to 2nd block
+ mov [HiLoadAddr],ax
+ shl bx,8 ; Convert to a byte address
+ mov cx,4000h ; Cheating! Copy all 32K
+ mov di,1000h ; Copy to address 100000h
+ call upload ; Transfer to high memory
+;
+ push word xfer_buf_seg ; Segment 7000h is xfer buffer
+ pop es
+high_load_loop:
+ mov si,dot_msg ; Progress report
+ call cwritestr
+ call abort_check
+ mov cx,[KernelClust]
+ cmp cx,[ClustPerMoby]
+ jna high_last_moby
+ mov cx,[ClustPerMoby]
+high_last_moby:
+ sub [KernelClust],cx
+ xor bx,bx ; Load at offset 0
+ pop si ; Restore cluster pointer
+ call getfssec
+ push si ; Save cluster pointer
+ pushf ; Save EOF
+ xor bx,bx
+ mov di,[HiLoadAddr] ; Destination address
+ mov cx,8000h ; Cheating - transfer 64K
+ call upload ; Transfer to high memory
+ popf ; Restore EOF
+ jc high_load_done ; If EOF we are done
+ add word [HiLoadAddr],100h ; Point to next 64K
+ cmp word [KernelClust],0 ; Are we done?
+ jne high_load_loop ; Apparently not
+high_load_done:
+ pop si ; No longer needed
+ push word real_mode_seg
+ pop es
+ jmp load_done
+;
+; Load an older kernel. Older kernels always have 4 setup sectors, can't have
+; initrd, and are always laoded low.
+;
+old_kernel:
+ test byte [initrd_flag],1 ; Old kernel can't have initrd
+ jz low_kernel
+ mov si,err_oldkernel
+ jmp abort_load
+ ; An old kernel is always loaded low...
+low_kernel:
+;
+; Low kernel: check that it will fit as a low kernel,
+; save the vkernel buffers into high memory in case we abort the
+; load, then transfer the kernel to low memory
+;
+ cmp word [KernelK],512 ; 512K maximum
+ jna low_kernel_ok
+ jmp kernel_corrupt
+low_kernel_ok: push es
+ mov bx,vk_seg
+ mov es,bx
+ xor bx,bx
+ mov di,1000h ; 100000h
+ mov cx,8000h ; 64K
+ call upload
+ pop es
+ mov byte [VKernelsHigh],1 ; VKernels now in high memory
+;
+; Transfer the already loaded protected-mode code down, then load the rest
+;
+ mov bx,1 ; 1 boot sector
+ add bl,[es:bs_setupsecs] ; Plus setup sectors
+ sbb bh,0
+ shl bx,5 ; Convert to a paragraph number
+ push bx ; Save paragraph
+ add bx,real_mode_seg
+ push ds ; Save DS
+ mov ds,bx
+ mov ax,1000h ; New kernel start at...
+ mov es,ax
+ xor si,si
+ xor di,di
+ mov cx,2000h ; Cheating: copy 32K
+ rep movsd ; Copy down non-setup code
+ pop ds
+ pop bx ; Segment count of setup
+ mov ax,1800h ; Paragraph for moby 2 if
+ ; setup is 0K
+ sub ax,bx ; AX now = this moby segment
+loadmoby:
+ mov si,dot_msg
+ call cwritestr
+ call abort_check
+ pop si ; Restore cluster pointer
+ mov cx,[KernelClust]
+ cmp cx,[ClustPerMoby]
+ jna last_moby
+ mov cx,[ClustPerMoby]
+last_moby:
+ sub [KernelClust],cx
+ xor bx,bx ; Load at zero
+ mov es,ax ; Segment address
+ push ax ; Save segment address
+ call getfssec
+ pop ax
+ jc load_done
+ cmp word [KernelClust],0
+ jz load_done
+ push si ; Save cluster pointer
+ add ax,1000h ; Advance to next moby
+ jmp loadmoby
+;
+; This is where both the high and low load routines end up after having
+; loaded
+;
+
+load_done:
+ mov ax,real_mode_seg
+ mov es,ax
+
+ mov si,dot_msg
+ call cwritestr
+;
+; If the default root device is set to FLOPPY (0000h), change to
+; /dev/fd0 (0200h)
+;
+ cmp word [es:bs_rootdev],0
+ jne root_not_floppy
+ mov word [es:bs_rootdev],0200h
+root_not_floppy:
+;
+; Copy the disk table to high memory, then re-initialize the floppy
+; controller
+;
+ mov si,floppy_table
+ mov di,linux_fdctab
+ mov cx,3 ; 12 bytes
+ push di
+ rep movsd
+ pop di
+ cli
+ mov [fdctab1],di ; Save new floppy tab pos
+ mov [fdctab2],es
+ sti
+ xor ax,ax
+ xor dx,dx
+ int 13h
+;
+; Linux wants the floppy motor shut off before starting the kernel,
+; at least bootsect.S seems to imply so
+;
+kill_motor:
+ mov dx,03F2h
+ xor al,al
+ out dx,al
+;
+; Now we're as close to be done as we can be and still use our normal
+; routines, print a CRLF to end the row of dots
+;
+ call abort_check ; Last chance!!
+ mov si,crlf
+ call writestr
+;
+; If we're debugging, wait for a keypress so we can read any debug messages
+;
+%ifdef debug
+ xor ax,ax
+ int 16h
+%endif
+;
+; Set up segment registers and the Linux real-mode stack
+;
+ mov ax,real_mode_seg
+ mov ds,ax
+ mov es,ax
+ mov fs,ax
+ mov gs,ax
+ cli
+ mov ss,ax
+ mov sp,linux_stack
+ sti
+;
+; We're done... now RUN THAT KERNEL!!!!
+;
+ jmp setup_seg:setup_entry
+;
+; cwritestr: write a null-terminated string to the console, saving
+; registers on entry (we can't use this in the boot sector,
+; since we haven't verified 386-ness yet)
+;
+cwritestr:
+ pusha
+cwstr_1: lodsb
+ and al,al
+ jz cwstr_2
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; White on black, current page
+ int 10h
+ jmp short cwstr_1
+cwstr_2: popa
+ ret
+
+;
+; Load RAM disk into high memory
+;
+loadinitrd:
+ push es ; Save ES on entry
+ mov ax,real_mode_seg
+ mov es,ax
+ mov si,[initrd_ptr]
+ and si,si
+ mov di,[InitRDat] ; initrd load address
+ movzx eax,di
+ shl eax,8 ; Convert to bytes
+ mov [es:su_ramdiskat],eax ; Offset for ram disk
+ push si
+ mov si,InitRDCName ; Write ramdisk name
+ call cwritestr
+ mov si,dotdot_msg ; Write dots
+ call cwritestr
+rd_load_loop:
+ mov si,dot_msg ; Progress report
+ call cwritestr
+ pop si ; Restore cluster pointer
+ call abort_check
+ mov cx,[InitRDClust]
+ cmp cx,[ClustPerMoby]
+ jna rd_last_moby
+ mov cx,[ClustPerMoby]
+rd_last_moby:
+ sub [InitRDClust],cx
+ xor bx,bx ; Load at offset 0
+ push word xfer_buf_seg ; Bounce buffer segment
+ pop es
+ call getfssec
+ push si ; Save cluster pointer
+ pushf ; Remember EOF
+ mov si,prot_xfer_gdt
+ xor bx,bx
+ mov di,[InitRDat]
+ mov cx,8000h ; Always transfer 64K
+ call upload
+ popf
+ jc rd_load_done ; EOF?
+ add word [InitRDat],100h ; Point to next 64K
+ cmp word [InitRDClust],0 ; Are we done?
+ jne rd_load_loop ; Apparently not
+rd_load_done:
+ pop si ; Clean up the stack
+ mov si,crlf_msg
+ call writestr
+ mov si,loading_msg ; Write new "Loading " for
+ call writestr ; the benefit of the kernel
+ pop es ; Restore original ES
+ ret
+
+;
+; upload: upload a chunk of data to high memory
+; es:bx = source address
+; di = linear target address (x 256)
+; cx = count (words) - max 8000h for now
+;
+
+upload:
+ pushad
+ push es
+ mov eax,09300000h ; Compute linear base [93h in field
+ mov ax,es ; right beyond the 3-byte address
+ shl eax,4 ; field!
+ movzx ebx,bx
+ add eax,ebx
+ mov dword [px_src_low],eax
+ul_dl: push cs ; Set ES=CS (=DS)
+ pop es
+ mov [px_dst],di ; Save destination address
+ push cx ; Save count
+ xor eax,eax
+ mov di,px_wipe_1
+ mov cx,4
+ stosd
+ mov di,px_wipe_2
+ mov cx,4
+ stosd
+ pop cx
+ mov si,prot_xfer_gdt
+ mov ah,87h
+ int 15h
+ jc ul_error
+ pop es
+ popad
+ ret
+ul_error: pop ax ; Leave ES=CS (=DS)
+ popad
+ mov si,err_highload
+ jmp abort_load
+
+;
+; download: same as upload, except si = linear source address (x 256)
+; currently used only to recover the vkernels in case of an
+; aborted low-kernel load (don't you love corner cases?)
+;
+download:
+ pushad
+ push es
+ mov byte [px_src_low],0
+ mov [px_src],si
+ jmp ul_dl
+
+;
+; GDT for protected-mode transfers (int 15h AH=87h). Note that the low
+; 8 bits are set to zero in all transfers, so they never change in this
+; block.
+;
+ align 4
+prot_xfer_gdt equ $
+px_wipe_1 times 16 db 0 ; Reserved
+ dw 0FFFFh ; Limit: 64K
+px_src_low db 0 ; Low 8 bits of source address
+px_src dw 0 ; High 16 bits of source address
+ db 93h ; Segment access flags
+ dw 0 ; Reserved
+ dw 0FFFFh ; Limit: 64K
+px_dst_low db 00h ; Low 8 bits of destination address
+px_dst dw 0 ; High 16 bits of destination address
+ db 93h ; Segment access flags
+ dw 0 ; Reserved
+px_wipe_2 times 16 db 0 ; Reserved
+;
+; abort_check: let the user abort with <ESC> or <Ctrl-C>
+;
+abort_check:
+ pusha
+ac1:
+ mov ah,1 ; Check for pending keystroke
+ int 16h
+ jz ac_ret ; If no pending keystroke
+ xor ax,ax ; Load pending keystroke
+ int 16h
+ cmp al,27 ; <ESC> aborts (DOS geeks)
+ je ac2
+ cmp al,3 ; So does Ctrl-C (UNIX geeks)
+ jne ac1 ; Unknown key... try again
+ac2: ; If we get here, ABORT!
+ mov si,aborted_msg
+ ; Fall through to abort_load
+;
+; abort_load: Called by various routines which wants to print a fatal
+; error message and return to the command prompt. Since this
+; may happen at just about any stage of the boot process, assume
+; our state is messed up, and just reset the segment registers
+; and the stack forcibly.
+;
+; SI = offset (in _text) of error message to print
+;
+abort_load:
+ mov ax,cs ; Restore CS = DS = ES
+ mov ds,ax
+ mov es,ax
+ cli
+ mov sp,StackBuf-2*3 ; Reset stack
+ mov ss,ax ; Just in case...
+ sti
+ call writestr ; Expects SI -> error msg
+ cmp byte [VKernelsHigh],0
+ je al_ok
+ mov si,1000h ; VKernels stashed high
+ mov di,vk_seg ; Recover
+ shr di,4
+ mov cx,8000h
+ call download
+ mov byte [VKernelsHigh],0
+al_ok: jmp enter_command ; Return to command prompt
+;
+; End of abort_check
+;
+ac_ret: popa
+ ret
+
+;
+; searchdir: Search the root directory for a pre-mangled filename in
+; DS:DI. This routine is similar to the one in the boot
+; sector, but is a little less Draconian when it comes to
+; error handling, plus it reads the root directory in
+; larger chunks than a sector at a time (which is probably
+; a waste of coding effort, but I like to do things right).
+;
+; NOTE: This file considers finding a zero-length file an
+; error. This is so we don't have to deal with that special
+; case elsewhere in the program (most loops have the test
+; at the end).
+;
+; If successful:
+; ZF clear
+; SI = cluster # for the first cluster
+; DX:AX = file length in bytes
+; If unsuccessful
+; ZF set
+;
+
+searchdir:
+ mov ax,[bsRootDirEnts]
+ mov [DirScanCtr],ax
+ mov ax,[RootDirSize]
+ mov [DirBlocksLeft],ax
+ mov ax,[RootDir1]
+ mov dx,[RootDir2]
+scan_group:
+ mov bp,[DirBlocksLeft]
+ and bp,bp
+ jz dir_return
+ cmp bp,[BufSafeSec]
+ jna load_last
+ mov bp,[BufSafeSec]
+load_last:
+ sub [DirBlocksLeft],bp
+ push ax
+ push dx
+ mov ax,[bsBytesPerSec]
+ mul bp
+ add ax,trackbuf-31
+ mov [EndofDirSec],ax ; End of loaded
+ pop dx
+ pop ax
+ push bp ; Save number of sectors
+ push ax ; Save present location
+ push dx
+ push di ; Save name
+ mov bx,trackbuf
+ call getlinsec
+ pop di
+ pop dx
+ pop ax
+ pop bp
+ mov si,trackbuf
+dir_test_name: cmp byte [si],0 ; Directory high water mark
+ je dir_return ; Failed
+ test byte [si+11],010h ; Check it really is a file
+ jnz dir_not_this
+ push di
+ push si
+ mov cx,11 ; Filename = 11 bytes
+ repe cmpsb
+ pop si
+ pop di
+ je dir_success
+dir_not_this: add si,32
+ dec word [DirScanCtr]
+ jz dir_return ; Out of it...
+ cmp si,[EndofDirSec]
+ jb dir_test_name
+ add ax,bp ; Increment linear sector number
+ adc dx,0
+ jmp scan_group
+dir_success:
+ mov ax,[si+28] ; Length of file
+ mov dx,[si+30]
+ mov si,[si+26] ; Cluster pointer
+ mov bx,ax
+ or bx,dx ; Sets ZF iff DX:AX is zero
+dir_return:
+ ret
+
+;
+; writechr: Write a single character in AL to the screen without
+; mangling any registers
+;
+writechr:
+ pusha
+ mov ah,0Eh
+ mov bx,0007h ; white text on this page
+ int 10h
+ popa
+ ret
+
+;
+; get_msg_file: Load a text file and write its contents to the screen,
+; interpreting color codes. Is called with SI and DX:AX
+; set by routine searchdir
+;
+get_msg_file:
+ mov word [NextCharJump],msg_putchar ; State machine for color
+ mov byte [TextAttribute],07h ; Default grey on white
+ pusha
+ mov bh,[TextPage]
+ mov ah,03h ; Read cursor position
+ int 10h
+ mov [CursorDX],dx
+ popa
+get_msg_chunk: push ax ; DX:AX = length of file
+ push dx
+ mov bx,trackbuf
+ mov cx,[BufSafe]
+ call getfssec
+ pop dx
+ pop ax
+ push si ; Save current cluster
+ mov si,trackbuf
+ mov cx,[BufSafeBytes] ; No more than many bytes
+print_msg_file: push cx
+ push ax
+ push dx
+ lodsb
+ cmp al,1Ah ; ASCII EOF?
+ je msg_done_pop
+ call [NextCharJump] ; Do what shall be done
+ pop dx
+ pop ax
+ pop cx
+ sub ax,1
+ sbb dx,0
+ mov bx,ax
+ or bx,dx
+ jz msg_done
+ loop print_msg_file
+ pop si
+ jmp get_msg_chunk
+msg_done_pop:
+ add sp,6 ; Lose 3 words on the stack
+msg_done:
+ pop si
+ ret
+msg_putchar: ; Normal character
+ cmp al,0Fh ; ^O = color code follows
+ je msg_ctrl_o
+ cmp al,0Dh ; Ignore <CR>
+ je msg_ignore
+ cmp al,0Ah ; <LF> = newline
+ je msg_newline
+ cmp al,0Ch ; <FF> = clear screen
+ je msg_formfeed
+ mov bx,[TextAttrBX]
+ mov ah,09h ; Write character/attribute
+ mov cx,1 ; One character only
+ int 10h ; Write to screen
+ mov al,[CursorCol]
+ inc ax
+ cmp al,[VidCols]
+ ja msg_newline
+ mov [CursorCol],al
+msg_gotoxy: mov bh,[TextPage]
+ mov dx,[CursorDX]
+ mov ah,02h ; Set cursor position
+ int 10h
+msg_ignore: ret
+msg_ctrl_o: ; ^O = color code follows
+ mov word [NextCharJump],msg_setbg
+ ret
+msg_newline: ; Newline char or end of line
+ mov byte [CursorCol],0
+ mov al,[CursorRow]
+ inc ax
+ cmp al,[VidRows]
+ ja msg_scroll
+ mov [CursorRow],al
+ jmp msg_gotoxy
+msg_scroll: xor cx,cx ; Upper left hand corner
+ mov dx,[ScreenSize]
+ mov [CursorRow],dh ; New cursor at the bottom
+ mov bh,[TextAttribute]
+ mov ax,0601h ; Scroll up one line
+ int 10h
+ jmp msg_gotoxy
+msg_formfeed: ; Form feed character
+ xor cx,cx
+ mov [CursorDX],cx ; Upper lefthand corner
+ mov dx,[ScreenSize]
+ mov bh,[TextAttribute]
+ mov ax,0600h ; Clear screen region
+ int 10h
+ jmp msg_gotoxy
+msg_setbg: ; Color background character
+ call unhexchar
+ jc msg_color_bad
+ shl al,4
+ mov [TextAttribute],al
+ mov word [NextCharJump],msg_setfg
+ ret
+msg_setfg: ; Color foreground character
+ call unhexchar
+ jc msg_color_bad
+ or [TextAttribute],al ; setbg set foreground to 0
+ mov word [NextCharJump],msg_putchar
+ ret
+msg_color_bad:
+ mov byte [TextAttribute],07h ; Default attribute
+ mov word [NextCharJump],msg_putchar
+ ret
+
+;
+; open,getc: Load a file a character at a time for parsing in a manner
+; similar to the C library getc routine. Only one simultaneous
+; use is supported. Note: "open" trashes the trackbuf.
+;
+; open: Input: mangled filename in DS:DI
+; Output: ZF set on file not found or zero length
+;
+; getc: Output: CF set on end of file
+; Character loaded in AL
+;
+open:
+ call searchdir
+ jz open_return
+ pushf
+ mov [FBytes1],ax
+ mov [FBytes2],dx
+ add ax,[ClustSize]
+ adc dx,0
+ sub ax,1
+ sbb dx,0
+ div word [ClustSize]
+ mov [FClust],ax ; Number of clusters
+ mov [FNextClust],si ; Cluster pointer
+ mov ax,[EndOfGetCBuf] ; Pointer at end of buffer ->
+ mov [FPtr],ax ; nothing loaded yet
+ popf ; Restore no ZF
+open_return: ret
+
+;
+getc:
+ mov ecx,[FBytes]
+ jecxz getc_end
+ mov si,[FPtr]
+ cmp si,[EndOfGetCBuf]
+ jb getc_loaded
+ ; Buffer empty -- load another set
+ mov cx,[FClust]
+ cmp cx,[BufSafe]
+ jna getc_oksize
+ mov cx,[BufSafe]
+getc_oksize: sub [FClust],cx ; Reduce remaining clusters
+ mov si,[FNextClust]
+ mov bx,getcbuf
+ push bx
+ push es ; ES may be != DS, save old ES
+ push ds ; Trackbuf is in DS, not ES
+ pop es
+ call getfssec ; Load a trackbuf full of data
+ mov [FNextClust],si ; Store new next pointer
+ pop es ; Restore ES
+ pop si ; SI -> newly loaded data
+getc_loaded: lodsb ; Load a byte
+ mov [FPtr],si ; Update next byte pointer
+ dec dword [FBytes] ; Update bytes left counter (CF = 1)
+getc_end: cmc ; Set CF = 1 on EOF, 0 if not
+ ret
+
+;
+; ungetc: Push a character (in AL) back into the getc buffer
+; Note: if more than one byte is pushed back, this may cause
+; bytes to be written below the getc buffer boundary. If there
+; is a risk for this to occur, the getcbuf base address should
+; be moved up.
+;
+ungetc:
+ mov si,[FPtr]
+ dec si
+ mov [si],al
+ mov [FPtr],si
+ inc dword [FBytes]
+ ret
+
+;
+; skipspace: Skip leading whitespace using "getc". If we hit end-of-line
+; or end-of-file, return with carry set; ZF = true of EOF
+; ZF = false for EOLN; otherwise CF = ZF = 0.
+;
+; Otherwise AL = first character after whitespace
+;
+skipspace:
+skipspace_loop: call getc
+ jc skipspace_eof
+ cmp al,1Ah ; DOS EOF
+ je skipspace_eof
+ cmp al,0Ah
+ je skipspace_eoln
+ cmp al,' '
+ jbe skipspace_loop
+ ret ; CF = ZF = 0
+skipspace_eof: cmp al,al ; Set ZF
+ stc ; Set CF
+ ret
+skipspace_eoln: add al,0FFh ; Set CF, clear ZF
+ ret
+
+;
+; getkeyword: Get a keyword from the current "getc" file; only the two
+; first characters are considered significant.
+;
+; Lines beginning with ASCII characters 33-47 are treated
+; as comments and ignored; other lines are checked for
+; validity by scanning through the keywd_table.
+;
+; The keyword and subsequent whitespace is skipped.
+;
+; On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair
+;
+getkeyword:
+gkw_find: call skipspace
+ jz gkw_eof ; end of file
+ jc gkw_find ; end of line: try again
+ cmp al,'0'
+ jb gkw_skipline ; skip comment line
+ push ax
+ call getc
+ pop bx
+ jc gkw_eof
+ mov bh,al ; Move character pair into BL:BH
+ or bx,2020h ; Lower-case it
+ mov si,keywd_table
+gkw_check: lodsw
+ and ax,ax
+ jz gkw_badline ; Bad keyword, write message
+ cmp ax,bx
+ jne gkw_check
+ push ax
+gkw_skiprest:
+ call getc
+ jc gkw_eof_pop
+ cmp al,'0'
+ ja gkw_skiprest
+ call ungetc
+ call skipspace
+ jz gkw_eof_pop
+ jc gkw_missingpar ; Missing parameter after keyword
+ call ungetc ; Return character to buffer
+ clc ; Successful return
+gkw_eof_pop: pop ax
+gkw_eof: ret ; CF = 1 on all EOF conditions
+gkw_missingpar: pop ax
+ mov si,err_noparm
+ call writestr
+ jmp gkw_find
+gkw_badline_pop: pop ax
+gkw_badline: mov si,err_badcfg
+ call writestr
+ jmp gkw_find
+gkw_skipline: cmp al,10 ; Scan for LF
+ je gkw_find
+ call getc
+ jc gkw_eof
+ jmp gkw_skipline
+
+;
+; getint: Load an integer from the getc file.
+; Return CF if error; otherwise return integer in EBX
+;
+getint:
+ mov di,NumBuf
+gi_getnum: cmp di,[NumBufEnd] ; Last byte in NumBuf
+ jae gi_loaded
+ push di
+ call getc
+ pop di
+ jc gi_loaded
+ stosb
+ cmp al,'-'
+ jnb gi_getnum
+ call ungetc ; Unget non-numeric
+gi_loaded: mov byte [di],0
+ mov si,NumBuf
+ ; Fall through to parseint
+
+;
+; parseint: Convert an integer to a number in EBX
+; Get characters from string in DS:SI
+; Return CF on error
+; DS:SI points to first character after number
+;
+; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M
+;
+parseint:
+ push eax
+ push ecx
+ push bp
+ xor eax,eax ; Current digit (keep eax == al)
+ mov ebx,eax ; Accumulator
+ mov ecx,ebx ; Base
+ xor bp,bp ; Used for negative flag
+pi_begin: lodsb
+ cmp al,'-'
+ jne pi_not_minus
+ xor bp,1 ; Set unary minus flag
+ jmp pi_begin
+pi_not_minus:
+ cmp al,'0'
+ jb pi_err
+ je pi_octhex
+ cmp al,'9'
+ ja pi_err
+ mov cl,10 ; Base = decimal
+ jmp pi_foundbase
+pi_octhex:
+ lodsb
+ cmp al,'0'
+ jb pi_km ; Value is zero
+ or al,20h ; Downcase
+ cmp al,'x'
+ je pi_ishex
+ cmp al,'7'
+ ja pi_err
+ mov cl,8 ; Base = octal
+ jmp pi_foundbase
+pi_ishex:
+ mov al,'0' ; No numeric value accrued yet
+ mov cl,16 ; Base = hex
+pi_foundbase:
+ call unhexchar
+ jc pi_km ; Not a (hex) digit
+ cmp al,cl
+ jae pi_km ; Invalid for base
+ imul ebx,ecx ; Multiply accumulated by base
+ add ebx,eax ; Add current digit
+ lodsb
+ jmp pi_foundbase
+pi_km:
+ dec si ; Back up to last non-numeric
+ lodsb
+ or al,20h
+ cmp al,'k'
+ je pi_isk
+ cmp al,'m'
+ je pi_ism
+ dec si ; Back up
+pi_fini: and bp,bp
+ jz pi_ret ; CF=0!
+ neg ebx ; Value was negative
+pi_done: clc
+pi_ret: pop bp
+ pop ecx
+ pop eax
+ ret
+pi_err: stc
+ jmp pi_ret
+pi_isk: shl ebx,10 ; x 2^10
+ jmp pi_done
+pi_ism: shl ebx,20 ; x 2^20
+ jmp pi_done
+
+;
+; unhexchar: Convert a hexadecimal digit in AL to the equivalent number;
+; return CF=1 if not a hex digit
+;
+unhexchar:
+ cmp al,'0'
+ jb uxc_err
+ cmp al,'9'
+ ja uxc_1
+ sub al,'0' ; CF=0
+ ret
+uxc_1: cmp al,'A'
+ jb uxc_err
+ cmp al,'F'
+ ja uxc_2
+ sub al,'A'-10 ; CF=0
+ ret
+uxc_2: cmp al,'a'
+ jb uxc_err
+ cmp al,'f'
+ ja uxc_err
+ sub al,'a'-10 ; CF=0
+ ret
+uxc_err: stc
+ ret
+
+;
+;
+; getline: Get a command line, converting control characters to spaces
+; and collapsing streches to one; a space is appended to the
+; end of the string, unless the line is empty.
+; The line is terminated by ^J, ^Z or EOF and is written
+; to ES:DI. On return, DI points to first char after string.
+; CF is set if we hit EOF.
+;
+getline:
+ call skipspace
+ mov dl,1 ; Empty line -> empty string.
+ jz gl_eof ; eof
+ jc gl_eoln ; eoln
+ call ungetc
+gl_fillloop: push dx
+ push di
+ call getc
+ pop di
+ pop dx
+ jc gl_ret ; CF set!
+ cmp al,' '
+ jna gl_ctrl
+ xor dx,dx
+gl_store: stosb
+ jmp gl_fillloop
+gl_ctrl: cmp al,10
+ je gl_ret ; CF clear!
+ cmp al,26
+ je gl_eof
+ and dl,dl
+ jnz gl_fillloop ; Ignore multiple spaces
+ mov al,' ' ; Ctrl -> space
+ inc dx
+ jmp gl_store
+gl_eoln: clc ; End of line is not end of file
+ jmp gl_ret
+gl_eof: stc
+gl_ret: pushf ; We want the last char to be space!
+ and dl,dl
+ jnz gl_xret
+ mov al,' '
+ stosb
+gl_xret: popf
+ ret
+
+
+%ifdef debug ; This code for debugging only
+;
+; dumpregs: Dumps the contents of all registers
+;
+ assume ds:_text, es:NOTHING, fs:NOTHING, gs:NOTHING
+dumpregs proc near ; When calling, IP is on stack
+ pushf ; Store flags
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+ push cs ; Set DS <- CS
+ pop ds
+ cld ; Clear direction flag
+ mov si,offset crlf
+ call writestr
+ mov bx,sp
+ add bx,26
+ mov si,offset regnames
+ mov cx,2 ; 2*7 registers to dump
+dump_line: push cx
+ mov cx,7 ; 7 registers per line
+dump_reg: push cx
+ mov cx,4 ; 4 characters/register name
+wr_reg_name: lodsb
+ call writechr
+ loop wr_reg_name
+ mov ax,ss:[bx]
+ dec bx
+ dec bx
+ call writehex
+ pop cx
+ loop dump_reg
+ mov al,0Dh ; <CR>
+ call writechr
+ mov al,0Ah ; <LF>
+ call writechr
+ pop cx
+ loop dump_line
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa ; Restore the remainder
+ popf ; Restore flags
+ ret
+dumpregs endp
+
+regnames db ' IP: FL: AX: CX: DX: BX: SP: BP: SI: DI: DS: ES: FS: GS:'
+
+;
+; writehex: Writes a 16-bit hexadecimal number (in AX)
+;
+writehex proc near
+ push bx
+ push cx
+ mov cx,4 ; 4 numbers
+write_hexdig: xor bx,bx
+ push cx
+ mov cx,4 ; 4 bits/digit
+xfer_digit: shl ax,1
+ rcl bx,1
+ loop xfer_digit
+ push ax
+ mov ax,bx
+ or al,'0'
+ cmp al,'9'
+ jna ok_digit
+ add al,'A'-'0'-10
+ok_digit: call writechr
+ pop ax
+ pop cx
+ loop write_hexdig
+ pop cx
+ pop bx
+ ret
+writehex endp
+
+debug_magic dw 0D00Dh
+
+%endif ; debug
+;
+; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
+; to by ES:DI; ends on encountering any whitespace
+;
+
+mangle_name:
+ mov cx,11 ; # of bytes to write
+mn_loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna mn_end
+ cmp al,'.' ; Period -> space-fill
+ je mn_is_period
+ cmp al,'a'
+ jb mn_not_lower
+ cmp al,'z'
+ ja mn_not_uslower
+ sub al,020h
+ jmp short mn_not_lower
+mn_is_period: mov al,' ' ; We need to space-fill
+mn_period_loop: cmp cx,3 ; If <= 3 characters left
+ jbe mn_loop ; Just ignore it
+ stosb ; Otherwise, write a period
+ loop mn_period_loop ; Dec CX and (always) jump
+mn_not_uslower: cmp al,ucase_low
+ jb mn_not_lower
+ cmp al,ucase_high
+ ja mn_not_lower
+ mov bx,ucase_tab-ucase_low
+ cs xlatb
+mn_not_lower: stosb
+ loop mn_loop ; Don't continue if too long
+mn_end:
+ mov al,' ' ; Space-fill name
+ rep stosb ; Doesn't do anything if CX=0
+ ret ; Done
+
+;
+; Upper-case table for extended characters; this is technically code page 865,
+; but code page 437 users will probably not miss not being able to use the
+; cent sign in kernel images too much :-)
+;
+; The table only covers the range 129 to 164; the rest we can deal with.
+;
+ucase_low equ 129
+ucase_high equ 164
+ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
+ db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
+ db 157, 156, 157, 158, 159, 'AIOU', 165
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+; filename to the conventional representation. This is needed
+; for the BOOT_IMAGE= parameter for the kernel.
+; NOTE: A 13-byte buffer is mandatory, even if the string is
+; known to be shorter.
+;
+; DS:SI -> input mangled file name
+; ES:DI -> output buffer
+;
+; On return, DI points to the first byte after the output name,
+; which is set to a null byte.
+;
+unmangle_name:
+ push si ; Save pointer to original name
+ mov cx,8
+ mov bp,di
+un_copy_body: lodsb
+ call lower_case
+ stosb
+ cmp al,' '
+ jbe un_cb_space
+ mov bp,di ; Position of last nonblank+1
+un_cb_space: loop un_copy_body
+ mov di,bp
+ mov al,'.' ; Don't save
+ stosb
+ mov cx,3
+un_copy_ext: lodsb
+ call lower_case
+ stosb
+ cmp al,' '
+ jbe un_ce_space
+ mov bp,di
+un_ce_space: loop un_copy_ext
+ mov di,bp
+ mov byte [es:di], 0
+ pop si
+ ret
+
+;
+; lower_case: Lower case a character in AL
+;
+lower_case:
+ cmp al,'A'
+ jb lc_ret
+ cmp al,'Z'
+ ja lc_1
+ or al,20h
+ ret
+lc_1: cmp al,lcase_low
+ jb lc_ret
+ cmp al,lcase_high
+ ja lc_ret
+ push bx
+ mov bx,lcase_tab-lcase_low
+ cs xlatb
+ pop bx
+lc_ret: ret
+
+;
+; Lower-case table for codepage 865
+;
+lcase_low equ 128
+lcase_high equ 165
+lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
+ db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
+ db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
+ db 161, 162, 163, 164, 164
+;
+; Various initialized or semi-initialized variables
+;
+boot_prompt db 'boot: ',0
+wipe_char db 08h, ' ', 08h, 0
+err_notfound db 'Could not find kernel image: ',0
+err_notkernel db 0Dh, 0Ah, 'Invalid or corrupt kernel image: ',0
+err_not386 db 'It appears your computer uses a 286 or lower CPU.'
+ db 0Dh, 0Ah
+ db 'You cannot run Linux unless you have a 386 or higher CPU'
+ db 0Dh, 0Ah
+ db 'in your machine. If you get this message in error, hold'
+ db 0Dh, 0Ah
+ db 'down the Ctrl key while booting, and I will take your'
+ db 0Dh, 0Ah
+ db 'word for it.', 0Dh, 0Ah, 0
+err_noram db 'It appears your computer has less than 608K of low ("DOS")'
+ db 0Dh, 0Ah
+ db 'RAM. Linux needs at least this amount to boot. If you get'
+ db 0Dh, 0Ah
+ db 'this message in error, hold down the Ctrl key while'
+ db 0Dh, 0Ah
+ db 'booting, and I will take your word for it.', 0Dh, 0Ah, 0
+err_badcfg db 'Unknown keyword in syslinux.cfg.', 0Dh, 0Ah, 0
+err_noparm db 'Missing parameter in syslinux.cfg.', 0Dh, 0Ah, 0
+err_noinitrd db 0Dh, 0Ah, 'Could not find ramdisk image: ', 0
+err_nohighmem db 'Not enough memory to load specified kernel.', 0Dh, 0Ah, 0
+err_highload db 0Dh, 0Ah, 'Kernel transfer failure.', 0Dh, 0Ah, 0
+err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
+ db 0Dh, 0Ah, 0
+loading_msg db 'Loading ', 0
+dotdot_msg db '.'
+dot_msg db '.', 0
+aborted_msg db ' aborted.' ; Fall through to crlf_msg!
+crlf_msg db 0Dh, 0Ah, 0
+syslinux_cfg db 'SYSLINUXCFG'
+;
+; Command line options we'd like to take a look at
+;
+; mem= and vga= are handled as normal 32-bit integer values
+initrd_cmd db 'initrd='
+initrd_cmd_len equ 7
+;
+; Config file keyword table
+;
+ align 2
+keywd_table db 'ap' ; append
+ db 'de' ; default
+ db 'ti' ; timeout
+ db 'di' ; display
+ db 'pr' ; prompt
+ db 'la' ; label
+ db 'ke' ; kernel
+ db 'im' ; implicit
+ db 'f1' ; F1
+ db 'f2' ; F2
+ db 'f3' ; F3
+ db 'f4' ; F4
+ db 'f5' ; F5
+ db 'f6' ; F6
+ db 'f7' ; F7
+ db 'f8' ; F8
+ db 'f9' ; F9
+ db 'f0' ; F10
+ dw 0
+;
+; Misc initialized (data) variables
+;
+AppendLen dw 0 ; Bytes in append= command
+KbdTimeOut dw 0 ; Keyboard timeout (if any)
+FKeyMap dw 0 ; Bitmap for F-keys loaded
+CmdLinePtr dw cmd_line_here ; Command line advancing pointer
+initrd_flag equ $
+initrd_ptr dw 0 ; Initial ramdisk pointer/flag
+VKernelCtr dw 0 ; Number of registered vkernels
+ForcePrompt dw 0 ; Force prompt
+AllowImplicit dw 1 ; Allow implicit kernels
+VKernelsHigh db 0 ; vkernel buffers in high memory
+;
+; Stuff for the command line; we do some trickery here with equ to avoid
+; tons of zeros appended to our file and wasting space
+;
+linuxauto_cmd db 'linux '
+auto_cmd db 'auto',0
+linuxauto_len equ $-linuxauto_cmd
+auto_len equ $-auto_cmd
+boot_image db 'BOOT_IMAGE='
+boot_image_len equ $-boot_image
+ align 4 ; For the good of REP MOVSD
+command_line equ $
+default_cmd equ $+(max_cmd_len+2)
+ldlinux_end equ default_cmd+(max_cmd_len+1)
+kern_cmd_len equ ldlinux_end-command_line
+ldlinux_len equ ldlinux_end-ldlinux_magic
+;
+; Put the FAT right after the code, aligned on a sector boundary
+;
+end_of_code equ (ldlinux_end-bootsec)+7C00h
+FAT equ (end_of_code + 511) & 0FE00h
+;
+; Put getc buffer right after FAT (the FAT buffer is 6K, the max size
+; of a 12-bit FAT)
+;
+getcbuf equ FAT+6*1024
diff --git a/now.pl b/now.pl
new file mode 100644
index 00000000..60e227ec
--- /dev/null
+++ b/now.pl
@@ -0,0 +1,6 @@
+#!/usr/bin/perl
+#
+# Print the time as a hexadecimal integer
+#
+
+printf "\'0x%08x\'\n", time;
diff --git a/syslinux.doc b/syslinux.doc
new file mode 100644
index 00000000..30f942b5
--- /dev/null
+++ b/syslinux.doc
@@ -0,0 +1,255 @@
+ SYSLINUX
+ Version 1.30
+
+ A bootloader for Linux using MS-DOS floppies
+
+ Copyright (C) 1994-96 H. Peter Anvin
+
+This program is provided under the terms of the GNU General Public
+License, version 2 or, at your option, any later version. There is no
+warranty, neither expressed nor implied, to the function of this
+program. Please see the included file COPYING for details.
+
+SYSLINUX is a boot loader for the Linux operating system which operates
+off MS-DOS floppies. It is intended to simplify first-time installation
+of Linux. It is *not* intended to be used as a general purpose boot
+loader; once the operating system is installed I recommend LILO as the
+boot loader, except for people whose root filesystem is a UMSDOS
+filesystem (where a DOS defragmenter could wreck havoc with LILO's
+carefully set up block tables) -- for them I recommend Loadlin (which
+loads Linux from the DOS command line).
+
+SYSLINUX could, however, in conjunction with the UMSDOS filesystem,
+completely eliminate the need for distribution of raw diskette images
+for boot floppies. A SYSLINUX floppy can be manipulated using standard
+MS-DOS (or any other OS that can access an MS-DOS filesystem) tools once
+it has been created.
+
+ ++++ CREATING A BOOTABLE LINUX FLOPPY +++
+
+In order to create a bootable Linux floppy using SYSLINUX, prepare a
+normal MS-DOS formatted floppy. Copy one or more Linux kernel files to
+it, then execute the DOS command:
+
+ SYSLINUX drive:
+
+This will alter the boot sector on the disk and copy a file named
+LDLINUX.SYS into its root directory.
+
+On boot time, by default, the kernel will be loaded from the image named
+LINUX on the boot floppy. This default can be changed, see the section
+on the SYSLINUX config file.
+
+If the Shift or Alt keys are held down during boot, or the Caps or Scroll
+locks are set, SYSLINUX will display a LILO-style "boot:" prompt. The
+user can then type a kernel file name followed by any kernel parameters.
+The SYSLINUX loader does not need to know about the kernel file in
+advance; all that is required is that it is a file located in the root
+directory on the disk.
+
+ ++++ CONFIGURATION FILE ++++
+
+All the configurable defaults in SYSLINUX can be changed by putting a
+file called SYSLINUX.CFG in the root directory of the boot floppy. This
+is a text file in either UNIX or DOS format, containing one or more of
+the following items (case is insensitive for keywords; upper case is used
+here to indicate that a word should be typed verbatim):
+
+DEFAULT kernel options...
+
+ Sets the default command line. If SYSLINUX boots automatically,
+ it will act just as if the entries after DEFAULT had been typed
+ in at the "boot:" prompt, except that the option "auto" is
+ automatically added, indicating an automatic boot.
+
+ If no configuration file is present, or no DEFAULT entry is
+ present in the config file, the default is kernel name "linux",
+ with no options.
+
+APPEND options...
+
+ Add one or more options to the kernel command line. These are
+ added both for automatic and manual boots. The options are
+ added at the very beginning of the kernel command line,
+ usually permitting explicitly entered kernel options to override
+ them. This is the equivalent of the LILO "append" option.
+
+LABEL label
+ KERNEL kernel
+ APPEND options...
+
+ Indicates that if "label" is entered as the kernel to boot,
+ SYSLINUX should instead boot "kernel", and the specified APPEND
+ options should be used instead of the ones specified in the
+ global section of the file (before the first LABEL command.)
+ The default for "kernel" is the same as "label", and if no
+ APPEND is given the default is to use the global entry (if any).
+ Up to 16 LABEL entries are permitted.
+
+ Note that LILO uses the syntax:
+ image = mykernel
+ label = mylabel
+ append = "myoptions"
+
+ corresponding to the SYSLINUX:
+ label mylabel
+ kernel mykernel
+ append myoptions
+
+ APPEND -
+
+ Append nothing. APPEND with a single hyphen as argument in a
+ LABEL section can be used to override a global APPEND.
+
+IMPLICIT flag_val
+
+ If flag_val is 0, do not load a kernel image unless it has been
+ explicitly named in a LABEL statement. The default is 1.
+
+TIMEOUT timeout
+
+ Indicates how long to wait at the boot: prompt until booting
+ automatically, in units of 1/10 s. The timeout is cancelled as
+ soon as the user types anything on the keyboard, the assumption
+ being that the user will complete the command line already
+ begun. A timeout of zero will disable the timeout completely,
+ this is also the default.
+
+ NOTE: The maximum possible timeout value is 35996; corresponding to
+ just below one hour.
+
+DISPLAY filename
+
+ Displays the indicated file on the screen at boot time (before
+ the boot: prompt, if displayed). This option takes the place of
+ the LINUXMSG.TXT and BOOTMSG.TXT files in SYSLINUX 1.0. Please
+ see the section below on DISPLAY files.
+
+ NOTE: If the file is missing, this option is simply ignored.
+
+PROMPT flag_val
+
+ If flag_val is 0, display the boot: prompt only if the Shift or Alt
+ key is pressed, or Caps Lock or Scroll lock is set (this is the
+ default). If flag_val is 1, always display the boot: prompt. This
+ option takes the place of testing for the LINUXMSG.TXT file in
+ SYSLINUX 1.0.
+
+F1 filename
+F2 filename
+ ...etc...
+F9 filename
+F0 filename
+
+ Displays the indicated file on the screen when a function key is
+ pressed at the boot: prompt. This can be used to implement
+ pre-boot online help (presumably for the kernel command line
+ options.) Note that F10 MUST be entered in the config file as
+ "F0", not "F10", and that there is currently no way to bind
+ file names to F11 and F12. Please see the section below on
+ DISPLAY files.
+
+Blank lines, and comment lines beginning with a hash mark (#) are ignored.
+
+Note that the configuration file is not completely decoded. Syntax
+different from the one described above may still work correctly in this
+version of SYSLINUX, but may break in a future one.
+
+The following combinations of options can be used to mimic the behaviour
+of SYSLINUX 1.0 with LINUXMSG.TXT or BOOTMSG.TXT present, respectively:
+
+# Mimic SYSLINUX 1.0 with LINUXMSG.TXT file present:
+display linuxmsg.txt
+prompt 1
+
+# Mimic SYSLINUX 1.0 with BOOTMSG.TXT file present:
+display bootmsg.txt
+
+ ++++ LARGE KERNELS AND INITIAL RAMDISK SUPPORT ++++
+
+This version of SYSLINUX supports large kernels (bzImage format),
+eliminating the 500K size limit of the zImage kernel format. bzImage
+format kernels are detected automatically and handled transparently to
+the user.
+
+This version of SYSLINUX also supports a boottime-loaded ramdisk
+(initrd). An initrd is loaded from a DOS file if the option
+"initrd=filename" (where filename is the filename of the initrd image;
+the file must be located in the root directory on the boot floppy) is
+present on the processed command line (after APPEND's have been added,
+etc.). If several initrd options are present, the last one has
+precedence; this permits user-entered options to override a config
+file APPEND. Specifying "initrd=" without a filename inhibits initrd
+loading. The file specified by the initrd= option will typically be a
+gzipped filesystem image.
+
+NOTE: One of the main advantages with SYSLINUX is that it makes it
+very easy to support users with new or unexpected configurations,
+especially in a distribution setting. If initrd is used to
+extensively modularize the distribution kernel, it is strongly
+recommended that a simple way of adding drivers to the boot floppy be
+provided. The suggested manner is to let the initrd system mount the
+boot floppy and look for additional drivers in a predetermined
+location.
+
+To bzImage and recent zImage kernels, SYSLINUX 1.30 will identify
+using the ID byte 0x31. The ID range 0x32-0x3f is reserved for future
+versions of SYSLINUX.
+
+ ++++ DISPLAY FILE FORMAT ++++
+
+DISPLAY and function-key help files are text files in either DOS or UNIX
+format (with or without <CR>). In addition, the following special codes
+are interpreted:
+
+<FF> <FF> = <Ctrl-L> = ASCII 12
+ Clear the screen, home the cursor. Note that the screen is
+ filled with the current display color.
+
+<SI><bg><fg> <SI> = <Ctrl-O> = ASCII 15
+ Set the display colors to the specified background and
+ foreground colors, where <bg> and <fg> are hex digits,
+ corresponding to the standard PC display attributes:
+
+ 0 = black 8 = dark grey
+ 1 = dark blue 9 = bright blue
+ 2 = dark green a = bright green
+ 3 = dark cyan b = bright cyan
+ 4 = dark red c = bright red
+ 5 = dark purple d = bright purple
+ 6 = brown e = yellow
+ 7 = light grey f = white
+
+ Picking a bright color (8-f) for the background results in the
+ corresponding dark color (0-7), with the foreground flashing.
+
+<SUB> <SUB> = <Ctrl-Z> = ASCII 26
+ End of file (DOS convention).
+
+ ++++ NOVICE PROTECTION ++++
+
+SYSLINUX will attempt to detect if the user is trying to boot on a 286
+or lower class machine, or a machine with less than 608K of low ("DOS")
+RAM (which means the Linux boot sequence cannot complete). If so, a
+message is displayed and the boot sequence aborted. Holding down the
+Ctrl key while booting disables this feature.
+
+The compile time and date of a specific SYSLINUX version can be obtained
+by the DOS command "type ldlinux.sys". This is also used as the
+signature for the LDLINUX.SYS file, which must match the boot sector.
+
+Any file that SYSLINUX uses can be marked hidden, system or readonly if
+so is convenient; SYSLINUX ignores all file attributes. The SYSLINUX
+installed automatically sets the readonly attribute on LDLINUX.SYS.
+
+ ++++ BUG REPORTS ++++
+
+I would appreciate hearing of any problems you have with SYSLINUX. I
+would also like to hear from you if you have successfully used SYSLINUX,
+*especially* if you are using it for a distribution.
+
+Please contact me at email <hpa@zytor.com>.
+
+Sincerely,
+
+ H. Peter Anvin November 2, 1996