Shop OBEX P1 Docs P2 Docs Learn Events
PASM NOP/Function return oddity — Parallax Forums

PASM NOP/Function return oddity

Timothy D. SwieterTimothy D. Swieter Posts: 1,613
edited 2009-03-15 22:36 in Propeller 1
I am coding up a driver for a commercial project I am working on (read: can't post full code). The driver is in PASM and it is working - now. The driver is setup in a manner that the driver sits and waits for a command and then executes the command and returns to waiting. Sort of like the Graphics.spin or Float drivers or LM9033A Graphical LCD.

Anyway, I was expanding the functionality in the driver and ran into an oddity. Boy, it took me a while to figure what was causing the problem. I am fairly sure the problem is resolved with the below change as described, but I don't understand why it is a problem in the first place.

This code here causes a problem. The problem manifest itself as that the funciton does not return therefore locking up the cog and program execution. I think the COG is going off in to la-la land.
forceTX
              tjnz      paramA, #:setIO       'Check if the flag/ I/O should be set or cleared
        
:clearIO     andn      outa,   ledMask     'Signal a packet transmission ended (clear bit)
                 andn      outa,  txMask            'Turn off the bit
                 jmp       :End

:setIO        or        outa, ledMask           'Signal the transmitter on
                 or        outa, txMask            'Turn on the bit              

:End          nop

forceTX_ret   ret




While this code works just fine. Note that the only difference is the removed NOP at the local label :End.
forceTX
              tjnz      paramA, #:setIO         'Check if the flag/ I/O should be set or cleared
        
:clearIO     andn      outa,   ledMask         'Signal a packet transmission ended (clear bit)
                 andn      outa, txMask            'Turn off the bit
                 jmp       :End

:setIO        or        outa, ledMask           'Signal the transmitter on
                 or        outa, txMask            'Turn on the bit              

:End          

forceTX_ret   ret




What is the explanation for this? Is this a real problem or a symptom of another problem?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter, E.I.
www.brilldea.com - Prop Blade, LED Painter, RGB LEDs, 3.0" LCD Composite video display, eProto for SunSPOT
www.tdswieter.com

Comments

  • BeanBean Posts: 8,129
    edited 2009-03-12 11:12
    Timothy,
    · I think "JMP :End" should be "JMP #:End".

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    There is a fine line between arrogance and confidence. Make sure you don't cross it...

    ·
  • Erik FriesenErik Friesen Posts: 1,071
    edited 2009-03-12 12:14
    The # bites again. Sorry tim.
  • Timothy D. SwieterTimothy D. Swieter Posts: 1,613
    edited 2009-03-12 14:27
    OH!! Hang my head in shame!!! I know better - geezzzzz. OK - I hope I can get back to the code work tomorrow, if not, then Monday.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Timothy D. Swieter, E.I.
    www.brilldea.com - Prop Blade, LED Painter, RGB LEDs, 3.0" LCD Composite video display, eProto for SunSPOT
    www.tdswieter.com
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2009-03-12 16:29
    Timothy,

    Don't beat yourself up: we all do it. It's the single most frustrating error I make, time after time after time.

    That said, there's nothing at all wrong with jmp sub_ret versus jmp #sub_ret when the target contains a return instruction. This is because the return address is in ret's (actually another jmp) source field, which are the nine lsbs. The jmp sub_ret will read all 32 bits of the jmp instruction at sub_ret but will ignore all but the nine lsbs. Moreover, the non-immediate version is four clocks quicker.

    -Phil
  • Timothy D. SwieterTimothy D. Swieter Posts: 1,613
    edited 2009-03-15 09:18
    Thanks guys!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Timothy D. Swieter, E.I.
    www.brilldea.com - Prop Blade, LED Painter, RGB LEDs, 3.0" LCD Composite video display, eProto for SunSPOT
    www.tdswieter.com
  • kwinnkwinn Posts: 8,697
    edited 2009-03-15 16:15
    What PhiPi said goes double for me. Exactly the opposite way of doing it than how I have written assembly programs for years and years. Also ergonomically unfriendly since that form (with #) is used most often.

    May I suggest to the folks at Parallax that the propeller tool be changed to make use of the # an option that can be changed. The default is as it is used now, and the option is to use it to indicate indirect addressing (the opposite of current usage).
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2009-03-15 17:50
    I propbably wouldn't go that far. The current method is pretty much set in stone. Even with an option, there's a problem, in that snippets of code that get copied from one program to another may not include the option switch at the top of the program. It could get very confusing if you end up with mixed modes. I'd be more in favor of a warning option that a person could turn on to flag non-immediate jumps on compile or to highlight them in real time when typing.

    -Phil
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2009-03-15 22:36
    Beginner's note: Read the Propeller Guts pdf, and maybe even print out this page:
    Branching Instructions
    Branching instructions are used to alter the course of the COG's program counter, or PC. The PC is a 9-bit counter that tracks the
    program's execution address. It normally increments with each instruction, but it can be loaded with an arbitrary value via branching
    instructions.
    Important note: All the branching instructions (except 'RET') use S to specify the branch address. Always remember to put '#' in front
    of the S expression when you intend to jump to a constant address. This will almost always be the case in your code. If you don't use
    the '#', you will be specifying a register's contents as your branch address. Sometimes, you may want to do this, but most often your
    intent will be to branch to a constant address and you must remember to use '#'. For the applicable instructions cited in this section,
    both forms will be shown to keep you mindful of this critical issue.
    Important note #2: Remember, like all other instructions, these branches can be preceded by conditionals, making them into
    conditional branches.
    The simplest branch instruction is 'JMP S'. This simply sets the PC to S[noparse][[/noparse]8..0]:
    JMP #S 'Jump to the constant address S
    JMP S 'Jump to where register S points
    The most complex branch instruction is 'JMPRET D,S'. This is like 'JMP S' but has the additional effect of writing PC+1 (what would
    have been the next execution address) into bits 8..0 of D. The purpose of that extra action is to insert a 'return address' into a 'JMP #S'
    instruction's sssssssss bit field. This way, if the code branched to by 'JMPRET D,S' ends with a 'JMP #S' instruction at the D address,
    that segment of code effectively becomes a subroutine that many identical 'JMPRET D,S' instructions could branch to, and then return
    from.
    Here is 'JMPRET':
    JMPRET D,#S 'Jump to the constant address S and set D[noparse][[/noparse]8..0] to PC+1
    JMPRET D,S 'Jump to where register S points and set D[noparse][[/noparse]8..0] to PC+1
    To keep you sane, the assembler provides special 'CALL' and 'RET' instructions which form 'JMPRET D,#S' and 'JMP #S'
    instructions, respectively. This makes it easy to realize the subroutine scheme described above. These special instructions must be
    used with coordinated symbol names that you make up. To use the 'CALL' instruction, you must specify a '#' followed by the symbolic
    name of the subroutine you are calling. There must be, somewhere in your program, an identical symbol name which ends with '_ret'
    and precedes a 'RET' instruction. The assembler uses the address of the +'_ret' symbol as the D in the 'JMPRET D,#S', or 'CALL'
    instruction; the S comes from the subroutine's start symbol. The 'RET' instruction is assembled as 'JMP #0', but gets modified at runtime.
    Here is a contextual example of these instructions:
    CALL #sub1 'Call to the constant address "sub1"
    '…and set the sssssssss bit field of "sub1_ret" to PC+1
    '
    sub1 <code> 'Start of "sub1" subroutine
    sub1_ret RET 'Return to caller (sssssssss modified, jump to #S)
    Note that the COG has no call stack. It uses end-of-subroutine jumps to return to callers. For this reason, subroutines cannot be called
    recursively. However, there is no 'depth' limit.
    Finally, there are three conditional branching instructions which branch according to D:
    DJNZ D,#S 'Decrement D, jump to constant address S if D is not 0
    DJNZ D,S 'Decrement D, jump to where register S points if D is not 0
    TJNZ D,#S 'Test D, jump to constant address S if D is not 0
    TJNZ D,S 'Test D, jump to where register S points if D is not 0
    TJZ D,#S 'Test D, jump to constant address S if D is 0
    TJZ D,S 'Test D, jump to where register S points if D is 0

    These D-dependent branches are very useful for fast looping. They take 4 clocks when they branch, and 8 clocks when they don't

Sign In or Register to comment.