multi.txt

Warning: if you clip this code into your program, look out for possible html code slipping in.

REM   Simulated multitasking in VSTB
REM
REM   Author: Steven R. Wheeler
REM
REM   Copyright 2000 Vesta Technology, Inc.
REM   All rights reserved.
REM
REM
REM   Description:
REM     Vesta Single-Tasking Basic will only handle a single application.
REM     Unlike Vesta Multi-Tasking Basic, you can't have multiple tasks
REM     which are time-shared. Or so you thought ...
REM
REM   This application uses the TIMER1 interrupt to invoke task-switching.
REM   There are several restrictions involved in such applications:
REM
REM    1. Each "task" is written as a separate subroutine. There must not
REM       be a main loop in the subroutine; the subroutines will be invoked
REM       repeatedly by the main loop of the overall application.
REM
REM    2. Global variables are now what VMTB calls "kernel" variables; they
REM       are available to _all_ tasks.
REM
REM    3. It is not possible to have global variables accessible only by
REM       a single task. STATIC variables in the task subroutines behave
REM       like single-task globals, but cannot be made available to subroutines
REM       or functions called by the individual task subroutines.
REM
REM    4. Task switching does not occur immediately upon the occurrence of the
REM       timer interrupt, but when the subroutine next exits. This is why
REM       loops must not be in the task subroutines.
REM
REM    5. Since the task subroutines are invoked repeatedly, you can either
REM       include a static variable in each task to determine whether or not
REM       initialization code should be executed. It may be better (depending
REM       on the requirements of the particular task) to have task initialization
REM       in a separate subroutine before the main task-switching loop starts.
REM
REM    6. If a task takes longer than its allotted timeslice to execute, the
REM       next sequential task will not be executed at all, because the timer
REM       interrupt will have executed again and incremented the task number
REM       past it.
REM
REM    7. A task can give up the remainder of its timeslice by changing the
REM       variable "Active_Task" before executing. If you do this, it is best
REM       to restart the TIMER1 timer first, so that you don't skip past the
REM       next task unintentionally.
REM
REM   Revision History
REM     May 25, 2000   SRW
REM         Initial version.


REM   Unlike VMTB, this application does not have a hard limit on the number of
REM   tasks which are supported.

GLOBAL Active_Task AS INTEGER


REM   By defining an array, with an entry for each task, we can have different
REM   timeslices for each task. The simplest thing is to have identical timeslices
REM   for each task, which allows us to use a constant. Note that timeslices are
REM   measured in 10-millisecond units.

CONSTANT Number_of_Tasks AS INTEGER = 4

DIM TimeSlice[4] AS BYTE CONSTANT = [4, 3, 2, 1]


REM   There are two ways to handle wrapping the task number back to the start ...
REM   we can test for the last task here (either by comparison or by taking the
REM   modulus), or we can define a CASE ELSE in the SELECT statement which will
REM   reset the task number for us. The latter may be best if you have identical
REM   timeslices for each task, but is not appropriate if you're allocating
REM   different timeslices with an array, like we're doing here.

VITAL SUBROUTINE Switch(which AS INTEGER)
	Active_Task = (Active_Task + 1) \ Number_of_Tasks
	SET TIMER1 TO TimeSlice[Active_Task]
END


REM   Define a few dummy tasks here

SUBROUTINE TASK_0()
STATIC foo AS INTEGER
	PRINT " ", foo, "\013\010"
	foo = foo + 1
END


SUBROUTINE TASK_1()
STATIC foo AS INTEGER
	PRINT "A"
	foo = foo + 1

	REM   If we've put out a multiple of 100 characters, then let's
	REM   relinquish our timeslice. Note that we're not _adding_ to
	REM   Active_Task, but setting it directly. If we wanted, we
	REM   could skip anywhere in the task list. We zero foo because
	REM   we don't want to run into problems when we move out of
	REM   the range of positive numbers.
	IF foo \ 100 = 0
		SET TIMER1 TO TimeSlice[2]
		Active_Task = 2
		foo = 0
	ENDIF
END


SUBROUTINE TASK_2()
	PRINT "B"
END


SUBROUTINE TASK_3()
	PRINT "C"
END


REM   Here is the start of the main application.

Active_Task = 0
ON TIMER1 Switch
SET TIMER1 TO 5

PRINT "\027[2J"

DO WHILE 1
	SELECT Active_Task
		CASE 0
			Task_0()
		CASE 1
			Task_1()
		CASE 2
			Task_2()
		CASE 3
			Task_3()
		CASE ELSE
			Active_Task = 0
	ENDSELECT
LOOP