
; Listing8b.s - Verwenden des universellen Startup als Beispiel
; eine Fusion von Listing7o.s der Sprites und der Druck Routine 
; von Lektion 6.

	SECTION	USESTARTUP,CODE

;	include	"DaWorkBench.s"	; entferne das; vor dem Speichern mit "wo"

*****************************************************************************
	include	"/Sources/Startup1.s"	; damit mache ich Einsparungen und 
									; schreib es nicht jedes mal neu!	
*****************************************************************************


; Mit DMASET entscheiden wir, welche DMA-Kanäle geöffnet und welche geschlossen 
; werden sollen

			;5432109876543210
DMASET	equ	%1000001110100000	; Copper, Bitplane, Sprite DMA aktiviert
;			 -----a-bcdefghij

;	a: Blitter Nasty   (Im Moment ist es uns egal, lassen wir es auf Null)
;	b: Bitplane DMA	   (Wenn es nicht gesetzt ist, verschwinden auch die Sprites)
;	c: Copper DMA	   (Auch die Copperliste wird nicht auf Null zurückgesetzt)
;	d: Blitter DMA	   (Im Moment sind wir nicht interessiert)
;	e: Sprite DMA	   (Nur die 8 Sprites verschwinden)
;	f: Disk DMA		   (Im Moment sind wir nicht interessiert)
;	g-j: Audio 3-0 DMA (Wir setzen den Amiga auf Null zurück)

; MAIN PROGRAM - Denken Sie daran, dass alle DMA-Kanäle gelöscht sind

Start:
;	 Zeiger auf unsere Bitplane

	move.l	#Bitplane,d0		; Adresse der Bitplane	
	lea	Bplpointers,a1			; Bitplanepointer in der Copperlist
	move.w	d0,6(a1)
	swap	d0
	move.w	d0,2(a1)

;	Wir richten alle Sprites auf das Sprite null

	move.l	#SpriteNull,d0		; Adresse von Sprite in d0
	lea	SpritePointers,a1		; Zeiger in Copperlist
	moveq	#8-1,d1				; alle 8 sprites
NulLoop:
	move.w	d0,6(a1)
	swap	d0
	move.w	d0,2(a1)
	swap	d0
	addq.w	#8,a1
	dbra	d1,NulLoop

;	Wir zielen auf die Sprites

	move.l	#MeinSprite,d0		; Adresse Sprite 1 in d0
	lea	SpritePointers,a1		; Zeiger in Copperlist
	move.w	d0,6(a1)
	swap	d0
	move.w	d0,2(a1)

	addq.w	#8,a1				; Zeiger auf sprite 2
	move.l	#MeinSprite2,d0		; Adresse von sprite 2 in d0
	move.w	d0,6(a1)
	swap	d0
	move.w	d0,2(a1)

	move.w	#DMASET,$96(a5)		; DMACON - aktivieren Sie Bitplane, Copper
								; und Sprites.

	move.l	#Copperlist,$80(a5)	; Zeiger Copperlist
	move.w	d0,$88(a5)			; Start Copperlist
	move.w	#0,$1fc(a5)			; AGA "deaktivieren"
	move.w	#$c00,$106(a5)		; AGA "deaktivieren"
	move.w	#$11,$10c(a5)		; AGA "deaktivieren"

Mouse:
	move.l	#$1ff00,d1			; Bit zur Auswahl durch UND
	move.l	#$13000,d2			; Warte auf Zeile = $130, = 304
WarteY1:
	move.l	4(a5),d0			; VPOSR und VHPOSR - $dff004/$dff006
	and.l   d1,d0				; Wählen Sie nur die Bits der vertikalen Pos. 
	cmp.l	d2,d0				; Warte auf Zeile = $130 (304)
	bne.s	WarteY1

	bsr.s	DruckeZeichen		; Drucke jeweils ein Zeichen
	bsr.w	BewegeSprite		; Bewege die Sprites 0 und 1

	move.l	#$1ff00,d1			; Bit zur Auswahl durch UND
	move.l	#$13000,d2			; Warte auf Zeile = $130, = 304
WarteY2:
	move.l	4(a5),d0			; VPOSR und VHPOSR - $dff004/$dff006
	and.l   d1,d0				; Wählen Sie nur die Bits der vertikalen Pos. 
	cmp.l   d2,d0				; Warte auf Zeile = $130 (304)
	beq.s	WarteY2

	btst	#6,$bfe001			; Maus gedrückt?
	bne.s	Mouse
	rts							; exit
		
*****************************************************************************
;			Druckroutine
*****************************************************************************

DruckeZeichen:
	move.l	PointerText(pc),a0	; Adresse des zu druckenden Textes in a0
	moveq	#0,d2				; löschen d2
	move.b	(a0)+,d2			; Nächstes Zeichen in d2
	cmp.b	#$ff,d2				; Ende des Textsignals? ($ff)
	beq.s	EndText				; Wenn ja, beenden ohne zu drucken
	tst.b	d2					; Zeilenende-Signal? ($00)
	bne.s	NotEndLine			; Wenn nicht, nicht beenden

	add.l	#40*7,PointerBitplane	; wir gehen zum Ende der Zeile
	addq.l	#1,PointerText		; erste Zeichenzeile nach
								; (überspringe die NULL)
	move.b	(a0)+,d2			; erstes Zeichen der nächsten Zeile
								; (überspringe die NULL)

NotEndLine:
	sub.b	#$20,d2				; ZÄHLE 32 VOM ASCII-WERT DES BUCHSTABEN WEG,
								; SOMIT VERWANDELN WIR Z.B. DAS LEERZEICHEN
								; (Das $20 entspricht), IN $00, DAS
								; AUSRUFUNGSZEICHEN ($21) IN $01....
	lsl.w	#3,d2				; MULTIPLIZIERE DIE ERHALTENE ZAHL MIT 8,
								; da die Charakter ja 8 Pixel hoch sind
	move.l	d2,a2
	add.l	#Font,a2			; FINDEN SIE DAS GEWÜNSCHTE ZEICHEN IM Font...

	move.l	PointerBitplane(pc),a3 ; Adresse der Zielbitebene in a3

								; DRUCKE DEN BUCHSTABEN ZEILE FÜR ZEILE
	move.b	(a2)+,(a3)			; Drucke Zeile 1 des Buchstaben
	move.b	(a2)+,40(a3)		; Drucke Zeile 2  " "
	move.b	(a2)+,40*2(a3)		; Drucke Zeile 3  " "
	move.b	(a2)+,40*3(a3)		; Drucke Zeile 4 " "
	move.b	(a2)+,40*4(a3)		; Drucke Zeile 5 " "
	move.b	(a2)+,40*5(a3)		; Drucke Zeile 6  " "
	move.b	(a2)+,40*6(a3)		; Drucke Zeile 7  " "
	move.b	(a2)+,40*7(a3)		; Drucke Zeile 8  " "

	addq.l	#1,PointerBitplane	; wir bewegen uns 8 Bits vorwärts (NÄCHSTES ZEICHEN)
	addq.l	#1,PointerText		; nächstes zu druckendes Zeichen

EndText:
	rts


PointerText:
	dc.l	Text

PointerBitplane:
	dc.l	Bitplane

;	$00 für "Zeilenende" - $ff für "Textende"

			 ; Anzahl der Zeichen pro Zeile: 40
Text:	     ;		  1111111111222222222233333333334
             ;   1234567890123456789012345678901234567890
	dc.b	'                                        ',0 ; 1
	dc.b	'    Dieses Listing verwendet die DMA    ',0 ; 2		
	dc.b	'                                        ',0 ; 3
	dc.b	'    Kanaele von COPPER, Bitplane und    ',0 ; 4		
	dc.b	'                                        ',0 ; 5
	dc.b	'    SPRITE, deaktiviere sie nacheinander',0 ; 6		
	dc.b	'                                        ',0 ; 7
	dc.b	'    und sie werden sehen erst           ',0 ; 8
	dc.b	'                                        ',0 ; 9
	dc.b	'    verschwinden die Sprites, dann der  ',0 ; 10 
	dc.b	'                                        ',0 ; 11
	dc.b	'    Text und dann auch die Coppertoene! ',$ff ; 12

	even
	
*****************************************************************************
;	Sprite-Routinen
*****************************************************************************

BewegeSprite:
	addq.l	#1,TabYpoint		; auf das nächste Byte zeigen
	move.l	TabYpoint(pc),a0	; Adresse, die im long TabXpoint enthalten ist
								; kopiert nach a0
	cmp.l	#EndeTabY-1,a0		; Sind wir beim letzten Byte der TAB?
	bne.s	NobStartY			; noch nicht? dann geht es weiter
	move.l	#TabY-1,TabYpoint	; Beginnt wieder ab dem ersten Byte
NobStartY:
	moveq	#0,d0				; d0 löschen
	move.b	(a0),d0				; Kopiert das Byte der Tabelle, dh die
								; Y-Koordinate in d0

	addq.l	#2,TabXpoint		; auf das nächste Wort zeigen
	move.l	TabXpoint(pc),a0	; Adresse, die im long TabXpoint enthalten ist
								; kopiert nach a0
	cmp.l	#EndeTabX-2,a0		; Sind wir beim letzten Wort der TAB?
	bne.s	NobStartX			; noch nicht? dann geht es weiter
	move.l	#TabX-2,TabXpoint	; Beginnt wieder ab dem ersten Wort-2
NobStartX:
	moveq	#0,d1				; d1 löschen
	move.w	(a0),d1				; wir setzen den Wert der Tabelle, das heißt
								; die X-Koordinate in d1

	lea	MeinSprite,a1			; Adresse des sprites in a1
	moveq	#13,d2				; Höhe von sprite in d2

	bsr.w	UniBewegeSprite		; führt die universelle Routine aus, die 
								; das Sprite positioniert
; zweites Sprite

	addq.l	#1,TabYpoint2		; auf das nächste Byte zeigen
	move.l	TabYpoint2(pc),a0	; Adresse, die im long TabXpoint enthalten ist
								; kopiert nach a0
	cmp.l	#EndeTabY-1,a0		; Sind wir beim letzten Byte der TAB?
	bne.s	NobStartY2			; noch nicht? dann geht es weiter
	move.l	#TabY-1,TabYpoint2  ; Beginnt wiedert ab dem ersten Byte
NobStartY2:
	moveq	#0,d0				; d0 löschen
	move.b	(a0),d0				; Kopiert das Byte der Tabelle, dh die
								; Y-Koordinate in d0

	addq.l	#2,TabXpoint2		; auf das nächste Wort zeigen
	move.l	TabXpoint2(pc),a0	; Adresse, die im long TabXpoint enthalten ist
								; kopiert nach a0
	cmp.l	#EndeTabX-2,a0		; Sind wir beim letzten Wort der TAB?
	bne.s	NobStartX2			; noch nicht? dann geht es weiter
	move.l	#TabX-2,TabXpoint2  ; Beginnt wieder ab dem ersten Wort-2
NobStartX2:
	moveq	#0,d1				; d1 löschen
	move.w	(a0),d1				; wir setzen den Wert der Tabelle, das heißt
								; die X-Koordinate in d1

	lea	MeinSprite2,a1			; Adresse des sprites in a1
	moveq	#8,d2				; Höhe des sprites in d2

	bsr.w	UniBewegeSprite		; führt die universelle Routine aus, die 
								; das Sprite positioniert
	rts

; Zeiger auf die Tabellen des ersten Sprites

TabYpoint:
	dc.l	TabY-1
TabXpoint:
	dc.l	TabX-2

; Zeiger auf die Tabellen des zweiten Sprites

TabYpoint2:
	dc.l	TabY+40-1
TabXpoint2:
	dc.l	TabX+96-2

; Tabelle mit vorberechneten Y-Koordinaten des Sprites.
TabY:
	incbin	"/Sources/ycoordinatok.tab"	; 200 Wert .B
EndeTabY:

; Tabelle mit vorberechneten X-Koordinaten des Sprites.
TabX:
	incbin	"/Sources/xcoordinatok.tab"	; 150 Wert .W
EndeTabX:

; Universelle Sprite-Positionierungsroutine

;
;	UniBewegeSprite Eingabeparameter:
;
;	a1 = Adresse des Sprites
;	d0 = vertikale Y-Position des Sprites auf dem Bildschirm (0-255)
;	d1 = horizontale X-Position des Sprites auf dem Bildschirm (0-320)
;	d2 = Höhe des Sprites
;

UniBewegeSprite:
; vertikale Positionierung
	add.w	#$2c,d0				; addiert den Offset vom Anfang des Bildschirms

; a1 enthält die Sprite-Adresse
	move.b	d0,(a1)				; Kopiert das Byte nach VStart
	btst.l	#8,d0
	beq.s	NichtVstartSet
	bset.b	#2,3(a1)			; Bit 8 von VStart setzen (Zahl > $ff)
	bra.s	ToVstop
NichtVstartSet:
	bclr.b	#2,3(a1)			; Bit 8 von VStart zurücksetzen (Zahl < $ff)
ToVstop:
	add.w	d2,d0				; addiert die Höhe des Sprites
								; Endposition ermitteln (Vstop)
	move.b	d0,2(a1)			; kopiert den richtigen Wert nach Vstop
	btst.l	#8,d0
	beq.s	NichtVstopSet
	bset.b	#1,3(a1)			; Bit 8 von Vstop setzen (Zahl > $ff)
	bra.w	VstopEnde
NichtVstopSet:
	bclr.b	#1,3(a1)			; Bit 8 von Vstop zurücksetzen (Zahl < $ff)
VstopEnde:

; horizontale Positionierung
	add.w	#128,d1				; 128 - um das Sprite zu zentrieren.
	btst	#0,d1				; niedriges Bit der X-Koordinate zurückgesetzt?
	beq.s	NiederBitNull
	bset	#0,3(a1)			; Wir setzen das Low-Bit von HStart
	bra.s	PlaceCoords

NiederBitNull:
	bclr	#0,3(a1)			; Wir setzen das Low-Bit von HStart zurück

PlaceCoords:
	lsr.w	#1,d1				; Wir SHIFTen, d.h. wir verschieben
								; der Wert von HStart um 1 Bit nach rechts,
								; um den Wert in das HStart-Byte zu setzen
								; ohne das niedrige Bit.
	move.b	d1,1(a1)			; Wir setzen den Wert XX im HStart-Byte
	rts

*****************************************************************************

; Die Font 8x8-Zeichen, die in CHIP von der CPU und nicht vom Blitter kopiert
; wurden, so kann es auch im schnellen RAM sein. In der Tat wäre es besser!

Font:
	incbin	"/Sources/nice.fnt"

*****************************************************************************

	SECTION	GRAPHIC,DATA_C

Copperlist:
SpritePointers:
	dc.w	$120,0,$122,0,$124,0,$126,0,$128,0 ; SPRITE
	dc.w	$12a,0,$12c,0,$12e,0,$130,0,$132,0
	dc.w	$134,0,$136,0,$138,0,$13a,0,$13c,0
	dc.w	$13e,0

	dc.w	$8e,$2c81			; DIWSTRT
	dc.w	$90,$2cc1			; DIWSTOP
	dc.w	$92,$0038			; DDFSTRT
	dc.w	$94,$00d0			; DDFSTOP
	dc.w	$102,0				; BPLCON1
	dc.w	$104,$24			; BPLCON2 - Alle Sprites über die Bitplane
	dc.w	$108,0				; BPL1MOD
	dc.w	$10a,0				; BPL2MOD
				; 5432109876543210
	dc.w	$100,%0001001000000000	; 1 Bitplane LOWRES 320x256

Bplpointers:
	dc.w	$e0,0,$e2,0			; erste Bitplane

	dc.w	$0180,$000			; COLOR00 - Hintergrund
	dc.w	$0182,$19a			; COLOR01 - Schrift
			
	dc.w	$1a2,$f00			; COLOR17, das ist color1 vom Sprite0 - rot
	dc.w	$1a4,$0f0			; COLOR18, das ist color2 des Sprite0 - grün
	dc.w	$1a6,$ff0			; COLOR19, das ist color3 des Sprite0 - gelb

;	Gradient Copperlist

	dc.w	$5007,$fffe			; Warte Zeile $50
	dc.w	$180,$001			; COLOR00
	dc.w	$5207,$fffe			; Warte Zeile $52
	dc.w	$180,$002			; COLOR00
	dc.w	$5407,$fffe			; Warte Zeile $54
	dc.w	$180,$003			; COLOR00
	dc.w	$5607,$fffe			; Warte Zeile $56
	dc.w	$180,$004			; COLOR00
	dc.w	$5807,$fffe			; Warte Zeilea $58
	dc.w	$180,$005			; COLOR00
	dc.w	$5a07,$fffe			; Warte Zeile $5a
	dc.w	$180,$006			; COLOR00
	dc.w	$5c07,$fffe			; Warte Zeile $5c
	dc.w	$180,$007			; COLOR00
	dc.w	$5e07,$fffe			; Warte Zeile $5e
	dc.w	$180,$008			; COLOR00
	dc.w	$6007,$fffe			; Warte Zeile $60
	dc.w	$180,$009			; COLOR00
	dc.w	$6207,$fffe			; Warte Zeile $62
	dc.w	$180,$00a			; COLOR00
	dc.w	$ffff,$fffe			; Ende Copperlist


; ************ Hier sind die Sprites: Natürlich müssen sie in CHIP RAM! sein ********

SpriteNull:					; Sprite null, um auf die Copperlist zu verweisen
	dc.l	0,0,0,0				; in nicht verwendeten Zeigern


MeinSprite:		; Länge 13 Zeilen
	dc.b $50	; vertikale Startposition des Sprites (von $2c bis $f2)
	dc.b $90	; horizontale Startposition des Sprites (von $40 bis $d8)
	dc.b $5d	; $50+13=$5d	; vertikale Endposition des Sprites
	dc.b $00
 dc.w	%0000000000000000,%0000110000110000 ; Binärformat für Änderungen
 dc.w	%0000000000000000,%0000011001100000
 dc.w	%0000000000000000,%0000001001000000
 dc.w	%0000000110000000,%0011000110001100 ; BINÄR 00=COLOR 0 (TRASPARENT)
 dc.w	%0000011111100000,%0110011111100110 ; BINÄR 10=COLOR 1 (rot)
 dc.w	%0000011111100000,%1100100110010011 ; BINÄR 01=COLOR 2 (grün)
 dc.w	%0000110110110000,%1111100110011111 ; BINäR 11=COLOR 3 (gelb)
 dc.w	%0000011111100000,%0000011111100000
 dc.w	%0000011111100000,%0001111001111000
 dc.w	%0000001111000000,%0011101111011100
 dc.w	%0000000110000000,%0011000110001100
 dc.w	%0000000000000000,%1111000000001111
 dc.w	%0000000000000000,%1111000000001111
 dc.w	0,0		; 2 word definiert das Ende des Sprites.


MeinSprite2:		; Länge 8 Zeilen
VStart2:
	dc.b $60	; Pos. vertikal (von $2c bis $f2)
HStart2:
	dc.b $60+(14*2)	; Pos. horizontal (von $40 bis $d8)
Vstop2:
	dc.b $68	; $60+8=$68	; Ende vertikal.
	dc.b $00
 dc.w	%0000001111000000,%0111110000111110
 dc.w	%0000111111110000,%1111000111001111
 dc.w	%0011111111111100,%1100001000100011
 dc.w	%0111111111111110,%1000000000100001
 dc.w	%0111111111111110,%1000000111000001
 dc.w	%0011111111111100,%1100001000000011
 dc.w	%0000111111110000,%1111001111101111
 dc.w	%0000001111000000,%0111110000111110
 dc.w	0,0		; Ende sprite


*****************************************************************************

	SECTION	MEINEPLANE,BSS_C

Bitplane:
	ds.b	40*256				; unsere Bitplane lowres 320x256

	end

In diesem Listing erscheinen zwei Optimierungen von bereits bekannten Routinen.
Die eine ist die in Lektion 8 besprochene, nämlich das Warten auf die vertikale
Zeile:

	move.l	#$1ff00,d1			; Bit zur Auswahl durch UND
	move.l	#$13000,d2			; Warte auf Zeile = $130, = 304
WarteY1:
	move.l	4(a5),d0			; VPOSR und VHPOSR - $dff004/$dff006
	and.l   d1,d0				; Wählen Sie nur die Bits der vertikalen Pos. 
	cmp.l   d2,d0				; Warte auf Zeile = $130 (304)
	bne.s	WarteY1	

Ich erinnere Sie daran, dass die maximale Zeile auf die sie warten können $138
beträgt, wenn Sie auf $139 oder höher warten, stürzt die Routine ab, weil
dieser Wert nie auftritt.

Die andere Optimierung, die möglicherweise unbemerkt geblieben ist, ist ein:

	mulu.w	#8,d2

Welches umgewandelt wurde in:

	lsl.w	#3,d2				; MULTIPLIZIERE DIE ERHALTENE ZAHL MIT 8,
								; da die Charakter ja 8 Pixel hoch sind

in der Druckroutine. Nun, eine Verschiebung um 3 Bits nach links entspricht
dem multiplizieren mit 8, genauso wie eine Verschiebung um ein Bit nach links
einer Multiplizikation mit 2 und eine Verschiebung um 2 Bits nach links 
mit 4 zu multiplizieren bedeutet.
Der Grund dafür ist, dass das Binärsystem die Multiplikation und Division
mit Potenzen von 2 erleichtert. Schauen wir uns ein Beispiel an:

	5 * 8 = 40

Schauen wir uns das in binärer Form an:

	%00000101 * %00001000 = %00101000

Wie Sie sehen können, ist das Ergebnis 40 dasselbe wie 5, jedoch mit den Bits
um 3 Positionen nach links verschoben. Wir werden später viele dieser Tricks
sehen, um den Code zu beschleunigen. Das Wichtigste ist, sich daran zu erinnern,
das die Multiplikation und Division SEHR LANGSAM sind, also schaffe sie aus dem
Weg. Es ist sehr nützlich, sie durch etwas anderes zu ersetzen.