Shop OBEX P1 Docs P2 Docs Learn Events
Need an inline PASM function — Parallax Forums

Need an inline PASM function

RS_JimRS_Jim Posts: 1,753
edited 2023-04-07 11:42 in PASM2/Spin2 (P2)

Can I get one of you PASM experts to create an inline function out of this equation?
`
{to clarify: x is the Var that changes each pass}
result=(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;}

`
all of the inputs and outputs are integers with the largest being 3300 and the smallest 0. Out min is 0 and out max is 200 in min lowest is 0 highest is 3300. X is automatically constrained by the environment and will never fall outside the the in_min and in_Max.
Thanks in advance.
Jim
Edit: added some clarification

Comments

  • JonnyMacJonnyMac Posts: 8,924
    edited 2023-04-07 01:04

    I'm not a PASM expert, but do dabble a bit.

    It sounds like you want to constrain an input between 0..3300 and scale it to 0..200.

    pub scale(x) : result
    
      x := 0 #> x <# 3300
    
      return x * 200 / 3300
    
    
    
    pub scale_pasm(x) : result
    
      org
                            fges      x, #0
                            fles      x, ##3300
                            mul       x, #200
                            qdiv      x, ##3300
                            getqx     result
      end
    

    In a timing test of both passing a variable to the method the first ran in 3.64us, the inline PASM version in 3.36us. If you need it to go faster you can install the PASM in the top part of the interpreter so you don't have the overhead of loading it every time.

    Update: I tried the technique of installing the PASM code in the interpreter cog. It still requires a Spin interface, and is faster than native Spin, but not quite as fast as the inline version of the code.

  • JonnyMacJonnyMac Posts: 8,924
    edited 2023-04-07 01:36

    Forget all that -- this is fastest (by 2x) since you're doing a simple mx equation.

      scaled := (0 #> raw <# 3300) sca 260_301_049
    

    The value for sca is 200 / 3300 * (2^32)

    It's even faster (by 2x again) if your input value doesn't need to be constrained.

  • Thanks jon,
    I have to think about about this for a while. What I see you doing is letting the compiler do some of the calculations on the numbers and only calculating on the fly the changing var.
    Jim

  • @RS_Jim : It's worth mentioning here that @JonnyMac is using the standard PNut / PropTool Spin2 interpreter. If you use a different compiler or interpreter your results will vary -- for example, in flexspin the inline PASM is actually slower than the regular method, because flexspin is compiling the code to PASM anyway. Even in flexspin the SCA operator method that Jon posted seems to be the fastest approach, so that's probably the best way to go.

  • @ersmith,
    Thanks for the input Eric. Yes I work in Linux and use only flexprop. I will see what happens when I use Jon's method.
    Jim

  • Wuerfel_21Wuerfel_21 Posts: 4,462
    edited 2023-04-07 12:59

    What Eric didn't mention: On FlexSpin, you generally want to use the ASM/ENDASM style inline assembly rather than ORG/END. The former is processable by the optimizer and can thus be inlined...

    You can go even faster if you use a somewhat lower precision approximation that can run on the fast multiplier

    pub scale_inline(x) : result
    
      asm
                            fges      x, #0
                            fles      x, ##3300
                            mul       x, #round((220.0 / 3300.0) 65536.0)
                            shr       x, #16
                            mov       result,x
      endasm
    

    or with slightly different rounding:

    pub scale_inline(x) : result
    
      asm
                            fges      x, #0
                            fles      x, ##3300
                            mul       x, #round((220.0 / 3300.0) 65536.0)
                            shr       x, #16    wc
                            addx      x,#0
                            mov       result,x
      endasm
    
  • Yes I work in Linux and use only flexprop

    Why, then, would you ask for PASM when you're using a tool that already outputs PASM?

    What I see you doing is letting the compiler do some of the calculations on the numbers and only calculating on the fly the changing var.

    I just simplified the expression. From your description, it would have been:

      scaled := (raw - 0) * (200 - 0) / (3300 - 0) + 0
    

    Internally, the sca instruction is identical to what Ada suggested. The constant used in that version represents 200/3300 but for 32 bits (Ada is using 16, which is probably fine for such a small output range).

  • @JonnyMac said:

    Yes I work in Linux and use only flexprop

    Why, then, would you ask for PASM when you're using a tool that already outputs PASM?

    Well, flexspin can very rarely choose MUL(S) over QMUL, so explicitly spelling things like that out in ASM is actual helpful. (In particular, both sides of a multiply need to either be a small constant, a word/byte sized variable or a zerox/signx sub-expression for flexspin to generate a MUL or MULS)

Sign In or Register to comment.