desplazamiento específico dentro del segmento de este segmento de código... siguiente.

Anuncio
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 9: Segmento del Coprocesador sobrepasado
Esta excepción se produce en el modo protegido cuando el 80386 detecta una violación de la página o el
segmento mientras se transfiere la parte de operandos del coprocesador al NPX. 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 10: TSS inválido
Esta excepción se produce cuando se encuentra un descriptor de TSS inválido. Esta excepción sólo la podrá
producir la tarea 0 o código de DMT, ya que es la única que referencia a los descriptores TSS para realizar
conmutaciones de tarea.
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 imprime un mensaje
por pantalla indicando TSS inválido.
• 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 11: Segmento no presente
Esta excepción se produce cuando se referencia a un descriptor que tiene el bit presente a cero. Esta excepción
sólo la podrá producir la tarea 0 o código de DMT, ya que es la única que referencia a los descriptores de la
GDT.
1
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 imprime un mensaje
por pantalla indicando Segmento no presente.
• 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 12: Excepción de Pila
Esta excepción se produce cuando se intenta violar los límites establecidos para la pila. Cuando se produce
esta excepción es muy difícil llevar al sistema a un estado estable, ya que antes de producirse seguramente se
han realizado otras operaciones inválidas que no han sido detectadas por DMT. Una excepción de pila por
parte de una tarea provocará no sólo que la tarea que se estaba ejecutando termine, sino que también producirá
la terminación de DMT y la vuelta al modo real. Aunque se ha intentado eliminar únicamente la tarea que
provocó la excepción, sin finalizar las otras tareas, se ha observado que tras producirse esta excepción se
produce un bloqueo de todo el sistema al poco tiempo de eliminar la tarea maligna de la memoria. Por tanto,
hemos optado por finalizar todas las tareas de forma abrupta antes de que se produzca el reset del ordenador.
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 imprime un mensaje
por pantalla indicando Excepción de pila y que termina la ejecución de DMT.
• 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 13: Excepción de Protección General
Esta excepción es la que más se produce en el sistema. Por ejemplo, cada vez que una tarea solicita un
servicio del MS−DOS a través de la instrucción int 21h, se produce una excepción de protección general. Esta
excepción es tratada por el monitor de V86, del que hablamos anteriormente, que detectará la instrucción que
provocó a excepción y llevará a cabo una acción específica. El monitor de V86 será expuesto detalladamente
mas adelante.
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 contiene el código
correspondiente al monitor de V86, que se encargará de gestionar la excepción para que la tarea siga
adelante
2
• 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 14: Falta de página
Esta excepción se produce cuando una tarea en modo protegido (tarea 0), accede a una página que tiene el bit
presente a cero.
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 imprime un mensaje
por pantalla indicando Falta de página.
• 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 15: Excepción Reservada
Descriptor 16: Error del Coprocesador
Esta excepción se produce cuando el 80386 detecta una señal mandada por el 80287 o del 80387 en el pin
ERROR.
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 17: Chequeo de Interrupción
Descriptor 18−31:Excepciones Reservadas por el 80386
Descriptores 32: Interrupción enmascarable 0, Temporizador
Esta interrupción se produce en el 80386 unas 18,2 veces por segundo. Esta interrupción es utilizada por el
80386 para refrescar la memoria y realizar otras operaciones críticas. Cada una de estas 18,2 interrupciones
por segundo se les suele llamar ticks.
Bajo DMT se intentó utilizar esta interrupción para despachar el tiempo de atención de la CPU a cada tarea, es
3
decir, cada vez que se producía esta interrupción se producía una conmutación de tarea. Se observó que dar a
cada una de las tareas un ticks del reloj era demasiado tiempo, y se observaba la ejecución de cada tarea de
forma intermitente. Por ejemplo, si teníamos dos tareas ejecutándose y una de ellas ejecutaba el comando dir
del MS−DOS, los archivos del directorio se mostraban en bloques de forma discontinua, no logrando que
todos los archivos se mostraran por pantalla de una forma suave y continua. Por tanto necesitábamos algún
componente hardware que produjera más interrupciones por segundo y ofrecer a cada tarea un tiempo menor
de atención de la CPU. Tras mucho investigar encontré que todos los PCs modernos poseían un reloj de
tiempo real que mantenía la hora en el sistema y que éste se podía programar por el usuario para que produjera
una interrupción tantas veces por segundo como deseaba el usuario. El descriptor 40 maneja las interrupciones
provocadas por el reloj de tiempo real.
Los atributos del descriptor 32 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que realiza una llamada a la
rutina original de esta interrupción antes de cargar DMT 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 33: Interrupción enmascarable 1, Teclado
Cada vez que se pulsa y libera una tecla del teclado, el 80386 recibe esta interrupción.
En DMT se ha redireccionado esta interrupción con el fin de que la tecla que se pulse o libere vaya a parar a la
tarea que está en primer plano. En el módulo NewIRQ.asm se describe detalladamente como se ha realizado
esto.
Los atributos del descriptor 33 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga de llevar la
pulsación o liberación de la tecla que provocó la interrupción a la tarea que está en primer plano.
• 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 34: Interrupción enmascarable 2, 2º controlador de interrupciones
El AT posee dos controladores de interrupciones para controlar todos los periféricos que se pueden conectar al
ordenador. Las interrupciones que se producen en el segundo controlador de interrupciones, provoca una
interrupción 2 en el primer controlador de interrupciones, así tenemos ambos controladores conectados en
cascada. Cada vez que se produce una interrupción 2 en el primer controlador de interrupciones, se ha de
mirar el segundo controlador de interrupciones para saber cual de fue la patilla que provocó tal interrupción en
4
el anterior controlador.
Las interrupciones enmascarables del segundo controlador se mapean a partir del descriptor 40 de la IDT.
Descriptor 35: Interrupción enmascarable 3, Puerto serie 2 (COM2)
Esta interrupción es producida en el 80386 por el primer puerto serie. Los ratones del COM2 producen una
interrupción de este tipo cada vez que se produce una acción sobre ellos.
En DMT se ha redireccionado la interrupción del puerto serie para que cada vez que se produzca una
interrupción del puerto serie, se llame a la rutina de tratamiento de la tarea que está en primer plano.
Los atributos del descriptor 35 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga de llamar a
la rutina de tratamiento de las interrupciones del COM2 para la tarea que está en primer plano.
• 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 36: Interrupción enmascarable 4, Puerto serie 1 (COM1)
Esta interrupción es producida en el 80386 por el primer puerto serie. Los ratones del COM1 producen una
interrupción de este tipo cada vez que se produce una acción sobre ellos.
En DMT se ha redireccionado la interrupción del puerto serie para que cada vez que se produzca una
interrupción del puerto serie, se llame a la rutina de tratamiento de la tarea que está en primer plano.
Los atributos del descriptor 36 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga de llamar a
la rutina de tratamiento de las interrupciones del COM1 para la tarea que está en primer plano.
• 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 37: Interrupción enmascarable 5, Puerto paralelo 2
Bajo el puerto paralelo se puede conectar un dispositivo como puede ser una impresora. En DMT no se ha
tenido en cuenta que varias tareas accedan al mismo tiempo a la impresora, por lo que no se asegura que la
impresión sea la correcta cuando varias tareas imprimen al mismo tiempo. La interrupción 5 del primer
controlador de interrupciones no ha sido modificada por DMT y simplemente realiza una llamada al
5
controlador antiguo cuando se produce esta interrupción.
Los atributos del descriptor 37 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se simplemente llama a
la rutina original de esta interrupció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 38: Interrupción enmascarable 6, Disquete
Cada vez que se produce un acceso al disquete se produce una interrupción de este tipo. DMT no se hace
cargo de implementar una rutina de 32 bits en modo protegido para accesos a disquetes. Por tanto se realiza
una llamada al controlador de interrupciones antiguo en modo real cada vez que se produce un acceso al
disquete.
Los atributos del descriptor 38 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se simplemente llama a
la rutina original de esta interrupció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 39: Interrupción enmascarable 7, Puerto paralelo 1
Bajo el puerto paralelo se puede conectar un dispositivo como puede ser una impresora. En DMT no se ha
tenido en cuenta que varias tareas accedan al mismo tiempo a la impresora, por lo que no se asegura que la
impresión sea la correcta cuando varias tareas imprimen al mismo tiempo. La interrupción 7 del primer
controlador de interrupciones no ha sido modificada por DMT y simplemente realiza una llamada al
controlador antiguo cuando se produce esta interrupción.
Los atributos del descriptor 39 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se simplemente llama a
la rutina original de esta interrupción.
6
• 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 40: Interrupción enmascarable 8, Reloj de Tiempo Real
El reloj de tiempo real puede ser programado por el usuario para que se produzca una interrupción de este tipo
tantas veces como se desea por segundo. Al disponer de esta característica, el reloj de tiempo real en DMT se
ha reprogramado para que se produzcan 1024 interrupciones por segundo. Ahora ya tenemos una buena forma
de contar el tiempo CPU que se cede a cada tarea entre conmutación y conmutación de tareas.
Al tiempo que se le cede a una tarea para ser despachada por la CPU se llama quantum. El quantum para cada
tarea bajo DMT corresponden a 4 interrupciones dadas por el reloj de tiempo real, es decir, de esas 1024
interrupciones que produce el reloj por segundo, cada grupo de 4 interrupciones forman un quantum.
Cada vez que se produce una interrupción del reloj de tiempo real, se llama a un procedimiento que se llama
despachador de tareas y que se encarga de ver si el quantum de tiempo para la tarea que estaba ejecutándose
ha terminado. El despachador también se encarga de ver si una tarea puede ser conmutada o no, ya sea porque
se esté ejecutando sola o porque esté realizando una operación de disco u otra acción crítica. El despachador
se explica detalladamente en el módulo despacha.asm.
Los atributos del descriptor 40 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que se encarga despachar el
tiempo de solicitud de CPU a cada tarea.
• 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.
Descriptores 41−45: Libres para tarjeta de expansión
El segundo controlador de interrupciones tiene ciertas patillas libres en el que se pueden conectar diversos
dispositivos como pueden ser el ratón del tipo PS/2, Streamer, CD−ROM, etc.
En DMT sólo se ha implementado una rutina para el tratamiento de los ratones del tipo PS/2 que se anclan en
el pin 4 del segundo controlador de interrupciones. Para el resto de interrupciones se llaman al controlador que
había por defecto antes de ejecutar DMT.
Los atributos de estos descriptores 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de estas excepciones apuntan a una rutina que se encarga de
llamar al controlador antiguo de interrupciones.
7
• 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 46: Interrupción enmascarable 14, Disco Duro
Cada vez que se produce un acceso al disco duro se produce una interrupción de este tipo.
Para evitar el problema que surgía al conmutar una tarea que estaba realizando un acceso a disco, se intentó
redireccionar la interrupción 14 del segundo controlador de interrupciones, pero el problema continuaba ya
que se producían varias interrupciones de este tipo para realizar una transferencia de disco. Así se optó por
redireccionar la interrupción de disco de la BIOS que realizaba la transferencia completa de datos a disco.
Los atributos del descriptor 46 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. Si la excepción se produce bajo el modo V86 se prepara la pila para poder realizar la
llamada de la interrupción original bajo este modo.
• Desplazamiento: El desplazamiento de esta excepción apunta a una rutina que llama al controlador
original de disco duro para realizar las transferencias disco−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.
Estos son todos los descriptores que DMT maneja para realizar su trabajo. Todos los atributos de los
descriptores son rellenados, así como el valor base y desplazamiento para que apunten a su rutina de
tratamiento de excepción.
• Paso 7: Inicializar el Directorio y Tablas de páginas
El directorio de páginas y las tablas de páginas componen el mecanismo de paginación en el 80386. A través
del mecanismo de paginación hacemos corresponder las direcciones lineales, de la segmentación, con
direcciones físicas de memoria.
Toda tarea V86, y tareas en modo protegido, accederá a la memoria indicando una dirección lineal. Parte de
esta dirección lineal se utilizará para acceder al directorio de páginas y obtener de allí una tabla de páginas.
Otra parte de la dirección lineal se utilizará para especificar una página física dentro de la tabla de páginas
obtenida anteriormente. Por último otra parte de la dirección lineal se utiliza para indicar un desplazamiento
dentro de la página física obtenida anteriormente. Así podemos decir que una dirección lógica tiene la
estructura que se muestra en la figura 8.3.
Figura 8.3. Formato de una dirección lineal
El 80386 sólo puede ver las direcciones físicas que son mapeadas por el directorio y las tablas de páginas. Así
si tenemos un directorio con un conjunto de tablas de páginas que sólo mapean el primer Mbyte físico, no
podremos acceder a ninguna dirección de memoria más allá del primer Mbyte y esto es cierto para todas las
tareas del sistema, incluso aquellas que poseen el nivel de privilegios cero. ¿Cual es la forma que tenemos
para dar a cada tarea un espacio de direcciones diferentes para que no haya interacciones entre ellas? La
respuesta nos la da la misma definición de directorio y tablas de páginas. Para tener espacios de direcciones
diferentes debemos de tener un directorio con sus tablas de páginas diferentes para cada tarea y cada uno de
8
estos directorios y tablas de páginas han de mapear direcciones físicas de memoria diferentes.
En nuestro Multitasker, DMT, cada vez que se crea una tarea V86 debemos de crear al mismo tiempo un
directorio de páginas y varias tablas de páginas para mapear el espacio de direcciones físicas de la nueva
tarea. Si tenemos cuatro tareas V86 ejecutándose concurrentemente bajo DMT debemos de tener cuatro
directorios de páginas con sus tablas de páginas, uno para cada tarea. Los directorios mapearán un espacio de
direcciones físicas diferentes para cada tarea, de este modo no habrá interacciones entre ellas ya que el
procesador no puede ver mas allá de las direcciones mapeadas por un directorio de páginas.
Vamos a poner un ejemplo de lo que podría ser una instancia de DMT, es decir, vamos a suponer que un
usuario va a poner en marcha varias tareas V86 bajo DMT. Supongamos que el usuario tiene cargada dos
tareas en memoria, tarea A y tarea B. Como no queremos que las tareas se machaquen unas a otras debemos
de tener dos directorios de páginas, uno para cada tarea, de forma que cada directorio de páginas mapee un
espacio de direcciones físicas diferentes para cada tarea. Supongamos que la tarea A se ejecuta en el segundo
Mbyte de memoria física, mientras la tarea B se ejecuta en el tercer Mbyte de memoria física. El directorio de
páginas de la tarea A ha de mapear las direcciones físicas que van desde el segundo Mbyte al tercer Mbyte de
memoria. El directorio de la tarea B mapeará las direcciones que van desde el tercer Mbyte hasta el cuarto
Mbyte. De esta forma cada vez que la CPU conmuta a la tarea A, cambiará el directorio de páginas actual por
el directorio de la tarea A, con lo que la tarea A no tendrá ocasión de acceder a una dirección física que no sea
mapeada por su directorio y de esta forma se dice que el resto de las tareas que están en memoria están
protegidas.
Otro aspecto importante referente a la paginación es que debemos de hacer creer a cada tarea V86 que se está
ejecutando en el primer Mbyte de memoria física, aunque a través del directorio de páginas se mapee en una
dirección mas allá del primer Mbyte. Esto es debido a que todas las tareas V86, que son programas de
MS−DOS, siempre han sido ejecutados y creados para ejecutarse en el primer Mbyte de memoria física ya
que el MS−DOS es la única zona de memoria donde ejecuta sus programas. Para conseguir esto, las tareas
generarán como siempre sus direcciones en el rango de cero a un Mbyte. A través del directorio de páginas y
las tablas de páginas, mapearemos las direcciones lineales correspondientes al rango anterior, de 0 a un
Mbyte, a direcciones físicas totalmente diferentes por encima del primer Mbyte. Con este mecanismo de
paginación, las tareas no son conscientes de ello y creen que se están ejecutando en el primer Mbyte de
memoria física, aunque realmente se estén ejecutando en el quinto Mbyte de memoria por ejemplo. En la
figura 8.4 mostramos como una tarea V86 genera direcciones lineales y estas son mapeadas a direcciones
físicas mas allá del primer Mbyte.
Como se comentó en el capítulo El modo protegido, cada entrada del directorio de página apunta a una tabla
de páginas que es capaz de direccionar 4 Mbytes. Por tanto, con una sola tabla de páginas podemos mapear la
zona de memoria física correspondiente a una tarea V86, ya que cada tarea V86 sólo necesita un Mbyte físico
para ejecutarse. Si miramos el código fuente de DMT, observaremos que cada directorio de páginas, para cada
tarea V86, consta de varias tablas de páginas y ¿por qué varias tablas de páginas si con una tenemos de sobra
para mapear la memoria de una tarea?. La primera tabla de páginas se utiliza para mapear la memoria física
correspondiente a una tarea. El resto de las tablas de páginas se incluye en todos los directorios de páginas de
cada una de las tareas. Estas tablas de páginas mapean toda la memoria física del ordenador y son utilizadas
por el código monitor de DMT, para poder realizar sus tareas. Pero si cada tarea V86 tiene en su directorio
tablas de páginas que mapean toda la memoria ¿podrá acceder cada tarea a cualquier dirección de memoria?
La respuesta es no. Las tareas V86 pueden generar sólo direcciones de cero a un Mbyte a través del
mecanismo segmento:desplazamiento. El directorio mapeará su primera entrada, correspondiente a las
direcciones lógicas de cero a cuatro Mbytes, a una tabla de páginas que mapea la dirección física de cada
tarea. La tarea V86 no podrá generar direcciones mayores de un Mbyte y por tanto no podrá acceder a la
segunda entrada del directorio y siguientes, con lo que no podrá acceder a las direcciones de memoria de otra
tarea V86 o al código monitor de DMT.
9
figura 8.4. Ejemplo de traducción lineal a físico en tarea V86
La segunda entrada del directorio de páginas mapea los primeros cuatro Mbytes físicos de memoria. En el
primer Mbyte físico se encuentra el código de DMT y por ello ha de estar presente en todos los directorios de
páginas. Cuando se produce una excepción en una tarea V86, buscamos la dirección de la rutina de
tratamiento para esa excepción en la IDT. Todas las entradas de la IDT mapean su rutina de tratamiento de
excepción a partir de la dirección base 400000h, que corresponde con el valor de 4 Mbytes, es decir, que se
mapea a partir de la segunda entrada del directorio. Con esto, DMT podrá ejecutar el código de las rutinas
para cada una de las excepciones desde la zona de direcciones de cada tarea V86. En la figura 8.5 puede verse
el mapeado de memoria que realiza cada uno de los directorios de cada tarea V86.
Una vez que hemos explicado cómo es la estructura del directorio de páginas y de las tablas de páginas bajo
DMT, el siguiente paso en el código fuente es la de rellenar las entradas del directorio y de las tablas de
páginas para la tarea 0, es decir, para la tarea que va a iniciar el modo protegido.
Cada tabla de páginas posee una longitud de 4 Kb, estos cuatro Kbytes se obtienen de multiplicar los 4 bytes
que forman cada entrada de una tabla de páginas con las 1024 entradas que posee cada tabla de páginas. La
dirección de comienzo de una tabla de páginas ha de ser múltiplo de 4 Kbytes, es decir, solo podemos crear
tablas de páginas en la dirección 0, 4096, 8192, etc. El directorio de páginas también ha de comenzar en una
dirección que sea múltiplo de cuatro Kbytes, aunque los manuales no dicen que el directorio tenga que
comenzar en una dirección múltiplo de 4 Kbytes.
rafaasd
asd
Figura 8.5. Estructura del directorio y tablas de páginas en DMT
Dependiendo de la memoria que haya instalada en el ordenador, DMT creará más o menos tablas de páginas
para poder mapear toda la memoria física disponible. La tabla de páginas que se corresponde con la primera
entrada del directorio (para direcciones lineales de 0 a 4 Mbytes) posee los atributos de lectura/escritura y
usuario. Los primeros 4 Mbytes lineales tienen el atributo usuario activo para que cuando se conmute a V86
para realizar un servicio del MS−DOS o BIOS solicitado por DMT, no se produzcan excepciones de páginas
por acceder una tarea de privilegios 3 a una página de memoria. El resto de las tablas de páginas, que mapean
el primer Mbyte de memoria físico y el resto de memoria disponible en el ordenador, sólo podrán ser usadas
por tareas que tengan el nivel de privilegios supervisor. Como sólo la tarea 0, que es el código de DMT, posee
el nivel de privilegios supervisor podrá acceder a toda el espacio de direcciones de memoria física.
El rellenado de todo el directorio de páginas y las tablas de páginas es una tarea bastante fácil, incluso desde
el ensamblador. Lo único que debemos de hacer es copiar los atributos de las entradas de las tablas de páginas
en cada una de las tablas de páginas que conforman el directorio de páginas.
• Paso 8: Inicializar TSS de DMT
Antes de entrar al modo protegido, necesitamos crear el TSS de la primera tarea. Esta tarea no es más que la
que inicializa el modo protegido y tiene por defecto el nivel de privilegios cero. Esta tarea será el código de
DMT que se encargará de preparar el resto de las estructuras para poder cumplir su objetivo, lograr la
multitarea bajo MS−DOS.
No es necesario rellenar todos los campos del TSS para la tarea 0, únicamente vamos a rellenar aquellos que
son absolutamente necesarios para entrar al modo protegido. Los campos que inicializamos del TSS son los
siguientes:
10
• Mapa de permisos de accesos a puertos de E/S: Como la tarea 0 ha de poseer el máximo nivel de
privilegios, debemos de darle permiso de acceso a todos los puertos de E/S. Para ello debemos de
apuntar el campo de permisos de E/S del TSS a una zona de memoria inicializada con 8 Kb a ceros.
• Directorio de páginas (CR3): Debemos de rellenar en el TSS el campo CR3, que indica la dirección
del directorio de páginas para la tarea 0. Debemos de saber la dirección donde hemos creado el
directorio de páginas para poder rellenar este campo.
• Campo SS y ESP: Debemos de indicar la dirección lineal de la pila para la tarea 0. Estos campos han
de apuntar a una zona de memoria que esté libre para poder usarla como pila.
No es necesario rellenar más campos para la tarea 0, ya que cuando se realice la primera conmutación de
tareas, la tarea 1, el 80386 rellenará el resto de los campos del TSS de la tarea 0.
Hasta este punto hemos preparado todas las estructuras necesarias para conmutar el procesador al modo
protegido, así que vamos a ello.
• Paso 9: Entrar a modo protegido de 16 bits
Antes de entrar en modo protegido de 32 bits, vamos a hacerlo entrando primero en modo protegido de 16 bits
y desde allí vamos a activar la paginación para entrar luego en modo protegido de 32 bits y ejecutar entonces
DMT.
Para entrar a modo protegido debemos de realizar las siguientes acciones:
• Cargar la dirección de comienzo de la IDT en el registro IDTR: Como comentamos antes, la IDT es
de vital importancia para la manipulación de excepciones desde el modo protegido, por tanto debemos
de cargar el registro IDTR con la dirección de la IDT que creamos antes. Para cargar el registro IDTR
debemos de usar la instrucción privilegiada lidt direccionIDT.
• Deshabilitar las interrupciones enmascarables: Para que no se produzcan interrupciones cuando se
vaya a conmutar a modo protegido, debemos de deshabilitar las interrupciones enmascarables para
evitar posibles anomalías que se puedan producir antes de entrar a modo protegido. Para deshabilitar
estas interrupciones, debemos de utilizar la instrucción cli desde el ensamblador.
• Cargar la dirección de la GDT en el registro GDTR: Para poder acceder a la memoria desde el modo
protegido el 80386 referencia continuamente el contenido de la GDT, por tanto el 80386 necesita
tener en un registro la dirección de comienzo de la GDT. El registro donde debemos de guardar la
dirección de la GDT es el GDTR. Para cargar el registro GDTR debemos de utilizar la instrucción
privilegiada lgdt direccionGDT.
• Habilitar el bit PE del registro CR0: Cuando activamos el bit PE del registro CR0 el procesador
conmuta inmediatamente a modo protegido. Llegados a este punto, es el momento de activar el bit PE
y entrar a modo protegido sin ningún problema.
• Limpiar la cola de prebúsqueda de instrucción: Cuando entramos en modo protegido, el procesador
seguramente haya descodificado la siguiente instrucción desde el modo real. Como la instrucción se
ha descodificado desde el modo real y va a ser ejecutada en modo protegido se puede producir una
ejecución errónea de la instrucción. Para solucionar este problema debemos de realizar un salto
inmediatamente después de entrar en modo protegido, de esta forma la cola de prebúsqueda quedará
limpia. Debemos de realizar el salto dentro de un segmento de 16 bits para conmutar a modo
protegido de 16 bits.
En la tabla 8.6 podemos ver la pequeña rutina de DMT que conmuta el procesador a modo protegido.
figura 8.6. Rutina para entrar a modo protegido
• Paso 10: Entrar a modo protegido de 32 bits
11
Ahora que estamos en modo protegido de 16 bits, debemos de acceder a la memoria a través del mecanismo
de selectores de la GDT. Para conmutar el procesador a modo protegido de 32 bits con la paginación
habilitada realizamos los siguientes pasos:
• Cargar el registro CR3 con la dirección de comienzo del directorio de páginas con el que deseamos
que comience la paginación.
• Activar el bit PG del registro CR0: Una vez que hemos cargado el registro CR3 debemos de poner el
bit PG a 1 y con ello la paginación ya está activa. Hay que mencionar que para que no se produzca
una caída del sistema justo al activar la paginación, necesitamos que la direcciones lógicas se mapeen
igual que las direcciones físicas justo en el punto que se activa la paginación, es decir, el código de
inicialización de la paginación ha de poseer la misma dirección lógica que física.
• Limpiar cola de prebúsqueda de instrucción.
• Cargar los registros de segmentos con valores correctos de selectores.
• Cargar el registro TR con el selector del TSS de la tarea 0.
• Realizar un salto hacia un segmento de código de 32 bits.
Tras esto ya tenemos en nuestras manos una máquina de 32 bits capaz de realizar multitarea y de direccionar
hasta 4 Gbytes de memoria.
• Paso 11: Últimos retoques antes de ejecutar la 1ª tarea
Una vez que entramos en modo protegido de 32 bits para ejecutar la primera tarea debemos de tener
correctamente instalado los diversos manejadores de excepciones así como el código monitor de DMT. En las
siguientes secciones se comentarán estos manejadores de excepción y el monitor de V86 de DMT. En esta
sección supondremos que todo lo anterior está preparado, así que nos lanzamos para poner en funcionamiento
la primera tarea y a partir de ella se podrán ejecutar el resto de las tareas.
Los nombres de las tareas V86
En DMT toda las tareas que se están ejecutando tienen un nombre. El nombre de la tarea coincide con el
nombre del programa DOS que está ejecutando. Por ejemplo, si tenemos la tarea A y la tarea B ejecutándose y
la tarea A ejecuta el programa Scandisk.exe del MS−DOS y la tarea B llama al programa Debug.exe, la tarea
A se llamará SCANDISK.EXE y la tarea B se llamará DEBUG.EXE.
Al iniciar cada tarea, lo que DMT hace es crear un nuevo shell a través del programa Command.com, todas las
tareas se llamarán inicialmente COMMAND.COM. Cada tarea tiene un área de memoria reservada dentro del
código de DMT para guardar su nombre de tarea. DMT inicializa este área de memoria con el nombre
COMMAND.COM para cada tarea.
Los registros de segmento virtuales de cada tarea
Como se verá posteriormente, cada tarea V86 es inicialmente una tarea en modo protegido que conmutará a
tarea V86 antes de crear un nuevo shell a través de command.com. Toda tarea V86 necesita tener unos registro
de segmento virtuales, antes de conmutar a modo V86, que referencian segmentos de memoria para el modo
V86.
Cuando se crea una nueva tarea, ésta es una tarea en modo protegido, que necesitará realizar llamadas al
MS−DOS o a la BIOS antes de conmutar a modo V86. Para poder realizar estas llamadas la tarea ha de poseer
unos registros de segmento correspondientes al modo real para que el servicio solicitado pueda tener una
finalización correcta. Como se ha comentado hasta ahora, los programas en modo protegido hacen referencias
a la memoria a través de la GDT. Como los programas en modo protegido necesitan llamar a servicios del
DOS o de la BIOS que fueron creados en modo real, necesitamos un conjunto de registros que se
12
correspondan con direcciones de segmento del modo real y no referencien selectores del modo protegido.
Estos registros son los registros virtuales de segmento.
En DMT antes de crear la primera tarea se inicializarán el contenido de todos los registros virtuales
correspondientes a cada tarea V86.
Habilitar el reloj de tiempo real y poner en marcha el Despachador de Tareas
Para que el tiempo de CPU se pueda repartir entre las diversas tareas que se están ejecutando, necesitamos
algo que nos mida el tiempo que cada tarea tiene posesión de la CPU y así saber si su tiempo de CPU ha
concluido.
A través del reloj de tiempo real podemos conseguir que se produzca una interrupción tantas veces como
queramos por segundo y poder medir el tiempo de posesión de CPU que lleva una tarea.
El reloj de tiempo real, por defecto, no genera ninguna interrupción por segundo. El programador ha de
reprogramar el reloj de tiempo real para que se produzcan tantas interrupciones como desea por segundo. Para
realizar tal operación debemos de activar un bit del puerto de estado B del reloj de tiempo real. Una vez que
activamos dicho bit se producirán por defecto 1024 interrupciones por segundo, que son suficientes para poder
despachar el tiempo de CPU entre las diversas tareas. El tiempo de despacho de la CPU o el tamaño del
quantum para cada tarea es de cuatro interrupciones de CPU. En la figura 8.7 se muestra un ejemplo de cómo
DMT despacha tres tareas en memoria.
Figura 8.7. Ejemplo de despacho de la CPU en DMT
Inicializar mapa de Memoria Extendida
Aunque DMT no soporte un sistema de memoria virtual, intercambiando páginas de memoria a disco, si posee
muchas estructuras que están orientadas a un sistema de memoria virtual. Ya que inicialmente se pensó
instalar uno de estos sistemas dentro de DMT, se conservan en el programa final muchas de estas estructuras
orientadas a la memoria virtual. Por falta de tiempo, y sobre todo por falta de documentación, no se ha podido
implementar un sistema de memoria virtual para lograr la ejecución simultánea de muchas mas tareas bajo
DMT.
El mapa de memoria extendida consta de un vector que guarda información acerca de bloques de memoria
extendida. Los atributos de cada uno de los elementos del vector son, por ejemplo, número de la tarea que
posee el bloque, privilegio del bloque, byte de accedido, etc. Este vector sirve para que el sistema de memoria
virtual pueda saber que bloque puede pasar a disco en caso de falta de memoria.
En este punto del programa nuestra misión será únicamente rellenar este vector con valores nulos para indicar
que todos los bloques de memoria extendida están libres.
Crear la primera tarea
Por último ya sólo tenemos que crear la primera tarea y a partir de ella el usuario podrá crear el resto de las
tareas que le sean necesarias.
Para crear una tarea debemos de crear su directorio de páginas y tablas de páginas, que mapearán una zona de
memoria que no esté utilizada por ninguna tarea y de este modo evitaremos conflictos entre las diversas
tareas.
Una vez creado el directorio y las tablas de páginas, debemos de rellenar el descriptor del TSS
13
correspondiente a la tarea que se va a crear. El TSS contendrá los valores de los registros de segmento
inicializados, así como el punto de entrada del programa una vez que se le ceda el control de la CPU.
En DMT se ha definido un procedimiento que se llama CrearTarea que realiza las dos labores anteriores. Este
procedimiento será descrito posteriormente.
A partir de la creación de la primera tarea, el usuario podrá crear nuevas tareas a través del despachador de
tareas.
• Paso 12: De vuelta al modo real. Terminar ejecución de DMT
En esta sección queremos comentar como se ha de finalizar DMT. Una vez que el usuario ha disfrutado de los
servicios de DMT y quiere terminar su ejecución, se ha de conmutar el procesador al modo real y restaurar
todo aquello que DMT ha modificado.
DMT puede terminar su ejecución por orden explícita del usuario o porque se ha producido una excepción de
pila dentro de una tarea V86. En ambos casos se produce la misma salida del programa, excepto que en caso
de terminar por una excepción de pila, se mostrará un mensaje al usuario indicando Excepción 12: Falta de
Pila.
Para salir de DMT, primero tenemos que poner el procesador en modo protegido de 16 bits. Para ello debemos
de realizar un salto hacia un segmento de código que tenga como atributos segmento de 16 bits. Una vez
hecho esto, debemos de descargar el registro IDTR para que se utilicen las interrupciones habituales del modo
real.
El próximo paso será deshabilitar la paginación. Para ello pondremos el bit PG del registro CR0 a cero. Hay
que recordar que en estos momentos las direcciones lógicas han de ser iguales a las direcciones físicas.
Una vez hecho esto, podemos poner el procesador en modo real a través del bit PE del registro CR0. Debemos
de limpiar la cola de prebúsqueda de instrucción a través de un salto para que no se produzca un error al
ejecutar la primera instrucción desde el modo real.
Como los registros de segmento contienen valores de selectores del modo protegido, debemos de cargarlos
con valores correctos del modo real. Estos valores correctos corresponden con el segmento de código del
segmento de 16 bits de DMT.
El siguiente paso consiste en restaurar todas las interrupciones modificadas. A través de la reprogramación del
PIC hacemos que se produzcan las interrupciones enmascarables que habían por defecto antes de entrar al
modo protegido. Las interrupciones 15h, 13h y 33h que fueron modificadas para la ejecución correcta de
DMT, han de ser restauradas y redirigirlas a su rutina de interrupción original.
Por último debemos de liberar toda la memoria extendida alojada por DMT y acto seguido solicitamos el
servicio terminar programa del DOS.
• Emular un servicio del DOS desde el modo protegido
Uno de los procedimientos más importantes en DMT es el de emulación de un servicio del DOS o la BIOS
desde el modo protegido. En DMT dicho procedimiento se llama INT_16 y su funcionamiento se describe a
continuación.
Como se sabe, para solicitar un servicio del DOS o la BIOS debemos de ejecutar la instrucción en
ensamblador int n, donde n identifica el tipo de servicio. Si desde el modo protegido se ejecuta la anterior
14
instrucción se ejecutará la rutina, en caso de que haya, correspondiente a la entrada n de la IDT. Por tanto si
queremos que desde el modo protegido se soliciten los servicios igual que desde el modo real, debemos de
crear una entrada en la IDT para cada número de interrupción existente en el DOS y la BIOS, lo que resulta un
arduo trabajo. Una solución para remediar el anterior problema, consiste en realizar un procedimiento en
modo protegido que conmuta a modo V86 ejecutando la rutina correspondiente a la interrupción en el modo
real, es decir, si queremos cargar por ejemplo un fichero desde el modo protegido, llamaremos al
procedimiento anterior y se conmutará el procesador al modo V86, donde se ejecutarán allí todas las
instrucciones correspondientes al servicio del DOS solicitado.
Debido a la importancia de tal procedimiento, vamos a describir como se ha implementado exactamente para
poder comprender así como funciona realmente tal procedimiento.
• El procedimiento es llamado desde el modo protegido y ha de poseer en la pila el número de la
interrupción que se quiere emular antes de llamar al procedimiento.
• Se han de salvar todos los registros de segmentos, registros de propósito general y todos aquellos
otros registros que vayan a ser modificados en el procedimiento. Los registros que realmente se
modifican son EFLAGS, gs, fs, ds, es, eax, ebx. En ensamblador quedaría del siguiente modo:
• Luego tenemos que recargar algunos registros de segmentos para poder referenciar los datos
necesarios que serán solicitados desde el procedimiento. El registro ds se cargará con el selector
SelData32 para poder referenciar los datos de DMT y el registro gs se cargará con el selector
SelZero16 para acceder al primer Mbyte de memoria física.
• El siguiente paso es el de guardar en la pila el valor de SS y ESP que hay en el TSS, para que una vez
que se haya terminado el servicio se recarguen los registros de pila correctamente. También se ha de
poner en los campos SS y ESP el valor de la pila actual para que cuando se produzca una excepción
desde el modo V86 se utilice la pila de actual.
• El siguiente paso consiste en meter en la pila el valor de todos los registros de segmentos que tendrán
una vez que se esté ejecutando la tarea en modo V86.
• Una vez que haya terminado el servicio solicitado, hacemos que se ejecute la instrucción INT 0 para
que se produzca una excepción desde V86. El código monitor se encargará de detectar que esa
instrucción INT 0 corresponde a la finalización de un servicio y ejecutará una rutina para sacar todos
los registros que se han metido anteriormente y seguir ejecutando la tarea en el punto posterior a la
solicitud del servicio. Debemos de insertar en la pila V86 la dirección de la instrucción INT 0 para
que una vez que se ejecute el IRET correspondiente a la finalización del servicio, se produzca una
excepción desde el modo V86.
• Por último debemos de colocar en la pila el contenido de los registros CS e EIP para que apunten a la
rutina del servicio solicitado cuando se conmute a modo V86. Además de meter en la pila los dos
registros anteriores, debemos de meter una copia del registro EFLAGS pero con el bit VM a 1 para
que cuando se ejecute la instrucción IRETD se saque de la pila el registro EFLAGS y se produzca la
conmutación a modo V86.
mov eax, ss:esp[11*4] ; recuperamos el valor de EAX y EBX al
mov ebx, ss:esp[12*4] ; entrar en este procedimiento
iretd ; ¡conmutamos a Virtual 8086 Mode!
• El monitor de V86
15
El monitor de V86 se va a encargar de tratar todas las excepciones que se produzcan por falta de protección
general, ya sea desde el modo protegido o desde el modo V86. El monitor de V86 tiene como misión principal
la de ejecutar un servicio del DOS o la BIOS desde el modo V86.
Cuando una tarea en modo protegido conmuta a modo V86 para realizar un servicio, seguramente este
servicio solicitado llame a otros servicios para poder ejecutarse. La llamada de estos últimos servicios
provocarán excepciones desde la tarea V86, ya que la ejecución de la instrucción INT n desde el modo V86
provocará una falta de protección general. El monitor de V86 ha de detectar que tal excepción se produjo por
intentar solicitar un servicio desde el modo V86 y deberá de realizar diversas operaciones para que el servicio
pueda ser llevado a cabo y la tarea V86 termine su ejecución felizmente.
Las tareas de las que se encarga el monitor de V86 en DMT son las siguientes:
• Determinar si la excepción de protección general se provocó debido a una solicitud de un servicio
desde el modo V86.
• Terminar una tarea en modo protegido si provoca una excepción de protección general.
• Terminar una tarea en modo V86 si intenta ejecutar una instrucción privilegiada.
• Terminar una tarea en modo V86 si intenta ejecutar una instrucción inválida.
• Terminar un servicio solicitado por una tarea en modo protegido al ejecutar el procedimiento, descrito
anteriormente, INT_16.
El código del monitor de V86 se ha anclado en la entrada 13 de la IDT Falta de protección general. Cada vez
que el procesador emita una falta de protección general el monitor de V86 se pondrá en marcha para intentar
remediar tal excepción. Al igual que hicimos con el procedimiento INT_16, vamos a describir detalladamente
la implementación del monitor de V86 para comprender plenamente todas las posibilidades que ofrece.
• Lo primero que hace el monitor de V86 es detectar si la excepción se produjo en modo V86 o en
modo protegido, ya que el contenido de la pila varía si la excepción se produjo en un modo u otro. Si
la excepción se produjo en modo protegido se debe a que el código de DMT es erróneo, por lo que
deberemos finalizar el programa y corregir dicho error. Si por el contrario se produjo en modo V86,
debemos de examinar la tarea V86 para ver que es lo que provocó tal excepción, esto se comenta en el
paso siguiente.
•
• Si la excepción se ha producido en modo V86, vamos a examinar en el segmento de código de la tarea
V86 la instrucción que provocó tal excepción. Como el 80386 introduce en la pila el contenido del
registro CS e IP de la tarea V86 cuando se produjo la excepción, podemos calcular a través de esos
dos registros la dirección de la instrucción que provocó la excepción y leer de allí su código de
instrucción. Una vez que leemos la dirección de la instrucción que provocó la excepción debemos de
comprobar si esa dirección corresponde con la de la instrucción INT 0 que introdujo el procedimiento
INT_16 en la pila para indicar que el servicio se ha completado. La rutina que se ejecuta cuando
coincida con la dirección de la instrucción INT 0 que introdujo el procedimiento INT_16 se describirá
posteriormente. Por ahora, suponemos que la excepción se ha producido por otra causa diferente en
V86.
V86_cont:
shl ebx, 4 ; pasamos CS a lineal
add ebx, ss:esp[2*4] ; EBX = dirección lineal CS:IP
inc dword ptr ss:esp[2*4] ; apuntamos IP a la siguiente ; instrucción
16
• El siguiente paso consiste en leer el código de la instrucción que provocó la excepción. Una vez que
leemos el código de operación realizamos las siguientes comprobaciones:
•
• Comprobar si la excepción se produjo al ejecutar la instrucción INT 3.
• Comprobar si la excepción se produjo al ejecutar la instrucción INTO.
• Comprobar si la excepción se produjo al ejecutar una instrucción de la forma INT n.
• Si no es ninguno de los caso anteriores, se llamará a un procedimiento que se encarga de detectar si la
excepción se produjo por acceder a un puerto de E/S que estaba bloqueado. Este procedimiento se
comentará posteriormente.
Todas estas comprobaciones se han implementado del siguiente modo:
• Supongamos que la excepción se ha producido al solicitar un servicio desde el modo V86. Las
comprobaciones que hacemos ahora son las siguientes:
• Ver si el servicio solicitado es ejecutar un nuevo programa (Función 4Bh de la interrupción 21h). Si es así
se llamará a un procedimiento que se encarga de capturar la cadena ASCII del nombre del fichero y
asignárselo a la tarea V86 que se está ejecutando. De esta forma la tarea V86 cambiará de nombre. Este
procedimiento será descrito en el módulo INTemul.asm.
• Comprobar si se ha solicitado un servicio para cambiar el modo de vídeo. En este caso, se llamará a un
procedimiento que comprueba si el modo de vídeo corresponde con algún modo gráfico y en tal caso la
tarea finaliza. Este procedimiento se describe en el módulo INTemul.asm.
• Comprobar si se ha solicitado el servicio 87h de la interrupción 15h Mover bloque de datos hacia/desde la
memoria extendida. Como este servicio conmuta el procesador al modo protegido, se producirá una
excepción en la tarea V86 al solicitar este servicio. Por tanto este servicio ha de ser emulado para que una
tarea V86 pueda mover datos desde la memoria extendida.
• La rutina en ensamblador que realiza los pasos anteriores se muestra a continuación:
• El siguiente paso consiste en preparar el procesador para que conmute nuevamente a modo V86 y
ejecute la rutina del servicio solicitado. Una vez que haya concluido el servicio se ha de ejecutar la
instrucción siguiente a la que provocó la excepción desde la tarea V86. Debemos de preparar por
tanto el punto de entrada para la ejecución del servicio, así como la pila para que cuando se termine el
servicio se vuelva al punto siguiente de la excepción. A continuación mostramos la rutina en
ensamblador que realiza todo esto.
• Hasta aquí el servicio ha sido emulado. En este punto vamos a comentar que es lo que hay que hacer
cuando la excepción se ha producido por la ejecución de la instrucción INT 0 que introdujo el
procedimiento INT_16 en la pila. Primero debemos de sacar los registros eax y ebx de la pila, que
fueron introducidos por el código monitor. Tras realizar esto, los registros eax y ebx contendrán los
valores que fueron devueltos por el servicio solicitado. Luego tenemos que sacar de la pila el registro
CS e EIP ya que no van a ser utilizados. A continuación restauramos los registros virtuales de
segmento de la tarea V86, ya que pueden haber sido modificados por el servicio. Por último
restauramos el valor de los campos SS y ESP del TSS para que contengan valores correctos cuando se
produzca otra excepción.
• El módulo Excep07.asm
Este módulo contiene dos procedimientos que se encargan del manejo de la excepción 7 Coprocesador no
disponible y de la excepción 1 Excepción de depurado.
17
• Función TratarExc07
Función: TratarExc07.
Descripción: Rutina para el manejo de la excepción 07.
Entrada: Nada.
Salida: Nada.
Cuando se entra en modo protegido, cualquier intento de ejecutar una instrucción de inicialización del
coprocesador sobre un ordenador sin coprocesador provocará una excepción de coprocesador. Cuando en
modo real se intenta ejecutar una de estas instrucciones no se realiza ninguna acción y los registros de la CPU
permanecen inalterados.
Como muchos programas detectan la presencia de un coprocesador a través de la ejecución de una instrucción
de inicialización del copro, estos programas provocarán una falta de coprocesador si se ejecutan en un
ordenador sin coprocesador. Para remediar esta falta en modo protegido, debemos de instalar una rutina de
manejo de la excepción 7 que se encargue de no hacer nada cuando se ejecuta una de estas instrucciones y
permita la ejecución del programa que provocó la excepción de coprocesador.
El procedimiento TratarExc07 se encarga de gestionar estas excepciones por falta de coprocesador. Cuando se
ejecuta una instrucción de inicialización del coprocesador, como FINIT, FNINIT, FSTCW, etc., se producirá
una excepción y este procedimiento se encargará de saltar la instrucción anterior de modo que no se realice
ninguna acción en el procesador. Cuando nos saltamos la instrucción de inicialización del coprocesador, el
80386 mantendrá los registros tal y como estaban antes de saltarse la instrucción de inicialización de
coprocesador, por tanto, el programa que ejecutó esa instrucción observará que los registros del 80386 no se
han modificado, por lo que detectará que no hay ningún coprocesador instalado en el ordenador.
El procedimiento TratarExc07 posee un mecanismo muy simple, tan solo comprueba cada una de las
instrucciones de inicialización del coprocesador con la instrucción en curso y se salta el número de bytes que
ocupa la instrucción. En el siguiente cuadro podemos ver una pequeña parte de este procedimiento.
• Procedimiento TratarExc01
Procedimiento: TratarExc01.
Descripción: Se encarga de gestionar la excepción 1.
Entrada: Nada.
Salida: Nada.
Cuando la tarea que se está ejecutando es un depurador, esta excepción aparecerá cada vez que se ejecute una
instrucción de ejecución paso a paso en el depurador. El 80386 provocará una excepción de depurado que ha
de ser gestionada por DMT. Como no queremos preocuparnos de la rutina que hay que realizar para remediar
esta excepción, se ha optado por llamar a la rutina de excepción de depurado que instala el depurador.
Cada vez que se produce una excepción de depurado, DMT prepara el procesador para volver a modo V86 y
desde allí ejecutar el código correspondiente a la interrupción 1 que ha colocado el depurador. Aunque esto es
lo que se ha realizado en DMT se han presentado diversos problemas que no permiten el depurado del
programa en curso.
18
Cuando se está depurando un programa que tiene la tabla de símbolos completa, la depuración de tal programa
se lleva a cabo normalmente. El problema aparece cuando se depura un programa que no posee tabla de
símbolos y se realiza la ejecución paso a paso. Cuando mandamos al depurador que realice la ejecución paso a
paso, el programa desemboca en una instrucción int 0 y se queda en un bucle infinito ejecutando la anterior
instrucción. Si desea comprobar tal problema, ejecute DMT y cargue el depurador Turbo Debugger, por
ejemplo, y depure un programa paso a paso. Creo que la intención de DMT de llamar a la rutina para el
manejo de la interrupción 01 que instala el depurador es buena pero... no funciona. Por tanto, bajo DMT no se
permite la depuración de un programa paso a paso, sin tabla de símbolos, en un depurador.
• El módulo V86excep.asm
Este módulo contiene un único procedimiento que se encarga de gestionar las faltas de protección general que
se producen cuando una tarea intenta acceder a un puerto de entrada/salida cuyo acceso a prohibido DMT.
• Función V86_exc
Función: V86_exc.
Descripción: Trata las excepciones en acceso de puertos de E/S en el modo V86.
Entrada: Nada.
Salida: Nada.
Para que DMT ofrezca una convivencia pacífica de todas las tareas, ha de poseer mecanismos que detecten
accesos a los puertos de E/S por las tareas V86 y gestionen tal acceso de tal modo que no interfiera en las
demás tareas que se están ejecutando en memoria.
Supongamos que tenemos dos tareas que se están ejecutando, tarea A y tarea B, la tarea A está en primer
plano y la tarea B en segundo plano. Como la tarea A tiene el control total de la pantalla, no queremos que un
intento de la tarea B de cambiar la resolución de la pantalla, por ejemplo, afecte a la visualización de la tarea
A. Para solucionar el anterior problema debemos de bloquear todos los puertos de E/S pertenecientes a la
pantalla, con el fin de detectar cuando una tarea en segundo plano intenta acceder a uno de estos puertos y
realizar una modificación sobre ellos. Si el usuario quiere pasar a primer plano la tarea B, se deberá de
inicializar la pantalla tal y como lo ha hecho la tarea B hasta ese momento. En el ejemplo de antes, si la tarea
B cambia el monitor a la resolución de 320x200 en segundo plano, no queremos que interfiera en la pantalla si
está en segundo plano. Cuando el usuario pasa a primer plano la tarea B, se ha de cambiar la resolución de la
pantalla a 320x200 para que la tarea B se visualice tal y como fue programada.
Los puertos que DMT bloquea son los correspondientes al DMA y a la tarjeta de vídeo. A continuación
describimos cada uno de los dos tipos de bloqueo anteriores.
Bloqueo de los puertos del DMA
Las tareas V86 poseen un espacio de direcciones lineales diferentes a las direcciones físicas. Esto es posible a
través del mecanismo de paginado y gracias a esto, es posible la ejecución de varias tareas V86 en memoria.
El problema surge cuando intentamos utilizar el chip DMA para realizar una transferencia en una tarea V86.
Ya que el chip DMA no nota la presencia del mecanismo de paginado activo, utiliza las direcciones lógicas
dadas por la tarea V86 como direcciones físicas con lo que se produce un caos en el sistema.
Supongamos que tenemos una tarea que se está ejecutando en el quinto Mbyte de memoria física. Esta tarea
generará direcciones lineales que irán desde 0 hasta 1 Mbyte. Si la tarea V86 pasa una de estas direcciones
19
lineales al chip DMA se producirá una transferencia de datos en el primer Mbyte y no en el quinto Mbyte, que
sería lo correcto.
Para que una tarea V86 que utiliza el DMA pueda ejecutarse correctamente, se ha de detectar cualquier acceso
a este chip y convertir la dirección lineal en una dirección física correcta para la tarea V86.
Las tareas V86 utilizan el DMA para realizar las transferencias de datos hacia/desde disquetes. En el
programa fuente se puede ver como se detecta el acceso al chip DMA y cómo se convierte la dirección lineal a
la dirección física correspondiente.
Bloqueo de los puertos de la VGA
Para permitir la visualización correcta de la tarea que está en primer plano, se ha de bloquear todos los puertos
correspondientes a la VGA con el fin de que una tarea en segundo plano modifique el aspecto de la pantalla.
Una tarea en primer plano puede realizar cualquier operación sobre los puertos de la VGA sin ningún
impedimento de DMT. Las tareas en segundo plano pueden realizar también cualquier operación sobre los
puertos de la VGA, pero estas operaciones no podrán ser visualizadas hasta que no se pasen a primer plano. Si
tenemos una tarea en segundo plano que realiza una modificación sobre cualquier puerto de la VGA, DMT se
encargará de guardar el acceso a dicho puerto en una variable con el fin de poder realizar la operación sobre el
puerto anterior cuando se pase a primer plano la tarea.
La tarjeta de vídeo VGA está formada por diversos registros que definen el aspecto de la imagen sobre la
pantalla. Cada tarea V86 ha de poseer una variable para cada registro de la VGA, con el fin de guardar el
acceso a los registros de la VGA en cada una de estas variables cuando están en segundo plano. Cuando se
pasa una tarea a primer plano, DMT se encarga de rellenar cada registro de la VGA con el contenido de estas
variables de esa tarea.
El objetivo de la función V86_exc es la de guardar cualquier acceso a los registros de la VGA en una variable
correspondiente para ese registro en cada tarea V86. Así cada tarea tendrá un conjunto de variables que
indican el estado de cada uno de los registros de la VGA para su pantalla virtual. Cuando una tarea en
segundo plano quiere leer el contenido de un registro de la VGA, leerá el valor de su variable correspondiente
a dicho registro de la VGA.
Como cada tarea V86 puede acceder a los registros de la VGA con instrucciones diferentes como OUT DX,
AL, OUT DX,AX, etc., se ha de detectar el tipo de instrucción que provocó la excepción para poder guardar el
acceso al registro de la VGA de la forma correcta. A continuación mostramos un pequeño fragmento de cómo
se gestionan las excepciones debido al acceso de un puerto de la VGA.
Así se ha implementado DMT 87
Directorio Página Desplazamiento
direcciones
de 0 − 1Mbyte
Tarea A
Primer
Mbyte
20
Directorio
Tablas Paginas
Segundo
Mbyte
Memoria física
Tarea en Ejecución
de 8 − 12M
de 4 − 8Mb
de 0 − 4Mb
Tablas de páginas
Directorio de páginas
Zona de
memoria
de la
tarea V86
Memoria física
lidt qword ptr ds:IDT32dir ; cargamos registro IDTR
lgdt qword ptr ds:GDT32dir ; cargamos registro GDTR
mov eax, cr0 ; leemos la MSW
or al, 21h ; habilitamos modo protegido
mov cr0, eax ; ponemos la máquina en ¡PMode!
jmp ActivarPG ; limpiamos cola de prebúsqueda
Tarea B
Tarea A
Tarea C
Tiempo del quantum 4 ticks del reloj
21
4 ticks
4 ticks
4 ticks
pushfd ; salvamos EFLGS en la pila
push gs fs es ebx eax ; y todos los registros que van a ser modificados
mov ax, SelData32 ; obtenemos selector del segmento de datos
mov ds, ax ; referencia a los datos a través de DS
mov ax, SelZero16 ; obtenemos selector del primer Mbyte físico
mov gs, ax ; acceso al primer Mbyte a través de GS
mov ebx, [Tactiva] ; EBX = numero de la tarea activa
shl ebx, 2
mov eax, [TSSXesp0+ebx] ; EAX = direccion del campo ESP0 del ; TSS
push dword ptr gs:eax[4] ; salvamos el valor de SS
push dword ptr gs:eax[0] ; salvamos el valor de ESP
mov gs:eax[0], esp ; ponemos en el TSS el nuevo valor de
mov gs:eax[4], ss ; ESP y SS
push ds:[V86_gs+ebx] ds:[V86_fs+ebx] ds:[V86_ds+ebx] ds:[V86_es+ebx]
mov eax, ds:[V86_sp+ebx] ; EAX = offset dentro de la pila V86
mov ebx, ds:[V86_ss+ebx] ; guardamos el valor de SS (V86)
push ebx
shl ebx, 4 ; EBX = dirección física de la pila V86
sub eax, 6 ; dejamos espacio en la pila para poder
; guardar EFlags, CS e IP al retornar
; de la instrucción INT desde V86
push eax
add ebx, eax ; EBX = dirección fisica de SS:SP en V86
22
push ax
mov ax, SelFlat32
mov gs, ax ; GS apuntando a la direccion lineal 0
pop ax
mov word ptr gs:ebx[0], offset int_0 ; valor de IP al regresar ; de V86
mov word ptr gs:ebx[2], codigo32 ; valor de CS al regresar ; de V86
mov eax, ss:esp[14*4] ; EAX = EFlags del PMODE
and ah, NOT 42h ; desactivamos bits IF y NT
push eax
popfd ; y lo ponemos en EFLAGS
mov gs:ebx[4], ax ; guardamos en la pila V86 los flags
or eax, 23000h ; ponemos el IOPL = 3 y bit VM activado
push eax
mov ebx, ss:esp[17*4] ; EBX = número de interrupción * 4
movzx eax, word ptr gs:ebx[2] ; EAX = segmento del servicio ; solicitado
push eax ; valor de CS al conmutar a V86
mov ax,word ptr gs:ebx[0] ; EAX = offset del servicio solicitado
push eax ; valor de IP al conmutar a V86
mov eax, ss:esp[11*4] ; recuperamos el valor de EAX y EBX al
mov ebx, ss:esp[12*4] ; entrar en este procedimiento
iretd ; ¡conmutamos a Virtual 8086 Mode!
test byte ptr ss:esp[3*4+2],2 ; ¿procesador en V86 Mode?
jz exc13 ; No, es una verdadera #GP
add esp, 4 ; Si, sacamos codigo de error de la pila
push ebx eax ; guardamos registros a modificar
mov ax, SelFlat32
23
mov ds, ax ; DS apuntando a la dirección física 0
movzx ebx, word ptr ss:esp[3*4] ; BX = segmento de código ; interrumpido
contInt:
cmp bx, codigo32 ; ¿Se ha terminado el servicio ofrecido?
jne V86_cont ; No, continuamos en modo V86
cmp word ptr ss:esp[2*4], offset int_0
je V86_exit
mov ah, ds:ebx[0] ; AH = código de instrucción
mov al, 3 ; interrupción 3
cmp ah, 0cch ; ¿Era la instrucción INT 3?
je short V86_int ; Si, ejecutar la interrupción 3
inc eax ; miramos para interrupción 4
cmp ah, 0ceh ; ¿Era la instrucción INTO?
je short V86_int ; SI, ejecutar INTO
cmp ah, 0cdh ; ¿Era de la forma INT n?
je V86_cont2 ; No, se ha producido una #GP
jmp V86_exc
V86_cont2:
inc dword ptr ss:esp[2*4] ; incrementamos de nuevo IP para ; apuntar despues de la instrucción INT n
mov al, ds:ebx[1] ; AL = número de interrupción
cmp al, 21h ; ¿es la interrupción 21h?
jne cmpInt10 ; No, comprobar si es la int. 10h
cmp byte ptr ss:esp[1], 4bh ; ¿función 4Bh?
jne cmpInt10 ; No, comprobar la int. 10h
call emul_21_4b ; cogemos nombre del fichero a ejecutar
jmp V86_int ; y ejecutamos el servicio 4Bh de la int 21h
24
jmp V86_int ; ejecutamos verdadera llamada a ; interrupción
cmpInt15:
cmp al, 15h ; ¿Interrupción 15h?
jne short V86_int ; No, es otra interrupción
cmp byte ptr ss:esp[1], 87h ; ¿Función 87h?
je emul_15_87 ; Si, emulamos la interrupción 15, ; servicio 87h
cmpInt10:
cmp al, 10h ; ¿interrupcion de de video 10h?
jne cmpInt15 ; No, comprobar si es la int. 15h
cmp byte ptr ss:esp[1], 0 ; ¿función "establecer modo de video"?
jne cmpInt15 ; No, comprobar si es la int. 15h
call emul_10_00 ; emulamos interrupcion de video
cmpInt15:
cmp al, 15h ; ¿Interrupción 15h?
jne short V86_int ; No, es otra interrupción
cmp byte ptr ss:esp[1], 87h ; ¿Función 87h?
je emul_15_87 ; Si, emulamos la interrupción 15, ; servicio 87h
V86_int:
push ecx ; guardamos registro a modificar en este procedimiento
movzx ebx, al ; EBX = número de interrupción
shl ebx, 2 ; EBX = numero de int * 4
mov ecx, ds:ebx[0] ; ECX = offset de la interrupción
movzx ebx, word ptr ss:esp[7*4] ; EBX = SS de la tarea V86
shl ebx, 4 ; convertimos a lineal
sub word ptr ss:esp[6*4], 6 ; SP = SP − 6 en la tarea V86
mov ds:ebx[2], ax ; ponemos en la pila V86 el valor de CS
25
mov ax, ss:esp[3*4] ; AX = IP
mov ds:ebx[0], ax ; ponemos en la pila V86 el valor de IP
mov ss:esp[3*4], cx ; offset de la interrupción
shr ecx, 16 ; cogemos el segmento
mov ss:esp[4*4], cx ; segmento de la interrupción
pop ecx eax ebx ; restauramos los registros modificados
iretd ; ejecutamos la interrupción V86
add ebx, ss:esp[6*4] ; EBX = dirección lineal de SS:SP en V86
mov ax, ss:esp[5*4] ; AX = FLAGS
mov ds:ebx[4], ax ; salvamos las banderas
and ah, NOT 3 ; ponemos en los flags los bits TF=0 y IF=0
mov ss:esp[5*4], ax ; retocamos FLAGS de la pila
mov ax, ss:esp[4*4] ; AX = CS
shr ecx, 16 ; cogemos el segmento
mov ss:esp[4*4], cx ; segmento de la interrupción
pop ecx eax ebx ; restauramos los registros modificados
iretd ; ejecutamos la interrupción V86
V86_exit:
mov ax, SelData32 ; selector de datos
mov ds,ax ; apuntamos DS al segmento de datos protegido
pop eax ebx ; restauramos valores originales de EAX y EX
mov ss:esp[11*4], eax
mov ss:esp[12*4], ebx
add esp,8 ; sacamos IP y CS de la pila
pop eax ; sacamos EFLAGS
mov eax, [Tactiva] ; numero de la tarea activa
26
shl eax, 2
mov ebx, [TSSXesp0+eax]
mov ax, SelZero16
mov ds, ax
pop dword ptr ds:ebx[0]
pop dword ptr ds:ebx[4]
pop eax ebx es ds fs gs
popfd
ret 4 ; cargamos EIP con el valor siguiente a la
; instrucción de la int16 y eliminamos 4
; bytes de la pila al mismo tiempo
mov byte ptr ss:esp[14*4], al ; parte baja de EFLAGS
mov eax, [Tactiva] ; numero de la tarea activa
cont5:
shl eax, 2 ; obtenemos indice
pop ds:[V86_sp+eax] ds:[V86_ss+eax] ; sacamos registros V86 de ; la pila
pop ds:[V86_es+eax] ds:[V86_ds+eax] ds:[V86_fs+eax] ds:[V86_gs+eax]
mov eax, [Tactiva] ; numero de la tarea activa
shl eax, 2
mov ebx, [TSSXesp0+eax]
mov ax, SelZero16
mov ds, ax
pop dword ptr ds:ebx[0]
pop dword ptr ds:ebx[4]
pop eax ebx es ds fs gs
popfd
27
ret 4 ; cargamos EIP con el valor siguiente a la instrucción de ; int16 y eliminamos 4 bytes de la pila al
mismo tiempo
add eax, ss:esp[2*4] ; obtenemos IP de la excepción
mov eax, ds:[eax] ; obtenemos direccion de la instrucción
cmp ax, 0e3dbh ; ¿era la instrucción FINIT/FNINIT?
je saltar2b ; Si, la saltamos
cmp ax, 3fd9h ; ¿era la instrucción FSTCW [BX]?
je saltar2b ; Si, la saltamos
cmp ax, 3fddh ; ¿era la instrucción FSTSW [BX]?
je saltar2b ; Si, la saltamos
cmp ax, 3dddh ; ¿era la instrucción FSTSW [DI]?
je saltar2b ; Si, la saltamos
cmp ax, 3dd9h ; ¿era la instrucción FSTCW [DI]?
je saltar2b ; Si, la saltamos
cmp ax, 3cd9h ; ¿era la instrucción FSTCW [SI]?
je saltar2b ; Si, la saltamos
cmp ax, 3cddh ; ¿era la instrucción FSTSW [SDI]?
je saltar2b ; Si, la saltamos
cmp ax, 7eddh ; ¿era la instrucción FSTSW [BP−byte]?
je saltar3b ; Si, la saltamos
cmp ax, 0beddh ; ¿era la instrucción FSTSW [BP−word]?
je saltar4b ; Si, la saltamos
; PUERTO 3D4h (indice) y 3D5h (datos) del CRT controller
cmp dx, 3d4h ; ¿controlador CRT?
jne cmp3d5 ; No, comprobamos si es registro de datos
mov ax, SelData32 ; selector de datos
mov ds, ax ; acceso a los datos mediante DS
28
mov eax, [Tactiva] ; obtenemos tarea que provoco la excep.
cmp eax, [TPrimerPlano] ; ¿tarea activa en primer plano?
je emulOutDxAl ; emulamos instruccion "Out Dx, Al"
mov bl, ss:esp ; valor de AL antes de la excepcion
mov ds:[regCRT+eax], bl ; guardamos valor del CRT
pop eax ebx ; sacamos registros de la pila
iretd ; volvemos a la tarea actual
cmp3d5:
cmp dx, 3d5h ; ¿registro de datos del CRT?
jne cmp3c4 ; no, comprobamos si es el Sequencer
mov ax, SelData32 ; selector de datos
mov ds, ax ; acceso a los datos mediante DS
mov eax, [Tactiva] ; tarea ejecutandose actualmente
cmp eax, [TPrimerPlano] ; ¿tarea activa en primer plano?
je emulOutDxAl ; si, emulamos instruccion OUT
mov bl, 19h ; numero de registros del CRTC
mul bl ; AX=indice a registros de la t. activa
mov ebx, [Tactiva] ; tarea activa
mov bl, [regCRT+ebx] ; valor del registro indice
add eax, ebx ; desplazamiento al registro CRT
mov bl, ss:esp ; valor de AL antes de la excepcion
mov [VideoRegCRTC+eax], bl ; guardamos reg. CRTC de la T. activa
pop eax ebx ; sacamos registros de la pila
iretd ; volvemos a la tarea activa
29
Descargar