VESA library: add support for reading lss16 images
[profile/ivi/syslinux.git] / isolinux.asm
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
3 ;
4 ;  isolinux.asm
5 ;
6 ;  A program to boot Linux kernels off a CD-ROM using the El Torito
7 ;  boot standard in "no emulation" mode, making the entire filesystem
8 ;  available.  It is based on the SYSLINUX boot loader for MS-DOS
9 ;  floppies.
10 ;
11 ;   Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
12 ;
13 ;  This program is free software; you can redistribute it and/or modify
14 ;  it under the terms of the GNU General Public License as published by
15 ;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16 ;  Boston MA 02111-1307, USA; either version 2 of the License, or
17 ;  (at your option) any later version; incorporated herein by reference.
18 ;
19 ; ****************************************************************************
20
21 %define IS_ISOLINUX 1
22 %include "head.inc"
23
24 ;
25 ; Some semi-configurable constants... change on your own risk.
26 ;
27 my_id           equ isolinux_id
28 FILENAME_MAX_LG2 equ 8                  ; log2(Max filename size Including final null)
29 FILENAME_MAX    equ (1 << FILENAME_MAX_LG2)
30 NULLFILE        equ 0                   ; Zero byte == null file name
31 NULLOFFSET      equ 0                   ; Position in which to look
32 retry_count     equ 6                   ; How patient are we with the BIOS?
33 %assign HIGHMEM_SLOP 128*1024           ; Avoid this much memory near the top
34 MAX_OPEN_LG2    equ 6                   ; log2(Max number of open files)
35 MAX_OPEN        equ (1 << MAX_OPEN_LG2)
36 SECTOR_SHIFT    equ 11                  ; 2048 bytes/sector (El Torito requirement)
37 SECTOR_SIZE     equ (1 << SECTOR_SHIFT)
38
39 ;
40 ; This is what we need to do when idle
41 ;
42 %macro  RESET_IDLE 0
43         ; Nothing
44 %endmacro
45 %macro  DO_IDLE 0
46         ; Nothing
47 %endmacro
48
49 ;
50 ; The following structure is used for "virtual kernels"; i.e. LILO-style
51 ; option labels.  The options we permit here are `kernel' and `append
52 ; Since there is no room in the bottom 64K for all of these, we
53 ; stick them in high memory and copy them down before we need them.
54 ;
55                 struc vkernel
56 vk_vname:       resb FILENAME_MAX       ; Virtual name **MUST BE FIRST!**
57 vk_rname:       resb FILENAME_MAX       ; Real name
58 vk_appendlen:   resw 1
59 vk_type:        resb 1                  ; Type of file
60                 alignb 4
61 vk_append:      resb max_cmd_len+1      ; Command line
62                 alignb 4
63 vk_end:         equ $                   ; Should be <= vk_size
64                 endstruc
65
66 ;
67 ; Segment assignments in the bottom 640K
68 ; 0000h - main code/data segment (and BIOS segment)
69 ;
70 real_mode_seg   equ 2000h
71 xfer_buf_seg    equ 1000h               ; Bounce buffer for I/O to high mem
72 comboot_seg     equ real_mode_seg       ; COMBOOT image loading zone
73
74 ;
75 ; File structure.  This holds the information for each currently open file.
76 ;
77                 struc open_file_t
78 file_sector     resd 1                  ; Sector pointer (0 = structure free)
79 file_bytesleft  resd 1                  ; Number of bytes left
80 file_left       resd 1                  ; Number of sectors left
81                 resd 1                  ; Unused
82                 endstruc
83
84 %ifndef DEPEND
85 %if (open_file_t_size & (open_file_t_size-1))
86 %error "open_file_t is not a power of 2"
87 %endif
88 %endif
89
90                 struc dir_t
91 dir_lba         resd 1                  ; Directory start (LBA)
92 dir_len         resd 1                  ; Length in bytes
93 dir_clust       resd 1                  ; Length in clusters
94                 endstruc
95
96 ; ---------------------------------------------------------------------------
97 ;   BEGIN CODE
98 ; ---------------------------------------------------------------------------
99
100 ;
101 ; Memory below this point is reserved for the BIOS and the MBR
102 ;
103                 section .earlybss
104 trackbufsize    equ 8192
105 trackbuf        resb trackbufsize       ; Track buffer goes here
106 ;               ends at 2800h
107
108                 ; Some of these are touched before the whole image
109                 ; is loaded.  DO NOT move this to .uibss.
110                 section .bss2
111                 alignb 4
112 ISOFileName     resb 64                 ; ISO filename canonicalization buffer
113 ISOFileNameEnd  equ $
114 CurDir          resb dir_t_size         ; Current directory
115 RootDir         resb dir_t_size         ; Root directory
116 FirstSecSum     resd 1                  ; Checksum of bytes 64-2048
117 ImageDwords     resd 1                  ; isolinux.bin size, dwords
118 InitStack       resd 1                  ; Initial stack pointer (SS:SP)
119 DiskSys         resw 1                  ; Last INT 13h call
120 ImageSectors    resw 1                  ; isolinux.bin size, sectors
121 DiskError       resb 1                  ; Error code for disk I/O
122 DriveNumber             resb 1                  ; CD-ROM BIOS drive number
123 ISOFlags        resb 1                  ; Flags for ISO directory search
124 RetryCount      resb 1                  ; Used for disk access retries
125
126 _spec_start     equ $
127
128 ;
129 ; El Torito spec packet
130 ;
131
132                 alignb 8
133 spec_packet:    resb 1                          ; Size of packet
134 sp_media:       resb 1                          ; Media type
135 sp_drive:       resb 1                          ; Drive number
136 sp_controller:  resb 1                          ; Controller index
137 sp_lba:         resd 1                          ; LBA for emulated disk image
138 sp_devspec:     resw 1                          ; IDE/SCSI information
139 sp_buffer:      resw 1                          ; User-provided buffer
140 sp_loadseg:     resw 1                          ; Load segment
141 sp_sectors:     resw 1                          ; Sector count
142 sp_chs:         resb 3                          ; Simulated CHS geometry
143 sp_dummy:       resb 1                          ; Scratch, safe to overwrite
144
145 ;
146 ; EBIOS drive parameter packet
147 ;
148                 alignb 8
149 drive_params:   resw 1                          ; Buffer size
150 dp_flags:       resw 1                          ; Information flags
151 dp_cyl:         resd 1                          ; Physical cylinders
152 dp_head:        resd 1                          ; Physical heads
153 dp_sec:         resd 1                          ; Physical sectors/track
154 dp_totalsec:    resd 2                          ; Total sectors
155 dp_secsize:     resw 1                          ; Bytes per sector
156 dp_dpte:        resd 1                          ; Device Parameter Table
157 dp_dpi_key:     resw 1                          ; 0BEDDh if rest valid
158 dp_dpi_len:     resb 1                          ; DPI len
159                 resb 1
160                 resw 1
161 dp_bus:         resb 4                          ; Host bus type
162 dp_interface:   resb 8                          ; Interface type
163 db_i_path:      resd 2                          ; Interface path
164 db_d_path:      resd 2                          ; Device path
165                 resb 1
166 db_dpi_csum:    resb 1                          ; Checksum for DPI info
167
168 ;
169 ; EBIOS disk address packet
170 ;
171                 alignb 8
172 dapa:           resw 1                          ; Packet size
173 .count:         resw 1                          ; Block count
174 .off:           resw 1                          ; Offset of buffer
175 .seg:           resw 1                          ; Segment of buffer
176 .lba:           resd 2                          ; LBA (LSW, MSW)
177
178 ;
179 ; Spec packet for disk image emulation
180 ;
181                 alignb 8
182 dspec_packet:   resb 1                          ; Size of packet
183 dsp_media:      resb 1                          ; Media type
184 dsp_drive:      resb 1                          ; Drive number
185 dsp_controller: resb 1                          ; Controller index
186 dsp_lba:        resd 1                          ; LBA for emulated disk image
187 dsp_devspec:    resw 1                          ; IDE/SCSI information
188 dsp_buffer:     resw 1                          ; User-provided buffer
189 dsp_loadseg:    resw 1                          ; Load segment
190 dsp_sectors:    resw 1                          ; Sector count
191 dsp_chs:        resb 3                          ; Simulated CHS geometry
192 dsp_dummy:      resb 1                          ; Scratch, safe to overwrite
193
194                 alignb 4
195 _spec_end       equ $
196 _spec_len       equ _spec_end - _spec_start
197
198                 alignb open_file_t_size
199 Files           resb MAX_OPEN*open_file_t_size
200
201                 section .text
202 ;;
203 ;; Primary entry point.  Because BIOSes are buggy, we only load the first
204 ;; CD-ROM sector (2K) of the file, so the number one priority is actually
205 ;; loading the rest.
206 ;;
207 StackBuf        equ $-44                ; 44 bytes needed for
208                                         ; the bootsector chainloading
209                                         ; code!
210 OrigESDI        equ StackBuf-4          ; The high dword on the stack
211
212 bootsec         equ $
213
214 _start:         ; Far jump makes sure we canonicalize the address
215                 cli
216                 jmp 0:_start1
217                 times 8-($-$$) nop              ; Pad to file offset 8
218
219                 ; This table hopefully gets filled in by mkisofs using the
220                 ; -boot-info-table option.  If not, the values in this
221                 ; table are default values that we can use to get us what
222                 ; we need, at least under a certain set of assumptions.
223 bi_pvd:         dd 16                           ; LBA of primary volume descriptor
224 bi_file:        dd 0                            ; LBA of boot file
225 bi_length:      dd 0xdeadbeef                   ; Length of boot file
226 bi_csum:        dd 0xdeadbeef                   ; Checksum of boot file
227 bi_reserved:    times 10 dd 0xdeadbeef          ; Reserved
228
229 _start1:        mov [cs:InitStack],sp           ; Save initial stack pointer
230                 mov [cs:InitStack+2],ss
231                 xor ax,ax
232                 mov ss,ax
233                 mov sp,StackBuf                 ; Set up stack
234                 push es                 ; Save initial ES:DI -> $PnP pointer
235                 push di
236                 mov ds,ax
237                 mov es,ax
238                 mov fs,ax
239                 mov gs,ax
240                 sti
241
242                 cld
243                 ; Show signs of life
244                 mov si,syslinux_banner
245                 call writestr
246 %ifdef DEBUG_MESSAGES
247                 mov si,copyright_str
248                 call writestr
249 %endif
250
251                 ;
252                 ; Before modifying any memory, get the checksum of bytes
253                 ; 64-2048
254                 ;
255 initial_csum:   xor edi,edi
256                 mov si,_start1
257                 mov cx,(SECTOR_SIZE-64) >> 2
258 .loop:          lodsd
259                 add edi,eax
260                 loop .loop
261                 mov [FirstSecSum],edi
262
263                 mov [DriveNumber],dl
264 %ifdef DEBUG_MESSAGES
265                 mov si,startup_msg
266                 call writemsg
267                 mov al,dl
268                 call writehex2
269                 call crlf
270 %endif
271                 ;
272                 ; Initialize spec packet buffers
273                 ;
274                 mov di,_spec_start
275                 mov cx,_spec_len >> 2
276                 xor eax,eax
277                 rep stosd
278
279                 ; Initialize length field of the various packets
280                 mov byte [spec_packet],13h
281                 mov byte [drive_params],30
282                 mov byte [dapa],16
283                 mov byte [dspec_packet],13h
284
285                 ; Other nonzero fields
286                 inc word [dsp_sectors]
287
288                 ; Now figure out what we're actually doing
289                 ; Note: use passed-in DL value rather than 7Fh because
290                 ; at least some BIOSes will get the wrong value otherwise
291                 mov ax,4B01h                    ; Get disk emulation status
292                 mov dl,[DriveNumber]
293                 mov si,spec_packet
294                 call int13
295                 jc award_hack                   ; changed for BrokenAwardHack
296                 mov dl,[DriveNumber]
297                 cmp [sp_drive],dl               ; Should contain the drive number
298                 jne spec_query_failed
299
300 %ifdef DEBUG_MESSAGES
301                 mov si,spec_ok_msg
302                 call writemsg
303                 mov al,byte [sp_drive]
304                 call writehex2
305                 call crlf
306 %endif
307
308 found_drive:
309                 ; Alright, we have found the drive.  Now, try to find the
310                 ; boot file itself.  If we have a boot info table, life is
311                 ; good; if not, we have to make some assumptions, and try
312                 ; to figure things out ourselves.  In particular, the
313                 ; assumptions we have to make are:
314                 ; - single session only
315                 ; - only one boot entry (no menu or other alternatives)
316
317                 cmp dword [bi_file],0           ; Address of code to load
318                 jne found_file                  ; Boot info table present :)
319
320 %ifdef DEBUG_MESSAGES
321                 mov si,noinfotable_msg
322                 call writemsg
323 %endif
324
325                 ; No such luck.  See if the spec packet contained one.
326                 mov eax,[sp_lba]
327                 and eax,eax
328                 jz set_file                     ; Good enough
329
330 %ifdef DEBUG_MESSAGES
331                 mov si,noinfoinspec_msg
332                 call writemsg
333 %endif
334
335                 ; No such luck.  Get the Boot Record Volume, assuming single
336                 ; session disk, and that we're the first entry in the chain
337                 mov eax,17                      ; Assumed address of BRV
338                 mov bx,trackbuf
339                 call getonesec
340
341                 mov eax,[trackbuf+47h]          ; Get boot catalog address
342                 mov bx,trackbuf
343                 call getonesec                  ; Get boot catalog
344
345                 mov eax,[trackbuf+28h]          ; First boot entry
346                 ; And hope and pray this is us...
347
348                 ; Some BIOSes apparently have limitations on the size
349                 ; that may be loaded (despite the El Torito spec being very
350                 ; clear on the fact that it must all be loaded.)  Therefore,
351                 ; we load it ourselves, and *bleep* the BIOS.
352
353 set_file:
354                 mov [bi_file],eax
355
356 found_file:
357                 ; Set up boot file sizes
358                 mov eax,[bi_length]
359                 sub eax,SECTOR_SIZE-3
360                 shr eax,2                       ; bytes->dwords
361                 mov [ImageDwords],eax           ; boot file dwords
362                 add eax,(2047 >> 2)
363                 shr eax,9                       ; dwords->sectors
364                 mov [ImageSectors],ax           ; boot file sectors
365
366                 mov eax,[bi_file]               ; Address of code to load
367                 inc eax                         ; Don't reload bootstrap code
368 %ifdef DEBUG_MESSAGES
369                 mov si,offset_msg
370                 call writemsg
371                 call writehex8
372                 call crlf
373 %endif
374
375                 ; Just in case some BIOSes have problems with
376                 ; segment wraparound, use the normalized address
377                 mov bx,((7C00h+2048) >> 4)
378                 mov es,bx
379                 xor bx,bx
380                 mov bp,[ImageSectors]
381 %ifdef DEBUG_MESSAGES
382                 push ax
383                 mov si,size_msg
384                 call writemsg
385                 mov ax,bp
386                 call writehex4
387                 call crlf
388                 pop ax
389 %endif
390                 call getlinsec
391
392                 push ds
393                 pop es
394
395 %ifdef DEBUG_MESSAGES
396                 mov si,loaded_msg
397                 call writemsg
398 %endif
399
400                 ; Verify the checksum on the loaded image.
401 verify_image:
402                 mov si,7C00h+2048
403                 mov bx,es
404                 mov ecx,[ImageDwords]
405                 mov edi,[FirstSecSum]           ; First sector checksum
406 .loop           es lodsd
407                 add edi,eax
408                 dec ecx
409                 jz .done
410                 and si,si
411                 jnz .loop
412                 ; SI wrapped around, advance ES
413                 add bx,1000h
414                 mov es,bx
415                 jmp short .loop
416 .done:          mov ax,ds
417                 mov es,ax
418                 cmp [bi_csum],edi
419                 je integrity_ok
420
421                 mov si,checkerr_msg
422                 call writemsg
423                 jmp kaboom
424
425 integrity_ok:
426 %ifdef DEBUG_MESSAGES
427                 mov si,allread_msg
428                 call writemsg
429 %endif
430                 jmp all_read                    ; Jump to main code
431
432 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
433 ;; Start of BrokenAwardHack --- 10-nov-2002           Knut_Petersen@t-online.de
434 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
435 ;;
436 ;; There is a problem with certain versions of the AWARD BIOS ...
437 ;; the boot sector will be loaded and executed correctly, but, because the
438 ;; int 13 vector points to the wrong code in the BIOS, every attempt to
439 ;; load the spec packet will fail. We scan for the equivalent of
440 ;;
441 ;;      mov     ax,0201h
442 ;;      mov     bx,7c00h
443 ;;      mov     cx,0006h
444 ;;      mov     dx,0180h
445 ;;      pushf
446 ;;      call    <direct far>
447 ;;
448 ;; and use <direct far> as the new vector for int 13. The code above is
449 ;; used to load the boot code into ram, and there should be no reason
450 ;; for anybody to change it now or in the future. There are no opcodes
451 ;; that use encodings relativ to IP, so scanning is easy. If we find the
452 ;; code above in the BIOS code we can be pretty sure to run on a machine
453 ;; with an broken AWARD BIOS ...
454 ;;
455 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
456                                                                              ;;
457 %ifdef DEBUG_MESSAGES                                                        ;;
458                                                                              ;;
459 award_notice    db      "Trying BrokenAwardHack first ...",CR,LF,0           ;;
460 award_not_orig  db      "BAH: Original Int 13 vector   : ",0                 ;;
461 award_not_new   db      "BAH: Int 13 vector changed to : ",0                 ;;
462 award_not_succ  db      "BAH: SUCCESS",CR,LF,0                               ;;
463 award_not_fail  db      "BAH: FAILURE"                                       ;;
464 award_not_crlf  db      CR,LF,0                                              ;;
465                                                                              ;;
466 %endif                                                                       ;;
467                                                                              ;;
468 award_oldint13  dd      0                                                    ;;
469 award_string    db      0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah    ;;
470                                                                              ;;
471                                                 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
472 award_hack:     mov     si,spec_err_msg         ; Moved to this place from
473                 call    writemsg                ; spec_query_faild
474                                                 ;
475 %ifdef DEBUG_MESSAGES                           ;
476                                                 ;
477                 mov     si,award_notice         ; display our plan
478                 call    writemsg                ;
479                 mov     si,award_not_orig       ; display original int 13
480                 call    writemsg                ; vector
481 %endif                                          ;
482                 mov     eax,[13h*4]             ;
483                 mov     [award_oldint13],eax    ;
484                                                 ;
485 %ifdef DEBUG_MESSAGES                           ;
486                                                 ;
487                 call    writehex8               ;
488                 mov     si,award_not_crlf       ;
489                 call    writestr                ;
490 %endif                                          ;
491                 push    es                      ; save ES
492                 mov     ax,0f000h               ; ES = BIOS Seg
493                 mov     es,ax                   ;
494                 cld                             ;
495                 xor     di,di                   ; start at ES:DI = f000:0
496 award_loop:     push    di                      ; save DI
497                 mov     si,award_string         ; scan for award_string
498                 mov     cx,7                    ; length of award_string = 7dw
499                 repz    cmpsw                   ; compare
500                 pop     di                      ; restore DI
501                 jcxz    award_found             ; jmp if found
502                 inc     di                      ; not found, inc di
503                 jno     award_loop              ;
504                                                 ;
505 award_failed:   pop     es                      ; No, not this way :-((
506 award_fail2:                                    ;
507                                                 ;
508 %ifdef DEBUG_MESSAGES                           ;
509                                                 ;
510                 mov     si,award_not_fail       ; display failure ...
511                 call    writemsg                ;
512 %endif                                          ;
513                 mov     eax,[award_oldint13]    ; restore the original int
514                 or      eax,eax                 ; 13 vector if there is one
515                 jz      spec_query_failed       ; and try other workarounds
516                 mov     [13h*4],eax             ;
517                 jmp     spec_query_failed       ;
518                                                 ;
519 award_found:    mov     eax,[es:di+0eh]         ; load possible int 13 addr
520                 pop     es                      ; restore ES
521                                                 ;
522                 cmp     eax,[award_oldint13]    ; give up if this is the
523                 jz      award_failed            ; active int 13 vector,
524                 mov     [13h*4],eax             ; otherwise change 0:13h*4
525                                                 ;
526                                                 ;
527 %ifdef DEBUG_MESSAGES                           ;
528                                                 ;
529                 push    eax                     ; display message and
530                 mov     si,award_not_new        ; new vector address
531                 call    writemsg                ;
532                 pop     eax                     ;
533                 call    writehex8               ;
534                 mov     si,award_not_crlf       ;
535                 call    writestr                ;
536 %endif                                          ;
537                 mov     ax,4B01h                ; try to read the spec packet
538                 mov     dl,[DriveNumber]        ; now ... it should not fail
539                 mov     si,spec_packet          ; any longer
540                 int     13h                     ;
541                 jc      award_fail2             ;
542                                                 ;
543 %ifdef DEBUG_MESSAGES                           ;
544                                                 ;
545                 mov     si,award_not_succ       ; display our SUCCESS
546                 call    writemsg                ;
547 %endif                                          ;
548                 jmp     found_drive             ; and leave error recovery code
549                                                 ;
550 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
551 ;; End of BrokenAwardHack ----            10-nov-2002 Knut_Petersen@t-online.de
552 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
553
554
555                 ; INT 13h, AX=4B01h, DL=<passed in value> failed.
556                 ; Try to scan the entire 80h-FFh from the end.
557
558 spec_query_failed:
559
560                 ; some code moved to BrokenAwardHack
561
562                 mov dl,0FFh
563 .test_loop:     pusha
564                 mov ax,4B01h
565                 mov si,spec_packet
566                 mov byte [si],13h               ; Size of buffer
567                 call int13
568                 popa
569                 jc .still_broken
570
571                 mov si,maybe_msg
572                 call writemsg
573                 mov al,dl
574                 call writehex2
575                 call crlf
576
577                 cmp byte [sp_drive],dl
578                 jne .maybe_broken
579
580                 ; Okay, good enough...
581                 mov si,alright_msg
582                 call writemsg
583 .found_drive0:  mov [DriveNumber],dl
584 .found_drive:   jmp found_drive
585
586                 ; Award BIOS 4.51 apparently passes garbage in sp_drive,
587                 ; but if this was the drive number originally passed in
588                 ; DL then consider it "good enough"
589 .maybe_broken:
590                 mov al,[DriveNumber]
591                 cmp al,dl
592                 je .found_drive
593
594                 ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
595                 ; passes garbage in sp_drive, and the drive number originally
596                 ; passed in DL does not have 80h bit set.
597                 or al,80h
598                 cmp al,dl
599                 je .found_drive0
600
601 .still_broken:  dec dx
602                 cmp dl, 80h
603                 jnb .test_loop
604
605                 ; No spec packet anywhere.  Some particularly pathetic
606                 ; BIOSes apparently don't even implement function
607                 ; 4B01h, so we can't query a spec packet no matter
608                 ; what.  If we got a drive number in DL, then try to
609                 ; use it, and if it works, then well...
610                 mov dl,[DriveNumber]
611                 cmp dl,81h                      ; Should be 81-FF at least
612                 jb fatal_error                  ; If not, it's hopeless
613
614                 ; Write a warning to indicate we're on *very* thin ice now
615                 mov si,nospec_msg
616                 call writemsg
617                 mov al,dl
618                 call writehex2
619                 call crlf
620                 mov si,trysbm_msg
621                 call writemsg
622                 jmp .found_drive                ; Pray that this works...
623
624 fatal_error:
625                 mov si,nothing_msg
626                 call writemsg
627
628 .norge:         jmp short .norge
629
630                 ; Information message (DS:SI) output
631                 ; Prefix with "isolinux: "
632                 ;
633 writemsg:       push ax
634                 push si
635                 mov si,isolinux_str
636                 call writestr
637                 pop si
638                 call writestr
639                 pop ax
640                 ret
641
642 ;
643 ; Write a character to the screen.  There is a more "sophisticated"
644 ; version of this in the subsequent code, so we patch the pointer
645 ; when appropriate.
646 ;
647
648 writechr:
649                 jmp near writechr_simple        ; 3-byte jump
650
651 writechr_simple:
652                 pushfd
653                 pushad
654                 mov ah,0Eh
655                 xor bx,bx
656                 int 10h
657                 popad
658                 popfd
659                 ret
660
661 ;
662 ; int13: save all the segment registers and call INT 13h
663 ;        Some CD-ROM BIOSes have been found to corrupt segment registers.
664 ;
665 int13:
666
667                 push ds
668                 push es
669                 push fs
670                 push gs
671                 int 13h
672                 pop gs
673                 pop fs
674                 pop es
675                 pop ds
676                 ret
677
678 ;
679 ; Get one sector.  Convenience entry point.
680 ;
681 getonesec:
682                 mov bp,1
683                 ; Fall through to getlinsec
684
685 ;
686 ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
687 ;
688 ; Note that we can't always do this as a single request, because at least
689 ; Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick
690 ; to 32 sectors (64K) per request.
691 ;
692 ; Input:
693 ;       EAX     - Linear sector number
694 ;       ES:BX   - Target buffer
695 ;       BP      - Sector count
696 ;
697 getlinsec:
698                 mov si,dapa                     ; Load up the DAPA
699                 mov [si+4],bx
700                 mov bx,es
701                 mov [si+6],bx
702                 mov [si+8],eax
703 .loop:
704                 push bp                         ; Sectors left
705                 cmp bp,[MaxTransfer]
706                 jbe .bp_ok
707                 mov bp,[MaxTransfer]
708 .bp_ok:
709                 mov [si+2],bp
710                 push si
711                 mov dl,[DriveNumber]
712                 mov ah,42h                      ; Extended Read
713                 call xint13
714                 pop si
715                 pop bp
716                 movzx eax,word [si+2]           ; Sectors we read
717                 add [si+8],eax                  ; Advance sector pointer
718                 sub bp,ax                       ; Sectors left
719                 shl ax,SECTOR_SHIFT-4           ; 2048-byte sectors -> segment
720                 add [si+6],ax                   ; Advance buffer pointer
721                 and bp,bp
722                 jnz .loop
723                 mov eax,[si+8]                  ; Next sector
724                 ret
725
726                 ; INT 13h with retry
727 xint13:         mov byte [RetryCount],retry_count
728 .try:           pushad
729                 call int13
730                 jc .error
731                 add sp,byte 8*4                 ; Clean up stack
732                 ret
733 .error:
734                 mov [DiskError],ah              ; Save error code
735                 popad
736                 mov [DiskSys],ax                ; Save system call number
737                 dec byte [RetryCount]
738                 jz .real_error
739                 push ax
740                 mov al,[RetryCount]
741                 mov ah,[dapa+2]                 ; Sector transfer count
742                 cmp al,2                        ; Only 2 attempts left
743                 ja .nodanger
744                 mov ah,1                        ; Drop transfer size to 1
745                 jmp short .setsize
746 .nodanger:
747                 cmp al,retry_count-2
748                 ja .again                       ; First time, just try again
749                 shr ah,1                        ; Otherwise, try to reduce
750                 adc ah,0                        ; the max transfer size, but not to 0
751 .setsize:
752                 mov [MaxTransfer],ah
753                 mov [dapa+2],ah
754 .again:
755                 pop ax
756                 jmp .try
757
758 .real_error:    mov si,diskerr_msg
759                 call writemsg
760                 mov al,[DiskError]
761                 call writehex2
762                 mov si,oncall_str
763                 call writestr
764                 mov ax,[DiskSys]
765                 call writehex4
766                 mov si,ondrive_str
767                 call writestr
768                 mov al,dl
769                 call writehex2
770                 call crlf
771                 ; Fall through to kaboom
772
773 ;
774 ; kaboom: write a message and bail out.  Wait for a user keypress,
775 ;         then do a hard reboot.
776 ;
777 kaboom:
778                 RESET_STACK_AND_SEGS AX
779                 mov si,err_bootfailed
780                 call cwritestr
781                 call getchar
782                 cli
783                 mov word [BIOS_magic],0 ; Cold reboot
784                 jmp 0F000h:0FFF0h       ; Reset vector address
785
786 ; -----------------------------------------------------------------------------
787 ;  Common modules needed in the first sector
788 ; -----------------------------------------------------------------------------
789
790 %include "writestr.inc"         ; String output
791 writestr        equ cwritestr
792 %include "writehex.inc"         ; Hexadecimal output
793
794 ; -----------------------------------------------------------------------------
795 ; Data that needs to be in the first sector
796 ; -----------------------------------------------------------------------------
797
798 syslinux_banner db CR, LF, 'ISOLINUX ', version_str, ' ', date, ' ', 0
799 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
800                 db CR, LF, 0
801 isolinux_str    db 'isolinux: ', 0
802 %ifdef DEBUG_MESSAGES
803 startup_msg:    db 'Starting up, DL = ', 0
804 spec_ok_msg:    db 'Loaded spec packet OK, drive = ', 0
805 secsize_msg:    db 'Sector size appears to be ', 0
806 offset_msg:     db 'Loading main image from LBA = ', 0
807 size_msg:       db 'Sectors to load = ', 0
808 loaded_msg:     db 'Loaded boot image, verifying...', CR, LF, 0
809 verify_msg:     db 'Image checksum verified.', CR, LF, 0
810 allread_msg     db 'Main image read, jumping to main code...', CR, LF, 0
811 %endif
812 noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0
813 noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0
814 spec_err_msg:   db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
815 maybe_msg:      db 'Found something at drive = ', 0
816 alright_msg:    db 'Looks like it might be right, continuing...', CR, LF, 0
817 nospec_msg      db 'Extremely broken BIOS detected, last ditch attempt with drive = ', 0
818 nothing_msg:    db 'Failed to locate CD-ROM device; boot failed.', CR, LF
819 trysbm_msg      db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0
820 diskerr_msg:    db 'Disk error ', 0
821 oncall_str:     db ', AX = ',0
822 ondrive_str:    db ', drive ', 0
823 checkerr_msg:   db 'Image checksum error, sorry...', CR, LF, 0
824
825 err_bootfailed  db CR, LF, 'Boot failed: press a key to retry...'
826 bailmsg         equ err_bootfailed
827 crlf_msg        db CR, LF
828 null_msg        db 0
829
830                 alignb 4, db 0
831 MaxTransfer     dw 32                           ; Max sectors per transfer
832
833 rl_checkpt      equ $                           ; Must be <= 800h
834
835 rl_checkpt_off  equ ($-$$)
836 ;%ifndef DEPEND
837 ;%if rl_checkpt_off > 0x800
838 ;%error "Sector 0 overflow"
839 ;%endif
840 ;%endif
841
842 ; ----------------------------------------------------------------------------
843 ;  End of code and data that have to be in the first sector
844 ; ----------------------------------------------------------------------------
845
846 all_read:
847
848 ; Test tracers
849                 TRACER 'T'
850                 TRACER '>'
851
852 ;
853 ; Common initialization code
854 ;
855 %include "init.inc"
856 %include "cpuinit.inc"
857
858                 ; Patch the writechr routine to point to the full code
859                 mov word [writechr+1], writechr_full-(writechr+3)
860
861 ; Tell the user we got this far...
862 %ifndef DEBUG_MESSAGES                  ; Gets messy with debugging on
863                 mov si,copyright_str
864                 call writestr
865 %endif
866
867 ;
868 ; Now we're all set to start with our *real* business.  First load the
869 ; configuration file (if any) and parse it.
870 ;
871 ; In previous versions I avoided using 32-bit registers because of a
872 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
873 ; random.  I figure, though, that if there are any of those still left
874 ; they probably won't be trying to install Linux on them...
875 ;
876 ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
877 ; to take'm out.  In fact, we may want to put them back if we're going
878 ; to boot ELKS at some point.
879 ;
880
881 ;
882 ; Now, we need to sniff out the actual filesystem data structures.
883 ; mkisofs gave us a pointer to the primary volume descriptor
884 ; (which will be at 16 only for a single-session disk!); from the PVD
885 ; we should be able to find the rest of what we need to know.
886 ;
887 get_fs_structures:
888                 mov eax,[bi_pvd]
889                 mov bx,trackbuf
890                 call getonesec
891
892                 mov eax,[trackbuf+156+2]
893                 mov [RootDir+dir_lba],eax
894                 mov [CurDir+dir_lba],eax
895 %ifdef DEBUG_MESSAGES
896                 mov si,dbg_rootdir_msg
897                 call writemsg
898                 call writehex8
899                 call crlf
900 %endif
901                 mov eax,[trackbuf+156+10]
902                 mov [RootDir+dir_len],eax
903                 mov [CurDir+dir_len],eax
904                 add eax,SECTOR_SIZE-1
905                 shr eax,SECTOR_SHIFT
906                 mov [RootDir+dir_clust],eax
907                 mov [CurDir+dir_clust],eax
908
909                 ; Look for an isolinux directory, and if found,
910                 ; make it the current directory instead of the root
911                 ; directory.
912                 mov di,boot_dir                 ; Search for /boot/isolinux
913                 mov al,02h
914                 call searchdir_iso
915                 jnz .found_dir
916                 mov di,isolinux_dir
917                 mov al,02h                      ; Search for /isolinux
918                 call searchdir_iso
919                 jz .no_isolinux_dir
920 .found_dir:
921                 mov [CurDir+dir_len],eax
922                 mov eax,[si+file_left]
923                 mov [CurDir+dir_clust],eax
924                 xor eax,eax                     ; Free this file pointer entry
925                 xchg eax,[si+file_sector]
926                 mov [CurDir+dir_lba],eax
927 %ifdef DEBUG_MESSAGES
928                 push si
929                 mov si,dbg_isodir_msg
930                 call writemsg
931                 pop si
932                 call writehex8
933                 call crlf
934 %endif
935 .no_isolinux_dir:
936
937 ;
938 ; Locate the configuration file
939 ;
940 load_config:
941 %ifdef DEBUG_MESSAGES
942                 mov si,dbg_config_msg
943                 call writemsg
944 %endif
945
946                 mov si,config_name
947                 mov di,ConfigName
948                 call strcpy
949
950                 mov di,ConfigName
951                 call open
952                 jz no_config_file               ; Not found or empty
953
954 %ifdef DEBUG_MESSAGES
955                 mov si,dbg_configok_msg
956                 call writemsg
957 %endif
958
959 ;
960 ; Now we have the config file open.  Parse the config file and
961 ; run the user interface.
962 ;
963 %include "ui.inc"
964
965 ;
966 ; Enable disk emulation.  The kind of disk we emulate is dependent on the
967 ; size of the file: 1200K, 1440K or 2880K floppy, otherwise harddisk.
968 ;
969 is_disk_image:
970                 TRACER CR
971                 TRACER LF
972                 TRACER 'D'
973                 TRACER ':'
974
975                 mov edx,eax                     ; File size
976                 mov di,img_table
977                 mov cx,img_table_count
978                 mov eax,[si+file_sector]        ; Starting LBA of file
979                 mov [dsp_lba],eax               ; Location of file
980                 mov byte [dsp_drive], 0         ; 00h floppy, 80h hard disk
981 .search_table:
982                 TRACER 't'
983                 mov eax,[di+4]
984                 cmp edx,[di]
985                 je .type_found
986                 add di,8
987                 loop .search_table
988
989                 ; Hard disk image.  Need to examine the partition table
990                 ; in order to deduce the C/H/S geometry.  Sigh.
991 .hard_disk_image:
992                 TRACER 'h'
993                 cmp edx,512
994                 jb .bad_image
995
996                 mov bx,trackbuf
997                 mov cx,1                        ; Load 1 sector
998                 call getfssec
999
1000                 cmp word [trackbuf+510],0aa55h  ; Boot signature
1001                 jne .bad_image          ; Image not bootable
1002
1003                 mov cx,4                        ; 4 partition entries
1004                 mov di,trackbuf+446             ; Start of partition table
1005
1006                 xor ax,ax                       ; Highest sector(al) head(ah)
1007
1008 .part_scan:
1009                 cmp byte [di+4], 0
1010                 jz .part_loop
1011                 lea si,[di+1]
1012                 call .hs_check
1013                 add si,byte 4
1014                 call .hs_check
1015 .part_loop:
1016                 add di,byte 16
1017                 loop .part_scan
1018
1019                 push eax                        ; H/S
1020                 push edx                        ; File size
1021                 mov bl,ah
1022                 xor bh,bh
1023                 inc bx                          ; # of heads in BX
1024                 xor ah,ah                       ; # of sectors in AX
1025                 cwde                            ; EAX[31:16] <- 0
1026                 mul bx
1027                 shl eax,9                       ; Convert to bytes
1028                 ; Now eax contains the number of bytes per cylinder
1029                 pop ebx                         ; File size
1030                 xor edx,edx
1031                 div ebx
1032                 and edx,edx
1033                 jz .no_remainder
1034                 inc eax                         ; Fractional cylinder...
1035                 ; Now (e)ax contains the number of cylinders
1036 .no_remainder:  cmp eax,1024
1037                 jna .ok_cyl
1038                 mov ax,1024                     ; Max possible #
1039 .ok_cyl:        dec ax                          ; Convert to max cylinder no
1040                 pop ebx                         ; S(bl) H(bh)
1041                 shl ah,6
1042                 or bl,ah
1043                 xchg ax,bx
1044                 shl eax,16
1045                 mov ah,bl
1046                 mov al,4                        ; Hard disk boot
1047                 mov byte [dsp_drive], 80h       ; Drive 80h = hard disk
1048
1049 .type_found:
1050                 TRACER 'T'
1051                 mov bl,[sp_media]
1052                 and bl,0F0h                     ; Copy controller info bits
1053                 or al,bl
1054                 mov [dsp_media],al              ; Emulation type
1055                 shr eax,8
1056                 mov [dsp_chs],eax               ; C/H/S geometry
1057                 mov ax,[sp_devspec]             ; Copy device spec
1058                 mov [dsp_devspec],ax
1059                 mov al,[sp_controller]          ; Copy controller index
1060                 mov [dsp_controller],al
1061
1062                 TRACER 'V'
1063                 call vgaclearmode               ; Reset video
1064
1065                 mov ax,4C00h                    ; Enable emulation and boot
1066                 mov si,dspec_packet
1067                 mov dl,[DriveNumber]
1068                 lss sp,[InitStack]
1069                 TRACER 'X'
1070
1071                 call int13
1072
1073                 ; If this returns, we have problems
1074 .bad_image:
1075                 mov si,err_disk_image
1076                 call cwritestr
1077                 jmp enter_command
1078
1079 ;
1080 ; Look for the highest seen H/S geometry
1081 ; We compute cylinders separately
1082 ;
1083 .hs_check:
1084                 mov bl,[si]                     ; Head #
1085                 cmp bl,ah
1086                 jna .done_track
1087                 mov ah,bl                       ; New highest head #
1088 .done_track:    mov bl,[si+1]
1089                 and bl,3Fh                      ; Sector #
1090                 cmp bl,al
1091                 jna .done_sector
1092                 mov al,bl
1093 .done_sector:   ret
1094
1095 ;
1096 ; Boot a specified local disk.  AX specifies the BIOS disk number; or
1097 ; 0xFFFF in case we should execute INT 18h ("next device.")
1098 ;
1099 local_boot:
1100                 call vgaclearmode
1101                 lss sp,[cs:Stack]               ; Restore stack pointer
1102                 xor dx,dx
1103                 mov ds,dx
1104                 mov es,dx
1105                 mov fs,dx
1106                 mov gs,dx
1107                 mov si,localboot_msg
1108                 call writestr
1109                 cmp ax,-1
1110                 je .int18
1111
1112                 ; Load boot sector from the specified BIOS device and jump to it.
1113                 mov dl,al
1114                 xor dh,dh
1115                 push dx
1116                 xor ax,ax                       ; Reset drive
1117                 call xint13
1118                 mov ax,0201h                    ; Read one sector
1119                 mov cx,0001h                    ; C/H/S = 0/0/1 (first sector)
1120                 mov bx,trackbuf
1121                 call xint13
1122                 pop dx
1123                 cli                             ; Abandon hope, ye who enter here
1124                 mov si,trackbuf
1125                 mov di,07C00h
1126                 mov cx,512                      ; Probably overkill, but should be safe
1127                 rep movsd
1128                 lss sp,[cs:InitStack]
1129                 jmp 0:07C00h                    ; Jump to new boot sector
1130
1131 .int18:
1132                 int 18h                         ; Hope this does the right thing...
1133                 jmp kaboom                      ; If we returned, oh boy...
1134
1135 ;
1136 ; close_file:
1137 ;            Deallocates a file structure (pointer in SI)
1138 ;            Assumes CS == DS.
1139 ;
1140 close_file:
1141                 and si,si
1142                 jz .closed
1143                 mov dword [si],0                ; First dword == file_left
1144                 xor si,si
1145 .closed:        ret
1146
1147 ;
1148 ; searchdir:
1149 ;
1150 ;       Open a file
1151 ;
1152 ;            On entry:
1153 ;               DS:DI   = filename
1154 ;            If successful:
1155 ;               ZF clear
1156 ;               SI              = file pointer
1157 ;               EAX             = file length in bytes
1158 ;            If unsuccessful
1159 ;               ZF set
1160 ;
1161 ; Assumes CS == DS == ES, and trashes BX and CX.
1162 ;
1163 ; searchdir_iso is a special entry point for ISOLINUX only.  In addition
1164 ; to the above, searchdir_iso passes a file flag mask in AL.  This is useful
1165 ; for searching for directories.
1166 ;
1167 alloc_failure:
1168                 xor ax,ax                       ; ZF <- 1
1169                 ret
1170
1171 searchdir:
1172                 xor al,al
1173 searchdir_iso:
1174                 mov [ISOFlags],al
1175                 TRACER 'S'
1176                 call allocate_file              ; Temporary file structure for directory
1177                 jnz alloc_failure
1178                 push es
1179                 push ds
1180                 pop es                          ; ES = DS
1181                 mov si,CurDir
1182                 cmp byte [di],'/'               ; If filename begins with slash
1183                 jne .not_rooted
1184                 inc di                          ; Skip leading slash
1185                 mov si,RootDir                  ; Reference root directory instead
1186 .not_rooted:
1187                 mov eax,[si+dir_clust]
1188                 mov [bx+file_left],eax
1189                 mov eax,[si+dir_lba]
1190                 mov [bx+file_sector],eax
1191                 mov edx,[si+dir_len]
1192
1193 .look_for_slash:
1194                 mov ax,di
1195 .scan:
1196                 mov cl,[di]
1197                 inc di
1198                 and cl,cl
1199                 jz .isfile
1200                 cmp cl,'/'
1201                 jne .scan
1202                 mov [di-1],byte 0               ; Terminate at directory name
1203                 mov cl,02h                      ; Search for directory
1204                 xchg cl,[ISOFlags]
1205
1206                 push di                         ; Save these...
1207                 push cx
1208
1209                 ; Create recursion stack frame...
1210                 push word .resume               ; Where to "return" to
1211                 push es
1212 .isfile:        xchg ax,di
1213
1214 .getsome:
1215                 ; Get a chunk of the directory
1216                 ; This relies on the fact that ISOLINUX doesn't change SI
1217                 mov si,trackbuf
1218                 TRACER 'g'
1219                 pushad
1220                 xchg bx,si
1221                 mov cx,[BufSafe]
1222                 call getfssec
1223                 popad
1224
1225 .compare:
1226                 movzx eax,byte [si]             ; Length of directory entry
1227                 cmp al,33
1228                 jb .next_sector
1229                 TRACER 'c'
1230                 mov cl,[si+25]
1231                 xor cl,[ISOFlags]
1232                 test cl, byte 8Eh               ; Unwanted file attributes!
1233                 jnz .not_file
1234                 pusha
1235                 movzx cx,byte [si+32]           ; File identifier length
1236                 add si,byte 33                  ; File identifier offset
1237                 TRACER 'i'
1238                 call iso_compare_names
1239                 popa
1240                 je .success
1241 .not_file:
1242                 sub edx,eax                     ; Decrease bytes left
1243                 jbe .failure
1244                 add si,ax                       ; Advance pointer
1245
1246 .check_overrun:
1247                 ; Did we finish the buffer?
1248                 cmp si,trackbuf+trackbufsize
1249                 jb .compare                     ; No, keep going
1250
1251                 jmp short .getsome              ; Get some more directory
1252
1253 .next_sector:
1254                 ; Advance to the beginning of next sector
1255                 lea ax,[si+SECTOR_SIZE-1]
1256                 and ax,~(SECTOR_SIZE-1)
1257                 sub ax,si
1258                 jmp short .not_file             ; We still need to do length checks
1259
1260 .failure:       xor eax,eax                     ; ZF = 1
1261                 mov [bx+file_sector],eax
1262                 pop es
1263                 ret
1264
1265 .success:
1266                 mov eax,[si+2]                  ; Location of extent
1267                 mov [bx+file_sector],eax
1268                 mov eax,[si+10]                 ; Data length
1269                 mov [bx+file_bytesleft],eax
1270                 push eax
1271                 add eax,SECTOR_SIZE-1
1272                 shr eax,SECTOR_SHIFT
1273                 mov [bx+file_left],eax
1274                 pop eax
1275                 and bx,bx                       ; ZF = 0
1276                 mov si,bx
1277                 pop es
1278                 ret
1279
1280 .resume:        ; We get here if we were only doing part of a lookup
1281                 ; This relies on the fact that .success returns bx == si
1282                 xchg edx,eax                    ; Directory length in edx
1283                 pop cx                          ; Old ISOFlags
1284                 pop di                          ; Next filename pointer
1285                 mov byte [di-1], '/'            ; Restore slash
1286                 mov [ISOFlags],cl               ; Restore the flags
1287                 jz .failure                     ; Did we fail?  If so fail for real!
1288                 jmp .look_for_slash             ; Otherwise, next level
1289
1290 ;
1291 ; allocate_file: Allocate a file structure
1292 ;
1293 ;               If successful:
1294 ;                 ZF set
1295 ;                 BX = file pointer
1296 ;               In unsuccessful:
1297 ;                 ZF clear
1298 ;
1299 allocate_file:
1300                 TRACER 'a'
1301                 push cx
1302                 mov bx,Files
1303                 mov cx,MAX_OPEN
1304 .check:         cmp dword [bx], byte 0
1305                 je .found
1306                 add bx,open_file_t_size         ; ZF = 0
1307                 loop .check
1308                 ; ZF = 0 if we fell out of the loop
1309 .found:         pop cx
1310                 ret
1311
1312 ;
1313 ; iso_compare_names:
1314 ;       Compare the names DS:SI and DS:DI and report if they are
1315 ;       equal from an ISO 9660 perspective.  SI is the name from
1316 ;       the filesystem; CX indicates its length, and ';' terminates.
1317 ;       DI is expected to end with a null.
1318 ;
1319 ;       Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
1320 ;
1321
1322 iso_compare_names:
1323                 ; First, terminate and canonicalize input filename
1324                 push di
1325                 mov di,ISOFileName
1326 .canon_loop:    jcxz .canon_end
1327                 lodsb
1328                 dec cx
1329                 cmp al,';'
1330                 je .canon_end
1331                 and al,al
1332                 je .canon_end
1333                 stosb
1334                 cmp di,ISOFileNameEnd-1         ; Guard against buffer overrun
1335                 jb .canon_loop
1336 .canon_end:
1337                 cmp di,ISOFileName
1338                 jbe .canon_done
1339                 cmp byte [di-1],'.'             ; Remove terminal dots
1340                 jne .canon_done
1341                 dec di
1342                 jmp short .canon_end
1343 .canon_done:
1344                 mov [di],byte 0                 ; Null-terminate string
1345                 pop di
1346                 mov si,ISOFileName
1347 .compare:
1348                 lodsb
1349                 mov ah,[di]
1350                 inc di
1351                 and ax,ax
1352                 jz .success                     ; End of string for both
1353                 and al,al                       ; Is either one end of string?
1354                 jz .failure                     ; If so, failure
1355                 and ah,ah
1356                 jz .failure
1357                 or ax,2020h                     ; Convert to lower case
1358                 cmp al,ah
1359                 je .compare
1360 .failure:       and ax,ax                       ; ZF = 0 (at least one will be nonzero)
1361 .success:       ret
1362
1363 ;
1364 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1365 ;              to by ES:DI; ends on encountering any whitespace.
1366 ;              DI is preserved.
1367 ;
1368 ;              This verifies that a filename is < FILENAME_MAX characters,
1369 ;              doesn't contain whitespace, zero-pads the output buffer,
1370 ;              and removes trailing dots and redundant slashes,
1371 ;              so "repe cmpsb" can do a compare, and the
1372 ;              path-searching routine gets a bit of an easier job.
1373 ;
1374 mangle_name:
1375                 push di
1376                 push bx
1377                 xor ax,ax
1378                 mov cx,FILENAME_MAX-1
1379                 mov bx,di
1380
1381 .mn_loop:
1382                 lodsb
1383                 cmp al,' '                      ; If control or space, end
1384                 jna .mn_end
1385                 cmp al,ah                       ; Repeated slash?
1386                 je .mn_skip
1387                 xor ah,ah
1388                 cmp al,'/'
1389                 jne .mn_ok
1390                 mov ah,al
1391 .mn_ok          stosb
1392 .mn_skip:       loop .mn_loop
1393 .mn_end:
1394                 cmp bx,di                       ; At the beginning of the buffer?
1395                 jbe .mn_zero
1396                 cmp byte [es:di-1],'.'          ; Terminal dot?
1397                 je .mn_kill
1398                 cmp byte [es:di-1],'/'          ; Terminal slash?
1399                 jne .mn_zero
1400 .mn_kill:       dec di                          ; If so, remove it
1401                 inc cx
1402                 jmp short .mn_end
1403 .mn_zero:
1404                 inc cx                          ; At least one null byte
1405                 xor ax,ax                       ; Zero-fill name
1406                 rep stosb
1407                 pop bx
1408                 pop di
1409                 ret                             ; Done
1410
1411 ;
1412 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1413 ;                filename to the conventional representation.  This is needed
1414 ;                for the BOOT_IMAGE= parameter for the kernel.
1415 ;
1416 ;                DS:SI -> input mangled file name
1417 ;                ES:DI -> output buffer
1418 ;
1419 ;                On return, DI points to the first byte after the output name,
1420 ;                which is set to a null byte.
1421 ;
1422 unmangle_name:  call strcpy
1423                 dec di                          ; Point to final null byte
1424                 ret
1425
1426 ;
1427 ; getfssec: Get multiple clusters from a file, given the file pointer.
1428 ;
1429 ;  On entry:
1430 ;       ES:BX   -> Buffer
1431 ;       SI      -> File pointer
1432 ;       CX      -> Cluster count
1433 ;  On exit:
1434 ;       SI      -> File pointer (or 0 on EOF)
1435 ;       CF = 1  -> Hit EOF
1436 ;       ECX     -> Bytes actually read
1437 ;
1438 getfssec:
1439                 TRACER 'F'
1440
1441                 push ds
1442                 push cs
1443                 pop ds                          ; DS <- CS
1444
1445                 movzx ecx,cx
1446                 cmp ecx,[si+file_left]
1447                 jna .ok_size
1448                 mov ecx,[si+file_left]
1449
1450 .ok_size:
1451                 mov bp,cx
1452                 push cx
1453                 push si
1454                 mov eax,[si+file_sector]
1455                 TRACER 'l'
1456                 call getlinsec
1457                 xor ecx,ecx
1458                 pop si
1459                 pop cx
1460
1461                 add [si+file_sector],ecx
1462                 sub [si+file_left],ecx
1463                 ja .not_eof                     ; CF = 0
1464                 stc
1465
1466 .not_eof:
1467                 pushf
1468                 shl ecx,SECTOR_SHIFT            ; Convert to bytes
1469                 cmp ecx,[si+file_bytesleft]
1470                 jb .not_all
1471                 mov ecx,[si+file_bytesleft]
1472 .not_all:       sub [si+file_bytesleft],ecx
1473                 popf
1474                 jnc .ret
1475                 push eax
1476                 xor eax,eax
1477                 mov [si+file_sector],eax        ; Unused
1478                 mov si,ax
1479                 pop eax
1480                 stc
1481 .ret:
1482                 pop ds
1483                 TRACER 'f'
1484                 ret
1485
1486 ; -----------------------------------------------------------------------------
1487 ;  Common modules
1488 ; -----------------------------------------------------------------------------
1489
1490 %include "getc.inc"             ; getc et al
1491 %include "conio.inc"            ; Console I/O
1492 %include "configinit.inc"       ; Initialize configuration
1493 %include "parseconfig.inc"      ; High-level config file handling
1494 %include "parsecmd.inc"         ; Low-level config file handling
1495 %include "bcopy32.inc"          ; 32-bit bcopy
1496 %include "loadhigh.inc"         ; Load a file into high memory
1497 %include "font.inc"             ; VGA font stuff
1498 %include "graphics.inc"         ; VGA graphics
1499 %include "highmem.inc"          ; High memory sizing
1500 %include "strcpy.inc"           ; strcpy()
1501 %include "rawcon.inc"           ; Console I/O w/o using the console functions
1502 %include "adv.inc"              ; Auxillary Data Vector
1503
1504 ; -----------------------------------------------------------------------------
1505 ;  Begin data section
1506 ; -----------------------------------------------------------------------------
1507
1508                 section .data
1509
1510 localboot_msg   db 'Booting from local disk...', CR, LF, 0
1511 default_str     db 'default', 0
1512 default_len     equ ($-default_str)
1513 boot_dir        db '/boot'                      ; /boot/isolinux
1514 isolinux_dir    db '/isolinux', 0
1515 config_name     db 'isolinux.cfg', 0
1516 err_disk_image  db 'Cannot load disk image (invalid file)?', CR, LF, 0
1517
1518 %ifdef DEBUG_MESSAGES
1519 dbg_rootdir_msg db 'Root directory at LBA = ', 0
1520 dbg_isodir_msg  db 'isolinux directory at LBA = ', 0
1521 dbg_config_msg  db 'About to load config file...', CR, LF, 0
1522 dbg_configok_msg        db 'Configuration file opened...', CR, LF, 0
1523 %endif
1524 ;
1525 ; Command line options we'd like to take a look at
1526 ;
1527 ; mem= and vga= are handled as normal 32-bit integer values
1528 initrd_cmd      db 'initrd='
1529 initrd_cmd_len  equ 7
1530
1531 ;
1532 ; Config file keyword table
1533 ;
1534 %include "keywords.inc"
1535
1536 ;
1537 ; Extensions to search for (in *forward* order).
1538 ;
1539                 align 4, db 0
1540 exten_table:    db '.cbt'               ; COMBOOT (specific)
1541                 db '.img'               ; Disk image
1542                 db '.bin'               ; CD boot sector
1543                 db '.com'               ; COMBOOT (same as DOS)
1544                 db '.c32'               ; COM32
1545 exten_table_end:
1546                 dd 0, 0                 ; Need 8 null bytes here
1547
1548 ;
1549 ; Floppy image table
1550 ;
1551                 align 4, db 0
1552 img_table_count equ 3
1553 img_table:
1554                 dd 1200*1024            ; 1200K floppy
1555                 db 1                    ; Emulation type
1556                 db 80-1                 ; Max cylinder
1557                 db 15                   ; Max sector
1558                 db 2-1                  ; Max head
1559
1560                 dd 1440*1024            ; 1440K floppy
1561                 db 2                    ; Emulation type
1562                 db 80-1                 ; Max cylinder
1563                 db 18                   ; Max sector
1564                 db 2-1                  ; Max head
1565
1566                 dd 2880*1024            ; 2880K floppy
1567                 db 3                    ; Emulation type
1568                 db 80-1                 ; Max cylinder
1569                 db 36                   ; Max sector
1570                 db 2-1                  ; Max head
1571
1572 ;
1573 ; Misc initialized (data) variables
1574 ;
1575
1576 ;
1577 ; Variables that are uninitialized in SYSLINUX but initialized here
1578 ;
1579 ; **** ISOLINUX:: We may have to make this flexible, based on what the
1580 ; **** BIOS expects our "sector size" to be.
1581 ;
1582                 alignb 4, db 0
1583 BufSafe         dw trackbufsize/SECTOR_SIZE     ; Clusters we can load into trackbuf
1584 BufSafeBytes    dw trackbufsize         ; = how many bytes?
1585 %ifndef DEPEND
1586 %if ( trackbufsize % SECTOR_SIZE ) != 0
1587 %error trackbufsize must be a multiple of SECTOR_SIZE
1588 %endif
1589 %endif