/* Released under GPL version 2
**
** How to read/update MultiBoot MBR:
** 1. Check that first 16 bytes are "xxxMultiBoot 0.4"
** 2. Update fields at 0x13..0x20 as you need.
** 3. offsets[0] (i.e. 2-byte field at 1a) holds a pointer to
**    the text label of 1st partition, do not change it,
**    but use it's value as a starting address of new labels.
** 4. offsets[1..3] can (should) be modified
** 5. Labels should be written as null-terminated strings
**    in the offsets[0]...0x1b5 range.
** 6. NB: you need to adjust offsets[i] values by 0x600
**    (i.e. offsets[i]==0x740 means "offset 0x140 from the start of MBR")
** 7. Do not touch stuff at and after 0x1b6 - it may be used
**    Windows/whatever
**
** A few special cases:
** if default_char doesn't match any of chars[], there is no default
**	and also there is no automatic boot timeout.
** if char[i] = '\0', the partition is unselectable.
** delay = 0 suppresses any output, and boots default partition
** with no delay; but if default_char doesn't match any of chars[], then
**	user will need BLINDLY type the desired partition char
**	in order to boot.
*/
		.code16
		.text

		.org	0x600
start0:
		cli	# lilo 22.5.8: "NT 4 blows up if this is missing"
		jmp	start1

banner:		.ascii	"\r\nExtBoot 0.4"

# The above is exactly 16 bytes and may be used as a signature
# for tools trying to directly manipulate the MBR

crlf:		.asciz	"\r\n"		# 10
default_char:	.byte	'A'		# 13
delay:		.word 	5*18		# 14
chars:		.byte	'A',0		# 16
offsets:	.word	part1_msg	# 18

// The disk I/O packet
edd_packet:	.word   16		# size of packet
		.word   1		# count of sectors to transfer
addr:		.word   0x7c00		# address offset to transfer to
					# the rest of packet is in bss! :)
bss:	# our bss, see (*) below
#		.word   0		# address segment to transfer to
#daddr:		.long   0		# low order disk address
#		.long   0		# high order disk address
#
#char_count:	.word	0
#
daddr = bss + 2
char_count = bss + 10
bss_size = 12

start1:
// Save our own ext partition's sector offset
		mov	8(%si),%ebx

// Move ourself to 0x600
		mov	$0x7c00,%si
		xor	%ax,%ax
		mov	%ax,%es
		mov	%ax,%ds
		mov	%ax,%ss
		mov	%si,%sp
		sti
		cld
		mov	$start0,%di
		mov	$0x200/2,%cx
		rep movsw
		jmp	$0,$next
next:
		mov	%ebx,read_ofs	# all nested ext parts have star_ofs
					# relative to _first ext part_!
// Zero out bss (*)
		mov	$bss,%di
		mov	$bss_size/2,%cl	# %ch and %ax are still zero
		repnz stosw

// Print banner
		mov	$banner,%si
		call	print_crlf

// Print list of partitions
		mov	$char_list,%di
next_sect:	mov	chars_orig,%al
		test	%al,%al
		jz	skip_part
		incw	char_count
		stosb
		mov	$delim,%si
		call	putc_and_print
		mov	offsets_orig,%si
		add	$0x7c00-0x600,%si
		call	print_crlf
skip_part:	mov	$part_tbl_orig + 0x10,%si
		cmpb	$0,4(%si)	# type = 0?
		jz	no_more_extd
		call	disk_read
		jmp	next_sect
no_more_extd:

// 'Choose:'
		mov	$choose,%si	
		call	print

// Calculate timeout deadline
		mov	$0,%ah		# get time
		int	$0x1a
		add	delay,%dx	# cx:dx += delay
		adc	$0,%cx
		mov	%dx,%bx		# si:bx = deadline
		mov	%cx,%si

wait_for_interrupt:
		hlt
		mov	$0,%ah		# get time
		int	$0x1a
		sub	%bx,%dx		# timed out? (cx:dx - si:bx > 0?)
		sbb	%si,%cx
		jae	use_default	# yes, bail out, use default
		mov	$0x1,%ah	# is a key pressed?
		int	$0x16
		je	wait_for_interrupt	# no, repeat
getchar:
		mov	$0,%ah
		int	$0x16
		cmp	$0xd,%al	# if <enter> - take default
		jne	not_default
use_default:
		mov	default_char,%al
not_default:	mov	char_count,%cx	# look up pressed char
		mov	$char_list,%di
		repne scasb
		jne	getchar		# not found? - repeat

// The partition has been selected
		mov	$crlf,%si	# display the char
		push	%ax
		call	putc_and_print
		pop	%ax

// Walk the partition chain until we see selected ext partition
		mov	$start0,%si
		mov	%sp,%di		# sp = 0x7c00
		mov	$0x200/2,%cx	# we start walk from ourself,
		rep movsw		# so we copy ourself back to 0x7c00
		mov	read_ofs,%ebx
		mov	%ebx,daddr

next_sect1:	cmp	%al,chars_orig
		je	found
		mov	$part_tbl_orig + 0x10,%si
		call	disk_read
		jmp	next_sect1
found:

// Boot the partition pointed by part_orig[0] in selected ext partition
		mov	daddr,%eax	# sect_ofs in part_tbl[0] is relative
		mov	%eax,read_ofs	# to ext part _it sits in_, that's why

		mov	$part_tbl_orig,%si
		call	disk_read
		cmpw	$0xaa55,0x7c00 + 0x200 - 2
		jne	missing_os
// By convention, ds:si must point to partition table entry
// which was used for booting.
// We copy entire partition entry for good...
		mov	$part_tbl_copy,%di
		push	%di
		mov	$0x10/2,%cx
		rep movsw
//mov $0x7c00,%si
//call print
		pop	%si
		movb	$0x80,(%si)	# "bootable". boot sectors may expect that
		jmp	*%sp		# jmp to 0x7c00
		
read_error:
		mov	$read_error_msg,%si
		jmp	do_print
missing_os:
		mov	$missing_os_msg,%si
do_print:	call	print
hang:		jmp	hang


/* Disk read subroutine
*/
disk_read:
		pusha
		mov	8(%si),%eax	# copy part_start into disk addr
		add	read_ofs,%eax
		mov	%eax,daddr

		mov	$0x80,%dl	# hdd0
		mov	$0x55aa,%bx	# magic number
		mov	$0x41,%ah
		int	$0x13
		jc	disk_read_old	# error?
		cmp	$0xaa55,%bx	# is magic number changed?
		jne	disk_read_old
		test	$1,%cl		# are EDD packet calls supported?
		jz	disk_read_old
		mov	$edd_packet,%si	# ds:si => packet
		mov	$0x42,%ah	# edd packet read
		int	$0x13
		jmp	chk_carry

disk_read_old:
		mov	(%si),%dx	# disk,head
		mov	$0x80,%dl
		mov	2(%si),%cx	# cyl,sect
		mov	$0x7c00,%bx	# es:bx => buffer
		mov	$0x201,%ax	# read, 1 sect
		int	$0x13
chk_carry:
		jc	read_error
		popa
		ret


/* Printing subroutines
*/
putc_and_print:
		mov	$0xe,%ah
		int	$0x10
print:
		lodsb
		test	%al,%al
		jnz	putc_and_print
		ret	
print_crlf:
		call	print
		mov	$crlf,%si
		jmp	print


choose:		.ascii	"\r\nChoose"
delim:		.asciz	": "
read_error_msg:	.asciz	"Read error"
missing_os_msg:	.asciz	"Missing OS"
part1_msg:	.asciz	"ExtPartition"

// Pad the .bin to the exact size of MBR code
			//    sector - sig - ptbl - pad - vol - pad
		.org	start0 + 0x200 - 2 - 4*16 - 2 - 4 - 2
end_mbr_code:

// Volume serial#
/*		.word	?
vol_serial_no:	.long	?
		.word	?
*/

part_tbl_copy = end_mbr_code + 2 + 4 + 2
// Partition table entry x4
/*		.byte	?	# +0: 0x80/0x00 - bootable/not bootable
		.byte	?	# +1: head (start)
		.word	?	# +2: cyl+sect (start)
		.byte	?	# +4: type
		.byte	?	# +5: head (end)
		.word	?	# +6: cyl+sec (end)
		.dword	?	# +8: offset in sectors
		.dword	?	# +12: size in sectors
				# +16: size of entry is 16 bytes
*/

// Signature
/*		.word	?
*/

// Uninitialied data past the sector end
read_ofs = start0 + 0x200
char_list = start0 + 0x204

// Fields in original bootsector's location
chars_orig = 0x7c00 + chars-start0
offsets_orig =  0x7c00 + offsets-start0
part_tbl_orig = 0x7c00 + 0x200 - 2 - 4*16
