Persistent variables in PASM in-line code?
bob_g4bby
Posts: 552
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
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.
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...
Other compilers are going to have different restrictions though, as I recall...
You can copy a hub array to the PRx regs with setregs().
You can copy all of the PRx registers to the hub with getregs().
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.
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.
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...
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 endNow I prefer this:
pub inline_pasm(param) : result org mov pr0, param ret tvar1 res 1 tvar2 res 1 endIf 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 endFlexspin 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