Shop OBEX P1 Docs P2 Docs Learn Events
Reduce a byte by percentage in fewest clocks in PASM — Parallax Forums

Reduce a byte by percentage in fewest clocks in PASM

ke4pjwke4pjw Posts: 1,155
edited 2022-10-18 01:50 in PASM2/Spin2 (P2)

I need to reduce a byte by a percentage in the fewest clocks. Even if the percentage resolution was 5 or 10%, that would be "good enough".

Are there any tricks for doing such a thing?

Comments

  • MUL + SHR, the obvious 4 clocks

    SCA / SCAS, 2 or 4 clocks depending what the next instruction is

    MULPIX if you have a bunch of unsigned bytes

    But the thing about all of these is they don't take 0-100 for the gain input.

    MOV  x,percent_reduction  ' once
    SUBR x,#100               ' once
    MUL  x,#328               ' once
    MUL  data,x               ' every time
    SHR  data,#15             ' every time
    

    The goal here is to stay within the 16x16 multiply that the P2 can do in 2 clocks.
    328 = 2.56 * 128 + a round-up, so 255 input = 255 output
    328 * 100 = 32800, ok for 16 bits
    655 * 100 = 65500, ok for 16 bits, but 255 input is reduced to 254
    656 * 100 = 65600, overflows 16 bits, max 65535

  • JonnyMacJonnyMac Posts: 9,104
    edited 2022-10-18 13:09

    Is this for your lighting controller? If that's the case, isn't the level going to be expressed as 0..255 for 0..100%? If yes, this is in my pixel driver; you could remove the checks for < 0 or > 255 since the adjustment level is coming from a control device. This scales all three/four bytes in the pixel color with the mulpix instruction (as James suggested above).

    pub scale_rgbw(rgbw, level) : result
    
    '' Scales rgbw value to level
    '' -- level is brightness, 0..255 (0..100%)
    
      org
                    cmps      level, #0                     wcz     ' 0 or lower?
          if_be     jmp       #.done                                '  yes, return 0
    
                    mov       result, rgbw                          ' get input color
    
                    cmps      level, #255                   wcz     ' 255 or higher?
          if_ae     jmp       #.done                                '  yes, return input
    
                    movbyts   level, #%%0000                        ' make multiplier
                    mulpix    result, level                         ' scale the color
    .done
      end
    
  • Thanks guys. I didn't even realize we had a MUL instruction! I was looking for something as a shortcut, but apparently it can just be done in a strightforward manner. Most likely I will perform this operation on the input.

    @JonnyMac Yes, this is for the light controller. Unfortunately, the data structure I use in my modified version of your driver is slightly different, as I don't support RGBW, just RGB. The reasoning is because at the time I was unsure how to fill the W in my E1.31 driver. Now I know how to, so that is some technical debt. The other was to save memory, as I wanted to scale to tens of thousands of pixels. Still unsure how I want to do this, but supporting RGBW would be nice. mulpix looks very interesting, but there is little to no documentation in the PASM manual. I may have a look at the hardware manual to see if I can find more detail.

  • JonnyMacJonnyMac Posts: 9,104
    edited 2022-10-18 16:50

    I think Evan or one of the other PASM gurus suggested mulpix to me. The dest field is the value you want adjusted -- and it is adjusted byte-by-byte based on what's in the source field. Here's a simple Spin method that does the same thing:

    pub mul_pix(value, adjust) | x
    
      repeat x from 0 to 3
        value.byte[x] := value.byte[x] * adjust.byte[x] / 255
    
      return value
    

    In my code I use the movbyts instruction to copy the adjustment (0..255) to all four bytes of that long. This allows the level to be applied to all four bytes of the color.

  • @JonnyMac said:
    I think Evan or one of the other PASM gurus suggested mulpix to me. The dest field is the value you want adjusted -- and it is adjusted byte-by-byte based on what's in the source field. Here's a simple Spin method that does the same thing:

    pub mul_pix(value, adjust) | x
    
      repeat x from 0 to 3
        value.byte[x] := value.byte[x] * adjust.byte[x] / 255
    
      return value
    

    Actually, it's

    pub mul_pix(value, adjust) | x
    
      repeat x from 0 to 3
        value.byte[x] := ( value.byte[x] * adjust.byte[x] ) / 256 ' could use >> 8 aswell
    
      return value
    
  • Also, instead of

                    movbyts   level, #%%0000                        ' make multiplier
                    mulpix    result, level                         ' scale the color
    

    you can use SETPIV and BLNPIX, with the caveat that the control goes the other way (255 means all black), but the advantage that you can use any color to fade toward instead of #0:

                    setpiv    level                                 ' only needed once, value sticks
                    blnpix    result, #0
    

    or, if one really wants that 0 = black, MIXPIX

                    setpix    #%010_011                             ' magic number, see manual. Also sticks.
                    setpiv    level                                 
                    mixpix    result, #0
    
  • @JonnyMac I took a look at my modifications. I can absolutely do this in the pixel driver, as I only shift out bytes 0,1,2 and ignore byte 3. Thank you sir!

  • So dumb question. I walked through @SaucySoliton 's steps with a calculator, and indeed it is a percent reduction. ie 0% reduction of 255 returns 255. 100% reduction in 255 returns 0.

    Does mulpix work the same way?

    Also I assume mulpix performs the multiply and shift right on every byte in the long?

  • The mulpix instruction treats 255 like 1.0, so

            mulpix    pixel, ##$80808080
    

    ...would set all the bytes in pixel to half of their present level. Honestly, I don't know what's happening at the microcode level, but the results match the description.

  • @JonnyMac said:
    Honestly, I don't know what's happening at the microcode level, but the results match the description.

    The silicon manual explains it:

    (That also means the equivalent code above is wrong, for it fails to take the $FF bias into account. Didn't realize that was there)

  • I hopped on Discord and @Wuerfel_21 helped me understand what to do for this specific use case. The code in question is as follows:

                    mul lbrightness,##652
                    movbyts lbrightness,#%%1111
                    mulpix pixel,lbrightness
    

    And here is the result:

    Thank you all so much!

Sign In or Register to comment.