Acceso a la tabla de vectores de interrupción
Otro bonito capítulo sobre mis prácticas de periféricos, de esos que nadie comenta pero luego cuando nos cruzamos por el campus me rascais la cabeza y me dais las gracias :P
Esta no pensaba hacerla todavía, pero por lo visto esta mañana a las 7.00 mis vecinos han considerado divertido poner música a todo volumen (nunca antes odié a Bach) y pasear con tacones (o equivalente). Así que como no podía dormir, y no tenía nada mejor que hacer (en realidad sí, pero no importa), le he dedicado un rato a esta práctica que es muy sencilla.
La tabla de vectores de interrupción es un cacho de memoria (concretamente, desde el 0000:0000) en el que se almacenan las direcciones de las funciones o rutinas que atenderán a cada interrupción. Es decir, que cuando se produce una interrupción, se transfiere el control del sistema a la rutina situada en la dirección indicada en la posición de memoria especificada en la posición correspondiente de este vector.
Hay 256 interrupciones (la lista está aquí).Cada una, usa 4bytes de este vector: 2bytes para el offset y otros 2bytes para el segmento en que se encuentra la función (no, no me he equivocado: guarda primero el offset y después el segmento). Por tanto, en total la tabla ocupa 1KB. Además, cada interrupción está en la posición indicada por su número: La dirección de la interrupción 0 se encuentra en los 4 primeros bytes, la de la interrupción 1 en los 4 siguientes, etc. En definitiva: el offset de la dirección de la interrupción i se encuentra en el byte tvi+4*i y su segmento en el tvi+4*i+2 (suponiendo tvi el inicio de la tabla de vectores de interrupción, o sea, 0000:0000)
Supongamos que no nos gusta la rutina normal de teclado, y queremos una propia que por ejemplo, en vez de escribir el carácter por pantalla, imprima el valor del código de teclado (es decir, el código de la tecla pulsada, que no depende del carácter sino de la posición de la tecla y de si se pulsa o se suelta. Por ejemplo, el ESC genera el scancode 1, el 1 genera el código 2, el 2 el 3, etc etc. La tabla completa está aquí -la Tabla90-). Podría ser algo tal que así:
void interrupt rutina_teclado(){
char codigo;
codigo = inport(0x60);
/*Blablablabla hacer cosas blablabla*/
outport (0x20, 0x20);}
Inciso: Sí, pongo las llaves así, mucha gente lo ve raro pero ... a mi me parece más amigable que ambas en nueva línea. :P
Encuesta: ¿Cómo las pones tú? xD
La palabra interrupt le indica al compilador que lo que está haciendo no es una función normal, sino una interrupción. O sea, que le está pidiendo al compilador que genere código para salvar y restaurar el estado de la CPU cada vez que se ejecute ese código (esa es la gracia de las interrupciones)
Con inport(0x60) lo que hace es leer el scancode de la tecla pulsada/soltada, y el outport(0x20, 0x20) simplemente es el EOI (End Of Interruption), que hace que se salga correctamente de una interrupción. ¡Debemos usarlo siempre para terminar una interrupción!
Bueno, obviamente, esto así solito, en el limbo de los códigos, no tiene mucho sentido, así que tendríamos que modificar la TVI para que cada vez que pulsemos una tecla, vaya a nuestra maravillosa rutina en vez de a la normalita. Como ya sabemos, la interrupción de teclado es la 9, así que lo que nos interesa está en esa posición (O sea, necesitamos un punterete hacia MK_FP (0 , 9*4)).
Lo primero, antes de romper nada, es guardarnos la dirección de la rutina original. (Aunque si se rompe algo, que no panda el cúnico: basta con cerrar y abrir nuestro maravilloso emulador de DOS). Para ello, guardamos en un par de variables el offset y el segmento, que como dije antes, están respectivamente en los dos primeros y dos siguientes bytes de 0 , 9*4.
Lo siguiente es modificar la posición de la tabla que nos interesa y decirle que ahora tiene que apuntar a nuestra rutina. El identificador de una función es en realidad un puntero a la misma, así que se convierte en algo tan simple como lo que sigue:
asm cli;
*(tvi+9*4)=FP_OFF(rutina_teclado);
*(tvi+9*4+2)=FP_SEG(rutina_teclado);
asm sti;
Es importante desactivar la llegada de interrupciones antes de cambiar nada. ¿Os imaginais la super catástrofe que sería que se produjera una interrupción de teclado después de haber cambiado el offset pero antes de haber modificado el segmento? ¡Eso sí que sería una fieshta! Podemos hacerlo con instrucciones en ensamblador: asm cli (desactivarlas) y asm sti (activarlas), una vez que se han modificado. Cuenta la leyenda que también se puede usar disable() y enable(), que están incluidas en dos.h . . . pero yo no las he usado así que no prometo nada.
Y una vez cambiado esto... ya podríamos casi decir que está todo hecho. Eso sí: En el tremendamente abierto "hacer cosas" de la rutina es bastante interesante configurar un caracter "de escape", para que cuando se detecte una determinada tecla pulsada, se salga de la interrupción restaurando la rutina original (y así además te ahorras tener que cerrar el DOS para poder usar el teclado normalmente xD). Pero eso lo haceis vosotros: no os lo voy a dar todo mascadito :P
Fail típico 1: poner un while(salir==false) dentro de la rutina. Esto es absurdo, porque la rutina se invocará SIEMPRE que pulse o suelte una tecla, así que el bucle no me hace falta.
Fail típico 2: llamar explícitamente a la función. ¡No hace falta! Vendrá ella solita cada vez que pulses una tecla.
Ale, espero que os haya servido de algo, y si no es así... al menos a mi me ha servido para "afianzar conocimientos". Acepto comentarios, mails y tabletas de chocolate :P
Os dejo con la típica captura de rigor: En ella pulso las teclas M O T A en ese orden. Si pinchais en la imagen y la veis en flickr, le he puesto notitas explicativas ^^
Por cierto, fe de erratas del artículo sobre Modificación de los patrones de bits de los caracteres: Comprobé una vez más el ejecutable sobre cmd, y resulta que no funcionaba por la simple razón de que no lo tenía configurado en el modo 80x25 y en pantalla completa. En cuanto lo cambié, comenzó a tirar. Cosas que pasan XD
22 comentarios
Nike Shox Shoes -
motagirl2 -
GatoVolador -
Orlando -
utilizando windows xp y copilandolo en Turbo C
miguel -
Salut!
miguel -
miguel -
miguel -
[por cierto, ya había visto los otros dos posts tuyos... :P]
char *p,*q,c;
struct REGPACK rp;
rp.r_ax=0x1130;
rp.r_bx=0x0600;
intr(0x10,&rp);
q=(char *)MK_FP(rp.r_es,rp.r_bp);
p=malloc(sizeof(char)*4096);
*p=*q;
*(p+65*16)=255;
motagirl2 -
Sobre la practica 4 escribí también un par de posts: http://motagirl.blogia.com/2009/022701-acceso-a-rom-para-obtener-los-patrones-de-bits-de-los-caracteres.php y http://motagirl.blogia.com/2009/030402-modificacion-de-los-patrones-de-bits-de-los-caracteres.php
(Ya me avisas cuando cuelgues lo tuyo jaja)
miguel -
Ah! y no te tienes que disculpar, jejeje, que encima de todo lo que haces... yo intentaré colgar tb todo lo que tenga... cuando lo tenga... jeje
See u
motagirl2 -
Bueno, yo tengo el bucle justo despues del
asm cli;
*(tvi+9*4)=FP_OFF(rutina_teclado);
*(tvi+9*4+2)=FP_SEG(rutina_teclado);
asm sti;
o sea, antes de volver a restaurarlo todo. Quizá falles en otra cosa :/
miguel -
Gracias!!
Carlos -
Con tu explicación creo que me has ahorrado 5 horas, por lo que...te debo 5 horas ;)
mgisbert -
int main(){
char *q;
struct REGPACK rp;
rp.r_ax=0x1130;
rp.r_bx=0x0600;
intr(0x10,&rp);
q=(char *)MK_FP(rp.r_es,rp.r_bp);
*(q+65*16)=255;
asm push ax;
asm push bx;
asm push cx;
asm push dx;
asm push es;
asm push bp;
asm mov ax, 1110h;
asm mov bx, 1000h;
asm mov cx, 0100h;
asm mov dx, 0;
asm les bp, q;
asm int 10h;
asm pop bp;
asm pop es;
asm pop dx;
asm pop cx;
asm pop bx;
asm pop ax;
}
motagirl2 -
mgisbert -
Esque el dosbox y yo no nos llevamos muy bien...
See u
Chuky -
Respecto a lo del bucle infinito while(true) xDD
motagirl2 -
haruma -
De todas formas esto es tema para un debate largo e irresoluble, del estilo de cómo hacer un bucle infinito. Todavía recuerdo un libro que decía que el método estándar para hacer un bucle infinito en C era for(;;), aunque "había una fiel minoría de programadores" que usaban while(1) (soy de esa minoría).
Contra-encuesta: ¿cómo hacéis un bucle infinito?
Scipion -
krone, siempre hay un raro/siervo de satan/amigo de microsoft que las pone mal pero que le vamos a hacer, el mundo es injusto.
P.D.: hagamos un grupo en facebook de llaves bien puestas y otro de fans de los lenguajes que llevan llaves (no me mola nada el if_then_fi etc.) XD
motagirl2 -
Hacemos un grupo de facebook? XD
krone -
Los que lo ponen al revés son unos raros :P