| AMIGA.InFo Nº2 - Marzo/Abril 1995 - ENSAMBLADOR |
|
| NOTA: Pulsando sobre las fotos con borde azul ampliarás la imagen. |
|
¡Por fin! Tras la introducción teórica del lenguaje ensamblador en el artículo anterior, ahora vamos a entrar en materia con ejemplos prácticos...
Instalaremos un ensamblador, lo configuraremos y por último, ensamblaremos nuestro primer ejemplo. No tienes excusa para no probarlo y observar cómo tu Amiga responde al lenguaje de programación más rápido donde los haya... |
| |
FMartin |
|
- INSTALACIÓN DEL ENSAMBLADOR A68K Y VARIAS UTILIDADES
- USO DEL A68K Y BLINK PARA ENSAMBLAR UN PROGRAMA
- LAS PRIMERAS INSTRUCCIONES DEL MOTOROLA 680X0 |
Instalemos el ensamblador en nuestro Amiga
Para poder seguir todos los ejemplos que aparecerán en esta serie de artículos sobre ensamblador, será necesaria la instalación de un programa ensamblador. En este caso, utilizaremos el A68K, un ensamblador muy sencillo que trabaja con todos los Amiga sin importar la versión del Sistema Operativo (Funciona en WB 1.x, 2.x y 3.x).
Para poder crear el ejecutable de nuestro programa, también necesitaremos otra utilidad, llamada BLink. Se trata de un "montador" o "linker", encargado de unir todas las partes necesarias (librerías, rutinas externas, startup, etc) para que el Amiga pueda ejecutar nuestro programa.
En el disco número 3 del AInFo no.1 (Enero-Febrero) están los dos programas comprimidos, junto a otros, en un directorio llamado "df0:programas" bajo el nombre "Ensamblador.lha".
¿Dónde debemos instalar estos ficheros?
Depende de donde queramos trabajar: si es en disco duro, se puede instalar en una partición y directorio al que llamaremos por ejemplo "dh0:asm68". Allí colocaremos todos los ficheros que se extraigan de la descompresión de "Ensamblador.lha". ("Dh0:"es un ejemplo de nombre de partición de un disco duro. Tú puede que tengas otros nombres (Work:, dh1:, etc), así que tienes que sustituir donde aparezca la palabra "dh0" por el nombre de tu partición).
Si es en disquete, podemos formatear uno y darle el nombre "asm68". En este disco, descomprimiremos todos los ficheros incluidos en "Ensamblador.lha".
¿Cómo podemos descomprimir el fichero "Ensamblador.lha"?
En caso de no tener disco duro, es muy recomendable que te crees tu propio disco de trabajo, de esta forma, cuando quieras programar en assembler, sólo tendrás que encender tu Amiga e introducir el disco. En la sección "Primeros Pasos" de este número de AInFo puedes encontrar todos los pasos necesarios para crear tu propio disco de trabajo. Una vez lo consigas, puedes instalar el fichero "Ensamblador.lha" siguiendo los pasos indicados para la instalación en disquete.
Para ayudaros, he congelado una a una las pantallas que aparecían en mi Amiga durante todo el proceso de instalación. En la siguiente página las podéis ver, junto con un número que indica el paso dónde nos encontramos. Las nueve primeras son para los que tengáis disco duro, las nueve siguientes son para los que quieran instalar en un disquete de trabajo propio.
El método más directo y fácil es descomprimir el fichero "Ensamblador.lha" arrancando desde el propio disco "AmigaInFo3". Así que, introduce el disco del anterior número de AInFo (número de disco 3) en la disquetera y haz un reset (CTRL+AMIGA+AMIGA). Una vez aparezca en la pantalla el WorkBench, abrimos el icono del disco y ejecutamos el programa "Instalación_AmigaInFo" (foto 1). Ahora hay que diferenciar los dos tipos de instalación, en disco duro o disquete.
Instalación en disco duro.
Cuando aparece en pantalla el "instalador", pulsamos <seguir> ("proceed" en inglés) en todas las ventanas que vayan saliendo (fotos 2 y 3), hasta llegar al punto donde se pregunta dónde quieres descomprimir los ficheros (foto 4). Marca la opción correspondiente a disco duro y pulsa <proceed>.
A continuación, selecciona el fichero a descomprimir. En este caso, nos interesa sólo el que se llama "Ensamblador.lha" (foto 5). Lo seleccionamos y pulsamos <proceed>.
Ahora viene el paso más importante: hay que seleccionar el destino de los ficheros que se descomprimirán. Para ello, aparecen varios nombres de directorios y particiones. Pulsa la opción <Show Drives>. Ahora te aparecen todos los nombres de tus particiones. Elige una de ellas y márcala. En este punto, crearás un nuevo directorio en una de las particiones, llamado "asm68". Pulsa la opción "Make New Drawer". Aparecerá una ventana (foto 6) donde se solicita el nombre del directorio a crear. Teclea el nombre "asm68" después de los ":" del nombre de la partición que hayas elegido y después marca <proceed>. Ahora ya has seleccionado el destino y tu lugar de trabajo donde residirán las utilidades de ensamblador. Marca de nuevo <proceed> (fotos 7 y 8). El fichero se descomprimirá en este nuevo directorio. Finalmente, pulsa <No> (foto 9).
Instalación en disquete.
Los tres primeros pasos son los mismos (fotos 1, 2 y 3) que en la instalación en disco duro. Una vez aparezca la ventana donde selecciona el tipo de disco (foto a), marca la opción de "disquete". Te aparecerá un mensaje indicándote que necesitarás un disco formateado previamente (foto b). En nuestro caso, ya te sirve el disco de trabajo que hemos creado siguiendo la sección "Primeros pasos" de AInFo. Pulsa <Proceed>.
Ahora tienes que seleccionar el fichero a descompactar. Nos interesa el llamado "Ensamblador.lha" (foto c). Márcalo y pulsa <Proceed>.
En la ventana donde aparece la pregunta ¿En qué directorio debe ser instalado Ensamblador.lha? (foto d) hay que hacer un par de cambios. Primero sacas de la unidad el disco de AmigaInFo 3 y colocas el tuyo. Recuerda que ya tiene que estar preparado, ver sección "Primeros Pasos", y que le has puesto el nombre "asm68". Una vez introducido en la unidad, pulsamos la opción <Ver unidades> ("Show Drives" en inglés). En ese momento aparecerá en la ventana el nombre "DF0:asm68" (foto e). Lo marcas y te leerá el directorio de tu disco. Seleccionas el directorio "c" y pulsas <Proceed> a ésta y la siguiente ventana (fotos g y h).
A continuación, sólo te queda cambiar los discos a medida que el Amiga te los vaya solicitando.
Después de varios cambios, todos los ficheros los tendrás en tu disco de trabajo en el directorio de comandos "c".
GUÍA PARA INSTALAR LAS UTILIDADES DE ENSAMBLADOR EN DISCO DURO
|
GUÍA PARA INSTALAR LAS UTILIDADES DE ENSAMBLADOR EN DISQUETE
|
¿Cómo vamos a crear ejecutables a partir de nuestros programas fuente?
El método empleado para que los programas en ensamblador sean ejecutables es muy parecido al utilizado en cualquier otro lenguaje que tenga que ser compilado, como por ejemplo el C.
Este proceso se divide en tres fases:
a- Editamos nuestro programa fuente con un editor de textos. Podemos emplear cualquiera, incluso los que acompañan al sistema operativo del Amiga, llamados "Ed" o "MEmacs". Estos editores son muy simples y siven perfectamente para nuestros propósitos, aparte de que todo el mundo puede disponer de ellos por estar en el disco del Workbench. Aún así, si tienes algún otro en el que trabajes más cómodamente puedes utilizarlo sin ningún problema. Una vez hayamos tecleado nuestro programa, salvamos el texto en un fichero cuyo nombre "xxx" tiene que tener la extensión ".s". A partir de ahora, la extensión ".s" nos servirá para identificar los ficheros que son programas fuente de ensamblador.
b- Ejecutaremos el script "asm xxx" donde "xxx" es el nombre del fuente que queremos ensamblar sin la extensión ".s".
En este paso nuestro programa será traducido a código máquina por el ensamblador y el montador.
Pueden aparecer errores en nuestro fuente y si es así, el proceso se parará y nos indicará qué errores son y la línea donde aparecen.
c- Si ha habido errores, volveremos al paso 1 para editar el fuente e intentar corregirlos.
Si no ha habido errores, el proceso de ensamblaje habrá finalizado, y se habrá creado un fichero llamado "xxx" sin la extensión ".s". Este fichero es directamente ejecutable desde el "shell" con tan sólo teclear su nombre.
Es muy normal pasar del paso 'c' al 'a' varias veces, ya que es muy frecuente equivocarse o cometer errores al teclear o al programar el fichero fuente. Este proceso se denomina "depurar de errores" un programa fuente ("debug" en inglés).
Creación del entorno de trabajo.
FIGURA 1. Pasos para ensamblar el ejemplo incluido en el disco de este número de AInFo. Para seguirlos, tendrás que abrir un shell y situarte en el directorio o disco de trabajo "asm68". |
Después de haber instalado los programas "asm", "a68k" y "blink" en nuestro lugar de trabajo (ya sea disco duro o disquete) tenemos que copiar los ejemplos de lenguaje ensamblador que aparezcan en este artículo. Para ello, utilizaremos el comando "copy" desde un "shell" y el disco de portada número 4 de este número de AInFo.
Por ejemplo, si vamos a copiarlo en el directorio "asm68" de una partición de disco duro llamada "DH0" podemos teclear el siguiente comando:
copy ALL AmigaInFo4:listados/asm/#? to DH0:asm68/
Si queremos copiarlos en un disquete de trabajo llamado "asm68" podemos hacerlo con:
copy ALL AmigaInFo4:listados/asm/#? to asm68:
Estos comandos crearán varios directorios nuevos en nuestro lugar de trabajo, necesarios para poder ensamblar los ejemplos. Estos directorios se llaman "macros" y "libreria_ASM" y contienen algunos ficheros con datos que necesitamos para poder programar en ensamblador.
ATENCIÓN: Es muy importante que estos directorios se creen correctamente y no los borres o renombres, puesto que contienen datos sin los cuales los ejemplos que aparecen aquí no funcionarían. Cada mes añadiremos nuevos ficheros a estos directorios para poder ensamblar los ejemplos.
Para que os aseguréis de que todo está correctamente copiado, en el directorio "asm68" (disco duro) o en vuestro disco de trabajo, aparecerá el siguiente directorio nuevo (que podéis ver con el comando "dir ALL asm68:") con algunos ficheros también nuevos:
c (dir)
a68k
A68k.doc
asm
blink
Blink.doc
calcu
macros (dir)
AbrirDOS.s
Decimal_ASCII.s
macros.i
hola
hola.s
El último paso es asignar "asm68" al directorio donde trabajamos normalmente y añadiremos un comando "path" para que el Amiga busque los comandos del ensamblador automáticamente en "asm68":
assign asm68: tu_partición:asm68/
path asm68: c add
Donde "tu_partición" es el nombre de tu partición de disco duro. (Ej. DH0:, WORK:)
Si es en un disquete de trabajo llamado "asm68":
path asm68:c add
Para no tener que teclearlo cada vez que conectemos el Amiga, sería muy conveniente que escribieras las líneas anteriores en el fichero "S:user-startup". (En la sección Primeros Pasos tienes más información de cómo editar este fichero y configurarlo).
Ensamblemos un pequeño ejemplo para probar que todo esté Ok.
Para verificar que toda la instalación ha sido correcta, vamos a ensamblar y ejecutar un pequeño programa de ejemplo: "hola.s". No nos importa como está hecho o qué utiliza, simplemente queremos ensamblarlo para familiarizarnos con el proceso y comprobar que todo funciona bien.
Veamos qué pasos hay que seguir para ensamblar correctamente el ejemplo "hola.s". Estos pasos también los puedes ver en la figura 1, donde aparecen todos tal y como yo los ejecuté en mi "shell":
a- Hay que situarse en el directorio de trabajo, donde esté situado el fichero "hola.s". Esto lo podemos hacer con el comando:
cd asm68:
b- Teclear "asm hola". Se cargará el ensamblador (a68k) y después el montador (blink) y se iniciará el proceso de ensamblaje, mostrando varios mensajes por pantalla sobre el estado del fuente y la fase donde se encuentra.
c- Si todo ha funcionado bien, tiene que aparecer el mensaje "O.K !!! Ejecutable salvado en el directorio de trabajo...". Esto significará que hemos tenido éxito en la instalación completa del ensamblador y que podemos ejecutar el ejemplo con tan sólo escribir "hola" en la línea de comandos del shell.
d- Al ejecutar "hola", nos aparecen en la ventana varios mensajes y se esperará a que se pulse el botón izquierdo del ratón. Después de pulsarlo, se vuelve al Shell y finaliza el programa.
e- ¡Enhorabuena! Si has llegado hasta aquí es que has tenido éxito en la instalación del ensamblador. Ahora ya sabes cómo ensamblar todos los ejemplos que aparecerán en Amiga.InFo e incluso tus propias rutinas.
Si por el contrario, has tenido problemas en algún paso, no dudes en escribirme y explicármelos. De esta forma intentaré solucionarlos... ¡Ánimo!
Comencemos a pensar en Assembler
Seguro que muchos de vosotros ya estáis impacientes por comenzar a programar vuestras primeras instrucciones en Assembler. Pero antes hay que saber algunos conceptos básicos, algunos de los cuales ya aparecieron en el artículo del número 1 de AInFo y que ahora volveremos a explicar con más detalle, puesto que los utilizaremos constantemente y serán necesarios para programar.
Todos sabemos que para ejecutar un programa, primero hay que cargarlo en memoria. Nosotros también utilizaremos la memoria para almacenar nuestros programas ejecutables y sus datos. Evidentemente, necesitamos algún método para poder referenciar o acceder a esos programas y datos durante la programación de un fuente. (Recuerda que un fuente es para nosotros nuestro programa escrito en assembler).
En realidad, existen distintos modos para acceder a memoria, aunque ahora sólo explicaré los dos más sencillos y los restantes irán apareciendo a medida que los necesitemos.
A la memoria accedemos mediante direcciones, es decir, leemos o escribimos en una determinada posición de la memoria utilizando, para identificarla, el número que le corresponda como dirección. La posición puede contener un dato de tamaño "byte" (8 bits). "word" (16 bits) o "long word" (32 bits).
|
FIGURA 2
Ocho Registros
de
Datos
|
|
Siete Registros
de
Dirección |
|
Dos Punteros
de Pila |
|
Contador de Programas |
|
|
|
|
| Byte Sistema |
Byte Usuario |
|
SR |
|
Registro de Estado |
|
|
Por ejemplo, una dirección de memoria podría ser la siguiente:
Dirección Contenido
$3456F4 $05060708
Para que todo sea más fácil, nos podemos imaginar la memoria como un conjunto de buzones de correo. Cada buzón tiene su propia dirección y en su interior se almacenan las cartas. Así pues, cada casilla de memoria tiene su dirección, y en su interior se guardan los datos. Para enviar o recibir las cartas necesitamos que figure nuestra dirección particular en el buzón.
Para escribir o leer en una posición de memoria, también necesitamos una dirección numérica que la identifique individualmente.
Como podéis observar, es muy común representar las direcciones y datos en el sistema numérico hexadecimal (base 16). Cada dígito hexadecimal representa 4 bits, por lo tanto, como en el ejemplo de dirección $3456F4 hay 6 dígitos, obtenemos un número binario de 24 bits. Las direcciones tienen casi siempre esta longitud, aunque, en realidad, depende del tipo de microprocesador que posea tu Amiga. En la sección Amiga Interno de este mes, puedes ver los diferentes modelos y sus características principales. Para ser totalmente compatibles con cualquier modelo de Amiga, nosotros emplearemos siempre direcciones de 24 bits de longitud.
Podéis utilizar la calculadora que tenéis en el directorio "c" de vuestro disco o directorio de trabajo y así podréis realizar cambios de base sin ningún problema.
En cuanto al contenido de esa dirección, es siempre un número de 32 bits de longitud. Sin embargo, es posible seleccionar el tamaño y coger sólo un "byte (8 bits)" o un "word (16 bits)" de esos 32 bits, según nos convenga.
Es importante hacer hincapié en la diferencia que existe entre la dirección de una posición concreta en memoria y su contenido. La primera es simplemente un número asignado a una zona determinada de memoria, mientras que la segunda es el dato que realmente posee en su interior esa zona.
Existe otra forma de almacenar datos, pero esta vez no en memoria, sino en un lugar especial que el microprocesador del Amiga posee. Esta zona se denomina "banco de registros internos".
Todo microprocesador posee internamente unas memorias rápidas donde es posible almacenar datos. Estas memorias se denominan registros internos y el número de ellos depende del fabricante. En nuestro caso, el 68000 está provisto de 18 registros de 32 bits y uno de 16 bits. En la figura 2, se muestran divididos en 8 registros de datos, siete de direcciones, dos punteros de pila, un contador de programa y el registro de estado.
Los registros de uso habitual (los que almacenan datos y direcciones) son todos de 32 bits, proporcionando al programador facilidad y velocidad a la hora de operar con ellos. A continuación, comentaremos cada uno de ellos, pero no sin antes avisar de que cuando hablamos de bits de mayor o menos peso, nos estamos refiriendo en todo momento a la relación que existe entre la posición ocupada por un bit dentro de un número binario y su valor específico. Por ejemplo, en el número binario de 16 bits:
1101 0101 1001 0010 + -
se tiene que de derecha a izquierda, cada bit tiene una unidad más de peso que el bit anterior. Así, el último bit que está en la posición quince, es decir, el "1" marcado con un "+" es el de más peso o también a veces denominado el más significativo. Por lo contrario, el "0" marcado con un "-" y situado en la posición cero, es el de menor peso.
Registros de datos D0..D7
Como su nombre indica, estos registros son de propósito general, es decir, se utilizan para almacenar cualquier tipo de dato que ha de ser procesado continuamente. Los ocho se pueden utilizar indistintamente, puesto que no existen funciones especiales asociadas a alguno de ellos.
El tamaño del dato almacenado puede ser de 32, 16 ó 8 bits, correspondiendo a los 8 bits de menor peso del registro cuando se trata de un "byte" o a los 16 bits de menor peso si se trata de un "word" y al total de 32 bits cuando es un "long".
Registros de direcciones A0..A7
Estos registros tienen la principal función de almacenar direcciones de 32 bits de longitud. Combinando estos registros con los modos de direccionamiento del 68000, podemos acceder de una forma sencilla, a cualquier dato que se encuentra en la memoria.
Son registros que pueden ser utilizados por cualquier instrucción, ya sea conteniendo la dirección base del operando fuente o la del destino.
Punteros de pila
El registro de direcciones A7 es utilizado como puntero de la pila del usuario o del sistema. Para ello denomina al puntero de usuario como USP (User Stack Pointer) y al del sistema como SSP (System Stack Pointer). Evidentemente los dos punteros no pueden estar simultáneamente almacenados en un solo registro de 32 bits, sino que siempre contiene uno de ellos (en función del modo de ejecución en que se halle el 68000). Cuando por ejemplo está en modo usuario, el A7 contiene la dirección de la pila de usuario, pero por el contrario, si está en modo sistema, contiene la dirección de la pila del sistema. El propósito de una pila es reservar una zona de memoria que contenga datos importantes para los programas en tiempo de ejecución, como puedan ser direcciones de retorno de subrutinas, parámetros y variables locales o estado de los registros. No os preocupéis si no comprendéis muy bien las funciones de este registro. Ya volveremos a explicar con más detalle su uso, cuando hablemos de rutinas y subrutinas en un próximo artículo.
FIGURA 3. Bits de condición contenidos en el registro de estado (SR). Sólo emplearemos de momento los 5 primeros bits de este registro. Estos bits son los que reflejan el estado del procesador después de ejecutar una instrucción. |
Contador de programa
Este registro es utilizado para contener la dirección de la siguiente instrucción que el procesador debe ejecutar. Las instrucciones de salto colocan aquí la dirección o le suman el desplazamiento de salto, pero normalmente es incrementado en dos para apuntar siempre a la siguiente instrucción que se ejecutará. Es un registro de 32 bits, pero únicamente son utilizados los 24 primeros, ya que el espacio de direcciones que puede generar es de 16 Mb (2^24 bits).
Registro de Estado
Este registro está dividido en dos partes: una dedicada al sistema y otra al usuario (Byte de sistema y Byte de usuario).
En él se refleja el estado del procesador después de ejecutar una instrucción, mostrando en cada bit del registro un determinado resultado. En el byte de usuario existen cinco bits de estado, denominados bits de condición. Cada uno corresponde a una condición distinta, tal y como se muestra en la figura 4.
Este registro es muy importante, puesto que como ya veremos, es utilizado como condición para los bucles e instrucciones de salto condicional. Expliquemos con más detalle la parte que más nos interesa ahora, la del "byte de usuario".
Bits de Condición del Usuario:
-
Acarreo (Carry): Este bit indica si la operación realizada produce un acarreo en el resultado, en este caso está activado (C=1). Operaciones como la suma, resta o comparación modifican este bit de condición según el resultado que obtengan. Cuando se utilizan las operaciones de desplazamiento o rotación, el bit que es extraído se conserva aquí. El acarreo o "carry" lo explicaremos cuando aparezcan las instrucciones aritméticas, y más en concreto, la suma.
-
Desbordamiento (oVerflow): Cuando una operación aritmética sobrepasa los límites permitidos en el tamaño del resultado, este bit está activado a 1 (El resultado es incorrecto). En caso contrario está a 0.
-
Cero (Zero): Si el resultado de una operación es cero, este bit de condición es activado a 1. En caso contrario, es decir, resultado diferente de cero, es desactivado (Z=0).
-
Negativo (Negative): El contenido del bit 3 (N) del registro de condición es una copia del bit de más peso durante operaciones aritméticas, lógicas, desplazamiento y rotación. Es decir, este bit muestra si el resultado tiene signo negativo (N=1) o positivo (N=0).
-
Extensión (eXtension): Durante las operaciones aritméticas, desplazamiento y rotación este bit recibe el estado del acarreo. Es utilizado en operaciones de precisión.
¿Para qué se utilizan estos bits? Estos bits son consultados por algunas instrucciones para decidir, en función del valor de ese bit, la ejecución o la operación que tienen que realizar.
El ejemplo más claro lo tenemos en los bucles. El procesador ejecuta las instrucciones contenidas en un bucle hasta que se cumple una condición, como por ejemplo que un registro o un resultado contenga el valor 0. Para indicar esa condición de salida del bucle, existen instrucciones que consultan los bits de estado y comprueban si el resultado es cero, negativo, erróneo, etc... según nos interese.
Instrucciones básicas
Después de saber dónde almacenar nuestros datos, es decir, en memoria o en registros internos, es un buen momento para introducir las primeras instrucciones de ensamblador.
Si no se indica lo contrario, las instrucciones ensamblador que aparecerán aquí siguen SIEMPRE alguno de los siguientes formatos:
1) instrucción (ej. rts / nop)
2) instrucción + operando (ej. dlr d0)
3) instrucción + operando_origen, operando_destino (ej. move d0,d3 / add #5,$57564)
Cualquiera de nosotros puede deducir fácilmente que una de las operaciones que más necesitamos en un programa es la de mover o copiar datos de un lugar a otro.
Por ejemplo, tenemos un dato en memoria y queremos pasarlo a un registro interno, o viceversa. También puede que nos interese copiar un dato de un registro a otro, o de una posición de memoria a otra.
Esta sencilla operación de "mover" datos desde un lugar origen a un lugar destino se realiza en assembler mediante la instrucción:
MOVE origen,destino
La acción de "mover" de un origen a un destino no significa, como algunos podrían pensar, copiar el dato origen en el lugar destino y borrar posteriormente el origen. Únicamente se limita a copiar, de tal forma que al finalizar la instrucción tanto el origen como el destino tendrán el mismo contenido.
Veamos algunos ejemplos prácticos en assembler:
a) move d0,d2
b) move $00fffe,d1
c) move #5,d7
d) move $003456,$007303
Estos ejemplos pueden servir para volver a introducir algo más de nomenclatura. Los registros siempre los escribo personalmente en minúsculas (d0,d1,d2..d7, a0,a1..a7), puesto que me parecen más visibles, pero no hay ningún problema si se escriben en mayúsculas (D0,D1,D2..D7).
Por otro lado, podéis observar como se utiliza el operador "$" para indicar que es un número hexadecimal. Existen más operadores de este tipo, como "%" para indicar que es un número binario. Por defecto, si no se utilizan "%" o "$" delante de un número, éste se toma en base decimal.
Pero quizás, la novedad más importante que se ha introducido en estos ejemplos, es los que se denomina: "modo de direccionamiento".
Sabemos que la mayoría de las instrucciones trabajan con datos que están en memoria o en los registros. Sin embargo, no hemos hablado del método empleado por el microprocesador para encontrar esos datos, a los cuales también hemos llamado en alguna que otra ocasión "operandos".
¿SISTEMA NUMÉRICO HEXADECIMAL?
Probablemente, algunos de vosotros no estará familiarizado con el sistema numérico en base 16 o hexadecimal. Es muy importante para el programador de ensamblador saber mínimamente los valores y equivalencias que existe entre el sistema decimal, el hexadecimal y binario. En esta tabla se indican todos los números comprendidos entre 0 y 15 en decimal, binario y hexadecimal.
Decimal Hexadecimal Binario
0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111 |
Es interesante saber pasar de una base a otra, pero sobre todo, de hexadecimal a binario y viceversa. El proceso para convertir un número binario en hexadecimal es muy sencillo. Pongamos un ejemplo: número hexadecimal -> $0F05FF
Cada dígito hexadecimal se convierte en 4 bits binarios, utilizando para ello la anterior tabla de conversión
$ 0 F 0 5 F F
% 0000 1111 0000 0101 1111 1111 |
Veamos como una $F en hexadecimal se convierte en un %1111 binario, un $5 hexadecimal en un %0101 binario, etc. Finalmente, resulta el número binario %000011110000010111111111.
Si se quiere pasar de binario a hexadecimal, se efectúa el proceso inverso, es decir cada grupo de 4 bits binarios se convierte a un dígito hexadecimal. Por ejemplo,
Número binario -> % 1010 1001 1111 0101 0111 1100
Paso a hexadecimal -> $ A 9 F 5 7 C |
Como sería muy lento tener que estar constantemente convirtiendo de una base a otra números en nuestros fuentes, podemos optar por utilizar una calculadora como la que acompaña el disco número 3 de AInFo 1 (Enero/Febrero). Esta calculadora, llamada "calcu", permite convertir rápidamente números de una base a otra e incluso efectuar operaciones entre ellos. |
|
Pues bien, es posible buscar los operandos de distintas formas. En realidad "buscar" un operando equivale a saber exactamente dónde se encuentra y acceder a él. Para ello, podemos emplear una dirección de memoria, un registro o incluso una combinación de los dos. Los "modos de direccionamiento" son los distintos métodos que podemos emplear para indicarle al procesador cómo debe determinar la dirección de los operandos que una instrucción posee.
Este es uno de los temas más conflictivos para el que se inicia en el mundo del ensamblador, puesto que existen como mínimo 14 modos de direccionamiento diferentes en un Motorola 68000. Pueden parecer pocos, pero es que también existe la posibilidad de combinarlos entre ellos, para dar cientos de formas de acceso diferentes.
Poco a poco irán apareciendo todos estos modos, a medida que los necesitemos en los ejemplos y programas. De momento, atacaremos tres modos muy sencillos y que como podremos comprobar, son muy empleados en nuestros fuentes. Se llaman "absoluto", "inmediato" y "directo".
El primero, es empleado en los "move's" de los ejemplos b) y d) y siempre consta de una dirección de memoria indicada por un valor numérico hexadecimal. Con estas dos instrucciones, estamos copiando el contenido de una posición de memoria, indicada en el primer operando origen, en el segundo operando destino. En el caso concreto d), copiamos el dato contenido en la posición de memoria $003456 a otra posición, la $007303. Sin embargo, en el caso b), lo hacemos de la posición $00fffe al registro de datos d1. Observad cómo, en este modo de direccionamiento, se utilizan las direcciones "absolutas", es decir, la dirección real y única de memoria donde reside un dato. Pero sólo para acceder al dato, en ningún momento la dirección es empleada como dato al efectuar la copia, sólo es transferido el contenido marcado por esa dirección.
El segundo modo, el inmediato, se observa en el "move" del ejemplo c).
Con este modo podemos introducir valores numéricos constantes en memoria o en registros.
Por ejemplo, es muy útil cuando queremos inicializar con un dato determinado un registro o escribirlo en una posición de memoria. Este modo se le indica al ensamblador precediendo con un símbolo "#" el dato del operando origen. Por supuesto, no podemos colocar este modo en el operando destino, puesto que no tendría sentido. En el ejemplo c), estamos almacenando un 5 decimal en el registro de datos d7.
Y por último, el modo más sencillo: el directo. Únicamente puede ser utilizado con operandos que sean registros internos (de datos o direcciones).
En el ejemplo a) tenemos una muestra de ello. En él copiamos el contenido del registro origen d0 al registro d2. Cuando finalice la operación, los dos contendrán el mismo valor. Este es el modo más sencillo, y también uno de los más utilizados, puesto que en él sólo intervienen directamente los registros.
Ok, ahora ya sabemos con qué instrucción podemos mover datos ("move") y también cómo decirle al microprocesador dónde debe encontrarlos (modos de direccionamiento directo, inmediato y absoluto).
Pero quizás alguien haya pensado en un pequeño detalle que todavía no hemos comentado: ¿Podemos decir el tamaño del operando origen que queremos mover? Es decir, si sólo nos interesa copiar un valor que ocupa 8 bits (un byte), o un "word", ¿es posible indicárselo al microprocesador? La respuesta es SÍ. Veamos cómo:
a) move.b d0,d2
b) move.w d0,d2
c) move.l d0,d2
Con las extensiones ".b", ".w" y ".l" estamos indicando que la instrucción SÓLO debe tratar con los 8 (byte), 16 (word) ó 32 (long word) bits respectivos de menor peso del operando origen. Para verlo más claro, vamos a dar valores concretos al contenido de los registros y posiciones de memoria de los ejemplos:
- d0 contiene un "158745" Y d2 un "525", que en binario son "101" y "101 010 0101", respectivamente.
Cuando ejecute al microprocesador la instrucción a), se encontrará con que tiene que copiar el contenido del registro d0 en el registro d2. Pero, como se la ha indicado con la extensión ".b" que sólo hay que copiar un byte de d0, obtendremos como resultado:
Antes de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0000 0000 0000 0101 0010 0101
Después de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0000 0000 0000 0101 1000 0101
Si comparamos d2 antes y después de ejecutar, observamos cómo SÓLO se han copiado los 8 primeros bits de d0 en los ocho primeros bits de d2. En el proceso de copia, ni d0, ni los bits restantes de d2 se han visto afectados por la operación.
El caso b) es similar, pero esta vez se copian los 16 bits de menor peso del registro d0 en el registro d2:
Antes de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0000 0000 0000 0101 0010 0101
Después de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0000 0000 0000 1110 1000 0101
Y por último, el caso c), donde se copian todos los 32 bits de d0 en d2:
Antes de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0000 0000 0000 0101 0010 0101
Después de ejecutar
Reg. Valor binario
d0: % 0010 1011 0000 1110 1000 0101
d2: % 0010 1011 0000 1110 1000 0101
En la próxima entrega de AInFo, hablaremos de instrucciones para realizar bucles, saltos y operaciones aritméticas como la suma y la resta. Con éstas y algunas más, crearemos ejemplos para ver su funcionamiento en el Amiga.
|