; Septic's Demoskola - Lektion 10 : Linjeritning
; Skriven av Vicious / Septic - 2 December 1993

DisableCache=1	; En nolla betyder att funktionen är avstängd...
DisableMotor=1

ScreenWidth=320
ScreenHeight=256
ScreenSize=Screenwidth/8*ScreenHeight

	section	Lektion10,code_c

j:	movem.l	d0-a6,-(sp)

	bsr.w	INIT
	tst.l	gfxbase		; Kolla om vi hittade gfx.library
	beq.s	error		; Nepp...Då hoppar vi ut...

	move.w	#%1000001111000000,$dff096	; Sätt DMA
	;	  S    NDBCBSD
	;	  E    aMPOLPS
	;	  T    sALPTRK
	;	       t
	;              y

	bsr.w	SetBplptr	; Sätt bitplanspekare

	move.l	#copper,$dff080	; Nu sätter vi vår egen copperlista
	move.w	d0,$dff088	; Och tvingar den att börja direkt...

	bsr.s	Draw		; Rita ut några fina figurer

main:
	bsr.s	sync
	btst	#6,$bfe001	; Testa vänster musknapp!
	bne.s	main

xit:	bsr.w	UnINIT
error:	movem.l	(sp)+,d0-a6
	rts

; Syncroniserings rutin
sync:	move.l	$dff004,d0
	and.l	#$1ff00,d0
	lsr.l	#8,d0
	cmp.w	#$f0,d0		; Kolla om rasterrad=$f0
	bne.s	sync
	rts

; Den här rutinen ritar ut två nonsens-figurer (en triangel och en
; fyrhörning) på två olika sätt, men använder sig utav samma linjeritnings-
; rutin.
Draw:
	movem.l	d0-a6,-(sp)
	move.l	showpage(pc),a0		; Skärmen vi ska rita på i A0
	move.l	#screenwidth/8,a1	; Skärmbredden i bytes
	move.l	#-1,a2			; Linjemask=$ffffffff (helt fylld)

	move.l	#100,d0		; X-koordinat 1 i D0
	move.l	#20,d1		; Y-koordinat 1 i D1
	move.l	#150,d2		; X-koordinat 2 i D2
	move.l	#200,d3		; Y-koordinat 2 i D3
	bsr.s	DrawLine	; Rita ut linjen

	move.l	#150,d0		; Gör samma sak med alla tre linjer
	move.l	#200,d1
	move.l	#50,d2
	move.l	#200,d3
	bsr.s	DrawLine

	move.l	#50,d0
	move.l	#200,d1
	move.l	#100,d2
	move.l	#20,d3
	bsr.s	DrawLine



	; Den här metoden är smidigare...Här har jag gjort en tabell med
	; koordinat-data som ligger vid labeln BalkData. Sen läser jag
	; bara från den och ritar ut linjerna i en loop tills X2 är
	; negativt, dvs vi har läst till slutet av tabellen.

	lea.l	balkdata(pc),a5	; Peka på koordinat-tabellen
.nxt:	movem.l	(a5)+,d0-d3	; Hämta fyra koordinater på en gång
	tst.l	d2		; Kolla om X2 är negativt
	bmi.s	.ut		; I så fall, hoppa ut...
	bsr.s	DrawLine	; Rita ut linjen
	subq.l	#8,a5		; Backa en koordinat i tabellen så att
				; nästa linje hänger ihop med den förra...
	bra.s	.nxt		; Loopa tillbaka
.ut:
	movem.l	(sp)+,d0-a6
	rts

*** Linjeritnings Rutin  ***
; Den här rutinen ritar en linje med blittern
; D0 - X1   D1 - Y1   D2 - X2   D3 - Y2
; A0 - Skärmpekare
; A1 - Skärmbredden i bytes
; A2 - Linjens mask ($ffff för helt fylld linje)

DrawLine:
	movem.l	d4/d5/d7/a0-a6,-(sp)
	cmp.l	d0,d2
	bne.s	.ok
	cmp.l	d1,d3
	beq.w	.dontdraw
.ok:
	move.l	a1,d4		; Lägg skärmbredden i D4
	mulu.w	d1,d4		; Multiplicera med Y1
	moveq	#-$10,d5	; D5=$fffffff0
	and.w	d0,d5		; Plocka bort de 4 lägsta bitarna
	lsr.w	#3,d5		; Multiplicera med 8 (2^3=8)
	add.w	d5,d4		; Addera detta till Y1
	add.l	a0,d4		; Addera skärmpekaren till detta

	moveq	#0,d5
	sub.w	d1,d3		; Ta reda på delta Y
	roxl.b	#1,d5		; Shifta ut X-flaggan i D5. Att X-flaggan
				; sätts här anger att D1 är högre än D3 och
				; därför kan vi senare med hjälp av D5 ta
				; fram rätt oktant utan större problem...
	tst.w	d3		; Kolla om D3 är negativt
	bge.s	.y2gy1
	neg.w	d3		; I så fall gör vi det positivt!
.y2gy1:
	sub.w	d0,d2		; Ta reda på delta X och gör samma sak som
				; med Y...
	roxl.b	#1,d5
	tst.w	d2
	bge.s	.x2gx1
	neg.w	d2
.x2gx1:
	move.w	d3,d1		; Flytta delta Y till D1
	sub.w	d2,d1		; Delta Y-Delta X anger vilket som är
				; störst!
	bge.s	.dygdx
	exg	d2,d3		; Vi vill ha det största värdet i D3.
				; Detta är nu dx.

.dygdx:	roxl.b	#1,d5		; Shifta in X-flaggan här också...

	move.b	Octant_table(pc,d5.l),d5	; Nu kan vi enkelt hämta
						; korrekt oktant från en
						; speciell tabell...

	add.w	d2,d2		; dy multiplicerat med 2

.WBlit:	btst #6,$dff002	; Vänta tills blittern är klar
	bne.s	.WBlit

	move.w	d2,$dff062	; Skriv dy till BLTBMOD
	sub.w	d3,d2		; Kolla om vi behöver sätta SIGN-biten
	bge.s	.signnl
	or.b	#$40,d5		; Sätt SIGN-flaggan i D5
.signnl: move.w	d2,$dff052	; Skriv (4*dy)-(2*dx) till BLTAPT
	sub.w	d3,d2
	move.w	d2,$dff064	; Och skriv 4*(dy-dx) till BLTAMOD

	move.w	#$8000,$dff074	; Sätt BLTADAT till $8000
	move.w	a2,$dff072	; Sätt linjemask i BLTBDAT
	move.w	#$ffff,$dff044	; Sätt rätt BLTAFWM
	and.w	#$000f,d0	; Maska bort allt utom de lägsta 4 bitarna
	ror.w	#4,d0		; Flytta upp 4 låga bitarna till de 4 höga

	or.w	#(srca+srcc+dest)+(a&b+na&c),d0	; Minterm AB+aC

	move.w	d0,$dff040	; Sätt korrekt värde i BLTCON0
	move.w	d5,$dff042	; och i BLTCON1
	move.l	d4,$dff048	; Första pixeln på linjen
	move.l	d4,$dff054	; Och första pixeln på linjen här också
	move.w	a1,$dff060	; Bredden på skärmen i BLTCMOD
	move.w	a1,$dff066	; och i BLTDMOD också

	lsl.w	#6,d3		; dx i bit 6-15
	addq.w	#2,d3		; och bit 0-5 = 2
	move.w	d3,$dff058	; Sätt detta sen i BLTSIZE för att starta
				; linjen...

.dontdraw:
	movem.l	(sp)+,d4/d5/d7/a0-a6
	rts

Octant_table:
	dc.b 0*4+1	; y1<y2, x1<x2, dx<dy = Okt6 
	dc.b 4*4+1	; y1<y2, x1<x2, dx>dy = Okt7
	dc.b 2*4+1	; y1<y2, x1>x2, dx<dy = Okt5
	dc.b 5*4+1	; y1<y2, x1>x2, dx>dy = Okt4
	dc.b 1*4+1	; y1>y2, x1<x2, dx<dy = Okt1
	dc.b 6*4+1	; y1>y2, x1<x2, dx>dy = Okt0
	dc.b 3*4+1	; y1>y2, x1>x2, dx<dy = Okt2
	dc.b 7*4+1	; y1>y2, x1>x2, dx>dy = Okt3


; Detta är initialiserings-rutinen....
AttnFlags=296
INIT:
	movem.l	d0-a6,-(sp)
	bsr.w	FetchVBR

	move.l	$4.w,a6
	jsr	-132(a6)	; Forbid - Stäng av multitasking

 IFNE DisableCache		; Kolla om den här rutinen ska vara med...
	cmp.w	#36,20(a6)	; Disable Cache funkar bara på KS2.0++
	bcs.s	.not20		; Om lägre än 2.0 så hoppar vi vidare...
	move.l	#$00003818,d0	; Har inte kunnat testa de här siffrorna
	move.l	#$80003b1b,d1	; men det ska förmodligen funka :-)
	jsr	-648(a6)	; CacheControl()
	lea.l	OldCache(pc),a0
	move.l	d0,(a0)
.not20:
 ENDC

	lea.l	gfxname(pc),a1
	jsr	-408(a6)	; OldOpenLibrary() - Funkar lika bra som
	tst.l	d0		; valiga openlibrary() om man inte behöver
	beq.w	noGfx		; öppna en speciell version...
	move.l	d0,GfxBase
	move.l	d0,a6
	jsr	-456(a6)	; Ownblitter - Ska vi använda blittern måste
				; vi se till att vi får ta den...
	jsr	-228(a6)	; Och det är lika bra att vänta på den oxå!
				; Det gör vi med WaitBlit()
	sub.l	a1,a1
	jsr	-222(a6)	; Loadview - Nu kysser vi copperlistan adjö!
	jsr	-270(a6)	; WaitTOF - Vänta tills vi med säkerhet kan
	jsr	-270(a6)	; installera våra egna saker...WaitTOF ska
				; enligt Commodore helst köras två gånger!
	lea.l	oldintena(pc),a1
	move.w	$dff01c,(a1)+	; Spara gamla interrupt statusen
	move.w	$dff002,(a1)	; Spara gammal DMA
	move.w	#$7fff,d0
	move.w	d0,$dff09a	; INTENA
	move.w	d0,$dff096	; DMACON - Vi rensar gammal skit...

 IFNE DisableMotor		; Kolla om vi ska kötta av drivemotorn...
	move.b	#-1,$bfd100	; Den här lilla kodsnutten är inte direkt
	nop			; snäll men bör fungera ändå. Det värsta
	nop			; som kan hända är att driven fortsätter
	move.b	#$81,$bfd100	; att vara på.
	nop			; Mer om denna lilla snutt kommer när jag
	nop			; tar upp HW-trackloading! Tills dess får
	move.b	#-1,$bfd100	; ni svälja den utan förklaring...
 ENDC
	movem.l	(sp)+,d0-a6
	rts

NoGFX:	move.l	$4.w,a6
	jsr	-138(a6)	; Permit multitasking...
	movem.l	(sp)+,d0-a6
	rts

; Denna rutin sätter upp systemet igen...
UNINIT:
	movem.l	d0-a6,-(sp)
	move.w	oldintena(pc),d0
	or.w	#$8000,d0		; Så bitarna sätts och inte rensas
	move.w	d0,$dff09a
	move.w	olddmacon(pc),d0
	or.w	#$8000,d0
	move.w	d0,$dff096

	move.l	gfxbase(pc),a6
	jsr	-228(a6)	; Waitblit()
	jsr	-462(a6)	; DisOwnBlit()

	lea.l	intname(pc),a1	; Nu måste vi öppna Intuition
	move.l	$4.w,a6
	jsr	-408(a6)	; OldOpenlibrary
	move.l	d0,a2
	tst.l	d0		; Kolla om intuition gick att öppna....
	beq.s	.closegfx	; Hoppa om det inte gick...

	lea.l	34(a2),a1	; Fetcha Viewporten!
	move.l	gfxbase(pc),a6
	jsr	-222(a6)	; Loadviewa in den riktiga viewporten!
	move.l	$26(a6),$dff080	; Och restora gamla copperlistorna...
	move.l	$30(a6),$dff084

	move.l	a2,a1
	move.l	$4.w,a6
	jsr	-414(a6)	; Stäng intuition

.closegfx:
	move.l	$4.w,a6
	move.l	gfxbase(pc),a1
	jsr	-414(a6)	; och stäng också graphics.library...

 IFNE DisableCache
; Nu sätter vi på cache-minnet igen...
	cmp.w	#36,20(a6)	; Kolla version igen...Verkligen dumt
	bcs.s	.not20		; om det skulle ändra sig under programmets
				; gång! :-)))
	move.l	oldcache(pc),d0
	moveq	#-1,d1
	jsr	-648(a6)	; CacheControl()
.not20:
 ENDC
	jsr	-138(a6)	; Permit multitasking

	bsr.s	FetchTime
	movem.l	(sp)+,d0-a6
	rts

; Den här rutinen hämtar VBR, vilket är en offset till vektorområdet
; på högre processorer än 68000. På en vanlig 68000 är VBR alltid 0.
FetchVBR:
	move.l	a6,-(sp)
	move.l	$4.w,a6			; Hämta exec
	sub.l	a0,a0			; Nollställ a0
	btst	#0,Attnflags+1(a6)	; Kolla om 68010++
	beq.s	.Lower
	move.l	a5,-(sp)
	lea.l	.getVBR(pc),a5
	jsr	-30(a6)		; Vi måste hämta VBR i supervisor mode
	move.l	(sp)+,a5
.lower:	move.l	(sp)+,a6
	rts

.GetVBR:movem.l	a0/a1,-(sp)
	lea.l	MyVBR(pc),a1	; Här ska vi skriva in VBRen.
	dc.l	$4e7a8801	; Detta är egentligen instruktionen
				; MOVEC VBR,A0 men eftersom äldre versioner
				; av AsmOne inte stödjer den instruktionen
				; gjorde jag ett litet specialtrick här...
	move.l	a0,(a1)
	movem.l	(sp)+,a0/a1
	rte			; Återvänd från supervisor...

; Fetch Time Variables
IOTV_SIZE		=$28
IO_COMMAND		=$1C
TR_SETSYSTIME		=$B
tr_time			=$20
_LVOCloseDevice		=-$1C2
_LVODoIO		=-$1C8
_LVOReadBattClock	=-$C
_LVOOpenDevice		=-$1BC
_LVOOpenResource	=-$1F2
FetchTime:
	movem.l	d0-a6,-(sp)
	move.l	4.w,a6
	lea	battclockname(pc),a1
	jsr	_LVOOpenResource(a6)
	move.l	d0,d6
	beq.b	.noclock

	moveq	#0,d0
	moveq	#0,d1
	lea	timername(pc),a0
	lea	timerio(pc),a2
	move.l	a2,a1
	jsr	_LVOOpenDevice(a6)
	move.l	d0,d7
	bne.b	.notimerdev

	exg.l	d6,a6
	jsr	_LVOReadBattClock(a6)
	exg.l	d6,a6
	move.l	a2,a1
	move.l	d0,tr_time(a1)
	move.w	#TR_SETSYSTIME,IO_COMMAND(a1)
	jsr	_LVODoIO(a6)
	move.l	a2,a1
	jsr	_LVOCloseDevice(a6)
.notimerdev
.noclock
	movem.l	(sp)+,d0-a6
	rts

timerio	dcb.b	IOTV_SIZE
battclockname	dc.b	'battclock.resource',0
timername	dc.b	'timer.device',0
		even
MyVBR:	dc.l	0		; Här lägger vi VBR!

*** End of INIT routine ***


SetBplPtr:
	movem.l	d0/a0,-(sp)
	move.l	ShowPage(pc),a0	; Hämta skärmpekaren
	move.l	a0,d0
	lea.l	bplptr+2(pc),a0
;	moveq	#4-1,d7
.setbpl:move.w	d0,4(a0)	; Ställ in bitplanspekare
	swap	d0
	move.w	d0,(a0)
;	swap	d0
;	lea.l	8(a0),a0
;	add.l	#screensize,d0
;	dbf	d7,.setbpl
	movem.l	(sp)+,d0/a0
	rts

************* COPPER ************

copper:
	dc.w	$008e,$2c81	; DIWSTRT
	dc.w	$0090,$2cc1	; DIWSTOP
	dc.w	$0092,$0038	; DDFSTRT
	dc.w	$0094,$00d0	; DDFSTOP
	dc.w	$0100,$1200	; BPLCON0
	dc.w	$0102,$0000	; BPLCON1
	dc.w	$0104,$0000	; BPLCON2
	dc.w	$0108,$0000	; BPLMOD0
	dc.w	$010a,$0000	; BPLMOD1

	; Bitplanspekare
bplptr:	dc.w	$00e0,$0000,$00e2,$0000,$00e4,$0000,$00e6,$0000
	dc.w	$00e8,$0000,$00ea,$0000,$00ec,$0000,$00ee,$0000

	dc.w	$0180,$003,$0182,$0e6

	dc.w	$ffff,$fffe

gfxname:dc.b	'graphics.library',0
intname:dc.b	'intuition.library',0
	even
gfxbase:dc.l	0
oldintena:
	dc.w	0
olddmacon:
	dc.w	0
OldCache:
	dc.l	0

ShowPage:
	dc.l	screen1

BalkData:
	dc.l	300,80,200,230,200,240,300,90,300,80
	dc.l	-1

	section	scr,BSS_c
screen1:ds.b	screensize*1	; 1 bitplan

; Blitter MINTERM data
DEST =	$100
SRCC =	$200
SRCB =	$400
SRCA =	$800

ABC =	$80
ABNC =	$40
ANBC =	$20
ANBNC =	$10
NABC =	$8
NABNC =	$4
NANBC =	$2
NANBNC =	$1

A = ABC!ABNC!ANBC!ANBNC
B = ABC!ABNC!NABC!NABNC
C = ABC!ANBC!NABC!NANBC

NA = (~A)&$FF
NB = (~B)&$FF
NC = (~C)&$FF
