VBR, MSWIN4.1, en, FAT32 ======================== The boot sector (VBR[0]) is copied into memory at 0000:7C00 by the "bootstrapper" (which might be started from BIOS via MBR or PXE). It should have set register DL to the drive number used for boot before it hands over control to the bootloader - here 0000:7C00. A FAT volume looks basically like this: 0 – Reserved Region (which starts with the VBR and usually consume a track) 1 – FAT Region 2 – Root Directory Region (doesn’t exist on FAT32 volumes) 3 – File and Directory Data Region what sectors sum absolute address hidden 0 0 0 0000 reserved 38 38 38 0000 fat 2*7825 15650 15688 4C00 / dirs 0*32/512 0 15688 7A9000 1st cluster 8 8 15696 7A9000 root dir 2nd cluster 8 8 15704 7AA000 IO.SYS (4 blocks) 7C00: eb 58 jmp 0x7C5A # jump to 0x7C00+0x58+2 = 0x7C5A 7C02: 90 nop 7C03: 4d 53 44 4f 53 35 2e 30 # OEM ID: MSWIN4.1 ==START BPB== 7C0B: 00 02 # bytes/sector: 512 (0x0200) 7C0D: 08 # sectors/cluster: 8 7C0E: 26 00 # reserved sectors: 38 (0x0026) 7C10: 02 # FAT count: 2 7C11: 00 00 # max. / dir entries_16: 0 (=FAT32) 7C13: 00 00 # volume size_16: 0 (=FAT32) 7C15: f8 # media type: 80 (=fixed disk) 7C16: 00 00 # FAT size_16: 0 (=FAT32) 7C18: 3f 00 # sector/track: 63 7C1A: ff 00 # heads: 255 7C1C: 00 00 00 00 # hidden sectors: 00 7C20: bf 7f 7a 00 # volume size_32: 8028095 (0x007A7FBF) 7C24: 91 1e 00 00 # FAT size_32: 7825 (0x0000911E) 7C28: 00 00 # Flags: FAT mirrored at runtime to all 7C2A: 00 00 # FS version: 0.0 7C2C: 02 00 00 00 # num of 1st cluster in /: 2 7C30: 01 00 # num of fsInfo sector: 1 7C32: 06 00 # num of backup sector start: 6 7C34: 00 00 00 00 00 00 00 00 00 00 00 00 # reserved: 12 byte 7C40: 80 # drive number: 0x80 (=1st HDD) 7C41: 00 # reserved: 1 byte 7C42: 29 # Ext BootSig: 0x29 (VolInfo available) 7C43: 1f 22 f2 2c # Volume ID: 2CF2-221F 7C47: 4e 4f 20 4e 41 4d 45 20 20 20 20 # Volume label: 'NO NAME ' 7C52: 46 41 54 33 32 20 20 20 # filesystem ID: 'FAT32 ' ==END BPB== ==BOOTLOADER START== # main start ; The first part basically copies the diskette parameter table (DPT) from the ; ROM to the DOS data area, re-routes the vector at 0000:0078 to the DOS DPT ; (0000:0522) and modifies the copy according to the values in the BPB. ; The mentioned interrupt vector will be used by INT 13h to setup the floppy ; controller properly for several operations provided by this interrupt. set_disk_params: 7C5A: fa cli # Avoid interrupts when modifying INT # vectors and data used by INTs 7C5B: 33 c9 xor CX,CX # Zero out Counter ... 7C5D: 8e d1 mov SS,CX # and StackSegment Register 7C5F: bc f8 7b mov SP,0x7BF8 # Set StackPointer to 0000:7BF8 7C62: 8e c1 mov ES,CX # Zero out ExtraSegment 7C64: bd 78 00 mov BP,0x78 # Set BP to point to 0000:0078, which # holds the vector for INT 1E. Vectors # are 4B in IP:CS format. 7C67: c5 76 00 lds SI,[BP] # Load the content from 0000:0078 into # DS:SI (upper 16b into DS, lower 16b # into SI) => points now to the ROM DPT 7C6A: 1e push DS # Save DataSegment 7C6B: 56 push SI # and SourceIndex 7C6C: 16 push SS # and StackSegment 7C6D: 55 push BP # and BasePointer values on the stack 7C6E: bf 22 05 mov DI,0x0522 # Set DI to 0522h (DOS DPT) 7C71: 89 7e 00 mov [BP], DI # Re-route: i.e. let 0000:0078 point to 7C74: 89 4e 02 mov [BP+2],CX # 0000:0522h (CX:DI) 7C77: b1 0b mov CL,0xB # Set number of bytes to move (CX=11) 7C79: fc cld # Clear direction flag => increment SI # as well as DI after each movsb 7C7A: f3 a4 rep movs ES:[DI],DS:[SI] # execute movsb CX times: i.e. # copy 11 bytes from DS:SI (*DPT) to # ES:DI (0000:0522). Finally DI is 52Dh # and CX == 0. 7C7C: 8e d9 mov DS,CX # Set DataSegment back to 0000 and 7C7E: bd 00 7c mov BP,0x7C00 # BP to 7C00h (this VBR[0] copy) 7C81: c6 45 fe 0f movb [DI-2],0xF # Set head settle time in the DOS DTP # to 15ms (0x52D-2 = 0x52B) 7C85: 8b 46 18 mov AX,[BP+0x18] # Copy sectors/track from BPB (0x7C18h) 7C88: 88 45 f9 mov [DI-7],AL # to the DOS DTP (0x52D-7 = 0x526) 7C8B: 38 4e 40 cmp [BP+0x40],CL # Check, whether the drive number in the # BPB (7C40h) denotes a valid device, # i.e. >= 0. Since this is always the # case, 7C8E: 7d 25 jge 0x7CB5 # jump to prepare_read (0x7C8E+0x25+2) ; end of set_disk_params dead_code_fillbytes: 7C90: 8b c1 mov AX,CX # 7C92: 99 cwtd # 7C93: bb 00 07 mov BX,0x0700 # 7C96: e8 97 00 call 0x7D30 # 7C99: 72 1a jc 0x7CB5 # 7C9B: 83 eb 3a sub BX,0x3a # 7C9E: 66 a1 1c 7c mov EAX,0x7C1C # 7CA2: 66 3b 07 cmp EAX,[BX] # 7CA5: 8a 57 fc mov DL,[BX-4] # 7CA8: 75 06 jne 0x7CB0 # 7CAA: 80 ca 02 or DL,2 # 7CAD: 88 56 02 mov [BP+2],DL # 7CB0: 80 c3 10 add BL,0x10 # 7CB3: 73 ed jae 0x7CA2 # ; end of dead_code_makes_no_sense ; basically load the after burner (VBR[1] and VBR[2] into 0000:7E00, 0000:8000) ; and continue execution by jumping to 0000:8000 prepare_read: 7CB5: bf 02 00 mov DI,2 # max. tries for reading (DI+1) # (sometimes engines are not hot enough) 7CB8: 83 7e 16 00 cmpw [BP+0x16],0 # If BPB FAT size (C716h) != 0, # (i.e. if this is not FAT32) 7CBC: 75 45 jne 0x7D03 # jump to invalid_sys_disk # (0x7CBC+0x45+2) 7CBE: 8b 46 1c mov AX,[BP+0x1c] # Move num of hidden sectors to DX:AX, 7CC1: 8b 56 1e mov DX,[BP+0x1e] # to get the absolute starting sector # num (7C1Ch is a DWORD, little endian) 7CC4: b9 03 00 mov CX,3 # and prepare to read 3 blocks try_again: 7CC7: 49 dec CX # Or better only 2 and clear flags ;-) 7CC8: 40 inc AX # Skip the VBR[0] (alread here) 7CC9: 75 01 jnz 0x7CCC # If increment did not cause an overflow # skip next instruction, i.e. jump to # 0x7CC9+1+2 7CCB: 42 inc DX # Take AX overflow into account 7CCC: bb 00 7e mov BX,0x7E00 # Set the address of the read buffer. # So the fsInfo block (VBR[1]) goes to # 0000:7E00 and VBR[2] to 0000:8000 7CCF: e8 5f 00 call 0x7D31 # Now readDisk CX=2 blocks into ES:BX= # 0000:7E00 starting at absolute block # DX:AX=hiddenSectors+1 7CD2: 73 26 jnc 0x7CFA # If no error occured, jump to # go_ahead (0x7CD2+0x26+2) 7CD4: b0 f8 mov AL,0xF8 # fill word 7CD6: 4f dec DI # decrement number of tries 7CD7: 74 1d jz 0x7CF6 # max. num of tries reached, jump to # select_error_msg: (0x7CD7+0x1d+2) 7CD9: 8b 46 32 mov AX,[BP+0x32] # Move backup sector start [7C32h] to AX 7CDC: 33 d2 xor DX,DX # Zero out DX 7CDE: b9 03 00 mov CX,3 # Set number of blocks to read 7CE1: 3b c8 cmp CX,AX # If backup start num < 3 blocks 7CE3: 77 1e ja 0x7D03 # jump to invalid_sys_disk 0x7CE3+0x1e+2 7CE5: 8b 76 0e mov SI,[BP+0xe] # Move num of reserved sectors to SI 7CE8: 3b ce cmp CX,SI # If block num is outside the reserved 7CEA: 73 17 jae 0x7D03 # sector range, jump to invalid_sys_disk # as well (0x7CEA+0x17+2) 7CEC: 2b f1 sub SI,CX # clear flags 7CEE: 03 46 1c add AX,[BP+0x1c] # Copy num of hidden sectors to AX:DX 7CF1: 13 56 1e adc DX,[BP+0x1e] # and 7CF4: eb d1 jmp 0x7CC7 # jump to try_again (0x7CF4-256+0xd1+2) select_error_msg: 7CF6: 73 0b jnc 0x7D03 # If not read error jump to # invalid_sys_disk (0x7CF6+0xb+2) 7CF8: eb 27 jmp 0x7D21 # else jump to io_error (0x7CF8+0x27+2) go_ahead: 7CFA: 83 7e 2a 00 cmpw [BP+0x2a],0 # If filesystem version (7C2Ah) != 0.0 7CFE: 77 03 ja 0x7D03 # jump to invalid_sys_disk (0x7CFE+3+2) 7D00: e9 fd 02 jmp 0x8000 # Jump to code in VBR[2] # (0x7D00+0x02fd+2=0x8000) # main "end" - goto main2 # function displayError invalid_sys_disk: 7D03: be 7e 7d mov SI,0x7D7E # SI=message offset in 0x7D7E (0x03) # => 7D82h: "\r\nInvalid system disk" print_msg: 7D06: ac lods AL,DS:[SI] # Load the offset into DS:AL 7D07: 98 cbtw # Convert AL to AX keeping the sign bit 7D08: 03 f0 add SI,AX # Add the message offset to SI print_next_line: 7D0A: ac lods AL,DS:[SI] # Load string byte at DS:SI into AL 7D0B: 84 c0 test AL,AL # If AL == 00 (end of messages) 7D0D: 74 17 je 0x7D26 # jump to wait_for_keystroke # (0x7D0D+0x17+2) 7D0F: 3c ff cmp AL,0xff # If AL == 0xff (EndOfLine marker) 7D11: 74 09 je 0x7D1C # jump to replace_disk (0x7D11+9+2) 7D13: b4 0e mov AH,0xe # Set function teletype output 7D15: bb 07 00 mov BX,7 # Set color to white (0x7) 7D18: cd 10 int 0x10 # Call INT10 to print out the char # in AL, advance the cursor and scroll # screen if necessary 7D1A: eb ee jmp 7D0A # jump to print_next_line # (0x7D1A-(256-0xee)+2) replace_disk: 7D1C: be 81 7d mov SI,0x7D81 # SI=load msg offset in 0x7D81 (0x27) # => 7DA9h: "\r\nReplace the disk, and \ # then press any key\n" 7D1F: eb e5 jmp 0x7D06 # jump to print_msg # (7D1F-(256-0xe5)+2) io_error: 7D21: be 7f 7d mov SI,0x7D7F # SI=load msg offset in 0x7D7F (0x18) # => 7D98h: "\r\nDisk I/O error" 7D24: eb e0 jmp 0x7D06 # jump to print_msg # (7D24-(256-0xe0)+2) wait_for_keystroke: 7D26: 98 cbtw # Convert byte to word 7D27: cd 16 int 0x16 # Get keystroke from keyboard 7D29: 5e pop SI # Restore the vector address 7D2A: 1f pop DS # of the ROM-DPT 7D2B: 66 8f 04 popl [SI] # to 0000:0078 (and clean up the stack) 7D2E: cd 19 int 19 # Call BIOS "bootstrapper" (reboot) # without clearing memory and restoring # interrupt vectors # function end # function readDisk 7D30: 41 inc CX # param: number of sectors to read read_disk: 7D31: 56 push SI # just save it to be able to restore its # value on return ; prepare the disk address paket (DAP) for INT13 42h (extended read) ; AH: extended read sector function (42h) ; DS:SI: address of the DAP ; DAP create starts 7D32: 66 6a 00 pushl 0 # absolut block number, where to start 7D35: 52 push DX # reading: 0:0:DX:AX (QWORD) 7D36: 50 push AX # 7D37: 06 push ES # buffer addr, where to store bytes read 7D38: 53 push BX # = ES:BX (same for normal read) 7D39: 6a 01 push 1 # number of blocks to read 7D3B: 6a 10 push 0x10 # length of the DAP in bytes: 16 ; DAP create ends 7D3D: 8b f4 mov SI,SP # remember position, where the DAP # starts (here always SI=7BF8+0x10) ; now save all regs and branch to the desired read method 7D3F: 60 pusha # push AX, CX, DX, BX, SP, BP, SI, DI 7D40: 80 7e 02 0e cmpb [BP+2],0xe # If jumpCode[2] != 14 7D44: 75 04 jne 0x7D4A # jump to prep_normal_read_params # (0x7D44+4+2) 7D46: b4 42 mov AH,0x42 # Set INT13 function to use to extended # read 7D48: eb 1d jmp 0x7D67 # Jump to do_read (0x7D48+0x1d+2) # divw BLA: devide DX:AX by BLA. Result quotient goes into AX, remainder into DX ; prepare parameters for int13 (disk read): ; AH: read sector function (02h) ; AL: number of sectors to read ; CH: number of cylinder (lower 8 bits), where to start reading ; CL: starting sector number (7:6 upper 2b of cylinder, 5:0 sector) ; DH: head number ; DL: drive number ; ES:BX: where to store bytes read (already set) prep_normal_read_params: 7D4A: 91 xchg CX,AX # this..7D62h: calculate CHS values 7D4B: 92 xchg AX,DX # 7D4C: 33 d2 xor DX,DX # zero out DX 7D4E: f7 76 18 divw [BP+0x18] # devide by sectors/track (7C18h) 7D51: 91 xchg CX,AX # 7D52: f7 76 18 divw [BP+0x18] # divide by sectors/track (7C18h) 7D55: 42 inc DX # 7D56: 87 ca xchg DX,CX # 7D58: f7 76 1a divw [BP+0x1a] # divide by num of heads (7C1Ah) 7D5B: 8a f2 mov DH,DL # Set head number (DL gets later # overwritten with drive number) 7D5D: 6a e8 mov CH,AL # Set starting cylinder (lower 8b) and 7D5F: c0 cc 02 ror AH,2 # set the upper 2b to bits 7:6 in the 7D62: 0a cc or CL,AH # starting sector field 7D64: b8 01 02 mov AX,0x0201 # Set function to "Read sectors into # memory" (AH=2) and number of sectors # to read 1 (AL=1) do_read: 7D67: 8a 56 40 mov DL,[BP+0x40] # Set drive number to read (7C40h) 7D6A: cd 13 int 13 # Read the sector into mem at ES:BX 7D6C: 61 popa # Restore all registers 7D6D: 8d 64 10 lea SP,[SI+0x10] # "Remove" the DAP from the stack by # changing the "start" of the stack 7D70: 5e pop SI # Restore SI to the value @fn:entry 7D71: 72 0a jc 0x7D7D # If an error occured (CF is set) jump # to return (0x7D71+0xa+2) 7D73: 40 inc AX # Increment the absolute number of block # to read next 7D74: 75 01 jnz 0x7D77 # If increment did not cause an overflow # skip next instruction 7D76: 42 inc DX # Take AX overflow into account 7D77: 03 5e 0b add BX,[BP+0xb] # Increment position, where to store # next bytes to read by bytes/sector 7D7A: 49 dec CX # Decrement number of sectors read 7D7B: 75 b4 jnz 0x7D31 # If not all sectors read (CX!=0), jump # to read_disk again 0x7D7B-256+0xb4+2 7D7D: c3 ret # return # function end # message strings and filenames (string table) 7D7E: 03 # message offset wrt. to this address 7D7F: 18 # message offset wrt. to this address 7D80: 01 # unused 7D81: 27 # message offset wrt. to this address 7D82: 0d 0a 49 6e 76 61 6c 69 # '\r\nInvali' 64 20 73 79 73 74 65 6d # 'd system d' 20 64 69 73 6b ff # 'isk'-Marker 7D98: 0d 0a 44 69 73 6b 20 49 # '\r\nDisk I' 2f 4f 20 65 72 72 6f 72 # '/O error' ff # -Marker 7DA9: 0d 0a 52 65 70 6c 61 63 # '\r\nReplac' 65 20 74 68 65 20 64 69 # 'e the di' 73 6b 2c 20 61 6e 64 20 # 'sk, and ' 74 68 65 6e 20 70 72 65 # 'then pre' 73 73 20 61 6e 79 20 6b # 'ss any k' 65 79 0d 0a # 'ey\r\n' 7DD5: 00 # End of messages Marker 7DD6: 00 00 # unused 7DD8: 49 4f 20 20 20 20 20 20 53 59 53 # 'IO SYS' 7DE3: 4d 53 44 4f 53 20 20 20 53 59 53 # 'MSDOS SYS' 7DEE: 7e 01 00 # unused 7DF1: 57 49 4e 42 4f 4f 54 20 53 59 53 # 'WINBOOT SYS' 7DFC: 00 00 # unused # string table end # boot signature 7DFE: 55 aa # 2 byte boot signature # boot sector 0 aka VBR[0] end # 7E00-7FFF: FAT32 filesystem Info block # 512 byte (not needed here) # boot sector 2 aka VBR[2] start ; NOTE: The first cluster in the data area has per definition the number 2 ; and NOT 0. So the absolute number of the first sector of a data ; cluster N is: (N-2)*SectorsPerCluster + Abs.NumberOfFirstDataSector # main2 start root_dir_calc: 8000: fa cli # Disable interrupts 8001: 66 0f b6 46 10 movzbl EAX,[BP+0x10] # EAX=number of FATs (7C10h) 8006: 66 8b 4e 24 mov ECX,[BP+0x24] # ECX=size of one FAT (7C24h) in # blocks 800A: 66 f7 e1 mul ECX # Set EDX:EAX to the size # all FATs consume (in blocks) 800D: 66 03 46 1c add EAX,[BP+0x1c] # add number of "hidden blocks" # (7C1Ch) 8011: 66 0f b7 56 0e movzwl EDX,[BP+0xe] # Set EDX to number of reserved # blocks (7C0Eh) and 8016: 66 03 c2 add EAX,EDX # Add them to EAX 8019: 33 c9 xor CX,CX # Zero-out CX 801B: 66 89 46 fc mov [BP-4],EAX # Set 0000:7BFC to the absolute # block number, where the data # area starts (save EAX) 801F: 66 c7 46 f8 ffffffff movl [BP-8],0xffffffff # Set 0000:7BF8 to ffff:ffff data_cluster_calc: 8027: fa cli # Disable interrupts 8028: 66 8b 46 2c mov EAX,[BP+0x2c] # Set EAX to the number of the # first cluster in the data area # (data cluster) 802C: 66 83 f8 02 cmp EAX,2 # Must start with at least 2 8030: 0f 82 cf fc jb 0x7D03 # So if < 2, jump to # invalid_sys_disk (7D03h) 8034: 66 3d f8 ff ff 0f cmp EAX,0x0fffff8 # or if >= 16M 803A: 0f 83 c5 fc jae 0x7D03 # jump to invalid_sys_disk 803E: 66 0f a4 c2 10 shld EDX,EAX,0x10 # Just "copy" the upper 16bit of # EAX to DX, so that DX:AX has # the number of the 1st data # cluster 8043: fb sti # Enable interrupts 8044: 52 push DX # Save 1st data cluster number 8045: 50 push AX # to the stack 8046: fa cli # Disable interrupts 8047: 66 c1 e0 10 shl EAX,0x10 # Put back 1st data cluster num 804B: 66 0f ac d0 10 shrd EAX,EDX,0x10 # to EAX 8050: 66 83 e8 02 sub EAX,2 # Decrement by 2 (num of the 1st # data cluster) 8054: 66 0f b6 5e 0d movzbl EBX,[BP+0xd] # EBX=sectors/cluster (7C0dh) 8059: 8b f3 mov SI,BX # Save BX to SI 805B: 66 f7 e3 mul EBX # EDX:EAX=EAX*EBX => number of # the block of the 1st data # cluster relative to the volume # start 805E: 66 03 46 fc add EAX,[BP-4] # Add absolute starting block # number of the data area 8062: 66 0f a4 c2 10 shld EDX,EAX,0x10 # "copy" the upper 16b of EAX # to DX 8067: fb sti # Enable interrupts read_next_clusterblock: 8068: bb 00 07 mov BX,0x700 # Set buffer address for # read_disk to 0000:0700h 806B: 8b fb mov DI,BX # Set DI=0700h 806D: b9 01 00 mov CX,1 # Set number of blocks to read 8070: e8 be fc call 0x7D31 # Call read_disk for reading # the 1st block of the data area 8073: 0f 82 aa fc jc 0x7D21 # On error jump to io_error search_io_sys: 8077: 38 2d cmp [DI],CH # If byte at DI == 0 (unused) 8079: 74 1e je 0x8099 # jump to not_found 807B: b1 0b mov CL,0xb # Set number of chars to compare 807D: 56 push SI # Save SI 807E: be d8 7d mov SI,0x7DD8 # Set index of the string to # compare to 0x7DD8 # ('IO SYS') 8081: f3 a6 repz cmpsb DS:[SI],ES:[DI] # compare 0000:7DD8 with # 0000:0700 , length=11 byte 8083: 5e pop SI # restore SI 8084: 74 19 je 0x809F # On match, jump to read_io_sys 8086: 03 f9 add DI,CX # Clear flags 8088: 83 c7 15 add DI,0x15 # Go forward 21 byte (to the # next 32 byte block) and 808B: 3b fb cmp DI,BX # While DI-0700h < bytes read 808D: 72 e8 jb 0x8077 # jump to search_io_sys 808F: 4e dec SI # Decrement remaining sectors in # current cluster 8090: 75 d6 jnz 0x8068 # If SI != 0 jump to # read_next_clusterblock ; still not found so try to find it via FAT 8092: 58 pop AX # Restore data cluster number 8093: 5a pop DX # to DX:AX and 8094: e8 66 00 call 0x80FD # jump to prepare_read_active_fat 8097: 72 ab jb 0x8044 # If < 0x0fffff8 (not EOC) try # next entry # (see after_fat_read (8108h)) not_found: 8099: 83 c4 04 add SP,4 # "pop" DX and AX 809C: e9 64 fc jmp 0x7D03 # jump to invalid_sys_disk read_io_sys: 809F: 83 c4 04 add SP,4 # "pop" DX and AX 80A2: 8b 75 09 mov SI,[DI+0x9] # High word of the entry’s # first cluster number (entry # bytes 20-21) 80A5: 8b 7d 0f mov DI,[DI+0xf] # Low word of the entry’s # first cluster number (entry # bytes 26-27) 80A8: 8b c6 mov AX,SI # Save both to EAX 80AA: fa cli # Disable interrupts 80AB: 66 c1 e0 10 shl EAX,0x10 # 80AF: 8b c7 mov AX,DI # EAX contains now the 1st # cluster num of the entry 80B1: 66 83 f8 02 cmp EAX,2 # Check cluster num: 80B5: 72 3b jb 0x80F2 # If <= 2 80B7: 66 3d f8 ff ff 0f cmp EAX,0x0fffff8 # Or 80BD: 73 33 jae 0x80F2 # >= 16M # jump to invalid_io_sys 80BF: 66 48 dec EAX # Decrement by 2 (num of 1st 80C1: 66 48 dec EAX # data cluster) 80C3: 66 0f b6 4e 0d movzbl ECX,[BP+0xd] # ECX=sectors/cluster (7C0dh) 80C8: 66 f7 e1 mul ECX # EDX:EAX = starting filename # block num 80CB: 66 03 46 fc add EAX,[BP-4] # add number of block preceding # the data area => EDX:EAX # contains absolute number of # the 1st file block 80CF: 66 0f a4 c2 10 shld EDX,EAX,0x10 # put that into DX:AX 80D4: fb sti # Enable interrupts 80D5: bb 00 07 mov BX,0x700 # Set address of the read buffer # (0000:0700) 80D8: 53 push BX # save it 80D9: b9 04 00 mov CX,4 # Set num of blocks to read (4) 80DC: e8 52 fc call 0x7D31 # Read the first 4 blocks of # IO.SYS 80DF: 5b pop BX # Restore buffer address 80E0: 0f 82 3d fc jc 0x7D21 # If an error occured, jump to # io_error # (80E0-(0xffff-0xfc3d+1)+2) 80E4: 81 3f 4d 5a cmpw [BX],0x5a4d # If file IO.SYS does not start # with 0x4d5a 80E8: 75 08 jne 0x80F2 # jump to invalid_io_sys (80F2h) 80EA: 81 bf 00 02 42 4a cmpw DS:[BX+0x200],4a42h# If IO.SYS offset 512 # starts with 0x424a than 80F0: 74 06 je 0x80F8 # skip next 2 instructions invalid_io_sys: 80F2: be 80 7d mov SI,0x7D80 # Set message offset to 0x7D80 80F5: e9 0e fc jmp 0x7D06 # (01) and jump to # invalid_sys_disk (7D06h) 80F8: ea 00 02 70 00 ljmp 0x200,0x70 # Long jump to 0070:0200 # which is the same as 0000:0900 # i.e. start DOS kernel prepare_read_active_fat: ; calculate FATOffset for cluster AX:DX, which is 4*DX:AX for FAT32 ; (2*DX:AX for FAT16) and its sector number is ; reserved + FATOffset/BytesPerSector ; FAT entry offset is the remainder of FATOffset/BytesPerSector 80FD: 03 c0 add AX,AX # AX+=AX 80FF: 13 d2 adc DX,DX # DX+=DX (adc takes possible # "overflow" from previous add # into account by adding 1 if # CF is set) 8101: 03 c0 add AX,AX # Same thing again, so we got 8103: 13 d2 adc DX,DX # => DX:AX = 4 * DX:AX 8105: e8 18 00 call 0x8120 # call read_active_fat (8120h) after_fat_read: 8108: fa cli # Disable interrupts 8109: 26 66 8b 01 mov EAX,ES:[BX+DI] # Check, 810D: 66 25 ff ff ff 0f and EAX,0x0ffffff # whether 8113: 66 0f a4 c2 10 shld EDX,EAX,0x10 # the end of cluster chain 8118: 66 3d f8 ff ff 0f cmp EAX,0x0ffffff8 # (>= 0x0fff.fff8) reached 811E: fb sti # Enable interrupts 811F: c3 ret # return read_active_fat: 8120: bf 00 7e mov DI,0x7E00 # Set buffer address to 7E00h 8123: fa cli # Disable interrupts 8124: 66 c1 e0 10 shl EAX,0x10 # "Copy" DX:AX 8128: 66 0f ac d0 10 shrd EAX,EDX,0x10 # into EAX 812D: 66 0f b7 4e 0b movzwl ECX,[BP+0xb] # ECX=bytes/sector (7C0Bh) 8132: 66 33 d2 xor EDX,EDX # Zero out EDX 8135: 66 f7 f1 div ECX # EAX/ECX => EAX=FAT sector num # EDX=FAT entry offset 8138: 66 3b 46 f8 cmp EAX,[BP-8] # If EAX == 0xffff.ffff 813C: 74 44 je 0x8182 # jump to end (8182h) 813E: 66 89 46 f8 mov [BP-8],EAX # Save FAT sector num to 7BF8h 8142: 66 03 46 1c add EAX,[BP+0x1c] # Add "hidden blocks" 8146: 66 0f b7 4e 0e movzwl ECX,[BP+0xe] # ECX=reserved sectors 814B: 66 03 c1 add EAX,ECX # EAX=start of FAT area [blocks] 814E: 66 0f b7 5e 28 movzwl EBX,[BP+0x28] # EBX=FAT flags 8153: 83 e3 0f and BX,0xf # Mask active FAT number 8156: 74 16 jz 0x816E # If 1st FAT (== FAT0) is active # jump to 816Eh 8158: 3a 5e 10 cmp BL,[BP+0x10] # If num of active FAT is >= # number of avail FATs (7C10h) 815B: 0f 83 a4 fb jae 0x7D03 # jump to invalid_sys_disk 7D03h 815F: 52 push DX # Save FAT entry offset 8160: 66 8b c8 mov ECX,EAX # ECX=absolute start of FAT sec 8163: 66 8b 46 24 mov EAX,[BP+0x24] # EAX=FAT size in blocks 8167: 66 f7 e3 mul EBX # EDX:EAX=relative starting # block of the active FAT 816A: 66 03 c1 add EAX,ECX # EAX=absolute starting block of # the active FAT 816D: 5a pop DX # Put back FAT entry offset 816E: 52 push DX # Save FAT entry offset 816F: 66 0f a4 c2 10 shld EDX,EAX,0x10 # "distribute" EAX to DX:AX 8174: fb sti # Enable interrupts 8175: 8b df mov BX,DI # Set buffer address for reading 8177: b9 01 00 mov CX,1 # Set number of blocks to read 817A: e8 b4 fb call 0x7D31 # readDisk(1 block at DX:AX to # 0000:7E00) 817D: 5a pop DX # restore FAT entry offset 817E: 0f 82 9f fb jc 0x7D21 # On error jump to io_error end: 8182: fb sti # Enable interrupts 8183: 8b da mov BX,DX # BX=FAT entry offset 8185: c3 ret # return http://onlinedisassembler.com/odaweb/run_hex http://thestarman.pcministry.com/asm/mbr/MSWIN41.htm http://www.unixwiz.net/techtips/x86-jumps.html http://www.ousob.com/ng/asm/ngb272.php http://www.ousob.com/ng/rbrown/ng1d537.php http://stanislavs.org/helppc/bios_data_area.html http://andremueller.gmxhome.de/befehle.html http://www.tu-chemnitz.de/informatik/RA/news/stack/kompendium/vortraege_96/Boot/boot3.html http://subversion.assembla.com/svn/Sodium_Boot/Viejos%20Papers/Disk%20Structure,%20BIOS,%20DOS%20Boot%20Code,%20Assembly%20Language.pdf http://www.thehackademy.net/madchat/vxdevl/vxsrc/MS-Dos/boot.asm