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++.
PRImainLoop...' init code here
stopByte := FALSE' state := "A" or state := "B" - whatever is the initial staterepeatuntil stopByte
if state == "A"
FunctionA
if state == "B"
FunctionB( .. )
PRIFunctionA' This does not need a repeat, as it's repeated by the mainif OutgoingNewString <> 0
IncomingNewString := .....
' remaining codeelse
state := "B"PRIFunctionBrepeat3' 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:
VARLONG IncomingString
LONG OutgoingString
LONG Stack[10]
BYTE StopByte
BYTE Cog
BYTE State
PUBStart(Pin) : Success
Stop
Success := (Cog := COGNEW(MainLoop(Pin), @Stack) + 1)
PUBStopIF Cog
COGSTOP(Cog~ - 1)
PRIMainLoopDIRA[Pin]~~
OUTA[Pin]~
StopByte := FALSE
State := "A"REPEATUNTIL StopByte
IF State == "A"IFNOT \ FunctionA
Stop
IF State == "B"IFNOT \ FunctionB(OutgoingString)
Stop
PRIFunctionAIF OutgoingString <> 0IF NoErrors
' remaining code
IncomingString := .....
ELSEABORTFALSE'ExceptionELSE
State := "B"PRIFunctionB(UpdateString)REPEAT3' Do something with UpdateString IF NoErrors := TRUE
OutgoingString := 0ELSEABORTFALSE'Exception
State := "A"PUBUpdateOutgoingString(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.
PRIInitDIRA[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:
VAR LONG IncomingNewString LONG IncomingOldString LONG OutgoingNewString LONG OutgoingOldString LONG Stack[10] BYTE StopByte BYTE Cog PUB Start(Pin1) : Success Stop Success := (Cog := COGNEW(MainLoop(Pin1), @Stack) + 1) PUB Stop IF Cog COGSTOP(Cog~ - 1) PUB MainLoop(Pin1) StopByte := FALSE DIRA[Pin1]~~ OUTA[Pin1]~ REPEAT IF OutgoingNewString == OutgoingOldString IF NOT \ FunctionA Stop ELSE NEXT ELSE IF NOT \ FunctionB(OutgoingNewString) Stop ELSE NEXT PUB FunctionA REPEAT IF StopByte == TRUE ABORT FALSE ELSEIF OutgoingNewString <> OutgoingOldString ABORT TRUE ELSE IncomingNewString := Some Value ### Remaining code goes here ### PUB FunctionB(UpdateString) REPEAT 3 IF StopByte == TRUE ABORT FALSE ELSE IF ### Some condition == TRUE ABORT TRUE ### Remaining code goes here ###
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.
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.
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:
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:
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.
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.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