PRÁCTICAS DE COMPUTADORES II

TERCERA SESIÓN


  1. Instrucciones de desplazamiento y rotación de bits

    El 6809 dispone de instrucciones para desplazar los bits de un acumulador (A o B) o una posición de memoria. Los desplazamientos y rotaciones pueden ser a la derecha o a la izquierda. Y, dentro de los desplazamientos, hay dos versiones, una lógica y otra aritmética. La razón de esto ya lo hemos visto en Computadores I. Recordemos simplemente en qué consiste cada tipo de operación, junto con los nemónicos que se usan en el ensamblador del 6809:

    Instrucciones de rotación y desplazamiento
  2. Multiplicaciones realizadas como sumas y desplazamientos

    Hay veces que no es conveniente o no se puede utilizar la instrucción MUL para hacer multiplicaciones porque lleva muchos ciclos o, simplemente, porque se quiere multiplicar algo mayor que 8 bits.

    Una solución puede ser usar desplazamientos hacia la izquierda. Cada desplazamiento de un bit a la izquierda multiplica por dos el contenido de un registro. De este modo, si queremos multiplicar por 8 el registro B, podemos hacerlo tan fácil como:

            lslb ; B*2
            lslb ; B*2*2 = B*4
            lslb ; B*2*2*2 = B*8
    
    Es una pena que no podamos multiplicar así de fácil por otros valores, ¿no? Bueno, pues no es del todo cierto. Imaginemos que queremos multiplicar B por 5, esto es, B*5. Pero B*5 = B*(4+1) = B*4 + B. Luego no es tan difícil:
            stb temp  ; guardamos B
            lslb      ; B*2
            lslb      ; B*4
            addb temp ; B*4+B = 5*B
    
  3. Ejemplo de aplicación de las órdenes de desplazamiento

    Vamos a intentar aplicar estas instrucciones en un ejemplo que saca por la pantalla el contenido del registro A, en hexadecimal. Tendremos que aguzar el ingenio, pues apenas hemos visto todavía instrucciones en ensamblador. El programa lo mejoraremos bastante en sucesivas sesiones:

    
    	.area PROG (ABS)
    
            ; definimos una constante
    fin     .equ 0xFF01
    pantalla .equ 0xFF00
    
    	.org 0x100
    	.globl programa
    
    temp:   .word 0 ; una variable temporal
    
    programa:
    	lda #28 ; pongamos este nUmero como prueba
    
            ; imprimamos 0x
            ldb #'0
            stb pantalla
            ldb #'x
            stb pantalla
    
            ; primero imprimamos la primera cifra hexadecimal
            tfr a,b
            lsrb
            lsrb
            lsrb
            lsrb ; en B estA la primera cifra, de 0 a 15
            std temp
            clra
            addb #246
            adca #0 ; en A hay un 1 si la primera cifra es mayor o igual que 10
            ldb #'A-'9-1
    	mul
    	addb temp+1
            addb #'0
            stb pantalla
    
            ; ahora imprimimos la segunda cifra hexadecimal
            ldb temp
            lslb
            lslb
            lslb
            lslb
            lsrb
            lsrb
            lsrb
            lsrb ; en B estA la segunda cifra, de 0 a 15
            std temp
            clra
            addb #246
            adca #0 ; en A hay un 1 si la segunda cifra es mayor o igual que 10
            ldb #'A-'9-1
    	mul
    	addb temp+1
            addb #'0
            stb pantalla
    
            ; imprimamos un salto de lInea al final
            ldb #'\n
            stb pantalla
    
            ; el programa acaba
            clra
    	sta fin
    
    	.org 0xFFFE	; vector de RESET
    	.word programa
    
    
    Hagamos un poco la luz en semejante embrollo. La salida del programa si cargamos A con 28, como en el ejemplo, ha de ser dicho número en hexadecimal: 0x1C. La primera parte es muy sencilla, pues solamente consiste en imprimir un cero y una equis. No obstante, prestad atención a que si queremos imprimir el carácter '0' en la pantalla no cargamos el registro con cero sino con el código ASCII del carácter '0', que es 48. O eso, o que haga el ensamblador el trabajo sucio y usamos, como en el ejemplo, el apóstrofo que aparece.

    A continuación vamos a imprimir la primera cifra hexadecimal. Vimos en Computadores I que cada cifra hexadecimal se corresponde con cuatro bits. Luego habrá en un número de 8 bits dos cifras hexadecimales. La primera de ellas se corresponde con los cuatro bits más significativos. Para quedarnos solo con ellos, usamos cuatro instrucciones de desplazamiento lógico a la derecha (LSR).

    Después de esto, tenemos en B la primera cifra hexadecimal o, quizá hablando con más propiedad, un número de 0 a 15 que representa la primera cifra hexadecimal del número. Todo sería más fácil si no fuera por el pequeño detalle de que tenemos que escribir el código ASCII en la dirección asociada con la pantalla para que aparezca la cifra hexadecimal correspondiente. Observemos la parte de la tabla ASCII que nos interesa, con sus valores:

    ASCII de los dígitos hexadecimales

    Queda claro, pues, que debemos imprimir 48 si B vale 0, 49 si vale uno, ..., 57 si B vale 9 y ... 65 si B vale 10, 66 si B vale 11, ... 70 si B vale 15. Como aún no sabemos cómo poner condiciones, seguimos la siguiente estrategia:

    1. Añadiremos siempre 48 al registro B, pero antes, si B vale 10 o más, añadiremos 7 para dar cuenta del salto que se produce en la tabla ASCII
    2. Eso es lo que precisamente hacen estas líneas del código:
              std temp
              clra
              addb #246
              adca #0 ; en A hay un 1 si la primera cifra es mayor o igual que 10
              ldb #'A-'9-1
              mul
              addb temp+1
              addb #'0
      
    3. Añadimos 246 a B. Si B vale 10 o más se activa el bit de acarreo
    4. Añadimos a A, que previamente lo hemos puesto a cero, el bit de acarreo. A tiene, pues, 1 si B valía 10 ó más o cero, en caso contrario
    5. Multiplicamos A por 7 ('A-'9-1 = 65-57-1 = 7. El resultado se ha quedado en D, cuya parte baja es B. Por consiguiente, en este punto B tiene 7 si B valía 10 o más y cero en caso contrario
    6. Añadimos a B su antiguo valor y, luego, 48 y conseguimos nuestro objetivo

    La segunda parte del código es igual que la primera. Solamente que usamos la parte baja de A en lugar de la parte alta. Para dejar a cero la parte alta, primero desplazamos cuatro bits a la izquierda y, luego, desplazamos otros cuatro, de modo lógico, a la derecha.

    Como habréis podido observar, un programa en ensamblador puede ser casi imposible de descifrar, incluso en los casos más sencillos. Es por eso fundamental que documentéis muy bien vuestros programas. Según vayamos avanzando en las sesiones, volveremos a este código varias veces para ver cómo se va simplificando.

    Más adelante en la carrera estudiaréis un tipo de procesadores más moderno (los RISCs) en los que, en ocasiones, se aplican técnicas parecidas a la que hemos estudiado para evitar las realización de condiciones, en las que este tipo de procesadores pierden mucha eficiencia.

  4. Ejercicio

    Añadid código a los ejercicios de suma y resta de la sesión anterior para que se imprima el resultado de la operación por la pantalla en hexadecimal, de modo que no sea necesario invocar el depurador para conocer el resultado
  5. Pregunta

    ¿Por qué no es posible, en el fragmento de código anterior:
            clra
            addb #246
            adca #0 ; en A hay un 1 si la primera cifra es mayor o igual que 10
    
    sumar primero 246 a B y, luego, limpiar A y añadir el bit de acarreo?
  6. Aritmética BCD

    Si alguno de vostros es lo suficientemente valiente, con los conocimientos que ahora tenéis, como para intentar sacar por la pantalla el contenido de un registro en decimal, se chocará contra una pared. Esa es la razón por la cual se inventa el BCD, que ya vimos en Computadores I. BCD significa, como ya sabemos, decimal codificado en binario. La idea es muy simple, en cada byte vamos a almacenar dos dígitos decimales, uno en los cuatro primeros bits y el segundo, en los cuatro últimos. Así, el número 27, que en binario normal es 110112, pasa a ser en BCD, 1001112, o, escrito como un byte, 001001112. Si separamos parte alta y parte baja, notamos que hay un 2 en binario en la parte alta y un 7 en la parte baja, cosa que aparece clarísima si lo expresamos en hexadecimal: 0x27.

    Uno de los problemas de trabajar en BCD es que, como vimos en la teoría de Computadores I, la suma/resta normal no nos funciona bien. En este punto acude en nuestra ayuda la instrucción del 6809 DAA (Decimal Adjustment for A). Se usa del siguiente modo:

            ; Vamos a sumar 6 y 7 en BCD
            lda #0x06
            adda #0x07  ; En A hay ahora 0xD (es decir, 13)
            daa         ; DespuEs del ajuste decimal, en A hay 0x13, 13 en BCD
    
    El 6809, como todos los procesadores, es un autómata que, una vez ejecutadas las instrucciones no recuerda nada más que lo que ha quedado en los registros o la memoria. ¿Cómo sabe el 6809 qué es lo que tiene que hacer con A para ajustarlo a BCD? El truco reside en que usa internamente el flag H cada vez que hace una suma. Desafortunadamente, el 6809 no está preparado para hacer ajustes decimales en las restas.

    Desafortunadamente, en muchas versiones del simulador m6809-run usado en estas prácticas hay un error que hace que la instrucción DAA no funcione correctamente. En caso de ser necesaria para el desarrollo de algún programa, se puede usar el código que aparece aquí.

  7. Ejercicio

    Haced un programa en ensamblador del 6809 de modo que:
    1. El usuario teclee dos cifras
    2. El programa imprima un espacio
    3. El usuario teclee otras dos cifras
    4. La salida del programa sea como la que se muestra para el siguiente ejemplo:
      37 17
      37+17=54
      
    Usad para ello aritmética BCD. No se debe hacer ningún control acerca de los valores introducidos.
  8. Ejercicio

    Haced un programa en que el usuario introduzca cuatro cifras decimales y, a la salida del programa, quede en el registro X el número introducido en formato BCD y en el registro D el número introducido tal cual
  9. Ejercicio

    Haced lo mismo que el ejercicio anterior, pero restando. Debido a que el 6809 no está preparado para hacer ajuste decimal en las restas, tenéis que proceder de igual modo a como se hace a veces en binario: plantear la resta como la suma del minuendo más el opuesto del sustraendo. En binario, el opuesto se calcula mediante el complemento a 2. En decimal, el procedimiento es similar:
    1. Cambiar las cifras del número según la siguiente tabla (complemento a 9):
       Cifra original:   01 23 4 56 78
       Cambiar por:  98 76 5 43 21
    2. Sumar 1 al resultado

    Veamos un ejemplo: pretendemos restar, en dos cifras y en decimal, 27-13. Hacemos el opuesto del 13. Primer paso, sustituir las cifras según la tabla anterior: 86. Ahora, sumar 1: 87. 87 es el equivalente a -13, pues. Sumamos ya directamente: 27+87=114. Despreciamos el primer 1, pues estamos trabajando con dos cifras y nos queda el resultado correcto: 27-13=14.

  10. Ejercicio

    Repetid el ejercicio anterior pero realizando una multiplicación aprovechando la instrucción MUL. Ahora no debéis usar aritmética BCD pues la instrucción DAA no funciona con MUL. Tampoco saquéis el resultado por la pantalla, pues aún no sabemos cómo hacerlo, sino miradlo con el depurador.
  11. Órdenes de ensamblador vistas.

    ROLx
    rota los bits de x una posición a la izquierda, apoyándose en C. x puede ser A o B
    Flags afectados: NZVC
    RORx
    rota los bits de x una posición a la derecha, apoyándose en C. x puede ser A o B
    Flags afectados: NZC
    LSLx y ASLx
    desplaza los bits de x una posición a la izquierda, el bit saliente se guarda en C. x puede ser A o B
    Flags afectados: NZVC
    LSRx
    desplaza los bits de x una posición a la derecha, el bit saliente se guarda en C. x puede ser A o B
    Flags afectados: ZC, N=0
    ASRx
    desplaza los bits de x una posición a la derecha, el bit saliente se guarda en C y el bit de signo se preserva (desplazamiento aritmético). x puede ser A o B
    Flags afectados: NZC
    ROL, ROR, LSL, ASL, LSR y ASR
    mismas operaciones pero sobre una dirección de memoria
    Flags afectados: igual que en los casos de arriba
    DAA
    ajuste decimal de justo la anterior operación de suma sobre A
    Flags afectados: NZC, V=0


  12. Ó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


  13. LPEs.


© 2010 Guillermo González Talaván.