forked from Bananymous/banan-os
				
			
			update main #1
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -1,10 +1,3 @@
 | 
			
		|||
# FIXME: don't assume 512 byte sectors
 | 
			
		||||
.set SECTOR_SIZE_SHIFT,	9
 | 
			
		||||
.set SECTOR_SIZE,		1 << SECTOR_SIZE_SHIFT
 | 
			
		||||
 | 
			
		||||
.set SCREEN_WIDTH,	80
 | 
			
		||||
.set SCREEN_HEIGHT,	25
 | 
			
		||||
 | 
			
		||||
.code16
 | 
			
		||||
 | 
			
		||||
#########################################
 | 
			
		||||
| 
						 | 
				
			
			@ -18,166 +11,7 @@
 | 
			
		|||
 | 
			
		||||
.section .stage1
 | 
			
		||||
 | 
			
		||||
stage1_start:
 | 
			
		||||
	jmp stage1_main
 | 
			
		||||
 | 
			
		||||
# prints character to screen
 | 
			
		||||
# al:		ascii character to print
 | 
			
		||||
putc:
 | 
			
		||||
	pusha
 | 
			
		||||
	movb $0x0E, %ah
 | 
			
		||||
	movb $0x00, %bh
 | 
			
		||||
	int $0x10
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# prints null terminated string to screen
 | 
			
		||||
# ds:si:	string address
 | 
			
		||||
puts:
 | 
			
		||||
	pushw %si
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	pushw %ax
 | 
			
		||||
 | 
			
		||||
	movb $0x0E, %ah
 | 
			
		||||
	movb $0x00, %bh
 | 
			
		||||
 | 
			
		||||
 .puts_loop:
 | 
			
		||||
	lodsb
 | 
			
		||||
 | 
			
		||||
	test %al, %al
 | 
			
		||||
	jz .puts_done
 | 
			
		||||
 | 
			
		||||
	int $0x10
 | 
			
		||||
	jmp .puts_loop
 | 
			
		||||
 | 
			
		||||
 .puts_done:
 | 
			
		||||
	popw %ax
 | 
			
		||||
	popw %bx
 | 
			
		||||
	popw %si
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# compares memory between addresses
 | 
			
		||||
# si:		ptr1
 | 
			
		||||
# di:		ptr2
 | 
			
		||||
# cx:		bytes count
 | 
			
		||||
# return:
 | 
			
		||||
#	al: 1 if equal, 0 otherwise
 | 
			
		||||
memcmp:
 | 
			
		||||
	pushw %si
 | 
			
		||||
	pushw %di
 | 
			
		||||
 | 
			
		||||
	cld
 | 
			
		||||
	repe cmpsb
 | 
			
		||||
	setzb %al
 | 
			
		||||
 | 
			
		||||
	popw %di
 | 
			
		||||
	popw %si
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# read sectors from disk
 | 
			
		||||
# bx:eax:	lba start
 | 
			
		||||
# cx:		lba count (has to less than 0x80)
 | 
			
		||||
# dl:		drive number
 | 
			
		||||
# ds:di:	physical address
 | 
			
		||||
# returns only on success
 | 
			
		||||
read_from_disk:
 | 
			
		||||
	push %ax
 | 
			
		||||
	push %si
 | 
			
		||||
 | 
			
		||||
	# prepare disk read packet
 | 
			
		||||
	mov $disk_address_packet, %si
 | 
			
		||||
	movb $0x10,	0x00(%si)	# packet size
 | 
			
		||||
	movb $0x00,	0x01(%si)	# always 0
 | 
			
		||||
	movw %cx,	0x02(%si)	# lba count
 | 
			
		||||
	movw %di,	0x04(%si)	# offset
 | 
			
		||||
	movw %ds,	0x06(%si)	# segment
 | 
			
		||||
	movl %eax,	0x08(%si)	# 32 bit lower lba
 | 
			
		||||
	movw %bx,	0x0C(%si)	# 16 bit upper lba
 | 
			
		||||
	movw $0,	0x0E(%si)	# zero
 | 
			
		||||
 | 
			
		||||
	# issue read command
 | 
			
		||||
	clc
 | 
			
		||||
	mov $0x42, %ah
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .read_failed
 | 
			
		||||
 | 
			
		||||
	pop %si
 | 
			
		||||
	pop %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Validates that GPT header is valid and usable
 | 
			
		||||
# doesn't return on invalid header
 | 
			
		||||
validate_gpt_header:
 | 
			
		||||
	# confirm header (starts with 'EFI PART')
 | 
			
		||||
	cmpl $0x20494645, (gpt_header + 0)
 | 
			
		||||
	jne .not_gpt_partition
 | 
			
		||||
	cmpl $0x54524150, (gpt_header + 4)
 | 
			
		||||
	jne .not_gpt_partition
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Find partition entry with type guid
 | 
			
		||||
# si:	address of type guid
 | 
			
		||||
# dl:	drive number
 | 
			
		||||
# returns:
 | 
			
		||||
#   al: non-zero if found
 | 
			
		||||
#	di: address of partition entry
 | 
			
		||||
find_gpt_partition_entry:
 | 
			
		||||
	# eax := entry_count
 | 
			
		||||
	movl (gpt_header + 80), %eax
 | 
			
		||||
	test %eax, %eax
 | 
			
		||||
	jz .no_gpt_partition_found
 | 
			
		||||
 | 
			
		||||
	# edx:eax := eax * entry_size
 | 
			
		||||
	pushw %dx
 | 
			
		||||
	mull (gpt_header + 84)
 | 
			
		||||
	test %edx, %edx
 | 
			
		||||
	jnz .too_gpt_big_entries
 | 
			
		||||
	popw %dx
 | 
			
		||||
 | 
			
		||||
	# FIXME: read one entry array section at a time
 | 
			
		||||
 | 
			
		||||
	# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
 | 
			
		||||
	movl %eax, %ecx
 | 
			
		||||
	shrl $SECTOR_SIZE_SHIFT, %ecx
 | 
			
		||||
 | 
			
		||||
	# start lba
 | 
			
		||||
	movl (gpt_header + 72), %eax
 | 
			
		||||
	movw (gpt_header + 76), %bx
 | 
			
		||||
 | 
			
		||||
	movw $gpt_entry_data, %di
 | 
			
		||||
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# NOTE: 'only' 0xFFFF partitions supported,
 | 
			
		||||
	#       although read will fail with more than 0x80
 | 
			
		||||
	movw (gpt_header + 80), %cx
 | 
			
		||||
 | 
			
		||||
 .loop_gpt_entries:
 | 
			
		||||
	pushw %cx
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	call memcmp
 | 
			
		||||
	popw %cx
 | 
			
		||||
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jnz .gpt_partition_found
 | 
			
		||||
 | 
			
		||||
	# add entry size to entry pointer
 | 
			
		||||
	addw (gpt_header + 84), %di
 | 
			
		||||
 | 
			
		||||
	loop .loop_gpt_entries
 | 
			
		||||
 | 
			
		||||
 .no_gpt_partition_found:
 | 
			
		||||
	movb $0, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .gpt_partition_found:
 | 
			
		||||
	movb $1, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.global stage1_main
 | 
			
		||||
stage1_main:
 | 
			
		||||
	# setup segments
 | 
			
		||||
	movw $0, %ax
 | 
			
		||||
| 
						 | 
				
			
			@ -189,107 +23,17 @@ stage1_main:
 | 
			
		|||
	movl $0x7C00, %esp
 | 
			
		||||
 | 
			
		||||
	# save boot disk number
 | 
			
		||||
	movb %dl, (boot_disk_number)
 | 
			
		||||
	call read_stage2_into_memory
 | 
			
		||||
 | 
			
		||||
	# FIXME: validate boot disk (needs size optizations)
 | 
			
		||||
 | 
			
		||||
	# confirm that int 13h extensions are available
 | 
			
		||||
	clc
 | 
			
		||||
	movb $0x41, %ah
 | 
			
		||||
	movw $0x55AA, %bx
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .no_int13h_ext
 | 
			
		||||
 | 
			
		||||
	# read gpt header
 | 
			
		||||
	movl $1, %eax
 | 
			
		||||
	movw $0, %bx
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
	movw $gpt_header, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	call validate_gpt_header
 | 
			
		||||
 | 
			
		||||
	movw $bios_boot_guid, %si
 | 
			
		||||
	movb boot_disk_number, %dl
 | 
			
		||||
	call find_gpt_partition_entry
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .no_bios_boot_partition
 | 
			
		||||
 | 
			
		||||
	# BIOS boot partiton found!
 | 
			
		||||
 | 
			
		||||
	# first lba
 | 
			
		||||
	movl 32(%di), %eax
 | 
			
		||||
	movw $0, %bx
 | 
			
		||||
 | 
			
		||||
	# count := last lba - first lba + 1
 | 
			
		||||
	movl 40(%di), %ecx
 | 
			
		||||
	subl %eax, %ecx
 | 
			
		||||
	addl $1, %ecx
 | 
			
		||||
 | 
			
		||||
	# calculate stage2 sector count
 | 
			
		||||
	movw $((stage2_end - stage2_start + SECTOR_SIZE - 1) / SECTOR_SIZE), %cx
 | 
			
		||||
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
	movw $stage2_start, %di
 | 
			
		||||
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
	jmp stage2_main
 | 
			
		||||
 | 
			
		||||
.global print_and_halt
 | 
			
		||||
print_and_halt:
 | 
			
		||||
	call puts
 | 
			
		||||
halt:
 | 
			
		||||
	hlt
 | 
			
		||||
	jmp halt
 | 
			
		||||
 | 
			
		||||
 .no_int13h_ext:
 | 
			
		||||
	mov $no_int13_ext_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .read_failed:
 | 
			
		||||
	mov $read_failed_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .not_gpt_partition:
 | 
			
		||||
	mov $not_gpt_partition_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .no_bios_boot_partition:
 | 
			
		||||
	mov $no_bios_boot_partition_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .too_gpt_big_entries:
 | 
			
		||||
	mov $too_gpt_big_entries_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 21686148-6449-6E6F-744E-656564454649
 | 
			
		||||
bios_boot_guid:
 | 
			
		||||
	.long 0x21686148		# little endian
 | 
			
		||||
	.word 0x6449			# little endian
 | 
			
		||||
	.word 0x6E6F			# little endian
 | 
			
		||||
	.word 0x4E74			# big endian
 | 
			
		||||
	.quad 0x494645646565	# big endian
 | 
			
		||||
 | 
			
		||||
no_int13_ext_msg:
 | 
			
		||||
	.asciz "no INT 13h ext"
 | 
			
		||||
 | 
			
		||||
read_failed_msg:
 | 
			
		||||
	.asciz "read error"
 | 
			
		||||
 | 
			
		||||
not_gpt_partition_msg:
 | 
			
		||||
	.asciz "not GPT"
 | 
			
		||||
 | 
			
		||||
no_bios_boot_partition_msg:
 | 
			
		||||
	.asciz "no bios boot partition"
 | 
			
		||||
 | 
			
		||||
too_gpt_big_entries_msg:
 | 
			
		||||
	.asciz "too big GPT array"
 | 
			
		||||
 | 
			
		||||
boot_disk_number:
 | 
			
		||||
	.skip 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#########################################
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -299,477 +43,6 @@ boot_disk_number:
 | 
			
		|||
 | 
			
		||||
.section .stage2
 | 
			
		||||
 | 
			
		||||
stage2_start:
 | 
			
		||||
 | 
			
		||||
# read a character from keyboard
 | 
			
		||||
# return:
 | 
			
		||||
#	al: ascii
 | 
			
		||||
#	ah: bios scan code
 | 
			
		||||
getc:
 | 
			
		||||
	movb $0x00, %ah
 | 
			
		||||
	int $0x16
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# prints newline to screen
 | 
			
		||||
print_newline:
 | 
			
		||||
	pushw %ax
 | 
			
		||||
	movb $'\r', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $'\n', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	pop %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# prints backspace to screen, can go back a line
 | 
			
		||||
print_backspace:
 | 
			
		||||
	pusha
 | 
			
		||||
 | 
			
		||||
	# get cursor position
 | 
			
		||||
	movb $0x03, %ah
 | 
			
		||||
	movb $0x00, %bh
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
	# don't do anyting if on first row
 | 
			
		||||
	testb %dh, %dh
 | 
			
		||||
	jz .print_backspace_done
 | 
			
		||||
 | 
			
		||||
	# go one line up if on first column
 | 
			
		||||
	test %dl, %dl
 | 
			
		||||
	jz .print_backspace_go_line_up
 | 
			
		||||
 | 
			
		||||
	# otherwise decrease column
 | 
			
		||||
	decb %dl
 | 
			
		||||
	jmp .print_backspace_do_print
 | 
			
		||||
 | 
			
		||||
 .print_backspace_go_line_up:
 | 
			
		||||
	# decrease row and set column to the last one
 | 
			
		||||
	decb %dh
 | 
			
		||||
	movb $(SCREEN_WIDTH - 1), %dl
 | 
			
		||||
 | 
			
		||||
 .print_backspace_do_print:
 | 
			
		||||
	# set cursor position
 | 
			
		||||
	movb $0x02, %ah
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
	# print 'empty' character (space)
 | 
			
		||||
	mov $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	# set cursor position
 | 
			
		||||
	movb $0x02, %ah
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
 .print_backspace_done:
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# print number to screen
 | 
			
		||||
# ax:	number to print
 | 
			
		||||
# bx:	number base
 | 
			
		||||
# cx:	min width (zero pads if shorter)
 | 
			
		||||
printnum:
 | 
			
		||||
	pusha
 | 
			
		||||
	pushw %cx
 | 
			
		||||
 | 
			
		||||
	movw $printnum_buffer, %si
 | 
			
		||||
	xorw %cx, %cx
 | 
			
		||||
 | 
			
		||||
 .printnum_fill_loop:
 | 
			
		||||
	# fill buffer with all remainders ax % bx
 | 
			
		||||
	xorw %dx, %dx
 | 
			
		||||
	divw %bx
 | 
			
		||||
	movb %dl, (%si)
 | 
			
		||||
	incw %si
 | 
			
		||||
	incw %cx
 | 
			
		||||
	testw %ax, %ax
 | 
			
		||||
	jnz .printnum_fill_loop
 | 
			
		||||
 | 
			
		||||
	# check if zero pad is required
 | 
			
		||||
	popw %dx
 | 
			
		||||
	cmpw %cx, %dx
 | 
			
		||||
	jbe .printnum_print_loop
 | 
			
		||||
 | 
			
		||||
	xchgw %dx, %cx
 | 
			
		||||
	subw %dx, %cx
 | 
			
		||||
	movb $'0', %al
 | 
			
		||||
 .printnum_pad_zeroes:
 | 
			
		||||
	call putc
 | 
			
		||||
	loop .printnum_pad_zeroes
 | 
			
		||||
 | 
			
		||||
	movw %dx, %cx
 | 
			
		||||
 | 
			
		||||
 .printnum_print_loop:
 | 
			
		||||
	decw %si
 | 
			
		||||
	movb (%si), %al
 | 
			
		||||
	cmpb $10, %al
 | 
			
		||||
	jae 1f
 | 
			
		||||
	addb $'0', %al
 | 
			
		||||
	jmp 2f
 | 
			
		||||
 1:	addb $('a' - 10), %al
 | 
			
		||||
 2:	call putc
 | 
			
		||||
	loop .printnum_print_loop
 | 
			
		||||
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# test if character is printable ascii
 | 
			
		||||
# al:	character to test
 | 
			
		||||
# sets ZF if is printable
 | 
			
		||||
isprint:
 | 
			
		||||
	cmpb $0x20, %al
 | 
			
		||||
	jb .isprint_done
 | 
			
		||||
	cmpb $0x7E, %al
 | 
			
		||||
	ja .isprint_done
 | 
			
		||||
	cmpb %al, %al
 | 
			
		||||
 .isprint_done:
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# check if drive exists
 | 
			
		||||
#	dl: drive number
 | 
			
		||||
# return:
 | 
			
		||||
#	al: 1 if disk is usable, 0 otherwise
 | 
			
		||||
drive_exists:
 | 
			
		||||
	pusha
 | 
			
		||||
 | 
			
		||||
	movb $0x48, %ah
 | 
			
		||||
	movw $disk_drive_parameters, %si
 | 
			
		||||
	movw $0x1A, (disk_drive_parameters) # set buffer size
 | 
			
		||||
 | 
			
		||||
	clc
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .drive_exists_nope
 | 
			
		||||
 | 
			
		||||
	popa
 | 
			
		||||
	movb $1, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .drive_exists_nope:
 | 
			
		||||
	popa
 | 
			
		||||
	movb $0, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# fills memory map data structure
 | 
			
		||||
# doesn't return on error
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
get_memory_map:
 | 
			
		||||
	movl $0, (memory_map_entry_count)
 | 
			
		||||
 | 
			
		||||
	movl $0x0000E820, %eax
 | 
			
		||||
	movl $0x534D4150, %edx
 | 
			
		||||
	xorl %ebx, %ebx
 | 
			
		||||
	movl $20, %ecx
 | 
			
		||||
	movw $memory_map_entries, %di
 | 
			
		||||
 | 
			
		||||
	clc
 | 
			
		||||
	int $0x15
 | 
			
		||||
	# If first call returs with CF set, the call failed
 | 
			
		||||
	jc .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_rest:
 | 
			
		||||
	cmpl $0x534D4150, %eax
 | 
			
		||||
	jne .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
	# FIXME: don't assume BIOS to always return 20 bytes
 | 
			
		||||
	cmpl $20, %ecx
 | 
			
		||||
	jne .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
	# increment entry count
 | 
			
		||||
	incl (memory_map_entry_count)
 | 
			
		||||
 | 
			
		||||
	# increment entry pointer
 | 
			
		||||
	addw %cx, %di
 | 
			
		||||
 | 
			
		||||
	# BIOS can indicate end of list by 0 in ebx
 | 
			
		||||
	testl %ebx, %ebx
 | 
			
		||||
	jz .get_memory_map_done
 | 
			
		||||
 | 
			
		||||
	movl $0x0000E820, %eax
 | 
			
		||||
	movl $0x534D4150, %edx
 | 
			
		||||
 | 
			
		||||
	clc
 | 
			
		||||
	int $0x15
 | 
			
		||||
	# BIOS can indicate end of list by setting CF
 | 
			
		||||
	jnc .get_memory_map_rest
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_done:
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_error:
 | 
			
		||||
	movw $memory_map_error_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# fills command line buffer
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
get_command_line:
 | 
			
		||||
	movw $command_line_enter_msg, %si
 | 
			
		||||
	call puts
 | 
			
		||||
 | 
			
		||||
	movw $command_line_buffer, %di
 | 
			
		||||
 | 
			
		||||
 .get_command_line_loop:
 | 
			
		||||
	call getc
 | 
			
		||||
 | 
			
		||||
	cmpb $'\b', %al
 | 
			
		||||
	je .get_command_line_backspace
 | 
			
		||||
 | 
			
		||||
	# Not sure if some BIOSes return '\n' as enter, but check it just in case
 | 
			
		||||
	cmpb $'\r', %al
 | 
			
		||||
	je .get_command_line_done
 | 
			
		||||
	cmpb $'\n', %al
 | 
			
		||||
	je .get_command_line_done
 | 
			
		||||
 | 
			
		||||
	call isprint
 | 
			
		||||
	jnz .get_command_line_loop
 | 
			
		||||
 | 
			
		||||
	# put byte to buffer
 | 
			
		||||
	movb %al, (%di)
 | 
			
		||||
	incw %di
 | 
			
		||||
 | 
			
		||||
	# print byte
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	jmp .get_command_line_loop
 | 
			
		||||
 | 
			
		||||
 .get_command_line_backspace:
 | 
			
		||||
	# don't do anything if at the beginning
 | 
			
		||||
	cmpw $command_line_buffer, %di
 | 
			
		||||
	je .get_command_line_loop
 | 
			
		||||
 | 
			
		||||
	# decrement buffer pointer
 | 
			
		||||
	decw %di
 | 
			
		||||
 | 
			
		||||
	# erase byte in display
 | 
			
		||||
	call print_backspace
 | 
			
		||||
 | 
			
		||||
	jmp .get_command_line_loop
 | 
			
		||||
 | 
			
		||||
 .get_command_line_done:
 | 
			
		||||
	# null terminate command line
 | 
			
		||||
	movb $0, (%di)
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# print memory map from memory_map_entries
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
print_memory_map:
 | 
			
		||||
	movw $memory_map_msg, %si
 | 
			
		||||
	call puts
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	movl (memory_map_entry_count), %edx
 | 
			
		||||
	movw $memory_map_entries, %si
 | 
			
		||||
 | 
			
		||||
	movw $16, %bx
 | 
			
		||||
	movw $4, %cx
 | 
			
		||||
 | 
			
		||||
 .loop_memory_map:
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	call putc
 | 
			
		||||
	call putc
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x06(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x04(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x02(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x00(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
 | 
			
		||||
	movb $',', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x0E(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x0C(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x0A(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x08(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
 | 
			
		||||
	movb $',', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x12(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
	movw 0x10(%si), %ax
 | 
			
		||||
	call printnum
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	addw $20, %si
 | 
			
		||||
 | 
			
		||||
	decl %edx
 | 
			
		||||
	jnz .loop_memory_map
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# find root disk and populate root_disk_drive_number field
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
find_root_disk:
 | 
			
		||||
	movb $0x80, %dl
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_loop:
 | 
			
		||||
	call drive_exists
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .find_root_disk_not_found
 | 
			
		||||
 | 
			
		||||
	# read GPT header
 | 
			
		||||
	xorw %bx, %bx
 | 
			
		||||
	movl $1, %eax
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movw $gpt_header, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# confirm header (starts with 'EFI PART')
 | 
			
		||||
	cmpl $0x20494645, (gpt_header + 0)
 | 
			
		||||
	jne .find_root_disk_next_disk
 | 
			
		||||
	cmpl $0x54524150, (gpt_header + 4)
 | 
			
		||||
	jne .find_root_disk_next_disk
 | 
			
		||||
 | 
			
		||||
	# compare disk GUID
 | 
			
		||||
	movw $root_disk_guid, %si
 | 
			
		||||
	movw $(gpt_header + 56), %di
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	call memcmp
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .find_root_disk_next_disk
 | 
			
		||||
 | 
			
		||||
	movb %dl, (root_disk_drive_number)
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_next_disk:
 | 
			
		||||
	incb %dl
 | 
			
		||||
	jmp .find_root_disk_loop
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_not_found:
 | 
			
		||||
	movw $no_root_disk_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# finds root partition from root disk
 | 
			
		||||
# fills root_partition_entry data structure
 | 
			
		||||
# NOTE: assumes GPT header is in `gpt_header`
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
find_root_partition:
 | 
			
		||||
	pushl %ebp
 | 
			
		||||
	movl %esp, %ebp
 | 
			
		||||
	subl $16, %esp
 | 
			
		||||
 | 
			
		||||
	# esp + 0: 8 byte entry array lba
 | 
			
		||||
	movl (gpt_header + 72), %eax
 | 
			
		||||
	movl %eax, 0(%esp)
 | 
			
		||||
	movl (gpt_header + 76), %eax
 | 
			
		||||
	movl %eax, 4(%esp)
 | 
			
		||||
	# FIXME: check that bits 48-63 are zero
 | 
			
		||||
 | 
			
		||||
	# esp + 8: 4 byte entries per sector
 | 
			
		||||
	xorl %edx, %edx
 | 
			
		||||
	movl $SECTOR_SIZE, %eax
 | 
			
		||||
	divl (gpt_header + 84)
 | 
			
		||||
	movl %eax, 8(%esp)
 | 
			
		||||
 | 
			
		||||
	# esp + 12: 4 byte entries remaining
 | 
			
		||||
	movl (gpt_header + 80), %eax
 | 
			
		||||
	testl %eax, %eax
 | 
			
		||||
	jz .find_root_partition_not_found
 | 
			
		||||
	movl %eax, 12(%esp)
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_read_entry_section:
 | 
			
		||||
	movl 0(%esp), %eax
 | 
			
		||||
	movl 4(%esp), %ebx
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movb (root_disk_drive_number), %dl
 | 
			
		||||
	movw $sector_buffer, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# ecx: min(entries per section, entries remaining)
 | 
			
		||||
	movl 8(%esp), %ecx
 | 
			
		||||
	cmpl 12(%esp), %ecx
 | 
			
		||||
	jae .find_root_partition_got_entry_count
 | 
			
		||||
	movl 12(%esp), %ecx
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_got_entry_count:
 | 
			
		||||
	# update entries remaining
 | 
			
		||||
	subl %ecx, 12(%esp)
 | 
			
		||||
 | 
			
		||||
	# si: entry pointer
 | 
			
		||||
	movw $sector_buffer, %si
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_loop_entries:
 | 
			
		||||
	# temporarily save cx in dx
 | 
			
		||||
	movw %cx, %dx
 | 
			
		||||
 | 
			
		||||
	# check that entry is used
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	movw $zero_guid, %di
 | 
			
		||||
	call memcmp
 | 
			
		||||
	test %al, %al
 | 
			
		||||
	jnz .find_root_partition_next_entry
 | 
			
		||||
 | 
			
		||||
	# compare entry guid to root guid
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	addw $16, %si
 | 
			
		||||
	movw $root_partition_guid, %di
 | 
			
		||||
	call memcmp
 | 
			
		||||
	subw $16, %si
 | 
			
		||||
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jnz .find_root_partition_found
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_next_entry:
 | 
			
		||||
 | 
			
		||||
	# restore cx
 | 
			
		||||
	movw %dx, %cx
 | 
			
		||||
 | 
			
		||||
	# entry pointer += entry size
 | 
			
		||||
	addw (gpt_header + 84), %si
 | 
			
		||||
	loop .find_root_partition_loop_entries
 | 
			
		||||
 | 
			
		||||
	# entry not found in this sector
 | 
			
		||||
 | 
			
		||||
	# increment 8 byte entry array lba
 | 
			
		||||
	incl 0(%esp)
 | 
			
		||||
	jno .find_root_partition_no_overflow
 | 
			
		||||
	incl 4(%esp)
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_no_overflow:
 | 
			
		||||
	# loop to read next section if entries remaining
 | 
			
		||||
	cmpl $0, 12(%esp)
 | 
			
		||||
	jnz .find_root_partition_read_entry_section
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_not_found:
 | 
			
		||||
	movw $no_root_partition_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_found:
 | 
			
		||||
	# copy entry to buffer
 | 
			
		||||
	movw $root_partition_entry, %di
 | 
			
		||||
	movw $128, %cx
 | 
			
		||||
	rep movsb
 | 
			
		||||
 | 
			
		||||
	leavel
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
stage2_main:
 | 
			
		||||
	# clear screen and enter 80x25 text mode
 | 
			
		||||
	movb $0x03, %al
 | 
			
		||||
| 
						 | 
				
			
			@ -781,7 +54,7 @@ stage2_main:
 | 
			
		|||
	call puts; call print_newline
 | 
			
		||||
 | 
			
		||||
	call get_memory_map
 | 
			
		||||
	call get_command_line
 | 
			
		||||
	call read_user_command_line
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -791,101 +64,14 @@ stage2_main:
 | 
			
		|||
	call print_memory_map
 | 
			
		||||
 | 
			
		||||
	call find_root_disk
 | 
			
		||||
	movw $root_disk_found_msg, %si
 | 
			
		||||
	call puts; call print_newline
 | 
			
		||||
 | 
			
		||||
	call find_root_partition
 | 
			
		||||
	movw $root_partition_found_msg, %si
 | 
			
		||||
	call puts; call print_newline
 | 
			
		||||
 | 
			
		||||
	movw $16, %bx
 | 
			
		||||
	movw $2,  %cx
 | 
			
		||||
	movw (root_partition_entry + 38), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 36), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 34), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 32), %ax; call printnum
 | 
			
		||||
 | 
			
		||||
	movb $'-', %al; call putc
 | 
			
		||||
	movb $'>', %al; call putc
 | 
			
		||||
 | 
			
		||||
	movw (root_partition_entry + 46), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 44), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 42), %ax; call printnum
 | 
			
		||||
	movw (root_partition_entry + 40), %ax; call printnum
 | 
			
		||||
	call print_root_partition_info
 | 
			
		||||
 | 
			
		||||
	jmp halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# These will be patched during bootloader installation
 | 
			
		||||
root_disk_guid:
 | 
			
		||||
	.ascii "root disk guid  "
 | 
			
		||||
root_partition_guid:
 | 
			
		||||
	.ascii "root part guid  "
 | 
			
		||||
zero_guid:
 | 
			
		||||
	.quad 0
 | 
			
		||||
	.quad 0
 | 
			
		||||
 | 
			
		||||
hello_msg:
 | 
			
		||||
	.asciz "This is banan-os bootloader"
 | 
			
		||||
 | 
			
		||||
command_line_msg:
 | 
			
		||||
command_line_enter_msg:
 | 
			
		||||
	.asciz "cmdline: "
 | 
			
		||||
 | 
			
		||||
memory_map_msg:
 | 
			
		||||
	.asciz "memmap:"
 | 
			
		||||
 | 
			
		||||
memory_map_error_msg:
 | 
			
		||||
	.asciz "Failed to get memory map"
 | 
			
		||||
 | 
			
		||||
start_kernel_load_msg:
 | 
			
		||||
	.asciz "Starting to load kernel"
 | 
			
		||||
 | 
			
		||||
root_disk_found_msg:
 | 
			
		||||
	.asciz "Root disk found!"
 | 
			
		||||
no_root_disk_msg:
 | 
			
		||||
	.asciz "Root disk not found"
 | 
			
		||||
 | 
			
		||||
root_partition_found_msg:
 | 
			
		||||
	.asciz "Root partition found!"
 | 
			
		||||
no_root_partition_msg:
 | 
			
		||||
	.asciz "Root partition not found"
 | 
			
		||||
 | 
			
		||||
stage2_end:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.section .bss
 | 
			
		||||
 | 
			
		||||
.align SECTOR_SIZE
 | 
			
		||||
gpt_header:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
gpt_entry_data:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
 | 
			
		||||
sector_buffer:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
 | 
			
		||||
disk_drive_parameters:
 | 
			
		||||
	.skip 0x1A
 | 
			
		||||
 | 
			
		||||
disk_address_packet:
 | 
			
		||||
	.skip 16
 | 
			
		||||
 | 
			
		||||
printnum_buffer:
 | 
			
		||||
	.skip 10
 | 
			
		||||
 | 
			
		||||
root_disk_drive_number:
 | 
			
		||||
	.skip 1
 | 
			
		||||
 | 
			
		||||
root_partition_entry:
 | 
			
		||||
	.skip 128
 | 
			
		||||
 | 
			
		||||
memory_map_entry_count:
 | 
			
		||||
	.skip 4
 | 
			
		||||
# 100 entries should be enough...
 | 
			
		||||
memory_map_entries:
 | 
			
		||||
	.skip 20 * 100
 | 
			
		||||
 | 
			
		||||
# 100 character command line
 | 
			
		||||
command_line_buffer:
 | 
			
		||||
	.skip 100
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
.code16
 | 
			
		||||
 | 
			
		||||
.section .stage2
 | 
			
		||||
 | 
			
		||||
# fills command line buffer
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
.global read_user_command_line
 | 
			
		||||
read_user_command_line:
 | 
			
		||||
	movw $command_line_enter_msg, %si
 | 
			
		||||
	call puts
 | 
			
		||||
 | 
			
		||||
	movw $command_line_buffer, %di
 | 
			
		||||
 | 
			
		||||
 .read_user_command_line_loop:
 | 
			
		||||
	call getc
 | 
			
		||||
 | 
			
		||||
	cmpb $'\b', %al
 | 
			
		||||
	je .read_user_command_line_backspace
 | 
			
		||||
 | 
			
		||||
	# Not sure if some BIOSes return '\n' as enter, but check it just in case
 | 
			
		||||
	cmpb $'\r', %al
 | 
			
		||||
	je .read_user_command_line_done
 | 
			
		||||
	cmpb $'\n', %al
 | 
			
		||||
	je .read_user_command_line_done
 | 
			
		||||
 | 
			
		||||
	call isprint
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jnz .read_user_command_line_loop
 | 
			
		||||
 | 
			
		||||
	# put byte to buffer
 | 
			
		||||
	movb %al, (%di)
 | 
			
		||||
	incw %di
 | 
			
		||||
 | 
			
		||||
	# print byte
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	jmp .read_user_command_line_loop
 | 
			
		||||
 | 
			
		||||
 .read_user_command_line_backspace:
 | 
			
		||||
	# don't do anything if at the beginning
 | 
			
		||||
	cmpw $command_line_buffer, %di
 | 
			
		||||
	je .read_user_command_line_loop
 | 
			
		||||
 | 
			
		||||
	# decrement buffer pointer
 | 
			
		||||
	decw %di
 | 
			
		||||
 | 
			
		||||
	# erase byte in display
 | 
			
		||||
	call print_backspace
 | 
			
		||||
 | 
			
		||||
	jmp .read_user_command_line_loop
 | 
			
		||||
 | 
			
		||||
 .read_user_command_line_done:
 | 
			
		||||
	# null terminate command line
 | 
			
		||||
	movb $0, (%di)
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
command_line_enter_msg:
 | 
			
		||||
	.asciz "cmdline: "
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.section .bss
 | 
			
		||||
 | 
			
		||||
# 100 character command line
 | 
			
		||||
command_line_buffer:
 | 
			
		||||
	.skip 100
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,491 @@
 | 
			
		|||
# FIXME: don't assume 512 byte sectors
 | 
			
		||||
.set SECTOR_SIZE_SHIFT,	9
 | 
			
		||||
.set SECTOR_SIZE,		1 << SECTOR_SIZE_SHIFT
 | 
			
		||||
 | 
			
		||||
.code16
 | 
			
		||||
 | 
			
		||||
.section .stage1
 | 
			
		||||
 | 
			
		||||
.global stage2_start
 | 
			
		||||
.global stage2_end
 | 
			
		||||
 | 
			
		||||
# check that drive has int13 ext
 | 
			
		||||
#	dl: drive number
 | 
			
		||||
# returns only if drive does have the extension
 | 
			
		||||
drive_has_int13_ext:
 | 
			
		||||
	pusha
 | 
			
		||||
 | 
			
		||||
	movb $0x41, %ah
 | 
			
		||||
	movw $0x55AA, %bx
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .drive_has_int13_ext_no_int13_ext
 | 
			
		||||
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .drive_has_int13_ext_no_int13_ext:
 | 
			
		||||
	mov $no_int13_ext_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# read sectors from disk
 | 
			
		||||
# bx:eax:	lba start
 | 
			
		||||
# cx:		lba count (has to less than 0x80)
 | 
			
		||||
# dl:		drive number
 | 
			
		||||
# ds:di:	physical address
 | 
			
		||||
# returns only on success
 | 
			
		||||
.global read_from_disk
 | 
			
		||||
read_from_disk:
 | 
			
		||||
	pusha
 | 
			
		||||
 | 
			
		||||
	call drive_has_int13_ext
 | 
			
		||||
 | 
			
		||||
	# prepare disk read packet
 | 
			
		||||
	mov $disk_address_packet, %si
 | 
			
		||||
	movb $0x10,	0x00(%si)	# packet size
 | 
			
		||||
	movb $0x00,	0x01(%si)	# always 0
 | 
			
		||||
	movw %cx,	0x02(%si)	# lba count
 | 
			
		||||
	movw %di,	0x04(%si)	# offset
 | 
			
		||||
	movw %ds,	0x06(%si)	# segment
 | 
			
		||||
	movl %eax,	0x08(%si)	# 32 bit lower lba
 | 
			
		||||
	movw %bx,	0x0C(%si)	# 16 bit upper lba
 | 
			
		||||
	movw $0,	0x0E(%si)	# zero
 | 
			
		||||
 | 
			
		||||
	# issue read command
 | 
			
		||||
	mov $0x42, %ah
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .read_from_disk_failed
 | 
			
		||||
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .read_from_disk_failed:
 | 
			
		||||
	mov $read_from_disk_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Reads GPT header into gpt_header buffer
 | 
			
		||||
#	dl: drive number
 | 
			
		||||
# return:
 | 
			
		||||
#	ax: 1 if has GPT header, 0 otherwise
 | 
			
		||||
.global read_gpt_header
 | 
			
		||||
read_gpt_header:
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	pushw %cx
 | 
			
		||||
	pushw %di
 | 
			
		||||
 | 
			
		||||
	xorw %bx, %bx
 | 
			
		||||
	movl $1, %eax
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movw $gpt_header, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	xorw %bx, %bx
 | 
			
		||||
	movw $1, %ax
 | 
			
		||||
 | 
			
		||||
	# check if header starts with 'EFI PART'
 | 
			
		||||
	cmpl $0x20494645, (gpt_header + 0)
 | 
			
		||||
	cmovnew %bx, %ax
 | 
			
		||||
	cmpl $0x54524150, (gpt_header + 4)
 | 
			
		||||
	cmovnew %bx, %ax
 | 
			
		||||
 | 
			
		||||
	popw %di
 | 
			
		||||
	popw %cx
 | 
			
		||||
	popw %bx
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Find bios boot partition from boot drive
 | 
			
		||||
# returns:
 | 
			
		||||
#	bx:eax:	first lba
 | 
			
		||||
#	cx:		sector count
 | 
			
		||||
find_stage2_partition:
 | 
			
		||||
	# read boot disk GPT header
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
	call read_gpt_header
 | 
			
		||||
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .find_stage2_partition_not_gpt
 | 
			
		||||
 | 
			
		||||
	# eax := entry_count
 | 
			
		||||
	movl (gpt_header + 80), %eax
 | 
			
		||||
	test %eax, %eax
 | 
			
		||||
	jz .find_stage2_partition_not_found
 | 
			
		||||
 | 
			
		||||
	# edx:eax := eax * entry_size
 | 
			
		||||
	mull (gpt_header + 84)
 | 
			
		||||
	test %edx, %edx
 | 
			
		||||
	jnz .find_stage2_partition_too_big_entries
 | 
			
		||||
 | 
			
		||||
	# FIXME: read one entry array section at a time
 | 
			
		||||
 | 
			
		||||
	# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
 | 
			
		||||
	movl %eax, %ecx
 | 
			
		||||
	shrl $SECTOR_SIZE_SHIFT, %ecx
 | 
			
		||||
 | 
			
		||||
	# start lba
 | 
			
		||||
	movl (gpt_header + 72), %eax
 | 
			
		||||
	movw (gpt_header + 76), %bx
 | 
			
		||||
 | 
			
		||||
	movw $gpt_entry_data, %di
 | 
			
		||||
	movw $bios_boot_guid, %si
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# NOTE: 'only' 0xFFFF partitions supported,
 | 
			
		||||
	#       although read will fail with more than 0x80
 | 
			
		||||
	movw (gpt_header + 80), %cx
 | 
			
		||||
 | 
			
		||||
 .find_stage2_partition_loop_gpt_entries:
 | 
			
		||||
	pushw %cx
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	call memcmp
 | 
			
		||||
	popw %cx
 | 
			
		||||
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jnz .find_stage2_partition_found
 | 
			
		||||
 | 
			
		||||
	# add entry size to entry pointer
 | 
			
		||||
	addw (gpt_header + 84), %di
 | 
			
		||||
 | 
			
		||||
	loop .find_stage2_partition_loop_gpt_entries
 | 
			
		||||
 | 
			
		||||
	# fall through to not found case
 | 
			
		||||
 .find_stage2_partition_not_found:
 | 
			
		||||
	movw $no_bios_boot_partition_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .find_stage2_partition_not_gpt:
 | 
			
		||||
	movw $not_gpt_partition_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .find_stage2_partition_too_big_entries:
 | 
			
		||||
	movw $too_gpt_big_entries_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .find_stage2_partition_found:
 | 
			
		||||
	# first lba
 | 
			
		||||
	movl 32(%di), %eax
 | 
			
		||||
	movw 36(%di), %bx
 | 
			
		||||
 | 
			
		||||
	# count := last lba - first lba + 1
 | 
			
		||||
	movl 40(%di), %ecx
 | 
			
		||||
	subl %eax, %ecx
 | 
			
		||||
	incl %ecx
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# reads stage2 into memory
 | 
			
		||||
#	dl: boot drive number
 | 
			
		||||
# returns only on success
 | 
			
		||||
.global read_stage2_into_memory
 | 
			
		||||
read_stage2_into_memory:
 | 
			
		||||
	movb %dl, (boot_disk_number)
 | 
			
		||||
 | 
			
		||||
	# push stage2 sector count
 | 
			
		||||
	movl $stage2_end, %eax
 | 
			
		||||
	subl $stage2_start, %eax
 | 
			
		||||
	addl $(SECTOR_SIZE - 1), %eax
 | 
			
		||||
	movl $SECTOR_SIZE, %ecx
 | 
			
		||||
	xorl %edx, %edx
 | 
			
		||||
	divl %ecx
 | 
			
		||||
	pushl %eax
 | 
			
		||||
 | 
			
		||||
	call find_stage2_partition
 | 
			
		||||
 | 
			
		||||
	movb (boot_disk_number), %dl
 | 
			
		||||
	popl %ecx # FIXME: validate that partition has enough sectors
 | 
			
		||||
	movw $stage2_start, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# 21686148-6449-6E6F-744E-656564454649
 | 
			
		||||
.align 4
 | 
			
		||||
bios_boot_guid:
 | 
			
		||||
	.long 0x21686148		# little endian
 | 
			
		||||
	.word 0x6449			# little endian
 | 
			
		||||
	.word 0x6E6F			# little endian
 | 
			
		||||
	.word 0x4E74			# big endian
 | 
			
		||||
	.quad 0x494645646565	# big endian
 | 
			
		||||
 | 
			
		||||
boot_disk_number:
 | 
			
		||||
	.skip 1
 | 
			
		||||
 | 
			
		||||
read_from_disk_msg:
 | 
			
		||||
	.asciz "read error"
 | 
			
		||||
 | 
			
		||||
no_int13_ext_msg:
 | 
			
		||||
	.asciz "no INT13 ext"
 | 
			
		||||
 | 
			
		||||
no_bios_boot_partition_msg:
 | 
			
		||||
	.asciz "no bios boot"
 | 
			
		||||
 | 
			
		||||
too_gpt_big_entries_msg:
 | 
			
		||||
	.asciz "too big GPT array"
 | 
			
		||||
 | 
			
		||||
not_gpt_partition_msg:
 | 
			
		||||
	.asciz "not GPT"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.section .stage2
 | 
			
		||||
 | 
			
		||||
# check if drive exists
 | 
			
		||||
#	dl: drive number
 | 
			
		||||
# return:
 | 
			
		||||
#	al: 1 if disk is usable, 0 otherwise
 | 
			
		||||
drive_exists:
 | 
			
		||||
	pusha
 | 
			
		||||
 | 
			
		||||
	movb $0x48, %ah
 | 
			
		||||
	movw $disk_drive_parameters, %si
 | 
			
		||||
	movw $0x1A, (disk_drive_parameters) # set buffer size
 | 
			
		||||
 | 
			
		||||
	int $0x13
 | 
			
		||||
	jc .drive_exists_nope
 | 
			
		||||
 | 
			
		||||
	popa
 | 
			
		||||
	movb $1, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .drive_exists_nope:
 | 
			
		||||
	popa
 | 
			
		||||
	movb $0, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# find root disk and populate root_disk_drive_number field
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
.global find_root_disk
 | 
			
		||||
find_root_disk:
 | 
			
		||||
	movb $0x80, %dl
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_loop:
 | 
			
		||||
	call drive_exists
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .find_root_disk_not_found
 | 
			
		||||
 | 
			
		||||
	# read GPT header
 | 
			
		||||
	xorw %bx, %bx
 | 
			
		||||
	movl $1, %eax
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movw $gpt_header, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# confirm header (starts with 'EFI PART')
 | 
			
		||||
	cmpl $0x20494645, (gpt_header + 0)
 | 
			
		||||
	jne .find_root_disk_next_disk
 | 
			
		||||
	cmpl $0x54524150, (gpt_header + 4)
 | 
			
		||||
	jne .find_root_disk_next_disk
 | 
			
		||||
 | 
			
		||||
	# compare disk GUID
 | 
			
		||||
	movw $root_disk_guid, %si
 | 
			
		||||
	movw $(gpt_header + 56), %di
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	call memcmp
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jz .find_root_disk_next_disk
 | 
			
		||||
 | 
			
		||||
	movw $root_disk_found_msg, %si
 | 
			
		||||
	call puts; call print_newline
 | 
			
		||||
 | 
			
		||||
	movb %dl, (root_disk_drive_number)
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_next_disk:
 | 
			
		||||
	incb %dl
 | 
			
		||||
	jmp .find_root_disk_loop
 | 
			
		||||
 | 
			
		||||
 .find_root_disk_not_found:
 | 
			
		||||
	movw $root_disk_not_found_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# finds root partition from root disk
 | 
			
		||||
# fills root_partition_entry data structure
 | 
			
		||||
# NOTE: assumes GPT header is in `gpt_header`
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
.global find_root_partition
 | 
			
		||||
find_root_partition:
 | 
			
		||||
	pushl %ebp
 | 
			
		||||
	movl %esp, %ebp
 | 
			
		||||
	subl $16, %esp
 | 
			
		||||
 | 
			
		||||
	# esp + 0: 8 byte entry array lba
 | 
			
		||||
	movl (gpt_header + 72), %eax
 | 
			
		||||
	movl %eax, 0(%esp)
 | 
			
		||||
	movl (gpt_header + 76), %eax
 | 
			
		||||
	movl %eax, 4(%esp)
 | 
			
		||||
	# FIXME: check that bits 48-63 are zero
 | 
			
		||||
 | 
			
		||||
	# esp + 8: 4 byte entries per sector
 | 
			
		||||
	xorl %edx, %edx
 | 
			
		||||
	movl $SECTOR_SIZE, %eax
 | 
			
		||||
	divl (gpt_header + 84)
 | 
			
		||||
	movl %eax, 8(%esp)
 | 
			
		||||
 | 
			
		||||
	# esp + 12: 4 byte entries remaining
 | 
			
		||||
	movl (gpt_header + 80), %eax
 | 
			
		||||
	testl %eax, %eax
 | 
			
		||||
	jz .find_root_partition_not_found
 | 
			
		||||
	movl %eax, 12(%esp)
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_read_entry_section:
 | 
			
		||||
	movl 0(%esp), %eax
 | 
			
		||||
	movl 4(%esp), %ebx
 | 
			
		||||
	movw $1, %cx
 | 
			
		||||
	movb (root_disk_drive_number), %dl
 | 
			
		||||
	movw $sector_buffer, %di
 | 
			
		||||
	call read_from_disk
 | 
			
		||||
 | 
			
		||||
	# ecx: min(entries per section, entries remaining)
 | 
			
		||||
	movl 8(%esp), %ecx
 | 
			
		||||
	cmpl 12(%esp), %ecx
 | 
			
		||||
	jae .find_root_partition_got_entry_count
 | 
			
		||||
	movl 12(%esp), %ecx
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_got_entry_count:
 | 
			
		||||
	# update entries remaining
 | 
			
		||||
	subl %ecx, 12(%esp)
 | 
			
		||||
 | 
			
		||||
	# si: entry pointer
 | 
			
		||||
	movw $sector_buffer, %si
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_loop_entries:
 | 
			
		||||
	# temporarily save cx in dx
 | 
			
		||||
	movw %cx, %dx
 | 
			
		||||
 | 
			
		||||
	# check that entry is used
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	movw $zero_guid, %di
 | 
			
		||||
	call memcmp
 | 
			
		||||
	test %al, %al
 | 
			
		||||
	jnz .find_root_partition_next_entry
 | 
			
		||||
 | 
			
		||||
	# compare entry guid to root guid
 | 
			
		||||
	movw $16, %cx
 | 
			
		||||
	addw $16, %si
 | 
			
		||||
	movw $root_partition_guid, %di
 | 
			
		||||
	call memcmp
 | 
			
		||||
	subw $16, %si
 | 
			
		||||
 | 
			
		||||
	testb %al, %al
 | 
			
		||||
	jnz .find_root_partition_found
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_next_entry:
 | 
			
		||||
 | 
			
		||||
	# restore cx
 | 
			
		||||
	movw %dx, %cx
 | 
			
		||||
 | 
			
		||||
	# entry pointer += entry size
 | 
			
		||||
	addw (gpt_header + 84), %si
 | 
			
		||||
	loop .find_root_partition_loop_entries
 | 
			
		||||
 | 
			
		||||
	# entry not found in this sector
 | 
			
		||||
 | 
			
		||||
	# increment 8 byte entry array lba
 | 
			
		||||
	incl 0(%esp)
 | 
			
		||||
	jno .find_root_partition_no_overflow
 | 
			
		||||
	incl 4(%esp)
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_no_overflow:
 | 
			
		||||
	# loop to read next section if entries remaining
 | 
			
		||||
	cmpl $0, 12(%esp)
 | 
			
		||||
	jnz .find_root_partition_read_entry_section
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_not_found:
 | 
			
		||||
	movw $root_partition_not_found_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 .find_root_partition_found:
 | 
			
		||||
	# copy entry to buffer
 | 
			
		||||
	movw $root_partition_entry, %di
 | 
			
		||||
	movw $128, %cx
 | 
			
		||||
	rep movsb
 | 
			
		||||
 | 
			
		||||
	movw $root_partition_found_msg, %si
 | 
			
		||||
	call puts; call print_newline
 | 
			
		||||
 | 
			
		||||
	leavel
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# print information about root partition
 | 
			
		||||
.global print_root_partition_info
 | 
			
		||||
print_root_partition_info:
 | 
			
		||||
	pushw %ax
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	pushw %cx
 | 
			
		||||
	pushw %si
 | 
			
		||||
 | 
			
		||||
	movw $root_partition_info_start_msg, %si
 | 
			
		||||
	call puts;
 | 
			
		||||
 | 
			
		||||
	movw $16, %bx
 | 
			
		||||
	movw $2,  %cx
 | 
			
		||||
	movw (root_partition_entry + 38), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 36), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 34), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 32), %ax; call print_number
 | 
			
		||||
 | 
			
		||||
	movb $'-', %al; call putc
 | 
			
		||||
	movb $'>', %al; call putc
 | 
			
		||||
 | 
			
		||||
	movw (root_partition_entry + 46), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 44), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 42), %ax; call print_number
 | 
			
		||||
	movw (root_partition_entry + 40), %ax; call print_number
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	popw %si
 | 
			
		||||
	popw %cx
 | 
			
		||||
	popw %bx
 | 
			
		||||
	popw %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# These will be patched during bootloader installation
 | 
			
		||||
root_disk_guid:
 | 
			
		||||
	.ascii "root disk guid  "
 | 
			
		||||
root_partition_guid:
 | 
			
		||||
	.ascii "root part guid  "
 | 
			
		||||
zero_guid:
 | 
			
		||||
	.skip 16, 0
 | 
			
		||||
 | 
			
		||||
root_disk_found_msg:
 | 
			
		||||
	.asciz "Root disk found!"
 | 
			
		||||
root_disk_not_found_msg:
 | 
			
		||||
	.asciz "Root disk not found"
 | 
			
		||||
 | 
			
		||||
root_partition_found_msg:
 | 
			
		||||
	.asciz "Root partition found!"
 | 
			
		||||
root_partition_not_found_msg:
 | 
			
		||||
	.asciz "Root partition not found"
 | 
			
		||||
 | 
			
		||||
root_partition_info_start_msg:
 | 
			
		||||
	.asciz "Root partition: "
 | 
			
		||||
 | 
			
		||||
.section .bss
 | 
			
		||||
 | 
			
		||||
.align SECTOR_SIZE
 | 
			
		||||
gpt_header:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
gpt_entry_data:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
sector_buffer:
 | 
			
		||||
	.skip SECTOR_SIZE
 | 
			
		||||
 | 
			
		||||
disk_address_packet:
 | 
			
		||||
	.skip 16
 | 
			
		||||
 | 
			
		||||
disk_drive_parameters:
 | 
			
		||||
	.skip 0x1A
 | 
			
		||||
	.skip 2 # padding
 | 
			
		||||
 | 
			
		||||
root_disk_drive_number:
 | 
			
		||||
	.skip 1
 | 
			
		||||
	.skip 3 # padding
 | 
			
		||||
 | 
			
		||||
root_partition_entry:
 | 
			
		||||
	.skip 128
 | 
			
		||||
| 
						 | 
				
			
			@ -30,10 +30,14 @@ make
 | 
			
		|||
mkdir -p $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
echo compiling bootloader
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/boot.S -o $BUILD_DIR/bootloader.o
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/boot.S        	-o $BUILD_DIR/boot.o
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/command_line.S	-o $BUILD_DIR/command_line.o
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/disk.S			-o $BUILD_DIR/disk.o
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/memory_map.S	-o $BUILD_DIR/memory_map.o
 | 
			
		||||
x86_64-banan_os-as $CURRENT_DIR/utils.S			-o $BUILD_DIR/utils.o
 | 
			
		||||
 | 
			
		||||
echo linking bootloader
 | 
			
		||||
x86_64-banan_os-ld -nostdlib -T $CURRENT_DIR/linker.ld $BUILD_DIR/bootloader.o -o $BUILD_DIR/bootloader
 | 
			
		||||
x86_64-banan_os-ld -nostdlib -T $CURRENT_DIR/linker.ld $BUILD_DIR/boot.o $BUILD_DIR/command_line.o $BUILD_DIR/disk.o $BUILD_DIR/memory_map.o $BUILD_DIR/utils.o -o $BUILD_DIR/bootloader
 | 
			
		||||
 | 
			
		||||
echo installing bootloader to
 | 
			
		||||
$INSTALLER_BUILD_DIR/x86_64-banan_os-bootloader-installer $BUILD_DIR/bootloader $BANAN_DISK_IMAGE_PATH $ROOT_PARTITION_GUID
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,14 @@
 | 
			
		|||
ENTRY(stage1_main)
 | 
			
		||||
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
	. = 0x7C00;
 | 
			
		||||
	.stage1 : { *(.stage1) }
 | 
			
		||||
 | 
			
		||||
	. = ALIGN(512);
 | 
			
		||||
	stage2_start = .;
 | 
			
		||||
	.stage2 : { *(.stage2) }
 | 
			
		||||
	stage2_end = .;
 | 
			
		||||
 | 
			
		||||
	. = ALIGN(512);
 | 
			
		||||
	.bss : { *(.bss) }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
.code16
 | 
			
		||||
 | 
			
		||||
.section .stage2
 | 
			
		||||
 | 
			
		||||
# fills memory map data structure
 | 
			
		||||
# doesn't return on error
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
.global get_memory_map
 | 
			
		||||
get_memory_map:
 | 
			
		||||
	movl $0, (memory_map_entry_count)
 | 
			
		||||
 | 
			
		||||
	movl $0x0000E820, %eax
 | 
			
		||||
	movl $0x534D4150, %edx
 | 
			
		||||
	xorl %ebx, %ebx
 | 
			
		||||
	movl $20, %ecx
 | 
			
		||||
	movw $memory_map_entries, %di
 | 
			
		||||
 | 
			
		||||
	clc
 | 
			
		||||
	int $0x15
 | 
			
		||||
	# If first call returs with CF set, the call failed
 | 
			
		||||
	jc .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_rest:
 | 
			
		||||
	cmpl $0x534D4150, %eax
 | 
			
		||||
	jne .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
	# FIXME: don't assume BIOS to always return 20 bytes
 | 
			
		||||
	cmpl $20, %ecx
 | 
			
		||||
	jne .get_memory_map_error
 | 
			
		||||
 | 
			
		||||
	# increment entry count
 | 
			
		||||
	incl (memory_map_entry_count)
 | 
			
		||||
 | 
			
		||||
	# increment entry pointer
 | 
			
		||||
	addw %cx, %di
 | 
			
		||||
 | 
			
		||||
	# BIOS can indicate end of list by 0 in ebx
 | 
			
		||||
	testl %ebx, %ebx
 | 
			
		||||
	jz .get_memory_map_done
 | 
			
		||||
 | 
			
		||||
	movl $0x0000E820, %eax
 | 
			
		||||
	movl $0x534D4150, %edx
 | 
			
		||||
 | 
			
		||||
	clc
 | 
			
		||||
	int $0x15
 | 
			
		||||
	# BIOS can indicate end of list by setting CF
 | 
			
		||||
	jnc .get_memory_map_rest
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_done:
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 .get_memory_map_error:
 | 
			
		||||
	movw $memory_map_error_msg, %si
 | 
			
		||||
	jmp print_and_halt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# print memory map from memory_map_entries
 | 
			
		||||
# NO REGISTERS SAVED
 | 
			
		||||
.global print_memory_map
 | 
			
		||||
print_memory_map:
 | 
			
		||||
	movw $memory_map_msg, %si
 | 
			
		||||
	call puts
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	movl (memory_map_entry_count), %edx
 | 
			
		||||
	movw $memory_map_entries, %si
 | 
			
		||||
 | 
			
		||||
	movw $16, %bx
 | 
			
		||||
	movw $4, %cx
 | 
			
		||||
 | 
			
		||||
 .loop_memory_map:
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc; call putc; call putc; call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x06(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x04(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x02(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x00(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
 | 
			
		||||
	movb $',', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x0E(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x0C(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x0A(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x08(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
 | 
			
		||||
	movb $',', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	movw 0x12(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
	movw 0x10(%si), %ax
 | 
			
		||||
	call print_number
 | 
			
		||||
 | 
			
		||||
	call print_newline
 | 
			
		||||
 | 
			
		||||
	addw $20, %si
 | 
			
		||||
 | 
			
		||||
	decl %edx
 | 
			
		||||
	jnz .loop_memory_map
 | 
			
		||||
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
memory_map_msg:
 | 
			
		||||
	.asciz "memmap:"
 | 
			
		||||
memory_map_error_msg:
 | 
			
		||||
	.asciz "Failed to get memory map"
 | 
			
		||||
 | 
			
		||||
.section .bss
 | 
			
		||||
 | 
			
		||||
memory_map_entry_count:
 | 
			
		||||
	.skip 4
 | 
			
		||||
# 100 entries should be enough...
 | 
			
		||||
memory_map_entries:
 | 
			
		||||
	.skip 20 * 100
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,218 @@
 | 
			
		|||
.set SCREEN_WIDTH,	80
 | 
			
		||||
.set SCREEN_HEIGHT,	25
 | 
			
		||||
 | 
			
		||||
.code16
 | 
			
		||||
 | 
			
		||||
.section .stage1
 | 
			
		||||
 | 
			
		||||
# prints character to screen
 | 
			
		||||
# al:		ascii character to print
 | 
			
		||||
.global putc
 | 
			
		||||
putc:
 | 
			
		||||
	pushw %ax
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	movb $0x0E, %ah
 | 
			
		||||
	xorb %bh, %bh
 | 
			
		||||
	int $0x10
 | 
			
		||||
	popw %bx
 | 
			
		||||
	popw %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# prints null terminated string to screen
 | 
			
		||||
# ds:si:	string address
 | 
			
		||||
.global puts
 | 
			
		||||
puts:
 | 
			
		||||
	pushw %si
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	pushw %ax
 | 
			
		||||
 | 
			
		||||
	movb $0x0E, %ah
 | 
			
		||||
	xorb %bh, %bh
 | 
			
		||||
 | 
			
		||||
 .puts_loop:
 | 
			
		||||
	lodsb
 | 
			
		||||
 | 
			
		||||
	test %al, %al
 | 
			
		||||
	jz .puts_done
 | 
			
		||||
 | 
			
		||||
	int $0x10
 | 
			
		||||
	jmp .puts_loop
 | 
			
		||||
 | 
			
		||||
 .puts_done:
 | 
			
		||||
	popw %ax
 | 
			
		||||
	popw %bx
 | 
			
		||||
	popw %si
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# compares memory between addresses
 | 
			
		||||
# si:		ptr1
 | 
			
		||||
# di:		ptr2
 | 
			
		||||
# cx:		bytes count
 | 
			
		||||
# return:
 | 
			
		||||
#	al: 1 if equal, 0 otherwise
 | 
			
		||||
.global memcmp
 | 
			
		||||
memcmp:
 | 
			
		||||
	# NOTE: using pusha + popa to save space
 | 
			
		||||
	pusha
 | 
			
		||||
	cld
 | 
			
		||||
	repe cmpsb
 | 
			
		||||
	popa
 | 
			
		||||
	setzb %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.section .stage2
 | 
			
		||||
 | 
			
		||||
# read a character from keyboard
 | 
			
		||||
# return:
 | 
			
		||||
#	al: ascii
 | 
			
		||||
#	ah: bios scan code
 | 
			
		||||
.global getc
 | 
			
		||||
getc:
 | 
			
		||||
	movb $0x00, %ah
 | 
			
		||||
	int $0x16
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# prints newline to screen
 | 
			
		||||
.global print_newline
 | 
			
		||||
print_newline:
 | 
			
		||||
	pushw %ax
 | 
			
		||||
	movb $'\r', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	movb $'\n', %al
 | 
			
		||||
	call putc
 | 
			
		||||
	pop %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# prints backspace to screen, can go back a line
 | 
			
		||||
.global print_backspace
 | 
			
		||||
print_backspace:
 | 
			
		||||
	pushw %ax
 | 
			
		||||
	pushw %bx
 | 
			
		||||
	pushw %cx
 | 
			
		||||
	pushw %dx
 | 
			
		||||
 | 
			
		||||
	# get cursor position
 | 
			
		||||
	movb $0x03, %ah
 | 
			
		||||
	movb $0x00, %bh
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
	# don't do anyting if on first row
 | 
			
		||||
	testb %dh, %dh
 | 
			
		||||
	jz .print_backspace_done
 | 
			
		||||
 | 
			
		||||
	# go one line up if on first column
 | 
			
		||||
	test %dl, %dl
 | 
			
		||||
	jz .print_backspace_go_line_up
 | 
			
		||||
 | 
			
		||||
	# otherwise decrease column
 | 
			
		||||
	decb %dl
 | 
			
		||||
	jmp .print_backspace_do_print
 | 
			
		||||
 | 
			
		||||
 .print_backspace_go_line_up:
 | 
			
		||||
	# decrease row and set column to the last one
 | 
			
		||||
	decb %dh
 | 
			
		||||
	movb $(SCREEN_WIDTH - 1), %dl
 | 
			
		||||
 | 
			
		||||
 .print_backspace_do_print:
 | 
			
		||||
	# set cursor position
 | 
			
		||||
	movb $0x02, %ah
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
	# print 'empty' character (space)
 | 
			
		||||
	mov $' ', %al
 | 
			
		||||
	call putc
 | 
			
		||||
 | 
			
		||||
	# set cursor position
 | 
			
		||||
	movb $0x02, %ah
 | 
			
		||||
	int $0x10
 | 
			
		||||
 | 
			
		||||
 .print_backspace_done:
 | 
			
		||||
	popw %dx
 | 
			
		||||
	popw %cx
 | 
			
		||||
	popw %bx
 | 
			
		||||
	popw %ax
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# print number to screen
 | 
			
		||||
# ax:	number to print
 | 
			
		||||
# bx:	number base
 | 
			
		||||
# cx:	min width (zero pads if shorter)
 | 
			
		||||
.global print_number
 | 
			
		||||
print_number:
 | 
			
		||||
	pusha
 | 
			
		||||
	pushl %ebp
 | 
			
		||||
	movl %esp, %ebp
 | 
			
		||||
 | 
			
		||||
	# save min width
 | 
			
		||||
	subl $4, %esp
 | 
			
		||||
	movw %cx, (%esp)
 | 
			
		||||
 | 
			
		||||
	movw $print_number_buffer, %si
 | 
			
		||||
	xorw %cx, %cx
 | 
			
		||||
 | 
			
		||||
 .print_number_fill_loop:
 | 
			
		||||
	# fill buffer with all remainders ax % bx
 | 
			
		||||
	xorw %dx, %dx
 | 
			
		||||
	divw %bx
 | 
			
		||||
	movb %dl, (%si)
 | 
			
		||||
	incw %si
 | 
			
		||||
	incw %cx
 | 
			
		||||
	testw %ax, %ax
 | 
			
		||||
	jnz .print_number_fill_loop
 | 
			
		||||
 | 
			
		||||
	# check if zero pad is required
 | 
			
		||||
	cmpw (%esp), %cx
 | 
			
		||||
	jae .print_number_print_loop
 | 
			
		||||
 | 
			
		||||
	# dx: saved number count
 | 
			
		||||
	# cx: zero pad count
 | 
			
		||||
	movw %cx, %dx
 | 
			
		||||
	movw (%esp), %cx
 | 
			
		||||
	subw %dx, %cx
 | 
			
		||||
	movb $'0', %al
 | 
			
		||||
 | 
			
		||||
 .print_number_pad_zeroes:
 | 
			
		||||
	call putc
 | 
			
		||||
	loop .print_number_pad_zeroes
 | 
			
		||||
 | 
			
		||||
	# restore number count
 | 
			
		||||
	movw %dx, %cx
 | 
			
		||||
 | 
			
		||||
 .print_number_print_loop:
 | 
			
		||||
	decw %si
 | 
			
		||||
	movb (%si), %al
 | 
			
		||||
	cmpb $10, %al
 | 
			
		||||
	jae .print_number_hex
 | 
			
		||||
	addb $'0', %al
 | 
			
		||||
	jmp .print_number_do_print
 | 
			
		||||
 .print_number_hex:
 | 
			
		||||
	addb $('a' - 10), %al
 | 
			
		||||
 .print_number_do_print:
 | 
			
		||||
	call putc
 | 
			
		||||
	loop .print_number_print_loop
 | 
			
		||||
 | 
			
		||||
	leavel
 | 
			
		||||
	popa
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
# test if character is printable ascii
 | 
			
		||||
#	al: character to test
 | 
			
		||||
# return:
 | 
			
		||||
#	al: 1 if is printable, 0 otherwise
 | 
			
		||||
.global isprint
 | 
			
		||||
isprint:
 | 
			
		||||
	subb $0x20, %al
 | 
			
		||||
	cmpb $(0x7E - 0x20), %al
 | 
			
		||||
	ja .isprint_not_printable
 | 
			
		||||
	movb $1, %al
 | 
			
		||||
	ret
 | 
			
		||||
 .isprint_not_printable:
 | 
			
		||||
	movb $0, %al
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
.section .bss
 | 
			
		||||
 | 
			
		||||
# enough for base 2 printing
 | 
			
		||||
print_number_buffer:
 | 
			
		||||
	.skip 16
 | 
			
		||||
		Loading…
	
		Reference in New Issue