Informática
Implantación de DMT (Discrete Multitone Technology)
Así se ha implementado DMT
En este capítulo vamos a mostrar los módulos en los que se ha dividido DMT, la función de cada uno de ellos y cómo se ha implementado. El objetivo principal de este capítulo es que el usuario comprenda perfectamente como se ha realizado DMT y pueda construirse, si desea, su propio Multitasker.
El módulo Test16.asm
Este módulo contiene tres funciones, donde dos de ellas se encargan de comprobar el tipo y el estado del procesador para ver si DMT puede ser ejecutado en esa máquina. Estas funciones se ejecutan estando en el modo real, antes de entrar en el modo protegido. La tercera de las funciones de este módulo únicamente tiene como misión la finalización de DMT debido a una condición que no se cumple en la máquina donde va a ser ejecutado. Estos tres módulos son explicados a continuación:
Función VerProcesador
Función: VerProcesador.
Descripción: Comprueba si el procesador es un 80386 o superior.
Entrada: Nada
Salida: Si CF = 0 el procesador es un 80386 o superior
Si CF = 1 el procesador es inferior a un 80386
Esta función comprueba si la máquina en la que se está ejecutando DMT es un 80386 o superior. Esta función es necesaria debido a que se necesita un ordenador que proporcione mecanismos de multitarea y protección para realizar el Multitasker. El modo protegido del 80386 y superiores nos proporciona estos dos mecanismos.
Aunque el 80286 incluye el modo protegido, este es bastante inferior al del 80386 por las siguientes razones:
El 80286 posee sólo registros de 16 bits: lo que supone un menor direccionamiento de memoria por parte del procesador.
No posee el modo V86: lo que imposibilita la ejecución de programas que fueron creados para el modo real bajo el modo protegido.
No posee mecanismo de paginado (directorio y tablas de páginas): no permitiendo la instalación de un sistema de memoria virtual por páginas.
Una vez entrado en el modo protegido no se puede volver al modo real, a no ser que se haga un reset del procesador.
Aunque existen algunas ligeras diferencias más, las más importantes son éstas y sobre todo la segunda mencionada anteriormente.
Un Multitasker tiene como función ejecutar programas que fueron diseñados para el modo real del MS-DOS, por tanto necesitamos que el procesador nos permita algún mecanismo de ejecutar estos programas bajo un telón donde exista la multitarea y la protección entre tareas. Este modo de trabajo nos lo ofrece el 80386 a través del modo virtual 8086.
Para determinar el tipo de procesador, INTEL propone un juego de instrucciones de manipulación del registro FLAGS que determinarán si el procesador es un 8086, 80286, 80386, 80486, Pentium, etc. Para ver si un procesador es un 80386, INTEL nos aconseja utilizar el código que se encuentra en la figura 8.1.
Tras ejecutar el código de la figura 8.1, si el contenido del registro AX es igual al contenido del registro BX, nos es encontramos frente a un 80386 o superior, con lo que podremos seguir chequeando la máquina para ver si cumple todas las condiciones para ejecutar DMT.
Figura 8.1. Juego de instrucciones propuesto por INTEL
Función CheqV86
Función: CheqV86.
Descripción: Comprueba si el procesador se está ejecutando en modo V86.
Entrada: Nada.
Salida: Si CF = 1 el procesador se encuentra en modo V86.
Si CF = 0 el procesador está en modo real.
Antes de entrar en el modo protegido debemos de comprobar si el procesador está en modo V86. Si no realizamos tal comprobación y el procesador está en modo V86, al intentar ejecutar la instrucción que pasa el procesador a modo protegido, se producirá una excepción por el 80386 y se tomará el control por el programa que cambió el procesador a modo V86, que seguramente decida eliminar nuestro programa de la memoria de una forma poco elegante.
Para comprobar si el procesador se encuentra en modo V86, seguramente se piense rápidamente en comprobar el bit VM del registro EFLAGS se encuentra a 1. Esto es un error, ya que desde el modo V86 no tenemos acceso a tal bit y cualquier lectura del registro EFLAGS indicará el bit VM a cero. La forma de comprobar si el procesador se encuentra en modo V86 es leer el contenido del registro CR0 y ver si el bit PE (Protection Enable) se encuentra a 1.
Procedimiento Abortar
Procedimiento: Abortar.
Descripción: Termina el programa debido a que no es posible ejecutar DMT en esa máquina.
Entrada: Nada.
Este procedimiento se encarga de mostrar un mensaje por pantalla indicando al usuario que no es posible ejecutar DMT en su máquina y finalizar el programa realizando una llamada al MS-DOS.
Para finalizar el programa se ha preferido utilizar el servicio 4Ch del DOS, debido a que este es el método que se aconseja de finalizar un programa en las últimas versiones del DOS, quedando cualquier otro tipo de finalización de un programa DOS obsoleta.
El módulo Pantalla.asm
Este módulo consta de dos procedimientos encargados de imprimir un mensaje por pantalla y de borrar la pantalla. Estos procedimientos funcionan sólo cuando la pantalla está en modo texto, debido a que se hace un acceso directo a pantalla.
Procedimiento Write
Procedimiento: Write
Descripción: Imprime un mensaje en la posición actual del cursor
Entrada: DS:SI = puntero a la cadena que se desea imprimir
BH = atributos del texto (color, parpadeo, etc.)
La función que se ha querido imitar aquí es el servicio “Imprimir cadena” del DOS, pero con mayores prestaciones como puede ser la salida de un texto por pantalla en diferentes colores y mayor velocidad de impresión.
Para imprimir una cadena que tenemos almacenada en una posición de memoria, debemos de realizar un acceso directo a la RAM de vídeo si no queremos usar ninguno de los servicios ofrecidos por el sistema operativo o la BIOS. Como sólo vamos a imprimir cadenas de caracteres en modo texto, solo nos tendremos que preocupar en cómo se puede imprimir un carácter por pantalla en modo texto.
La RAM de vídeo es un segmento de memoria de donde es leída la información que se va a llevar a pantalla, por tanto, cualquier acceso a esta zona de memoria producirá un cambio en el contenido de la pantalla. La RAM de vídeo para los formatos textos comienza en la dirección B800:0000h. A partir de esta dirección de memoria, cada pareja de bytes componen un carácter en pantalla. El primer byte indica el código ASCII del carácter que se va a imprimir en pantalla, el segundo byte indica los atributos con los que se desea que aparezca el carácter (color, parpadeo, etc.).
Para sacar una cadena por pantalla, tan solo deberemos de ir leyendo carácter a carácter de la cadena e ir insertándolos en la RAM de vídeo acompañados de su byte de atributos.
El procedimiento Write realiza exactamente ese funcionamiento, añadiendo algunas funciones extras:
-
Si el carácter que se va a imprimir tiene como código ASCII el valor 13, se realiza un retorno de carro, es decir, se salta a la línea siguiente.
-
Si el carácter que se va a imprimir tiene como código ASCII el valor 36 ó el carácter `$' indicará fin de la cadena a imprimir, por lo que el procedimiento terminará.
Para realizar múltiples salidas de textos por pantalla, se ha respetado la posición del cursor en pantalla, por lo que habrá que leer la posición del cursor antes de llevar la cadena a pantalla y posicionarlo al final de la cadena una vez que esta se haya sacado por pantalla.
Procedimiento Cls
Procedimiento: CLS.
Descripción: Borra la pantalla en modo texto.
Entrada: Nada.
Este simple procedimiento se encarga de borrar el contenido de la pantalla en modo texto. Para realizar tal función, debemos de rellenar el contenido de la RAM de vídeo desde la dirección B800:0000h hasta la dirección B800:1000h con espacios en blancos.
El módulo DMAfunc.asm
Este módulo es encargado de ver si hay instalado en el ordenador un driver virtual de DMA. Un driver virtual DMA es ofrecido por los servidores VCPI y DPMI y son utilizados para resolver los problemas que ofrece el DMA en el modo protegido.
El driver virtual DMA (VDMA) posee una especificación (VDS) dada por fabricantes de software y provee de servicios que usan el DMA para realizar transferencias de datos en el modo protegido con el mecanismo de paginación activado.
Como se comentó en el capítulo 2, el chip DMA no comprueba si el procesador está trabajando en modo protegido para realizar las transferencias de datos de un dispositivo a memoria y viceversa. Cuando el procesador está en modo protegido y activa el mecanismo de paginación, las direcciones lineales son mapeadas a direcciones físicas diferentes, por lo que el DMA accederá a direcciones físicas de memoria cuando una tarea le manda una dirección lógica para realizar la transferencia. Un driver VDMA resuelve todos estos problemas con el DMA a través de diversos servicios y estructuras de memoria que ofrece al programador.
El único problema es que DMT no funciona bajo servidores VCPI o DPMI, por lo que no se han podido aprovechar los servicios ofrecidos por la VDS para realizar transferencias con DMA desde el modo protegido. Las transferencias con DMA han sido posibles en DMT a través del bloqueo del chip DMA y convirtiendo las direcciones lineales a físicas antes de pasárselas a este chip.
Función VDMAinst
Función: VDMAinst
Descripción: Comprueba si hay un driver virtual de DMA instalado.
Entrada: Nada.
Salida: Si CF = 0 el driver VDMA está instalado.
Si CF = 1 el dirver VDMA no está instalado.
Esta función realiza una simple llamada a un servicio ofrecido por VCPI y DPMI para comprobar la existencia de este driver. El por qué se comprueba si hay instalado un driver VDMA si no es posible ejecutar DMT bajo VCPI o DPMI es debido a que inicialmente se intentó realizar DMT bajo un servidor VCPI y se hicieron algunas comprobaciones extras para VCPI. Tras cambiar de opinión y abandonar el VCPI, decidí dejar algunas de estas funciones incluidas en el código de DMT por si algún día decidía incluir el VCPI bajo DMT.
El módulo Mensajes.asm
Este módulo contiene dos funciones de las cuales una de ellas se encarga de sacar el Copyright de DMT por pantalla y la otra imprime por pantalla las características del ordenador.
Procedimiento Copyright
Procedimiento: Copyright
Descripción: Imprime por pantalla un cuadro con el nombre del proyecto y su autor
Entrada: Nada
Este procedimiento utiliza la función definida anteriormente (Cls y Write) para borrar la pantalla y sacar cadenas de caracteres por pantalla.
Función Testear
Función: Testear.
Descripción: Comprueba si se puede ejecutar DMT en la máquina actual.
Entrada: Nada.
Salida: Si CF = 1 entonces no es posible ejecutar DMT.
Si CF = 0 entonces es posible ejecutar DMT.
Esta función realiza diversas comprobaciones con respecto al procesador y los drivers que hay instalado en el ordenador.
Los pasos seguidos por esta función se definen a continuación:
Comprobar si el procesador es un 80386 o superior: A través de la función definida anteriormente podemos observar si el procesador es un 80386 o superior. Si el procesador es menor que un 80386, saldremos del procedimiento indicando a través de la bandera de acarreo que no es posible ejecutar DMT en la máquina actual.
Comprobar si hay un servidor VCPI instalado: El resultado de esta comprobación no es tenida en cuenta por DMT, debido a que no será posible ejecutar DMT bajo este servidor. Dicha comprobación será utilizada para versiones futuras de DMT.
Comprobar si el procesador está trabajando en modo V86: Para saber si el procesador se encuentra en modo V86 utilizamos la función CheqV86 del módulo test16.asm. En caso de que el procesador se encuentre en modo V86, no será posible ejecutar DMT debido a que no podemos reiniciar el modo protegido.
Comprobar si hay un driver virtual DMA instalado: Esta comprobación se realiza para saber si podemos utilizar los servicios ofrecidos por un driver VDMA bajo configuraciones VCPI o DPMI.
En caso de que todas estas restricciones se hayan cumplido, esta función indicará a través de la bandera de acarreo que DMT puede ser ejecutado en la máquina actual.
El módulo DMT.asm
Este es el módulo principal del programa, encargado de conmutar el procesador al modo protegido e inicializar todas las estructuras necesarias para la ejecución de DMT. Este módulo es bastante complejo y extenso, por lo que lo iremos explicando a través de una serie de pasos que siguen el flujo secuencial de instrucciones que componen el módulo.
Paso 1: Definición de variables para el modo real
Antes de entrar al modo protegido, el procesador se encuentra ejecutándose en el modo real, por lo que deberemos de mantener una serie de variables para: comprobar si es posible ejecutar DMT bajo la máquina actual, guardar vectores de interrupción que se van a modificar, tipo de configuración, etc. Estas variables se definirán dentro del segmento de código para mantener todo el código y datos del modo real en un único segmento y permitir un acceso más fácil a este segmento desde el modo protegido.
Paso 2: Definición de mensajes utilizados en el modo real
Antes de inicializar el modo protegido necesitamos realizar ciertas comprobaciones acerca de la computadora, para ver si DMT puede ser ejecutado en ella. En el módulo mensajes.asm se realizan ciertas comprobaciones que comprueban el estado del procesador. En este módulo se realizan algunas comprobaciones más antes de entrar al modo protegido.
Todos los mensajes que indican al usuario el estado de la computadora, antes de entrar al modo protegido, son definidos en este módulo.
Paso 3: Comprobar si DMT puede ser ejecutado
DMT permite recibir un argumento desde la línea de comando, la opción “/d”. Esta opción indicará a DMT que todas las tareas pueden acceder a la vez a la pantalla, de este modo podremos ver como todas las tareas se ejecutan de forma concurrente.
El paso siguiente es el de mostrar el Copyright por pantalla y luego realizamos el chequeo de la computadora para ver si DMT puede ser ejecutado. Las condiciones que ha de tener la computadora para que DMT pueda ejecutarse son las siguientes:
-
Tener un procesador igual o superior a un 80386.
-
Que el procesador se encuentre trabajando en el modo real.
-
Que no haya instalado ningún gestor de memoria.
-
Que haya suficiente memoria extendida libre.
-
Que se pueda liberar la línea de direcciones A20.
Las dos primeras condiciones se pueden comprobar a través de una llamada a la función realizada anteriormente (función testear).
Para comprobar que no hay ningún gestor de memoria instalado hay que comprobar que no se encuentra un driver de XMS en memoria. Tan sólo hay que comprobar que no exista un driver XMS y no un driver EMS, ya que los gestores de memoria que proporcionan un driver EMS conmutan el procesador a modo V86, lo que no hubiera superado la condición de tener el procesador en el modo real. Para saber si existe un driver XMS instalado hay que realizar una llamada al servicio 4300h del multiplexor (interrupción 2fh) y comprobar el resultado devuelto por tal servicio en el registro AL del procesador. Si el valor devuelto en AL es igual a 80h entonces existe un driver XMS instalado, lo que supondrá que DMT no pueda ser ejecutado mientras exista ese driver en memoria.
El siguiente paso es habilitar la línea de direcciones A20 para que podamos acceder más allá del primer Mbyte de memoria física. Para liberar la línea A20 debemos hacerlo a través del controlador del teclado ya que está íntimamente ligado a esta línea. En caso de que se haya podido liberar la línea A20, podemos seguir con la ejecución de DMT.
El último paso referente a las comprobaciones realizadas es ver cuanta memoria extendida se encuentra disponible a través de la BIOS. Toda la memoria que haya libre será solicitada por DMT para que ninguna de las tareas ejecutadas bajo él puedan acceder a la memoria extendida a través de la BIOS y pueda machacar las zonas de memoria correspondientes a otras tareas. De este modo será DMT el único que podrá gestionar la memoria libre que se encuentra en el ordenador.
Una vez que solicitamos toda la memoria extendida libre calculamos cuantas tareas podrán ser ejecutadas bajo DMT. Como cada tarea ocupa un Mbyte, bastará con dividir la memoria disponible entre un Mbyte y obtendremos el número de tareas que podrán ser ejecutadas en DMT. El numero de tareas obtenido se utilizará para mostrarle al usuario por pantalla cuantas tareas puede ejecutar y además será utilizada por DMT para realizar el barrido de teclas de función. Este barrido de teclas se explicará posteriormente.
Paso 4: Redireccionar algunos vectores de interrupción
Para crear cada una de las tareas, DMT copiará el contenido del primer Mbyte de memoria física a la zona de memoria correspondiente a la tarea que se va a crear. Por tanto si ahora modificamos un vector de interrupción, antes de entrar al modo protegido, conseguiremos que ese vector de interrupción quede modificado para todas las tareas que vaya a crear DMT.
Los vectores de interrupción que redirecciona DMT, sin incluir los que redirecciona en el modo protegido, son:
-
Interrupción 15h: Servicios del sistema de la BIOS.
-
Interrupción 13h: Servicios de disco de la BIOS.
-
Interrupción 33h: Servicios del ratón.
La interrupción 15h es redireccionada para manipular el servicio 88h “Obtener memoria extendida libre”. Cuando una tarea solicite este servicio para ver de cuanta memoria extendida puede obtener, se le devolverá un código señalando que no hay memoria extendida libre. En tal caso, la tarea tomará sus propias decisiones para seguir ejecutándose o no sin memoria extendida.
La interrupción 13h es redireccionada por DMT para indicar cuando se está ejecutando esta interrupción. Cuando una tarea está realizando una operación de disco y DMT provoca una conmutación de tarea, por haber finalizado su porción de tiempo, se puede llegar a grandes perdidas de datos en disco. Esto es así porque el procesador cambia el espacio de direcciones visibles al de la nueva tarea, con lo que el disco leerá o escribirá datos de una zona de memoria que no se corresponde a la que la primera tarea le indicó. Por tanto debemos de tener un mecanismo que nos indique cuando se está realizando una operación de disco. Una de las soluciones es interceptar la interrupción de disco y tener una variable que indique “disco activo” cada vez que se llama a esta interrupción. Cuando el servicio de disco ha finalizado se vuelve a poner esta variable a “disco inactivo”. A través de este simple mecanismo, DMT puede saber cuando una tarea está realizando una operación de disco y, por tanto, sabrá que no puede interrumpirla.
La interrupción 33h es redireccionada debido a que se observó un problema cuando dos tareas solicitaban el servicio de inicialización del ratón al mismo tiempo. Cuando dos tareas inicializaban el ratón al mismo tiempo, ambas se quedaban bloqueadas indefinidamente, y a veces se deshacía el bloqueo cuando una tercera tarea inicializaba más tarde el ratón. La razón de porque esto ocurre no he llegado a saberlo aún, aunque si he encontrado una solución para que este bloqueo no se dé. Para evitar este bloqueo entre tareas he optado por utilizar el mismo mecanismo que en el mostrado para solucionar el problema del disco. Es decir, tener una variable que indique “ratón ocupado” cada vez que se solicita un servicio del ratón, y poner esa misma variable a “ratón libre” cuando el servicio haya terminado.
Paso 5: Rellenar la GDT
La tabla de descriptores global (GDT) de DMT posee los siguientes descriptores para acceder a la memoria desde el modo protegido:
-
Descriptor nulo.
-
Descriptor GDTcode32.
-
Descriptor GDTdata32.
-
Descriptor GDTcode16.
-
Descriptor GDTdata16.
-
Descriptor GDTzero16.
-
Descriptor GDTflat32.
-
Descriptor GDText.
-
Descriptor GDTtask0.
-
Descriptor GDTtask1.
-
Descriptor GDTtask2.
-
.....
-
Descriptor GDTtaskN.
En la siguiente tabla puede observarse una tabla resumen con el contenido de la GDT:
Nombre del descriptor | Descripción del descriptor |
GDTnulo | Descriptor nulo necesario por el 80386. |
GDTcode32 | Base: 4xxxxxx (4Mbyte = 2ª entrada del directorio de páginas. Límite: 0FFFFFh Atributos: No granular, 32bits, DPL=0, Ejecución, Presente. |
GDTdata32 | Base: 4xxxxxx Límite: 0FFFFFh Atributos: No granular, 32bits, DPL=0, Lectura Escritura, Presente. |
GDTcode16 | Base: Segmento de código de 16 bits (codigo16) Límite: 0FFFFh (64 Kbytes) Atributos: No granular, 16, DPL=0, Ejecución, Presente. |
GDTdata16 | Base: Segmento de código de 16 bits (codigo16) Límite: 0FFFFh (64 Kbytes) Atributos: No granular, 16, DPL=0, Lectura, Escritura. |
GDTzero16 | Base: 4xxxxxxh Límite: 0FFFFFh (1 Mbyte) Atributos: No granular, 16bits, DPL=0, Lectura Escritura, Presente. |
GDTflat32 | Base: 0 (Acceso a la memoria de cada tarea) Límite: 0FFFFFh (4 Gbytes, al tener granularidad) Atributos: Granular, 32bits, DPL=0, Lectura Escritura, Presente. |
GDText | Base: xxxxxx (dirección de comienzo de la memoria extendida) Límite: 0FFFFFh Atributos: Granular, 32bits, DPL=0, Lectura Escritura, Presente. |
GDTtask0 | Base: 4xxxxxx (TSS para DMT) Límite: 0FFFFh Atributos: No granular, DPL=0, Lectura Escritura, Presente. |
GDTtaskN | Base: 4xxxxxx (TSS para las tareas V86) Límite: 0FFFFh Atributos: No granular, DPL=0, Lectura Escritura. |
Cada uno de estos descriptores tiene asociada un área de memoria, que puede ser traslapada con la de otros descriptores, con funciones específicas. La función de cada uno de estos descriptores se muestra a continuación.
Descriptor nulo:
Este descriptor no tiene ninguna función bajo DMT. En realidad, todos los programas que se realizan bajo el modo protegido ha de tener un descriptor nulo y se ha de colocar en la primera entrada de la GDT. El descriptor nulo se caracteriza por tener todos los valores de su entrada a cero. El 80386 necesita la presencia de este descriptor para poder manipular la GDT en el modo protegido.
Descriptor GDTcode32:
Este descriptor referencia el segmento de código protegido de DMT. Bajo este segmento de código se encuentra todo el código en modo protegido encargado de llevar a cabo el funcionamiento de DMT.
La base del segmento, es decir la dirección de comienzo empieza en el cuarto Mbyte de memoria + la dirección de comienzo dentro del primer Mbyte de memoria. Esto puede parecer un poco complicado, pero se entenderá fácilmente si decimos que la razón por la que se ha sumado 4 Mbytes a la base del segmento es debido a que queremos que el segmento de código se mapee a través de la segunda entrada del directorio de páginas. En el apartado La paginación en DMT se explicará esto más detalladamente.
Continuando con los atributos que posee el segmento del descriptor GDTcode32, hay que señalar que:
-
Es de sólo lectura y ejecución: condiciones que ha de cumplir un segmento de código.
-
El segmento será del tipo 32 bits: lo que supondrá que todas las instrucciones y operandos serán por defecto de 32 bits.
-
El segmento es del tipo no granular: Al ser no granular, el límite del segmento coincidirá con el tamaño del atributo Límite del descriptor.
-
Nivel de privilegios 0: El segmento de código poseerá el nivel de prioridad supervisor, con lo que podrá realizar cualquier acción sobre el sistema.
-
Segmento presente: Indicamos que el segmento se encuentra en memoria.
-
Limite del segmento: 1 Mbyte.
Para referenciar este segmento debemos de usar un selector que se ha definido y tiene como nombre SelCode32. El selector SelCode32 tiene como valor de selector el número 8h, en binario 1000b (descriptor 1, tabla GDT (0), nivel de prioridad 00).
Descriptor GDTdata32:
Este descriptor es un alias del segmento de código, es decir, mapea exactamente el mismo segmento que el descriptor GDTcode32. La razón de declarar un alias es debido a que en un segmento de código está prohibido escribir, por lo que necesitamos declarar otro descriptor que mapee las mismas direcciones que el segmento de código, pero que tenga el atributo de escritura activado para que podamos escribir sobre él.
Los atributos que definen el segmento del descriptor GDTdata32 son los siguientes:
-
Base del segmento: 4 Mbyte + dirección del segmento de código protegido dentro del primer Mbyte. Se han sumado 4 Mbytes por el motivo anterior. La dirección del segmento de código protegido dentro del primer Mbyte será rellenada en la GDT mientras la estamos inicializando desde el modo real.
-
Límite: 1 Mbyte.
-
No granular: El límite del segmento seguirá siendo de un Mbyte.
-
Segmento de 32 bits: Todos los datos que hay en segmento se suponen que tienen una longitud de 32 bits por defecto.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura/escritura: Se podrá leer cualquier dato que se encuentra en dicho segmento y se podrán modificar sus valores.
Para referenciar este segmento lo debemos de hacer a través del selector SelData32, cuyo valor de selector es el 10h (descriptor 2, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTcode16:
Este descriptor referencia al segmento de código que DMT utiliza antes de entrar al modo protegido. Este segmento corresponde al código que DMT ejecuta nada mas empezar a ejecutarse (en el modo real). Antes de entrar a modo protegido de 32 bits, es decir, antes de entrar en el segmento mapeado por GDTcode32, necesitamos entrar en un segmento de código en modo protegido de 16 bits. Ese segmento de código en modo protegido de 16 bits estará incluido dentro del segmento que mapea el descriptor GDTcode16.
El segmento mapeado por GDTcode16 posee los siguientes atributos:
-
Base: dirección del segmento de código de 16 bits dentro del primer Mbyte de memoria. Es decir, la dirección base coincide con la dirección en la que el MS-DOS coloca el programa DMT al ejecutarlo.
-
Límite: 64 Kbytes, ya que en modo real no puede haber un segmento mayor de 64 Kbytes de memoria.
-
No granular: El límite del segmento seguirá siendo de 64 Kbytes.
-
Segmento de 16 bits: Todos los operandos de las instrucciones se supondrán que tienen un tamaño de 16 bits.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura y ejecución: Indicará que es un segmento de ejecución.
Para acceder a este segmento necesitamos usar el selector definido en DMT llamado SelCode16, cuyo valor de selector es 18h (descriptor 3, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTdata16:
Este descriptor es utilizado como alias para acceder a los datos que se encuentran dentro del segmento GDTcode16 y poder realizar modificaciones sobre ellos.
Los atributos que poseen este segmento son:
-
Base: dirección del segmento de código de 16 bits dentro del primer Mbyte de memoria. Es decir, la dirección base coincide con la dirección en la que el MS-DOS coloca el programa DMT al ejecutarlo.
-
Límite: 64 Kbytes, ya que en modo real no puede haber un segmento mayor de 64 Kbytes de memoria.
-
No granular: El límite del segmento seguirá siendo de 64 Kbytes.
-
Segmento de 16 bits: Todos los datos que hay en segmento se suponen que tienen una longitud de 16 bits.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura y escritura: Podremos acceder a cualquier variable que se encuentre dentro de este segmento y modificar su valor.
Para acceder a este segmento necesitamos usar el selector definido en DMT llamado SelData16, cuyo valor de selector es 20h (descriptor 4, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTzero16:
Bajo este descriptor se encuentra un segmento de datos el cual mapea el primer Mbyte de memoria física, es decir, mapea toda la memoria en la que se encuentra cargado el MS-DOS y DMT. La razón por la que hemos definido un segmento que mapee el primer Mbyte de memoria física, es debido a que necesitamos tener acceso a este Mbyte para copiarlo a la zona de memoria correspondiente a cada una de las tareas que vaya a crear DMT.
Las características que posee este segmento son las siguientes:
-
Base: 4 Mbyte. A través del cuarto Mbyte podremos acceder a la segunda entrada del directorio de páginas. Bajo esta segunda entrada se encontrará la dirección de una tabla de páginas que mapea el primer Mbyte de memoria física, por lo que la dirección lógica 4 Mbyte corresponde a la dirección física 0.
-
Límite: 1 Mbyte, para acceder a todo el primer Mbyte de memoria física.
-
No granular: El límite del segmento seguirá siendo de un Mbyte.
-
Segmento de 16 bits: Todos los datos que hay en segmento se suponen que tienen una longitud de 16 bits.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura y escritura: Podremos acceder a cualquier variable que se encuentre dentro de este segmento y modificar su valor.
Para acceder a este segmento necesitamos usar el selector definido en DMT llamado SelZero16, cuyo valor de selector es 28h (descriptor 5, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTflat32
Este descriptor se ha creado para acceder a la zona de memoria correspondiente a cada una de las tareas que DMT ejecuta. Además se utiliza para poder acceder a cualquier dirección dentro del espacio de direcciones lineales.
Las características que presenta el segmento del descriptor GDTflat32 son las siguientes:
-
Base: dirección lineal 0, para acceder a la zona de memoria correspondiente a cada tarea. Las tareas formarán sus direcciones lineales a partir de la dirección lógica 0. A través del directorio de páginas, la primera entrada apuntará a una tabla de páginas que será la que mapee las direcciones de cada tarea. Cada tarea tendrá su propio directorio de páginas, que contendrá en la primera entrada la dirección de una tabla de páginas que mapea sus direcciones lógicas. La segunda entrada de cada uno de los directorios de páginas apuntará a una tabla de páginas que contiene el código de DMT (por eso los segmentos de DMT se mapean a partir de la dirección lógica 4 Mbytes). Como el segmento GDTflat32 tiene su base en la dirección lógica 0, corresponderá a la dirección 0 de cada una de las tareas.
-
Límite: 4 Gigabytes. Podremos acceder a toda la memoria física disponible en el ordenador a través de este segmento.
-
Granular: Segmento de tipo granular, para poder dar al segmento la longitud de 4 Gbytes.
-
Segmento de 32 bits: Todos los datos que hay en segmento se suponen que tienen una longitud de 32 bits.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura y escritura: Podremos acceder a cualquier variable que se encuentre dentro de este segmento y modificar su valor.
Para acceder a este segmento necesitamos usar el selector definido en DMT llamado SelFlat32, cuyo valor de selector es 30h (descriptor 6, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDText
Este descriptor es utilizado para acceder al segmento de memoria donde comienza la memoria extendida. Aunque podríamos haber utilizado el descriptor GDTflat32 para acceder a la memoria extendida, hemos preferido crear otro descriptor para realizar un acceso más simple a esta memoria.
Los atributos que definen a este segmento son los siguientes:
-
Base: Depende de la dirección donde comienza la memoria extendida libre. Normalmente será la dirección donde comienza el segundo Mbyte de memoria física.
-
Límite: Depende del tamaño de la memoria extendida.
-
Granular: Segmento de tipo granular, para que se pueda acceder a cualquier dirección de memoria extendida, independientemente del tamaño de ésta.
-
Segmento de 32 bits: Todos los datos que hay en segmento se suponen que tienen una longitud de 32 bits.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
-
Segmento de lectura y escritura: Podremos acceder a cualquier variable que se encuentre dentro de este segmento y modificar su valor.
Para acceder a este segmento necesitamos usar el selector definido en DMT llamado SelExt, cuyo valor de selector es 38h (descriptor 7, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTtask0
Este descriptor referencia al segmento correspondiente al TSS de la tarea cero. La tarea cero corresponde a aquella que llevó al procesador al modo protegido, en nuestro caso la tarea número cero será DMT.
El segmento TSS de la tarea 0 posee los siguientes atributos:
-
Base: 4 Mbyte + dirección donde se encuentra el TSS de la tarea 0. Se vuelve a sumar 4 Mbytes a la dirección para acceder a la segunda entrada del directorio de páginas, que apuntará a una tabla de páginas que mapeará el primer Mbyte de memoria física.
-
Límite: 64Kb, aunque el TSS ocupa mucho menos damos por defecto 64Kb para cada TSS de cada una de las tareas.
-
No granular: El límite del segmento seguirá siendo de 64 Kbytes.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero. Ninguna tarea con un nivel de privilegios menor podrá provocar una conmutación de tareas, ya que no tiene acceso a los descriptores de cada uno de los TSS.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
Para llevar a cabo una conmutación de tareas a la tarea 0, debemos de utilizar un selector definido por DMT cuyo valor de selector es 40h (descriptor 8, tabla GDT, nivel de prioridad del solicitante 0).
Descriptor GDTtaskN
En realidad, el descriptor GDTtaskN no existe, sino que representa a un conjunto de descriptores del TSS de cada una de las tareas creadas por DMT. Así la tarea número uno utilizará el descriptor GDTtask1, la tarea número 2 utilizará el descriptor GDTtask2, etc.
Las características de estos segmentos del TSS para cada una de las tareas creadas por DMT son:
-
Base: 4 Mbyte + dirección donde se encuentra el TSS de cada tarea.
-
Límite: 64Kb.
-
No granular: El límite del segmento seguirá siendo de 64 Kbytes.
-
Nivel de prioridad 0: Indicará que es un segmento que solo puede ser usado por un usuario o segmento de código que tenga nivel de prioridad cero. Ninguna tarea con un nivel de privilegios menor podrá provocar una conmutación de tareas, ya que no tiene acceso a los descriptores de cada uno de los TSS.
-
Segmento presente: Indicamos que el segmento se encuentra cargado en memoria y es posible acceder a él.
Para provocar una conmutación a cada una de las tareas creadas por DMT, debemos de referenciar el selector correspondiente al descriptor de cada una de ellas. La tarea 1 tendrá como valor de selector el número 48h, la tarea 2 tendrá como selector el 50h, la tarea 3 tendrá el selector 58h, la tarea 4 el 60h, etc.
Todos estos descriptores de la GDT han sido rellenados a través del ensamblador, es decir, los hemos inicializado a través de la definición de variables en ensamblador y asignándole un valor por defecto. La siguiente secuencia en ensamblador muestra como se ha definido la GDT en DMT:
GDT label dword
GDTnulo dq 0
GDTcode32 db 0ffh, 0ffh, 0, 0, 40h, 9ah, 4fh, 0
GDTdata32 db 0ffh, 0ffh, 0, 0, 40h, 92h, 4fh, 0
GDTcode16 db 0ffh, 0ffh, 0, 0, 0, 9ah, 0, 0
GDTdata16 db 0ffh, 0ffh, 0, 0, 0, 92h, 0, 0
GDTzero16 db 0ffh, 0ffh, 0, 0, 40h, 92h, 0cfh, 0
GDTflat32 db 0ffh, 0ffh, 0, 0, 0, 92h, 0cfh, 0
GDText db 0ffh, 0ffh, 0, 0, 80h, 92h, 8fh, 0
GDTtask0 db 0ffh, 0ffh, 0, 0, 40h, 89h, 0, 0
GDTtaskN db NumTareas*8 dup(0)
Algunos valores de la GDT aún no han sido rellenados, estos valores son los que se corresponden con el valor Base de algunos segmentos como GDTcode32, GDTdata32, GDTcode16 y GDTdata16, que dependen de la posición de memoria donde el MS-DOS haya colocado DMT. El siguiente paso por tanto es rellenar estos valores de descriptores para que la GDT pueda ser usada en el modo protegido.
Para encontrar el valor base de los segmentos anteriores debemos de obtener la dirección de comienzo de DMT en memoria. Como los segmentos de los descriptores GDTcode16 y GDTdata16 se corresponden con el comienzo del código de arranque de DMT, debemos de coger la dirección del segmento donde comienza este código de arranque y pasar esta dirección a lineal. Para pasar a lineal una dirección en el modo real tan solo debemos de multiplicar por 16. Una vez obtenida la dirección lineal debemos de rellenar el atributo Base de los descriptores GDTcode16 y GDTdata16 con el valor de esa dirección.
Para rellenar el valor Base de los descriptores GDTdata32 y GDTcode32 debemos seguir el proceso anterior, pero sabiendo que la dirección del segmento de estos descriptores se corresponde con el comienzo del segmento protegido de 32 bits. En DMT este segmento de 32 bits tiene como nombre codigo32.
Paso 6: Rellenar la IDT
El 80386 consulta la IDT cada vez que se produce una interrupción. La IDT contiene un conjunto de descriptores y cada uno de ellos se encarga de tratar a una excepción o interrupción específica. Las primeras entradas de la IDT están definidas por el 80386 y el programador debe de colocar una rutina de tratamiento para cada una de ellas, es decir, cada vez que se produce un excepción por falta de página, el 80386 lee el descriptor número 14 para buscar la dirección del segmento donde comienza la rutina de tratamiento, que el programador ha elaborado, para tratar el fallo de página. Por tanto, el programador ha de saber qué número de descriptor corresponde a cada excepción y elaborar una rutina de tratamiento de estas excepciones.
Bajo DMT la IDT está formada por todos los descriptores definidos por el 80386 más un conjunto de descriptores que son los encargados de gestionar las interrupciones enmascarables que son producidas por las líneas IRQ del procesador.
Las líneas IRQ del procesador le indican a éste cuando se produce una interrupción por alguno de los componentes del ordenador. Por ejemplo, cuando se pulsa una tecla se activa la línea IRQ1, indicando al procesador que se ha producido un evento en el teclado. El procesador llamará a una rutina, que ha de tener en memoria, para gestionar el evento del teclado. Así cada componente hardware tendrá una línea IRQ disponible para “llamar” al procesador y que éste le atienda.
El controlador de interrupciones enmascarables (PIC) se encarga de gestionar todas estas interrupciones. El PIC se puede reprogramar para que las IRQ se correspondan con una interrupción diferente. Por ejemplo, cada vez que se activa la IRQ0 el PIC avisa al procesador con la interrupción 08h, cuando se activa la línea IRQ1 el PIC avisa al procesador con la interrupción 09h, cuando se activa la línea IRQ2 el PIC avisa al procesador con la interrupción 0Ah, etc. Estos valores son tomados tras el arranque normal del ordenador.
Estas interrupciones enmascarables han de tener una rutina en modo protegido para no perder el enlace con los componentes hardware desde el modo protegido. El problema que surge en el modo protegido es que el PIC produce interrupciones que están en el rango de 08h hasta 0Fh, lo que significa que cada vez que se pulsa una tecla, por ejemplo, se produce una interrupción numero 09h que corresponde con la entrada 09 de la IDT (la entrada 09 de la IDT indica “Segmento del coprocesador sobrepasado”) lo cual es un grave error. Para solucionar este problema debemos de reprogramar el PIC para que las interrupciones producidas por los componentes hardware no hagan referencia a entradas de la IDT que ya están definidas por el 80386.
En DMT se ha reprogramado el PIC para que las interrupciones enmascarables se mapeen a partir de la entrada 32 de la IDT. Es decir, cada vez que se habilite la línea IRQ0 se producirá la interrupción 32, la línea IRQ1 provocará una interrupción 33, etc. En la figura 8.2 se puede ver una secuencia en ensamblador, tomada del extensor de Thomas “Tran” Pytel, para reprogramar el PIC.
set_PIC proc near
mov al,11h ; ICW4 es necesitado
out 20h,al ; ponemos valor en Command Word 1
out 0a0h,al ; ponemos valor en Command Word 1 (2º)
mov al,bl ; nueva dirección de la IRQ0-7
out 21h,al ; ponemos valor en Command Word 2
mov al,bh ; dirección para IRQ8-15
out 0a1h,al ; ponemos valor en Command Word 2 (2º)
mov al,4h ; IRQ 2 como esclavo
out 21h,al ; Command Word 3
mov al,2h ; respuesta de maestro a esclavo en IRQ 2
out 0a1h,al ; ICW3 para dispositivo esclavo
mov al,1h ; 80x86 Mode
out 21h,al ; Command word 4
out 0a1h,al ; Coomand word 4 (2º)
ret ; devolvemos control al invocador
set_PIC endp
figura 8.2
En la tabla siguiente puede observarse el estado final de la IDT en DMT.
Estructura Física de la IDT |
Excepción 0: Error de División |
Excepción 1: Excepción de depurado |
Excepción 2: Interrupción no Enmascarable |
Excepción 3: Punto de ruptura |
Excepción 4: Desbordamiento (overflow) |
Excepción 5: Comprobación de fronteras |
Excepción 6: Código de operación invalido |
Excepción 7: Coprocesador no disponible |
Excepción 8: Fallo doble |
Excepción 9: Segmento del copro sobrepasado |
Excepción 10: TSS inválido |
Excepción 11: Segmento no presente |
Excepción 12: Excepción de pila |
Excepción 13: Falta de Protección General |
Excepción 14: Falta de página |
Excepción 15: Reservada |
Excepción 16: Error del coprocesador |
Excepción 17: Chequeo de interrupción |
Excepción 18-31: Reservadas |
Excepción 32-48: Interrupciones enmascarables |
El siguiente paso en el programa es rellenar cada uno de los descriptores de la IDT para que apunten a la rutina de tratamiento de excepción correspondiente. A continuación se describen las características correspondiente a cada uno de los descriptores que forman la IDT de DMT.
Descriptor 0: Error de División
Cada vez que se produce un error de división, como puede ser una división por cero, el 80386 genera esta excepción.
Los atributos correspondientes a este descriptor son:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 1: Excepción de depurado
Cuando se produce una excepción estando el procesador con el bit TF de EFLAGS a 1 se produce esta excepción. Esta excepción sólo se da cuando una de las tareas que se ejecuta es un depurador.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga de preparar el procesador para que vuelva al modo V86 y ejecute la rutina de tratamiento para esta excepción que el depurador instaló en memoria. Una vez que esta excepción se haya tratado por el depurador, se retomará el control por el gestor de la excepción 1 y se volverá a la tarea que provocó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 2: Interrupción no Enmascarable
Este excepción provocará la terminación de la tarea que se está ejecutando de forma inmediata, debido a que la recuperación de este error es muy difícil de tratar. Esta excepción podría interferir en el resto del sistema y producir un bloqueo total.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción. Sería recomendable reiniciar el sistema tras producirse esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 3: Punto de ruptura
Esta excepción es producida por el procesador cuando se ha establecido un punto de ruptura en el código de un programa dentro de un depurador. Para trata a esta excepción debemos de llamar a la rutina que el depurador posee para tratar esta excepción.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga de preparar el procesador para que vuelva al modo V86 y ejecute la rutina de tratamiento para esta excepción que el depurador instaló en memoria.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 4: Desbordamiento
Esta excepción es producida por el 80386 cuando el resultado de una operación no se puede almacenar en un registro. DMT eliminará la tarea que produce esta excepción debido a que seguramente esa tarea tenga errores de programación.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 5: Comprobación de fronteras
Esta excepción se produce cuando se ejecuta una instrucción BOUND y encuentra que el operando excede los límites impuestos. Cualquier tarea que produzca esta excepción será eliminada de la memoria.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 6: Código de Operación Inválido
Esta excepción se produce cuando una tarea ejecuta un código de operación que no está definido por el 80386. DMT toma el derecho de eliminar cualquier tarea que intente ejecutar una instrucción inválida, ya que podría interferir en el funcionamiento global del sistema.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 7: Coprocesador No Disponible
Esta excepción se produce cuando se intenta ejecutar una instrucción correspondiente al coprocesador matemático y no se dispone de él.
Bajo el modo real, cuando se ejecuta una instrucción correspondiente al coprocesador matemático y no se dispone de ningún copro en el sistema, el procesador no modifica ninguno de sus registros. El programa que ejecuta esta instrucción observará que los registros del procesador no son modificados, con lo que supondrá que el sistema no posee ningún coprocesador matemático. En el modo protegido esto no funciona igual. Cuando se ejecuta una instrucción correspondiente al coprocesador matemático, se producirá una excepción 7. Para que el funcionamiento sea igual que en el modo real y no se produzca esta excepción, debemos instalar una rutina de tratamiento para la excepción 7 que se encargue de saltarse la instrucción correspondiente al coprocesador matemático. La rutina de tratamiento de la excepción 7 puede verse en el módulo excep07.asm.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encargará de saltarse la instrucción que va dirigida al coprocesador matemático.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Descriptor 8: Fallo Doble
Esta excepción se produce cuando produce una excepción mientras se estaba tratando a otra. Cualquier tarea que produzca esta excepción será eliminada de la memoria.
Los atributos de este descriptor son los siguientes:
-
Selector: SelCode32. Cada vez que se produzca esta excepción, el procesador ejecutará el código que se encuentra en el segmento de código protegido de 32 bits, referenciado por SelCode32. El
-
desplazamiento específico dentro del segmento de este segmento de código viene dado por el atributo siguiente.
-
Desplazamiento: El desplazamiento de esta excepción apunta a una rutina de tratamiento de excepciones general, donde se produce la finalización de la tarea que generó esta excepción.
-
Nivel de privilegios: Cero. Este descriptor sólo puede ser utilizado por un usuario que tenga el nivel de privilegios cero. En DMT el único “usuario” que posee el nivel de privilegios cero es la tarea cero.
-
Bit presente activado. La rutina de tratamiento de la excepción se encuentra cargada en memoria.
Así se ha implementado DMT 57
54
pushf
pop ax
xor ah,40h
push ax
popf
pushf
pop bx
popf
cmp ax,bx
Descargar
Enviado por: | Ahucha |
Idioma: | castellano |
País: | España |