DEFINITION MODULE Kernel;

(*	An 'operating system' kernel. This module is used to
	run multi-tasking programs on top of a single-tasking operating 
	system such as MSDOS, CP/M GEMDOS etc. It provides the following 
	facilities:
	
	1/	A modified round robin scheduler. The scheduler is modified 
	by providing several priority levels implemented by means of 
	several ready lists rather than a single ready list.
	
	2/	Entry points for DOS access which ensure that only only DOS 
	call is being executed at a time.
	
	3/	Facilities for handling interrupts. These will need 
	modifying for individual applications.
	
	References:
	==========
	
	Operating Systems, Design and Implementation, Andrew S Tanenbaum,
	Prentice-Hall International ISBN 0-13-637331-3
	*)
FROM	SYSTEM IMPORT BYTE;	
FROM	Processes IMPORT InterruptStub;
CONST	PriorityLevels=3;

(*	You can change this value at will, but having to many 
	levels may cause low-level operations to be executed too 
	infrequently. Priorities start at 1 and work down, so that 3 is a 
	lower priority than 1. Tasks which are CPU intensive must be given 
	the lowest prority level or else nothing at a lower priority will 
	ever be executed 
	*)

TYPE	Task;

(*	There is a 'task' for each task in your multi-tasking program.

	This type maintains the execution state of your task when it is not 
	executing and also is used to retain scheduling information, such 
	as list pointers and blocking information *)
	
PROCEDURE AddTask(p:PROC;prio:CARDINAL;Work:CARDINAL);

(*	Create a new task and schedule it at the given priority level. The 
	procedure always returns to the caller, even though the new task 
	may be at a higher priority than the caller. This is because, as 
	well as using this call to create new tasks while running in multi-
	tasking mode, you also use it to set up your tasks when 
	initializing the system, at which time the multi-tasking has not 
	yet started. 
	
	If you want to force a rechedule, call WAIT(0) after doing the 
	AddTask call 

	'Work' is the size of the work area you want for the task
*)
	
PROCEDURE DeleteTask(t:Task):BOOLEAN;

(*	Delete a task. To delete the current task, pass the variable
	CurrentTask, which is exported from this module (see below )

	returns TRUE if the task was deleted. A task must be on
	the round-robin queue (that is, it must be ready to execute) if
	it is to be deleted. 
*)
	
PROCEDURE Wait(Time:CARDINAL);

(*	Wait for the given amount of time to elapse. The time interval used 
	depends upon the clock tick routine which you may want to modify. 
	
	If Time is zero, a reschedule is forced but the task will get 
	control back again immediately if it is the only task at the 
	highest priority level that can be executed.
	
*)

PROCEDURE Clock():CARDINAL;

(*	returns the current time in clock ticks (or whatever units you are 
	using for the clock *)
	
	
PROCEDURE StartTasking;

(*	Starts the multi-tasking executive *)

PROCEDURE EndTasking;

(*	Ends the multi-tasking executive. This call is intended as a 
	general cleanup, so you may want to add extra code of your own to 
	clean up your application *)

PROCEDURE StartDosCall;

(*	The program is about to call DOS. This locks DOS so that no
	other task will attempt to call DOS while this task is in there *)

PROCEDURE EndDosCall;

(*	End DOS call. DOS is now available again *)

(*	The following procedures and types allow you to send and 
	receive messages. For this, you need a post-box *)

TYPE 	MailBox;

PROCEDURE InitMailBox(VAR b:MailBox);

(*	initialize a postbox. You must call this for every postbox. 
*)

PROCEDURE SendMessage(VAR b:MailBox;Message:ARRAY OF BYTE);

(*	Send a message to the given postbox. The contents of the message 
	are user defined. A copy is made of the message on the heap *)

PROCEDURE ReceiveMessage(VAR b:MailBox;VAR Message:ARRAY OF BYTE);

(*	Receive a message. If no message is available, the task waits
	until a message becomes available. *)

PROCEDURE MessageCount(VAR b:MailBox):INTEGER;

(*	Return number of messages pending on a post-box. The returned value 
	is positive if there are messages in the postbox. It is negative if 
	there are tasks waiting for messages.

*)

(*	The following types and procedures implement semaphores *)

TYPE	Semaphore;

PROCEDURE InitSemaphore(VAR s:Semaphore;SimUsers:CARDINAL);

PROCEDURE LockSemaphore(VAR s:Semaphore);

PROCEDURE UnlockSemaphore(VAR s:Semaphore);

PROCEDURE Waiters(VAR s:Semaphore):INTEGER;

(*	initSemaphore must be called to initialize a semaphore before the
	semaphore is used. SimUsers is the count of simultaeneous users
	that are allowed for the semaphore. (The most common case is for
	this to have the value 1)

	LockSemaphore should be called before you enter a critical region 
	of code.

	UnlockSemaphore must be called when you exit the critical region.

	Waiters returns the number of tasks waiting on a semaphore. The 
	value is negative if the critical region is free.

*)
	
VAR	CurrentTask:Task;
	
(*	The following routines perform input-output operations. These
	will need to be modified by you for your particular application 
	since they depend in part upon the machine on which you are running 
	and the external environment *)

TYPE 	IORecord=RECORD
		IntNo:CARDINAL;
		IntStub:InterruptStub; (*a work area *)
		END;

PROCEDURE DoIO(IOInfo:IORecord;VAR PreviousTask:Task);

(*	Perform an Input-output operation. The task is suspended until the 
	operation is complete. This routine should be used rather than
	doing direct IOTRANSFERS.

	PreviousTask returns the task that was interrupted by this task.
	It is an input-output variable that is used to control re-scheduling.

	Normally, a DoIO is in a loop. The first call to DoIO must set this
	variable to NIL before calling DoIO.

	On subsequent calls, if you set it to NIL before making the call, a
	reschedule operation is performed. This takes time.

	If you leave it with the value that it came back with from the previous
	DoIO call, the task that was interrupted is started without a 
	complete re-schedule being required. This is faster.

 *)

END Kernel.
