¿Cómo puedo configurar el código de condición en lenguaje ensamblador?

7

Quiero probar el signo variable. En otras palabras, quiero saber si una variable es positiva o negativa. ¿Cómo puedo escribir las siguientes condiciones de if-then-else en lenguaje ensamblador?

  if X > 0 
      goto A
  else 
      goto B

Estoy usando PİC16f87x

Para que mi pregunta sea más general, si sabe, ¿puede responder a las siguientes preguntas?

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
    
pregunta JRobert

5 respuestas

11

No estoy familiarizado con el microcontrolador PIC, así que daré una respuesta más general. (edición: respuesta específica de PIC agregada al final de esta publicación)

Los microcontroladores especialmente pequeños, como 8 bits y sus derivados, están limitados en lo que pueden hacer en una sola instrucción. Una instrucción puede contener la dirección de destino para un salto, pero no dos de ellas, por lo que then-else está fuera. Solo tienes la parte if-then , pero eso es suficiente. Hay dos enfoques. Algunos controladores le permiten saltar a una dirección dada si se cumple una condición, otros solo le permiten omitir la siguiente instrucción. En el primer caso, su código tendrá el siguiente aspecto:

              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

Si solo puedes omitir la siguiente instrucción, escribirás algo como

              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  

La prueba en sí también tiene posibilidades limitadas. Como no puedes pasar dos números para compararlos. Lo que hace es cargar el acumulador con el primer número y en la siguiente instrucción restar el segundo número. Esto hará que los códigos de condición como el cero y los indicadores de acarreo se establezcan / borren. Las instrucciones condicionales probarán esas banderas. Así que si quieres escribir if A = B then do-equal else do-not-equal esto se convertiría en

              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

Importante: el manual del conjunto de instrucciones le indicará qué códigos de condición se verán afectados por una instrucción determinada. Por ejemplo, en la Z80 la instrucción ld (para "acumulador de carga") no cambiará ninguna marca. Entonces, cargar el acumulador es no suficiente para determinar si los datos son cero.

editar
OK, así que hice una investigación y encontré lo siguiente:
El PIC tiene solo 2 instrucciones de salto condicional, BTFSS y BTFSC .

BTFSS : Prueba de bits F, Omitir si está configurado
Sintaxis: BTFSS f, b
donde f es un registro [0..127]
y b es el bit en ese registro para ser probado [0..7]
Descripción: Si el bit en el registro es 0 , se ejecuta la siguiente instrucción. Si el bit es 1 , la siguiente instrucción se descarta y en su lugar se ejecuta un NOP .

BTFSC : Prueba de bits F, omitir si está claro
Sintaxis: BTFSC f, b
donde f es un registro [0..127]
y b es el bit en ese registro para ser probado [0..7]
Descripción: Si el bit en el registro es 1 , se ejecuta la siguiente instrucción. Si el bit es 0 , la siguiente instrucción se descarta y en su lugar se ejecuta un NOP .

    
respondido por el stevenvh
6

Para probar si un entero es negativo, solo necesitas probar el bit superior. Si el bit superior es 1, entonces es negativo.

Ahora, para un entero de 8 bits en un clabacchio de 8 bits mencionado, puedes usar una resta para hacer la prueba. Sin embargo, como regla general, esto no es óptimo:

  1. Los PIC tienen las útiles instrucciones btfsc y btfss, que puede utilizar para probar directamente el bit superior, como se mencionó en rokjarc. Esto se puede usar incluso para probar enteros de 16 y 32 bits (¡e incluso flotadores!)

Vale la pena señalarlo, incluso en C, porque he visto a los compiladores del Microchip C producir código estúpido. Al probar si un entero de 16 bits es mayor que cero, literalmente producirán el ensamblaje para restar el entero de cero y verificar las banderas, en lugar de simplemente usar el btfss.

Por lo tanto, siempre que escribo PIC C, siempre verifico la salida del ensamblaje para ver si parece razonable. Y luego, por lo general, escribe macros para realizar las operaciones. (Por cierto, no he comprobado estas macros, están muy por encima de mi cabeza).

#define IF_NEGATIVE_16_BIT(x) if (*((int8*)((&x)+1)) & 0x80)
#define IF_NEGATIVE_32_BIT(x) if (*((int8*)((&x)+3)) & 0x80)
  1. Otros microcontroladores tienen la instrucción AND. Solo Y el byte superior del entero con 0x80. Si se establece el indicador cero, entonces el entero no fue negativo.
respondido por el Rocketmagnet
6

Para hacer comparaciones de enteros y casi cualquier otra cosa en lenguaje ensamblador, debes entender cómo se representan los números.

La mayoría de las variables en un sistema embebido pequeño no están firmadas, o al menos deberían estarlo. Los programadores de lenguaje de alto nivel que están acostumbrados a sistemas más grandes tienden a no pensar en esto y usar variables con signo cuando solo se necesita sin firmar. Los no firmados son generalmente más fáciles de trabajar en el nivel bajo.

Su código es para un PIC 16, así que asumiré que está utilizando el ensamblador MPASM. Tengo un montón de macros y otras instalaciones para ayudar con cosas como la comparación de magnitud en mi archivo de inclusión común STD.INS .ASPIC , que forma parte de mi entorno de desarrollo PIC . En particular, las macros SKIP_WLE (omitir si W era menor o igual que) y SKIP_WGT (omitir si W era mayor que) son para comparaciones de números sin signo.

El PIC tiene un bit de estado de acarreo, llamado C, que es la salida de acarreo de la ALU. Este bit se puede probar después de un agregado o una resta para ver si hubo un arrastre del bit alto de la ALU. La forma en que los restados funcionan con dos números de complemento, el bit C se convierte en un bit de no préstamo después de una resta. Para complicar más las cosas, las instrucciones de restar PIC restan el registro W del operando, no al revés. Tienes que hacer un poco de gimnasia mental para mantener un registro de que C no es prestado y lo que realmente se resta de lo que significa y, por lo tanto, lo que realmente significa un préstamo. Hice todo esto una vez y encapsulé el resultado en estas dos macros simples, así que no tengo que pensar en ello cada vez y arriesgarme a desordenarlo. En un PIC 16, SKIP_WLE se convierte en la única instrucción BTFSS STATUS, C, y SKIP_WGT se convierte en BTFSC STATUS, C.

Aquí es cómo utilizarías estas macros en el código:

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.
;

Hay muchas otras cosas útiles en STD.INS.ASPIC. También tengo un preprocesador que agrega muchas funciones útiles al lenguaje MPASM. Por ejemplo, puede hacer cálculos de punto flotante en el momento de la construcción, puede realizar una manipulación real de cadenas para crear nombres de símbolos, tiene una capacidad de macro más flexible y mucho más.

    
respondido por el Olin Lathrop
6

Por ahora solo responderé la primera parte de la pregunta.

No mencionas el tipo de X, supongamos que está firmado. Char firmado tiene b.7 == 1 cuando es negativo, así que solo tenemos que verificar el estado de ese 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

Aquí hay un enlace a una buena guía: Una guía para construcciones de programación de ensamblajes PIC comunes

El ejemplo anterior se puede encontrar en la página 7.

Intenta descifrar el resto de las respuestas por tu cuenta. Sugerencia: el comentario de clabacchio es muy útil.

    
respondido por el Rok Jarc
0

16 bits compara

  • X = XH: XL los bytes Hi y Lo de la variable de RAM X,
  • Y = YH: YL los bytes Hi y Lo de la variable de RAM Y.

Todos los valores están sin firmar.

; 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:

Los programadores de lenguaje ensamblador típicamente cambiar las etiquetas "else:" y "endif:" a un único (y con suerte descriptivo) etiqueta para cada sentencia if. Tal vez algo como "then_in_normal_range:", "else_outside_normal_range:", y "finished_range_handling:". (A diferencia de los lenguajes de nivel superior como Pascal y BASIC, en los que usamos las mismas palabras "then" y "else" y "endif" para cada sentencia if).

El código anterior solo funciona cuando tanto X como Y están en RAM - El PIC16f87x requiere diferentes instrucciones para comparar X con un valor constante.

Una forma de manejar números firmados de 16 bits es voltear primero el bit alto

movlw 0x80
xorwf XH,w
xorwf YH,w

luego use el código de comparación anterior. Más tarde, restaure los valores originales cambiando el bit alto nuevamente

movlw 0x80
xorwf XH,w
xorwf YH,w

.

p.s. A menudo puede obtener una respuesta más rápida a las preguntas sobre el PIC16f87x mucho buscando en el sitio web de PIClist enlace y el wiki de Microchip enlace . Para comparaciones en lenguaje ensamblador, vea La página "Comparaciones de lenguaje ensamblador" en la wiki de Microchip , y Métodos matemáticos de comparación de microcontroladores PIC en el sitio web de PIClist (de los cuales lo anterior es un pequeño extracto).

    
respondido por el davidcary

Lea otras preguntas en las etiquetas