Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Tricks & Traps (Last update 21 June 2007) - Page 3 — Parallax Forums

Propeller Tricks & Traps (Last update 21 June 2007)



  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2007-06-20 05:52
    Right you are on both counts. Good eye! I've made the requisite changes to the document in the first post.

  • mparkmpark Posts: 1,305
    edited 2007-06-20 13:56
    I'm glad that's straightened out, but now the example hardly seems to be about indirect JMP at all. Perhaps it would be more compelling if it used an explicit state variable?
    Init         MOV State,#State1
    '            NOP <- no longer needed, I guess
                 JMP State
    State1       do state1 stuff
    Common       common post-state code
                 JMP State
    State        RES 1

    Post Edited (mpark) : 6/20/2007 2:22:00 PM GMT
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2007-06-20 17:50
    Yeah, I thought about that, too, as I was posting the error correction. But I was in such a hurry just to fix it that I didn't follow through with the tighter, more illustrative code. That has now been remedied.

    Thanks for the prodding! smile.gif
  • mparkmpark Posts: 1,305
    edited 2007-06-21 07:00
    One more thing while I have your attention: The last trick, on sign-extension, says

    In Spin, do this:
    Value := Value << 24 ~> 24

    Wouldn't it be more idiomatic to do this instead:


    I know the trick is about sign-extending from any bit, but using a byte as an example made me half expect that the trick would be the ~ operator. (Of course, for C/C++ guys, this meaning of ~ may be more of a trap than a trick.)
    Perhaps the text could mention the two sign-extension operators ~ and ~~, then describe the left/right shifty trick for arbitrary sign bit positions, with an example that uses an oddball shift count rather than 24 or 16. It could also mention that execution time is independent of shift count, assuming that's the case, which I believe it is.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2007-06-21 17:42
    'Another good suggestion, which I've now incorporated.

    You're right about the execution time of the shifts. The Propeller uses a barrel shifter, rather than an incremental one. However, since this is neither a trick nor a trap, there wasn't a good place to incorporate such info in the document.

  • mparkmpark Posts: 1,305
    edited 2007-07-03 07:44
    A trick suggestion: I thought the use of jmpret as a task dispatcher in tv_drv was pretty darn clever. Some coroutine action going on there.

    A trap suggestion: Square brackets mean array indexing -- except when they don't. After INA, DIRA, etc., they are pin specifiers. I just find that sort of special-case hijinx annoying (probably just me).
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-07-04 05:32
    mpark said...
    ....·Square brackets mean array indexing -- except when they don't. ...
    A nice list would summarize all the various parentheticals (and where they are used) be they (), [noparse]/noparse or what have you. The compiler sure does (expletive) obscurely when you·get them wrong...
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-21 14:52
    The COGNEW/COGINIT Spin statements allow an arbitrary Spin method call as the first parameter including a call of a method in another object (like foobah.myMethod). This is accepted by the compiler without comment, but will not be executed properly. The method must be one in the same object as the COGNEW/COGINIT call.
  • Jasper_MJasper_M Posts: 222
    edited 2007-08-25 17:00
    2 traps:

    values in DAT section byte etc. are not range checked (1.05.5):
    byte $555

    will silently fail, and it's equivalent to byte $55.

    EDIT: I couldn't replicate the following trap when I tried with simple code...

    x := 1 + y

    will first write 1 into x, then add y to that.
    So if you have an assembly driver polling for that location, it will see it when it's 1. eg. I had hard time debugging the following code:
        s_ras_cmd := const#CMD_SAVE_MAP + p_tilemap_index

    The following works:

        x := const#CMD_SAVE_MAP + p_tilemap_index
        s_ras_cmd := x 

    Post Edited (Jasper_M) : 8/25/2007 5:51:06 PM GMT
  • Jasper_MJasper_M Posts: 222
    edited 2007-08-25 17:46
    Your code has a bug, rdlong RA, opflag WZ reads address 0 in hub ram. you should pass a pointer of opflag in PAR etc. EDIT: A post got deleted between my messages.

    I wrote some test code, but now I can't even replicate the bug myself... but the temporary variable really solved the problem :/
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-25 17:51
    Sorry, Jasper, I was unsure for a second.. No, my program is fine smile.gif But I cannot produce your bug
     Exploring JASPER's Bug  v04
     2007 deSilva 
      _clkmode = xtal1 + pll8x
      _xinfreq = 10_000_000
      pc   :       "PC_Interface"  ' Ariba's PropTerminal
    PUB main
      alarm := @alarm
      opflag := @opflag
        pc.str(string("checking JASPER's trap any key"))
        if alarm
           pc.str(string(13, "There's a fly in the ointment"))
           alarm := 0 
        if pc.gotKey   ' press a key to show the bug (?)
          ' according to the report, first 0 should be written to opflag
          opflag := 0 + takeYourTime 
    pri takeYourTime
        return 1  ' change this to 0 and the programm will trigger...
        org    0
        ' Wait for opflag ==0
        rdlong RA, opflag  WZ
        if_Z   wrlong  opflag, alarm  'write any value as long as its not 0  
        jmp    #asm
      RA       long 0 
      alarm    long 0
      opflag   long 0

    Post Edited (deSilva) : 8/25/2007 11:45:37 PM GMT
  • Jasper_MJasper_M Posts: 222
    edited 2007-08-25 20:25
    EDIT: POST REMOVED : D I just didn't read the whole code, there¨s nothing wrong with it.

    Post Edited (Jasper_M) : 8/25/2007 8:34:02 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-25 21:38
    There was no good countercheck in the program, I tried this, and it triggers:
      if pc.gotKey   ' press a key to show program works
          ' countercheck
          opflag := 0
          opflag := 1

    ... and I finally found the "bug" in it: It has to be "ointment" not "oinment" - idiot that I am.

    Post Edited (deSilva) : 8/25/2007 11:43:10 PM GMT
  • hippyhippy Posts: 1,981
    edited 2007-09-21 20:38
    A trick for saving stack space is to use the 'result' variable associated with each method, whether it returns a result or not. A benefit is that unlike other local variables it is zeroed on entry to the method.

    If using a variable named 'result' isn't appealing, it can be renamed as the return variable ...
    PRI MySub : tmp
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2007-09-28 19:22
    I've corrected an error on page 8, where it stated that bits 31..2 are passed to the PAR register by COGNEW. Actually it's bits 15..2. My thanks to Rayman and BradC for pointing this out!

    At some point I need to collect and filter the latest batch of user-contributed material for inclusion. I haven't forgotten, and thanks for all the input!


    Post Edited (Phil Pilgrim (PhiPi)) : 9/28/2007 7:29:43 PM GMT
  • LewisDLewisD Posts: 29
    edited 2007-10-09 21:01
    Hi Phil,

    I was talking with Chip about the Turboprop and ran into something new.(I think)
    My discussion was in “What would you want more of, cogs or RAM?” starting on page 25.
    I'm still looking to see if all 512 COG RAM location are indeed filled with HUB data during initialization.
    This would be the easiest way to get code/data into the 'Shadow Registers'.


    Chip said...

    Cog RAM is read directly for instructions, not special 'source' registers such as PAR, CNT, etc. So, you could write a JMP instruction into the PAR register ($1F0) and have it execute, not having to consider what PAR would return as a 'source'. In other words, the large-model would work fine by reading eight contiguous hub longs into $1E8-$1EF and then JMPing to $1E8, and then having a JMP back execute from RAM (not PAR) at $1F0.

    Chip how many of the registers are like that?(all?)

    Yes, all. Some registers like PAR, CNT, INA, etc. have special mux's for reading live states when accessed as source registers. All instructions and destination registers are read from RAM, though - not mux's.

    Would the code look like this to stuff $1f0?

    mov $1f0,stuff
    stuff jmp next-page

    That's it.
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-09 21:32
    @LewisD: You are mixing here some things that do not belong together. What Chip was saying is correct to the point but not what you insinuate...
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-17 17:34
    A SPIN REPEAT trap, well known, but sometimes forgotten...
      st := 0
      REPEAT i  FROM  st TO 1
           ' executed twice  (i = 0,1)
      st := 2
      REPEAT i  FROM  st TO 1
           'also executed twice  (i  = 2,1)

    SPIN evaluates both limits in the beginning and decides whether to run the loop forward or backward. This is a great feature, but extremely surprising for experienced programmers.

    Post Edited (deSilva) : 10/17/2007 5:39:23 PM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,547
    edited 2007-10-20 07:19
    Since _XINFREQ and _CLKFREQ are mutually exclusive, meaning you can only use one or the other, but·not both in a CON block.

    Here is a trick that·allows you to·calculate·_CLKFREQ·within the CON block while·using _XINFREQ...
      [b]_CLKMODE[/b] = [b]XTAL1[/b] + [b]PLL16X[/b]
      [b]_XINFREQ[/b] = 5_000_000 
      clockfreq = (([b]_CLKMODE [/b]- [b]XTAL1[/b]) >> 6) * [b]_XINFREQ[/b]

    Likewise, here is how you can calculate·_XINFREQ within the CON block while using _CLKFREQ...
      [b]_CLKMODE[/b] = [b]XTAL1[/b] + [b]PLL16X[/b]
      [b]_CLKFREQ[/b] = 80_000_000
      Xinfreq = [b]_CLKFREQ[/b] / (([b]_CLKMODE [/b]- [b]XTAL1[/b]) >> 6)

    A situation where this might be useful would be to setup timing constants within the CON block without resorting to using any variables.
      [b]_CLKMODE[/b] = [b]XTAL1[/b] + [b]PLL16X[/b]
      [b]_XINFREQ[/b] = 5_000_000 
      clockfreq = (([b]_CLKMODE [/b]- [b]XTAL1[/b]) >> 6) * [b]_XINFREQ[/b]
      _1uS  = clockfreq / 1_000_000 'Divisor for  1 uS
      _10uS = clockfreq /   100_000 'Divisor for 10 uS
      _1mS  = clockfreq /     1_000 'Divisor for  1 mS
      _1S   = clockfreq /         1 'Divisor for  1 S

    [b]PUB[/b] Pause_1uS(Period)                                    '1 uS 
        [b]waitcnt[/b](_1uS * Period + [b]cnt[/b])

    [b]PUB[/b] Pause_10uS(Period)                                   '10 uS 
        [b]waitcnt[/b](_10uS * Period + [b]cnt[/b])

    [b]PUB[/b] Pause_1mS(Period)                                    '1 mS 
        [b]waitcnt[/b](_1mS * Period + [b]cnt[/b])

    [b]PUB[/b] Pause_1S(Period)                                     '1 S 
        [b]waitcnt[/b](_1S * Period + [b]cnt[/b])

    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 10/20/2007 8:32:11 PM GMT
  • Timothy D. SwieterTimothy D. Swieter Posts: 1,613
    edited 2007-10-20 08:01
    Excellent suggestion Beau! I think I will use this method in the future. Not only does it eliminate some variables, but it also saves the execution time where other routines do the calculation each time to determine the correct delay length.

    Timothy D. Swieter
    One little spark of imagination is all it takes for an idea to explode
  • hippyhippy Posts: 1,981
    edited 2007-10-20 13:19
    It would be nicer if the Propeller Tool could (1) create a _CLKFREQ or _XINFREQ based on the other (2) allow both _CLKFREQ and _XINFREQ and produce an error only if the two do not match up using the _CLKMODE setting.
  • Ken PetersonKen Peterson Posts: 806
    edited 2007-10-21 02:35
    might be a trap if you change clock modes within your program..


    The more I know, the more I know I don't know.· Is this what they call Wisdom?
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-21 09:57
    It's a trap anyhow when you forget to change the byte at adress #0 smile.gif
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-27 10:32
    Trap AND Trick: Not sure how well this was known before:
    The compiler remembers the size of a label in the DAT section according to the size identifier after this label.
    lo   LONG 10,20,30
    by   BYTE  "ABC",0

    However a "standalone label" is always considered as a LONG:
        BYTE "ABC",0

    is no longer correctly addressable by by[noparse][[/noparse]...]

    There is a work around as follows
    by  BYTE
        BYTE "ABC",0

    A size identifier without preset does not allocate space but assigns the correct attribute to the label only!

    It most likely also aligns for WORD and LONG, but I have not checked it.

    Post Edited (deSilva) : 10/27/2007 10:37:32 AM GMT
  • mparkmpark Posts: 1,305
    edited 2007-10-27 11:05
    deSilva said...

    However a "standalone label" is always considered as a LONG:

    But, strangely enough, the compiler complains if you say "standaloneLabel.long[noparse][[/noparse]...]"

    (See also
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-27 11:25
    I shall investigate a little bit deeper.. Alas, I do no longer read the Hydra threads..
    But I already can assure you it is not BYTE a standalone label defaults to...
  • mparkmpark Posts: 1,305
    edited 2007-10-27 16:36
    I wasn't disagreeing with you, just adding an additional quirk, but on digging a little deeper it appears that the rules for standalone labels are:
    a. The last size identifier preceding a standalone label determines its "type".
    b. If there is no preceding size identifier, a standalone label defaults to BYTE.

    long0                           ' defaults to BYTE
            long $1234, $abcd  
    PUB Start | ch
      tv.start( 24 )
      tv.hex( long0, 8 )            ' 00000034
      tv.out( 13 )
      tv.hex( long0[noparse][[/noparse] 1], 8 )        ' 00000012
      tv.out( 13 )
      tv.hex( word0, 8 )            ' 20012000
      tv.out( 13 )
      tv.hex( word0[noparse][[/noparse] 1], 8 )        ' 13121110
      tv.out( 13 )
      tv.hex( byte0, 8 )            ' 00001110
      tv.out( 13 )             
      tv.hex( byte0[noparse][[/noparse] 1], 8 )        ' 00001312
      tv.out( 13 )
      tv.hex( long1, 8 )            ' 00000078
      tv.out( 13 )
      tv.hex( long1[noparse][[/noparse] 1], 8 )        ' 00000056
      tv.out( 13 )
    word0                           ' LONG because of LONG in previous DAT section
            word $2000, $2001
    byte0                           ' WORD
            byte $10, $11, $12, $13
    long1                           ' BYTE
            long $12345678

    Post Edited (mpark) : 10/27/2007 4:45:36 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-10-27 17:10
    THAT'S IT! A little bit tricky to find out (advanced debugging) smile.gif Congrat!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2007-10-27 17:29
    This is where a list file from the compiler/assembler would come in handy.

  • Beau SchwabeBeau Schwabe Posts: 6,547
    edited 2007-11-20 16:26
    Here is a cool trick using the muxc command to self modify the instruction bits of another command, thus changing the command entirely....
    The example uses this technique to Bit-Bang data to an I/O port with 50nS output transitions (20MHz).


      [b]_clkmode[/b]      = [b]xtal[/b]1 + [b]pll[/b]16x
      [b]_xinfreq[/b]      = 5_000_000
    OutputPin = 0
      DataOut = %1000_1111_0000_1010_1010_1001_1001_1011 '<- Data to send
    [b]PUB[/b] ASM_FAST_BitBang_DEMO
    FAST_BitBang  [b]org[/b]       0
                  [b]mov[/b]       OutputPinMask,          #1                              '' Create Pin Mask for Output pin
                  [b]shl[/b]       OutputPinMask,          #OutputPin
                  [b]mov[/b]       LoopCounts,             #32              'Self Modifying Bit-Bang Code
    Loop          [b]ror[/b]       Buff,                   #1 [b]wc[/b]            
    BitIndex      [b]muxc[/b]      Bit0,                   instr_mux
                  [b]add[/b]       BitIndex,               dest_inc
                  [b]djnz[/b]      LoopCounts,             #Loop
                                                    'This section of code self modifies the code below,
                                                    'so that each of the bits can transition in only 50nS.
                                                    'It does this by MUXing the "C" flag into the instruction
                                                    'location of the muxz commands below.
                                                    'By default, the way that the muxz is used, it will make
                                                    'output Pin LOW.  When the instruction location is changed...
                                                    '...from 011110 to 011111 (<-Notice only one bit difference)
                                                    ' this section of the code via the muxc instruction, 
                                                    'muxz below effectively becomes a muxnz.  Cool Huh!?
                  [b]or[/b]        [b]dira[/b],   OutputPinMask   'Make Pin an Output
                  [b]mov[/b]       [b]outa[/b],   #1 [b]nr[/b],[b]wz[/b]        'Clear Z flag for TRUE data
    '              [b]mov[/b]       [b]outa[/b],   #0 [b]nr[/b],[b]wz[/b]        'Set Z flag for INVERTED data
                                                                       The following MUX commands use the "Z" flag set above
    Bit0          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW      Self Modified Code to Bit Bang at 20MHz
    Bit1          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW      
    Bit2          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           The lines above that read...
    Bit3          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit4          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           muxc {Bit#}, instr_mux
    Bit5          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit6          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           ...self modify the instruction bits
    Bit7          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           of the lines that read...
    Bit8          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit9          [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           {Bit#} muxz outa, OutputPinMask
    Bit10         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW            
    Bit11         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW  that when the BIT in Buff is
    Bit12         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           a "1", the lines that did read... 
    Bit13         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit14         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           {Bit#} muxz outa, OutputPinMask                                           
    Bit15         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit16         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW  function as if they read...
    Bit17         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit18         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           {Bit#} muxnz outa, OutputPinMask
    Bit19         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit20         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           ...with 'muxz' the output pin is made
    Bit21         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           LOW ... with 'muxnz' the output pin
    Bit22         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           is made HIGH.  This is so that a pin
    Bit23         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           can transition in 4 Clocks or 50nS rather
    Bit24         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           than 8 Clocks or 100nS. This makes it twice
    Bit25         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           as fast.
    Bit26         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW           
    Bit27         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit28         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit29         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit30         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    Bit31         [b]muxz[/b]      [b]outa[/b],   OutputPinMask   'Default LOW
    instr_mux               [b]long[/b]    $0400_0000
    dest_inc                [b]long[/b]    $0000_0200
    Buff                    [b]long[/b]    DataOut
    OutputPinMask           [b]long[/b]    0
    LoopCounts              [b]long[/b]    0 

    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/20/2007 6:26:56 PM GMT
Sign In or Register to comment.