;
; hour.s
;
; Handling of the hourglass (MDW)
;
;  1994-1998 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Sapphire library.
;
; Sapphire is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; Sapphire is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Sapphire.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;----- Here is Wisdom -------------------------------------------------------;
; The standard Hourglass system is OK but for one small problem: you can't
; stop the thing and restart where you left off.  So, we put a wrapper round
; it so that we can do this.  This involves keeping our own record of
; exactly what's going on.
;
; This module is designed to be very small and unobtrusive, so you can have
; dependencies on it all over without much hassle.  The only external
; dependency is on except, and wimp includes that anyway, so it's not really
; a great problem.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

;----- External dependencies ------------------------------------------------

		GET	sapphire:except
		GET	sapphire:sapphire

;----- Main code ------------------------------------------------------------

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- hour_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the hour system, so it will display an hourglass
;		when necessary.
;
;		Since this gets called at a random point during the Sapphire
;		initialisation, and we can rely on Hourglass keeping its
;		own count, the suggested way of handling everything properly
;		is as follows:
;
;				SWI	Hourglass_On
;				BL	sapphire_init
;				SWI	Hourglass_Off

		EXPORT	hour_init
hour_init	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers
		WSPACE	hour__wSpace		;Locate my workspace address

		; --- Make sure I haven't already done this ---

		LDR	R14,hour__flags		;Find my flags word nicely
		TST	R14,#hFlag__inited	;Am I initialised yet?
		LDMNEFD	R13!,{R12,PC}^		;Yes -- return right now

		; --- Set up my workspace properly ---

		STMFD	R13!,{R0-R2}		;Save some more registers
		MOV	R0,#hFlag__inited	;Say I'm initialised
		MOV	R1,#0			;No Hourglass count yet
		MOV	R2,#255			;No other status stuff either
		STMIA	R12,{R0-R2}		;Save the stuff in workspace

		; --- Register my atexit handler ---

		BL	except_init		;Make sure except is awake
		ADR	R0,hour__exit		;Point to the handler
		MOV	R1,R12			;Pass my workspace along
		BL	except_atExit		;Make sure I can tidy up

		LDMFD	R13!,{R0-R2,R12,PC}^	;Return to caller

		LTORG

hour__wSpace	DCD	0

; --- hour__exit ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Kills off the Hourglass if it's displaying when the program
;		finally quits.

hour__exit	ROUT

		STMFD	R13!,{R14}		;Store the return address
		LDR	R14,hour__count		;Find my Hourglass counter
		CMP	R14,#0			;Is it not displaying?
		SWIGT	Hourglass_Off		;No -- turn it off then
		LDMFD	R13!,{PC}^		;Return to caller finally

		LTORG

; --- hour_on ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Turns the Hourglass on only if it isn't on already.
;		Otherwise its status is left as it was.

		EXPORT	hour_on
hour_on		ROUT

		STMFD	R13!,{R12,R14}		;Save a couple of registers
		WSPACE	hour__wSpace		;Load my workspace pointer
		LDR	R14,hour__count		;Find my Hourglass counter
		ADD	R14,R14,#1		;Bump the counter on one
		STR	R14,hour__count		;Store it back again
		CMP	R14,#1			;Has it just been turned on?
		SWIEQ	Hourglass_On		;Yes -- turn it on then
		LDMFD	R13!,{R12,PC}^		;Return to caller finally

		LTORG

; --- hour_off ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Turns the Hourglass off if it's only been turned on once.
;		If the Hourglass gets turned off, all the information about
;		it (percentage and LEDs) get forgotten.

		EXPORT	hour_off
hour_off	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers
		WSPACE	hour__wSpace		;Load my workspace pointer
		LDR	R14,hour__count		;Find my Hourglass counter
		SUBS	R14,R14,#1		;Decrement it nicely
		MOVLT	R14,#0			;If negative, keep at zero
		STR	R14,hour__count		;Store counter back anyway
		STRLE	R14,hour__percent	;If turning off, zero percent
		SWILE	Hourglass_Off		;and turn Hourglass off too
		LDMFD	R13!,{R12,PC}^		;Return to caller

		LTORG

; --- hour_percent ---
;
; On entry:	R0 == percentage value to display, or -1 to remove
;
; On exit:	--
;
; Use:		Attaches a percentage display to the Hourglass.

		EXPORT	hour_percent
hour_percent	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers away
		WSPACE	hour__wSpace		;Find my workspace address
		LDR	R14,hour__count		;Get the counter out
		CMP	R14,#0			;Has it been turned on yet?
		STRGTB	R0,hour__percent	;Yes -- store the percentage
		SWIGT	Hourglass_Percentage	;And display it properly
		LDMFD	R13!,{R12,PC}^		;Return to caller

		LTORG

; --- hour_leds ---
;
; On entry:	R0 == LED mask EOR value
;		R1 == LED mask AND value
;
; On exit:	--
;
; Use:		Changes the Hourglass LED status.

		EXPORT	hour_leds
hour_leds	STMFD	R13!,{R0,R1,R12,R14}	;Save some registers away
		WSPACE	hour__wSpace		;Find my workspace address
		LDR	R14,hour__count		;Get the counter out
		CMP	R14,#0			;Has it been turned on yet?
		LDMLEFD	R13!,{R0,R1,R12,PC}^	;No -- just return then
		LDR	R14,hour__leds		;Get the current LED state
		AND	R14,R14,R1		;Apply the AND mask to it
		EOR	R0,R14,R0		;Apply the EOR mask to it
		LDR	R0,hour__leds		;Store the status back again
		MOV	R1,#0			;Clear all LED bits
		SWI	Hourglass_LEDs		;Now set the actual LEDs
		LDMFD	R13!,{R0,R1,R12,PC}^	;Return to caller

		LTORG

; --- hour_suspend ---
;
; On entry:	R0 == pointer to 2 word block to save status in
;
; On exit:	--
;
; Use:		Saves the Hourglass state in a block you've pointed at,
;		and disables the Hourglass.  Useful if you want to do some
;		user interaction without polling (e.g. an error box).

		EXPORT	hour_suspend
hour_suspend	ROUT

		STMFD	R13!,{R0-R2,R12,R14}	;Save a bunch of registers
		WSPACE	hour__wSpace		;Load my workspace address
		LDMIB	R12,{R1,R2}		;Load the Hourglass state
		STMIA	R0,{R1,R2}		;Save it in caller's block
		CMP	R1,#0			;Is the Hourglass on ATM?
		SWIGT	Hourglass_Off		;Yes -- turn it off then
		MOV	R1,#0			;Zero the counter
		MOV	R2,#255			;And the other status bits
		STMIB	R12,{R1,R2}		;Save over my old state
		LDMFD	R13!,{R0-R2,R12,PC}^	;Return to caller nicely

		LTORG

; --- hour_save ---
;
; On entry:	R0 == pointer to 2 word block to save status in
;
; On exit:	--
;
; Use:		Saves the current Hourglass status without altering it.

		EXPORT	hour_save
hour_save	ROUT

		STMFD	R13!,{R0-R2,R12,R14}	;Save a bunch of registers
		WSPACE	hour__wSpace		;Load my workspace address
		LDMIB	R12,{R1,R2}		;Load the Hourglass state
		STMIA	R0,{R1,R2}		;Save it in caller's block
		LDMFD	R13!,{R0-R2,R12,PC}^	;Return to caller finally

		LTORG

; --- hour_resume, hour_restore ---
;
; On entry:	R0 == pointer to 2 words filled by hour_suspend or hour_save
;
; On exit:	--
;
; Use:		Restores the Hourglass state to that saved away by one
;		of the previous two calls.  This routine has two names.

		EXPORT	hour_resume
		EXPORT	hour_restore
hour_resume
hour_restore	ROUT

		STMFD	R13!,{R0,R1,R12,R14}	;Save some registers away
		WSPACE	hour__wSpace		;Load my workspace address
		LDR	R14,hour__count		;Load the current counter

		; --- Restore the workspace contents ---

		LDMIA	R0,{R0,R1}		;Load the saved status
		CMP	R0,#0			;Is the Hourglass on?
		MOVLT	R0,#0			;If negative, restore to 0
		MOVLE	R1,#255			;If off, force percent off
		STMIB	R12,{R0,R1}		;Save in my workspace

		; --- Now restore the Hourglass onness ---

		MOVGT	R0,#1			;If now on, store as 1
		CMP	R14,#0			;Is the old state on?
		MOVLT	R14,#0			;Force result nonnegative
		MOVGT	R14,#1			;If was on, store as 1
		CMP	R0,R14			;Are the states the same?
		SWIGT	Hourglass_On		;If now on, was off, enable
		SWILT	Hourglass_Off		;If now off, was on, disable
		CMP	R0,#0			;Is it now off?
		LDMEQFD	R13!,{R0,R1,R12,PC}^	;Yes -- return to caller

		; --- Now restore other Hourglass bits ---

		AND	R0,R1,#&FF		;Get the percentage state
		SWI	Hourglass_Percentage	;Send that to the Hourglass
		AND	R0,R1,#&FF00		;Get the LED state too
		MOV	R0,R0,LSR #8		;Shift down to bottom of R0
		MOV	R1,#0			;Don't leave old state there
		SWI	Hourglass_LEDs		;And restore LED state
		LDMFD	R13!,{R0,R1,R12,PC}^	;Return, we did everything

		LTORG

;----- Workspace ------------------------------------------------------------

		^	0,R12
hour__wStart	#	0

hour__flags	#	4			;A flags word (see later)
hour__count	#	4			;no(hour_on) - no(hour_off)
hour__percent	#	1			;Current percentage displayed
hour__leds	#	1			;Current LED status
hour__padding	#	2			;Pad size to word boundary

hour__wSize	EQU	{VAR}-hour__wStart

hFlag__inited	EQU	(1<<0)			;Am I initialised yet?

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	hour__wSize
		DCD	hour__wSpace
		DCD	0
		DCD	hour_init

;----- That's all, folks ----------------------------------------------------

		END
