*******************************************************************************
*                         Example scope for HippoPlayer
*	                        By K-P Koljonen
*******************************************************************************
* Can be assembled with Asm-One v1.25, at least. 
* Works on all Amiga configurations!

* Version 1.1: Considered sample length to be bytes when it actually was in
*              words.
* V1.2:	       Little fix.


*** Includes:

 	incdir	include:
	include	exec/exec_lib.i
	include	exec/ports.i
	include	exec/types.i
	include	graphics/graphics_lib.i
	include	graphics/rastport.i
	include	intuition/intuition_lib.i
	include	intuition/intuition.i
	incdir

*** Some useful macros

lob	macro
	jsr	_LVO\1(a6)
	endm

lore	macro
	ifc	"\1","Exec"
	ifd	_ExecBase
	ifeq	_ExecBase
	move.l	(a5),a6
	else
	move.l	_ExecBase(a5),a6
	endc
	else
	move.l	4.w,a6
	endc
	else
	move.l	_\1Base(a5),a6
	endc
	jsr	_LVO\2(a6)
	endm

pushm	macro
	ifc	"\1","all"
	movem.l	d0-a6,-(sp)
	else
	movem.l	\1,-(sp)
	endc
	endm

popm	macro
	ifc	"\1","all"
	movem.l	(sp)+,d0-a6
	else
	movem.l	(sp)+,\1
	endc
	endm

push	macro
	move.l	\1,-(sp)
	endm

pop	macro
	move.l	(sp)+,\1
	endm



*** HippoPlayer's port:

	STRUCTURE	HippoPort,MP_SIZE
	LONG		hip_private1	* Private..
	APTR		hip_kplbase	* kplbase address
	WORD		hip_reserved0	* Private..
	BYTE		hip_reserved1	* Private..
	BYTE		hip_opencount	* Open count
	BYTE		hip_mainvolume	* Main volume, 0-64
	BYTE		hip_play	* If non-zero, HiP is playing
	BYTE		hip_playertype 	* 33 = Protracker, 49 = PS3M. 
	*** Protracker ***
	BYTE		hip_reserved2
	APTR		hip_PTch1	* Protracker channel data for ch1
	APTR		hip_PTch2	* ch2
	APTR		hip_PTch3	* ch3
	APTR		hip_PTch4	* ch4
	*** PS3M ***
	APTR		hip_ps3mleft	* Buffer for the left side
	APTR		hip_ps3mright	* Buffer for the right side
	LONG		hip_ps3moffs	* Playing position
	LONG		hip_ps3mmaxoffs	* Max value for hip_ps3moffs

	BYTE		hip_PTtrigger1
	BYTE		hip_PTtrigger2
	BYTE		hip_PTtrigger3
	BYTE		hip_PTtrigger4

	LABEL		HippoPort_SIZEOF 

	*** PT channel data block
	STRUCTURE	PTch,0
	LONG		PTch_start	* Start address of sample
	WORD		PTch_length	* Length of sample in words
	LONG		PTch_loopstart	* Start address of loop
	WORD		PTch_replen	* Loop length in words
	WORD		PTch_volume	* Channel volume
	WORD		PTch_period	* Channel period
	WORD		PTch_private1	* Private...

*** Dimensions:

WIDTH	=	320	
HEIGHT	=	64

*** Variables:

	rsreset
_ExecBase	rs.l	1
_GFXBase	rs.l	1
_IntuiBase	rs.l	1
port		rs.l	1
owntask		rs.l	1
screenlock	rs.l	1
oldpri		rs.l	1
windowbase	rs.l	1
rastport	rs.l	1
userport	rs.l	1
windowtop	rs	1
windowright	rs	1
windowleft	rs	1
windowbottom	rs	1
draw1		rs.l	1
draw2		rs.l	1
omabitmap	rs.b	bm_SIZEOF
size_var	rs.b	0

*** Main program

main	lea	var_b,a5		* Store execbase
	move.l	4.w,a6
	move.l	a6,(a5)
	
	sub.l	a1,a1			* Find our process
	lob	FindTask
	move.l	d0,owntask(a5)

	lea	intuiname(pc),a1	* Open libs
	lore	Exec,OldOpenLibrary
	move.l	d0,_IntuiBase(a5)

	lea 	gfxname(pc),a1		
	lob	OldOpenLibrary
	move.l	d0,_GFXBase(a5)

*** Try to find HippoPlayer's port. If succesful, add 1 to hip_opencount
*** indicating we are using the information in the port.
*** Protect this procedure with Forbid()-Permit()!

	lob	Forbid
	lea	portname(pc),a1
	lob	FindPort
	move.l	d0,port(a5)
	beq.w	exit
	move.l	d0,a0
	addq.b	#1,hip_opencount(a0)	* We are using the port now!
	lob	Permit

*** Get some info about the screen we're running on

	bsr.w	getscreendata

*** Open our window

	lea	winstruc,a0
	lore	Intui,OpenWindow
	move.l	d0,windowbase(a5)
	beq.w	exit
	move.l	d0,a0
	move.l	wd_RPort(a0),rastport(a5)	* Store rastport and userport
	move.l	wd_UserPort(a0),userport(a5)

*** Draw a bevel box

plx1	equr	d4
plx2	equr	d5
ply1	equr	d6
ply2	equr	d7
 
	moveq   #8,plx1
	move    #331,plx2
	moveq   #13,ply1
	moveq   #80,ply2
	add	windowleft(a5),plx1
	add	windowleft(a5),plx2
	add	windowtop(a5),ply1
	add	windowtop(a5),ply2
	move.l	rastport(a5),a2
	bsr.w	piirra_loota2
	subq	#1,plx1
	addq	#1,plx2
	bsr.w	piirra_loota2a

*** Initialize the bitmap structure

	lea	omabitmap(a5),a0
	moveq	#1,d0			* depth (1 bitplane)
	move	#WIDTH,d1		* width
	move	#HEIGHT,d2		* height 
	lore	GFX,InitBitMap
	move.l	#buffer1,omabitmap+bm_Planes(a5) * Plane pointer

	move.l	#buffer1,draw1(a5)	* Buffer pointers for drawing
	move.l	#buffer2,draw2(a5)

*** Set task priority to -30 to prevent messing up with other programs

	move.l	owntask(a5),a1		
	moveq	#-30,d0
	lore	Exec,SetTaskPri
	move.l	d0,oldpri(a5)		* Store the old priority

*** Main loop begins here

loop	move.l	_GFXBase(a5),a6		* Wait a while..
	lob	WaitTOF

	move.l	port(a5),a0		* Check if HiP is playing
	tst.b	hip_play(a0)
	beq.b	.oh
	bsr.w	dung			* Do the scope
.oh
	move.l	userport(a5),a0		* Get messages from IDCMP
	lore	Exec,GetMsg
	tst.l	d0
	beq.b	loop
	move.l	d0,a1

	move.l	im_Class(a1),d2		
	lob	ReplyMsg
	cmp.l	#IDCMP_CLOSEWINDOW,d2	* Should we exit?
	bne.b	loop			* No. Keep loopin'
	
	move.l	owntask(a5),a1		* Restore the old priority
	move.l	oldpri(a5),d0
	lore	Exec,SetTaskPri

exit

*** Exit program
	
	move.l	port(a5),d0		* IMPORTANT! Subtract 1 from
	beq.b	.uh0			* hip_opencount when the port is not
	move.l	d0,a0			* needed anymore!
	subq.b	#1,hip_opencount(a0)
.uh0

	move.l	windowbase(a5),d0	* Close the window
	beq.b	.uh1
	move.l	d0,a0
	lore	Intui,CloseWindow
.uh1
	move.l	_IntuiBase(a5),a1	* And the libs
	lore	Exec,CloseLibrary
	move.l	_GFXBase(a5),a1
	lob	CloseLibrary

	moveq	#0,d0			* No error
	rts
	
***** Get some info about the screen we're running on

getscreendata
	move.l	(a5),a0			* Running kick2.0 or newer?
	cmp	#37,LIB_VERSION(a0)
	bhs.b	.new		
	rts				
.new					* Yes.
	
	sub.l	a0,a0			* Default public screen
	lore	Intui,LockPubScreen  	* Kick2.0+ function
	move.l	d0,d7
	beq.b	exit

	move.l	d0,a0			* Store the values
	move.b	sc_BarHeight(a0),windowtop+1(a5) 
	move.b	sc_WBorLeft(a0),windowleft+1(a5)
	move.b	sc_WBorRight(a0),windowright+1(a5)
	move.b	sc_WBorBottom(a0),windowbottom+1(a5)
	subq	#4,windowleft(a5)
	subq	#4,windowright(a5)
	subq	#2,windowbottom(a5)
	sub	#10,windowtop(a5)
	bpl.b	.olde
	clr	windowtop(a5)
.olde
	move	windowtop(a5),d0	* Adjust the window size
	add	d0,winstruc+nw_Height
	move	windowleft(a5),d1
	add	d1,winstruc+nw_Width
	move	windowbottom(a5),d3
	add	d3,winstruc+nw_Height

	move.l	d7,a1			* Unlock it. Let's hope it doesn't
	sub.l	a0,a0			* go anywhere before we open our
	lob	UnlockPubScreen		* window ;-)
	rts


*** Draw a bevel box

piirra_loota2a
	pushm	all
	moveq	#1,d3
	bra.b	prl

piirra_loota2
	pushm	all
	moveq	#0,d3
prl
	move.l	rastport(a5),a2
	move	#1,a3
	move	#2,a4

	move.l	_GFXBase(a5),a6
	move.l	a3,d0
	move.l	a2,a1
	lob	SetAPen

	move.l	plx1,d0
	move.l	ply2,d1
	lob	Move
	move.l	a2,a1
	move.l	plx1,d0
	move.l	ply1,d1
	lob	Draw

	move.l	a2,a1
	move.l	plx2,d0
	move.l	ply1,d1
	lob	Draw

	move.l	a2,a1
	move.l	a4,d0
	lob	SetAPen

	move.l	a2,a1
	move.l	plx2,d0
	move.l	ply1,d1
	addq.l	#1,d1
	sub.l	d3,d1
	lob	Move
	move.l	a2,a1
	move.l	plx2,d0
	move.l	ply2,d1
	lob	Draw

	move.l	a2,a1
	move.l	plx1,d0
	addq.l	#1,d0
	move.l	ply2,d1
	lob	Draw

	move.l	a2,a1
	moveq	#1,d0
	lob	SetAPen

	popm	all
	rts



*** Display the scope

* I have two buffers, one for drawing and one for clearing.
* Clearing is done with blitter during which cpu draws into the other
* buffer. The drawn buffer is then dumped into the window using
* BltBitMapRastPort().  If someone knows a faster way for doing this please
* tell me.

dung
	move.l	_GFXBase(a5),a6		* Grab the blitter
	lob	OwnBlitter
	lob	WaitBlit

* Clear the drawing area using 'demo programmer code'.. :)
	move.l	draw2(a5),$dff054	* Blitter destination D
	move	#0,$dff066		* Modulo D
	move.l	#$01000000,$dff040	* Enable D
	move	#HEIGHT*64+WIDTH/16,$dff058 * Write size, start blitter

	lob	DisownBlitter		* Free the blitter

	pushm	all
	move.l	port(a5),a0
	cmp.b	#33,hip_playertype(a0)	* Protracker?
	beq.b	.1
	cmp.b	#49,hip_playertype(a0)	* PS3M?
	beq.b	.2
	bra.b	.3
.1	bsr.b	quadrascope		* Quadrascope for PT
	bra.b	.3
.2	bsr.w	multiscope		* Stereoscope for PS3M
.3	popm	all

	movem.l	draw1(a5),d0/d1		* Switch the buffers
	exg	d0,d1
	movem.l	d0/d1,draw1(a5)

	lea	omabitmap(a5),a0	* Set the bitplane pointer
	move.l	d1,bm_Planes(a0)

;	lea	omabitmap(a5),a0	* Copy from bitmap to rastport
	move.l	rastport(a5),a1
	moveq	#0,d0		* source x,y
	moveq	#0,d1
	moveq	#10,d2		* dest x,y
	moveq	#15,d3
	add	windowleft(a5),d2
	add	windowtop(a5),d3
	move	#$c0,d6		* minterm a->d
	move	#WIDTH,d4	* x-size
	move	#HEIGHT,d5	* y-size
	lore	GFX,BltBitMapRastPort	* Zwoosh!
	rts

*** Quarascope routine for Protracker

* This (and the stereoscope) are very unoptimized. The reason for this is
* that unoptimized code is usually easier to understand than optimized code.
* Also this leaves a certain challenge for coders to try to get the loop
* as fast as possible.

quadrascope
	move.l	port(a5),a3
	move.l	hip_PTch1(a3),a3	* Channel 1 data
	move.l	draw1(a5),a0		
	bsr.b	.scope

* WIDTH/8/4 = 10

	move.l	port(a5),a3
	move.l	hip_PTch2(a3),a3
	move.l	draw1(a5),a0
	lea	10(a0),a0		* Position
	bsr.b	.scope

	move.l	port(a5),a3
	move.l	hip_PTch3(a3),a3
	move.l	draw1(a5),a0
	lea	10+10(a0),a0
	bsr.b	.scope

	move.l	port(a5),a3
	move.l	hip_PTch4(a3),a3
	move.l	draw1(a5),a0
	lea	10+10+10(a0),a0
	bsr.b	.scope
	rts

.scope
	tst.l	PTch_loopstart(a3)	* Always check these to avoid
	beq.b	.halt			* enforcer hits!
	move.l	PTch_start(a3),d0
	bne.b	.jolt
.halt	rts

.jolt	
	move.l	d0,a1				* Sample start
	moveq	#0,d5
	move	PTch_length(a3),d5		* Sample length in words
	add.l	d5,d5				* Convert to bytes

	move.l	port(a5),a2		* Get mainvolume
	moveq	#0,d1
	move.b	hip_mainvolume(a2),d1	* (Main volume * sample volume)/64
	mulu	PTch_volume(a3),d1	
	lsr	#6,d1			* Value for scaling the data

	moveq	#0,d0			* X coordinate
	moveq	#80-1,d7		* Loop counter, do 80 pixels
drlo	
	move.b	(a1)+,d2		* Read one byte sample data
	ext	d2			* Sign extend to word
	muls	d1,d2			* Scale according to volume
	asr	#6,d2			* ...
	add	#$80,d2			* Change the sign
	asr	#2,d2			* Scale down to 0-63
	mulu	#WIDTH/8,d2		* Get Y coordinate in the bitplane

	move	d0,d4			* X
	move	d0,d3
	lsr	#3,d4			* X offset in bytes = x-coord/8
	add	d4,d2			* Add to Y
	not	d3			* Plot pixel
	bset	d3,(a0,d2)		* ...

	subq.l	#1,d5			* Subtract sample length
	bpl.b	.l			* sample end?
	moveq	#0,d5			* Get values for loop 
	move	PTch_replen(a3),d5	
	add.l	d5,d5
	move.l	PTch_loopstart(a3),a1
.l
	addq	#1,d0		* Increase X

	dbf	d7,drlo		* Loop..
	rts



*** Stereoscope for PS3M

multiscope
	move.l	port(a5),a1
	move.l	hip_ps3mleft(a1),a1
	move.l	draw1(a5),a0
	bsr.b	.h

	move.l	port(a5),a1
	move.l	hip_ps3mright(a1),a1
	move.l	draw1(a5),a0
	lea	WIDTH/8/2(a0),a0
	bsr.b	.h
	rts

.h	move.l	port(a5),a2
	move.l	hip_ps3moffs(a2),d5		* Get offset in buffers
	move.l	hip_ps3mmaxoffs(a2),d4		* Get max offset
		
	move	#160-1,d7		* Draw 160 pixels
	moveq	#0,d0			* X coord
.drlo	
	moveq	#0,d2
	move.b	(a1,d5.l),d2		* Get data from mixing buffer
	add.b	#$80,d2
	lsr	#2,d2
	mulu	#WIDTH/8,d2		* Y

	move	d0,d6
	move	d0,d3
	lsr	#3,d6			* X offset in bytes = x-coord/8
	add	d6,d2			
	not	d3			* Plot pixel
	bset	d3,(a0,d2)		* ...

	addq	#1,d0			* Increase x coord
	addq.l	#1,d5			* Increase buffer position
	and.l	d4,d5			* make sure it stays in the buffer

	dbf	d7,.drlo		* Loop
	rts



*******************************************************************************
* Window

wflags 	= WFLG_SMART_REFRESH!WFLG_DRAGBAR!WFLG_CLOSEGADGET!WFLG_DEPTHGADGET
idcmpflags = IDCMP_CLOSEWINDOW

winstruc
	dc	110,85	* x,y position
winsiz	dc	340,85	* x,y size
	dc.b	2,1	
	dc.l	idcmpflags
	dc.l	wflags
	dc.l	0
	dc.l	0	
	dc.l	.t	* title
	dc.l	0
	dc.l	0	
	dc	0,640	* min/max x
	dc	0,256	* min/max y
	dc	WBENCHSCREEN
	dc.l	0

.t	dc.b	"HiP - Example scope",0

intuiname	dc.b	"intuition.library",0
gfxname		dc.b	"graphics.library",0
portname	dc.b	"HiP-Port",0
 even


 	section	udnm,bss_p

* Variables

var_b		ds.b	size_var

	section	hihi,bss_c

* GFX buffers

buffer1	ds.b	WIDTH/8*HEIGHT
buffer2	ds.b	WIDTH/8*HEIGHT

 end
