renamed isoboot_regtest.asm to isobtrt.asm to comply with ISO-9660 standard
[reactos.git] / reactos / boot / freeldr / bootsect / isobtrt.asm
diff --git a/reactos/boot/freeldr/bootsect/isobtrt.asm b/reactos/boot/freeldr/bootsect/isobtrt.asm
new file mode 100644 (file)
index 0000000..1188634
--- /dev/null
@@ -0,0 +1,1023 @@
+; ****************************************************************************\r
+;\r
+;  isolinux.asm\r
+;\r
+;  A program to boot Linux kernels off a CD-ROM using the El Torito\r
+;  boot standard in "no emulation" mode, making the entire filesystem\r
+;  available.  It is based on the SYSLINUX boot loader for MS-DOS\r
+;  floppies.\r
+;\r
+;   Copyright (C) 1994-2001  H. Peter Anvin\r
+;\r
+;  This program is free software; you can redistribute it and/or modify\r
+;  it under the terms of the GNU General Public License as published by\r
+;  the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,\r
+;  USA; either version 2 of the License, or (at your option) any later\r
+;  version; incorporated herein by reference.\r
+; \r
+; ****************************************************************************\r
+;\r
+; THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM\r
+; MODIFICATION DONE BY MICHAEL K TER LOUW\r
+; LAST UPDATED 3-9-2002\r
+; SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE\r
+;\r
+; ****************************************************************************\r
+;\r
+; This file is a modified version of ISOLINUX.ASM.\r
+; Modification done by Eric Kohl\r
+; Last update 04-25-2002\r
+;\r
+; ****************************************************************************\r
+;\r
+; This file is a modified version of ISOLINUX.ASM.\r
+; (for ReactOS regression testing)\r
+; Modification done by Christoph von Wittich\r
+; Last update 08-27-2006\r
+;\r
+; ****************************************************************************\r
+\r
+\r
+; Note: The Makefile builds one version with DEBUG_MESSAGES automatically.\r
+;%define DEBUG_MESSAGES                ; Uncomment to get debugging messages\r
+\r
+%define WAIT_FOR_KEY\r
+\r
+\r
+; ---------------------------------------------------------------------------\r
+;   BEGIN THE BIOS/CODE/DATA SEGMENT\r
+; ---------------------------------------------------------------------------\r
+\r
+               absolute 0400h\r
+serial_base    resw 4                  ; Base addresses for 4 serial ports\r
+               absolute 0413h\r
+BIOS_fbm       resw 1                  ; Free Base Memory (kilobytes)\r
+               absolute 046Ch\r
+BIOS_timer     resw 1                  ; Timer ticks\r
+               absolute 0472h\r
+BIOS_magic     resw 1                  ; BIOS reset magic\r
+               absolute 0484h\r
+BIOS_vidrows   resb 1                  ; Number of screen rows\r
+\r
+;\r
+; Memory below this point is reserved for the BIOS and the MBR\r
+;\r
+               absolute 1000h\r
+trackbuf       resb 8192               ; Track buffer goes here\r
+trackbufsize   equ $-trackbuf\r
+;              trackbuf ends at 3000h\r
+\r
+               struc open_file_t\r
+file_sector    resd 1                  ; Sector pointer (0 = structure free)\r
+file_left      resd 1                  ; Number of sectors left\r
+               endstruc\r
+\r
+               struc dir_t\r
+dir_lba                resd 1                  ; Directory start (LBA)\r
+dir_len                resd 1                  ; Length in bytes\r
+dir_clust      resd 1                  ; Length in clusters\r
+               endstruc\r
+\r
+\r
+MAX_OPEN_LG2   equ 2                   ; log2(Max number of open files)\r
+MAX_OPEN       equ (1 << MAX_OPEN_LG2)\r
+SECTORSIZE_LG2 equ 11                  ; 2048 bytes/sector (El Torito requirement)\r
+SECTORSIZE     equ (1 << SECTORSIZE_LG2)\r
+CR             equ 13                  ; Carriage Return\r
+LF             equ 10                  ; Line Feed\r
+retry_count    equ 6                   ; How patient are we with the BIOS?\r
+\r
+\r
+\r
+       absolute 5000h                          ; Here we keep our BSS stuff\r
+\r
+DriveNo                resb 1                  ; CD-ROM BIOS drive number\r
+DiskError      resb 1                  ; Error code for disk I/O\r
+RetryCount     resb 1                  ; Used for disk access retries\r
+TimeoutCount   resb 1                  ; Timeout counter\r
+ISOFlags       resb 1                  ; Flags for ISO directory search\r
+RootDir                resb dir_t_size         ; Root directory\r
+CurDir         resb dir_t_size         ; Current directory\r
+ISOFileName    resb 64                 ; ISO filename canonicalization buffer\r
+ISOFileNameEnd equ $\r
+\r
+\r
+               alignb open_file_t_size\r
+Files          resb MAX_OPEN*open_file_t_size\r
+\r
+\r
+\r
+       section .text\r
+       org 7000h\r
+\r
+start:\r
+       cli                                     ; Disable interrupts\r
+       xor     ax, ax                          ; ax = segment zero\r
+       mov     ss, ax                          ; Initialize stack segment\r
+       mov     sp, start                       ; Set up stack\r
+       mov     ds, ax                          ; Initialize other segment registers\r
+       mov     es, ax\r
+       mov     fs, ax\r
+       mov     gs, ax\r
+       sti                                     ; Enable interrupts\r
+       cld                                     ; Increment pointers\r
+\r
+       mov     cx, 2048 >> 2                   ; Copy the bootsector\r
+       mov     si, 0x7C00                      ; from 0000:7C00\r
+       mov     di, 0x7000                      ; to 0000:7000\r
+       rep     movsd                           ; copy the program\r
+       jmp     0:relocate                      ; jump into relocated code\r
+\r
+relocate:\r
+       ; Display the banner and copyright\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, isolinux_banner             ; si points to hello message\r
+       call    writestr                        ; display the message\r
+       mov     si,copyright_str\r
+       call    writestr\r
+%endif\r
+\r
+\r
+       ; Make sure the keyboard buffer is empty\r
+%ifdef WAIT_FOR_KEY\r
+.kbd_buffer_test:\r
+       call    pollchar\r
+       jz      .kbd_buffer_empty\r
+       call    getchar\r
+       jmp     .kbd_buffer_test\r
+.kbd_buffer_empty:\r
+\r
+       ; Check if there is harddisk\r
+       pusha\r
+       mov     ax, 0800h\r
+       mov     dx, 0080h\r
+       int     13h\r
+       popa\r
+       jmp     .boot_cdrom\r
+\r
+       ; Display the 'Press key' message and wait for a maximum of 5 seconds\r
+       call    crlf\r
+       mov     si, presskey_msg                ; si points to 'Press key' message\r
+       call    writestr                        ; display the message\r
+\r
+       mov     byte [TimeoutCount], 5\r
+.next_second:\r
+       mov     eax, [BIOS_timer]               ; load current tick counter\r
+       add     eax, 19                         ; \r
+\r
+.poll_again:\r
+       call    pollchar\r
+       jnz     .boot_cdrom\r
+\r
+       mov     ebx, [BIOS_timer]\r
+       cmp     eax, ebx\r
+       jnz     .poll_again\r
+\r
+       mov     si, dot_msg                     ; print '.'\r
+       call    writestr\r
+       dec     byte [TimeoutCount]             ; decrement timeout counter\r
+       jz      .boot_harddisk\r
+       jmp     .next_second\r
+\r
+.boot_harddisk:\r
+       call    crlf\r
+\r
+       ; Boot first harddisk (drive 0x80)\r
+       mov     ax, 0201h\r
+       mov     dx, 0080h\r
+       mov     cx, 0001h\r
+       mov     bx, 7C00h\r
+       int     13h\r
+       jnc     .go_hd\r
+       jmp     kaboom\r
+.go_hd:\r
+       mov     ax, cs\r
+       mov     ds, ax\r
+       mov     es, ax\r
+       mov     fs, ax\r
+       mov     gs, ax\r
+       mov     dx, 0080h\r
+\r
+       jmp     0:0x7C00\r
+%endif\r
+\r
+.boot_cdrom:\r
+%ifdef WAIT_FOR_KEY\r
+       call    crlf\r
+       call    crlf\r
+%endif\r
+\r
+       ; Save and display the boot drive number\r
+       mov     [DriveNo], dl\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, startup_msg\r
+       call    writemsg\r
+       mov     al, dl\r
+       call    writehex2\r
+       call    crlf\r
+%endif\r
+\r
+       ; Now figure out what we're actually doing\r
+       ; Note: use passed-in DL value rather than 7Fh because\r
+       ; at least some BIOSes will get the wrong value otherwise\r
+       mov     ax, 4B01h                       ; Get disk emulation status\r
+       mov     dl, [DriveNo]\r
+       mov     si, spec_packet\r
+       int     13h\r
+       jc      near spec_query_failed          ; Shouldn't happen (BIOS bug)\r
+       mov     dl, [DriveNo]\r
+       cmp     [sp_drive], dl                  ; Should contain the drive number\r
+       jne     near spec_query_failed\r
+\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, spec_ok_msg\r
+       call    writemsg\r
+       mov     al, byte [sp_drive]\r
+       call    writehex2\r
+       call    crlf\r
+%endif\r
+\r
+found_drive:\r
+       ; Get drive information\r
+       mov     ah, 48h\r
+       mov     dl, [DriveNo]\r
+       mov     si, drive_params\r
+       int     13h\r
+       jnc     params_ok\r
+\r
+       ; mov   si, nosecsize_msg       No use in reporting this\r
+       ; call  writemsg\r
+\r
+params_ok:\r
+       ; Check for the sector size (should be 2048, but\r
+       ; some BIOSes apparently think we're 512-byte media)\r
+       ;\r
+       ; FIX: We need to check what the proper behaviour\r
+       ; is for getlinsec when the BIOS thinks the sector\r
+       ; size is 512!!!  For that, we need such a BIOS, though...\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, secsize_msg\r
+       call    writemsg\r
+       mov     ax, [dp_secsize]\r
+       call    writehex4\r
+       call    crlf\r
+%endif\r
+\r
+\r
+       ;\r
+       ; Clear Files structures\r
+       ;\r
+       mov     di, Files\r
+       mov     cx, (MAX_OPEN*open_file_t_size)/4\r
+       xor     eax, eax\r
+       rep     stosd\r
+\r
+       ;\r
+       ; Now, we need to sniff out the actual filesystem data structures.\r
+       ; mkisofs gave us a pointer to the primary volume descriptor\r
+       ; (which will be at 16 only for a single-session disk!); from the PVD\r
+       ; we should be able to find the rest of what we need to know.\r
+       ;\r
+get_fs_structures:\r
+       mov     eax, 16                 ; Primary Volume Descriptor (sector 16)\r
+       mov     bx, trackbuf\r
+       call    getonesec\r
+\r
+       mov     eax, [trackbuf+156+2]\r
+       mov     [RootDir+dir_lba],eax\r
+       mov     [CurDir+dir_lba],eax\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, rootloc_msg\r
+       call    writemsg\r
+       call    writehex8\r
+       call    crlf\r
+%endif\r
+\r
+       mov     eax,[trackbuf+156+10]\r
+       mov     [RootDir+dir_len],eax\r
+       mov     [CurDir+dir_len],eax\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, rootlen_msg\r
+       call    writemsg\r
+       call    writehex8\r
+       call    crlf\r
+%endif\r
+       add     eax,SECTORSIZE-1\r
+       shr     eax,SECTORSIZE_LG2\r
+       mov     [RootDir+dir_clust],eax\r
+       mov     [CurDir+dir_clust],eax\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, rootsect_msg\r
+       call    writemsg\r
+       call    writehex8\r
+       call    crlf\r
+%endif\r
+\r
+       ; Look for the "REACTOS" directory, and if found,\r
+       ; make it the current directory instead of the root\r
+       ; directory.\r
+       mov     di,isolinux_dir\r
+       mov     al,02h                          ; Search for a directory\r
+       call    searchdir_iso\r
+       jnz     .dir_found\r
+       mov     si,no_dir_msg\r
+       call    writemsg\r
+       jmp     kaboom\r
+\r
+.dir_found:\r
+       mov     [CurDir+dir_len],eax\r
+       mov     eax,[si+file_left]\r
+       mov     [CurDir+dir_clust],eax\r
+       xor     eax,eax                         ; Free this file pointer entry\r
+       xchg    eax,[si+file_sector]\r
+       mov     [CurDir+dir_lba],eax\r
+\r
+\r
+       mov     di, isolinux_bin                ; di points to Isolinux filename\r
+       call    searchdir                       ; look for the file\r
+       jnz     .isolinux_opened                ; got the file\r
+       mov     si, no_isolinux_msg             ; si points to error message\r
+       call    writemsg                        ; display the message\r
+       jmp     kaboom                          ; fail boot\r
+\r
+.isolinux_opened:\r
+       mov     di, si                          ; save file pointer\r
+\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, filelen_msg\r
+       call    writemsg\r
+       call    writehex8\r
+       call    crlf\r
+%endif\r
+\r
+       mov ecx, eax                    ; calculate sector count\r
+       shr ecx, 11\r
+       test eax, 0x7FF\r
+       jz .full_sector\r
+       inc ecx\r
+.full_sector:\r
+\r
+%ifdef DEBUG_MESSAGES\r
+       mov eax, ecx\r
+       mov     si, filesect_msg\r
+       call    writemsg\r
+       call    writehex8\r
+       call    crlf\r
+%endif\r
+\r
+       mov     bx, 0x8000                      ; bx = load address\r
+       mov     si, di                          ; restore file pointer\r
+       mov     cx, 0xFFFF                      ; load the whole file\r
+       call    getfssec                        ; get the whole file\r
+\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, startldr_msg\r
+       call    writemsg\r
+       call    crlf\r
+%endif\r
+\r
+       mov     dl, [DriveNo]                   ; dl = boot drive\r
+       mov dh, 0                                       ; dh = boot partition\r
+       jmp     0:0x8000                        ; jump into OSLoader\r
+\r
+\r
+\r
+;\r
+; searchdir:\r
+;\r
+; Open a file\r
+;\r
+;  On entry:\r
+;      DS:DI   = filename\r
+;  If successful:\r
+;      ZF clear\r
+;      SI              = file pointer\r
+;      DX:AX or EAX    = file length in bytes\r
+;  If unsuccessful\r
+;      ZF set\r
+;\r
+\r
+;\r
+; searchdir_iso is a special entry point for ISOLINUX only.  In addition\r
+; to the above, searchdir_iso passes a file flag mask in AL.  This is useful\r
+; for searching for directories.\r
+;\r
+alloc_failure:\r
+       xor     ax,ax                           ; ZF <- 1\r
+       ret\r
+\r
+searchdir:\r
+       xor     al,al\r
+searchdir_iso:\r
+       mov     [ISOFlags],al\r
+       call    allocate_file                   ; Temporary file structure for directory\r
+       jnz     alloc_failure\r
+       push    es\r
+       push    ds\r
+       pop     es                              ; ES = DS\r
+       mov     si,CurDir\r
+       cmp     byte [di],'\'                   ; If filename begins with slash\r
+       jne     .not_rooted\r
+       inc     di                              ; Skip leading slash\r
+       mov     si,RootDir                      ; Reference root directory instead\r
+.not_rooted:\r
+       mov     eax,[si+dir_clust]\r
+       mov     [bx+file_left],eax\r
+       mov     eax,[si+dir_lba]\r
+       mov     [bx+file_sector],eax\r
+       mov     edx,[si+dir_len]\r
+\r
+.look_for_slash:\r
+       mov     ax,di\r
+.scan:\r
+       mov     cl,[di]\r
+       inc     di\r
+       and     cl,cl\r
+       jz      .isfile\r
+       cmp     cl,'\'\r
+       jne     .scan\r
+       mov     [di-1],byte 0                   ; Terminate at directory name\r
+       mov     cl,02h                          ; Search for directory\r
+       xchg    cl,[ISOFlags]\r
+       push    di\r
+       push    cx\r
+       push    word .resume                    ; Where to "return" to\r
+       push    es\r
+.isfile:\r
+       xchg    ax,di\r
+\r
+.getsome:\r
+       ; Get a chunk of the directory\r
+       mov     si,trackbuf\r
+       pushad\r
+       xchg    bx,si\r
+       mov     cx,1                            ; load one sector\r
+       call    getfssec\r
+       popad\r
+\r
+.compare:\r
+       movzx   eax, byte [si]                  ; Length of directory entry\r
+       cmp     al, 33\r
+       jb      .next_sector\r
+       mov     cl, [si+25]\r
+       xor     cl, [ISOFlags]\r
+       test    cl, byte 8Eh                    ; Unwanted file attributes!\r
+       jnz     .not_file\r
+       pusha\r
+       movzx   cx, byte [si+32]                ; File identifier length\r
+       add     si, byte 33                     ; File identifier offset\r
+       call    iso_compare_names\r
+       popa\r
+       je      .success\r
+.not_file:\r
+       sub     edx, eax                        ; Decrease bytes left\r
+       jbe     .failure\r
+       add     si, ax                          ; Advance pointer\r
+\r
+.check_overrun:\r
+       ; Did we finish the buffer?\r
+       cmp     si, trackbuf+trackbufsize\r
+       jb      .compare                        ; No, keep going\r
+\r
+       jmp     short .getsome                  ; Get some more directory\r
+\r
+.next_sector:\r
+       ; Advance to the beginning of next sector\r
+       lea     ax, [si+SECTORSIZE-1]\r
+       and     ax, ~(SECTORSIZE-1)\r
+       sub     ax, si\r
+       jmp     short .not_file                 ; We still need to do length checks\r
+\r
+.failure:\r
+%ifdef DEBUG_MESSAGES\r
+       mov     si, findfail_msg\r
+       call    writemsg\r
+       call    crlf\r
+%endif\r
+       xor     eax, eax                        ; ZF = 1\r
+       mov     [bx+file_sector], eax\r
+       pop     es\r
+       ret\r
+\r
+.success:\r
+       mov     eax, [si+2]                     ; Location of extent\r
+       mov     [bx+file_sector], eax\r
+       mov     eax, [si+10]                    ; Data length\r
+       push    eax\r
+       add     eax, SECTORSIZE-1\r
+       shr     eax, SECTORSIZE_LG2\r
+       mov     [bx+file_left], eax\r
+       pop     eax\r
+       mov     edx, eax\r
+       shr     edx, 16\r
+       and     bx, bx                          ; ZF = 0\r
+       mov     si, bx\r
+       pop     es\r
+       ret\r
+\r
+.resume:\r
+       ; We get here if we were only doing part of a lookup\r
+       ; This relies on the fact that .success returns bx == si\r
+       xchg    edx, eax                        ; Directory length in edx\r
+       pop     cx                              ; Old ISOFlags\r
+       pop     di                              ; Next filename pointer\r
+\r
+       mov     byte [di-1], '\'                ; restore the backslash in the filename\r
+\r
+       mov     [ISOFlags], cl                  ; Restore the flags\r
+       jz      .failure                        ; Did we fail?  If so fail for real!\r
+       jmp     .look_for_slash                 ; Otherwise, next level\r
+\r
+;\r
+; allocate_file: Allocate a file structure\r
+;\r
+;              If successful:\r
+;                ZF set\r
+;                BX = file pointer\r
+;              In unsuccessful:\r
+;                ZF clear\r
+;\r
+allocate_file:\r
+       push    cx\r
+       mov     bx, Files\r
+       mov     cx, MAX_OPEN\r
+.check:\r
+       cmp     dword [bx], byte 0\r
+       je      .found\r
+       add     bx, open_file_t_size            ; ZF = 0\r
+       loop    .check\r
+       ; ZF = 0 if we fell out of the loop\r
+.found:\r
+       pop     cx\r
+       ret\r
+\r
+;\r
+; iso_compare_names:\r
+;      Compare the names DS:SI and DS:DI and report if they are\r
+;      equal from an ISO 9660 perspective.  SI is the name from\r
+;      the filesystem; CX indicates its length, and ';' terminates.\r
+;      DI is expected to end with a null.\r
+;\r
+;      Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment\r
+;\r
+iso_compare_names:\r
+       ; First, terminate and canonicalize input filename\r
+       push    di\r
+       mov     di, ISOFileName\r
+.canon_loop:\r
+       jcxz    .canon_end\r
+       lodsb\r
+       dec     cx\r
+       cmp     al, ';'\r
+       je      .canon_end\r
+       and     al, al\r
+       je      .canon_end\r
+       stosb\r
+       cmp     di, ISOFileNameEnd-1            ; Guard against buffer overrun\r
+       jb      .canon_loop\r
+.canon_end:\r
+       cmp     di, ISOFileName\r
+       jbe     .canon_done\r
+       cmp     byte [di-1], '.'                ; Remove terminal dots\r
+       jne     .canon_done\r
+       dec     di\r
+       jmp     short .canon_end\r
+.canon_done:\r
+       mov     [di], byte 0                    ; Null-terminate string\r
+       pop     di\r
+       mov     si, ISOFileName\r
+.compare:\r
+       lodsb\r
+       mov     ah, [di]\r
+       inc     di\r
+       and     ax, ax\r
+       jz      .success                        ; End of string for both\r
+       and     al, al                          ; Is either one end of string?\r
+       jz      .failure                        ; If so, failure\r
+       and     ah, ah\r
+       jz      .failure\r
+       or      ax, 2020h                       ; Convert to lower case\r
+       cmp     al, ah\r
+       je      .compare\r
+.failure:\r
+       and     ax, ax                          ; ZF = 0 (at least one will be nonzero)\r
+.success:\r
+       ret\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+;\r
+; getfssec: Get multiple clusters from a file, given the file pointer.\r
+;\r
+;  On entry:\r
+;      ES:BX   -> Buffer\r
+;      SI      -> File pointer\r
+;      CX      -> Cluster count; 0FFFFh = until end of file\r
+;  On exit:\r
+;      SI      -> File pointer (or 0 on EOF)\r
+;      CF = 1  -> Hit EOF\r
+;\r
+getfssec:\r
+       cmp     cx, [si+file_left]\r
+       jna     .ok_size\r
+       mov     cx, [si+file_left]\r
+\r
+.ok_size:\r
+       mov     bp, cx\r
+       push    cx\r
+       push    si\r
+       mov     eax, [si+file_sector]\r
+       call    getlinsec\r
+       xor     ecx, ecx\r
+       pop     si\r
+       pop     cx\r
+\r
+       add     [si+file_sector], ecx\r
+       sub     [si+file_left], ecx\r
+       ja      .not_eof                        ; CF = 0\r
+\r
+       xor     ecx, ecx\r
+       mov     [si+file_sector], ecx           ; Mark as unused\r
+       xor     si,si\r
+       stc\r
+\r
+.not_eof:\r
+       ret\r
+\r
+\r
+\r
+; INT 13h, AX=4B01h, DL=<passed in value> failed.\r
+; Try to scan the entire 80h-FFh from the end.\r
+spec_query_failed:\r
+       mov     si,spec_err_msg\r
+       call    writemsg\r
+\r
+       mov     dl, 0FFh\r
+.test_loop:\r
+       pusha\r
+       mov     ax, 4B01h\r
+       mov     si, spec_packet\r
+       mov     byte [si], 13                   ; Size of buffer\r
+       int     13h\r
+       popa\r
+       jc      .still_broken\r
+\r
+       mov     si, maybe_msg\r
+       call    writemsg\r
+       mov     al, dl\r
+       call    writehex2\r
+       call    crlf\r
+\r
+       cmp     byte [sp_drive], dl\r
+       jne     .maybe_broken\r
+\r
+       ; Okay, good enough...\r
+       mov     si, alright_msg\r
+       call    writemsg\r
+       mov     [DriveNo], dl\r
+.found_drive:\r
+       jmp     found_drive\r
+\r
+       ; Award BIOS 4.51 apparently passes garbage in sp_drive,\r
+       ; but if this was the drive number originally passed in\r
+       ; DL then consider it "good enough"\r
+.maybe_broken:\r
+       cmp     byte [DriveNo], dl\r
+       je      .found_drive\r
+\r
+.still_broken:\r
+       dec dx\r
+       cmp     dl, 80h\r
+       jnb     .test_loop\r
+\r
+fatal_error:\r
+       mov     si, nothing_msg\r
+       call    writemsg\r
+\r
+.norge:\r
+       jmp     short .norge\r
+\r
+\r
+\r
+       ; Information message (DS:SI) output\r
+       ; Prefix with "isolinux: "\r
+       ;\r
+writemsg:\r
+       push    ax\r
+       push    si\r
+       mov     si, isolinux_str\r
+       call    writestr\r
+       pop     si\r
+       call    writestr\r
+       pop     ax\r
+       ret\r
+\r
+;\r
+; crlf: Print a newline\r
+;\r
+crlf:\r
+       mov     si, crlf_msg\r
+       ; Fall through\r
+\r
+;\r
+; writestr: write a null-terminated string to the console, saving\r
+;           registers on entry.\r
+;\r
+writestr:\r
+       pushfd\r
+       pushad\r
+.top:\r
+       lodsb\r
+       and     al, al\r
+       jz      .end\r
+       call    writechr\r
+       jmp     short .top\r
+.end:\r
+       popad\r
+       popfd\r
+       ret\r
+\r
+\r
+;\r
+; writehex[248]: Write a hex number in (AL, AX, EAX) to the console\r
+;\r
+writehex2:\r
+       pushfd\r
+       pushad\r
+       shl     eax, 24\r
+       mov     cx, 2\r
+       jmp     short writehex_common\r
+writehex4:\r
+       pushfd\r
+       pushad\r
+       shl     eax, 16\r
+       mov     cx, 4\r
+       jmp     short writehex_common\r
+writehex8:\r
+       pushfd\r
+       pushad\r
+       mov     cx, 8\r
+writehex_common:\r
+.loop:\r
+       rol     eax, 4\r
+       push    eax\r
+       and     al, 0Fh\r
+       cmp     al, 10\r
+       jae     .high\r
+.low:\r
+       add     al, '0'\r
+       jmp     short .ischar\r
+.high:\r
+       add     al, 'A'-10\r
+.ischar:\r
+       call    writechr\r
+       pop     eax\r
+       loop    .loop\r
+       popad\r
+       popfd\r
+       ret\r
+\r
+;\r
+; Write a character to the screen.  There is a more "sophisticated"\r
+; version of this in the subsequent code, so we patch the pointer\r
+; when appropriate.\r
+;\r
+\r
+writechr:\r
+       pushfd\r
+       pushad\r
+       mov     ah, 0Eh\r
+       xor     bx, bx\r
+       int     10h\r
+       popad\r
+       popfd\r
+       ret\r
+\r
+;\r
+; Get one sector.  Convenience entry point.\r
+;\r
+getonesec:\r
+       mov     bp, 1\r
+       ; Fall through to getlinsec\r
+\r
+;\r
+; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.\r
+;\r
+; Note that we can't always do this as a single request, because at least\r
+; Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick\r
+; to 32 sectors (64K) per request.\r
+;\r
+; Input:\r
+;      EAX     - Linear sector number\r
+;      ES:BX   - Target buffer\r
+;      BP      - Sector count\r
+;\r
+getlinsec:\r
+       mov si,dapa                     ; Load up the DAPA\r
+       mov [si+4],bx\r
+       mov bx,es\r
+       mov [si+6],bx\r
+       mov [si+8],eax\r
+.loop2:\r
+       push bp                         ; Sectors left\r
+       cmp bp,[MaxTransfer]\r
+       jbe .bp_ok\r
+       mov bp,[MaxTransfer]\r
+.bp_ok:\r
+       mov [si+2],bp\r
+       push si\r
+       mov dl,[DriveNo]\r
+       mov ah,42h                      ; Extended Read\r
+       call xint13\r
+       pop si\r
+       pop bp\r
+       movzx eax,word [si+2]           ; Sectors we read\r
+       add [si+8],eax                  ; Advance sector pointer\r
+       sub bp,ax                       ; Sectors left\r
+       shl ax,SECTORSIZE_LG2-4         ; 2048-byte sectors -> segment\r
+       add [si+6],ax                   ; Advance buffer pointer\r
+       and bp,bp\r
+       jnz .loop2\r
+       mov eax,[si+8]                  ; Next sector\r
+       ret\r
+\r
+       ; INT 13h with retry\r
+xint13:\r
+       mov     byte [RetryCount], retry_count\r
+.try:\r
+       pushad\r
+       int     13h\r
+       jc      .error\r
+       add     sp, byte 8*4                    ; Clean up stack\r
+       ret\r
+.error:\r
+       mov     [DiskError], ah         ; Save error code\r
+       popad\r
+       dec byte [RetryCount]\r
+       jz .real_error\r
+       push ax\r
+       mov al,[RetryCount]\r
+       mov ah,[dapa+2]                 ; Sector transfer count\r
+       cmp al,2                        ; Only 2 attempts left\r
+       ja .nodanger\r
+       mov ah,1                        ; Drop transfer size to 1\r
+       jmp short .setsize\r
+.nodanger:\r
+       cmp al,retry_count-2\r
+       ja .again                       ; First time, just try again\r
+       shr ah,1                        ; Otherwise, try to reduce\r
+       adc ah,0                        ; the max transfer size, but not to 0\r
+.setsize:\r
+       mov [MaxTransfer],ah\r
+       mov [dapa+2],ah\r
+.again:\r
+       pop ax\r
+       jmp .try\r
+\r
+.real_error:\r
+       mov     si, diskerr_msg\r
+       call    writemsg\r
+       mov     al, [DiskError]\r
+       call    writehex2\r
+       mov     si, ondrive_str\r
+       call    writestr\r
+       mov     al, dl\r
+       call    writehex2\r
+       call    crlf\r
+       ; Fall through to kaboom\r
+\r
+;\r
+; kaboom: write a message and bail out.  Wait for a user keypress,\r
+;        then do a hard reboot.\r
+;\r
+kaboom:\r
+       mov     ax, cs\r
+       mov     ds, ax\r
+       mov     es, ax\r
+       mov     fs, ax\r
+       mov     gs, ax\r
+       sti\r
+       mov     si, err_bootfailed\r
+       call    writestr\r
+       call    getchar\r
+       cli\r
+       mov     word [BIOS_magic], 0    ; Cold reboot\r
+       jmp     0F000h:0FFF0h           ; Reset vector address\r
+\r
+getchar:\r
+.again:\r
+       mov     ah, 1           ; Poll keyboard\r
+       int     16h\r
+       jz      .again\r
+.kbd:\r
+       xor     ax, ax          ; Get keyboard input\r
+       int     16h\r
+.func_key:\r
+       ret\r
+\r
+\r
+;\r
+; pollchar: check if we have an input character pending (ZF = 0)\r
+;\r
+pollchar:\r
+       pushad\r
+       mov ah,1                ; Poll keyboard\r
+       int 16h\r
+       popad\r
+       ret\r
+\r
+\r
+\r
+isolinux_banner        db CR, LF, 'Loading IsoBoot...', CR, LF, 0\r
+copyright_str  db ' Copyright (C) 1994-2002 H. Peter Anvin', CR, LF, 0\r
+presskey_msg   db 'Press any key to boot from CD', 0\r
+dot_msg                db '.',0\r
+\r
+%ifdef DEBUG_MESSAGES\r
+startup_msg:   db 'Starting up, DL = ', 0\r
+spec_ok_msg:   db 'Loaded spec packet OK, drive = ', 0\r
+secsize_msg:   db 'Sector size appears to be ', 0\r
+rootloc_msg:   db 'Root directory location: ', 0\r
+rootlen_msg:   db 'Root directory length: ', 0\r
+rootsect_msg:  db 'Root directory length(sectors): ', 0\r
+fileloc_msg:   db 'SETUPLDR.SYS location: ', 0\r
+filelen_msg:   db 'SETUPLDR.SYS length: ', 0\r
+filesect_msg:  db 'SETUPLDR.SYS length(sectors): ', 0\r
+findfail_msg:  db 'Failed to find file!', 0\r
+startldr_msg:  db 'Starting SETUPLDR.SYS', 0\r
+%endif\r
+\r
+nosecsize_msg: db 'Failed to get sector size, assuming 0800', CR, LF, 0\r
+spec_err_msg:  db 'Loading spec packet failed, trying to wing it...', CR, LF, 0\r
+maybe_msg:     db 'Found something at drive = ', 0\r
+alright_msg:   db 'Looks like it might be right, continuing...', CR, LF, 0\r
+nothing_msg:   db 'Failed to locate CD-ROM device; boot failed.', CR, LF, 0\r
+isolinux_str   db 'IsoBoot: ', 0\r
+crlf_msg       db CR, LF, 0\r
+diskerr_msg:   db 'Disk error ', 0\r
+ondrive_str:   db ', drive ', 0\r
+err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'\r
+isolinux_dir   db '\LOADER', 0\r
+no_dir_msg     db 'Could not find the LOADER directory.', CR, LF, 0\r
+isolinux_bin   db 'SETUPLDR.SYS', 0\r
+no_isolinux_msg        db 'Could not find SETUPLDR.SYS.', CR, LF, 0\r
+\r
+;\r
+; El Torito spec packet\r
+;\r
+               align 8, db 0\r
+spec_packet:   db 13h                          ; Size of packet\r
+sp_media:      db 0                            ; Media type\r
+sp_drive:      db 0                            ; Drive number\r
+sp_controller: db 0                            ; Controller index\r
+sp_lba:                dd 0                            ; LBA for emulated disk image\r
+sp_devspec:    dw 0                            ; IDE/SCSI information\r
+sp_buffer:     dw 0                            ; User-provided buffer\r
+sp_loadseg:    dw 0                            ; Load segment\r
+sp_sectors:    dw 0                            ; Sector count\r
+sp_chs:                db 0,0,0                        ; Simulated CHS geometry\r
+sp_dummy:      db 0                            ; Scratch, safe to overwrite\r
+\r
+;\r
+; EBIOS drive parameter packet\r
+;\r
+               align 8, db 0\r
+drive_params:  dw 30                           ; Buffer size\r
+dp_flags:      dw 0                            ; Information flags\r
+dp_cyl:                dd 0                            ; Physical cylinders\r
+dp_head:       dd 0                            ; Physical heads\r
+dp_sec:                dd 0                            ; Physical sectors/track\r
+dp_totalsec:   dd 0,0                          ; Total sectors\r
+dp_secsize:    dw 0                            ; Bytes per sector\r
+dp_dpte:       dd 0                            ; Device Parameter Table\r
+dp_dpi_key:    dw 0                            ; 0BEDDh if rest valid\r
+dp_dpi_len:    db 0                            ; DPI len\r
+               db 0\r
+               dw 0\r
+dp_bus:                times 4 db 0                    ; Host bus type\r
+dp_interface:  times 8 db 0                    ; Interface type\r
+db_i_path:     dd 0,0                          ; Interface path\r
+db_d_path:     dd 0,0                          ; Device path\r
+               db 0\r
+db_dpi_csum:   db 0                            ; Checksum for DPI info\r
+\r
+;\r
+; EBIOS disk address packet\r
+;\r
+               align 8, db 0\r
+dapa:          dw 16                           ; Packet size\r
+.count:                dw 0                            ; Block count\r
+.off:          dw 0                            ; Offset of buffer\r
+.seg:          dw 0                            ; Segment of buffer\r
+.lba:          dd 0                            ; LBA (LSW)\r
+               dd 0                            ; LBA (MSW)\r
+\r
+               alignb 4, db 0\r
+MaxTransfer    dw 2 ;32                                ; Max sectors per transfer\r
+\r
+               times 2046-($-$$) db 0          ; Pad to file offset 2046\r
+               dw 0aa55h                       ; BootSector signature\r