*****************************************************************************
;                         STOLEN DATA Issue #3
;                         ~~~~~~~~~~~~~~~~~~~~
;   Double buffered one bitplane scroller
;
;   Copyright ANARCHY 1990
;
;   Written on 3.6.90 by Kreator of ANARCHY
;
; This type of scroller is very efficient speedwise, but causes a number of
; problems, for instance it is difficult to implement variable speeds because
; the charcters are read a long time before they actually appear on screen.
; However even very large scrollers can be run in a very small amount of
; raster time using this technique.
;
;  General notes - At all times (unless specified) a6 points to the custom
; chip registers ($dff000) and a5 points to the list of variables.This allows
; indirect addressing modes to be used, which are much faster than absolute
; addressing and consume less memory. If these registers are needed in the
; course of a routine simply stick them on the stack, and pop them back off
; later.
; Scroller2 is very similar to this one but the scroll routine is not fully
; commented, but uses a technique commonly employed recently to get a base-
; relief 2 bitplane font from a single bitplane font. You'll have to figure
; it out for yourself, but its not difficult.
*****************************************************************************

	Incdir	DF1:
	Include	Source/NewCustomReg	; Custom register label equates
	Section	Scroller,Code_c		; Assemble into chip mem if > ½ meg

TEST		= 1		; Set this if you are only testing the menu
				; as it will then return to DEVPAC

Trap_vec0	= $80
Execbase	= $4
Oldopen		= -$198

	Move.l	#Start,$80.w	; Trap vector 0
	Trap	#0		; Execute trap 0 to give full control of the
	Rts			; 68000, needs an RTE to return.

Start
	Bsr	Killsys		; Disable the operating system
	Bsr	Setup		; Setup the demo
	Bsr	Main		; Now run the main demo routines
	Bsr	Sysreturn	; Return the system to its original state
	Moveq.l	#0,d0		; No errors !! A number of people neglect to
				; do this, and that causes batch files (such
				; as the startup sequence) to bomb out.
	Rte		; Return from exception ie. to the instruction after
			; the TRAP #0 call.

*****************************************************************************
;Killsys - Disable operating system and interrupts
*****************************************************************************
Killsys
	Move.l	$4.w,a6		; Execbase vector
	Lea	Gfxname(pc),a1	; Pointer to "graphics.library" into a1
	Jsr	Oldopen(a6)	; Open this library
	Lea	Variables,a5	; Get a pointer to the variable space
	Lea	$dff000,a6	; Pointer to custom chips
	Move.l	d0,a0		; Address of the graphics library
	Move.l	38(a0),Sys_copl(a5)	; Remember the System copperlist
	Move	Intenar(a6),Int_set(a5)	; System interrupts
	Move	#$7fff,Intena(a6)	; Now switch off all interrupts
	Move.l	$6c.w,Vbl_vec(a5)	; System vertical blanking int.
	Move	Dmaconr(a6),Dma_set(a5)	; System DMA
	Move	#$7fff,Dmacon(a6)	; Clear all DMA
	Move	#$87c0,Dmacon(a6)	; Now set the required DMA channels
	Rts

Gfxname		Dc.b "graphics.library",0
	Even
*****************************************************************************
;Sysreturn - Re-enable operating system and interrupts
*****************************************************************************
Sysreturn
	Move	#$7fff,Intena(a6)	; Clear all interrupts
	Move.l	Vbl_vec(a5),$6c.w	; Restore VBL interrupt
	Move	Int_set(a5),d0
	Or	#$c000,d0
	Move	d0,Intena(a6)		; Restore system interrupts
	Move	#$7fff,Dmacon(a6)
	Move	Dma_set(a5),d0
	Or	#$8200,d0
	Move	d0,Dmacon(a6)		; Restore system DMA
	Move.l	Sys_copl(a5),Cop1lch(a6)	; Restore system copperlist
	Clr	Copjmp1(a6)
	Rts
*****************************************************************************
;Setup - Setup the menu system
*****************************************************************************
Setup

;-- Remove the sprite data (prevents sprite streaks down the screen)

	Lea	Spr0data(a6),a0
	Moveq	#7,d0
Clop
	Clr.l	(a0)
	Addq.l	#8,a0
	Dbf	d0,Clop

;-- Clear the scroll area

	Lea	Screen1,a0
	Move	#$bff,d0
Clear_scroll
	Clr.l	(a0)+
	Dbf	d0,Clear_scroll

;-- Initialise some variables

	Move.l	#Screen1,Current_bank(a5)	; The two double buffered
	Move.l	#Screen2,Other_bank(a5)		; scroll areas
	Move.l	#Scrolltext,Text_pt(a5)	; Initialise the scrolltext
	Move	#5,Speed(a5)		; Set the scroll speed
	Move.l	#Clstart,Cop1lch(a6)	; Init. our own copperlist
	Clr	Copjmp1(a6)
	Rts

*****************************************************************************
; Main - Central demo calling routine + mouse button check
*****************************************************************************
Main
	Move.l	Vposr(a6),d0
	And.l	#$1ff00,d0
	Cmp.l	#$00100,d0	; Wait for vertical position 1
	Bne.s	Main

	Bsr	Scroller	; Jump to the scroll routine

	Move	#$200,d0	; Small delay loop, this is needed because
.Loop 				; in general the scroll routine uses less
	Dbf	d0,.Loop	; than 1 raster line.As soon as more routines
				; are added this is no longer required.

	Btst	#6,$bfe001	; Check the left mouse button
	Bne.s	Main

	Rts
*****************************************************************************
; Scroller - The scroller is 80 bytes across of which only 46 are displayed
; at any one time. While one is displayed the other is updated, the last 48
; bytes (ie. 24 charcters) of the displayed scroller are copied into the
; first 48 bytes of the other. Then the remaining 32 bytes (16 characters)
; are plotted, from the scrolltext. Every frame two af these characters are
; copied, so it takes 20 frames to create the new scroll bank, and the banks
; are switched when the scroll pointer exceeds 256 (this pointer is just a
; pixel offset from the start of the displayed scroller) This means that the
; Maximum speed of the scroller is about 256/20 = 12 pixels per frame.
; Scroll_pt - the aforementioned scroll pointer
; Mode - Indicates if we need to copy the old data, or create new data from
;	 the scrolltext. <12 means copy old data =>12&<20 means use the
;	 scrolltext =20 means finished updating the scroller.
*****************************************************************************
Scroller
	Move	Scroll_pt(a5),d0
	Add	Speed(a5),d0
	Cmp	#256,d0			; Do we need a bank change
	Blt.s	No_bank_change

	Move.l	Current_bank(a5),a0		; Swap the display and update
	Move.l	Other_bank(a5),Current_bank(a5)	; scroll banks
	Move.l	a0,Other_bank(a5)
	Sub	#256,d0			; Reset the scroll pointer
	Clr	Mode(a5)

No_bank_change
	Move	d0,Scroll_pt(a5)
	Move	d0,d1	; Remember d0 for calculating the shift register
	Lsr	#4,d0	; Convert d0 into a byte pointer on a word boundary
	Add	d0,d0
	Add	Current_bank+2(a5),d0	; Add the current 
	Move	d0,Scroll_planes+2	; Stick it into the bitplane pointers
	And	#$0f,d1	; Only first four bits needed for shift register
	Eor	#$0f,d1 ; Its a right shift so invert the bits as we are 
			; scrolling left
	Move	d1,Scroll+2	; Put it into the BPLCON1 register

	Cmp	#20,Mode(a5)
	Beq	No_blitting	; The update screen is ready
	Cmp	#12,Mode(a5)
	Blt	Char_scroll	; Move the old characters
New_chars
	Moveq	#0,d0
	Move	Mode(a5),d2
	Lsl	#2,d2		; Multiply mode by four ( 48-76 )
	Move.l	Other_bank(a5),a2
	Lea	(a2,d2),a2	; Add mode*4 to the update screen pointer
	Bsr	Read_char	; Read the next character and plot it
	Addq.l	#2,a2		; Point to the next screen character position
	Bsr	Read_char	; Then read another character and plot it
	Bra	Blit_exit	; Thats it for now.

Read_char
	Move.l	Text_pt(a5),a0	; Scrolltext pointer
New_char
	Move.b	(a0)+,d0
	Bne.s	Not_a_null	; If 0 then reset the scrolltext pointer
	Move.l	#Scrolltext,a0
	Bra.s	New_char	; Read the first character
Not_a_null
	Cmp	#$20,d0		; Less than 32, then consider as a space
	Bge.s	Not_control
	Moveq	#$20,d0
Not_control
	Cmp	#$5b,d0		; Convert lowercase to uppercase
	Blt.s	Not_lower_case
	Sub	#$20,d0
Not_lower_case
	Move.l	a0,Text_pt(a5)	; Save the new scrolltext pointer
	Sub	#$20,d0
	Add	d0,d0		; Convert ascii into a word pointer
	Lea	Charpos(pc),a0	; Table of character positions
	Move	(a0,d0),d1
	Lea	Font(pc),a1
	Lea	(a1,d1),a1	; a1 now points to the required character
				; graphic image.

	Move.l	#-1,Bltafwm(a6)
	Move.l	a2,Bltdpth(a6)
	Move.l	a1,Bltapth(a6)
	Move.l	#$09f00000,Bltcon0(a6)	; Just a direct copy from A to D
	Move.l	#$0026004e,Bltamod(a6)	; Screen is $50 wide, font is $28 wide
	Move	#(16<<6)!1,Bltsize(a6)	; 16 pixels deep
	Bra	Blit_wait		; Wait for the blitter to finish


Char_scroll
	Move	Mode(a5),d0
	Lsl	#2,d0
	Move.l	Other_bank(a5),a0
	Move.l	Current_bank(a5),a1
	Lea	(a0,d0),a0
	Lea	32(a1,d0),a1
	
	Move.l	#-1,Bltafwm(a6)
	Move.l	a0,Bltdpth(a6)
	Move.l	a1,Bltapth(a6)
	Move.l	#$09f00000,Bltcon0(a6)
	Move.l	#$004c004c,Bltamod(a6)
	Move	#64*16+2,Bltsize(a6)	; Copy two whole characters at once
	Bsr	Blit_wait
Blit_exit
	Addq	#1,Mode(a5)	; Move the mode on one
No_blitting
	Rts


Blit_wait
	Btst	#14,Dmaconr(a6)	; Is the blitter still at work?
	Bne.s	Blit_wait	; Yes, well carry on waiting then.
	Rts
*****************************************************************************
;   Copperlist1 for the menu selector screen
; For those readers unfamiliar with my coding, I generally use macros for
; generating my copperlists. These I find to be easier to debug etc. but do
; take a little longer to assemble, obviously though for much larger
; copperlists (such as the background effect on the menu-loader) a generating
; routine should be employed.
*****************************************************************************
Clstart
	Wait	0,20		; Give the processor time to update the cl.
	Mov	$9a64,Diwstrt
	Mov	$aad1,Diwstop
	Mov	$0028,Ddfstrt	; Horizontal scrolling overscan screen
	Mov	$00d8,Ddfstop	; ie. 368 pixels wide
	
	Mov	0,Color00
	Mov	$666,Color01
	Mov	$fff,Color02
	Mov	$aaa,Color03
	Mov	34,Bpl1mod	; Scroller is actually 80 bytes wide
Scroll
	Mov	0,Bplcon1
	Mov	0,Bplcon2
Scroll_planes
	Mov	$00,Bpl1ptl
	Mov	7,Bpl1pth
	Mov	$1200,Bplcon0	; Single bitplane
	Wait	$fe,$ff

*****************************************************************************
; Binaries
*****************************************************************************
Font
	Incbin	Binary/Bubble.font	; This font was drawn by KREATOR
					; so credit me if you use it

; The following table is used to calculate a pointer to the required
; character and in general must be created by hand. Some coders advocate a
; different approach, which is to have all your characters stacked on top
; of each other and then the pointers are very easy to calculate with a
; multiply. I still prefer this method myself.

Charpos	Dc.w	$0000,$0002,$0004,$0006,$0008,$000a,$000c,$000e ; !"#$%&'
	Dc.w	$0010,$0012,$0014,$0016,$0018,$001a,$001c,$001e ;()*+,-./
	Dc.w	$0020,$0022,$0024,$0026,$0280,$0282,$0284,$0286 ;01234567
	Dc.w	$0288,$028a,$028c,$028e,$0290,$0292,$0294,$0296 ;89:;<=>?
	Dc.w	$0298,$029a,$029c,$029e,$02a0,$02a2,$02a4,$02a6 ;@ADCDEFG
	Dc.w	$0500,$0502,$0504,$0506,$0508,$050a,$050c,$050e ;HIJKLMNO
	Dc.w	$0510,$0512,$0514,$0516,$0518,$051a,$051c,$051e ;PQRSTUVW
	Dc.w	$0520,$0522,$0524,$0526                         ;XYZ[\]^_

; Zero terminated scrolltext, allows a mixture of upper and lower case
; although all the text will be displayed in uppercase. Also you can edit
; the text in any text editor, as the scroller counts all control characters
; such as 10 (line feed) as spaces.

Scrolltext
	Dc.b	"insert your own scrolltext here !!!"
	Dc.b	"     this scroller was written for "
	Dc.b	"STOLEN DATA, copyright ANARCHY 1990"
	Dc.b	"                                   ",0
	Even

*****************************************************************************
;   Variables
*****************************************************************************

	Rsreset
Int_set		Rs.w 1	; System interrupts
Dma_set		Rs.w 1	; System DMA channels
Sys_copl	Rs.l 1	;  ""    copperlist
Vbl_vec		Rs.l 1	;  ""    vertical blanking interrupt
Scroll_pt	Rs.w 1	; Scroll pointer, offset in pixels
Mode		Rs.w 1	; <12 COPY OLD, >=12 <20 NEW CHARS, =20 STOP
Current_bank	Rs.l 1	; Currently displayed scroller
Other_bank	Rs.l 1	; The update scroller
Text_pt		Rs.l 1	; Pointer to the scrolltext
Speed		Rs.w 1	; Scroll speed (must be less than 12)

Varisize	Rs.b 0	; Simply used to determine how much space is needed
			; for the variable list

Variables	Dcb.b Varisize	; Reserve space for the variables

;-- Reserve some space for screens etc..

	Rsset $70000
Screen1		Rs.b	16*80
Screen2		Rs.b	16*80



