Command: Start the next available cog to run Spin code or Propeller Assembly code.
...
- SpinMethod is the PUB or PRI Spin method that the new cog should run. Optionally, it can be followed by a parameter list enclosed in parentheses.
- ParameterList is an optional, comma-delimited list of one or more parameters for SpinMethod. It must be included only if SpinMethod requires parameters.
- StackPointer is a pointer to memory, such as a long array, reserved for stack space for the new cog. The new cog uses this space to store temporary data during further calls and expression evaluations. If insufficient space is allocated, either the application will fail to run or it will run with strange results.
- AsmAddress is the address of a Propeller Assembly routine, usually from a DAT block.
- Parameter is used to optionally pass a value to the new cog. This value ends up in the new cog's read-only Cog Boot Parameter (PAR) register. Parameter can be used to pass a either a single 14-bit value or the address of a block of memory to be used by the assembly routine. Parameter is required by COGNEW, but if not needed for your routine, simply set it to an innocuous value like zero (0).
Explanation
COGNEW starts a new cog and runs either a Spin method or a Propeller Assembly routine within it. For Spin methods, the new cog's RAM is loaded with the Spin Interpreter from the Main ROM, which then fetches and executes the Spin code tokens from the application image in Main RAM. For Propeller Assembly, the targeted assembly code itself is loaded from the application image in Main RAM into the cog's RAM for execution.
...
- It launches code into the next available cog
- It returns the ID of the cog that it started, if any.
Spin Code (Syntax 1)
To run a Spin method in another cog, the COGNEW command needs the method name, its parameters, and a pointer to some stack space. For example:
Code Block |
---|
VAR
long SqStack[6] 'Stack space for Square cog
PUB Main | X
X := 2 'Initialize X
cognew(Square(@X), @SqStack) 'Launch square cog
<check X here> 'Loop here and check X
PUB Square(XAddr)
'Square the value at XAddr
repeat 'Repeat the following endlessly
long[XAddr] *= long[XAddr] 'Square value, store back
waitcnt(2_000_000 + cnt) 'Wait 2 million cycles |
This example shows two methods, Main and Square. Main starts another cog that runs Square endlessly, then Main can monitor the results in the X variable. Square, being run by another cog, takes the value of XAddr, squares it and stores the result back into XAddr, then waits for 2 million cycles before it does it again. More explanation follows, but the result is that X starts out as 2, and the second cog, running Square, iteratively sets X to 4, 16, 256, 65536 and then finally to 0 (it overflowed 32 bits), all independent of the first cog which may be checking the value of X or performing some other task.
The Main method declares a local variable, X, that is set to 2 in its first line. Then Main starts a new cog, with COGNEW, to run the Square method in a separate cog. COGNEW's first parameter, Square(@X), is the Spin method to run and its required parameter; in this case we pass it the address of the X variable. The second parameter of COGNEW, @SqStack, is the address of stack space reserved for the new cog. When a cog is started to run Spin code, it needs some stack space where it can store temporary data. This example only requires 6 longs of stack space for proper operation (see The Need for Stack Space, below, for more information).
After the COGNEW command is executed, two cogs are running; the first is still running the Main method and the second is starting to run the Square method. Despite the fact that they are using code from the same Spin object, they are running independently. The "<check X here>" line can be replaced with code that uses the value of X in some way.
The Need for Stack Space
A cog executing Spin code, unlike one executing Propeller Assembly code, needs some temporary workspace, called "stack space," to hold operational data such as call stacks, parameters and intermediate expression results. Without this, sophisticated features such as method calls, result values, and complex expressions would not be possible without severe limitations.
The Spin compiler automatically provides stack space for the Propeller Application's initial code, the top-level Spin code of the application. The "free space" following the application's memory image is used for this purpose. However, the compiler is not able to provide distinct blocks of stack space for Spin code that the application may launch on its own, therefore, the application must provide that stack space itself.
Typically, this stack space is provided in the form of a declared global variable meant only for that use, such as the SqStack variable in the example above. Unfortunately, it's difficult to determine just how much stack space should be provided, so when developing an object, it is suggested to initially provide a large amount of longs of memory (like 128 longs or more) and once the object is deemed complete, use an object like the Stack Length object in the Propeller Library to determine the optimal length. See the Stack Length object for further explanation.
Spin Code Can Only be Launched by its Containing Object
In the Spin language, by design, objects must intelligently manage their own data, the methods that operate on that data, the cogs that execute those methods, and the interface that other objects use to affect it. These are all aspects that serve to maintain the integrity of the object and increase its useful and reliable nature.
For these reasons, the object and its designer are notably the best equipped to provide the proper stack space that is required for Spin code being launched into another cog.
To enforce this principle, the COGNEW and COGINIT commands cannot launch Spin code outside of its containing object. This means that a statement like the following will not work as expected.
Code Block |
---|
cognew(SomeObject.Method, @StackSpace) |
Instead of launching SomeObject.Method into another cog, the Propeller will instead execute SomeObject.Method within the current cog and if that method returns a value, the Propeller will take that value and use it as the address of code to launch with the COGNEW command. This will not result in the code writer's intended effect.
If Method is determined to be code that is truly important to run in another cog, rather than write code like the example above, SomeObject should instead be rewritten similar to the example below.
Code Block |
---|
VAR
long StackSpace[8] 'Stack space for new cog
byte CogID 'Stores the ID of new cog
PUB Start
Stop 'Prevent multiple starts
CogID := cognew(Method, @StackSpace) 'Launch method in another cog
PUB Stop
if CogID > -1
cogstop(CogID) 'Stop previously launched cog
PRI Method
<some code here> |
The sample above includes two public interface methods, Start and Stop, which an outside object can use to properly launch the object's code into another cog. The important principle is that the object itself is providing this capability, and in doing so, is managing the stack memory required for proper operation. Also note that Method was changed to a private (PRI) method to discourage direct calling from the outside.
Propeller Assembly Code (Syntax 2) )
To run Propeller Assembly code in another cog, the COGNEW command needs the address of the assembly routine and a value that can optionally be used by the assembly routine. For example:
Code Block |
---|
PUB Main
cognew(@Toggle, 0) 'Launch Toggle code
DAT
org 0 'Reset assembly pointer
Toggle rdlong Delay, #0 'Get clock frequency
shr Delay, #2 'Divide by 4
mov Time, cnt 'Get current time
add Time, Delay 'Adjust by 1/4 second
mov dira, #1 'set pin 0 to output
Loop waitcnt Time, Delay 'Wait for 1/4 second
xor outa, #1 'toggle pin
jmp #Loop 'loop back
Delay res 1
Time res 1 |
...
The Parameter Field
...
- .
Excerpt | ||
---|---|---|
| ||
Start the next available cog and return the new cog's ID. |