;
; event.s
;
; Event handling routines (TMA)
;
;  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.

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

		GET	libs:header
		GET	libs:swis

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

		GET	sapphire:sapphire
		GET	sapphire:suballoc
		GET	sapphire:hour

;----- Macros ---------------------------------------------------------------

		MACRO
		BUFCOPY

		LCLA	counter
counter		SETA	0

		STMFD	R13!,{R0-R6}

	WHILE counter<8

		LDMIA	R9!,{R0-R6,R14}
		STMIA	R8!,{R0-R6,R14}
counter		SETA	counter+1

	WEND

		LDMFD	R13!,{R0-R6}

		MEND

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

		AREA	|Sapphire$$Code|,CODE,READONLY


; --- event__addToList ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R12 value to call routine
;		R2 == pointer to list head to use
;
; On exit:	R0-R1 preserved, unless V set (when R0 points to error)
;
; Use:		Adds a rountine to the given list. Later added
;		routines are called first

event__addToList
		ROUT

		STMFD	R13!,{R0-R1,R14}	;Stack some registers

		; --- Allocate a block ---

		MOV	R0,#list__size		;Size to allocate
		BL	sub_alloc		;Allocate the block
		BVS	%01			;Branch ahead if error

		; --- Fill the block in ---

		LDR	R1,[R2]			;Get the list head
		STR	R1,[R0,#list__next]	;Store in next field
		STR	R0,[R2]			;Store new block at head

		LDMFD	R13,{R1,R2}		;Get parameters
		STMIB	R0,{R1,R2}		;Store them in the block

		; --- And return to user ---

		LDMFD	R13!,{R0-R1,R14}	;Load back link
		BICS	PC,R14,#V_flag		;Return without error

		; --- Barf if an error occured ---

01		ADD	R13,R13,#4		;Skip over R0
		LDMFD	R13!,{R1,R14}		;Branch if error
		ORRS	PC,R14,#V_flag		;Return with error

		LTORG

; --- event_preFilter ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R12 value to call routine
;
; On exit:	May return an error
;
; Use:		Adds a routine to the pre-filter list. Later added
;		routines are called first.

		EXPORT	event_preFilter
event_preFilter	ROUT

		STMFD	R13!,{R2,R10,R14}	;Save some registers

		; --- Be careful not to alter flags ---

		WSPACE	event__wSpace,R10	;Get my workspace pointer
		ADR	R2,event__preList	;Get the pre-list pointer
		BL	event__addToList	;Add the routine to the list
		LDMFD	R13!,{R2,R10,PC}	;Return cunningly

		LTORG

; --- event_fakeHandler ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R12 value to call routine
;
; On exit:	May return an error
;
; Use:		Adds a routine to the fake handler list. Later added
;		routines are called first.

		EXPORT	event_fakeHandler
event_fakeHandler
		ROUT

		STMFD	R13!,{R2,R10,R14}	;Save some registers

		; --- Be careful not to alter flags ---

		WSPACE	event__wSpace,R10	;Get my workspace pointer
		ADR	R2,event__fakeList	;Get the fake-list pointer
		BL	event__addToList	;Add the routine to the list
		LDMFD	R13!,{R2,R10,PC}	;Return cunningly

		LTORG

; --- event_postFilter ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R12 value to call routine
;
; On exit:	May return an error
;
; Use:		Adds a routine to the post-poll list. Later added
;		routines are called first.

		EXPORT	event_postFilter
event_postFilter
		ROUT

		STMFD	R13!,{R2,R10,R14}	;Save some registers

		; --- Be careful not to alter flags ---

		WSPACE	event__wSpace,R10	;Get my workspace pointer
		ADR	R2,event__postList	;Get the post-list pointer
		BL	event__addToList	;Add the routine to the list
		LDMFD	R13!,{R2,R10,PC}	;Return cunningly

		LTORG

; --- event_poll ---
;
; On entry:	R0 == event mask and flags
;		R1 == pointer to block to use
;		R2 == earliest time to return with NULL event
;		R3 == optional pointer to poll word
;
; On exit:	R0 == reason code
;		CS if the event was claimed, CC otherwise
;
; Use:		This call perform a Wimp_Poll, and dispatches events to
;		interested parties.

		EXPORT	event_poll
event_poll	ROUT

		STMFD	R13!,{R8-R10,R12,R14}
		WSPACE	event__wSpace,R10	;Find my workspace

		; --- Run through the pre poll list here ---

		ADDS	R0,R0,#0		;Clear the carry flag
		LDR	R8,event__preList	;Get pre-list pointer
00event_poll	TEQ	R8,#0			;Are we at the end
		BEQ	%05event_poll		;Yes -- branch ahead
		LDMIA	R8,{R8,R9,R12}		;Get list fields
		MOV	R14,PC			;Set up return address
		MOV	PC,R9			;Branch to client routine
		BCC	%00event_poll		;Keep going through list

		; -- If carry is set, jump the Wimp_Poll & fake list ---

		BCS	%19event_poll		;Jump ahead

		; --- If we are faking, do fake like things ---

05event_poll	LDR	R8,event__fakePos	;Get the current fake pos
		CMP	R8,#-1			;Are there any waiting?
		BEQ	%10event_poll		;No -- do the Wimp_Poll
		MOV	R8,R1			;Point to users buffer
		ADR	R9,event__buffer	;Point to my buffer
		BUFCOPY				;Copy the event
		LDR	R0,event__prevR0	;Get the event type
		B	%11event_poll		;Continue with fakes

		; --- Do the Wimp_Poll ---
		;
		; Make sure the hourglass goes off during the poll, though

10event_poll	SUB	R13,R13,#8		;Make space for hourglass blk
		STMFD	R13!,{R0}		;Save the event mask
		ADD	R0,R13,#4		;Point to the block
		BL	hour_suspend		;Turn the hourglass off a bit
		LDR	R0,[R13,#0]		;Load the event mask again

		CMP	R2,#0			;Are we getting NULLs ASAP
		SWIEQ	Wimp_Poll		;Yes -- Normal Poll
		SWINE	Wimp_PollIdle		;No -- PollIdle

		STR	R0,[R13,#0]		;Save the Wimp_Poll reason
		ADD	R0,R13,#4		;Point to the block
		BL	hour_resume		;Restore the hourglass state
		LDMFD	R13!,{R0}		;Restore the reason code
		ADD	R13,R13,#8		;And reclaim the stack space

		; --- Copy over the event ---

		ADR	R8,event__buffer	;Point to my buffer
		MOV	R9,R1			;Point to users buffer
		BUFCOPY				;Copy the event
		STR	R0,event__prevR0	;Remember event type

		; --- Deal with fake events ---

11event_poll	LDR	R8,event__fakePos	;Get the fake list
		CMP	R8,#-1			;Is there one?
		LDREQ	R8,event__fakeList	;Get fake list if !resuming
		ADDS	R0,R0,#0		;Clear the carry flag
12event_poll	TEQ	R8,#0			;Is there a fake handler?
		MOVEQ	R8,#-1			;No more to do
		BEQ	%13event_poll		;No -- Scan filters
		LDMIA	R8,{R8-R9,R12}		;Get arguments
		MOV	R14,PC			;Set return point
		MOV	PC,R9			;Branch to handler
		BCC	%12event_poll		;Keep trying

13event_poll	STR	R8,event__fakePos	;Store the fake position

		; --- Store the last event away ---

19event_poll	ADR	R14,event__lastCode	;Point to last event cache
		STMIA	R14,{R0,R1}		;Save the information away

		; --- Scan the post-filters ---

		LDR	R8,event__postList	;Get post-list pointer
		ADDS	R0,R0,#0		;Clear carry
20event_poll	TEQ	R8,#0			;Are we at the end
		BEQ	%30event_poll		;Yes -- branch ahead
		LDMIA	R8,{R8-R9,R12}		;Get list fields
		MOV	R14,PC			;Set up return address
		MOV	PC,R9			;Branch to client routine
		BCC	%20event_poll		;Keep going through list

30event_poll	LDMFD	R13!,{R8-R10,R12,R14}
		BICCCS	PC,R14,#C_flag		;Clear C if C clear :-)
		ORRCSS	PC,R14,#C_flag		;Set C if C set

		LTORG

; --- event_last ---
;
; On entry:	--
;
; On exit:	R0 == last event code received from Wimp_Poll
;		R1 == pointer to accompanying event data
;
; Use:		Allows you to read the full event information.  The event
;		is the same one currently being or most recently dispatched
;		to the postfilter list, i.e. fake events are also returned
;		by this call.  If no event has yet been received, the return
;		values are undefined.

		EXPORT	event_last
event_last	ROUT

		LDR	R0,event__wSpace	;Load my workspace offset
		LDR	R1,sapph_workspace	;Load workspace base
		ADD	R0,R1,R0		;Find actual workspace addr
		ADD	R0,R0,#:INDEX: event__lastCode
		LDMIA	R0,{R0,R1}		;Load the information
		MOVS	PC,R14			;Return to caller

		LTORG

; --- event_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the event system.

		EXPORT	event_init
event_init	ROUT

		STMFD	R13!,{R0-R3,R10,R14}	;Stack some registers
		WSPACE	event__wSpace,R10	;Get my workspace

		; --- Are we already initialised? ---

		LDR	R0,event__flags		;Get my flags
		TST	R0,#event__INITED	;Are we initialised?
		LDMNEFD	R13!,{R0-R3,R10,PC}^	;Yes -- return

		ORR	R0,R0,#event__INITED	;Set initialised flag
		STR	R0,event__flags		;And store them back

		; --- Clear rest of workspace ---

		MOV	R0,#0			;Zero some registers
		MOV	R1,#0
		MOV	R2,#0
		MOV	R3,#-1
		STMIB	R10,{R0-R3}		;Clear pre/post list

		; --- Initialise suballoc ---

		BL	sub_init		;Sub, short for SUBmarine

		; --- That's it now ---

		LDMFD	R13!,{R0-R3,R10,PC}^	;Return

		LTORG

event__wSpace	DCD	0			;My workspace pointer

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

		^	0,R10
event__wStart	#	0

event__flags	#	4			;Flags

event__INITED	EQU	(1<<0)			;I've been initialised

event__preList	#	4			;Pre-Poll list
event__fakeList	#	4			;Event-faking list
event__postList	#	4			;Post-Poll list
event__fakePos	#	4			;The current fake position

event__lastCode	#	4			;The last event reason code
event__lastData	#	4			;The last event data pointer

event__prevR0	#	4			;R0 for real event
event__buffer	#	256			;The event received

event__wSize	EQU	{VAR}-event__wStart

; --- Pre/Post list structure ---

		^	0
list__next	#	4
list__proc	#	4
list__r12	#	4

list__size	#	0

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	event__wSize		;Workspace size
		DCD	event__wSpace		;Workspace pointer
		DCD	0			;Scratchpad size
		DCD	event_init		;Initialisation code

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

		END
