1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
7 ; functionality is good to have for installation floppies, where it may
8 ; be hard to find a functional Linux system to run LILO off.
10 ; This program allows manipulation of the disk to take place entirely
11 ; from MS-LOSS, and can be especially useful in conjunction with the
14 ; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
16 ; This program is free software; you can redistribute it and/or modify
17 ; it under the terms of the GNU General Public License as published by
18 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
19 ; Boston MA 02111-1307, USA; either version 2 of the License, or
20 ; (at your option) any later version; incorporated herein by reference.
22 ; ****************************************************************************
30 ; Some semi-configurable constants... change on your own risk.
33 FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
34 FILENAME_MAX equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
35 NULLFILE equ 0 ; First char space == null filename
36 NULLOFFSET equ 0 ; Position in which to look
37 retry_count equ 16 ; How patient are we with the disk?
38 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
39 LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
41 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
42 MAX_OPEN equ (1 << MAX_OPEN_LG2)
45 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
48 ; This is what we need to do when idle
58 ; The following structure is used for "virtual kernels"; i.e. LILO-style
59 ; option labels. The options we permit here are `kernel' and `append
60 ; Since there is no room in the bottom 64K for all of these, we
61 ; stick them in high memory and copy them down before we need them.
64 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
65 vk_rname: resb FILENAME_MAX ; Real name
67 vk_type: resb 1 ; Type of file
69 vk_append: resb max_cmd_len+1 ; Command line
71 vk_end: equ $ ; Should be <= vk_size
75 ; Segment assignments in the bottom 640K
76 ; Stick to the low 512K in case we're using something like M-systems flash
77 ; which load a driver into low RAM (evil!!)
79 ; 0000h - main code/data segment (and BIOS segment)
81 real_mode_seg equ 3000h
82 cache_seg equ 2000h ; 64K area for metadata cache
83 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
84 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
87 ; File structure. This holds the information for each currently open file.
90 file_sector resd 1 ; Sector pointer (0 = structure free)
91 file_left resd 1 ; Number of sectors left
95 %if (open_file_t_size & (open_file_t_size-1))
96 %error "open_file_t is not a power of 2"
100 ; ---------------------------------------------------------------------------
102 ; ---------------------------------------------------------------------------
105 ; Memory below this point is reserved for the BIOS and the MBR
108 trackbufsize equ 8192
109 trackbuf resb trackbufsize ; Track buffer goes here
115 ; Expanded superblock
117 resq 16 ; The first 16 bytes expanded 8 times
118 FAT resd 1 ; Location of (first) FAT
119 RootDirArea resd 1 ; Location of root directory area
120 RootDir resd 1 ; Location of root directory proper
121 DataArea resd 1 ; Location of data area
122 RootDirSize resd 1 ; Root dir size in sectors
123 TotalSectors resd 1 ; Total number of sectors
124 ClustSize resd 1 ; Bytes/cluster
125 ClustMask resd 1 ; Sectors/cluster - 1
126 CopySuper resb 1 ; Distinguish .bs versus .bss
127 DriveNumber resb 1 ; BIOS drive number
128 ClustShift resb 1 ; Shift count for sectors/cluster
129 ClustByteShift resb 1 ; Shift count for bytes/cluster
131 alignb open_file_t_size
132 Files resb MAX_OPEN*open_file_t_size
136 ; Some of the things that have to be saved very early are saved
137 ; "close" to the initial stack pointer offset, in order to
138 ; reduce the code size...
140 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
141 PartInfo equ StackBuf ; Saved partition table entry
142 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
143 OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack
144 OrigESDI equ StackBuf-4 ; The high dword on the stack
147 ; Primary entry point. Tempting as though it may be, we can't put the
148 ; initial "cli" here; the jmp opcode in the first byte is part of the
149 ; "magic number" (using the term very loosely) for the DOS superblock.
152 jmp short start ; 2 bytes
155 ; "Superblock" follows -- it's in the boot sector, so it's already
156 ; loaded and ready for us
158 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
160 ; These are the fields we actually care about. We end up expanding them
161 ; all to dword size early in the code, so generate labels for both
162 ; the expanded and unexpanded versions.
165 bx %+ %1 equ SuperInfo+($-superblock)*8+4
170 bx %+ %1 equ SuperInfo+($-superblock)*8
175 bx %+ %1 equ $ ; no expansion for dwords
190 superinfo_size equ ($-superblock)-1 ; How much to expand
194 ; This is as far as FAT12/16 and FAT32 are consistent
196 zb 54 ; FAT12/16 need 26 more bytes,
197 ; FAT32 need 54 more bytes
198 superblock_len equ $-superblock
200 SecPerClust equ bxSecPerClust
202 ; Note we don't check the constraints above now; we did that at install
206 cli ; No interrupts yet, please
213 mov sp,StackBuf ; Just below BSS
214 push es ; Save initial ES:DI -> $PnP pointer
218 ; DS:SI may contain a partition table entry. Preserve it for us.
220 mov cx,8 ; Save partition info
224 mov ds,ax ; Now we can initialize DS...
227 ; Now sautee the BIOS floppy info block to that it will support decent-
228 ; size transfers; the floppy block is 11 bytes and is stored in the
229 ; INT 1Eh vector (brilliant waste of resources, eh?)
231 ; Of course, if BIOSes had been properly programmed, we wouldn't have
232 ; had to waste precious space with this code.
235 lfs si,[bx] ; FS:SI -> original fdctab
236 push fs ; Save on stack in case we need to bail
239 ; Save the old fdctab even if hard disk so the stack layout
240 ; is the same. The instructions above do not change the flags
241 mov [DriveNumber],dl ; Save drive number in DL
242 and dl,dl ; If floppy disk (00-7F), assume no
247 mov cl,6 ; 12 bytes (CX == 0)
248 ; es:di -> FloppyTable already
249 ; This should be safe to do now, interrupts are off...
250 mov [bx],di ; FloppyTable
251 mov [bx+2],ax ; Segment 0
252 fs rep movsw ; Faster to move words
253 mov cl,[bsSecPerTrack] ; Patch the sector count
256 int 13h ; Some BIOSes need this
258 jmp short not_harddisk
260 ; The drive number and possibly partition information was passed to us
261 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
262 ; trust that rather than what the superblock contains.
264 ; Would it be better to zero out bsHidden if we don't have a partition table?
266 ; Note: di points to beyond the end of PartInfo
269 test byte [di-16],7Fh ; Sanity check: "active flag" should
270 jnz no_partition ; be 00 or 80
271 mov eax,[di-8] ; Partition offset (dword)
275 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
276 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
277 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
278 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
280 ; DL == drive # still
287 inc dx ; Contains # of heads - 1
290 mov [bsSecPerTrack],cx
294 ; Ready to enable interrupts, captain
299 ; Do we have EBIOS (EDD)?
303 mov ah,41h ; EDD existence query
309 test cl,1 ; Extended disk access functionality set
312 ; We have EDD support...
314 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
318 ; Load the first sector of LDLINUX.SYS; this used to be all proper
319 ; with parsing the superblock and root directory; it doesn't fit
320 ; together with EBIOS support, unfortunately.
322 mov eax,[FirstSector] ; Sector start
323 mov bx,ldlinux_sys ; Where to load it
326 ; Some modicum of integrity checking
327 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
334 ; getonesec: get one disk sector
337 mov bp,1 ; One sector
341 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
342 ; number in EAX into the buffer at ES:BX. We try to optimize
343 ; by loading up to a whole track at a time, but the user
344 ; is responsible for not crossing a 64K boundary.
345 ; (Yes, BP is weird for a count, but it was available...)
347 ; On return, BX points to the first byte after the transferred
350 ; This routine assumes CS == DS, and trashes most registers.
352 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
353 ; that is dead from that point; this saves space. However, please keep
354 ; the order to dst,src to keep things sane.
357 add eax,[bsHidden] ; Add partition offset
358 xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
360 .jmp: jmp strict short getlinsec_cbios
365 ; getlinsec implementation for EBIOS (EDD)
369 push bp ; Sectors left
371 call maxtrans ; Enforce maximum transfer size
372 movzx edi,bp ; Sectors we are about to read
389 mov ah,42h ; Extended Read
393 lea sp,[si+16] ; Remove DAPA
396 add eax,edi ; Advance sector pointer
397 sub bp,di ; Sectors left
398 shl di,SECTOR_SHIFT ; 512-byte sectors
399 add bx,di ; Advance buffer pointer
406 ; Some systems seem to get "stuck" in an error state when
407 ; using EBIOS. Doesn't happen when using CBIOS, which is
408 ; good, since some other systems get timeout failures
409 ; waiting for the floppy disk to spin up.
411 pushad ; Try resetting the device
416 loop .retry ; CX-- and jump if not zero
418 ;shr word [MaxTransfer],1 ; Reduce the transfer size
421 ; Total failure. Try falling back to CBIOS.
422 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
423 ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
426 ; ... fall through ...
431 ; getlinsec implementation for legacy CBIOS
440 movzx esi,word [bsSecPerTrack]
441 movzx edi,word [bsHeads]
443 ; Dividing by sectors to get (track,sector): we may have
444 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
448 xchg cx,dx ; CX <- sector index (0-based)
451 div edi ; Convert track to head/cyl
453 ; We should test this, but it doesn't fit...
458 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
459 ; BP = sectors to transfer, SI = bsSecPerTrack,
460 ; ES:BX = data target
463 call maxtrans ; Enforce maximum transfer size
465 ; Must not cross track boundaries, so BP <= SI-CX
472 shl ah,6 ; Because IBM was STOOPID
473 ; and thought 8 bits were enough
474 ; then thought 10 bits were enough...
475 inc cx ; Sector numbers are 1-based, sigh
480 xchg ax,bp ; Sector to transfer count
481 mov ah,02h ; Read sectors
489 movzx ecx,al ; ECX <- sectors transferred
490 shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
505 xchg ax,bp ; Sectors transferred <- 0
506 shr word [MaxTransfer],1
508 ; Fall through to disk_error
511 ; kaboom: write a message and bail out.
517 mov sp,StackBuf-4 ; Reset stack
518 mov ds,si ; Reset data segment
519 pop dword [fdctab] ; Restore FDC table
520 .patch: ; When we have full code, intercept here
523 ; Write error message, this assumes screen page 0
527 mov ah,0Eh ; Write to screen as TTY
528 mov bx,0007h ; Attribute
533 .again: int 16h ; Wait for keypress
534 ; NB: replaced by int 18h if
535 ; chosen at install time..
536 int 19h ; And try once more to boot...
537 .norge: jmp short .norge ; If int 19h returned; this is the end
540 ; Truncate BP to MaxTransfer
549 ; Error message on failure
551 bailmsg: db 'Boot error', 0Dh, 0Ah, 0
553 ; This fails if the boot sector overflows
556 FirstSector dd 0xDEADBEEF ; Location of sector 1
557 MaxTransfer dw 0x007F ; Max transfer size
559 ; This field will be filled in 0xAA55 by the installer, but we abuse it
560 ; to house a pointer to the INT 16h instruction at
561 ; kaboom.again, which gets patched to INT 18h in RAID mode.
562 bootsignature dw kaboom.again-bootsec
565 ; ===========================================================================
567 ; ===========================================================================
568 ; Start of LDLINUX.SYS
569 ; ===========================================================================
573 syslinux_banner db 0Dh, 0Ah
579 db version_str, ' ', date, ' ', 0
580 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
583 ldlinux_magic dd LDLINUX_MAGIC
584 dd LDLINUX_MAGIC^HEXDATE
587 ; This area is patched by the installer. It is found by looking for
588 ; LDLINUX_MAGIC, plus 8 bytes.
591 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
592 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
593 CheckSum dd 0 ; Checksum starting at ldlinux_sys
594 ; value = LDLINUX_MAGIC - [sum of dwords]
596 ; Space for up to 64 sectors, the theoretical maximum
597 SectorPtrs times 64 dd 0
601 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
602 ; instead of 0000:7C00 and the like. We don't want to add anything
603 ; more to the boot sector, so it is written to not assume a fixed
604 ; value in CS, but we don't want to deal with that anymore from now
611 ; Tell the user we got this far
613 mov si,syslinux_banner
617 ; Tell the user if we're using EBIOS or CBIOS
621 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
629 %define HAVE_BIOSNAME 1
634 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
635 ; sector again, though.
639 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
645 lodsd ; First sector of this chunk
653 inc edx ; Next linear sector
654 cmp [si],edx ; Does it match
655 jnz .chunk_ready ; If not, this is it
656 add si,4 ; If so, add sector to chunk
657 jmp short .make_chunk
668 ; All loaded up, verify that we got what we needed.
669 ; Note: the checksum field is embedded in the checksum region, so
670 ; by the time we get to the end it should all cancel out.
675 mov edx,-LDLINUX_MAGIC
681 and edx,edx ; Should be zero
682 jz all_read ; We're cool, go for it!
685 ; Uh-oh, something went bad...
687 mov si,checksumerr_msg
692 ; -----------------------------------------------------------------------------
693 ; Subroutines that have to be in the first sector
694 ; -----------------------------------------------------------------------------
698 ; writestr: write a null-terminated string to the console
699 ; This assumes we're on page 0. This is only used for early
700 ; messages, so it should be OK.
706 mov ah,0Eh ; Write to screen as TTY
707 mov bx,0007h ; Attribute
713 ; getlinsecsr: save registers, call getlinsec, restore registers
721 ; Checksum error message
723 checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
728 cbios_name db 'CBIOS', 0
729 ebios_name db 'EBIOS', 0
736 cmp word [Debug_Magic],0D00Dh
741 rl_checkpt equ $ ; Must be <= 8000h
743 rl_checkpt_off equ ($-$$)
745 %if rl_checkpt_off > 400h
746 %error "Sector 1 overflow"
750 ; ----------------------------------------------------------------------------
751 ; End of code and data that have to be in the first sector
752 ; ----------------------------------------------------------------------------
756 ; Let the user (and programmer!) know we got this far. This used to be
757 ; in Sector 1, but makes a lot more sense here.
764 ; Insane hack to expand the superblock to dwords
770 mov cx,superinfo_size
774 stosd ; Store expanded word
776 stosd ; Store expanded byte
780 ; Compute some information about this filesystem.
783 ; First, generate the map of regions
788 mov edx,[bsHugeSectors]
790 mov [TotalSectors],edx
792 mov eax,[bxResSectors]
793 mov [FAT],eax ; Beginning of FAT
797 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
801 mov [RootDirArea],eax ; Beginning of root directory
802 mov [RootDir],eax ; For FAT12/16 == root dir location
804 mov edx,[bxRootDirEnts]
805 add dx,SECTOR_SIZE/32-1
806 shr dx,SECTOR_SHIFT-5
807 mov [RootDirSize],edx
809 mov [DataArea],eax ; Beginning of data area
811 ; Next, generate a cluster size shift count and mask
812 mov eax,[bxSecPerClust]
817 mov [ClustByteShift],cl
826 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
829 mov eax,[TotalSectors]
831 shr eax,cl ; cl == ClustShift
832 mov cl,nextcluster_fat12-(nextcluster+2)
833 cmp eax,4085 ; FAT12 limit
835 mov cl,nextcluster_fat16-(nextcluster+2)
836 cmp eax,65525 ; FAT16 limit
839 ; FAT32, root directory is a cluster chain
842 mov eax,[bootsec+44] ; Root directory cluster
847 mov cl,nextcluster_fat28-(nextcluster+2)
849 mov byte [nextcluster+1],cl
852 ; Common initialization code
854 %include "cpuinit.inc"
858 ; Initialize the metadata cache
863 ; Now, everything is "up and running"... patch kaboom for more
864 ; verbosity and using the full screen system
867 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
870 ; Now we're all set to start with our *real* business. First load the
871 ; configuration file (if any) and parse it.
873 ; In previous versions I avoided using 32-bit registers because of a
874 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
875 ; random. I figure, though, that if there are any of those still left
876 ; they probably won't be trying to install Linux on them...
878 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
879 ; to take'm out. In fact, we may want to put them back if we're going
880 ; to boot ELKS at some point.
884 ; Load configuration file
886 mov si,config_name ; Save configuration file name
900 mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
901 mov [CurrentDir],eax ; ... the current directory
904 ; Now we have the config file open. Parse the config file and
905 ; run the user interface.
910 ; allocate_file: Allocate a file structure
923 .check: cmp dword [bx], byte 0
925 add bx,open_file_t_size ; ZF = 0
927 ; ZF = 0 if we fell out of the loop
933 ; Search a specific directory for a pre-mangled filename in
934 ; MangledBuf, in the directory starting in sector EAX.
936 ; NOTE: This file considers finding a zero-length file an
937 ; error. This is so we don't have to deal with that special
938 ; case elsewhere in the program (most loops have the test
941 ; Assumes DS == ES == CS.
946 ; EAX = file length (MAY BE ZERO!)
947 ; DL = file attributes
964 ; EAX <- directory sector to scan
966 ; GS:SI now points to this sector
968 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
971 jz .failure ; Hit directory high water mark
972 test byte [gs:si+11],8 ; Ignore volume labels and
973 ; VFAT long filename entries
990 jnc .scansector ; CF is set if we're at end
992 ; If we get here, we failed
999 xor eax,eax ; ZF <- 1
1002 mov eax,[gs:si+28] ; File size
1003 add eax,SECTOR_SIZE-1
1004 shr eax,SECTOR_SHIFT
1005 mov [bx+4],eax ; Sector count
1008 mov dx,[gs:si+20] ; High cluster word
1010 mov dx,[gs:si+26] ; Low cluster word
1014 mov [bx],edx ; Starting sector
1016 mov eax,[gs:si+28] ; File length again
1017 mov dl,[gs:si+11] ; File attribute
1018 mov si,bx ; File pointer...
1029 ; Deallocates a file structure (pointer in SI)
1035 mov dword [si],0 ; First dword == file_left
1048 ; EAX = file length in bytes
1052 ; Assumes CS == DS == ES, and trashes BX and CX.
1055 mov eax,[CurrentDir]
1056 cmp byte [di],'/' ; Root directory?
1063 push eax ; <A> Current directory sector
1073 pop eax ; <A> Current directory sector
1075 mov [PrevDir],eax ; Remember last directory searched
1078 call mangle_dos_name ; MangledBuf <- component
1081 jz .notfound ; Pathname component missing
1083 cmp byte [di-1],'/' ; Do we expect a directory
1086 ; Otherwise, it should be a file
1088 test dl,18h ; Subdirectory|Volume Label
1089 jnz .badfile ; If not a file, it's a bad thing
1091 ; SI and EAX are already set
1092 and eax,eax ; EAX != 0
1096 ; If we expected a directory, it better be one...
1098 test dl,10h ; Subdirectory
1102 xchg eax,[si+file_sector] ; Get sector number and free file structure
1103 jmp .pathwalk ; Walk the next bit of the path
1107 mov [si],eax ; Free file structure
1115 CurrentDir resd 1 ; Current directory
1116 PrevDir resd 1 ; Last scanned directory
1122 ; kaboom2: once everything is loaded, replace the part of kaboom
1123 ; starting with "kaboom.patch" with this part
1126 mov si,err_bootfailed
1128 cmp byte [kaboom.again+1],18h ; INT 18h version?
1132 int 19h ; And try once more to boot...
1133 .norge: jmp short .norge ; If int 19h returned; this is the end
1137 .noreg: jmp short .noreg ; Nynorsk
1140 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1141 ; to by ES:DI; ends on encountering any whitespace.
1144 ; This verifies that a filename is < FILENAME_MAX characters,
1145 ; doesn't contain whitespace, zero-pads the output buffer,
1146 ; and removes trailing dots and redundant slashes, plus changes
1147 ; backslashes to forward slashes,
1148 ; so "repe cmpsb" can do a compare, and the path-searching routine
1149 ; gets a bit of an easier job.
1156 mov cx,FILENAME_MAX-1
1161 cmp al,' ' ; If control or space, end
1163 cmp al,'\' ; Backslash?
1165 mov al,'/' ; Change to forward slash
1167 cmp al,ah ; Repeated slash?
1174 .mn_skip: loop .mn_loop
1176 cmp bx,di ; At the beginning of the buffer?
1178 cmp byte [es:di-1],'.' ; Terminal dot?
1180 cmp byte [es:di-1],'/' ; Terminal slash?
1182 .mn_kill: dec di ; If so, remove it
1186 inc cx ; At least one null byte
1187 xor ax,ax ; Zero-fill name
1194 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1195 ; filename to the conventional representation. This is needed
1196 ; for the BOOT_IMAGE= parameter for the kernel.
1197 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1198 ; known to be shorter.
1200 ; DS:SI -> input mangled file name
1201 ; ES:DI -> output buffer
1203 ; On return, DI points to the first byte after the output name,
1204 ; which is set to a null byte.
1206 unmangle_name: call strcpy
1207 dec di ; Point to final null byte
1212 ; Mangle a DOS filename component pointed to by DS:SI
1213 ; into [MangledBuf]; ends on encountering any whitespace or slash.
1214 ; Assumes CS == DS == ES.
1221 mov cx,11 ; # of bytes to write
1224 cmp al,' ' ; If control or space, end
1226 cmp al,'/' ; Slash, too
1228 cmp al,'.' ; Period -> space-fill
1235 jmp short .not_lower
1236 .is_period: mov al,' ' ; We need to space-fill
1237 .period_loop: cmp cx,3 ; If <= 3 characters left
1238 jbe .loop ; Just ignore it
1239 stosb ; Otherwise, write a period
1240 loop .period_loop ; Dec CX and (always) jump
1241 .not_uslower: cmp al,ucase_low
1245 mov bx,ucase_tab-ucase_low
1248 loop .loop ; Don't continue if too long
1250 mov al,' ' ; Space-fill name
1251 rep stosb ; Doesn't do anything if CX=0
1260 ; Case tables for extended characters; this is technically code page 865,
1261 ; but code page 437 users will probably not miss not being able to use the
1262 ; cent sign in kernel images too much :-)
1264 ; The table only covers the range 129 to 164; the rest we can deal with.
1270 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1271 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1272 db 157, 156, 157, 158, 159, 'AIOU', 165
1276 ; getfssec_edx: Get multiple sectors from a file
1278 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1279 ; and will correct the situation if it does, UNLESS *sectors* cross
1283 ; EDX -> Current sector number
1284 ; CX -> Sector count (0FFFFh = until end of file)
1285 ; Must not exceed the ES segment
1286 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1287 ; All arguments are advanced to reflect data read.
1293 xor ebp,ebp ; Fragment sector count
1294 push edx ; Starting sector pointer
1302 add ax,bx ; Now AX = how far into 64K block we are
1303 not ax ; Bytes left in 64K block
1305 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1307 jnb .do_read ; Unless there is at least 1 more sector room...
1308 mov eax,edx ; Current sector
1309 inc edx ; Predict it's the linearly next sector
1312 cmp edx,eax ; Did it match?
1315 pop eax ; Starting sector pointer
1317 lea eax,[eax+ebp-1] ; This is the last sector actually read
1319 add bx,bp ; Adjust buffer pointer
1335 ; getfssec: Get multiple sectors from a file
1337 ; Same as above, except SI is a pointer to a open_file_t
1340 ; DS:SI -> Pointer to open_file_t
1341 ; CX -> Sector count (0FFFFh = until end of file)
1342 ; Must not exceed the ES segment
1343 ; Returns CF=1 on EOF (not necessarily error)
1344 ; All arguments are advanced to reflect data read.
1362 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1363 ; pointed at in the FAT tables. CF=0 on return if end of file.
1366 jmp strict short nextcluster_fat28 ; This gets patched
1376 pushf ; Save the shifted-out LSB (=CF)
1405 ; FAT16 decoding routine.
1412 shr eax,SECTOR_SHIFT-1
1417 movzx edi,word [gs:si+bx]
1424 ; FAT28 ("FAT32") decoding routine.
1431 shr eax,SECTOR_SHIFT-2
1437 mov edi,dword [gs:si+bx]
1438 and edi,0FFFFFFFh ; 28 bits only
1446 ; nextsector: Given a sector in EAX on input, return the next sector
1447 ; of the same filesystem object, which may be the root
1448 ; directory or a cluster chain. Returns EOF.
1468 test edi,[ClustMask]
1471 ; It's not the final sector in a cluster
1476 push gs ; nextcluster trashes gs
1483 ; Now EDI contains the cluster number
1486 jc .exit ; There isn't anything else...
1488 ; New cluster number now in EDI
1490 shl edi,cl ; CF <- 0, unless something is very wrong
1501 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1502 ; and return a pointer in GS:SI, loading it if needed.
1507 add eax,[FAT] ; FAT starting address
1510 ; -----------------------------------------------------------------------------
1512 ; -----------------------------------------------------------------------------
1514 %include "getc.inc" ; getc et al
1515 %include "conio.inc" ; Console I/O
1516 %include "plaincon.inc" ; writechr
1517 %include "writestr.inc" ; String output
1518 %include "configinit.inc" ; Initialize configuration
1519 %include "parseconfig.inc" ; High-level config file handling
1520 %include "parsecmd.inc" ; Low-level config file handling
1521 %include "bcopy32.inc" ; 32-bit bcopy
1522 %include "loadhigh.inc" ; Load a file into high memory
1523 %include "font.inc" ; VGA font stuff
1524 %include "graphics.inc" ; VGA graphics
1525 %include "highmem.inc" ; High memory sizing
1526 %include "strcpy.inc" ; strcpy()
1527 %include "cache.inc" ; Metadata disk cache
1528 %include "adv.inc" ; Auxillary Data Vector
1530 ; -----------------------------------------------------------------------------
1531 ; Begin data section
1532 ; -----------------------------------------------------------------------------
1535 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1537 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1538 db 'a key to continue.', CR, LF, 0
1539 syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
1540 syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
1541 syslinux_cfg3 db '/' ; /syslinux.cfg
1542 config_name db 'syslinux.cfg', 0 ; syslinux.cfg
1545 ; Command line options we'd like to take a look at
1547 ; mem= and vga= are handled as normal 32-bit integer values
1548 initrd_cmd db 'initrd='
1549 initrd_cmd_len equ 7
1552 ; Config file keyword table
1554 %include "keywords.inc"
1557 ; Extensions to search for (in *forward* order).
1559 exten_table: db '.cbt' ; COMBOOT (specific)
1560 db '.bss' ; Boot Sector (add superblock)
1561 db '.bs', 0 ; Boot Sector
1562 db '.com' ; COMBOOT (same as DOS)
1565 dd 0, 0 ; Need 8 null bytes here
1568 ; Misc initialized (data) variables
1570 %ifdef debug ; This code for debugging only
1571 debug_magic dw 0D00Dh ; Debug code sentinel
1575 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1576 BufSafeBytes dw trackbufsize ; = how many bytes?
1578 %if ( trackbufsize % SECTOR_SIZE ) != 0
1579 %error trackbufsize must be a multiple of SECTOR_SIZE