;**************************************************************************

;*

;* FLASH - Program FlashCard flash EPROM

;*

;* Module:  flash.asm

;* Purpose: Program FlashCard flash EPROM

;* Entries: start

;*

;**************************************************************************

;*

;* Copyright (C) 1995-2003 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.

;*

;**************************************************************************

;*


;

;**************************************************************************

;

; The purpose of this program is to program the flash EPROM on the FlashCard

; ISA card. The flash EPROM has to be an AMD 29F010, and the programming

; algorithm is the same as that suggested by AMD in the appropriate data

; sheets.

;

.model	small,pascal
.stack	256				; 256 stack bytes should be sufficient



;

;**************************************************************************

;

; Miscellaneous defines:

;

CMDLINE		equ	00080h		; offset of command line into PSP


BUFSIZE		equ	512		; size of read buffer

FNAMSIZE	equ	64		; maximum size of filename


SEGLOW		equ	0C800h		; lower range for EPROM segment

SEGHIGH		equ	0E800h		; upper range for EPROM segment

SEGMASK		equ	0FE00h		; mask for 8kB boundary


AMD_ID		equ	2001h		; flash EPROM ID, only support AMD


ERASE1_CMD	equ	080h		; first command for erasing full chip

ERASE2_CMD	equ	010h		; second command for erasing full chip

READID_CMD	equ	090h		; command to read chip ID

PROG_CMD	equ	0A0h		; command to program a byte

RESET_CMD	equ	0F0h		; command to reset chip state machine



;

;**************************************************************************

;

; Constant segment:

;

.const

cpymsg		db	'flash - Flash EPROM programming utility',0Dh,0Ah
		db	'Copyright (C) 1996-2003 Gero Kuhlmann',0Dh,0Ah
		db	0Dh,0Ah,'$'

segerr		db	'Illegal segment '
		db	'(must be between C800 and E800 on 8kB boundary)'
		db	0Dh,0Ah,'$'

useerr		db	'usage: flash <seg-addr> <file name>'
		db	0Dh,0Ah,'$'

filerr		db	'Cannot open input file'
		db	0Dh,0Ah,'$'

rderr		db	'Cannot read from input file'
		db	0Dh,0Ah,'$'

iderr		db	'Invalid manufacturer ID or no Flash-EPROM installed'
		db	0Dh,0Ah,'$'

prgerr		db	'Timeout while programming or erasing Flash-EPROM'
		db	0Dh,0Ah,'$'

prgmsg		db	'Programming... $'

endmsg		db	'finished'
		db	0Dh,0Ah,'$'


;

;**************************************************************************

;

; Data segment:

;

.data

fseg		dw	0D000h			; flash EPROM segment

handle		dw	0			; data file handle

filename	db	FNAMSIZE dup (0)	; file name buffer

buf		db	BUFSIZE dup (0)		; read buffer



;

;**************************************************************************

;

; Program start

;

.code

	assume	cs:_text
	assume	ds:nothing
	assume	es:nothing

start:	mov	ax,@data
	mov	es,ax
	assume	es:dgroup

; First analyze the command line. The only arguments for this program

; are the EPROM segment address in hex and the data file name.


	mov	si,CMDLINE + 1
	call	skipblank
	cmp	al,0Dh
	je	short start5
	call	gethex			; get hex number

	cmp	bx,SEGLOW		; should be in the range of C800h

	jb	short start2		; and E800h

	cmp	bx,SEGHIGH
	ja	short start2
	test	bx,not SEGMASK		; segment has to be on 8kB boundary

	jz	short start4
start2:	mov	dx,offset segerr
start3:	mov	ax,@data
	mov	ds,ax
start1:	mov	ah,09h			; print error message

	int	21h
	jmp	doend

start4:	mov	es:fseg,bx		; save EPROM segment address

	call	skipblank
	cmp	al,0Dh
	je	short start5
	call	copyfnam		; copy filename

	call	skipblank		; nothing should follow anymore

	cmp	al,0Dh
	je	short start6
start5:	mov	dx,offset useerr	; print usage message

	jmp	short start3

; We now analyzed the whole command line, so the PSP segment in DS is

; no longer needed. Setup all segment registers correctly, and then try

; to open the data file.


start6:	mov	ax,@data
	mov	ds,ax			; set segment registers

	mov	es,ax
	assume	ds:dgroup
	assume	es:dgroup

	mov	dx,offset cpymsg	; print copyright

	mov	ah,09h
	int	21h

	mov	ax,3D00h
	mov	dx,offset filename	; open the data file

	int	21h
	mov	dx,offset filerr
	jc	short error		; print error message if unsuccessful

	mov	handle,ax		; save file handle


; Read the flash EPROM ID to check if there really is an AMD flash EPROM

; where the user says it should be. Then erase the whole chip to allow

; programming lateron.


	call	readid			; read manufacturer ID from chip

	mov	dx,offset iderr
	cmp	bx,AMD_ID		; check for AMD

	jne	short error

	call	erase
	mov	dx,offset prgerr	; erase the whole chip

	jc	short error

; Now read every block from the data file and program it into the flash

; EPROM. Verification of the programmed data is not necessary, as the

; flash EPROM will do that by itself. It will return an unsuccessful

; programming cycle if it's internal verification showed an error.


	mov	dx,offset prgmsg
	mov	ah,09h			; print programming message

	int	21h
	xor	bx,bx
start7:	push	bx
	mov	ax,3F00h
	mov	bx,handle
	mov	cx,BUFSIZE		; read next block from data file

	mov	dx,offset buf
	int	21h
	pop	bx
	mov	dx,offset rderr		; check for read error

	jc	short error
	or	ax,ax			; check for end of file

	jz	short start8
	mov	cx,ax
	call	program			; program the data block into the

	mov	dx,offset prgerr	; flash EPROM and check for a pro-

	jnc	short start7		; gramming error

error:	jmp	start1


start8:	mov	dx,offset endmsg
	mov	ah,09h			; print end-of-programming message

	int	21h
doend:	mov	ax,4C01h		; terminate program

	int	21h
	ret


;

;**************************************************************************

;

; Skip blanks on command line.

; Input:  DS:SI  -  pointer to command line

; Output: DS:SI  -  pointer to first non-blank

;         AL     -  first non-blank character

; Registers changed: AX, SI

;

skipblank	proc	near

	cld
skip1:	lodsb
	cmp	al,' '			; skip blanks

	je	short skip1
	cmp	al,09h			; skip tabs

	je	short skip1
	dec	si			; point SI to first non-blank

	ret

skipblank	endp


;

;**************************************************************************

;

; Return hex number from string pointed to by DS:SI.

; Input:  DS:SI  -  pointer to string

; Output: DS:SI  -  pointer to first character after hex number

;         BX     -  hex number

; Registers changed: AX, BX, CX, SI

;

gethex		proc	near

	xor	bx,bx
geth1:	lodsb
	cmp	al,'0'
	jb	short geth9		; check for valid hex digit

	cmp	al,'9'
	jbe	short geth3
	cmp	al,'A'
	jb	short geth9
	cmp	al,'a'
	jb	short geth2
	sub	al,20h			; convert character to upper case

geth2:	cmp	al,'F'
	ja	short geth9
	sub	al,7
geth3:	sub	al,'0'			; convert character to digit

	and	al,0Fh
	mov	cl,4
	shl	bx,cl			; include digit into result

	or	bl,al
	jmp	short geth1		; get next character


geth9:	dec	si			; point SI to last non-digit

	ret

gethex		endp


;

;**************************************************************************

;

; Copy filename from command line into buffer

; Input:  DS:SI  -  pointer to string

; Output: DS:SI  -  pointer to first character after filename

; Registers changed: AX, CX, SI, DI

;

copyfnam	proc	near

	push	es
	mov	ax,@data
	mov	es,ax
	mov	cx,FNAMSIZE - 1		; don't copy past filename buffer

	mov	di,offset filename	; put address to filename buffer

	cld				; into ES:DI

copyf1:	lodsb
	or	al,al
	jz	short copyf9		; check for valid filename character

	cmp	al,' '
	jbe	short copyf9
	cmp	al,7Fh
	jae	short copyf9
	stosb				; copy character

	loop	short copyf1
copyf9:	xor	al,al
	stosb				; copy terminating null

	dec	si
	pop	es
	ret

copyfnam	endp


;

;**************************************************************************

;

; Read manufacturer ID from Flash-EPROM

; Input:  none

; Output: BX  -  manufacturer ID

; Registers changed: AX, BX

;

readid		proc	near

	mov	al,READID_CMD
	call	sendop			; send READID command

	push	es
	mov	es,fseg
	mov	bx,es:[0000h]		; read manufacturer ID

	pop	es
	mov	al,RESET_CMD
	call	sendop			; reset chip

	ret

readid		endp


;

;**************************************************************************

;

; Erase whole chip

; Input:  none

; Output: carry flag set if error

; Registers changed: AX, BX

;

erase		proc	near

	mov	al,ERASE1_CMD
	call	sendop			; send ERASE1 command

	mov	al,ERASE2_CMD
	call	sendop			; send ERASE2 command

	xor	bx,bx
	mov	al,0FFh
	call	waitop			; wait until operation finished

	jnc	short erase9
	mov	al,RESET_CMD
	call	sendop			; reset chip

	stc
erase9:	ret

erase		endp


;

;**************************************************************************

;

; Program Flash-EPROM with contents in read buffer

; Input:  BX  -  offset to next byte to program

;         CX  -  number of bytes to program

; Output: BX  -  offset to byte after last programmed byte

;         carry flag set if error

; Registers changed: AX, BX, CX, SI

;

program		proc	near

	mov	si,offset buf
prog1:	mov	al,PROG_CMD
	call	sendop			; send programming command

	lodsb				; get next byte from buffer

	push	es
	mov	es,fseg
	mov	es:[bx],al		; write next byte into flash EPROM

	pop	es
	call	waitop			; wait until programming operation is

	jc	short prog9		; completed

	inc	bx
	loop	prog1			; continue with next byte

	clc				; return without error

prog9:	ret

program		endp


;

;**************************************************************************

;

; Send OP code to Flash_EPROM. This involves writing three bytes into the

; flash EPROM at exactly specified locations. See AMD data sheets for

; further information.

; Input:  AL  -  command byte

; Output: none

; Registers changed: none

;

sendop		proc	near

	push	es
	mov	es,fseg
	mov	byte ptr es:[5555h],0AAh
	jcxz	@F
@@:	jcxz	@F
@@:	mov	byte ptr es:[2AAAh],055h
	jcxz	@F
@@:	jcxz	@F
@@:	mov	byte ptr es:[5555h],al
	pop	es
	ret

sendop		endp


;

;**************************************************************************

;

; Wait for the chip to process programming/erase. When programming is in

; progress, the flash EPROM toggles the highest bit, and converts it back

; to it's original value when the programming is done. For a further ex-

; planation of this algorithm see the AMD data sheets.

; Input:  AL  -  programmed byte

;         BX  -  offset to programming location

; Output: carry flag set if error

; Registers changed: AX

;

waitop		proc	near

	push	cx
	and	al,10000000b
	push	es
	mov	es,fseg
wait1:	mov	ah,es:[bx]
	mov	ch,ah
	and	ah,10000000b
	cmp	al,ah
	je	short wait8
	test	ch,00100000b
	jz	short wait1
	mov	ah,es:[bx]
	and	ah,10000000b
	cmp	al,ah
	je	short wait8
	stc
	jmp	short wait9
wait8:	clc
wait9:	pop	es
	pop	cx
	ret

waitop		endp


;

;**************************************************************************

;


	end	start



syntax highlighted by Code2HTML, v. 0.9.1