
;****************************************************************************
; Beispiel für die Benutzung der objektorientierten Assemblerprogrammierung.
;
; Diese Datei darf nur in Benutzung mit dem Optimierenden Makro-Assembler
; verwendet werden.
;****************************************************************************

	INCLUDE	"exec/types.i"
	INCLUDE	"macros.i"
	INCLUDE	"OOMA.i"


	IMPORT	InitObjectModule,RegisterClass,InitializeClasses
	IMPORT	CreateObject
	IMPORT	ClassObject

	; Springe den Startcode an.
	jmp	Start










;****************************************************************************
; Hier folgt die komplette Rechteck-Klasse. Sie erbt von Object.


	; Erst einmal müssen die Instanzvariablen der Klasse Rechteck
	; plaziert werden. Dies geschieht immer hinter den Variablen der
	; Superklasse, in diesem Fall "Object". Hier wird also ein Objekt
	; "Rechteck" definiert.
 STRUCTURE Rechteck,object_SIZEOF	; wir erben von Object
 UWORD	rechteck_Höhe
 UWORD	rechteck_Breite
 LABEL	rechteck_SIZEOF			; Größe eines Rechtecks

	; Definiere Methodennamen der Superklasse für unsere Klasse. Dies
	; entlastet uns davon, sich daran erinnern zu müssen, in welcher
	; Superklasse die Methode tatsächlich deklariert ist. Damit ist
	; jede MethodenID mit dem Präfix "Rechteck_" ansprechbar. Jede
	; MethodenID sollte mit dem Klassennamen als Präfix beginnen.
 SAMEMETHOD	Rechteck_Initialize,Object_Initialize
 SAMEMETHOD	Rechteck_Delete,Object_Delete
	; Definiere neue Methoden, die es in der Superklasse noch nicht gab.
	; Dies beginnt immer bei der Klassengröße der Superklasse.
 BEGINMETHOD	Object_ClassSize
 DEFINEMETHOD	Rechteck_SetzeHöhe
 DEFINEMETHOD	Rechteck_SetzeBreite
 DEFINEMETHOD	Rechteck_BerechneUmfang
 DEFINEMETHOD	Rechteck_BerechneFläche
	; Zum Abschluß muß immer die Klassengröße definiert werden. Sie gibt
	; von Rechteck erbenden Klassen einen Aufsetzpunkt für neue Methoden.
	; Wir selber haben dies oben per Object_ClassSize im Makroaufruf
	; BEGINMETHOD ausgenutzt.
 ENDMETHOD	Rechteck_ClassSize



	; Jetzt kennen wir die Größe der Klassenstruktur (dies ist nämlich
	; Rechteck_ClassSize) und können den Platz für die Klassenstruktur
	; anlegen. Zweckmäßigerweise ist der in einem BSS-Hunk.
	; Der Name einer Klasse sollte immer mit "Class" beginnen.
	BSS
	CNOP	0,4
	DEFINECLASS	ClassRechteck,Rechteck_ClassSize
	CODE



	; Melde die Rechteck-Klasse an.
MeldeRechteckAn
	PROC	d0-d1/a0-a2
	lea	ClassRechteck,a0	; Klassenzeiger
	lea	ClassObject,a1		; Superklasse
	lea	.array,a2		; InitArray
	moveq	#rechteck_SIZEOF,d0	; Objektgröße
	moveq	#Rechteck_ClassSize,d1	; Klassengröße
	jsr	RegisterClass
	ENDPROC

	; Dieses Feld legt fest, welche Methoden in der Klasse Rechteck
	; selber vorhanden sind. Es wird immer mit dem ENDOVERWRITE-Makro
	; abgeschlossen. Wird eine Methode der Klasse hier vergessen, dann
	; ist entweder der Zeiger der Superklasse oder ein Nullzeiger in der
	; Methodentabelle eingetragen. Dies führt dann beim Methodenaufruf zu
	; einem Fehler.
.array	OVERWRITE	Rechteck_Initialize
	OVERWRITE	Rechteck_SetzeHöhe
	OVERWRITE	Rechteck_SetzeBreite
	OVERWRITE	Rechteck_BerechneUmfang
	OVERWRITE	Rechteck_BerechneFläche
	ENDOVERWRITE	; Abschluß



	; Dies ist die Methode zum Setzen der Höhe. Jede Methode wird mit
	; dem Zeiger auf das Objekt in A5 aufgerufen, dieser heißt auch Self.
	; Das Label dieser Methode hat zusätzlich das Präfix "m_". Daher
	; lautet es hier "m_Rechteck_SetzeHöhe".
	; Eingabe:  A5: Self.
	;	    D0: neue Höhe.
 METHODBODY	Rechteck_SetzeHöhe
	PROC
	move	d0,rechteck_Höhe(a5)
	ENDPROC



	; Das gleiche noch einmal für die Breite.
	; Eingabe:  A5: Self.
	;	    D0: neue Breite.
 METHODBODY	Rechteck_SetzeBreite
	PROC
	move	d0,rechteck_Breite(a5)
	ENDPROC



	; Berechne den Umfang des Rechtecks.
	; Eingabe:  A5: Self.
	; Ausgabe:  D0: Umfang.
 METHODBODY	Rechteck_BerechneUmfang
 	PROC	d1
	move	rechteck_Höhe(a5),d1
	add	d1,d1
	move	rechteck_Breite(a5),d0
	add	d0,d0
	add	d1,d0			; ergibt Umfang
	ENDPROC



	; Berechne die Fläche des Rechtecks.
	; Eingabe:  A5: Self.
	; Ausgabe:  D0: Fläche.
 METHODBODY	Rechteck_BerechneFläche
 	PROC
	move	rechteck_Höhe(a5),d0
	mulu	rechteck_Breite(a5),d0	; ergibt Fläche
	ENDPROC



	; Diese Methode gibt es schon in der Superklasse Object. Sie sorgt
	; hier noch für eine Standardeinstellung eines neuen Rechtecks.
	; Eingabe:  A5: Self.
 METHODBODY	Rechteck_Initialize
	PROC
	SUPER	a5,Rechteck_Initialize	; erst Superklasse aufrufen
	move	#1,rechteck_Höhe(a5)	; jetzt Höhe auf 1
	move	#1,rechteck_Breite(a5)	; und Breite auch
	ENDPROC










;****************************************************************************
; Hier folgt die komplette Quadrat-Klasse. Sie erbt von Rechteck. Der Quell-
; text hier ist einem Format gehalten, das wegen guter Lesbarkeit als Vorbild
; genommen werden sollte, Prozeduren und Methoden zu deklarieren und zu
; dokumentieren.



; Die Objektstruktur für Quadrate.

 STRUCTURE Quadrat,rechteck_SIZEOF	; wir erben von Rechteck
 LABEL	quadrat_SIZEOF			; keine zusätzlichen Variablen nötig

; Quadrat_SetzeHöhe und Quadrat_SetzeBreite werden hier nicht definiert, da
; ein Quadrat nur eine Kantenlänge hat.
 SAMEMETHOD	Quadrat_Initialize,Rechteck_Initialize
 SAMEMETHOD	Quadrat_Delete,Rechteck_Delete
 SAMEMETHOD	Quadrat_BerechneUmfang,Rechteck_BerechneUmfang
 SAMEMETHOD	Quadrat_BerechneFläche,Rechteck_BerechneFläche
 BEGINMETHOD	Rechteck_ClassSize
 DEFINEMETHOD	Quadrat_SetzeKante	; neue Methode
 ENDMETHOD	Quadrat_ClassSize	; Abschluß



	; Die Quadrat-Klasse.
	BSS
	CNOP	0,4
	DEFINECLASS	ClassQuadrat,Quadrat_ClassSize
	CODE



; Melde die Quadratklasse an.

MeldeQuadratAn
	PROC	d0-d1/a0-a2
	lea	ClassQuadrat,a0		; Klassenzeiger
	lea	ClassRechteck,a1	; Superklasse
	lea	.array,a2		; InitArray
	moveq	#quadrat_SIZEOF,d0	; Objektgröße
	moveq	#Quadrat_ClassSize,d1	; Klassengröße
	jsr	RegisterClass
	ENDPROC

.array	OVERWRITE	Quadrat_SetzeKante
	OVERWRITE	Quadrat_BerechneUmfang
	ENDOVERWRITE	; Abschluß



;****************************************************************************
; Berechne den Umfang des Rechtecks. Diese Methode ist schneller als die der
; Rechteck-Klasse und ersetzt diese daher vollständig.
; Eingabe:  A5: Self.
; Ausgabe:  D0: Umfang.

 METHODBODY	Quadrat_BerechneUmfang
 	PROC
	move	rechteck_Höhe(a5),d0	; oder rechteck_Breite
	lsl	#2,d0			; * 4 ergibt Umfang
	ENDPROC



;****************************************************************************
; Setze Kantenlänge.
; Eingabe:  A5: Self.
;	    D0: neue Länge.
; Ausgabe:  Keine.

 METHODBODY	Quadrat_SetzeKante
	PROC
	IFEQ	0
	 ; Erste Implementierungsalternative.
	 move	d0,rechteck_Höhe(a5)
	 move	d0,rechteck_Breite(a5)
	ELSE
	 ; Zweite Implementierungsalternative.
	 METHOD	a5,Quadrat_SetzeHöhe
	 METHOD	a5,Quadrat_SetzeBreite
	ENDC
	ENDPROC










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

Start
	PROC
	LOCAL	einQuadrat,quadrat_SIZEOF	; Platz auf dem Stack
	SETLOC

	; Zuerst muß das Objektmodul initialisiert werden.
	jsr	InitObjectModule

	; Melde alle Klassen an. Dabei ist die Reihenfolge völlig egal.
	; Es ist wichtig, vor der Benutzung alle Klassen anzumelden und zu
	; initialisieren.
	bsr	MeldeQuadratAn
	bsr	MeldeRechteckAn

	; Nun müssen alle Klassen initialisiert werden. Wurde eine Klasse
	; beim Anmelden vergessen, bekommen wir hier den Zeiger auf diese
	; Klasse als Fehleranzeige zurück. Der Vererbungsbaum muß also
	; zusammenhängen.
	jsr	InitializeClasses
	tst.l	d0
	bne	.ende			; Fehler !!!

	; Nun erzeugen wir ein Rechteck. Sein Speicher wird mit AllocMem
	; belegt.
	lea	ClassRechteck,a0
	jsr	CreateObject
	move.l	d0,d7
	beq	.ende			; Fehler !!!
	; Initialisiere das Rechteck.
	METHOD	d7,Rechteck_Initialize
	; Jetzt ist das Objekt gebrauchsfähig. Je nach Definition kann eine
	; Initialize-Methode einer Klasse auch einen Erfolgswert o.ä. zurück-
	; liefern.

	; Jetzt einmal ein Objekt auf dem Stack. Es ist diesmal ein Quadrat.
	lea	einQuadrat(SP),a4
	INITOBJECT	a4,ClassQuadrat
	METHOD	a4,Quadrat_Initialize
	; Es wäre auch möglich, den Methodenaufruf wie folgt zu machen:
;	METHOD	einQuadrat(SP),Quadrat_Initialize
	; Dies zeigt, daß eine beliebige Adressierungsart als Objektzeiger
	; erlaubt ist.

	; Setze die Breite des Rechtecks auf 2 und berechne den Umfang.
	; Der Methodenaufruf landet bei SetzeBreite der Rechteck-Klasse bzw.
	; bei BerechneUmfang der Rechteck-Klasse.
	moveq	#2,d0
	METHOD	d7,Rechteck_SetzeBreite
	METHOD	d7,Rechteck_BerechneUmfang
	; Ergebnis in D0.

	; Setze die Kantenlänge des Quadrats auf 3 und berechne die Fläche.
	; Der Methodenaufruf landet bei SetzeKante der Quadrat-Klasse bzw.
	; bei BerechneFläche der Rechteck-Klasse, da die Quadrat-Klasse
	; keine eigene Methode zur Flächenberechnung hat.
	moveq	#3,d0
	METHOD	a4,Quadrat_SetzeKante
	METHOD	a4,Quadrat_BerechneFläche
	; Ergebnis in D0.

	; Das Rechteck muß noch wieder freigegeben werden. Wäre beim
	; Initialisieren weiteres Belegen vorgekommen, dann müßte eine
	; Methode "Rechteck_Cleanup" definiert sein, die nun hier vor dem
	; Löschen aufgerufen würde.
	; METHOD  d7,Rechteck_Cleanup   (entfällt hier)
	METHOD	d7,Rechteck_Delete
	; Das Quadrat hat Speicherplatz auf dem Stack, braucht also nicht
	; freigegeben zu werden. Hätten wir für dieses Objekt Zusatzspeicher
	; per AllocMem belegt und in einer Instanzvariablen vermerkt, müßte
	; wie oben gesagt noch eine Aufräummethode "Quadrat_Cleanup" aufgerufen
	; werden.

	; Ende des Tests.
.ende	moveq	#0,d0
	ENDPROC










	END

