
;****************************************************************************
;                  Vergleichsprogramm Version 1.0 - Cmp
; Das Programm vergleicht zwei Binärfiles miteinander und meldet, ob und wo
; sie unterschiedlich sind.
;
;                  Copyright in 1991 by Dietmar Heidrich.
; Dieses Programm ist Bestandteil von OMA V2.0 und nur als Beispiel vorhan-
; den.  Seine Vervielfältigung, Veränderung oder Verbreitung ist verboten.
;
;  Dieses Cmp kann NICHT gleichzeitig von mehreren Tasks benutzt werden,
;  wenn es unter der Workbench 1.3 resident gehalten wird.  Compress-Tab 8.
;****************************************************************************

	SECTION	CmpCODE,CODE

	NOPAGE
	NOLIST

	INCLUDE	"exec/types.i"
	INCLUDE	"exec/alerts.i"
	INCLUDE	"exec/tasks.i"
	INCLUDE	"libraries/dos.i"
	INCLUDE	"libraries/dosextens.i"
	INCLUDE	"lvo/exec.i"
	INCLUDE	"lvo/dos.i"

	LIST




BP			EQUR	A5

_SysBase		EQU	4

TAB			EQU	9
LF			EQU	10
CR			EQU	13
SPACE			EQU	' '
PUFFERGROESSE		EQU	10000
MAXIMUM_ARGUMENTS	EQU	6
MAXIMUM_ARGBUFFER	EQU	256



CALL	MACRO
	IFEQ	NARG-2
	move.l	\2,a6
	ENDC
	IFLE	NARG-2
	jsr	_LVO\1(a6)
	MEXIT
	ENDC
	FAIL	CALL MACRO ERROR
	ENDM



STRINGLF	MACRO
	dc.b	'\1',LF,0
	ENDM










;****************************************************************************
; Der Beginn.

Startup
	lea	Basisadresse+32768,BP

	BASEREG	BP










;****************************************************************************
; Variablen.

	SECTION	CmpBSS,BSS

	CNOP	  0,4

Basisadresse

_DOSBase	ds.l	1
HauptSP		ds.l	1
_stdout		ds.l	1

argvArray	ds.l	MAXIMUM_ARGUMENTS
argvBuffer	ds.b	MAXIMUM_ARGBUFFER



	CNOP	0,4

AnfangBss


Seitennummer	ds.l	1
Fehlerflag	ds.l	1

Filename1	ds.l	1
Filename2	ds.l	1

File1		ds.l	1
File2		ds.l	1

Filepuffer1	ds.b	PUFFERGROESSE
Filepuffer2	ds.b	PUFFERGROESSE

Formatierstring	ds.b	256


	CNOP	0,4

EndeBss










;****************************************************************************
; Parse die CLI-Argumente.

	SECTION	CmpCODE,CODE

ParseArgumente

	; Öffne DOS.
	move.l	SP,HauptSP
	movem.l	d0/a0,-(SP)
	lea	DOSName(PC),a1
	moveq	#LIBRARY_VERSION,d0
	CALL	OpenLibrary,_SysBase
	move.l	d0,_DOSBase
	bne.s	1$

	BASEREG	OFF

	; Fehler: DOS nicht offen.
	move.l	#(AG_OpenLib!AO_DOSLib),d7
	lea	AlertWert(PC),a5
	CALL	Alert
	addq	#2*4,SP			; korrigiere Stack
	moveq	#100,d0
	rts				; zurück zum DOS

	BASEREG	BP

	; Initialisiere Zähler und Zeiger.
1$	move.l	d0,a6
	CALL	Output
	move.l	d0,_stdout
	move.l	#MAXIMUM_ARGBUFFER-1,d7
	moveq	#MAXIMUM_ARGUMENTS-1,d6	; Argumentzähler
	lea	argvArray,a1
	lea	argvBuffer,a3
	movem.l	(SP)+,d0/a0
	cmp.l	d7,d0			; Kommandozeile zu lang ?
	bhs.s	FehlerBufferOverflow	; ja

	; Parse die Argumente.
	moveq	#SPACE,d5		; verkürzt Code
	moveq	#'"',d3			; verkürzt Code
	bra	NächstesArgument



ArgumentSchleife
	move.b	(a0)+,d4		; hole Zeichen
	cmp.b	d5,d4			; legales Zeichen ?
	bls.s	NächstesArgument	; nein, nächstes Zeichen

	move.l	a3,a2			; Zeiger auf Argumentpuffer-Stelle merken
	cmp.b	d3,d4			; ist es doppeltes Hochkomma ?
	bne.s	1$			; nein
	bra.s	3$			; ja



	; Argument darf mit einem SPACE enden.
2$	move.b	(a0)+,d4		; hole Zeichen
	cmp.b	d5,d4			; Argument beendet ?
	bls.s	ArgumentEnde		; ja
1$	cmp.b	#'=',d4			; ein '=' ist nicht erlaubt
	beq.s	FehlerEquateUse		; wenn Fehler
	move.b	d4,(a3)+		; speichere Zeichen
	dbra	d0,2$			; Zeichen übrig ?
	bra.s	ArgumentEnde		; nein



	; Argument ist in doppelte Hochkommata eingefaßt.
4$	move.b	(a0)+,d4		; hole Zeichen
	cmp.b	d5,d4			; Argument beendet ?
	blo.s	FehlerIllegalArgEnd	; ja, Fehler

	; Endet Argument mit doppeltem Hochkomma ?
	cmp.b	d3,d4
	bne.s	5$			; nein
	subq	#1,d0			; Zeichen übrig ?
	blt.s	ArgumentEnde		; nein, alles OK
	move.b	(a0)+,d4		; ja, nächstes Zeichen
	cmp.b	d5,d4			; legales Zeichen ?
	bls.s	ArgumentEnde		; ja, alles OK
	bra.s	FehlerIllegalArgEnd	; nein, Fehler

	; Ist es ein Stern ?
5$	cmp.b	#'*',d4
	bne.s	6$			; nein, speichere Zeichen
	subq	#1,d0			; Zeichen übrig ?
	blt.s	FehlerIllegalArgEnd	; nein, Fehler
	move.b	(a0)+,d4		; hole Zeichen
	cmp.b	d5,d4			; Argument beendet ?
	blo.s	FehlerIllegalArgEnd	; ja, Fehler

6$	move.b	d4,(a3)+		; speichere Zeichen

	; Es MUß noch Zeichen geben, oder Fehler.
3$	dbra	d0,4$			; Zeichen übrig ?



FehlerIllegalArgEnd
	lea	TextBadArgs(PC),a0
	bra.s	ArgumentFehler

FehlerBufferOverflow
	lea	TextBufferOverflow(PC),a0
	bra.s	ArgumentFehler

FehlerEquateUse
	lea	TextEquateUse(PC),a0
	bra.s	ArgumentFehler

FehlerTooManyArguments
	lea	TextTooManyArgs(PC),a0

ArgumentFehler
	bra	Programmende



ArgumentEnde
	clr.b	(a3)+			; Argumentende
	subq.l	#1,d6			; noch freie Zeiger übrig ?
	ble.s	FehlerTooManyArguments	; nein
	move.l	a2,(a1)+		; merke Zeiger auf Argumentstring

NächstesArgument
	dbra	d0,ArgumentSchleife	; Zeichen übrig ?

	; Setze die Startwerte.
	lea	argvArray,a0
	neg	d6
	add	#MAXIMUM_ARGUMENTS-1,d6
	move.l	d6,d0










;****************************************************************************
; Beginn des Programmes.  D0 enthält die Anzahl der CLI-Parameter und A0 den
; Zeiger auf das Feld der Zeiger auf die CLI-Argumente.

_main
	; Initialisierung.
	lea	AnfangBss,a1
	move	#(EndeBss-AnfangBss)/4-1,d1
1$	clr.l	(a1)+
	dbra	d1,1$
	move.l	a0,a2			; retten

	; Es müssen 2 Argumente sein.
	subq.l	#2,d0
	bne	FehlerIllegalArgEnd
	move.l	(a0)+,Filename1
	move.l	(a0)+,Filename2

	; Lösche die Signale.
	moveq	#0,d0
	move.l	#SIGBREAKF_CTRL_D!SIGBREAKF_CTRL_C,d1
	CALL	SetSignal,_SysBase

	; Öffne das erste File.
	move.l	Filename1,d1
	move.l	d1,a4
	move.l	#MODE_OLDFILE,d2
	CALL	Open,_DOSBase
	move.l	d0,File1
	bne.s	2$
	; Fehler: File nicht offen.
3$	CALL	IoErr
	move.l	a4,-(SP)
	move.l	d0,-(SP)
	lea	TextErrorOpen(PC),a0
	bra	Programmende

	; Öffne das zweite File.
2$	move.l	Filename2,d1
	move.l	d1,a4
	move.l	#MODE_OLDFILE,d2
	CALL	Open
	move.l	d0,File2
	beq.s	3$




	; Fülle die Puffer.  A4 & A3 sind Pufferzeiger, D7 & D6 Restbytezähler.
8$	move.l	File1,d1
	move.l	Filename1,a2
	lea	Filepuffer1,a4
	move.l	a4,a0
	bsr	FuelleFilepuffer
	move.l	d0,d7
	move.l	File2,d1
	move.l	Filename2,a2
	lea	Filepuffer2,a3
	move.l	a3,a0
	bsr	FuelleFilepuffer
	addq.l	#1,Seitennummer
	move.l	d0,d6

	; Sind beide Längen Null ?
	suba.l	a0,a0
	or.l	d7,d0
	beq.s	Programmende		; ja, Files zu Ende und beide gleich


	; Sind die Längen verschieden ?
	cmp.l	d6,d7
	beq.s	4$			; nein (BNE ist falsch)
	lea	TextDifferentLength(PC),a0
	bsr	Stringausgabe
	move	d6,d0
	cmp	d6,d7
	bhs.s	0$
	move	d7,d0
0$	moveq	#0,d1			; mache BNE falsch
	bra.s	6$

7$	cmp.b	(a3)+,(a4)+
6$	dbne	d0,7$
	bne	Verschieden
	suba.l	a0,a0
	bra	Programmende		; Files zu Ende


	; Vergleiche die Bytes.
5$	cmp.b	(a3)+,(a4)+
4$	dbne	d6,5$

	beq	8$			; wenn bis hier gleich



Verschieden
	; Berechne die Byteposition.
	move.l	Seitennummer,d0
	subq.l	#1,d0			; da immer eine Seite zuviel
	move.l	d0,d1
	mulu	#PUFFERGROESSE,d0
	swap	d1
	mulu	#PUFFERGROESSE,d1
	swap	d1
	clr	d1
	add.l	d1,d0			; Beginn der Seite
	lea	Filepuffer1,a0
	sub.l	a0,a4
	subq	#1,a4			; da A4 einen zu weit zeigte
	add.l	a4,d0
	move.l	d0,-(SP)
	lea	TextFilesDiffer(PC),a0
	; und durchfallen...










;******************************************************************************
; Beende das Programm.
; Eingabe:  A0 zeigt auf den Fehlerstring oder ist Null.

Programmende
	move.l	_DOSBase,a6
	move.l	a0,Fehlerflag		; existiert String ?
	beq.s	1$			; nein
	bsr	Stringausgabe

	; Schließe File 1.
1$	move.l	File1,d1
	beq.s	2$
	CALL	Close

	; Schließe File 2.
2$	move.l	File2,d1
	beq.s	3$
	CALL	Close

	; Schließe DOS.
3$	move.l	_DOSBase,d0
	beq.s	4$
	move.l	d0,a1
	CALL	CloseLibrary,_SysBase

	; Zurück ins DOS.
4$	move.l	Fehlerflag,d0		; trat Fehler auf ?
	beq.s	5$			; nein, D0=0 und fertig
	moveq	#RETURN_FAIL,d0		; ja, melde AmigaDOS einen Fehler
5$	move.l	HauptSP,SP
	rts










;******************************************************************************
; Fülle einen Filepuffer.
; Eingabe:  A0 zeigt auf den Puffer.
;           A2 zeigt auf den Namen.
;           D1 enthält den Filezeiger.
; Ausgabe:  D0 enthält die Restbytes.

FuelleFilepuffer_REG	REG	d1-d3/a0-a1/a6

FuelleFilepuffer
	movem.l	FuelleFilepuffer_REG,-(SP)
	move.l	a0,d2			; Pufferzeiger
	move.l	#PUFFERGROESSE,d3
	CALL	Read,_DOSBase
	moveq	#-1,d1
	cmp.l	d0,d1			; Lesefehler ?
	beq.s	1$			; ja
	movem.l	(SP)+,FuelleFilepuffer_REG
	rts

	; Lesefehler im File.
1$	CALL	IoErr
	move.l	a2,-(SP)		; Filename
	move.l	d0,-(SP)
	lea	TextReadError(PC),a0
	bra	Programmende










;******************************************************************************
; Konvertiere ein Langwort in einen maximal 11 Stellen langen Dezimalstring.
; Die Routine vermeidet überflüssige Nullen am Zahlenanfang wie z.B. 0019.
; Eingabe:  D0 enthält das Langwort.
;           A0 zeigt auf den Ablagestring.
; Ausgabe:  A0 zeigt direkt hinter den Dezimalstring.  Der Dezimalstring landet
;           im Ablagestring.

ErzeugeDezimalZahl_REG	REG	d0-d4/a1

ErzeugeDezimalZahl
	movem.l	ErzeugeDezimalZahl_REG,-(SP)
	lea	DezimalTabelle(PC),a1
	moveq	#9,d1			; Stellenzähler
2$	cmp.l	(a1)+,d0		; ist Stellenwertigkeit zu hoch ?
	bhs.s	1$
	dbra	d1,2$			; ja, nächste Stelle
	moveq	#0,d1			; hier landen wir, falls D0 = 0

1$	subq	#4,a1			; ergibt die richtige Stellenwertigkeit

5$	move.l	d0,d3			; Rest der Zahl retten
	moveq	#'0',d2
	move.l	(a1)+,d4		; Stellenwertigkeit holen
	bra.s	3$

4$	addq	#1,d2			; Ziffer höher stellen
3$	move.l	d3,d0			; Rest der Zahl
	sub.l	d4,d3			; ist Stellenwertigkeit zu hoch ?
	bcc.s	4$			; nein

	move.b	d2,(a0)+		; ja, erhaltene Ziffer in Ausgabepuffer
	dbra	d1,5$			; alle Stellen durch ?

	movem.l	(SP)+,ErzeugeDezimalZahl_REG
	rts










;******************************************************************************
; Die Stringausgabe formatiert einen String und gibt ihn auf _stdout aus.
; Eingabe:  A0 zeigt auf den auszugebenden String.

Stringausgabe_REG	REG	d0-d3/a0-a3/a6

Stringausgabe
	movem.l	Stringausgabe_REG,-(SP)
	move.l	a0,a1
	lea	Formatierstring,a0	; hier hinein kommt der fertige String
	move.l	a0,d2			; merken für DOS-Write und Längenberechnung

	; Beachte, daß 9 Register auf den Stack gerettet wurden.
	lea	4+4*9(SP),a3		; Zeiger auf die Parameter
	bra.s	1$



	; Setze Dezimalzahl ein.
2$	move.b	(a1)+,d0		; hole Zeichen
	move.b	d0,d1
	bclr	#5,d1			; wandele in Großbuchstaben
	cmp.b	#'D',d1			; soll eine Dezimalzahl eingesetzt werden ?
	bne.s	4$			; nein
	move.l	(a3)+,d0		; hole Parameter (Dezimalzahl)
	bsr	ErzeugeDezimalZahl	; und schreibe Dezimalstring nach A0
	bra.s	1$			; hole das nächste Zeichen


	; Setze einen String ein.
4$	cmp.b	#'S',d1			; soll ein String eingesetzt werden ?
	bne.s	5$			; nein
	move.l	(a3)+,a2		; hole Parameter (Stringzeiger)
6$	move.b	(a2)+,(a0)+		; kopiere Zeichen
	bne.s	6$			; bis String zu Ende
	subq	#1,a0			; zeigt jetzt direkt hinter String
	bra.s	1$


	; Setze Hexzahl ein.
5$	cmp.b	#'X',d1
	bne.s	3$
	move.l	(a3)+,d0		; hole Parameter
	moveq	#8-1,d1			; ein Langwort hat 8 Nibbles
7$	rol.l	#4,d0
	move.b	d0,d3
	and.b	#%1111,d3
	cmp.b	#10,d3
	blt.s	8$
	addq	#('A'-'0'-10),d3
8$	add.b	#'0',d3
	move.b	d3,(a0)+		; Nibble ausgeben
	dbra	d1,7$			; bis alle Nibbles durch sind


	; Hole ein Zeichen.
1$	move.b	(a1)+,d0		; hole Zeichen
	cmp.b	#'%',d0			; ist es '%' ?
	beq.s	2$			; ja, ersetze einen Parameter
	; Speichere das Zeichen nur.
3$	move.b	d0,(a0)+		; Zeichen speichern
	bne.s	1$			; bis String zu Ende ist


	; Wir sind fertig mit Formatieren.
	subq	#1,a0			; da A0 einen zu weit zeigt
	move.l	a0,d3
	sub.l	d2,d3			; ergibt Stringlänge
	move.l	_stdout,d1		; Filezeiger
	CALL	Write,_DOSBase		; scher dich nicht darum, ob's klappt
	movem.l	(SP)+,Stringausgabe_REG
	rts










;******************************************************************************
; Die Tabellen.

AlertWert	dc.l	-1

DezimalTabelle
	dc.l	1000000000,100000000,10000000,1000000,100000,10000,1000,100,10,1










;******************************************************************************
; Hier kommen die Texte.

DOSName	DOSNAME


	IFD	ENGLISCH

TextBadArgs		STRINGLF <Bad args>
TextBufferOverflow	STRINGLF <Argument buffer overflow>
TextEquateUse		STRINGLF <Illegally used ''=''>
TextTooManyArgs		STRINGLF <Too many arguments>

TextErrorOpen		STRINGLF [DOS error %d opening file <%s>]
TextReadError		STRINGLF [DOS error %d reading file <%s>]
TextDifferentLength	STRINGLF [Files have different length]
TextFilesDiffer		STRINGLF [Files differ at byte $%x]

	ELSE

TextBadArgs		STRINGLF <Falsche Argumente>
TextBufferOverflow	STRINGLF <Argumentpuffer-Überlauf>
TextEquateUse		STRINGLF <Unzulässiges ''=''>
TextTooManyArgs		STRINGLF <Zu viele Argumente>

TextErrorOpen		STRINGLF [DOS-Fehler %d beim Öffnen von File <%s>]
TextReadError		STRINGLF [DOS-Lesefehler %d im File <%s>]
TextDifferentLength	STRINGLF [Files sind verschieden lang]
TextFilesDiffer		STRINGLF [Files sind verschieden am Byte $%x]

	ENDC










; Ende des Programmes

	END

