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