Shop OBEX P1 Docs P2 Docs Learn Events
propeller spin address operator @ — Parallax Forums

propeller spin address operator @

iammegatroniammegatron Posts: 40
edited 2015-02-25 12:27 in Propeller 1
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?

Comments

  • msrobotsmsrobots Posts: 3,709
    edited 2015-02-24 19:43
    Double indirection.

    as you said
    @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.
    exactly!
    same with command
    @command is the address of a VAR, (long) passed into the new cog.
    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
  • iammegatroniammegatron Posts: 40
    edited 2015-02-25 08:22
    Here is why I got puzzled. There are many ways to pass parameters from a spin program (running in HUB-RAM address space) to propeller assembly (running in 512-long cog ram space).


    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.
  • tonyp12tonyp12 Posts: 1,951
    edited 2015-02-25 09:00
    >There are many ways to pass parameters from a spin program

    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".
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-02-25 12:27
    My question is, why not directly

    cog := cognew(@ws2812ss, @resetticks) + 1


    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).
    long  resetticks                                              ' ticks in reset period
      long  t0h                                                     ' bit0 high time (ticks)      
      long  t0l                                                     ' bit0 low time
      long  t1h                                                     ' bit1 high time
      long  t1l                                                     ' bit1 low time
      long  rgfix                                                   ' swap
    


    That is handled in the top of the cog with this -- as you pointed out -- self-modifiying code.
    ws2812ss                rdlong  r1, par                         ' hub address of parameters -> r1
                            movd    :read, #resettix                ' location of cog parameters -> :read(dest)
                            mov     r2, #6                          ' get 6 parameters
    :read                   rdlong  0-0, r1                         ' copy parameter from hub to cog
                            add     r1, #4                          ' next hub element
                            add     :read, INC_DEST                 ' next cog element                         
                            djnz    r2, #:read                      ' done?
    


    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.
Sign In or Register to comment.