PRÁCTICAS DE COMPUTADORES II

QUINTA SESIÓN


  1. Operaciones con bits

    Ya hemos visto en este curso unas operaciones fundamentales que se realizan con los bits: las rotaciones y los desplazamientos. Quedan por ver aquellas operaciones clásicas que, adecuadamente empleadas, permiten establecer el valor de un bit a cero o a uno o voltear su valor dejando inalterados al resto de sus compañeros. Son, cómo no, las operaciones AND, OR, NOT (que, en ensamblador del 6809, se llama COM, por COMplement) y XOR (que se denomina EOR, por Exclusive OR).

    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.

  2. Ejercicio

    Se denominan cuadrados perfectos a aquellos números que resultan de multiplicar por sí mismo un número entero. Los primeros cuadrados perfectos son: 0, 1, 4, 9, 16, 25, ...

    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.

  3. Saber el valor de un bit determinado

    A veces interesa conocer el valor de un determinado bit de un registro, sin importarnos el valor de los demás. Una vez conocido, podemos efectuar un salto condicional.

    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.

  4. Ejercicio

    Optimizar el programa del ejercicio anterior basándonos en esta sencilla, a la vez que sorprendente, propiedad de los cuadrados perfectos: "Todo cuadrado perfecto, escrito en binario, tiene su penúltima cifra, comenzando por la izquierda, igual a cero":
    0 = 002
    1 = 012
    4 = 1002
    9 = 10012
    16 = 100002
    25 = 110012
    ...
  5. Ejercicio

    Basándose en el ejercicio anterior, realizar otra versión considerando la siguiente propiedad de los cuadrados perfectos: "Los cuadrados perfectos son el resultado de sumar los primeros números impares"
    0 = 0
    1 = 0+1
    4 = 0+1+3
    9 = 0+1+3+5
    16 = 0+1+3+5+7
    25 = 0+1+3+5+7+9
    ...

    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?

  6. Ejercicio

    Optimizar el antepenúltimo ejercicio basándose en la siguiente propiedad: "Si un cuadrado perfecto, escrito en binario, tiene n cifras, el número del cual es cuadrado tiene como mínimo (n+1)/2 cifras".

    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í

  7. Ejercicio

    Optimizar el código anterior basándose en la siguiente propiedad: "Un cuadrado perfecto impar proviene de multiplicar un número impar por sí mismo. Un cuadrado perfecto par proviene de multiplicar un número par por sí mismo".

    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?

  8. Divide y vencerás

    Llega el momento de hacer un programa que sepa dividir. Para ello usaremos el algoritmo tradicional, aunque modificado un poco.

    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
    522
    1044
    2088
    41616
    83232
    166464

    A continuación se escribe el divisor a la derecha del último valor de la columna del divisor:

    26      1
    522
    1044
    2088
    41616
    83232
    1257   166464

    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
    522
    1044
    2088
    41616
    125783232
    1257   166464

    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
    522
    1044
    2088
    42541616
    125783232
    1257   166464

    Seguimos con las mismas reglas hasta llegar hasta arriba:

    9 26      1
    9522
    91044
    92088
    42541616
    125783232
    1257   166464

    El resto es el número que queda arriba a la izquierda. En nuestro ejemplo, el resto es 9.

    9 26      1
    9522
    91044
    92088
    42541616
    125783232
    1257   166464

    Para sacar el cociente, se suman los números de la tercera columna que no estén tachados:

    9 26      1
    9522
    91044
    92088
    42541616
    125783232
    1257   166464 +
    48

    Por tanto, 1257 dividido por 26 da 48 y de resto 9.

  9. Ejercicio

    Haced un programa que calcule el cociente y el resto de dividir el número almacenado en X entre el número almacenado en B, ambos considerados sin signo, usando el algoritmo del apartado anterior. Tratad de que el programa consuma el menor número de ciclos posible.
  10. Órdenes de ensamblador vistas.

    COMx
    Complementa a 1 x. x pueden ser A o B
    Flags afectados: NZ, V=0, C=1
    COM y
    Complementa a 1 el contenido de la dirección de memoria y o apuntada por la etiqueta y.
    Flags afectados: NZ, V=0, C=1
    ANDx
    hace la operación AND entre x y el valor o el contenido de la dirección de memoria especificados. El resultado se almacena en x. x pueden ser A o B
    Flags afectados: NZ, V=0
    ORx
    hace la operación OR entre x y el valor o el contenido de la dirección de memoria especificados. El resultado se almacena en x. x pueden ser A o B
    Flags afectados: NZ, V=0
    EORx
    hace la operación XOR entre x y el valor o el contenido de la dirección de memoria especificados. El resultado se almacena en x. x pueden ser A o B
    Flags afectados: NZ, V=0
    BITx
    hace la operación AND entre x y el valor o el contenido de la dirección de memoria especificados. El resultado se descarta. x pueden ser A o B
    Flags afectados: NZ, V=0
    ANDC y
    hace la operación AND entre el registro de flags CC y el valor especificado. El resultado se se almacena en CC.
    Flags afectados: todos, por la propia operación
    ORCC y
    hace la operación OR entre el registro de flags CC y el valor especificado. El resultado se se almacena en CC.
    Flags afectados: todos, por la propia operación


  11. Órdenes de la shell relacionadas.

    ls
    lista el contenido de un directorio
    cd
    cambia el directorio de trabajo
    rm
    borra un fichero
    man
    muestra la página de manual de una orden
    cat
    muestra el contenido de un fichero
    echo $?
    muestra el código devuelto por el último programa ejecutado


  12. LPEs.


© 2010 Guillermo González Talaván.