TODOPIC

Microcontroladores PIC => Almacén del Assembler => Mensaje iniciado por: BrunoF en 17 de Noviembre de 2006, 08:48:38

Título: ¿Sabías que...
Publicado por: BrunoF en 17 de Noviembre de 2006, 08:48:38
¿Sabías que... es un hilo en el cual pondremos todos los pequeños trucos, curiosidades y demás del lenguaje ensamblador y el entorno de desarrollo integral MPLAB.

Índice de contenidos:

Estás invitad@ a compartir y postear aquí todos esas mañas que has aprendido programando en assembler.
Saludos.
Título: Rotar un registro a la izquierda es, matemáticamente, multiplicarlo por dos
Publicado por: BrunoF en 17 de Noviembre de 2006, 08:49:49
Aplicado a familias PIC12 y PIC16

La instrucción:

Código: ASM
  1. RLF   Registro,Destino

rota los bits de un registro un lugar hacia la izquierda.
Veámosla actuando sobre un ejemplo:

MiReg = b'00010110'

ahora: RLF MiReg,F

luego de la instrucción:

MiReg = b'0010110C'

Donde C es el valor que tenía el bit STATUS,C al momento de ejecutarse la instrucción RLF.

¿Te perdiste?

Veamos un poco mas en detalle cómo trabaja la función RLF:

Un registro es un grupo de 8 bits:
Registro= B7 B6 B5 B4 B3 B2 B1 B0

Ahora veamos qué hace la instrucción RLF aplicada a dicho registro:

    RLF Registro,F

(STATUS,C <--  B7) <-- B6 B5 B4 B3 B2 B1 B0 (C <-- STATUS,C)

¿Qué significa esto?

Significa que todos los bits de Registro son rotados una posición hacia la izquierda. El espacio generado a la derecha de Registro(es decir, el bit0 de registro) es rellenado con el valor que tenía en ese momento el bit C del registro STATUS.
A su vez, el Bit7(B7) de Registro sale de Registro e ingresa al bit C del registro STATUS.

Veamoslo entonces una vez mas, suponiendo que:

ANTES DE LA INSTRUCCIÓN:

Registro= b'00001100'
STATUS,C = 0

al hacer RLF Registro,F....

DESPUÉS DE LA INSTRUCCIÓN:

Registro= b'00011000'
STATUS,C = 0

Por lo tanto, podemos observar que antes de aplicar la RLF, Registro valía 12 en sistema decimal. Luego de la instrucción RLF Registro vale 24. Hemos multiplicado a Registro por dos, utilizando una instrucción RLF.

Consideraciones y precauciones:

Veamos el siguiente ejemplo:

ANTES DE LA INSTRUCCIÓN:

Registro= b'00001100'
STATUS,C = 1

al hacer RLF Registro,F....

DESPUÉS DE LA INSTRUCCIÓN:

Registro= b'00011001'
STATUS,C = 0

En este ejemplo Registro valía 12 en sistema decimal antes de la instrucción RLF. Pero luego de la instrucción RLF Registro vale 25. ¿Por que?

La diferencia radica en que el bit STATUS,C valía en esta caso 1 antes de ejecutarse la instrucción RLF e ingresó al bit0 de Registro al ejecutarse la instrucción RLF. Por lo tanto, en este caso, hacer RLF Registro,F será igual a hacer Registro*2 + 1. ¡Hay que tener cuidado con esto!

Para asegurar una multiplicación por dos, limpiaremos el bit STATUS,C antes de realizar la RLF para asegurar que el bit STATUS,C no "ensucie" nuestra multiplicación.

Ejemplo:

Código: ASM
  1. bcf       STATUS,C          ;Limpio STATUS,C para asegurar que no "ensuciará" la multiplicación
  2. RLF      Registro,F           ;y ahora si. Roto el Registro a la izquierda.

Siguiendo con la mísma lógica rotar dos veces a la izquierda un registro es igual a decir Registro * 2 * 2, lo que simplificado es Registro * 4.Y así sucesivamente.

Si no lo sabías... ¡es hora de aplicar lo aprendido!

Saludos.
Título: Rotar un registro a la derecha es, matemáticamente, dividirlo por dos
Publicado por: BrunoF en 17 de Noviembre de 2006, 20:03:53
Aplicado a familias PIC12 y PIC16

La instrucción:

Código: ASM
  1. RRF   Registro,Destino

rota los bits de un registro un lugar hacia la derecha.
Veámosla actuando sobre un ejemplo:

MiReg = b'00010110'

ahora: RRF MiReg,F

luego de la instrucción:

MiReg = b'C0001011'

Donde C es el valor que tenía el bit STATUS,C al momento de ejecutarse la instrucción RRF.

Veamos un poco mas en detalle cómo trabaja la función RRF:

Un registro es un grupo de 8 bits:
Registro= B7 B6 B5 B4 B3 B2 B1 B0

Ahora veamos qué hace la instrucción RRF aplicada a dicho registro:

    RRF Registro,F

 (STATUS,C--> C) B7 B6 B5 B4 B3 B2 B1 --> (B0 --> STATUS,C)

¿Qué significa esto?

Significa que todos los bits de Registro son rotados una posición hacia la derecha. El espacio generado a la izquierda de Registro(es decir, el bit7 de registro) es rellenado con el valor que tenía en ese momento el bit C del registro STATUS.
A su vez, el Bit0(B0) de Registro sale de Registro e ingresa al bit C del registro STATUS.

Veamoslo entonces una vez mas, suponiendo que:

ANTES DE LA INSTRUCCIÓN:

Registro= b'00001100'
STATUS,C = 0

al hacer RRF Registro,F....

DESPUÉS DE LA INSTRUCCIÓN:

Registro= b'00000110'
STATUS,C = 0

Por lo tanto, podemos observar que antes de aplicar la RRF, Registro valía 12 en sistema decimal. Luego de la instrucción RRF Registro vale 6. Hemos dividido a Registro por dos, utilizando una instrucción RRF.

Consideraciones y precauciones:

Veamos el siguiente ejemplo:

ANTES DE LA INSTRUCCIÓN:

Registro= b'00001100'
STATUS,C = 1

al hacer RRF Registro,F....

DESPUÉS DE LA INSTRUCCIÓN:

Registro= b'10000110'
STATUS,C = 0

En este ejemplo Registro valía 12 en sistema decimal antes de la instrucción RRF. Pero luego de la instrucción RRF Registro vale 134. ¿Por que?

La diferencia radica en que el bit STATUS,C valía en esta caso 1 antes de ejecutarse la instrucción RRF e ingresó al bit7 de Registro al ejecutarse la instrucción RRF. ¡Hay que tener cuidado con esto!

Para asegurar una división por dos, limpiaremos el bit STATUS,C antes de realizar la RRF para asegurar que el bit STATUS,C no "ensucie" nuestra división.

Ejemplo:

Código: ASM
  1. bcf       STATUS,C          ;Limpio STATUS,C para asegurar que no "ensuciará" la división
  2. RRF      Registro,F           ;y ahora si. Roto el Registro a la derecha.

Siguiendo con la mísma lógica rotar dos veces a la derecha un registro es igual a decir Registro / 2 / 2, lo que simplificado es Registro / 4.Y así sucesivamente.
Título: Puedes enviar un registro serialmente sin requerir otra variable temporal?
Publicado por: BrunoF en 17 de Noviembre de 2006, 20:32:14
Enviar datos serialmente es una necesidad muy común entre los programas que realizamos con un PIC.
Podríamos hacerlo de la siguiente forma:

Código: ASM
  1. movf   DatoAEnviar,W     ;Copio en W el registro a enviar serialmente y llamo a la funcion
  2.     call      EnviaDato
  3. ...
  4. ...
  5.  
  6. EnviaDato
  7.     movwf Aux                     ;muevo a Aux el contenido a enviar
  8.     movlw   .8                      ;indico 8 veces
  9.     movwf   Contador           ;y muevo a Contador
  10.    
  11. ProximoBit
  12.     rrf      Aux,F                    ;Roto a la derecha Aux
  13.     btfsc  STATUS,C              ;el bit0 de Aux es movido a STATUS,C al rotar a la derecha, por lo tanto lo reviso.
  14.     goto   Alto
  15.  
  16. Bajo
  17.     bcf     DATA                    ;STATUS,C estaba en bajo, poner en bajo pin DATA
  18.     goto   ListoParaEnviar
  19.  
  20. Alto
  21.     bsf     DATA                    ;STATUS,C estaba en alto, poner en alto pin DATA
  22. ListoParaEnviar
  23.     bsf    CLOCK                   ;pin CLOCK en alto
  24.     bcf    CLOCK                   ;pin CLOCK en bajo    
  25.  
  26.     decfsz  Contador,F
  27.     goto     ProximoBit
  28.  
  29.     return

Este método puede mejorarse y mucho. Volvamos a plantearlo una vez más para optimizarlo un poco....

Código: ASM
  1. movf   DatoAEnviar,W     ;Copio en W el registro a enviar serialmente y llamo a la funcion
  2.     call      EnviaDato
  3. ...
  4. ...
  5.  
  6. EnviaDato
  7.     movwf Aux                     ;muevo a Aux el contenido a enviar
  8.     movlw   .8                      ;indico 8 veces
  9.     movwf   Contador           ;y muevo a Contador
  10.    
  11. ProximoBit
  12.     rrf      Aux,F                    ;Roto a la derecha Aux
  13.     btfsc  STATUS,C              ;el bit0 de Aux es movido a STATUS,C al rotar a la derecha, por lo tanto lo reviso.
  14.     bsf     DATA                    ;STATUS,C estaba en alto, poner en alto pin DATA
  15.     btfss   STATUS,C
  16.     bcf     DATA                    ;STATUS,C estaba en bajo, poner en alto pin DATA
  17.  
  18.     bsf    CLOCK                   ;pin CLOCK en alto
  19.     bcf    CLOCK                   ;pin CLOCK en bajo    
  20.  
  21.     decfsz  Contador,F
  22.     goto     ProximoBit
  23.  
  24.     return

Bueno, este ejemplo está un poco mejor, ¿no?.
Pero podemos aún mejorarlo un poquito más. Veamos cómo:

Código: ASM
  1. movf   DatoAEnviar,W     ;Copio en W el registro a enviar serialmente y llamo a la funcion
  2.     call      EnviaDato
  3. ...
  4. ...
  5.  
  6. EnviaDato
  7.     movwf Aux                     ;muevo a Aux el contenido a enviar
  8.     movlw   .8                      ;indico 8 veces
  9.     movwf   Contador           ;y muevo a Contador
  10.    
  11. ProximoBit
  12.     rrf      Aux,F                    ;Roto a la derecha Aux
  13.     bsf     DATA                    ;SUPONGO que el dato a enviar será un 1. Por lo tanto pongo el pin DATA en alto...
  14.     btfss  STATUS,C              ;¿El bit a enviar es realmente un 1?
  15.     bcf     DATA                    ;No.Entonces pongo DATA en bajo
  16.  
  17.     bsf    CLOCK                   ;pin CLOCK en alto
  18.     bcf    CLOCK                   ;pin CLOCK en bajo    
  19.  
  20.     decfsz  Contador,F
  21.     goto     ProximoBit
  22.  
  23.     return

Hemos logrado agilizar un poco mas el método presuponiendo que el bit a enviar era un alto y, de ser necesario, lo corregimos antes de elevar el bit CLOCK.

Ahora, finalmente veamos cómo hacer para enviar un byte sin necesidad de requerir una variable auxiliar(Ej. Aux).

Código: ASM
  1. movlw  0xF0              ;Muevo 0xF0 a W
  2.     movwf  DatoAEnviar   ;Muevo W en DatoAEnviar
  3.     call      EnviaDato
  4. ...
  5. ...
  6.  
  7. EnviaDato    
  8.     movlw   .8                      ;indico 8 veces
  9.     movwf   Contador           ;y muevo a Contador
  10.  
  11. ProximoBit
  12.     rrf      DatoAEnviar,F       ;Roto a la derecha DatoAEnviar
  13.     bsf     DATA                    ;SUPONGO que el dato a enviar será un 1. Por lo tanto pongo el pin DATA en alto...
  14.     btfss  STATUS,C              ;¿El bit a enviar es realmente un 1?
  15.     bcf     DATA                    ;No.Entonces pongo DATA en bajo
  16.  
  17.     bsf    CLOCK                   ;pin CLOCK en alto
  18.     bcf    CLOCK                   ;pin CLOCK en bajo    
  19.  
  20.     decfsz  Contador,F
  21.     goto     ProximoBit
  22.  
  23.     rrf        DatoAEnviar,F                  ;Roto una vez más el registro antes de volver
  24.     return

De esta manera, hemos logrado enviar el registro DatoAEnviar sin necesidad de un registro temporal para su envío.
Cabe destacar que el registro DatoAEnviar no es corrompido por la función de envío. ¿Por qué?
Puedes analizar y comprobar que si rotas un registro 9 veces en una dirección, el registro volverá a contener su valor original. En esto se basa el algoritmo anterior.
Título: Puedes enviar un registro serialmente sin necesitar una variable que cuente?
Publicado por: BrunoF en 17 de Noviembre de 2006, 20:46:04
En este caso evitaremos usar una variable que cuente 8 veces cada bit a enviar.

Código: ASM
  1. movlw  0xF0              ;Muevo 0xF0 a W
  2.     movwf  DatoAEnviar   ;Muevo W en DatoAEnviar
  3.     call      EnviaDato
  4. ...
  5. ...
  6.  
  7. EnviaDato    
  8.    bsf   STATUS,C         ;Pongo en alto STATUS,C para "ensuciar" DatoAEnviar
  9.    rrf   DatoAEnviar,F    ;Roto a la derecha DatoAEnviar
  10.  
  11. ProximoBit
  12.    bsf   DATA           ;Supongo que el bit a enviar es un alto
  13.    btfss STATUS,C    ;¿El bit a enviar es realmente un alto?
  14.    bcf   DATA           ;No. Entonces DATA en bajo
  15.  
  16.    bcf    STATUS,C         ;Pongo en bajo STATUS,C
  17.    rrf     DatoAEnviar,F    ;y roto a la derecha el registro
  18.    movf  DatoAEnviar,F   ;Muevo el registro sobre si mísmo(recuerden que esto setea el flag STATUS,Z)
  19.    btfss  STATUS,Z         ;¿Registro=0?
  20.    goto   ProximoBit       ;No. Seguir
  21.  
  22.    return                      ;Si. volver

¡Cuidado! Que en este ejemplo, el registro DatoAEnviar retorna de la subrutina siempre valiendo cero, por lo tanto se destruye su contenido original.

Para que no se destruya su contenido original, debemos usar una variable auxiliar.
Ejemplo:

Código: ASM
  1. movf  DatoAEnviar,W   ;Muevo DatoAEnviar a W
  2.     call      EnviaDato
  3. ...
  4. ...
  5.  
  6. EnviaDato    
  7.    movwf Aux
  8.    bsf   STATUS,C         ;Pongo en alto STATUS,C para "ensuciar" Aux
  9.    rrf    Aux,F               ;Roto a la derecha Aux
  10. ProximoBit
  11.    bsf   DATA           ;Supongo que el bit a enviar es un alto
  12.    btfss STATUS,C    ;¿El bit a enviar es realmente un alto?
  13.    bcf   DATA           ;No. Entonces DATA en bajo
  14.  
  15.    bcf    STATUS,C         ;Pongo en bajo STATUS,C
  16.    rrf     Aux,F               ;y roto a la derecha el registro
  17.    movf  Aux,F              ;Muevo el registro sobre si mísmo(recuerden que esto setea el flag STATUS,Z)
  18.    btfss  STATUS,Z        ;¿Registro=0?
  19.    goto   ProximoBit      ;No. Seguir
  20.  
  21.    return                      ;Si. volver

Fuente: Tips & Tricks del MPLAB IDE
Título: Puedes intercambiar los contenidos de W y un registro sin una variable auxiliar?
Publicado por: BrunoF en 17 de Noviembre de 2006, 20:54:47
Lo que se intenta aquí es intercambiar el contenido de W y un registro cualquiera sin tener que requerir a una tercera variable para almacenar un valor temporalmente.

Veámoslo:

Código: ASM
  1. Intercambiar
  2.         XORWF  Registro,F
  3.         XORWF  Registro,W
  4.         XORWF  Registro,F

Por lo tanto, luego de eso, W tendrá ahora el valor de Registro y Registro tendrá el valor de W.

Fuente: Tips & Tricks del MPLAB IDE
Título: Puedes generar mensajes y errores personalizados en el MPLAB?
Publicado por: BrunoF en 17 de Noviembre de 2006, 21:02:11
Ejemplo:

Código: ASM
  1. LIST      P=16F84A
  2.         INCLUDE  <P16F84A.INC>
  3.  
  4.         messg "¡Este es un mensaje creado por mi"
  5.         error "¡Este es un error creado por mi!"
  6.  
  7.         END

Al ensamblarlo con el MPASM dentro del MPLAB, podremos ver en la ventana Build, que aparecerá nuestro mensaje y error personalizado.
Título: Puedes comprobar si un registro contiene un valor 0 con pocas instrucciones?
Publicado por: BrunoF en 18 de Noviembre de 2006, 04:26:57
Para comprobar su un registro posee un valor 0 o no, podemos hacer lo siguiente:

Código: ASM
  1. movf   Registro,F  ;Sobreescribe el contenido de Registro en si mismo. Esto parecería no tener sentido
  2.                        ;pero recordemos que la instruccion movf afecta al flag STATUS,Z.
  3.     btfsc   STATUS,Z   ;Registro=0?
  4.     goto    EsCero     ;Si.
  5.  
  6.     nop                ;No.
Título: Re: ¿Sabías que...
Publicado por: todopic en 05 de Diciembre de 2006, 23:12:06
Hola, yo empleo tambien para verificar si un registro es cero, lo siguiente

incf    registro,f
decfsz  registro,f
goto nocero          :mrgreen:  :D  y son tambien 3 rengloncitos!...


Un abrazo

Norberto
Título: Re: ¿Sabías que...
Publicado por: BrunoF en 07 de Diciembre de 2006, 19:42:23
También es válido Norberto.
Pero qué manera de complicarte! :P

Aporto una cosita más.
A veces usamos algunas sub-rutinas que devuelven valores en W(usando retlw por ejemplo) el resultado de la subrutina.

Supongamos una subrutina llamada "Envio" que devuelve W = 1 si se envio satisfactoriamente y W != 1 si surgió algún error.

Forma rápida de comprobar si el envío fue correcto(devolvería W=1)

    call Envio
    xorlw .1
    btfss STATUS,Z ;¿W valia 1?
    goto Errores  ;No. Hubo algun error. Ir a Errores

    Nop     ;Si. Envio ok

Lo mísmo usando un sublw

    call Envio
    sublw .1
    btfss STATUS,Z ;¿W valia 1?
    goto Errores  ;No. Hubo algun error. Ir a Errores

    Nop     ;Si. Envio ok

Lo mísmo usando un addlw

    call Envio
    addlw 0xFF
    btfss STATUS,Z ;¿W valia 1?
    goto Errores  ;No. Hubo algun error. Ir a Errores

    Nop     ;Si. Envio ok

Saludos.
Título: Re: ¿Sabías que...
Publicado por: elmasvital en 03 de Febrero de 2007, 23:21:45
Solo queria agradecerte el esfuerzo bruno... está perfectamente explicado.

1 saludo
Título: Re: ¿Sabías que...
Publicado por: BrunoF en 03 de Febrero de 2007, 23:41:18
Y yo te agradezco a vos por decirmelo.

Un abrazo.
Título: Re: ¿Sabías que...
Publicado por: Resistencio en 04 de Abril de 2007, 02:23:32
Hola BRUNO, ando por aca, aprendiendo nomas !!! Buenos tus ejemplos !!!

En el ultimo, para verificar si W vuelve con 1, se podria hacer, esto ???

CALL ENVIO
btfss W,00
goto errores

Nunca lo vi escrito por ningun, lado...la idea es consultar al bit 0 del W...

Perdon por tantas dudas y preguntas !!!

SALUDOS !!!

______________________________
"Justo a mi, me toco ser YO" QUINO
Título: Re: ¿Sabías que...
Publicado por: BrunoF en 04 de Abril de 2007, 08:36:27
Hola Resistencio.
La respuesta es NO.
W no es un registro físico. Significa que no podés aplicarle el btfss, ni btfsc, ni incf, decf, etc etc.

para verificar si W volvió con 1 podés hacer:

call ENVIO
sublw .1
btfsc  STATUS,Z
goto VolvioConUno
goto VolvioDistintoAUno


Saludos
Título: Re: ¿Sabías que...
Publicado por: Resistencio en 04 de Abril de 2007, 12:11:52
Aclarado, BRUNO, gracias...es un registro fantasma !!!  :D

ESTO ME PARECIO INTERESANTE PARA EL "sabias que ???"...

Alguno de estos nemonicos son mas faciles de recordar !!!

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Mnemonic    Description          Equivalent      Status
                  Operation(s)

===============================================================
    
ADDCF    f,d    Add Carry to File       BTFSC    3,0
                  INCF   f,d          Z

===============================================================
                   
ADDDCF    f,d    Add Digit Carry to File    BTFSC    3,1
                  INCF    f,d          Z

===============================================================

B    k    Branch             GOTO    k         -

===============================================================

BC    k    Branch on Carry       BTFSC    3,0          -   
                  GOTO   k
 
===============================================================

BDC    k    Branch on Digit Carry       BTFSC    3,1         -   
                  GOTO   k

===============================================================

BNC    k    Branch on No Carry       BTFSS    3,0         -   
                  GOTO   k

===============================================================

BNDC    k    Branch on No Digit Carry    BTFSS    3,1         -   
                  GOTO    k

===============================================================

BNZ    k    Branch on No Zero       BTFSS   3,2         -   
                  GOTO   k

===============================================================
 
BZ    k    Branch on Zero          BTFSC   3,2         -
                  GOTO   k

===============================================================
 
CLRC        Clear Carry          BCF    3,0          -

===============================================================

CLRDC        Clear Digit Carry       BCF    3,1          -

===============================================================

CLRZ        Clear Zero          BCF    3,2          -

===============================================================
 
LCALL    k    Long Call          BCF/BSF   0x0A,3       -
                  BCF/BSF   0x0A,4
                  CALL   k

===============================================================

LGOTO    k    Long GOTO          BCF/BSF   0x0A,3       -
                  BCF/BSF   0x0A,4
                  GOTO    k

===============================================================

    
MOVFW    f    Move File to W          MOVF    f,0                   Z

===============================================================
 
NEGF    f,d    Negate File          COMF   f,1
                  INCF   f,d                   Z

===============================================================
 
SETC        Set Carry          BSF    3,0                   -

===============================================================

SETDC        Set Digit Carry                   BSF    3,1                    -

===============================================================

SETZ        Set Zero          BSF    3,2                    -

===============================================================

SKPC        Skip on Carry          BTFSS    3,0                    -

===============================================================

SKPDC        Skip on Digit Carry       BTFSS    3,1                     -

===============================================================
 
SKPNC        Skip on No Carry       BTFSC    3,0                     -

===============================================================
 
SKPNDC        Skip on No Digit Carry       BTFSC    3,1         -

===============================================================
 
SKPNZ        Skip on Non Zero       BTFSC    3,2                       -

===============================================================
 
SKPZ        Skip on Zero          BTFSS    3,2                       -

===============================================================
 
SUBCF    f,d    Subtract Carry from File    BTFSC    3,0                                 Z
                  DECF    f,d

===============================================================   
 
SUBDCF    f,d    Subtract Digit Carry from File    BTFSC   3,1          Z
                  DECF    f,d

===============================================================
 
TSTF    f    Test File          MOVF    f,1                       Z

===============================================================

Otro SQ? (Sabias Que ???) IMPORTANTE, es que:

SUBLW ---------------------> aca no se resta el LITERAL de W, sino exactamente lo contrario !!! GUARDIA !!!


BRUNO, si preferis que quite esto de aqui decime...me parecio que podia ir...SALUDOS !!!

_____________________________
"Justo a mi, me toco ser Yo" QUINO
Título: Re: ¿Sabías que...
Publicado por: BrunoF en 21 de Abril de 2007, 12:56:42
No! para nada! muchas gracias! toda colaboración será bienvenida!

Saludos
Título: Re: ¿Sabías que...
Publicado por: microcom en 21 de Abril de 2007, 19:09:30
viejo brunof y sus buenos tutoriales
excelente.

soy muy participe de tus buenos esfuerzos para enriquezer  el lenguaje  tutoriales.
hasta puedo molestarlo un poco.
saludos.