/*
 * flashcard.S86  -  Routines for supporting FlashCard
 *
 * Copyright (C) 2002-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: flashcard.S86,v 1.9 2007/01/08 22:02:20 gkminix Exp $
 *
 *====================================================================
 */
#include <common.i86>
#include "flash.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Some definitions which are local to this module
 */
#define MAXSIZE		131072		/* max supported EPROM size */
#define MINSIZE		AMD_MINSIZE	/* min supported EPROM size */

#define SEGLOW		0xC800		/* lower range for EPROM segment */
#define SEGHIGH		0xE800		/* upper range for EPROM segment */
#define SEGBIOS		0xF000		/* BIOS segment */
#define SEGSTEP		0x0200		/* 8kB steps for EPROM segment */
#define SEGCHECK	(MINSIZE / 16)	/* 32kB block for RAM check */




.line __LINE__
/*
 *====================================================================
 *
 * Define some external routines
 */
	.text


	.extern	amd_readid		# read Flash EPROM ID
	.extern	amd_erase		# erase area of Flash EPROM
	.extern	amd_doprog		# program a byte into Flash EPROM

	.extern	chkram			# check for RAM area
	.extern	gen_verify		# generic verification routine




/*
 *====================================================================
 *
 * Detect Flash EPROM interface
 * Input:  FS:SI -  Pointer to bootrom image
 * Output: EDI   -  Start offset into Flash EPROM for bootrom image
 *         ECX   -  Size of Flash EPROM available for bootrom image
 *         EAX   -  Linear address of Flash EPROM (zero if N/A) or error code
 *         DX    -  Offset to Flash EPROM info structure
 *         Carry set if interface not found
 * Changed registers: EAX, EBX, ECX, EDX, EDI
 */
ifdetect:

	push	es
	push	si

/*
 * Detecting a FlashCard is not as easy as it is with PCI network cards,
 * since a FlashCard doesnt have any identifying register on board. It simply
 * maps the Flash EPROM into the ISA address space. Therefore we have to scan
 * through the whole extension ROM address space at 8kB intervalls, check if
 * there is ROM within the following 32 kB, and then try to identify a
 * Flash EPROM chip at that intervall. Of course, this only works, if the BIOS
 * did NOT copy the Flash EPROM contents into shadow RAM.
 * As a side effect, this routine will also be able to find any Flash EPROM on
 * a network card which has not been detected previously. Therefore, this FlasCard
 * detection routine has to be called last after all other detection routines
 * havent found anything.
 */

	mov	word ptr [deterr],1
	mov	bx,SEGLOW
	mov	dword ptr [winsize],SEGCHECK * 16
1:	mov	cx,SEGCHECK
	call	chkram			# check if this is RAM
	jnc	2f
	and	ax,0 - SEGSTEP + 1	# skip the memory which we know
	mov	bx,ax			# contains RAM
	jmp	3f

2:	push	bx
	movzx32	ebx,bx
	shl	ebx,4			# save address of Flash EPROM
	mov	[romaddr],ebx		# for calls to ifread
	mov	bx,offset istruct_fc
	call	amd_readid		# read ID from Flash EPROM
	pop	bx
	or	si,si			# check if Flash EPROM found
	jnz	4f
3:	cmp	bx,SEGHIGH		# dont go into BIOS ROM area
	jae	8f
	add	bx,SEGSTEP		# continue with next 8kB block
	jmp	1b

/*
 * Determine the size of the Flash EPROM and set an upper limit for the
 * window size. The FlashCard allows selecting only a portion of the whole
 * Flash EPROM size to be accessible, starting at the beginning of the
 * Flash EPROM address space. We call this the window size. It can not
 * be detected by reading a certain register, as there are no registers
 * on the FlashCard. However, the window size cant be larger than the
 * physical Flash EPROM size, and it cant go into the BIOS.
 */

4:	mov	[curchip],si
	mov	eax,fstruct_romsize[si]
	mov	[romsize],eax		# save Flash EPROM size
	mov	ecx,SEGBIOS * 16
	sub	ecx,[romaddr]
	cmp	eax,ecx			# check that the flash EPROM
	jbe	5f			# doesnt go into the BIOS area
	mov	eax,ecx
5:	mov	word ptr [deterr],2
	cmp	eax,MINSIZE		# should be at least 32kB
	jb	8f
	cmp	eax,MAXSIZE		# limit to a maximum of 128kB
	jbe	6f
	mov	eax,MAXSIZE
6:	mov	[winsize],eax		# save maximum window size

/*
 * Now we know the size of the area which can be accessed at most. There
 * is no reasonable way to find out about the real window size except
 * programming into the Flash EPROM. This is not suitable at this point.
 * We therefore just accept what we found so far. If the real window
 * size is smaller as what we thought, then all programming above the
 * real window size wont happen, and we will produce an error upon verify.
 * However, if the jumpered real window size is smaller than what we
 * guessed above, there might still be some RAM following the Flash EPROM.
 * In order to prevent the programming routines to write into that RAM,
 * we first have to find this RAM, and will then set the window size so
 * that it is no longer possible to clobber the RAM contents. The following
 * code will scan through the Flash EPROM and see if it really contains
 * ROM.
 */

	mov	word ptr [deterr],3
	mov	ebx,[romaddr]
	shr	ebx,4
	shr	eax,4
	mov	cx,ax
	call	chkram			# check if this is RAM
	jnc	7f
	mov	ebx,[romaddr]		# we found some RAM, so AX contains the
	shr	ebx,4			# segment where this RAM has been found
	sub	ax,bx
	movzx32	eax,ax			# compute the size of the remaining
	shl	eax,4			# Flash EPROM address space and check
	cmp	eax,MINSIZE		# that it is sufficiently large
	jb	8f
	mov	[winsize],eax		# we use this as our new window size

/*
 * After all this checking and memory scanning we finally found a FlashCard,
 * so we can prepare the return values.
 */

7:	mov	dx,[curchip]
	mov	ecx,[winsize]
	mov	eax,[romaddr]
	xor	edi,edi
	clc
	jmp	9f

/*
 * Return to caller.
 */

8:	stc				# return with error
	mov	ax,[deterr]
9:	pop	es
	pop	si
	ret



/*
 *====================================================================
 *
 * Read one byte from Flash EPROM.
 * Input:  EDI  -  Offset into Flash EPROM
 * Output: AL   -  Byte value
 * Changed registers: AL
 */
ifread:

	push	es
	push	edi
	xor	al,al
	cmp	edi,[winsize]		# check if offset is within limits
	jae	9f
	push	eax
	add	edi,[romaddr]
	mov	eax,edi
	shr	eax,4			# compute pointer to byte
	mov	es,ax
	and	di,0x000F
	pop	eax
	mov	al,es:[di]		# get byte from Flash EPROM
9:	pop	edi
	pop	es
	ret



/*
 *====================================================================
 *
 * Write one byte into Flash EPROM.
 * Input:  EDI  -  Offset into Flash EPROM
 *         AL   -  Byte value
 * Output: none
 * Changed registers: none
 */
ifwrite:

	push	es
	push	edi
	cmp	edi,[winsize]		# check if offset is within limits
	jae	9f
	push	eax
	add	edi,[romaddr]
	mov	eax,edi
	shr	eax,4			# compute pointer to byte
	mov	es,ax
	and	di,0x000F
	pop	eax
	mov	es:[di],al		# put byte into Flash EPROM
9:	pop	edi
	pop	es
	ret



/*
 *====================================================================
 *
 * Initialize NIC for programming.
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifinit:
					# fall through



/*
 *====================================================================
 *
 * Terminate programming.
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifclean:

	clc
	ret



/*
 *====================================================================
 *
 * Turn programming voltage VPP on
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifvppon:
					# fall through



/*
 *====================================================================
 *
 * Turn programming voltage VPP off
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifvppoff:

	stc
	ret



/*
 *====================================================================
 *
 * Erase the Flash EPROM area occupied by the rom image. We simply call
 * the routine for the Flash EPROM chip.
 * Input:  EDI -  Offset to rom image block within EPROM
 *         ECX -  Number of bytes to erase
 * Output: carry flag set if error
 * Registers changed: EAX
 */
iferase:

	push	bx
	mov	bx,offset istruct_fc
	call	amd_erase		# call chip erasure routine
	pop	bx
	ret



/*
 *====================================================================
 *
 * Program a memory area into the Flash EPROM.
 * Input:  FS:SI -  Pointer to memory area with source data
 *         ECX   -  Number of bytes to program
 *         EDI   -  Offset to rom image block within EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
ifprog:

	push	bx
	push	ecx
	push	edi
	push	si
	push	fs
	mov	bx,offset istruct_fc

1:	mov	al,fs:[si]
	call	amd_doprog		# program one byte into Flash EPROM
	jc	9f
	inc	si			# increment source pointer
	test	si,0x8000
	jz	2f			# avoid source pointer overflow
	mov	ax,fs
	add	ax,0x0800
	mov	fs,ax
	and	si,0x7FFF
2:	inc	edi			# increment offset into Flash EPROM
 data32	loop	1b			# continue with next byte
	clc

9:	pop	fs
	pop	si
	pop	edi
	pop	ecx
	pop	bx
	ret



/*
 *====================================================================
 *
 * Verify a memory area against the contents of the Flash EPROM.
 * Input:  FS:SI -  Pointer to memory area with source data
 *         ECX   -  Number of bytes to verify
 *         EDI   -  Offset to rom image block within EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
ifverify:

	push	bx
	mov	bx,offset istruct_fc
	call	gen_verify		# call generic routine
	pop	bx
	ret




/*
 *====================================================================
 *
 * String and constants definitions
 */
	_rodata


/*
 * Interface name
 */
ifname:	.asciz	"FlashCard"




/*
 *====================================================================
 *
 * String and constants definitions
 */
	.data

	.global	istruct_fc


/*
 * Interface definition
 */
istruct_fc:
	.word	ifname			# pointer to interface name
	.word	IDEVTYPE_NONE		# interface device type
	.word	0			# no device ID list
	.word	ifdetect		# pointer to detection routine
	.word	ifread			# pointer to byte read routine
	.word	ifwrite			# pointer to byte write routine
	.word	ifvppon			# pointer to VPP-on routine
	.word	ifvppoff		# pointer to VPP-off routine
	.word	ifinit			# pointer to initialization routine
	.word	ifclean			# pointer to cleanup routine
	.word	iferase			# pointer to erase routine
	.word	ifprog			# pointer to programming routine
	.word	ifverify		# pointer to verify routine
	.word	0			# no loader necessary
	.word	IFLAGS_NEEDMEM + IFLAGS_NOTONIF




/*
 *====================================================================
 *
 * Variable definitions
 */
	.bss

	.lcomm	deterr,2		# detection routine error code
	.lcomm	curchip,2		# pointer to chip definition struct
	.lcomm	romaddr,4		# base address of Flash ROM
	.lcomm	romsize,4		# size of Flash ROM
	.lcomm	winsize,4		# size of ROM window




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

