/* NOTE: this boot sector contains instructions that need at least an 80186.
 * Yes, as86 has a bug somewhere in the valid instruction set checks.
 *
 */

/*	floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
 *	modified by Drew Eckhardt
 *	modified by Bruce Evans (bde)
 *
 * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
 *
 * It then loads the system at SYSSEG<<4, using BIOS interrupts.
 *
 * The loader has been made as simple as possible, and continuous read errors
 * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
 * getting whole tracks at a time whenever possible.
 */

FILE_LICENCE ( GPL2_ONLY )

#include <librm.h>

.equ	BOOTSEG, 0x07C0			/* original address of boot-sector */

.equ	SYSSEG, 0x1000			/* system loaded at SYSSEG<<4 */

	.org	0
	.arch i386
	.text
	.section ".prefix", "ax", @progbits
	.code16
	.globl	_dsk_start
_dsk_start:

	jmp	$BOOTSEG, $go		/* reload cs:ip to match relocation addr */
go: 
	movw	$0x2000-12, %di		/* 0x2000 is arbitrary value >= length */
					/* of bootsect + room for stack + 12 for */
					/* saved disk parm block */

	movw	$BOOTSEG, %ax
	movw	%ax,%ds
	movw	%ax,%es
	movw	%ax,%ss			/* put stack at BOOTSEG:0x4000-12. */
	movw	%di,%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 36 - the most we will encounter on an
 * ED 2.88.  High doesn't hurt.	Low does.
 *
 * Segments are as follows: ds=es=ss=cs - BOOTSEG
 */

	xorw	%cx,%cx
	movw	%cx,%es			/* access segment 0 */
	movw	$0x78, %bx		/* 0:bx is parameter table address */
	pushw	%ds			/* save ds */
/* 0:bx is parameter table address */
	ldsw	%es:(%bx),%si		/* loads ds and si */

	movw	%ax,%es			/* ax is BOOTSECT (loaded above) */
	movb	$6, %cl			/* copy 12 bytes */
	cld
	pushw	%di			/* keep a copy for later */
	rep
	movsw				/* ds:si is source, es:di is dest */
	popw	%di

	movb	$36,%es:4(%di)

	movw	%cx,%ds			/* access segment 0 */
	xchgw	%di,(%bx)
	movw	%es,%si
	xchgw	%si,2(%bx)
	popw	%ds			/* restore ds */
	movw	%di, dpoff		/* save old parameters */
	movw	%si, dpseg		/* to restore just before finishing */
	pushw	%ds
	popw	%es			/* reload es */

/* Note that es is already set up.  Also cx is 0 from rep movsw above. */

	xorb	%ah,%ah			/* reset FDC */
	xorb	%dl,%dl
	int	$0x13

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

	movw	$disksizes, %si		/* table of sizes to try */

probe_loop: 
	lodsb
	cbtw				/* extend to word */
	movw	%ax, sectors
	cmpw	$disksizes+4, %si
	jae	got_sectors		/* if all else fails, try 9 */
	xchgw	%cx,%ax			/* cx = track and sector */
	xorw	%dx,%dx			/* drive 0, head 0 */
	movw	$0x0200, %bx		/* address after boot sector */
					/*   (512 bytes from origin, es = cs) */
	movw	$0x0201, %ax		/* service 2, 1 sector */
	int	$0x13
	jc	probe_loop		/* try next value */

got_sectors: 
	movw	$msg1end-msg1, %cx
	movw	$msg1, %si
	call	print_str

/* ok, we've written the Loading... message, now we want to load the system */

	movw	$SYSSEG, %ax
	movw	%ax,%es			/* segment of SYSSEG<<4 */
	pushw	%es
	call	read_it

/* This 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.
 */
	movw	$0x3f2, %dx
	xorb	%al,%al
	outb	%al,%dx

	call	print_nl
	pop	%es			/* = SYSSEG */

/* Restore original disk parameters */
	movw	$0x78, %bx
	movw	dpoff, %di
	movw	dpseg, %si
	xorw	%ax,%ax
	movw	%ax,%ds
	movw	%di,(%bx)
	movw	%si,2(%bx)

	/* Everything now loaded.  %es = SYSSEG, so %es:0000 points to
	 * start of loaded image.
	 */

	/* Jump to loaded copy */
	ljmp	$SYSSEG, $start_runtime

endseg:	.word SYSSEG
	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
	.ascii	"ADDW"
	.long	endseg
	.long	16
	.long	0
	.previous

/* This routine loads the system at address SYSSEG<<4, 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 SYSSEG)
 */
read_it: 
	movw	$0,sread		/* load whole image including prefix */
	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
	movw	%bx,%dx
	movb	$4, %cl
	shrw	%cl,%dx			/* bx is always divisible by 16 */
	addw	%dx,%ax
	cmpw	endseg, %ax	/* have we loaded all yet? */
	jb	ok1_read
	ret
ok1_read: 
	movw	sectors, %ax
	subw	sread, %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
	addw	sread, %ax
	cmpw	sectors, %ax
	jne	ok3_read
	movw	$1, %ax
	subw	head, %ax
	jne	ok4_read
	incw	track
ok4_read: 
	movw	%ax, head
	xorw	%ax,%ax
ok3_read: 
	movw	%ax, sread
	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
	pushw	%ax
	pushw	%bx
	pushw	%bp			/* just in case the BIOS is buggy */
	movw	$0x0e2e, %ax		/* 0x2e = . */
	movw	$0x0007, %bx
	int	$0x10
	popw	%bp
	popw	%bx
	popw	%ax

	movw	track, %dx
	movw	sread, %cx
	incw	%cx
	movb	%dl,%ch
	movw	head, %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: 
	call	print_nl		/* nl for readability */
	movw	$5, %cx			/* error code + 4 registers */
	movw	%sp,%bp

print_loop: 
	pushw	%cx			/* save count left */

	cmpb	$5, %cl
	jae	no_reg			/* see if register name is needed */

	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
	movw	$0xe05+0x41-1, %ax
	subb	%cl,%al
	int	$0x10

	movb	$0x58, %al		/* 'X' */
	int	$0x10

	movb	$0x3A, %al		/* ':' */
	int	$0x10

no_reg: 
	addw	$2, %bp			/* next register */
	call	print_hex		/* print it */
	movb	$0x20, %al		/* print a space */
	int	$0x10
	popw	%cx
	loop	print_loop
	call	print_nl		/* nl for readability */
	ret

print_str: 
	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
	movb	$0x0e, %ah		/* write char, tty mode */
prloop: 
	lodsb
	int	$0x10
	loop	prloop
	ret

print_nl: 
	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
	movw	$0xe0d, %ax		/* CR */
	int	$0x10
	movb	$0xa, %al		/* LF */
	int	$0x10
	ret

/* print_hex prints the word pointed to by ss:bp in hexadecimal. */

print_hex: 
	movw	(%bp),%dx		/* load word into dx */
	movb	$4, %cl
	movb	$0x0e, %ah		/* write char, tty mode */
	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
	call	print_digit
	call	print_digit
	call	print_digit
/* fall through */
print_digit: 
	rol	%cl,%dx			/* rotate so that lowest 4 bits are used */
	movb	$0x0f, %al		/* mask for nybble */
	andb	%dl,%al
	addb	$0x90, %al		/* convert al to ascii hex (four instructions) */
	daa
	adcb	$0x40, %al
	daa
	int	$0x10
	ret

sread:	.word 0				/* sectors read of current track */
head:	.word 0				/* current head */
track:	.word 0				/* current track */

sectors: 
	.word 0

dpseg:	.word 0
dpoff:	.word 0

disksizes: 
	.byte 36,18,15,9

msg1: 
	.ascii "Loading ROM image"
msg1end: 

	.org 510, 0
	.word 0xAA55

start_runtime:
	/* Install iPXE */
	call	install

	/* Set up real-mode stack */
	movw	%bx, %ss
	movw	$_estack16, %sp

	/* Jump to .text16 segment */
	pushw	%ax
	pushw	$1f
	lret
	.section ".text16", "awx", @progbits
1:
	/* Run iPXE */
	virtcall main

	/* Uninstall iPXE */
	call	uninstall

	/* Boot next device */
	int $0x18

