Assembly Programming - rosco-pc/propeller-wiki GitHub Wiki

Cog assembler programs are embedded within Spin source code by including them in a DAT section.

While compilation and assembly is underway there are two separate addressing concepts in play; the address of where the assembly code is in Hub memory ( and Eeprom and download image ) and the address in Cog memory when the assembly code is loaded for execution at runtime. These are very distinct addresses but both are important.

ORG

To ensure that an assembly program's Cog addressing is correct it must begin with an "ORG 0" directive. This ensures that ( regardless of where the assembly code may be in Hub memory ) Cog addressing is relative to the first instruction which is loaded into the Cog.

When a Cog program is loaded to a Cog from Hub memory, 496 longs of consecutive Hub memory are taken and placed into the Cog. If the Cog program is shorter than 496 longs, whatever follows it in Hub memory will also be placed in the Cog, however, this will be normally be ignored or be irrelevant to the Cog when executing.

Because of this it generally makes no sense to include any ORG directives within the Cog program other than the initial "ORG 0". Doing so will simply create a mismatch between the addressing the assembler maintains during assembly and what is ultimately placed in the Cog. Only if the Cog program itself explicitly relocates what is loaded to where it should be does using ORG make sense.

To ensure that a Cog program will fit within the Cog, the FIT directive may be used. It is recommended that every Cog program ends with a "FIT $1F0" to avoid errors when a Cog program exceeds its largest possible size.

Every assembler instruction of the Cog program will take up space within the Hub memory, as will every variable ( register or Cog memory location ) defined by a LONG directive.

RES

Variables may also be defined by using the RES directive. When this occurs, nothing will be placed in Hub memory, for the compiler and assembler the Hub address will remain unchanged, but the Cog address will change as appropriate. Loaded Cog program instructions will therefore have the correct addresses for those variables even though they take up no space in the Hub memory. The consequence of this is that mixing RES definitions with LONG definitions or assembler instructions will cause a mismatch of addressing when the Cog program is loaded to the Cog as a consecutive block of 496 longs and intended execution is unlikely to be achieved.

Variables defined using the RES directive should only appear at the end of an assembler program, after all other LONG definitions and assembler instructions have been specified. RES should only be used for variables which do not require an initial, pre-defined starting value before use, and ( to minimise Hub memory use ), variables which do not should be defined using RES.

Multiple DAT Sections

Assembly code and shared memory blocks may be split into multiple DAT sections within the same spin file. The address counter for each DAT segment starts where the previous segment stopped (first one starts at 0), unless there is an ORG directive.

References within assembly to DAT variables use the offset within the variable's own DAT section. If this affect is not anticipated, then you could find yourself writing unexpected self-modifying code. The compiler will NOT issue any sort of warning for the following code:


DAT
    ORG 0
v1  LONG 0
v2  LONG 0

DAT
    ORG 0
:L  MOV v2,#0     ' This line overwrites the next location in memory with a 0.
    JMP #:L       ' This line of code is corrupted by the time you get here.

Real programs, step 1: Flashing a LED

The following code will flash a LED connected to the pin 16. For that purpose a small two object files program will be used. The file ledflash.spin should look like:


VAR

  long  cog                           ' This is in hub ram

PUB start(pin) : okay

pinmask := |<pin                     ' This can be done because pinmask(cog ram) is filled before the data is loaded into
                                     ' the cog, notice pinmask is declared a long not a res.  After the cog is loaded
                                     ' the value cannot be modified in this way.

' Now to start the cog, notice the @entry, look at the assembly, you can see where it will begin

okay := cog := cognew(@entry, 0) + 1


PUB stop

'' Stop flashing - frees a cog

  if cog
    cogstop(cog~ -  1)


DAT
                      org                           ' Sets the origin at zero for this point
entry
                      or      DIRA,pinmask          ' Sets relevant pin to an output
                      or      OUTA,pinmask          ' Make pin high
                      mov     time,CNT              ' Current clock loaded into time
                      add     time,on_time          ' on_time added to time

:loop                 waitcnt time,off_time         ' waits for time to pass, off_time added to time automatically
                      xor     OUTA,pinmask          ' toggles pin
                      waitcnt time,on_time          ' waits for time to pass, on_time added to time automatically
                      xor     OUTA,pinmask          ' toggles pin
                      jmp     #:loop                ' loop, the # means that :loop is a literal (something typed into program
                                                    ' rather than a register).

                       ' These variables are in cog ram!

' Initialized data

on_time              long    80_000*500             ' on time in clock cycles, 500 * 80000 (cycles per millisecond)
off_time             long    80_000*500             ' off time, also 500 milliseconds
pinmask              long    0                      ' pinmask can be changed when the program is in HUB RAM

'**** ATTENTION, all res variables MUST come after other variables ************

' Uninitialized data

time                 res     1                      ' res is used only to create the label, so no code or other variables
                                                    ' except more res can be placed here, because no real space is used


The file ledflash_demo.spin should look like:


{
  Step 1 Flash an LED demo program.
  See ledflash.spin for full details.

}
CON

        _clkmode        = xtal1 + pll16x
        _xinfreq        = 5_000_000

OBJ

  led    : "ledflash"        ' The led object

PUB start

  'start led flashing
  led.start(16)              ' Start the led object (pin 16 flashes a VGA led on the demoboard)

  repeat   'loop forever more

See Also

Assembler Subroutines | Common Assembler Bugs