                      INTRODUCCION AL ASM: LA PILA
                      ============================

   Como comentbamos al final del cuarto captulo, en este quinto captulo
vamos a estudiar uno de los elementos de mayor utilidad del ASM: la pila.
Al estudiarla veremos dos nuevos registros, el SS y el SP, y dos nuevas
instrucciones, PUSH y POP.

   Primero explicar lo que se entiende por una pila en general y despus vere-
mos la pila del 8086 en concreto. Por eso, si alguien sabe lo que es una pila,
puede saltarse algunas lneas.

   Un ordenador siempre guarda los datos en la memoria, exceptuando los regis-
tros de la CPU. Estos datos pueden estar dispuestos de diferentes maneras, y se
puede acceder a ellos de diversas formas. Una particular organizacin y modo de
acceso a los datos se suele denominar 'estructura de datos', de las que existen
muchas y muy diferentes, cada una adecuada para un propsito. Son estructuras
de datos los arrays, las listas encadenadas, los rboles binarios, etc...

   Las estructuras que nos interesan ahora aqu son las estructuras lineales,
en que cada elemento (cada dato) va despus del anterior. Es decir, cada ele-
mento tiene un predecesor y un sucesor, excepto el primero y el ltimo. Son
estructuras de este tipo los arrays y las listas encadenadas, mientras que no
lo son los rboles binarios (no os preocupis los que no sepis que son estas
cosillas). Las estructuras lineales se pueden clasificar principalmente en dos
grupos: las llamadas FIFO (First In First Out, el primero en entrar es el pri-
mero en salir) y las llamadas LIFO (Last In First Out, el ltimo en entrar es
el primero en salir). Aqu van dos ejemplos de cada una para que quede claro:

   Una estructura FIFO es similar a la cola del autobs: los elementos se orde-
nan segn llegan (los pasajeros se ponen a la cola cada uno detrs del anterior
en llegar) y se sacan empezando por el primero que lleg y acabando por el l-
timo (el primero en llegar a la parada es el primero en montar). Esta estructu-
ra se suele denominar 'cola' o 'queue' ('cola' en ingls).

   En cambio, una estructura LIFO es similar al montn de papeles que suelen
estar pinchados todos juntos en una tintorera: los elementos se van ordenando
segn llegan (cada papel se pincha sobre el anterior), pero al sacarse del
montn se sacan empezando por el ltimo que lleg (se empiezan a sacar por el
ltimo que se pinch, justo en orden inverso al que se pincharon). Esta estruc-
tura se suele denominar 'pila' o 'stack' ('pila' en ingls).

   Estas estructuras se pueden implementar de muchas maneras diferentes por un
programa (todava no estamos viendo la pila del 8086 en concreto, sta viene ya
implementada por el hard del uP). Una manera sencilla de hacerlo es un array
del tipo de dato que queremos cada elemento: si queremos una cola de enteros
ser un array de enteros, si queremos una pila de nmeros reales ser un array
de nmeros en coma flotante, etc... Necesitaremos tambin una variable entera
que llevar la cuenta de los elementos insertados hasta el momento, que llama-
remos 'cuenta'. Veamos cmo se implementara una pila con este esquema:
inicialmente, la variable 'cuenta' valdra -1 (suponemos que los ndices de los
arrays van como en C, comenzando por el cero). Para insertar un elemento, se
incrementara 'cuenta' y se guardara el elemento en la posicin nmero 'cuen-
ta' del array. De esta manera, siempre tendramos accesible el ltimo valor
empujado en 'pila[cuenta]'. Una jugada inteligente sera meter en este punto
una comprobacin de que 'cuenta' no ha sobrepasado el lmite superior de la
pila. En caso de que as fuera, emitiramos un mensaje de error. Este error se
suele denominar 'stack overflow' o 'desbordamiento de la pila', y aunque en la
pila del 8086 no se comprueba internamente, los compiladores de lenguajes de
alto nivel suelen incluir cdigo para comprobarlo. A esta operacin de intro-
ducir un valor en la pila se le suele llamar 'empujar' un valor en la pila (en
ingls 'push').

   La otra operacin que nos queda ver es la operacin contraria, la de sacar
un valor de la pila. Ya que queremos acceder a la estructura en la forma LIFO,
cada vez que se extrae un valor debe ser del extremo superior de la pila. Por
ejemplo, si 'cuenta' vale 4 y queremos extraer un valor, ste deber tomarse de
la posicin 4 del array, que ser el ltimo elemento empujado. Adems, hay que
decrementar la variable 'cuenta' para apuntar al nuevo 'tope' de la pila. Es
importante tener en cuenta que no hace falta sobreescribir la posicin 4 del
array, esto ocurrir la prxima vez que se empuje un valor, sino que basta con
decrementar la variable 'cuenta'. A esta otra operacin se le llama 'pop' en
ingls, en espaol se suele decir simplemente 'sacar' o 'extraer' un valor de
la pila.

   La pila del 8086 viene implementada por el hard del uP, pero los datos resi-
den en memoria. El uP tiene dos registros para manejar la pila: el SS ('Stack
Segment', 'segmento de pila'), que es el cuarto registro de segmento que junto
con CS, DS y ES se utilizan para formar direcciones de 20 bits, y el SP ('Stack
Pointer', 'puntero de pila'), que es un registro de 16 bits que cumple la
funcin de nuestra variable 'cuenta' y que se une al SP para formar la direc-
cin de memoria completa. Cada programa suele destinar una zona de la memoria
para la pila, y es esta zona la que alberga los valores introducidos en sta.
Todos los elementos de la pila son valores enteros de 16 bits, por lo que se
utiliza una palabra de la memoria para cada valor.

   Un aspecto que en un principio puede llamar la atencin es que la pila del
8086, al igual que la de todos los micros que conozco, crece hacia abajo en
lugar de hacia arriba. El funcionamiento de sta es, por lo dems, idntico al
de nuestra pila imaginaria: al principio 'cuenta' apuntara al ltimo elemento
del array ms uno, al empujar un valor se se decrementara cuenta y metera en
'pila[cuenta]' , y al sacarlo se incrementara cuenta y se devolvera
'pila[cuenta]'. Ms adelante se vern las ventajas de que la pila crezca hacia
abajo.

   Los registros SS y SP forman la direccin absoluta donde reside el ltimo
valor empujado. Al empujar un valor, se resta dos a SP (porque cada elemento
son dos bytes, es decir, dos posiciones de memoria) y el nuevo valor se guarda
en la dir SS:SP (y el byte de mayor peso en SS:SP+1). Al sacar un valor, se
toma de las posiciones SS:SP y SS:SP+1 y se suma dos a SP.

   Las instrucciones PUSH y POP nos permiten empujar un valor de 16 bits y
recoger el ltimo valor empujado. El uP se encarga de actualizar SP.

   Vamos a ver un ejemplo un poco 'incivilizado' de cmo pondramos a punto la
pila en una direccin determinada. Digo 'incivilizado' porque lo hacemos en una
direccin arbitraria, donde en un PC podra haber un driver o cualquier cosa.
De esto normalmente se encarga la rutina del DOS que carga un programa y lo
ejecuta, por lo que slo es un ejemplo para comprender el funcionamiento.

   Como sagazmente habris deducido (y por si acaso no, os lo cuento), ya que
la gestin de la pila se hace modificando slo el SP al hacer PUSH y POP, la
pila no puede exceder de 64K. Supongamos que queremos poner una pila de 64K en
el segmento que comienza en la direccin absoluta 80000h (8000h:0). La ltima
palabra de este segmento est en 8FFFEh (el byte de menor peso en sta y el de
mayor en la 8FFFFh). Por tanto, habra que cargar los registros como sigue:

		MOV	AX,8000h
		MOV	SS,AX
		MOV	SP,0FFFEh

   Ms adelante veremos que esto no se puede hacer as, sino que hay que tener
en cuenta las interrupciones, etc.. Por lo que no lo probis. Pero para hacerse
una idea, est bien. En realidad, no es necesario reservar 64K para la pila,
por lo que SP no tiene por qu inicializarse a 0FFFEh. Por ejemplo, si queremos
reservar 32K para la pila inicializaremos SP a 7FFEh.

   El funcionamiento exacto de la pila es el siguiente: cuando se ejecuta una
instruccin PUSH se decrementa SP en dos unidades y se guarda en la direccin
SS:SP el valor a empujar. De esta forma, SS:SP siempre apunta al ltimo valor
empujado. Cuando se ejecuta un POP, se recoge el valor de la direccin SS:SP y
se incrementa SP en dos unidades.

   Ambas instrucciones llevan un solo operando, que especifica de donde se toma
el valor (en el caso de PUSH) o donde se almacena (en el caso de POP). Los
operandos pueden ser uno cualquiera de los siguientes:

   - Un registro de 16 bits de los siguientes: AX, BX, CX, DX, SI, DI, BP o SP.
   - Un registro de segmento (no se permite POPear CS, porque implicara un
     salto del programa a otra direccin, y para esto ya hay otras instruccio-
     nes).
   - Una referencia a memoria (con cualquiera de los modos de direccionamiento
     que vimos con la instruccin MOV).
   - Un valor inmediato (por ejemplo, PUSH 55AAh) (slo para PUSH y en 386 o
     superiores).

   Veamos un pequeo listado en ASM y los contenidos de la pila y los registros
con cada instruccin. El listado es el siguiente:

		MOV  AX,8000h
		MOV  SS,AX
		MOV  SP,0FFFEh		; inicializa la pila
		MOV  AX,55AAh
		PUSH AX			; empuja el valor 55AAh
		MOV  BX,AX
		ADD  BX,1111h		; suma a BX el valor 1111h
		PUSH BX
		POP  AX
		POP  BX

   Estudiemos lo que ocurre a cada paso:

*		MOV  AX,8000h
*		MOV  SS,AX
*		MOV  SP,0FFFEh		; inicializa la pila

   Estas instrucciones inicializan SS y SP para situar la pila al final del
segmento que comienza en la direccin absoluta 80000h. En este caso, no nos
interesan los contenidos anteriores de esa zona de memoria. Representaremos el
contenido de la pila como sigue:

     Direccin de memoria                      Valor         Registros del uP
   --------------------------------------    ------------   ------------------
   Segmento   Offset   Direccin absoluta       Valor           SS = 8000h
   --------   ------   ------------------       -----           SP = 0FFFEh
    8000h     0FFFFh        8FFFFh .............. ??
    8000h     0FFFEh        8FFFEh .............. ??
    8000h     0FFFDh        8FFFDh .............. ??
    8000h     0FFFCh        8FFFCh .............. ??
    8000h     0FFFBh        8FFFBh .............. ??
    8000h     0FFFAh        8FFFAh .............. ??

*		MOV  AX,55AAh

 Despus de esta instruccin, el registro AX contendr 55AAh.

*		PUSH AX			; empuja el valor 55AAh

   Nada ms ejecutarla, as quedarn la pila y los registros del micro:

     Direccin de memoria                      Valor         Registros del uP
   --------------------------------------    ------------   ------------------
   Segmento   Offset   Direccin absoluta       Valor           SS = 8000h
   --------   ------   ------------------       -----           SP = 0FFFCh
    8000h     0FFFFh        8FFFFh .............. ??            AX = 55AAh
    8000h     0FFFEh        8FFFEh .............. ??
    8000h     0FFFDh        8FFFDh .............. 55h
    8000h     0FFFCh        8FFFCh .............. AAh
    8000h     0FFFBh        8FFFBh .............. ??
    8000h     0FFFAh        8FFFAh .............. ??

*		MOV  BX,AX
*		ADD  BX,1111h		; suma a BX el valor 1111h
*		PUSH BX

   Ya que 55AAh + 1111h = 66BBh, as quedarn las cosas:

     Direccin de memoria                      Valor         Registros del uP
   --------------------------------------    ------------   ------------------
   Segmento   Offset   Direccin absoluta       Valor           SS = 8000h
   --------   ------   ------------------       -----           SP = 0FFFAh
    8000h     0FFFFh        8FFFFh .............. ??            AX = 55AAh
    8000h     0FFFEh        8FFFEh .............. ??            BX = 66BBh
    8000h     0FFFDh        8FFFDh .............. 55h
    8000h     0FFFCh        8FFFCh .............. AAh
    8000h     0FFFBh        8FFFBh .............. 66h
    8000h     0FFFAh        8FFFAh .............. BBh

*		POP  AX

   Se tomar el valor de SS:SP, se guardar en AX, y se sumar dos a SP:

     Direccin de memoria                      Valor         Registros del uP
   --------------------------------------    ------------   ------------------
   Segmento   Offset   Direccin absoluta       Valor           SS = 8000h
   --------   ------   ------------------       -----           SP = 0FFFCh
    8000h     0FFFFh        8FFFFh .............. ??            AX = 66BBh
    8000h     0FFFEh        8FFFEh .............. ??            BX = 66BBh
    8000h     0FFFDh        8FFFDh .............. 55h
    8000h     0FFFCh        8FFFCh .............. AAh
    8000h     0FFFBh        8FFFBh .............. 66h
    8000h     0FFFAh        8FFFAh .............. BBh

*		POP  BX

   Anlogo a la instruccin anterior, pero con BX:

     Direccin de memoria                      Valor         Registros del uP
   --------------------------------------    ------------   ------------------
   Segmento   Offset   Direccin absoluta       Valor           SS = 8000h
   --------   ------   ------------------       -----           SP = 0FFFEh
    8000h     0FFFFh        8FFFFh .............. ??            AX = 66BBh
    8000h     0FFFEh        8FFFEh .............. ??            BX = 55AAh
    8000h     0FFFDh        8FFFDh .............. 55h
    8000h     0FFFCh        8FFFCh .............. AAh
    8000h     0FFFBh        8FFFBh .............. 66h
    8000h     0FFFAh        8FFFAh .............. BBh

   Os habris dado cuenta de que podramos haber inicializado SP a 0 para apro-
vechar tambin los dos ltimos bytes del segmento. En realidad, esto es algo
de lo que no es necesario preocuparse prcticamente nunca, pues el DOS se en-
carga de inicializar la pila generalmente.

   Uno de los usos ms habituales de la pila es el de preservar el contenido de
uo o varios registros que van a ser utilizados en un fragmento de cdigo. Suele
ser algo as:

		PUSH AX		; Guardamos AX, BX y CX
		PUSH BX
		PUSH CX
		; [...] Cdigo que modifica AX, BX y CX
		POP  CX		; Recuperamos AX, BX y CX
		POP  BX
		POP  CX

   Ahora ya estamos preparados para introducir un importante aspecto de la
programacin en ASM: las interrupciones. Conociendo las interrupciones, ya ten-
dremos el bagaje necesario para acometer la escritura de programas usando
ensambladores comerciales como el TASM o el MASM, comenzando con el ya tradi-
cional 'Hello, world'.

   Salut :-)

   Jon