Support subdirectories in the FAT version of SYSLINUX
[profile/ivi/syslinux.git] / ldlinux.asm
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
3 ;
4 ;  ldlinux.asm
5 ;
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.
9 ;
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
12 ;  umsdos filesystem.
13 ;
14 ;   Copyright (C) 1994-2007   H. Peter Anvin
15 ;
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.
21 ;
22 ; ****************************************************************************
23
24 %ifndef IS_MDSLINUX
25 %define IS_SYSLINUX 1
26 %endif
27 %include "head.inc"
28
29 ;
30 ; Some semi-configurable constants... change on your own risk.
31 ;
32 my_id           equ syslinux_id
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
40
41 MAX_OPEN_LG2    equ 6                   ; log2(Max number of open files)
42 MAX_OPEN        equ (1 << MAX_OPEN_LG2)
43
44 SECTOR_SHIFT    equ 9
45 SECTOR_SIZE     equ (1 << SECTOR_SHIFT)
46
47 ;
48 ; This is what we need to do when idle
49 ;
50 %macro  RESET_IDLE 0
51         ; Nothing
52 %endmacro
53 %macro  DO_IDLE 0
54         ; Nothing
55 %endmacro
56
57 ;
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 at vk_seg:0000 and copy them down before we need them.
62 ;
63                 struc vkernel
64 vk_vname:       resb FILENAME_MAX       ; Virtual name **MUST BE FIRST!**
65 vk_rname:       resb FILENAME_MAX       ; Real name
66 vk_appendlen:   resw 1
67                 alignb 4
68 vk_append:      resb max_cmd_len+1      ; Command line
69                 alignb 4
70 vk_end:         equ $                   ; Should be <= vk_size
71                 endstruc
72
73 ;
74 ; Segment assignments in the bottom 640K
75 ; Stick to the low 512K in case we're using something like M-systems flash
76 ; which load a driver into low RAM (evil!!)
77 ;
78 ; 0000h - main code/data segment (and BIOS segment)
79 ;
80 real_mode_seg   equ 4000h
81 cache_seg       equ 3000h               ; 64K area for metadata cache
82 vk_seg          equ 2000h               ; Virtual kernels
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
85
86 ;
87 ; File structure.  This holds the information for each currently open file.
88 ;
89                 struc open_file_t
90 file_sector     resd 1                  ; Sector pointer (0 = structure free)
91 file_left       resd 1                  ; Number of sectors left
92                 endstruc
93
94 %ifndef DEPEND
95 %if (open_file_t_size & (open_file_t_size-1))
96 %error "open_file_t is not a power of 2"
97 %endif
98 %endif
99
100 ; ---------------------------------------------------------------------------
101 ;   BEGIN CODE
102 ; ---------------------------------------------------------------------------
103
104 ;
105 ; Memory below this point is reserved for the BIOS and the MBR
106 ;
107                 section .earlybss
108 trackbufsize    equ 8192
109 trackbuf        resb trackbufsize       ; Track buffer goes here
110 getcbuf         resb trackbufsize
111                 ; ends at 4800h
112
113                 section .bss
114                 alignb 8
115
116                 ; Expanded superblock
117 SuperInfo       equ $
118                 resq 16                 ; The first 16 bytes expanded 8 times
119 FAT             resd 1                  ; Location of (first) FAT
120 RootDirArea     resd 1                  ; Location of root directory area
121 RootDir         resd 1                  ; Location of root directory proper
122 DataArea        resd 1                  ; Location of data area
123 RootDirSize     resd 1                  ; Root dir size in sectors
124 TotalSectors    resd 1                  ; Total number of sectors
125 EndSector       resd 1                  ; Location of filesystem end
126 ClustSize       resd 1                  ; Bytes/cluster
127 ClustMask       resd 1                  ; Sectors/cluster - 1
128 CopySuper       resb 1                  ; Distinguish .bs versus .bss
129 DriveNumber     resb 1                  ; BIOS drive number
130 ClustShift      resb 1                  ; Shift count for sectors/cluster
131 ClustByteShift  resb 1                  ; Shift count for bytes/cluster
132
133                 alignb open_file_t_size
134 Files           resb MAX_OPEN*open_file_t_size
135
136 ;
137 ; Constants for the xfer_buf_seg
138 ;
139 ; The xfer_buf_seg is also used to store message file buffers.  We
140 ; need two trackbuffers (text and graphics), plus a work buffer
141 ; for the graphics decompressor.
142 ;
143 xbs_textbuf     equ 0                   ; Also hard-coded, do not change
144 xbs_vgabuf      equ trackbufsize
145 xbs_vgatmpbuf   equ 2*trackbufsize
146
147
148                 section .text
149 ;
150 ; Some of the things that have to be saved very early are saved
151 ; "close" to the initial stack pointer offset, in order to
152 ; reduce the code size...
153 ;
154 StackBuf        equ $-44-32             ; Start the stack here (grow down - 4K)
155 PartInfo        equ StackBuf            ; Saved partition table entry
156 FloppyTable     equ PartInfo+16         ; Floppy info table (must follow PartInfo)
157 OrigFDCTabPtr   equ StackBuf-4          ; The high dword on the stack
158
159 ;
160 ; Primary entry point.  Tempting as though it may be, we can't put the
161 ; initial "cli" here; the jmp opcode in the first byte is part of the
162 ; "magic number" (using the term very loosely) for the DOS superblock.
163 ;
164 bootsec         equ $
165                 jmp short start         ; 2 bytes
166                 nop                     ; 1 byte
167 ;
168 ; "Superblock" follows -- it's in the boot sector, so it's already
169 ; loaded and ready for us
170 ;
171 bsOemName       db 'SYSLINUX'           ; The SYS command sets this, so...
172 ;
173 ; These are the fields we actually care about.  We end up expanding them
174 ; all to dword size early in the code, so generate labels for both
175 ; the expanded and unexpanded versions.
176 ;
177 %macro          superb 1
178 bx %+ %1        equ SuperInfo+($-superblock)*8+4
179 bs %+ %1        equ $
180                 zb 1
181 %endmacro
182 %macro          superw 1
183 bx %+ %1        equ SuperInfo+($-superblock)*8
184 bs %+ %1        equ $
185                 zw 1
186 %endmacro
187 %macro          superd 1
188 bx %+ %1        equ $                   ; no expansion for dwords
189 bs %+ %1        equ $
190                 zd 1
191 %endmacro
192 superblock      equ $
193                 superw BytesPerSec
194                 superb SecPerClust
195                 superw ResSectors
196                 superb FATs
197                 superw RootDirEnts
198                 superw Sectors
199                 superb Media
200                 superw FATsecs
201                 superw SecPerTrack
202                 superw Heads
203 superinfo_size  equ ($-superblock)-1    ; How much to expand
204                 superd Hidden
205                 superd HugeSectors
206                 ;
207                 ; This is as far as FAT12/16 and FAT32 are consistent
208                 ;
209                 zb 54                   ; FAT12/16 need 26 more bytes,
210                                         ; FAT32 need 54 more bytes
211 superblock_len  equ $-superblock
212
213 SecPerClust     equ bxSecPerClust
214 ;
215 ; Note we don't check the constraints above now; we did that at install
216 ; time (we hope!)
217 ;
218 start:
219                 cli                     ; No interrupts yet, please
220                 cld                     ; Copy upwards
221 ;
222 ; Set up the stack
223 ;
224                 xor ax,ax
225                 mov ss,ax
226                 mov sp,StackBuf         ; Just below BSS
227                 mov es,ax
228 ;
229 ; DS:SI may contain a partition table entry.  Preserve it for us.
230 ;
231                 mov cx,8                ; Save partition info
232                 mov di,sp
233                 rep movsw
234
235                 mov ds,ax               ; Now we can initialize DS...
236
237 ;
238 ; Now sautee the BIOS floppy info block to that it will support decent-
239 ; size transfers; the floppy block is 11 bytes and is stored in the
240 ; INT 1Eh vector (brilliant waste of resources, eh?)
241 ;
242 ; Of course, if BIOSes had been properly programmed, we wouldn't have
243 ; had to waste precious space with this code.
244 ;
245                 mov bx,fdctab
246                 lfs si,[bx]             ; FS:SI -> original fdctab
247                 push fs                 ; Save on stack in case we need to bail
248                 push si
249
250                 ; Save the old fdctab even if hard disk so the stack layout
251                 ; is the same.  The instructions above do not change the flags
252                 mov [DriveNumber],dl    ; Save drive number in DL
253                 and dl,dl               ; If floppy disk (00-7F), assume no
254                                         ; partition table
255                 js harddisk
256
257 floppy:
258                 mov cl,6                ; 12 bytes (CX == 0)
259                 ; es:di -> FloppyTable already
260                 ; This should be safe to do now, interrupts are off...
261                 mov [bx],di             ; FloppyTable
262                 mov [bx+2],ax           ; Segment 0
263                 fs rep movsw            ; Faster to move words
264                 mov cl,[bsSecPerTrack]  ; Patch the sector count
265                 mov [di-8],cl
266                 ; AX == 0 here
267                 int 13h                 ; Some BIOSes need this
268
269                 jmp short not_harddisk
270 ;
271 ; The drive number and possibly partition information was passed to us
272 ; by the BIOS or previous boot loader (MBR).  Current "best practice" is to
273 ; trust that rather than what the superblock contains.
274 ;
275 ; Would it be better to zero out bsHidden if we don't have a partition table?
276 ;
277 ; Note: di points to beyond the end of PartInfo
278 ;
279 harddisk:
280                 test byte [di-16],7Fh   ; Sanity check: "active flag" should
281                 jnz no_partition        ; be 00 or 80
282                 mov eax,[di-8]          ; Partition offset (dword)
283                 mov [bsHidden],eax
284 no_partition:
285 ;
286 ; Get disk drive parameters (don't trust the superblock.)  Don't do this for
287 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
288 ; what the *drive* supports, not about the *media*.  Fortunately floppy disks
289 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
290 ;
291                 ; DL == drive # still
292                 mov ah,08h
293                 int 13h
294                 jc no_driveparm
295                 and ah,ah
296                 jnz no_driveparm
297                 shr dx,8
298                 inc dx                  ; Contains # of heads - 1
299                 mov [bsHeads],dx
300                 and cx,3fh
301                 mov [bsSecPerTrack],cx
302 no_driveparm:
303 not_harddisk:
304 ;
305 ; Ready to enable interrupts, captain
306 ;
307                 sti
308
309 ;
310 ; Do we have EBIOS (EDD)?
311 ;
312 eddcheck:
313                 mov bx,55AAh
314                 mov ah,41h              ; EDD existence query
315                 mov dl,[DriveNumber]
316                 int 13h
317                 jc .noedd
318                 cmp bx,0AA55h
319                 jne .noedd
320                 test cl,1               ; Extended disk access functionality set
321                 jz .noedd
322                 ;
323                 ; We have EDD support...
324                 ;
325                 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
326 .noedd:
327
328 ;
329 ; Load the first sector of LDLINUX.SYS; this used to be all proper
330 ; with parsing the superblock and root directory; it doesn't fit
331 ; together with EBIOS support, unfortunately.
332 ;
333                 mov eax,[FirstSector]   ; Sector start
334                 mov bx,ldlinux_sys      ; Where to load it
335                 call getonesec
336
337                 ; Some modicum of integrity checking
338                 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
339                 jne kaboom
340
341                 ; Go for it...
342                 jmp ldlinux_ent
343
344 ;
345 ; getonesec: get one disk sector
346 ;
347 getonesec:
348                 mov bp,1                ; One sector
349                 ; Fall through
350
351 ;
352 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
353 ;            number in EAX into the buffer at ES:BX.  We try to optimize
354 ;            by loading up to a whole track at a time, but the user
355 ;            is responsible for not crossing a 64K boundary.
356 ;            (Yes, BP is weird for a count, but it was available...)
357 ;
358 ;            On return, BX points to the first byte after the transferred
359 ;            block.
360 ;
361 ;            This routine assumes CS == DS, and trashes most registers.
362 ;
363 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
364 ; that is dead from that point; this saves space.  However, please keep
365 ; the order to dst,src to keep things sane.
366 ;
367 getlinsec:
368                 add eax,[bsHidden]              ; Add partition offset
369                 xor edx,edx                     ; Zero-extend LBA (eventually allow 64 bits)
370
371 .jmp:           jmp strict short getlinsec_cbios
372
373 ;
374 ; getlinsec_ebios:
375 ;
376 ; getlinsec implementation for EBIOS (EDD)
377 ;
378 getlinsec_ebios:
379 .loop:
380                 push bp                         ; Sectors left
381 .retry2:
382                 call maxtrans                   ; Enforce maximum transfer size
383                 movzx edi,bp                    ; Sectors we are about to read
384                 mov cx,retry_count
385 .retry:
386
387                 ; Form DAPA on stack
388                 push edx
389                 push eax
390                 push es
391                 push bx
392                 push di
393                 push word 16
394                 mov si,sp
395                 pushad
396                 mov dl,[DriveNumber]
397                 push ds
398                 push ss
399                 pop ds                          ; DS <- SS
400                 mov ah,42h                      ; Extended Read
401                 int 13h
402                 pop ds
403                 popad
404                 lea sp,[si+16]                  ; Remove DAPA
405                 jc .error
406                 pop bp
407                 add eax,edi                     ; Advance sector pointer
408                 sub bp,di                       ; Sectors left
409                 shl di,SECTOR_SHIFT             ; 512-byte sectors
410                 add bx,di                       ; Advance buffer pointer
411                 and bp,bp
412                 jnz .loop
413
414                 ret
415
416 .error:
417                 ; Some systems seem to get "stuck" in an error state when
418                 ; using EBIOS.  Doesn't happen when using CBIOS, which is
419                 ; good, since some other systems get timeout failures
420                 ; waiting for the floppy disk to spin up.
421
422                 pushad                          ; Try resetting the device
423                 xor ax,ax
424                 mov dl,[DriveNumber]
425                 int 13h
426                 popad
427                 loop .retry                     ; CX-- and jump if not zero
428
429                 ;shr word [MaxTransfer],1       ; Reduce the transfer size
430                 ;jnz .retry2
431
432                 ; Total failure.  Try falling back to CBIOS.
433                 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
434                 ;mov byte [MaxTransfer],63      ; Max possibe CBIOS transfer
435
436                 pop bp
437                 ; ... fall through ...
438
439 ;
440 ; getlinsec_cbios:
441 ;
442 ; getlinsec implementation for legacy CBIOS
443 ;
444 getlinsec_cbios:
445 .loop:
446                 push edx
447                 push eax
448                 push bp
449                 push bx
450
451                 movzx esi,word [bsSecPerTrack]
452                 movzx edi,word [bsHeads]
453                 ;
454                 ; Dividing by sectors to get (track,sector): we may have
455                 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
456                 ;
457                 div esi
458                 xor cx,cx
459                 xchg cx,dx              ; CX <- sector index (0-based)
460                                         ; EDX <- 0
461                 ; eax = track #
462                 div edi                 ; Convert track to head/cyl
463
464                 ; We should test this, but it doesn't fit...
465                 ; cmp eax,1023
466                 ; ja .error
467
468                 ;
469                 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
470                 ; BP = sectors to transfer, SI = bsSecPerTrack,
471                 ; ES:BX = data target
472                 ;
473
474                 call maxtrans                   ; Enforce maximum transfer size
475
476                 ; Must not cross track boundaries, so BP <= SI-CX
477                 sub si,cx
478                 cmp bp,si
479                 jna .bp_ok
480                 mov bp,si
481 .bp_ok:
482
483                 shl ah,6                ; Because IBM was STOOPID
484                                         ; and thought 8 bits were enough
485                                         ; then thought 10 bits were enough...
486                 inc cx                  ; Sector numbers are 1-based, sigh
487                 or cl,ah
488                 mov ch,al
489                 mov dh,dl
490                 mov dl,[DriveNumber]
491                 xchg ax,bp              ; Sector to transfer count
492                 mov ah,02h              ; Read sectors
493                 mov bp,retry_count
494 .retry:
495                 pushad
496                 int 13h
497                 popad
498                 jc .error
499 .resume:
500                 movzx ecx,al            ; ECX <- sectors transferred
501                 shl ax,SECTOR_SHIFT     ; Convert sectors in AL to bytes in AX
502                 pop bx
503                 add bx,ax
504                 pop bp
505                 pop eax
506                 pop edx
507                 add eax,ecx
508                 sub bp,cx
509                 jnz .loop
510                 ret
511
512 .error:
513                 dec bp
514                 jnz .retry
515
516                 xchg ax,bp              ; Sectors transferred <- 0
517                 shr word [MaxTransfer],1
518                 jnz .resume
519                 ; Fall through to disk_error
520
521 ;
522 ; kaboom: write a message and bail out.
523 ;
524 disk_error:
525 kaboom:
526                 xor si,si
527                 mov ss,si
528                 mov sp,StackBuf-4       ; Reset stack
529                 mov ds,si               ; Reset data segment
530                 pop dword [fdctab]      ; Restore FDC table
531 .patch:                                 ; When we have full code, intercept here
532                 mov si,bailmsg
533
534                 ; Write error message, this assumes screen page 0
535 .loop:          lodsb
536                 and al,al
537                 jz .done
538                 mov ah,0Eh              ; Write to screen as TTY
539                 mov bx,0007h            ; Attribute
540                 int 10h
541                 jmp short .loop
542 .done:
543                 cbw                     ; AH <- 0
544                 int 16h                 ; Wait for keypress
545                 int 19h                 ; And try once more to boot...
546 .norge:         jmp short .norge        ; If int 19h returned; this is the end
547
548 ;
549 ; Truncate BP to MaxTransfer
550 ;
551 maxtrans:
552                 cmp bp,[MaxTransfer]
553                 jna .ok
554                 mov bp,[MaxTransfer]
555 .ok:            ret
556
557 ;
558 ; Error message on failure
559 ;
560 bailmsg:        db 'Boot error', 0Dh, 0Ah, 0
561
562                 ; This fails if the boot sector overflows
563                 zb 1F8h-($-$$)
564
565 FirstSector     dd 0xDEADBEEF                   ; Location of sector 1
566 MaxTransfer     dw 0x007F                       ; Max transfer size
567 bootsignature   dw 0AA55h
568
569 ;
570 ; ===========================================================================
571 ;  End of boot sector
572 ; ===========================================================================
573 ;  Start of LDLINUX.SYS
574 ; ===========================================================================
575
576 ldlinux_sys:
577
578 syslinux_banner db 0Dh, 0Ah
579 %if IS_MDSLINUX
580                 db 'MDSLINUX '
581 %else
582                 db 'SYSLINUX '
583 %endif
584                 db version_str, ' ', date, ' ', 0
585                 db 0Dh, 0Ah, 1Ah        ; EOF if we "type" this in DOS
586
587                 align 8, db 0
588 ldlinux_magic   dd LDLINUX_MAGIC
589                 dd LDLINUX_MAGIC^HEXDATE
590
591 ;
592 ; This area is patched by the installer.  It is found by looking for
593 ; LDLINUX_MAGIC, plus 8 bytes.
594 ;
595 patch_area:
596 LDLDwords       dw 0            ; Total dwords starting at ldlinux_sys
597 LDLSectors      dw 0            ; Number of sectors - (bootsec+this sec)
598 CheckSum        dd 0            ; Checksum starting at ldlinux_sys
599                                 ; value = LDLINUX_MAGIC - [sum of dwords]
600
601 ; Space for up to 64 sectors, the theoretical maximum
602 SectorPtrs      times 64 dd 0
603
604 ldlinux_ent:
605 ;
606 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
607 ; instead of 0000:7C00 and the like.  We don't want to add anything
608 ; more to the boot sector, so it is written to not assume a fixed
609 ; value in CS, but we don't want to deal with that anymore from now
610 ; on.
611 ;
612                 jmp 0:.next
613 .next:
614
615 ;
616 ; Tell the user we got this far
617 ;
618                 mov si,syslinux_banner
619                 call writestr
620
621 ;
622 ; Tell the user if we're using EBIOS or CBIOS
623 ;
624 print_bios:
625                 mov si,cbios_name
626                 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
627                 jne .cbios
628                 mov si,ebios_name
629 .cbios:
630                 mov [BIOSName],si
631                 call writestr
632
633                 section .bss
634 %define HAVE_BIOSNAME 1
635 BIOSName        resw 1
636
637                 section .text
638 ;
639 ; Now we read the rest of LDLINUX.SYS.  Don't bother loading the first
640 ; sector again, though.
641 ;
642 load_rest:
643                 mov si,SectorPtrs
644                 mov bx,7C00h+2*SECTOR_SIZE      ; Where we start loading
645                 mov cx,[LDLSectors]
646
647 .get_chunk:
648                 jcxz .done
649                 xor bp,bp
650                 lodsd                           ; First sector of this chunk
651
652                 mov edx,eax
653
654 .make_chunk:
655                 inc bp
656                 dec cx
657                 jz .chunk_ready
658                 inc edx                         ; Next linear sector
659                 cmp [si],edx                    ; Does it match
660                 jnz .chunk_ready                ; If not, this is it
661                 add si,4                        ; If so, add sector to chunk
662                 jmp short .make_chunk
663
664 .chunk_ready:
665                 call getlinsecsr
666                 shl bp,SECTOR_SHIFT
667                 add bx,bp
668                 jmp .get_chunk
669
670 .done:
671
672 ;
673 ; All loaded up, verify that we got what we needed.
674 ; Note: the checksum field is embedded in the checksum region, so
675 ; by the time we get to the end it should all cancel out.
676 ;
677 verify_checksum:
678                 mov si,ldlinux_sys
679                 mov cx,[LDLDwords]
680                 mov edx,-LDLINUX_MAGIC
681 .checksum:
682                 lodsd
683                 add edx,eax
684                 loop .checksum
685
686                 and edx,edx                     ; Should be zero
687                 jz all_read                     ; We're cool, go for it!
688
689 ;
690 ; Uh-oh, something went bad...
691 ;
692                 mov si,checksumerr_msg
693                 call writestr
694                 jmp kaboom
695
696 ;
697 ; -----------------------------------------------------------------------------
698 ; Subroutines that have to be in the first sector
699 ; -----------------------------------------------------------------------------
700
701 ;
702 ;
703 ; writestr: write a null-terminated string to the console
704 ;           This assumes we're on page 0.  This is only used for early
705 ;           messages, so it should be OK.
706 ;
707 writestr:
708 .loop:          lodsb
709                 and al,al
710                 jz .return
711                 mov ah,0Eh              ; Write to screen as TTY
712                 mov bx,0007h            ; Attribute
713                 int 10h
714                 jmp short .loop
715 .return:        ret
716
717
718 ; getlinsecsr: save registers, call getlinsec, restore registers
719 ;
720 getlinsecsr:    pushad
721                 call getlinsec
722                 popad
723                 ret
724
725 ;
726 ; Checksum error message
727 ;
728 checksumerr_msg db ' Load error - ', 0  ; Boot failed appended
729
730 ;
731 ; BIOS type string
732 ;
733 cbios_name      db 'CBIOS', 0
734 ebios_name      db 'EBIOS', 0
735
736 ;
737 ; Debug routine
738 ;
739 %ifdef debug
740 safedumpregs:
741                 cmp word [Debug_Magic],0D00Dh
742                 jnz nc_return
743                 jmp dumpregs
744 %endif
745
746 rl_checkpt      equ $                           ; Must be <= 8000h
747
748 rl_checkpt_off  equ ($-$$)
749 %ifndef DEPEND
750 %if rl_checkpt_off > 400h
751 %error "Sector 1 overflow"
752 %endif
753 %endif
754
755 ; ----------------------------------------------------------------------------
756 ;  End of code and data that have to be in the first sector
757 ; ----------------------------------------------------------------------------
758
759 all_read:
760 ;
761 ; Let the user (and programmer!) know we got this far.  This used to be
762 ; in Sector 1, but makes a lot more sense here.
763 ;
764                 mov si,copyright_str
765                 call writestr
766
767
768 ;
769 ; Insane hack to expand the superblock to dwords
770 ;
771 expand_super:
772                 xor eax,eax
773                 mov si,superblock
774                 mov di,SuperInfo
775                 mov cx,superinfo_size
776 .loop:
777                 lodsw
778                 dec si
779                 stosd                           ; Store expanded word
780                 xor ah,ah
781                 stosd                           ; Store expanded byte
782                 loop .loop
783
784 ;
785 ; Compute some information about this filesystem.
786 ;
787
788 ; First, generate the map of regions
789 genfatinfo:
790                 mov edx,[bxSectors]
791                 and dx,dx
792                 jnz .have_secs
793                 mov edx,[bsHugeSectors]
794 .have_secs:
795                 mov [TotalSectors],edx
796
797                 add edx,eax
798                 mov [EndSector],edx
799
800                 mov eax,[bxResSectors]
801                 mov [FAT],eax                   ; Beginning of FAT
802                 mov edx,[bxFATsecs]
803                 and dx,dx
804                 jnz .have_fatsecs
805                 mov edx,[bootsec+36]            ; FAT32 BPB_FATsz32
806 .have_fatsecs:
807                 imul edx,[bxFATs]
808                 add eax,edx
809                 mov [RootDirArea],eax           ; Beginning of root directory
810                 mov [RootDir],eax               ; For FAT12/16 == root dir location
811
812                 mov edx,[bxRootDirEnts]
813                 add dx,SECTOR_SIZE/32-1
814                 shr dx,SECTOR_SHIFT-5
815                 mov [RootDirSize],edx
816                 add eax,edx
817                 mov [DataArea],eax              ; Beginning of data area
818
819 ; Next, generate a cluster size shift count and mask
820                 mov eax,[bxSecPerClust]
821                 bsr cx,ax
822                 mov [ClustShift],cl
823                 push cx
824                 add cl,9
825                 mov [ClustByteShift],cl
826                 pop cx
827                 dec ax
828                 mov [ClustMask],eax
829                 inc ax
830                 shl eax,9
831                 mov [ClustSize],eax
832
833 ;
834 ; FAT12, FAT16 or FAT28^H^H32?  This computation is fscking ridiculous.
835 ;
836 getfattype:
837                 mov eax,[EndSector]
838                 sub eax,[DataArea]
839                 shr eax,cl                      ; cl == ClustShift
840                 mov cl,nextcluster_fat12-(nextcluster+2)
841                 cmp eax,4085                    ; FAT12 limit
842                 jb .setsize
843                 mov cl,nextcluster_fat16-(nextcluster+2)
844                 cmp eax,65525                   ; FAT16 limit
845                 jb .setsize
846                 ;
847                 ; FAT32, root directory is a cluster chain
848                 ;
849                 mov cl,[ClustShift]
850                 mov eax,[bootsec+44]            ; Root directory cluster
851                 sub eax,2
852                 shl eax,cl
853                 add eax,[DataArea]
854                 mov [RootDir],eax
855                 mov cl,nextcluster_fat28-(nextcluster+2)
856 .setsize:
857                 mov byte [nextcluster+1],cl
858
859 ;
860 ; Common initialization code
861 ;
862 %include "cpuinit.inc"
863 %include "init.inc"
864
865 ;
866 ; Clear Files structures
867 ;
868                 mov di,Files
869                 mov cx,(MAX_OPEN*open_file_t_size)/4
870                 xor eax,eax
871                 rep stosd
872
873 ;
874 ; Initialize the metadata cache
875 ;
876                 call initcache
877
878 ;
879 ; Now, everything is "up and running"... patch kaboom for more
880 ; verbosity and using the full screen system
881 ;
882                 ; E9 = JMP NEAR
883                 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
884
885 ;
886 ; Now we're all set to start with our *real* business.  First load the
887 ; configuration file (if any) and parse it.
888 ;
889 ; In previous versions I avoided using 32-bit registers because of a
890 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
891 ; random.  I figure, though, that if there are any of those still left
892 ; they probably won't be trying to install Linux on them...
893 ;
894 ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
895 ; to take'm out.  In fact, we may want to put them back if we're going
896 ; to boot ELKS at some point.
897 ;
898
899 ;
900 ; Load configuration file
901 ;
902                 mov di,syslinux_cfg1
903                 call open
904                 jnz .config_open
905                 mov di,syslinux_cfg2
906                 call open
907                 jnz .config_open
908                 mov di,syslinux_cfg3
909                 call open
910                 jz no_config_file
911 .config_open:
912                 mov eax,[PrevDir]       ; Make the directory with syslinux.cfg ...
913                 mov [CurrentDir],eax    ; ... the current directory
914
915 ;
916 ; Now we have the config file open.  Parse the config file and
917 ; run the user interface.
918 ;
919 %include "ui.inc"
920
921 ;
922 ; Linux kernel loading code is common.
923 ;
924 %include "runkernel.inc"
925
926 ;
927 ; COMBOOT-loading code
928 ;
929 %include "comboot.inc"
930 %include "com32.inc"
931 %include "cmdline.inc"
932
933 ;
934 ; Boot sector loading code
935 ;
936 %include "bootsect.inc"
937
938 ;
939 ; Abort loading code
940 ;
941 %include "abort.inc"
942
943 ;
944 ; allocate_file: Allocate a file structure
945 ;
946 ;               If successful:
947 ;                 ZF set
948 ;                 BX = file pointer
949 ;               In unsuccessful:
950 ;                 ZF clear
951 ;
952 allocate_file:
953                 TRACER 'a'
954                 push cx
955                 mov bx,Files
956                 mov cx,MAX_OPEN
957 .check:         cmp dword [bx], byte 0
958                 je .found
959                 add bx,open_file_t_size         ; ZF = 0
960                 loop .check
961                 ; ZF = 0 if we fell out of the loop
962 .found:         pop cx
963                 ret
964
965 ;
966 ; search_dos_dir:
967 ;            Search a specific directory for a pre-mangled filename in
968 ;            MangledBuf, in the directory starting in sector EAX.
969 ;
970 ;            NOTE: This file considers finding a zero-length file an
971 ;            error.  This is so we don't have to deal with that special
972 ;            case elsewhere in the program (most loops have the test
973 ;            at the end).
974 ;
975 ;            Assumes DS == ES == CS.
976 ;
977 ;            If successful:
978 ;               ZF clear
979 ;               SI      = file pointer
980 ;               EAX     = file length (MAY BE ZERO!)
981 ;               DL      = file attributes
982 ;            If unsuccessful
983 ;               ZF set
984 ;
985
986 search_dos_dir:
987                 push bx
988                 call allocate_file
989                 jnz .alloc_failure
990
991                 push cx
992                 push gs
993                 push es
994                 push ds
995                 pop es                          ; ES = DS
996
997 .scansector:
998                 ; EAX <- directory sector to scan
999                 call getcachesector
1000                 ; GS:SI now points to this sector
1001
1002                 mov cx,SECTOR_SIZE/32           ; 32 == directory entry size
1003 .scanentry:
1004                 cmp byte [gs:si],0
1005                 jz .failure                     ; Hit directory high water mark
1006                 test byte [gs:si+11],8          ; Ignore volume labels and
1007                                                 ; VFAT long filename entries
1008                 jnz .nomatch
1009                 push cx
1010                 push si
1011                 push di
1012                 mov di,MangledBuf
1013                 mov cx,11
1014                 gs repe cmpsb
1015                 pop di
1016                 pop si
1017                 pop cx
1018                 jz .found
1019 .nomatch:
1020                 add si,32
1021                 loop .scanentry
1022
1023                 call nextsector
1024                 jnc .scansector                 ; CF is set if we're at end
1025
1026                 ; If we get here, we failed
1027 .failure:
1028                 pop es
1029                 pop gs
1030                 pop cx
1031 .alloc_failure:
1032                 pop bx
1033                 xor eax,eax                     ; ZF <- 1
1034                 ret
1035 .found:
1036                 mov eax,[gs:si+28]              ; File size
1037                 add eax,SECTOR_SIZE-1
1038                 shr eax,SECTOR_SHIFT
1039                 mov [bx+4],eax                  ; Sector count
1040
1041                 mov cl,[ClustShift]
1042                 mov dx,[gs:si+20]               ; High cluster word
1043                 shl edx,16
1044                 mov dx,[gs:si+26]               ; Low cluster word
1045                 sub edx,2
1046                 shl edx,cl
1047                 add edx,[DataArea]
1048                 mov [bx],edx                    ; Starting sector
1049
1050                 mov eax,[gs:si+28]              ; File length again
1051                 mov dl,[gs:si+11]               ; File attribute
1052                 mov si,bx                       ; File pointer...
1053                 and si,si                       ; ZF <- 0
1054
1055                 pop es
1056                 pop gs
1057                 pop cx
1058                 pop bx
1059                 ret
1060
1061 ;
1062 ; searchdir:
1063 ;
1064 ;       Open a file
1065 ;
1066 ;            On entry:
1067 ;               DS:DI   = filename
1068 ;            If successful:
1069 ;               ZF clear
1070 ;               SI              = file pointer
1071 ;               DX:AX or EAX    = file length in bytes
1072 ;            If unsuccessful
1073 ;               ZF set
1074 ;
1075 ; Assumes CS == DS == ES, and trashes BX and CX.
1076 ;
1077 searchdir:
1078                 mov eax,[CurrentDir]
1079                 cmp byte [di],'/'       ; Root directory?
1080                 jne .notroot
1081                 mov eax,[RootDir]
1082                 inc di
1083 .notroot:
1084
1085 .pathwalk:
1086                 push eax                ; <A> Current directory sector
1087                 mov si,di
1088 .findend:
1089                 lodsb
1090                 cmp al,' '
1091                 jbe .endpath
1092                 cmp al,'/'
1093                 jne .findend
1094 .endpath:
1095                 xchg si,di
1096                 pop eax                 ; <A> Current directory sector
1097
1098                 mov [PrevDir],eax       ; Remember last directory searched
1099
1100                 push di
1101                 call mangle_dos_name    ; MangledBuf <- component
1102                 call search_dos_dir
1103                 pop di
1104                 jz .notfound            ; Pathname component missing
1105
1106                 cmp byte [di-1],'/'     ; Do we expect a directory
1107                 je .isdir
1108
1109                 ; Otherwise, it should be a file
1110 .isfile:
1111                 test dl,18h             ; Subdirectory|Volume Label
1112                 jnz .badfile            ; If not a file, it's a bad thing
1113
1114                 ; SI and EAX are already set
1115                 mov edx,eax
1116                 shr edx,16              ; Old 16-bit remnant...
1117                 and eax,eax             ; EAX != 0
1118                 jz .badfile
1119                 ret                     ; Done!
1120
1121                 ; If we expected a directory, it better be one...
1122 .isdir:
1123                 test dl,10h             ; Subdirectory
1124                 jz .badfile
1125
1126                 xor eax,eax
1127                 xchg eax,[si+file_sector] ; Get sector number and free file structure
1128                 jmp .pathwalk           ; Walk the next bit of the path
1129
1130 .badfile:
1131                 xor eax,eax
1132                 mov [si],eax            ; Free file structure
1133
1134 .notfound:
1135                 xor eax,eax
1136                 xor dx,dx
1137                 ret
1138
1139                 section .bss
1140                 alignb 4
1141 CurrentDir      resd 1                  ; Current directory
1142 PrevDir         resd 1                  ; Last scanned directory
1143
1144                 section .text
1145
1146 ;
1147 ;
1148 ; kaboom2: once everything is loaded, replace the part of kaboom
1149 ;          starting with "kaboom.patch" with this part
1150
1151 kaboom2:
1152                 mov si,err_bootfailed
1153                 call cwritestr
1154                 call getchar
1155                 call vgaclearmode
1156                 int 19h                 ; And try once more to boot...
1157 .norge:         jmp short .norge        ; If int 19h returned; this is the end
1158
1159 ;
1160 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1161 ;              to by ES:DI; ends on encountering any whitespace.
1162 ;
1163 ;              This verifies that a filename is < FILENAME_MAX characters,
1164 ;              doesn't contain whitespace, zero-pads the output buffer,
1165 ;              and removes trailing dots and redundant slashes, plus changes
1166 ;              backslashes to forward slashes,
1167 ;              so "repe cmpsb" can do a compare, and the path-searching routine
1168 ;              gets a bit of an easier job.
1169 ;
1170 ;
1171 mangle_name:
1172                 push bx
1173                 xor ax,ax
1174                 mov cx,FILENAME_MAX-1
1175                 mov bx,di
1176
1177 .mn_loop:
1178                 lodsb
1179                 cmp al,' '                      ; If control or space, end
1180                 jna .mn_end
1181                 cmp al,'\'                      ; Backslash?
1182                 jne .mn_not_bs
1183                 mov al,'/'                      ; Change to forward slash
1184 .mn_not_bs:
1185                 cmp al,ah                       ; Repeated slash?
1186                 je .mn_skip
1187                 xor ah,ah
1188                 cmp al,'/'
1189                 jne .mn_ok
1190                 mov ah,al
1191 .mn_ok          stosb
1192 .mn_skip:       loop .mn_loop
1193 .mn_end:
1194                 cmp bx,di                       ; At the beginning of the buffer?
1195                 jbe .mn_zero
1196                 cmp byte [es:di-1],'.'          ; Terminal dot?
1197                 je .mn_kill
1198                 cmp byte [es:di-1],'/'          ; Terminal slash?
1199                 jne .mn_zero
1200 .mn_kill:       dec di                          ; If so, remove it
1201                 inc cx
1202                 jmp short .mn_end
1203 .mn_zero:
1204                 inc cx                          ; At least one null byte
1205                 xor ax,ax                       ; Zero-fill name
1206                 rep stosb
1207                 pop bx
1208                 ret                             ; Done
1209
1210 ;
1211 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1212 ;                filename to the conventional representation.  This is needed
1213 ;                for the BOOT_IMAGE= parameter for the kernel.
1214 ;                NOTE: A 13-byte buffer is mandatory, even if the string is
1215 ;                known to be shorter.
1216 ;
1217 ;                DS:SI -> input mangled file name
1218 ;                ES:DI -> output buffer
1219 ;
1220 ;                On return, DI points to the first byte after the output name,
1221 ;                which is set to a null byte.
1222 ;
1223 unmangle_name:  call strcpy
1224                 dec di                          ; Point to final null byte
1225                 ret
1226
1227 ;
1228 ; mangle_dos_name:
1229 ;               Mangle a DOS filename component pointed to by DS:SI
1230 ;               into [MangledBuf]; ends on encountering any whitespace or slash.
1231 ;               Assumes CS == DS == ES.
1232 ;
1233
1234 mangle_dos_name:
1235                 pusha
1236                 mov di,MangledBuf
1237
1238                 mov cx,11                       ; # of bytes to write
1239 .loop:
1240                 lodsb
1241                 cmp al,' '                      ; If control or space, end
1242                 jna .end
1243                 cmp al,'/'                      ; Slash, too
1244                 je .end
1245                 cmp al,'.'                      ; Period -> space-fill
1246                 je .is_period
1247                 cmp al,'a'
1248                 jb .not_lower
1249                 cmp al,'z'
1250                 ja .not_uslower
1251                 sub al,020h
1252                 jmp short .not_lower
1253 .is_period:     mov al,' '                      ; We need to space-fill
1254 .period_loop:   cmp cx,3                        ; If <= 3 characters left
1255                 jbe .loop                       ; Just ignore it
1256                 stosb                           ; Otherwise, write a period
1257                 loop .period_loop               ; Dec CX and (always) jump
1258 .not_uslower:   cmp al,ucase_low
1259                 jb .not_lower
1260                 cmp al,ucase_high
1261                 ja .not_lower
1262                 mov bx,ucase_tab-ucase_low
1263                 xlatb
1264 .not_lower:     stosb
1265                 loop .loop                      ; Don't continue if too long
1266 .end:
1267                 mov al,' '                      ; Space-fill name
1268                 rep stosb                       ; Doesn't do anything if CX=0
1269                 popa
1270                 ret                             ; Done
1271
1272                 section .bss
1273 MangledBuf      resb 11
1274
1275                 section .text
1276 ;
1277 ; Case tables for extended characters; this is technically code page 865,
1278 ; but code page 437 users will probably not miss not being able to use the
1279 ; cent sign in kernel images too much :-)
1280 ;
1281 ; The table only covers the range 129 to 164; the rest we can deal with.
1282 ;
1283                 section .data
1284
1285 ucase_low       equ 129
1286 ucase_high      equ 164
1287 ucase_tab       db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1288                 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1289                 db 157, 156, 157, 158, 159, 'AIOU', 165
1290
1291                 section .text
1292 ;
1293 ; getfssec_edx: Get multiple sectors from a file
1294 ;
1295 ;       This routine makes sure the subtransfers do not cross a 64K boundary,
1296 ;       and will correct the situation if it does, UNLESS *sectors* cross
1297 ;       64K boundaries.
1298 ;
1299 ;       ES:BX   -> Buffer
1300 ;       EDX     -> Current sector number
1301 ;       CX      -> Sector count (0FFFFh = until end of file)
1302 ;                  Must not exceed the ES segment
1303 ;       Returns EDX=0, CF=1 on EOF (not necessarily error)
1304 ;       All arguments are advanced to reflect data read.
1305 ;
1306 getfssec_edx:
1307                 push ebp
1308                 push eax
1309 .getfragment:
1310                 xor ebp,ebp                     ; Fragment sector count
1311                 push edx                        ; Starting sector pointer
1312 .getseccnt:
1313                 inc bp
1314                 dec cx
1315                 jz .do_read
1316                 xor eax,eax
1317                 mov ax,es
1318                 shl ax,4
1319                 add ax,bx                       ; Now AX = how far into 64K block we are
1320                 not ax                          ; Bytes left in 64K block
1321                 inc eax
1322                 shr eax,SECTOR_SHIFT            ; Sectors left in 64K block
1323                 cmp bp,ax
1324                 jnb .do_read                    ; Unless there is at least 1 more sector room...
1325                 mov eax,edx                     ; Current sector
1326                 inc edx                         ; Predict it's the linearly next sector
1327                 call nextsector
1328                 jc .do_read
1329                 cmp edx,eax                     ; Did it match?
1330                 jz .getseccnt
1331 .do_read:
1332                 pop eax                         ; Starting sector pointer
1333                 call getlinsecsr
1334                 lea eax,[eax+ebp-1]             ; This is the last sector actually read
1335                 shl bp,9
1336                 add bx,bp                       ; Adjust buffer pointer
1337                 call nextsector
1338                 jc .eof
1339                 mov edx,eax
1340                 and cx,cx
1341                 jnz .getfragment
1342 .done:
1343                 pop eax
1344                 pop ebp
1345                 ret
1346 .eof:
1347                 xor edx,edx
1348                 stc
1349                 jmp .done
1350
1351 ;
1352 ; getfssec: Get multiple sectors from a file
1353 ;
1354 ;       Same as above, except SI is a pointer to a open_file_t
1355 ;
1356 ;       ES:BX   -> Buffer
1357 ;       DS:SI   -> Pointer to open_file_t
1358 ;       CX      -> Sector count (0FFFFh = until end of file)
1359 ;                  Must not exceed the ES segment
1360 ;       Returns CF=1 on EOF (not necessarily error)
1361 ;       All arguments are advanced to reflect data read.
1362 ;
1363 getfssec:
1364                 push edx
1365                 movzx edx,cx
1366                 cmp edx,[si+4]
1367                 jbe .sizeok
1368                 mov edx,[si+4]
1369                 mov cx,dx
1370 .sizeok:
1371                 sub [si+4],edx
1372                 mov edx,[si]
1373                 call getfssec_edx
1374                 mov [si],edx
1375                 pop edx
1376                 ret
1377
1378 ;
1379 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1380 ;              pointed at in the FAT tables.  CF=0 on return if end of file.
1381 ;
1382 nextcluster:
1383                 jmp strict short nextcluster_fat28      ; This gets patched
1384
1385 nextcluster_fat12:
1386                 push eax
1387                 push edx
1388                 push bx
1389                 push cx
1390                 push si
1391                 mov edx,edi
1392                 shr edi,1
1393                 pushf                   ; Save the shifted-out LSB (=CF)
1394                 add edx,edi
1395                 mov eax,edx
1396                 shr eax,9
1397                 call getfatsector
1398                 mov bx,dx
1399                 and bx,1FFh
1400                 mov cl,[gs:si+bx]
1401                 inc edx
1402                 mov eax,edx
1403                 shr eax,9
1404                 call getfatsector
1405                 mov bx,dx
1406                 and bx,1FFh
1407                 mov ch,[gs:si+bx]
1408                 popf
1409                 jnc .even
1410                 shr cx,4
1411 .even:          and cx,0FFFh
1412                 movzx edi,cx
1413                 cmp di,0FF0h
1414                 pop si
1415                 pop cx
1416                 pop bx
1417                 pop edx
1418                 pop eax
1419                 ret
1420
1421 ;
1422 ; FAT16 decoding routine.
1423 ;
1424 nextcluster_fat16:
1425                 push eax
1426                 push si
1427                 push bx
1428                 mov eax,edi
1429                 shr eax,SECTOR_SHIFT-1
1430                 call getfatsector
1431                 mov bx,di
1432                 add bx,bx
1433                 and bx,1FEh
1434                 movzx edi,word [gs:si+bx]
1435                 cmp di,0FFF0h
1436                 pop bx
1437                 pop si
1438                 pop eax
1439                 ret
1440 ;
1441 ; FAT28 ("FAT32") decoding routine.
1442 ;
1443 nextcluster_fat28:
1444                 push eax
1445                 push si
1446                 push bx
1447                 mov eax,edi
1448                 shr eax,SECTOR_SHIFT-2
1449                 call getfatsector
1450                 mov bx,di
1451                 add bx,bx
1452                 add bx,bx
1453                 and bx,1FCh
1454                 mov edi,dword [gs:si+bx]
1455                 and edi,0FFFFFFFh       ; 28 bits only
1456                 cmp edi,0FFFFFF0h
1457                 pop bx
1458                 pop si
1459                 pop eax
1460                 ret
1461
1462 ;
1463 ; nextsector:   Given a sector in EAX on input, return the next sector
1464 ;               of the same filesystem object, which may be the root
1465 ;               directory or a cluster chain.  Returns  EOF.
1466 ;
1467 ;               Assumes CS == DS.
1468 ;
1469 nextsector:
1470                 push edi
1471                 push edx
1472                 mov edx,[DataArea]
1473                 mov edi,eax
1474                 sub edi,edx
1475                 jae .isdata
1476
1477                 ; Root directory
1478                 inc eax
1479                 cmp eax,edx
1480                 cmc
1481                 jmp .done
1482
1483 .isdata:
1484                 not edi
1485                 test edi,[ClustMask]
1486                 jz .endcluster
1487
1488                 ; It's not the final sector in a cluster
1489                 inc eax
1490                 jmp .done
1491
1492 .endcluster:
1493                 push gs                 ; nextcluster trashes gs
1494                 push cx
1495                 not edi
1496                 mov cl,[ClustShift]
1497                 shr edi,cl
1498                 add edi,2
1499
1500                 ; Now EDI contains the cluster number
1501                 call nextcluster
1502                 cmc
1503                 jc .exit                ; There isn't anything else...
1504
1505                 ; New cluster number now in EDI
1506                 sub edi,2
1507                 shl edi,cl              ; CF <- 0, unless something is very wrong
1508                 lea eax,[edi+edx]
1509 .exit:
1510                 pop cx
1511                 pop gs
1512 .done:
1513                 pop edx
1514                 pop edi
1515                 ret
1516
1517 ;
1518 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1519 ;               and return a pointer in GS:SI, loading it if needed.
1520 ;
1521 ;               Assumes CS == DS.
1522 ;
1523 getfatsector:
1524                 add eax,[FAT]           ; FAT starting address
1525                 jmp getcachesector
1526
1527 ; -----------------------------------------------------------------------------
1528 ;  Common modules
1529 ; -----------------------------------------------------------------------------
1530
1531 %include "getc.inc"             ; getc et al
1532 %include "conio.inc"            ; Console I/O
1533 %include "plaincon.inc"         ; writechr
1534 %include "writestr.inc"         ; String output
1535 %include "parseconfig.inc"      ; High-level config file handling
1536 %include "parsecmd.inc"         ; Low-level config file handling
1537 %include "bcopy32.inc"          ; 32-bit bcopy
1538 %include "loadhigh.inc"         ; Load a file into high memory
1539 %include "font.inc"             ; VGA font stuff
1540 %include "graphics.inc"         ; VGA graphics
1541 %include "highmem.inc"          ; High memory sizing
1542 %include "strcpy.inc"           ; strcpy()
1543 %include "cache.inc"            ; Metadata disk cache
1544
1545 ; -----------------------------------------------------------------------------
1546 ;  Begin data section
1547 ; -----------------------------------------------------------------------------
1548
1549                 section .data
1550 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1551                 db CR, LF, 0
1552 boot_prompt     db 'boot: ', 0
1553 wipe_char       db BS, ' ', BS, 0
1554 err_notfound    db 'Could not find kernel image: ',0
1555 err_notkernel   db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1556 err_noram       db 'It appears your computer has less than '
1557                 asciidec dosram_k
1558                 db 'K of low ("DOS")'
1559                 db CR, LF
1560                 db 'RAM.  Linux needs at least this amount to boot.  If you get'
1561                 db CR, LF
1562                 db 'this message in error, hold down the Ctrl key while'
1563                 db CR, LF
1564                 db 'booting, and I will take your word for it.', CR, LF, 0
1565 err_badcfg      db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1566 err_noparm      db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1567 err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
1568 err_nohighmem   db 'Not enough memory to load specified kernel.', CR, LF, 0
1569 err_highload    db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1570 err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
1571                 db CR, LF, 0
1572 err_notdos      db ': attempted DOS system call', CR, LF, 0
1573 err_comlarge    db 'COMBOOT image too large.', CR, LF, 0
1574 err_a20         db CR, LF, 'A20 gate not responding!', CR, LF, 0
1575 err_bootfailed  db CR, LF, 'Boot failed: please change disks and press '
1576                 db 'a key to continue.', CR, LF, 0
1577 ready_msg       db 'Ready.', CR, LF, 0
1578 crlfloading_msg db CR, LF
1579 loading_msg     db 'Loading ', 0
1580 dotdot_msg      db '.'
1581 dot_msg         db '.', 0
1582 aborted_msg     db ' aborted.'                  ; Fall through to crlf_msg!
1583 crlf_msg        db CR, LF
1584 null_msg        db 0
1585 crff_msg        db CR, FF, 0
1586 syslinux_cfg1   db '/boot'                      ; /boot/syslinux/syslinux.cfg
1587 syslinux_cfg2   db '/syslinux'                  ; /syslinux/syslinux.cfg
1588 syslinux_cfg3   db '/'                          ; /syslinux.cfg
1589 ConfigName      db 'syslinux.cfg', 0            ; syslinux.cfg
1590
1591 ;
1592 ; Command line options we'd like to take a look at
1593 ;
1594 ; mem= and vga= are handled as normal 32-bit integer values
1595 initrd_cmd      db 'initrd='
1596 initrd_cmd_len  equ 7
1597
1598 ;
1599 ; Config file keyword table
1600 ;
1601 %include "keywords.inc"
1602
1603 ;
1604 ; Extensions to search for (in *forward* order).
1605 ;
1606 exten_table:    db 'CBT',0              ; COMBOOT (specific)
1607                 db 'BSS',0              ; Boot Sector (add superblock)
1608                 db 'BS ',0              ; Boot Sector
1609                 db 'COM',0              ; COMBOOT (same as DOS)
1610                 db 'C32',0              ; COM32
1611 exten_table_end:
1612                 dd 0, 0                 ; Need 8 null bytes here
1613
1614 ;
1615 ; Misc initialized (data) variables
1616 ;
1617 %ifdef debug                            ; This code for debugging only
1618 debug_magic     dw 0D00Dh               ; Debug code sentinel
1619 %endif
1620
1621                 alignb 4, db 0
1622 BufSafe         dw trackbufsize/SECTOR_SIZE     ; Clusters we can load into trackbuf
1623 BufSafeSec      dw trackbufsize/SECTOR_SIZE     ; = how many sectors?
1624 BufSafeBytes    dw trackbufsize         ; = how many bytes?
1625 EndOfGetCBuf    dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1626 %ifndef DEPEND
1627 %if ( trackbufsize % SECTOR_SIZE ) != 0
1628 %error trackbufsize must be a multiple of SECTOR_SIZE
1629 %endif
1630 %endif