*****************************************************************************
*
* AUTHOR	: Max Bithead
* DATE	: 2-06-91
*
* NAME	: Blankette (very tiny screen blanker, hence "blankette")
*
* DESC.	: Blankette simply dims the screen when the user is away.  This
*	  avoids phospher burns in your monitor that may occur if the screen
*	  displays a bright image for an extended period of time.
*
*	  To accomplish the screen "dimming", the color values are simply
*	  halved on the currently active viewport.
*
*	  Blankette uses very little memory (under 2k) and very little CPU
*	  time, thus making it a practical screen blanker.
*
*	  Blankette must be started under CLI by typing:
*
*		run >nil: Blankette [seconds of inactivity before blank]
*
*	  You may use any value you wish for the delay period.  If Blankette
*	  is started by just typing "Blankette n", it will be attached to
*	  the CLI.  Hit Ctrl-C to cancel it.
*
*	  Blankette first cancels any other copies of itself that may be
*	  running, so if you want a new delay period, simply restart
*	  Blankette.  To remove Blankette completely, simply type
*	  "Blankette" without any parameters, or "Blankette 0".
*
*	  If you are running a program that is a real CPU hog, never waits
*	  on any event, and never does any I/O, then the chances are 
*	  Blankette won't get a chance to run.  This is due to the low 
*	  priority at which Blankette operates.  If Blankette wasn't at
*	  such a low priority, system performance would suffer.  I don't
*	  want that on my system.  If I need to walk away while a CPU 
*	  intensive program is running, shutting off the monitor works
*	  nicely to preserve it's life.  Blankette will otherwise kick in
*	  during normal system activity.
*
*	  A word about HAM:  Normally, halving the color values of a HAM
*	  picture does little or no good, such is the nature of Hold And
*	  Modify mode.  To get around this, Blankette turns HAM mode off
*	  to dim the screen, and back on again when it detects user input.
*
*	  Blankette is sensitive to mouse movement, joystick movement, the
*	  left mouse button, the joystick button, and keyboard input.
*
*
* HAPPY	: So far, Blankette has proven to be compatible with just about
*	  everything!  In fact, I haven't been able to crash the system
*	  by using it. :)  Enjoy!
*
*	  Ooops.  It's possible, by way of the DisplayBeep function, to
*	  get your screen stuck in the halved colors.  To fix this situation,
*	  simply pull up the system preferences.  When the preferences 
*	  window pops up it resets your colors for you.  Happy again.
*
*****************************************************************************
	bra	start

author	dc.b	$0a,"(C)opyright Max Bithead",$0a,"Boulder, Colorado, USA",$0a,"1991",$0a,0

	incdir	"sys:include/"

	include	exec/exec_lib.i
	include	libraries/dosextens.i
	include	graphics/graphics_lib.i
	include	graphics/view.i
	include	exec/nodes.i

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

start
	move.l	a0,a2		Save input parm ptr
	jsr	startup(pc)
	tst.l	d0		If error
	beq.s	.abort1		..abort
	jsr	no_clone(pc)	Only one copy of us at a time
	jsr	parse(pc)
	tst.l	d0
	bne.s	.abort2
	jsr	low_pri(pc)	Put us at very low priority
	jsr	process(pc)	Check input & blank
.abort2
	jsr	cleanup(pc)	Clean up (free resouces, etc.)
.abort1
	rts			Return

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

graph_nam	dc.b	"graphics.library",0
_GfxBase	dc.l	0

	EVEN
startup
	lea	graph_nam(pc),a1	Addr of graphics lib name
	moveq	#33,d0  		Open latest version (1.2 or >)
	move.l	_SysBase,a6	SysBase offset (at addr 4)
	jsr	_LVOOpenLibrary(a6)	Open graphics library
	move.l	d0,_GfxBase
	rts

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

task	dc.l	0		Ptr to our task control block (TCB)
old_name	dc.l	0		Ptr to old task name
task_name	dc.b	"$Max@Blanker",0

	EVEN
no_clone
	lea	task_name(pc),a1	Ptr to name of task to find
	CALLEXEC	FindTask		If (already running blanker)
	tst.l	d0
	beq.s	.ok
	move.l	d0,a1		..Addr of other blanker TCB
	move.l	#$1000,d0		..Send CTRL-C abort signal
	CALLEXEC	Signal		..to other blanker task
.ok
	move.l	#0,a1		Ptr to name of task to find
	CALLEXEC	FindTask		Find addr of our task
	move.l	d0,task		Addr of our task
	move.l	d0,a0
	move.l	LN_NAME(a0),old_name Save ptr to old name
	lea	task_name(pc),a1
	move.l	a1,LN_NAME(a0)	Change our task name
	rts

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

time_val	dc.l	0		(# seconds before blank) * 60

	EVEN
parse
	moveq	#0,d0
	move.b	(a2)+,d0		Get byte of input line
	cmpi.b	#$0d,d0		If CR (eol)
	beq	.done		..end of input line
	cmpi.b	#$0a,d0		If (line feed)
	beq	.done		..end of input line
	cmpi.b	#$30,d0		If (NOT number value)
	blt.s	parse		..ignore it
	cmpi.b	#$3a,d0
	bgt.s	parse
	subi.b	#$30,d0		Else convert to ascii
	move.l	time_val(pc),d1
	mulu	#10,d1
	add.l	d0,d1		Add to time value
	move.l	d1,time_val
	bra.s	parse
.done
	tst.l	time_val		If (no time value)
	beq.s	.error		..error
	move.l	time_val(pc),d1	Get time value
	mulu	#60,d1		Convert to seconds (for 60hz clock)
	move.l	d1,time_val
	moveq	#0,d0		Good return
	rts
.error
	moveq	#1,d0		Error return
	rts

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

low_pri
	move.l	task(pc),a1	Addr of our task
	moveq	#-128,d0		Lowest priority possible
	CALLEXEC	SetTaskPri	Set our priority
	rts

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

ActiView	equ	34	Pointer to currently active View struct
view	ds.l	1	Addr of active View struct
vport	ds.l	1	Addr of active ViewPort struct
cmap	ds.l	1	Addr of active ColorMap struct
ctable	ds.w	32	Save area for color table
vmodes	ds.w	1	Display modes

	EVEN
get_cmap
	move.l	_GfxBase(pc),a0	Addr of graphics library base
	move.l	ActiView(a0),a0	Addr of active View
	move.l	a0,view		Save it
	move.l	v_ViewPort(a0),a0	Addr of ViewPort struct
.loop1
	move.l	a0,vport		Save it
	move.w	vp_Modes(a0),d0
	andi.w	#$2000,d0		WHILE (hidden view)
	beq.s	.gotem
	tst.l	vp_Next(a0)	..IF (no more viewport structs)
	beq.s	.gotem		....BREAK
	move.l	vp_Next(a0),a0	..ELSE (get next viewport)
	bra.s	.loop1		ENDWHILE
.gotem
	move.w	vp_Modes(a0),vmodes	Save ViewPort modes
	move.l	vp_ColorMap(a0),a0	Addr of ColorMap struct
	move.l	a0,cmap		Save it
	move.l	cm_ColorTable(a0),a0  Addr of color values for this view
	lea	ctable(pc),a1	Addr of our color save area
	moveq	#15,d0
.loop2
	move.l	(a0)+,(a1)+	Copy color map
	dbra	d0,.loop2
	rts

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

newctable	ds.w	32

	EVEN
half_colors
	CALLEXEC	Forbid		Don't be changing VP's on us here
	jsr	get_cmap(pc)	Find current view
	lea	ctable(pc),a0	Addr of saved colormap
	move.l	cmap(pc),a1	Addr of ColorMap struct
	move.l	cm_ColorTable(a1),a1  Addr of color values for this view
	lea	newctable(pc),a2	Addr of new color table
	moveq	#31,d0		Dew all colors
.loop
	move.b	(a0)+,d1		1st byte of color value
	andi.b	#$0f,d1		Get just red value (hi nibble xtra)
	lsr.b	#1,d1		halve red value
	move.b	d1,d2
	lsl.w	#8,d2		save in upper byte of d2 word
	move.b	(a0),d1		Get green value
	lsr.b	#1,d1		halve green value
	andi.b	#$f0,d1		just green value
	move.b	d1,d2		save in lower byte of d2 word
	move.b	(a0)+,d1		get blue value
	andi.b	#$0f,d1		just blue value
	lsr.b	#1,d1		halve blue value
	or.b	d1,d2		put with other halved values
	move.w	d2,(a1)+		Put halved color in view color map
	move.w	d2,(a2)+		Remember what we put there
	dbra	d0,.loop
	move.l	view(pc),a0	Addr of active view
	move.l	vport(pc),a1	Addr of view port struct
	andi.w	#$f7ff,vp_Modes(a1)	Make sure HAM is off
	CALLGRAF	MakeVPort		Reset color instructs
	move.l	view(pc),a1	Addr of active view
	CALLGRAF	MrgCop		Merge copper instructions
	move.l	view(pc),a1	Addr of active view
	CALLGRAF	LoadView		Display color changes
	CALLEXEC	Permit		Ok system, go for it
	rts

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

restore_colors
	CALLEXEC	Forbid		Don't be changing VP's on us here
	move.l	_GfxBase(pc),a0	Addr of graphics library base
	move.l	ActiView(a0),a0	Addr of active View
	cmp.l	view(pc),a0	IF (NOT same view that got changed)
	bne.s	.done		..forget it
	move.l	v_ViewPort(a0),a0	Addr of 1st ViewPort struct
.loop
	cmp.l	vport(pc),a0	IF (ViewPort is gone)
	beq.s	.gotem
	tst.l	vp_Next(a0)
	beq.s	.done		..forget it
	move.l	vp_Next(a0),a0
	bra.s	.loop
.gotem
	lea	newctable(pc),a0	Addr of saved new colors
	move.l	cmap(pc),a1	Addr of ColorMap struct
	move.l	cm_ColorTable(a1),a1  Addr of color values for this view
	moveq	#15,d0		Check all colors
.loop1
	cmp.l	(a0)+,(a1)+	If (program has changed its colors)
	bne.s	.done		..abort, don't need to restore 'em
	dbra	d0,.loop1
	lea	ctable(pc),a0	Addr of saved colormap
	move.l	cmap(pc),a1	Addr of ColorMap struct
	move.l	cm_ColorTable(a1),a1  Addr of color values for this view
	moveq	#15,d0		Dew all colors
.loop2
	move.l	(a0)+,(a1)+	Restore colors
	dbra	d0,.loop2
	move.l	view(pc),a0	Addr of active view
	move.l	vport(pc),a1	Addr of view port struct
	move.w	vmodes(pc),d0	Get previous modes
	andi.w	#$0800,d0
	or.w	d0,vp_Modes(a1)	Turn HAM back on if needs to be
	CALLGRAF	MakeVPort		Reset color instructs & display modes
	move.l	view(pc),a1	Addr of active view
	CALLGRAF	MrgCop		Merge copper instructions
	move.l	view(pc),a1	Addr of active view
	CALLGRAF	LoadView		Display color changes
.done
	CALLEXEC	Permit		Ok system, go for it
	rts

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

strt_clock  dc.l	0
screen	dc.b	0	Status of screen colors (0=normal, 1=dim)

	EVEN
process
	jsr	get_clock_60hz(pc)	Get value of 60hz clock
	move.l	d0,strt_clock	Save initial value
.loop
	moveq	#0,d0
	moveq	#0,d1
	CALLEXEC	SetSignal		Get copy of our signals
	andi.l	#$1000,d0		CTRL-C pressed?
	bne.s	.done
	jsr	check_input(pc)	If (user input)
	tst.l	d0
	beq.s	.clock
	tst.b	screen
	beq.s	process		..If (screen dim)
	jsr	restore_colors(pc)	....restore it
	clr.b	screen
	bra.s	process
.clock
	jsr	get_clock_60hz(pc)	Keep checking the clock
	sub.l	strt_clock(pc),d0
	cmp.l	time_val,d0
	blt.s	.loop		If (times up)
	tst.b	screen		..If (screen NOT dim)
	bne.s	process
	jsr	half_colors(pc)	....dim screen
	move.b	#1,screen
	bra.s	process
.done
	tst.b	screen
	beq.s	.done2		..If (screen dim)
	jsr	restore_colors(pc)	....restore it
.done2
	rts

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

mouse	dc.l	0	Current mouse movement value
keyboard	dc.b	0	Current keyboard value
fire	dc.b	0	Current fire/mouse button value

check_input
	move.l	$dff00a,d0	Check mouse & joystick movement
	cmp.l	mouse(pc),d0	If (!same as last check)
	beq.s	.more
	move.l	d0,mouse		..save current position
	bra.s	.input		..return input
.more
	move.b	$bfec01,d0	else if (keyboard input)
	cmp.b	keyboard,d0
	beq.s	.more2
	move.b	d0,keyboard	..save current value
	bra.s	.input		..return input
.more2
	move.b	$bfe001,d0	else if (fire or mouse button)
	andi.b	#$c0,d0
	cmp.b	fire,d0
	beq.s	.more3
	move.b	d0,fire		..save current value
	bra.s	.input		..return input
.more3
	moveq	#0,d0		else return no input
	rts
.input
	moveq	#1,d0
	rts

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

get_clock_60hz
	moveq	#0,d0		Return 60hz clock value (24 bit)
	move.b	$bfea01,d0	Read of this reg latches clock
	lsl.l	#8,d0
	move.b	$bfe901,d0
	lsl.l	#8,d0
	move.b	$bfe801,d0	Read of low byte unlatches clock
	rts

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

cleanup
	move.l	task(pc),a1	Addr of our task
	moveq	#0,d0		Priority
	CALLEXEC	SetTaskPri	Set our priority back to normal
				;This is so we don't accidently
				;leave a shell or cli at low pri
	move.l	task(pc),a1	Addr of our task
	move.l	old_name(pc),d0	Addr of old task name
	move.l	d0,LN_NAME(a1)	Restore old task name
				;Won't completely abort without this
				;if running directly from shell or CLI
	move.l	_GfxBase(pc),a1	Addr of lib base to close
	move.l	_SysBase,a6
	jsr	_LVOCloseLibrary(a6) Close the lib
	rts

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

