This is where PASM comes in handy. Take a look at Jonny Mac's January Spin Zone article. There is also the LED blink demo code in the Propeller manual. Or you can do what Hugh suggests and write a SPIN dispatch scheme.
I would only use aborts for real problems. These are meant to be used like exceptions in C++.
PRI mainLoop...
' init code here
stopByte := FALSE
' state := "A" or state := "B" - whatever is the initial state
repeat until stopByte
if state == "A"
FunctionA
if state == "B"
FunctionB( .. )
PRI FunctionA
' This does not need a repeat, as it's repeated by the main
if OutgoingNewString <> 0
IncomingNewString := .....
' remaining code
else
state := "B"
PRI FunctionB
repeat 3
' Your code
state := "A"
For stopping the whole thing you'd add another PUB function which simply sets the stopByte to true.
If I understand your code correctly OutgoingNewString is a variable which holds the address of a string-buffer, right? Otherwise OutgoingNewString == OutgoingOldString makes no sense, as you can not compare strings directly.
So, you can do it a different way:
The code that prepares the string to be send, would set the pointer to the buffer-address (using a PUB function)
The code that sends the string sets this pointer to 0 if done.
This way you don't need a New and a Old variable.
For the IncomingString you can do the same. For your object you have 2 possibilities, either you give it the buffer-address with the start function. In this case the buffer is part of the code using this object.
Or you have another PUB function called getIncomingString. Then your object has a buffer for the Incoming message.
Overall, I like your ideas and source. I would imagine it would now look something similar to this:
VAR
LONG IncomingString
LONG OutgoingString
LONG Stack[10]
BYTE StopByte
BYTE Cog
BYTE State
PUB Start(Pin) : Success
Stop
Success := (Cog := COGNEW(MainLoop(Pin), @Stack) + 1)
PUB Stop
IF Cog
COGSTOP(Cog~ - 1)
PRI MainLoop
DIRA[Pin]~~
OUTA[Pin]~
StopByte := FALSE
State := "A"
REPEAT UNTIL StopByte
IF State == "A"
IF NOT \ FunctionA
Stop
IF State == "B"
IF NOT \ FunctionB(OutgoingString)
Stop
PRI FunctionA
IF OutgoingString <> 0
IF NoErrors
' remaining code
IncomingString := .....
ELSE
ABORT FALSE 'Exception
ELSE
State := "B"
PRI FunctionB(UpdateString)
REPEAT 3
' Do something with UpdateString
IF NoErrors := TRUE
OutgoingString := 0
ELSE
ABORT FALSE 'Exception
State := "A"
PUB UpdateOutgoingString(OutgoingStringPointer)
' Set the value for OutgoingString
However I still have a two remaining questions, which are:
While the object is running in a loop, can the parent object still call the PUB Stop method and stop the cog, and can the parent call the PUB UpdateOutgoingString?
It would seem to me that the child object would be wrapped up in the MainLoop, and be unable to accomplish these tasks.
While the object is running in a loop, can the parent object still call the PUB Stop method and stop the cog....
Yes. That is the normal way things are done.
Don't forget when the parent calls Start which does a COGNEW, a new instance of the Spin interpreter is started on a new COG just to run MainLoop (In your example).
After that Start returns back to the parent who can then continue, in its original COG, and do whatever it likes. Including calling Stop.
I think the point is not to confuse objects and COGs.
When a parent object calls a method in a child object the code of that method is run in the same COG as the parent.
When you do a COGNEW an new COG is started and it only runs the method that was passed to COGNEW. The rest of the methods in that object are still available to be called from a parent.
In fact if the method started my COGNEW is PUB then it can be called directly by the barent object and then you might have two COGS running the same method. Which makes sense in some cases.
That was very enlightening. Thank you very much for your input. I was missing that whole concept, now I understand completely, and it makes perfect sense. Just one of those "A DUH" things
1. Simply call the abort whereever an error occurs. So, no NoErrors variable needed. Except you do it for a reason that I don't see here.
2. The other improvement is mainly beautifying code and maybe saves some bytes.
PRI Init
DIRA[Pin]~~
OUTA[Pin]~
StopByte := FALSE
State := "A
If NOT \ MainLoop
Stop
PRI MainLoop
REPEAT UNTIL StopByte
IF State == "A"
FunctionA
IF State == "B"
FunctionB(OutgoingString)
Of course start would then call Init instead of the MainLoop.
Comments
0 = run 'as is'
1 = Forced change to Function A
2 = Forced change to Function B
3 = run Stop
?
I was thinking along the lines of the following, but I don't know if it will work:
The parent object would need to be able to read:
OutgoingNewString
Bruce
P.S. As you can clearly see, I have very little knowledge of working with new cogs, as to what is possible and what is not.
For stopping the whole thing you'd add another PUB function which simply sets the stopByte to true.
If I understand your code correctly OutgoingNewString is a variable which holds the address of a string-buffer, right? Otherwise OutgoingNewString == OutgoingOldString makes no sense, as you can not compare strings directly.
So, you can do it a different way:
The code that prepares the string to be send, would set the pointer to the buffer-address (using a PUB function)
The code that sends the string sets this pointer to 0 if done.
This way you don't need a New and a Old variable.
For the IncomingString you can do the same. For your object you have 2 possibilities, either you give it the buffer-address with the start function. In this case the buffer is part of the code using this object.
Or you have another PUB function called getIncomingString. Then your object has a buffer for the Incoming message.
Thank you very much for your input. Please give me a minute or two to examine what you wrote. I will get back to you.
Bruce
Overall, I like your ideas and source. I would imagine it would now look something similar to this:
However I still have a two remaining questions, which are:
Bruce
Yes. That is the normal way things are done.
Don't forget when the parent calls Start which does a COGNEW, a new instance of the Spin interpreter is started on a new COG just to run MainLoop (In your example).
After that Start returns back to the parent who can then continue, in its original COG, and do whatever it likes. Including calling Stop.
Oh I think see the forest now. The whole object is not running in a new cog, it is just the MainLoop. Is this correct?
Bruce
When a parent object calls a method in a child object the code of that method is run in the same COG as the parent.
When you do a COGNEW an new COG is started and it only runs the method that was passed to COGNEW. The rest of the methods in that object are still available to be called from a parent.
In fact if the method started my COGNEW is PUB then it can be called directly by the barent object and then you might have two COGS running the same method. Which makes sense in some cases.
That was very enlightening. Thank you very much for your input. I was missing that whole concept, now I understand completely, and it makes perfect sense. Just one of those "A DUH" things
Thanks Heater
Bruce
1. Simply call the abort whereever an error occurs. So, no NoErrors variable needed. Except you do it for a reason that I don't see here.
2. The other improvement is mainly beautifying code and maybe saves some bytes. Of course start would then call Init instead of the MainLoop.
I truly appreciate your input in this matter. And I really like your modifications, which were some very nice suggestions.
I was just using NoErrors as an example
OutgoingNewString == OutgoingOldString LOL Sometimes I still tend to think like I am working with a CString object in MFC.
I would imagine this would be a lot better:
Bruce