Cuando se quiere cambiar alguno de los valores de los bits de un registro sin tocar los demás, se hace una operación lógica con otro valor, denominado máscara. Dependiendo del tipo de operación que se quiera hacer, se ha de usar una operación lógica u otra y usar una máscara de un determinado tipo u otro. Ya lo hemos visto en Computadores I, pero recordémoslo en una tabla:
Operación | Máscara | Operación lógica |
---|---|---|
Desactivar bits | 0 en los bits que se quiere desactivar, 1 en el resto | AND | Activar bits | 1 en los bits que se quiere activar, 0 en el resto | OR |
Voltear bits | 1 en los bits que se quiere voltear, 0 en el resto | EOR |
El contenido de la tabla anterior se deduce directamente de la tabla de verdad de las operaciones lógicas mencionadas.
Recordemos ahora el programa que hicimos para sacar el contenido de un registro por la pantalla en hexa. Ya le sometimos a un buen arreglo en la sesión anterior. Mejorémoslo aún más con lo que hemos aprendido. En este fragmento nos queríamos quedar en B con los cuatro bits menos significativos de A. Para ello, hacíamos:
; ahora imprimimos la segunda cifra hexadecimal tfr a,b lslb lslb lslb lslb lsrb lsrb lsrb lsrb ; en B estA la segunda cifra, de 0 a 15
Consistía el truco en expulsar a los cuatro bits de arriba mediante los cuatro LSLB y luego, como los otros se habían quedado en la parte alta, volverlos a traer hacia abajo con los cuatro LSRB. Esto es un trabajo que hace mucho mejor las operaciones de bits. Miramos la tabla y vemos que para desactivar los cuatro bits de arriba se ha de usar la operación AND con una máscara que contenga ceros donde queremos desactivar. La máscara es, pues, 000011112. Por consiguiente, todas las líneas de arriba se reducen a un escueto:
; ahora imprimimos la segunda cifra hexadecimal tfr a,b andb #0x0F ; en B estA la segunda cifra, de 0 a 15
Mucho mejor, ¿verdad?
Existen las siguientes instrucciones disponibles: ANDA, ANDB, ANDC, ORA, ORB, ORCC, EORA y EORB. ANDC (notad que solamente lleva una C) hace una operación AND con el registro de flags CC. Lo mismo que ORCC, pero haciendo una operación OR. Las operaciones con los acumuladores admiten un valor o el contenido de una dirección de memoria como máscara. Las operaciones con el registro CC solamente admiten valores inmediatos.
Haced un programa que admita como entrada por teclado un número
del 0 al 50000. Debe imprimir SI
, si el número introducido
es un cuadrado perfecto, o No
, si no lo es.
Para averiguarlo, irá multiplicando 0*0, 1*1, 2*2, 3*3 y así sucesivamente hasta que se pase del número introducido (no es cuadrado perfecto) o atine exactamente en él (sí es cuadrado perfecto).
La opción -C
del simulador da la cuenta en ciclos
que lleva la ejecución de un programa. Anotadla para un número
de entrada grande que queráis.
La manera de hacerlo es muy sencilla. Nos basamos en la operación AND. Se crea una máscara que tenga ceros en todos los bits menos en el lugar del bit que queremos saber su valor. De este modo los habremos puesto todos a cero, salvo el que nos interesa. Si el flag Z es 1, el registro es cero y también lo es el bit que nos interesa. Si el flag Z es 0, el registro es distinto de cero, y, por consiguiente, el bit que nos interesa es 1.
Los números pares tienen su último bit, cuando se escriben en binario, igual a cero. Los números impares lo tienen igual a uno. Con este sencillo código podemos hacer algo en el caso que el registro A sea par:
anda #1 ; mAscara para preguntar por el bit menos significativo bne impar ; si es impar saltamos ;; AquI ponemos el cOdigo que queremos hacer cuando A sea par impar: ...
Le ocurre a AND el mismo problema que le pasaba a SUB cuando lo usábamos para ejecutar saltos condicionales: que nos destruyen el contenido del registro. Recordamos que en aquel caso teníamos la orden CMP que hacía la resta, activaba los flags, pero descartaba el resultado. Su equivalente para AND se llama BIT y hace lo propio: realiza la operación AND, activa los flags correspondientes, pero descarta el resultado para no alterar el contenido del registro.
Para ello, restáis del número que os dan, primero 0, luego, 1, luego, 3, luego, 5, y así hasta que os dé 0 (el número original era un cuadrado perfecto) o el número que quede sea inferior al siguiente impar que hay que restar (el número original no era un cuadrado perfecto)
¿Qué tal quedan los ciclos con esta optimización?
Pista: imaginemos que queremos saber si 100101012 es un cuadrado perfecto. Como el número tiene 8 cifras, sabemos que proviene de uno que tiene (8+1)/2 = 4 cifras como mínimo. Luego no empezamos a probar con el 0*0, 1*1, 2*2, etc. sino con 10002=8. Empezaríamos 8*8, 9*9, y así
Pista:: en el caso del ejemplo anterior, como 100101012 es impar, no comenzamos en 8, sino en nueve y probaríamos: 9*9, 11*11, 13*13, etc.
¿Cómo se han reflejado estas optimizaciones en el número de ciclos que consume vuestro programa?
Con este algoritmo, de paso, aprenderéis a dividir sin necesidad de saberse la tabla de multiplicar. ¿No os lo creéis? Veamos cómo se hace con un ejemplo. Dividamos 1257 entre 26. Se comienza poniendo dos columnas, la primera con el divisor y, en la segunda, un 1:
26 | 1 |
Se va duplicando (sumando consigo mismo) en filas sucesivas cada una de las columnas. Se sigue hasta que el número de la columna del divisor supera o iguala al dividendo (1257):
26 | 1 |
52 | 2 |
104 | 4 |
208 | 8 |
416 | 16 |
832 | 32 |
1664 | 64 |
A continuación se escribe el divisor a la derecha del último valor de la columna del divisor:
26 | 1 | |
52 | 2 | |
104 | 4 | |
208 | 8 | |
416 | 16 | |
832 | 32 | |
1257 | 1664 | 64 |
Ahora el dividendo debe escalar la torre. Cada vez que sea menor que el número en la columna del divisor, se tacha el número de la tercera columna. Es lo que ocurre en el primer caso:
26 | 1 | |
52 | 2 | |
104 | 4 | |
208 | 8 | |
416 | 16 | |
1257 | 832 | 32 |
1257 | 1664 |
Cuando el número en la columna del dividendo es mayor que el número en la columna del divisor, se resta uno de otro. Es lo que sucede ahora (1257-832 = 425):
26 | 1 | |
52 | 2 | |
104 | 4 | |
208 | 8 | |
425 | 416 | 16 |
1257 | 832 | 32 |
1257 | 1664 |
Seguimos con las mismas reglas hasta llegar hasta arriba:
9 | 26 | |
9 | 52 | |
9 | 104 | |
9 | 208 | |
425 | 416 | 16 |
1257 | 832 | 32 |
1257 | 1664 |
El resto es el número que queda arriba a la izquierda. En nuestro ejemplo, el resto es 9.
9 | 26 | |
9 | 52 | |
9 | 104 | |
9 | 208 | |
425 | 416 | 16 |
1257 | 832 | 32 |
1257 | 1664 |
Para sacar el cociente, se suman los números de la tercera columna que no estén tachados:
9 | 26 | ||
9 | 52 | ||
9 | 104 | ||
9 | 208 | ||
425 | 416 | 16 | |
1257 | 832 | 32 | |
1257 | 1664 | + | |
48 |
Por tanto, 1257 dividido por 26 da 48 y de resto 9.
COMx
COM y
ANDx
ORx
EORx
BITx
ANDC y
ORCC y
ls
cd
rm
man
cat
echo $?