Multitasking: Tasks as Objects

I like to call the function boxes in my block diagrams objects. This is an echo of Object Oriented Programming (OOP) in Information Technology. The use of the term for SPLat programs may be a bit presumptuous (there is no suggestion that SPLat has such OOP capabilities as inheritance), but it lets me borrow a few other terms and concepts as well.

An object is a lump of code that takes care of a particular task or process. An object can have several entry points (i.e. subroutine addresses or named lines that you can do a GoSub to). These entry points provide an interface for the object's Properties and Methods.

Properties provide for the passing of variable data. For example, the Coffee Grinder object has a property called CG_Done. For another task/object to access the property CG_Done, it must call the subroutine with the name CG_Get_Done. CG_Get_Done returns the requested information in the X register as a True/False value.

Methods are a way of sending a command to an object to do something. For example, the coffee grinder has a method called CG_Grind. Calling CG_Grind will result in the grinder starting up. At the time of the call, the required amount of coffee (for "Strong", "Interesting" or "Raise the Dead") is provided CG_Grind as a number in the X register.

Another concept that is central to this is Data Hiding.

Data hiding means is that no object must be allowed to directly access memory (RAM) or other resources that are managed ("owned") by another object, but must instead use the object's Properties or Methods to access its data. Why is this important? Consider the Water Heater object. Its interface to the rest of the program is defined by 3 properties: WH_SetPoint, WH_Ready and WH_Fault. The block diagram implies that a thermistor will be used for temperature sensing, and indeed (let's pretend that) the first version of our coffee machine uses a thermistor. Each time the Water Heater object is asked to supply the WH_Ready property, it looks at the current temperature, and returns True if 95°C or more, otherwise False. But one day, 6 months and 5000 coffee machines later, some bright spark in Purchasing decides to switch over to a new water heater design that uses a simple bimetallic thermostat. We no longer have a temperature reading, just a simple on/off thermostat switch. Clearly, the Water Heater object will need to be re-written. But providing it still returns a valid True/False from its WH_Ready property, nothing else in the program need change. If we had originally allowed other parts of the program to access the temperature directly, or worse still take their own thermistor readings, then we might be in for long sessions of re-writing little bits all over our program.

There will be times when you want to bend the rules on data hiding. It can sometimes seem absurd to have to create a subroutine that does nothing but Recall a memory variable and return. I bend the rules sometimes. And almost every time I bend the rules I later get caught out with a program change that is made more complicated as a result.

The one area where I do bend the data hiding is in configuration menus. The menu system usually needs to poke and prod at variables all over the place, and to have to worry about using properties and methods while coding a complex menu is beyond the pale. So I mentally declare the menu system to be a privileged function and let it poke and prod as much as necessary. This is why the configuration memory is shown on the previous screen with arrows going everywhere.

In summary: An Object is a bunch of code dedicated to performing a certain task. It may have several subroutine entry points. Each entry point corresponds to a Method or a Property. An object may own certain resources like RAM locations or I/O points. No other part of the program is allowed to access those resources directly, except a possible configuration function.

As a matter of good style and readability, an object should have all its code in one contiguous block, delineated by a clearly visible border such as a line of asterisks. At the start of the code there should be a block of comments that define the name, function, methods and properties. Here is an example of what the header comments would look like for a Water Heater object

************ Water Heater object ***********************
;Short name    WH_
;Purpose: Control the water heater, providing PID control of
;         temperature.

;Methods: 
;         WH_Init            initialize
;         WH_Update          Recalculate the PID control

;Properties:
;         WH_SET_SetPoint    Call with desired target temperature in W in 'C
;         WH_GET_Ready       Return True in X if the water is hot
;         WH_GET_Fault       Return True in X if there is a fault

;Resources:
;         Thermistor input
;         Heater output
;         Any RAM named with the WH_ prefix

;Note:    Faults are 'over-temp' and 'taking too long to heat up'. They are latching
;         and get reset by a call to WH_Init. The heater is forced off by a fault

These comments are a capsule specification of the object. The code for the object should follow immediately after the header comments.