propeller spin address operator @
iammegatron
Posts: 40
I came upon this while reading Jon McPhalen's WS2812 LED driver. He used this "self-mod" code to pass parameters from the main routine to a cog, as follows: (line: 121; file: jsm_ws2812_ss)
command := @resetticks
cog := cognew(@ws2812ss, @command) + 1
My question is, why not directly
cog := cognew(@ws2812ss, @resetticks) + 1
Or, would
cog := cognew(@ws2812ss, @@resetticks) + 1
work?
However, for example, Chip Gracey's Full-Duplex Serial Driver, line 47
okay := cog := cognew(@entry, @rx_head) + 1
@rx_head is the address of a VAR, which is also the first element of a bunch of parameters (longs) or (similar to C's struct, I guess) passed into the new cog.
So what's the difference between the two?
command := @resetticks
cog := cognew(@ws2812ss, @command) + 1
My question is, why not directly
cog := cognew(@ws2812ss, @resetticks) + 1
Or, would
cog := cognew(@ws2812ss, @@resetticks) + 1
work?
However, for example, Chip Gracey's Full-Duplex Serial Driver, line 47
okay := cog := cognew(@entry, @rx_head) + 1
@rx_head is the address of a VAR, which is also the first element of a bunch of parameters (longs) or (similar to C's struct, I guess) passed into the new cog.
So what's the difference between the two?
Comments
as you said exactly!
same with command and this VAR contains the address of a long called resetticks, not the content.
command := @resetticks
cog := cognew(@ws2812ss, @command) + 1
is not the same as
cog := cognew(@ws2812ss, @resetticks) + 1
Enjoy!
Mike
In Chip Gracey's FulDuplexSerial, the spin code:
cognew(@entry, @rx_head)
is followed by propeller assembly code:
mov t1, par
in the assembly. So "t1" is holding the HUB-RAM address of VAR rx_head. In C's words, t1 is a pointer, pointing to rx_head. Because the assumption of continuous HUB-RAM layout, to access the next VAR rx_tail, you can do
add t1, #4 ' 4-byte = long
pointer arithmetic.
On the other hand, in Jon McPhalen's WS2812 LED driver code, the spin code:
command := @resetticks
cognew(@ws2812ss, @command)
is followed by propeller assembly code:
rdlong r1, par
So r1 is the same as t1 in the previous code, a pointer, pointing at the first VAR: resetticks. The rest is pointer arithmetic.
In other words, I didn't read careful enough to distinguish "mov r1, par" versus "rdlong r1, par". Just 2 different styles, nothing to do with the address operator "@" actually.
You can only pass one long aligned hub address (a regular value<<2, not very common)
As you're only allowed to pass one pointer you better make good use of it.
If Spin have 5 long VARs, you simple pass the location of the first one and the others will be PAR+4, PAR+8 etc
But if Spin's VAR's is a mix of VAR types and length, probably best to set up a structure VAR array of address pointers to these varied locations.
Use PAR as usual, but you would of course need to use the value it gets to do a second rdlong instruction, like Spin's @@ (indirect read)
So it all depends how many and/or the mixture of VARs you need to pass between Spin and Pasm how you should set up the "mailbox".
Remember that cognew is instantiating the cog, hence I want to put the address of command into the PAR register for later use. For setup, these variables have to be moved to the cog (I'm doing it this way to make the PASM easier to port to other langauges).
That is handled in the top of the cog with this -- as you pointed out -- self-modifiying code.
At the top we read from the address in PAR to the r1 register -- this is the address of resetticks in the hub (@resetticks). Note that r1 is used as the source field in the :read instruction. The destination of the :read instruction is set to the cog address of resettix (#resettix). The loop knows there are six parameters. The cog sees the hub as an array of bytes, hence 4 must be added to r1 to get the correct address of the next long (@t0h). The cog sees itself as an array of longs. The value of INC_DEST is a constant: 1 << 9; this increments the destination field by 1.
After these values are transfered the only thing the cog cares about is command; when not 0 this holds the output pin, the pixel count, and the hub address of the array to transmit. At the moment the code only supports 256 LEDs because I'm using a clean byte to hold the pixel count. Since we only need 5 bits to hold the pin, I can use those other 3 bits to extend the pixel count to > 2000. I'll do that later.