=======================================================
Introduccin a la programacin en lenguaje ensamblador
para procesadores Intel serie x86 y compatibles (VI)
=======================================================

Por nmt
numit_or@cantv.net

=========================================================
MODO PROTEGIDO: INTRODUCCIN A LOS REGISTROS DE SISTEMA Y
A LAS ESTRUCTURAS DE DATOS EN ENSAMBLADOR.
=========================================================


--------------------------------------------------------
  CONTENIDO

   Modo protegido: preliminares
	 NOTA:	Sistemas multitareas
	 Multiprogramacin y proteccin
	 Gestin de memoria en sistemas multiprogramacin
		 Segmentacin
		 Paginacin  
		 Segmentacin paginada

   Memoria y privilegios en el Intel 80386

   Estructuras en lenguaje ensamblador

   Estructuras en NASM

   Segmentacin en el 80386
	 Selectores
	 Descriptores
	 Interrupciones
	 Excepciones
	 Conmutacin de tareas
	 Nuevas instrucciones
	 Instrucciones privilegiadas

   Instruciones para la gestin de Entrada/Salida

   Pequea nota sobre algunas intrucciones de operaciones lgicas

   Conmutacin entre modos de funcionamiento
	 Definicin de los segmentos
  	 Acceder a los 4 GB
		 NOTA SOBRE LA LNEA A20
	 Definicin de los selectores
	 Establecimiento de las tablas de descriptores y de los selectores
	 Constantes tiles para definir tablas de descriptores
		 NOTA SOBRE EL DESCRIPTOR NULO
	 Pasar a modo protegido
	 Determinacin del tipo de procesador y revisin del modo actual

   Ejemplo de conmutador de modo real a modo protegido

   Anlisis de load.asm

   El modo plano real

   A modo de recapitulacin

   Apndice:
	 NOTA SOBRE EL DESCRIPTOR NULO
	 NOTA SOBRE BOCHS


-----------------------------
Modo protegido: preliminares
-----------------------------
Aunque el 80386 fue el primer procesador de la serie ix836 en aumentar el tamao de 
los registros de uso geeral de 16 a 32 bits,  ya a partir de 80286 los procesadores 
de esta serie pueden correr en  modo  protegido.  Antes del 80286,  los  ix86  slo 
podan correr en modo real Qu significa esto? qu diferencia hay entre  el  modo 
real y el modo protegido? qu se entiende aqu por modo?

En su manual del 80386,  Intel  afirma  que la implementacin del modo protegido en
este procesador  tiene  como  objeto  ofrecer  un  buen  apoyo a las eleboracin de 
programas,  suministrando un  entorno  con eficientes mecanismos para la depuracin
de programas.  Pero la historia de los sistemas operativos nos  informan  sobre  la
finalidad ltima de la implementacin de sitemas de proteccin  en los procesadores


Multiprogramacin y proteccin
------------------------------
El 80386 puede programarse para que se comporte de  diversas  maneras.  De  acuerdo 
al modo en que corra, favorecer o no requerimientos del sistema:  un  software  de 
sistema operativo carece de viabilidad sin un hardware que le de soporte.

Cuando el MIT dise a comienzos  de la dcada de 1960 un sistema capaz de soportar 
mltiples  usuarios  conectados  a l al mismo tiempo --es decir,  con soporte para 
multiprogramacin--,  no haba  todava  suficientemente  difundido un  hardware de 
proteccin necesario. Por ese motivo, la propuesta del MIT no alcanz popularidad.

La  multiprogramacin  necesita  software  de proteccin Por qu? porque si varios 
usuarios realizan trabajos sobre un sistema,  estarn  corriendo  varias  tareas al 
mismo tiempo,  cada  una con su propio cdigo  y sus propios datos.  Un sistema as 
no debe permitir que  las  tareas que  realiza  un usuario afecte las de los dems: 
sera una verdadera catsrofe que los datos de un usuario se vieran modificados por 
las tarea de otro. Para evitar esto, debe implementarse algn mecanismo que proteja 
los datos de cada usuario.  Este  mecanismo  debe  ser  proporcionado por el propio 
procesador.

Pero  un sistema multiprogramacin exige algo ms que proteccin. Si se trabaja con 
un solo procesador,  su  tiempo  de  trabajo  debe ser repartido entre las diversas 
tareas que se ejecutan,  ya que en principio un procesador slo  puede atender  una 
tarea a la vez.  Para facilitar este proceso,  se parte la memoria del sistema y se 
asigna una parte a cada tarea.  Una vez dividida en partes la memoria, es ms fcil 
asignar a cada tarea un espacio propio y una serie de atributos de proteccin.

Hay que observar tambin que no slo deben correr tareas de usuario:  tambin deben 
correr tareas propias del sistema que administra el proceso general. Evidentemente, 
estas tareas deben tener un privilegio  mayor que todas las dems,  deben estar por 
encima  de  las  tareas  de  los  usuarios.  El  mecanismo de proteccin debe poder 
discriminar y establecer privilegios de tareas y de acceso a memoria.

	*NOTA: 	Sistemas multitareas*
	Decimos que un sistema es mulitareas si puede controlar la ejecucin
	simultnea de varias aplicaciones.  No es  necesario que se ejecuten 
	todas al mismo tiempo,  sino  que el procesador atienda a cada tarea 
	con la rapidez suficiente para dar la impresin de que es as.  Para 
	hacer eso, el sistema debe ubicar en memoria las mltiples "tareas", 
	habilitar  un  espacio que ellas puedan compartir con datos y cdigo 
	comunes  a  todas ellas y administrar el tiempo de ejecucin de cada 
	tarea.  Cuando esto ocurre,  cuando los datos y las instrucciones de 
	varias  tareas  distintas  son  localizadas  en  la memoria al mismo 
	tiempo, decimos que nuestro sistema es multiprogramacin. Un sistema 
	multitareas no debe ser necesariamente multiprogramado. Los sistemas 
	multiprogramacin deben manejar una lista con las tareas pendientes, 
	asignar a cada tarea una zona de memoria  exclusiva  --el rea local 
	de la tarea--,  disponer  una zona de memoria comn y compartida por 
	todas  las  tareas  --el rea global;  el acceso al rea local de la 
	tarea  debe estar restringido a la propia tarea: ninguna tarea podr 
	acceder al rea local de otra,  pero todas las tareas deberan poder 
	acceder al rea global.  Si el sistema da acceso a varios usuarios a 
	un mismo procesador, decimos que este sistema es multiusuario. 

	Para apoyar un sistema multitareas, el procesador debe soportar tres 
	conceptos:

		 Memoria  virtual  que  permita  el  empleo de una  memoria
		  superior a la que realmente posee el sistema.

		 Conmutacin rpida de tareas.

		 Sistema  de proteccin para evitar posibles interferencias
		  en el espacio de memoria privado de cada tarea.

	Cuando  el  sistema soporta accseo de varios usuarios al procesador, 
	se le llama sistema multiusuario.  Un  sistema  de  este tipo es ms
	crtico ya que cada ususario puede ser multitarea,  lo que significa
	otro nivel de complejidad. 	


Gestin de memoria en sistemas multiprogramacin
------------------------------------------------
Para  manejar  simultneamente  varias  tareas al mismo tiempo, los sistemas
multiprogramacin  ubican  varias  tareas  o procesos en la memoria al mismo 
tiempo.  Luego  ir  pasando  el  control de una tarea a otra segn lo vayan 
requiriendo las  circunstancias y de manera tal que se tenga la impresin de 
que todas las tareas corren realmente al mismo tiempo. Este proceso exige un 
mecanismo de gestin de memoria que lo facilite. Para ellos se han concebido 
dos mecanismos: segmentacin y paginacin. Uno tercero combina ambos.


*Segmentacin*
La  gestin  por  segmentacin  divide la memoria en porciones de diferentes
tamaos llamadas segmentos.  Cada  una d e estas particiones es tratada como
una  unidad lgica u objeto  que se puede referenciar por un nombre asociado
a la localidad de memoria donde se ubica, y que posee atributos que  definen 
el tipo de su contenido, su funcin y su tamao.

La segmentacin permite localizar un espacio de memoria mayor al que dispone 
la memoria real del sistema.  Esto  lo logra por reubicacin dinmica de las 
tareas.  Si  se  requiere  que se ejecute una tarea y la memoria fsica est 
ocupada,  el  sistema puede usar el espacio del segmento de una tarea que no 
est activa en ese momento o que no tenga una prioridad alta;  guardar todo 
su contexto (los valores con los que trabaja, su estado actual) y cargar el 
segmento  que  ocupa  esa  tarea por el otro que ha solicitado una localidad 
"libre".  La  tarea que ha sido temporalmente descargada,  luego  podr  ser 
repuesta, no necesariamente en la misma posicin que antes, si se solicita.

Para  la  relocalizacin dinmica se emplean direcciones con dos campos: uno 
para la direccin del segmento y otra para la ubicacin del elemento  dentro 
del segmento.  La  direccin  del  segmento vara con su reubicacin  en  la
memoria pero el otro valor en la direccin referenciada,  el desplazamiento, 
siempre es fija.  Cuando los programas son desplazados, slo hay que cambiar 
los registros de segmento para ajustarlos al cambio de lugar.  Los elementos
se localizan sumando al nuevo valor del  segmento  el  desplazamiento,  cuyo
valor es siempre el mismo.

La  reubicacin  dinmica  tambin  permite que ms de un programa o proceso 
comparta adecuadamente parte de su cdigo:  basta slo ajustar los registros 
de segmento  en las diversas tareas para que apunten al  mismo segmento  que 
ser compartido.

La segmentacin proporciona un mtodo efectivo para el manejo del espacio de 
direcciones virtuales:  se trata de un concepto ms lgico que fsico.  Pero
tiene algunos inconvenientes.  Un segmento de cdigo de un programa ser tan 
grande como el cdigo que contenga;  un  segmento  que  contenga un array de 
datos ser tan grande como lo sea dicho array. El manejo de bloques de datos 
de tamao distinto exige procedimientos ms complejos y costosos,  lo que se 
agrava  por  el hecho  de que los discos almacenan datos en bloques de igual 
tamao. Por ejemplo,  se  requiere  ms  tiempo para encontrar un espacio de 
memoria cuyo tamao se adece al del segmento que sea necesario cargar. Irn 
apareciendo huecos  no ocupados de diferentes temaos que tienden a aparecer 
entre segmentos sucesivos dando lugar a fragmentacin externa,  que  resulta 
en un manejo ineficiente de la memoria. Tambin est la situacin en las que 
slo se usa una pequea porcin de un gran segmento: como tiene que cargarse 
todo el segmento para el manejo de esta pequea porcin del segmento,  habr 
desperdicio de memoria.


*Paginacin*
La gestin  de memoria  por paginacin divide el espacio fsico en porciones 
de igual tamao llamados pginas.  Con  este  sistema  se  busca superar los 
inconvenientes de la segmentacin en  la  reubicacin dinmica de objetos en 
memoria.

Las pginas deben  cargarse desde el disco a la memoria en bloques de tamao 
constante  llamados marcos de pgina,  que  tienen  el  mismo tamao que las 
pginas. Estos marcos son alineados en el lmite de un valor igual al tamao 
de una pgina:  sus  direcciones iniciales siempre son mltiplos enteros del 
tamao  fijo  de  la  pgina.  Siempre  podr  colocarse una pgina nueva en 
cualquier marco de pgina disponible.

La  localizacin de una pgina en la memoria fsica es ms simple que hallar
un segmento.  Una  direccin  de  pgina puede dividirse en por lo menos dos
campos.  Uno  es un ndice a un marco de pgina. Para encontrar el marco con
este valor se multiplica por el tamao constante  de un marco de pgina.  El
otro valor puede ser un desplazamiento dentro del marco de pgina alelemento
refrenciado.

El  sistema  de  paginacin  tambin  permite  compartir  pginas de memoria 
declarando  algunas  pginas  de cdigo  compartibles por ms de una tarea y 
haciendo  que  las  entradas de las tablas de pgina de las dibversas tareas 
apunten al mismo marco.  Pero  compartir pginas no  es  tan  sencillo  como 
compartir segmentos:  si  se  quiere compartir un cdigo o un array de datos 
que  estn  conformados  por  ms de dos pginas, entonces hay que usar tres 
entradas separadas para cada una de las pginas del rea  compartida;  si se 
tratara de un segmento, ser suficiente una entrada simple para facilitar el 
direccionamiento del rea compartida.  Por otro lado,  aunque  la paginacin 
parece eliminar espacios vacos, ya que se ubican en los marcos disponibles, 
que son de su mismo tamao,  la  paginacin  tiende a producir fragmentacin 
interna:  algunos bloques pueden tener menos tamao que el  marco de pgina, 
quedando un hueco dentro del marco cuando se carga ese bloque.


* Segmentacin paginada *
Para superar  las desventajas  de  cada  modo de manejo de la memoria, se ha 
implementado  una  combinacin  de  ambos:  segmentacin  paginada.  En este 
esquema,  cada  segmento  es  dividido  en  pginas y es referenciado por el 
procesador  a  travs  de  una  tabla  de pginas para dicho segmento.  Como 
consecuencia,  cada  segmento tendr siempre un tamao que ser mltiplo del 
tamao  de la  pgina.  Las  pginas  de  un mismo segmento, contiguas en la 
memoria virtual, no tienen que aparecer as en la memoria real ni tienen que 
aparecer todas a la vez,  lo  que  elimina  la necesidad de reubicar todo el 
segmento cuando se necesita slo una porcin de l y solventa el problema de 
fragmentacin externa de la segmentacin.

En segmentacin  paginada la direccin virtual v consta de tres  partes:  un 
ndice s con el  nmero  de  entrada dentro de una tabla con descriptores de 
segmentos;  un  ndice  p  con  el  nmero de entrada dentro de una tabla de 
proyecciones de pginas;  un desplazamiento d en la  pgina  indicada  en la 
entrada sealada antes en la entrada en la tabla de proyeciones de pgina.

Un registro  especial del procesador contiene la direccin fsica base de la 
tabla con los descriptores de segmentos del proceso actual. El campo s de la 
direccin  virtual  especifica cul entrada de esta tabla tiene la direccin 
fsica de la base del segmento donde est nuestra tabla de  proyecciones  de 
pginas.  El campo p de la direccin virtual es un ndice que seala en cul 
entrada de esta tabla se encuentra la direccin fsica de  nuestra pgina, y 
la direccin fsica de la instruccin se obtiene a esta  direccin fsica de 
pgina el valor del campo d de la direccin virtual.



Memoria y privilegios en el Intel 80386
---------------------------------------
Si revisamos  la arquitectura y rasgos del procesador i80386,  encontraremos
claramente  que  fue pensado especialmente  para entornos multiprogramacin.
Primero,  puede correr en tres modos: real,  protegido y virtual;  es decir,
entre sus modos de funcionamiento,  hay  soporte para proteccin de memoria. 
Segundo,  su  MMU  (Memory Management  Unity:  Unidad de Manejo de  Memoria) 
posee dos subunidades,  una  que  permite segmentacin  de memoria,  llamada 
unidad de segmentacin y  que traduce direcciones  virtuales  a  direcciones
lineales,  y  otra para paginacin;  la  unidad de paginacin,  que  traduce 
direcciones lineales a fsicas o reales. Tercero,  contiene nuevos segmentos 
de sistema  que  apoyan la comutacin de un modo a  otro y la implementacin 
de los diversos modos de funcionamiento.

Como  hemos sealado,  un entorno  protegido impide interferencias entre las
tareas,  entre  objetos de una misma tarea y prohibe la ejecucin de ciertas
instrucciones  del procesador si se  piden en condiciones p rohibidas.  Para
ello  debe  comenzar  por  especifiar  la  direccin  base,  el tamao y los 
derechos  de  acceso  de  las  reas locales de cada tarea y del rea global 
compartida.  Tambin  deben  estar  especificados los objetos en cada una de
las reas.  Sobre cada rea de la memoria se esteblecen zonas con niveles de 
privilegio propios.  El nivel de privilegio es una medida para la  seguridad 
de una zona: cuanto mayor privilegio haya,  mayor la seguridad.  Al  asignar 
mayor privilegio a una zona se especifica que los objetos  ah ubicados  son 
bastante fiables, ms que los que se hallan en zonas de menor privilegio.

La  implementacin  de un entorno protegido en el 80386 divide el espacio de
memoria en cuatro niveles de privilegio (PL), desde 0 (mayor privilegio) a 3 
(menor privilegio).  El  nivel  0  se destinar por lo general a los objetos 
propios de ls istema y el nivel de  menor privilegio, el 3, se deja para los 
objetos de las aplicaciones del usuario.

Los  niveles  de  privilegio establecen una serie de reglas de acceso en las
que  luego  profundizaremos.  Ahora  nos  interesa  el tema de la gestin de 
memoria.  Uno de los beneficios de que dispone el 80386 son sus registros de
32 bits y  un apoyo en su mecanismo en la gestin de memoria que nos permite
acceder a un espacio lineal y plano de 4 GB.  Vamos a comenzar por ac,  por
el manejo de memoria en modo  protegido,  especficamente vamos a centrarnos
en la segmentacin en modo protegido. Pero antes algunas acotaciones  nuevas
sobre estructuras de datos en lenguaje ensamblador.



Estructuras en lenguaje ensamblador
-----------------------------------
Los  datos tipo  estructura son  uno de los ms importantes en programacin. 
Hasta ahora, hemos considerado los tipos de datos casi exclusivamente por su
tamao.  Al  hablar de tipos de datos,  estamos  agrupndolos  de acuerdo  a 
ciertas  caractersticas  que  no  se  reducen  exclusivamente  a su tamao:
tambin  la  funcin y el modo de acceso forman parte de las caractersticas
de un tipo de datos.  Incluso,  una coleccin de datos puede ser agrupada en
un  conjunto de acuerdo a su organizacin y a las operaciones que se definen
en ella. Cuando el criterio de clasificacin de los datos es su organizacin
decimos que se trata de una estructura  de  datos;  si adems,  se  incluyen
definiciones  de  operaciones  sobre  la   estructura   como   criterio   de 
clasificacin, entonces hablamos de clases.

Vamos a referirnos aqu a estructuras de datos,  cmo se definen en lenguaje
ensamblador.  Si  estos  tipos de datos se clasifican segn su organizacin, 
entonces estn determinados por la posicin de sus elementos.  A  este grupo
de elementos se le asigna un nombre,  que lo identifica  y  permite  acceder
a sus miembros a travs de ndices;  estos  ndices  seran  nombres  de los
elementos de la estructura.

En ensamblador, la definicin de una estructura tiene el siguiente formato:

 - TASM -
	nombre	STRUCT
		nombre_a	tamao	?
		nombre_b	tamao	?
		...
		nombre_x	tamao	?
	nombre	ENDS

Este  formato  define  una  estructura  compleja  de  datos  que  puede ser 
manipulados  como  un nico tipo de dato.  Para usar una estructura de este 
tipo,  debemos  declararla,  despus  de su definicin,  en  el segmento de
datos:

		.data
	nombre1	nombre	<?>
	nombre2 nombre	<A, B, ..., X>

Tenemos aqu datosdel tipo 'nombre':  nombre1  y nombre2;  nombre1 no tiene
datos inicializados; nombre2 si tiene datos inicializados en su definicin:
'nombre_a = A', 'nombre_b = B', ..., 'nombre_x = X'.

Para asignar valores a los miembros del tipo de variable nombre1, usamos el
siguiente formato:

	nombre_estructura.miembro_estructura

"nombre_estructura"   es  la  direccin de  la  variable  de   este   tipo; 
"miembro_estructura" es un ndice dentro de  la  estructura  la  respectivo
miembro_estructura.

Las siguiente instrucciones asignan valores a los miembros de la estructura
"nombre1":

	mov	nombre1.nombre_a, 1
	mov	nombre1.nombre_b, 2
	...
	mov	nombre1.nombre_x, N


Estructuras en NASM
-------------------
NASM no ofrece medios intrnsecos para estructuras de datos; con l se usan
macros implcitas: las macros STRUC y ENDSTRUC.

Para asignar valores a los miembros del tipo de variable nombre1, usamos el
smbolo STRUC, que toma un parmetro, el nombre del tipo de dato.  Una  vez 
suministrada  la  macro  STRUC,  se  define  la  estructura  y  los  campos  
usando  la familia  de  pseudo  instrucciones RESx, y al final se invoca la 
macro ENDSTRUC para finalizar la definicin.

Las pseudo instrucciones RESx: RESB, RESW, RESD, RESQ y  REST,  se  emplean 
para definir datos no inicializados que sern indicados  en  la seccin BSS
de un mdulo,  donde se reserva espacio en memoria,  no en el archivo, para
este tipo de datos. Cada una de estas instrtucciones almacena  un operando, 
que es el nmero de bytes, words, double words o lo que haya que  reservar. 
NASM no soporta la sintaxisMASM/TASM para reservar espacio no inicializado:
"DW ?".
El operando para la pseudo instrruccin RESBtipo es una expresin crtica: 

buffer:   resb 64                ; reserva 64 bytes 

wordvar:  resw 1                 ; reserva un espacio tamao word 
realarray resq 10                ; array de diez reales

Este  tipo  de  declaraciones  se aplica a la definicin de estructuras de
datos.  Por  ejemplo,  para  una  estructura  llamada  mytype que contenga 
miembros  dword,  word,  byte  y una cadena de bytes,  se podra escribir:

          struc mytype 
		mt_long:  resd 1 
		mt_word:  resw 1 
		mt_byte:  resb 1 
		mt_str:   resb 32 
          endstruc

que define seis smbolos:  mt_long como 0 (desplazamiento desde el comienzo
de una  estructura mytype al campo dword)  mt_word como 4,  mt_byte como 6, 
mt_str como 7, mytype_size como 39, y mytype mismo como.

Para mover un dato al segundo miembro de mytipe, debera hacer lago como:

		mov	[mytype+mt_word], 5

que movera 5 a [mytype+4], direccin del segundo miembro de mytype.

Para  que  los miembros de la estructura puedan tener los mismos nombres en 
ms de una estructura, se define la estructura anteponiendo un punto a cada
uno de sus miembros:

          struc mytype 
		.long:    resd 1 
		.word:    resw 1 
		.byte:    resb 1 
		.str:     resb 32 
          endstruc

as se definen los desplazamientos a  los  campos  de  la  estructura  como 
mytype.long, mytype.word, mytype.byte y mytype.str.

Una vez  definido  un  tipo  de  estructura,  lo  siguiente  ser  declarar 
instancias de ella  en el segmento  de datos.  Para  ello,  NASM  provee el 
mecanismo   ISTRUC.  Para  declarar  una estructura de tipo  mytype  en  un 
programa, se escribe algo como:

mystruc:  istruc mytype 
          at mt_long, dd 123456 
          at mt_word, dw 1024 
          at mt_byte, db 'x' 
          at mt_str, db 'hello, world', 13, 10, 0 
          iend

La  funcin  de  la macro AT es usar el prefijo TIMES para hacer avanzar la 
posicin  del  ensamblado  al  punto  correcto del campo especificado de la
estructura, y luego declarar los datos  especificados.  Por  lo  tanto  los
campos de la estructura deben ser declarados en el mismo orden especificado
en la definicin de la estructura.

Si  el dato que va en un campo de la estructura requiere que se especifique 
ms de una lnea, el resto de  las lneas de la fuente pueden ir despus de 
la lnea AT. Por ejemplo:

          at mt_str, db 123,134,145,156,167,178,189 
		     db 190,100,0

Se puede comenzar el campo de la estructura en la siguiente lnea:

          at mt_str 
          db 'hello, world' 
          db 13,10,0

En  NASM  no  se puede usar el siguiente cdigo para hacer referencia a los
elementos de una estructura:

	mov ax,[mystruc.mt_word] 

mt_word es una constante como cualquier otra, as que la sintaxis  correcta
es como mencionamos arriba: 

	mov ax,[mystruc+mt_word] 

o

	mov ax,[mystruc+mytype.word]

Vamos  a  tener  ocasin  de usar estos tipos de datos. Volvamos a nuestro 
estudio del modo protegido en el 80386.


Segmentacin en el 80386
------------------------
Desde el punto de vista de programacin, un segmento es un tipo de dato o de objeto
caracterizado por ser un bloque de memoria de tamao variable con informacin de la
misma clase. Los segmentos son un objeto principal en un sistema con proteccin. La 
divisin de la memoria en  segmentos ofrece  un soporte eficiente para un estilo de
programacin estructurada,  donde el  cdigo  y  los  datos  de  los  programas son 
agrupados  en  mdulos  lgicos  de  acuerdo a su importancia, funcin y calidad de 
acceso.  Un segmento puede ser concebido para que contenga datos, pueda ser ledo y
escrito, pueda ser compartido su acceso por varias tarea.  O puede ser pensado para 
que contenga cdigo, sea ejecutable y no pueda ser accedido desde otras tareas.

Los procesadores  ix86 incorporan segmentacin como principal sistema de gestin de 
memoria.  Dividen  el espacio  de un programa en por  lo menos tres segmentos:  uno
de datos, uno de cdigo y otro para la pila.

Para  localizar  o  identificar un elemento dentro del un segmento,  se emplean dos
direcciones: la base b del segmento y el desplazamiento d dentro del segmento. Para
soportar este tipo de direccionamiento,  el  80386  dispone  de  seis  registros de
segmento de  16  bits:  el registro de segmento de cdigo CS,  el  del  segmento de 
datos CS,  el de la pila SS,  y  tres  registros extra:  ES, FS y GS.  Cada vez que
se quiera acceder a un segmento, su direccin  base  debe estar en alguno de  estos
registros.  Para localizar  un elemento especfico en el segmento,  se le suma a la
base b el desplazamiento d,  cuyo valor puede estar en un registro de  uso general,
puede ser un valor inmediato o un valor en alguna localidad de la memoria. 

As que la direccin lgica de todo elemento de memoria est formada por un puntero 
de dos campos:

	Selector s: valor de 16 bits en un registro de segmento y que identifica la 
		direccin de la base del segmento.

	Desplazamiento d:  un  valor de 32 bits que se aade a la base del segmento
		para el elemento referenciado.

Esta divisin en dos campos permite dinamizar la ubicacin de los segmentos: en los
programas, los desplazamientos nunca cambian, permanecen constantes; pero cuando un
programa es cargado en memoria para su ejecucin,  no tiene por qu ser ubicado  en
una misma direccin:  la  base  puede  cambiar  de acuerdo al estado actual  de  la 
memoria. Los segmentos flotan en un espacio de memoria virtual.

Una direccin lgica es  una  localidad  de  memoria  considerada desde el punto de 
vista del programador;  por  lo  tanto  es  el  tipo  de  direcciones que genera un 
programa.  Para ubicar el dato,  el sistema debe traducir dicho valor lgico en una
localidad de la memoria fsica. Como la gestin por segmentacin ordena los bloques
en orden consecutivo,  uno  despus de otro,  el  procesador  suma  la  base  y  el 
desplazamiento para obtener un valor absoluto  conocido como direccin lineal,  que
indica la localidad de memoria fsica donde se encuentra el elemento refrenciado:

	Direccin lineal = Selector + Desplazamiento

En  modo real,  que es el modo por defecto en que corre el 80386, un segmento queda
identificado  por  su base  y su tamao,  que puede ser de hasta 64 KB.  El espacio
de  memoria  de  un s egmento  en  modo  real  est limitado por el  tamao  de los 
registros: 16 bits.

En modo protegido, el selector ya no apunta  directamente a la base de un segmento: 
ahora  es  un  ndice  dentro  de  una tabla de punteros:  el registro  de segmento 
"selecciona"  uno  de l os  punteros de dicha tabla y elpuntero  seleccionado es la 
direccin base del segmento.  El  puntero  al  segmento puede tener un tamao de 32 
bits,  as que un segmento puede ser ubicado en un espacio de memoria por encima de 
1 MB.  Y como el 80386 tiene segmentos de 32 bits, en teora,  los segmentos pueden
tener un tamao de hasta 2^32 = 4GB.

En modo protegido, los segmentos quedan caracterizados por tres parmetros:

	base: direccin lineal del comienzo del segmento

	lmite: tamao del segmento

	derechos de acceso: atributos del segmento como tipo, nivel de privilegio e 
	indicadores de estado.

Uno  puede  pensar  que  es  imposible  localizar  un segmento de digamos 1GB en un 
espacio de memoria fsica de 32 MB. Pero el 80386 implementa otro mtodo de gestin 
de memoria adems de la segmentacin que permite manejar  segmentos de este tamao: 
paginacin;  en modo protegido,  los segmentos pueden  subdividirse en bloques  del 
mismo tamao,  llamados pginas. Se implementa un mtodo de relocalizacin dinmica
parecido  al  de  la  segmentacin  para  relocalizar pginas de segmento en vez de 
segmentos completos,  segn  sea  necesario  hacer  uso de los elementos de ciertas
programas.

Cuando se paginan los segmentos, debe aplicarse un segundo nivel de traduccin:  ya
las direcciones lineales obtenidas a partir de las direcciones lgicas no coinciden
con  las de la memoria fsica y deben  ser  traducidas  para  localizar el elemento
referenciado; las direcciones lineales debern ser traducidas a direcciones fsicas
de memoria. 

El  proceso de traduccin de direcin lineal a fsica de pgina, es ms complejo en 
el 80386 que la  traduccin de memoria lgica a lineal:  la  direccin lineal de 32 
bits  es  dividida en tres campos:  directorio  de  pginas,  tabla  de  pginas  y 
desplazamiento.

      31                 22 21                 12 11                 0
     ͻ
                                                                   
              DIR                 PAGE                OFFSET       
                                                                   
     ͼ

	           - Formato de una direccin lineal -

El directorio de pginas  contiene en cada una de  sus entradas la direccin de una
tabla sde pginas.  La direccin de la base del directorio  se encuentra en uno  de 
los registros de control:  CR3.  El campo de directorio de  pginas se usa como  un 
ndice dentro del  directorio  cuya  base  est en CR3.  En la entrada indicada  se 
encuentra la base de una tabla de pginas el campo tabla de pginas de la direccin
lineal se usa como  un ndice  dentro de la tabla  de pginas  para  encontrar  una
entrada donde se encuentra la  direccin base de la  pgina donde se  encuentra  el
elemento referenciado:  el  campo  desplazamiento  de  la  direccin  lineal se usa
como ndice dentro de la pgina para encontrar el elemento.

                                                              PAGE FRAME
              ͻ         ͻ
                  DIR       PAGE      OFFSET                          
              ͼ                        
                                                                       
                                PHYSICAL   
                                                             ADDRESS    
         PAGE DIRECTORY              PAGE TABLE                         
        ͻ         ͻ                     
                                                     ͼ
                                ͹              
                              PG TBL ENTRY  
        ͹          ͹
         DIR ENTRY   Ŀ                      
         ͹                        
                                                
         ͼ         ͼ
                                          
ͻ                  
  CR3  
ͼ

		- Traduccin de direccin lneal a fsica -


* Selectores *
En  modo  protegido  llamamos  selectores a los registros de segmentos.  Uno de los 
aspectos  que  cambia en modo protegido es el par segmento:desplazamiento (offset), 
usada para direcciones lejanas: es reemplazado por par selector:desplazamiento.

Aunque  el tamao de los registros de segmento siguen siendo el mismo (16bits),  su 
implementacin  es  nueva.  Ahora  son  llamados  selectores y se usan como ndices 
dentro  de  tablas  del  sistema  llamadas GDTs (global descriptor table:  tabla de 
descriptores globales)  o  la  LDT  (local descriptor table:  tabla de descriptores 
locales). Cada una de las entradas de estas tablas consta de un record  de  64 bits 
con informacin que describe los atributos de un segmento y su localizacin.

  TABLA DE DESCRIPTORES GLOBALES          TABLA DE DESCRIPTORES LOCALES
    ͻ            ͻ
                                                            
    Ķ            Ķ
                             M                                   M
    ͼ            ͼ
    |                         |            |                         |
    |                         |            |                         |
    ͻ            ͻ
                                                            
    Ķ            Ķ
                             N + 3                               N + 3
    ͹            ͹
                                                            
    Ķ            Ķ
                             N + 2                               N + 2
    ͹            ͹
                                                            
    Ķ            Ķ
                             N + 1                               N + 1
    ͹            ͹
                                                            
    Ķ            Ķ
                             N                                   N
    ͼ            ͼ
    |                         |            |                         |
    |                         |            |                         |
    ͻ            ͻ
                                                            
    (UNUSED)Ķ            Ķ
                                                                
    ͼ            ͼ
                                                                    
     ͻ               ͻ  
              GDTR                              LDTR        
     ͼ                ͼ

			- Tablas de descriptores -



El selector de 16 bits se divide en 3 partes.

                         15                      4 3   0
                        ͻ
                                                 T   
                                   NDICE         RPL
                                                 I   
                        ͼ

                         TI  - TABLE INDICATOR: Indicador de tabla 
                         RPL - REQUESTOR'S PRIVILEGE LEVEL: nivel
			       de privilegio requerido

			    - Formato de un Selector -

  bit    descripcin

  15-3 = NDICE: desplazamiento dentro de la GDT o de la LDT

  2    = TI: est activo (en 1) si el descriptor est en una LDT, sino est en una 
	     GDT

  1-0  = RPL (requested  priviledge  level:  nivel  de  privilegio requerido):  El 
	     desplazamiento (bits 15-3) indica cual entrada, de una de las  nuevas 
	     tablas del sistema contiene,  el descriptor que indica la ubicacin y 
	     propiedades de un segmento.

Los bits 2-0 describen atributos  especiales del descriptor. Si el bit 2 (TI) est 
establecido  (est  en  1)  el descriptor se encuentra en una LDT, sino est en la 
GDT.

Los  bits  1-0  son  el  RPL,  que  describe el nivel de proteccin que amerita el
segmento. Como son dos bits, pueden haber hasta 2^2=4 niveles de privilegio, desde
cero a tres.  En el 80386,  cero es el nivel de privilegio  ms alto,  tres el ms
bajo. El nivel  ms  alto se reserva a los segmentos que contengan cdigos o datos
ms seguros y esenciales para el funcionamiento del sistema.

Los bits 15-3 sealan cul de las entradas contiene el descriptor del segmento que
buscamos. Como cada descriptor tiene un tamao de 8  bytes,  el  NDICE  debe  ser
multiplicado por 8 para hallar el descriptor.

Los  selectores,  en conjuncin con otros registros especiales, permiten localizar 
la ubicacin de los descriptores de segmento.


           15              0    31                                   0
  DIR.    ͻ   ͻ
  LGICA      SELECTOR                  DESPLAZAMIENTO            
          ͼ   ͼ
                                          
        TABLA DESCRIPTORA                          
         ͻ                           
                                                
                                                
                                                
                                                
         ͹                           
          DESCRIPTOR  BASE          ͻ       
       DE SEGMENTO   + 
          ͹ DIRECCIN     ͼ
                                       
          ͼ                 
                                         
              DIR.    ͻ
              LINEAL      DIR        PAG.    DESPLAZAMIENTO
                      ͼ

	       - Traduccin de direccin lgica a fsica -
 

La  base  de la tabla descriptores se encuentra en un registro especial del 80386; 
la de la GDT se encuentra en el registro GDTR y la  de  la  LDT  en  el  LDTR.  El 
procesador usa el ndice del selector para  ubicar  la  entrada  de  la  tabla  de 
descriptores cuya direccin base est en el GDTR o en LDTR del caso.



* Descriptores *
Los descriptores son una  estructura  con  informacin  sobre  los  segmentos  que 
maneja el procesador.  Estos descriptores constituyen las entradas de unas  tablas 
que el sistema ubica en  la memoria para  facilitar  el  manejo  dinmico  de  los 
segmentos.

Ya  dijimos  que  estas  tablas  se  llaman  Tablas  Descriptoras. Cada una de las 
entradas de estas tablas, los llamados descriptores,  tienen un  tamao de 8 bytes 
(64 bits). La informacin que contiene un descriptor incluye la ubicacin y tamao 
del segmento, su nivel de privilegio y el modo permitido de acceso.

Existen dos tipos de tablas de descriptores:  globales  y  locales.  Las  primeras 
contienen informacin sobre segmentos con informacin comn para todas las tareas. 
Las locales incluyen informacin sobre el rea de memoria privada de una tarea.

La GDT y la LDT pueden mantener hasta 8192 decriptores de 64 bits cada uno, debido 
a que contamos con  los  bits  15-3  del  selector  para  direccionarlos.  El 1er. 
descriptor en la GDT es reservado y nunca puede ser usado  para  nada  (es NULO). 
Cada estructura de descriptor en las tablas es como a continuacin:

	descriptor struct
	  limit_lo 	dw ?    ; bits de lmite 15-0
	  base_lo 	dw ?    ; bits de base 15-0
	  base_mid 	db ?    ; bits de base 23-16
	  type1 	db ?    ; tipo de selector
	  limit_hi 	db ?    ; bits de lmite 19-16 y otra informacin
	  base_hi 	db ?    ; bits de base 31-24
	descriptor ends

A continuacin el formato general de un descriptor de segmento:

                                           Atributos
                                        (23-20)(15-8)
                                         /            \
                                       /                \
           31            24-23       /   20-19    16-15   \            8-7        0
 Direccin   -------------- --- --- --- --- -------- --- --- --- --- --- ---------
    n+4	    |    base_hi   | G |D/B| 0 |AVL|limit_hi| P |DPL| S |tip| A |base_mid |
            |    (31-24)   |               |(16-19) |                   | (23-16) |
 Direccin   -------------- --------------- -------- ------------------- ---------
     n      |           base_lo            |                limit_lo              |
            |            (15-0)            |                  (15-0)              |
             ------------------------------ --------------------------------------
          

Cuando todos los bits de base (bits 15-25; 60-63) son puestos juntos,   forman un 
valor de 32 bits que representa el comienzo del segmento en la  memoria.  Con una 
direccin  de 32  bits  puede  accederse  a  4GB  de  memoria.  El campo limit_hi 
se reparte as:

	bit #  		Nombre 	Descripcin
	   0-3      	lmite	bits altos de lmite 19-16
	   4         	AVL    	disponible slo para el programador
	   5         	-      	reservado (debe ser 0)
	   6         	D      	tamao por defecto del segmento
	   7         	G      	granularidad

El lmite total es 20 bits (que permite un acceso de hasta un 1MB) pero si el bit 
G bit est activo (1) entonces  el lmite  es  multiplicado  por 4096  (4kb)  que 
establece ahora un mximo de cuatro gigabytes (4 GBs).  El bit D define el tamao 
por defecto del cdigo de segmento,  si  est  establecido es de 32bit sino es de 
16 bits.

El campo type1 de la estructura del descriptor define ms atributos del segmento.

bit # 		Nombre  Descripcin
   4       	  S     Define el  tipo de descriptor.  Si  S=1  entonces  es  un 
			segmento  estndar  de  cdigo/datos  y  el  resto  de la 
			estructura es como sigue:

   3       	  T     define  si  se trata de un segmento de cdigo o de datos.
			Si T=0,  es un segmento estndar de datos y el  resto  de 
			la estructura es como sigue:
   0       	  A     Accedido
   1       	  W     escribible
   2       	  E     Expandido

 			pero si T=1, un segmento de cdigo  y el resto de la 
			estructura es como sigue:
   0       	  A     Accedido
   1       	  R     Leble
   2       	  C     Conformante

 			Ahora, si S=0, se trata de un descriptor de sistema y el
			resto de la estructura es como sigue:

   0-3		TYPE	Define el tipo (LDT, puerta INT o de trampa, etc.)

			El resto ya no depende de T ni de S:

   5-6		DPL	Nivel de privilegio del descriptor (descriptor privledge 
			level)
   7    	P     	presente


Esto parece muy complejo. He aqu lo que los campos pueden significar:

S (bit4) :		Define si es un segmento de cdigo/datos o un descriptor 
			especial.

T (bit3) :		Define si es un segmento de cdigo o de datos

A (bit0) :		Este bit es puesto por el CPU cada vez que este segmento 
			es para programar (bit1):  
			Define si los segmentos de cdigo pueden ser  ledos, lo 
			que permitira instrucciones como:  mov ax, cs:[ebx]

W (bit1) :		Este  bit  define  si  los segmentos de datos pueden ser 
			escritos.

C (bit2) :		Lo explicaremos luego.

E (bit2) :		Define si el segmento de datos de expansin descendente.

DPL (bits5-6) :   	nivel de privilegio del descriptor (descriptor privledge 
			level)

P (bit7) :              Siest  activo  el descrptor est presente (vlido) sino 
			slo  el  byte  del  tipo de descriptor debe mantener la 
			informacin y el resto es ignorado por el CPU, cargar un 
			selector nopresente es invlido.  



Campos de proteccin de los descriptores de segmento

                      DESCRIPTOR DE SEGMENTO DE DATOS

  31                23                15                7               0
 ͻ
 A LMITE         TIPO   
 BASE 31..24GB0V 19..16  P DPL          BASE 23..16 4
 L              10EWA
 Ķ
                                    
 BASE DEL SEGMENTO 15..0     LMITE DEL SEGMENTO 15..0      0
                                    
 ͼ



                      DESCRIPTOR DE SEGMENTO EXECUTABLE

  31                23                15                7               0
 ͻ
 A LIMIT          TIPO   
 BASE 31..24GD0V 19..16  P DPL          BASE 23..16 4
 L              10CRA
 Ķ
                                    
 BASE DEL SEGMENTO 15..0     LMITE DEL SEGMENTO 15..0      0
                                    
 ͼ



                      DESCRIPTOR DE SEGMENTO DE SISTEMA

  31                23                15                7               0
 ͻ
 A LMITE               
 BASE 31..24GX0V 19..16  P DPL 0 TIPO  BASE 23..16 4
 L                      
 Ķ
                                    
 BASE DEL SEGMENTO 15..0     LMITE DEL SEGMENTO 15..0      0
                                    
 ͼ


Notas:
El LMITE de un segmento define el valor  mximo al que un  desplazamiento en  el 
segmento puede ser accedido (mov al,[1000h] cuando LIMITE=9999h es invlido).

La BASE de un segmento define en qu lugar de la memoria comienza (mov al,[1000h] 
cuando BASE=10000h acceder a la RAM en la direccin 10000h+1000h=11000h).

Los segmentos de datos pueden ser ledos desde:

	SS debe ser cargado con un selector de datos que debe ser para escritura.
	CS slo puede ser cargado con un selector de cdigo.
	DS, ES, FS, GS cpuede ser cargado con un selector datos o de cdigo.

Si E (expand down) est activo, cambia la interpretacin del LMITE: ahora LMITE 
define la direccin ms baja que puede ser accedida desde el  segmento.  Debido a 
que los segmentos de pila se extienden hacia direcciones inferiores,  respecto  a 
la base, el  LMITE puede ser reducido para dar ms espacio a la pila.

La LDT es la tabla de descriptores locales (local descriptor table):  es lo mismo 
que la GDT,  pero con la excepcin de que cada tarea tiene su propia LDT y la GDT 
es compartida por todo el sistema.  La  LDT es simplemente una entrada en la GDT, 
que describe donde est esta LDT.

La LDT puede contener cualquier cosa que pueda contener una GDT. 

Si  se  necesita  usar  un  selector  que est en la LDT, entonces el bit 2 de un 
selector debe activarse y el descriptor vendr de la LDT.

Cualquiera  de  las  reglas  de  arriba pueden romperse cuando ocurren INTs (como 
cuando se dispara una IRQ).  Este  tipo de interrupciones se llaman excepciones y 
sern detalladas ms abajo.


* Interrupciones *
El antiguo vector de interrupciones usado en modo real,  que ocupaba la localidad 
0:0 de la memoria, ya no es usado en modo protegido.  Ahora hay un nuevo conjunto 
de hasta 256 interrupciones posibles que  se acceden de forma parecida  a como se
acceden los segmentos va la GDT:  a travs de una tabla de descriptores que,  en
este  caso,  contiene  punteros descriptores a los servicios o manejadores de las
interrupciones; pero ahora sera una tabla  con  descriptores  de interrupciones, 
conocida como la IDT (Interrupt Descriptor Table):  las  interrupciones  en  modo 
protegido ya no estn fijadas a una ubicacin nica, la localidad 0 de la memoria 
fsica, sino que ahora pueden flotar en el espacio de memoria lineal con absoluta 
libertad. 

El formato  de una  entrada de la IDT es un poco diferente al de un descriptor de 
la GDT:                                        
                                    
           31                            16-15                8-7         0
 Direccin   ------------------------------ --- --- --- --- --- ----------
    n+4	    | Desplazamiento (offset16_31) | P |DPL| S |tip|000|zero_byte |
            |                              |       (flags)     | (23-16)  |
 Direccin   ------------------------------ ------------------------------
     n      |    Selector (selector0_1)    |  Desplazamiento (offset0_15) |
            |            (31-16)           |              (15-0)          |
             ------------------------------ ------------------------------
          
En ensamblador:

	interrupt_descriptor struc
	  offset0_15	dw	?	; palabra baja del despl. del manejador
	  selector0_15	dw	?	; selector del segmento
	  zero_byte	db	0	; sinuso en este formato
	  flags		db	?	; byte de banderas
	  offset16_31	dw	?	; palabra alta del despl. del manejador
	interrupt_descriptor ends 

Cada  entrada de la IDT, sus descriptores,  son llamados descriptores de puerta 
porque permiten son una  especie de va de acceso  controlada a servicios a los
que debera ser imposible acceder por otras vas. Pueden ser de tres tipos:

	 Puerta  de  interrupcin:  transfieren  el  control a un manejador de
	  interrupcin con interrupciones deshabiitadas.

	 Puerta   de   trampa:   transfieren  el control  a  un  manejador  de
	  interrupcin sin cambiar la badera IF.

	 Puertas de tareas: permiten pasar el control de una tarea a otra.

Las  puertas  de llamada,  como tambin se conoce a los descriptores de puerta, 
permiten acceso a los puntos de entrada de un segmento de cdigo de mayor nivel 
de privilegio, sin detrimento del nivel de seguridad.

Los  manejadores de interrupciones son los servicios que ofrece el sistema.  Es 
lgico que estn protegidos y ubicados en un nivel de privilegio ms alto.  Por 
ese motivo, los programas de aplicacin,  que corren en un nivel de  privilegio 
menor, acceden a esos servicios a traves de puertas de llamada.

La  direccin lineal de una IDT se ubica en un registro especial del procesador 
llamado IDTR. Se pone ah usando la instruccin LIDT (Load IDT). 

	* NOTA *
	Windows ofrece  servicios  alternativos  a los del BIOS y a los de DOS,
	escritos especialmente para modo protegido. Cuando un programa los usa,
	el CPU revisa la IDT  (no  la  tabla del vector de interrupciones, IVT: 
	Interrupt Vector Table) para obtener su direccin.

	En algunos caos,  la ejecucin va a los servicios originales del BIOS o
	del DOS.  En estos casos,  la entrada de la IDT apunta a un manipulador
	(handler)  especial  que  adems  de cambiar el CPU a  modo real,  debe
	convertir todos los punteros del selector a un valor de segmento. Luego
	el manipulador revisar en la IVT para obtener la direccin del servico
	en modo real.

Para  hallar el manipulador (handler) de una int en modo protegido, por ejemplo 
de la int 80h, se multiplica el  nmero de la  interrupcin  (80h)  por  8  (el 
tamao de un descriptor) y ya tenemos la ubicacin del descriptor en la IDT (la 
ubicacin de la IDT est en el IDTR).

El descriptor ubicado,  cuya estructura expusimos arriba,  es del tipo  puerta.  
Hay un campo de 16 bits para un selector,  que se  carga en CS cuando se activa 
la interrupcin, y designa un segmento de cdigo; mejor dicho, "selecciona" una 
entrada en la GDT donde est  un descriptor de segmento de cdigo que indica la 
direccin del handler de la interrupcin. Al segmento obtenido,  hay que aadir 
el desplazamiento (offset) en el descriptor de la IDT,  y con ello obtenemos la 
direccin lineal de la entrada al handler de la interrupcin.

Las primeras 32 interrupciones de la IDT se reservan para excepciones,  especie
de interrupciones que son llamadas por el procesador para sealar  eventos como 
una ejecucin de cdigo invlida,  intento de acceso ms all del lmite fijado 
en el campo LIMIT de un descriptor, o cualquier otro evento que no sea vlido.


* Excepciones *
Como ya hemos dicho  una excepcin o falta es una especie de sea que genera el
el procesador cuando ocurre algo errneo.  En  realidad  son  manipuladores  de
interrupciones que se  acceden desde  la interrupcin 0 a la 01Fh (32).  La ms
popular de las excepciones es la 13h. Esta excepcin manipula la mayora de los
errores de programacin:

	 carga  del  CS  (con JMP o CALL)  con  un descriptor que no sea de un 
	  segmento de cdigo.

	 escritura en un segmento de cdigo

	 carga de SS con  un descriptor que no sea de un segmento de datos que 
	  acepte escritura.

	 acceso a un segmento por encima de su lmite.

	 intento de cambiar a modo protegido directamente,  mientras  se corre 
	  en modo V86.

Todava hay muchas ms:

  INT : DESCRIPCIN

   0    divisin por cero: llamada cada vez que una instruccin div/idiv intenta 
	dividir entre 0

   1    excepcin de depuracin: 

   2    NMI, interrupcin no enmascarable (Non-Maskable Interrupt):  llamada por
	cualquier dispositivo de hardware que haya fallado; es esencial para los
	sistemas de operacin (es decir: falla de paridad sobre la RAM)

   3    punto de ruptura (breakpoint): llamada cada vez que ocurre la INT3, cuyo
	cdigo de operacin es 0CCh). Este byte de cdigo de operacin puede ser
	insertado "manualmente" en un cdigo para propsitos de depuracin.

   4    desbordamiento (overflow): llamada cuando se ejecuta la instruccin INTO 
	y se activa la bandera de desbordamiento OF.

   5    chequeo de lmites (bounds check):  ocurre  si se ejecuta la instruccin 
	BOUND y el operando supera el lmite establecido.

   6    cdigo invlido: llamada cuando no es vlida la siguiente instruccin en 
	una direccin CS:(E)IP no es una instruccin 80x86 vlida.  Esto tambin
	podra llamarse si la instruccin es mayor a los 15 bytes que puede 
	ocurrir si se usan muchos prefijos.

   7    dipositivo no disponible: usada generalmente para optimizar el uso de la
	FPU.

   8    doble falta:  generada  si ocurre una excepcin mientras el sistema est 
	procesando ya otra.  Naturalemente,  esto puede ocurrir, pero el sistema
	debe  ser muy  cuidadoso ahora  porque una tercera excepcin har que el
	procesador se reinicie.

   9    80486+ = reservado (80386- = co-pro segment overrun):  en el 80486, esta 
	excepcin es nueva. En el 80386 seala un error de la FPU.

  10    TSS (task state segment) invlido:  se  genera  siempre que se carga una 
	tarea invlida.

  11    segmento no presente: se produce si se usa un descriptor que no tiene el 
	bit P (bit de presencia) activo. Esto significa que el segmento no  est 
	presente en ese momento en la memoria.  Se usa generalmente en el manejo
	de la memoria virtual.

  12    excepcin de la pila: esto puede ocurrir de dos maneras: a) cuando SS es 
	cargado con un descriptor que no est presente; b) cuando el lmte de la 
	pila es sobrepasado o una instruccin  trata de acceder a un operando en 
	el segmento de pila (por ejemplo: mov al,ss:[ebx])

  13    fallo de proteccin general:  si ocurre algo invlido que no pertenece a
	la categora de las otras excepciones entonces se produce esta excepcin.


  14    fallo de pgina: usada en el sistema de paginacin para indicar que se ha 
	solicitado un  elemento ubicado en una pgina que no  est cargada en ese
	momento en la memoria.

  15    reservado

  16    error del FPU:  ocurre  cuando la FPU detecta un error en una instruccin 
	que est ejecutando.

  17    chequeo de alineacin: es una innovacin qgregada en el i80486 que obliga
	al programador a alinear los datos.  Se  produce  cuando  se  detecta una 
	referencia a una direccin de memoria que est desalineada. Esto activar
	el bit 18, agregado al registro de banderas, el bit AC.

  18-31 excepciones reservadas

  32-255  disponible para el software

Como  puede  observarse,  las  interrupciones  normales ya no son las mismas; por 
ejemplo, la IRQ#5 genera la INT13,  la  misma que la falla de proteccin general. 
Ahora, en el 386+  el PIC (programable interrupt controller) puede ser proyectado 
de nuevo para establecer las IRQs en localizaciones diferentes dentro de la tabla 
de interrupciones.  Los  antiguos   extensores  DOS  y  los  sistemas  operativos 
acostumbraban hacer esto, mientras estaban en modo protegido,  pero resultaba muy 
lento: cada vez que el extensor DOS deba regresar a modo real,  por alguna razn 
el PIC tena que ser reprogramado en el estilo tradicional: 

IRQ#0 = INT#8 e IRQ#8 = INT #70h.  Cuando el control regresaba al modo protegido, 
nuevamente tena que reprogramarse el PIC.  Esto ya no es as en los extensores y 
OSs  modernos.

Entonces cmo un manipulador de IRQ determina si un evento que ha  disparado una 
INT viene de una IRQ, de una INT de usuario o de una excepcin?

Es muy simple.  Se revisan los PICs para determinar si esperan por alguna IRQ. Si 
es as, salta al manejador de IRQ.  Luego chequea dentro del segmento de cdigo y 
ve si la ltima operacin ejecutada fue la INTx (donde x es el actual manipulador 
de INT).  Si  es  el caso,  entonces  el servicio  que  llama la INT es el que le 
corresponde;  por  ejemplo  la INT 10h es es la misma que le de una excepcin por 
error  de  la  FPU  y si el programa ejecut la INT 10h para llamar los servicios 
de  video se debera pasar el control al manipulador de la INT 10h.  Si estos dos 
casos fueran falsos, entonces la INT debi haber sido causada por una excepcin.

Ahora el problema es que toda IRQ toma un tiempo adicional antes de que el 
procesador tome el control.


* Conmutacin de Tareas *
La conmutacin de tareas es la base de la concurrencia en  sistemas  multitareas.
En estos sistemas, el procesador maneja varias tareas al  mismo  tiempo.  Como el
procesador no puede hacerlo simultneamente entonces reparte su tiempo de trabajo
entre las tareas activas siguiendo algn criterio de planificacin. 
 
Para cambiar de una tarea a otra,  lo que se hace es usar una nueva LDT,  lo cual 
supone que el procesador revise la GDT y obtenga la direccin de la nueva LDT.

El estado de la tarea abandonada debe ser guardado y el de la tarea entrante debe 
restaurarse:  el  estado consiste en el contenido de los registros del procesador
y del coprocesador, adems de varios valores en variables y punteros a memoria.

El CPU tiene que mantener un segmento  especial para cada tarea llamado  Segmento 
del Estado de la Tarea (TSS: Task State Segment ), donde coloca lo necesario para
el cambio.  El  CPU  lleva  una  cuenta de donde estn los TSSs, as que mantiene 
descriptores para el TSSs en la GDT:  la  GDT no  mantiene slo descriptores para 
las LDTs.

Ya  profundizaremos  sobre  la  planificacin  de  tareas  en  sistemas de tiempo 
compartido.


* Nuevas instrucciones *
Instruccin                     Descripcin                              Modo

LGDT S      Load Global Descriptor Table Register. Cargar el registro	Ambos
	    de la tabla de descriptores globales.  S  especififica la       
            localidad  de  memoria  que  contiene el primero de los 6 
	    bytes a ser cargado en el GDTR.

SGDT D      Store the global descriptor table register. Almacenar el	Ambos
	    registro   de  la  tabla  de  descriptores  globales.  D 
	    especifica  la localidad  de  memoria  que  obtendr  el
            primero de los 6 bytes a ser almacenado en el GDTR.

LIDT S      Load the interrupt descriptor table register.  Cargar el 	Ambos
	    registro de la tabla de descriptores de  interrupciones.
	    S  especififica la localidad  de  memoria  que  contiene 
	    el primero de los 6 bytes a ser cargado en el IDTR.

SIDT D      Store the interrupt descriptor table register. Almacenar 	Ambos
	    el registro de la tabla de descriptores  interrupciones.   
	    D especifica la localidad  de  memoria  que  obtendr el
            primero de los 6 bytes a ser almacenado en el IDTR.


LMSW S      Load the machine status word. Carga la palabra de estado	Ambos
	    de la mquina. S especifica la palabra a ser cargada  en
	    MSW. Es la instruccin que se usaba para cambiar de un
	    modo a otro; con el 80386 cay en desuso. 

SMSW D      Store the machine status word.  Almacenar la palabra  de	Ambos
	    estado de la mquina.  D es  un  operando que especifica
            la localizacin de la palabra o  registro  donde  el MSW 
	    debe ser salvado.

LLDT S      Load the  local  descriptor  table  register.  Cargar  el	Protegido
	    registro de la tabla de descriptores  locales.  S  indica  
	    el operando  que especifica  una palabra a ser cargada en 
	    la LDTR.

SLDT D      Store the local descriptor table  register.  Almacenar el	Protegido
	    registro  de la tabla de descriptores locales.  D  es  un
	    operando  que  specifica  la  localizacin  de la palabra 
	    donde la LDTR ser salvada.

LTR S       Load the task  register.  Cargar el registro de tareas. S	Protegido 
	    es un operando que especifica  una palabra a ser  cargada 
	    en el registro de tarea (TR: Task Register).

STR D       Store the task register. Almacenar el registro de tareas.	Protegido
	    D  es  un  operando  que  specifica la localizacin de la 
	    palabra donde ser almacenado el TR.

LAR D,S     Load access rights byte.  Cargar  el  byte de derechos de	Protegido 
	    acceso.  S especifica el selector para el descriptor cuyo
	    byte  de  acceso  es  cargado  en  el  byte  superior del 
            operando  de  D.  El  byte  bajo  especificado  por  D es 
	    limpiado.  La bandera  de cero ZF es activada si la carga
	    se completa exitosamente; sino es limpiada.

LSL R16,S   Load segment limit.  Cargar  el  lmite  del  segmento. S	Protegido 
	    especifica  el  selector para el descriptor cuya  palabra        
	    es cargada en el *word register operand R16*. La bandera
	    de   cero  ZF  es   activada  si  la  carga  se  completa 
	    exitosamente; sino es limpiada.

ARPL D,R16  Adjust RPL field of the selector.  Ajuste  del  campo RPL 	Protegido
	    del selector.  D especifica un selector cuyo campo RPL es
	    incrementado  para  que  coincida  con el campo PRL en el
            registro.   La  bandera  de  cero ZF  es  activada  si es
	    exitoso el ajuste; en otro caso, es limpiada.

VERR S      Verify  read  access.  Verificar  acceso  de  lectura.  S 	Protegido
            especifica  el selector para el segmento a ser verificado 
            para operacin de lectura. Si es satisfactorio, se activa
	    la bandera de cero ZF; en otro caso es limpiada.

VERW S      Verify write access.  Verificar  acceso  de  estritura. S 	Protegido
	    especifica  el selector para el segmento a ser verificado 
            para  operacin  de  escritura.  Si es satisfactorio,  se 
	    activa la bandera de cero ZF; en otro caso es limpiada.

CLTS        Limpiar la bandera de cambio de tarea.                     Protegido


A continuacin algunos ejemplos:

1.                LGDT [INIT_GDTR]

Carga  GDTR con la base y lmite apuntado por la direccin  INIT_GDTR para  crear 
una GDT en memoria. Esta instruccin es para ser usada durante la  inicializacin
del sistema y antes de cambiar el 80386 a modo protegido.

Una vez  cargado el  contenido actual de la GDTR  puede  ser  salvada  en memoria 
ejecutando la instruccin SGDT:


2.                SGDT [SAVE_GDTR]


3. Las instrucciones LMSW y SMSW se usan para cargar  y almacenar el contenido de 
   la palabra de estado de la mquina MSW, respectivamente. Hay instrucciones que 
   son usadas para cambiar el 80386 de modo real  a modo  protegido.  Para  hacer
   esto debemos poner el bit menos significante en el MSW  a  1.  Esto  se  puede
   hacer leyendo primero el contenido de la MSW, modificando el LSB (PE), y luego 
   escribiendo  el valor devuelta a la parte MSW del CR0.  La siguiente secuencia 
   cambia un 80386 que opera en modo real a modo protegido:

                SMSW AX  ;read from the MSW
                OR AX,1  ;modify the PE bit
                LMSW AX  ;write to the MSW

   Un secuencia equivalente es:

		mov	ax, cr0
		or	ax, 1
		mov	cr0, ax

   Esta  ltima  secuencia  es  la  que debe ser usada;  la anterior se considera 
   obsoleta.


* Instrucciones Privilegiadas *
El procesador 386 se ejecuta en uno de cuatro niveles de privilegio definidos por 
el  nivel  CPL  en  el  registro EFLAGS.   En  el nivel 0,  se permiten todas las 
instrucciones. Cuando se ejecuta en cualquier  otro nivel  (1-3),  las siguientes 
instrucciones causan un fallo o excepcin de proteccin general:

 SGDT  SIDT  STR  SLDGT

 LGDT  LIDT  LTR  LLDGT

 ARPL  LAR   LSL

 VERR  VERW

 LMSW

 SMSW


Adems,  si  se usa la LDT,  cada tarea  es asignada a  un bitmap  que representa 
cuales puertos E/S pueden ser accedidos.  Las instrucciones  E/S para los puertos 
que no estn permitidos tambin causan excepciones de proteccin general.


Instrucciones para la gestin de Entrada/Salida
-----------------------------------------------
Para comunicarse con ciertos dispositivos del sistema --como el temporizador,  el 
puerto de impresin, la tarjeta de sonido, el controlador de disco o de DMA, etc.
--  el procesador hace uso  de unos canales asignados en la memoria para ello que 
se llaman puertos de hardware. Cada uno de estos canales se localiza o identifica 
con un valor entero y funcionan como una especie de  ndice  o  puntero  a  algn 
registro de los dispositivos con los que se quiere establecer la  operacin  E/S. 
Estos registros  pueden ser de Entrada  (lectura) o de Salida  (escritura).  Para 
escribir en los registros de algn dispositivo,  habr que indicar el nmero  del 
puerto de salida pertinente y la orden o el  dato que va a  pasrsele.  Lo  mismo 
para leer un dato de un registro de lectura de un dispositivo:  debe indicarse el 
nmero del puerto de entrada correspondiente y el almacn donde debe ser devuelto 
el valor ledo.

Las principales instrucciones del 8086 para la gestin de los puertos de entrada/
salida del sistema son: IN y OUT, respecivamente.

El formato para la instruccin IN es:

	IN registro_acumulador, puerto

y dice al procesador que ponga en AL,  AX  o  EAX,  dependiendo  del  tamao  del 
operando, el valor en el puerto indicado por el segundo operando,  que  puede ser 
un valor inmediato o un valor en el registro DL, DX o EDX.  El  uso  del registro 
DL, DX, o EDX, permite un uso dinmico de la instruccin.

El formato para la instruccin OUT es:

	OUT puerto, registro_acumulador

y ordena que se enve al puerto indicado por el primer operando, el contenido en
AL, AX o EAX, dependiendo del tamao del operando. De nuevo, el puerto puede ser
indicado por un valor inmediato o usando el registro DL, DX o EDX.

Algunas direcciones de puertos bien conocidas son:

	020h - 023h	Registro de la mscara de interrupcin

	040h - 043h	Temporizador/contador

	060h		Entrada del teclado

	061h		Varias funciones: salida del teclado,  bocina (bits 0 y
			1), etc.

	064h		Estado del buffer del teclado

	200h - 20Fh	Controlador de juego - Interface MIDI

	2F8h - 2FFh	Puerto serial COM2

	3C0h - 3CFh	EGA/VGA

	3F0h - 3F7h	Contrilador de disco

	3F8h - 3FFh	Puerto serial COM1

 - Un ejemplo -
Un  ejemplo  rpido es la siguiente rutina que usa el puerto 61h para producir un
sonido con la bocina del PC.  Para ello,  usamos dos dispositivos especficos del
PC:  la  Interfaz  Programable  de  Perifricos  y el Temporizador Programable de
Intervalo.

; -----------------------------------------------------------------------
  TITLE BOCINA.ASM
; -----------------------------------------------------------------------

	.model tiny
	.code
	org	100h
main:
	cli

	in	al, 61h		;  Salvar el byte de informacin en el
	push	ax		; puerto 61h.
	cli

	mov	cx, 100h
	call	sound		;  Llamar a la rutina que hace sonar
				; el parlante.

        in      al, 61h         ; Desactivar el parlante  
        and     al, 11111100b   ; 0FCh desactiva el parlante
        out     61h, al         ; enviarlo al puerto

	pop	ax		;  Restablecer el byte de informacin
	out	61h, al		; original que estaba en el puerto 61h.
	sti
	int	20h		;  Salir a DOS.

; -----------------------------------------------------------------------

sound:
 set_ppi:
	mov     al, 10110110b   ; canal 2
        out     43h, al         ; operacin y modo 3
 set_freq:
        mov     ax, 2711      	; frecuencia en ax: 1.193.180 / 400
        out     42h, al         ; enviar byte bajo primero
        mov     al, ah          ; colocar byte alto en al
        out     42h, al		; y enviarlo 
 active_spk:
        or      al, 00000011b   ; bit 0 y bit 1 activos
        out     61h, al         ; y excitar el parlante con el PPI 
 clean_counter:
	mov	ah, 1		
	mov	cx, 0
	mov	dx, cx
	int	1Ah
 delay_it:
	mov	ah, 0
        int     1Ah             ; leer de nuevo
        cmp     dl, 10          ; y comparar con el conteo de retardo
        jne     delay_it        ; si no es igual, seguir esperando
	ret

end	main

; -----------------------------------------------------------------------


Analicemos el programa. Lo primero que hace es obtener un byte de la informacin
que se encuentra en el puerto 61h y guardarla en la pila:

	in	al, 61h
	push	ax

Por qu? porque luego va a llamrase a una rutina

	call	sound

que seguramente va a cambiar la informacin original en el registro que comunica 
con el puerto 61h.

Con cul dispositivo comunica el puerto 61h? Con uno de los registros E/S de la
Interfaz Programable de Perifricos,  PPI (Programmable Peripherical Interface),
un dispositivo programable de E/S general.  Dispone de tres registros E/S, a los 
que pueden accederse a travs de tres puertos:  60h,  61h y 62h. Podramos decir
que el puerto 61h comunica con el registro B de E/S.  Un cuarto registro,  el de
control, se accedera a travs del puerto 63h.

La PPI se  usa  para almacenar los datos que llegan del teclado (puerto A, 60h),  
para  leer  la  configuracin del ordenador en los conmutadores de la placa base
(puerto C, 62h) y para controlar el altavoz (puerto B, 61h).

Dado que vamos a trabajar con el altavoz del sistema, usamos el puerto 61h.

Ahora  revisemos la rutina "sound".  Primero ponemos en el registro DX el valor
que determina la duracin del sonido:

	sound:
		mov	dx, duracion

Antes  de excitar el altavoz con el PPI,  debemos configurar el  PPI.  Para  no
profundizar mucho, digamos que basta con enviar 10110110b (0B6h=182) al  puerto
43h, que es el puerto donde debe enviarse la palabra de control  que  configura
el temporizador. La frecuencia que ser generada se establece enviando un valor 
de retardo apropiado al puerto 42h; este valor es:  1.193.180 / freq,  donde la 
constante 1.193.180 es la frecuencia de la seal  generada  por  el  reloj  que 
excita el PPI y "freq" es la frecuencia que deseamos genere  el  PPI.  Como  el 
puerto 42h es un registro de 8 bits y el dato que indica la frecuencia es de 16 
bits, debe usarse dos instrucciones OUT; al hacer esto primero debe enviarse el 
byte menos significante (el bajo) y luego el ms significante (el alto).

sound:
 set_ppi:
	mov     al, 10110110b   ; canal 2
        out     43h, al         ; operacin y modo 3
 set_freq:
        mov     ax, 2711      	; frecuencia en ax: 2711 = 1.193.180 / 400
        out     42h, al         ; enviar byte bajo primero
        mov     al, ah          ; colocar byte alto en AL
        out     42h, al		; y enviarlo 

Para  que  el PPI  excite el parlante, se activa el bit 1 del puerto 61h,  que
habilita el PPI, y el bit 0 tambin del puierto 61h,  que comunica  la  salida
del PPI con el parlante. Los otros bits del  puerto  61h  tienen  otros  usos,
as que no deben modificarse;  esta  es  la  razn  por  la  cual  salvamos el 
contenido original del puerto 61h y nunca establecemos los nuevos bits  usando
la instruccin MOV, sino la intruccin OR.

 active_spk:
        or      al, 00000011b   ; bit 0 y bit 1 activos
        out     61h, al         ; y excitar el parlante con el PPI

La instruccin  OR  es  un operador lgico que compara el  contenido  de  los
operandos, bit por bit; si uno de los bits del segundo operando es 1,  el bit
correspondiente en el primer operando es puesto en 1. Por ejemplo:

	mov	al, 01010101
	mov	bl, 10100011
	or	al, bl		; AL = 11110111
 

Luego incluimos cdigo para mantener el tono. Pudimos haber usado una  rutina
simple como la siguiente,  que no merece explicacin,  ya que lo que hace  es
realizar gran cantidad de bucles:

        mov     bx, 25
pause1:
        mov     cx, 65535
pause2:
        dec     cx
        jne     pause2
        dec     bx
        jne     pause1

pero  esto  hara  que  la  duracin  del  tono  variara con la diferencia de 
velocidad del procesador.  Por  ese  motivo  hemos usado el reloj del sistema
como cronmetro. La interrupcin 1Ah permite leer y establecer este reloj. Si 
en AH hay un cero,  la interrupcin devuelve la parte alta del contador en CX 
y  la  parte baja en DX;  si colocamos uno en AH, podemos establecer la parte 
del contador pasando este valor en CX y la parte a travs de DX:

 clean_counter:
	mov	ah, 1		; solicitud de escritura		
	mov	cx, 0		; escribir 0 en la parte alta
	mov	dx, cx		; y en la baja del contador
	int	1Ah

La rutina anterior, limpia el contador. La siguiente espera a que en la parte 
baja halla un 10:

 delay_it:
	mov	ah, 0
        int     1Ah             ; leer de nuevo
        cmp     dl, 10          ; y comparar con el conteo de retardo
        jne     delay_it        ; si no es igual, seguir esperando

Terminada la pausa,  regresamos  a  la  rutina  principal  y  desactivamos el
parlante:

        in      al, 61h         ; Desactivar el parlante  
        and     al, 11111100b   ; 0FCh desactiva el parlante
        out     61h, al         ; enviarlo al puerto

Finalmente restauramos el puerto 61h original y salimos a DOS:

	pop	ax		;  Restablecer el byte de informacin
	out	61h, al		; original que estaba en el puerto 61h.
	sti
	int	20h		;  Salir a DOS.


 - Instrucciones adicionales -
Hay instrucciones adicionales para la entrada/salida de cadenas de caracteres:

	INSx	Transfiere  datos  desde un puerto E/S,  especificado en DX, a un
		buffer apuntado por ES:DI. El tamao del registro de ndice vara 
		con el tamao del dato,  que  se especifica usando B para byte, W
		para  word y D para dword en donde est la x.  As que tendramos 
		las siguientes instrucciones: INSB, INSW, INSD. El formato sera.		

			INSB	reg/mem, DL
			INSW	reg/mem, DX
			INSD	reg/mem, EDX

	OUTSx	Transfiere datos al puerto E/S especificado por DX. El origen del 
		los  datos  estn  en  DS:SI.  El  tamao de los datos se indican
		reemplazando el sufijo x por B para byte,  W para word y  D  para 
		dword. El valor de SI se aumenta o decrementa con cada ejecuccin 
		dependiendo siest activada o no DF. El formato es:

			OUTSB	DL, reg/mem 
			OUTSW	DX, reg/mem
			OUTSD	EDX, reg/mem

	Ambas instrucciones pueden usarse en conjuncin con los prefijos tipo REP:
	
			mov	cl, 5
			mov	dl, 61h
			lea	si, string
			rep outsd dl, si

	Esta  secuencia  transfiere  cinco caracteres desde 'string' al puerto de 
	salida 61h.

Las  instrucciones  de  transferencia  a  puertos E/S tienen acceso restringido y 
controlado en modo protegido.


Pequea nota sobre intrucciones de operaciones lgicas
------------------------------------------------------
Otro tipo de intrucciones que vamos a usar un poco ms adelante son las que  rea-
lizan  operaciones  lgicas.  Su  estudio  detallado  lo  dejamos para el prximo
captulo. Aqu vamos a emplear tres:

	AND:	operador binario de funcin AND lgica
	OR:	operador binario de funcin OR lgica
	XOR:	operador binario de OR eXclusivo

Como puede verse, son tres operadores o funciones binarios  (de dos  operadores). 
Tambin  son  binarios  por que trabajan con lgica binaria sobre los bits de sus 
operadores (un bit puede tener dos estados, 1 o 0, de ah que hablemos de  lgica 
binaria).

Una operacin AND slo da VERDAD (1) si todos sus operadores son VERDAD (1).  Las 
instrucciones producen un cambio sobre el primer operador o de destino  y afectan 
algunas banderas de acuerdo a las reglas que definen cada operacin. Estas reglas 
se definen mediante tablas lgicas, pero aqu bastar dar una definicin rpida.

Una operacin AND slo da VERDAD (1) si todos sus operadores son VERDAD (1). Esta 
operacin compara cada bit de sus operadores,  si uno de ellos es FALSO (0),  en-
tonces el bit correspondiente del destino se pondr en FALSO (0) y se activar la 
bandera de cero ZF. La instruccin se usa mucho para borrar uno de los nibles del 
operando de destino:

	mov	al, 02Fh
	and	al, F0h		; borrar el nible bajo

este cdigo d como resultado AL = 20h

Otro ejemplo:

	mov	al, 10011101b
	mov	dl, 00110011b
	and	al, dl

dar como resultado AL = 00010001b

La  operacin lgica OR es casi la contraria de AND:  da  FALSO (0) slo si todos 
los operandos tambin son falsos (0).  Se  emplea para escribir bits en operandos 
sin afectar otros:

	mov	al, 02Eh
	or	al, 1

dar AL = 02Fh.

Otro ejemplo:

	mov	al, 10011101b
	mov	dl, 00110011b
	and	al, dl
 
dar como resultado AL = 10111111b

La  operacin XOR es parecida a OR,  pero es excluyente, es decir, slo dara valor 
VERDAD (1) si un operando es FALSO (0) y el otro es VERDAD (1): 

	mov	al, 02Eh
	xor	al, 1

dar AL = 0FFh.

Otro ejemplo:

	mov	al, 10011101b
	mov	dl, 00110011b
	and	al, dl
 
dar como resultado AL = 10110110b


Conmutacin entre modos de funcionamiento
-----------------------------------------
Los pasos para pasar de modo real a modo protegido son:

	 crear una GDT vlida

	 opcionalmente, crear una IDT vlida

	 deshabilitar las interrupciones 

	 cargar en el GDTR un puntero a la GDT

	 si se cre una IDT, cargar en el IDTR un puntero a la IDT

	 activar el bit PE (protection enable) en el registro CR0. Esto activa la 
	  unidad de paginacin y  pone en marcha la  gestin de  memoria  virtual,
	  basada en el manejo de tablas, y todo el sistema de proteccin.	  

	 realizar un salto lejano (cargar CS e IP/EIP) para entrar a modo
	  protegido (cargar CS con el selector del segmento de cdigo),

	 cargar los registros DS y SS sobn el selctor del segmento de datos/pila

	 establecer una pila para el modo protegido

	 habilitar las interrupciones, si se van a usar.

Como  en  modo protegido se precisa leer un descriptor para aceder a un segmento,  
se  necesita tener una imagen mnima de la GDT en la memoria principal y tener en 
el registro GDTR su direccin antes de pasar a modo protegido.  Debido  a  que el 
paso a modo protegido puede provocar interrupciones  y  excepciones,  tambin  es 
necesario una imagen mnima de la IDT y su direccin  en  el  IDTR  antes  de  la 
conmutacin. As que la definicin de la GDT y de la IDT,  lo mismo que la  carga 
de los registros con sus direciones, GDTR e IDTR, debe hacerse en modo real.

Para regresar a modo real:

	 hacer un salto largo a un segmento de cdigo de 16-bits

	 cargar SS con un selector a un segmento de datos/pila de 16-bits

	 limpiar el bit PE

	 hacer un salto largo a una direccin en modo real

	 cargar los registros DS, ES, FS, GS, y SS con valores en modo real 

	 establecer el IDTR a los valores en modo real (base 0, lmite 0xFFFF), 

	 rehabilitar interrupciones

Antes  de regresar a modo real,  CS y  SS deben contener selectores que apunten a 
descriptores  que sean apropiados al modo real.  Estos tienen un lmite de 64 KB, 
tienen una granularidad de un byte,  expansin descendente,  se  pueden  escribir 
(data/stack segment only), y estn presentes (Access byte=1xx1001x).

Antes de iniciar el proceso de cambio, es bueno hacer algunas comprobaciones como 
obtener el tipo de procesador, revisin del modo actual del procesador, etc.



* Definicin de los segmentos *
Debemos comenzar declararando dnde poner todos los segmentos. Con TASM y MASM
podemos usar:

segment code16 para public use16	; <- segmento de cdigo y de datos de 16 bits
assume cs:code16, ds:code16

ends	code16


segment code32 para public use32	; <- segmento de cdigo y de datos de 32 bits
assume cs:code32, ds:code32

ends	code32


Con NASM, para binarios que vamos a usar como bootstraps (cdigo de arranque)
podemos usar:

	BITS 16
	ORG 0

para la seccin en modo real, y

	BITS 32

para la seccin en modo protegido.



* Acceder a los 4GB *
Como  en  modo protegido se precisa leer un descriptor para aceder a un segmento,  
Para  acceder  a  los  4GB  de memoria desde modo protegido, hay que habilitar la
puerta A20, sino el direccionamiento de memoria quedar limitado a slo 1 MB.

Para  habilitar  la puerta  A20 en los compatibles AT podemos usar las siguientes 
lneas de cdigo:
	
	mov	al, 0D1h	; Enviar la orden 0D1h de escritura
	out	64h, al		; al puerto 64h.

	mov	al, 0DFh	; Escribir 0DFh (1101 1111b)
	out	60h, al		; en el puerto E/S 60h

Y para deshabilitarla:

	mov	al, 0D1h	; Enviar la orden 0D1h de escritura
	out	64h, al		; al puerto 64h.

	mov	al, 0DDh	; Escribir 0DDh (1101 1101h)
	out	60h, al		; en el puerto E/S 60h


Como  en  modo protegido se precisa leer un descriptor para aceder a un segmento,  
Como puede observarse,  estas rutinas  hacen uso de dos puertos E/S de  hardware: 
los puertos 60h y 64h,  que comunican con el controlador hardware del teclado.


	* NOTA SOBRE LA LNEA A20 *
	Ya hemos comentado  que para manipular cualquier dato,  el CPU debe tener
	su direccin de  destino,  que  obtiene a travs del bus  de direcciones,
	mientras el dato  mismo espera en el bus de  datos.  Un  bus  simplemente
	es un conjunto de  lneas  que  conectan  los  dispositivos  harware  del
	ordenador.  El  bus  de direcciones,  por ejemplo,  sera  un conjunto de
	lneas reservadas  para comunicar al CPU  la  ubicacin  de los datos que
	debe  manipular;  consta  de  20  lneas  de seales para treansmitir las
	direcciones   de   las  posiciones  de  memoria  y  de  los  dispositivos 
	conectados  a  la  tarjeta  madre.  As  que el bus de direcciones es una
	especie de registro de 20 bits que usa el CPU  para  conocer la ubicacin
	de  los  datos  que debe manipular. Las tarjetas para el i8088, tenan 20 
	lneas de direcciones, as que abracaban un espacio de 2^20  direcciones, 
	es  decir,  un  mbito  de 1MB. A partir del 80286, el bus se ampli a 24 
	lneas de direcciones,  lo que permite especificar  2^24 direcciones,  es 
	decir, abarcar un mbito de 16MB.  Para asegurar compatibilidad entre los
	dos procesadores,  IBM  decidi  agregar  hardware  extra y conectaron la
	lnea A20 del CPU a  una salida del controlador del  teclado (KBC),  para
	que  fuera  posible  habilitar  o  inhibir  esa lnea.  La posibilidad de 
	inhabilitar la lnea A20, permita seguir usando software  original  para
	el 8088 en plataformas ms avanzadas. 

	Cuando el KBC es programado apropiadamente,  inhibe  la lnea A20 del CPU 
	del bus de direcciones.  Esta  condicin  emula el  mbito de memoria del
	i8088 en un sistema controlado por un 80286.  Cuando el KBC es programado 
	para habilitar la lnea A20 en el bus de direcciones,  se puede acceder a
	un espacio de direcciones de hasta 16MB.

	Como la lnea de direccin A20 del CPU se halla por defecto inhabilitada,
	cualquier acceso a memoria que se extienda  sobre  lmites  de  megabytes
	impares (1M-2M, 3M-4M, etc.) est inhibida. Independientemente del estado 
	de la compuerta,  el  programador siempre tiene acceso a todo megabyte de
	memoria par (0M-1M, 2M-3M, etc.).  Para  acceder  a  todo  el  espacio de 
	direcciones debe habilitarse la lnea A20.  DOS  incluye  un controlador,
	HIMEM.SYS, que habilita esta lnea.  Pero  si  estamos escribiendo cdigo
	que no corre en DOS y queremos acceder a todo el espacio de  direcciones,
	entonces debemos programar el KBC para habilitar la lnea A20.


La  lnea  A20,  controlada  por  el controlador del teclado, permite o inhibe el
acceso a la memoria por encima de  1 Mb.  Al quedar inhibida, el sistema emula el
direccionamiento de los antiguos  PC/XT;  cuando est habilitada,  el  CPU  tiene
acceso a todo el espacio de direcciones.  Como  la  lnea  est controlada por el
controlador de teclado, revisemos un poco cmo se programa este controlador.

El  controlador del teclado se programa a travs de dos puertos:  60h  y  64h. El
puerto 60h se usa para el envo y recepcin de datos a y  desde  el  controlador.
Este puerto puede ser usado para escribir o leer datos en controlador. Pero antes 
de usarlo, debemos solicitar su uso al controlador envindole la orden pertinente 
a travs del puerto para envo de rdenes: el 64h.

El puerto 64h tiene dos usos: 
	 lectura de estado del controlador 
	 escritura o envo de rdenes al controlador

Antes  de escribir o leer datos a o desde el  controlador  del  teclado,  debemos
avisarle envindole la orden pertinente por el puerto 64h.  Si  queremos escribir 
un dato que cambie la configuracin del controlador y  active algn  dispositivo,
debe envirsele primero el requerimiento de escritura,  que  para  el  puerto  de
salida del contrtoador es la orden 0D1h; para leer el puerto de salida deberamos
enviar antes la orden 0D0h. Es lo que hacemos en el cdigo de arriba:

	mov	al, 0D1h	; Enviar la orden 0D1h (1101 0001) de escritura
	out	64h, al		; al puerto 64h.

	mov	al, 0DFh	; Escribir 0DFh (1101 1111b)
	out	60h, al		; en el puerto E/S 60h

Con este cdigo se espera habilitar la lnea A20, ya que para hacer esto el bit 1
del puerto de salida del controlador  del teclado debe estar activo (1).  As que 
para deshabilitar la lnea A20, slo limpiamos ese bit enviando 0DDh (1101 1101b)
en vez de 0DFh (1101 1111b).

Aunque en teora el cdigo presentado debera funcionar, en la prctica puede 
fallar. Hay otra forma ms segura:

Gate_a20:
	call	empty_8042
	mov	al, 0D1h	; orden de escritura
	out	64h, al
	call	empty_8042
	mov	al, ah		; Activar (ah=0DFh) o desactivar (ah=0DEh)
	out	60h, al		; lnea A20
	call	empty_8042
	ret

; ==================================================================
; Esta rutina se asegura de que el requerimiento de envo de rdenes
; al teclado est vaco o disponible (despus de vaciar los  buffers
; de salida) para las rdenes que habilitan la lnea A20  que  tiene
; que ver con los puertos usados para controlar el teclado en PCs. 
; ==================================================================
empty_8042:
	call	Delay
	in	al, 64h		; puerto de estado
	test	al, 1		; buffer de salida disponible?
	jz	no_output

	call	Delay
	in	al, 60h		; s, entonces leerlo
	jmp	empty_8042	; y lo ignoramos

no_output:
	test	al, 2		; buffer de entrada lleno?
	jnz	empty_8042	; s, seguir el bucle
	ret

; -------------------------------------
; Breve retardo para operaciones de E/S
; -------------------------------------
Delay:
	jmp	delay_01
delay_01:
	jmp	delay_02
delay_02:
	ret

Como puede observarse, es esencialmente la misma rutina que presentamos antes pero 
aade cdigo que se asegura que los buffers de salida del controlador del  teclado
estn vacos antes de enviar rdenes y datos al buffer de salida. Adems, la misma
rutina sirve para habilitar o deshabilitar la linea A20, dependiendo del valor que
se pase a travs de AH (0DFh para activar o 0DDh para desactivar).

Podemos notar que primero se consulta el contenido del puerto 64h,  que  da acceso
al byte de estado dedl controlador del teclado:

	in	al, 64h		; puerto de estado

Luego  se  revisa  si  el buffer de salida est vaco comprobando si el bit 0 est
activo. Si este es el caso,  entonces  hay  un  dato  en  el buffer de salida y se
procede a leerlo hasta que est vaco. 

	test	al, 1		; buffer de salida disponible?
	jz	no_output

	call	Delay
	in	al, 60h		; s, entonces leerlo
	jmp	empty_8042	; y lo ignoramos

Ya seguros de que est vaco el buffer de salida, revisamos el bit 1  del byte de
estado para asegurarnos que el buffer de entrada no est lleno:  si  el  bit est
activo,  el  buffer de entrada est lleno an y se sigue esperando hasta que est 
vaco de la rutina.

no_output:
	test	al, 2		; buffer de entrada lleno?
	jnz	empty_8042	; s, seguir el bucle
	ret



* Definicin de los selectores *
Otro asunto que debemos resolver es la definicin de los selectores.  Selector es 
el nombre que toman los registros de  segmentos  en MP  (CS, DS, ES, FS, GS).  Se 
llaman  as  porque  su funcin es  "seleccionar"  el  descriptor apropiado  para 
definir un segmento. Tiene un tamao de 16 bits y tiene la siguiente estructura:


   15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
   --------------------------------------------------- -- -------- 
  |        ndice en la tabla de descriptores         |TI|   RPL  |
   --------------------------------------------------- -- -------- 

RPL (bits 0-1] es el nivel de privilegio  solicitado  [Requested Privilege Level]. 
Si el descriptor en la tabla tiene un nivel de privilegio ms alto que el del RPL, 
se produce una falla de proteccin general.

TI [bit 02]  selecciona el tipo de tabla de descriptores: TI=0 para GDT, TI=1 para 
LDT.

El ndice [bits 03-15] selecciona una entrada  en  la  tabla de  descriptores;  su 
valor es multiplicado por ocho  (el tamao de un descriptor)  para  establecer  la
direccin del descriptor buscado en la tabla.

Podemos definir unas constantes para la definicin de selectores.

RPL_0	equ	0
RPL_1	equ	1
RPL_2	equ	2
RPL_3	equ	3

TI_GDT	equ	0
TI_LDT	equ	4

La siguiente macro ayuda a establecer el ndice:

	SET_DT_INDEX	macro	selector, XDT, index
		mov	ax, selector
		lea	cx, XDT
		mov	dx, index*8
		add	cx, dx
		shl	cx, 3
		or	ax, cx
	endm

(no se puede usar CX para indicar el ndice y el segundo parmetro debe ser
una etiqueta o nombre en el rea de datos)

El siguiente cdigo define un selector para un segmento de datos con RPL=0 en una 
LDT cuya base se indica con la etiqueta "LDT", en la entrada 2:

	SET_DT_INDEX	RPL_0+TI_LDT, LDT, 2
	mov	ds, ax



* Establecimiento de las tablas de descriptores y de los selectores *
Para  establecer los tipos de segmentos que vamos a  usar,  debemos  confeccionar 
unas tablas con los descriptores apropiados.

Los descriptores bsicos son mantenidos en la Tabla de Descriptores Globales [GDT
Global Descriptor Table].

Opcionalmente pueden haber Tablas de Descriptores Locales [Local Descriptor Table: 
LDT], cuyos descriptores slo son usados por las aplicaciones, no por el sistema.

Arriba hemos presentado la escritura de un descriptor de GDT:

	descriptor struct
	  limit_lo 	dw ?    ; bits de lmite 15-0
	  base_lo 	dw ?    ; bits de base 15-0
	  base_mid 	db ?    ; bits de base 23-16
	  type1 	db ?    ; tipo de selector
	  limit_hi 	db ?    ; bits de lmite 19-16 y otra informacin
	  base_hi 	db ?    ; bits de base 31-24
	descriptor ends

Otra  tabla de descriptores es la Tabla de los Descriptores de las  Interrupciones
(Interrupt Descriptor Table:  IDT)  que  contiene  una  entrada  o descriptor  por 
interrupcin y que dice al procesador dnde encontrar los manipuladores (handlers)
de las interrupciones.

Tambin presentamos arriba la estructura de un descriptor de IDT.

	interrupt_descriptor struc
	  offset0_15	dw	?	; palabra baja del despl. del manejador
	  selector0_15	dw	?	; selector del segmento
	  zero_byte	db	0	; sinuso en este formato
	  flags		db	?	; byte de banderas
	  offset16_31	dw	?	; palabra alta del despl. del manejador
	interrupt_descriptor ends 

Para  establecer estas tablas, simplemente definimos las tablas ya inicializadas 
con sus respectivos valores:

GDT_TABLE	LABEL		BYTE
dummy_dscr	descriptor	<0,0,0,0,0>
code32_dscr	descriptor	<0ffffh,0,0,9ah,0cfh,0>
data32_dscr	descriptor	<0ffffh,0,0,92h,0cfh,0>
code16_dscr	descriptor	<0ffffh,0,0,9ah,0,0>
data16_dscr	descriptor	<0ffffh,0,0,92h,0,0>

La lnea "GDT_TABLE LABEL BYTE" define una etiqueta para una direccin en el rea 
de datos que va a estar alineada en el lmte de un byte.  De esta manera podremos 
hacer  referencia  a  la  direccin  del  comienzo  de  la GDT usando la etiqueta 
GDT_TABLE.

El primer descriptor es nulo, as que todos sus campos no  inicializados en cero.
El primer campo de los siguientes descriptores, limit_lo, vale 0ffffh, que indica
el tamao mximo de cada segmento,  el  segundo y  el  tercer  campo,  base_lo  y 
base_mid, que indica la direccin de la base de cada segmento, se inicializan con 
cero,  porque  estos  valores  sern  determinados  en  tiempo de ejecucin en el
cdigo del programa; el cuarto campo, type1, indica el tipo de segmento:

	9Ah = 1001 1010: P   = 1 (Presente)		bit 7
			 DPL = 00 (mximo: anillo 0)	bits 6-5
			 S   = 1  (segmento estndar	bit 4 
			       de cdigo/datos)
			 T   = 1 (cdigo, el resto de 	bit 3 
			       los bits son ARC)
			 C   = 0 			bit 2 
			 R   = 1 (leble)		bit 1
			 A   = 0 			bit 0


	92h = 1001 0010: P   = 1 (Presente)		bit 7
			 DPL = 00 (mximo: anillo 0)	bits 6-5
			 S   = 1  (segmento estndar	bit 4 
			       de cdigo/datos)
			 T   = 0 (datos, el resto de 	bit 3 
			       los bits son AWC)
			 C   = 0 			bit 2 
			 W   = 1 (escribible)		bit 1
			 A   = 0 			bit 0
			   
El quinto campo, limit_hi, se divide en dos nibles (nible=4bits):  el  nible alto 
tiene informacin adicional sobre el tipo de segmento y el nible bajo es la parte 
alta del lmite; para los descriptores de segmentos de 32 bits este valor es:

	0CFh = 1100 1111: G    = 1 (lmite X 4K)	bit 7
			  D    = 1 (ancho de los 	bit 6
				   registros es 32bits)
			  -    = 0			bit 5
			  AVL  = 0 			bit 4
			  lim. = 0Fh			bits 0-3


El sexto campo, base_hi, es nulo.

As que tenemos dos registros estndar de 32 bits,  uno de cdigo, code32_dscr, y
uno de datos data32_dscr;  y dos registros estndar de 16 bits,  uno para cdigo,
code16_dscr, y otro para datos, data16_dscr.

Luego  decimos al CPU dnde estn nuestras tablas  de  descriptores.  Para  ello,
podemos definir una estructura XDT_PTR,  que  permite  declarar  tipos  de  datos
XDT_PTR, es decir, punteros a tablas de descriptores:

 ; definicin:
	XDT_PTR		struct
		xdt_size	dw	?
		xdt_base	dd	?
	XDT_PTR		ends
 ; ...

SIZE_OF_GDT	EQU	sizeof descriptor

	.data
 ; declaracin (en el rea de datos):
	gdt_reg		XDT_PTR		<?>
 ; ...

	.code
	mov	gdt_reg.xdt_size, SIZE_OF_GDT*5
	mov	gdt_reg.xdt_base, offset GDT_TABLE

La  lnea  A20,  controlada  por  el controlador del teclado, permite o inhibe el
La lneas "SIZE_OF_GDT EQU SIZEOF descriptor" emplea la directiva  "SIZEOF",  que 
devuelve el tamao del tipo de dato de su operando.  As  que  en  vez  de  hacer 
"SIZE_OF_GDT EQU 8",  que permite usar SIZE_OF_GDT  para indicar que la GDT tiene 
un tamao de 8 bytes (64 bits), usamos 

	SIZE_OF_GDT EQU SIZEOF descriptor

que es ms intuitiva.

No podemos inicializar gdt_reg en el rea de datos porque no hay manera de indicar 
la direccin del comienzo de la GDT, as que lo hacemos en tiempo de ejecucin con 
las lneas:

	mov	gdt_reg.xdt_size, SIZE_OF_GDT*5
	mov	gdt_reg.xdt_base, offset GDT_TABLE

la  primera instruccin seala que la GDT tiene 5 entradas y la segunda indica la 
ubicacin de la GDT.

Si empleamos NASM, tenemos otras opciones. Podemos definir as nuestra GDT:
	 
; descriptor nulo
gdt:	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24
; descriptor del segmento lineal de datos
LINEAR_SEL	equ	$-gdt	; selector lineal
        dw 0xFFFF               ; limit 0xFFFFF (1 meg? 4 gig?)
	dw 0			; base pare ste siempre es 0
	db 0
	db 0x92			; presente, anillo 0, datos, expand arriba, escribible
        db 0xCF                 ; granularidad 4KB (4 gig limit), 32-bit
	db 0
; descriptor del segmento de cdigo
SYS_CODE_SEL	equ	$-gdt	; selector del cdigo del sistema
gdt2:	dw 0xFFFF
	dw 0			 
	db 0
	db 0x9A			; presente, anillo 0, cdigo, no-conforme, leble
	db 0xCF
	db 0
; descriptor del segmento de datos
SYS_DATA_SEL	equ	$-gdt
gdt3:	dw 0xFFFF
	dw 0			; 
	db 0
	db 0x92			; presente, anillo 0, datos, expandible arriba, escribible
	db 0xCF
	db 0
gdt_end:

gdt_ptr:
	dw gdt_end - gdt - 1	; lmite de la GDT
	dd gdt			; direccin lineal, fsica de la direccin
 address of GDT


Esta declaracin tiene varias peculiaridades. Una son las constantes: LINEAR_SEL, 
SYS_CODE_SEL y SYS_DATA_SEL, cada una teniendo un valor igual al ndice desde  la 
base de la GDT al respectivo descriptor.  La  otra  peculiaridad  es  que aqu s 
podemos inicializar en el rea de datos el puntero a la  GDT.  El  resto  de  los 
valores de la inicializacin para los  descriptores coinciden con los que presen-
tamos ms arriba. En este caso tenemos trtes descriptores,  adems del descriptor 
nulo: dos de datos de 32 bits y uno de cdigo de 32 bits.  

En ambos casos, tendremos que indicar la direccin de la base para cada segmemto 
en su respectivo descriptor.


* Constantes tiles para definir tablas de descriptores *
En  nuestra GDT,  podemos definir varios tipos de descriptores,  de acuerdo a los 
segmentos que queramos acceder.  Para aliviar el trabajo, podemos definir, quizs
en un archivo de cabecera aparte, una lista de los tipos estndar de segmentos:
 
ACS_PRESENT     equ	0x80            ; segmento presente:      1000 0000
ACS_CSEG        equ	0x18            ; segmento de cdigo:     0001 1000
ACS_DSEG        equ	0x10            ; segmento de datos:      1000 0000
ACS_CONFORM     equ	0x04            ; segmento conformante:   0000 0100
ACS_READ        equ	0x02            ; segmento leble:        0000 0010
ACS_WRITE       equ	0x02            ; segmento escribible:    0000 0010
ACS_IDT         equ	ACS_DSEG        ; el tipo de segmento es el mismo tipo 
ACS_INT_GATE    equ	0x06            ; puerta de int para 286: 0000 0110 
ACS_INT         equ	(ACS_PRESENT | ACS_INT_GATE) ; puerta de int presente 

ACS_CODE        (ACS_PRESENT or ACS_CSEG or ACS_READ)
ACS_DATA        (ACS_PRESENT or ACS_DSEG or ACS_WRITE)
ACS_STACK       (ACS_PRESENT or ACS_DSEG or ACS_WRITE)

Cada  una  de estas constantes es un valor que tiene activo (en 1)  los  bits  de 
bandera  pertinentes  para  determinar  el  tipo de segmento deseado.  Con  estas
constantes podemos declarar as la GDT de arriba:

dummy_dscr	segment_descriptor	<0,0,0,0,0>
code32_dscr	segment_descriptor	<0ffffh,0,0,9ah,0cfh,0>
data32_dscr	segment_descriptor	<0ffffh,0,0,92h,0cfh,0>
code16_dscr	segment_descriptor	<0ffffh,0,0,9ah,0,0>
data16_dscr	segment_descriptor	<0ffffh,0,0,92h,0,0>

Obsrvese que en los segmentos de datos el cuarto campo (tipo) es 92h:

	ACS_PRESENT+ACS_DSEG+ACS_WRITE

y en los de cdigo 9ah 

	ACS_PRESENT+ACS_CSEG+ACS_READ

As que tambin podemos declarar los descriptores de la GDT as:

dummy_dscr	segment_descriptor	<0,0,0,0,0>
code32_dscr	segment_descriptor	<0ffffh,0,0,ACS_PRESENT+ACS_CSEG+ACS_READ,0cfh,0>
data32_dscr	segment_descriptor	<0ffffh,0,0,ACS_PRESENT+ACS_DSEG+ACS_WRITE,0cfh,0>
code16_dscr	segment_descriptor	<0ffffh,0,0,ACS_PRESENT+ACS_CSEG+ACS_READ,0,0>
data16_dscr	segment_descriptor	<0ffffh,0,0,ACS_PRESENT+ACS_CSEG+ACS_READ,0,0>


 - NOTA SOBRE EL DESCRIPTOR NULO -
El descriptor nulo (dummy) no debe ser usado. En Modo Protegido hay mecanismos de 
proteccin como el descriptor "invlido" (cero): si un segmento es cargado con un 
descriptor cero, cada vez que se trate de acceder a  la memoria a travs de l se 
producir un error de proteccin. El selector nulo es usado como "marcador" y  es 
til para depuradores si van a revisar si se ha usado un registro de segmento.


* Cargar el GDTR y el IDTR *
Despus de declarar nuestra GDT,  cargamos  la direccin de la GDT en el registro 
GDTR con la instruccin LGDT:

 - TASM -
	lgdt	[fword ds:gdt_reg]

 - NASM -
	LGDT	gdt_reg

Lo mismo vale para la IDT,  si existe. La diferencia es que tenemos que cargar el
puntero a la IDT en el registro IDTR,  no en el GDTR;  entonces debemos  usar  la
instruccin:

 - TASM -
	lidt	[fword ds:idt_reg]

 - NASM -
	LIDT	idt_reg



*Pasar a modo protegido*
Ahora deshabilitamos las interrupciones:

		cli

y pasamos a modo protegido:

		mov	eax, cr0
		or	al, 1
		mov	cr0, eax

(tambin puede usarse LMSW AX, pero el manual de Intel recomienda la anterior)

Lo mismo vale para la IDT,  si existe. La diferencia es que tenemos que cargar el
Luego hay que realizar un JMP que limpie ciertas reas del procesador de  colas y 
cualquier otro material ah guardado para ser usado en modo real:

		jmp	offset start32, code32_idx

CS queda cargado con el nuevo descriptor de modo protegido. Si no se hace esto el 
CPU no establecer la cach del descriptor y el Instruction Prefetch Queue podra 
contener instrucciones escritas en una manera slo vlida para el Modo Real.

Hecho esto,  el CPU est establecido para modo protegido y se inicia la ejecucin 
en un punto  donde  cargamos los otros registros de segmentos con sus respectivos 
selectores de modo protegido:

 - NASM -
BITS 32
code32_idx:
	mov	ax, SYS_DATA_SEL     ; now in 32-bit pmode
	mov	ds, eax		; EAX works, one byte smaller :)
	mov	ss, eax
        nop
	mov	es, eax
	mov	fs, eax
	mov	gs, eax
	xor	eax, eax		; zero top 16 bits of ESP
	mov	ax, sp
	mov	esp, eax

	 ...


* Determinacin del tipo de procesador *
Por razones de seguridad, podemos revisar al comienzo sobre cul procesador corre 
el programa para asegurarnos de que soporta nuestro mtodo de cambio de modos. Lo 
expuesto, est pensado para procesadores 80386+; los procesadores 80286 necesitan 
un mtodo de cambio distinto que no vamos a exponer porque  consideramos obsoleto 
este procesador. Los procesadores anteriores el 80286 de la serie ix86, no sopor-
tan cambio de modos: slo corren en modo real.

Si  queremos comprobar el modo actual en que corre el procesador, podemos usar el 
siguiente cdigo, que ser suficiente para nosotrros por ahora:

;---
;	vfycpu	- asegurarse de que est corriendo el CPU apropiado, en Modo Real 
;		  y la lnea A20 est habilitada.
;
;	cambia	AX,DX
;	regresa AL=cdigo de error su algo malo ocurri
;	cod. de error:	040h	- no es un CPU de 32-bits (PM y A20 no se probaron)
;			001h	- ya  en PM (386+, A20 no se prob)
;			0FFh	- A20 deshabilitada (CPU de 32-bits en Modo real)
;
;	Por Jerzy Tarasiuk
;
vfycpu	proc
	pushf			; salvar banderas
	cli			; asegurarse de que las ISRs no interrumpirn
;
; 1. revisar si es al menos un 386
;
	pushf			
	pop	ax		; AX=banderas
	xor	ah, 40h		; toggle NT
	push	ax		; pila: modified_flags,original_flags
	popf
	pushf			; pila: modified_flags_from_cpu,original_flags
	pop	dx		; DX=BANDERAS pasasads a trevs del CPU
	mov	al,dh
	xor	al,ah
	jnz	vcfail		; CPU inapropiado
;
; 2. asegurarse de que estamos en modo real
;
	mov	eax, cr0
	and	al, 1
	jnz	vcfail		; si ya estamos in PM (quizs VM86)
;
; 3. asegurarse que est habilitada la lnea
;
	push	ds
	push	es
	push	bx
	xor	bx,bx
	mov	ds,bx
	mov	ax,-1
	mov	es,ax
	xor	[bx],ah		; cambiar el byte[000000h]
	mov	al,es:[bx+10h]	; obtener el byte[100000h]
	xor	[bx],ah		; cambiar el byte[000000h]
	xor	al,es:[bx+10h]	; comparar el byte[100000h] con su valor previo
				; 0 si no ha cambiado, -1 significa [000000h]==[100000h]
	pop	bx
	pop	es
	pop	ds
;
vcfail:	popf			; restablecer las banderas
	test	al,al		; comprobar el cdigo de error
	ret
vfycpu	endp

; --------------------------------------------------------------------------------------

Una  forma de obtener con presicin el tipo de procesador es usar la instruccin 
CPUID, que slo la tenemos a partir de los procesadores ix486+. Por este motivo, 
antes de usarla hay que realizar algunas pruebas. El algoritmo para su uso sera 
algo como:

	 chequear si el CPU soporta CPUID. 
		 Si soporta, es 486, Pentium o superior y 
		  lo identificaremos usando CPUID. 
			 Si tenemos un Cyrix con CPUID, es mejor
	          	  usar registros DIR para una identificacin 
		 	  ms precisa.
		 Si el CPU no soporta CPUID, chequeamos si es de la familia
		  Cyrix.
		 Si el CPU no tiene CPUID y no es Cyrix, debe ser un chip
		  Intel/AMD "clsico. 
	 fin del chequeo

 - Chequeo de CPUID -
Es fcil: se intenta cambiar el valor del bit de CPUID bit (bit 21) en el registro
EFLAGS.  Si el bit puede ser *toggled, se soporta CPUID y puede ser usada para una 
identificacin  ms precisa.  En  los chips Cyrix el bit no puede ser *toggled* si 
no se permite explicitamente. No nos extenderemos ahora en su uso.

 - Chequeo de CPU Cyrix -
Se realiza una rutina de prueba de divisin; si el valor de la bandera es 00 o 40h, 
el CPU es Cyrix.

 - Chequeo de un intel/amd clsico -
Si no podemos obtener la signatura RESET, no podemos hacer mucho aunque los  Intel 
SX y DX pueden distinguirse verificando la presencia de FPU.

No profundizaremos ahora en ninguno de estos mtodos.



Ejemplo de conmutador de modo real a modo protegido
---------------------------------------------------
Una  forma de obtener con presicin el tipo de procesador es usar la instruccin 
A continuacin todo el cdigo de un bootstrap que incluye un  mdulo  (load.asm) 
que realiza cambio de modo real a modo protegido.  Cuando  se arranca el sistema 
con este bootstrap, llegamos primero a un shell que incluye la  orden  "protek", 
que dice al sistema que pase el control al cdigo que  vemos  en  load.asm,  que 
realiza el paso a modo protegido. En modo protegido llegamos a un shell que tra-
baja slo con golpes de tecla especficos, que activan interrupciones o conducen 
de vuelta al shell de modo real.


    ; -------------------------------------------------------------------
    ; IMAGE.ASM
    ; Disk image

      %include 'boot.asm'
      %include 'console.asm'
      %include 'load.asm'

    ; para ensamblar con NASM:
    ;	    nasm image.asm -o IMAGE.bin
    ; -------------------------------------------------------------------

; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    ; -----------------------------------------------------------------
    ; boots.ASM
    ; Pequeo bootstrap
    ; Carga un programa del disco y le pasa el control
    ; Versin: n u MiT_o r
    ; -----------------------------------------------------------------
    ;
    ; ----------------------------------------------------------------
    ; Hay que decirle al ensamblador que este es el desplazamiento 0.
    ; Si no es el desplazamiento 0, lo ser despus del salto.
    ; ----------------------------------------------------------------

	    ORG 0

            jmp 07C0h:start     ; Ir al segmento 07C0
     start: ; -------------------------------------
            ; Actualizar los registros de segmento
            ; -------------------------------------
	    cli			; Deshabilitar las interrupciones
            mov ax, 07C0h	; Ajustar los registros del
            mov ds, ax		; segmento de datos
            mov es, ax
            ; -------------------
            ; Establecer la pila
            ; -------------------
	    mov ax, 9000h	; Poner la pila en 90000h
	    mov ss, ax		; SS = 9000h
	    mov sp, 0FFF0h	; SP = 0FFF0h => Stack = 90000h
	    sti			; habilitar interrupciones
            ; --------------------------------
            ; Desplegar mensaje de bienvenida
            ; --------------------------------
            lea si, [__msg]
            call _szDisplay_
            ; --------------------------------
            ; Restablecer la unidad de floppy
            ; --------------------------------
     reset:
	    mov cx, 0
	    mov ax, cx       	; reestablecer sistema de disco
	    mov dl, al		; 0 para floppy, cambiar a 80h para discos duros
	    int 13h
            ; ----------------------------------
            ; Subir el 2do. sector a la memoria
            ; ----------------------------------
     read:
            mov bx, 200h        ; desplazamiento de la segunda etapa

            mov ah, 2           ; Cargar los datos del disco a ES:BX
            mov al, 5           ; Cargar 3 sectores
            mov ch, 0           ; Cilindros=0
            mov cl, 2           ; Sector=2
            mov dh, 0           ; Cabezal=0
            mov dl, 0           ; Unidad=0
            int 13h             ; Leer!

	    jnc ok_load_setup	; si no hubo problemas, se contina

	    lea  si, [_err_]	; despliega mensaje de error
            call _szDisplay_
	    mov  ah, 16
	    int  16h
	    jmp  read

ok_load_setup:
            lea si, [__msg_]	; despliega mensaje de aviso
            call _szDisplay_
	    mov	    ah, 16
	    int     16h

	    DB 0xEA		; jump to sector 02
	    DW 0x7E00		; offset 
	    DW 0x0000		; segment 
 
; ====================================================================

        _szDisplay_:
		push	si
		_print_string:
			lodsb
			test	al, al
                        jz      _exit_szDisplay_

                        mov     bx, 000Fh                                                   
                        mov     ah, 0Eh                                                     
                        int     10h

			jmp	_print_string 
                _exit_szDisplay_:
                        pop     si
			ret

; --------------------------------------------------------------------
__msg  db " --------------------------------------", 13, 10
       db "Prueba de dise", 0A4h, "o de bootstrap", 13, 10
       db "Por  n u MiT_o r", 13, 10
       db "----------------------------------------", 0

__msg_ db 13, 10, "Sector 2 copiado en 07C0h:0200h", 13, 10
       db "Pulsa una tecla para continuar....", 13, 10, 0
_err_  db 13, 10, "Error al leer desde el disco", 13, 10, 0

    times 510-($-$$) db 0
    dw 0AA55h
; ---------------------------------------------------------------------------

; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; -------------------------------------
; console.asm
; Pequeo intrprete de rdenes
; (C) n u MiT_o r 2002
; -------------------------------------

; -------------------------------------
; Acepta tres rdenes:
;	help: imprime lista de rdenes
;	clean: limpia la pantalla
;	exit: reinicia del sistema
; -------------------------------------

_init:
	; Despliega de bienvenida
	lea	si, [msg1]
	call	_szDisplay
_loop:
	; Despliega puntero
	lea	si, [puntero]
	call	_szDisplay

	; Obtiene la orden
	lea	di, [command]
	mov	[di], byte 0
	call	_get_command
_is_clean:
	; Es la orden clean?
	lea	si, [_clean_]
	call	_compare
	test	ax, ax
	je	_is_exit
	; Limpiar el monitor
	call	clean_
	jmp	_loop	
_is_exit:
	; Es la orden "exit"
	lea	si, [_exit_]
	call	_compare
	test	ax, ax
	je	_is_protek
	; Es la orden protek?
	; reiniciar el sistema
_reboot:
        db 	0EAh		; Cdigo de operacin para JMP
        dw 	0FFFFh		; Saltar a 0000:FFFFh, que reinicia
        dw 	0		; el sistema
	dw	0
	dw	0
_is_protek:
	lea	si, [_prot_]
	call	_compare
	test	ax, ax
	je	_is_help
	; Pasar a modo protegido
	jmp	_protek
_is_help:
	; Es la orden "help"
	lea	si, [_help_]
	call	_compare
	test	ax, ax
	je	_no_order
_help:
	lea	si, [hmsg]
	call	_szDisplay
	jmp	_loop	
	
_no_order:
	lea	si, [msg2]
	call	_szDisplay
	jmp	_loop

; ====================================================================
	_szDisplay:
		push	si
                __print_string:
			lodsb
			test	al, al
                        jz      __exit_szDisplay

			call	__display_char

                        jmp     __print_string 
                __exit_szDisplay:
		pop	si
			ret


	__display_char:
		mov	bx, 0007h                                                   
		mov	ah, 0Eh                                                     
		int	10h
		ret

; --------------------------------------------------------------------
	_get_command:
		push	di
		mov	cx, 12
	_loop_:
		mov	ah, 10h
		int	16h
	filt_extended_function_keys:
		test	al, al
		je _loop_
		cmp	al, 0E0h
		je _loop_
	is_enter_key:
		cmp	al, 0Dh
		je _exit_get_command
		dec	cx
		test	cx, cx
		je _excess
	get_character:
		stosb
		call	__display_char
		jmp _loop_
	_exit_get_command:
		xor	al, al
		stosb
		pop	di
		ret

	_excess:
		lea	si, [msg3]
		call	_szDisplay
		mov	cx, 12
		pop	di
		push	di
		jmp _loop_
	
; --------------------------------------------------------------------
	_compare:
		push	si
		push	di
		push	bx
	get_string_lenght:
		call	_strlen
		mov	bx, cx	
		xchg	di, si
		call	_strlen
		xchg	si, di
		cmp	bx, cx
		jne	_not_equals
	_compare_strings:
		repe cmpsb
		test	cx, cx
		jne	_not_equals
		mov	ax, 1
		jmp	_exit_compare
	_not_equals:
		mov	ax, 0
	_exit_compare:
		mov	cx, bx
		pop	bx
		pop	di
		pop	si
		ret	


	_strlen:
		push	di
		mov	al, 0
		mov	cx, -1	; cx = -1 = FFFFh = 65535 = infinito
		repne scasb
		neg	cx
		dec	cx
		dec	cx
		pop	di
		ret

; --------------------------------------------------------------------
	clean_:
		mov	dx, 0
		call	set_cursor
		mov	cx, 80*25
	_print:
		mov	al, 32	; 32 = 20h = espacio
		call	__display_char
		loop	_print

		mov	dx, 0
		call	set_cursor
		ret

	set_cursor:
		mov	ah, 2
		int	10h
		ret

; ----------------------------------------------------------------------------------

	msg1	db	"Int",82h,"rprete de ",0A2h,"rdenes", 10, 13
		db	"Escribe 'help' para ver las opciones", 10, 13, 0
	msg2	db	7, 10, 13, "No es una orden del programa!", 10, 13, 0
	msg3	db	7, 10, 13, "Introdujo m",0A0h,"s de 12 caracteres", 10, 13
	puntero	db	10, 13, "> ", 0
	hmsg	db	10, 13, "Ordenes disponibles en este shell:", 10, 13
		db	9, "help: despliega este mensaje", 10, 13
		db	9, "clean: limpia el monitor", 10, 13
		db	9, "protek: pasa a modo protegido", 10, 13
		db	9, "exit: reinicia el sistema", 10, 13, 0
	_help_	db	"help", 0
	_clean_	db	"clean", 0
	_prot_	db	"protek", 0
	_exit_	db	"exit", 0

	command:
		times 12 db 0

; ----------------------------------------------------------------------------------

; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


;/****************************************************************************
; load.asm
;
; Ejemplo 1: implementacin simple de un conmutador de 
;	modo real a modo protegido.
;
;****************************************************************************/

@msg0	db 13, 10
@msg1	db "Vamos a pasar a modo protegido", 13, 10, 0
@msg2	db "Ya estamos en modo protegido: ", 0
@msg3	db "Linea A20 habilitada", 13, 10, 0
@msg4   db "Pulsa 'escape' para volver a modo real...", 0

ALIGN 4
Reset_IDTR      dw      400h - 1	; Limit portion
		dw      0, 0, 0, 0

Orig_stack	
	.DD_offset	dw	0
	.DD_segment	dw	0
Orig_data		dw	0

;---------------------------------------------------------------

_protek:
	lea 	si, [@msg0]
	call	 _szDisplay

; ----------------------------------------------------------------------------
; Habilitar linea A20: necesario para acceder a la memoria por encima de 1 MB
; ----------------------------------------------------------------------------
	mov	ah, 0DDh
	call	Gate_A20
	lea 	si, [@msg3]
	call	 _szDisplay
; --------------------------------------------------------------------
; Guardar el segmento y la direccin del puntero de pila en modo real 
; --------------------------------------------------------------------
        mov     [Orig_stack.DD_segment], SS	; guardar SS:SP
        mov     [Orig_stack.DD_offset], SP
	mov	ax, ds
	mov	[Orig_data], ax
; --------------------------------------------------------------
; Establecer la direccin dnde regresar de m. protegido a real
; --------------------------------------------------------------
	mov	[_offs], word 7C00h+_RMode
	mov	[_segm], cs
; ------------------------------------------------------------------------
; Cargar la direccin de la GDT en el GDTR. Los descriptores de la GDT ya
; fueron inicializados en nuestra seccin de datos
; ------------------------------------------------------------------------
	lgdt [gdt_ptr]
; ------------------------------------------------------
; Deshabilitar las interrupciones (pudo haber sido CLI)
; ------------------------------------------------------
	push dword 0		; poner en 0 EFLAGS (interrupciones,
	popfd			; deshabilitadas, IOPL=0, bit NT=0)
; --------------------------
; conmutar a modo protegido
; --------------------------
	mov	eax, CR0
	or	al, 1
	mov	CR0, eax
; -------------------------------
; ir al cdigo en modo protegido
; -------------------------------
	jmp	SYS_CODE_SEL:do_pm


; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Rutina _A20_ENABLE: habilita la linea A20, que permite acceder a la memoria
; por encima de 1 MB
; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Gate_A20:
	call	empty_8042
	mov	al, 0D1h	; orden de escritura
	out	64h, al
	call	empty_8042
	mov	al, ah		; Activar (ah=0DFh) o desactivar (ah=0DEh)
	out	60h, al		; lnea A20
	call	empty_8042
	ret

; ==================================================================
; Esta rutina se asegura de que el requerimiento de envo de rdenes
; al teclado est vaco o disponible (despus de vaciar los  buffers
; de salida) para las rdenes que habilitan la lnea A20  que  tiene
; que ver con los puertos usados para controlar el teclado en PCs. 
; ==================================================================
empty_8042:
	call	Delay
	in	al, 64h		; puerto de estado
	test	al, 1		; buffer de salida disponible?
	jz	no_output

	call	Delay
	in	al, 60h		; s, entonces leerlo
	jmp	empty_8042	; y lo ignoramos

no_output:
	test	al, 2		; buffer de entrada lleno?
	jnz	empty_8042	; s, seguir el bucle
	ret

; -------------------------------------
; Breve retardo para operaciones de E/S
; -------------------------------------
Delay:
	jmp	delay_01
delay_01:
	jmp	delay_02
delay_02:
	ret

; ------------------------------------------------------------
; Restablecer los registros de segmento de 16 bits en valores
; compatibles con el modo real
; ------------------------------------------------------------
_RMode:
	mov     ax, [Orig_data]
        mov     ds, ax
        mov     es, ax
; Restablecer la pila SS:SP
        mov     ss, [Orig_stack.DD_segment]        ; Restore SS:SP
        mov     sp, [Orig_stack.DD_offset]
; Inhabilitar la linea A20 del bus de direcciones (gate A20 off)
	mov	ah, 0DFh
        call    Gate_A20
; Limpiar el monitor y regresar
	call	clean_
	jmp	_loop



; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; 			Fragmento que corre en modo protegido
; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



	BITS 32			; Para indicar que la base son 32 bits, no 16.
do_pm:
	; --------------------------------
	; Definir los selectores de datos
	; --------------------------------
	mov eax, SYS_DATA_SEL   ; ahora estamos en modo protegido de 32-bits
	mov ss, eax
	mov ds, eax
	mov fs, eax
	mov gs, eax
	; ------------------------------------------------------
	; Definir un selector de rea de despliegue de video
	; ------------------------------------------------------
	mov eax, SYS_VIDEO_SEL
	mov es, eax

	xor eax, eax		; el puntero de pila ESP de 16 bits
	mov ax, sp
	mov esp, eax
	; ------------------
	; Limpiar el monitor
	; ------------------
	mov	ecx, SYS_DYSPLAY/2
	xor	edi, edi
	mov	eax, 0F20h
	rep stosw
	; ---------------------------------------------------------------------
	; Desplegar un mensaje que avise que ya terminamos usando punteros de
	; direcciones lineales de 32 bits
	; -------------------- -------------------------------------------------
	lea 	esi, [ds:@msg2]	; puntero a cadena erminada en cero
	mov	cl, 0Fh		; color
	xor	eax, eax	; No. de linea
	call	xDisplay

	lea 	esi, [ds:@msg4]	; puntero a cadena terminada en cero
	mov	cl, 0Eh		; color
	mov	eax, 1		; No. de linea
	call	xDisplay

.a	in	al, 60h
	cmp	al, 1
	jne	.a
	; -------------------
	; Limpiar el monitor
	; -------------------
	mov	ecx, SYS_DYSPLAY/2
	xor	edi, edi
	mov	eax, 0720h
	rep stosw
	; --------------------------------
	; restablecer el valor compatible
	; --------------------------------
        lidt    [cs:Reset_IDTR] ;   w/ con el modo real.  En modo real
                                ;   las interrupciones son relocalizables
                                ;   a travs del registro IDT
	; ------------------
	; Pasar a modo real
	; ------------------
	mov	eax, CR0
	and	al, 0FEh
	mov	CR0, eax
	; --------------------------------
	; Regresar al cdigo en modo real
	; --------------------------------
	db	0EAh		; JMP DWORD CS:_RMode
_offs  	dw 	0
	dw	0
_segm  	dw 	0

; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

xDisplay:
	xor	edx, edx
	mov	ebx, 80*2
	mul	ebx
	mov 	edi, eax
	mov	ah, cl
again:
	lodsb
	or  	al, al
	je  	.exit
	stosw
	jmp 	again
.exit	ret

; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; 				TABLA DE DESCRIPTORES: GDT
; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

[SECTION .data]
; null descriptor
SYS_DYSPLAY	EQU	80*25*2
gdt:	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24
; linear data segment descriptor
LINEAR_SEL	equ	$-gdt
        dw 0xFFFF               ; limit 0xFFFFF (1 meg? 4 gig?)
	dw 0			; base for this one is always 0
	db 0
	db 0x92			; present,ring 0,data,expand-up,writable
        db 0xCF                 ; page-granular (4 gig limit), 32-bit
	db 0
; code segment descriptor
SYS_CODE_SEL	equ	$-gdt
gdt2:	dw 0xFFFF
	dw 0x7C00		; (base gets set above)
	db 0
	db 0x9A			; present,ring 0,code,non-conforming,readable
	db 0xCF
	db 0
; data segment descriptor
SYS_DATA_SEL	equ	$-gdt
gdt3:	dw 0xFFFF
	dw 0x7C00		; (base gets set above)
	db 0
	db 0x92			; present,ring 0,data,expand-up,writable
	db 0xCF
	db 0
SYS_VIDEO_SEL	equ	$-gdt
gdt4:	dw SYS_DYSPLAY		; Tamao de una pantalla en modo de texto
	dw 0x8000		; (base gets set above)
	db 0x0B
	db 0x92			; present,ring 0,data,expand-up,writable
	db 0xCF
	db 0
gdt_end:

gdt_ptr:
	dw gdt_end - gdt - 1	; lmite de la GDT 
	dd gdt+0x7C00		; dir. lineal, fsica de GDT

; -------------------------- END ---------------------------


Una  vez compilado este programa obtndremos el archivo IMAGE.IMG.  Hasta  ahora
hemos copiado este tipo de ficheros binarios en  los  primeros  sectores  de  un
disquete y lo hemos ejecutado reinicializando el sistema y pasndole el  control
a la unidad de floppy durante el arranque. Es algo engorroso realmente. Hay otra 
opcin: usar un  emulador. Podemos usar el programa BOCHS, que crea una  mquina 
virtual en la que podemos correr un sistema operativo dentro de otro. Es de suma
utilidad  en la prueba de sistemas operativos que estemos desarrollando. Para su
provecgo ac,  puedes seguir las instrucciones que damos en la Nota sobre el uso 
de BOCHS en el apndice del captulo.



Anlisis de load.asm 
--------------------
El  cdigo de boot.asm (el bootstrap) y de console.asm (la cnsola), ya lo hemos 
analizado.  Nos  limitaremos aqu a revisar el cdigo de load.asm: un conmutador
de modo. El algoritmo es enrealidad muy simple.

El  punto  de  entrada  es  "protek",  y  lo primero que se hace es desplegar un 
mensaje: "Vamos a pasar a modo protegido".

_protek:
	lea 	si, [@msg0]
	call	 _szDisplay


De  inmediato vamos a nuestro asunto: habilitamos la lnea A20 para tener acceso
a toda la memoria por encima de 1MB. 

	mov	ah, 0DDh
	call	Gate_A20

No  analizaremos el procedimiento Gate_A20 porque ya lo hicimos arriba de manera
exhaustiva.  Luego  guardamos el segmento y el puntero de pila (SS:SP), as como
el segmento de datos: los vamos a necesitar cuando retornemos a modo real:

        mov     [Orig_stack.DD_segment], SS	; guardar SS:SP
        mov     [Orig_stack.DD_offset], SP
	mov	ax, ds
	mov	[Orig_data], ax

Ahora  establecemos  la  direccin  dnde  regresaremos  cuando volvamos de modo
protegido a modo real: 

	mov	[_offs], word 7C00h+_RMode
	mov	[_segm], cs

Hecho esto, cargamos nuestro puntero a la GDT, que ya hemos inicializado:

	lgdt [gdt_ptr]

deshabilitamos las interrupciones

	push dword 0		; poner en 0 EFLAGS (interrupciones,
	popfd			; deshabilitadas, IOPL=0, bit NT=0)

y, por fin, conmutamos a modo protegido, tal como expusimos arriba, activando el
bit 0 dedl registro de control CR0:

	mov	eax, CR0
	or	al, 1
	mov	CR0, eax

Pasamos entonces al cdigo de 32 bits en modo protegido mediante un salto incon-
dicional que limpiar reas del procesador de restos tiles slo para modo real:

	jmp	SYS_CODE_SEL:do_pm

El descriptor del segmento de cdigo lo hemos definido as:

SYS_CODE_SEL	equ	$-gdt
gdt2:	dw 0xFFFF
	dw 0x7C00		; (base gets set above)
	db 0
	db 0x9A			; present,ring 0,code,non-conforming,readable
	db 0xCF
	db 0

Con la explicacin que dimos arriba se entiende  claramente.  Slo  diremos  que
como segundo dw, la base,  hemos puesto la base donde se carga nuestra imagen en 
memoria.

Como puede verse,  usamos  como direccin de segmento nuestro selector de cdigo 
de modo protegido.  Antes  de  el  punto de entrada del cdigo de modo protegido 
colocamos:

	BITS 32			; Para indicar que la base son 32 bits, no 16.

que  especifica cdigo y datos de 32 bits.  Inmediatamente  damos  definicin  a 
nuestros selectores;  primero definimos los que apuntarn al descriptor del seg-
mento de datos del sistema:

	mov eax, SYS_DATA_SEL   ; ahora estamos en modo protegido de 32-bits
	mov ss, eax
	mov ds, eax
	mov fs, eax
	mov gs, eax

y  luego  uno que apuntar al descriptor del segmento correspondiente al rea de
vdeo:

	mov eax, SYS_VIDEO_SEL
	mov es, eax

El  descriptor seleccionado por SYS_DATA_SEL es semejante a  SYS_CODE_SEL,  slo
cambia el tipo, porque es un segmento de datos (0x92: escribible), no de  cdigo 
(0x9A: leble).

Ms interesante es el descriptor seleccionado por SYS_VIDEO_SEL:

gdt4:	dw SYS_DYSPLAY		; Tamao de una pantalla en modo de texto
	dw 0x8000		; (base gets set above)
	db 0x0B
	db 0x92			; present,ring 0,data,expand-up,writable
	db 0xCF
	db 0

Su base apunta al inicio del rea de video (0x8000)

Es este descriptor el que usaremos para despliegue en pantalla.

As que despus de definir el puntero de pila de 32 bits para modo  protegido: 

	xor eax, eax		; el puntero de pila ESP de 16 bits
	mov ax, sp
	mov esp, eax

Procedemos a limpiar el monitor:

	mov	ecx, SYS_DYSPLAY/2
	xor	edi, edi
	mov	eax, 0F20h
	rep stosw

La instruccin "rep stosw" mueve tantas veces como indique el registro ecx (aqu 
SYS_DYSPLAY/2 veces) el contenido en ax (0F20h: 0F es el cdigo  de  color  para 
fondo negro --el 00-- y fuentes blancas brillantes --la 0Fh--, y 20h es el cdi-
go ASCII correspondiente al espacio) a la direccin apuntada en ES:EDI. Al hacer 
esto, se escriben espacios en blanco en todo el monitor. Cada ejecucin de stosw 
incrementa en dos el valor de EDI,  lo  que pone  un caracter ms adelante en el 
rea de video. Un caracter es un byte,  pero es necesario otro byte con el atri-
buto de color para el caracter; as que finalmente tenemos 2 bytes por caracter, 
uno para el cdigo ascii y otro para el color.

El monitor, en modo de texto, que es el que empleamos,  tiene  espacio  para  el
despliegue de 80 caracteres por fila, y hay 25 filas disponibles. Esto, junto al
hecho de que necesitamos dos bytes por caracter, explica nuestrta de finicin de
la constante SYS_DYSPLAY:

	SYS_DYSPLAY	EQU	80*25*2

Limpio el monitor, desplegamos un par de mensajes llamando a xDisplay:

	lea 	esi, [ds:@msg2]	; puntero a cadena erminada en cero
	mov	cl, 0Fh		; color
	xor	eax, eax	; No. de linea
	call	xDisplay

	lea 	esi, [ds:@msg4]	; puntero a cadena terminada en cero
	mov	cl, 0Eh		; color
	mov	eax, 1		; No. de linea
	call	xDisplay

Para usar este procedimiento, pasamos en ESI la direccin de la cadena terminada
en cero.  El  atributo de color lo especificamios en CL y el nmero dee lnea lo
indocamos en EAX.  Para  la segunda cadena hemos escogido el color amarillo bri-
llante sobre fondo negro (0Eh).

Para  leer  las cadenas,  detenemos la ejecucin del programa hasta que pulsemos
la tecla "escape": 

.a	in	al, 60h
	cmp	al, 1
	jne	.a

De nuevo empleamos el puerto del teclado.  Aqu lo que hacemos es leer el buffer
de salida para revisar si el cdigo de rastreo de la tecla presionada;  si es 1,
entonces se presion la tecla de escape y el programa continuar  su  ejecucin,
que conducir de nuevo a modo real. Volvemos a limpiar,  pero  con otro atributo
de color, el original (07h)

	mov	ecx, SYS_DYSPLAY/2
	xor	edi, edi
	mov	eax, 0720h
	rep stosw

y restablecemos el  vector de  interrupciones;  recurdese  que  el  vector  de
interrupciones debe ser el original de modo real cuando pasamos a modoprotegido 
y su direccin va a estar en el registro IDTR.  Antes de regresar al modo real, 
nos aseguramos que el IDTR apunte al vector de interrupciones compatible con el 
modo real en 0000:0000 y con un tamao de 400h-1 bytes:

        lidt    [cs:Reset_IDTR] ;   w/ con el modo real.  En modo real
                                ;   las interrupciones son relocalizables
                                ;   a travs del registro IDT

Despus de esto,  slo limpiamos el bit 0 del registro CR0 y ya estamos de nuevo 
en modo real:

	mov	eax, CR0
	and	al, 0FEh
	mov	CR0, eax

El  siguiente  cdigo  nos lleva de regreso a la direccin donde indicamos antes 
que debamos regresar; como puede observarse es de nuevo un salto incondicional, 
que debe limpiar las reas del proceador de materia intil en modo real:

	db	0EAh		; JMP DWORD CS:_RMode
_offs  	dw 	0
	dw	0
_segm  	dw 	0

para _offs,  habamos elegido _RMode, donde se encuentra la siguiente rutina de 
16 bits:

_RMode:
	mov     ax, [Orig_data]
        mov     ds, ax
        mov     es, ax
; Restablecer la pila SS:SP
        mov     ss, [Orig_stack.DD_segment]        ; Restore SS:SP
        mov     sp, [Orig_stack.DD_offset]
; Inhabilitar la linea A20 del bus de direcciones (gate A20 off)
	mov	ah, 0DFh
        call    Gate_A20
; Limpiar el monitor y regresar
	call	clean_
	jmp	_loop

La  rutina  restablece los valores originales de modo real para los registros de 
segmento de datos (ds y es), la pila (ss:sp), deshabilita de nuevo la lnea A20. 
Luego limpiamos y regresamos al bucle principal de la cnsola.



El modo plano real
------------------
En  el  programa  que  hemos  realizado  no  aprovechamos  las virtudes del modo 
protegido:

	 espacio de memoria virtual de hasta 4GB
	 jerarqua de reas de trabajo o de niveles de privilegio
	 conmutacin de tareas

Para  explotar  las virtudes de los niveles de privilegio y de la conmutacin de
tareas, tendramos que dar un paso ms hacia adelante. Por ahora vamos a conten-
tarnos con un espacio ms amplio de memoria. Si esto es lo que nos interesa, no 
necesitamos correr en modo protegido. Podemos ganar acceso a toda la memoria real 
disponible en nuestro sistema (que seguramente supera 1 MB), pasando a modo plano 
real. En este modo, toda la memoria es tratada como un segmento nico de 4GB para 
cdigo, datos y pila.

Para pasar a modo plano real,  cambiamos primero a modo protegido y establecemos
la GDT de manera que haya un  segmento  que  comience en la direcin 0000:0000 y
que tenga un lmite de 4 gigabytes.  Luego cambiamos de regreso a modo real man-
teniendo la GDT. Para que la operacin tenga xito, previamente debemos habilitar 
la lnea A20 para acceder libremente a toda la memoria alta, por encima de 1MB y 
no dehabilitarla al regresar al modo real.

A continuacin el cdigo que hace el cambio  modo real plano:

   cli                ; Deshabilitar las interrupciones
   push ds            ; Salvar los selectores de modo real
   push es
   xor  eax, eax      ; Parchear el psuedo-descriptor de la GDT
   mov  ax, ds        ; (asumiendo que ds apunta al segmento que contiene la GDT)
   shl  eax, 4
   add  [GDT+2], eax
   lgdt [GDT]         ; Se carga la GDT
   mov  eax, cr0      ; Cambiamos a modo protegido
   inc  ax
   mov  cr0, eax
   mov  bx, flat_data ; Un nico slector en modo protegido
   mov  ds, bx        ; Instalamos el lmite de 4GB
   mov  es, bx
   dec  ax            ; Regresamos a modo real
   mov  cr0, eax
   pop  es            ; Reestablecemos los selectores de modo real
   pop  ds
   sti                ; Rehabilitamos las interrupciones
. . .
           %include "gdtn.inc"
GDT        start_gdt
flat_data  desc 0, 0xFFFFF, D_DATA+D_WRITE+D_BIG_LIM
           end_gdt

Obsrvese que nunca tocamos el DS en modo protegido.

Si no usamos macros ni constantes simblicas, entonces arriba ponemos entonces:

GDT:
dummy	   times 8 byte 0	   
flat_desc  dw  0xF, GDT, 0, 0, 0xFFFF, 0, 0x9200, 0x8F
flat_data  equ 8

Aqui  flata_data  pasa a ser una constante cuyo valor es un ndice al descriptor 
de datos de nuestra GDT, y que puede ser usado como parte del record de nuestros
selectores.

Veamos un par de ejemplos:

Ejemplo 1 
----------

; +------------------------------------------------------------------+
; | Rutina para activar el modo plano del 386 y superiores (acceso   |
; | a 4 Gb en modo real).                                            |
; |                                                                  |
; |      TASM  flat386 /m5                                           |
; |      TLINK flat386 /t /32                                        |
; +------------------------------------------------------------------+
; | Tomado de: Ciriaco Garca de Celis: El Universo Digital del IBM  |
; | PC, AT y PS/2: http://meltingpot.fortunecity.com/uruguay/978/    |
; +------------------------------------------------------------------+


               .386p                   ; slo para 386 o superior

segmento       SEGMENT USE16
               ASSUME CS:segmento, DS:segmento

               ORG   100h
test:
               CALL  flat386           ; activar modo flat
               XOR   AX,AX
               MOV   DS,AX
               MOV   EBX,0B8000h       ; direccin de vdeo absoluta
               MOV   CX,2000
llena_pant:    MOV   BYTE PTR [EBX],'A'
               INC   EBX
               MOV   BYTE PTR [EBX],15
               INC   EBX
               LOOP  llena_pant
               INT   20h               ; fin de programa

; ------------ Esta rutina pasa momentneamente a modo protegido de
;              manera directa (necesita la CPU en modo real). No se
;              activa la lnea A20  (necesario hacerlo directamente
;              o a travs de algn servicio XMS  antes de acceder a
;              las reas de memoria extendida afectadas).

flat386        PROC
               PUSH  DS
               PUSH  ES
               PUSH  EAX
               PUSH  BX
               PUSH  CX
               MOV   CX,SS
               XOR   EAX,EAX
               MOV   AX,CS
               SHL   EAX,4             ; direccin lineal de segmento CS
               ADD   EAX,OFFSET gdt    ; desplazamiento de GDT
               MOV   CS:[gd2],EAX      ; guardar direccin lineal de GDT
               CLI
               LGDT  CS:[gdtr]         ; cargar tabla global de descriptores
               MOV   EAX,CR0
               OR    AL,1              ; bit de modo protegido
               MOV   CR0,EAX           ; pasar a modo protegido
               JMP   SHORT $+2         ; borrar cola de prebsqueda
				       ;
               MOV   BX,gcodl          ; ndice de descriptor en BX
               MOV   DS,BX             ; cargar registro de segmento DS
               MOV   ES,BX             ; ES
               MOV   SS,BX             ; SS
               MOV   FS,BX             ; FS
               MOV   GS,BX             ; GS
               AND   AL,11111110b
               MOV   CR0,EAX           ; volver a modo real
               JMP   SHORT $+2         ; borrar cola de prebsqueda

               MOV   SS,CX
               STI
               POP   CX
               POP   BX
               POP   EAX
               POP   ES
               POP   DS
               RET

gdtr           LABEL QWORD             ; datos para cargar en GDTR
gd1            DW    gdtl-1
gd2            DD    ?

gdt            DB    0,0,0,0,0,0,0,0             ; GDT
gcod           DB    0ffh,0ffh,0,0,0,9fh,0cfh,0
gcodl          EQU   $-OFFSET gdt
gdat           DB    0ffh,0ffh,0,0,0,93h,0cfh,0
gdtl           EQU   $-OFFSET gdt

flat386        ENDP


segmento       ENDS
               END   test



Esta vez el anlisis lo dejaremos al lector. Slo resaltar que esta rutina corre
en DOS y pueden ser usada en consecuencia, aprovechando para DOS punteros  de 32 
bits y todo un espacio plano de memoria por encima de  1 MB.


A modo de recapitulacin
------------------------
Ya hemos visto como es el proceso para poner un procesador compatible i80386+ en
modo protegido o en modo real plano. Sin embargo, no aprovechamos an las virtu-
des de estos modos. Ello requerira antes una informacin adicional, que vamos a
tratar a continuacin.  No  todo  est perdido, ya que los logros obtenidos aqu 
nos proporcionan  un  entorno interesante para la prueba de algoritmos y cdigos
que vamos a estudiar pronto.

Queda pendiente todava ver cmo se estable una IDT, su uso,  implementacin de
un sistema multitareas y revisar los niveles de privilegio en modo protegido.
	 



=================================================================================

APNDICE

NOTA SOBRE EL DESCRIPTOR NULO
-----------------------------
La primera entrada en la GDT se llama descriptor nulo y es ncio en la  GDT,  ya 
que tiene TI=0 e INDEX=0. Como el procesador nunca referencia a este descriptor, 
entonces  pueden  almacenase  datos  en  su  lugar para cualquier propsito. Por 
ejemplo, para poner un puntero a la GDT misma.  La  instrucin  LGDT necesita un 
puntero de seis bytes a la GDT, y el descriptor NULL tiene 8 bytes  que  no  son 
accedidos por el CPU --esto  lo  convierte  en  el  candidato  ideal  para  este 
propsito. 

El  protocolo  normal usado para la un puntero a la GDT, que debe ser cargado en
el registro GDTR es:

     GDT_PTR   DW   GDT_LENGTH-1
               DD   PHYSICAL_GDT_ADDRESS


Luego en el segmento de cdigo:

     LGDT      GDT_PTR


Usar el  descriptor nulo como un puntero a la GDT, simplifica el segmento de da-
tos, y la conceptualizacin de la GDT queda as:

                  +-----------------+
                  |                 |
                  V                 |  Desplazamiento (Offset)
     +------------------------+     |
GDT  |    Puntero a la GDT    |  ---+  00h
     +------------------------+
     |                        |        08h
     +------------------------+
     |    ...  ...  ...  ...  |


Luego en el segmento de cdigo:

     LGDT      GDT

La variable GDT_PTR ya no es necesaria, ya que el descriptor nulo es usado en su 
lugar.  Al  usar  el  descriptor nulo de est manera tenemos una aproximacin ms 
clara al direccionamiento de la GDT. 



NOTA SOBRE BOCHS
----------------
BOCHS  es  un emulator para plataforma IA-32;  la  plataforma  de 32 bits de los
procesadores  compatibles Intel 80386+. Es de distribucin libre y hay versiones
para Windows y Linux. Lo puedes conseguir en: http://bochs.sourceforge.net/.

Una vez que hayas descomprimido e instalado BOCHS, busca el fichero bochsrc.txt,
que seguro estar en el directorio dlxlinux, donde se  halla la distribucin DLX 
de LINUX que se pasa como una especie de DEMO.

Para  ejecutar  IMAGE.IMG,  por  ejemplo, puedes copiar este archivo en el mismo
directorio de trabajo e BOCHS. Tambin puedes copiar  bochsrc.txt  en  el  mismo
directorio.

Editas bochsrc.txt asegurndote que tenga los siguientes datos:

# what disk images will be used 
floppya: 1_44=image.img, status=inserted
floppyb: 1_44=floppyb.img, status=inserted
newharddrivesupport: enabled=1

# choose the boot disk.
boot: a

Lo dems lo dejas igual. Lo que haces ac es decir a BOCHS que  en  su  floppya
virtual cargue "image.img"  (floppya: 1_44=image.img, status=inserted) e inicie 
desde "a" (boot: a).

Despus  slo tienes que ejecutar BOCHS.EXE y pulsar ENTER para aceptar los va-
lores por  defecto dee BOCHS,  y ya,  nuestra  imagen estar ejecutndose en el 
entorno de BOCHS.



------------------------------------------------- TO BE CONTINUED ----------->

emailme: numit_or@cantv.net
webZ: http://mipagina.cantv.net/numetorl869/
      http://oberon.spaceports.com/~tutorial/aks/



