/*
 * misc.S86  -  Misc. routines for flash EPROM programmer
 *
 * 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: misc.S86,v 1.8 2007/01/08 22:02:20 gkminix Exp $
 *
 *====================================================================
 */
#include <common.i86>
#include <netboot.i86>
#include <system/bios.i86>
#include <system/pnp.i86>
#include <system/pci.i86>
#include <system/bootrom.i86>
#include "flash.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Some definitions which are local to this module
 *
 *
 * I/O ports used for the sound generator
 */
#define SPKTMR		0x42		/* 8253 port to set the sound frequency */
#define KBCTL		0x61		/* PPI port for the keyboard controller */


/*
 * Base clock in MHz times 100 of 8253 timer
 */
#define STD_CLOCK_LOW	42480
#define STD_CLOCK_HIGH	1820




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


	.extern	set_ticks
	.extern	chk_timeout

	.global	fndpci
	.global	fndpnp
	.global	chkram
	.global	gen_verify
	.global	play



/*
 *====================================================================
 *
 * Find a PCI interface
 * Input:  FS:SI -  Pointer to bootrom image
 *         BX    -  Offset to list of device IDs
 *         DX    -  Allowed vendor ID
 * Output: AX    -  Index into device table
 *         BX    -  PCI PFN
 *         Carry set if interface not found
 * Changed registers: EAX, EBX, ECX, EDX
 */
fndpci:	push	edi
	push	esi
	push	bx
	push	dx
	push	bp
	mov	bp,sp			# use BP to access caller values

/*
 * First check if we have a PCI BIOS at all
 */

	test	byte ptr [pciini],0xFF
	jnz	1f			# check if checked already
	mov	ax,PCI_FUNC_INST_CHECK
	xor	edi,edi
	int	INT_PCI			# call BIOS installation check
	jc	8f
	or	ah,ah			# check if PCI BIOS installed
	jnz	8f
	cmp	edx,PCI_INST_MAGIC
	jne	8f
	inc	byte ptr [pciini]

/*
 * Now determine the PCI vendor and device ID's from bootrom image and then
 * try to find the device ID in the caller-provided list of allowed device IDs.
 */

1:	cld
	mov	di,[bp + 0x0006]	# get pointer to PCI header
	mov	si,fs:[di + ROMPCIPTROFS]
	or	si,si
	jz	8f
	add	si,di
	mov	dx,fs:pcihdr_vendorid[si]
	mov	cx,fs:pcihdr_deviceid[si]
	cmp	dx,[bp + 0x0002]	# check if vendor ID allowed
	jne	8f
	mov	si,[bp + 0x0004]
2:	lodsw				# get next device ID
	or	ax,ax
	jz	8f			# error if not found
	cmp	ax,cx
	jne	2b			# continue with next device ID
	sub	si,[bp + 0x0004]	# compute index into table
	shr	si,1
	dec	si
	mov	[bp + 0x0004],si	# save it for function return

/*
 * Finally check if the device is installed at all by calling the BIOS.
 */

	mov	ax,PCI_FUNC_FIND_DEV
	xor	si,si			# we better save them
	int	INT_PCI			# call BIOS to find first device
	jc	8f
	or	ah,ah			# check if NIC is installed
	jz	9f
8:	stc
9:	pop	bp
	pop	dx
	pop	ax
	pop	esi
	pop	edi
	ret



/*
 *====================================================================
 *
 * Find a PnP signature in the bootrom
 * Input:  FS:SI -  Pointer to bootrom image
 *         BX    -  Offset to list of device IDs
 * Output: BX    -  Device index
 *         EDX   -  PnP vendor and device ID
 *         Carry set if signature not found
 * Changed registers: AX, BX, CX, EDX
 */
fndpnp:	push	si
	push	di
	mov	di,fs:[si + ROMPNPPTROFS]
	or	di,di			# get pointer to PnP header within
	jz	8f			# bootrom
	add	di,si			# FS:DI points to PnP header
	mov	si,bx			# DS:SI points to list of PnP IDs

	cld
	xor	bx,bx			# BX becomes device index
1:	xor	edx,edx			# EDX becomes the binary device ID

	lodsb
	or	al,al
	jz	8f
	sub	al,0x41			# convert first character
	shl	al,2
	or	dl,al
	lodsb
	sub	al,0x41			# convert second character
	mov	ah,al
	shr	al,3
	or	dl,al
	shl	ah,5
	or	dh,ah
	lodsb
	sub	al,0x41			# convert third character
	or	dh,al

	mov	cx,4
3:	shl	edx,4
	lodsb				# get next hex char of PnP ID
	sub	al,0x30
	cmp	al,0x10			# convert it into binary
	jb	4f
	sub	al,0x07
4:	and	al,0x0F
	or	dl,al			# put it into EDX and continue with
	loop	3b			# next character
	xchg	dl,dh

	rol	edx,16			# convert EDX into memory format
	cmp	edx,fs:pnphdr_devid[di]	# check if its the required device ID
	je	9f
	inc	bx			# no, continue with next ID string
	jmp	1b

8:	stc
9:	pop	di
	pop	si
	ret



/*
 *====================================================================
 *
 * Check that there is no RAM at a given paragraph address range.
 * Input:  BX    -  Segment to check
 *         CX    -  Number of segments to check
 * Output: AX    -  Last checked segment
 *         carry flag set if RAM found
 * Registers changed: AX
 */
chkram:	push	es
	push	bx
	mov	ax,bx
	add	bx,cx			# BX points to the end of the scan area
1:	mov	es,ax
	cli
	mov	al,es:[0]
	mov	ah,al
	not	al			# read first byte of paragraph, invert
	mov	es:[0],al		# all bits, write it back and the read
	xchg	es:[0],ah		# it again
	sti
	cmp	al,ah			# check if it was possible to write the
	je	8f			# modified value, we have RAM
	mov	ax,es
	inc	ax
	cmp	ax,bx			# continue with next paragraph
	jb	1b
	dec	ax
	jmp	9f

8:	stc				# return with error
	mov	ax,es
9:	pop	bx
	pop	es
	ret



/*
 *====================================================================
 *
 * Verify a memory area against the contents of the Flash EPROM. This
 * is a generic routine which uses the read routine of the interface module.
 * If an interface requires a special routine for verification, this generic
 * one cant be used.
 * Input:  FS:SI -  Pointer to memory area with source data
 *         ECX   -  Number of bytes to verify
 *         EDI   -  Offset to rom image block within EPROM
 *         BX    -  Pointer to interface definition structure
 * Output: carry flag set if error
 * Registers changed: EAX
 */
gen_verify:

	push	ecx
	push	edi
	push	si
	push	fs

1:	call	istruct_read[bx]	# read next byte from Flash EPROM
	cmp	al,fs:[si]		# check if its the same as in the
	stc				# source image
	jnz	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				# return without error

9:	pop	fs
	pop	si
	pop	edi
	pop	ecx
	ret



/*
 *====================================================================
 *
 * Turn speaker on
 * Input:  none
 * Output: none
 * Registers changed: AL
 */
spkr_on:

	in	al,KBCTL
	or	al,0x03			# turn speaker output on
	out	KBCTL,al
	ret



/*
 *====================================================================
 *
 * Turn speaker off
 * Input:  none
 * Output: none
 * Registers changed: AL
 */
spkr_off:

	in	al,KBCTL
	and	al,0xFC			# turn speaker output off
	out	KBCTL,al
	ret



/*
 *====================================================================
 *
 * Set speaker frequency
 * Input:  BX  -  speaker frequency in HZ
 * Output: none
 * Registers changed: AX, BX, DX
 */
spkr_freq:

	or	bx,bx
	jz	1f
	mov	dx,STD_CLOCK_HIGH
	mov	ax,STD_CLOCK_LOW	# compute number of timer ticks for
	div	bx			# given frequency
	mov	bx,ax

1:	mov	al,0xB6
	out	SPKTMR + 1,al		# write new count into timer chip
	mov	al,bl
	out	SPKTMR,al
	mov	al,bh
	out	SPKTMR,al
	ret



/*
 *====================================================================
 *
 * Play a sound
 * Input:  BX  -  speaker frequency in HZ
 *         CX  -  duration of sound in timer ticks
 * Output: none
 * Registers changed: AX, BX, EDX
 */
sound:

	or	bx,bx
	jnz	1f
	call	spkr_off		# turn speaker off if frequency
	jmp	2f			# is zero

1:	call	spkr_freq		# set frequency
	call	spkr_on			# turn speaker on
2:	jcxz	9f
	movzx32	edx,cx
	call	set_ticks
3:	call	chk_timeout		# wait until time elapsed
	jnc	3b
9:	call	spkr_off		# turn speaker off
	ret



/*
 *====================================================================
 *
 * Play a sequence of sounds
 * Input:  SI  -  offset to list of sounds
 * Output: none
 * Registers changed: AX
 */
play:	cld
	push	bx
	push	edx
1:	lodsw
	or	ax,ax
	jz	9f
	mov	cx,ax			# read all tick/frequency pairs
	lodsw				# and play them in sequence
	mov	bx,ax
	call	sound
	jmp	1b

9:	pop	edx
	pop	bx
	ret




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

	.lcomm	pciini,1		# flag if PCI interface initialized




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

