The tutorial link is https://www.cs.bham.ac.uk/~exr/lectures ... os-dev.pdf.
Now I will give you technical details :
Source code of BSECT.ASM (Bootsector):
Code: Select all
[org 0x7c00]
KERNEL_OFFSET equ 0x1000
mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's
; best to remember this for later.
mov bp, 0x9000
mov sp, bp
mov si, MSG_REAL_MODE
call print_string
call load_kernel
call switch_to_pm
jmp $
%include "features/print_string.asm"
%include "features/disk_load.asm"
%include "features/gdt.asm"
%include "features/print_string_pm_OLD.asm"
%include "features/switch_to_pm.asm"
[bits 16]
load_kernel:
mov si, MSG_LOAD_KERNEL
call print_string
mov bx, KERNEL_OFFSET
mov dh, 30
mov dl, [BOOT_DRIVE]
call disk_load
ret
[bits 32]
BEGIN_PM:
mov ebx, MSG_PROT_MODE
call print_string_pm
call KERNEL_OFFSET
jmp $
BOOT_DRIVE:
db 0
MSG_REAL_MODE:
db "Loading 16-bit mode...", 0
MSG_PROT_MODE:
db "Switching to 32-bit mode...", 0
MSG_LOAD_KERNEL:
db "Loading kernel into memory...", 0
times 510-($-$$) db 0
dw 0xaa55
Code: Select all
[bits 32]
jmp main
%include "features/print_string.asm"
%include "features/print_string_pm.asm"
main:
mov bx, KERNEL_LOADED
call print_string
jmp loop
loop:
; Stuff here
jmp loop
KERNEL_LOADED:
db "The kernel has been successfully loaded !", 0
Code: Select all
BSECT.BIN: BSECT.ASM
@echo off
nasm -f bin -o BSECT.BIN BSECT.ASM
cp BSECT.BIN C:\\Users\\LEO\\Desktop\\MinkizzOS\\versions\\1\\compiled
rm BSECT.BIN
nasm -f bin -o KERNEL.BIN KERNEL.ASM
cp KERNEL.BIN C:\\Users\\LEO\\Desktop\\MinkizzOS\\versions\\1\\compiled
rm KERNEL.BIN
I'm sure it is bootable because it print me this message :
"Loading 16-bit mode...Loading kernel into memory..."
That's strange because the 32-bit message don't print.
I will also give you the #include files :
print_string.asm :
Code: Select all
print_string: ; Routine: output string in SI to screen
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
Code: Select all
; load DH sectors to ES:BX from drive DL
disk_load.asm :
push dx ; Store DX on stack so later we can recall
; how many sectors were request to be read ,
; even if it is altered in the meantime
mov ah, 0x02 ; BIOS read sector function
mov al, dh ; Read DH sectors
mov ch, 0x00 ; Select cylinder 0
mov dh, 0x00 ; Select head 0
mov cl, 0x02 ; Start reading from second sector ( i.e.
; after the boot sector )
int 0x13 ; BIOS interrupt
jc disk_error ; Jump if error ( i.e. carry flag set )
pop dx ; Restore DX from the stack
cmp dh , al ; if AL ( sectors read ) != DH ( sectors expected )
jne disk_error ; display error message
ret
disk_error :
mov bx, DISK_ERROR_MSG
call print_string
jmp $
; Variables
DISK_ERROR_MSG db " Disk read error !" , 0
Code: Select all
; GDT
gdt_start :
gdt_null : ; the mandatory null descriptor
dd 0x0 ; ’dd ’ means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code : ; the code segment descriptor
; base=0x0 , limit=0xfffff ,
; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b
; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b
; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data : ; the data segment descriptor
; Same as code segment except for the type flags :
; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end : ; The reason for putting a label at the end of the
; GDT is so we can have the assembler calculate
; the size of the GDT for the GDT decriptor ( below )
; GDT descriptior
gdt_descriptor :
dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one
; of the true size
dd gdt_start ; Start address of our GDT
; Define some handy constants for the GDT segment descriptor offsets , which
; are what segment registers must contain when in protected mode. For example ,
; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the
; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our
; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA )
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
Code: Select all
[BITS 32]
print_string_pm: ; Routine: output string in SI to screen
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
Code: Select all
[bits 32]
; Define some constants
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
; prints a null - terminated string pointed to by EDX
print_string_pm :
pusha
mov edx, VIDEO_MEMORY ; Set edx to the start of vid mem.
print_string_pm_loop :
mov al, [ebx] ; Store the char at EBX in AL
mov ah, WHITE_ON_BLACK ; Store the attributes in AH
cmp al, 0 ; if (al == 0) , at end of string , so
je print_string_pm_done ; jump to done
mov [edx], ax ; Store char and attributes at current
; character cell.
add ebx, 1 ; Increment EBX to the next char in string.
add edx, 2 ; Move to next character cell in vid mem.
jmp print_string_pm_loop ; loop around to print the next char.
print_string_pm_done :
popa
ret ; Return from the function
Code: Select all
[bits 16]
; Switch to protected mode
switch_to_pm :
cli ; We must switch of interrupts until we have
; set -up the protected mode interrupt vector
; otherwise interrupts will run riot.
lgdt [ gdt_descriptor ] ; Load our global descriptor table , which defines
; the protected mode segments ( e.g. for code and data )
mov eax, cr0 ; To make the switch to protected mode , we set
or eax, 0x1 ; the first bit of CR0 , a control register
mov cr0, eax
jmp CODE_SEG : init_pm ; Make a far jump ( i.e. to a new segment ) to our 32 - bit
; code. This also forces the CPU to flush its cache of
; pre - fetched and real - mode decoded instructions , which can
; cause problems.
[bits 32]
; Initialise registers and the stack once in PM.
init_pm :
mov ax, DATA_SEG ; Now in PM , our old segments are meaningless ,
mov ds, ax ; so we point our segment registers to the
mov ss, ax ; data selector we defined in our GDT
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; Update our stack position so it is right
mov esp, ebp ; at the top of the free space.
call BEGIN_PM ; Finally , call some well - known label