
;
; Copyright (c) 1988 Commodore-Amiga, Inc.
;
; Executables based on this information may be used in software
; for Commodore Amiga computers.  All other rights reserved.
;
; This information is provided "as is"; no warranties are made.
; All use is at your own risk, and no liability or responsibility is assumed.
;

;==============================================================================
; An example of Amiga assembly language programming to complement the notes
; provided for the Developers Conference.  Written by Steve Beats, 4/26/88.
;
; This is just a simple letter square program that allows you to slide letters
; around in a grid with a view to getting them in the right order.  Use the
; right mouse button to shuffle the letters and the left mouse button to put
; them back in order again.  A count of shuffles versus moves is kept so you
; have some idea of how well you are doing.
;
; It's left as an excercise for the reader to detect when game is over :-)
;
; It would probably be a good idea to re-organise the source into separate,
; smaller modules, but I left it as one humongous source file so that I didn't
; have to get bogged down in linker details.  To assemble with the Metacomco
; assembler, use the following command:-
;
;	assem example.asm -o example.o -i INCLUDE: -c=W150000
;
; where INCLUDE: is assigned to the directory containing the standard includes.
; Linking is also simple, just use the following:-
;
;	blink example.o to example lib INCLUDE:amiga.lib
;
;==============================================================================
		SECTION	example,CODE

		NOLIST
		INCLUDE	"exec/types.i"
		INCLUDE	"exec/memory.i"
		INCLUDE	"exec/ports.i"
		INCLUDE	"intuition/intuition.i"
		LIST

; first we define all the global data we will require for this program
	STRUCTURE globals,0
		APTR	SysLib			exec library
		APTR	IntLib			intuition library
		APTR	GfxLib			graphics library
		APTR	DosLib			dos library

		APTR	MainWindow		pointer to the window
		LONG	WaitMask		mask of bits to wait for
		APTR	TheRastPort		windows rastport

		APTR	GameArray		tile memory
		UWORD	SpaceX			x co-ordinate of the hole
		UWORD	SpaceY			y co-ordinate of the hole
		ULONG	Shuffles		number of shuffles we've done

		APTR	TextBuff		for making numbers
		STRUCT  RND,8			current random seed

		UWORD	Closed			flag, user hit close gadget
	LABEL globals_SIZEOF

; now define data structures for private stuff used within the program
	STRUCTURE tile,0
		UWORD	t_x			position of tile
		UWORD	t_y
		UWORD	t_type			0=no tile else the letter
		UWORD	t_moved			flag for shuffling
	LABEL tile_SIZEOF

; next, define all library routines we will be calling as external symbols
		XREF	_AbsExecBase,_LVOOpenLibrary,_LVOCloseLibrary
		XREF	_LVOAllocMem,_LVOFreeMem
		XREF	_LVOWait,_LVOGetMsg,_LVOReplyMsg

		XREF	_LVOOpenWindow,_LVOCloseWindow
		XREF	_LVOBeginRefresh,_LVOEndRefresh
		XREF	_LVOModifyIDCMP,_LVOCurrentTime
		XREF	_LVODisplayBeep

		XREF	_LVOSetAPen,_LVOSetDrMd,_LVOMove
		XREF	_LVOText,_LVORectFill

;==============================================================================
; The entry point of the program.  Reserve space for globals on the stack.
;==============================================================================
Start		moveq.l	#(globals_SIZEOF/2)-1,d0
ClrStack	clr.w	-(sp)
		dbra	d0,ClrStack
		movea.l	sp,a5			a5 always points to globals
		move.l	_AbsExecBase,SysLib(a5)	stash exec base

		lea.l	Libraries(pc),a0	open all our libraries
		bsr	OpenLibs
		tst.l	d0			did it work ?
		beq	Exit			no, exit now

		lea.l	MemTable(pc),a0		allocate all our static memory
		bsr	AllocMemory
		tst.l	d0			did it work ?
		beq	Exit			no, exit now

		movea.l	IntLib(a5),a6		seed random number generator
		lea.l	RND(a5),a0
		lea.l	RND+4(a5),a1
		jsr	_LVOCurrentTime(a6)

		lea.l	MyWindow(pc),a0		open the window
		move.l	a0,d0
		add.l	d0,nw_Title(a0)		relocate title reference
		jsr	_LVOOpenWindow(a6)
		move.l	d0,MainWindow(a5)	did it open OK ?
		beq	Exit			no, exit now

		movea.l	d0,a0			stash important values
		move.l	wd_RPort(a0),TheRastPort(a5)
		moveq.l	#0,d0
		movea.l	wd_UserPort(a0),a1
		move.b	MP_SIGBIT(a1),d1
		bset.l	d1,d0
		move.l	d0,WaitMask(a5)		bit mask we will wait on

		bsr	InitTiles		initialise tile memory
		moveq.l	#0,d0			so we redraw OK first time thru
		bsr	RefreshWindow		and draw the tiles

;==============================================================================
; The main loop of the program.  Wait on some kind of input and dispatch it.
;==============================================================================
MainLoop	movea.l	SysLib(a5),a6		wait for something to happen
		move.l	WaitMask(a5),d0
		jsr	_LVOWait(a6)

NextMessage	movea.l	MainWindow(a5),a0
		movea.l	wd_UserPort(a0),a0	woken up, get a message
		jsr	_LVOGetMsg(a6)
		tst.l	d0
		beq.s	MainLoop		no message, wait again

		movea.l	d0,a2			save message pointer
		move.l	im_Class(a2),d0		search table for this class
		lea.l	Dispatch(pc),a0
DispatchLoop	move.l	(a0)+,d1		get function offset
		beq.s	DispatchDone		unknown message!
		cmp.l	(a0)+,d0		was this the correct one ?
		bne.s	DispatchLoop		no, check the next one

		movea.l	a2,a0			message to a0 for the routine
		jsr	Dispatch(pc,d1.l)	call correct routine
DispatchDone	movea.l	a2,a1			reply the message
		jsr	_LVOReplyMsg(a6)
		tst.w	Closed(a5)		are we finished ?
		bne.s	Exit			yes
		bra.s	NextMessage		no, look for another message

;==============================================================================
; a dispatch table containing an offset to a routine and the IDCMP class
; required for that routine to be called.  Zero terminates the list.
;==============================================================================
Dispatch	DC.L	HandleClick-Dispatch,MOUSEBUTTONS
		DC.L	RefreshWindow-Dispatch,REFRESHWINDOW
		DC.L	ShuffleOnce-Dispatch,INTUITICKS
		DC.L	CloseDown-Dispatch,CLOSEWINDOW
		DC.L	0

;==============================================================================
; Program exits here.
;==============================================================================
Exit		move.l	MainWindow(a5),d0	close window if it opened
		beq.s	Exit_1			no window to close
		movea.l	d0,a0
		movea.l	IntLib(a5),a6		using intuition library
		jsr	_LVOCloseWindow(a6)

Exit_1		lea.l	MemTable(pc),a0		free any allocated memory
		bsr	FreeMemory
		lea.l	Libraries(pc),a0	close any opened libraries
		bsr	CloseLibs
		lea.l	globals_SIZEOF(sp),sp	free stack space
		moveq.l	#0,d0			and do a non error return
		rts


;==============================================================================
; CloseDown()
;
; Just sets the Closed state variable so we exit the program
;==============================================================================
CloseDown	move.w	#-1,Closed(a5)
		rts

;==============================================================================
; RefreshWindow( firsttime )
;		    d0
;
; Completely redraws everything in the window, border, tiles and such.  If
; firsttime == 0 then the Begin and End refresh calls are not made.
;==============================================================================
RefreshWindow	move.l	a6,-(sp)
		move.l	d0,-(sp)		is this first time
		beq.s	10$			yes, dont beginrefresh
		movea.l	IntLib(a5),a6		using intuition
		movea.l	MainWindow(a5),a0	begin refresh sequence
		jsr	_LVOBeginRefresh(a6)	to fix up layers

10$		bsr	DrawBorder		do the border outline
		bsr	DrawAllTiles		and all the tiles
		bsr	ShuffleCount		and the score area
		move.l	(sp)+,d0		was this first time ?
		beq.s	20$			yes, don't end refresh

		movea.l	MainWindow(a5),a0
		jsr	_LVOEndRefresh(a6)	refresh done
20$		move.l	(sp)+,a6
		rts

;==============================================================================
; HandleClick( message )
;		 a0
;
; Moves a tile based on the position of the given MOUSEBUTTONS message.
; Left mouse button down will attempt to move the tiles
; Left mouse button up will do nothing
; Right mouse button down will start shuffling the tiles (switch on INTUITICKS)
; Right mouse button up will stop shuffling the tiles (switch off INTUITICKS)
;==============================================================================
HandleClick	movem.l	a2/a6,-(sp)
		movea.l	a0,a2			stash message address
		move.w	im_Code(a2),d1		check for left or right button
		cmpi.w	#IECODE_LBUTTON!IECODE_UP_PREFIX,d1
		beq.s	ClickDone		nothing for left button up
		cmpi.w	#IECODE_LBUTTON,d1
		beq.s	HandleLeft

; it was a right button click, if down turn on INTUITICKS in the IDCMP port
		move.l	#IDCMPFLAGS!INTUITICKS,d0	assume down
		cmpi.w	#IECODE_RBUTTON!IECODE_UP_PREFIX,d1
		bne.s	10$			assumption correct
		move.l	#IDCMPFLAGS,d0		stop shuffling now
10$		movea.l	IntLib(a5),a6		using intuition
		movea.l	MainWindow(a5),a0
		jsr	_LVOModifyIDCMP(a6)	set new IDCMP values
		bra.s	ClickDone

; user clicked on a tile, move it if the chosen tile was a legal selection
HandleLeft	movem.w	im_MouseX(a2),d0/d1	convert d0/d1 to tile x,y
		subi.w	#9,d0			allow for border
		bmi.s	ClickError		out of range
		ext.l	d0
		divs	#35,d0
		cmpi.w	#4,d0
		bge.s	ClickError

		subi.w	#39,d1			allow for offset to grid
		bmi.s	ClickError		out of range
		ext.l	d1
		divs	#17,d1
		cmpi.w	#4,d1
		bge.s	ClickError

		bsr	MoveTiles		move correct tiles
		subq.l	#1,Shuffles(a5)		subtract from shuffle count
		bsr	ShuffleCount		and update it on the screen
		bra.s	ClickDone

ClickError	bsr	Bleep			bleep display for error

ClickDone	movem.l	(sp)+,a2/a6
		rts

;==============================================================================
; ShuffleOnce()
;
; Performs one shuffle on the tiles and updates the display to reflect changes
;==============================================================================
ShuffleOnce	moveq.l	#3,d0			figure which tile to move
		bsr	Random
		move.w	d0,-(sp)		and save it
		moveq.l	#1,d0			now see if it's a row or col
		and.l	Shuffles(a5),d0		columns on even iterations
		beq.s	ShuffleColumn		we want to move up or down

; execute this code when we are sliding the tiles left or right into the space
; the row number will be the same as the space but the column number can't be.
		move.w	SpaceY(a5),d1		tile is in this row
		move.w	(sp)+,d0		get the column number
		cmp.w	SpaceX(a5),d0		is it the same
		bne.s	DoShuffle		no, so it's valid now
		addq.w	#1,d0			yes, so bump to the next tile
		bra.s	DoShuffle		go move it

; execute this code when we are sliding the tiles up or down into the space
; the column number will be the same as the space but the row number can't be.
ShuffleColumn	move.w	SpaceX(a5),d0		tile is in this column
		move.w	(sp)+,d1		get row number
		cmp.w	SpaceY(a5),d1		is it the same
		bne.s	DoShuffle		no, so it's valid now
		addq.w	#1,d1			yes, so bump to the next tile

; we have chosen the tile we want to move, update shuffle count and then call
; MoveTiles to move 1 or more tiles in the right direction (into the space)
DoShuffle	bsr	MoveTiles		move relevant tiles
		addq.l	#1,Shuffles(a5)		add one to number of shuffles
		bsr	ShuffleCount		print current shufflecount
		rts

;==============================================================================
; Bleep()
;
; Just does a DisplayBeep on the screen this window is in
;==============================================================================
Bleep		move.l	a6,-(sp)
		movea.l	IntLib(a5),a6		using intuition
		movea.l	MainWindow(a5),a0	find the screen to beep
		movea.l	wd_WScreen(a0),a0
		jsr	_LVODisplayBeep(a6)	do the flash
		move.l	(sp)+,a6		and that's all
		rts

;==============================================================================
; ShuffleCount()
;
; Displays current number of shuffles in the top portion of the screen.
;==============================================================================
ShuffleCount	movem.l	d2/d3/a6,-(sp)
		movea.l	GfxLib(a5),a6		using graphics library

		movea.l	TheRastPort(a5),a1	set pen to background color
		moveq.l	#0,d0
		jsr	_LVOSetAPen(a6)

		moveq.l	#10,d0			erase the score area
		moveq.l	#13,d1
		moveq.l	#80,d2
		moveq.l	#22,d3
		movea.l	TheRastPort(a5),a1
		jsr	_LVORectFill(a6)

		moveq.l	#15,d0			move to place for score
		moveq.l	#20,d1
		movea.l	TheRastPort(a5),a1
		jsr	_LVOMove(a6)

		movea.l	TheRastPort(a5),a1	set pen to black
		moveq.l	#2,d0
		jsr	_LVOSetAPen(a6)

		movea.l	TheRastPort(a5),a1	and drawmode to jam1
		moveq.l	#RP_JAM1,d0
		jsr	_LVOSetDrMd(a6)

		movea.l	TextBuff(a5),a0
		move.l	Shuffles(a5),d0		convert shuffles to ascii
		bsr	bin2asc32		returns d0 = length
		move.w	d0,-(sp)		save length for later
		movea.l	TextBuff(a5),a0
		movea.l	TheRastPort(a5),a1
		jsr	_LVOText(a6)		write the text out

		movea.l	TheRastPort(a5),a1	set pen to white now
		moveq.l	#1,d0
		jsr	_LVOSetAPen(a6)

		moveq.l	#13,d0			move to place for score
		moveq.l	#19,d1			offset it 2 in x and 1 in y
		movea.l	TheRastPort(a5),a1
		jsr	_LVOMove(a6)

		move.w	(sp)+,d0		get back the text length
		movea.l	TextBuff(a5),a0		and the text buffer
		movea.l	TheRastPort(a5),a1
		jsr	_LVOText(a6)		write the text out

		movem.l	(sp)+,d2/d3/a6
		rts

;=============================================================================
; nextplace = bin2asc[nn]( value,buffer )
;   d0			     d0    a0
;
; converts the given binary value to a null terminated string of ascii digits
; in the buffer provided.  [nn] can be 08,16 or 32 to handle different sizes.
;============================================================================
bin2asc08	ext.w	d0			make bytes into words
bin2asc16	ext.l	d0			make words into a longwords
bin2asc32	movem.l	d2/a2,-(sp)
		movea.l	a0,a2			save buffer pointer
		tst.l	d0			is number negative ?
		bpl.s	bin2asc			nope, call main routine
		neg.l	d0			yes, so make it positive
		move.b	#'-',(a0)+		and output a minus sign

bin2asc		lea.l	PowersOfTen(pc),a1	point to powers of ten table

; first we quickly check for the first real digit we will be using (non 0)
10$		move.l	(a1)+,d2		get next power of ten
		beq.s	40$			value was zero to start
		cmp.l	d2,d0			is the number this big ?
		blt.s	10$			no, keep searching

		moveq.l	#'0',d1			current digit value
20$		addq.w	#1,d1			update this digit
		sub.l	d2,d0			subtract current pwr of 10
30$		cmp.l	d2,d0			can we do it again ?
		bge.s	20$			yes, keep going
		move.b	d1,(a0)+		no, save the current digit
		moveq.l	#'0',d1			digit value back to '0' now
		move.l	(a1)+,d2		get next power of 10
		bne.s	30$			and check against number
		bra.s	50$			all done. terminate string

40$		move.b	#'0',(a0)+		store an ascii 0
50$		clr.b	(a0)			terminate the string
		move.l	a0,d0			compute length
		sub.l	a2,d0
		movem.l	(sp)+,d2/a2		and return
		rts

PowersOfTen	DC.L	1000000000,100000000,10000000,1000000
		DC.L	100000,10000,1000,100,10,1,0


;==============================================================================
; MoveTiles( xtile, ytile )
;	      d0     d1
;
; Given a tile that was clicked on or a tile that was chosen by ShuffleOnce,
; moves the appropriate number of tiles towards the space and renders them.
;==============================================================================
MoveTiles	movem.l	d2-d3,-(sp)
		cmp.w	SpaceX(a5),d0		consistency check
		bne.s	Check2			only row or column...
		cmp.w	SpaceY(a5),d1		...can be the same...
		beq	MoveError		...as the space
		bra.s	MovingColumn		tiles are sliding up or down

Check2		cmp.w	SpaceY(a5),d1		but at least one of them...
		bne.s	MoveError		...must be the same

; executes this code when we are sliding the tiles along a row into the space
		moveq.l	#tile_SIZEOF,d2		array increment when moving
		move.w	d0,d3			see how many to move
		sub.w	SpaceX(a5),d3
		bpl.s	DoMove
		neg.w	d2			have to go backwards
		neg.w	d3
		bra.s	DoMove

; executes this code when we are sliding the tiles up or down into the space
MovingColumn	moveq.l	#4*tile_SIZEOF,d2	array increment when moving
		move.w	d1,d3			see how many tiles to move
		sub.w	SpaceY(a5),d3
		bpl.s	DoMove
		neg.w	d2			have to go backwards
		neg.w	d3

; gets here with d2 containing the array increment for moving from one tile
; to the next and d3 containing the number of tiles affected ( 1 to 3 tiles)
; first, we set up a0 pointing to the tile that contains the space.
DoMove		movem.w	d0/d1,-(sp)		save tile that becomes space
		movem.w	SpaceX(a5),d0/d1	we start from the space
		mulu.w	#tile_SIZEOF,d0		get tile x array position
		mulu.w	#4*tile_SIZEOF,d1	get tile y array position
		add.l	d1,d0			d0 = offset into array
		movea.l	GameArray(a5),a0
		adda.l	d0,a0			a0 points to the space tile
		subq.w	#1,d3			fix up count for dbra

MoveNextTile	move.b	t_type(a0,d2.w),t_type(a0)	slide a tile into space
		move.w	#1,t_moved(a0)		mark this one as changed
		lea.l	0(a0,d2.w),a0		move to the other tile
		dbra	d3,MoveNextTile		and carry on if more
		clr.b	t_type(a0)		now mark this one as the space
		move.w	#1,t_moved(a0)		and mark it as moved

		move.l	(sp)+,SpaceX(a5)	set up the new space
		bsr	DrawMovedTiles		re-draw the ones that moved
		bra.s	MoveComplete
MoveError	bsr	Bleep			beep display for errors
MoveComplete	movem.l	(sp)+,d2-d3
		rts

;==============================================================================
; DrawBorder()
;
; Draws the border around the playing area.
;==============================================================================
DrawBorder	movem.l	d2-d6,-(sp)
		movea.l	MainWindow(a5),a0	get pointer to a window
		moveq.l	#0,d0			x start
		move.w	wd_Height(a0),d1	y start
		subi.w	#75,d1
		move.w	wd_Width(a0),d2		width
		moveq.l	#75,d3			height
		moveq.l	#1,d4			border color
		moveq.l	#0,d5			middle color
		moveq.l	#3,d6			border width
		bsr	SpecialBox
		movem.l	(sp)+,d2-d6
		rts

;==============================================================================
; InitTiles()
;
; Initialises the tile array with tile positions and letters to go in them.
;==============================================================================
InitTiles	movem.l	d2-d3/a2,-(sp)
		movea.l	GameArray(a5),a0	a0 points to tile data
		lea.l	15*tile_SIZEOF(a0),a0	point to the last tile
		lea.l	Letters(pc),a1		a1 points to current letter
		movea.l	MainWindow(a5),a2
		moveq.l	#3,d3			row counter
		move.w	d3,SpaceX(a5)		this is where the space is
		move.w	d3,SpaceY(a5)		when we initialise
NextRow		moveq.l	#3,d2			column counter
NextColumn	move.w	d2,d0			x pos = col*35+9
		mulu.w	#35,d0
		addi.w	#9,d0
		move.w	d0,t_x(a0)
		move.w	d3,d0			y pos = row*17+4
		mulu.w	#17,d0
		addq.w	#4,d0
		subi.w	#75,d0			ypos += wd_Height-75
		add.w	wd_Height(a2),d0
		move.w	d0,t_y(a0)
		move.b	(a1)+,t_type(a0)	fill in the letter
		lea.l	-tile_SIZEOF(a0),a0	point to previous tile
		dbra	d2,NextColumn
		dbra	d3,NextRow
		movem.l	(sp)+,d2-d3/a2
		rts

;==============================================================================
; DrawAllTiles()
;
; refreshes tile display by marking all tiles as moved and calls DrawMovedTiles
;==============================================================================
DrawAllTiles	movea.l	GameArray(a5),a0	get to first tile
		moveq.l	#15,d0			doing 16 of them
10$		move.w	#1,t_moved(a0)		mark this one as moved
		lea.l	tile_SIZEOF(a0),a0	move to next tile
		dbra	d0,10$			drop through to DrawMovedTiles

;==============================================================================
; DrawMovedTiles()
;
; Redraws any tiles that have a non-zero t_moved field and then clears it.
;==============================================================================
DrawMovedTiles	movem.l	d2/a2,-(sp)
		movea.l	GameArray(a5),a2	point to first tile
		moveq.l	#15,d2			have to check all of them
10$		tst.w	t_moved(a2)		was this tile moved
		beq.s	20$			no, so no work
		movea.l	a2,a0			yes, so draw it
		bsr.s	DrawTile
		clr.w	t_moved(a2)		mark as not moved now
20$		lea.l	tile_SIZEOF(a2),a2	point to next tile
		dbra	d2,10$			and carry on
		movem.l	(sp)+,d2/a2
		rts

;==============================================================================
; DrawTile( tile )
;	     a0
;
; Draws a tile based on its array position and the letter assigned to that tile
;==============================================================================
DrawTile	movem.l	d2-d6/a2/a6,-(sp)
		movea.l	a0,a2			stash tile address
		movem.w	t_x(a2),d0/d1		get tile start position
		moveq.l	#32,d2			tile is this wide
		moveq.l	#16,d3			and this high
		moveq.l	#3,d4			assume a colored tile
		moveq.l	#2,d5
		moveq.l	#1,d6			border width is 1
		tst.b	t_type(a2)		is this the blank tile ?
		bne.s	10$			no
		moveq.l	#0,d4			just do background color...
		moveq.l	#0,d5			...for the blank tile
10$		bsr	SpecialBox
		tst.b	t_type(a2)		is this the blank tile ?
		beq.s	TileDrawn		yes, no more work to do

		movea.l	GfxLib(a5),a6		using graphics library
		moveq.l	#1,d0			set pen to white
		movea.l	TheRastPort(a5),a1
		jsr	_LVOSetAPen(a6)

		moveq.l	#RP_JAM1,d0		only jam 1 color for text
		movea.l	TheRastPort(a5),a1
		jsr	_LVOSetDrMd(a6)

		movem.w	t_x(a2),d0/d1		get position to move to
		addi.w	#12,d0			add offset for letters
		addi.w	#10,d1
		jsr	_LVOMove(a6)

		lea.l	t_type(a2),a0		address of string
		movea.l	TheRastPort(a5),a1	to this rastport
		moveq.l	#1,d0			drawing 1 character
		jsr	_LVOText(a6)

TileDrawn	movem.l	(sp)+,d2-d6/a2/a6
		rts

;==============================================================================
; SpecialBox( left, top, width, height, bordercolor, boxcolor, thickness)
;	       d0    d1	   d2	  d3	    d4		d5	  d6
;
; Draws a filled in box with a border of the required thickness using the
; specified colors.  The thickness is doubled in the x direction so that
; the border is the same physical width all the way around.  As with the
; RectFill function, xmin<=xmax and ymin<=ymax.
;==============================================================================
SpecialBox	movem.l	d2-d3/d6/a6,-(sp)
		movea.l	GfxLib(a5),a6		using graphics library
		subq.w	#1,d2			convert width and height...
		subq.w	#1,d3			...to absolute co-ordinates
		add.w	d0,d2
		add.w	d1,d3
		movem.w	d0-d3,-(sp)		save the box co-ordinates

		movea.l	TheRastPort(a5),a1
		move.w	d4,d0			set pen to border color
		jsr	_LVOSetAPen(a6)
		movem.w	(sp),d0-d3		get box parameters
		movea.l	TheRastPort(a5),a1	and draw it
		jsr	_LVORectFill(a6)

		movea.l	TheRastPort(a5),a1
		move.w	d5,d0			set pen to middle color
		jsr	_LVOSetAPen(a6)
		movem.w	(sp)+,d0-d3		get old box co-ordinates
		add.w	d6,d1			ymin += thickness
		sub.w	d6,d3			ymax -= thickness
		asl.w	#1,d6			double thickness in x
		add.w	d6,d0			xmin += thickness*2
		sub.w	d6,d2			xmax -= thickness*2
		movea.l	TheRastPort(a5),a1	and draw it
		jsr	_LVORectFill(a6)

		movem.l	(sp)+,d2-d3/d6/a6
		rts

;==============================================================================
; RndNum = Random( UpperLimit )
;   d0		      d0
;
; returns a random integer in the range 0 to UpperLimit-1
;============================================================================
Random		move.w	d0,-(sp)	save range
		beq.s	10$		range of 0 returns 0 always
		bsr.s	LongRnd		get a longword random number
		clr.w	d0		use upper word (it's most random)
		swap	d0
		divu.w	(sp),d0		divide by range...
		clr.w	d0
		swap	d0		...and use remainder for the result
10$		addq.l	#2,sp		scrap range on stack
		rts

; this is the main random number generation routine. Not user callable
LongRnd		movem.l	d2-d3,-(sp)	
		movem.l	RND(a5),d0/d1	D0=LSB's, D1=MSB's of random number
		andi.b	#$0e,d0		ensure upper 59 bits are an...
		ori.b	#$20,d0		...odd binary number
		move.l	d0,d2
		move.l	d1,d3
		add.l	d2,d2		accounts for 1 of 17 left shifts
		addx.l	d3,d3		[D2/D3] = RND*2
		add.l	d2,d0
		addx.l	d3,d1		[D0/D1] = RND*3
		swap	d3		shift [D2/D3] additional 16 times
		swap	d2
		move.w	d2,d3
		clr.w	d2
		add.l	d2,d0		add to [D0/D1]
		addx.l	d3,d1
		movem.l	d0/d1,RND(a5)	save for next time through
		move.l	d1,d0		most random part to D0
		movem.l	(sp)+,d2-d3
		rts

;==============================================================================
; Success = OpenLibs( libtable )
;   d0			 a0
;==============================================================================
OpenLibs	movem.l	a2-a3/a6,-(sp)
		movea.l	SysLib(a5),a6		using exec library
		movea.l	a0,a2			save working libtable ptr
		movea.l	a0,a3			a3 used to calculate name addr

10$		moveq.l	#0,d0			get the version
		move.w	(a2)+,d0
		bmi.s	LibsOpened		return TRUE
		move.w	(a2)+,d1		get offset to name
		lea.l	0(a3,d1.w),a1		addr of name in a1
		jsr	_LVOOpenLibrary(a6)	open the lib
		move.w	(a2)+,d1		offset to store at
		move.l	d0,0(a5,d1.w)		save in global table
		bne.s	10$			OK, go for the next
LibsOpened	movem.l	(sp)+,a2-a3/a6		could return 0 if failed above
		rts		

;==============================================================================
; CloseLibs( libtable )
;		a0
;==============================================================================
CloseLibs	movem.l	a2/a6,-(sp)
		movea.l	SysLib(a5),a6		using exec library
		movea.l	a0,a2			save libtable pointer

10$		tst.w	(a2)+			end of list ?
		bmi.s	LibsClosed		yes, quit now
		move.l	(a2)+,d0		offset to lower word
		move.l	0(a5,d0.w),d0		get lib pointer
		beq.s	LibsClosed		that's it, this one failed
		movea.l	d0,a1
		jsr	_LVOCloseLibrary(a6)	close this library
		bra.s	10$			and go for the next
LibsClosed	movem.l	(sp)+,a2/a6
		rts

;==============================================================================
; Success = AllocMemory( memtable )
;   d0			    a0
;==============================================================================
AllocMemory	movem.l	a2/a6,-(sp)
		movea.l	SysLib(a5),a6		using exec library
		movea.l	a0,a2			stash memtable address

10$		move.l	(a2)+,d0		get size
		bmi.s	MemAlloced		last entry, return true
		moveq.l	#0,d1
		move.w	(a2)+,d1		get type
		bset.l	#MEMB_CLEAR,d1		clear it too
		jsr	_LVOAllocMem(a6)	allocate it
		move.w	(a2)+,d1		get store offset
		move.l	d0,0(a5,d1.w)		save it
		bne.s	10$			OK, memory fetched
MemAlloced	movem.l	(sp)+,a2/a6		could return 0 if it failed
		rts

;==============================================================================
; FreeMemory( memtable )
;		 a0
;==============================================================================
FreeMemory	movem.l	a2/a6,-(sp)
		movea.l	SysLib(a5),a6		using exec library
		movea.l	a0,a2			stash table pointer

10$		move.l	(a2)+,d0		end of list
		bmi.s	MemFreed		yep, exit now
		move.l	(a2)+,d1		offset to lower word
		move.l	0(a5,d1.w),d1		get pointer
		beq.s	MemFreed		quit! alloc failed on this one
		movea.l	d1,a1
		jsr	_LVOFreeMem(a6)
		bra.s	10$
MemFreed	movem.l	(sp)+,a2/a6
		rts

;==============================================================================
; List of libraries required to run this little demonstration program
;==============================================================================
Libraries	DC.W	33,IntName-Libraries,IntLib
		DC.W	33,GfxName-Libraries,GfxLib
		DC.W	33,DosName-Libraries,DosLib
		DC.W	-1
IntName		DC.B	'intuition.library',0
		CNOP	0,2
GfxName		DC.B	'graphics.library',0
		CNOP	0,2
DosName		DC.B	'dos.library',0
		CNOP	0,2

;==============================================================================
; list of memory requirements to run the demonstration program
;==============================================================================
MemTable	DC.L	16*tile_SIZEOF,(MEMF_PUBLIC<<16)+GameArray
		DC.L	80,(MEMF_PUBLIC<<16)+TextBuff
		DC.L	-1

;==============================================================================
; The new window definition for the window that this program will run in
;==============================================================================
WINDOWFLAGS SET WINDOWDEPTH!WINDOWCLOSE!WINDOWDRAG!SIMPLE_REFRESH!ACTIVATE
WINDOWFLAGS SET WINDOWFLAGS!RMBTRAP

IDCMPFLAGS EQU CLOSEWINDOW!REFRESHWINDOW!MOUSEBUTTONS

MyWindow	DC.W	0,0,155,110	left,top,width,height
		DC.B	2,1		detailpen,blockpen
		DC.L	IDCMPFLAGS
		DC.L 	WINDOWFLAGS
		DC.L	0		Gadgets-MyWindow
		DC.L	0		no checkmark
		DC.L	Title-MyWindow	relative ref to title
		DC.L	0		no screen
		DC.L	0		no bitmap
		DC.W	0,0,0,0		no min or max sizes
		DC.W	WBENCHSCREEN	where this window lives

Title		DC.B	'Slider    ',0
		CNOP	0,2
Letters		DC.B	0,'ONMLKJIHGFEDCBA'	easier init like this
		CNOP	0,2

		END

