;
; thread.sh
;
; Preemptive multitasking of idle threads
;
;  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.

;----- Overview -------------------------------------------------------------
;
; Functions provided:
;
;  thread_create
;  thread_setPriority
;  thread_setTimeSlice
;  thread_destroy
;  thread_suspend
;  thread_resume
;  thread_yield
;  thread_createSem
;  thread_destroySem
;  thread_threaded
;  thread_wait
;  thread_signal
;  thread_init
;  thread_enterCrit
;  thread_leaveCrit
;  thread_errorHandler

		[	:LNOT::DEF:thread__dfn
		GBLL	thread__dfn

; --- thread_create ---
;
; On entry:	R0 == size of stack to allocate, or 0 for a default
;		R1 == pointer to thread routine
;		R2 == workspace pointer to pass in R10
;		R3 == workspace pointer to pass in R12
;
; On exit:	R0 == thread handle for the thread
;		May return an error
;
; Use:		Creates a new thread running `in the background' (i.e. over
;		idle events).
;
;		The thread is passed control with the registers R10 and R12
;		set up from R1 and R2 passed to this routine and R13 pointing
;		to the top of a stack chunk.  R0 on entry contains the
;		thread's handle.  The thread is passed the scratchpad
;		address (in R11).  The values of other registers are
;		indeterminate and must not be relied upon.
;
;		The default stack size for a new thread is 1K, although this
;		may change in future.
;
;		The thread may exit by calling thread_destroy or by
;		returning in the normal way.

		IMPORT	thread_create

; --- thread_setPriority ---
;
; On entry:	R0 == thread handle
;		R1 == new priority to set
;
; On exit:	--
;
; Use:		Changes the priority of a thread.  The priority if a thread
;		is a signed integer.  The highest priority thread is the one
;		which runs.  If more than one thread has maximum priority,
;		they are run in a cyclical order.

		IMPORT	thread_setPriority

; --- thread_setTimeSlice ---
;
; On entry:	R0 == thread handle
;		R1 == new timeslice size, in centiseconds
;
; On exit:	--
;
; Use:		Changes a thread's timeslice size.  Specify 0 to indicate
;		that thread shouldn't be pre-empted.

		IMPORT	thread_setTimeSlice

; --- thread_destroy ---
;
; On entry:	R0 == thread handle to destroy, if not executing a thread
;
; On exit:	--
;
; Use:		Destroys either the current thread or a thread with the
;		the given handle if no thread is executing currently.  You
;		can't destroy an arbitrary thread while running in one.
;
;		If a thread is waiting for a semaphore, it is removed from
;		the waiting list.

		IMPORT	thread_destroy

; --- thread_suspend ---
;
; On entry:	R0 == thread handle, or 0 for the current thread
;
; On exit:	--
;
; Use:		Suspends a thread's execution.  If a thread is currently
;		running, that thread is suspended.  Otherwise, any thread
;		may be suspended.
;
;		If the thread is currently claiming semaphores, the
;		semaphores are not released, because we don't whether the
;		system is in a fit state for this.
;
;		Thread suspensions are counted.  i.e. if you suspend a thread
;		5 times, you have to resume it 5 times for it to become
;		active again.

		IMPORT	thread_suspend

; --- thread_resume ---
;
; On entry:	R0 == thread handle
;
; On exit:	--
;
; Use:		Allows a suspended thread to continue operations.  If you
;		resume a thread more times than it has been suspended,
;		any excess resumes are ignored.  You can't resume a thread
;		to stop it being blocked by a semaphore.

		IMPORT	thread_resume

; --- thread_yield ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Pauses the thread for a while.  You only need to use this
;		call if you have stopped the current thread from being
;		timesliced.

		IMPORT	thread_yield

; --- thread_createSem ---
;
; On entry:	R0 == initial value for semaphore (0 for counter, 1 for
;		      mutex)
;
; On exit:	R0 == semaphore handle and V clear if all went well
;		R0 == pointer to error and V set if something went wrong
;
; Use:		Creates a semaphore with the given initial counter value.
;
;		The semaphore can be used to provide serialised access to
;		a resource by initialising its value to 1 and performing the
;		following:
;
;		thread_wait(mySemaphore)
;		//
;		// Do things with the resource
;		//
;		thread_signal(mySemaphore)
;
;		Or you can inform a thread that it has items in its input
;		queue by having the following in the thread code:
;
;		while true
;		  thread_wait(theSemaphore)
;		  getFromQueue(myQueue,item)
;		  process(item)
;		endWhile
;
;		and when inserting queue items:
;
;		addToQueue(item)
;		thread_signal(theSemaphore)
;
;		It is distinctly possible that input queue management will
;		be introduced in a separate Sapphire module.

		IMPORT	thread_createSem

; --- thread_destroySem ---
;
; On entry:	R0 == semaphore handle
;
; On exit:	--
;
; Use:		Destroys a semaphore when it's no use any more.  If threads
;		are waiting for	it, an error is generated.

		IMPORT	thread_destroySem

; --- thread_threaded ---
;
; On entry:	--
;
; On exit:	CS if currently running a thread, CC otherwise
;
; Use:		Informs the caller whether a thread is currently executing.

		IMPORT	thread_threaded

; --- thread_wait ---
;
; On entry:	R0 == semaphore handle
;
; On exit:	If successful, R0 preserved and V clear.
;		If failed, R0 == pointer to error block and V set
;
; Use:		Waits on a sempahore.  The algorithm actually is as follows:
;
;		if semaphore.counter == 0 then
;		  addToWaitingList(semaphore,currentThread)
;		  suspend(currentThread)
;		else
;		  semaphore.counter -= 1
;		endIf
;
;		See thread_createSem for suggestions on how to make use of
;		semaphores.

		IMPORT	thread_wait

; --- thread_signal ---
;
; On entry:	R0 == semaphore handle
;
; On exit:	--
;
; Use:		Increments a semaphore's counter if no threads are waiting
;		for it, or releases a thread waiting for the semaphore.
;
;		The actual algorithm is shown below:
;
;		if semaphore.waitingList != 0 then
;		  thread = removeFromWaitingList(semaphore)
;		  unSuspend(thread)
;		else
;		  semaphore.counter += 1;
;		endif
;
;		See thread_createSem for suggestions on how to make use of
;		semaphores.

		IMPORT	thread_signal

; --- thread_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the thread system for use.

		IMPORT	thread_init

; --- thread_enterCrit ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Declares that the current thread is about to enter a
;		critical section and must not be interrupted.

		IMPORT	thread_enterCrit

; --- thread_leaveCrit ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Declares that the current thread has left the critical
;		section and can be interrupted again.

		IMPORT	thread_leaveCrit

; --- thread_errorHandler ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R12 value to call with
;		R2 == R13 value to call with
;
; On exit:	--
;
; Use:		Sets up the error handler for a thread.

		IMPORT	thread_errorHandler

		]

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

		END
