TSTEQ and BNZ/BZ Command Confusion

randyth

Member
On page 24 of version 3.2.18d of the PLC User Guide we have the following sample code:
 
   TSTEQ IP1 1 # sets zero bit based on the result of the test instruction
   BNZ label # branches to label if zero bit is non-zero
 
…and the following description of the TSTEQ opcode:
 
   Tests if a is equal to b. Boolean result loaded into optional
   destination (d). Zero bit updated with result. If test
   evaluates to false then the next instruction is skipped.
 
My question/confusion starts with that last sentence, “If test evaluates to false then the next instruction is skipped.”  If this statement is accurate, then it seems to me that any BNZ or BZ command that immediately follows a TSTEQ command will either always branch (might as well be a GOTO) or never branch (might as well be a NOP).
 
Take the sample code above, for example.  If IP1 doesn’t equal 1 (test evaluates to false), then the BNZ command (“the next instruction”) is skipped.  Conversely, if IP1 does equal 1 (test evaluates to true), then the zero bit is set and the branch should always occur and therefore could have been a GOTO command.
 
Clearly I’m missing something or don’t understanding how the zero bit is set as a result of a TST command.  Any clarification here would be appreciated.
 
Thanks,
-Randy
 
 
You don't have to use TSTEQ with BNZ together.
The statement skip next line is accurate.
You may use zero bit to branch in later code, or with other calculation to branch to different part of the PLC code.
 
The sample code showing it can branch to a different section of code, like you said, it could be GOTO as well, depending if the test against 1 or 0.  However, use BNZ or BZ will help coder himself to remember how his logic flows.
 
Hope this helps.
 
OK, that makes sense.  Using the BNZ or BZ immediately after a TSTEQ (as found in the User Guide) makes it potentially more readable code.
 
Thanks for the quick and clear answer.
 
-Randy
 
randyth said:
OK, that makes sense.  Using the BNZ or BZ immediately after a TSTEQ (as found in the User Guide) makes it potentially more readable code.
 
Thanks for the quick and clear answer.
 
-Randy
 
heureka, this might be a quirky solution for the if-then-else construct I was searching for:
TSTEQ == IF
BNZ == THEN
BZ== ELSE
But only one line allowed or you have to uses GOTO/CALLSUB...
 
TSTEQ  =/=  IF
 
rather
TSTEQ == IF + THEN (assumed). BZ and BNZ are not typically used with TSTxx
 
Eg.
TSTEQ A B  is equivalent to  IF "A==B"
GOTO C       is equivalent to THEN (assumed) GOTO C
NEXTOP      is not equivalent to  ELSE = else case does not exist with TSTxx opcodes structure
 
Larry is exactly right. 
 
TSTEQ can test against 0 or test against 1.  In the coder's mind, there are people prefer to against 0 in his logic, some users prefer to against 1 to test in his logic.   TSTEQ can also be used for temperature, analog input level, current reading, etc.  It sets zero bit for later use, in addition for itself skip next line if test result set zero bit.
 
One thing needs to pay attention is:  do not use TSTxy before END, so that it might  loop over END and skip PLC code at line1.  If you have to use it that way, NOP is needed, so that it does not skip END.
 
CAI_Support said:
...
 
One thing needs to pay attention is:  do not use TSTxy before END, so that it might  loop over END and skip PLC code at line1.  If you have to use it that way, NOP is needed, so that it does not skip END.
 
Ah, that brings up another thing that made me wonder.  In the PCL User Guide's "Example 5, Parallel I/O" code, it has a TSTxy before a RET in three subroutines.  What happens here if the test evaluates to false in these cases?  Does it skip the RET and fall through to the subsequent subroutine?  If so, what happens on the _last_ subroutine if the TSTEQ command evaluates to false?  Or, does it skip the statement after the line that called the subroutine?  Or?
 
-Randy
 
Great question, if the TSTxy result is false, it will skip next line. RET is not END, so that will be skipped.  Which may not what you want.  In this particular case, the better way to handle them is to below, but that is less readable.  By skip RET during execution, it will not cause any harm, but better practice is not to skip them.
 
 
Code:
START
   TSTGT T3 500 OP1
   TSTEQ IP1[300] 1 OP2
   CALLSUB checkOP3
   TSTEQ OP1 1 OP4
END

checkOP3:
   ADD AIP1 AIP2 RAM1
   TSTGT RAM1 1024
   BNZ l1
   TSTEQ IP4 1
   BNZ l2
RET

l1:
   SET O3 1
RET
l2:
   SET O3 0
RET
 
Sorry to keep hammering on this, but I still have some confusion.  Looking at the code on page 35 of the 3-02-18d PLC User Manual, the final lines of example code consist of this subroutine:
 
checkOP4:
   TSTEQ OP1 1 OP4
   RET
 
[...no code follows...]
 
What happens if OP1 doesn't equal 1 when this subroutine executes?
 
Thanks,
-Randy (who really should boot up his PLC and simply run some tests to answer his own questions)
 
It will skip RET and go directly to the first line of PLC code, because START and END does not counted as PLC code.
The net result is same as it executed RET, END, START then first line of PLC code.
 
LarrylLix said:
What happens if OP1 doesn't equal 1 when this subroutine executes?



END == GOTO START
 
Yes, but there was no END below the subroutine in question; thus my question.  In any case, I get it now -- an END is implied when there is no more code to execute.  (And here I thought dragons lived beyond the defined code segment!)
 
Yes, but there was no END below the subroutine in question; thus my question. In any case, I get it now -- an END is implied when there is no more code to execute.

I was elaborating on CAI_Support's descriptive comment but.....
 
Sure it would jump over the RET and just execute the next opcodes, whatever is there. People use this technique for multiple exits from subroutines. It's cheap and dirty but not very structured code writing. I have mixed feelings about that one.
 
SAME:
    TSTNE A B
     GOTO BIGGER
      CALLSUB SAME_LED
      RET
 
BIGGER:
    TSTLE A B
    GOTO SMALLER
    CALLSUB BIG_LED
    RET
 
SMALLER:
    CALLSUB SMALL_LED
     RET
 
That uses multiple exits from the subroutine. Structured code writing wants one exit.
 
SAME:
     TSTNE A B
      GOTO BIGGER
      CALLSUB SAME LED
      GOTO SAME_X
 
BIGGER:
      TST.....
   ...
      GOTO SAME_X
......
 
SMALLER:
....
   
 
SAME_X:
      RET
 
I like both methods. I usually find that any code that can fit one "idea" or "concept" on one viewable page can be read  and understood much easier, later. Use subroutine calls to break up complex concepts or code too long to read  that lump in one viewing. Multiple GOTOs are called "spaghetti bowl code" by some. Some languages do not have GOTOs period as they are deemed evil and the dragons will definitely get you if you get caught by the Code Police.
 
Good labels are an art. In the old days assemblers only allowed 6 characters labels. It took half and hour to think up a meaningful label at times.  With 9 chars allowed it is much more useful.
 
I believe the END may be necessary to indicate to the editor to stop sending and the PLC interpreter engine to loop back. I don't forget it. Dragon bites can be more serious than Python bites and END your code writing until it heals.
 
LarrylLix said:
...
Good labels are an art. In the old days assemblers only allowed 6 characters labels. It took half and hour to think up a meaningful label at times.  With 9 chars allowed it is much more useful.
...
 
Ha!  I remember writing 650x code by typing hex numbers into a memory monitor, calculating branch offsets in my head (no assembler with fancy mnemonics and labels required!).  :)  I got really good at it.  I can still remember the hex equivalent of many of the common opcodes.  I'm looking forward to getting my hands dirty coding my new CAI WebControl board.  High level languages have been putting me to sleep the last several years.
 
Back
Top