All pastes #1360270 Raw Edit

nasm bootloader

public text v1 · immutable
#1360270 ·published 2009-03-13 23:00 UTC
rendered paste body
;
; Copyright (c) Eike Dehling 2004, all rights reserved
;
; Compile with nasm, write to first sector of a floppy, have fun
;

[BITS 16]
[ORG 0x7c00]

; -------------------------------------
; disk description
; -------------------------------------
	jmp short start	; 3 bytes to e.g. jump
	nop
	db "EikeBoot"	; 8 byte label / OemId
	dw 512		; bytes per sector
	db 1		; sectors per cluster
	dw 1		; size of the bootloader, in sectors
	db 2		; number of copies of the FAT
	dw 224		; number of entries in Root-Dir
	dw 2880 	; 16-bit number of sectors
	db 0xf0		; media descriptor
	dw 9		; number of sectors per FAT
	dw 18		; sectors per track
	dw 2		; number of heads
	dd 0		; number of hidden sectors
	dd 0		; 32-bit number of sectors
	db 0		; bios drive number
	db 0		; reserved
	db 0x29 	; extended boot signature
	dd 0		; volume ID
	db "NO NAME    "; volume label
	db "FAT12   "	; filesystem type

; -------------------------------------
; bootloader code
; -------------------------------------
print_io_error_trampoline:
	jmp print_io_error	; out of range crap :(

start:
	; set segments, bios may set 0x07c0:0x0000 or 0x0000:0x7c00
	jmp 0:start2
start2: mov ax, 0
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7000

	; save boot device
	mov byte [bootdev], dl
	
	; reset disks 
	mov ah, 0
	mov dl, byte [bootdev]
	int 0x13

	; load FAT
	mov ah, 0x02			; read from disk
	mov al, 9			; number of sectors to read
	mov bx, fatbuf			; es: bx = buffer
	mov ch, 0			; track ((1 / 18) / 2)
	mov cl, 2			; sector (1 % 18 + 1)
	mov dh, 0			; head ((1 / 18) % 2)
	mov dl, byte [bootdev]		; dl = device, but that's still correct
	int 0x13
	jc print_io_error_trampoline

	; load root directory
	mov ah, 0x02			; read from disk
	mov al, 14			; number of sectors to read
	mov bx, rootdir			; es: bx = buffer
	mov ch, 0			; track ((19 / 18) / 2)
	mov cl, 2			; sector (19 % 18 + 1)
	mov dh, 1			; head ((19 / 18) % 2)
	mov dl, byte [bootdev]		; dl = device, but that's still correct
	int 0x13
	jc print_io_error_trampoline
	
	; find file
	mov ax, rootdir			; ax = pointer to current dir-entry
	cld

find_start:
	mov si, ax			; point si to current filename
	mov di, filename		; point di to the filename we need
	mov cx, 11			; compare max 11 bytes

	repe cmpsb
	je found_file
	
	add ax, 32			; done yet?
	cmp ax, rootdir_end
	jne find_start

	; print error and halt
	mov si, file_not_found
	call print
	jmp $

found_file:
	; save cluster
	mov si, ax
	mov ax, word [si + 26]		; load cluster of file from the directory entry
	mov word [cluster], ax

	; decode FAT
	mov cx, 512*(9/3)		; number of entries in the fat.
	mov si, fatbuf
	mov di, fatdecoded

	;
	; XXX - WATCH OUT, THIS REALLY NEEDS TO USE EAX, EBX & CO!!!
	;
fat_decode_loop:		; load dword, split into two pieces of 12 bits, and discard last byte
	lodsd				; load dword
	dec si				; need only 3 bytes
	mov ebx, eax
	and eax, 0xFFF			; mask
	stosw
	mov eax, ebx
	shr eax, 12			
	and eax, 0xFFF			; shift & mask
	stosw
	loop fat_decode_loop
	
	; load file:
	;	es:bx = buffer
	;	ds:si = decoded fat buffer
	;	rest is for temporary usage only	
	
	; prepare buffer
	mov ax, 0x1000
	mov es, ax
	mov bx, 0
	mov ax, word [cluster]

load_loop:
	; calculate next cluster
	mov si, ax
	shl si, 1
	mov cx, word [fatdecoded + si]
	mov word [cluster], cx
	
	; calculate track, head and sector
	add ax, 31			; ax = logical sector
	mov dx, 0
	mov di, 18
	div di
	mov cl, dl
	add cl, 1			; cl = sector = (logical % 18 + 1)
	mov dx, 0
	mov di, 2
	div di
	mov ch, al			; ch = track = ((logical / 18) / 2)
	mov dh, dl			; dh = head = ((logical / 18) % 2)

	; read data
	mov ah, 0x02
	mov al, 1
	mov dl, byte [bootdev]
	int 0x13
	jc print_io_error
	add bx, 512

	; done?
	mov ax, word [cluster]
	cmp ax, 0xFF8
	jb load_loop

	; DEBUG: print loaded file && halt
	;mov si, 0
	;mov di, 0
	;mov ax, 0x1000
	;mov ds, ax
	;mov ax, 0xb800
	;mov es, ax
	;mov cx, 2000
	;mov al, ' '
	;print_file_loop:
	;movsb
	;stosb
	;loop print_file_loop
	;jmp $

	; execute file?
	; jmp 0x1000:0x0000
	; jmp $

	; disable interrupts
	cli

	; load our GDT
	lgdt [gdt]
	
	; enable protected mode  ...
	mov eax, cr0
	or eax, 1
	mov cr0, eax
	jmp 0x08 : dword cs_flush	; jump into 32-bit

[BITS 32]
cs_flush:

	; setup segements
	mov eax, 0x10
	mov ds, eax
	mov es, eax
	mov ss, eax
	mov fs, eax
	mov gs, eax

	; make stack-register p-mode compliant
	shl esp, 4

	; clear flags
	push dword 0
	popf

	; Execute loaded file ...
	jmp 0x10000

[BITS 16]
; -------------------------------------
; procedures
; -------------------------------------

print_io_error:
	mov si, io_error
	call print
	jmp $

	; print zero-terminated string (without formatting)
	;	ds:si = string
	;	es:di = position on screen
print:
	mov ax, 0xb800
	mov es, ax
	mov di, 0
print_loop:
	lodsw
	cmp al, 0
	jz print_done
	stosw
	jmp print_loop
print_done:
	ret

; -------------------------------------
; data & variables (not in a separate section, cause that makes padding to the
; 	512 byte border easier)
; -------------------------------------

	; filename
	filename: db "KERNEL  BIN"

	; messages
	io_error:		db "I / O   e r r o r ", 0
	file_not_found:	db "F i l e   n o t   f o u n d ", 0
	executing:		db "E x e c u t i n g . . . ", 0

gdt:
	dw (gdtend - gdt)
	dd gdt
	dw 0						; first entry is our gdt-pointer
	dd 0x0000ffff, 0x00cf9a00	; second entry: code
	dd 0x0000ffff, 0x00cf9200	; third entry: data
	gdtend:						; end

	; boot signature
	resb 510 - ($ - $$) ; align at 512 bytes
	dw 0xAA55

; -------------------------------------
; BSS data (starts at 0x8000, that leaves 32k space in the first 64k segment)
; -------------------------------------

[ABSOLUTE 0x8000]
	
	; boot device
	bootdev: resb 1
	
	; first cluster of the file we need
	cluster: resw 1
	
	; FAT buffer
	fatbuf: resb 9 * 512

	; root directory
	rootdir: resb 224 * 32
	rootdir_end:

	; buffer for decoding the FAT
	fatdecoded: resb (9 * 512 * 3) / 2