/*
 * loader.S86  -  floppy loader for romcheck program
 *
 * Copyright (C) 1996-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: loader.S86,v 1.7 2007/01/06 18:31:21 gkminix Exp $
 *
 *====================================================================
 */


#include <common.i86>
#include <system/bios.i86>

.file __FILE__
.line __LINE__




/*
 *====================================================================
 *
 * Definitions for the floppy loader:
 */

#define BOOTSIG		0xAA55		/* signature of boot sector */
#define BOOTSEG		0xFFFF		/* segment of cold boot vector */
#define SECTSIZE	512		/* size of one floppy sector */
#define SECTNUM		64		/* number of sectors to load (32kB) */



.line __LINE__
/*
 *====================================================================
 *
 * Start the text segment. This segment has to start at 0x7C00,
 * but we cant use the .org directive. The assembler would then
 * write 0x7C00 times 0 values. Instead we have to tell the linker
 * using a ldscript file that this starts at an absolute address.
 *
 * The loader works by first patching the floppy disk parameter table,
 * and then trying to read the last sector of the floppy in order to
 * find out about the disk size. Then it will load the remaining romcheck
 * program from the diskette.
 */

	.text


	.global	_start

_start:	mov	ax,cs
	mov	ds,ax
	mov	es,ax

/*
 * Now determine the number of sectors per track on the floppy. To do
 * this the disk parameter table has first to be changed, so that the
 * BIOS is able to access at least 18 sectors in a track.
 */

	cld
	push	ds
	mov	dx,ds
	xor	ax,ax
	mov	es,ax				# first save the old disk
	lds	si,es:[DPB_VECT]		# parameter table
	mov	es,dx
	mov	word ptr es:[oldvect+0],si	# save old vector
	mov	word ptr es:[oldvect+2],ds
	mov	di,offset newdpb
	mov	cx,DPB_SIZE
	rep
	movsb
	pop	ds
	mov	al,18				# set new number of sectors
	mov	dpb_sptrack[newdpb],al		# per track

	xor	ax,ax				# set pointer to new DPB
	mov	es,ax
	mov	word ptr es:[DPB_VECT + 0],offset newdpb
	mov	word ptr es:[DPB_VECT + 2],ds

/*
 * Next try to find the number of sectors per track. To do this, the
 * last sector for each disk format is read. This idea has been taken
 * from the Linux boot sector.
 */

	xor	ah,ah			# reset drive 0
	xor	dl,dl
	int 	INT_DISK

	mov	ax,ds
	mov	es,ax			# set load address to end of loader
	mov	bx,offset flopend
	xor	dx,dx
	mov	cx,1			# load one sector to initialize drive
	mov	ax,0x0201
	int	INT_DISK

	xor	dx,dx			# drive 0, head 0
	mov	cx,18			# sector 18, track 0
	mov	ax,0x0201		# read one sector
	int	INT_DISK
	jnc	gotit
	mov	cx,0x15			# sector 15
	mov	ax,0x0201		# read one sector
	int	INT_DISK
	jnc	gotit
	mov	cx,9
gotit:	mov	[sects],cx		# save number of sector per track

/*
 * Now load the romcheck program starting at the second sector of track 0
 * directly at the end of this floppy loader. We blindly load 32kB in the
 * hope that romcheck is not larger than this.
 */

	mov	cl,4
	mov	bx,offset flopend + 15	# starting offset
	shr	bx,cl
	mov	ax,cs			# convert into a segment:offset
	add	ax,bx			# pointer with offset 0
	mov	es,ax
	xor	bx,bx
	mov	dx,SECTNUM		# number of sectors
	mov	ax,1			# starting sector
rdloop:	or	dx,dx
	jz	loaded
	push	ax
	push	dx
	call	cvtsec			# compute sector information
	mov	ax,0x0201		# load one sector
	int	INT_DISK
	pop	dx
	pop	ax
	jc	lderr
	dec	dx			# decrement sector count
	inc	ax			# increment sector number
	add	bx,SECTSIZE		# increment load pointer
	jmp	rdloop

lderr:	mov	si,offset dskmsg	# print error message
	call	prnstr
	jmp	reboot

/*
 * Restore the old DPB vector and call the romcheck program
 */

loaded:	cld
	push	ds
	push	es
	les	si,[oldvect]
	xor	ax,ax			# restore old vector
	mov	ds,ax
	mov	word ptr ds:[DPB_VECT + 0],si
	mov	word ptr ds:[DPB_VECT + 2],es
	pop	es
	pop	ds
	xor	ax,ax
	xor	dx,dx			# reset disk system
	int	INT_DISK
	push	es			# push pointer to romcheck program
	xor	ax,ax
	push	ax
	lret				# call romcheck program



/*
 *====================================================================
 *
 * Reboot the system
 * Input:  None
 * Output: Routine does not return
 * Registers changed: All
 */
reboot:	mov	si,offset botmsg
	call	prnstr
	xor	ah,ah				# wait for a key press
	int	INT_KBD
	mov	ax,BIOS_SEG
	mov	ds,ax
	mov	word ptr ds:[0x0072],0x1234	# indicates warm boot
	ljmp	BOOTSEG,0			# reboot the system



/*
 *====================================================================
 *
 * Routine to convert an absolute sector into a sector/track/head information.
 * Input:  AX  -  absolute sector number
 * Output: CL  -  sector number
 *         CH  -  track number
 *         DH  -  head number
 *         DL  -  0 (number for drive 0)
 * Registers changed: AX, CX, DX
 */
cvtsec:	xor	dx,dx
	div	word ptr [sects]	# compute sector number in a track
	inc	dl			# sector numbers always start with 1
	mov	cl,dl
	mov	dx,ax
	shr	ax,1			# divide by number of heads
	mov	ch,al			# save track number
	and	dx,1			# mask head number
	xchg	dl,dh
	ret



/*
 *====================================================================
 *
 * Routine to print a string onto the console.
 * Input:  DS:SI  -  address of NULL terminated string
 * Output: None
 * Registers changed: AX, SI
 */
prnstr:	push	bx
	mov	bx,0x0007		# set color and screen page
prnst1:	lodsb				# loop over all characters
	or	al,al
	jz	prnst9
	mov	ah,0x0E			# BIOS command for character output
	int	INT_VIDEO		# send character to screen
	jmp	prnst1
prnst9:	pop	bx
	ret




/*
 *====================================================================
 *
 * Strings and messages:
 */
dskmsg:	.ascii	"romcheck: Error reading diskette"
	.byte	0x0D, 0x0A
	.byte	0

botmsg:	.asciz	"Press any key to reboot..."




/*
 *====================================================================
 *
 * Data space for the floppy loader:
 */
sects:		.word	0		# number of sectors per track
oldvect:	.long	0		# old dpt vector
newdpb:		.skip	DPB_SIZE	# changed disk parameter block

		.org	SECTSIZE - 2
		.word	BOOTSIG

flopend:				# end of floppy loader sector



/*
 *====================================================================
 */
	.end

