VBR, MSWIN4.1, en, FAT16 ======================== what sectors sum absolute address hidden 63 63 63 0000 reserved 2 2 65 7E00 fat 2*247 494 559 8200 / dirs 512*32/512 32 591 45E00 1st cluster 1 1 592 49E00 IO.SYS 7C00: eb 3c # jump to 7C3Eh 7C02: 90 nop 7C03: 4d 53 57 49 4e 34 2e 31 # OEM ID: MSWIN4.1 ==START BPB== 7C0B: 00 02 # bytes/sector: 512 (0x0200) 7C0D: 01 # sectors/cluster: 1 7C0E: 02 00 # reserved sectors: 2 (0x0002) 7C10: 02 # FAT count: 2 7C11: 00 02 # max. / dir entries_16: 512 (0x0200) 7C13: e1 f8 # volume size_16: 63713 (0xf8e1) 7C15: f8 # media type: 0xf8 (fixed disk) 7C16: f7 00 # FAT size_16: 247 sectors (0x00f7) 7C18: 3f 00 # sector/track: 63 (0x003f) 7C1A: ff 00 # heads: 255 (0x00ff) 7C1C: 3f 00 00 00 # hidden sectors: 63 (0x003f) 7C20: 00 00 00 00 # volume size_32: 0 7C24: 80 # drive number (00=floppy) 7C25: 00 # reserved (used by NT) 7C26: 29 # extended boot signature 7C27: 21 91 55 b4 # volumeID: B455-9121 7C2B: 4e 4f 20 4e 41 4d 45 20 20 20 20 # Volume label: 'NO NAME ' 7C36: 46 41 54 31 36 20 20 20 # filesystemID: 'FAT16 ' ==END BPB== ==BOOTLOADER START== 7C3E: fa cli # Disable interrupts 7C3F: 33 c9 xor CX,CX # Zero out CX, 7C41: 8e d1 mov SS,CX # and SS 7C43: bc fc 7b mov SP,0x7bfc # Set StackPointer to 0000:7BFC 7C46: 16 push SS # Zero out ... 7C47: 07 pop ES # ES 7C48: 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. 7C4B: c5 76 00 lds SI,[BP] # Load the content from 0000:0078 into 7C4E: 1e push DS # DS:SI (upper 16b into DS, lower 16b 7C4F: 56 push SI # into SI) => points now to the ROM DPT 7C50: 16 push SS # Save SS and 7C51: 55 push BP # BP 7C52: bf 22 05 mov DI,0x522 # Set DI to 0522h (DOS DPT) 7C55: 89 7e 00 mov [BP],DI # Re-route: i.e. let 0000:0078 point to 7C58: 89 4e 02 mov [BP+2],CX # 0000:0522h (CX:DI) 7C5B: b1 0b mov CL,0xb # Set number of bytes to move (CX=11) 7C5D: fc cld # Clear direction flag => increment SI # as well as DI after each movsb 7C5E: 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. 7C60: 06 push ES # Set DataSegment back 7C61: 1f pop DS # to 0000 and 7C62: bd 00 7c mov BP,0x7c00 # BP to 7C00h (this VBR[0] copy) 7C65: c6 45 fe 0f mov [DI-2],0xf # Set head settle time in the DOS DTP # to 15ms (0x52D-2 = 0x52B) 7C69: 8b 46 18 mov AX,[BP+0x18] # Copy sectors/track from BPB (0x7C18h) 7C6C: 88 45 f9 mov [DI-7],AL # to the DOS DTP (0x52D-7 = 0x526) 7C6F: 38 4e 24 cmp [BP+0x24],CL # Check, whether the drive number in the 7C72: 7d 22 jge 0x7C96 # BPB (7C24h) denotes a valid device, # i.e. >= 0. Since this is usually the # case, jump to prepare_read dead_code_fillbytes: 7C74: 8b c1 mov AX,CX # 7C76: 99 cwd # 7C77: e8 77 01 call 0x7DF0 # 7C7A: 72 1a jb 0x7C96 # 7C7C: 83 eb 3a sub BX,0x3a # 7C7F: 66 a1 1c 7c mov EAX,DS:0x7c1c # 7C83: 66 3b 07 cmp eax,[BX] # 7C86: 8a 57 fc mov DL,[BP-4] # 7C89: 75 06 jne 0x7C91 # 7C8B: 80 ca 02 or DL,2 # 7C8E: 88 56 02 mov [BP+2],DL # 7C91: 80 c3 10 add BL,0x10 # 7C94: 73 ed jae 0x7C83 # ; end of dead_code_makes_no_sense prepare_read: 7C96: 33 c9 xor CX,CX # Zero out CX 7C98: 8a 46 10 mov AL,[BP+0x10] # Set AX=number of FATs (7C10) 7C9B: 98 cbw # by converting byte to word 7C9C: f7 66 16 mul [BP+0x16] # Set DX:AX to the size of all FATs in # blocks (7C16h == size of one FAT) 7C9F: 03 46 1c add AX,[BP+0x1c] # Add number of "hidden blocks" (7C1Ch) 7CA2: 13 56 1e adc DX,[BP+0x1e] # and take possible overflow (if CF==1) # into account 7CA5: 03 46 0e add AX,[BP+0xe] # Add number of reserved sectors (incl. 7CA8: 13 d1 adc DX,CX # boot sector 7C0Eh) - usually 1 7CAA: 8b 76 11 mov SI,[BP+0x11] # SI=Max. entries in root dir (7C11h) 7CAD: 60 pusha # save all registers 7CAE: 89 46 fc mov [BP-4],AX # Put the absolute block number of the 7CB1: 89 56 fe mov [BP-2],DX # first root dir sector on the stack # (DX:AX) 7CB4: b8 20 00 mov AX,0x20 # AX=32 (bytes per entry) 7CB7: f7 e6 mul SI # Calculate the size of the root dir # region: DX:AX=SI*AX 7CB9: 8b 5e 0b mov BX,[BP+0xb] # BX=bytes/sector (7C0Bh) 7CBC: 03 c3 add AX,BX # Root dir size in sectors gets calced, 7CBE: 48 dec AX # so round up that all entries will fit 7CBF: f7 f3 div BX # and finally set AX to the root dir sz # in blocks (AX=AX/BX) 7CC1: 01 46 fc add [BP-0x4],AX # Add root dir size to the absolute num 7CC4: 11 4e fe adc [BP-0x2],CX # of the first root dir block stored on # the stack at 7BFCh and clear flags 7CC7: 61 popa # Restore all register read_next_clusterblock: 7CC8: bf 00 07 mov DI,0x700 # Set read buffer address to 0000:0700 7CCB: e8 23 01 call 0x7DF0 # Jump to mark_and_read (next block) 7CCE: 72 39 jc 0x7D09 # On error jump to io_error search_io_sys: 7CD0: 38 2d cmp [DI],CH # If the byte at DI == 0 (unused) 7CD2: 74 17 je 0x7CEB # jump to invalid_sys_disk 7CD4: 60 pusha # Save all registers 7CD5: b1 0b mov CL,0xb # Set number of chars to compare 7CD7: be d8 7d mov SI,0x7dd8 # Set index of the string to compare to # 0x7DD8 ('IO SYS') 7CDA: f3 a6 repz cmps DS:[SI],ES:[DI] # compare 0000:7DD8 with 0000:0700, # length=11 byte 7CDC: 61 popa # Restore all registers 7CDD: 74 39 je 0x7D18 # jump to read_io_sys 7CDF: 4e dec SI # Decrement remaining dir entries 7CE0: 74 09 jz 0x7CEB # If SI == 0 jump to invalid_sys_disk 7CE2: 83 c7 20 add DI,0x20 # Set current position to the next # root dir entry 7CE5: 3b fb cmp DI,BX # While DI-0700h < bytes read 7CE7: 72 e7 jb 0x7CD0 # jump to search_io_sys (7CD0h) 7CE9: eb dd jmp 0x7CC8 # Jump to read_next_clusterblock # function displayError invalid_sys_disk: 7CEB: be 7f 7d mov SI,0x7D7F # SI=message offset in 0x7D7F (0x3) # => 7D83h '\r\nUngueltiges System' print_msg: 7CEE: ac lods AL,DS:[SI] # Load the msg offset into DS:AL 7CEF: 98 cbw # Convert AL to AX keeping the sign bit 7CF0: 03 f0 add SI,AX # Add the message offset to SI print_next_line: 7CF2: ac lods AL,DS:[SI] # Load string byte at DS:SI into AL 7CF3: 84 c0 test AL,AL # If AL == 00 (end of messages) 7CF5: 74 17 je 0x7D0E # jump to wait_for_keystroke 7CF7: 3c ff cmp AL,0xff # If AL == 0xff (EndOfLine marker) 7CF9: 74 09 je 0x7D04 # jump to replace_disk 7CFB: b4 0e mov AH,0xe # Set function teletype output 7CFD: bb 07 00 mov BX,0x7 # Set color to white (0x7) 7D00: cd 10 int 0x10 # Call INT10 to print out the char # in AL, advance the cursor and scroll # screen if necessary 7D02: eb ee jmp 0x7CF2 # jump to print_next_line replace_disk: 7D04: be 82 7d mov SI,0x7d82 # SI=load msg offset in 0x7D82 (0x27) # => 7DAAh '\r\nDatentraeger wechseln \ # und Taste druecken\r\n' 7D07: eb e5 jmp 0x7CEE # jump to print_msg io_error: 7D09: be 80 7d mov SI,0x7d80 # SI=load msg offset in 0x7d80 (0x18) # => 7D99h '\r\nE/A-Fehler ' 7D0C: eb e0 jmp 0x7CEE # jump to print_msg wait_for_keystroke: 7D0E: 98 cbw # Convert byte to word 7D0F: cd 16 int 0x16 # Get keystroke from keyboard 7D11: 5e pop SI # Restore the vector address 7D12: 1f pop DS # of the ROM-DPT 7D13: 66 8f 04 pop [SI] # to 0000:0078 (and clean up the stack) 7D16: cd 19 int 0x19 # Call BIOS "bootstrapper" (reboot) # without clearing memory and restoring # interrupt vectors read_io_sys: 7D18: be 81 7d mov SI,0x7d81 # SI=0x7d81 7D1B: 8b 7d 1a mov DI,[DI+0x1a] # Set DI to the address of the entry's # 1st cluster number (entry byte 26-27) 7D1E: 8d 45 fe lea AX,[DI-2] # Load the 1st cluster number of the # file into AX and decrement by 2 # (1st data cluster has number 2) 7D21: 8a 4e 0d mov CL,[BP+0xd] # CL=sectors/cluster (7C0Dh) 7D24: f7 e1 mul CX # DX:AX==block number of the 1st file # block relative to the data area start 7D26: 03 46 fc add AX,[BP-4] # Add number of blocks preceding data 7D29: 13 56 fe adc DX,[BP-2] # area => DX:AX == absolute number of # the 1st file block 7D2C: b1 04 mov CL,4 # Set num of blocks to read (4) 7D2E: e8 c1 00 call 0x07F2 # Jump to set_read_buffer_addr # (0x7D2E+0xc1+3) (and read 4 blocks of # IO.SYS) 7D31: 72 d6 jc 0x7D09 # On error jump to io_error 7D33: ea 00 02 70 00 jmp 0070:00200 # Jump to 0070:0200 which is the same as # 0000:0900, i.e. start DOS read_disk: ; prepare the disk address paket (DAP) for INT13 42h (extended read) ; AH: extended read sector function (42h) ; DS:SI: address of the DAP 7D38: b4 42 mov AH,0x42 # Set INT13 function to use to ext read 7D3A: eb 2d jmp 0x7D69 # jump to do_read (0x7D3A+0x2d+2) read_next_block: 7D3C: 60 pusha # Save all registers ; DAP create starts 7D3D: 66 6a 00 push 0 # absolut block number, where to start 7D40: 52 push DX # reading: 0:0:DX:AX (QWORD) 7D41: 50 push AX # 7D42: 06 push ES # buffer addr, where to store bytes read 7D43: 53 push BX # = ES:BX (same for normal read) 7D44: 6a 01 push 0x1 # number of blocks to read 7D46: 6a 10 push 0x10 # length of the DAP in bytes: 16 ; DAP create ends 7D48: 8b f4 mov SI,SP # remember DAP starting position 7D4A: 74 ec je 0x7D38 # jump to read_disk 0x7D4A-(256-0xec)+2 ; 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: 7D4C: 91 xchg CX,AX # this..7D64h: calculate CHS values 7D4D: 92 xchg DX,AX # 7D4E: 33 d2 xor DX,DX # zero out DX 7D50: f7 76 18 div [BP+0x18] # devide by sectors/track (7C18h) 7D53: 91 xchg CX,AX # 7D54: f7 76 18 div [BP+0x18] # devide by sectors/track (7C18h) 7D57: 42 inc DX # 7D58: 87 ca xchg DX,CX # 7D5A: f7 76 1a div [BP+0x1a] # divide by num of heads (7C1Ah) 7D5D: 8a f2 mov DH,DL # Set head number (DL gets later # overwritten with drive number) 7D5F: 8a e8 mov CH,AL # Set starting cylinder (lower 8b) and 7D61: c0 cc 02 ror AH,0x2 # set the upper 2b to bits 7:6 in the 7D64: 0a cc or CL,AH # starting sector field 7D66: b8 01 02 mov AX,0x201 # Set function to "Read sectors into # memory" (AH=2) and number of sectors # to read 1 (AL=1) do_read: 7D69: 8a 56 24 mov DL,[BP+0x24] # Set drive number to read (7C40h) 7D6C: cd 13 int 0x13 # Read the sector into mem at ES:BX 7D6E: 8d 64 10 lea SP,[SI+0x10] # "Remove" the DAP from the stack by # changing the "start" of the stack 7D71: 61 popa # Restore all registers 7D72: 72 0a jc 0x7D7E # If an error occured (CF is set) jump # to return (0x7D72+0xa+2) 7D74: 40 inc AX # Increment the absolute number of block # to read next 7D75: 75 01 jnz 0x7D78 # If increment did not cause an overflow # skip next instruction 7D77: 42 inc DX # Take AX overflow into account 7D78: 03 5e 0b add BX,[BP+0xb] # Increment position, where to store # next bytes to read by bytes/sector 7D7B: 49 dec CX # Decrement number of blocks to read 7D7C: 75 77 jnz 0x7DF5 # Jump to jump_to_read_next # (0x7D7C+0x77+2) return: 7D7E: c3 ret # return # message strings and filenames (string table) 7D7F: 03 # message offset wrt. to this address 7D80: 18 # message offset wrt. to this address 7D81: 01 # unused 7D82: 27 # message offset wrt. to this address 7D83: 0d 0a 49 6e 76 61 6c 69 # '\r\nInvalid system disk' 64 20 73 79 73 74 65 6d 20 64 69 73 6b ff 7D99: 0d 0a 44 69 73 6b 20 49 # '\r\nDisk I/O error' 2f 4f 20 65 72 72 6f 72 ff 7DAA: 0d 0a 52 65 70 6c 61 63 65 # '\r\nReplace the disk, and then \ 20 74 68 65 20 64 69 73 6b # press any key\r\n' 2c 20 61 6e 64 20 74 68 65 6e 20 70 72 65 73 73 20 61 6e 79 20 6b 65 79 0d 0a 7DD6: 00 # End of messages Marker 7DD7: 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' # string table end 7DEE: 7f # unused mark_and_read: 7DF0: 01 00 add [BX+SI],AX # Put a marker on the next pos. to read 7DF1: 41 inc CX # Increment blocks2read set_read_buffer_addr: 7DF2: bb 00 07 mov bx,0x7000 # Set read buffer address jump_to_read_next: 7DF5: 80 7e 02 0e cmp [BP+2],0xe # If jumpCode[2] != 14 7DF9: e9 40 ff jmp 0x7D3C # jump to read_next_block # (0x7DF9-(0x10000-0xff40)+3) 7DFC: 00 00 # unused 7DFE: 55 aa # 2 byte boot signature