[BOOTSECT]
authorColin Finck <colin@reactos.org>
Tue, 24 Jan 2017 22:45:37 +0000 (22:45 +0000)
committerColin Finck <colin@reactos.org>
Tue, 24 Jan 2017 22:45:37 +0000 (22:45 +0000)
Overhaul our ISO boot sector:
- Add another entry point for hybrid booting (called by isombr) and the hybrid signature needed for isohybrid.
- Import latest isolinux.asm "API" from http://repo.or.cz/syslinux.git/blob/8bbb10b70905339b7ca6b58d6833b6a95ab03dae:/core/isolinux.asm (latest version to date) and http://repo.or.cz/syslinux.git/blob/9141c603930ef23da1e08e487a0c26750fbb4fbe:/core/isolinux.asm (latest version that came with all functions in ASM).
  Among other things, this brings us getlinsec_ebios (for reading setupldr.sys in hybrid/MBR mode) and BrokenAwardHack (for working around broken Award BIOSes)
- Fix upstream getlinsec_ebios to load files >64K.
- Load the HDD boot sector directly to 0x7C00 instead of trackbuf. This way, we don't have to load it again when the user decides to boot from HDD.
- Check for the 0xAA55 signature to decide if the HDD contains a valid MBR. This was done differently and inconsistently for isoboot and isobtrt.
- Adapt the file and comment style to ReactOS guidelines. Add meaningful comments to our main code. Remove stuff that was just taken from isolinux.asm, but makes no sense for our version.
- Remove DEBUG_MESSAGES ifdef. There is no chance they can ever fit into the 2K sector with all the added features. Debugging can still happen using Bochs or adding specific messages.

Tested with Bochs, QEMU, VMware, VirtualBox and four real computers.
Many thanks to Herm├Ęs for all the helpful suggestions and additional investigations! :)

CORE-12648

svn path=/trunk/; revision=73594

reactos/boot/freeldr/bootsect/isoboot.S

index a3733e0..9a4b057 100644 (file)
@@ -1,63 +1,47 @@
 /*
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS Bootsector for ISO file system
- * FILE:            boot/freeldr/bootsect/isoboot.S
- * PURPOSE:
- * PROGRAMMERS:     ?
- */
+ * PROJECT:        ReactOS Boot Sector for ISO file system (based on ISOLINUX)
+ * LICENSE:        GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PROGRAMMERS:    H. Peter Anvin
+ *                 Michael K. Ter Louw
+ *                 Eric Kohl
+ *                 Timo Kreuzer <timo.kreuzer@reactos.org>
+ *                 Colin Finck <colin@reactos.org>
+ *
+ *****************************************************************************
+ *
+ * isolinux.asm
+ *
+ * A program to boot Linux kernels off a CD-ROM using the El Torito
+ * boot standard in "no emulation" mode, making the entire filesystem
+ * available.  It is based on the SYSLINUX boot loader for MS-DOS
+ * floppies.
+ *
+ *  Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+ *  Copyright 2009 Intel Corporation *author: H. Peter Anvin
+ *
+ * This program is free software *you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA *either version 2 of the License, or
+ * (at your option) any later version *incorporated herein by reference.
+ *
+ *****************************************************************************/
 
 /* INCLUDES ******************************************************************/
-
 #include <asm.inc>
 #include <freeldr/include/arch/pc/x86common.h>
 
-.code16
-
-// ****************************************************************************
-//
-//  isolinux.asm
-//
-//  A program to boot Linux kernels off a CD-ROM using the El Torito
-//  boot standard in "no emulation" mode, making the entire filesystem
-//  available.  It is based on the SYSLINUX boot loader for MS-DOS
-//  floppies.
-//
-//   Copyright (C) 1994-2001  H. Peter Anvin
-//
-//  This program is free software; you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
-//  USA; either version 2 of the License, or (at your option) any later
-//  version; incorporated herein by reference.
-//
-// ****************************************************************************
-//
-// THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
-// MODIFICATION DONE BY MICHAEL K TER LOUW
-// LAST UPDATED 3-9-2002
-// SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
-//
-// ****************************************************************************
-//
-// This file is a modified version of ISOLINUX.ASM.
-// Modification done by Eric Kohl
-// Last update 04-25-2002
-//
-// ****************************************************************************
-
-//#define DEBUG_MESSAGES                /* Uncomment to get debugging messages */
 #ifndef ROS_REGTEST
 #define WAIT_FOR_KEY
 #endif
 
-// ****************************************************************************
-//    BEGIN THE BIOS/CODE/DATA SEGMENT
-// ****************************************************************************
-serial_base = HEX(0400)     // Base addresses for 4 serial ports (4 words)
-BIOS_fbm = HEX(0413)        // Free Base Memory (kilobytes) (1 word)
+
+.code16
+ASSUME CS:.text, DS:.text, ES:.text
+
+/* CONSTANTS ******************************************************************/
 BIOS_timer = HEX(046C)      // Timer ticks (1 word)
 BIOS_magic = HEX(0472)      // BIOS reset magic (1 word)
-BIOS_vidrows = HEX(0484)    // Number of screen rows (1 byte)
 
 // Memory below this point is reserved for the BIOS and the MBR
 trackbuf = HEX(1000)        // Track buffer goes here (8192 bytes)
@@ -65,132 +49,150 @@ trackbufsize = 8192         // trackbuf ends at 3000h
 
 // struct open_file_t
 file_sector = 0             // Sector pointer (0 = structure free)
-file_left = 4               // Number of sectors left
+file_bytesleft = 4          // Number of bytes left
+file_left = 8               // Number of sectors left
+// Another unused DWORD follows here in ISOLINUX
+#define open_file_t_size 16
 
-//struct dir_t
+// struct dir_t
 dir_lba = 0                 // Directory start (LBA)
 dir_len = 4                 // Length in bytes
 dir_clust = 8               // Length in clusters
-
 #define dir_t_size 12
-#define open_file_t_size 8
 
 MAX_OPEN_LG2 = 2            // log2(Max number of open files)
 MAX_OPEN = 4
-SECTORSIZE_LG2 = 11         // 2048 bytes/sector (El Torito requirement)
-SECTORSIZE = 2048
-retry_count    = 6          // How patient are we with the BIOS?
+SECTOR_SHIFT = 11           // 2048 bytes/sector (El Torito requirement)
+SECTOR_SIZE = 2048
+retry_count = 6             // How patient are we with the BIOS?
 
-/******************************************************************************/
+/* UNINITIALIZED VARIABLES ****************************************************/
 absolute HEX(5000)          // Here we keep our BSS stuff
 
-resb DriveNo, 1             // CD-ROM BIOS drive number (BYTE)
-resb DiskError, 1           // Error code for disk I/O (BYTE)
-resb RetryCount, 1          // Used for disk access retries (BYTE)
-resb TimeoutCount, 1        // Timeout counter (BYTE)
-resb ISOFlags, 1            // Flags for ISO directory search (BYTE)
-resb RootDir, dir_t_size    // Root directory (dir_t_size BYTES)
-resb CurDir, dir_t_size     // Current directory (dir_t_size BYTES)
 resb ISOFileName, 64        // ISO filename canonicalization buffer
 resb ISOFileNameEnd, 1
+resb CurrentDir, dir_t_size // Current directory
+resb RootDir, dir_t_size    // Root directory
+resb DiskSys, 2             // Last INT 13h call
+resb GetlinsecPtr, 2        // The sector-read pointer
+resb DiskError, 1           // Error code for disk I/O
+resb DriveNumber, 1         // CD-ROM BIOS drive number
+resb ISOFlags, 1            // Flags for ISO directory search
+resb RetryCount, 1          // Used for disk access retries
 
 //align open_file_t_size
-absolute HEX(5060)
+absolute HEX(5070)
 resb Files, (MAX_OPEN * open_file_t_size)
 
 
-/******************************************************************************/
-
+/* ENTRY POINTS ***************************************************************/
 
+// Entry point when booted from CD (El Torito standard)
 start:
-    cli                     // Disable interrupts
-    xor ax, ax              // ax = segment zero
-    mov ss, ax              // Initialize stack segment
-    mov sp, offset start    // Set up stack
-    mov ds, ax              // Initialize other segment registers
+    mov bx, offset getlinsec_cdrom
+    // Fall through
+
+start_common:
+    // Set up our stack and a flat addressing model.
+    cli
+    xor ax, ax
+    mov ss, ax
+    mov sp, offset start
+    mov ds, ax
     mov es, ax
     mov fs, ax
     mov gs, ax
-    sti                     // Enable interrupts
-    cld                     // Increment pointers
-
-    mov cx, 2048 / 4        // Copy the bootsector
-    mov si, HEX(7C00)       // from 0000:7C00
-    mov di, HEX(7000)       // to 0000:7000
-    rep movsd               // copy the program
-
-    ljmp16 0, relocate      // jump into relocated code
-
-relocate:
-#ifdef DEBUG_MESSAGES
-    // Display the banner and copyright
-    mov si, offset isolinux_banner // si points to hello message
-    call writestr            // display the message
-    mov si, offset copyright_str
-    call writestr
-#endif
+    sti
+
+    // Our boot sector has been loaded to address 0x7C00.
+    // Relocate our 2048 bytes boot sector to the given base address (should be 0x7000).
+    cld
+    mov cx, 2048 / 4
+    mov si, HEX(7C00)
+    mov di, offset start
+    rep movsd
+
+    ljmp16 0, relocated     // jump into relocated code
+
+.org 64
+hybrid_signature:
+    .long HEX(7078c0fb)
+
+// Entry point when booted through ISOMBR from a drive (isohybrid mode)
+start_hybrid:
+    mov bx, offset getlinsec_ebios
+    jmp start_common
+
+relocated:
+    // Save our passed variables (BX from the entry point, DL from the BIOS) before anybody clobbers the registers.
+    mov word ptr ds:[GetlinsecPtr], bx
+    mov byte ptr ds:[DriveNumber], dl
 
     // Make sure the keyboard buffer is empty
     call pollchar_and_empty
 
-    // Check for MBR on harddisk
-    pusha
+    // If we're booting in hybrid mode and our boot drive is the first HDD (drive 80h),
+    // we have no other option than booting into SETUPLDR.
+    cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
+    jne .read_mbr
+    cmp byte ptr ds:[DriveNumber], HEX(80)
+    je .boot_setupldr
+
+.read_mbr:
+    // Read the first sector (MBR) from the first hard disk (drive 80h) to 7C00h.
+    // If we then decide to boot from HDD, we already have it at the right place.
+    // In case of an error (indicated by the Carry Flag), just boot SETUPLDR from our ReactOS Medium.
     mov ax, HEX(0201)
     mov dx, HEX(0080)
     mov cx, HEX(0001)
-    mov bx, trackbuf
-    int HEX(13)
-    popa
-    jc .boot_cdrom          // could not read hdd
+    mov bx, HEX(7C00)
+    call int13
+    jc .boot_setupldr
 
-    push ax
-#ifdef ROS_REGTEST // this change is taken from the original isobtrt.asm
-    mov ax, word ptr ds:[trackbuf+510]
-#else
-    mov ax, word ptr ds:[trackbuf]
-#endif
-    cmp ax, 0
-    je .boot_cdrom // no boot sector found (hopefully there are no weird bootsectors which begin with 0)
-    pop ax
+    // Verify the signature of the read MBR.
+    // If it's invalid, there is probably no OS installed and we just boot SETUPLDR from our ReactOS Medium.
+    mov ax, word ptr ds:[HEX(7C00)+510]
+    cmp ax, HEX(AA55)
+    jne .boot_setupldr
 
 #ifdef WAIT_FOR_KEY
-    // Display the 'Press key' message and wait for a maximum of 5 seconds
-    call crlf
-    mov si, offset presskey_msg // si points to 'Press key' message
-    call writestr               // display the message
+    // We could either boot from the ReactOS Medium or from hard disk. Let the user decide!
+    // Display the 'Press key' message.
+    call crlf_early
+    mov si, offset presskey_msg
+    call writestr_early
+
+    // Count down 5 seconds.
+    mov cx, 5
 
-    mov byte ptr ds:[TimeoutCount], 5
 .next_second:
-    mov eax, ds:[BIOS_timer]        // load current tick counter
+    // Count in seconds using the BIOS Timer, which runs roughly at 19 ticks per second.
+    // Load its value plus one second into EAX for comparison later.
+    mov eax, ds:[BIOS_timer]
     add eax, 19
 
 .poll_again:
+    // Check for a keypress, boot SETUPLDR from our ReactOS Medium if a key was pressed.
     call pollchar_and_empty
-    jnz .boot_cdrom
+    jnz .boot_setupldr
 
+    // Check if another second has passed (in BIOS Timer ticks).
     mov ebx, ds:[BIOS_timer]
     cmp eax, ebx
     jnz .poll_again
 
-    mov si, offset dot_msg            // print '.'
-    call writestr
-    dec byte ptr ds:[TimeoutCount]        // decrement timeout counter
+    // Another second has passed, so print the dot and decrement the second counter.
+    // If the user hasn't pressed a key after the entire 5 seconds have elapsed, just boot from the first hard disk.
+    mov si, offset dot_msg
+    call writestr_early
+    dec cx
     jz .boot_harddisk
     jmp .next_second
 #endif
 
 .boot_harddisk:
-    call crlf
-
-    // Boot first harddisk (drive 0x80)
-    mov ax, HEX(0201)
-    mov dx, HEX(0080)
-    mov cx, HEX(0001)
-    mov bx, HEX(7C00)
-    int HEX(13)
-    jnc .go_hd
-    jmp kaboom
-.go_hd:
+    // Restore a clean context for the hard disk MBR and boot the already loaded MBR.
+    call crlf_early
     mov ax, cs
     mov ds, ax
     mov es, ax
@@ -200,336 +202,425 @@ relocate:
 
     ljmp16 0, HEX(7C00)
 
-.boot_cdrom:
+.boot_setupldr:
 #ifdef WAIT_FOR_KEY
-    call crlf
-    call crlf
+    call crlf_early
+    call crlf_early
 #endif
 
-    // Save and display the boot drive number
-    mov byte ptr ds:[DriveNo], dl
-#ifdef DEBUG_MESSAGES
-    mov si, offset startup_msg
-    call writemsg
-    mov al, dl
-    call writehex2
-    call crlf
-#endif
+    // The BIOS gave us a boot drive number, so in a perfect world we could just use that one now.
+    // Unfortunately, there are many broken BIOSes around, which is why ISOLINUX verifies it and applies some hacks if the number is wrong.
+    // Let's do exactly the same here to achieve maximum compatibility.
 
-    // Now figure out what we're actually doing
-    // Note: use passed-in DL value rather than 7Fh because
-    // at least some BIOSes will get the wrong value otherwise
-    mov ax, HEX(4B01)            // Get disk emulation status
-    mov dl, byte ptr ds:[DriveNo]
-    mov si, offset spec_packet
-    int HEX(13)
-    jc    spec_query_failed        // Shouldn't happen (BIOS bug)
-    mov dl, byte ptr ds:[DriveNo]
-    cmp byte ptr ds:[sp_drive], dl            // Should contain the drive number
-    jne    spec_query_failed
+    // Don't do this if we are running in hybrid mode.
+    cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
+    je found_drive
 
-#ifdef DEBUG_MESSAGES
-    mov si, offset spec_ok_msg
-    call writemsg
-    mov al, byte ptr ds:[sp_drive]
-    call writehex2
-    call crlf
-#endif
+    // Use the INT 13 function 4B01h (Get Disk Emulation Status) to fetch the El Torito Spec Packet.
+    // We can use this information to verify that our passed boot drive number really belongs to our CD.
+    mov ax, HEX(4B01)
+    mov dl, byte ptr ds:[DriveNumber]
+    mov si, offset spec_packet
+    call int13
 
-found_drive:
-    // Get drive information
-    mov ah, HEX(48)
-    mov dl, byte ptr ds:[DriveNo]
-    mov si, offset drive_params
-    int HEX(13)
-    jnc params_ok
-
-    // mov si, nosecsize_msg    No use in reporting this
-    // call writemsg
-
-params_ok:
-    // Check for the sector size (should be 2048, but
-    // some BIOSes apparently think we're 512-byte media)
-    //
-    // FIX: We need to check what the proper behaviour
-    // is for getlinsec when the BIOS thinks the sector
-    // size is 512!!!  For that, we need such a BIOS, though...
-#ifdef DEBUG_MESSAGES
-    mov si, offset secsize_msg
-    call writemsg
-    mov ax, word ptr ds:[dp_secsize]
-    call writehex4
-    call crlf
-#endif
+    // If this INT 13 function yields an error, we may be on a broken AWARD BIOS.
+    // Check this and patch if possible.
+    jc award_hack
 
+    // Check that our passed boot drive number and the number in the Spec Packet match.
+    // If not, try some workarounds to find our drive anyway.
+    mov dl, byte ptr ds:[DriveNumber]
+    cmp byte ptr ds:[sp_drive], dl
+    jne spec_query_failed
 
-    //
+found_drive:
     // Clear Files structures
-    //
     mov di, Files
     mov cx, (MAX_OPEN*open_file_t_size)/4
-    xor    eax, eax
-    rep    stosd
-
-    //
-    // Now, we need to sniff out the actual filesystem data structures.
-    // mkisofs gave us a pointer to the primary volume descriptor
-    // (which will be at 16 only for a single-session disk!); from the PVD
-    // we should be able to find the rest of what we need to know.
-    //
-get_fs_structures:
-    mov eax, 16            // Primary Volume Descriptor (sector 16)
+    xor eax, eax
+    rep stosd
+
+    // Read the entire 2K-sized ISO9660 Primary Volume Descriptor at sector 16 (32K).
+    // This calculation only holds for single-session ISOs, but we should never encounter anything else.
+    mov eax, 16
     mov bx, trackbuf
     call getonesec
 
+    // Read the LBA address (offset 2 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
     mov eax, dword ptr ds:[trackbuf+156+2]
-    mov dword ptr ds:[RootDir+dir_lba],eax
-    mov dword ptr ds:[CurDir+dir_lba],eax
-#ifdef DEBUG_MESSAGES
-    mov si, offset rootloc_msg
-    call writemsg
-    call writehex8
-    call crlf
-#endif
+    mov dword ptr ds:[RootDir+dir_lba], eax
+    mov dword ptr ds:[CurrentDir+dir_lba], eax
 
+    // Read the data length (offset 10 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
     mov eax, dword ptr ds:[trackbuf+156+10]
-    mov dword ptr ds:[RootDir+dir_len],eax
-    mov dword ptr ds:[CurDir+dir_len],eax
-#ifdef DEBUG_MESSAGES
-    mov si, offset rootlen_msg
-    call writemsg
-    call writehex8
-    call crlf
-#endif
-    add    eax,SECTORSIZE-1
-    shr    eax,SECTORSIZE_LG2
+    mov dword ptr ds:[RootDir+dir_len], eax
+    mov dword ptr ds:[CurrentDir+dir_len], eax
+
+    // Calculate the number of clusters and write that to our RootDir and CurrentDir structures.
+    add eax, SECTOR_SIZE-1
+    shr eax, SECTOR_SHIFT
     mov dword ptr ds:[RootDir+dir_clust],eax
-    mov dword ptr ds:[CurDir+dir_clust],eax
-#ifdef DEBUG_MESSAGES
-    mov si, offset rootsect_msg
-    call writemsg
-    call writehex8
-    call crlf
-#endif
+    mov dword ptr ds:[CurrentDir+dir_clust],eax
 
-    // Look for the "REACTOS" directory, and if found,
-    // make it the current directory instead of the root
-    // directory.
-    mov di, offset isolinux_dir
-    mov al, 2                // Search for a directory
+    // Look for the "LOADER" directory (directory is indicated by AL = 2 when using searchdir_iso).
+    mov di, offset loader_dir
+    mov al, 2
     call searchdir_iso
     jnz .dir_found
+
+    // No directory was found, so bail out with an error message.
     mov si, offset no_dir_msg
     call writemsg
-    jmp    kaboom
+    jmp kaboom
 
 .dir_found:
-    mov dword ptr ds:[CurDir+dir_len],eax
+    // The directory was found, so update the information in our CurrentDir structure.
+    // Free the file pointer entry at SI in the process.
+    mov dword ptr ds:[CurrentDir+dir_len], eax
     mov eax, dword ptr ds:[si+file_left]
-    mov dword ptr ds:[CurDir+dir_clust],eax
-    xor    eax,eax                // Free this file pointer entry
-    xchg    eax,dword ptr ds:[si+file_sector]
-    mov dword ptr ds:[CurDir+dir_lba],eax
+    mov dword ptr ds:[CurrentDir+dir_clust], eax
+    xor eax, eax
+    xchg eax, dword ptr ds:[si+file_sector]
+    mov dword ptr ds:[CurrentDir+dir_lba], eax
+
+    // Look for the "SETUPLDR.SYS" file.
+    mov di, offset setupldr_sys
+    call searchdir
+    jnz .setupldr_found
+
+    // The SETUPLDR file was not found, so bail out with an error message.
+    mov si, offset no_setupldr_msg
+    call writemsg
+    jmp kaboom
+
+.setupldr_found:
+    // Calculate the rounded up number of 2K sectors that need to be read.
+    mov ecx, eax
+    shr ecx, SECTOR_SHIFT
+    test eax, HEX(7FF)
+    jz .load_setupldr
+    inc ecx
+
+.load_setupldr:
+    // Load the entire SETUPLDR.SYS (parameter CX = FFFFh) to its designated base address FREELDR_BASE.
+    // Using a high segment address with offset 0 instead of segment 0 with offset FREELDR_BASE apparently increases compatibility with some BIOSes.
+    mov bx, FREELDR_BASE / 16
+    mov es, bx
+    xor ebx, ebx
+    mov cx, HEX(FFFF)
+    call getfssec
+
+    // Fetch our stored drive number to DL and set the boot partition to 0 in DH.
+    mov dl, byte ptr ds:[DriveNumber]
+    mov dh, 0
+
+    // Transfer execution to the bootloader.
+    ljmp16 0, FREELDR_BASE
+
+
+/* FUNCTIONS *****************************************************************/
+
+///////////////////////////////////////////////////////////////////////////////
+// Start of BrokenAwardHack --- 10-nov-2002           Knut_Petersen@t-online.de
+///////////////////////////////////////////////////////////////////////////////
+//
+// There is a problem with certain versions of the AWARD BIOS ...
+// the boot sector will be loaded and executed correctly, but, because the
+// int 13 vector points to the wrong code in the BIOS, every attempt to
+// load the spec packet will fail. We scan for the equivalent of
+//
+//     mov ax,0201h
+//     mov bx,7c00h
+//     mov cx,0006h
+//     mov dx,0180h
+//     pushf
+//     call <direct far>
+//
+// and use <direct far> as the new vector for int 13. The code above is
+// used to load the boot code into ram, and there should be no reason
+// for anybody to change it now or in the future. There are no opcodes
+// that use encodings relativ to IP, so scanning is easy. If we find the
+// code above in the BIOS code we can be pretty sure to run on a machine
+// with an broken AWARD BIOS ...
+//
+///////////////////////////////////////////////////////////////////////////////
+award_oldint13:
+    .long 0
+award_string:
+    .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a)
+
+award_hack:
+    mov si, offset spec_err_msg                         // Moved to this place from
+    call writemsg                                       // spec_query_failed
+
+    mov eax, dword ptr ds:[HEX(13)*4]
+    mov dword ptr ds:[award_oldint13], eax
+
+    push es
+    mov ax, HEX(F000)                                   // ES = BIOS Seg
+    mov es, ax
+    cld
+    xor di, di                                          // start at ES:DI = f000:0
+award_loop:
+    push di                                             // save DI
+    mov si, offset award_string                         // scan for award_string
+    mov cx, 7                                           // length of award_string = 7dw
+    repz cmpsw                                          // compare
+    pop di                                              // restore DI
+    jcxz award_found                                    // jmp if found
+    inc di                                              // not found, inc di
+    jno award_loop
+
+award_failed:
+    pop es                                              // No, not this way :-((
+award_fail2:
+    mov eax, dword ptr ds:[award_oldint13]              // restore the original int
+    or eax, eax                                         // 13 vector if there is one
+    jz spec_query_failed                                // and try other workarounds
+    mov dword ptr ds:[HEX(13)*4], eax
+    jmp spec_query_failed
+
+award_found:
+    mov eax, dword ptr es:[di+HEX(0e)]                  // load possible int 13 addr
+    pop es                                              // restore ES
+
+    cmp eax, dword ptr ds:[award_oldint13]              // give up if this is the
+    jz award_failed                                     // active int 13 vector,
+    mov dword ptr ds:[HEX(13)*4], eax                   // otherwise change 0:13h*4
+
+    mov ax, HEX(4B01)                                   // try to read the spec packet
+    mov dl, byte ptr ds:[DriveNumber]                   // now ... it should not fail
+    mov si, offset spec_packet                          // any longer
+    int HEX(13)
+    jc award_fail2
+
+    jmp found_drive                                     // and leave error recovery code
+///////////////////////////////////////////////////////////////////////////////
+// End of BrokenAwardHack ----            10-nov-2002 Knut_Petersen@t-online.de
+///////////////////////////////////////////////////////////////////////////////
+
 
+// INT 13h, AX=4B01h, DL=<passed in value> failed.
+// Try to scan the entire 80h-FFh from the end.
+spec_query_failed:
+    // some code moved to BrokenAwardHack
 
-    mov di, offset isolinux_bin        // di points to Isolinux filename
-    call searchdir            // look for the file
-    jnz .isolinux_opened        // got the file
-    mov si, offset no_isolinux_msg        // si points to error message
-    call writemsg            // display the message
-    jmp    kaboom                // fail boot
+    mov dl, HEX(FF)
 
-.isolinux_opened:
-    mov di, si                // save file pointer
+.test_loop:
+    pusha
+    mov ax, HEX(4B01)
+    mov si, offset spec_packet
+    mov byte ptr ds:[si], HEX(13)                       // Size of buffer
+    call int13
+    popa
+    jc .still_broken
 
-#ifdef DEBUG_MESSAGES
-    mov si, offset filelen_msg
+    mov si, offset maybe_msg
     call writemsg
-    call writehex8
-    call crlf
-#endif
+    mov al, dl
+    call writehex2
+    call crlf_early
 
-    mov ecx, eax            // calculate sector count
-    shr ecx, 11
-    test eax, HEX(7FF)
-    jz .full_sector
-    inc ecx
-.full_sector:
+    cmp byte ptr ds:[sp_drive], dl
+    jne .maybe_broken
 
-#ifdef DEBUG_MESSAGES
-    mov eax, ecx
-    mov si, offset filesect_msg
+    // Okay, good enough...
+    mov si, offset alright_msg
     call writemsg
-    call writehex8
-    call crlf
-#endif
+.found_drive0:
+    mov byte ptr ds:[DriveNumber], dl
+.found_drive:
+    jmp found_drive
 
-// use high segment, as some bios can fail, when offset is too big
-    mov bx, FREELDR_BASE / 16 // es = load segment
-    mov es, bx
-    xor ebx, ebx              // bx = load offset
-    mov si, di                // restore file pointer
-    mov cx, HEX(0FFFF)        // load the whole file
-    call getfssec             // get the whole file
+    // Award BIOS 4.51 apparently passes garbage in sp_drive,
+    // but if this was the drive number originally passed in
+    // DL then consider it "good enough"
+.maybe_broken:
+    mov al, byte ptr ds:[DriveNumber]
+    cmp al, dl
+    je .found_drive
 
-#ifdef DEBUG_MESSAGES
-    mov si, offset startldr_msg
+    // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
+    // passes garbage in sp_drive, and the drive number originally
+    // passed in DL does not have 80h bit set.
+    or al, HEX(80)
+    cmp al, dl
+    je .found_drive0
+
+.still_broken:
+    dec dx
+    cmp dl, HEX(80)
+    jnb .test_loop
+
+    // No spec packet anywhere.  Some particularly pathetic
+    // BIOSes apparently don't even implement function
+    // 4B01h, so we can't query a spec packet no matter
+    // what.  If we got a drive number in DL, then try to
+    // use it, and if it works, then well...
+    mov dl, byte ptr ds:[DriveNumber]
+    cmp dl, HEX(81)                                     // Should be 81-FF at least
+    jb fatal_error                                      // If not, it's hopeless
+
+    // Write a warning to indicate we're on *very* thin ice now
+    mov si, offset nospec_msg
     call writemsg
-    call crlf
-#endif
+    mov al, dl
+    call writehex2
+    call crlf_early
+    jmp .found_drive                                    // Pray that this works...
 
-    mov dl, byte ptr ds:[DriveNo]            // dl = boot drive
-    mov dh, 0                    // dh = boot partition
+fatal_error:
+    mov si, offset nothing_msg
+    call writemsg
 
-    /* Transfer execution to the bootloader */
-    ljmp16 0, FREELDR_BASE
+.norge:
+    jmp short .norge
 
 //
 // searchdir:
 //
-// Open a file
+//      Open a file
 //
-//  On entry:
-//    DS:DI    = filename
-//  If successful:
-//    ZF clear
-//    SI        = file pointer
-//    DX:AX or EAX    = file length in bytes
-//  If unsuccessful
-//    ZF set
+//          On entry:
+//              DS:DI   = filename
+//          If successful:
+//              ZF clear
+//              SI      = file pointer
+//              EAX     = file length in bytes
+//          If unsuccessful
+//              ZF set
 //
-
+// Assumes CS == DS == ES, and trashes BX and CX.
 //
 // searchdir_iso is a special entry point for ISOLINUX only.  In addition
 // to the above, searchdir_iso passes a file flag mask in AL.  This is useful
 // for searching for directories.
 //
 alloc_failure:
-    xor    ax,ax                // ZF <- 1
+    xor ax, ax                                  // ZF <- 1
     ret
 
 searchdir:
-    xor    al, al
+    xor al, al
 searchdir_iso:
-    mov byte ptr ds:[ISOFlags],al
-    call allocate_file            // Temporary file structure for directory
+    mov byte ptr ds:[ISOFlags], al
+    call allocate_file                          // Temporary file structure for directory
     jnz alloc_failure
-    push    es
-    push    ds
-    pop    es                // ES = DS
-    mov si, offset CurDir
-    cmp byte ptr ds:[di], 92 //'\'            // If filename begins with slash
-    jne    .not_rooted
-    inc    di                // Skip leading slash
-    mov si, offset RootDir            // Reference root directory instead
+    push es
+    push ds
+    pop es                                      // ES = DS
+    mov si, offset CurrentDir
+    cmp byte ptr ds:[di], '/'                   // If filename begins with slash
+    jne .not_rooted
+    inc di                                      // Skip leading slash
+    mov si, offset RootDir                      // Reference root directory instead
 .not_rooted:
     mov eax, dword ptr ds:[si+dir_clust]
-    mov dword ptr ds:[bx+file_left],eax
-    mov eax,dword ptr ds:[si+dir_lba]
-    mov dword ptr ds:[bx+file_sector],eax
-    mov edx,dword ptr ds:[si+dir_len]
+    mov dword ptr ds:[bx+file_left], eax
+    shl eax, SECTOR_SHIFT
+    mov dword ptr ds:[bx+file_bytesleft], eax
+    mov eax, dword ptr ds:[si+dir_lba]
+    mov dword ptr ds:[bx+file_sector], eax
+    mov edx, dword ptr ds:[si+dir_len]
 
 .look_for_slash:
-    mov ax,di
+    mov ax, di
 .scan:
     mov cl, byte ptr ds:[di]
-    inc    di
-    and    cl,cl
-    jz    .isfile
-    cmp cl, 92 // '\'
-    jne    .scan
-    mov byte ptr ds:[di-1], 0            // Terminate at directory name
-    mov cl,2                // Search for directory
-    xchg    cl, byte ptr ds:[ISOFlags]
-    push    di
-    push    cx
-    push    offset .resume            // Where to "return" to
-    push    es
+    inc di
+    and cl, cl
+    jz .isfile
+    cmp cl, '/'
+    jne .scan
+    mov byte ptr ds:[di-1], 0                   // Terminate at directory name
+    mov cl, 2                                   // Search for directory
+    xchg cl, byte ptr ds:[ISOFlags]
+
+    push di                                     // Save these...
+    push cx
+
+    // Create recursion stack frame...
+    push offset .resume                         // Where to "return" to
+    push es
 .isfile:
-    xchg    ax,di
+    xchg ax, di
 
 .getsome:
     // Get a chunk of the directory
-    mov si,trackbuf
+    // This relies on the fact that ISOLINUX doesn't change SI
+    mov si, trackbuf
     pushad
-    xchg    bx,si
-    mov cx,1                // load one sector
+    xchg bx, si
+    mov cx, word ptr ds:[BufSafe]
     call getfssec
     popad
 
 .compare:
-    movzx    eax, byte ptr ds:[si]            // Length of directory entry
+    movzx eax, byte ptr ds:[si]                 // Length of directory entry
     cmp al, 33
-    jb    .next_sector
+    jb .next_sector
     mov cl, byte ptr ds:[si+25]
-    xor    cl, byte ptr ds:[ISOFlags]
-    test    cl, HEX(8E)            // Unwanted file attributes!
+    xor cl, byte ptr ds:[ISOFlags]
+    test cl, HEX(8E)                            // Unwanted file attributes!
     jnz .not_file
     pusha
-    movzx    cx, byte ptr ds:[si+32]        // File identifier length
-    add    si, 33            // File identifier offset
+    movzx cx, byte ptr ds:[si+32]               // File identifier length
+    add si, 33                                  // File identifier offset
     call iso_compare_names
     popa
-    je    .success
+    je .success
 .not_file:
-    sub    edx, eax            // Decrease bytes left
-    jbe    .failure
-    add    si, ax                // Advance pointer
+    sub edx, eax                                // Decrease bytes left
+    jbe .failure
+    add si, ax                                  // Advance pointer
 
 .check_overrun:
     // Did we finish the buffer?
     cmp si, trackbuf+trackbufsize
-    jb    .compare            // No, keep going
+    jb .compare                                 // No, keep going
 
-    jmp    .getsome            // Get some more directory
+    jmp short .getsome                          // Get some more directory
 
 .next_sector:
     // Advance to the beginning of next sector
-    lea    ax, [si+SECTORSIZE-1]
-    and    ax, not (SECTORSIZE-1)
-    sub    ax, si
-    jmp    .not_file            // We still need to do length checks
+    lea ax, [si+SECTOR_SIZE-1]
+    and ax, not (SECTOR_SIZE-1)
+    sub ax, si
+    jmp short .not_file                         // We still need to do length checks
 
 .failure:
-#ifdef DEBUG_MESSAGES
-    mov si, offset findfail_msg
-    call writemsg
-    call crlf
-#endif
-    xor    eax, eax            // ZF = 1
+    xor eax, eax                                // ZF = 1
     mov dword ptr ds:[bx+file_sector], eax
-    pop    es
+    pop es
     ret
 
 .success:
-    mov eax, dword ptr ds:[si+2]            // Location of extent
+    mov eax, dword ptr ds:[si+2]                // Location of extent
     mov dword ptr ds:[bx+file_sector], eax
-    mov eax, dword ptr ds:[si+10]            // Data length
-    push    eax
-    add    eax, SECTORSIZE-1
-    shr    eax, SECTORSIZE_LG2
+    mov eax, dword ptr ds:[si+10]               // Data length
+    mov dword ptr ds:[bx+file_bytesleft], eax
+    push eax
+    add eax, SECTOR_SIZE-1
+    shr eax, SECTOR_SHIFT
     mov dword ptr ds:[bx+file_left], eax
-    pop    eax
-    mov edx, eax
-    shr    edx, 16
-    and    bx, bx                // ZF = 0
+    pop eax
+    jz .failure                                 // Empty file?
+    // ZF = 0
     mov si, bx
-    pop    es
+    pop es
     ret
 
 .resume:
     // We get here if we were only doing part of a lookup
     // This relies on the fact that .success returns bx == si
-    xchg    edx, eax            // Directory length in edx
-    pop    cx                // Old ISOFlags
-    pop    di                // Next filename pointer
-
-    // restore the backslash in the filename
-    mov byte ptr ds:[di-1], 92 // '\'
-
-    mov byte ptr ds:[ISOFlags], cl            // Restore the flags
-    jz    .failure            // Did we fail?  If so fail for real!
-    jmp    .look_for_slash            // Otherwise, next level
+    xchg edx, eax                               // Directory length in edx
+    pop cx                                      // Old ISOFlags
+    pop di                                      // Next filename pointer
+    mov byte ptr ds:[di-1], '/'                 // Restore slash
+    mov byte ptr ds:[ISOFlags], cl              // Restore the flags
+    jz .failure                                 // Did we fail?  If so fail for real!
+    jmp .look_for_slash                         // Otherwise, next level
 
 //
 // allocate_file: Allocate a file structure
@@ -546,12 +637,12 @@ allocate_file:
     mov cx, MAX_OPEN
 .check:
     cmp dword ptr ds:[bx], 0
-    je    .found
-    add    bx, open_file_t_size        // ZF = 0
-    loop    .check
+    je .found
+    add bx, open_file_t_size                    // ZF = 0
+    loop .check
     // ZF = 0 if we fell out of the loop
 .found:
-    pop    cx
+    pop cx
     ret
 
 //
@@ -565,235 +656,169 @@ allocate_file:
 //
 iso_compare_names:
     // First, terminate and canonicalize input filename
-    push    di
+    push di
     mov di, offset ISOFileName
 .canon_loop:
-    jcxz    .canon_end
+    jcxz .canon_end
     lodsb
-    dec    cx
+    dec cx
     cmp al, ';'
-    je    .canon_end
-    and    al, al
-    je    .canon_end
+    je .canon_end
+    and al, al
+    je .canon_end
     stosb
-    cmp di, offset ISOFileNameEnd-1        // Guard against buffer overrun
-    jb    .canon_loop
+    cmp di, offset ISOFileNameEnd-1             // Guard against buffer overrun
+    jb .canon_loop
 .canon_end:
     cmp di, ISOFileName
-    jbe    .canon_done
-    cmp byte ptr ds:[di-1], '.'        // Remove terminal dots
-    jne    .canon_done
-    dec    di
-    jmp    short .canon_end
+    jbe .canon_done
+    cmp byte ptr ds:[di-1], '.'                 // Remove terminal dots
+    jne .canon_done
+    dec di
+    jmp short .canon_end
 .canon_done:
-    mov byte ptr ds:[di], 0            // Null-terminate string
-    pop    di
+    mov byte ptr ds:[di], 0                     // Null-terminate string
+    pop di
     mov si, ISOFileName
 .compare2:
     lodsb
     mov ah, byte ptr ds:[di]
-    inc    di
-    and    ax, ax
-    jz    .success2            // End of string for both
-    and    al, al                // Is either one end of string?
-    jz    .failure2            // If so, failure
-    and    ah, ah
-    jz    .failure2
-    or    ax, HEX(2020)            // Convert to lower case
+    inc di
+    and ax, ax
+    jz .success2                                // End of string for both
+    and al, al                                  // Is either one end of string?
+    jz .failure2                                // If so, failure
+    and ah, ah
+    jz .failure2
+    or ax, HEX(2020)                            // Convert to lower case
     cmp al, ah
-    je    .compare2
+    je .compare2
 .failure2:
-    and    ax, ax                // ZF = 0 (at least one will be nonzero)
+    and ax, ax                                  // ZF = 0 (at least one will be nonzero)
 .success2:
     ret
 
-
-
-
-
-
-
 //
 // getfssec: Get multiple clusters from a file, given the file pointer.
 //
 //  On entry:
-//    ES:BX    -> Buffer
-//    SI    -> File pointer
-//    CX    -> Cluster count; 0FFFFh = until end of file
+//       ES:BX   -> Buffer
+//       SI      -> File pointer
+//       CX      -> Cluster count
 //  On exit:
-//    SI    -> File pointer (or 0 on EOF)
-//    CF = 1    -> Hit EOF
+//       SI      -> File pointer (or 0 on EOF)
+//       CF = 1  -> Hit EOF
+//       ECX     -> Bytes actually read
 //
 getfssec:
-    cmp cx, word ptr ds:[si+file_left]
-    jna    .ok_size
-    mov cx, word ptr ds:[si+file_left]
-
+    push ds
+    push cs
+    pop ds                                      // DS <- CS
+
+    movzx ecx, cx
+    cmp ecx, dword ptr ds:[si+file_left]
+    jna .ok_size
+    mov ecx, dword ptr ds:[si+file_left]
 .ok_size:
-    mov bp, cx
-    push    cx
-    push    si
+    pushad
     mov eax, dword ptr ds:[si+file_sector]
+    mov bp, cx
     call getlinsec
-    xor    ecx, ecx
-    pop    si
-    pop    cx
-
-    add    dword ptr ds:[si+file_sector], ecx
-    sub    dword ptr ds:[si+file_left], ecx
-    ja    .not_eof            // CF = 0
+    popad
 
-    xor    ecx, ecx
-    mov dword ptr ds:[si+file_sector], ecx        // Mark as unused
-    xor    si,si
+    // ECX[31:16] == 0 here...
+    add dword ptr ds:[si+file_sector], ecx
+    sub dword ptr ds:[si+file_left], ecx
+    shl ecx, SECTOR_SHIFT                       // Convert to bytes
+    cmp ecx, dword ptr ds:[si+file_bytesleft]
+    jb .not_all
+    mov ecx, dword ptr ds:[si+file_bytesleft]
+.not_all:
+    sub dword ptr ds:[si+file_bytesleft], ecx
+    jnz .ret                                    // CF = 0 in this case...
+    push eax
+    xor eax, eax
+    mov dword ptr ds:[si+file_sector], eax      // Unused
+    mov si, ax
+    pop eax
     stc
-
-.not_eof:
+.ret:
+    pop ds
     ret
 
-
-// INT 13h, AX=4B01h, DL=<passed in value> failed.
-// Try to scan the entire 80h-FFh from the end.
-spec_query_failed:
-    mov si, offset spec_err_msg
-    call writemsg
-
-    mov dl, HEX(0FF)
-.test_loop:
-    pusha
-    mov ax, HEX(4B01)
-    mov si, offset spec_packet
-    mov byte ptr ds:[si], 13            // Size of buffer
-    int HEX(13)
-    popa
-    jc    .still_broken
-
-    mov si, offset maybe_msg
-    call writemsg
-    mov al, dl
-    call writehex2
-    call crlf
-
-    cmp byte ptr ds:[sp_drive], dl
-    jne    .maybe_broken
-
-    // Okay, good enough...
-    mov si, offset alright_msg
-    call writemsg
-    mov byte ptr ds:[DriveNo], dl
-.found_drive:
-    jmp    found_drive
-
-    // Award BIOS 4.51 apparently passes garbage in sp_drive,
-    // but if this was the drive number originally passed in
-    // DL then consider it "good enough"
-.maybe_broken:
-    cmp byte ptr ds:[DriveNo], dl
-    je .found_drive
-
-.still_broken:
-    dec dx
-    cmp dl, HEX(80)
-    jnb    .test_loop
-
-fatal_error:
-    mov si, offset nothing_msg
-    call writemsg
-
-.norge:
-    jmp    .norge
-
-
-
+//
 // Information message (DS:SI) output
-// Prefix with "isolinux: "
+// Prefix with "ISOBOOT: "
+//
 writemsg:
-    push    ax
-    push    si
-    mov si, offset isolinux_str
-    call writestr
-    pop    si
-    call writestr
-    pop    ax
+    push ax
+    push si
+    mov si, offset isoboot_str
+    call writestr_early
+    pop si
+    call writestr_early
+    pop ax
     ret
 
-//
-// crlf: Print a newline
-crlf:
-    mov si, offset crlf_msg
-    // Fall through
-
-//
-// writestr: write a null-terminated string to the console, saving
-//           registers on entry.
-//
-writestr:
+writestr_early:
     pushfd
     pushad
-writestr_top:
+.top:
     lodsb
-    and    al, al
-    jz    writestr_end
+    and al, al
+    jz .end_writestr
     call writechr
-    jmp    short writestr_top
-writestr_end:
+    jmp short .top
+.end_writestr:
     popad
     popfd
     ret
 
-//
-// writehex[248]: Write a hex number in (AL, AX, EAX) to the console
-//
-writehex2:
-    pushfd
-    pushad
-    shl    eax, 24
-    mov cx, 2
-    jmp    short writehex_common
-writehex4:
-    pushfd
-    pushad
-    shl    eax, 16
-    mov cx, 4
-    jmp    short writehex_common
-writehex8:
-    pushfd
-    pushad
-    mov cx, 8
-writehex_common:
-.loop:
-    rol    eax, 4
-    push    eax
-    and    al, HEX(0F)
-    cmp al, 10
-    jae    .high
-.low:
-    add    al, '0'
-    jmp    short .ischar
-.high:
-    add    al, 'A'-10
-.ischar:
+crlf_early:
+    push ax
+    mov al, 13
     call writechr
-    pop    eax
-    loop    .loop
-    popad
-    popfd
+    mov al, 10
+    call writechr
+    pop ax
     ret
 
 //
-// writechr: Write a character to the screen.  There is a more "sophisticated"
-// version of this in the subsequent code, so we patch the pointer
-// when appropriate.
+// writechr: Write a character to the screen.
+//
 writechr:
     pushfd
     pushad
     mov ah, HEX(0E)
-    xor    bx, bx
+    xor bx, bx
     int HEX(10)
     popad
     popfd
     ret
 
+//
+// int13: save all the segment registers and call INT 13h.
+// Some CD-ROM BIOSes have been found to corrupt segment registers
+// and/or disable interrupts.
+//
+int13:
+    pushf
+    push bp
+    push ds
+    push es
+    push fs
+    push gs
+    int HEX(13)
+    mov bp, sp
+    setc byte ptr ds:[bp+10]                    // Propagate CF to the caller
+    pop gs
+    pop fs
+    pop es
+    pop ds
+    pop bp
+    popf
+    ret
+
 //
 // Get one sector.  Convenience entry point.
 //
@@ -804,48 +829,113 @@ getonesec:
 //
 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
 //
-// Note that we can't always do this as a single request, because at least
-// Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick
-// to 32 sectors (64K) per request.
+getlinsec:
+    jmp word ptr cs:[GetlinsecPtr]
+
 //
-// Input:
-//    EAX    - Linear sector number
-//    ES:BX    - Target buffer
-//    BP    - Sector count
+// getlinsec_ebios:
 //
-getlinsec:
-    push es // save es, we reset it later to 0
+// getlinsec implementation for floppy/HDD EBIOS (EDD)
+//
+getlinsec_ebios:
+    xor edx, edx
+    shld edx, eax, 2
+    shl eax, 2                                  // Convert to HDD sectors
+    shl bp, 2
+
+.loop_ebios:
+    push bp                                     // Sectors left
+.retry2:
+    call maxtrans                               // Enforce maximum transfer size
+    movzx edi, bp                               // Sectors we are about to read
+    mov cx, retry_count
+.retry:
+    // Form DAPA on stack
+    push edx
+    push eax
+    push es
+    push bx
+    push di
+    push 16
+    mov si, sp
+    pushad
+    mov dl, byte ptr ds:[DriveNumber]
+    push ds
+    push ss
+    pop ds                                      // DS <- SS
+    mov ah, HEX(42)                             // Extended Read
+    call int13
+    pop ds
+    popad
+    lea sp, [si+16]                             // Remove DAPA
+    jc .error_ebios
+    pop bp
+    add eax, edi                                // Advance sector pointer
+    adc edx, 0
+    sub bp, di                                  // Sectors left
+    shl di, 9                                   // 512-byte sectors
+    add bx, di                                  // Advance buffer pointer
+    jnc .no_overflow                            // Check if we have read more than 64K and need to adjust ES
+    mov di, es
+    add di, HEX(1000)                           // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1)
+    mov es, di
+.no_overflow:
+    and bp, bp
+    jnz .loop_ebios
+
+    ret
+
+.error_ebios:
+    pushad                                      // Try resetting the device
+    xor ax, ax
+    mov dl, byte ptr ds:[DriveNumber]
+    call int13
+    popad
+    loop .retry                                 // CX-- and jump if not zero
+
+    // Total failure.
+    jmp kaboom
 
-    mov si, offset dapa            // Load up the DAPA
+//
+// Truncate BP to MaxTransfer
+//
+maxtrans:
+    cmp bp, word ptr ds:[MaxTransfer]
+    jna .ok
+    mov bp, word ptr ds:[MaxTransfer]
+.ok:
+    ret
+
+//
+// This is the variant we use for real CD-ROMs:
+// LBA, 2K sectors, some special error handling.
+//
+getlinsec_cdrom:
+    mov si, offset dapa                         // Load up the DAPA
     mov word ptr ds:[si+4], bx
-    mov bx, es
-    mov word ptr ds:[si+6], bx
-    xor bx, bx // reset es to 0, some bioses (KVM) require that
-    mov es, bx
+    mov word ptr ds:[si+6], es
     mov dword ptr ds:[si+8], eax
-.loop2:
-    push bp                // Sectors left
-    cmp bp, word ptr ds:[MaxTransfer]
+.loop_cdrom:
+    push bp                                     // Sectors left
+    cmp bp, word ptr ds:[MaxTransferCD]
     jbe .bp_ok
-    mov bp, word ptr ds:[MaxTransfer]
+    mov bp, word ptr ds:[MaxTransferCD]
 .bp_ok:
     mov word ptr ds:[si+2], bp
     push si
-    mov dl, byte ptr ds:[DriveNo]
-    mov ah, HEX(42)            // Extended Read
+    mov dl, byte ptr ds:[DriveNumber]
+    mov ah, HEX(42)                             // Extended Read
     call xint13
     pop si
     pop bp
-    movzx eax,word ptr ds:[si+2]        // Sectors we read
-    add dword ptr ds:[si+8], eax        // Advance sector pointer
-    sub bp, ax            // Sectors left
-    shl ax, SECTORSIZE_LG2-4        // 2048-byte sectors -> segment
-    add word ptr ds:[si+6], ax            // Advance buffer pointer
+    movzx eax, word ptr ds:[si+2]               // Sectors we read
+    add dword ptr ds:[si+8], eax                // Advance sector pointer
+    sub bp, ax                                  // Sectors left
+    shl ax, SECTOR_SHIFT-4                      // 2048-byte sectors -> segment
+    add word ptr ds:[si+6], ax                  // Advance buffer pointer
     and bp, bp
-    jnz .loop2
-    mov eax, dword ptr ds:[si+8]            // Next sector
-
-    pop es
+    jnz .loop_cdrom
+    mov eax, dword ptr ds:[si+8]                // Next sector
     ret
 
     // INT 13h with retry
@@ -853,30 +943,31 @@ xint13:
     mov byte ptr ds:[RetryCount], retry_count
 .try:
     pushad
-    int HEX(13)
-    jc    .error
-    add    sp, 8*4            // Clean up stack
+    call int13
+    jc .error_cdrom
+    add sp, 8*4                                 // Clean up stack
     ret
-.error:
-    mov byte ptr ds:[DiskError], ah        // Save error code
+.error_cdrom:
+    mov byte ptr ds:[DiskError], ah             // Save error code
     popad
+    mov word ptr ds:[DiskSys], ax               // Save system call number
     dec byte ptr ds:[RetryCount]
     jz .real_error
     push ax
     mov al, byte ptr ds:[RetryCount]
-    mov ah, byte ptr ds:[dapa+2]            // Sector transfer count
-    cmp al,2            // Only 2 attempts left
+    mov ah, byte ptr ds:[dapa+2]                // Sector transfer count
+    cmp al, 2                                   // Only 2 attempts left
     ja .nodanger
-    mov ah,1            // Drop transfer size to 1
+    mov ah, 1                                   // Drop transfer size to 1
     jmp short .setsize
 .nodanger:
     cmp al, retry_count-2
-    ja .again            // First time, just try again
-    shr ah,1            // Otherwise, try to reduce
-    adc ah,0            // the max transfer size, but not to 0
+    ja .again                                   // First time, just try again
+    shr ah, 1                                   // Otherwise, try to reduce
+    adc ah, 0                                   // the max transfer size, but not to 0
 .setsize:
-    mov byte ptr ds:[MaxTransfer],ah
-    mov byte ptr ds:[dapa+2],ah
+    mov byte ptr ds:[MaxTransferCD], ah
+    mov byte ptr ds:[dapa+2], ah
 .again:
     pop ax
     jmp .try
@@ -886,11 +977,15 @@ xint13:
     call writemsg
     mov al, byte ptr ds:[DiskError]
     call writehex2
+    mov si, offset oncall_str
+    call writestr_early
+    mov ax, word ptr ds:[DiskSys]
+    call writehex4
     mov si, offset ondrive_str
-    call writestr
+    call writestr_early
     mov al, dl
     call writehex2
-    call crlf
+    call crlf_early
     // Fall through to kaboom
 
 //
@@ -898,187 +993,174 @@ xint13:
 //      then do a hard reboot.
 //
 kaboom:
+    // Restore a clean context.
     mov ax, cs
     mov ds, ax
     mov es, ax
     mov fs, ax
     mov gs, ax
     sti
+
+    // Display the failure message.
     mov si, offset err_bootfailed
-    call writestr
-    xor    ax, ax        // Wait for keypress
+    call writestr_early
+
+    // Wait for a keypress.
+    xor ax, ax
     int HEX(16)
+
+    // Disable interrupts and reset the system through a magic BIOS call.
     cli
-    mov word ptr ds:[BIOS_magic], 0    // Cold reboot
-    ljmp16 HEX(0F000), HEX(0FFF0)        // Reset vector address
+    mov word ptr ds:[BIOS_magic], 0
+    ljmp16 HEX(0F000), HEX(0FFF0)
 
+//
+// writehex[248]: Write a hex number in (AL, AX, EAX) to the console
+//
+writehex2:
+    pushfd
+    pushad
+    rol eax, 24
+    mov cx,2
+    jmp short writehex_common
+writehex4:
+    pushfd
+    pushad
+    rol eax, 16
+    mov cx, 4
+    jmp short writehex_common
+writehex8:
+    pushfd
+    pushad
+    mov cx, 8
+writehex_common:
+.loop_writehex:
+    rol eax, 4
+    push eax
+    and al, HEX(0F)
+    cmp al, 10
+    jae .high
+.low:
+    add al, '0'
+    jmp short .ischar
+.high:
+    add al, 'A'-10
+.ischar:
+    call writechr
+    pop eax
+    loop .loop_writehex
+    popad
+    popfd
+    ret
 
 //
-// pollchar_and_empty: check if we have an input character pending (ZF = 0) and empty the input buffer afterwards
+// pollchar_and_empty: Check if we have an input character pending (ZF = 0)
+// and empty the input buffer afterwards.
 //
 pollchar_and_empty:
     pushad
-    mov ah, 1        // Did the user press a key?
+    mov ah, 1                                   // Did the user press a key?
     int HEX(16)
-    jz    .end        // No, then we're done
-    mov ah, 0        // Otherwise empty the buffer by reading it
+    jz .end_pollchar                            // No, then we're done
+    mov ah, 0                                   // Otherwise empty the buffer by reading it
     int HEX(16)
-.end:
+.end_pollchar:
     popad
     ret
 
 
-isolinux_banner:
-    .ascii CR, LF, "Loading IsoBoot...", CR, LF, NUL
-copyright_str:
-    .ascii " (C) 1994-2002 H. Peter Anvin", CR, LF, NUL
+/* INITIALIZED VARIABLES *****************************************************/
 presskey_msg:
-    .ascii "Press any key to boot from CD", NUL
+    .ascii "Press any key to boot from the ReactOS Medium", NUL
 dot_msg:
     .ascii ".", NUL
-
-#ifdef DEBUG_MESSAGES
-startup_msg:
-    .ascii "Startup, DL = '", NUL
-spec_ok_msg:
-    .ascii "packet OK, drive = ", NUL
-secsize_msg:
-    .ascii "size appears to be ", NUL
-rootloc_msg:
-    .ascii "Root dir loc: ", NUL
-rootlen_msg:
-    .ascii "Root dir len: ", NUL
-rootsect_msg:
-    .ascii "Root dir len(sect): ", NUL
-fileloc_msg:
-    .ascii "SETUPLDR loc: ", NUL
-filelen_msg:
-    .ascii "SETUPLDR len: ", NUL
-filesect_msg:
-    .ascii "SETUPLDR len(sect): ", NUL
-findfail_msg:
-    .ascii "Failed to find file!", NUL
-startldr_msg:
-    .ascii "Starting SETUPLDR.SYS", NUL
-#endif
-
+isoboot_str:
+    .ascii "ISOBOOT: ", NUL
 spec_err_msg:
-    .ascii "Load spec failed, trying wing ...", CR, LF, NUL
+    .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL
 maybe_msg:
-    .ascii "Found smth at drive = ", NUL
+    .ascii "Found something at drive = ", NUL
 alright_msg:
-    .ascii "might be ok, continuing...", CR, LF, NUL
+    .ascii "Looks reasonable, continuing...", CR, LF, NUL
+nospec_msg:
+    .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL
 nothing_msg:
-    .ascii "Failed locate CD-ROM; boot failed.", CR, LF, NUL
-
-isolinux_str:
-    .ascii "IsoBoot: ", NUL
-crlf_msg:
-    .ascii CR, LF, NUL
+    .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL
 diskerr_msg:
     .ascii "Disk error ", NUL
+oncall_str:
+    .ascii ", AX = ", NUL
 ondrive_str:
     .ascii ", drive ", NUL
-
 err_bootfailed:
-    .ascii CR, LF, "failed..", NUL
-isolinux_dir:
-    .ascii "\\LOADER", NUL
+    .ascii CR, LF, "Boot failed: press a key to retry...", NUL
+loader_dir:
+    .ascii "/LOADER", NUL
 no_dir_msg:
     .ascii "LOADER dir not found.", CR, LF, NUL
-isolinux_bin:
+setupldr_sys:
     .ascii "SETUPLDR.SYS", NUL
-no_isolinux_msg:
-    .ascii "SETUPLDR not found.", CR, LF, NUL
+no_setupldr_msg:
+    .ascii "SETUPLDR.SYS not found.", CR, LF, NUL
+
+.align 4
+BufSafe:
+    .word trackbufsize/SECTOR_SIZE              // Clusters we can load into trackbuf
 
+// Maximum transfer size
+.align 4
+MaxTransfer:
+    .word 127                                   // Hard disk modes
+MaxTransferCD:
+    .word 32                                    // CD mode
 
 //
 // El Torito spec packet
 //
 .align 8
 spec_packet:
-    .byte HEX(13)          // Size of packet
+    .byte HEX(13)                               // Size of packet
 sp_media:
-    .byte 0                // Media type
+    .byte 0                                     // Media type
 sp_drive:
-    .byte 0                // Drive number
+    .byte 0                                     // Drive number
 sp_controller:
-    .byte 0                // Controller index
+    .byte 0                                     // Controller index
 sp_lba:
-    .long 0                // LBA for emulated disk image
+    .long 0                                     // LBA for emulated disk image
 sp_devspec:
-    .word 0                // IDE/SCSI information
+    .word 0                                     // IDE/SCSI information
 sp_buffer:
-    .word 0                // User-provided buffer
+    .word 0                                     // User-provided buffer
 sp_loadseg:
-    .word 0                // Load segment
+    .word 0                                     // Load segment
 sp_sectors:
-    .word 0                // Sector count
+    .word 0                                     // Sector count
 sp_chs:
-    .byte 0,0,0            // Simulated CHS geometry
+    .byte 0,0,0                                 // Simulated CHS geometry
 sp_dummy:
-    .byte 0                // Scratch, safe to overwrite
-
-//
-// EBIOS drive parameter packet
-//
-.align 8
-drive_params:
-    .word 30                // Buffer size
-dp_flags:
-    .word 0                // Information flags
-dp_cyl:
-    .long 0                // Physical cylinders
-dp_head:
-    .long 0                // Physical heads
-dp_sec:
-    .long 0                // Physical sectors/track
-dp_totalsec:
-    .long 0,0                // Total sectors
-dp_secsize:
-    .word 0                // Bytes per sector
-dp_dpte:
-    .long 0                // Device Parameter Table
-dp_dpi_key:
-    .word 0                // 0BEDDh if rest valid
-dp_dpi_len:
-    .byte 0                // DPI len
-    .byte 0
-    .word 0
-dp_bus:
-    .byte 0,0,0,0            // Host bus type
-dp_interface:
-    .byte 0,0,0,0,0,0,0,0            // Interface type
-db_i_path:
-    .long 0,0                // Interface path
-db_d_path:
-    .long 0,0                // Device path
-    .byte 0
-db_dpi_csum:
-    .byte 0                // Checksum for DPI info
+    .byte 0                                     // Scratch, safe to overwrite
 
 //
 // EBIOS disk address packet
 //
 .align 8
 dapa:
-    .word 16                // Packet size
+    .word 16                                    // Packet size
 .count:
-    .word 0                // Block count
+    .word 0                                     // Block count
 .off:
-    .word 0                // Offset of buffer
+    .word 0                                     // Offset of buffer
 .seg:
-    .word 0                // Segment of buffer
+    .word 0                                     // Segment of buffer
 .lba:
-    .long 0                // LBA (LSW)
-    .long 0                // LBA (MSW)
+    .long 0                                     // LBA (LSW)
+    .long 0                                     // LBA (MSW)
 
-.align 4
-MaxTransfer:
-    .word 2 //32                // Max sectors per transfer
 
-.org 2046        // Pad to file offset 2046
-.word HEX(0aa55)    // BootSector signature
+// Extend the size to cover one 2K-sized sector
+.org 2047
+    .byte 0
 
 .endcode16