                  INTRODUCCION AL ASM: LAS INTERRUPCIONES
                  =======================================

   Como comentbamos al final del captulo dedicado a la pila, en este captulo
vamos a abordar el tema de las interrupciones. Comencemos pues.

   Al principio del curso, vimos el funcionamiento del uP, que contnuamente
recoge instrucciones de la memoria (de la direccin apuntada por CS:IP) y las
ejecuta. Aunque ste es el funcionamiento habitual, hay momentos en los que
esta actividad se tiene que interrumpir. Para ver la razn de la existencia de
las interrupciones, veamos un ejemplo.
   Supongamos que el funcionamiento del teclado fuese el siguiente: el programa
enviara una seal al teclado, a la que el teclado respondera con un cdigo
indicativo de la tecla. Existira tambin un cdigo para indicar 'ninguna
tecla pulsada'. Dentro de este esquema de funcionamiento, imaginemos que un
programa est en un momento dado llevando a cabo una actividad de clculo, sin
comprobar para nada lo que ocurre con el teclado. Es perfectamente posible que
en ese momento el usuario del ordenador pulse una tecla y la suelte antes de
que el programa compruebe el teclado; si el funcionamiento del teclado fuese el
que hemos visto, esa pulsacin de tecla se perdera. Existen varias posibles
soluciones a este problema: una sera que el programa comprobase constantemente
el teclado, con la consiguiente molestia al desarrollar el programa y la prdi-
da de velocidad. Otra sera aadir hardware al teclado para llevar un buffer
con las teclas pulsadas. Esto ocurre as en los PCs, pero este buffer slo
tiene capacidad para unas cuatro pulsaciones, con lo que el problema sigue pre-
sentndose. La solucin ms adecuada, ya que se puede extender a otros proble-
mas parecidos, son las interrupciones hardware, que adems es la solucin
implementada en los PCs.

   Cuando algn perifrico necesita que el uP se encargue de algo con urgencia
(por ejemplo, leer el valor de una tecla) pone a nivel activo uno de los pines
del uP, denominado INTR. En este momento, el uP para lo que est haciendo (si
est en medio de la ejecucin de una instruccin, se completa sta primero) y
lee el valor de 8 bits (0 a 255d) de las 8 lneas bajas del bus de datos. El
dispositivo que provoca la interrupcin se debe haber encargado de poner en
stas lneas un cdigo que identifica el nmero de interrupcin. Este nmero es
un cdigo que identifica el dispositivo que ha provocado la interrupcin; por
ejemplo, el teclado tiene el nmero 9 como identificativo y por tanto pone en
las 8 lneas bajas del bus de datos el valor 00001001b cuando se pulsa una te-
cla. Ahora, lo que ocurre es lo siguiente: el uP no est especialmente diseado
para manejar algunos dispositivos en concreto, por lo que cuando recibe una in-
terrupcin lo que hace es llamar a una rutina que el programador ha dejado re-
sidente en la memoria, que es la que lee la tecla pulsada y la almacena en un
buffer en memoria en el caso de la interrupcin 9 (para otros nmeros de inte-
rrupcin, se lleva a cabo otra tarea). Esta rutina se suele denominar 'rutina
de servicio a la interrupcin', por razones bastante claras (eso espero). La
ltima instruccin de la rutina de servicio a la interrupcin es la instruccin
IRET, que hace que el uP vuelva a la direccin donde estaba antes de la inte-
rrupcin. An nos queda por ver cmo el uP, a partir del nmero recibido por el
bus de datos con el nmero de interrupcin, identifica a qu rutina ha de
saltar.

   Los diseadores del 8086 reservaron los primeros 4*256d bytes de la memoria
(1 kilobyte desde la direccin absoluta 00000h hasta la 003FFh, ambas inclu-
das) para una tabla denominada 'tabla de vectores de interrupcin'. Esta tabla
contiene cuatro bytes para cada nmero de interrupcin (por eso son 4*256d
bytes, 4 bytes/n de interrupcion * 256d nmeros de interrupcin posibles), que
especifican la direccin de memoria donde comienza la rutina de servicio a cada
interrupcin. Al recibir una demanda de interrupcin, el micro toma el valor
del bus de datos (el n de interrupcin) y lo multiplica por cuatro. Usa el va-
lor obtenido como direccin de la que leer 4 bytes con la direccin a la que
saltar. Estos cuatro bytes son el offset y el segmento que forman la direccin
absoluta. El ordenamiento Intel especifica que se almacenan primero los bytes
de menor peso, y en el caso del 8086 se almacena primero el offset que el valor
de segmento. Por ejemplo, si instalsemos una rutina de servicio a la interrup-
cin CCh en la direccin de memoria 1122h:3344h, tendramos que introducir los
siguientes valores en la tabla de vectores de interrupcin:

           DIRECCION ABSOLUTA        VALOR
           ------------------        -----
           4 * CCh     = 816d         44h
           4 * CCh + 1 = 817d         33h
           4 * CCh + 2 = 818d         22h
           4 * CCh + 3 = 819d         11h

   Este es el modelo de funcionamiento, pero todava no hemos vistos varios
detalles muy importantes. En realidad, la arquitectura del PC hace que los
perifricos no interrumpan directamente al uP (qu ocurrira si dos perifri-
cos deciden generar una seal de interrupcin a la vez?), sino que stas estn
jerarquizadas mediante unos chips denominados 8259 o PIC ('Programmable
interrupt controller', Controlador de Interrupciones Programable), interactuan-
do de una manera bastante compleja. De momento, esto no nos interesa demasiado.

   Cuando el micro recibe la seal de interrupcin, el CS y el IP contienen la
direccin de la siguiente instruccin a ejecutar (ya que el IP se incrementa
nada ms cargar la instruccin, antes incluso de ejecutarla). Estos se cargan
con los valores ledos de la tabla de vectores de interrupcin, por lo que es
necesario preservar su valor actual para luego volver al punto donde se estaba
cuando se produjo la interrupcin. Lo que se hace es empujar estos valores en
la pila antes de saltar a la rutina de servicio a la interrupcin. Adems,
antes de empujar el CS y el IP (en este orden), se empuja el contenido de un
registro que todava no hemos visto (lo veremos dentro de poco), llamado
'registro de flags'. Los bits de este registro tienen cada uno un significado
especial, indicando en todo momento el estado actual del uP y, en el caso de
algunos bits, el resultado de la ltima operacin ejecutada (por ejemplo, el
flag ZERO indica si la ltima operacin dio un resultado de 0). Ya que algunos
flags se utilizan para indicar si se esta ejecutando una rutina de servicio a
la interrupcin (y por tanto se modifican antes de saltar a la rutina), este
registro se salva a fin de que al volver al hilo normal de ejecucin del pro-
grama se restaure completamente el estado anterior de micro.

   Ya que una interrupcin se puede producir en cualquier momento, sta debe
ser transparente al programa en ejecucin. Cuando se escribe un programa, no
se piensa en que entre una instruccin y la siguiente pueden ejecutarse 200
instrucciones que leen un valor de teclado y lo almacenan en un buffer en
memoria. Por tanto, al volver de una interrupcin se tiene que restaurar por
completo el estado del uP, de forma que el programa interrumpido no se vea
afectado. Por ejemplo, supongamos un fragmento de cdigo as:

		MOV AX,[200h]
		ADD AX,30h

   Si en medio de las dos instrucciones se ejecuta una rutina de servicio a la
interrupcin que modifica el contenido de AX, el resultado para el programa
puede ser catastrfico. El uP se encarga de que el registro de flags sea res-
taurado, ya que siempre es modificado. Pero es la propia rutina de servicio a
la interrupcin la que debe preservar el contenido de los registros que sta
modifique. Por tanto, las rutinas de servicio a la interrupcin suelen tener el
siguiente aspecto:

		PUSH AX
		PUSH BX
		PUSH CX
		PUSH DX
		PUSH DS
		PUSH ES
		PUSH BP
		PUSH SI
		PUSH DI

		[...]		;cdigo que modifica los anteriores 9 registros

		POP  DI
		POP  SI
		POP  BP
		POP  ES
		POP  DS
		POP  DX
		POP  CX
		POP  BX
		POP  AX
		IRET		;retorna al programa interrumpido

   Otro de los aspectos importantes de las interrupciones es el de la pila.
Hemos visto que el uP empuja el contenido de los flags, el de CS y el de IP en
la pila apuntada por SS:SP en el momento de la interrupcin. Aunque la rutina
de servicio a la interrupcin podra modificar SS y SP para usar su propia
pila, esto no es lo habitual; es decir, las rutinas de servicio a la interrup-
cin suelen utilizar la pila del programa interrumpido. Por tanto, aunque vimos
que un POP no borra el valor apuntado por SS:SP despus de leerlo, no se puede
suponer que despus de un POP los valores inmediatamente por debajo de SS:SP
continen igual, ya que pueden haber sido pisados debido a una interrupcin.

   En realidad, cuando se pone a nivel activo la patilla INTR, el uP no siempre
interrumpe el programa en ejecucin, sino que primero consulta uno de los bits
del registro de flags, llamado IF ('Interruption Flag', Flag de Interrupcin)
y slo si este est a 1 se atiende la interrupcin. En caso de que IF est a 0,
simplemente se ignora la peticin de interrupcin. Es el 8259 (el PIC) el que
se encarga de volver a generar la interrupcin repetidas veces hasta que el uP
est listo. Cuando se salta a una rutina de servicio a la interrupcin, una de
las cosas que se hace es poner este bit a 1 de manera que no se pueda interrum-
pir la propia rutina de servicio a la interrupcin. Pero esta caracterstica la
puede aprovechar cualquier programa, ya que existen dos instrucciones que ponen
a 0 o a 1 el flag IF:

		STI	Pone a 1 el bit IF, para que se atienda a las
			peticiones de interrupcin.
		CLI	Pone a 0 el bit IF, para desactivar las interrupciones.

   Debido a que se puede controlar el uP para que atienda o no a las peticiones
de servicio a la interrupcin, este tipo de interrupciones se denominan
'Interrupciones enmascarables'; existe otra patilla denominada 'NMI' ('Non
Maskable Interrupt', Interrupcin No Enmascarable) de funcin parecida a INTR,
pero a cuya peticin el uP siempre responde. En realidad, no s con certeza
cmo funcionan las NMI, ya que no se usan habitualmente para la programacin.
En el Spectrum, se utilizaba esta interrupcin para los 'transfer', aparatos
que permitian en cualquier momento generar una NMI, por medio de un pulsador, y
que se grabara a cinta o disco los registros del uP y los contenidos de toda la
memoria (con cualquier programa que sta tuviera), para luego poder cargarlo y
continuar la ejecucin en el mismo punto; estos eran copiadores infalibles.

   Todo esto que hemos visto hasta ahora es interesante para nosotros, pero en
realidad es al diseador de hardware y al programador de drivers de dispositi-
vos a quien ms le concierne. Adems, las interrupciones hardware son an ms
complicadas, ya que hay que controlar el PIC, etc... En realidad, slo en algu-
nos pocos casos nos interesar todo esto (el caso ms habitual es el de querer
instalar una rutina de servicio a la interrupcin de teclado para poder detec-
tar la pulsacin simultnea de varias teclas, como en los juegos). Estas inte-
rrupciones que hemos visto se denominan interrupciones hardware, pero lo que
principalmente nos interesa son las interrupciones software.

   Existe una instruccin en ASM llamada INT, que fuerza al uP a realizar el
mismo proceso que lleva a cabo para atender una interrupcin. El nmero de
interrupcin no se lee del bus de datos, sino que viene dado por el operando de
la instruccin INT. Por ejemplo, puede usarse la instruccin 'INT 9' para lla-
mar a la rutina de servicio a la interrupcin de teclado. En realidad, ste no
es el uso habitual, ya que, por ejemplo, la rutina de servicio a la interrup-
cin 9 espera que el teclado tenga una pulsacin de tecla que enviar, por lo
que el funcionamiento de sta sera anmalo al invocar la INT 9 sin ninguna te-
cla esperando. En realidad, la utilidad de las interrupciones software es otra.

   Una parte muy importante de un sistema operativo (quiz la ms importante)
es el 'kernel' o ncleo. Este kernel no es ms que un conjunto de rutinas o
funciones que ejecutan diversas tareas, como por ejemplo escribir una cadena en
el monitor o escribir datos en un fichero. Estas funciones pueden estar en dis-
tintas direcciones de memoria en distintos ordenadores, por lo que es necesaria
una manera de encontrarlas. En el caso del MS-DOS, la mayora de los servicios
del kernel estn integrados en una funcin que lleva a cabo distintas acciones
segn el contenido del registro AH a la entrada; a cada tarea distinta que se
puede ejecutar en funcin de AH se le denomina 'servicio'. En los dems regis-
tros se pasan parmetros, cuyo significado vara de un servicio a otro; en caso
de que sea necesario devolver algn valor, se devuelve en los registros. Por
ejemplo, el servicio 9 (AH = 9) imprime una cadena en la pantalla. La cadena se
recoge de la direccin dada por el par DS:DX, y el final de sta se indica con
el carcter '$'. La funcin que ofrece todos estos servicios se instala en
memoria cuando se carga el sistema operativo de los ficheros IO.SYS y
MSDOS.SYS. Pero cada vez que se carga puede hacerse en una direccin distinta,
por lo que se almacena en el vector nmero 21h de la tabla de vectores de inte-
rrupcin la direccin donde reside la rutina. Por lo tanto, cuando un programa
quiere invocar algn servicio lo que se hace es ejecutar una instruccin 'INT
21h'. A modo de ejemplo, veamos qu haramos para imprimir una cadena almacena-
da en la direccin 1234h:ABCDh :

		PUSH DS		; preserva el contenido de DS
		MOV  AX,1234h	; segmento de la dir. de la cadena
		MOV  DS,AX	; en DS
		MOV  DX,ABCDh	; offset de la dir. de la cadena
		MOV  AH,9	; servicio 9: escribir cadena
		INT  21h	; invoca la funcin del DOS
		POP  DS		; restaura DS

   La interrupcin 21h nunca se invoca a causa de una interrupcin hardware,
por lo que no se ejecuta entre dos instrucciones cualquiera. Cuando sta se
ejecuta, es debido a que el programa la ha invocado expresamente. Por tanto, no
es necesario preservar completamente el estado del uP, y de hecho no se hace.
Muchos de los servicios de la interrupcin 21h devuelven valores en los regis-
tros, por lo que necesariamente son modificados; en los servicios de ficheros
un flag se utiliza para indicar si ha habido un error, y en caso de que lo haya
habido AX contiene un cdigo de error. SS y SP siempre se conservan (por el
propio funcionamiento de las interrupciones, si la rutina modificase estos re-
gistros no se podra volver al programa), pero los dems registros no siempre
lo hacen. En los manuales de referencia de las interrupciones del DOS suele
venir especificado qu registros se conservan, pero lo habitual es empujar los
registros cuyo contenido interesa conservar en lugar de consultar la referen-
cia. A no ser que se use para devolver algn valor, se suele suponer que DS se
conserva. Si interesa conservar el contenido de algn otro registro, es mejor
empujarlo en la pila.

   El BIOS tambin es, principalmente, un conjunto de funciones que el progra-
mador puede usar. Las funciones son de ms bajo nivel que las del DOS, y de
hecho las funciones del DOS suelen valerse de llamadas a la BIOS para cumplir
su tarea. La BIOS puede variar de una placa a otra, pero siempre debe propor-
cionar algunas funciones que vienen definidas por el estndar PC, como por
ejemplo la funcin para escribir un carcter en la pantalla o la funcin para
poner un pixel a un color determinado, usada en modo grfico. Las funciones
grficas del BIOS estn agrupadas en la INT 10h, siendo AH el que especifica el
servicio o funcin en concreto, y, en algunas funciones, AL la subfuncin.

   Con lo que ya sabemos, podemos utilizar las interrupciones del BIOS y del
DOS sin ms problema. Al igual que para programar en un lenguaje como Pascal o
C es tan importante conocer la sintxis como las funciones que nos da la
librera estndar de nuestro compilador, para programar en ASM tambin es nece-
sario conocer las interrupciones del BIOS y el DOS. Que yo sepa, nadie se
aprende los servicios de memoria, por lo que lo habitual es programar con un
manual de referencia de las interrupciones. Por poco dinero, encontraris en
cualquier librera un libro con estos datos. Se hace casi imprescindible para
programar en ASM, por lo que os recomiendo que os hagis con uno. Adems, a
partir de ahora escribiremos programitas en ASM que habr que ensamblar y
linkar, por lo que os har falta el Macro Assembler de Microsoft (abreviadamen-
te MASM) o el Turbo Assembler de Borland (TASM). No os voy a recomendar ninguno
personalmente, ya que las caractersticas principales de ambos son iguales,
pero debera transmitiros la opinin generalizada del rea de programacin ASM
de FidoNET, que se decanta claramente por el TASM. De todas maneras, en el
curso no usaremos caractersticas particulares de ninguno de los dos, por lo
que podris usar cualquiera de los dos para seguirlo.

   En el captulo que viene veremos el 'Hello, world' en ASM, con el objetivo
principal de ver el esqueleto de un listado ASM para el MASM o el TASM. Segui-
remos despus con el juego de instrucciones, viendo los flags y los saltos
primero. Cuando conozcamos la mayora de las instrucciones, comenzaremos a ver
las tareas principales de cualquier programa: interaccin con el usuario,
manejo de ficheros, gestin de memoria, etc... Si alguien tiene inters en al-
gn tema en especial, no tiene ms que comentrmelo. Personalmente, me gustara
ver algo de programacin grfica, que es mi hobby preferido, pero slo si hay
gente interesada. Dentro de unos pocos captulos, iremos viendo a la vez
nuevas instrucciones y servicios que proporcionan el DOS y el BIOS, ya que esto
es un curso y no una referencia de ASM. Al igual que hasta ahora, no busco dar
una definicin terica del ASM (esto no es la Universidad), sino que quiero que
quien siga el curso pueda hacer en ASM todo lo que hace en otros lenguajes.

   Salut :-)

   Jon