2 * PROJECT: ReactOS Boot Sector for ISO file system (based on ISOLINUX)
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PROGRAMMERS: H. Peter Anvin
7 * Timo Kreuzer <timo.kreuzer@reactos.org>
8 * Colin Finck <colin@reactos.org>
10 *****************************************************************************
14 * A program to boot Linux kernels off a CD-ROM using the El Torito
15 * boot standard in "no emulation" mode, making the entire filesystem
16 * available. It is based on the SYSLINUX boot loader for MS-DOS
19 * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
20 * Copyright 2009 Intel Corporation *author: H. Peter Anvin
22 * This program is free software *you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
25 * Boston MA 02111-1307, USA *either version 2 of the License, or
26 * (at your option) any later version *incorporated herein by reference.
28 *****************************************************************************/
30 /* INCLUDES ******************************************************************/
32 #include <freeldr/include/arch/pc/x86common.h>
40 ASSUME CS:.text, DS:.text, ES:.text
42 /* CONSTANTS ******************************************************************/
43 BIOS_timer = HEX(046C) // Timer ticks (1 word)
44 BIOS_magic = HEX(0472) // BIOS reset magic (1 word)
46 // Memory below this point is reserved for the BIOS and the MBR
47 trackbuf = HEX(1000) // Track buffer goes here (8192 bytes)
48 trackbufsize = 8192 // trackbuf ends at 3000h
51 file_sector = 0 // Sector pointer (0 = structure free)
52 file_bytesleft = 4 // Number of bytes left
53 file_left = 8 // Number of sectors left
54 // Another unused DWORD follows here in ISOLINUX
55 #define open_file_t_size 16
58 dir_lba = 0 // Directory start (LBA)
59 dir_len = 4 // Length in bytes
60 dir_clust = 8 // Length in clusters
63 MAX_OPEN_LG2 = 2 // log2(Max number of open files)
65 SECTOR_SHIFT = 11 // 2048 bytes/sector (El Torito requirement)
67 retry_count = 6 // How patient are we with the BIOS?
69 /* UNINITIALIZED VARIABLES ****************************************************/
70 absolute HEX(5000) // Here we keep our BSS stuff
72 resb ISOFileName, 64 // ISO filename canonicalization buffer
73 resb ISOFileNameEnd, 1
74 resb CurrentDir, dir_t_size // Current directory
75 resb RootDir, dir_t_size // Root directory
76 resb DiskSys, 2 // Last INT 13h call
77 resb GetlinsecPtr, 2 // The sector-read pointer
78 resb DiskError, 1 // Error code for disk I/O
79 resb DriveNumber, 1 // CD-ROM BIOS drive number
80 resb ISOFlags, 1 // Flags for ISO directory search
81 resb RetryCount, 1 // Used for disk access retries
83 //align open_file_t_size
85 resb Files, (MAX_OPEN * open_file_t_size)
88 /* ENTRY POINTS ***************************************************************/
90 // Entry point when booted from CD (El Torito standard)
92 mov bx, offset getlinsec_cdrom
96 // Set up our stack and a flat addressing model.
107 // Our boot sector has been loaded to address 0x7C00.
108 // Relocate our 2048 bytes boot sector to the given base address (should be 0x7000).
115 ljmp16 0, relocated // jump into relocated code
121 // Entry point when booted through ISOMBR from a drive (isohybrid mode)
123 mov bx, offset getlinsec_ebios
127 // Save our passed variables (BX from the entry point, DL from the BIOS) before anybody clobbers the registers.
128 mov word ptr ds:[GetlinsecPtr], bx
129 mov byte ptr ds:[DriveNumber], dl
131 // Make sure the keyboard buffer is empty
132 call pollchar_and_empty
134 // If we're booting in hybrid mode and our boot drive is the first HDD (drive 80h),
135 // we have no other option than booting into SETUPLDR.
136 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
138 cmp byte ptr ds:[DriveNumber], HEX(80)
142 // Read the first sector (MBR) from the first hard disk (drive 80h) to 7C00h.
143 // If we then decide to boot from HDD, we already have it at the right place.
144 // In case of an error (indicated by the Carry Flag), just boot SETUPLDR from our ReactOS medium.
152 // Verify the signature of the read MBR.
153 // If it's invalid, there is probably no OS installed and we just boot SETUPLDR from our ReactOS medium.
154 mov ax, word ptr ds:[HEX(7C00)+510]
159 // We could either boot from the ReactOS medium or from hard disk. Let the user decide!
160 // Display the 'Press key' message.
162 mov si, offset presskey_msg
165 // Count down 5 seconds.
169 // Count in seconds using the BIOS Timer, which runs roughly at 19 ticks per second.
170 // Load its value plus one second into EAX for comparison later.
171 mov eax, ds:[BIOS_timer]
175 // Check for a keypress, boot SETUPLDR from our ReactOS medium if a key was pressed.
176 call pollchar_and_empty
179 // Check if another second has passed (in BIOS Timer ticks).
180 mov ebx, ds:[BIOS_timer]
184 // Another second has passed, so print the dot and decrement the second counter.
185 // If the user hasn't pressed a key after the entire 5 seconds have elapsed, just boot from the first hard disk.
186 mov si, offset dot_msg
194 // Restore a clean context for the hard disk MBR and boot the already loaded MBR.
211 // The BIOS gave us a boot drive number, so in a perfect world we could just use that one now.
212 // Unfortunately, there are many broken BIOSes around, which is why ISOLINUX verifies it and applies some hacks if the number is wrong.
213 // Let's do exactly the same here to achieve maximum compatibility.
215 // Don't do this if we are running in hybrid mode.
216 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
219 // Use the INT 13 function 4B01h (Get Disk Emulation Status) to fetch the El Torito Spec Packet.
220 // We can use this information to verify that our passed boot drive number really belongs to our CD.
222 mov dl, byte ptr ds:[DriveNumber]
223 mov si, offset spec_packet
226 // If this INT 13 function yields an error, we may be on a broken AWARD BIOS.
227 // Check this and patch if possible.
230 // Check that our passed boot drive number and the number in the Spec Packet match.
231 // If not, try some workarounds to find our drive anyway.
232 mov dl, byte ptr ds:[DriveNumber]
233 cmp byte ptr ds:[sp_drive], dl
234 jne spec_query_failed
237 // Clear Files structures
239 mov cx, (MAX_OPEN*open_file_t_size)/4
243 // Read the entire 2K-sized ISO9660 Primary Volume Descriptor at sector 16 (32K).
244 // This calculation only holds for single-session ISOs, but we should never encounter anything else.
249 // Read the LBA address (offset 2 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
250 mov eax, dword ptr ds:[trackbuf+156+2]
251 mov dword ptr ds:[RootDir+dir_lba], eax
252 mov dword ptr ds:[CurrentDir+dir_lba], eax
254 // Read the data length (offset 10 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
255 mov eax, dword ptr ds:[trackbuf+156+10]
256 mov dword ptr ds:[RootDir+dir_len], eax
257 mov dword ptr ds:[CurrentDir+dir_len], eax
259 // Calculate the number of clusters and write that to our RootDir and CurrentDir structures.
260 add eax, SECTOR_SIZE-1
261 shr eax, SECTOR_SHIFT
262 mov dword ptr ds:[RootDir+dir_clust],eax
263 mov dword ptr ds:[CurrentDir+dir_clust],eax
265 // Look for the "LOADER" directory (directory is indicated by AL = 2 when using searchdir_iso).
266 mov di, offset loader_dir
271 // No directory was found, so bail out with an error message.
272 mov si, offset no_dir_msg
277 // The directory was found, so update the information in our CurrentDir structure.
278 // Free the file pointer entry at SI in the process.
279 mov dword ptr ds:[CurrentDir+dir_len], eax
280 mov eax, dword ptr ds:[si+file_left]
281 mov dword ptr ds:[CurrentDir+dir_clust], eax
283 xchg eax, dword ptr ds:[si+file_sector]
284 mov dword ptr ds:[CurrentDir+dir_lba], eax
286 // Look for the "SETUPLDR.SYS" file.
287 mov di, offset setupldr_sys
291 // The SETUPLDR file was not found, so bail out with an error message.
292 mov si, offset no_setupldr_msg
297 // Calculate the rounded up number of 2K sectors that need to be read.
299 shr ecx, SECTOR_SHIFT
305 // Load the entire SETUPLDR.SYS (parameter CX = FFFFh) to its designated base address FREELDR_BASE.
306 // Using a high segment address with offset 0 instead of segment 0 with offset FREELDR_BASE apparently increases compatibility with some BIOSes.
307 mov bx, FREELDR_BASE / 16
313 // Fetch our stored drive number to DL and set the boot partition to 0 in DH.
314 mov dl, byte ptr ds:[DriveNumber]
317 // Transfer execution to the bootloader.
318 ljmp16 0, FREELDR_BASE
321 /* FUNCTIONS *****************************************************************/
323 ///////////////////////////////////////////////////////////////////////////////
324 // Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
325 ///////////////////////////////////////////////////////////////////////////////
327 // There is a problem with certain versions of the AWARD BIOS ...
328 // the boot sector will be loaded and executed correctly, but, because the
329 // int 13 vector points to the wrong code in the BIOS, every attempt to
330 // load the spec packet will fail. We scan for the equivalent of
339 // and use <direct far> as the new vector for int 13. The code above is
340 // used to load the boot code into ram, and there should be no reason
341 // for anybody to change it now or in the future. There are no opcodes
342 // that use encodings relativ to IP, so scanning is easy. If we find the
343 // code above in the BIOS code we can be pretty sure to run on a machine
344 // with an broken AWARD BIOS ...
346 ///////////////////////////////////////////////////////////////////////////////
350 .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a)
353 mov si, offset spec_err_msg // Moved to this place from
354 call writemsg // spec_query_failed
356 mov eax, dword ptr ds:[HEX(13)*4]
357 mov dword ptr ds:[award_oldint13], eax
360 mov ax, HEX(F000) // ES = BIOS Seg
363 xor di, di // start at ES:DI = f000:0
366 mov si, offset award_string // scan for award_string
367 mov cx, 7 // length of award_string = 7dw
368 repz cmpsw // compare
370 jcxz award_found // jmp if found
371 inc di // not found, inc di
375 pop es // No, not this way :-((
377 mov eax, dword ptr ds:[award_oldint13] // restore the original int
378 or eax, eax // 13 vector if there is one
379 jz spec_query_failed // and try other workarounds
380 mov dword ptr ds:[HEX(13)*4], eax
381 jmp spec_query_failed
384 mov eax, dword ptr es:[di+HEX(0e)] // load possible int 13 addr
387 cmp eax, dword ptr ds:[award_oldint13] // give up if this is the
388 jz award_failed // active int 13 vector,
389 mov dword ptr ds:[HEX(13)*4], eax // otherwise change 0:13h*4
391 mov ax, HEX(4B01) // try to read the spec packet
392 mov dl, byte ptr ds:[DriveNumber] // now ... it should not fail
393 mov si, offset spec_packet // any longer
397 jmp found_drive // and leave error recovery code
398 ///////////////////////////////////////////////////////////////////////////////
399 // End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
400 ///////////////////////////////////////////////////////////////////////////////
403 // INT 13h, AX=4B01h, DL=<passed in value> failed.
404 // Try to scan the entire 80h-FFh from the end.
406 // some code moved to BrokenAwardHack
413 mov si, offset spec_packet
414 mov byte ptr ds:[si], HEX(13) // Size of buffer
419 mov si, offset maybe_msg
425 cmp byte ptr ds:[sp_drive], dl
428 // Okay, good enough...
429 mov si, offset alright_msg
432 mov byte ptr ds:[DriveNumber], dl
436 // Award BIOS 4.51 apparently passes garbage in sp_drive,
437 // but if this was the drive number originally passed in
438 // DL then consider it "good enough"
440 mov al, byte ptr ds:[DriveNumber]
444 // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
445 // passes garbage in sp_drive, and the drive number originally
446 // passed in DL does not have 80h bit set.
456 // No spec packet anywhere. Some particularly pathetic
457 // BIOSes apparently don't even implement function
458 // 4B01h, so we can't query a spec packet no matter
459 // what. If we got a drive number in DL, then try to
460 // use it, and if it works, then well...
461 mov dl, byte ptr ds:[DriveNumber]
462 cmp dl, HEX(81) // Should be 81-FF at least
463 jb fatal_error // If not, it's hopeless
465 // Write a warning to indicate we're on *very* thin ice now
466 mov si, offset nospec_msg
471 jmp .found_drive // Pray that this works...
474 mov si, offset nothing_msg
490 // EAX = file length in bytes
494 // Assumes CS == DS == ES, and trashes BX and CX.
496 // searchdir_iso is a special entry point for ISOLINUX only. In addition
497 // to the above, searchdir_iso passes a file flag mask in AL. This is useful
498 // for searching for directories.
501 xor ax, ax // ZF <- 1
507 mov byte ptr ds:[ISOFlags], al
508 call allocate_file // Temporary file structure for directory
513 mov si, offset CurrentDir
514 cmp byte ptr ds:[di], '/' // If filename begins with slash
516 inc di // Skip leading slash
517 mov si, offset RootDir // Reference root directory instead
519 mov eax, dword ptr ds:[si+dir_clust]
520 mov dword ptr ds:[bx+file_left], eax
521 shl eax, SECTOR_SHIFT
522 mov dword ptr ds:[bx+file_bytesleft], eax
523 mov eax, dword ptr ds:[si+dir_lba]
524 mov dword ptr ds:[bx+file_sector], eax
525 mov edx, dword ptr ds:[si+dir_len]
530 mov cl, byte ptr ds:[di]
536 mov byte ptr ds:[di-1], 0 // Terminate at directory name
537 mov cl, 2 // Search for directory
538 xchg cl, byte ptr ds:[ISOFlags]
540 push di // Save these...
543 // Create recursion stack frame...
544 push offset .resume // Where to "return" to
550 // Get a chunk of the directory
551 // This relies on the fact that ISOLINUX doesn't change SI
555 mov cx, word ptr ds:[BufSafe]
560 movzx eax, byte ptr ds:[si] // Length of directory entry
563 mov cl, byte ptr ds:[si+25]
564 xor cl, byte ptr ds:[ISOFlags]
565 test cl, HEX(8E) // Unwanted file attributes!
568 movzx cx, byte ptr ds:[si+32] // File identifier length
569 add si, 33 // File identifier offset
570 call iso_compare_names
574 sub edx, eax // Decrease bytes left
576 add si, ax // Advance pointer
579 // Did we finish the buffer?
580 cmp si, trackbuf+trackbufsize
581 jb .compare // No, keep going
583 jmp short .getsome // Get some more directory
586 // Advance to the beginning of next sector
587 lea ax, [si+SECTOR_SIZE-1]
588 and ax, not (SECTOR_SIZE-1)
590 jmp short .not_file // We still need to do length checks
593 xor eax, eax // ZF = 1
594 mov dword ptr ds:[bx+file_sector], eax
599 mov eax, dword ptr ds:[si+2] // Location of extent
600 mov dword ptr ds:[bx+file_sector], eax
601 mov eax, dword ptr ds:[si+10] // Data length
602 mov dword ptr ds:[bx+file_bytesleft], eax
604 add eax, SECTOR_SIZE-1
605 shr eax, SECTOR_SHIFT
606 mov dword ptr ds:[bx+file_left], eax
608 jz .failure // Empty file?
615 // We get here if we were only doing part of a lookup
616 // This relies on the fact that .success returns bx == si
617 xchg edx, eax // Directory length in edx
618 pop cx // Old ISOFlags
619 pop di // Next filename pointer
620 mov byte ptr ds:[di-1], '/' // Restore slash
621 mov byte ptr ds:[ISOFlags], cl // Restore the flags
622 jz .failure // Did we fail? If so fail for real!
623 jmp .look_for_slash // Otherwise, next level
626 // allocate_file: Allocate a file structure
639 cmp dword ptr ds:[bx], 0
641 add bx, open_file_t_size // ZF = 0
643 // ZF = 0 if we fell out of the loop
649 // iso_compare_names:
650 // Compare the names DS:SI and DS:DI and report if they are
651 // equal from an ISO 9660 perspective. SI is the name from
652 // the filesystem; CX indicates its length, and ';' terminates.
653 // DI is expected to end with a null.
655 // Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
658 // First, terminate and canonicalize input filename
660 mov di, offset ISOFileName
670 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun
675 cmp byte ptr ds:[di-1], '.' // Remove terminal dots
680 mov byte ptr ds:[di], 0 // Null-terminate string
685 mov ah, byte ptr ds:[di]
688 jz .success2 // End of string for both
689 and al, al // Is either one end of string?
690 jz .failure2 // If so, failure
693 or ax, HEX(2020) // Convert to lower case
697 and ax, ax // ZF = 0 (at least one will be nonzero)
702 // getfssec: Get multiple clusters from a file, given the file pointer.
706 // SI -> File pointer
707 // CX -> Cluster count
709 // SI -> File pointer (or 0 on EOF)
711 // ECX -> Bytes actually read
719 cmp ecx, dword ptr ds:[si+file_left]
721 mov ecx, dword ptr ds:[si+file_left]
724 mov eax, dword ptr ds:[si+file_sector]
729 // ECX[31:16] == 0 here...
730 add dword ptr ds:[si+file_sector], ecx
731 sub dword ptr ds:[si+file_left], ecx
732 shl ecx, SECTOR_SHIFT // Convert to bytes
733 cmp ecx, dword ptr ds:[si+file_bytesleft]
735 mov ecx, dword ptr ds:[si+file_bytesleft]
737 sub dword ptr ds:[si+file_bytesleft], ecx
738 jnz .ret // CF = 0 in this case...
741 mov dword ptr ds:[si+file_sector], eax // Unused
750 // Information message (DS:SI) output
751 // Prefix with "ISOBOOT: "
756 mov si, offset isoboot_str
787 // writechr: Write a character to the screen.
800 // int13: save all the segment registers and call INT 13h.
801 // Some CD-ROM BIOSes have been found to corrupt segment registers
802 // and/or disable interrupts.
813 setc byte ptr ds:[bp+10] // Propagate CF to the caller
823 // Get one sector. Convenience entry point.
827 // Fall through to getlinsec
830 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
833 jmp word ptr cs:[GetlinsecPtr]
838 // getlinsec implementation for floppy/HDD EBIOS (EDD)
843 shl eax, 2 // Convert to HDD sectors
847 push bp // Sectors left
849 call maxtrans // Enforce maximum transfer size
850 movzx edi, bp // Sectors we are about to read
853 // Form DAPA on stack
862 mov dl, byte ptr ds:[DriveNumber]
866 mov ah, HEX(42) // Extended Read
870 lea sp, [si+16] // Remove DAPA
873 add eax, edi // Advance sector pointer
875 sub bp, di // Sectors left
876 shl di, 9 // 512-byte sectors
877 add bx, di // Advance buffer pointer
878 jnc .no_overflow // Check if we have read more than 64K and need to adjust ES
880 add di, HEX(1000) // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1)
889 pushad // Try resetting the device
891 mov dl, byte ptr ds:[DriveNumber]
894 loop .retry // CX-- and jump if not zero
900 // Truncate BP to MaxTransfer
903 cmp bp, word ptr ds:[MaxTransfer]
905 mov bp, word ptr ds:[MaxTransfer]
910 // This is the variant we use for real CD-ROMs:
911 // LBA, 2K sectors, some special error handling.
914 mov si, offset dapa // Load up the DAPA
915 mov word ptr ds:[si+4], bx
916 mov word ptr ds:[si+6], es
917 mov dword ptr ds:[si+8], eax
919 push bp // Sectors left
920 cmp bp, word ptr ds:[MaxTransferCD]
922 mov bp, word ptr ds:[MaxTransferCD]
924 mov word ptr ds:[si+2], bp
926 mov dl, byte ptr ds:[DriveNumber]
927 mov ah, HEX(42) // Extended Read
931 movzx eax, word ptr ds:[si+2] // Sectors we read
932 add dword ptr ds:[si+8], eax // Advance sector pointer
933 sub bp, ax // Sectors left
934 shl ax, SECTOR_SHIFT-4 // 2048-byte sectors -> segment
935 add word ptr ds:[si+6], ax // Advance buffer pointer
938 mov eax, dword ptr ds:[si+8] // Next sector
941 // INT 13h with retry
943 mov byte ptr ds:[RetryCount], retry_count
948 add sp, 8*4 // Clean up stack
951 mov byte ptr ds:[DiskError], ah // Save error code
953 mov word ptr ds:[DiskSys], ax // Save system call number
954 dec byte ptr ds:[RetryCount]
957 mov al, byte ptr ds:[RetryCount]
958 mov ah, byte ptr ds:[dapa+2] // Sector transfer count
959 cmp al, 2 // Only 2 attempts left
961 mov ah, 1 // Drop transfer size to 1
964 cmp al, retry_count-2
965 ja .again // First time, just try again
966 shr ah, 1 // Otherwise, try to reduce
967 adc ah, 0 // the max transfer size, but not to 0
969 mov byte ptr ds:[MaxTransferCD], ah
970 mov byte ptr ds:[dapa+2], ah
976 mov si, offset diskerr_msg
978 mov al, byte ptr ds:[DiskError]
980 mov si, offset oncall_str
982 mov ax, word ptr ds:[DiskSys]
984 mov si, offset ondrive_str
989 // Fall through to kaboom
992 // kaboom: write a message and bail out. Wait for a user keypress,
993 // then do a hard reboot.
996 // Restore a clean context.
1004 // Display the failure message.
1005 mov si, offset err_bootfailed
1008 // Wait for a keypress.
1012 // Disable interrupts and reset the system through a magic BIOS call.
1014 mov word ptr ds:[BIOS_magic], 0
1015 ljmp16 HEX(0F000), HEX(0FFF0)
1018 // writehex[248]: Write a hex number in (AL, AX, EAX) to the console
1025 jmp short writehex_common
1031 jmp short writehex_common
1057 // pollchar_and_empty: Check if we have an input character pending (ZF = 0)
1058 // and empty the input buffer afterwards.
1062 mov ah, 1 // Did the user press a key?
1064 jz .end_pollchar // No, then we're done
1065 mov ah, 0 // Otherwise empty the buffer by reading it
1072 /* INITIALIZED VARIABLES *****************************************************/
1074 .ascii "Press any key to boot from the ReactOS medium", NUL
1078 .ascii "ISOBOOT: ", NUL
1080 .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL
1082 .ascii "Found something at drive = ", NUL
1084 .ascii "Looks reasonable, continuing...", CR, LF, NUL
1086 .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL
1088 .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL
1090 .ascii "Disk error ", NUL
1092 .ascii ", AX = ", NUL
1094 .ascii ", drive ", NUL
1096 .ascii CR, LF, "Boot failed: press a key to retry...", NUL
1098 .ascii "/LOADER", NUL
1100 .ascii "LOADER dir not found.", CR, LF, NUL
1102 .ascii "SETUPLDR.SYS", NUL
1104 .ascii "SETUPLDR.SYS not found.", CR, LF, NUL
1108 .word trackbufsize/SECTOR_SIZE // Clusters we can load into trackbuf
1110 // Maximum transfer size
1113 .word 127 // Hard disk modes
1118 // El Torito spec packet
1122 .byte HEX(13) // Size of packet
1124 .byte 0 // Media type
1126 .byte 0 // Drive number
1128 .byte 0 // Controller index
1130 .long 0 // LBA for emulated disk image
1132 .word 0 // IDE/SCSI information
1134 .word 0 // User-provided buffer
1136 .word 0 // Load segment
1138 .word 0 // Sector count
1140 .byte 0,0,0 // Simulated CHS geometry
1142 .byte 0 // Scratch, safe to overwrite
1145 // EBIOS disk address packet
1149 .word 16 // Packet size
1151 .word 0 // Block count
1153 .word 0 // Offset of buffer
1155 .word 0 // Segment of buffer
1157 .long 0 // LBA (LSW)
1158 .long 0 // LBA (MSW)
1161 // Extend the size to cover one 2K-sized sector