/*
 **************************************************************************
 *
 * UNDI network driver
 *
 * Module:  cstartup.S86
 * Purpose: C startup file for UNDI driver
 * Entries: specified by header structure
 *
 **************************************************************************
 *
 * Copyright (C) 2005-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: cstartup.S86,v 1.8 2007/01/06 18:30:50 gkminix Exp $
 *
 */


/*
 **************************************************************************
 *
 * Include assembler macros:
 *
 */
#include <common.i86>
#include <pxe/general.i86>
#include <pxe/loader.i86>
#include <pxe/undi.i86>

.file __FILE__
.line __LINE__



/*
 **************************************************************************
 *
 * Start code segment.
 *
 */
	.text

	.extern	_pxe_load
	.extern	_pxe_entry


/*
 * Start of text segment. The ELF loader will add the correct offset
 * to this label lateron.
 */

__text_start:


/*
 * UNDI driver header
 */

	.ascii	"UNDI"
	.word	__text_end
	.word	__data_end
	.word	__bss_end
	.word	drv_start
	.word	__text_start
	.word	__data_start
	.word	__stack_size
	.word	__heap_size



/*
 **************************************************************************
 *
 * Jumps to the UNDI driver entry point. The data segment has already
 * been setup by the UNDI driver interface.
 * Input:  BX     -  UNDI function code
 *         ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: EAX, EBX, ECX, EDX, ESI
 */
drv_start:

/*
 * Initialize the C environment (stack, heap etc.)
 */

	cmp	bx,0xFFFF
	jne	1f
	mov	ax,offset __bss_end
	add	ax,heap_size_value
	add	ax,stack_size_value
	and	ax,0xFFF0		# compute top of stack
	sub	ax,2
	mov	word ptr [stktop + 0],ax
	jmp	2f

/*
 * Check for correct function number
 */

1:	cmp	bx,UNDI_MAXNUM
	jbe	2f
	mov	ax,PXENV_STATUS_BAD_FUNC
	ret

/*
 * Setup new stack. Keep care for recursive calls. Remember to keep BX
 * untouched.
 */

2:	mov	cx,ss			# get old stack pointer
	mov	dx,sp
	mov	ax,ds
	cmp	ax,cx			# check if local stack is in use
	je	4f			# already

	pushf
	pop	ax			# save interrupt flag
	cli
	test	word ptr [stktop + 2],0xFFFF
	jz	3f
	push	ax			# stack is in use already but not
	popf				# currently active: this should never
	mov	ax,PXENV_STATUS_FAILURE	# happen, so now we are in big trouble
	ret

3:	mov	word ptr [stktop + 2],ds
	lss	sp,[stktop]		# set new stack pointer
	push	ax
	popf				# restore interrupt flag

4:	push	cx			# save old stack pointer onto new stack
	push	dx

/*
 * Call UNDI loader routine
 */

	cmp	bx,0xFFFF
	jne	5f
	push	es
	push	di
	push	es:o_ul_dx[di]		# push parameters for loader routine
	push	es:o_ul_bx[di]
	mov	ax,es:o_ul_ax[di]
	call	_pxe_load		# call loader routine
	add	sp,4
	pop	di
	pop	es
	jmp	8f

/*
 * Copy parameter structure onto stack. This is necessary for the C
 * routines because code compiled by GCC expects to access any data
 * using DS.
 */

5:	mov	si,bx
	shl	si,1
	mov	cx,siztab[si]		# get size of parameter structure
	mov	dx,cx
	add	dx,2
	and	dx,0xFFFE		# align on word boundary
	sub	sp,dx			# make space on stack
	movzx32	esi,sp

	cld
	push	cx
	push	es
	push	edi
	push	esi
	mov	ax,ds
	mov	dx,es
	mov	ds,dx
	mov	es,ax
	xchg	si,di			# copy parameter structure
	rep	movsb
	mov	ds,ax			# restore DS

/*
 * Now call the C handler. It expects the function code in EAX, and the
 * address of the parameter structure on the stack (which is already
 * there).
 */

	movzx32	eax,bx
	call	_pxe_entry

/*
 * Copy the parameter structure back from the stack. Dont change AX
 * because it contains the result code
 */

	cld
	pop	esi
	pop	edi
	pop	es
	pop	cx
	mov	dx,cx
	push	di
	rep	movsb			# copy parameter structure back
	pop	di

	add	dx,2
	and	dx,0xFFFE		# align on word boundary
	add	sp,dx			# restore stack

/*
 * Restore the old stack and return to caller
 */

8:	pop	dx			# get old stack pointer
	pop	cx
	mov	bx,ds
	cmp	bx,cx			# check if we have to reset the stack
	je	9f			# pointer

	pushf
	pop	bx			# save interrupt flag into BX
	cli
	mov	ss,cx			# restore old stack pointer
	mov	sp,dx
	mov	word ptr [stktop + 2],0	# mark local stack as unused
	push	bx
	popf
9:	lret




/*
 **************************************************************************
 *
 * Data segment
 *
 */
	.data

/*
 * Table containing the sizes of the parameter structure for each UNDI
 * function
 */

siztab:	.word	start_undi_size		# undi_start_undi
	.word	startup_size		# undi_startup
	.word	cleanup_size		# undi_cleanup
	.word	initialize_size		# undi_initialize
	.word	reset_size		# undi_reset_nic
	.word	shutdown_size		# undi_shutdown
	.word	open_size		# undi_open
	.word	close_size		# undi_close
	.word	transmit_size		# undi_transmit
	.word	setmcast_size		# undi_set_mcast_addr
	.word	setstation_size		# undi_set_station_addr
	.word	setfilter_size		# undi_set_packet_filter
	.word	getinfo_size		# undi_get_information
	.word	getstat_size		# undi_get_statistics
	.word	clearstat_size		# undi_clear_statistics
	.word	initdiag_size		# undi_initiate_diags
	.word	forceint_size		# undi_force_interrupt
	.word	getmcast_size		# undi_get_mcast_addr
	.word	getnic_size		# undi_get_nic_type
	.word	getiface_size		# undi_get_iface_info
	.word	isr_size		# undi_isr
	.word	stop_undi_size		# undi_stop_undi
	.word	get_state_size		# undi_get_state




/*
 **************************************************************************
 *
 * BSS segment
 *
 */
	.bss

	.global	stktop

	.lcomm	stktop,4		# top of local stack




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

