/*
 * boot.S86  -  boot sector for DOS ramdisk
 *
 * 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: boot.S86,v 1.6 2007/01/06 18:31:23 gkminix Exp $
 *
 *====================================================================
 */

#include <common.i86>
#include <system/bios.i86>
#include "headers/boot.i86"
#include "headers/dos.i86"
#include "headers/fat.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Define 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 an ldscript file that this starts at an absolute address.
 */

	.text


	.global	_start

_start:	jmp	start
	nop

/*
 * Define boot record (defaults are for 1.44MB floppy disk)
 */

	.ascii	BOOT_OEM_NAME		# OEM name
	.word	SECTSIZE		# bytes per sector
	.byte	BOOT_SPC		# number of sectors per cluster
	.word	1			# number of reserved sectors
	.byte	BOOT_FATNUM		# number of FATs
	.word	BOOT_DIRNUM		# number of entries in root directory
	.word	BOOT_SECNUM		# total number of sectors on disk
	.byte	BOOT_FORMAT		# media ID
	.word	BOOT_SPFAT		# sectors per fat
	.word	BOOT_SPTRK		# sectors per track
	.word	BOOT_HEADS		# number of heads
	.long	0			# number of hidden sectors
	.long	BOOT_SECNUM		# total number of sectors on disk
	.byte	BOOT_ID			# BIOS number of boot device
	.byte	0			# head number of boot sector
	.byte	0x29			# disk attributes
	.long	0			# volume number
	.ascii	BOOT_VOL_NAME		# volume name
	.ascii	BOOT_FAT12_NAME		# filesystem name
	.word	4			# min no of sectors in IO.SYS
	.word	IOSYS_SEG * 16		# load offset for IO.SYS


/*
 * Start of main routine. First we have to check if we have to relocate
 * ourselves in case we have to load so many IO.SYS sectors that we are
 * going to overwrite something.
 *
 * Note that we can safely use 32-bit instructions here, because this
 * boot sector gets only executed when booting from a ramdisk created
 * by mknbi-dos, and the DOS boot loader already checked for 386+.
 */

start:	mov	bx,cs
	mov	ds,bx
	mov	[runseg],bx		# save run segment
	mov	si,offset _start
	mov	ax,[bootrec_io_sys_sects + _start]
	inc	ax			# compute size of IO.SYS in bytes
	mov	cx,bpb_sectsize[bootrec_bpb + _start]
	mul	cx
	add	ax,[bootrec_io_sys_ofs + _start]
	adc	dx,0			# add to load offset
	jnz	1f
	cmp	ax,si			# should not exceed start address
	jbe	3f
1:	sub	ax,si
	sbb	dx,0
	shr	ax,4
	shl	dx,12			# compute start segment number of
	or	ax,dx			# this boot sector
	add	bx,ax
	mov	es,bx
	mov	di,si
	cld
	rep	movsb			# move boot sector
	push	bx
	mov	ax,offset 3f		# jump to new copy
	push	ax
	lret

3:	cli
	mov	cx,cs
	mov	ss,cx
	mov	sp,offset _start - 4	# set stack to beginning of code
	sti

/*
 * Get DPB vector and initialize disk system. The disk parameter block vector
 * is only required by DOS.
 */

	mov	ax,0x9C05
	int	0xF8			# get DPB address from ramdisk driver
	xor	ax,ax
	mov	ds,ax
	mov	si,DPB_VECT
	push	dword ptr [si]		# push old DPB address
	push	ax			# push address of interrupt vector
	push	si
	mov	word ptr [si + 0],dx	# set new DPB address
	mov	word ptr [si + 2],bx
	mov	ds,cx
	mov	es,cx

	xor	dx,dx
	int	INT_DISK		# initialize disk system

/*
 * Compute the logical number of the first sector of the data area. We
 * assume that IO.SYS is always located at the beginning. This might
 * not be true for a real DOS disk, but is always correct for a disk
 * image produced by mknbi.
 */

	mov	al,bpb_fatnum[bootrec_bpb + _start]
	xor	ah,ah
	mul	word ptr bpb_spfat[bootrec_bpb + _start]
	add	ax,word ptr bpb_hidden[bootrec_bpb + _start + 0]
	adc	dx,word ptr bpb_hidden[bootrec_bpb + _start + 2]
	add	ax,bpb_reserved[bootrec_bpb + _start]
	adc	dx,0
	mov	bx,DIR_SEG
	add	bx,[runseg]
	mov	word ptr [ldseg],bx
	call	rdsect			# read first directory sector
	jc	doerr

	push	dx
	mov	cx,ax			# compute size of root directory area
	mov	ax,32
	mul	word ptr bpb_dirnum[bootrec_bpb + _start]
	mov	bx,bpb_sectsize[bootrec_bpb + _start]
	add	ax,bx
	dec	ax			# round up to sector size
	div	bx
	add	ax,cx			# add number of sectors used by root
	pop	dx			# directory
	adc	dx,0

/*
 * Now load the first 4 sectors of IO.SYS at 0x0070. Note that starting
 * with Windows 95b (aka OSR2), Microsoft changed the format of IO.SYS
 * from a plain binary executable into a sort of EXE-style file. If the
 * EXE-file header can be found at the beginning of IO.SYS, we have to
 * call it at offset 0x0200, otherwise at offset 0x0000.
 */

	push	dx
	push	ax
	mov	bx,[bootrec_io_sys_ofs + _start]
	shr	bx,4				# compute load segment
	add	bx,[runseg]
	mov	[ldseg],bx
	mov	[runseg],bx
	mov	cx,[bootrec_io_sys_sects + _start]
7:	call	rdsect				# read sector
	jc	doerr
	add	ax,1
	adc	dx,0				# continue with next sector
	mov	bx,bpb_sectsize[bootrec_bpb + _start]
	shr	bx,4
	add	[ldseg],bx
	loop	7b
	pop	bx				# get first sector of IO.SYS
	pop	ax

	mov	cx,[runseg]
	mov	es,cx
	cmp	word ptr es:[0],EXE_SIG
	jne	8f				# set IO.SYS execution address
	mov	word ptr [jmpadr],EXE_EXECADDR
						# prepare registers for IO.SYS
8:	mov	ch,bpb_mediaid[bootrec_bpb + _start]
	mov	dl,[bootrec_boot_id + _start]
	mov	bp,offset _start
	mov	word ptr [bp - 2],ax
	mov	word ptr [bp - 4],bx
	cmp	word ptr [bootrec_io_sys_ofs + _start],FREEDOS_SEG * 16
	jne	9f				# Arghhh... FreeDOS expects the
	mov	bl,dl				# boot drive in BL, not DL as
9:	mov	si,_I(bootrec_bpb + _start)	# all others
	mov	di,2				# set first cluster no. of IO.SYS
	ljmp	[jmpadr]			# call IO.SYS

 

/*
 *====================================================================
 *
 * Print error message and terminate
 * Input:  none
 * Output: Routine does not return
 * Changed registers: All
 */
doerr:	mov	si,offset errmsg
2:	lodsb
	or	al,al
	jz	3f
	mov	ah,0x0E
	mov	bx,7			# print using BIOS
	int	INT_VIDEO
	jmp	2b

3:	xor	ax,ax
	int	INT_KBD			# wait for key press
	pop	si			# get pointer to int 0x1E vector
	pop	ds
	pop	word ptr [si]		# restore old DPB address
	pop	word ptr [si + 2]
	int	INT_BOOTSTRAP		# reboot the system



/*
 *====================================================================
 *
 * Read one logical sector from the ram disk
 * Input:  DX:AX  -  32-bit logical sector number
 * Output: none
 * Changed registers: none
 */
rdsect:	push	ax
	push	bx
	push	dx
	push	cx
	div	word ptr bpb_sptrack[bootrec_bpb + _start]
	inc	dl
	mov	cl,dl
	xor	dx,dx
	div	word ptr bpb_headnum[bootrec_bpb + _start]
	mov	dh,dl				# convert logical sector number
	mov	ch,al				# into cylinder/head/sector
	ror	ah,2				# format
	or	cl,ah
	mov	ax,0x0201
	mov	dl,[bootrec_boot_id + _start]
	push	es
	mov	es,[ldseg]			# set load address
	xor	bx,bx
	int	INT_DISK			# read one sector from ramdisk
	pop	es
	pop	cx
	pop	dx
	pop	bx
	pop	ax
	ret




/*
 *====================================================================
 *
 * Data area
 */
errmsg:	.byte	0x0D, 0x0A
	.ascii	"Ramdisk error - hit a key"
	.byte	0x0D, 0x0A
	.byte	0


/*
 * This "magic" word needs to be here for MS-DOS 7.1 compatibility
 */

	.org	BOOT_MAGIC_OFS
	.word	BOOT_MAGIC


/*
 * Some misc. variables
 */

jmpadr:	.word	0			# address of IO.SYS to jump to
runseg:	.word	0			# run segment
ldseg:	.word	0			# load segment


/*
 * The end of the boot sectors has to contain the boot signature.
 */

	.org	BOOT_SIG_OFS
	.word	BOOT_SIG_PRIV




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

