Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Every Propeller Object consists of Spin code plus optional assembly code and data. An object's Spin code provides it with structure, consisting of special-purpose blocks. Data and Propeller Assembly code are located in the DAT block; see DAT on page .

Spin code is executed from Main RAM by a cog running the Spin Interpreter, however, Propeller Assembly code is executed directly from within a cog itself. Because of this nature, Propeller Assembly code and any data belonging to it must be loaded (in its entirety) into a cog in order to execute it. In this way, both assembly code and data are treated the same during the cog loading process.

Here's an example Propeller object. Its Spin code in the PUB block, Main, launches another cog to run the DAT block's Propeller Assembly routine, Toggle.

Code Block
{{ AssemblyToggle.spin }}

...

 

CON
  _clkmode = xtal1 + pll16x

...


  _xinfreq = 5_000_000

...

 
 
PUB Main

...


{Launch cog to toggle P16 endlessly}

...

 

  cognew(@Toggle, 0)                 'Launch new cog

...

 
 
DAT
{Toggle P16}

...


             org     0               'Begin at Cog RAM addr 0
Toggle       mov     dira, Pin       'Set Pin to output
             mov     Time, cnt       'Calculate delay time
             add     Time, #9        'Set minimum delay here
:loop        waitcnt Time, Delay     'Wait
             xor     outa, Pin       'Toggle Pin
             jmp     #:loop          'Loop endlessly 

Pin    long  |< 16                   'Pin number
Delay  long  6_000_000               'Clock cycles to delay
Time         res 1                   'System Counter Workspace 

When the Main method's COGNEW command is executed, a new cog begins filling its Cog RAM with 496 consecutive longs from Main Memory, starting with the instruction at the address of Toggle. Afterwards, the new cog initializes its special purpose registers and begins executing the code starting at Cog RAM register 0.

Both assembly and data may be intermixed within this DAT block but care should be taken to arrange it such that all critical elements are loaded into the cog in the proper order for execution. It is recommended to write it in the following order: 1) assembly code, 2) initialized symbolic data (i.e.: LONGs), 3) reserved symbolic memory (i.e.: RESs). This causes the cog to load up the assembly code first, followed immediately by initialized data, and any application data after that, whether or not it is required by the code. See the sections discussing ORG (page ), RES (page ), and and DAT (page ) for more information.

...

Cog RAM is similar to Main RAM in the following ways:

  • Each can contain program instruction(s) and/or data.
  • Each can be modified at run-time (example: variables occupy RAM locations).

Cog RAM is different from Main RAM in the following ways:

  • Cog RAM is smaller and faster than Main RAM.
  • Cog RAM is a set of "registers" addressable only as longs (four bytes) while Main RAM is a set of "locations" addressable as bytes, words, or longs.
  • Propeller Assembly executes right from Cog RAM while Spin code is fetched and executed from Main RAM.
  • Cog RAM is available only to its own cog while Main RAM is shared by all cogs.

Once assembly code is loaded into Cog RAM, the cog executes it by reading a long (32-bit) opcode from a register (starting with register 0), resolving its destination and source data, executing the instruction (possibly writing the result to another register) and then moving on to the next register address to repeat the process. Cog RAM registers may contain instructions or pure data, and each may be modified as the result of the execution of another instruction.

...

Most instructions have two data operands; a destination value and a source value. For example, the format for an ADD instruction is:
add destination, #source
The destination operand is the 9-bit address of a register containing the desired value to operate on. The source operand is either a 9-bit literal value (constant) or a 9-bit address of a register containing the desired value. The meaning of the source operand depends on whether or not the literal indicator "#" was specified. For example:
add X, #25 'Add 25 to X
add X, Y 'Add Y to X
X long 50
Y long 10
The first instruction adds the literal value 25 to the value stored in the register X. The second instruction adds the value stored in register Y to the value stored in the register X. In both cases, the result of the addition is stored back into register X.
The last two lines define data symbols X and Y as long values 50 and 10, respectively. Since launching assembly code into the cog caused this data to enter Cog RAM right after the instructions, X naturally is a symbol that points to the register containing 50, and Y is a symbol that points to the register containing 10.
Thus, the result of the first ADD instruction is 75 (i.e.: X + 25 → 50 + 25 = 75) and that value, 75, is stored back in the X register. Similarly, the result of the second ADD instruction is 85 (i.e.: X + Y → 75 + 10 = 85) and so X is set to 85.

...

Make sure to enter the literal indicator, #, when a literal value (a.k.a. immediate value) is intended. Modifying the first line of the above example by omitting the # character (ex: ADD X, 25) causes the value in register 25 to be added to X instead of the value 25 being added to X.
Another possible mistake is to omit the # on branching instructions like JMP and DJNZ. If the intended branch destination is a label named MyRoutine, a JMP instruction should normally look like JMP #MyRoutine rather than JMP MyRoutine. The latter causes the value stored in the MyRoutine register to be used as the address to jump to; that's handy for indirect jumping but it is usually not the intention of the developer.

...

The source operand is only 9 bits wide; it can hold a value from 0 to 511 ($000 to $1FF). Keep this in mind when specifying literal values. If a value is too big to fit in 9 bits, it must be stored in a register and accessed via the register's address. For example:
add X, BigValue 'Add BigValue to X
X long 50
BigValue long 1024

...

To give names to special routines, Propeller Assembly code can make use of two types of labels: global and local.
Global labels look just like other symbols and follow the same rules as symbols; they begin with an underscore '_' or a letter and are followed by more letters, underscores, and/or numbers. See Symbol Rules, page , for more information.
Local labels are similar to global labels except they start with a colon ':' and must be separated from other same-named local labels by at least one global label. Here's an example:
Addition mov Count, #9 'Set up 'Add' loop counter
:loop add Temp, X 'Iteratively do Temp+X
djnz Count, #:loop 'Dec counter, loop back
Subtraction mov Count, #15 'Set up 'Sub' loop counter
:loop sub Temp, Y 'Iteratively do Temp-Y
djnz Count, #:loop 'Dec counter, loop back
jmp #Addition 'Go add more
This example has two global labels, Addition and Subtraction, and two local labels, both named :loop. The local labels can have the exact same name, :loop, because at least one global label separates them. In fact, this is the point of local labels; they indicate common, generic things like loops without requiring unique names for each of them.
The two DJNZ instructions are exactly the same, but they each jump to different places. The Addition routine's DJNZ jumps back to the local label :loop within Addition, and the Subtraction routine's DJNZ jumps back to the local label :loop within Subtraction.
Note that the DJNZ instructions use the literal indicator, #, and the exact name of the local label, including the colon. Without the # the code would execute improperly (jumping indirectly rather than directly), and without the colon the code would result in a compile error. For more Propeller Assembly format information, see Common Syntax Elements, page .