Multitasking: Properties and Data Hiding

By now you should have a program running in SPLat/PC that is merrily flashing a light and at the same time, and quite independently, toggling another light on and off each time you press a button. You will have (should have!) studied how the Suspend/Resume mechanism works, and be able to see how such a simple idea can allow 2, 3 or more tasks to run quite independently and asynchronously.

But what if we need to have the tasks interact? Suppose we want to make it so the flashing light is only active while the other light is off?

There are many ways of achieving this, some better than others. The way I will show you is a "good" way, trust me!

Firstly, I will obviously need to re-write the flasher task to take into account the state of the on/off light. In fact, it will now become a full-blown SPLatMap based state machine, with Suspend/Resume instructions.

Multitasking flasher splatmap

The SPLatMap shows the logic of the revised flasher. It starts in state 0, the initialize state. I have called the flashing light "flash" and the other one "lamp", to try and avoid confusion in the diagram. Assuming the flasher task can somehow discover the condition of the lamp, it will flash its output only while the lamp is off.

So how does the flasher find out about the condition of the lamp? It asks the on/off lamp switch task (OOS_)!

What I will do is create a "Property" (attribute) of the OOS_ task that tells me if the lamp is on (true) or off (false). I will call this property OOS_LampOn. The flasher (or any other task in my program) can get OOS_LampOn property by calling the "property get" subroutine OOS_GET_LampOn. It will then be the responsibility of the OOS_ task to return the correct value in the X register.

How do I implement the property and the property get? There are several ways. What is really interesting is that it doesn't matter to the flasher how it is done, just as long as a call to OOS_GET_LampOn returns the correct value. The method I will use is to have OOS_ maintain a semaphore that tracks the condition of the lamp. All OOS_GET_LampOn then has to do is grab the semaphore and return it.

In the new program below you will notice the following changes (I have flagged them with a row of <<<) compared to the previous version:

  1. I have reserved the first 8 bytes of RAM for semaphores. This is in accordance with our simplified semaphore strategy.
  2. There is a semaphore called OOS_Lamp, which is owned and maintained by the OOS_ task.
  3. I've (somewhat pedantically - read "boringly consistent") added "real" initialization code to OOS_
  4. OOS_ has been modified so it updates the semaphore to match the state of the lamp.
  5. The new OOS_GET_LampOn property get entry point returns the state of the semaphore in X
  6. The flasher, FTL_, has been completely re-written to follow the SPLatMap above.

Here is the complete program.

*****************************************
; I/O assignments
Light      oEQU      15            ;MMi99 front panel LED
FlashLight oEQU      14            ;MMi99 front panel LED
Switch     iEQU      8             ;MMi99 front panel push button
*****************************************
; RAM assignments
* Reserve the first 8 bytes for 64 semaphores<<<<<<<<<<<<<<<<<<<<<
; Semaphores
OOS_Lamp   sEQU     0             ;Light is on   <<<<<<<<<<<<<<<<<<<<<
*****************************************

OOS_Address mEQU     20            ;2 bytes for a suspend address.
FTL_Address mEQU     22            ;2 bytes for a suspend address.
*****************************************
; initialize code
         GoSub       FTL_Init
         GoSub       OOS_Init      ;Absolutely essential!!!!!!
*****************************************
; Main loop
MainLoop
         GoSub       FTL_Update    ;Update the flashing light
         GoSub       OOS_Update    ;Update the push on/push off light
         GoTo        MainLoop
*****************************************
;On-off switch function
;Short name: OOS_
;Function: Toggles the Lamp output each time the front panel button
;          switch is pressed.
;Methods:  OOS_Init
;          OOS_Update

;Properties:                     ; <<<<<<<<<<<<<<<<<<<<<
;          OOS_GET_LampOn    Boolean in X, true if lamp is on

OOS_GET_LampOn                     ;<<<<<<<<<<<<<<<<<<<<<
         RecallS     OOS_Lamp
         Return
OOS_Init                           ;<<<<<<<<<<<<<<<<<<<<<
         Off         Light
         ClrS        OOS_Lamp
         GoTo        OOS_0
OOS_Update
         Resume      OOS_Address   ;Resume from where we last Suspended
OOS_0
         Suspend     OOS_Address
         GoIfInOff   Switch,OOS_0
         On          Light
         SetS        OOS_Lamp      ;<<<<<<<<<<<<<<<<<<<<<
OOS_1
         Suspend     OOS_Address
         GoIfInOn    Switch,OOS_1
OOS_2
         Suspend     OOS_Address
         GoIfInOff   Switch,OOS_2
         Off         Light
         ClrS        OOS_Lamp      ;<<<<<<<<<<<<<<<<<<<<<
OOS_3
         Suspend     OOS_Address
         GoIfInOn    Switch,OOS_3
         GoTo        OOS_0
******************************************
;Light flasher function Totally rewritten <<<<<<<<<<<<<<<<<<<<<
;Short name: FTL
;Function: Flash output FlashLight at 1Hz, 50% duty cycle.
;Methods:  FTL_Update
;          FTL_Init
FTL_Update
         Resume      FTL_Address
FTL_Init
FTL_Set0
         Off         FlashLight
FTL_0
         Suspend     FTL_Address
         GoSub       OOS_GET_LampOn
         GoIfT       FTL_0
FTL_Set1
         On          FlashLight
         SetTimer    0,5
FTL_1
         Suspend     FTL_Address
         GoSub       OOS_GET_LampOn
         GoIfT       FTL_Set0
         Test        0
         GoIfT       FTL_1
FTL_Set2
         Off          FlashLight
         SetTimer    0,5
FTL_2
         Suspend     FTL_Address
         GoSub       OOS_GET_LampOn
         GoIfT       FTL_Set0
         Test        0
         GoIfT       FTL_2
         GoTo        FTL_Set1

Copy and paste this program into SPLat/PC, and get it going (you may have to manually fix up some weird stuff inserted by the copy and paste).

Once you have fully absorbed how this works, here's an exercise for you: Change the way OOS_GET_LampOn works. Rewrite it to use an InputO instruction instead of the semaphore. Does this affect how FTL_ uses the OOS_LampOn property?

What you have just seen with that exercise is the principle of Data Hiding (or encapsulation ... the two terms tend to get used interchangeably though strictly speaking that is incorrect.). What that means is that how OOS_ keeps track of its own internal stuff is its own business and no-one else's. As long as OOS_ coughs up the right answer when asked for a property, it doesn't matter to the caller how it happens. The other side of this is that no-one outside OOS_ is allowed to directly access data or resources that belong to OOS_. This principle is carried further. In terms of coding SPLat, if you stick to the rule that all exchanges with a task must be via GoSubs, you will stay on the straight and narrow. We call the entry points Methods if they result in an action, and Property GETs and SETs if they involve GET-ing or SET-ing of data values. Where-ever possible any data passed during these calls should be held in registers like X or W, not in RAM.

You can consider the Methods and Properties of a task as its interface to the world. Think of the task as a black box. From outside the box you do not know, or care, what goes on inside. All you want to know is that if you poke and prod it according to the interface specification, it will do the things it is meant to do. If for some reason you have to modify the internals of the box, then as long as you don't alter its interface you can be confident that the rest of your program will be unaffected.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.