View Full Version : easy way to tell PASM-code HUB-RAM-variables adresses

03-20-2012, 04:46 PM
I played around a little bit with the capswitch-code (capacitive proximity sensor).
Code is attached.

When I analysed the code first I wondered where the heck gets this variable "reslt" updated??

Then I discovered in the PASM-DAT-section of the code a "PASM"-variable "reslt_ptr" is defined.

First thing that is done in the SPIN-code is assigning
reslt_ptr := @reslt

After this assigning the PASM-code can use this "PASM"-variable "reslt_ptr" to do HUB-RAM-writes to the SPIN-variable "reslt"
Wow cool so this is a second way how to tell PASM-code HUB-RAM-adresses. First way is the second parameter of cognew which starts PASM-code

Do I understand right that the assigning has to be done before starting the PASM-cog? The cognew(@PASM-entry..) copies the DAT-section to the cog-RAM
and this means all initialisation óf the PASM-code has to be done before starting the PASM-cog?

This second way save PASM-code to transfer HUB-RAM-adresses from the PAR-register to PASM-"variables"

Are there any cases where the method of cognew(@PASM-entry,@SPIN-Variable) has advanteges over the MySPINVar_ptr := @MySPINVar - method?

best regards

Duane Degn
03-20-2012, 05:45 PM
The problem with assigning location from Spin is the object can't be used by other languages besides Spin.

The perferred method of passing location is to use the address passed to par and use that address as the starting point and add 4 to get the next long location in hub (only pass long locations with par).

Poking the values in with Spin prior to lanching the code is easier though (which is why I usually do it that way).

If you want your code to be C friendly, you shouldn't go poking around in Spin.

Mike Green
03-20-2012, 06:00 PM
The main reason for these ways to supply hub addresses to PASM code is that the Spin compiler/assembler doesn't know where the hub variables will be placed at the time the PASM code is assembled and, as the various sections of the program are laid out in memory by a later phase of the compiler, there's no provision to "fix them up" to their absolute addresses. The Spin code can get an absolute address when it's running and can either modify the PASM code in hub memory before it's copied to a cog for execution or pass an address in PAR via the COGNEW call or build a table of addresses and pass the address of the table in PAR.

03-20-2012, 07:49 PM
Do I understand right that the assigning has to be done before starting the PASM-cog?

Yes, because the code resides in the hub at that point and your Spin code can get to it; once it's in the cog it's beyond reach.

An argument has been made -- and I have come to agree -- that the interface code should not modify the assembly code. While this is possible in Spin, it limits that object and the assembly code used in it to Spin projects. With PropGCC coming this is considered bad form (my opinion), and I just chastised myself in print no less (May Nuts & Volts column) for doing this in past projects.

I'm not a big C programmer but I do write objects that many find useful. My goal, then, is to have any PASM code I write be useful in my Spin projects, as well as be compatible with PropGCC (and other languages that support launching PASM code into its own cog). I realize that this can create some small redundancies, but it's worth it.

For example, in an AC phase dimmer I'm working on now, here is the interface and the associated PASM. Note that variables in the object are modified and then the first in the group is passed via the PAR register to the PASM code that knows how deal with things from there.


long cog ' cog running driver

long ch0pin ' io pins for AC
long ch1pin
long ch2pin
long ch3pin
long zcpin
long brpntr ' pointer to brightness array

byte brightness[4] ' dimmer levels, 0 to 255

pub start(ch0, zc)

'' Start 4-channel AC dimmer
'' -- user responsible for correct pin assignments
'' * no error checking!
'' -- assumes ch0..ch3 contiguous and in ascending order

return startx(ch0+0, ch0+1, ch0+2, ch0+3, zc)

pub startx(ch0, ch1, ch2, ch3, zc) | ok

'' Start 4-channel AC dimmer
'' -- user responsible for correct pin assignments
'' * no error checking!


longmove(@ch0, @ch0pin, 5) ' copy pins
brpntr := @brightness ' point to levels array

ok := cog := cognew(@dimmer, @ch0pin) + 1 ' start the dimmer cog

return ok


org 0

dimmer mov outa, #0 ' all outputs off
mov dira, #0 ' clear all outputs

mov t1, par ' start of structure
rdlong t2, t1 ' read ch0 pin
mov ch0mask, #1 ' convert to mask
shl ch0mask, t2
or dira, ch0mask ' make output

add t1, #4
rdlong t2, t1
mov ch1mask, #1
shl ch1mask, t2
or dira, ch1mask

add t1, #4
rdlong t2, t1
mov ch2mask, #1
shl ch2mask, t2
or dira, ch2mask

add t1, #4
rdlong t2, t1
mov ch3mask, #1
shl ch3mask, t2
or dira, ch3mask

add t1, #4
rdlong t2, t1
mov zcmask, #1
shl zcmask, t2

add t1, #4
rdlong hubch0, t1 ' read hub addr of levels array

03-20-2012, 08:52 PM
The only disadvantage coming into my mind is that you can't start the same code one after the other. You have to wait until the COGNEW is done with loading the code up to the 'injection variable' before you can inject the next and call a COGNEW again.

And to be honest that's what you should be allowed to do with most of the objects! Say you instanciate 3 full duplex serial interfaces. You'd have to wait until the first one is loaded into the COG before you can start the next one. Using PAR you can fire all three up in parallel which means call the start methods directly one after the other. (COGNEW does not wait for the COG to be loaded completely, it immediately returns and you can continue)