*********************************************************
*							*
* fcomp.asm - file comparison utility			*
*							*
* usage: fcomp <filea> <fileb>				*
*							*
* a freely-redistributable program			*
*							*
* by: W. Wesley Howe  (bix: whowe  plink: WHowe)	*
*							*
*     27-Apr-88						*
*							*
*********************************************************
*
	ifd	Cape
	EXEOBJ
	OBJFILE	"ram:fcomp"
	endc
	ifnd	Cape
PSTRING	macro
	DC.B	\@B-\@A
\@A:	DC.B	\1
	ifeq	narg-2
	DC.B	\2
	endc
\@B:
	endm
	endc
*
BR		EQUR	A4		;base-register
*
AbsExecBase	EQU	4		;THE fixed location
*
OldOpenLibrary	EQU	-408		;exec library offsets
CloseLibrary	EQU	-414
*
Open		EQU	-30		;DOS library offsets
Close		EQU	-36
Read		EQU	-42
Write		EQU	-48
Output		EQU	-60
*
MODE_OLDFILE	EQU	1005		;DOS equate
*
	CODE
*
fcomp:	movem.l	d2-d7/a2-a6,-(sp)	;save registers
	LEA	Bseg,BR
	move.l	a0,a2			;save command pointer
	bsr.l	OpenDOS
	tst.w	d0
	beq.l	998$			;big problem
	move.l	a2,a0
	moveq	#0,d0
	cmp.l	a0,d0
	beq.s	3$			;no command - issue usage message
	LEA	Fnam1(BR),a1		;our destination
1$:	cmpi.b	#' ',(a0)		;strip any leading spaces
	bne.s	2$
	addq.l	#1,a0
	bra.s	1$
2$:	move.b	(a0)+,d0
	beq.s	3$
	cmpi.b	#$0a,d0
	beq.s	3$
	cmpi.b	#' ',d0
	beq.s	10$
	move.b	d0,(a1)+
	bra.s	2$
3$:	bra.l	950$
10$:	moveq	#0,d0			;we have 1st filename
	move.b	d0,(a1)			;NULL terminate it
11$:	move.b	(a0)+,d0		;strip intervening spaces
	beq.s	3$
	cmpi.b	#$0a,d0
	beq.s	3$
	cmpi.b	#' ',d0
	beq.s	11$
	LEA	Fnam2(BR),a1
20$:	move.b	d0,(a1)+
	move.b	(a0)+,d0
	beq.s	30$
	cmpi.b	#$0a,d0
	beq.s	30$
	cmpi.b	#' ',d0
	bne.s	20$
30$:	moveq	#0,d0			;we now have both filenames
	move.b	d0,(a1)			;another NULL terminator
	move.l	d0,File1(BR)		;clear handles for fileclosing
	move.l	d0,File2(BR)
	move.l	d0,DiffFlag(BR)		;clear flag for difference detect
	move.l	d0,Offset(BR)		;set offset at zero
	move.l	DOSBase(BR),a6
	move.w	#MODE_OLDFILE,d2
	ext.l	d2
	LEA	Fnam1(BR),a0
	move.l	a0,d1
	jsr	Open(a6)
	tst.l	d0
	beq.s	940$
	move.l	d0,File1(BR)
	move.w	#MODE_OLDFILE,d2
	ext.l	d2
	LEA	Fnam2(BR),a0
	move.l	a0,d1
	jsr	Open(a6)
	tst.l	d0
	beq.s	930$
	move.l	d0,File2(BR)		;both opened successfully
40$:	bsr.l	GetData			;loop to compare longwords
	tst.w	d0
	beq.s	900$			;one EOF was detected
	move.l	Buff1(BR),d0
	move.l	Buff2(BR),d1
	cmp.l	d0,d1
	beq.s	41$			;ok - go another round
	bsr.l	Report
	tst.w	d0			;check on errors
	bmi.s	998$
41$:	addq.l	#4,Offset(BR)		;tick offset
	bra.s	40$
900$:	bsr.s	CloseFiles
	moveq	#0,d0
	tst.l	DiffFlag(BR)
	bne.s	999$
	LEA	NoDiff(PC),a0
	bsr.s	Print
	tst.w	d0
	bmi.s	998$
	moveq	#0,d0
	bra.s	999$
930$:	LEA	Nof2(PC),a0
	bsr.s	Print
	bsr.s	CloseFiles
	bra.s	998$
940$:	LEA	Nof1(PC),a0
	bsr.s	Print			;skip error test - this is an error
	bra.s	998$
950$:	LEA	Usage(PC),a0
	bsr.s	Print			;skip error test - this is an error
998$:	move.l	#20,d0
999$:	bsr.l	CloseDOS
	movem.l	(sp)+,d2-d7/a2-a6
	rts
*
Print:	move.l	Screen(BR),d1		;'file' pointer
	moveq	#0,d3			;clear length upper bytes
	move.b	(a0)+,d3		;get length from first byte
	move.l	a0,d2			;and pass new value as buffer
	move.l	DOSBase(BR),a6
	jsr	Write(a6)
	rts				;check for error return after call
*
CloseFiles:
	move.l	DOSBase(BR),a6
	move.l	File2(BR),d1
	beq.s	1$			;#2 never opened
	jsr	Close(a6)
1$:	move.l	File1(BR),d1
	beq.s	2$			;#1 never opened
	jsr	Close(a6)
2$:	rts
*
GetData:
	move.l	DOSBase(BR),a6
	move.l	File1(BR),d1		;file
	LEA	Buff1(BR),a0
	move.l	a0,d2			;buffer
	moveq	#4,d3			;length
	jsr	Read(a6)
	tst.l	d0
	bmi.s	1$			;error
	beq.s	1$			;EOF
	move.l	File2(BR),d1		;file
	LEA	Buff2(BR),a0
	move.l	a0,d2			;buffer
	moveq	#4,d3			;length
	jsr	Read(a6)
	tst.l	d0
	bmi.s	1$			;error
	beq.s	1$			;EOF
	moveq	#1,d0			;success
	rts
1$:	moveq	#0,d0			;failure
	rts
*
Report:	Addq.l	#1,DiffFlag(BR)		;bump count
	LEA	D1Str(PC),a0		;first part of report
	bsr.s	Print
	tst.w	d0
	bmi.s	100$
	move.l	Offset(BR),d0		;print offset
	bsr.s	PrtHex
	tst.w	d0
	bmi.s	100$
	LEA	D2Str(PC),a0		;parm 1 lead-in
	bsr.l	Print
	tst.w	d0
	bmi.s	100$
	move.l	Buff1(BR),d0
	bsr.s	PrtHex			;print parm 1
	tst.w	d0
	bmi.s	100$
	LEA	D3Str(PC),a0
	bsr.l	Print			;parm 2 lead-in
	tst.w	d0
	bmi.s	100$
	move.l	Buff2(BR),d0
	bsr.s	PrtHex
	tst.w	d0
	bmi.s	100$
	LEA	D4Str(PC),a0
	bsr.l	Print
	tst.w	d0
	bmi.s	100$
	moveq	#0,d0
100$:	rts
*
PrtHex:	move.l	d0,-(sp)		;save on stack
	move.w	(sp),d0			;retreive high word
	LEA	HxBuff(BR),a0
	bsr.s	WordHex
	move.w	2(sp),d0		;retrieve low word
	bsr.s	WordHex
	addq.l	#4,sp			;fix stack
	move.l	Screen(BR),d1		;file
	LEA	HxBuff(BR),a0
	move.l	a0,d2			;buffer
	moveq	#8,d3			;length
	move.l	DOSBase(BR),a6
	jsr	Write(a6)
	rts
*
WordHex:
	move.w	d0,-(sp)		;save on stack
	lsr.w	#8,d0			;high byte
	bsr.s	ByteHex
	move.w	(sp),d0			;low byte
	bsr.s	ByteHex
	addq.l	#2,sp			;fix stack
	rts
*
ByteHex:
	move.w	d0,d1			;save for low nibble
	lsr.w	#4,d0
	andi.b	#15,d0
	add.b	#'0',d0			;convert to ascii
	cmpi.b	#'9'+1,d0
	bmi.s	1$
	addq.b	#7,d0			;letter code
1$:	move.b	d0,(a0)+
	move.w	d1,d0			;get low nibble back
	andi.b	#15,d0
	add.b	#'0',d0			;convert to ascii
	cmpi.b	#'9'+1,d0
	bmi.s	2$
	addq.b	#7,d0			;letter code
2$:	move.b	d0,(a0)+
	rts
*
OpenDOS:
	move.l	AbsExecBase,a6
	moveq	#0,d0			;any version
	LEA	DOSName(PC),a1
	jsr	OldOpenLibrary(a6)
	move.l	d0,DOSBase(BR)
	tst.w	d0
	beq.s	1$
	move.l	d0,a6
	jsr	Output(a6)
	move.l	d0,Screen(BR)
	moveq	#1,d0
1$:	rts				;retval checked after call
*
CloseDOS:
	tst.l	DOSBase(BR)
	beq.s	1$
	move.l	AbsExecBase,a6
	move.l	DOSBase(BR),a1
	jsr	CloseLibrary(a6)
1$:	rts
*
*	Fixed DATA (for PC-Relative reference)
*
DOSName:
	DC.B	'dos.library',0
	DS.W	0
Usage:	PSTRING	'Usage: fcomp <file1> <file2>',$0a
Nof1:	PSTRING	'Unable to open file #1',$0a
Nof2:	PSTRING	'Unable to open file #2',$0a
D1Str:	PSTRING	'Difference at offset $'
D2Str:	PSTRING	': file1 = $'
D3Str:	PSTRING	', file2 = $'
D4Str:	PSTRING	'.',$0a
NoDiff:	PSTRING	'No difference between files 1 and 2 (check length).',$0a
*
	BSS
*
*	uninitialized DATA storage space (for Base-register references)
*
Bseg:
DOSBase:
	DS.L	1
File1:	DS.L	1
File2:	DS.L	1
Screen:	DS.L	1
Fnam1:	DS.B	120
Fnam2:	DS.B	120
Buff1:	DS.L	1
Buff2:	DS.L	1
DiffFlag:
	DS.L	1
Offset:	DS.L	1
HxBuff:	DS.B	8
*
