BITS 16 SECTION .text buffer EQU 512 image_size EQU 512 ; ================================================================ ; IBM-PC DISK BOOT LOADER FOR PROTECTED MODE EMBEDDED APPLICATIONS ; ; This boot laoder loads a file called "EMBEDDED.BIN" from ; the same diskette into memory starting at physical address ; zero. Before transferring control to the application, it ; enables the A20 line to make memory above 1MB accessible ; and disables the interrupt system. The application is ; responsible for establishing its own protected mode ; interrupt service routines and for putting the processor ; into protected mode. ; ================================================================ ; ---------------------------------------------------------------- ; Disk Area Sectors ; ----------------------- ---------------------------------- ; Boot Loader (this code) 1 ; FAT #1 sctrs_per_fat ; FAT #2 sctrs_per_fat ; Root Directory (32 * dir_entries) / bytes_per_sctr ; Files Area ..... ; ---------------------------------------------------------------- ORG 0 Boot_Loader: JMP start oem_name: DB 'EMBEDDED' ; Windows writes over these bytes! ; ---------------------------------------------------------------- ; Bios Parameter Block (BPB) ; ---------------------------------------------------------------- bytes_per_sctr: DW 512 ; Bytes per sector sctrs_per_clust:DB 1 ; Sectors per cluster rsvd_sectors: DW 1 ; Reserved sectors (the boot sector) number_fats: DB 2 ; Number of FATs dir_entries: DW 224 ; Number of root-directory entries total_sctrs: DW 2880 ; Total sectors in logical volume media_desc: DB 0F0h ; Media descriptor byte sctrs_per_fat: DW 9 ; Sectors per FAT ; ---------------------------------------------------------------- ; Additional information (MS/DOS 3.0) ; ---------------------------------------------------------------- sctrs_per_track:DW 18 ; Sectors per track number_heads: DW 2 ; Number of heads hidden_sctrs: DW 0 ; Number of hidden sectors ; ---------------------------------------------------------------- ; Additional information (MS/DOS 4.0) ; ---------------------------------------------------------------- DW 0 ; MSW of 'hidden_sctrs' (above) ttl_sctrs_vol: DD 0 ; total log sctrs in log volume phys_drive: DB 0 ; physical drive number DB 0 ; reserved ext_boot_sig: DB 29h ; extended boot signature volume_id: DD 0 ; 32-bit binary volume ID volume_label: DB 'BOOT.LOADER' ; volume label DB '????????' ; reserved ; ---------------------------------------------------------------- ; Additional information written by COPYBOOT.EXE ; ---------------------------------------------------------------- fat_nibbles: DW 0 ; 4-bit Nibbles per FAT entry bytes_per_clust:DW 0 ; Bytes per cluster dir_sector0: DW 0 ; log sctr # of 1st sctr in dir area data_sector0: DW 0 ; log sctr # of 1st sctr in files area dir_sectors: DW 0 ; # directory sectors paras_per_clust:DW 0 ; paragraphs per cluster kb_needed: DW 0 ; total KB memory req'd by loader end_of_chain: DW 0 ; FAT12 => 0FFFH or FAT16 => FFFFH ; ---------------------------------------------------------------- ; Additional data NOT written by COPYBOOT.EXE ; ---------------------------------------------------------------- file2load: DB 'EMBEDDEDBIN' ; Filename: "EMBEDDED.BIN" ; ---------------------------------------------------------------- start: ; Loader routine starts here ; ---------------------------------------------------------------- STI ; Just in case CLD ; Needed by all REP's that follow XOR AX,AX MOV SS,AX ; The ROM BIOS only uses the lower MOV SP,0400h ; half of the interrupt vector table ; Relocate myself to the top of base memory PUSH CS POP DS CALL next ; PUSH offset of 'next' next: POP SI SUB SI,next ; DS:SI = source address INT 12H ; AX = Memory Size in KB SUB AX,[kb_needed+SI] ; Reserve room for loader SHL AX,6 ; AX = target segment MOV ES,AX XOR DI,DI ; ES:DI = target address MOV CX,image_size REP MOVSB PUSH ES MOV AX,continue PUSH AX RETF continue: MOV AX,CS ; Now initialize DS and ES MOV DS,AX ; so that our offsets are MOV ES,AX ; meaningful. MOV DI,[dir_sector0]; Load root directory MOV BP,[dir_sectors]; into the buffer LEA BX,[buffer] CALL Read_Sectors ; (Modifies AX,BX,CX,DX,SI,DI,BP) CALL Find_File ; (Modifies BX,CX,BP,SI) PUSH DI ; DI = 1st cluster # MOV DI,[rsvd_sectors] ; Load FAT into the buffer MOV BP,[sctrs_per_fat] LEA BX,[buffer] CALL Read_Sectors ; (Modifies AX,BX,CX,DX,SI,DI,BP) POP DI MOV BX,0080H ; load address = 0080:0000 MOV ES,BX BL1: CALL Read_Cluster ; (Modifies AX, CX, DX, BP, SI, ES) CALL Next_Cluster ; (Modifies AX, CX, DX) MOV AX,DI AND AX,0FFF0h CMP AX,[end_of_chain] JNE BL1 ; save ending paragraph # for later PUSH ES ; Turn off floppy drive light MOV CX,100 WaitForDrive: INT 08h ; Forced timer tick LOOP WaitForDrive ; Make memory above 1MB accessible CALL Enable_A20 ; The BIOS, its ISRs, and the interrupt vector ; table are no longer needed. Disable interrupts ; before writing over the interrupt vector table ; and the BIOS data area just above it. CLI ; Relocate image to 0000:0000 ; Can't use the stack anymore! POP BP ; retrieve ending src paragraph # MOV AX,0080H ; source segment XOR BX,BX ; destination segment MOV DX,[paras_per_clust] MOV SP,[bytes_per_clust] BL2: MOV CX,SP ; reload byte count MOV DS,AX MOV ES,BX XOR SI,SI XOR DI,DI REP MOVSB ADD AX,DX ADD BX,DX CMP AX,BP JB BL2 ; transfer control to the application DB 0EAh ; An direct intersegment DW 0,0 ; jump to 0000:0000 Find_File: ; ---------------------------------------------------------------- ; Parameters: None ; Returns: DI = Starting Cluster ; Modifies: BX, CX, BP, SI ; ---------------------------------------------------------------- MOV BP,[dir_entries]; #entries to search XOR BX,BX ; start w/entry #0 FF1: CMP BYTE [buffer+BX],0 JE FF3 ; done if rest is unused TEST BYTE [buffer+BX+11],00011000B JNZ FF4 ; skip if dir or vol label LEA SI,[file2load] ; compare filename LEA DI,[buffer+BX] MOV CX,11 REP CMPSB JE FF2 FF4: ADD BX,32 ; BX -> next entry DEC BP ; any more entries? JNZ FF1 FF3: HLT ; file not found FF2: MOV DI,[buffer+BX+1Ah] RET Read_Cluster: ; ---------------------------------------------------------------- ; Parameters: DI = current cluster # (Preserved) ; ES = buffer segment ; Returns: Nothing ; Modifies: AX, CX, DX, BP, ES, SI ; ---------------------------------------------------------------- PUSH DI MOVZX BP,BYTE [sctrs_per_clust] LEA AX,[DI-2] ; 1st cluster in file space is #2 MUL BP ADD AX,[data_sector0] MOV DI,AX ; DI = 1st logical sector XOR BX,BX ; load at offset 0 of segment CALL Read_Sectors ; (Modifies AX,BX,CX,DX,SI,DI,BP) MOV AX,ES ADD AX,[paras_per_clust] MOV ES,AX POP DI RET Next_Cluster: ; ---------------------------------------------------------------- ; Parameters: DI = current cluster # ; Returns: DI = next cluster # ; Modifies: AX, CX, DX ; ---------------------------------------------------------------- MOV AX,DI MUL WORD [fat_nibbles] ; (DX will be zero) SHR AX,1 ; AX = rel offset into FAT entries PUSHF ; CF = 1: Left-Aligned FAT12 entry MOV DI,AX POP AX MOV DI,[buffer+DI] CMP WORD [fat_nibbles],4 ; FAT12 or FAT16? JE NC2 ; If FAT16, we're done. SHR AX,1 ; Get CF JNC NC1 SHR DI,4 ; Left-aligned FAT12 NC1: AND DI,0FFFH NC2: RET Read_Sectors: ; ---------------------------------------------------------------- ; Parameters: DI = 1st logical sector # ; BP = sector count ; ES:BX = load address ; Returns: Nothing ; Modifies: AX, BX, CX, DX, SI, DI, BP ; ---------------------------------------------------------------- MOV SI,3 ; retry count RS1: MOV AX,DI DIV BYTE [sctrs_per_track] XOR DX,DX ; DL = drive 0 SHR AL,1 RCL DH,1 ; DH = head INC AH ; disk sectors start at 1 XCHG AL,AH MOV CX,AX ; CH = track, CL = sector MOV AX,0201H ; Read 1 sector INT 13H JNC RS3 DEC SI ; decrement retry count JNZ RS2 HLT ; halt after 3 attempts RS2: XOR AX,AX ; reset disk... INT 13H JMP RS1 ; ...and try again RS3: MOV SI,3 ; reset retry count ADD BX,[bytes_per_sctr] INC DI ; next logical sector DEC BP ; any more? JNZ RS1 RET Wait8042: ; ---------------------------------------------------------------- ; Wait for 8042 to complete previous command. ; ---------------------------------------------------------------- XOR CX,CX ; CX=65536 for timeout value Wait8042Loop: IN AL,64h ; Read the status port, find TEST AL,00000010b ; out if the buffer is full LOOPNZ Wait8042Loop ; Loop until buffer empty RET Enable_A20: ; ---------------------------------------------------------------- ; Enable the A20 line to make memory above 1MB accessible ; ---------------------------------------------------------------- CALL Wait8042 ; Be sure 8042 is ready JNZ EnableA20Failed MOV AL,~00101110b ; Prepare to Write output port OUT 64h,AL CALL Wait8042 JNZ EnableA20Failed MOV AL,~00100000b ; Enable the A20 line OUT 60h,AL CALL Wait8042 JNZ EnableA20Failed XOR CX,CX ; Command started; give EnableA20Wait: LOOP EnableA20Wait ; it time to complete. RET EnableA20Failed:HLT TIMES 510 - ($-$$) DB 0 DW 0AA55h