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