PASM NOP/Function return oddity

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.
While this code works just fine. Note that the only difference is the removed NOP at the local label :End.
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
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
· 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...
·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
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
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. Swieter, E.I.
www.brilldea.com - Prop Blade, LED Painter, RGB LEDs, 3.0" LCD Composite video display, eProto for SunSPOT
www.tdswieter.com
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
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
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