Traducciones de esta página:

Herramientas de usuario

Herramientas del sitio


es:capítulo_0_-_introducción_general

CAPÍTULO 0

INTRODUCCIÓN GENERAL

El lenguaje ensamblador puede considerarse en gran parte equivalente al lenguaje máquina. En realidad, a cada instrucción de lenguaje ensamblador le corresponde una instrucción de código máquina, si bien el ensamblador utiliza unos nombres relativamente más sencillos de comprender y recordar que el lenguaje máquina. Así, para alguien que sepa inglés, DCR B puede significarle algo si se le dice que es la abreviatura de Decrement Register B (Decrementa el registro B). Sin embargo, 00000101 no dice gran cosa a nadie, sepa o no inglés, y sin embargo es la instrucción en código máquina que se encarga de decrementar el registro B.

A estas instrucciones de lenguaje ensamblador, formadas por abreviaturas o acrónimos de términos del lenguaje normal (inglés) se las llama nemónicos o nemotécnicos, aunque en este curso nos referiremos a ellas por el nombre más sencillo de «intrucciones».

Dado que las instrucciones del lenguaje ensamblador se convierten directamente en instrucciones de código máquina, su ámbito de trabajo es el mismo que permite el microprocesador. Por tanto, para poder manejar correctamente esas instrucciones necesitamos conocer bien el microprocesador (o por lo menos su estructura de registros y, cómo no, su juego de instrucciones).

Aquí nos encontramos con, en principio, una paradoja. Muchos habréis leído que los ordenadores AMSTRAD PCW utilizan un microprocesador Z80, pero las herramientas de programación en ensamblador que se dan con el CP/M (MAC, RMAC, SID, etcétera) están diseñadas para el microprocesador 8080. Entonces, ¿no nos sirven? Pues aunque parezca increíble, sí que nos servirán.

La explicación es ésta: el 8080 es cronológicamente anterior al Z80, y éste se diseñó de forma que fuera compatible con el 8080 y además tuviera nuevas instrucciones. Esto hace que un programa escrito para el 8080 funcione siempre con un Z80. Sin embargo, esto puede no ser cierto al revés, y un programa escrito para el Z80 puede funcionar o no con un 8080, según que utilice o no las nuevas instrucciones que aporta el Z80.

Otra curiosidad es que las instrucciones o nemónicos del lenguaje ensamblador de estos dos microprocesadores son distintos: por ejemplo, la instrucción DCR B que vimos antes, correspondiente al 8080, tiene su equivalente en el Z80 con el nombre de DEC B, y ambas se convierten en la instrucción de lenguaje máquina 00000101, ejecutando ambas la misma función de restarle una unidad al contenido del registro B (Esto es así en el 8080 y el Z80). En otros microprocesadores el efecto de la instrucción 00000101 puede ser muy distinto.

Antes de pasar a presentaros a vuestro nuevo amigo, el microprocesador 8080, una puntualización. Todo o casi todo lo explicado en este curso se puede aplicar tanto a los AMSTRAD PCW como a los CPC 6128 y 464. La razón: todos llevan un Z80, todos tienen CP/M y todos tienen herramientas para programar sobre 8080. En el caso de los CPC 6128, la única diferencia con el PCW estará en ciertas características del hardware (por ejemplo, el tamaño de la pantalla). En el caso del 464, las herramientas de programación bajo CP/M 2,2 son algo distintas, y comentaremos las diferencias cuando llegue el momento de explicar el uso del programa ensamblador.

Los registros del 8080

El microprocesador 8080 dispone de seis registros de 8 bits de propósito general, que se pueden emparejar para formar 3 registros de 16 bits (B,C,D,E,H y L, o emparejados como BC, DE y HL: un registro acumulador de 8 bits para operaciones matemáticas o lógicas (A); un registro de 16 bits para gestionar una pila de datos (SP); un registro de 16 bits para gestionar el flujo del programa (PC); y un registro formado por 5 bits llamados “bits de estado” o “flags” (del inglés “banderas”), utilizados por el microprocesador para marcar diversos hechos. A estos cinco bits podemos acceder de forma directa probándolos, o bien de forma indirecta guardándolos en memoria como ocho bits.

La figura 1 ilustra la estructura de registros del 8080, y los flags se representan como si fueran ocho y formaran un byte, señalando con una X los tres bits que no se corresponden con ninguno de los flags.

El registro SP (Stack Pointer=Puntero de Pila) se utiliza para guardar datos en memoria con comodidad. Su contenido es la dirección de un área de memoria adonde va a parar el contenido de los registros cuando utilizamos una instrucción PUSH, y de donde recuperamos su contenido cuando utilizamos una instrucción POP. Este área también la utiliza el microprocesador de forma automática cuando se ejecuta una instrucción CAL, pero esto lo veremos con más detalle en su momento.

El registro PC (Programa Counter=Contador de Programa) contiene siempre la dirección de la siguiente instrucción a ejecutar, y el programador no tiene que preocuparse por él, ya que el microprocesador lo gestiona de forma automática.

Los flags tienen una doble función. Además de permitir al programador saber el estado en que se encuentra el microprocesador, contaremos con una serie de instrucciones llamadas condicionales, las cuales se ejecutan o no en función del estado de alguno de esos flags. Estas instrucciones son las que permiten que los programas “tomen decisiones”.

Las operaciones que puede realizar el microprocesador con estos registros son bastante elementales: mover el contenido de uno a otro, sumar sus contenidos, restarlos, rotarlos a la derecha o a la izquierda, complementarlos, y las funciones lógicas OR, AND y XOR. Como veis, no hay multiplicación, ni división, ni números decimales, ni sentencia PRINT, ni bucles FOR…NEXT, ni nada parecido. Pero que no cunda el pánico, que todo esto es posible. Y para el que lo dude, le recordamos que todas las instrucciones BASIC son ejecutadas por el intérprete BASIC grabado en la ROM, y que no es más que un programa escrito en código máquina.

Usando los registros

El uso más elemental de los registros es introducir un valor en ellos. Esto podemos hacerlo en los registros A, B, C, D, E, H y L considerándolos como registros de 8 bits, o en los registros BC, DE y HL considerándolos como registros de 16 bits.

Como registros de 8 bits tenemos estas instrucciones:

  • MVI A,nn
  • MVI B,nn
  • MVI C,nn
  • MVI D,nn
  • MVI E,nn
  • MVI H,nn
  • MVI L,nn
  • MVI M,nn

Como registros de 16 bits tenemos estas instrucciones:

  • LXI B,nnnn
  • LXI D,nnnn
  • LXI H,nnnn
  • LXI SP,nnnn

MVI es abreviatura de MoVe Inmediate (Mueve el dato inmediato), y LXI es abreviatura de Load Index register Inmediate (Carga el registro índice con el dato inmediato). Inmediato se refiere a que el dato está situado en la posición de memoria siguiente a la instrucción. Registro índice se refiere a los registros de 16 bits.

La palabra nn representa un número de 8 bits (entre 0 y 255), y la palabra nnnn representa un número de 16 bits (entre 0 y 65535). Observaréis un registro que no hemos descrito, el registro M. En realidad no se trata de un registro, sino de una posición de memoria. En efecto, el 8080 nos permite manejar posiciones de memoria como si fueran registros, incrementándolas, decrementándolas, sumándolas, restándolas o asignándoles un valor. M quiere decir “la dirección de memoria a la que apunta HL”. Es decir, suponiendo que el registro doble HL contenga la dirección 1345, la instrucción MOV M, 26 hace que la dirección de memoria 1345 pierda su contenido anterior y pase a contener 26.

Las instrucciones LXI B, LXI D y LXI H, aunque sólo figure una letra, cargan el par de registros correspondiente (LXI B, nnnn carga el par BC con nnnn, LXI D, nnnn carga el par DE con nnnn y LXI H, nnnn carga el par HL con nnnn). La instrucción LXI SP, nnnn carga el registro SP o puntero de pila con el valor nnnn. La pila es una estructura de datos en memoria descedente, por lo que el valor con el que carguemos el registro SP será el más alto en memoria. Cada vez que enviemos un dato a la pila con PUSH el valor de SP se decrementa, y cuando recuperemos un valor de ella con POP el puntero SP se incrementa.

El hecho de que esta estructura se llame pila se debe a que se comporta de forma similar a un apilamiento (por ejemplo, de platos o de libros), es decir, el primer elemento que recuperamos es el último que hemos depositado.

Los datos que se manejan con la pila constan siempre de dos bytes, por lo que los incrementos y decrementos automáticos del registro SP producidos por las instrucciones PUSH, POP, CALL y RET, que veremos más adelante, son siempre de dos unidades.

Movimiento de datos entre registros

De forma genérica podemos describir todas estas instrucciones como:

MOV R1, R2

donde R1 y R2 representan ambos a uno cualquiera de los registros de 8 bits A, B, C, D, E, H, L y M. La excepción es la instrucción MOV M,M que no existe. Por tanto, las posibles instrucciones de este tipo son las que muestra la figura 2.

Las instrucciones de transferencia de datos se completan con las siguientes:

XCHG: intercambia el contenido del registro doble DE con el del registro doble HL (XCHG es abreviatura de eXCHanGe, intercambia).

LDAX B: carga el registro A con el contenido de la dirección de memoria a la que apunta el registro BC (LDAX B es abreviatura de LoaD A indeXed with B, carga A indexado con B).

LDAX D: carga el registro A con el contenido de la dirección de memoria a la que apunta el registro DE.

LDHL nnnn: carga el registro HL con el contenido de la dirección de memoria especificada por nnnn y la siguiente. Esto se hace de este modo: L se carga con el valor de la dirección indicada por nnnn, y H se carga con el valor de la dirección indicada por nnnn +1.

LDA nnnn: carga el registro A con el contenido de la dirección de memoria indicada por nnnn.

STAX B: guarda en la dirección de memoria a la que apunta BC el contenido del registro A (STAX B es abreviatura de STore Acumulator indeXed with B, almacena el contenido del acumulador indexado con B).

STAX D: guarda en la dirección de memoria a la que apunta DE el contenido del registro A.

SHLD nnnn: guarda en la dirección de memoria especificada por nnnn y en la siguiente el contenido del registro HL. Esto se hace de este modo: en la dirección nnnn se guarda el contenido del registro L, y en la dirección nnnn+1 se guarda el contenido del registro L.

STA nnnn: guarda en la dirección de memoria indicada por nnnn el contenido del registro A o Acumulador.

Bien, esto es todo por el momento. En el próximo capítulo veremos un poco cómo manejar estas instrucciones, las llamadas subrutinas, y comenzaremos a manejar las herramientas de programación. Os recomendamos antes de pasar al siguiente capítulo que leáis despacio este capítulo y tratad de entenderlo bien.

es/capítulo_0_-_introducción_general.txt · Última modificación: 2016/07/10 01:50 por jevicac