INTERRUPCIONES ENSAMBLADOR

April 10, 2019 | Author: Miguel Liborio Maceda | Category: Bios, Office Equipment, Computing, Tecnología, Computer Engineering
Share Embed Donate


Short Description

Download INTERRUPCIONES ENSAMBLADOR...

Description

11.1. Interrupciones Las interrupciones son un mecanismo por el que un dispositivo externo puede provocar que el procesador interrumpa momentáneamente la ejecución del programa para atender su petición y luego continuar con el programa desde el punto en que lo había había dejado. Pe ro el procesador 8086 amplía este concepto y permite que un programa pueda generar una interrupción en cualquier momento. A este tipo de interrupciones se las conoce

como

interrupciones

sofware

para

diferenciarlas

de

las

interrupciones hardware, generadas por dispositivos externos al procesador. Existen, no obstante, de 8 a 15 interrupciones que no se llaman de la misma forma que las interrupciones de software, sino mediante el controlador de interrupciones, como interrupciones hardware.

11.1.1. Polling ver versus sus Interrupciones Ambos son protocolos de comunicación entre dispositivos de E/S y el microprocesador. En capítulos posteriores veremos ve remos una técnica más llamada DMA.

11.1.1.1. Polling También llamada técnica de las consultas consultas sucesivas. Es la más simple de las dos. Para ella los dispositivos se conectan al bus de direcciones, al de datos y al de control. El procesador se encarga de ir consultando a los dispositivos conectados a él si necesitan atención o no. En caso negativo se pasa a consultar al siguiente. En cuanto uno conteste que sí, se empezará la transmisión. Esto requiere de un programa llamado bucle de consulta que compruebe un bit del dispositivo de su interfaz. Con este método se sabe exactamente cuándo se pregunta a un dispositivo, cuándo cuándo se comun ica con él y cuánto se tarda en darle servicio. Es una operación sincronizada con el programa.

Se

consigue

así

establecer

fácilmente

unas

prioridades,

consultando antes al dispositivo de mayor prioridad. Por otro lado, este sistema representa una carga para el programa y responde con excesiva lentitud a la llamada del dispositivo, pues hasta que el programa no consulte no se establece la comunicación.

11.1.1.2. Interrupciones

Su filosofía es contraria a la anterior, consiste en que son los dispositivos de E/S los que solicitan atención al procesador, quien al recibirla, puede aceptarla o no. Puede ser que el proceso que se esté efectuando en ese momento requiera la prohibición de esa interrupción, se habla entonces de enmascaramiento de interrupciones . Si no están enmascaradas las interrupciones se suspende el programa que se estaba ejecutando y el control se transfiere a una rutina de atención al dispositivo en cuestión, que una vez terminado, vuelve a ejecutar el proceso interrumpido por donde se suspendió. Para realizar este proceso, la unidad de entrada y salida (su inferfaz) tiene que enviar lo que se llama un vector (la clave que le identifica), normalmente por por el bus de da tos.

11.1.1.3. Conclusión El método polling es más sencillo que el interruptivo, pero más lento. Imaginemos que estamos escribiendo en el ordenador a razón de una pulsación por segundo. El microprocesador debe interrumpir su ejecución cada segundo para recoger esta información. Sin embargo, es posible que a veces escribamos más rápido y otras veces más lento, por lo que el procesador debería mirar más a menudo si ha habido pulsación de tecla. En muchas ocasiones volverá de vacío. Por tanto, vemos que este s istema desperdicia mucho tiempo. Por el contrario, en el sistema interruptivo el microprocesador no deja de hacer sus tareas y, cuando le llega una petición de atención, deja momentáneamente su proceso para atender esta petición (siempre que el proceso que estuviese haciendo no fuese delicado).

11.1.2. Interrupciones internas o excepciones Las genera la propia CPU cuando se produce una situación anormal o cuando llega su momento. Intel definió del 0 al 20h para uso interno de la CPU, desgraciadamente IBM se saltó esta especificación y redefinió del 0 al 1Fh para su propio uso, por lo que existen definiciones duplicadas en las tablas. y

INT 0: Error de división, generada

automáticamente

cuando

el

cociente no cabe en el registro o el divisor es cero. Sólo puede ser generada mediante DIV o IDIV. El 8088/8086 guardan en la pila la sentencia siguiente a la que causó la excepción, mientras que el 286

y superiores guardan la sentencia que la generó. y

INT 1: Paso a paso. Se produce

tras cada instrucción cuando el procesador está en modo traza (utilizado para la depuración de programas). y

2:

INT

Interrupción

enmascarable.

Tiene

absoluta

y

produce

aunque

estén

se

interrupciones

no

prioridad incluso

inhibidas para

las

indicar

un

hecho muy urgente. y

INT

3:

Utilizada

para

poner

puntos de ruptura en la depuración de programas. y

INT

4:

Desbordamiento.

Se

dispara cuando se ejecuta un INTO y había desbordamiento. Si no hay desbordamiento INTO equivale a NOP. y

INT

5:

rango excedido en la

instrucción BOUND (sólo 186 y superiores).

Ha

sido

incorrectamente empleada por IBM para

volcar

la

pantalla

por

impresora. y

INT

6:

Código

de

operación

inválido (sólo a aprtir del 186). Se produce al ejecutar una instrucción indefinida. En la pila se guarda el CS:IP de la instrucción ilegal. y

INT 7: Dispositivo no disponible

(sólo a partir del 286).

y

INT 8: Excepción de doble fallo (a

partir del 286) y

INT

9:

segmento

Desbordamiento del

coprocesador

del (a

partir del 286) y

INT A: Segmento de estado de

tarea inválida (a partir del 286) y

INT B. Segmento no presente (a

partir del 286) y

INT C: Excepción de pila (a partir

del 286) y

INT D: Excepción de protección

general (a partir del 286) y

INT E: Fallo de página (a partir

del 286) y

INT F: Reservado

y

INT 10: Error de coprocesador ( a

partir del 286)

11.1.3. Interrupciones hardware (IRQs) Acrónimo

de

Interrupt

ReQuest

o

Petición

de

Interrupción.

Es

la

denominación habitual de las interrupciones hardware, generadas por la circuitería del ordenador en respuesta a algún evento. En orden de prioridad: IRQ Interrupción Función

Se produce con una frecuencia de 18,2 veces por segundo. Hay un

0

8

1

9

Generada al pulsar o soltar una tecla.

2

A

Retrazo vertical en EGA/VGA

8

70

Generados en los AT y superiores por el segundo chip controlador

9

71

de interrupciones

pulso cada 55 milisegundos.

10

72

11

73

12

74

13

75

14

76

15

77

3

B

Se requiere servicio COM2 o COM4

4

C

Se requiere servicio COM1 o COM3

5

D

Disco duro o datos requeridos por LPT2

6

E

Servicio de disquete requerido

7

F

Datos requeridos por LPT1 Tabla 11-01 - Interrupciones Hardware

En el PC, los dispositivos susceptibles de provocar interrupciones están conectados al chip 8259, que es el que está conectado a la pata de petición de interrupción del procesador y es el que genera las señales de interrupción en la forma en que las espera éste. Este circuito soporta ocho líneas de entrada de petición de interrupción (IRQ0 A IRQ7). El número de interrupción generada por el 8259 se calcula como un número base de 8 al que se le suma un número entre 0 y 7 correspondiente a la línea de interrupción, por lo que las ocho distintas IRQs ge neran interrupciones entre 8 y 15.

11.1.4. Interrupciones software Acceder directamente a cada uno de los elementos del ordenador sería terrible, además de que existen multitud de modelos diferentes, cada uno con sus propias identificaciones y formas diferentes de programarlas. Para allanar este problema existe la BIOS que es un extenso conjunto de rutinas de entrada/salida que podemos usar para comunicarnos con el ordenador sin tener en cuenta el modelo de sus componentes, de esta manera estandarizamos nuestro código.

11.1.4.1. Localización La BIOS se encuentra en la memoria ROM. Cuando se enciende la computadora "arranque en frío" el procesador ingresa un estado de restablecer, pone todas las localidades de la memoria en cero, realiza una verificación de la paridad de memoria y coloca FFFFh en el registro CS y cero en el IP, por lo que la primera instrucción a ejecutar está en FFFF:0000 que es el punto de entrada de la BIOS. A partir de aquí, la BIOS verifica los

diferentes puertos para identificar e inicializar dispositivos que están conectados, y a continuación define el "Vector de Interrupciones" a partir de la localización 0000:0000 de la memoria, que es una secuencia de punteros lejanos (4 bytes) a las rutinas de interrupción de la BIOS. A continuación comprueba si existe un sistema operativo en el disco para acceder a su primer sector que contiene el cargador de arranque que es un sistema operativo temporal que recoge el control de la BIOS para cargar en memoria el sistema operativo final. Una vez que se ha cargado el MS -DOS en memoria tenemos relleno el Vector de Interrupciones que comprende las localidades de memoria 0000:0000 ± 0000:03FF donde se hayan todas las interrupciones de la BIOS (desde 00h hasta 1Fh) y del DOS (desde 20h hasta 3FFh), que comienza a partir de la última de la BIOS, por ejemplo, la primera del DOS es 20h para la terminación del programa. Puesto que son punteros lejanos (4 bytes) y su posición se corresponde con el número de la interrupción en la forma: para la interrupción i, la posición i*4. Así, para INT 21h buscaremos en 21h * 4h = 84h, es decir en 0000:0084. Existe además un área de datos reservado para la BIOS de 256 bytes (100h) a partir de la posición 0040 :0000 con abundante inform ación del sistema. Obsérvese que 0040:0000h equivale a 0000:0400h, que está justo a continuación de la última interrupción MS- DOS.

11.1.4.2. Ejemplo / traceo Usaremos el programa dosCM3 para rastrear el uso de la interrupción INT 21h: F:\Alfonso\Codigos\Cap8>debug DOSCM3.COM  -u 100, 10B 0CC8:0100 BA0C01

MOV

DX,010C 

0CC8:0103 B409

MOV

AH,09

0CC8:0105 CD21

INT

21

0CC8:0107 B8004C

MOV

AX,4C00

0CC8:010A CD21

INT

21

-r ES  ES 0CC8 :0 -d ES:84 l4 0000:0080 |...

7C 10 A7 00

-p

AX=0000

BX=0000

CX=002D

DX=010C

SP=FFFE

ES=0000

SS=0CC8

CS=0CC8

IP=0103

BP=0000

SI=0000

DI=0000 DS=0CC8

NV UP EI PL NZ NA 

PO NC  0CC8:0103 B409

MOV

AH,09

-p

AX=0900

BX=0000

CX=002D

DX=010C

SP=FFFE

ES=0000

SS=0CC8

CS=0CC8

IP=0105

BP=0000

SI=0000

DI=0000 DS=0CC8

NV UP EI PL NZ NA 

PO NC  0CC8:0105 CD21

INT

21

-t

AX=0900

BX=0000

CX=002D

DX=010C

SP=FFF8

ES=0000

SS=0CC8

CS=00A7

IP=107C

BP=0000

SI=0000

DI=0000 DS=0CC8

NV UP DI PL NZ NA 

PO NC  00A7:107C 90

NOP 

-q 

En primer lugar desensamblamos el código para tenerlo presente, a continuación anulamos el valor de ES para poder acceder al área 0000 :0084 del vector de Interrupciones donde se guarda el puntero al código de la interrupción 21h (obsérvese que 21h * 4h = 84h) donde obtenemos el valor "7C 10 A7 00" en formato little endian. Seguidamente ejecutamos el código hasta llegar a "INT 21" donde ejecutamos con "t" para tracearla y comprobamos que saltamos a la posición 00A7 :107C, que es justamente el valor que obtuvimos en ES:84 traducido del little endian. A continuación salimos.

11.1.4.3. Proceso de la interrupción Las interrupciones pueden ser activadas directamente por el ensamblador invocando el número de la interrupción a través de la instrucción INT y el número de función deseada pasada generalmente en el registro AH. Cuando se ejecuta esta instrucción el procesador :

1. Mira hacia la dirección de la rutina de interrupción en la Tabla del Vector de Interrupciones

(en

la

posición

0000:0000), llegando a la deseada mediante la fórmula 0000:i*4 donde i es el número de interrupción. 2. Borra la bandera de atrape (TF) y activa la bandera de interrupciones (IF) 3. Guarda el registro de banderas en la pila, el segmento de código actual (CS) y el puntero de instrucciones actual

(IP),

en

este

orden

(la

instrucción actual es la siguiente a la sentencia INT). Al igual que con "CALL" esto asegura que el control vuelve a la siguiente posición lógica en el programa. 4. Salta a la dirección de la rutina de interrupciones, como se especificó en la Tabla del Vector de Interrupciones. 5. Ejecuta el código de la rutina de interrupciones hasta encontrarse una instrucción "IRET". 6. Recupera de la pila los valores de los registros IP, CS y de banderas. Hemos visto que todas las interrupciones guardan en la pila el registro de banderas y el CS:IP de la siguiente instrucciones, pero las interrupciones internas o excepciones sólo guardan la CS :IP de la instrucción causante. Además, las excepciones de división del 8086/88 son diferentes, pues devuelven la instrucción siguiente a la división.

11.1.5.Tabla de interrupciones del sistema INT

Situación Nombre

00h

CPU

División por cero

01h

CPU

Ejecución paso a paso

02h

CPU

No enmascarable (NMI)

03h

CPU

Puntos de ruptura

04h

CPU

Desbordamiento (INTO)

05h

BIOS

Volcar pantalla por impresora

06h

CPU

Código de operación incorrecto

07h

CPU

Reservada

08h

IRQ0

IRQ 0 : Contador de hora del sistema

09h

IRQ1

IRQ 1 : Interrupción de teclado

0Ah

IRQ2

IRQ 2 : canal E/S, segundo 8259 del AT

0Bh

IRQ3

IRQ 3 : COM2

0Ch

IRQ4

IRQ 4 : COM 1

0Dh

IRQ5

IRQ 5 : disco duro XT, LPT2 en AT, retrazo vertical PCjr

0Eh

IRQ6

IRQ 6 : Controlador del disquete

0Fh

IRQ7

IRQ 7 : LPT1

10h

BIOS

Servicios de vídeo

11h

BIOS

Listado de equipos

12h

BIOS

Tamaño de memoria

13h

BIOS

Servicios de disco

14h

BIOS

Comunicaciones en serie

15h

BIOS

Servicios del sistema

16h

BIOS

Servicios de teclado

17h

BIOS

Servicios de impresora

18h

BIOS

IBM Basic (ROM del Basic)

19h

BIOS

Arranque del sistema

1Ah

BIOS

Fecha/hora del sistema

1Bh

BIOS

Acción de CTRL-Break

1Ch

BIOS

Proceso periódico del usuario

1Dh

BIOS

Dirección de la tabla de parámetros de vídeo

1Eh

BIOS

Dirección de la tabla de parámetros de disquete

1Fh

BIOS

Dirección de caracteres gráficos

20h

DOS

Fin de programa

21h

DOS

Llamar función DOS

22h

DOS

Dirección de terminación

23h

DOS

Dirección de la rutina CTRL-BREAK del DOS

24h

DOS

Dirección manipulador de errores críticos

25h

DOS

Lectura absoluta de disco

26h

DOS

Lectura absoluta de disco

27h

DOS

Finalización de programa residente

28h

DOS

El DOS está desocupado

29h

DOS

DOS TTY (impresión en pantalla)

2Ah

DOS

Red local MS net

2Bh-2Dh DOS

Uso interno del DOS

2Eh

DOS

Procesos Batch

2Fh

DOS

Multiplex

30h

CPM

Compatibilidad CP/M-80 (xx :YYyy en JMP Xxxx :YYhh)

31h

DPMI

Compatibilidad CP/M-80 (XX en JMP Xxx :YYyy)

32h

Reservada

33h

Controlador del ratón

34h-3Fh

Reservadas

40h

BIOS

Interrupción de disquete

41h

BIOS

Parámetros del disco duro 1

42h

BIOS

Apunta a la INT 10h original de la BIOS si existe VGA

43h

BIOS

Caracteres gráficos EGA

44h-45h BIOS

Reservadas

46h

Parámetros del disco duro 2

BIOS

47h-49h BIOS

Reservadas

4Ah

Alarma del usuario

BIOS

4Bh-5Fh BIOS

Reservadas

60h-66h

Para uso de los programas

67h

EMS

68h-6Fh

Interrupción de EMS (controlador EMS) Reservadas

70h

IRQ8

IRQ 8 : Reloj de tiempo real AT

71h

IRQ9

IRQ 9 : IRQ 2 redireccionada

72h

IRQ10

IRQ 10 : reservada

73h

IRQ11

IRQ 11 : reservada

74h

IRQ12

IRQ 12 : Interrupción de ratón IBM

75h

IRQ13

IRQ 13 : error de coprocesador matemático

76h

IRQ14

IRQ 14 : controlador de disco fijo

77h

IRQ15

IRQ 15 : reservada

78h-7Fh

Reservadas

80h-85h

Reservadas para el Basic

86h-F0h DOS

Usadas por el BASIC

F1h-FFh

Para uso de los programas Tabla 11-02 - Tabla de interrupciones del sistema

11.1.6. ISRs Acrónimo de Interruption Service Routine o Rutina de Servicio a la Interrupción. Es la rutina para cada tipo de interrupción. Ya hemos visto que básicamente, cuando ejecutamos un "INT" metemos en la pila el registro de banderas, el CS y el IP, que apuntan a la siguiente instrucción, y redireccionamos la ejecución a la rutina de interrupción pasada en "INT" que termina cuando encuentra un "IRET" que hace un "POP" de la pila del IP, CS y del registro de banderas. Es decir, el funcionamiento es muy similar al de una subrutina llamada con un "CALL" y terminada con un "RET". También hemos dicho que la dirección de la rutina a llamar con un "INT" se encuentra en la tabla de vectores de interrupción localizada a partir de 0000:0000h. Veamos algunas características y consejos interesantes de las ISR.

11.1.6.1. Preservar contenido de registros Según la filosofía que hemos comentado al principo de este capítulo, las interrupciones pueden ser ejecutadas en cualquier momento, por ejemplo, supongamos que tenemos el siguiente código: MOV AX, 500 ADD BX, AX 

Y la interrupción que tratamos se ejecuta entre ambas, de tal modo que modifica el contenido del registro AX, el resultado puede ser muy grave. Por tanto, es conveniente preservar el contenido de todos los registros en el código de nuestra ISR, el registro de banderas ya se hace de forma automática, de nosotros depende hacer el resto con un "PUSH" de todos los registros y un "POP" de éstos al terminar la ISR antes del "IRET".

11.1.6.2. Deshabilitar ejecución de interrupciones De nuevo, debido a que las interrupciones pueden ser ejecutadas en cualquier momento, hay veces que es necesario deshabilitar esta posibilidad

para asegurarnos de que no se va a alterar cierto sector crítico de nuestro código. La bandera IF indica si las interrupciones están activadas o desa ctivadas con un 1 y un 0 respectivamente. Esto se hace con STI y CLI respectivamente. Supongamos que queremos definir dentro de nuestra ISR una pila propia en otro segmento: MOV AX, 9000h MOV SS, AX  MOV SP, 3000h

Pero ocurre una interrupción justo de pués de "MOV SS, AX". Ahora el registro de pila SS contiene un valor, seguramente, diferente al esperado en la ISR que ha tomado el control, con lo que el resultado puede ser desastroso. Una forma de evitar esto es desactivando temporalmente las interrupciones: MOV AX, 9000h CLI

; Deshabilitamos interrupciones

MOV SS, AX  MOV SP, 3000h STI

; Habilitamos interrupciones

De esta forma nos aseguramos que en la parte crítica de nuestro código no ocurre ninguna interrupción. Sin embargo, existen ciertas situaciones críticas en las que ocurren excepciones, aunque las tengamos deshabilitadas con CLI. Es el caso, por ejemplo de fallos importantes de hardware. En el caso de modificar manualmente el contenido del vector de interrupciones, sería necesario deshabilitar éstas, por si se ejecuta alguna antes de completar este proceso, pero ya hemos visto que esto no siempre es

suficiente

puesto

que

un

CLI

no

inhibe

una

interrupción

no

enmascarable, por lo que una opción buena sería modificar la doble palabr a de un solo golpe con "REP MOVS" con lo que no damos oportunidad a ninguna interrupción a actuar en medio. Obsérvese que si hemos cambiado sólo una palabra de las dos y una salta una interrupción en medio que hace uso de la que estamos cambiando, el resul tado puede ser impredecible, puesto que sólo hemos cambiado su dirección a medias.

11.1.6.3. Reservar suficiente espacio de pila

Aunque para nuestro código no necesitemos mucho espacio de pila, sí será necesario reservar el suficiente para las posibles

interrupciones que

pudieran surgir durante su ejecución.

11.1.7. Gestión de interrupciones Vamos a ver algunos ejemplos de creación de interrupciones y solapamiento de otras. Es necesario informar al controlador de interrupciones que ya ha sido atendida una IRQ inmediatamente antes de retornar de la rutina de servicio a la interrupción con IRET. Esto se hace enviando el valor 20h al puerto 20h, que es recogido por el 8259. No es necesario hacer esto, sin embargo, en el caso de que nuestra ISR pase el control a la antigua ISR, puesto que esta última se encargará de hacerlo. MUY IMPORTANTE Cuando se intercepta una interrupción, con nuestra propia ISR, hay que proc urar preservar los registros de segment o, no sólo en la ISR, sino en el resto de procedimientos que usemos fuer a de ella, especialmente con las interrupciones del reloj. En mi caso, he interceptado la 1Ch del reloj en el Tetris y me daba problemas algún otro procedimiento. Tuve que poner en cada procedimiento explícitamente qué valían DS y ES. Imagínate que usamos variables, pero nuestra rutina ya no sabe qué vale DS o ES, podremos tener resultados insospechados.

Se puede reemplazar una rutina de interrupción con nuestro código, para lo cuál debe cumplir: y

Provee

una

nueva

ISR

para

manejar la interrupción. y

Guardamos la dirección de la ISR que queremos cambiar.

y

Reemplaza

la

dirección

de

la

antigua rutina en la Tabla del Vector de Interrupciones con la dirección de la nueva rutina. y

La

nueva

rutina

debería

ser

siempre definida como LEJANA y terminar

con

una

instrucción

"IRET" en lugar de "RET".

y

Antes de terminar el programa, volvemos a poner en la Tabla del Vector

De

dirección

Interrupciones

guardada

de

la

la ISR

cambiada original. Lo que normalmente haremos será modificar temporalmente una ISR por una nuestra para cumplir con algún tipo de gestión. Una vez termi nada, deberíamos restaurar el método original. Sin embargo, no tenemos por qué hacer esto forzosamente, existen algunas localidades en la Tabla de Vectores de Interrupciones reservadas para las interrupciones del usuario, que si utilizamos, no es necesario volver a poner el valor original, de modo que nuestra interrupción estará disponible durante toda la vida de la sesión del

MS-DOS.

Asimismo

quizás

queramos

modificar

alguna

ISR

indefinidamente para establecer un nuevo controlador de dispositivos, por ejemplo. El uso de interrupciones nos ayuda a la creación de programas haciéndolos más pequeños y fáciles de entender, más estándares y menos dependientes del tipo de hardware instalado, puesto que son la BIOS y el DOS los encargados de proporcionarnos estos servicios.

11.1.7.1. Interrupciones para gestión de interrupciones El MS-DOS nos proporciona ciertas herramientas que nos facilitan la gestión de interrupciones. Int AH

21h 25h

Inf. Extra

Descripción

AL = nº interrupción (0 a 255) DS :DX = dirección nueva ISR

Establece una nueva ISR para el número de interrupción indicado en AL Devuelve en ES:BX la dirección

21h 35h

de la ISR correspondiente al

AL = nº interrupción (0 a 255)

número de interrupción pasado en AL

En AH ponemos el identificador del programa que engancha con la interr upción. Del 00h al 2Fh BFh están reservados, sólo disponemos para los programas del C0h al FFh. AL es el có digo de función

A esta interrupción se la llama multiplex. Es un método para verificar la presencia de un TSR y comunicarse con él.

Tabla 11-03 - Funciones para gestión de Interrupciones

Con la función 35h obtenemos de la Tabla de Vectores de Interrupción la dirección de la ISR que queremos modificar. Esta función la guardamos. Con la función 25h establecemos la nueva ISR que sustituirá a la anterior, básicamente lo que hace es meter en aquella casilla de la Tabla del Vector de Interrupciones la dirección de nuestra ISR. Una vez que nuestra ISR haya cumplido su cometido deberemos restaurar la dirección de la antigua ISR que tenemos guardada en su casilla de la Tabla del Vector de Interrupciones. Este es el método recomendable para la gestión de interrupciones.

11.1.7.2. Métodos para la gestión de interrupciones El método deseado, puesto que es el más sencilloy el estándar porque nos lo ofrece el MS-DOS, es el uso de las interrupciones que hemos visto arriba. Sin embargo, la interrupción 21h también puede ser interceptada, con lo que el primer método podría no funcionar. En este caso tan extraordinario podríamos vernos obligados a hacerlo a mano.

Equivalente a la función 25h PUSH ES  MOV DI, Vector*4

; Dirección en la Tabla de Vectores a

cambiar  MOV AX, 0 MOV ES, AX  LEA SI, newISR

; DS: SI

-> newISR

MOV CX, 2 CLI

; Desactivamos interrupciones

REP MOVSW  STI  POP ES 

Equivalente a la función 35h PUSH DS  PUSH ES  MOV SI, Vector*4 guardar  PUSH DS  POP ES  MOV AX, 0 MOV DS, AX  LEA DI, oldISR MOV CX, 2

; Dirección en la Tabla de Vectores a

CLI

; Desactivamos interrupciones

REP MOVSW  STI  POP ES  POP DS 

11.1.8. Ejemplos Veremos algunos códigos de ejemplo de intercepción de interrupciones. Importante Es más que conveniente ejecutar estos ejemplos desde la ventana de c omandos de Windows. Si posees un W95 o W98 te irán todos bien, pero si tienes un XP seguramente no te f uncionarán algunos, en este c aso habría que echar mano de una plataforma como DOSBox, especialmente los pr ogramas residentes. Y es que WXP no parece tratarlos muy bien.

11.1.8.1. Int 4h IntCM1 IntCF1 IntCN1 R esultado en pantalla

Código

Código Código

C:\Trabajo\AOE\Codigos\Cap11>IntCF1

[bin]

[bin]

Mensaje de desbordamiento desde mi ISR

[bin]

S ource

11-01 - Intercepción de la interrupción 4

Con la función 35h de Int 21h la dirección de la ISR de la interrupción 4h en ES:BX, que guardamos en la variable "vector" (little endian). Con la función 25h de Int 21h modificamos en la Tabla de Vectores de Interrupción la dirección de la ISR de la Int 4h por nuestra nueva rutina en "newISR". A continuación comprobamos si funciona. Primero hacemos una multiplicación que no desborda, por lo que el "INTO" equivale a un "NOP". La siguiente multiplicación sí desborda, por lo que el "INTO" pasa a "newISR" que muestra el mensaje. Finamente, reponemos la dirección de la ISR original de la Int 4h. Obsérvese que la nueva ISR termina con un "IRET".

11.1.8.2. Int 21h IntCM2 IntCF2 IntCN2 R esultado en pantalla

Código

Código Código

C:\Trabajo\AOE\Codigos\Cap11>IntCM2 Instalada nueva Int 21h

[bin]

[bin]

[bin]

alfonso Has pulsado 8 teclas

S ource

11-02 - Intercepción de la interrupción 21h

Primero guardamos la dirección de la ISR original en la variable "OldInt21h" que se encuentra dentro de la rutina "NuevaInt21h" e instalamos la nueva ISR

para

la

interrupción

21h

correspondiente

al

procedimiento

NuevaInt21h. En un bucle vamos recogiendo la s teclas pulsadas con la función 1h de Int 21h y terminamos al pulsar la tecla retorno. Esta función vuelca por pantalla la tecla pulsada, por lo que vamos viendo lo que vamos escribiendo. Para terminar restauramos en la Tabla de Vectores de Interrupción la dirección de la ISR original de la Int 21h. Nuestra nueva ISR para Int 21h activa las interrupciones con "STI", guarda el registro de banderas con "PUSHF" (puesto que el último POP de IRET es "POPF") y hace un "CALL" lejano (DB 9Ah) a la ISR original de la Int 21h para

que

se

encargue

de

todo

el

trabajo

duro.

A

continuación

incrementamos el contador. Esto se ejecuta cada vez que pulsamos una tecla, con lo que finalmente tendremos la cuenta de teclas pulsadas. Obsérvese que la tecla retorno también cuenta. Obsérvese que nuestra ISR la podemos construir de otra manera quizás más limpia y elegante. Puesto que hacemos una llamada a la antigua ISR de la Int 21h y ésta finaliza con un "IRET" y el acceso a nuestra ISR se efectúa mediante una "INT", podríamos hacer la llamada a la antigua ISR mediante un simple "JMP". No necesitamos un "PUSHF" puesto que esto ya lo hizo la "INT" que llamó a nuestra ISR, la cuál tampoco necesitamos terminar con una "IRET", puesto que eso ya lo hace la antigua ISR. En resumen, el código del procedimiento "NuevaInt21h" quedaría como sigue: NuevaInt21h PROC FAR ; Propósito: Nueva ISR para INT 21h, cuenta las teclas pulsadas hasta intro ; entrada

: NumTeclas

; salida

: NumTeclas

; Destruye : Ninguna STI

; Activa bandera de

interrupciones INC

BYTE PTR [NumTeclas]

JMP

[CS:OldInt21h]

CLI NuevaInt21h

; Código de la tecla pulsada

; to old BIOS INT9 handler  ENDP 

La variable "OldInt21h" la hemos situado en la zona de datos. Tenemos el código completo en IntC?2b.asm2. Obsérvese que cuando entramos en una ISR sólo conocemos el valor del registro "CS", que en un archivo "COM" coincide con "DS".

IntCM2b IntCF2b IntCN2b R esultado en pantalla

Código

Código

Código

C:\Trabajo\AOE\Codigos\Cap11>IntCN2b Instalada nueva Int 21h

[bin]

[bin]

[bin]

alfonso Has pulsado 8 teclas S ource

11-03 - Intercepción de la interrupción 21h

11.1.8.3. Int 9h IntCM3 IntCF3 IntCN3 R esultado en pantalla

Código

Código Código

C:\Trabajo\AOE\Codigos\Cap11>IntCM3 Instalada nueva Int 9h

[bin]

[bin]

[bin] Has pulsado 0 teclas S ource

11-04 - Intercepción de la interrupción 9

El mecanismo es igual al programa anterior sólo que en el bucle usamos el puerto 60h para recoger la tecla pulsada y la comparamos con el código scan "1Ch" para comprobar si hemos pulsado la tecla retorno, en cuyo caso salimos. Al pulsar o soltar una tecla se activa la IRQ1 correspondiente a la interrupción 9h, que es la que interceptamos en este ejemplo. Por tanto, en nuestra ISR distinguimos esta posibilidad para incrementar el contador sólo cuando pulsamos una tecla.

11.1.8.4. Int 8h Vamos a ver en este ejemplo cómo interceptamos la IRQ0 que se produce 18,2 veces por segundo (se genera un pulso cada 55 milisegundos) y que gestiona el tiempo. El resultado es simular un reloj en modo texto, cuyas agujas se mueven cada dos segundos. IntCM4 IntCF4 IntCN4 R esultado en pantalla

Código

Código Código

[bin]

[bin]

[bin]

S ource

11-05 - Intercepción de la interrupción 8

El código de intercepción de la interrupción 8h es similar a como lo hemos venido haciendo. En la nueva ISR preservamos los registros que usamos e incrementamos el contador de pulsos y el puntero a las "agujas" guardadas en la variable "Indica". Cuando el contador de pulsos ha llegado a los dos segundos pintamos en la esquina superior derecha la aguja apuntada por

"Posicion", el cuál movemos un punto a la derecha hasta que llega a la última aguja, en cuyo caso le hacemos apuntar a la primera. Además limpiamos la variable que cuenta los pulsos.

11.1.8.5. Reloj en tiempo real Vamos a ver otro ejemplo de intercepción de la Int 8h para mostrar en la esquina superior derecha de la pantalla un reloj en tiempo real. R elojCM1 R elojCF1 R elojCN1 R esultado en pantalla

Código

Código

Código

[bin]

[bin]

[bin]

17:01:14 C:\Trabajo\AOE\Codigos\Cap11>RelojCM1 S ource

11-06 - Reloj interceptando Int 8h

Primero guardamos la dirección de la ISR original de la Int 8h. A continuación la cambiamos por la nuestra, situada en el procedimiento "Reloj". Para obtener la hora actual accedemos al CMOS a través de los puertos 71h y 72h (más información en el siguiente capítulo) que se encuentran en formato BCD empaquetado, por lo que necesitamos una rutina que lo convierta a ASCII y lo imprima por pantalla, esta rutina es el procedimiento "BCD2ASCII" que usa el acceso directo a memoria de vídeo para escribir en pantalla. Una vez establecida nuestra rutina esperamos con un bucle a pulsar la tecla escape, tras lo cuál restituimos la ISR original de la Int 8h. Este ejemplo es interesante, pues es la base para la construcción de un TSR que muestre por pantalla un reloj en tiempo real en la esquina superior izquierda de la pantalla, lo veremos en el siguiente capítulo. Obsérvese que para hacer un salto lejano en NASM es necesario el uso del prefijo "FAR" delante de la dirección a saltar.

11.1.8.6. Muestra teclado Echemos un vistazo al programa "KeybC?1.asm" que finalmente colgué en el capítulo 9.1.4.. En este ejemplo interceptamos la Int 9h para mostrar gráficamente qué teclas pulsamos y soltamos. Además tenemos la posibilidad de pulsar varias teclas a la vez, muy útil en los videojuegos.

11.1.8.7. Hora y fecha actuales

En este ejemplo no interceptamos interrupciones, simplemente, usamos éstas para obtener la fecha y hora actuales. IntCM5 IntCF5 IntCN5 R esultado en pantalla

Código

Código Código

[bin]

[bin]

C:\Trabajo\AOE\Codigos\Cap11>IntCN5 La fecha actual es 01-08-2010

[bin]

La hora actual es 17:11:04 S ource

11-07 - Hora y fecha actuales con interrupciones

11.1.8.8. Establece rutina propia para interrupción Vamos a ver un ejemplo con sintaxis FASM (otras dos también disponibles) de cómo establecemos nuestra propia rutina ISR en un vector de interrupción libre que luego podremos usar con una instrucción "Int" normal: VectLCM1 VectLCF1 VectLC N1

R esultado en pantalla

Código

C:\Trabajo\AOE\Codigos\Cap11>VectLCF1

Código

Código

El vector 80h está libre Este es un mensaje desde la interrupcion 80 C:\Trabajo\AOE\Codigos\Cap11>VectLCF1

[bin]

[bin]

[bin]

El vector 81h está libre Este es un mensaje desde la interrupcion 81 C:\Trabajo\AOE\Codigos\Cap11>  S ource

11-08 - Búsqueda de un vector de interrupción libre

Observamos en la salida la cadena de texto imprimida por nuestra ISR. Si ejecutamos de nuevo el programa, el vector 80h ya no está libre porque lo ocupamos en la anterior ejecución. Podemos comprobar haciendo otro programa que podemos llamar a la interrupción 80h y nos imprimirá un mensaje (el número de interrupción libre que mostrará será el último establecido), de modo que tendremos este servicio disponible hasta que otro programa pise el código de nuestra ISR o cerremos nuestra sesión de MS-DOS. Un hecho destacado en este código es que usamos un artificio para modificar el código en ejecución, para lo que definimos la etiqueta "NumVect" que apunta al número de la sentenci a "INT 0", que modificamos en cuanto conocemos el número de vector libre. De esta forma usamos nuestra ISR como un procedimiento cualquiera con la salvedad de que la llamamos con una interrupción en lugar de un "CALL". Normalmente, cuando modificamos un ve ctor de interrupción, tras haber finalizado su trabajo, deberíamos dejarlo en su formato original, no hacerlo

así puede resultar peligroso. En este programa no lo hemos hecho porque el objetivo perseguido es mostrar el uso de nuestra ISR con una instrucción "Int".

11.1.8.9. Buscar Interrupción Supongamos que hemos instalado una interrupción que alguien quiere usar pero no sabe dónde está. Para resolver este problema debemos dotar a nuestra ISR de un método de identificación, normalmente haciendo que la función 0 del gestor de la interrupción devuelva una cadena o un código identificativo que otros programas podrán testear antes de usarlo. Además ya hemos visto que usualmente todas las interrupciones disponen de varias funciones ofreciendo un trabajo diferente según la elegida. Es importante recalcar que la interrupción que hayamos creado permanecerá funcional mientras no hay otro programa que pise el código de nuestra ISR, cuando cargamos otro programa en memoria, lo más probable es que lo pise. La forma correcta de crear una interrupción y que ésta permanezca funcional en memoria indefinidamente es a través de una TSR que veremos en el capítulo siguiente, pues así nos aseguramos que esa porción de memoria ocupada por nuestra ISR no va a ser pisada por ningún otro código. Vamos a ver con un ejemplo todo ello dentro de un mismo programa por lo que acabamos de comentar. VectLCM2 VectLCF2 VectLC N2

R esultado en pantalla

Código

c:\Trabajo\aoe\codigos\cap11>VectLCM2

Código

Código

Interrupcion establecida en el vector 80h Mensaje de la funcion 1 de la interrupcion 80h

[bin]

[bin]

[bin]

Mensaje de la funcion 2 de la interrupcion 80h No existe esta funcion S ource

11-09 - Creamos interrupción con funciones e identificación

Primero buscamos un vector de interrupción libre y aquí establecemos la dirección de nuestra ISR que chequea si le pedimos su identificación (AH=0) en cuyo caso devuelve AX=1212h, si AH=1 ejecuta la función 1 que manda un mensaje a pantalla y si AH=2 ejecu ta la función 2 que manda otro mensaje a pantalla, si AH posee cualquier otro valor indica que no existe esa función. Tras esto buscamos la interrupción recién creada entre las 128 últimas posiciones de la tabla de interrupciones. Para ello comprueba que no es un vector de interrupción libre, en cuyo caso le pide que se identifique y si el resultado devuelto es el correcto llama a las funciones 1, 2 y 3.

Finalmente restablece el valor original en el vector de interrupciones y sale al DOS. Como

apunte

interesante,

cabe

destacar

que

en

el

procedimiento

RestoreOldInt la orden "LDS DX, OldInt" cambia el valor de DS, por lo que si hacemos antes "MOV AL, NumIntL" fallará por razones obvias. Además, quizás ya te hayas percatado que tanto en FASM como en NASM hay que utilizar "LDS DX, [OldInt]".

11.1.9. Interrupción Multiplex Hasta ahora, para encontrar una interrupción teníamos que recorrer el vector de interrupciones y preguntar por ella, lo cuál implica ejecutar cada una de las interrupciones. Es natural estremecerse ante este sistema porque podemos llegar a ejecutar cientos de códigos de los que, en principio, no tenemos por qué saber nada y lo normal será que se bloquee el sistema. Disponemos de un mecanismo mucho más eficiente y elegante desde la versión 3.0 del MS-DOS, se trata de la interrupción multiplex 2Fh, que básicamente es un gestor de interrupciones, cuando un programa necesita usar un código residente, genera una interrupción 2Fh. En AH debe ir el identificador de la interrupción. Para engancharse a la interrupción múltiple, no debemos sustituirla sin más, puesto que existen multitud de programas que también la siguen utilizando, por lo que deberemos interceptarla como ya sabemos dejando una puerta abierta para ejecutar la ISR estándar en el caso de que no pasemos por nuestro código. Puesto que ésta es una interrupción software, el registro de banderas puede ser utilizado por algún gestor para devolver información, por lo que no deberíamos terminar la Int 2Fh con un "IRET", usaremos en su lugar "RET n" donde n indica el número de bytes adicionales a extraer de la pila. Obsérvese que Int 2Fh equivale a "PUSHF" y "CALL 2Fh". VectLCM3 VectLCF3 VectLC N3

R esultado en pantalla

Código

Código

Código

c:\Trabajo\aoe\codigos\cap11>VectLCN3

[bin]

[bin]

[bin]

Mensaje de la funcion 1

S ource

11-10 - Creamos interrupción con funciones e identificación vía múltiplex

Antes de interceptar la interrupción 2Fh guardamos su dirección del vector de interrupciones para restablecerla al final del programa. En nuestra ISR "NueVector" comprobamos que preguntan por nosotros mirando si AH = C0h, si no es así saltamos a la ISR original de 2Fh. Si nos buscaban a nosotros, el desarrollo es similar al VectLC?2.asm. Probamos nuestro código con el procedimiento "LlamaInt" donde prime ro comprobamos que está

disponible y a continuación la llamamos de nuevo usando la función AL = 1 para mostrar un mensaje por pantalla.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF