Shop OBEX P1 Docs P2 Docs Learn Events
Persistent variables in PASM in-line code? — Parallax Forums

Persistent variables in PASM in-line code?

The values saved in local variables in an In-Line PASM method are not saved from call to call of the method. I have a need in several inline PASM methods to save internal state between calls to the methods - for the "correct IQ" and "automatic gain control" functions for example. What's the neatest way to achieve that, please?
Cheers Bob

Comments

  • JonnyMacJonnyMac Posts: 9,693

    There are eight longs in the cog called pr0..pr7 that are intended for Spin-to-cog communications, and are not used by the interpreter.

    If you can live with 8 longs, you're good to go with something like this.

  • RaymanRayman Posts: 16,080

    The Spin2 docs have some suggestions:

    Read and write the following cog register and LUT areas:
    $000..$11F, which may contain PASM code and/or data which you previously loaded.
    $1D8..$1DF, which are general-purpose registers, named PR0..PR7, available to both PASM and Spin2 code.
    $1E0..$1EF, which are available for scratchpad use, but will likely be rewritten when Spin2 resumes.
    $1F0..$1FF, which include IJMP3, IRET3, IJMP2, IRET2, IJMP1, IRET1, PA, PB, PTRA, PTRB, DIRA, DIRB, OUTA, OUTB, INA, and INB.
    LUT $000..$00F, which are available for any use and ideal for streamer modes which use the LUT.
    Avoid writing to registers $120..$1D7 and LUT RAM $010..$1FF, since the Spin2 interpreter occupies these areas. You can look in "Spin2_interpreter.spin2" to see the interpreter code.

    See the "CALLING PASM FROM SPIN2" section...

  • RaymanRayman Posts: 16,080

    Other compilers are going to have different restrictions though, as I recall...

  • JonnyMacJonnyMac Posts: 9,693
    edited 2026-02-23 15:58

    You can copy a hub array to the PRx regs with setregs().

      setregs(@array, $1D8, 8)
    

    You can copy all of the PRx registers to the hub with getregs().

      getregs(@array, $1D8, 8) 
    

    If you need more than 8 longs you could map them to the upper user area of the cog RAM. As Ray points out, those addresses are $000..$11F (288 longs) -- most inline code is not going to be that long, so you could map your array to the top of that space (it's possible to build custom instructions this way). I would suggest leaving a bit of buffer at the top of that space so you don't accidentally bang into the interpreter code. Let's say you need 32 longs; you could map those to cog RAM $0F0..$10F, and still have 240 longs for inline code/vars.

  • The iq correction method (I'm cribbing from a windows project) has two 32 member arrays of longs and a single long that have to persist, so I could keep those in hub ram and have SPIN2 point to them via PR0-PR2, I suppose. That leaves the call to the inline method nice and simple as correctiq(buffer_in, buffer_out), the two parameters being pointers to 2048 member arrays of longs - the iq signal buffers.

  • JonnyMacJonnyMac Posts: 9,693
    edited 2026-02-23 19:58

    It just dawned on me that Spin and PASM understand pr0..pr7 so there is no need for inline PASM to set or get one of those registers -- using Spin's inherent indexing does the trick without a special method.

  • RaymanRayman Posts: 16,080

    Guess we are supposed to use ret on the last line of inline assembly?
    don't do it myself, but guess it is more efficient...

  • JonnyMacJonnyMac Posts: 9,693
    edited 2026-02-23 23:09

    The goal is to not let the code hit any vars or tables we've included as part of the inline code so they don't get misinterpreted as instructions -- that could cause problems.

    I used to do this:

    pub inline_pasm(param) : result
    
      org
                            mov       pr0, param
                            jmp       #.done
    
    tvar1                   res       1
    tvar2                   res       1
    
    .done 
      end
    

    Now I prefer this:

    pub inline_pasm(param) : result
    
      org
                            mov       pr0, param      
                            ret
    
    tvar1                   res       1
    tvar2                   res       1
      end
    

    If there are no vars or tables at the end, we can just let the code run out because the Spin2 compiler will insert ret at the end (this is why the jump to .done works).

    pub inline_pasm(param) : result
    
      org
                            mov       pr0, param      
      end
    
  • evanhevanh Posts: 17,084
    edited 2026-02-23 23:42

    Flexspin does not support setregs() nor getregs(). Instead, I do a simple fast copy using SETQ + RDLONG as first two inline instructions for equivalent of setregs(). I've not yet needed a getregs() equivalent.

    However, unlike setregs(), the RES variables only exist for the inline pasm, vanishing at the RET. Eg:

    PS: Presumably, the cmdset[] could be done as a structure now too.

    VAR
        long cmdset[6]    ' p_clk, p_cmd, m_align, v_nco, m_ca, m_se1
    
    
    PRI  tx_command( cmd, arg ) : rc  ' rc = 0:CMD pin stuck low
    
        rc := @cmdset
        org
            setq    #5
            rdlong  p_clk, rc
            ...
            ...
            ret
    
    lnco        long $8000_0000
    p_clk       res 1
    p_cmd       res 1
    m_align     res 1
    v_nco       res 1
    m_ca        res 1
    m_se1       res 1
        end
    
Sign In or Register to comment.