Shop OBEX P1 Docs P2 Docs Learn Events
Use of PASM res statement — Parallax Forums

Use of PASM res statement

David BetzDavid Betz Posts: 14,516
edited 2013-12-19 08:23 in Propeller 1
Will the following code work? I have a feeling that someone once told me that I had to put all of my "res" statements at the end of my PASM code. Should I change all of the "res 1" statements to "long 0"?
'' Modified for use with PropGCC by David Betz
''  Derived from:
''   File....... jm_ws2812.spin
''   Purpose.... 800kHz driver for WS2812 LEDs
''   Author..... Jon "JonnyMac" McPhalen
''               Copyright (c) 2013 Jon McPhalen

pub driver
  return @ws2812

dat
                        org     0

ws2812                  jmp     #ws2812_cont

txpin                   res     1                               ' tx pin #
ledcount                res     1                               ' # of rgb leds in chain
resettix                res     1                               ' frame reset timing
bit0hi                  res     1                               ' bit0 high timing
bit0lo                  res     1                               ' bit0 low timing
bit1hi                  res     1                               ' bit1 high timing    
bit1lo                  res     1                               ' bit1 low timing     
hubpntr                 res     1                               ' pointer to rgb array

ws2812_cont             mov     txmask, #1                      ' create mask for tx
                        shl     txmask, txpin
                        andn    outa, txmask                    ' set to output low
                        or      dira, txmask

Comments

  • Cluso99Cluso99 Posts: 18,069
    edited 2013-12-19 04:20
    RES only reserves space so itbumps the pc, but it doesnt put any data there. So you need to move them to the end of you code or make them longs.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-12-19 04:23
    Cluso99 wrote: »
    RES only reserves space so itbumps the pc, but it doesnt put any data there. So you need to move them to the end of you code or make them longs.
    Thanks! I thought I remembered that. It's different than any other assembler I've used though. You'd think "res" which presumably stands for "reserve" would actually reserve space. :-)
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-12-19 06:09
    I just tested this and it seems to work if you place the org below the res statements. Your first line of code does still need to be below the res's.
    CON
      _clkmode = xtal1 + pll16x                                                   
      _xinfreq = 5_000_000
    
    VAR
      long  LED_pin
      long  rate
      
    PUB Main
      LED_pin := 16
      rate := clkfreq / 10
      cognew(@pasm,@LED_pin)
    
    DAT                  
    PASM
    
    t1                      res       1  
    LED_mask                res       1 
    t2                      res       1 
    delay_time              res       1
    
                            org
    
    _1                      mov       t1,par                      ' This line overwrites itself                      
    _2                      mov       LED_mask,#1                 ' as does this one                 
    _3                      rdlong    t2,t1                       ' and this one                       
    _4                      shl       LED_mask,t2                 
                            add       t1,#4
                            rdlong    delay_time,t1               ' This line overwrites _4
                            or        dira,LED_mask
                            mov       cnt,delay_time
                            add       cnt,cnt
    :loop
                            waitcnt   cnt,delay_time
                            xor       outa,LED_mask
                            jmp       #:loop
    
    {{
    If the res's are located at the bottom, _1 through _4 contain:
    _1     %10100000101111000111011111110000 
    _2     %10100000111111000111100000000001 
    _3     %00001000101111000111101000111011 
    _4     %00101100101111000111100000111101
    
    With the res's located above the org,
    _1     %00000000000000000000000011111100
    _2     %00000000000000010000000000000000
    _3     %00000000000000000000000000010000
    _4     %00000000011110100001001000000000
    }}
    
    It might be useful for saving cog memory by overwriting one-time instructions.
    Dunno if I'd ever use it simply due to how unreadable / unmaintainable it could make the code if used for anything more complicated.
  • AribaAriba Posts: 2,690
    edited 2013-12-19 07:54
    David Betz wrote: »
    Will the following code work? I have a feeling that someone once told me that I had to put all of my "res" statements at the end of my PASM code. Should I change all of the "res 1" statements to "long 0"?
    '' Modified for use with PropGCC by David Betz
    ''  Derived from:
    ''   File....... jm_ws2812.spin
    ''   Purpose.... 800kHz driver for WS2812 LEDs
    ''   Author..... Jon "JonnyMac" McPhalen
    ''               Copyright (c) 2013 Jon McPhalen
    
    pub driver
      return @ws2812
    
    dat
                            org     0
    
    ws2812                  jmp     #ws2812_cont
    
    txpin                   res     1                               ' tx pin #
    ledcount                res     1                               ' # of rgb leds in chain
    resettix                res     1                               ' frame reset timing
    bit0hi                  res     1                               ' bit0 high timing
    bit0lo                  res     1                               ' bit0 low timing
    bit1hi                  res     1                               ' bit1 high timing    
    bit1lo                  res     1                               ' bit1 low timing     
    hubpntr                 res     1                               ' pointer to rgb array
    
    ws2812_cont             mov     txmask, #1                      ' create mask for tx
                            shl     txmask, txpin
                            andn    outa, txmask                    ' set to output low
                            or      dira, txmask
    

    Having all RES at the end is the simplest to make it always work.
    But if you know what you do, then you can use it for some tricks like in the code snippet you posted. Here the RES is used to give CogReg1 ..8 alternative names, to use it later as variables that overlay the Init code, which is only used once at startup of the cog.
    ( I think thats the case here, but without seeing the whole code I can't tell for sure).

    Andy

    Edit: Looking again at the code, this only would work if there is another ORG 1 before the Init code.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-12-19 08:02
    Ariba wrote: »
    Having all RES at the end is the simplest to make it always work.
    But if you know what you do, then you can use it for some tricks like in the code snippet you posted. Here the RES is used to give CogReg1 ..8 alternative names, to use it later as variables that overlay the Init code, which is only used once at startup of the cog.
    ( I think thats the case here, but without seeing the whole code I can't tell for sure).

    Andy
    I've modified it a bit since my original post but here is the current code:
    '' Modified for use with PropGCC by David Betz
    ''  Derived from:
    ''   File....... jm_ws2812.spin
    ''   Purpose.... 800kHz driver for WS2812 LEDs
    ''   Author..... Jon "JonnyMac" McPhalen
    ''               Copyright (c) 2013 Jon McPhalen
    
    {{
        // parameter structure
        typedef struct {
            uint32_t pin;
            uint32_t *colors;
            uint32_t count;
        } ws2812_params;
        
        // driver header structure
        typedef struct {
            uint32_t    jmp_inst;
            uint32_t    resettix;
            uint32_t    bit0hi;
            uint32_t    bit0lo;
            uint32_t    bit1hi;
            uint32_t    bit1lo;
        } ws2812_hdr;
    }}
    
    pub driver
      return @ws2812
    
    dat
                            org     0
    
    ws2812                  jmp     #ws2812_cont
    
    resettix                long    0                               ' frame reset timing
    bit0hi                  long    0                               ' bit0 high timing
    bit0lo                  long    0                               ' bit0 low timing
    bit1hi                  long    0                               ' bit1 high timing    
    bit1lo                  long    0                               ' bit1 low timing     
    
    ws2812_cont             mov     t1, par                         ' get the pin number
                            rdlong  t2, t1
                            mov     txmask, #1                      ' create mask for tx
                            shl     txmask, t2
                            andn    outa, txmask                    ' set to output low
                            or      dira, txmask
                            add     t1, #4
                            rdlong  hubpntr, t1                     ' get the buffer address
                            add     t1, #4
                            rdlong  ledcount, t1                    ' get the led count
                            mov     t2, #0                          ' init is done
                            wrlong  t2, t1
                            
    rgbmain                 mov     bittimer, resettix              ' set reset timing  
                            add     bittimer, cnt                   ' sync timer 
                            waitcnt bittimer, #0                    ' let timer expire                             
                            
                            mov     addr, hubpntr                   ' point to rgbbuf[0]
                            mov     nleds, ledcount                 ' set # active leds
    
    frameloop               rdlong  colorbits, addr                 ' read a channel
                            add     addr, #4                        ' point to next
    
    ' Shifts long in colorbits to WS2812 chain
    '
    '  WS2812 Timing (slot is 1.25us for 800kHz)
    '
    '  0      0.35us / 0.80us
    '  1      0.70us / 0.60us
    '
    '  At least 50us (reset) between frames
    '
    '  This routine manipulates the value in colorbits so that elements are
    '  transmitted GRB as required by the WS2812. Rearrange-on-the-fly code
    '  trick by TonyP12 in the Propeller forum.  
    '
    '  Entry      $00_RR_GG_BB
    '  Step 1     $BB_00_RR_GG
    '  Step 2-24  $GG_BB_00_RR - when nbits == 24
    '             $BB_00_RR_GG - after sending GG
    '  Step 2-16  $RR_GG_BB_00 - when nbits == 16
    '             $GG_BB_00_RR - after sending RR
    '  Step 2-8   $BB_00_RR_GG - when nbits == 8
    
    shiftout                ror     colorbits, #8                   ' {1} to offset the rol 24 below
                            mov     nbits, #24                      ' shift 24 bits (3 x 8) 
                            
    :loop                   test    nbits, #%111            wz      ' {2} nbits at 24, 16 or 8?
            if_z            rol     colorbits, nbits                ' if yes, modify colorbits 
                            rol     colorbits, #1           wc      ' msb --> C
            if_c            mov     bittimer, bit1hi                ' set bit timing  
            if_nc           mov     bittimer, bit0hi                
                            or      outa, txmask                    ' tx line 1  
                            add     bittimer, cnt                   ' sync bit timer  
            if_c            waitcnt bittimer, bit1lo                
            if_nc           waitcnt bittimer, bit0lo 
                            andn    outa, txmask                    ' tx line 0             
                            waitcnt bittimer, #0                    ' hold while low
                            djnz    nbits, #:loop                   ' next bit
    
                            djnz    nleds, #frameloop               ' done with all leds?
    
                            jmp     #rgbmain                        ' back to top
    
    ' --------------------------------------------------------------------------------------------------
    
    hubpntr                 res     1                               ' pointer to rgb array
    ledcount                res     1                               ' # of rgb leds in chain
    
    txmask                  res     1                               ' mask for tx output
    
    bittimer                res     1                               ' timer for reset/bit
    addr                    res     1                               ' address of current rgb bit
    nleds                   res     1                               ' # of channels to process
    colorbits               res     1                               ' rgb for current channel
    nbits                   res     1                               ' # of bits to process
    
    t1                      res     1                               ' work vars
    t2                      res     1
    
                            fit     496                                    
    {{
    
      Terms of Use: MIT License
    
      Permission is hereby granted, free of charge, to any person obtaining a copy of this
      software and associated documentation files (the "Software"), to deal in the Software
      without restriction, including without limitation the rights to use, copy, modify,
      merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      permit persons to whom the Software is furnished to do so, subject to the following
      conditions:
    
      The above copyright notice and this permission notice shall be included in all copies
      or substantial portions of the Software.
    
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
      CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
      OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    
    }}
    
    The idea is that the C code that loads this driver will overwrite the variable in right after the initial jmp instruction with values it computes for the protocol timing. This eliminates having to pass each of these as parameters to the COG code and then fetch them to store them into local registers. It's how a number of the PropGCC drivers are written. So overlaying instructions won't really work here. To get around that problem I've changed all of the "res 1" statements to "long 0".

    By the way, thanks JonnyMac for your elegant code!
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-12-19 08:18
    You're welcome! -- and I'm looking forward to learning how to port my PASM drivers to Prop GCC. Honestly, that has been my hold-up with moving over to C (lack of understanding the multi-core approach in C which is so easy in Spin).

    BTW, and it's noted in the code, I got a great assist from TonyP12 with rearranging the RGB long to GRB.

    Just last night a friend pointed out that Adafruit has a link to my WS2812 object in ObEx. As a community, we could probably do more to show them that the Arduino, while the most popular kid in school right now, isn't necessarily the smartest. With the popularity of WS2812 LED strips and modules, your project in C could well be an invitation for those struggling to get more performance out of their Arduinos.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-12-19 08:23
    Thanks Jon. I'll work more on this later this evening. Unfortunately, I have to do my day job now. :-)
Sign In or Register to comment.