/*
 *	 bootsect.s		Copyright (C) 1991, 1992 Linus Torvalds
 *
 * bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
 * itself out of the way to address 0x90000, and jumps there.
 *
 * It then loads 'setup' directly after itself (0x90200), and the system
 * at 0x10000, using BIOS interrupts.
 *
 * The loader has been made as simple as possible, and continuos
 * read errors will result in a unbreakable loop. Reboot by hand. It
 * loads pretty fast by getting whole tracks at a time whenever possible.
 *
 * 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.
 */

#include "defs.h"

ROOT_DEV = 0

.code16
.section ".bootsect", "ax", @progbits
_boot:


# ld86 requires an entry symbol. This may as well be the usual one.
.globl	_main
_main:
	movw	$BOOTSEG, %ax
	movw	%ax, %ds
	movw	$INITSEG, %ax
	movw	%ax, %es
	movw	$256, %cx
	subw	%si, %si
	subw	%di, %di
	cld
	rep
	movsw
	ljmp	$INITSEG, $go - _boot

go:
	movw	%cs, %ax
	movw	$(0x4000-12), %dx	# 0x4000 is arbitrary value >= length of
					# bootsect + length of setup + room for stack
					# 12 is disk parm size

# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
# wouldn't have to worry about this if we checked the top of memory.  Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table.  The old stack might have clobbered the
# drive table.

	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss		# put stack at INITSEG:0x4000-12.
	movw	%dx, %sp

/*
 *	Many BIOS's default disk parameter tables will not
 *	recognize multi-sector reads beyond the maximum sector number
 *	specified in the default diskette parameter tables - this may
 *	mean 7 sectors in some cases.
 *
 *	Since single sector reads are slow and out of the question,
 *	we must take care of this by creating new parameter tables
 *	(for the first disk) in RAM.  We will set the maximum sector
 *	count to 18 - the most we will encounter on an HD 1.44.
 *
 *	High doesn't hurt.  Low does.
 *
 *	Segments are as follows: ds=es=ss=cs - INITSEG,
 *		fs = 0, gs = parameter table segment
 */
	pushw	$0
	popw	%fs
	movw	$0x78, %bx		# fs:bx is parameter table address
	lgs	%fs:(%bx),%si		# gs:si is source

	movw	%dx, %di		# es:di is destination
	movw	$6, %cx			# copy 12 bytes
	cld

	rep	movsw %gs:(%si), (%di)

	movw	%dx, %di
	movb	$18, 4(%di)		# patch sector count

	movw	%di, %fs:(%bx)
	movw	%es, %fs:2(%bx)

	movw	%cs, %ax
	movw	%ax, %fs
	movw	%ax, %gs

	xorb	%ah, %ah		# reset FDC
	xorb	%dl, %dl
	int	$0x13

# load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.

load_setup:
	xorw	%dx, %dx			# drive 0, head 0
	movw	$0x0002, %cx			# sector 2, track 0
	movw	$0x0200, %bx			# address = 512, in INITSEG
	movw	$(0x0200 + SETUPSECS), %ax	# service 2, nr of sectors
						# (assume all on head 0, track 0)
	int	$0x13				# read it
	jnc	ok_load_setup			# ok - continue

	pushw	%ax			# dump error code
	call	print_nl
	movw	%sp, %bp
	call	print_hex
	popw	%ax

	xorb	%dl, %dl		# reset FDC
	xorb	%ah, %ah
	int	$0x13
	jmp	load_setup

ok_load_setup:

# Get disk drive parameters, specifically nr of sectors/track


/* It seems that there is no BIOS call to get the number of sectors.  Guess
 * 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
 * Otherwise guess 9
 */

	xorw	%dx, %dx			# drive 0, head 0
	movw	$0x0012, %cx			# sector 18, track 0
	movw	$(0x200+(SETUPSECS*0x200)), %bx	# address after setup (es = cs)
	movw	$0x0201, %ax			# service 2, 1 sector
	int	$0x13
	jnc	got_sectors
	movb	$0x0f, %cl			# sector 15
	movw	$0x0201, %ax			# service 2, 1 sector
	int	$0x13
	jnc	got_sectors
	movb	$0x09, %cl

got_sectors:
	movw	%cx, %cs:sectors - _boot
	movw	$INITSEG, %ax
	movw	%ax, %es

# Print some inane message

	movb	$0x03, %ah		# read cursor pos
	xorb	%bh, %bh
	int	$0x10

	movw	$9, %cx
	movw	$0x0007, %bx		# page 0, attribute 7 (normal)
	movw	$msg1 - _boot, %bp
	movw	$0x1301, %ax		# write string, move cursor
	int	$0x10

# ok, we've written the message, now
# we want to load the system (at 0x10000)

	movw	$TSTLOAD, %ax
	movw	%ax, %es		# segment of 0x010000
	call	read_it
	call	kill_motor
	call  turnoffcursor
	call	print_nl

# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:

	ljmp	$SETUPSEG,$0

# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
#
# in:	es - starting address segment (normally 0x1000)
#
sread:	.word 1+SETUPSECS	# sectors read of current track
head:	.word 0			# current head
track:	.word 0			# current track

read_it:
	movw	%es, %ax
	testw	$0x0fff, %ax
die:
	jne	die		# es must be at 64kB boundary
	xorw	%bx,%bx		# bx is starting address within segment
rp_read:
	movw	%es, %ax
	subw	$TSTLOAD, %ax	# have we loaded all yet?
	cmpw	syssize - _boot, %ax
	jbe	ok1_read
	ret
ok1_read:
	movw	%cs:sectors - _boot, %ax
	subw	sread - _boot, %ax
	movw	%ax, %cx
	shlw	$9, %cx
	addw	%bx, %cx
	jnc	ok2_read
	je	ok2_read
	xorw	%ax, %ax
	subw	%bx, %ax
	shrw	$9, %ax
ok2_read:
	call	read_track
	movw	%ax, %cx
	add	sread - _boot, %ax
	cmpw	%cs:sectors - _boot, %ax
	jne	ok3_read
	movw	$1, %ax
	subw	head - _boot, %ax
	jne	ok4_read
	incw	track - _boot
ok4_read:
	movw	%ax, head - _boot
	xorw	%ax, %ax
ok3_read:
	movw	%ax, sread - _boot
	shlw	$9, %cx
	addw	%cx, %bx
	jnc	rp_read
	movw	%es, %ax
	addb	$0x10, %ah
	movw	%ax, %es
	xorw	%bx, %bx
	jmp	rp_read

read_track:
	pusha
	pusha
	movw	$0xe2e, %ax 	# loading... message 2e = .
	movw	$7, %bx
	int	$0x10
	popa

	movw	track - _boot, %dx
	movw	sread - _boot, %cx
	incw	%cx
	movb	%dl, %ch
	movw	head - _boot, %dx
	movb	%dl, %dh
	andw	$0x0100, %dx
	movb	$2, %ah

	pushw	%dx				# save for error dump
	pushw	%cx
	pushw	%bx
	pushw	%ax

	int	$0x13
	jc	bad_rt
	addw	$8, %sp
	popa
	ret

bad_rt:
	pushw	%ax				# save error code
	call	print_all			# ah = error, al = read

	xorb	%ah, %ah
	xorb	%dl, %dl
	int	$0x13

	addw	$10, %sp
	popa
	jmp read_track

/*
 *	print_all is for debugging purposes.
 *	It will print out all of the registers.  The assumption is that this is
 *	called from a routine, with a stack frame like
 *	dx
 *	cx
 *	bx
 *	ax
 *	error
 *	ret <- sp
 *
*/

print_all:
	movw	$5, %cx		# error code + 4 registers
	movw	%sp, %bp

print_loop:
	pushw	%cx		# save count left
	call	print_nl	# nl for readability

	cmpb	5, %cl		# see if register name is needed
	jae	no_reg

	movw	$(0xe05	+ 'A' - 1), %ax
	subb	%cl, %al
	int	$0x10
	movb	$'X', %al
	int	$0x10
	movb	$':', %al
	int	$0x10

no_reg:
	addw	$2, %bp		# next register
	call	print_hex	# print it
	popw	%cx
	loop	print_loop
	ret

print_nl:
	movw	$0xe0d, %ax	# CR
	int	$0x10
	movb	$0x0a, %al	# LF
	int	$0x10
	ret

/*
 *	print_hex is for debugging purposes, and prints the word
 *	pointed to by ss:bp in hexadecmial.
 */

print_hex:
	movw	$4, %cx		# 4 hex digits
	movw	(%bp), %dx	# load word into dx

print_digit:
	rolw	$4, %dx		# rotate so that lowest 4 bits are used
	movb	$0xe, %ah
	movb	%dl, %al	# mask off so we have only next nibble
	andb	$0xf, %al
	addb	$'0', %al	# convert to 0-based digit
	cmpb	$'9', %al	# check for overflow
	jbe	good_digit
	addb	$('A' - '0' - 10), %al

good_digit:
	int	$0x10
	loop	print_digit
	ret


/*
 * This procedure turns off the floppy drive motor, so
 * that we enter the kernel in a known state, and
 * don't have to worry about it later.
 */
kill_motor:
	pushw	%dx
	movw	$0x3f2, %dx
	xorb	%al, %al
	outb	%al, %dx
	popw	%dx
	ret

turnoffcursor:
  movb  $0x01, %ah      # turn off the cursor
  movb  $0x00, %bh
  movw  $0x2000, %cx
  int   $0x10 
	ret
	
sectors:
	.word 0

msg1:
	.byte 13,10
	.ascii "Loading"

.org 497
setup_sects:
	.byte SETUPSECS
.org 500
syssize:
	.word _syssize
.org 508
root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55
_eboot:
