viernes, 9 de septiembre de 2016

¿Par o impar? ¿Cero o no cero? ¿Multiplo de algo? ¿Bit o no bit? (parte 4 y final)

En las entregas previas vimos la dinámica del CCR y cómo nos arreglamos para ver si un número es par, cero, etc. Nos resta aprovechar lo aprendido y aplicarlo a la determinación de múltiplos. Veamos en esta última parte de la serie cómo se logra esto.
Recordemos en primer lugar el concepto de múltiplo: un número es múltiplo de otro cuando puede expresarse como un producto de dos enteros siendo el segundo uno de los factores. Por ejemplo, el número 12 es múltiplo de 2 porque es el producto de 2 * 6. No debemos confundir múltiplos con potencias enteras. Las potencias enteras de un número obviamente también serán múltiplos del mismo, pero no son los únicos múltiplos. Pongamos por caso el 8, que es potencia entera de 2 y por tanto también múltiplo de 2. 
Nota: aunque el concepto de múltiplos se puede aplicar perfectamente a números fraccionarios, vamos a restringir las explicaciones al campo de los enteros.
¿Cómo se determina si un número (A) es múltiplo de otro (B)? El algoritmo matemático es sencillo: dividimos el número A por B y observamos el resto de la división... si es cero, significa que A es múltiplo de B. El asunto es que como hemos visto en otra entrada de esta serie, la división con el HC11 no es precisamente lo más sencillo de realizar. ¿Hay forma de verificar si un número es múltiplo de otro sin requerir de la división? A veces...

¿Cómo verificamos si un número es múltiplo de 2?
¡Esto equivale a determinar si el número es par! Hemos visto que basta con hacer un desplazamiento (shift) a derecha y observar si el LSB -copiado a carry- es cero o uno. Si es cero, entonces el número es par.

¿Cómo podríamos verificar si es múltiplo de 4?
Un número es múltiplo de 4 si sus dos dígitos menos significativos (los de menor peso) son cero. Por tanto bastaría con desplazar a derecha una vez, verificar que carry sea cero, volver a desplazar y verificar nuevamente si es cero. Algo así:

      ORG   0000
A     db    8      veremos si este numero es multiplo de 4
esMul RMB   1      y si lo es, lo indicaremos con un 1 en este

      ORG   $C000
      CLR   esMul  al indicador de multiplo lo ponemos en cero
      LDAA  A      cargamos el numero en el acumulador A...
      LSRA         ...y hacemos el primer desplazamiento a derecha
      BCS   fin    entonces LSB->Carry, si Cy=1, no es multiplo de 4
      LSRA         sino, volvemos a desplazar
      BCS   fin    y verificamos lo mismo. Si Cy=1, no es multiplo
      INC   esMul  sino, es multiplo de cuatro
fin   BRA   fin


Si quisiéramos determinar si es múltiplo de 8, es tan sencillo como seguir haciendo desplazamientos bajo la misma premisa del caso anterior (todos los bits menos significativos debieran ser cero). Este método es válido para determinar si un número es múltiplo de cualquier potencia entera de 2. 

¿Cómo podemos verificar si un número es múltiplo de cualquier otro?
Usando el método clásico: dividiendo. La instrucción IDIV realiza una división entera de 16 bits entre los operandos que ubiquemos en D (dividendo) y IX (divisor). El resultado queda en IX y el resto en D. Por tanto debemos verificar si D es cero y de esa forma determinamos si es múltiplo o no. Pongamos un par de números de ejemplo para facilitar el "copy-paste" al THRSim...

       ORG    $0000
num    FDB    23456  * quiero saber si este
mul    FDB    2      * es multiplo de este
esMul  RMB    1      * lo voy a indicar aqui 0=no 1=si

       ORG    $C000
       CLR    esMul
       LDD    num    * cargo el numero en num
       LDX    mul    * y el divisor en mul
       IDIV          * El resultado va a IX y el resto a D
       CPD    #0   
       BNE    fin
       INC    esMul
FIN    BRA    fin


Algunos comentarios adicionales:

Puede que esté pensando... ¿y no conviene directamente usar el algoritmo general en todos los casos? La respuesta es no por al menos dos razones: primero requerimos el uso de D (o sea de los dos acumuladores) y del índice IX, los que puede que tengan información que no podamos perder, por lo que habría que guardar su contenido y recuperarlo, por ejemplo si recorriéramos un vector. La segunda tiene que ver con la performance. Sume la cantidad de ciclos de cada rutina y me cuenta...

Detalle para quien copie el segundo fuente en el simulador: tenga presente que las directivas FDB cargan un valor en memoria solo cuando se ensambla. Por tanto si realizará sucesivas pruebas tendría que modificar directamente los valores de divisor y dividendo en memoria, o bien re-ensamblar con cada modificación del fuente.

Con esto cerramos la serie preguntona. ¿Alguna pregunta adicional? Me anticipo a una que debe girar en su mente en este momento...
 

2 comentarios:

  1. Muy buen contenido. Recomiendo la lectura para una buena complementación a las clases y a la práctica de cada uno, ya que tiene tips para hacer más simple y óptimo el código que uno desee escribir.

    ResponderBorrar
    Respuestas
    1. Gracias Florencia! Pasá la voz a la gente que (re)cursa y aun no le sacó el jugo.

      Borrar

Aunque no es obligatorio, es de buena educación firmar los comentarios.