



































Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Prepara tus exámenes
Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Prepara tus exámenes con los documentos que comparten otros estudiantes como tú en Docsity
Encuentra los documentos específicos para los exámenes de tu universidad
Estudia con lecciones y exámenes resueltos basados en los programas académicos de las mejores universidades
Responde a preguntas de exámenes reales y pon a prueba tu preparación
Consigue puntos base para descargar
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Comunidad
Pide ayuda a la comunidad y resuelve tus dudas de estudio
Ebooks gratuitos
Descarga nuestras guías gratuitas sobre técnicas de estudio, métodos para controlar la ansiedad y consejos para la tesis preparadas por los tutores de Docsity
Una guia para programar con el microcontrolador 8051
Tipo: Apuntes
1 / 43
Esta página no es visible en la vista previa
¡No te pierdas las partes importantes!




































e
Manuel Sánchez Raya Versión 1. 31 de Marzo de 2000
Comenzaremos dando una visión general de la programación en ensamblador del microcontrolador 8051. Incluso si no vamos a programar en lenguaje ensamblador, es necesario introducir las instrucciones que encontraremos si la búsqueda de errores en el código nos fuerza a observar el listado que genera el compilador.
Las tablas que siguen muestran todas las instrucciones para referencia posterior. Las siguientes definiciones son necesarias para entender las instrucciones:
?? A : (el acumulador o ACC) es el registro más relacionado con la ALU. ?? #dato : es un valor numérico precedido por el signo #, no una dirección de memoria. Es un dato numérico. ?? #dato 16 : es una constante de 16 bits incluida en la instrucción. ?? directo : es la dirección de memoria donde se deben encontrar los datos utilizados por la instrucción, NO los datos si no una dirección interna entre 0 y 7FH (o 80H a FFH para registros de función especial). ?? Rn : se refiere al contenido de un registro. ?? @Ri : precedido por el signo @ indica que el registro es un puntero y se refiere al valor en memoria a donde apunta el registro. Solo R0 y R1 pueden usarse de esta forma. ?? DPTR : El puntero de datos, usado para direccionar datos fuera del chip y código con la instrucción MOVX o MOVC. ?? PC : el contador de programa, almacena la dirección de donde se obtendrá el siguiente byte de código.
Las instrucciones más simples son las que mueven números entre los registros y varios tipos de memoria. Hay varias formas de hacerlo, denominadas modos de direccionamiento. La instrucción MOV tiene varias formas dependiendo de donde venga el dato y a donde vaya.
MOV no destruye el dato de la fuente si no que lo copia al destino. Por ejemplo, no podemos mover datos de un registro a otro, pero una vez visto esto, para un banco de registros determinado cada registro también tiene una dirección directa, a la que podemos acceder. Las instrucciones de movimiento de bit están agrupadas con la boleanas.
Para referirnos a los registros de función especial se emplea el nombre, por lo que podemos escribir MOV TCON, #013H. En ensamblador podemos sustituir números por nombres. en lugar de usar direcciones directas podemos emplear etiquetas mediante EQU: RELAY EQU 045H y más adelante en el programa usar: MOV RELAY, #0FFH.
Acumulador/Registro:
MOV A,R7 ; copia el contenido de R7 al acumulador MOV R1, A ; copia el contenido del acumulador a R
Acumulador/Directo:
MOV A, 22H ; copia el contenido de la dir. 22H al acumulador MOV 03H, A ; acumulador => 03H (R3 en banco de registros 0) MOV A, 1BH ; 1BH (R3 en banco de registros 3) => acumulador
Acumulador/Datos:
MOV A, #22 ; el número 22 (16H) pasa al acumulador MOV A, #7EH ; el número 7EH => acumulador
Registro/Datos:
MOV R1, #5FH ; el número 5FH => R
Acumulador/Indirecto : Esto se puede hacer solo con R0 o R1 como punteros.
MOV R1, #4BH ; el número 4BH => R MOV A, @R1 ; contenido de donde apunte R1 (4BH) => acumulador MOV @R0, A ; acumulador => donde R0 apunte
Registro/Directo
MOV R3, 7FH ; contenido de la dirección 7FH => R MOV 6EH, R2 ; R2 => dirección 6EH
Directo/Directo
MOV 1FH, 7EH ; contenido de dirección 7EH => dirección 1FH
Directo/Indirecto
MOV R3, @R1 ; contenido de dirección apuntada por R1 => R MOV @R0, R6 ; R6 => dirección apuntada por R
Directo/Datos
MOV R7, #01 ; el número 01 => R
Indirecto/Datos
MOV @R0, #7FH ; el número 7FH => donde apunte R
Hay otra forma de almacenar datos, la pila. Hasta ahora hemos puesto un byte de información en una dirección especifica. Con una pila, los valores se almacenan en direcciones sucesivas direccionadas por el puntero de pila. Cada vez que introducimos un valor en la pila, el puntero de pila se decrementa en uno. Constituye un buffer primero en entrar, ultimo en salir.
Con la familia 8051, la pila se localiza en la RAM interna. Dos instrucciones introducen bytes en la pila. Primero, la instrucción PUSH introduce un byte cada vez que se usa. Segundo, cada instrucción CALL y cada interrupción hardware introduce el valor actual del contador de programa (2 bytes) en la pila.
PUSH : El contenido de cualquier de las direcciones directas puede introducirse incluyendo los SFR. El puntero de pila apunta al valor de la cima, no el espacio vacio sobre la pila. Es posible guardar ACC, B, PSW y los registros de control hardware. No es posible introducir R0 a R7 en la pila por nombre porque se espera que cambiemos el banco de registros modificando los dos bits del PSW.
MOV SP, #09CH ; pone el puntero de pila apuntando a la dirección 9CH PUSH B ; incrementa el puntero de pila hasta 9DH y pone el contenido del registro B (dirección directa F0H) en la pila en la dirección de memoria interna 9DH
POP : Esta el la instrucción inversa de PUSH. Recordemos que restaurar tras multiples PUSH debe hacerse en orden inverso. Push/pop no en orden pueden servir para intercambiar valores fácilmente.
POP PSW ; cima de la pila => PSW (dirección directa D0H) y SP decrementado en una unidad
Las instrucciones de salto encauzan la secuencia de ejecución del programa. Hay tres métodos de direccionamiento para las instrucciones de salto y llamada a subprograma. El salto corto (SJMP) cubre un rango de direcciones de 128 bytes atrás a 127 bytes delante de la dirección de la próxima instrucción. Los saltos absolutos, AJMP y ACALL proporcionan los 11 bits menos significativos de la dirección de 16 bits necesaria y mantienen los 5 bits de la siguiente instrucción del programa. Esto fuerza al destino a estar en el mismo bloque de 2K que la instrucción CALL. Finalmente, tenemos los saltos largos, LCALL o LJMP que incluyen la dirección absoluta y completa de 16 bits del destino.
1.3.1.- Salto incondicional
Lo veremos usando etiquetas en lugar de números puesto que esta es la forma de escribir código ensamblador legible, pero podemos poner una dirección de código numérica especifica usando # (como #215EH). La instrucción provoca que el contador de programa (registro PC) cambie de apuntar a la próxima instrucción tras la instrucción de salto a apuntar a la nueva dirección. La siguiente instrucción a ejecutar es, por tanto, la de la nueva dirección.
AJMP SUB ; en el mismo bloque de 2K LJMP POINTA ; en cualquier sitio del código SJMP LOOP ; relativo: +127 a – JMP @A+DPTR ; esta instrucción puede usarse para hacer un salto a múltiples direcciones, pero no se usa mucho. Incluso con una tabla de saltos el valor de ACC debe multiplicarse por dos o tres para encontrar la instrucción de salto.
1.3.2.- Salto condicional.
Esta instrucción realiza la prueba y hace un salto corto o en caso contrario sigue con la siguiente instrucción.
JZ POINTX ; salta si ACC es todo cero JNZ POINTY ; salta si cualquier bit de ACC es diferente de cero JC POINTZ ; salta si el indicador de acarreo es 1 (puesto) JNC POINTZ JB P3.5,POINTA ; salta si el bit del puerto es 1 (aquí en puerto 3) JNB 06EH, POINTB ; salta si bit en zona de RAM direccionable a bit es 0 JBC 22.3, POINTB ; esto también pone a cero el bit (22.3= bit 19 = 13H)
1.3.3.- Comparaciones.
Comparar es para la ALU una cuestión de restar (sumar el inverso) y comprobar el acarreo. Hay dispositivos de comparación que devuelven tres resultados sobre la magnitud relativa de dos números binarios (igual, mayor que o menor que), pero la ALU del 8051 evita esto usando el acarreo de la sustracción.
CJNE : CJNE es comparar y saltar (corto) si no igual. No existen todas las combinaciones. Podemos comparar solo un registro con el acumulador usando la dirección directa del registro, que depende del banco de registros usado. No podemos, por ejemplo referirnos a R0 o R7, pero si usamos el banco de registros 0, nos referiremos como 00 o 07 (o 08 y 0FH si usamos el banco de registros 1).
CJNE A, 3EH, POINTZ ; compara contenido de dirección 3EH CJNE A, #10, POINTW ; compara ACC con número 10 CJNE R5, #34, LOOP CJNE @R1, #5, GOINGON ; compara número 5 con contenido RAM interna donde apunta registro R
El flag de carry se ve alterado por esta instrucción. CJNE puede ser la primera parte de una prueba mayor que/igual que/menor que. Si el segundo número de la comparación es mayor, no habrá carry. Si el segundo número es igual o menor que el primero, habrá carry en la salida. Por lo que es posible comprobar y saltar a tres direcciones de la siguiente forma:
ACALL STEPRUTINA ; si la siguiente línea de código comienza en 201DH, STEPRUTINA comienza en 2500H, y si el SP está inicialmente en 95H, esta instrucción deja el puntero de pila con 97H, 1DH en la dirección 96H, 20H en 97H, y deja el contador de programa con 2500H
Las direcciones se colocan de forma simbólica como:
LCALL : La llamada larga puede estar en cualquier posición del espacio de código. Las funciones de la pila y contador de programa son las misma que en el caso anterior, con la diferencia que la dirección completa de 16 bits se encuentra en el segundo y tercer bytes de la instrucción.
LCALL DISPLAY ; si la siguiente línea de código comienza en 23F1H, DISPLAY comienza en 24AFH, y el SP está en 38H, esta instrucción deja el puntero de pila con 3AH, deja F1 en la dirección 39H, deja 23H en 3AH, y deja el contador de programa con 24AFH.
RET : La instrucción de retorno pone los dos valores de la cima de la pila en el contador de programa. Permite que continúe el flujo del programa principal después que ha terminado la subrutina.
RET ; si la situación es la del ACALL anterior, esto devolvería el SP a 95H, y devuelve el contador de programa a 201DH
RETI : La instrucción RETI funciona como RET con la característica adicional que pone a cero de forma automática el hardware que permite atender más interrupciones del mismo nivel de prioridad. O sea que se usa para retornar de una interrupción.
NOP : Esta instrucción no hace nada, pero es muy utilizada por el tiempo que tarda en ejecutarse para producir retardos de tiempo.
ANL: Realiza un AND lógico produce un 1 solo en las posiciones de bit donde ambos bits valgan 1. Esta instrucción deja el resultado en el acumulador.
ANL A,R6 ; ACC(1101 1000) con R6(1000 1111) da en ACC(1000 1000) ANL A, 25H ; AND ACC con el contenido de la dirección 25H ANL A, @R1 ; AND ACC con contenido de dirección externa apuntada por R ANL A, #03H ; AND ACC con el número 3
ANL 25H, A ; lo mismo que ANL A, 25H
ORL: Hacer un OR lógico pone 1 en lugares donde cualquier bit valga 1.
ORL A, R6 ; ACC(1101 1000) con R6(1000 1111) da en ACC(1101 1111) ORL A, 25H ORL A, @R ORL A, #03H ORL 25H, A
XRL: realiza el OR eXclusivo dando un 1 en posiciones si uno y solo uno de los bits es 1, si ambos son 1 pone 0.
XRL A, R6 ; ACC(1101 1000) con R6(1000 1111) da en ACC(0101 0111) XRL A, 25H XRL A, @R XRL A, #03H XRL 25H, A
CPL : realiza el complemento lógico que cambia ceros por unos y unos por ceros.
CPL A ; con ACC(1101 1000) deja en ACC(0010 0111)
CLR : esto pone todos los bits a cero.
CLR A ; pone a cero los 8 bits del acumulador
1.4.1.- Rotaciones.
Además de sumar e invertir hay más formas de mover datos a izquierda y derecha, denominadas desplazamiento o rotaciones. Un valor de 0001 desplazado a la izquierda es 0010. De la misma forma, 1010 desplazado a derecha es 0101. En matemática binaria, un desplazamiento es multiplicar o dividir por dos.
RR, RL, RRC, RLC : Con estas instrucciones, el acumulador se desplaza un lugar a la izquierda (hacia MSB) o hacia la derecha (hacia LSB). si el acarreo está incluido el bit final va al acarreo y el acarreo va a entrar por el otro lado de la rotación.
RL A ; desplazamiento de 8 bits hacia el MSB: 1011 1100 pasa a 0111 1001 RR A ; desplazamiento a derecha, 1011 1100 pasa a 0101 1110 RRC A ; desplazamiento de 9 bits hacia LSB, el carry se copia al MSB: 1 1011 1100 pasa a 0 1101 1110 RLC A ; desplazamiento de 9 bits a izquierdas, el carry va al LSB: 1 1011 1100 pasa a 1 0111 1001
CPL (bit) : Complementa el bit (cambia 0 por 1 o 1 por 0).
CPL C ; invierte el bit de acarreo CPL P3.5 ; invierte el tercero desde msb del puerto 3
ANL (bit) :
ANL C,ACC.5 ; AND acarreo y bit 5 del acumulador, resultado en carry ANL C, /ACC.5 ; AND carry y complemento de bit 5 del acum.. resultado en carry, el acumulador no cambia
ORL C, ACC.5 ; OR acarreo y bit 5 del acumulador, resultado en carry ORL C, /ACC.5 ; OR carry y complemento de bit 5 del acum. resultado en carry, el acumulador no cambia
MOV (bit): Esta puede agruparse con las otras instrucciones MOV, pero la vemos aquí porque está orientada a bit.
MOV C, ACC.2 ; contenido del tercer bit del acumulador (un 1 ó 0) se copia en el bit de carry MOV 20.3, C ; el contenido del acarreo se copia en la dirección de RAM direccionable a bit 3 (20.3 es el cuarto bit del byte 20H que es el primer byte de la zona de RAM direccionable a nivel de bit)
La familia básica 8051 dispone de una capacidad matemática muy limitada. Se pueden encadenar instrucciones sobre byte simple en trozos de código para realizar tratamientos matemáticos más elaborados. Excepto el incremento y decremento, todas las operaciones aritméticas sobrescriben el contenido del acumulador con el nuevo resultado.
1.6.1.- Suma.
ADD : Esta instrucción no suma el bit de acarreo con el bit menos significativo, si no que produce un acarreo de salida. Es la primera instrucción de una operación con varios bytes, pero podemos poner a cero el bit de carry y usar ADDC a través de una secuencia de varios bytes.
ADD A, R5 ; suma contenido de R5 a ACC; resultado en ACC; overflow en carry ADD A, 22 ; suma contenido de RAM interna 22(16H) al ACC: resultado en ACC, overflow en carry ADD A, @R0 ; suma contenido de RAM interna apuntada por el contenido de R0 a ACC; resultado en ACC; overflow en carry ADD A, #22 ; suma número 22(16H) al ACC; resultado en ACC; overflow en carry
ADDC : Esta instrucción incluye el bit de carry en la suma.
ADDC A, R5 ; suma contenido de R5 con acarreo a ACC; resultado en ACC; overflow en carry ADDC A, 22 ; suma contenido de RAM interna 22(16H) con acarreo al ACC: resultado en ACC, overflow en carry ADDC A, @R0 ; suma contenido de RAM interna apuntada por el contenido de R0 con acarreo a ACC; resultado en ACC; overflow en carry ADDC A, #22 ; suma número 22(16H) con acarreo al ACC; resultado en ACC; overflow en carry
1.6.2.- Resta.
SUBB : El indicador de borrow es el inverso del acarreo obtenido de un sumador normal. No existe instrucción de resta sin el borrow, por lo que tendremos que ponerlo a cero, antes de comenzar la resta. El borrow indica que algo demasiado grande se restó y obtendremos una respuesta negativa en lugar de lo que parece un número positivo muy grande. Para la resta de múltiples bytes, comenzamos desde el byte de menor peso hasta el de mayor peso, borrows generados durante la operación son aceptables. La respuesta es positiva (y correcta) si no hay borrow cuando los bytes más significativos hayan sido restados. Recordar que estamos realizando matemática sin signo. Para usar números negativos tenemos que hacer algunos ajustes en el software. Operaciones con enteros con signo de 16 bits también se pueden realizar de forma directa, pero se sugiere emplear C.
SUBB A, R5 ; resta contenido de R5 con borrow a ACC; resultado en ACC; overflow en carry SUBB A, 22 ; resta contenido de RAM interna 22(16H) con borrow al ACC: resultado en ACC, overflow en carry SUBB A, @R0 ; resta contenido de RAM interna apuntada por el contenido de R0 con borrow a ACC; resultado en ACC; overflow en carry SUBB A, #22 ; resta número 22(16H) con borrow al ACC; resultado en ACC; overflow en carry
1.6.3.- Otras operaciones.
INC y DEC : Estas son instrucciones simétricas excepto para el puntero de datos. Decrementar 0 o incrementar FFH genera FFH o 0 respectivamente.
INC A DEC A INC R2 DEC R INC 45H DEC 3EH INC @R0 DEC @R INC DPTR ;no existe equivalente para decremento
MUL : Solo hay una multiplicación por hardware, que produce un resultado de 16 bits en el acumulador (byte bajo) y el registro B (byte alto). Si el producto excede 8 bits, el indicador de overflow se pone a uno. El indicador de acarreo siempre se pone a cero.
C es el lenguaje usado originalmente para escribir el sistema operativo UNIX y por mucho tiempo estrechamente relacionado con UNIX. Es un lenguaje estructurado , y puede generar código fuente compacto. Llaves { } en lugar de palabras marcan la estructura y el lenguaje emplea bastantes símbolos poco usados. Podemos controlar varias funciones a nivel máquina sin emplear lenguaje ensamblador. Podemos escribir C condensado, sin embargo, el siguiente programador que tenga que estudiar el programa empleará bastante tiempo intentando entender como funciona. C es mucho más fácil de escribir que lenguaje ensamblador, porque el software de desarrollo se encarga de los detalles.
El lenguaje ensamblador para el 8051, descrito con detalle en el capitulo anterior, es como cualquier otro lenguaje ensamblador. Aunque es un fastidio aprender otro lenguaje ensamblador, el proceso no es difícil si ya se ha visto uno.
Tipo de datos Tamaño Rango Bit 1 bit 0 o 1 unsigned char 1 byte 0 a 255 unsigned int 2 bytes 0 a 65535 (signed) char 1 byte -128 a + (signed) (int) 2 bytes -32768 a + (signed) long 4 bytes -2147483647 a + unsigned long 4 bytes 0 a 4294967295 float 4 bytes 6 dígitos decimales double 8 bytes 10 dígitos decimales
Debido a que la computación comienza con números, necesitamos entender como se almacenan y se representan. La elección del tipo de la variable o tipo de datos es más critico en el 8051 que con otros ordenadores. Las instrucciones máquina soportan de forma directa solo los dos primeros tipos de la tabla. Aunque una operación de lenguaje de alto nivel puede parecer muy simple en el programa, son necesarias una serie de instrucciones máquina para realizar la manipulación de las variables más complejas. Usar variables de punto flotante en particular añade tiempo de calculo al programa y aumenta en gran medida su tamaño.
Un tipo de variable es bit. Un bit puede ser 1 (“verdadero”) o 0 (“falso”). Las variables bit que usan las instrucciones del 8051 se colocan en la RAM interna. Aunque las instrucciones 8051 lo soportan de forma directa, en C el uso de estos bits es una extensión al lenguaje estándar. El lenguaje C no está orientado a bit. No podemos emplear notación binaria, debemos emplear notación hexadecimal. La mayoría de compiladores 8051 añaden alguna forma de definir y usar el direccionamiento a bit del 8051, pero técnicamente estas extensiones hacen que el lenguaje no sea plenamente portable. Un programa realizado con estas extensiones del lenguaje no funcionaria en otro procesador que no dispusiese de este tipo de direccionamiento a bit.
Las variables de tipo char en C son valores de 8 bits, que encajan idealmente con el 8051 puesto que este puede manejar solo 8 bits a la vez. Tiene valores de 0 a 255 (sin signo) a no ser que sean con signo. El bit más significativo será el signo. Un 1 representa negativo , por lo que
las representaciones con signo y sin signo son las mismas para 0 a 127. El ordenador representa números negativos mediante notación en complemento a dos, que hace –1 11111111 y -
las variables int (enteros) en C son valores de 16 bits. A diferencia de otras familias de computadores se almacena el byte más significativo en la dirección menor. Los valores con signo también tienen en el msb el bit de signo y emplean la notación en complemento a dos. De forma similar a las variables int tenemos las variables long de 4 bytes (32 bits).
Representaciones exponenciales más complicadas en C son float y double.
Varios programadores emplean abreviaturas para evitar escribir demasiado. Esto se puede hacer fácilmente con expresiones #define en la cabecera del listado. Podemos usar en C por ejemplo la abreviatura uchar por unsigned char y uint por unsigned int.
En ensamblador podemos hacer lo mismo con una línea EQU. Podemos sustituir una larga expresión por una simple palabra.
Al menos tres espacios de memoria diferentes pueden tener la misma dirección. Primero, tenemos el espacio de código, code , para el código del programa y otra información que no cambie (constantes). Esto se introduciría en EPROM. No existen instrucciones para escribir en el espacio de código porque el programa no puede modificarse a sí mismo. El espacio de código también es el lugar lógico para guardar los mensajes empleados para la interacción con el usuario.
El segundo espacio de memoria es la RAM interna data , (el lugar para las variables de datos). Tiene un tamaño entre 64 y 256 bytes dependiendo del procesador, y siempre forma parte del microcontrolador. Esto no es mucha memoria, pero hay gran cantidad de formas de accederla. La memoria de datos interna es un buen lugar para mantener variables temporales para cálculos, así como para mantener variables que se usan con frecuencia.
Finalmente, hay memoria externa de datos xdata que no radica en el propio chip. Se emplea normalmente de 2 a 64Kbytes en un solo chip externo. Las instrucciones máquina para acceder esta memoria deben trasladar el valor a memoria interna antes de poder usarlo, un proceso que implica varias instrucciones máquina en sí mismo, y luego devolver el resultado a memoria externa. La memoria externa es el lugar para almacenar variables usadas con poca frecuencia y para recoger datos que esperan ser procesados o enviados a otro ordenador.
#define PORTA XBYTE[0x4000]; bit flag1; /* ejemplo de asignación */ code char table1[ ]={1, 2, 3, “AYUDA”, 0xff}; data unsigned int temp1;
#include <reg51.h> void msec (unsigned int); void main(void) { unsigned char array[10]; unsigned char I; while (1) { for (i=0; i<=9;i++) { array[i]=P2=P0; msec(100); } } }
En ensamblador resulta:
CODIGO SEGMENT CODE DATOS SEGMENT DATA
RSEG DATOS ARRAY DS 10; RSEG CODIGO INICO: MOV R0, #ARRAY ; pone puntero de array OTRO: MOV ACC, P MOV P2, ACC MOV @R0, ACC ; almacenamos un byte MOV R2, #0 ; byte alto MOV R1, #100 ; byte bajo CALL MSEC INC R0 ; apuntamos al siguiente byte CJNE R0, #ARRAY+10, OTRO ; fin? JMP INICO END
Las aplicaciones de control a menudo emplean operaciones lógicas de bit en lugar de aritméticas. Con los puertos de entrada y salida, resulta deseable leer o cambiar un bit de un byte sin afectar a los demás bits. Puede ser que este bit apague o encienda un motor mientras que los otros bits del puerto activan indicadores de aviso o comienzan la conversión de un convertidor A/D. Algunos puertos se direccionan a bit (esos conectados directamente al chip, por ejemplo), pero la mayoría de puertos añadidos responden solo como bytes enteros. Aquí es donde entran en juego los operadores lógicos de bit. La tabla siguiente los lista para los dos lenguajes.
Operación lógica Instrucción ensamblador C NOT CPL A (^)? AND ANL A, # & OR ORL A, # | OR EXCLUSIVA XRL A, # ^
En los siguientes ejemplos PORTA es un puerto externo direccionable a byte del que necesitamos que el tercer bit desde abajo (bit 2, porque comenzamos con 0) se ponga a uno y que el bit 6 se ponga a cero sin afectar a los demás bits.
extern xdata unsigned char PORTA;
void main (void) { PORTA = (PORTA & 0xbf) | 0x04; }
En ensamblador:
PORTA EQU 6000H CSEG AT 2000H MOV DPTR, #PORTA MOVX A, @DPTR ; Obtiene la lectura actual ANL A, #10111111 ; pone bit 6 bajo ORL A, #00000100 ; pone bit 2 alto MOBS @DPTR, A ; emite nuevos valores END
Además de las operaciones ya mencionadas, dos operadores relacionados a bit reorganizan un byte. Primero tenemos la rotación. Si pensamos que un byte es una colección de bits ordenados desde la izquierda (msb, bit más significativo) a la derecha (lsb, bit menos significativo), podemos observar que una rotación a la derecha mueve todos los bits a la derecha un lugar. Es decir, cada bit se mueve a un lugar con menos pero. El resultado es una división por dos, de la misma forma que mover un punto decimal a la izquierda para un sistema base 10 es una división por 10. Una rotación a la izquierda produce una multiplicación por dos de la misma forma.
¿ Que le ocurre al último bit de la fila? En lenguaje ensamblador tenemos dos tipos de rotaciones. La primera, usando RR o RL, es una rotación simple de 8 bits donde el bit más a la izquierda vuelve a la posición más a la derecha o viceversa. Por otro lado, RRC o RLC, incluye el bit de acarreo. Si el acarreo es cero, un cero rota dentro del byte.
C soporta solo el desplazamiento, a=x>>3; a la izquierda y a=x<<3; a la derecha. Un desplazamiento es una rotación que siempre rellena con ceros la entrada y descarta cualquier bit que salga por el otro lado. Un desplazamiento de 8 bits en una variable de tipo char siempre da cero.
Solo C dispone de una representación abreviada para modificar una variable y reasignar el resultado de nuevo a la variable original. Normalmente escribiremos algo así:
PORTA = PORTA & 0xf7;