Each Slave must have a parsing routine which interprets the Commands and Parameters which are received from the SB-Bus.
I can imagine that a novice programmer may not know where to begin.
Therefore I've collected some software tips in this chapter.
Needless to say that you don't have to understand how the Slave software operates if you're just interested in building one or some of my published projects.
First of all make sure there is only one major program loop. All possible tasks are called from this loop, one after the other. Usually every task is rather short and it will appear as if all tasks are running simultaneously. Don't allow a task to sit and wait for an event to happen! Such a task might easily lock out control of other tasks. If a task does have to wait for an event to happen let it return to the main loop and test the event again the next time the task is called.
Our parsing routine is one of those tasks that has to wait for an event to happen.
The event we're waiting for is the arrival of a CR character from the SB-Bus.
So if we didn't receive a CR character yet we know that we haven't received a complete command yet and we must return control to the main program loop.
Usually the serial characters are received by an interrupt routine, which places them all in an input buffer.
Once we do receive a CR character we may start parsing what's in the input buffer. You may have to strip down all leading spaces to start with. Then we check to see if the next character is the CR character itself. If it is, we repeat the last Command if any was given. If it is not a CR character it must be a command, and that is what we're going to decode next.
All possible Commands are collected in a Command table in alphabetical order. Be sure that the parsing routine can recognize the end of each of the Commands in the table. You can do that by adding a $00 byte after each Command, or you can set bit 7 of each last character. The end of the table should also be marked. I usually do that with a $00 byte.
What we do next is compare the Command in the input buffer to all the Commands in the Command Table.
The Command in the input buffer ends when you encounter a space or the end of the line which is indicated by a CR character.
Each time you find a mismatch between the Command in the input buffer and the current one in the table you must increment a counter, which was cleared at the beginning.
Once you find a mismatching character you can stop comparing the rest of the Command for it will fail to match anyway. In that case you must first find the beginning of the next Command in the table before you continue with the operation.
You may stop comparing once you do find a complete match, in which case we know what Command was given. But before you do decide that it is a match be sure you know that both Commands are of the same length!
However if we reach the end of the Command Table without finding a match we have a Syntax Error situation and should act accordingly. To give you a hint about what to do in case of a Syntax Error: Clear the input buffer (because it holds an unknown Command) and send a Syntax Error Prompt ?>.
Once we find the Command in the table we must jump to a routine which knows all about its expected/required parameters and how to execute the Command.
That's where the Command Jump Table comes in.
The Command Jump Table is a table containing all the destination addresses of the routines of all possible Commands. We know the Command number already, because we counted all the wrong Commands during our search through the Command Table. Usually each destination address in the Command Jump Table is 2 bytes long, so all we have to do now is multiply the Command number by 2 and add the start offset of the Command Jump Table to it and we've got ourselves the destination address.
We get the 16-bit destination address from the Jump table and we are going to use that as destination address in just a second.
But before we do that we add 3 to that value and save it in a location which can be used later, just in case we have to repeat the Command.
Why adding 3?
I'll explain that in a minute.
But now it's time to jump to the address we've taken from the Command Jump Table.
We're going to structure all Command Routines to make it easier to call them.
Every Command will have its own Command Routine, and we get to each of them through the Command Jump Table.
Apart from the main starting point each Command Routine has a second entry. This second entry always starts 3 bytes after the main entry. Why 3 bytes? I did promise I would explain that. Because usually a JSR or CALL instruction takes 3 bytes of program memory. You probably can think of what to do should your processor have a different JSR or CALL instruction length.
The main entry to the Command Routine should always start with a call to a subroutine which parses the Parameters that are present in the input buffer.
This is even true if the Command in question doesn't need any Parameters.
In such a case you could scan the input buffer to find a CR character, any other character apart from spaces would mean that the user entered a Parameter anyway.
Parsing parameters can be a process that is fairly similar to parsing the Command which we did just a moment ago. Simply compare the given Parameter with all entries in a table which holds all possible Parameters. Remember that a Parameter may end with a comma or a CR character, whereas a Command ends with a space or a CR character.
It goes without saying that you cannot use a table if a Parameter can have many different values. In that case you should write a routine which accepts all digits of the value, one at a time, and compose the binary or BCD value until you find a comma or a CR character.
In any case you need to parse all expected parameters and store them all in a list which should be big enough to hold them all. We should not change the Parameters in this list during the execution of the Command Routine because we may need them again in case we have to repeat the Command again.
The second entry in the Command Routine is used whenever we have to repeat the Command (in case we sent a CR character on an empty line).
This second entry is also used when a *TRIG Command was given to start the Command which was previously placed in Hold.
The second entry is always a call to a subroutine which checks whether we are allowed to execute the Command immediately or not. We are not allowed to execute the Command directly if the previous Command was the *HOLD System Command. In that case we must save the second Command Routine's entry in the Hold pointer and we're done.
If the Hold checking subroutine decides we can execute the Command directly we return back to the Command Routine which finally executes the Command.
Don't forget to clear the Command from the input buffer when you're done, otherwise it is executed again and again.