Try the following boot record. It can be compiled with NASM and is fully functional. Currently it loads the kernel at 0x100000, and must be called KERNEL.BIN in the floppy root directory. It's based in a ZIP file called
bootf02.zip.
It enables Unreal Mode (to forget greatly about segment issues and being able to address the full 4GB from RM) and Protected Mode, enables A20 address line, only a code and data selectors, and no paging.
Since it's FAT12-compliant, you just have to copy this MBR to a standard 1.4Mb floppy and copy the kernel file. You must name it "KERNEL.BIN" or "kernel.bin". If you want to use another 8.3 name, you will have to change the 11-byte
kernelFile variable.
To change the address where the kernel is copied and control is passed on, you must change the value of the
__MyKernelPlainEntry__ constant.
Since the original boot tutorial was free to use, the following can be used freely as well.
Code: Select all
org 7C00h
__MyKernelPlainEntry__ equ 0x100000
_jmp: jmp short start
_nop: nop
_OEMid db " "
_bytesPerSect dw 0200h
_sectsPerClus db 001h
_resrvedSects dw 0001h
_numOfFATs db 002h
_numRootDirEntries dw 00E0h
_numSectors dw 0B40h
_mediaType db 0F0h
_numFATsectors dw 0009h
_sectorsPerTrack dw 0012h
_numHeads dw 0002h
_numHiddenSects dd 00000000h
_numSectorsHuge dd 00000000h
_driveNumber db 00h
_reserved db 00h
_signature db 29h
_volumeID db " "
_volumeLabel db " "
_FSType db "FAT12 "
;INIT: 448 free bytes:
;INIT: 448 free bytes:
;INIT: 448 free bytes:
;> cs = 0
;>> dl = drive we were booted from
;INIT: enable protected mode (time 1 out of 2) to load GDT
;INIT: enable protected mode (time 1 out of 2) to load GDT
;INIT: enable protected mode (time 1 out of 2) to load GDT
start: cli ;{0}
lgdt [cs:GDT] ;Load GDT.
mov ecx, CR0 ;Switch to protected mode
inc cx
mov CR0, ecx ;{5}
;END: enable protected mode (time 1 out of 2) to load GDT
;END: enable protected mode (time 1 out of 2) to load GDT
;END: enable protected mode (time 1 out of 2) to load GDT
;;INIT: enable A20
;;INIT: enable A20
;;INIT: enable A20
;registers modified in this INIT--END section:
;AX (EAX)
;registers wich values to reuse in the next INIT--END:
;AH (will be 0)
.5: in al, 0x64 ;Enable A20 {4A}.
test al, 2 ;see if bit 1 returned from port
;0x64 is 1, which means that
;controller is not ready
jnz .5 ;repeat until bit 1 from 0x64 is cleared to 0,
;indicating the keyboard controller is ready
mov al, 0xD1 ;*WRITE OUTPUT PORT* command
out 0x64, al ;send it
.6: in al, 0x64 ;read port 0x64
and ax, byte 2 ;see if bit 1 is set to 0
;NOTE: it will leave AH set to 0 at once
; to be used in the next INIT--END block.
jnz .6 ;repeat until keyboard controller is ready
;(bit 1 cleared to 0)
mov al, 0xDF ;configure keyb. controller bit-set
out 0x60, al ;send it to data port, to enable A20
;;END: enable A20
;;END: enable A20
;;END: enable A20
;;INIT: register/memory parameters configuration
;;INIT: register/memory parameters configuration
;;INIT: register/memory parameters configuration
;registers to modify in this INIT--END section:
;AX (EAX) 0
;DS 0
;ES 0x800
;SS 0
;SP 0x800
;registers with values to reuse in the next INIT--END block:
;apparently ALL of the above
;> ah = 0
;> dl = drive we were booted from
mov al,Data32
mov ds, ax ;{2} Extend limit for ds.
mov es, ax ;Extend limit for es.
dec cx ;Switch back to and into (Un)Real mode.
mov CR0, ecx ;{5}
;Here we are in (Un)Real Mode:
;Here we are in (Un)Real Mode:
;Here we are in (Un)Real Mode:
mov [_jmp], dl ;Save drive number we came from.
mov sp, 0x800 ;Configure stack end to 0x800
xor eax, eax ;Segment. 32 bits cleared to 0
mov ds, ax ;DS (Data Segment) cleared to 0
mov ss, ax ;SS (Stack Segment) cleared to 0
mov es, sp ;Read directory at 800:0 {1C}. ES
;(Extra Segment) 0x800.
;;END: register/memory parameters configuration
;;END: register/memory parameters configuration
;;END: register/memory parameters configuration
;;INIT: configure floppy information
;;INIT: configure floppy information
;;INIT: configure floppy information
;> eax = 00000000
;We keep in Unreal Mode from the previous INIT--END:
;We keep in Unreal Mode from the previous INIT--END:
;We keep in Unreal Mode from the previous INIT--END:
mov al, [_numOfFATs] ;Number of FATs
mul byte [_numFATsectors] ;Times size of FAT |(in sectors?)
add ax, [_resrvedSects] ;Plus Sectors before first FAT
;(_numOfFATs*_numFATsectors)+_resrvedSects
;eax = LBN of Root directory
movzx edi,word [_numRootDirEntries] ;Root directory entries
push di ; used again later
dec di ;Convert to number of sectors
shr di, 4 ;16 directory entries per sector
inc di
call read_sectors ;call forward
;;END: configure floppy information
;;END: configure floppy information
;;END: configure floppy information
;;INIT: look for 8.3 name in the floppy root directory
;;INIT: look for 8.3 name in the floppy root directory
;;INIT: look for 8.3 name in the floppy root directory
;> eax = LBN of root directory
;> edi = length of root directory in sectors
;> [sp] = length of root directory in entries
;> esi = 00000000
;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
lea ebp, [eax+edi] ;ebp = LBN of cluster 2
pop bx ;Root directory entries.
xor di, di ;Point at directory {1C}
.20: mov si, kernelFile ;Name of file we want.
xor ecx, ecx
mov cl, 11 ;bytes in string "KERNEL BIN"
a32 rep cmpsb ;Found the file?.
;we use the 11 in CL
je found ;Yes
add cl, 21 ;Offset to next directory entry
add edi, ecx ;Advance to next entry
dec bx ;Loop through all entries
jnz .20
;Couldn't find file in directory
boot_error:
disk_error:
mov ax, 0xE07 ;{3}
int 10h ;Ring the bell on error...
jmp short $ ;end with infinite loop
;;END: look for 8.3 name in the floppy root directory
;;END: look for 8.3 name in the floppy root directory
;;END: look for 8.3 name in the floppy root directory
;;INIT: read file according what the previous INIT--END found
;;INIT: read file according what the previous INIT--END found
;;INIT: read file according what the previous INIT--END found
;>> ecx = 00000000
;> es = 800
;> es:edi = Directory entry of file
;> ebp = LBN of cluster 2
;> eax = 0000????
found: push word [es:edi+0xF] ;Starting cluster of file
mov di, [_numFATsectors] ;Size of FAT (in sectors)
mov ax, [_resrvedSects] ;LBN of FAT
call read_sectors ;call forward
mov bx, 0x4000
mov es, bx ;es = 0x4000
mov edi, __MyKernelPlainEntry__-0x40000 ;{1D}{4B} One megabyte minus ES base
;;END: read file according what the previous INIT--END found
;;END: read file according what the previous INIT--END found
;;END: read file according what the previous INIT--END found
;;INIT: final processes and control transfer to kernel entry point
;;INIT: final processes and control transfer to kernel entry point
;;INIT: final processes and control transfer to kernel entry point
.10:
;>> ecx = 0000????
;> [sp] = Next cluster of file
;> esi = 0000????
;>> edx = 0000????
;> es:edi = Destination address
;> ebp = LBN of cluster 2
;> ds = 0
xor eax, eax
pop si ;Next cluster of file
mov bx, si
cmp si, 0xFF8 ;Valid cluster?
jae eof ;No: assume end of file
;Yes: (c-bit set)
rcr bx, 1 ;bx = 0x8000 + cluster/2
mov bx, [bx+si] ;Get word containing FAT entry
jnc .11 ;Entry is low 12 bits
shr bx, 4 ;Entry was high 12 bits
.11: and bh, 0xF ;Mask to just 12 bits
push bx ;Save cluster after next
push di ;Save destination address {7}
mov al, [_sectsPerClus] ;Size of each cluster
mov di, ax ; (in sectors)
dec si
dec si
mul esi ;Times cluster number minus 2
add eax, ebp ;Plus LBN of cluster 2
call read_sectors ;Read that cluster
;> ecx = 0000????
;>> edx = 0000????
;> di = Clustersize in sectors
;> esi = 0
;>> ebp = LBN of cluster 2
;> [sp] = Bottom 16-bits of destination address {7}
;>> [sp+2] = Following cluster
;> ds = 0
;> es = 4000
mov cx, di ;Cluster size in sectors
xchg ch, cl ;Cluster size in words
pop di ;Restore destination address {7}
es a32 rep movsw
jmp short .10 ;Loop until end of file
;> eax = 0
;> edx = 0000????
;> bx = 0FF?
eof:
.20:
int 8 ;{8}
loop .20
cli ;{6}
mov eax, CR0 ;Turn on protected mode
or eax, 0x00000001
mov CR0, eax
mov cl, Data32 ;Setup ds and es
push cx ;{5}
pop ds
mov es, cx
a32 jmp dword Code32:__MyKernelPlainEntry__ ;Go
;;END: final processes and control transfer to kernel entry point
;;END: final processes and control transfer to kernel entry point
;;END: final processes and control transfer to kernel entry point
;;INIT: read_sectors
;;INIT: read_sectors
;;INIT: read_sectors
read_sectors:
; Input:
; EAX = LBN
; DI = sector count
; ES = segment
; Output:
; EBX high half cleared
; DL = drive #
; EDX high half cleared
; ESI = 0
; Clobbered:
; BX, CX, DH
push eax
push di
push es
.10: push eax ;LBN
cdq ;edx = 0
movzx ebx, byte [_sectorsPerTrack]
div ebx ;EAX=track ;EDX=sector-1
mov cx, dx ;CL=sector-1 ;CH=0
sub bl, dl ;BX = max transfer before end of track
cmp di, bx ;Do we want more than that?
ja .20 ;Yes, do just this much now
mov bx, di ;No, do it all now
.20: mov esi, ebx ;Save count for this transfer.
inc cx ;CL=Sector number
xor dx, dx
mov bl, [_numHeads]
div ebx ;EAX=cylinder ;EDX=head
mov dh, dl ;Head
mov dl, [_jmp] ;Drive number
xchg ch, al ;CH=Low 8 bits of cylinder number; AL=0
shr ax, 2 ;AL[6:7]=High two bits of cylinder
or cl, al ;CX = Cylinder and sector
mov ax, si ;Sector count
mov ah, 2 ;Read
xor bx, bx
push ax
int 13h
pop ax
jnc .30
int 13h ;If at second you don't succeed, give up
jc near disk_error
.30: pop eax
add eax, esi ;Advance LBN
push si
shl si, 5
mov bx, es
add bx, si ;Advance segment
mov es, bx
pop si
sub di, si
ja .10
pop es
pop di
pop eax
xor si, si
ret
;;END: read_sectors
;;END: read_sectors
;;END: read_sectors
kernelFile db 'KERNEL BIN'
;INIT: GDT
;INIT: GDT
;INIT: GDT
GDT:
_SELNulo equ 0 ;WARNING: this sector, besides being the GDT pointer,
GDT_size: ; is the NULL selector.
dw GDTsize
GDT_actualptr:
dd GDT
dw 0x0000
Code32 equ 8
dw 0FFFFh ; bits 0-15 length
dw 00000h ; bits 0-15 base addr
db 0 ; bits 16-23 base addr
db 10011010b ; bits P,DPL,DT & type
db 11001111b ; bits G,D & bits 16-19 length
db 0 ; bits 24-31 base addr
Data32 equ 16
dw 0FFFFh ; bits 0-15 length
dw 00000h ; bits 0-15 base addr
db 0 ; bits 16-23 base addr
db 10010010b ; bits P,DPL,DT & type
db 11001111b ; bits G,D & bits 16-19 length
db 0 ; bits 24-31 base addr
GDT_end:
GDTsize equ (GDT_end-GDT)-1
;END: GDT
;END: GDT
;END: GDT
times (510-($-$$)) db 0
;END: 448 free bytes
;END: 448 free bytes
;END: 448 free bytes
dw 0xAA55