7
\$\begingroup\$

I want test the variable sign. In other words, I want know answer to the whether a variable is positive or negative. How can I write following if-then-else conditions in assembly language ?

  if X > 0 
      goto A
  else 
      goto B

I am using PİC16f87x

To do my question to be more general, if you know can you give answer to below questions?

i.)

    if X > Y 
        goto A
    else 
        goto B

ii.)

    if X >= Y 
        goto A
    else 
        goto B

iii.)

    if X != Y 
        goto A
    else 
        goto B
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I remember the z80 assembler code, but you achieve the comparison with subtraction and checking the carry and zero bit \$\endgroup\$
    – clabacchio
    Commented Mar 29, 2012 at 13:17

5 Answers 5

11
\$\begingroup\$

I'm not acquainted with the PIC microcontroller, so I'll give a more general answer. (edit: PIC-specific answer added at the bottom of this post)

Especially smaller microcontrollers, like 8-bit and their derivatives, are limited in what they can do in a single instruction. An instruction may contain the target address for a jump, but not two of them, so then-else is out. You only have the if-then part, but that's enough. There are two approaches. Some controllers let you jump to a given address if a condition is met, others only let you skip the next instruction. In the former case your code will look as follows:

              if-test-succeeds goto test-success
test-failed   first instruction of `else` block
              ...
              goto continue
test-success  first instruction of `then` block
              ...
continue      first instruction after if-then-else

If you can only skip the next instruction you'll write something like

              if-test-succeeds skip next instruction
              goto test-failed
test-success  first instruction of `then` block
              ...
              goto continue
test-failed   first instruction of `else` block
              ...
continue      first instruction after if-then-else  

The test itself has also limited possibilities. Like you can't pass two numbers to compare them. What you do is load the accumulator with the first number and in the next instruction subtract the second number. This will cause condition codes like the zero and carry flags to be set/cleared. Conditional instructions will test for those flags. So if you want to write if A = B then do-equal else do-not-equal this would become

              load accumulator with A
              subtract B from accumulator
              if zero-flag set goto do-equal
do-not-equal  first instruction of `else` block
              ...
              goto continue
do-equal      first instruction of `then` block
              ...
continue      first instruction after if-then-else

Important: the instruction set manual will tell you which condition codes will be affected by a given instruction. For instance, in the Z80 the ld instruction (for "load accumulator") will not change any flags. So here loading the accumulator is not sufficient to determine if the data is zero.


edit
OK, so I did some research and I found the following:
The PIC has only 2 conditional jump instructions, BTFSS and BTFSC.

BTFSS: Bit Test F, Skip if Set
Syntax: BTFSS f,b
where f is a register [0..127]
and b is the bit in that register to be tested [0..7]
Description: If bit in register is 0 the next instruction is executed. If the bit is 1 the next instruction is discarded and a NOP is executed instead.

BTFSC: Bit Test F, Skip if Clear
Syntax: BTFSC f,b
where f is a register [0..127]
and b is the bit in that register to be tested [0..7]
Description: If bit in register is 1 the next instruction is executed. If the bit is 0 the next instruction is discarded and a NOP is executed instead.

\$\endgroup\$
6
\$\begingroup\$

To test if an integer is negative, you only need to test the top bit. If the top bit is 1, then it's negative.

Now, for an 8-bit integer on an 8-bit clabacchio mentioned, you can use a subtraction to do the test. However, as a general rule, this is not optimal:

  1. PICs have the useful btfsc and btfss instructions, which you can use to directly test the top bit, as rokjarc mentioned. This can even be used to test 16 and 32 bit integers (and even floats!)

This is worth noting, even in C, because I have seen the Microchip C compilers produce stupid code. When testing if a 16-bit integer is greater than zero, they will literally produce the assembly to subtract the integer from zero, and check the flags, rather than simply using the btfss.

So, whenever I'm writing PIC C, I always check the assembly output to see if it looks sensible. And then, usually, write macros to perform the operations. (BTW, I haven't actually checked these macros, they're just off the top of my head).

#define IF_NEGATIVE_16_BIT(x) if (*((int8*)((&x)+1)) & 0x80)
#define IF_NEGATIVE_32_BIT(x) if (*((int8*)((&x)+3)) & 0x80)
  1. Other microcontrollers have the AND instruction. Just AND the top byte of the integer with 0x80. If the zero flag gets set, then the integer was not negative.
\$\endgroup\$
6
\$\begingroup\$

To do integer comparisons and most anything else in assembly language, you have to understand how the numbers are represented.

Most variables in a small embedded system are unsigned, or at least should be. High level language programmers that are used to bigger systems tend to not think about this and use signed variables when only unsigned is needed. Unsigned are generally easier to work with at the low level.

Your code is for a PIC 16, so I'll assume you are using the MPASM assembler. I have a bunch of macros and other facilities to help with things like magnitude compare in my common include file STD.INS.ASPIC, which is part of my PIC development environment. In particular, the SKIP_WLE (skip if W was less than or equal to) and SKIP_WGT (skip if W was greater than) macros are for comparisons of unsigned numbers.

The PIC has a carry status bit, called C, which is the carry output of the ALU. This bit can be tested after a add or subtract to see if there was a carry out of the high bit of the ALU. The way subtracts work with twos complement numbers, the C bit becomes a not-borrow bit after a subtract. To make things more complicated, the PIC subtract instructions subtract the W register from the operand, not the other way around. You have to do some mental gymnastics to keep track of C being not-borrow and what was really subtracted from what and therefore what a borrow actually means. I did all this once and encapsulated the result in these two simple macros so I don't have to think about it each time and take a chance on messing it up. On a PIC 16, SKIP_WLE turns into the single instruction BTFSS STATUS,C, and SKIP_WGT turns into BTFSC STATUS,C.

Here is how you would use these macros in code:

maxok    equ     37          ;maximum OK tank level
         ...
;
;   Check the tank level and run the exception routine if it is
;   too high.
;
         banksel tanklev     ;set bank for access to TANKLEV
         movf    tanklev, w  ;get the tank full level
         sublw   maxok       ;compare to maximum normal level
         skip_wle            ;tank level is within range ?
         goto    ohcrap      ;no
;
;   The tank level is within normal operating limits.
;

There are a lot of other useful things in STD.INS.ASPIC. I also have a preprocessor that adds a lot of useful features to the MPASM language. For example, it can do floating point math at build time, can do real string manipulation for building symbol names, has a more flexible macro capability, and lots more.

\$\endgroup\$
6
\$\begingroup\$

I'll answer just first part of the question for now.

You don't mention the type of X - let's assume it's signed char. Signed char has b.7 == 1 when negative, so we only have to check the state of that bit.

//checking if bit 7 of X is cleared (X.7 == 0)
BTFSC   X,7    //if X.7 == 0 (X is positive) we'll skip the next instruction
GOTO    B
GOTO    A

//checking if bit 7 of X is set (X.7 == 1)
BTFSS   X,7    //if X.7 == 1 (X is negative) we'll skip the next instruction
GOTO    A
GOTO    B

Here's a link to nice guide: A Guide to Common PIC Assembly Programming Constructs

Above example can be found on page 7.

Try to figure out the rest of answers on your own. Hint: clabacchio's comment is very useful.

\$\endgroup\$
2
  • \$\begingroup\$ Is that the same as: Jason Thweatt. "A Guide to Common PIC Assembly Programming Constructs". ucpic.byethost7.com/Downloads/ApuntesAssembler/Apunte2.pdf ? \$\endgroup\$
    – davidcary
    Commented Oct 12, 2012 at 12:16
  • \$\begingroup\$ yes! thanks for the heads up, i didn't notice that the old link is down. will edit. \$\endgroup\$
    – Rok Jarc
    Commented Oct 12, 2012 at 17:47
0
\$\begingroup\$

16 bit compares

  • X = XH:XL the Hi and Lo bytes of the RAM variable X,
  • Y = YH:YL the Hi and Lo bytes of the RAM variable Y.

All values are unsigned.

; if( y < x )
  movfw XL
  subwf YL,w
  movfw XH
  skpc ; c=0 indicates a borrow we need to propagate.
    incfsz XH,w ; handle XH=0xff correctly, unlike ``incf XH,w''.
      subwf YH,w
  skpnc
    goto else
; then:
; /* y is less than x */
  ...
  goto endif
else:
; /* y is NOT less than x */
  ...
endif:

; if( x <= y )
  movfw XL
  subwf YL,w
  movfw XH
  skpc ; c=0 indicates a borrow we need to propagate.
    incfsz XH,w ; handle XH=0xff correctly, unlike ``incf XH,w''.
      subwf YH,w
  skpc
    goto else
; then:
; /* x is less than or equal to y */
  ...
  goto endif
else:
; /* x is NOT less than or equal to y */
  ...
endif:

; if(X == Y)
  movf XH,w
  xorwf YH,w
  skpz
  goto else
  movf XL,w
  xorwf YL
  skpz
  goto else
; then:
; /* X equals Y */
  ...
  goto endif
else:
; /* X is not the same as Y */
  ...
endif:

Assembly language programmers typically change the "else:" and "endif:" tags to a unique (and hopefully descriptive) label for each if-statement. Perhaps something like "then_in_normal_range:", "else_outside_normal_range:", and "finished_range_handling:". (Unlike higher-level languages like Pascal and BASIC where we use the same words "then" and "else" and "endif" for every if-statement.).

The above code only works when both X and Y are in RAM -- The PIC16f87x requires different instructions to compare X to a constant value.

One way to handle 16-bit signed numbers is to first flip the high bit

movlw 0x80
xorwf XH,w
xorwf YH,w

then use the above comparison code. Later, restore the original values by flipping the high bit again

movlw 0x80
xorwf XH,w
xorwf YH,w

.

p.s. You can often get a much quicker answer to questions about the PIC16f87x by searching the PIClist website http://piclist.com/ and the Microchip wiki http://microchip.wikidot.com . For assembly-language comparisons, see The "Assembly Language Comparisons" page on the Microchip wiki, and PIC Microcontroller Comparison Math Methods on the PIClist website (of which the above is a small extract).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.