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