<-Informática\ensamblador 80x86...

Ejemplo 2

La instalación de manejadores de interrupción es uno de los tipos de programas que más cuesta de entender o que más dudas sugiere, lo cual es comprensible en gran medida.
¿cómo entenderlo?¿qué hacer si se propone un ejercicio de este estilo?....son algunas de las preguntas típicas.
Para resolver estas preguntas nos vamos a basar en nuestro programa, el cual instala un manejador que se ejecutará automáticamente cada vez q se produzca la interrupción 8h (del oscilador) y que escribira un '*' en una posición determinada de la pantalla.
Evidentemente esta substituira a la subrutina del oscilador pero en esta se hara una llamada a la antigua para que el reloj del sistema no deje de funcionar.
Hay diversas cosas que se deben considerar en este tipo de programa:
A)El manejador no es mas que un trozo de código que nosotros escribiremos y que no tendremos que llamar desde ninguna parte en nuestro programa puesto que se ejecutara automáticamente de alguna forma.
B)En el ordenador,como ya se conoce,se generan una serie de interrupciones hardware cuando algún suceso externo se produce. Como ya se sabe también esto genera una interrupción de la tarea q realizaba el procesador pasando a ejecutar el código asignado a esta situado en alguna parte de la memoria que desconocemos.
C)La forma en que el procesador conoce la dirección en memoria donde se encuentran cada uno de los manejadores de interrupción es por la tabla de vectores de interrupción que situada entre las direcciones 00000h y 003FFh (1 Kbyte) incluyen secuencialmente para cada uno de los 256 códigos de interrupción posibles 4 bytes para incluir el segmento y el desplazamiento, 2 bytes cada uno.
ej: interrupción 8(oscilador):
en la dirección 8*4 y 8*4+1 esta el dsp del manejador
en la dirección 8*4+2 y 8*4+3 esta el segmento
Estos vectores son modificables y son los que reescribiremos con la posición (segmento:desplazamiento) en memoria de nuestro manejador, de forma que el procesador cuando reciba una interrupcion 8 en sus patas ejecutara nuestro codigo en lugar del código por defecto.Eso si, no nos olvidaremos de llamar desde nuestro manejador al antiguo para que se sigan haciendo las funciones del reloj.
D)El programa se divide principalmente en 3 partes importantes: la función instalar en la que modificaremos los vectores de interrupción después de haber guardado primero la direccion antigua, el manejador que pondrá los '*' en pantalla según las especificaciones y la función desinstalar que restaura los valores de la dirección antigua en el vector de interupción.
E)Para acceder al vector de interrupción más facilmente se crea un segmento denominado vecint que mediante una etiqueta apunta a este.
F)Muy importante a la hora de intalar y desinstalar los vectores de interrupción desactivar las interrupciones en el momento en el que se esten cambiando los valores del vector, podría ocurrir una situación muy poco deseada si se produjera una interrupción justo en el momento en que se haya cambiado un valor y el otro no. Luego acuerdate de habilitarlas.
G)En cuanto al manejador dos aspectos muy importantes, todos los segmentos que vayamos a utilizar debemos reiniciar sus registros al principio...se ejecutará en cualquier momento, pudiendose estar ejecutandose otro programa con otros registros de segmento distintos. Al final de esta función se deberá hacer una llamada al manejador antiguo, apilando antes el registro de estado, puesto que los manejadores cuando son llamados ademas de las direcciones de retorno apilan también el registro de estado automáticamente, nosotros lo haremos manualmente. Como ultima consideración, este finalizará con IRET puesto que debe desapilar tambéin el registro de estado, no os olvideis aunque os pese es un manejador en todas sus reglas.

Para la creación del ejecutable se necesita el fichero Print.obj.

			extrn prxy:far

			;** Definición de constantes **

			subir EQU 48h
			bajar EQU 50h
			izda EQU 4bh
			dcha EQU 4dh
			teclafin EQU 4fh

			;** Segmento Vecint **
			vecint segment at 0h
			org 8*4
			vec8 label word ;vec8 apunta a la dirección del vector viejo.
			vecint ends

			;** Segmento de Datos **
			datos segment
			dirint dd ?			; Variable que almacenará la dirección antigua del manejador.
			cursorx db 0
			cursory db 0
			tecla db dcha
			datos ends

			;** Segmento de Pila **
			pila segment stack
			db 100 dup(0)
			pila ends

			;** Segmento de código **
			codigo segment
			assume ds:datos, cs:codigo, ss:pila, es:vecint

			;*************************************************
			;* Nombre: ppal					 *
			;* Descripción: llama al instalador del ma- 	 *
			;* nejador de interrupción y lee teclas 	 *
			;* almacenando su valor mientras sea 		 *
			;* diferente de fin. Antes de salir llama al 	 *
			;* desinstalador del manejador. 		 *
			;*************************************************
			ppal proc far

			push ds
			xor ax, ax
			push ax

			mov ax, vecint
			mov es, ax
			mov ax, datos
			mov ds, ax

			call instalar 	; Llamada al instalador del manejador

		bucle:	mov ah, 0
			int 16h 	; Interrupción para la lectura de tecla
					; En ah tecla leida 

					; A continuación se compara la tecla 
					; leida con subir, bajar,izda,dcha 
					;si es alguna de estas se guarda en tecla 
			cmp ah,subir
			je guardar
			cmp ah,bajar
			je guardar
			cmp ah,izda
			je guardar
			cmp ah,dcha
			je guardar

			cmp ah,teclafin ;Comparación con teclafin (Fin) si lo es
			je fin 		;salta a fin.

			jmp bucle 	;Si no es ninguna de las anteriores vuelve 
					;a bucle, para leer otra tecla. 
	     guardar:	mov tecla, ah	;Almacenamos en tecla la tecla leida.
			jmp bucle
		 fin:   call desinst 	;Llamamos al desinstalador del manejador 
			ret

			ppal endp

			;*************************************************
			;* Nombre: instalar				 *
			;* Descripción: instala el manejador de 	 *	
			;* nuestra interrupción que queremos que 	 *
			;* se ejecute en lugar de la INT 8. Para 	 *
			;* ello variamos los vectores de interupcion	 *
			;*************************************************

			instalar proc far
			push ax 
			mov ax, vec8 		;Guardamos en ax el desplazamiento del vector de interrupción 8
			mov word ptr dirint,ax 	;Almacenamos en dirint este desplazamiento
			mov ax, vec8+2 		;Guardamos en ax el segmento del vector de interrupción 8 
			mov word ptr dirint+2, ax ;Almacenamos en dirint el segmento
			cli 			;Desabilitamos las interrupciones
						
						; A continuación se instala el nuevo vector 
						; de interrupcion que apunta a la nueva rutina. 
			mov ax, offset manejador; Ax guarda el desplazamiento de la rutina 
			mov vec8, ax 		; Se actualiza el desplazamiento del vector con el de ax
			mov vec8+2, cs 		; El nuevo segmento es CS
			sti 			;Habilitamos las interrupciones
			pop ax
			ret
			instalar endp

			;************************************************
			;* Nombre: desinst 				*
			;* Descripción: restaura el antiguo vector	*
			;* de interrupción 8. 				*
			;************************************************ 

			desinst proc far
			push ax
			cli 			  ;Deshabilitamos interrupciones
			mov ax, word ptr dirint   ;Ax contiene el desplazamiento de la interrupción 8
			mov vec8, ax 		  ;Se restaura el desplazamiento con el de Ax 
			mov ax, word ptr dirint+2 ;Ax contiene el segmento de la interrupción 8
			mov vec8+2, ax 		  ;Se restaura el segmento con el de Ax
			sti 			  ;Habilitamos interrupciones
			pop ax
			ret
			desinst endp

			
			;************************************************
			;* Nombre:Manejador 				*
			;* Descripción: Rutina que según la última 	*
			;* tecla pulsada actualiza la posición en 	*
			;* pantalla del cursor. 			*
			;************************************************ 
			

			manejador proc far
			push ax ds 
			mov ax, datos 
			mov ds, ax 		;Reiniciamos el segmento de datos puesto que no 
						;sabemos cuando se invocará este procedimiento.
			xor ah, ah
			mov al, tecla 		; Al contiene última tecla pulsada

			cmp ax, subir 
			je arriba 		; Salta a arriba si es tecla subir
			cmp ax, bajar
			je abajo 		;Salta a abajp si es tecla bajar
			cmp ax, izda
			je lado_i 		;Salta a lado_i si es tecla izquierda
			cmp ax, dcha
			je lado_d 		;Salta a lado_d si es tecla derecha
			jmp final 		;Salta a final con cualquier otra tecla

		arriba: cmp cursory,0 
			je mas_y 		;Si cursory = 0 (arriba de pantalla) saltar a mas_y
			dec cursory 		;Decrementamos en 1 línea
			jmp ver
		mas_y:  mov cursory, 24 	;Movemos cursor a última línea
			jmp ver
		abajo: 	cmp cursory,24 
			je menos_y 		;Si cursory = 24 (Abajo de la pantalla) saltar a menos_y
			inc cursory 		; Incrementamos en 1 línea 
			jmp ver		
		menos_y:mov cursory,0 		;Movemos el cursor a la primera línea
			jmp ver
		lado_d: cmp cursorx,80
			je mas_x 		;Si cursorx = 80 (Fin de Línea) saltar a mas_x
			inc cursorx 		;Incrementamos una posición en la línea
			jmp ver
		mas_x:	mov cursorx,0 		;Movemos el cursor al principio de la línea
			jmp ver
		lado_i: cmp cursorx,0 
			je menos_x 		;Si cursorx = 0 (Principio de Línea) saltar a menos_x
			dec cursorx 		;Decrementamos una posición en la línea
			jmp ver
	       menos_x: mov cursorx,80 		;Movemos el cursor al final de la línea
		   ver: xor ah, ah
			mov al, cursorx
			push ax 		;Apilar posición en X del cursor
			mov al, cursory
			push ax 		;Apilar posición en Y del cursor
			mov al, '*'
			push ax 		;Apilar caracter a ecribir en la posición
			call prxy 		;Llamada a la función ya definida
			pushf 			;Se apila el registro de estado
			call dirint ;Llamada a la interrupción 8
		final:	pop ds ax
			iret 			;Retorno a ppal desapilando también el registro de estado.
			manejador endp
			codigo ends
			end ppal