Code: Select all
[BITS 16]
[ORG 0x7C00]
ALIGN 4
jmp start ; jump to the code // if any other things a decleared after this
%include 'gdt.inc'
msg_ok db "OK",10,13,0
msg_failed db "Failed!!!",10,13,"Halt CPU",0
msg_enable_a20 db "Enable the A20 Gate...",0
msg_load_kernel db "Load the Kernel...",0
msg_check_cpu db "Check if CPU is >= 368...",0
msg_need_368 db "Failed",10,13,"Sorry your CPU must be a 368 or better",0
bootdrive db 0
kernel_addr dw 0x100
read_sectors db 0x04
putstr:
lodsb ; load the next character
or al,al ; if al = 0 (= character)
jz short putstrd ; return
mov ah, 0x0E ; function 0x0E (display character)
mov bh, 0x00 ; current page
mov bl, 0x07 ; color
int 0x10 ; call display interrupt
jmp putstr
putstrd:
ret
check_cpu:
; i left this out because the message was too long
.386
enable_a20:
cli ; disable interrupts
call wait_key_buf ; wait for kbd buffer to clear
mov al,0xd1 ; tell it we want to write to output port
out 0x64,al
call wait_key_buf ; wait again for kbd to clear
mov al,0xdf ; set desired settings (A20 gate)
out 0x60,al ; send value to data reg
call wait_key_buf ; wait for kbd to clear
mov cx,0x10 ; loop count
kbdwait:
xor ax,ax ; do anything
out 0xe0,ax ; some mor nonsense
loop kbdwait ; loop to waste time
;-------------- check if a20 was enabled --------------
mov al,0xd0
out 0x64,al ; tell kbdc we want to read output port
call wait_key_buf_data ; wait for data to get in it
in al,0x60 ; get it
test al,2 ; test if A20 is on
jnz a20_on ; if it is clear, then it is off
jmp error
a20_on:
ret
wait_key_buf:
xor al,al ; al = 0
in al, 0x64 ; get kbd status
test al, 2 ; is bit 1 clear?
jnz wait_key_buf ; if not wait some more
ret
wait_key_buf_data:
xor cx,cx ; cx = 0
in al, 0x64 ; get kbd status
test al, 1 ; is bit 0 clear?
jz wait_key_buf_data ; if not wait some more
ret
load_kernel:
clc ; clear the carry flag
mov ecx, 5 ; try to reset the driver 5 times
reset_drive:
xor ax, ax ; ax = 0
mov dl, [bootdrive] ; dl = bootdrive
int 0x13 ; reset drive
jnc reset_ok ; if carry flag = 0
loop reset_drive ; else try it again if ecx != 0
jmp error ; if ecx = 0 and the reset always failed, halt
reset_ok:
mov ecx, 5 ; try to read the kernel 5 times
read_kernel:
mov ax, [kernel_addr]
mov es, ax ; address of the buffer
mov ah, 0x02 ; function number 2 (read from disk)
mov al, [read_sectors] ; number of sectors to read
mov dl, [bootdrive] ; set drive
mov dh, 0x00 ; read/write head number
mov ch, 0x00 ; number of the track or cylinder (0 = first)
mov cl, 0x02 ; number of the sector where to start (1 = first)
xor bx, bx ; offset of the buffer
int 0x13
jnc load_ok ; if all is ok return
loop read_kernel ; try once again if there is an error
jmp error ; if ecx = 0 and the read operation always failed, halt
load_ok:
ret
enter_pmode:
cli
lgdt [gdt_desc]
mov eax, cr0 ; enable pmode
or eax, 1
mov cr0, eax
mov eax, DATA_SEL
mov ds, eax
mov es, eax
mov ss, eax
mov ds,eax
mov gs,eax
mov fs,eax
mov ax, STACK_SEL
mov ss,ax
mov esp, 0xFFFF
jmp CODE_SEL:0x1000
cli
hlt
jmp $-2
error:
mov si, msg_failed
call putstr
cli
hlt
jmp $-2
start:
cli
mov [bootdrive], dl ; save the bootdrive
mov ax, 0x9000
mov ss, ax ; setup the stack at 0x9000
mov esp, 0x8000 ; set the stack size
sti
mov ax,0x0000 ; ax = 0
mov es,ax ; correct the segment register
mov ds,ax
call check_cpu
.386
mov si, msg_enable_a20
call putstr
call enable_a20
mov si, msg_ok
call putstr
mov si, msg_load_kernel
call putstr
call load_kernel ; load the kernel
mov si, msg_ok
call putstr
call enter_pmode
cli
hlt
jmp $-2
times 510-($-$$) db 0
db 0x55
db 0xAA