Académique Documents
Professionnel Documents
Culture Documents
1 - INTRODUCCIN
10 - PROGRAMAS RESIDENTES
11 - CONTROLADORES DE DISPOSITIVO
11.1 - Introduccin
11.2 - Encabezamiento y palabra de atributos
11.3 - Rutinas de estrategia e interrupcin
11.4 - Ordenes a soportar por el controlador de dispositivo
11.5 - La cadena de controladores de dispositivo instalados
11.6 - Ejemplo de controlador de dispositivo de caracteres
11.7 - Ejemplo de controlador de dispositivo de bloques
11.7.1 - Disco virtual TURBODSK: Caractersticas
11.7.2 - Ensamblando TURBODSK
11.7.3 - Anlisis detallado del listado de TURBODSK
11.8 - Los controladores de dispositivo y el DOS
13 - EL ENSAMBLADOR Y EL LENGUAJE C
APNDICES
I Mapa de memoria
II Tabla de interrupciones del sistema
III Tabla de variables de la BIOS
IV Puertos de E/S
V Cdigos de rastreo del teclado
VI Tamaos y tiempos de ejecucin de las instrucciones
VII Seales del slot de expansin ISA
VIII Funciones del sistema, la BIOS y el DOS aludidas en este libro
IX Especificaciones XMS y EMS: Todas sus funciones
X Juego de caracteres ASCII extendido
XI Bibliografa
PRLOGO
DE LA EDICIN 4.0 ELECTRNICA*
(*) http://www.gui.uva.es/udigital
Nota: Pudiendo haber discrepancias entre sucesivas ediciones de estas normas, la versi n de
referencia vlida e inapelable ser la ubicada en todo momento en la red, en la direcci n electr nica
arriba indicada o cualquier otra que pudiera sucederla.
La edicin 4.0 (4 edicin) de El Universo Digital del IBM PC, AT y PS/2 es un libro
electrnico/impreso de dominio pblico; de libre uso, difusin, copia y distribucin entre
particulares, en cualquier soporte. Quienes decidan utilizarlo debern registrarse por v a
electrnica una sola vez, por razones de tica (http://www.gui.uva.es/udigital). Tambin es
posible hacerlo enviando una carta o postal ordinaria (mejor en un sobre) al autor, con cualquier
texto, a la siguiente direccin:
Ciriaco Garca de Celis
Apartado 6105
47080 Valladolid
Espaa
Indicando claramente que el motivo es registrar el Universo Digital. Los que hayan comprado la
versin impresa en persona no necesitan registrarse, aunque lo recibira con agrado, incluso si ha
pasado bastante tiempo (pero si lo compraron por correo no deben registrarse: conservo su pedido).
Me gustara conocer en alguna medida la difusin de la obra, en especial a partir de este
momento, lo que hasta ahora me resultaba algo ms sencillo. Por supuesto, los datos o direcciones
indicadas por los usuarios nunca sern divulgados por m.
Se aplican exactamente las mismas condiciones que para usuarios particulares, con la
excepcin de que se recomienda un nico registro electrnico o una sola carta o postal en
representacin de todos los posibles usuarios de la entidad.
Licencia de distribucin para empresas, asociaciones y organizaciones.
Editando revistas (no libros) la distribucin est permitida en cualquier formato digital
(HTML, PostScript, WordPerfect, texto, o cualesquiera otros) tanto en fragmentos como toda la
obra completa. Siendo el formato una revista impresa slo se permiten fragmentos que no totalicen
ms del 75% de la obra en los sucesivos nmeros publicados. Es necesario citar la procedencia.
La distribucin por empresas que cobren una cierta cantidad por el soporte es libre. Mi nica
sugerencia es que la empresa me enve una copia del soporte (CD, etc.) en que se publique, por
cortesa.
Tratndose de empresas editoriales u otras cualesquiera que planeen incluirlo, entero o por
fragmentos, en el soporte impreso, electrnico u online de algn libro que vayan a publicar,
deberan contactar primero conmigo para negociar una nueva versin (que en todo caso no
implicara la desaparicin de sta en su estatus actual).
Modificaciones.
El Universo Digital no naci tras una decisin premeditada. Su objetivo inicial fue dotar de un
manual de apoyo al Curso de Lenguaje Ensamblador, que ofrece todos los aos la asociacin
Grupo Universitario de Informtica de la Universidad de Valladolid, en el marco de unos Cursos
de Introduccin a la Informtica -para los alumnos y personal en general de la Universidad- que
abarcan un espectro mucho ms amplio que el de la programacin de los ordenadores.
El DOS en la actualidad.
El futuro de la programacin, sin embargo, no es slo para los programadores de alto nivel.
En alguna manera, los propios usuarios pueden y podrn cada vez en mayor medida hacer sus
propios programas incluso sin darse cuenta. Sin embargo, siempre hay alguien que tiene que
construir los sistemas operativos, y sobre todo, los controladores para dar soporte a los dispositivos
en los diversos sistemas operativos. Por no mencionar las aplicaciones especializadas, desde
mquinas industriales al microprocesador de las sondas espaciales (que, evidentemente, no corre
bajo Windows). Es para los programadores de sistemas, y para aquellos que necesitan o quieren
saber cmo funciona el PC por dentro, como ejemplo prctico de arquitectura interna de un
ordenador, para los que va destinado este libro. Que podrn practicar en un entorno c modo para
este tipo de programacin, como es el DOS (que deja todo el control de la m quina a cada tarea).
Aunque algunos contenidos muy relacionados con el DOS siguen presentes en esta obra, el lector
habr de tener en cuenta si es pertinente profundizar en ellos o no, en la poca que vivimos.
Mi objetivo inicial no fue publicarlo, aunque hace dos o tres aos s me lo plante un poco
en serio.
Las ventajas de una edicin oficial sera su no engorrosa distribucin (uno de los motivos
por los que siempre ha costado poco es porque nuestra Asociacin y el propio autor ha puesto su
mano de obra gratis), as como su mayor difusin. Puesto en contacto con cuatro prestigiosas
editoriales; las que han respondido han valorado muy positivamente la obra, sin embargo la han
rechazado aduciendo otros motivos (sobrecarga del programa editorial, solapamiento en
contenidos con obras publicadas o en fase de publicacin, o simplemente falta de inters
comercial). Una de ellas an no ha respondido.
Sin embargo, la ventaja de la publicacin para facilitar la difusin popular es obvia, mxime
si lo hace una editorial importante (si no, no aparecera en todas las estanteras, la publicidad la
haran los lectores lentamente, como ya se vena haciendo, y la distribucin sera incluso
ms limitada pese al recurso a los baratos servicios de reprografa por parte de los usuarios).
Mi decisin final ya la haba acariciado con anterioridad. Algo haba que hacer, pues la
distribucin gratuita del libro llevaba mucho tiempo.
Uno de los motivos que han terminado empujndome a esta decisin, ha sido la considerable
cantidad de pedidos que hemos recibido desde pases de hispanoamrica. Se trata de ciudadanos
que conocen el ndice del libro a travs del Web y lo piden, sobre todo desde M xico. Sin
embargo, slo en la primera ocasin lo he enviado (a Per); los motivos son, desgraciadamente,
la prctica imposibilidad de comerciar a pequea escala con esos pases (no existe el envo
contrarreembolso, por ejemplo); las enormes demoras del envo por superficie (el coste del
envo areo supera el del propio libro) y las complicadas gestiones de pago e injustas comisiones
bancarias (aunque las pague el usuario final); finalmente habra que aadir incluso mi temor
inconsciente a un aumento incontrolado de la demanda, cuando ya haba demasiado trabajo que
hacer para atender la de origen nacional (en mi memoria estaba lo que ocurri cuando empezaron
a aparecer mensajes y comenzaron a recibirse pedidos por FidoNET). Pido desde aqu disculpas a
todos los que lo han solicitado desde fuera de Espaa, mayores adems si no he contestado el E-
Mail por no haber tomado an una decisin al respecto.
El Universo Digital de dominio pblico en formato electrnico, podr ser accedido desde
cualquier lugar del mundo, y en cualquier CD de los kioscos.
El inconveniente es que no todos tienen igual acceso a estas redes y medios, aunque ese
inconveniente disminuir exponencialmente con el tiempo (con el mismo exponente con que
crezca la red).
Naturalmente, una vez que he renunciado a mis derechos sobre el libro, donndolo al dominio
pblico, ya no estoy obligado a venderlo impreso (medida tomada nicamente para mantener el
copyright). Realmente, no tenemos tiempo ni medios para atender la demanda actual: aunque es una
medida dura de imponer, lamento renunciar a realizar ms envos de ejemplares impresos.
Renuncio con ello a facilitar su difusin a los lectores menos introducidos en las redes
telemticas, pero beneficio a otros muchos, que adems podrn seguir usando la versin
manuscrita utilizando una impresora.
Por otro lado, haber facturado slo aproximadamente el coste de impresin y distribucin,
me permiten tomar esa decisin sin temer el enfado de quienes lo haban comprado. El coste de
impresin de los ltimos nmeros en la reprografa oficial de la Universidad (rechazamos
opciones ms baratas de menor calidad), encuadernacin y disquete era de 1900 pts. El libro
(realmente, apuntes tcnicos fotocopiados) se venda a 2100 pts ms gastos de envo. Ese
margen de beneficios era ms bien de maniobra, ya que por ejemplo, en los ejemplares que no
llegaban a su destino, el coste del envo y la devoluci n lo pagbamos nosotros. Cada env o
llevaba una media de 20 minutos de tiempo total de mano de obra, contabilizando la preparaci n
de los libros (transporte fsico, disquete, gestin del pedido...), y la mayora eran de una sola
unidad (pese a que se penalizaba su envo con 100 pts adicionales). El precio de los ms de 1200
Universos Digitales vendidos ha tenido un crecimiento nominal cero en los cinco aos de
difusin impresa.
Los pedidos de ejemplares impresos sern admitidos slo desde Espaa. Habrn de
realizarse exclusivamente por carta impresa, que deber estar compulsada por el sello y en su caso
papel oficial de la biblioteca que hace el pedido, adems de debidamente firmada por quien
corresponda. Es conveniente que figure el telfono de la biblioteca o en su defecto de la
conserjera del centro. Adems del nombre completo, direccin y NIF. Nos reservamos el
derecho de rechazar aquellos pedidos que no cumplan alguno de estos requisitos, o los de
sospechosa procedencia. La direccin es: Grupo Universitario de Informtica. Apartado
6062. 47080 Valladolid. El precio por ejemplar ser el que figure en la factura que realizar el
propio servicio de reprografa (unas 2000 pts/unidad); sumando al final el coste exacto del envo
y los disquetes.
Agradecimientos.
No puedo decir lo mismo de los funcionarios de Correos: aunque algunos son amables, en
general, el funcionamiento de esa institucin es el que caba esperar de un monopolio no
sometido a la libre competencia en envos postales ordinarios (y que, por tanto, no tiene la
obligacin de tratar bien a sus clientes, porque tambin volvern maana). El trato que reciben
los clientes no se diferencia mucho del de los paquetes, y estos son muy expresivos en ocasiones al
llegar al destino. Por otro lado, la cantidad de papeles que hay que rellenar en cada env o, y
algunas normas de la empresa (como el plomo adherido a los paquetes postales) no se han
simplificado desde finales del siglo XIX. Tampoco es comprensible que s lo Argentaria sea a n
la nica entidad financiera con el privilegio de gestionar las denominadas Cuentas Corrientes
Postales. Adems de que el servicio de correos es caro en la realidad (esto es, cuando se incluye lo
que pagamos en impuestos para cubrir las prdidas de la compaa) se mantiene el viejo vicio
de indexar las tarifas anuales (aumento del 8% en 1997, cuando hay un 2% de inflacin nacional).
Sin embargo, he de reconocer que la fiabilidad de Correos (entendida en cuanto a paquetes que
llegan a su destino o en su defecto vuelven por motivo de direccin incorrecta) es pr xima al
100%: los envos no suelen perderse, al menos los de los reembolsos. En puntualidad, aunque hay
extremos de gran aleatoriedad (desde paquetes que llegan en tres das a un pueblo perdido en la
otra punta del pas, a los que tardan quince en ir de Valladolid a Madrid) el tiempo promedio
podra aproximarse, aunque por debajo, a lo que afirma la empresa.
Ciriaco Garca de Celis
Valladolid, Noviembre de 1997
Volver al ndice
PRLOGO
DE LA TERCERA EDICIN (1994)
Ha pasado un ao desde la publicacin de la primera edicin de esta obra. Desde entonces,
ha continuado la expansin de los interfaces grficos de usuario y los sistemas operativos
avanzados para PC. Sin embargo, pese a que la programacin contina alejndose cada vez
ms del bajo nivel de las mquinas, los programadores de sistemas en el entorno del PC siguen
existiendo y son muchos ms que los que trabajan para las empresas punteras en el desarrollo de
los sistemas operativos. Los ordenadores compatibles poseen numerosas aplicaciones en el campo
industrial, para las que es conveniente un conocimiento elevado del funcionamiento interno del
ordenador en general y del MS-DOS en particular. Para aquellas personas que necesitan comprender
el funcionamiento de un ordenador, las mquinas compatibles constituyen una interesante
oportunidad y punto de partida. Este libro pretende cubrir una importante laguna en la bibliografa
disponible actualmente sobre la programacin a nivel de sistemas de los ordenadores compatibles.
Las memorias extendida XMS y expandida EMS son descritas con cierto detenimiento, dada su
presencia en todos los ordenadores modernos y su importancia.
Existen apndices que describen todas las funciones del DOS, de la BIOS y del sistema usadas
en las rutinas y programas desarrollados, as como la totalidad de las funciones XMS y EMS. Sin
embargo, no estn ni muchsimo menos todas las interrupciones necesarias, por lo que se insta al
lector a conseguir el impresionante fichero de dominio pblico INTERRUPT.LST, complemento
ideal de este libro (ver bibliografa).
Los programas residentes reciben un tratamiento especialmente profundo: desde los mtodos
ms eficientes para que detecten su propia presencia en memoria, a las tcnicas ms avanzadas
para economizar memoria, pasando por el uso de funciones del DOS de manera concurrente al
programa principal, as como tcnicas de empleo de memoria extendida y superior para
conseguir programas que usen 0 Kb dentro de los primeros 640 Kb de la m quina y todo ello sin
olvidar la convivencia con los actuales entornos operativos, como Windows, y la posibilidad de ser
activados desde pantallas grficas.
Este libro tambin trata los controladores de dispositivo o device drivers, desde los dos posibles
enfoques de su uso: bien sea la creacin de controladores de dispositivo de caracteres, bien la de
nuevas unidades de disco aadidas a las del sistema; en ambos casos se incluyen ejemplos reales
de controladores completos y comprobados, en particular el ejemplo de disco virtual: un completo
ejemplo de controlador redimensionable que soporta memoria convencional, XMS y EMS.
Existe un captulo muy prximo al hardware en el que se describen a fondo y sin omisiones
todos los chips del ordenador, para permitir al programador de sistemas un control completo del
equipo. Para asimilar este captulo hace falta cierta formacin previa en los sistemas digitales;
sin embargo, los ejemplos que siguen a la informacin tcnica aclaran las explicaciones previas y
pueden ser aprovechados de manera inmediata incluso sin entender todo lo anterior. Los chips de
apoyo al microprocesador son descritos de manera total: primero, no relacionados con el PC sino
como tales circuitos; despus integrndolos en el ordenador y documentando profusamente su
uso, con ejemplos probados. Se consideran el interfaz de perifricos 8255 (til para averiguar la
configuracin de los PC/XT), el temporizador 8253/8254 (para temporizacin y sntesis de
sonido), el controlador de interrupciones 8259, el controlador de DMA 8237 (para acceso a disco),
el controlador de disquetes 765 (acceso directo a los sectores), la controladora de disco duro de los
AT (IDE, MFM Bus Local); el controlador del teclado del AT (8042); el UART 8250 (empleado
en las comunicaciones serie) y el reloj de tiempo real MC146818 (configuracin de AT y
programacin de alarmas y temporizaciones). Los ejemplos en este captulo experimentan una
importante potenciacin respecto a la edicin anterior; en particular, en lo relacionado con el
controlador de disquetes se puede considerar que la informacin vertida es prcticamente casi
toda la existente, existiendo pautas suficientes para que el lector cree sus propios programas
copiones, protecciones de disco, formatos de alta capacidad, etc.
Resumiendo, el libro pretende reunir en una sola obra la mayora de la informaci n necesaria
para el programador de sistemas, exponiendo toda la informacin y no slo lo imprescindible, sin
olvidos ni omisiones; tambin se pretende explicar las tcnicas ms avanzadas de creacin de
programas residentes. Este afn de informacin completa es el responsable del ttulo del libro.
Todos los listados de ejemplo se suponen de dominio pblico y las rutinas pueden ser incluidas
por los lectores libremente en sus propios programas, aunque en el caso de los programas completos
debe citarse la procedencia y dejar bien claro en las versiones modificadas quin las ha alterado.
En todo caso, pese a que todas las rutinas y programas han sido probados debidamente en un 8088,
un 286, un 386 o un 486 -bajo varios sistemas operativos y con diferentes configuraciones del
hardware- el autor del libro no se responsabiliza de su correcto funcionamiento en todas las
circunstancias.
PRLOGO
DE LA TERCERA EDICIN (1994)
Las memorias extendida XMS y expandida EMS son descritas con cierto detenimiento, dada su
presencia en todos los ordenadores modernos y su importancia.
Existen apndices que describen todas las funciones del DOS, de la BIOS y del sistema usadas
en las rutinas y programas desarrollados, as como la totalidad de las funciones XMS y EMS. Sin
embargo, no estn ni muchsimo menos todas las interrupciones necesarias, por lo que se insta al
lector a conseguir el impresionante fichero de dominio pblico INTERRUPT.LST, complemento
ideal de este libro (ver bibliografa).
Los programas residentes reciben un tratamiento especialmente profundo: desde los mtodos
ms eficientes para que detecten su propia presencia en memoria, a las tcnicas ms avanzadas
para economizar memoria, pasando por el uso de funciones del DOS de manera concurrente al
programa principal, as como tcnicas de empleo de memoria extendida y superior para
conseguir programas que usen 0 Kb dentro de los primeros 640 Kb de la m quina y todo ello sin
olvidar la convivencia con los actuales entornos operativos, como Windows, y la posibilidad de ser
activados desde pantallas grficas.
Este libro tambin trata los controladores de dispositivo o device drivers, desde los dos posibles
enfoques de su uso: bien sea la creacin de controladores de dispositivo de caracteres, bien la de
nuevas unidades de disco aadidas a las del sistema; en ambos casos se incluyen ejemplos reales
de controladores completos y comprobados, en particular el ejemplo de disco virtual: un completo
ejemplo de controlador redimensionable que soporta memoria convencional, XMS y EMS.
Existe un captulo muy prximo al hardware en el que se describen a fondo y sin omisiones
todos los chips del ordenador, para permitir al programador de sistemas un control completo del
equipo. Para asimilar este captulo hace falta cierta formacin previa en los sistemas digitales;
sin embargo, los ejemplos que siguen a la informacin tcnica aclaran las explicaciones previas y
pueden ser aprovechados de manera inmediata incluso sin entender todo lo anterior. Los chips de
apoyo al microprocesador son descritos de manera total: primero, no relacionados con el PC sino
como tales circuitos; despus integrndolos en el ordenador y documentando profusamente su
uso, con ejemplos probados. Se consideran el interfaz de perifricos 8255 (til para averiguar la
configuracin de los PC/XT), el temporizador 8253/8254 (para temporizacin y sntesis de
sonido), el controlador de interrupciones 8259, el controlador de DMA 8237 (para acceso a disco),
el controlador de disquetes 765 (acceso directo a los sectores), la controladora de disco duro de los
AT (IDE, MFM Bus Local); el controlador del teclado del AT (8042); el UART 8250 (empleado
en las comunicaciones serie) y el reloj de tiempo real MC146818 (configuracin de AT y
programacin de alarmas y temporizaciones). Los ejemplos en este captulo experimentan una
importante potenciacin respecto a la edicin anterior; en particular, en lo relacionado con el
controlador de disquetes se puede considerar que la informacin vertida es prcticamente casi
toda la existente, existiendo pautas suficientes para que el lector cree sus propios programas
copiones, protecciones de disco, formatos de alta capacidad, etc.
Resumiendo, el libro pretende reunir en una sola obra la mayora de la informaci n necesaria
para el programador de sistemas, exponiendo toda la informacin y no slo lo imprescindible, sin
olvidos ni omisiones; tambin se pretende explicar las tcnicas ms avanzadas de creacin de
programas residentes. Este afn de informacin completa es el responsable del ttulo del libro.
Todos los listados de ejemplo se suponen de dominio pblico y las rutinas pueden ser incluidas
por los lectores libremente en sus propios programas, aunque en el caso de los programas completos
debe citarse la procedencia y dejar bien claro en las versiones modificadas quin las ha alterado.
En todo caso, pese a que todas las rutinas y programas han sido probados debidamente en un 8088,
un 286, un 386 o un 486 -bajo varios sistemas operativos y con diferentes configuraciones del
hardware- el autor del libro no se responsabiliza de su correcto funcionamiento en todas las
circunstancias.
Captulo I: INTRODUCCIN
Cada posicin tiene un valor o peso de 10n donde n representa el lugar contado por la derecha:
1357 = 1 x 103 + 3 x 102 + 5 x 101 + 7 x 100
Explcitamente, se indica la base de numeracin como 135710.
Llegar a un nmero en estos sistemas desde base 2 es realmente sencillo si agrupamos las cifras
binarias de 3 en 3 (octal) o de 4 en 4 (hexadecimal):
Base 2 a base 8: 101 0112 = 538
Base 2 a base 16: 0010 10112 = 2B16
A la inversa, basta convertir cada dgito octal o hexadecimal en binario:
Base 8 a base 2: 248 = 010 1002
Base 16 a base 2: 2416 = 0010 01002
De ahora en adelante, se utilizarn una serie de sufijos para determinar el sistema de
numeracin empleado:
Sufijo Base Ejemplos
b 2 01101010b
o,q 8 175o
d 10 789d
h 16 6A5h
En caso de que no aparezca el sufijo, el nmero se considera decimal; es decir, en base 10.
donde 4 es el ltimo cociente (menor que la base) y los restantes dgitos son los restos en orden
inverso.
1.3.1. - BIT.
Toda la memoria del ordenador se compone de dispositivos electrnicos que pueden adoptar
nicamente dos estados, que representamos matemticamente por 0 y 1. Cualquiera de estas
unidades de informacin se denomina BIT, contraccin de binary digit en ingls.
1.3.2. - BYTE.
1.3.3. - NIBBLE.
Cada grupo de cuatro bits de un byte constituye un nibble, de forma que los dos nibbles de un
byte se llaman nibble superior (el compuesto por los bits 4 a 7) e inferior (el compuesto por los bits
0 a 3). El nibble tiene gran utilidad debido a que cada uno almacena un d gito hexadecimal:
Para sumar nmeros, tanto en base 2 como hexadecimal, se sigue el mismo proceso que en base
10:
Podemos observar que la suma se desa-
1010 1010b rrolla de la forma tradicional; es decir:
+ 0011 1100b sumamos normalmente, salvo en el caso de
-------------- 1 + 1 = 102 , en cuyo caso tenemos un aca-
1110 0110b rreo de 1 (lo que nos llevamos).
Por esta razn, el nmero 80h, cuyo complemento a dos es l mismo, se considera
negativo (-128) y el nmero 00h, positivo. En general, para hallar el complemento a dos de
un nmero cualquiera basta con calcular primero su complemento a uno, que consiste en
cambiar los unos por ceros y los ceros por unos en su notaci n binaria; a continuaci n se le
suma una unidad para calcular el complemento a dos. Con una calculadora, la operaci n es
ms sencilla: el complemento a dos de un nmero A de n bits es 2 n-A.
Otro factor a considerar es cuando se pasa de operar con un nmero de cierto tama o
(ej., 8 bits) a otro mayor (pongamos de 16 bits). Si el nmero es positivo, la parte que se
aade por la izquierda son bits a 0. Sin embargo, si era negativo (bit ms significativo
activo) la parte que se aade por la izquierda son bits a 1. Este fen meno, en cuya
demostracin matemtica no entraremos, se puede resumir en que el bit ms significativo
se copia en todos los aadidos: es lo que se denomina la extensi n del signo: los dos
siguientes nmeros son realmente el mismo nmero (el -310): 11012 (4 bits) y 111111012 (8
bits).
Consiste en emplear cuatro bits para codificar los dgitos del 0 al 9 (desperdiciando las seis
combinaciones que van de la 1010 a la 1111). La ventaja es la simplicidad de conversi n a/de base
10, que resulta inmediata. Los nmeros BCD pueden almacenarse desempaquetados, en cuyo caso
cada byte contiene un dgito BCD (Binary-Coded Decimal); o empaquetados, almacenando dos
dgitos por byte (para construir los nmeros que van del 00 al 99). La notacin BCD ocupa
cuatro bits -un nibble- por cifra, de forma que en el formato desempaquetado el nibble superior
siempre es 0.
Son grupos de bytes en los que una parte se emplea para guardar las cifras del n mero
(mantisa) y otra para indicar la posicin del punto flotante (exponente), de modo equivalente a la
notacin cientfica. Esto permite trabajar con nmeros de muy elevado tamao -seg n el
exponente- y con una mayor o menor precisin en funcin de los bits empleados para codificar la
mantisa.
Con posterioridad, con la aparicin de los microordenadores y la gran expansin entre ellos de
los IBM-PC y compatibles, la ampliacin del cdigo ASCII realizada por esta marca a 8 bits, con
capacidad para 128 smbolos adicionales, experimenta un considerable auge, siendo en la
actualidad muy utilizada y recibiendo la denominacin oficial de pgina de cdigos 437
(EEUU). Se puede consultar al final de este libro. Es habitualmente la nica pgina soportada por
las BIOS de los PC. Para ciertas nacionalidades se han diseado otras p ginas espec ficas que
requieren de un software externo. En las lenguas del estado espaol y en las de la mayor a de los
dems pases de la UE, esta tabla cubre todas las necesidades del idioma.
x y x AND y x OR y x XOR y
00 0 0 0
0x 1NOT 0(x) 1 1
10 0 1 0 1 1
1 1 01 1 0
Captulo II: ARQUITECTURA E HISTORIA DE LOS
MICROORDENADORES
Es sobradamente conocido que los actuales sistemas operativos son programados en su mayor
parte en lenguajes de alto nivel, especialmente C, pero siempre hay una parte en la que el
ensamblador se hace casi insustituible bajo DOS y es la programacin de los drivers para los
controladores de dispositivos, relacionados con las tareas de ms bajo nivel de una mquina,
fundamentalmente las operaciones de entrada/salida en las que es preciso actuar directamente sobre
los dems chips que acompaan al microprocesador. Por ello y porque las instrucciones del
lenguaje ensamblador estn ntimamente ligadas a la mquina, vamos a realizar primero un
somero repaso a la arquitectura interna de un microordenador.
Centrndonos en los ordenadores sobre los que vamos a trabajar desarrollar a grandes rasgos
la arquitectura Von Newman que, si bien no es la primera en aparecer, s que lo hizo
prcticamente desde el comienzo de los ordenadores y se sigue desarrollando actualmente. Claro
es que est siendo desplazada por otra que permiten una mayor velocidad de proceso, la RISC.
En los primeros tiempos de los ordenadores, con sistemas de numeracin decimal, una
electrnica sumamente complicada muy susceptible a fallos y un sistema de programaci n
cableado o mediante fichas, Von Newman propuso dos conceptos bsicos que revolucionar an la
incipiente informtica:
Tomando como modelo las mquinas que aparecieron incorporando las anteriores
caractersticas, el ordenador se puede considerar compuesto por las siguientes partes:
- La Unidad Central de Proceso, U.C.P., ms conocida por sus siglas en ingl s (CPU).
- La Memoria Interna, MI.
- Unidad de Entrada y Salida, E/S.
- Memoria masiva Externa, ME.
Realicemos a continuacin una descripcin de lo que se entiende por cada una de estas partes
y cmo estn relacionadas entre si:
- La Unidad Central de Proceso (CPU) viene a ser el cerebro del ordenador y tiene por misi n
efectuar las operaciones aritmtico-lgicas y controlar las transferencias de informacin a
realizar.
Como es de suponer, estas tres partes principales de que consta el ordenador deben estar
ntimamente conectadas; aparece en este momento el concepto de bus: el bus es un conjunto de
lneas que enlazan los distintos componentes del ordenador, por ellas se realiza la transferencia de
datos entre todos sus elementos.
- De control: forman parte de l las lneas que seleccionan desde dnde y hacia dnde va
dirigida la informacin, tambin las que marcan la secuencia de los pasos a seguir para dicha
transferencia.
- De datos: por l, de forma bidireccional, fluyen los datos entre las distintas partes del
ordenador.
- De direcciones: como vimos, la memoria est dividida en pequeas unidades de
almacenamiento que contienen las instrucciones del programa y los datos. El bus de direcciones
consta de un conjunto de lneas que permite seleccionar de qu posicin de la memoria se
quiere leer su contenido. Tambin direcciona los puertos de E/S.
2.2. - EL MICROPROCESADOR.
La premonicin.
El microprocesador.
El desarrollo del primer microprocesador por Intel en 1971, el 4004 (de 4 bits), supuso el primer
paso hacia el logro de un PC personal, al reducir drsticamente la circuiter a adicional necesaria.
Sucesores de este procesador fueron el 8008 y el 8080, de 8 bits. Ed Roberts construy en 1975 el
Altair 8800 basndose en el 8080; aunque esta mquina no tena teclado ni pantalla (slo
interruptores y luces), era una arquitectura abierta (conocida por todo el mundo) y cuyas tarjetas se
conectaban a la placa principal a travs de 100 terminales, que ms tarde terminaran
convirtindose en el bus estndar S-100 de la industria.
El IBM PC.
Y es que IBM tambin fabric su propio ordenador personal con vocacin profesional: el 12
de agosto de 1981 present el IBM PC. Estaba basado en el microprocesador 8088, de 16 bits,
cuyas instrucciones sern las que usemos en este libro, ya que todos los procesadores posteriores
son bsicamente (en MS-DOS) versiones mucho ms rpidas del mismo. El equipamiento de
serie consista en 16 Kbytes de memoria ampliables a 64 en la placa base (y a 256 a adiendo
tarjetas); el almacenamiento externo se haca en cintas de casete, aunque pronto aparecieron las
unidades de disco de 5 pulgadas y simple cara (160/180 Kb por disco) o doble cara (320/360 Kb).
En 1983 apareci el IBM PC-XT, que traa como novedad un disco duro de 10 Mbytes. Un
ao ms tarde aparecera el IBM PC-AT, introduciendo el microprocesador 286, as como
ranuras de expansin de 16 bits (el bus ISA de 16 bits) en contraposicin con las de 8 bits del PC
y el XT (bus ISA de 8 bits), adems incorporaba un disco duro de 20 Mbytes y disquetes de 5
pero con 1.2 Mbytes.
En general, todos los equipos con procesador 286 o superior pueden catalogarse dentro de la
categora AT; el trmino XT hace referencia al 8088/8086 y similares. Finalmente, por PC (a
secas) se entiende cualquiera de ambos; aunque si se hace distincin entre un PC y un AT en la
misma frase, por PC se sobreentiende un XT, menos potente. El trmino PC ya digo, no obstante,
es hoy en da mucho ms general, referenciando habitualmente a cualquier ordenador personal.
A partir de 1986, IBM fue paulatinamente dejando de tener la batuta del mercado del PC. La
razn es que la propia IBM tena que respetar la compatibilidad con lo anterior, y en ese terreno
no tena ms facilidades para innovar que la competencia. El primer problema vino con la
aparicin de los procesadores 386: los dems fabricantes se adelantaron a IBM y lanzaron
mquinas con ranuras de expansin an de 16 bits, que no permitan obtener todo el
rendimiento. IBM desarroll demasiado tarde, en 1987, la arquitectura Microchannel, con bus de
32 bits pero cerrada e incompatible con tarjetas anteriores (aunque se desarrollaron nuevas tarjetas,
eran caras) y la incluy en su gama de ordenadores PS/2 (alguno de cuyos modelos era an
realmente ISA). La insolente respuesta de la competencia fue la arquitectura EISA, tambin de 32
bits pero compatible con la ISA anterior.
Otro ejemplo: si IBM gobern los estndares grficos hasta la VGA, a partir de ah
sucedi un fenmeno similar y los dems fabricantes se adelantaron a finales de los 80 con
mejores tarjetas y ms baratas; sin embargo, se perdi la ventaja de la normalizacin (no hay
dos tarjetas superiores a la VGA que funcionen igual).
EISA tambin era caro, as que los fabricantes orientales, cruzada ya la barrera de los a os
90, desarrollaron con la norma VESA las placas con bus local (VESA Local Bus); bsicamente es
una prolongacin de las patillas de la CPU a las ranuras de expansin, lo que permite tarjetas
rpidas de 32 bits pero muy conflictivas entre s. Esta arquitectura de bus se populariz mucho
con los procesadores 486. Sin embargo, al final el estndar que se ha impuesto ha sido el
propuesto por el propio fabricante de las CPU: Intel, con su bus PCI, que con el Pentium se ha
convertido finalmente en el nico estndar de bus de 32 bits. Estas mquinas a n admiten no
obstante las viejas tarjetas ISA, suficientes para algunas aplicaciones de baja velocidad (modems,...
etc).
En 1979, Seatle Computer necesitaba apoyar de alguna manera a sus incipientes placas basadas
en el 8086. Como Digital Research estaba tardando demasiado en convertir el CP/M-80 a CP/M-86,
desarroll su propio sistema: el QDOS 0.1, que fue presentado en 1980. Antes de finales de a o
apareci QDOS 0.3.
Bill Gates, dueo de Microsoft, de momento slo posea una versin de lenguaje BASIC
para 8086 no orientada a ningn sistema operativo particular, que le gust a alg n directivo de
IBM. Bill Gates ya haba hecho la primera demostracin mundial de BASIC corriendo en un
8086 en las placas de Seatle Computer (en julio de 1979) y haba firmado un contrato de
distribucin no exclusiva para el QDOS 0.3 a finales de 1980. En abril de 1981 aparecieron las
primeras versiones de CP/M-86 de Digital, a la vez que QDOS se renombraba a 86-DOS 1.0 aunque
en principio pareca tener menos futuro que el CP/M. En Julio, sin embargo, Microsoft adquir a
todos los derechos del 86-DOS.
Digital Research no ocupa actualmente el lugar de Microsoft porque en 1981 era una
compaa demasiado importante como para cerrar un acuerdo con IBM sin imponer sus
condiciones para cederle los derechos del sistema operativo CP/M. As que IBM opt por Bill
Gates, que acababa de adquirir un sistema operativo, el 86-DOS, que pas a denominarse PC-DOS
1.0. Las versiones de PC-DOS no dependientes de la ROM BIOS de IBM se denominar an MS-
DOS, trmino que ha terminado siendo ms popular.
El futuro.
En ese caso, sera de agradecer que algn juez les obligara a publicar una especificacin
completa de las funciones y protocolos del sistema, con objeto de que alg n organismo de
normalizacin internacional las recogiera sin ambigedades para permitir la libre competencia de
otros fabricantes. El DOS y el Windows actuales no son ningn invento maravilloso de Microsoft.
Por poner un ejemplo, el MS-DOS 1.0 careca de funcin para identificar la versin del
sistema. Exactamente lo mismo le ha sucedido a las primeras versiones de Windows (hay varios
chequeos distintos para detectarlas, segn el modo de funcionamiento y la versin): el MS-DOS
no lo escribi inicialmente Microsoft, pero Windows s, y salta a la vista que sus programadores,
para cometer semejante despiste, se sentaron delante del teclado antes de hacer un anlisis de la
aplicacin a desarrollar, igual que lo hubiera hecho alguien que hubiera aprendido a programar con
unos fascculos comprados en el kiosco. Con tanto analista en el paro...
No olvidemos que el DOS y Windows son el fruto de toda la sociedad utilizando el mismo tipo
de ordenadores y necesitando la compatibilidad con lo anterior a cualquier precio. La prueba
evidente son los procesadores de Intel, construidos desde hace tiempo para dar servicio al sistema
operativo del PC. Somos prisioneros, usuarios obligados de Microsoft. Naturalmente, no tengo nada
contra Microsoft, pero opino que el poder adquirido durante una dcada, gracias a la exclusiva de
los derechos sobre un sistema operativo sin ayuda en la lnea de comandos, o de un Windows
cerrado ntimamente ligado al DOS (de quien slo Microsoft tiene el cdigo fuente) no legitima
a ninguna empresa a tener tanto poder. No lo olvidemos: el MS-DOS ha dado un vuelco hacia la
amigabilidad con el usuario cuando Digital Research ha aparecido con el DR-DOS. Del mismo
modo que Windows seguir lento o colgndose mientras Unix no tenga ms aplicaciones
comerciales.
Si hay alguien que puede competir con Windows es Unix. Y en Unix no dependemos de ningn
fabricante concreto, ni de hardware ni de software. Probablemente, la insuficiente normalizacin
actual la corregira pronto el propio mercado. Tiene usted Linux instalado en casa y lo utiliza al
menos para conectarse a Internet por Infova, o quiz le gustara hacerlo algn da?. O
por el contrario es de los que piensan que Bill Gates es un genio?. Si se queda con la segunda
opcin, es que ve mucho la tele, aunque evidentemente tiene razn: y cuantos ms como usted,
ms genio que ser... ;-)
Captulo III: Microprocesadores 8086/88, 286, 386, 486 y Pentium.
Poseen una arquitectura interna de 16 bits y pueden trabajar con operandos de 8 y 16 bits; una
capacidad de direccionamiento de 20 bits (hasta 1 Mb) y comparten el mismo juego de
instrucciones.
Entre esas instrucciones, las ms rpidas se ejecutan en 2 ciclos tericos de reloj y unos 9
reales (se trata del movimiento de datos entre registros internos) y las ms lentas en 206 (divisin
entera con signo del acumulador por una palabra extrada de la memoria). Las frecuencias internas
de reloj tpicas son 4.77 MHz en la versin 8086; 8 MHz en la versin 8086-2 y 10 MHz en la
8086-1. Recurdese que un MHz son un milln de ciclos de reloj, por lo que un PC estndar a
4,77 MHz puede ejecutar de 20.000 a unos 0,5 millones de instrucciones por segundo, seg n la
complejidad de las mismas (un 486 a 50 MHz, incluso sin memoria cach externa es capaz de
ejecutar entre 1,8 y 30 millones de estas instrucciones por segundo).
Cuando la CPU est en modo protegido, los programas de usuario tienen un acceso limitado al
juego de instrucciones; slo el proceso supervisor -normalmente el sistema operativo- est
capacitado para realizar ciertas tareas. Esto es as para evitar que los programas de usuario puedan
campar a sus anchas y entrar en conflictos unos con otros, en materia de recursos como memoria o
perifricos. Adems, de esta manera, aunque un error software provoque el cuelgue de un
proceso, los dems pueden seguir funcionando normalmente, y el sistema operativo podra
abortar el proceso colgado. Por desgracia, con el DOS el 286 no est en modo protegido y el
cuelgue de un solo proceso -bien el programa principal o una rutina operada por interrupciones-
significa la cada inmediata de todo el sistema.
El 8086 no posee ningn mecanismo para apoyar la multitarea ni la memoria virtual desde el
procesador, por lo que es difcil disear un sistema multitarea para el mismo y casi imposible
conseguir que sea realmente operativo. Obviamente, el 286 en modo protegido pierde
absolutamente toda la compatibilidad con los procesadores anteriores. Por ello, en este libro s lo
trataremos el modo real, nico disponible bajo DOS, aunque veremos alguna instrucci n extra
que tambin se puede emplear en modo real.
Las caractersticas generales del 286 son: tiene un bus de datos de 16 bits, un bus de
direcciones de 24 bits (16 Mb); posee 25 instrucciones ms que el 8086 y admite 8 modos de
direccionamiento. En modo virtual permite direccionar hasta 1 Gigabyte. Las frecuencias de trabajo
tpicas son de 12 y 16 MHz, aunque existen versiones a 20 y 25 MHz. Aqu , la instrucci n
ms lenta es la misma que en el caso del 8086, solo que emplea 29 ciclos de reloj en lugar de 206.
Un 286 de categora media (16 MHz) podra ejecutar ms de medio mill n de instrucciones
de estas en un segundo, casi 15 veces ms que un 8086 medio a 8 MHz. Sin embargo,
transfiriendo datos entre registros la diferencia de un procesador a otro se reduce notablemente,
aunque el 286 es ms rpido y no slo gracias a los MHz adicionales.
Versiones mejoradas de los Intel 8086 y 8088 se encuentran tambin en los procesadores NEC-
V30 y NEC-V20 respectivamente. Ambos son compatibles Hardware y Software, con la ventaja de
que el procesado de instrucciones est optimizado, llegando a superar casi en tres veces la
velocidad de los originales en algunas instrucciones aritmticas. Tambin poseen una cola de
prebsqueda mayor (cuando el microprocesador est ejecutando una instruccin, si no hace uso
de los buses externos, carga en una cola FIFO de unos pocos bytes las posiciones posteriores a la
que est procesando, de esta forma una vez que concluye la instrucci n en curso ya tiene
internamente la que le sigue). Adems, los NEC V20 y V30 disponen de las mismas instrucciones
adicionales del 286 en modo real, al igual que el 80186 y el 80188.
Por su parte, el 386 dispone de una arquitectura de registros de 32 bits, con un bus de direcciones
tambin de 32 bits (direcciona hasta 4 Gigabytes = 4096 Mb) y ms modos posibles de
funcionamiento: el modo real (compatible 8086), el modo protegido (relativamente compatible con
el del 286), un modo protegido propio que permite -por fin!- romper la barrera de los
tradicionales segmentos y el modo virtual 86, en el que puede emular el funcionamiento
simultneo de varios 8086. Una vez ms, todos los modos son incompatibles entre s y
requieren de un sistema operativo especfico: si se puede perdonar al fabricante la p rdida de
compatibilidad del modo avanzados del 286 frente al 8086, debido a la lgica evolucin
tecnolgica, no se puede decir lo mismo del 386 respecto al 286: no hubiera sido necesario aadir
un nuevo modo protegido si hubiera sido mejor construido el del 286 apenas un par de aos atrs.
Normalmente, los 386 suelen operar en modo real (debido al DOS) por lo que no se aprovechan las
posibilidades multitarea ni de gestin de memoria. Por otra parte, aunque se pueden emplear los
registros de 32 bits en modo real, ello no suele hacerse -para mantener la compatibilidad con
procesadores anteriores- con lo que de entrada se est tirando a la basura un 50% de la capacidad
de proceso del chip, aunque por fortuna estos procesadores suelen trabajar a frecuencias de 16/20
MHz (obsoletas) y normalmente de 33 y hasta 40 MHz.
El 386sx es una variante del 386 a nivel de hardware, aunque es compatible en software.
Bsicamente, es un 386 con un bus de datos de slo 16 bits -ms lento, al tener que dar dos
pasadas para un dato de 32 bits-. De hecho, podra haber sido diseado perfectamente para
mantener una compatibilidad hardware con el 286, aunque el fabricante lo evit probablemente
por razones comerciales.
El 486 se diferencia del 386 en la integracin en un solo chip del coprocesador 387. Tambin
se ha mejorado la velocidad de operacin: la versin de 25 MHz dobla en trminos reales a un
386 a 25 MHz equipado con el mismo tamao de memoria cach. La versin 486sx no se
diferencia en el tamao del bus, tambin de 32 bits, sino en la ausencia del 387 (que puede ser
aadido externamente). Tambin existen versiones de 486 con buses de 16 bits, el primer
fabricante de estos chips, denominados 486SLC, ha sido Cyrix. Una tendencia iniciada por el 486
fue la de duplicar la velocidad del reloj interno (pongamos por caso de 33 a 66 MHz) aunque en las
comunicaciones con los buses exteriores se respeten los 33 MHz. Ello agiliza la ejecuci n de las
instrucciones ms largas: bajo DOS, el rendimiento general del sistema se puede considerar
prcticamente el doble. Son los chips DX2 (tambin hay una variante a 50 MHz: 25 x 2). La
culminacin de esta tecnologa viene de la mano de los DX4 a 75/100 MHz (25/33 x 3).
Estos procesadores disponen de 14 registros de 16 bits (el 286 alguno ms, pero no se suele
emplear bajo DOS). La misin de estos registros es almacenar las posiciones de memoria que van
a experimentar repetidas manipulaciones, ya que los accesos a memoria son mucho ms lentos que
los accesos a los registros. Adems, hay ciertas operaciones que slo se pueden realizar sobre los
registros. No todos los registros sirven para almacenar datos, algunos estn especializados en
apuntar a las direcciones de memoria. La mecnica bsica de funcionamiento de un programa
consiste en cargar los registros con datos de la memoria o de un puerto de E/S, procesar los datos y
devolver el resultado a la memoria o a otro puerto de E/S. Obviamente, si un dato s lo va a
experimentar un cambio, es preferible realizar la operacin directamente sobre la memoria, si ello
es posible. A continuacin se describen los registros del 8086.
AX SP CS IP
BX BP DS flags
CX SI SS
DX DI ES
Registros
Registros Registro puntero
Registros punteros de
de de instrucciones
de datos pila e
segmento y flags
ndices
- Registros de datos:
AX, BX, CX, DX: pueden utilizarse bien como registros de 16 bits o como dos registros
separados de 8 bits (byte superior e inferior) cambiando la X por H o L segn queramos referirnos
a la parte alta o baja respectivamente. Por ejemplo, AX se descompone en AH (parte alta) y AL
(parte baja). Evidentemente, cualquier cambio sobre AH o AL altera AX!: valga como ejemplo
que al incrementar AH se le estn aadiendo 256 unidades a AX.
AX = Acumulador.
BX = Base.
Se usa como registro base para referenciar direcciones de memoria con direccionamiento
indirecto, manteniendo la direccin de la base o comienzo de tablas o matrices. De esta manera, no
es preciso indicar una posicin de memoria fija, sino la nmero BX (as, haciendo avanzar de
unidad en unidad a BX, por ejemplo, se puede ir accediendo a un gran bloque de memoria en un
bucle).
CX = Contador.
Se utiliza comnmente como contador en bucles y operaciones repetitivas de manejo de
cadenas. En las instrucciones de desplazamiento y rotacin se utiliza como contador de 8 bits.
DX = Datos.
Usado en conjuncin con AX en las operaciones de multiplicacin y divisin que
involucran o generan datos de 32 bits. En las de entrada y salida se emplea para especificar la
direccin del puerto E/S.
- Registros de segmento:
Definen reas de 64 Kb dentro del espacio de direcciones de 1 Mb del 8086. Estas reas
pueden solaparse total o parcialmente. No es posible acceder a una posici n de memoria no
definida por algn segmento: si es preciso, habr de moverse alguno.
- Registros ndices:
Es un registro de 16 bits de los cuales 9 son utilizados para indicar diversas situaciones
durante la ejecucin de un programa. Los bits 0, 2, 4, 6, 7 y 11 son indicadores de condici n, que
reflejan los resultados de operaciones del programa; los bits del 8 al 10 son indicadores de control y
el resto no se utilizan. Estos indicadores pueden ser comprobados por las instrucciones de salto
condicional, lo que permite variar el flujo secuencial del programa segn el resultado de las
operaciones.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
CF (Carry Flag): Indicador de acarreo. Su valor ms habitual es lo que nos llevamos en una
suma o resta.
OF (Overflow Flag): Indicador de desbordamiento. Indica que el resultado de una
operacin no cabe en el tamao del operando destino.
ZF (Zero Flag): Indicador de resultado 0 o comparacin igual.
SF (Sign Flag): Indicador de resultado o comparacin negativa.
PF (Parity Flag): Indicador de paridad. Se activa tras algunas operaciones aritmtico-
lgicas para indicar que el nmero de bits a uno resultante es par.
AF (Auxiliary Flag): Para ajuste en operaciones BCD.
DF (Direction Flag): Indicador de direccin. Manipulando bloques de memoria, indica el
sentido de avance (ascendente/descendente).
IF (Interrupt Flag): Indicador de interrupciones: puesto a 1 estn permitidas.
TF (Trap Flag): Indicador de atrape (ejecucin paso a paso).
Los 386 y superiores disponen de muchos ms registros de los que vamos a ver ahora. Sin
embargo, bajo el sistema operativo DOS slo se suelen emplear los que veremos, que constituyen
bsicamente una extensin a 32 bits de los registros originales del 8086.
Se ampla el tamao de los registros de datos (que pueden ser accedidos en fragmentos de 8,
16 32 bits) y se aaden dos nuevos registros de segmento multipropsito (FS y GS). Algunos
de los registros aqu mostrados son realmente de 32 bits (como EIP en vez de IP), pero bajo
sistema operativo DOS no pueden ser empleados de manera directa, por lo que no les
consideraremos.
Son los distintos modos de acceder a los datos en memoria por parte del procesador. Antes de ver
los modos de direccionamiento, echaremos un vistazo a la sintaxis general de las instrucciones, ya
que pondremos alguna en los ejemplos:
INSTRUCCIN DESTINO, FUENTE
Donde destino indica dnde se deja el resultado de la operacin en la que pueden participar
(segn casos) FUENTE e incluso el propio DESTINO. Hay instrucciones, sin embargo, que s lo
tienen un operando, como la siguiente, e incluso ninguno:
INSTRUCCIN DESTINO
Como ejemplos, aunque no hemos visto an las instrucciones utilizaremos un par de ellas: la de
copia o movimiento de datos (MOV) y la de suma (ADD).
Porque hay que tener en cuenta que cuando traduzcamos a nmeros el smbolo podra
quedar:
17F3:0A11 DW FFF
MOV AX,0A11
- Direccionamiento de registro: Los operandos, necesariamente de igual tamao, estn
contenidos en los registros indicados en la instruccin:
MOV DX,AX
MOV AH,AL
Esta sintaxis (quitando la 'h' de hexadecimal) sera la que admite el programa DEBUG
(realmente habra que poner, en el segundo caso, ES: en una lnea y el MOV en otra). Al trabajar
con ensambladores, las variables en memoria se pueden referenciar con etiquetas simblicas:
MOV AX,dato
MOV AX,ES:dato
- Indirecto con ndice o indexado: El operando se encuentra en una direccin determinada por
la suma de un registro de segmento*16, un registro de ndice, SI o DI y un desplazamiento de 8
16 bits. Ejemplos:
MOV AX,[DI+DESP] MOV AX,desp[DI]
ADD [SI+DESP],BX ADD desp[SI],BX
- Indirecto con base e ndice o indexado a base: El operando se encuentra en una direccin
especificada por la suma de un registro de segmento*16, uno de base, uno de ndice y
opcionalmente un desplazamiento de 8 16 bits:
MOV AX,ES:[BX+DI+DESP] MOV AX,ES:desp[BX][DI]
MOV CS:[BX+SI+DESP],CX MOV CS:desp[BX][SI],CX
Como se ve en los modos de direccionamiento, hay casos en los que se indica expl citamente
el registro de segmento a usar para acceder a los datos. Existen unos segmentos asociados por
defecto a los registros de desplazamiento (IP, SP, BP, BX, DI, SI); s lo es necesario declarar el
segmento cuando no coincide con el asignado por defecto. En ese caso, el ensamblador genera un
byte adicional (a modo de prefijo) para indicar cul es el segmento referenciado. La siguiente tabla
relaciona las posibles combinaciones de los registros de segmento y los de desplazamiento:
CS SS DS ES
IP S No No No
SP No S No No
BP con prefijo por defecto con prefijo con prefijo
BX con prefijo con prefijo por defecto con prefijo
SI con prefijo con prefijo por defecto con prefijo
DI con prefijo con prefijo por defecto con prefijo(1)
(1) Tambin por defecto en el manejo de cadenas.
Los 386 y superiores admiten otros modos de direccionamiento ms sofisticados, que se vern
en el prximo captulo, despus de conocer todas las instrucciones del 8086. Por ahora, con
todos estos modos se puede considerar que hay ms que suficiente. De hecho, algunos se utilizan
en muy contadas ocasiones.
3.5. - LA PILA.
La pila es un bloque de memoria de estructura LIFO (Last Input First Output: ltimo en entrar,
primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de
pila). Las posiciones individuales dentro de la pila se calculan sumando al contenido del segmento
de pila SS un desplazamiento contenido en el registro puntero de pila SP. Todos los datos que se
almacenan en la pila son de longitud palabra, y cada vez que se introduce algo en ella por medio de
las instrucciones de manejo de pila (PUSH y POP), el puntero se decrementa en dos; es decir, la pila
avanza hacia direcciones decrecientes. El registro BP suele utilizarse normalmente para apuntar a
una cierta posicin de la pila y acceder indexadamente a sus elementos -generalmente en el caso
de variables- sin necesidad de desapilarlos para consultarlos.
La pila es utilizada frecuentemente al principio de una subrutina para preservar los registros que
no se desean modificar; al final de la subrutina basta con recuperarlos en orden inverso al que
fueron depositados. En estas operaciones conviene tener cuidado, ya que la pila en los 8086 es
comn al procesador y al usuario, por lo que se almacenan en ella tambin las direcciones de
retorno de las subrutinas. Esta ltima es, de hecho, la ms importante de sus funciones. La
estructura de pila permite que unas subrutinas llamen a otras que a su vez pueden llamar a otras y
as sucesivamente: en la pila se almacenan las direcciones de retorno, que sern las de la
siguiente instruccin que provoc la llamada a la subrutina. As, al retornar de la subrutina se
extrae de la pila la direccin a donde volver. Los compiladores de los lenguajes de alto nivel la
emplean tambin para pasar los parmetros de los procedimientos y para generar en ella las
variables automticas -variables locales que existen durante la ejecucin del subprograma y se
destruyen inmediatamente despus-. Por ello, una norma bsica es que se debe desapilar siempre
todo lo apilado para evitar una prdida de control inmediata del ordenador.
Aunque las instrucciones del procesador no sern vistas hasta el prximo captulo, con
objeto de ayudar a la imaginacin del lector elaboraremos un primer programa de ejemplo en
lenguaje ensamblador. La utilidad de este programa es dejar patente que lo nico que entiende el
8086 son nmeros, aunque nosotros nos referiremos a ellos con unos s mbolos que faciliten
entenderlos. Tambin es interesante este ejemplo para afianzar el concepto de registro de
segmento.
En este programa slo vamos a emplear las instrucciones MOV, ya conocida, y alguna otra
ms como la instruccin INC (incrementar), DEC (disminuir una unidad) y JNZ (saltar si el
resultado no es cero). Suponemos que el programa est ubicado a partir de la direcci n de
memoria 14D3:7A10 (arbitrariamente elegida) y que lo que pretendemos hacer con l es limpiar la
pantalla. Como el ordenador es un PC con monitor en color, la pantalla de texto comienza en
B800:0000 (no es ms que una zona de memoria). Por cada carcter que hay en dicha pantalla,
comenzando arriba a la izquierda, a partir de la direccin B800:0000 tenemos dos bytes: el
primero, con el cdigo ASCII del carcter y el segundo con el color. Lo que vamos a hacer es
rellenar los 2000 caracteres (80 columnas x 25 lneas) con espacios en blanco (c digo ASCII 32,
20h en hexadecimal), sin modificar el color que hubiera antes. Esto es, se trata de poner el valor
32 en la direccin B800:0000, la B800:0002, la B800:0004... y as sucesivamente.
Como se puede ver, la segunda instruccin (bytes de cdigo mquina 0B8h, 0 y 0B8h
colocados en posiciones consecutivas) est colocada a partir del desplazamiento 7A13h, ya que la
anterior que ocupaba 3 bytes comenzaba en 7A10h. En el ejemplo cargamos el valor 0B800h en DS
apoyndonos en AX como intermediario. El motivo es que los registros de segmento no admiten el
direccionamiento inmediato. A medida que se van haciendo programas, el ensamblador da mensajes
de error cuando se encuentra con estos fallos y permite ir aprendiendo con facilidad las normas, que
tampoco son demasiadas. La instruccin MOV BYTE PTR [BX],32 equivale a decir: poner en la
direccin de memoria apuntada por BX (DS:[BX] para ser ms exactos) el byte de valor 32. El
valor 0F8h del cdigo mquina de la ltima instruccin es el complemento a dos (n mero
negativo) del valor 8.
Normalmente, casi nunca habr que ensamblar a mano consultando unas tablas, como hemos
hecho en este ejemplo. Sin embargo, la mejor manera de aprender ensamblador es no olvidando la
estrecha relacin de cada lnea de programa con la CPU y la memoria.
Captulo IV: JUEGO DE INSTRUCCIONES 80x86
4.1. - DESCRIPCIN COMPLETA DE LAS INSTRUCCIONES.
MOV (transferencia)
Transfiere datos de longitud byte o palabra del operando origen al operando destino.
Pueden ser operando origen y operando destino cualquier registro o posicin de memoria
direccionada de las formas ya vistas, con la nica condicin de que origen y destino tengan la
misma dimensin. Existen ciertas limitaciones, como que los registros de segmento no admiten el
direccionamiento inmediato: es incorrecto MOV DS,4000h; pero no lo es por ejemplo MOV
DS,AX o MOV DS,VARIABLE. No es posible, as mismo, utilizar CS como destino (es
incorrecto hacer MOV CS,AX aunque pueda admitirlo algn ensamblador). Al hacer MOV hacia
un registro de segmento, las interrupciones quedan inhibidas hasta despus de ejecutarse la
siguiente instruccin (8086/88 de 1983 y procesadores posteriores).
Ejemplos: mov ds,ax
mov bx,es:[si]
mov si,offset dato
XCHG (intercambiar)
XLAT (traduccin)
Sintaxis: LAHF
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Carga los bits 7, 6, 4, 2 y 0 del registro AH con el contenido de los indicadores SF, ZF, AF,
PF Y CF respectivamente. El contenido de los dems bits queda sin definir.
Sintaxis: SAHF
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - x x x x x
Sintaxis: CLC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - 0
Sintaxis: CLD
Indicadores: OF DF IF TF SF ZF AF PF CF
- 0 - - - - - - -
Sintaxis: CLI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 - - - - - -
Sintaxis: CMC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - x
Sintaxis: STC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - 1
Sintaxis: STI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 1 - - - - - -
Transfiere el elemento palabra que se encuentra en lo alto de la pila (apuntado por SP) al
operando destino que a de ser tipo palabra, e incrementa en dos el registro SP. La instrucci n POP
CS, poco til, no funciona correctamente en los 286 y superiores.
Ejemplos: pop ax
pop pepe
Sintaxis: POPF
Indicadores: OF DF IF TF SF ZF AF PF CF
x x x x x x x x x
Sintaxis: PUSHF
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Incondicional
dir dd 0f000e987h
call dword ptr dir
Condicional
Gestin de bucle
LOOP (bucle)
Interrupciones
INT (interrupcin)
Sintaxis: INTO
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 0 - - - - -
Sintaxis: IRET
Indicadores: OF DF IF TF SF ZF AF PF CF
x x x x x x x x x
IN (entrada)
OUT (salida)
Sintaxis: AAA
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - ? ? x ? x
Sintaxis: DAA
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x x x x
Convierte el contenido del registro AL en un par de valores BCD: si los cuatro bits menos
significativos de AL son un nmero mayor que 9, el indicador AF se pone a 1 y se suma 6 a AL.
De igual forma, si los cuatro bits ms significativos de AL tras la operaci n anterior son un
nmero mayor que 9, el indicador CF se pone a 1 y se suma 60h a AL.
Ejemplo: add al,cl
daa
INC (incrementar)
* * * R E S TA* * *
CMP (comparacin)
Resta origen de destino sin retornar ningn resultado. Los operandos quedan inalterados,
paro los indicadores pueden ser consultados mediante instrucciones de bifurcacin condicional.
Los operandos pueden ser de tipo byte o palabra pero ambos de la misma dimensin.
Ejemplo: cmp bx, mem_pal
cmp ch,cl
Sintaxis: DAS
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - x x x x x
DEC (decrementar)
Resta una unidad del operando destino. El operando puede ser byte o palabra. Obsrvese
que esta instruccin no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento
por este procedimiento (utilcese ZF).
Ejemplo: dec ax
dec mem_byte
NEG (negacin)
SUB (resta)
Sintaxis: AAM
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
Sintaxis: AAD
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
En el ejemplo, tras convertir los dos nmeros BCD no empaquetados (en AX) en un
dividendo vlido, la instruccin de dividir genera un resultado correcto.
*** CONVERSIONES***
Sintaxis: CBW
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Copia el bit 7 del registro AL en todos los bits del registro AH, es decir, expande el signo
de AL a AX como paso previo a una operacin de 16 bits.
Sintaxis: CWD
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Expande el signo del registro AX sobre el registro DX, copiando el bit ms significativo
de AH en todo DX.
Compara dos cadenas restando al origen el destino. Ninguno de los operandos se alteran,
pero los indicadores resultan afectados. La cadena origen se direcciona con registro SI sobre el
segmento de datos DS y la cadena destino se direcciona con el registro DI sobre el segmento extra
ES. Los registros DI y SI se autoincrementan o autodecrementan seg n el valor del indicador DF
(vanse CLD y STD) en una o dos unidades, dependiendo de si se trabaja con bytes o con
palabras. Cadena origen y cadena destino son dos operandos redundantes que slo indican el tipo
del dato (byte o palabra) a comparar, es ms cmodo colocar CMPSB o CMPSW para indicar
bytes/palabras. Si se indica un registro de segmento, ste sustituir en la cadena origen al DS
ordinario. Ejemplo:
lea si,origen
lea di,destino
cmpsb
Transfiere un byte o una palabra de la cadena origen direccionada por DS:SI a la cadena
destino direccionada por ES:DI, incrementando o decrementando a continuacin los registros SI y
DI segn el valor de DF (vanse CLD y STD) en una o dos unidades, dependiendo de si se
trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos redundantes
que slo indican el tipo del dato (byte o palabra) a comparar, es ms cmodo colocar MOVSB o
MOVSW para indicar bytes/palabras. Si se indica un registro de segmento, ste sustituir en la
cadena origen al DS ordinario.
Ejemplo: lea si,origen
lea di,destino
movsw
REP/REPE/REPZ/REPNE/REPNZ (repetir)
Ejemplos:
1) Buscar el byte 69 entre las 200 primeras posiciones de tabla (se supone tabla en el
segmento ES):
LEA DI,tabla
MOV CX,200
MOV AL,69
CLD
REPNE SCASB
JE encontrado
2) Rellenar de ceros 5000 bytes de una tabla colocada en datos (se supone datos en el
segmento ES):
LEA DI,datos
MOV AX,0
MOV CX,2500
CLD
REP STOSW
AND (y lgico)
Realiza el complemento a uno del operando destino, invirtiendo cada uno de sus bits. Los
indicadores no resultan afectados.
Ejemplo: not ax
OR (O lgico)
Realiza una operacin O lgico a nivel de bits entre los dos operandos, almacenndose
despus el resultado en el operando destino.
Ejemplo: or ax,bx
Realiza una operacin Y lgica entre los dos operandos pero sin almacenar el resultado.
Los indicadores son afectados con la operacin.
Ejemplo: test al,bh
XOR (O exclusivo)
Sintaxis: NOP
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Sintaxis: HLT
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Sintaxis: LOCK
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
WAIT (espera)
Sintaxis: WAIT
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Provoca la espera del procesador hasta que se detecta una seal en la patilla TEST.
Ocurre, por ejemplo, cuando el copro ha terminado una operacin e indica su finalizacin. Suele
preceder a ESC para sincronizar las acciones del procesador y coprocesador.
4.1.10. - INSTRUCCIONES DE ROTACIN Y DESPLAZAMIENTO.
Rotar a la izquierda los bits del operando destino junto con el indicador de acarreo CF el
nmero de bits especificado en el segundo operando. Si el nmero de bits a desplazar es 1, se
puede especificar directamente, en caso contrario el valor debe cargarse en CL y especificar CL
como segundo operando. No es conveniente que CL sea mayor de 7, en bytes; 15, en palabras.
Rotar a la derecha los bits del operando destino junto con el indicador de acarreo CF el
nmero de bits especificado en el segundo operando. Si el nmero de bits es 1 se puede
especificar directamente; en caso contrario su valor debe cargarse en CL y especificar CL como
segundo operando:
Rota a la izquierda los bits del operando destino el nmero de bits especificado en el
segundo operando, que puede ser 1 CL previamente cargado con el valor del nmero de veces.
Rota a la derecha los bits del operando destino el nmero de bits especificado en el
segundo operando. Si el nmero de bits es 1 se puede poner directamente, en caso contrario debe
ponerse a travs de CL.
Desplaza a la derecha los bits del operando destino el nmero de bits especificado en el
segundo operando. Los bits de la izquierda se rellenan con el bit de signo del primer operando. Si el
nmero de bits a desplazar es 1 se puede especificar directamente, si es mayor se especifica a
travs de CL.
Desplaza a la derecha los bits del operando destino el nmero de los bits especificados en
el segundo operando. Los bits de la izquierda se llena con cero. Si el n mero de bits a desplazar es
1 se puede especificar directamente en el caso en que no ocurra se pone el valor en CL:
- Desplazamientos y rotaciones.
El valor de desplazamiento en las operaciones de manipulacin de bits del 8086 es una
constante de 8 bits (indicada en CL); en el 286 y superiores se toma mdulo 32 (s lo se
consideran los 5 bits menos significativos).
- Prefijos redundantes.
Las instrucciones tienen una longitud ilimitada en el 8086; en el 286 y superiores no pueden
exceder de 15 bytes. Por tanto, los prefijos redundantes pueden producir excepciones de c digo de
operacin no vlido.
- LOCK.
Esta instruccin no est limitada de ninguna manera en el 8086 y en el 286. En el 386 y
superiores su uso est restringido a determinadas instrucciones.
- Registro de FLAGS.
Difiere algo en los bits 12 al 15 en todos los procesadores; el 386 dispone adem s de un
registro de flags de 32 bits.
- Interrupcin NMI.
Desde el 286 y superiores, una NMI no puede interrumpir una rutina de tratamiento NMI.
A continuacin se describen las instrucciones adicionales que incorporan los 286 en modo real,
que tambin pueden ser consideradas cuando trabajamos con los microprocesadores compatibles
V20 y V30, as como con los procesadores superiores al 286. Las instrucciones del modo
protegido se dirigen especialmente a la multiprogramacin y el tiempo compartido, siendo
especficas de la conmutacin de procesos y tratamiento de la memoria virtual y no pueden
emplearse directamente bajo DOS.
BOUND r16, mem16: Comprueba si el registro de 16 bits indicado como primer operando
est dentro de los lmites de una matriz. Los lmites de la matriz los definen dos
palabras consecutivas en la memoria apuntadas por mem16. Si est fuera de los lmites,
se produce una interrupcin 5 en la que el IP apilado queda apuntando a la instrucci n
BOUND (no se incrementa!).
Las instrucciones PUSH permiten meter valores inmediatos a la pila: es vlido hacer
PUSH 40h.
IMUL puede multiplicar cualquier registro de 16 bits por una constante inmediata,
devolviendo un resultado palabra (CF=1 si no cabe en 16 bits); por ejemplo, es v lido
IMUL CX,25. Tambin se admiten tres operandos: IMUL r1, r2, imm. En este caso, se
multiplica r2 por el valor inmediato
(8/16 bits) y el resultado se almacena en r1. Tanto r1 como r2 han de ser de 16 bits.
LEAVE abandona los procedimientos de alto nivel (equivale a MOV SP,BP / POP BP).
PUSHA/POPA: Introduce en la pila y en este orden los registros AX, CX, DX, BX, SP, BP,
SI y DI -o los saca en orden inverso-. Ideal en el manejo de interrupciones y muy usada en
las BIOS de 286 y 386.
Adems de todas las posibilidades adicionales del 286, el 386 y el 486 permiten utilizar
cualquier registro de 32 bits de propsito general en todos los modos de funcionamiento,
incluido el modo real, tales como EAX, EBX, ECX, EDX, ESI, EDI, EBP. Sin embargo no
deben intentarse direccionamientos por encima de los 64K. En otras palabras, se pueden
utilizar para acelerar las operaciones pero no para acceder a ms memoria. Por ejemplo, si
EBX > 0FFFFh, la instruccin MOV AX,[EBX] tendra un resultado impredecible.
Adems, estos procesadores cuentan con dos segmentos ms: adems de DS, ES, CS y
SS se pueden emplear tambin FS y GS. Aviso: parece ser que en algunos 386 fallan
ocasionalmente las instrucciones de multiplicar de 32 bits.
Nota: No es del todo cierto que el 386 y el 486 no permitan acceder a ms de 64
Kb en modo real: en la seccin 4.3.6 hay un ejemplo de ello.
Donde reg puede ser de 16 32 bits. Se comienza a explorar por el bit 0 (BSF) o por el
ms significativo (BSR) del segundo operando: si no aparece ningn bit activo (a 1) el indicador
ZF se activa; en caso contrario se almacena en el primer operando la posicin relativa de ese bit:
MOV AX,8
BSF BX,AX
JZ ax_es_0 ; no se saltar, adems BX = 3
CMPSD: Similar a CMPSW pero empleando ESI, EDI, ECX y comparando datos de 32
bits. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP tambi n ECX)
no excedan de 0FFFFh.
Jcc: Los saltos condicionales ahora pueden ser de 32 bits!. Mucho cuidado con la
directiva .386 en los programas en que se desee mantener la compatibilidad con
procesadores anteriores. JECXZ se utiliza en vez de JCXZ (mismo cdigo de operaci n).
LODSD: Similar a LODSW pero empleando ESI, EDI y ECX y cargando datos de 32 bits
en EAX. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP tambi n
ECX) no excedan de 0FFFFh.
LSS, LFS, LGS: similar a LDS o LES pero con esos registros de segmento.
MOVSD: Similar a MOVSW pero empleando ESI, EDI, ECX y moviendo datos de 32 bits.
Se puede emplear bajo DOS para acelerar las transferencias siempre que ESI y EDI
(utilizando REP tambin ECX) no excedan de 0FFFFh. Operando sobre la memoria de
vdeo slo se obtiene ventaja si la tarjeta es realmente de 32 bits.
MOVSX / MOVZX: carga con extensin de signo o cero. Toma el segundo operando, le
extiende adecuadamente el signo (o le pone a cero la parte alta) hasta que sea tan grande
como el primer operando y luego lo carga en el primer operando. Si el primer operando es
de 16 bits, el segundo slo puede ser de 8; si el primero es de 32 bits el segundo puede ser
de 8 16. El primer operando debe ser un registro, el segundo puede ser un registro u
operando en memoria (nunca inmediato):
MOV EAX,0FFFFFFFFh
MOV AX,7FFFh ; resultado: EAX = 0FFFF7FFFh
MOVSX EAX,AX ; resultado: EAX = 000007FFFh
OUTSD: Similar a OUTSW pero empleando ESI, EDI, ECX y enviando datos de 32 bits. Se
puede emplear bajo DOS siempre que ESI y EDI (usando REP tambin ECX) no rebasen
0FFFFh.
PUSHAD / POPAD: Similares a PUSHA y POPA pero con los registro de 32 bits. La
instruccin POPAD falla en la mayora de los 386, incluidos los de AMD. Para solventar
el fallo (que consiste en que EAX no se restaura correctamente) basta colocar un NOP
inmediatamente detrs de POPAD.
SCASD: Similar a SCASW pero empleando ESI, EDI, ECX y buscando datos de 32 bits. Se
puede emplear bajo DOS siempre que ESI y EDI (usando REP tambin ECX) no rebasen
0FFFFh.
STOSD: Similar a STOSW pero empleando ESI, EDI, ECX y almacenando EAX. Se puede
emplear bajo DOS siempre que ESI y EDI (utilizando REP tambin ECX) no excedan de
0FFFFh.
;
******************************************************************
**
; *
*
; * CPU v2.2 (c) Septiembre 1992 CiriSOFT
*
; * (c) Grupo Universitario de Informtica - Valladolid
*
; *
*
; * Este programa determina el tipo de microprocesador del
equipo *
; * y devuelve un cdigo ERRORLEVEL indicndolo:
*
; *
*
; * 0-8088, 1-8086, 2-NEC V20, 3-NEC V30,
*
; * 4-80188, 5-80186, 6-286, 7-386, 8-486
*
; *
*
; * Aviso: Utilizar TASM 2.0 o compatible exclusivamente.
*
; *
*
;
******************************************************************
**
cpu SEGMENT
ASSUME CS:cpu, DS:cpu
.386
ORG 100h
inicio:
LEA DX,texto_ini ; texto de saludo
MOV AH,9
INT 21h ; imprimirlo
CALL procesador? ; tipo de procesador en AX
PUSH AX ; guardarlo para el final
LEA BX,cpus_indice-2 ; tabla de nombres-2
MOV CX,0FFFFh ; nmero de iteracin-1
otro_proc: INC CX
ADD BX,2
MOV DX,[BX] ; nombre del primer procesador
CALL print
CMP CX,AX ; procesador del equipo?
JNE no_es_este
LEA DX,apuntador_txt ; s lo es: indicarlo
CALL print
no_es_este: LEA DX,separador_txt
CALL print
CMP CX,7 ; nmero de CPUs tratadas-1
JBE otro_proc
LEA DX,texto_fin ; ltimos caracteres
CALL print
MOV AH,4Ch ; retornar cdigo errorlevel
AL
INT 21h ; fin de programa
print PROC
PUSH AX
PUSH BX
PUSH CX
MOV AH,9
INT 21h
POP CX
POP BX
POP AX
RET
print ENDP
cpus_indice DW i88,i86,v20,v30,i188,i186,i286,i386,i486
i88 DB "Intel 8088 $"
i86 DB "Intel 8086 $"
v20 DB " NEC V20 $"
v30 DB " NEC V30 $"
i188 DB "Intel 80188$"
i186 DB "Intel 80186$"
i286 DB "Intel 80286$"
i386 DB "Intel 80386$"
i486 DB "Intel 80486$"
cpu ENDS
END inicio
El problema es que pasar a modo protegido no es sencillo cuando la mquina ya est en modo
protegido emulando al modo real (el conocido como modo virtual 86). Por tanto, el siguiente
programa de ejemplo no funciona si est cargado un controlador de memoria expandida
(EMM386, QEMM) o dentro de Windows 3.x. Arrancando sin controlador de memoria (excepto
HIMEM) no habr problema alguno. El programa de ejemplo se limita a llenar la pantalla de texto
(empleando ahora la direccin absoluta 0B8000h a travs de EBX) de letras 'A'.
Otra restriccin de este programa de ejemplo es que no activa la lnea A20 de direcciones;
dicho de otro modo, el bit 21 (de los 32 bits de la direccin de memoria) suele estar forzado a 0
por defecto al arrancar. Para acceder a la memoria de vdeo esto no es problema, pero por encima
del primer megabyte podra haber problemas segn a qu direccin se pretenda acceder. De
todos modos, sera relativamente sencillo habilitar la lnea A20 directamente o a travs de una
funcin del controlador XMS.
Naturalmente, se sale de los objetivos de este libro describir el modo protegido o explicar los
pasos que realiza esta rutina de demostracin. Consltese al efecto la bibliografa recomendada
del apndice.
;
+-----------------------------------------------------------------
-+
; | Rutina para activar el modo flat del 386 y superiores (acceso
|
; | a 4 Gb en modo real).
|
; |
|
; | TASM flat386 /m5
|
; | TLINK flat386 /t /32
|
;
+-----------------------------------------------------------------
-+
ORG 100h
prueba:
CALL flat386 ; activar modo flat
XOR AX,AX
MOV DS,AX
MOV EBX,0B8000h ; direccin de vdeo absoluta
MOV CX,2000
llena_pant: MOV BYTE PTR [EBX],'A'
INC EBX
MOV BYTE PTR [EBX],15
INC EBX
LOOP llena_pant
INT 20h ; fin de programa
flat386 PROC
PUSH DS
PUSH ES
PUSH EAX
PUSH BX
PUSH CX
MOV CX,SS
XOR EAX,EAX
MOV AX,CS
SHL EAX,4 ; direccin lineal de segmento
CS
ADD EAX,OFFSET gdt ; desplazamiento de GDT
MOV CS:[gd2],EAX ; guardar direccin lineal de
GDT
CLI
LGDT CS:[gdtr] ; cargar tabla global de
descriptores
MOV EAX,CR0
OR AL,1 ; bit de modo protegido
MOV CR0,EAX ; pasar a modo protegido
JMP SHORT $+2 ; borrar cola de prebsqueda
MOV BX,gcodl ; ndice de descriptor en BX
MOV DS,BX ; cargar registro de segmento
DS
MOV ES,BX ; ES
MOV SS,BX ; SS
MOV FS,BX ; FS
MOV GS,BX ; GS
AND AL,11111110b
MOV CR0,EAX ; volver a modo real
JMP SHORT $+2 ; borrar cola de prebsqueda
MOV SS,CX
STI
POP CX
POP BX
POP EAX
POP ES
POP DS
RET
flat386 ENDP
segmento ENDS
END prueba
Los programas de ejemplo de este libro y la sintaxis de ensamblador tratada son las del MASM
de Microsoft y el ensamblador de IBM. No obstante, todos los programas han sido desarrollados
con el Turbo Assembler 2.0 de Borland (TASM), compatible con el cl sico MASM 5.0 de
Microsoft pero ms potente y al mismo tiempo mucho ms rpido y flexible. TASM genera
adems un cdigo ms reducido y optimizado. Por otra parte, MASM 5.0 no permite cambiar
(aunque s la 6.0) dentro de un segmento el modo del procesador: esto conlleva el riesgo de
ejecutar indeseadamente instrucciones de 32 bits al no poder acotar exactamente las lneas donde
se desea emplearlas, algo vital para mantener la compatibilidad con procesadores anteriores.
Tambin es propenso a generar errores de fase y otros similares al tratar con listados un poco
grandes. Respecto a MASM 6.0, el autor de este libro encontr que en ocasiones calcula
incorrectamente el valor de algunos smbolos y etiquetas, aunque es probable que la versin 6.1
(aparecida sospechosa e inusualmente muy poco tiempo despus) haya corregido dichos fallos,
intolerables en un ensamblador. Por otro lado, las posibilidades adicionales de TASM no han sido
empleadas por lo general. Muchos programas han sido ensamblados una vez con MASM, para
asegurar que ste puede ensamblarlos.
Conviene decir aqu que este captulo es especialmente arduo para aquellos que no conocen
el lenguaje ensamblador de ninguna mquina. La razn es que la informacin est organizada
a modo de referencia, por lo que con frecuencia se utilizan unos elementos -para explicar otros- que
an no han sido definidos. Ello por otra parte resulta inevitable tambin en algunos libros m s
bsicos, debido a la complejidad de la sintaxis del lenguaje ensamblador ideada por el fabricante
(que no la del microprocesador). Por ello, es un buen consejo actuar a dos pasadas, al igual que el
propio ensamblador en ocasiones: leer todo una vez primero -aunque no se entienda del todo- y
volverlo a leer despus ms despacio.
Un programa fuente en ensamblador contiene dos tipos de sentencias: las instrucciones y las
directivas. Las instrucciones se aplican en tiempo de ejecucin, pero las directivas s lo son
utilizadas durante el ensamblaje. El formato de una sentencia de instruccin es el siguiente:
[etiqueta] nombre_instruccin [operandos] [comentario]
Los corchetes, como es normal al explicar instrucciones en informtica, indican que lo
especificado entre ellos es opcional, dependiendo de la situacin que se trate.
las etiquetas son de tipo NEAR cuando el campo de etiqueta finaliza con dos puntos (:); esto
es, se considera cercana: quiere esto decir que cuando realizamos una llamada sobre dicha etiqueta
el ensamblador considera que est dentro del mismo segmento de cdigo (llamadas
intrasegmento) y el procesador slo carga el puntero de instrucciones IP. Tngase en cuenta que
hablamos de instrucciones; las etiquetas empleadas antes de las directivas, como las directivas de
definicin de datos por ejemplo, no llevan los dos puntos y sin embargo son cercanas.
Las etiquetas son de tipo FAR si el campo de etiqueta no termina con los dos puntos: en estas
etiquetas la instruccin a la que apunta no se encuentra en el mismo segmento de c digo sino en
otro. Cuando es referenciada en una transferencia de control se carga el puntero de instrucciones IP
y el segmento de cdigo CS (llamadas intersegmento).
Campo de operandos. Indica cuales son los datos implicados en la operacin. Puede haber
0, 1 2; en el caso de que sean dos al 1 se le llama destino y al 2 -separado por una coma-
fuente.
mov ax, es:[di] --> ax destino
es:[di] origen
Campo de comentarios. Cuando en una lnea hay un punto y coma (;) todo lo que sigue en
la lnea es un comentario que realiza aclaraciones sobre lo que se est haciendo en ese programa,
resulta de gran utilidad de cara a realizar futuras modificaciones al mismo.
Las sentencias fuente -tanto instrucciones como directivas- pueden contener constantes y
operadores.
5.2.1. - CONSTANTES.
Pueden ser binarias (ej. 10010b), decimales (ej. 34d), hexadecimales (ej. 0E0h) u octales (ej. 21o
21q); tambin las hay de cadena (ej. 'pepe', "juan") e incluso con comillas dentro de comillas de
distinto tipo (como 'hola,"amigo"'). En las hexadecimales, si el primer d gito no es num rico hay
que poner un 0. Slo se puede poner el signo (-) en las decimales (en las dems, calc lese el
complemento a dos). Por defecto, las numricas estn en base 10 si no se indica lo contrario con
una directiva (poco recomendable como se ver).
Pueden emplearse libremente (+), (-), (*) y (/) -en este ltimo caso la divisi n es siempre
entera-. Es vlida, por ejemplo, la siguiente lnea en ensamblador (que se apoya en la directiva
DW, que se ver ms adelante, para reservar memoria para una palabra de 16 bits):
dato DW 12*(numero+65)/7
Tambin se admiten los operadores MOD (resto de la divisin) y SHL/SHR (desplazar a la
izquierda/derecha cierto nmero de bits). Obviamente, el ensamblador no codifica las
instrucciones de desplazamiento (al aplicarse sobre datos constantes el resultado se calcula en
tiempo de ensamblaje):
dato DW (12 SHR 2) + 5
Pueden ser el AND, OR, XOR y NOT. Realizan las operaciones lgicas en las expresiones. Ej.:
MOV BL,(255 AND 128) XOR 128 ; BL = 0
tambin es vlido:
MOV AX,OFFSET DS:variable
* Operador .TYPE: devuelve el modo de la expresin indicada en un byte. El bit 0 indica modo
relativo al cdigo y el 1 modo relativo a datos, si ambos bits estn inactivos significa modo
absoluto. El bit 5 indica si la expresin es local (0 si est definida externamente o indefinida); el
bit 7 indica si la expresin contiene una referencia externa. El TASM utiliza tambi n el bit 3 para
indicar algo que desconozco. Este operador es til sobre todo en las macros para determinar el tipo
de los parmetros:
info .TYPE variable
Tratndose de etiquetas -en lugar de variables- indica si es lejana o FAR (0FFFEh) o cercana
o NEAR (0FFFFh).
* Operadores SIZE y LENGTH: devuelven el tamao (en bytes) o el n de elementos,
respectivamente, de la variable indicada (definida obligatoriamente con DUP):
matriz DW 100 DUP (12345)
MOV AX,SIZE matriz ; AX = 200
MOV BX,LENGTH matriz ; BX = 100
Trabajando con varios segmentos, PTR puede redefinir una etiqueta NEAR de uno de ellos
para convertirla en FAR desde el otro, con objeto de poder llamarla.
* Operadores CS:, DS:, ES: y SS: el ensamblador genera un prefijo de un byte que indica al
microprocesador el segmento que debe emplear para acceder a los datos en memoria. Por defecto,
se supone DS para los registros BX, DI o SI (o sin registros de base o ndice) y SS para SP y BP.
Si al acceder a un dato ste no se encuentra en el segmento por defecto, el ensamblador a adir
el byte adicional de manera automtica. Sin embargo, el programador puede forzar tambin esta
circunstancia:
MOV AL,ES:variable
Para solucionarlo hay que indicar en qu segmento est el dato (incluso aunque ste sea
DS):
MOV AL,DS:[0]
* Operador SHORT: indica que la etiqueta referenciada, de tipo NEAR, puede alcanzarse con un
salto corto (-128 a +127 posiciones) desde la actual situacin del contador de programa. El
ensamblador TASM, si se solicitan dos pasadas, coloca automticamente instrucciones SHORT
all donde es posible, para economizar memoria (el MASM no).
* Operador '$': indica la posicin del contador de posiciones (Location Counter) utilizado por
el ensamblador dentro del segmento para llevar la cuenta de por dnde se llega ensamblando. Muy
til:
frase DB "simptico"
longitud EQU $-OFFSET frase
Se pueden definir nmeros reales de simple precisin (4 bytes) con DD, de doble
precisin (8 bytes) con DQ y reales temporales (10 bytes) con DT; todos ellos con el formato
empleado por el coprocesador. Para que el ensamblador interprete el nmero como real ha de
llevar el punto decimal:
temperatura DD 29.72
espanoles91 DQ 38.9E6
Con el operando DUP pueden definirse estructuras repetitivas. Por ejemplo, para asignar 100
bytes a cero y 25 palabras de contenido indefinido (no importa lo que el ensamblador asigne):
ceros DB 100 DUP (0)
basura DW 25 DUP (?)
Se admiten tambin los anidamientos. El siguiente ejemplo crea una tabla de bytes donde se
repite 50 veces la secuencia 1,2,3,7,7:
tabla DB 50 DUP (1, 2, 3, 2 DUP (7))
* = (signo '='): asigna el valor de la expresin a un nombre simb lico variable: An logo al
anterior pero con posibilidad de cambiar en el futuro. Muy usada en macros (sobre todo con REPT).
num = 19
num = pepe + 1
dato = [BX+3]
dato = ES:[BP+1]
* END [expresin]: indica el final del fichero fuente. Si se incluye, expresin indica el punto
donde arranca el programa. Puede omitirse en los programas EXE si stos constan de un s lo
mdulo. En los COM es preciso indicarla y, adems, la expresin -realmente una etiqueta- debe
estar inmediatamente despus del ORG 100h.
* .286, .386 Y .8087 obligan al ensamblador a reconocer instrucciones especficas del 286, el
386 y del 8087. Tambin debe ponerse el . inicial. Con .8086 se fuerza a que de nuevo s lo se
reconozcan instrucciones del 8086 (modo por defecto). La directiva .386 puede ser colocada dentro
de un segmento (entre las directivas SEGMENT/ENDS) con el ensamblador TASM, lo que permite
emplear instrucciones de 386 con segmentos de 16 bits; alternativamente se puede ubicar fuera de
los segmentos (obligatorio en MASM) y definir stos explcitamente como de 16 bits con
USE16.
* EVEN: fuerza el contador de posiciones a una posicin par, intercalando un byte con la
instruccin NOP si es preciso. En buses de 16 ms bits (8086 y superiores, no en 8088) es dos
veces ms rpido el acceso a palabras en posicin par:
EVEN
dato_rapido DW 0
* .RADIX n: cambia la base de numeracin por defecto. Bastante desaconsejable dada la
notacin elegida para indicar las bases por parte de IBM/Microsoft (si se cambia la base por
defecto a 16, los nmeros no pueden acabar en 'd' ya que se confundiran con el sufijo de
decimal!: lo ideal sera emplear un prefijo y no un sufijo, que a menudo obliga adems a iniciar
los nmeros por 0 para distinguirlos de las etiquetas).
5.3.4. - DIRECTIVAS DE DEFINICIN DE SEGMENTOS Y PROCEDIMIENTOS.
* SEGMENT-ENDS: SEGMENT indica el comienzo de un segmento (cdigo, datos, pila, etc.)
y ENDS su final. El programa ms simple, de tipo COM, necesita la declaraci n de un segmento
(comn para datos, cdigo y pila). Junto a SEGMENT puede aparecer, opcionalmente, el tipo de
alineamiento, la combinacin, el uso y la clase:
Se pueden definir unos segmentos dentro de otros (el ensamblador los ubicar unos tras
otros). El alineamiento puede ser BYTE (ninguno), WORD (el segmento comienza en posici n
par), DWORD (comienza en posicin mltiplo de 4), PARA (comienza en una direcci n
mltiplo de 16, opcin por defecto) y PAGE (comienza en direcci n m ltiplo de 256). La
combinacin puede ser:
- (No indicada): los segmentos se colocan unos tras otros fsicamente, pero son
lgicamente independientes: cada uno tiene su propia base y sus propios offsets relativos.
- PUBLIC: usado especialmente cuando se trabaja con segmentos definidos en varios
ficheros que se ensamblan por separado o se compilan con otros lenguajes, por ello debe declararse
un nombre entre comillas simples -'clase'- para ayudar al linkador. Todos los segmentos PUBLIC de
igual nombre y clase tienen una base comn y son colocados adyacentemente unos tras otros,
siendo el offset relativo al primer segmento cargado.
- COMMON: similar, aunque ahora los segmentos de igual nombre y clase se solapan.
Por ello, las variables declaradas han de serlo en el mismo orden y tamao.
- AT: asocia un segmento a una posicin de memoria fija, no para ensamblar sino para
declarar variables (inicializadas siempre con '?') de cara a acceder con comodidad a zonas de ROM,
vectores de interrupcin, etc. Ejemplo:
vars_bios SEGMENT AT 40h
p_serie0 DW ?
vars_bios ENDS
De esta manera, la direccin del primer puerto serie puede obtenerse de esta manera
(por ejemplo):
MOV AX,variables_bios ; segmento
MOV ES,AX ; inicializar ES
MOV AX,ES:p_serie0
- STACK: segmento de pila, debe existir uno en los programas de tipo EXE; adems el
Linkador de Borland (TLINK 4.0) exige obligatoriamente que la clase de ste sea tambi n
'STACK', con el LINK de Microsoft no siempre es necesario indicar la clase del segmento de pila.
Similar, por lo dems, a PUBLIC.
- MEMORY: segmento que el linkador ubicar al final de todos los dems, lo que
permitira saber dnde acaba el programa. Si se definen varios segmentos de este tipo el
ensamblador acepta el primero y trata a los dems como COMMON. Tngase en cuenta que el
linkador no soporta esta caracterstica, por lo que emplear MEMORY es equivalente a todos los
efectos a utilizar COMMON. Olvdate de MEMORY.
El uso indica si el segmento es de 16 bits o de 32; al emplear la directiva .386 se asumen por
defecto segmentos de 32 bits por lo que es necesario declarar USE16 para conseguir que los
segmentos sean interpretados como de 16 bits por el linkador, lo que permite emplear algunas
instrucciones del 386 en el modo real del microprocesador y bajo el sistema operativo DOS.
Por ltimo, 'clase' es un nombre opcional que emplear el linkador para encadenar los
mdulos, siendo conveniente nombrar la clase del segmento de pila con 'STACK'.
ASSUME reg_segmento:nombre_segmento[,...]
* PROC-ENDP permite dar nombre a una subrutina, marcando con claridad su inicio y su fin.
Aunque es redundante, es muy recomendable para estructurar los programas.
cls PROC
...
cls ENDP
El atributo FAR que aparece en ocasiones junto a PROC indica que es un procedimiento
lejano y las instrucciones RET en su interior se ensamblan como RETF (los CALL hacia l
sern, adems, de 32 bits). Observar que la etiqueta nunca termina con dos puntos.
* PUBLIC: permite hacer visibles al exterior (otros ficheros objeto resultantes de otros listados
en ensamblador u otro lenguaje) los smbolos -variables y procedimientos- indicados. Necesario
para programacin modular e interfaces con lenguajes de alto nivel. Por ejemplo:
PUBLIC proc1, var_x
proc1 PROC FAR
...
proc1 ENDP
var_x DW 0
Declara la variable var_x y el procedimiento proc1 como accesibles desde el exterior por
medio de la directiva EXTRN.
* EXTRN: Permite acceder a smbolos definidos en otro fichero objeto (resultante de otro
ensamblaje o de una compilacin de un lenguaje de alto nivel); es necesario tambin indicar el
tipo del dato o procedimiento (BYTE, WORD o DWORD; NEAR o FAR; se emplea adem s ABS
para las constantes numricas):
EXTRN proc1:FAR, var_x:WORD
En el ejemplo se accede a los smbolos externos proc1 y var_x (ver ejemplos de PUBLIC) y
a continuacin sera posible hacer un CALL proc1 o un MOV CX,var_x. Si la directiva EXTRN
se coloca dentro de un segmento, se supone el smbolo dentro del mismo. Si el s mbolo est en
otro segmento, debe colocarse EXTRN fuera de todos los segmentos indicando expl citamente el
prefijo del registro de segmento (o bien hacer el ASSUME apropiado) al referenciarlo.
Evidentemente, al final, al linkar habr que enlazar este mdulo con el que define los elementos
externos.
codigo SEGMENT
...
codigo ENDS
datos SEGMENT
dato DW 1234
datos ENDS
La ventaja de agrupar segmentos es poder crear programas COM y SYS que contengan varios
segmentos. En todo caso, tngase en cuenta an en ese caso que no pueden emplearse todas las
caractersticas de la programacin con segmentos (por ejemplo, no se puede utilizar la directiva
SEG ni debe existir segmento de pila).
* LABEL: Permite referenciar un smbolo con otro nombre, siendo factible redefinir el tipo. La
sintaxis es: nombre LABEL tipo (tipo = BYTE, WORD, DWORD, NEAR o FAR). Ejemplo:
palabra LABEL WORD
byte_bajo DB 0
byte_alto DB 0
En el ejemplo, con MOV AX,palabra se acceder a ambos bytes a la vez (el empleo de
MOV AX,byte_bajo dara error: no se puede cargar un slo byte en un registro de 16 bits y el
ensamblador no supone que realmente pretendamos tomar dos bytes consecutivos de la
memoria).
* STRUC - ENDS: permite definir registros al estilo de los lenguajes de alto nivel, para acceder
de una manera ms elegante a los campos de una informacin con cierta estructura. Estos campos
pueden componerse de cualquiera de los tipos de datos simples (DB, DW, DD, DQ, DT) y pueden
ser modificables o no en funcin de si son simples o mltiples, respectivamente:
alumno STRUC
mote DB '0123456789' ; modificable
edadaltura DB 20,175 ; no modificable
peso DB 0 ; modificable
otros DB 10 DUP(0) ; no modificable
telefono DD ? ; modificable
alumno ENDS
En el ejemplo se definen los campos modificables (los nicos definibles) dejando sin definir
(comas consecutivas) los no modificables, crendose la estructura 'felipe' que ocupa 27 bytes. Las
cadenas de caracteres son rellenadas con espacios en blanco al final si no alcanzan el tama o
mximo de la declaracin. El TASM es ms flexible y permite definir tambin el primer
elemento de los campos mltiples sin dar error. Tras crear la estructura, es posible acceder a sus
elementos utilizando un (.) para separar el nombre del campo:
MOV AX,OFFSET felipe.telefono
LEA BX,felipe
MOV CL,[BX].peso ; equivale a [BX+12]
* RECORD: similar a STRUC pero operando con campos de bits. Permite definir una estructura
determinada de byte o palabra para operar con comodidad. Sintaxis:
nombre RECORD nombre_de_campo:tamao[=valor],...
La estructura registro totaliza 7 bits, por lo que ocupa un byte. Est dividida en tres campos
que ocupan los 7 bits menos significativos del byte: el campo A ocupa los bits 6 y 5, el B los bits
del byte: el campo A ocupa los bi1 al 4 y el C el bit 0:
65 4321 0
11 0101 ?
Quedando reg1 con el valor binario 1001011 (el campo B permanece inalterado y el A y C
toman los valores indicados). Ejemplos de operaciones soportadas:
MOV AL, A ; AL = 5 (desplazamiento del bit
; menos significativo de A)
MOV AL, MASK A ; AL = 01100000b (mscara de A)
MOV AL, WIDTH A ; AL = 2 (anchura de A)
* TITLE ttulo: indica el ttulo que aparece en la 1 lnea de cada pgina (mximo 60
caracteres).
* .LFCOND: Listar los bloques de cdigo asociados a una condicin falsa (IF).
* .TFCOND: Invertir el modo vigente de listado de los bloques asociados a una condici n
falsa.
5.4. - MACROS.
No conviene confundir las macros con subrutinas: es estas ltimas, el conjunto de instrucciones
aparece una sola vez en todo el programa y luego se invoca con CALL. Sin embargo, cada vez que
se referencia a una macro, el cdigo que sta representa se expande en el programa definitivo,
duplicndose tantas veces como se use la macro. Por ello, aquellas tareas que puedan ser
realizadas con subrutinas siempre ser ms conveniente realizarlas con las mismas, con objeto de
economizar memoria. Es cierto que las macros son algo ms rpidas que las subrutinas (se ahorra
un CALL y un RET) pero la diferencia es tan mnima que en la prctica es despreciable en el
99,99% de los casos. Por ello, es absurdo e irracional realizar ciertas tareas con macros que pueden
ser desarrolladas mucho ms eficientemente con subrutinas: es una pena que en muchos manuales
de ensamblador an se hable de macros para realizar operaciones sobre cadenas de caracteres, que
generaran programas gigantescos con menos de un 1% de velocidad adicional.
La macro se define por medio de la directiva MACRO. Es necesario definir la macro antes de
utilizarla. Una macro puede llamar a otra. Con frecuencia, las macros se colocan juntas en un
fichero independiente y luego se mezclan en el programa principal con la directiva INCLUDE:
IF1
INCLUDE fichero.ext
ENDIF
La sentencia IF1 asegura que el ensamblador lea el fichero fuente de las macros s lo en la
primera pasada, para acelerar el ensamblaje y evitar que aparezcan en el listado (generado en la
segunda fase). Conviene hacer hincapi en que la definicin de la macro no consume memoria,
por lo que en la prctica es indiferente declarar cientos que ninguna macro:
nombre_simblico MACRO [parmetros]
...
... ; instrucciones de la macro
ENDM
En realidad, y a diferencia de lo que sucede con los dems s mbolos, el nombre de una macro
puede coincidir con el de una instruccin mquina o una directiva del ensamblador: a partir de
ese momento, la instruccin o directiva machacada pierde su significado original. El ensamblador
dar adems un aviso de advertencia si se emplea una instruccin o directiva como nombre de
macro, aunque tolerar la operacin. Normalmente se las asignar nombres normales, como a
las variables. Sin embargo, si alguna vez se redefiniera una instruccin mquina o directiva, para
restaurar el significado original del smbolo, la macro puede ser borrada -o simplemente porque
ya no va a ser usada a partir de cierto punto del listado, y as ya no consumir espacio en las
tablas de macros que mantiene en memoria el ensamblador al ensamblar-. No es necesario borrar las
macros antes de redefinirlas. Para borrarlas, la sintaxis es la siguiente:
PURGE nombre_simblico[,nombre_simblico,...]
5.4.2. - EJEMPLO DE UNA MACRO SENCILLA.
Desde el 286 existe una instruccin muy cmoda que introduce en la pila 8 registros, y otra
que los saca (PUSHA y POPA). Quien est acostumbrado a emplearlas, puede crear unas macros
que simulen estas instrucciones en los 8086:
SUPERPUSH MACRO
PUSH AX
PUSH CX
PUSH DX
PUSH BX
PUSH SP
PUSH BP
PUSH SI
PUSH DI
ENDM
Para quien no haya tenido relacin previa con algn lenguaje estructurado de alto nivel, har
un breve comentario acerca de lo que son los parmetros formales y actuales en una macro, similar
aqu a los procedimientos de los lenguajes de alto nivel.
Cuando se llama a una macro se le pueden pasar opcionalmente un cierto nmero de
parmetros de cierto tipo. Estos parmetros se denominan parmetros actuales. En la
definicin de la macro, dichos parmetros aparecen asociados a ciertos nombres arbitrarios, cuya
nica misin es permitir distinguir unos parmetros de otros e indicar en qu orden son
entregados: son los parmetros formales. Cuando el ensamblador expanda la macro al ensamblar,
los parmetros formales sern sustituidos por sus correspondientes parmetros actuales.
Considerar el siguiente ejemplo:
SUMAR MACRO a,b,total
PUSH AX
MOV AX,a
ADD AX,b
MOV total,AX
POP AX
ENDM
....
SUMAR positivos, negativos, total
Las instrucciones PUSH y POP sirven para no alterar el valor de AX y conseguir que la macro se
comporte como una caja negra; no es necesario que esto sea as pero es una buena costumbre de
programacin para evitar que los programas hagan cosas raras. En general, las macros de este tipo
no deberan alterar los registros y, si los cambian, hay que tener muy claro cules.
Si se indican ms parmetros de los que una macro necesita, se ignorarn los restantes. En
cambio, si faltan, el MASM asumir que son nulos (0) y dar un mensaje de advertencia, el
TASM es algo ms rgido y podra dar un error. En general, se trata de situaciones at picas
que deben ser evitadas.
Tambin puede darse el caso de que no sea posible expandir la macro. En el ejemplo, no
hubiera sido posible ejecutar SUMAR AX,BX,DL porque DL es de 8 bits y la instrucci n MOV
DL,AX sera ilegal.
Son necesarias normalmente para los saltos condicionales que contengan las macros ms
complejas. Si se pone una etiqueta a donde saltar, la macro slo podra ser empleada una vez en
todo el programa para evitar que dicha etiqueta aparezca duplicada. La solucin est en emplear
la directiva LOCAL que ha de ir colocada justo despus de la directiva MACRO:
MINIMO MACRO dato1, dato2, resultado
LOCAL ya_esta
MOV AX,dato1
CMP AX,dato2 ; es dato1 el menor?
JB ya_esta ; s
MOV AX,dato2 ; no, es dato2
ya_esta: MOV resultado,AX
ENDM
* Operador ;;
Indica que lo que viene a continuacin es un comentario que no debe aparecer al
expansionar la macro. Cuando al ensamblar se genera un listado del programa, las macros suelen
aparecer expandidas en los puntos en que se invocan; sin embargo slo aparecern los
comentarios normales que comiencen por (;). Los comentarios relacionados con el funcionamiento
interno de la macro deberan ir con (;;), los relativos al uso y sintaxis de la misma con (;). Esto es
adems conveniente porque durante el ensamblaje son mantenidos en memoria los comentarios de
macros (no los del resto del programa) que comienzan por (;), y no conviene desperdiciar
memoria...
* Operador &
Utilizado para concatenar texto o smbolos. Es necesario para lograr que el ensamblador
sustituya un parmetro dentro de una cadena de caracteres o como parte de un smbolo:
SALUDO MACRO c
MOV AL,"&c"
etiqueta&c: CALL imprimir
ENDM
Cuando se utilizan estructuras repetitivas REPT, IRP o IRPC (que se ver n m s adelante)
existe un problema adicional al intentar crear etiquetas, ya que el ensamblador se come un & al
hacer la primera sustitucin, generando la misma etiqueta a menos que se duplique el operador &:
MEMORIA MACRO x
IRP i, <1, 2>
x&i DB i
ENDM
ENDM
* Operador ! o <>
Empleado para indicar que el carcter que viene a continuacin debe ser interpretado
literalmente y no como un smbolo. Por ello, !; es equivalente a <;>.
* Operador %
Convierte la expresin que le sigue -generalmente un smbolo- a un nmero; la
expresin debe ser una constante (no relocalizable). Slo se emplea en los argumentos de macros.
Dada la macro siguiente:
PSUM MACRO mensaje, suma
%OUT * mensaje, suma *
ENDM
* IRP simbolo_control, <arg1, arg2, ..., arg_n> ... ENDM (Indefinite repeat)
Es relativamente similar a la instruccin FOR de los lenguajes de alto nivel. Los ngulos
(<) y (>) son obligatorios. El smbolo de control va tomando sucesivamente los valores (no
necesariamente numricos) arg1, arg2, ... y recorre en cada pasada todo el bloque de instrucciones
hasta alcanzar el ENDM (no confundirlo con fin de macro) sustituyendo simbolo_control por esos
valores en todos los lugares en que aparece:
IRP i, <1,2,3>
DB 0, i, i*i
ENDM
Nota: Todo lo encerrado entre los ngulos se considera un nico parmetro. Un (;) dentro
de los ngulos no se interpreta como el inicio de un comentario sino como un elemento m s. Por
otra parte, al emplear macros anidadas, deben indicarse tantos smbolos angulares '<' y '>'
consecutivos como niveles de anidamiento existan.
Lgicamente, dentro de una macro tambin resulta bastante til la estructura IRP:
TETRAOUT MACRO p1, p2, p3, p4, valor
PUSH AX
PUSH DX
MOV AL,valor
IRP cn, <p1, p2, p3, p4>
MOV DX, cn
OUT DX, AL
ENDM ; fin de IRP
POP DX
POP AX
ENDM ; fin de macro
Cuando se pasan listas como parmetros hay que encerrarlas entre '<' y '>' al llamar, para no
confundirlas con elementos independientes. Por ejemplo, supuesta la macro INCD:
INCD MACRO lista, p
IRP i, <lista>
INC i
ENDM ; fin de IRP
DEC p
ENDM ; fin de macro
* IRPC simbolo_control, <c1c2 ... cn> ... ENDM (Indefinite repeat character)
Esta directiva es similar a la anterior, con una salvedad: los elementos situados entre los
ngulos (<) y (>) -ahora opcionales, por cierto- son caracteres ASCII y no van separados por
comas:
IRPC i, <813>
DB i
ENDM
Ejemplo de utilizacin dentro de una macro (en combinacin con el operador &):
INICIALIZA MACRO a, b, c, d
IRPC iter, <&a&b&c&d>
DB iter
ENDM ; fin de IRPC
ENDM ; fin de macro
Se expandir en:
PUSH AX
PUSH AX
PUSH DS
PUSH ES
PUSH VAR1
- Una sola entrada y salida en cada mdulo: un mdulo slo debe llamar al inicio de otro
(con CALL) y ste debe retornar al final con un nico RET, no debiendo existir ms puntos de
salida y no siendo recomendable alterar la direccin de retorno.
- Excepto en los puntos en que la velocidad o la memoria son crticas (la experiencia
demuestra que son menos del 1%) debe codificarse el programa con claridad, si es preciso
perdiendo eficiencia. Ese 1% documentarlo profusamente como se hara para que lo lea otra
persona.
- Los mdulos han de ser cajas negras y no deben modificar el entorno exterior. Esto
significa que no deben actuar sobre variables globales ni modificar los registros (excepto aquellos
registros y variables en que devuelven los resultados, lo que debe documentarse claramente al
principio del mdulo). Tampoco deben depender de ejecuciones anteriores, salvo excepciones en
que la propia claridad del programa obligue a lo contrario (por ejemplo, los generadores de
nmeros aleatorios pueden depender de la llamada anterior).
Para el paso de parmetros entre mdulos existen varios mtodos que se exponen a
continuacin. Los parmetros pueden pasarse adems de dos maneras: directamente por valor,
o bien indirectamente por referencia o direccin. En el primer caso se enva el valor del
parmetro y en el segundo la direccin inicial de memoria a partir de la que est almacenado. El
tipo de los parmetros habr de estar debidamente documentado al principio de los mdulos.
- Paso de parmetros en los registros: Los mdulos utilizan ciertos registros muy concretos
para comunicarse. Todos los dems registros han de permanecer inalterados, por lo cual, si son
empleados internamente, han de ser preservados al principio del mdulo y restaurados al final.
Este es el mtodo empleado por el DOS y la BIOS en la mayor a de las ocasiones para
comunicarse con quien los llama. Los registros sern preservados preferiblemente en la pila (con
PUSH) y recuperados de la misma (con POP en orden inverso); de esta manera, los m dulos son
reentrantes y pueden ser llamados de manera mltiple soportando, entre otras caractersticas, la
recursividad (sin embargo, se requerir tambin que las variables locales se generen sobre la
pila).
- Paso de parmetros a travs de un rea comn: se utiliza una zona de memoria para la
comunicacin. Este tipo de mdulos no son reentrantes y hasta que no acaben de procesar una
llamada no se les debe llamar de nuevo en medio de la faena.
- Paso de parmetros por la pila. En este mtodo, los parmetros son apilados antes de
llamar al mdulo que los va a recoger. Este debe conocer el nmero y tamao de los mismos,
para equilibrar el puntero de pila al final antes de retornar (mtodo de los compiladores de
lenguaje Pascal) o en caso contrario el programa que llama deber encargarse de esta operaci n
(lenguaje C). La ventaja del paso de parmetros por la pila es el pr cticamente ilimitado
nmero de parmetros admitido, de cmodo acceso, y que los mdulos siguen siendo
reentrantes. Un ejemplo puede ser el siguiente:
datoL DW ?
datoH DW ?
...
PUSH datoL ; apilar parmetros
PUSH datoH
CALL moduloA ; llamada
ADD SP,4 ; equilibrar pila
...
En el ejemplo, tenemos la variable dato de 32 bits dividida en dos partes de 16. Dicha variable es
colocada en la pila empezando por la parte menos significativa. A continuacin se llama a
MODULOA, el cual comienza por preservar BP (lo usar posteriormente) para respetar la norma
de caja negra. Se carga BP con SP debido a que el 8086 no permite el direccionamiento indexado
sobre SP. Como la instruccin CALL se dirige a una direcci n cercana (NEAR), en la pila se
almacena slo el registro IP. Por tanto, en [BP+0] est el BP del programa que llama, en [BP+2]
el registro IP del programa que llama y en [BP+4] y [BP+6] la variable enviada, que es el caso
ms complejo (variables de 32 bits). Dicha variable es cargada en DX:AX antes de proceder a
usarla (tambin deberan apilarse AX y DX para conservar la estructura de caja negra). Al final,
se retorna con RET y el programa principal equilibra la pila aumentando SP en 4 unidades para
compensar el apilamiento previo de dos palabras antes de llamar. Si MODULOA fuera un
procedimiento lejano (FAR) la variable estara en [BP+6] y [BP+8], debido a que al llamar al
mdulo se habra guardado tambin en la pila el CS del programa que llama. El lenguaje Pascal
hubiera retornado con RET 4, haciendo innecesario que el programa que llama equilibre la pila. Sin
embargo, el mtodo del lenguaje C expuesto es ms eficiente porque no requiere que el m dulo
llamado conozca el nmero de parmetros que se le envan: ste puede ser variable (de hecho,
el C apila los parmetros antes de llamar en orden inverso, empezando por el ltimo: de esta
manera se accede correctamente a los primeros N parmetros que se necesiten).
Captulo VI: EL ENSAMBLADOR EN ENTORNO DOS
Antes de que el COMMAND.COM pase el control al programa que se pretende ejecutar, se crea
un bloque de 256 bytes llamado PSP (Program Segment Prefix), cuya descripcin detallada se
ver en el prximo captulo. En l aparecen datos tales como la direccin de retorno al dos
cuando finalice el programa, la direccin de retorno en caso de Ctrl-Break y en caso de errores
crticos. Adems de la cantidad de memoria disponible y los posibles parmetros suministrados
del programa. Cuando el programa toma el control, DS y ES apuntan al PSP. Tipos de programas:
Si el programa es COM podemos terminarlo con la interrupcin 20h (INT 20h), o simplemente
con un RET si la pila no est desequilibrada (apunta a un INT 20h que hay en la posici n 0 del
PSP); otra manera de acabar es por medio de la funcin 4Ch del sistema (disponible desde el DOS
2.0) que acaba cualquier programa sin problemas y sin ningn tipo de requerimientos adicionales,
tanto COM como EXE.
Los programas de tipo COM se cargan en memoria tal y como estn en disco, entregndoseles
el control. Los de tipo EXE, que pueden llegar a manejar mltiples segmentos de c digo de hasta
64 Kb, se almacenan en disco semiensamblados. En realidad, al ser cargados en memoria, el
DOS tiene que realizar la ltima fase de montaje, calculando las direcciones de memoria
absolutas. Por ello, estos programas tienen un formato especial en disco, generado por los
ensambladores y compiladores, y su imagen en memoria no se corresponde realmente con lo que
est grabado en el disco, aunque esto al usuario no le importe. Por ello, no se extra e el lector de
haber visto alguna vez ficheros EXE de ms de 640 Kb: evidentemente, no se cargan enteros en
memoria aunque lo parezca. Los programas COM no hacen referencias a datos o direcciones
separados ms de 64 Kb, por lo que todos los saltos y desplazamientos son relativos a los registros
de segmento (no se cambia CS ni DS) con lo que no es necesaria la fase de montaje. No obstante,
un programa COM puede hacer lo que le de la gana con los registros de segmento y acceder a m s
de 64 Kb de memoria, por cuenta y riesgo del programador. En general, la programacin en
ensamblador est hoy en da relegada a pequeos programas residentes, controladores de
dispositivos o rutinas de apoyo a programas hechos en otros lenguajes, por lo que no es
estrictamente necesario trabajar con programas EXE realizados en ensamblador. Salvo excepciones,
la mayora de los programas desarrollados en este libro sern de tipo COM ya que los EXE
ocuparan algo ms, aunque el ensamblador da algo ms de comodidad al programador en los
mismos.
6.2. - EJEMPLO DE PROGRAMA DE TIPO COM.
El siguiente ejemplo escribe una cadena en pantalla llamando a uno de los servicios estndar de
impresin del DOS (funcin 9 de INT 21h):
cr EQU 13 ; constante de retorno de carro
lf EQU 10 ; constante de salto de lnea
Olvidndonos de los comentarios que comienzan por ;, en las primeras lineas las directivas
EQU definen dos constantes para el preprocesador del compilador: cr=13 y lf=10. El programa, de
tipo COM, consta de un nico segmento. La directiva ASSUME indica que, por defecto, las
instrucciones mquina se ensamblarn para el registro CS en este segmento (lo m s l gico,
por otra parte); tambin conviene asumir el registro DS, de lo contrario, si hubiera que acceder a
una variable, el ensamblador aadira el prefijo del segmento CS a la instruccin al no estar
seguro de que DS apunta a los datos, consumiendo ms memoria. Se pueden aadir los dem s
registros de segmento en el ASSUME, aunque es redundante. El ORG 100h es obligatorio en
programas COM, ya que estos programas sern cargados en memoria en la posicin CS:100h. Al
final, la direccin del texto a imprimir se coloca en DS:DX (CS=DS=ES=SS en un programa
COM recin ejecutado) y se llama al DOS. El carcter '$' delimita la cadena a imprimir, lo cual
es una herencia del CP/M (sera ms interesante que fuera el 0 el delimitador) por razones
histricas. Se acaba el programa con INT 20h. El punto de arranque es indicado con la directiva
END, aunque en realidad en los programas COM el punto indicado (en el ejemplo, inicio) debe
estar forzosamente al principio del programa. Obsrvese que no se genera cdigo hasta llegar a la
lnea inicio:, todo lo anterior son directivas.
Los programas EXE (listado al final de esta seccin) requieren algo ms de elaboracin. En
primer lugar, es necesario definir una pila y reservar espacio para la misma. Al contrario que los
programas COM (cuya pila se sita al final del segmento compartido tambin con el c digo y
los datos) esta caracterstica obliga a definir un tamao prudente en funcin de las necesidades
del programa. Tngase en cuenta que en la pila se almacenan las direcciones de retorno de las
subrutinas y al llamar a una funcin de la BIOS la pila es usada con intensidad. En general, con
medio kilobyte basta para programas tan sencillos como el del ejemplo, e incluso para otros mucho
ms complejos. El lmite mximo est en 64 Kb. El segmento de pila se nombra siempre
STACK y con el TLINK de Borland es necesario indicar tambin la clase 'STACK'.
Como se ve, son definidos por separado el segmento de cdigo, pila y datos, lo que tambi n
ayuda a estructurar ms el programa. El segmento de cdigo se define como procedimiento FAR,
entre otras razones para que el ensamblador ensamble el RET del final (con el que se vuelve al
DOS) como un RETF. La directiva ASSUME asocia cada registro de segmento con su
correspondiente segmento. Como puede observarse al principio del programa, es necesario preparar
a mano la direccin de retorno al sistema. El PUSH DS del principio coloca el segmento del
PSP en la pila; el XOR AX,AX coloca un cero en AX (esta instrucci n gasta un byte menos que
MOV AX,0) y el PUSH AX mete ese 0 en la pila. Con ello, al volver al DOS con RET (RETF en
realidad) el control pasar a DS:0, esto es, a la primera instruccin del PSP (INT 20h). Aunque
pueda parecer un tanto lioso, es un juego de nios y estas tres instrucciones consecutivas (PUSH
DS / XOR AX,AX / PUSH AX) son la manera de empezar de cientos de programas EXE, que
despus acaban con RET. En general, a partir del DOS 2.0 es m s aconsejable terminar el
programa con la funcin 4Ch del DOS, que no requiere que CS apunte al PSP ni precisa de
preparacin alguna en la pila y adems permite retornar un cdigo de ERRORLEVEL en AL:
en los programas futuros esto se har con bastante frecuencia.
Tambin debe observarse cmo se inicializa DS, ya que en los programas EXE por defecto no
apunta a los datos. Ahora puede preguntarse el lector, por curiosidad, qu valdr datos?:
datos tiene un valor relativo asignado por el ensamblador; cuando el programa sea cargado en
memoria, en el proceso de montaje y en funcin de cul sea la primera posicin de memoria
libre, se le asignar un valor determinado por el montador del sistema operativo.
cr EQU 13
lf EQU 10
; Segmento de datos
datos SEGMENT
texto DB cr,lf,"Texto a imprimir",cr,lf,"$"
datos ENDS
; Segmento de pila
; Segmento de cdigo
codigo SEGMENT
ejemplo PROC FAR
ASSUME CS:codigo, DS:datos, SS:pila
; escribir texto
; volver al DOS
ejemplo ENDP
6.4.1. - TASM/MASM.
Es el programa que convierte nuestro listado fuente en cdigo objeto, es decir, lenguaje
mquina en el que slo faltan las referencias a rutinas externas. Permite la obtenci n de listados
de cdigo y de referencias cruzadas (smbolos, etiquetas, variables). En general, bastar con
hacer TASM nombre_programa (se supone la extensin .ASM por defecto). El fichero final tiene
extensin OBJ. En general, la sintaxis del TASM y MASM es ms o menos equivalente: en el
primero se obtiene ayuda con /H y en el segundo con /HELP. Con TASM, cuando se va a obtener la
versin definitiva del programa, o si ste es corto -o el ordenador rpido- merece la pena
utilizar el parmetro /m3, con objeto de que de dos/tres pasadas y optimize m s el c digo. Por
su lado, MASM presenta estadsticas adicionales si se indica /v y se puede cambiar con
/Btamao el n de Kb de memoria que destina al fichero fuente, entre 1 y 63. La sintaxis es
(tanto para TASM como MASM):
TASM fichero_fuente, fichero_listado, fichero_referencias_cruzadas
Se puede omitir el fichero de listado y el de referencias cruzadas. Cuando se emplea MASM 6.X,
para ensamblar los listados de este libro hay que indicar la opcin /Zm para mantener la
compatibilidad con las versiones anteriores del ensamblador, siendo adems obligatorio indicar la
extensin; como se genera directamente el fichero EXE hay que indicar /c si se desea evitar esto
(si no se quiere que linke). La sintaxis quedara:
ML /Zm fihero_fuente.asm
A continuacin se listan los parmetros comunes a TASM 2.0 (y posterior) y MASM 4.0/5.0 ( NO
la 6.X):
/a y /s Seleccionan un orden alfabtico o secuencial de los segmentos.
Genera un listado de referencias cruzadas en un fichero de extensin CRF listo para ser
procesado por CREF (MASM) aadiendo adems nmeros de lnea al listado, o bien
/c incluye el listado de referencias cruzadas directamente dentro del listado del programa (caso
de TASM). Las referencias cruzadas son un listado de todos los smbolos del programa,
indicando los nmeros de lnea del mismo en que son definidos y referenciados.
De la manera /Dsmbolo[=valor] permite crear el smbolo indicado, cuya presencia
puede comprobarse en el programa con una directiva IF (es til para definir externamente
un smbolo que indique que el programa est en fase de depuracin, de cara a
/D ensamblar cierto cdigo adicional). Aunque /d (en minsculas) es un obsoleto parmetro
de MASM para obtener un listado de la primera pasada del ensamblador, MASM 4.0 es
capaz de darse cuenta de que se pretende definir un smbolo con /d a menos que se indique
solo /d.
Emula las instrucciones de punto flotante del 80x87, apoyndose en una librera al
/e
efecto.
Permite indicar el directorio donde el ensamblador debe de buscar los ficheros indicados en
/Iruta
el programa fuente con INCLUDE.
/l[a] Con /l se genera un listado de ensamblaje y con /la un listado expandido.
Con /m se indica el nivel de preservacin del sentido de maysculas y minsculas en los
smbolos: /ml hace que se consideres diferentes maysculas de minsculas en todos los
smbolos, /mx slo con los smbolos globales y /mu hace que se mayusculicen todos
/m
los smbolos globales. Al ensamblar mdulos para usar desde lenguaje C hay que indicar
por lo menos /mx. En MASM 6.X se emplea /Cx en lugar de /mx, /Cp en lugar de /ml y /Cu
en vez de /mu.
/n Suprime las tablas de smbolos en el listado.
Verifica que el cdigo generado para el modo protegido es correcto (al emplear la directiva
/p
para generar instrucciones de modo protegido).
/t Suprime los mensajes si el ensamblaje es correcto.
/w Indica el nivel de advertencias: /w0 ninguna, /w1 slo las serias y /w2 slo consejos.
/X Lista las condiciones falsas (ensamblaje condicional).
/z Visualiza la lnea del error y no slo el nmero de la misma.
/Zi Genera informacin simblica para los depuradores de cdigo.
/Zd Incluye slo la informacin del nmero de lnea.
6.4.2. - TLINK/LINK.
El montador o linkador permite combinar varios mdulos objeto, realizando las conexiones
entre ellos y, finalmente, los convierte en mdulo ejecutable de tipo EXE (empleando el ML de
MASM 6.X se obtiene directamente el fichero EXE ya que invoca automticamente al linkador).
El linkador permite el uso de libreras de funciones y rutinas. TLINK, a diferencia de LINK,
permite generar un fichero de tipo COM directamente de un OBJ si se indica el par metro /t, lo
que agiliza an ms el proceso. Puede obtenerse ayuda ejecutndolo sin parmetros. Los
parmetros de TLINK son sensibles a maysculas y minsculas, por lo que /T no es lo mismo
que /t. Con LINK se obtiene ayuda indicando /HELP. Aunque los parmetros de uno y otro son
bastante distintos, la sintaxis genrica de ambos es:
TLINK fich_obj(s), fich_exe, fich_map, fich_libreria, fich_def
Los ficheros no necesarios se pueden omitir (o indicar NUL): para linkar el fichero prog1.obj y
el prog2.obj con la librera math.lib generando PROG1.EXE basta con ejecutar TLINK
prog1+prog2,,,math. Alternativamente se puede indicar TLINK @fichero para que tome los
parmetros del fichero de texto FICHERO, en el caso de que estos sean demasiados y sea
incmodo teclearlos cada vez que se linka. Los ficheros de texto de extensin MAP contienen
informacin til para el programador sobre la distribucin de memoria de los segmentos.
6.4.3. - EXE2BIN.
Los ficheros EXE generados por TLINK o LINK no son copia exacta de lo que aparece en la
memoria, sino que el DOS -tras cargarlos- debe realizar una ltima operacin de montaje. Un
programa COM en memoria es una copia del fichero del disco, es algo ms corto y ms sencillo
de desensamblar. Al contrario de lo que algunos opinaron en su da, el tiempo ha demostrado que
nunca llegaran a ser directamente compatibles con los actuales entornos multitarea.
EXE2BIN permite transformar un fichero EXE en COM siempre que el m dulo ocupe menos
de 64K y que est ensamblado con ORG 100h. Si no se indic el par metro /t en TLINK, ser
necesario este programa (al igual que cuando se utiliza LINK). Cuando se crean programas SYS
(que se diferencian de los COM bsicamente en que no tienen ORG 100h) no se puede ejecutar
TLINK /t, por lo que es necesaria la ayuda de EXE2BIN para convertir el programa EXE en SYS.
Sintaxis:
EXE2BIN fich.exe (a veces hay que indicar EXE2BIN fich.exe fich.com)
Si el programa no contiene ORG 100h, EXE2BIN genera un fichero binario puro de extensi n
BIN. Si adems existen referencias absolutas a segmentos, EXE2BIN preguntar el segmento en
que va a correr (algunas versiones permiten indicarlo de la manera /Ssegmento): esto permite
generar cdigo para ser ejecutado en un segmento determinado de la memoria (como pueda ser
una memoria EPROM o ROM).
6.4.4. - TLIB/LIB.
El gestor de libreras permite reunir mdulos objeto en un nico fichero para poder tomar de
l las rutinas que se necesiten en cada caso. En este libro no se desarrollan programas tan
complejos que justifiquen su utilizacin. En cualquier caso, la sintaxis es la siguiente:
TLIB fichero_libreria comandos, fichero_listado
Si no se indican comandos se obtiene simplemente informacin del contenido de la librera en
el fichero de listado (que puede ser CON para listado por pantalla). Los comandos son de la forma
<simbolo>nombre_de_mdulo y pueden ser los siguientes:
+ aade el mdulo objeto indicado a la librera
- borra el mdulo indicado de la librera
* saca el mdulo de la librera sin borrarlo (extrae fichero OBJ)
-+ alternativamente +-, reemplaza el mdulo existente en la librera
-* alternativamente *-, extrae el mdulo de la librera y lo borra de
ella
6.4.5. TCREF/CREF.
Esta utilidad genera listados en orden alfabtico de los smbolos, como ayuda a la
depuracin. Con el MASM la opcin /c crea un fichero de referencias cruzadas de extensi n
CRF (respondiendo afirmativamente cuando pregunta por el mismo o indicndolo
explcitamente en la lnea de comandos); la opcin /c de TASM lo incluye en el listado, aunque
si se indica el nombre del fichero de referencias cruzadas genera un fichero de extensin XRF.
CREF y TCREF interpretan respectivamente los ficheros CRF y XRF generando un fichero de texto
con extensin REF que contiene el listado de referencias cruzadas. Ej.:
TASM fichero,,,fichero
TCREF fichero
Las referencias cruzadas son un listado de todos los smbolos del programa, indicando los
nmeros de lnea del mismo en que son referenciados (la lnea en que son definidos se marca
con #); estos nmeros de lnea son relativos al listado de ensamblaje del programa (y no al
fichero fuente). Es til para depurar programas grandes y complejos.
6.4.6. - MAKE.
Esta utilidad se apoya en unos ficheros especiales, al estilo de los BAT del DOS, de cara a
automatizar el proceso de ensamblaje. Slo es recomendable para programas grandes, divididos en
mdulos, en los que MAKE chequea la fecha y hora para ensamblar slo las partes que hayan
sido modificadas.
La utilidad DEBUG includa en los sistemas MS-DOS, es una herramienta para depuraci n
de programas muy interesante que permite desensamblar los mdulos y, adems, ejecutar
programas paso a paso, viendo las modificaciones que sufren los registros y banderas. Se trata de un
programa menos complejo, cmodo y potente que depuradores de cdigo como Turbo Debugger
(de Borland) o Codeview (Microsoft), pero en algunos casos es ms til. Veremos ahora los
principales comandos del DEBUG, los cuales tambin son admitidos en su mayor a por
Codeview, por lo que el tiempo invertido en aprenderlos ser til no slo para conocer el
clsico y mtico DEBUG.
Antes de empezar con ellos, conviene hacer referencia al programa SYMDEB que acompa a al
MASM de Microsoft: se trata de un DEBUG mejorado, con ayuda, m s r pido e inteligente
(indica el tipo de funcin del sistema cuando al tracear un programa ste llama al DOS) y, en la
prctica, es 99% compatible. Tambin admite las instrucciones adicionales del 286 y los NEC
V20/V30. Su diferencia principal es que al abandonarlo para volver al DOS restaura los vectores de
interrupcin, lo que puede no ser deseable en algunos casos muy concretos. Adems, desde la
versin 4.0 se admite el parmetro /S (con SYMDEB /S nomfich.ext) lo que permite conmutar
entre la pantalla de depuracin y la de ejecucin pulsando la tecla '\'.
Los programas pueden ser de tipo EXE o COM; en el caso de los primeros se les cargar ya
montados y con los registros inicializados, listos para su ejecucin. Evidentemente, los programas
COM tambin se cargan con los registros inicializados y el correspondiente PSP preparado, as
como con IP=100h. Los parmetros opcionales no son los de el DEBUG o SYMDEB sino los que
normalmente se suministraran al programa a depurar. Tambin se pueden cargar otros ficheros
de cualquier extensin o simplemente entrar en el programa sin cargar ningn fichero. Al entrar,
aparecer el prompt particular del DEBUG: un guin (-). Entonces se pueden teclear rdenes
que constarn generalmente de una sola letra. La mayora de las mismas admiten par metros,
que normalmente irn separados por comas. Estos parmetos pueden ser nmeros
hexadecimales de hasta dos o cuatro dgitos, registros y, adems:
- Cadenas de caracteres: Encerradas entre comillas simples o dobles. El texto puede a su vez
encerrar fragmentos entrecomillados, empleando comillas distintas a las ms exteriores. Ejemplo:
La cadena 'ES:' no ser bien traducida a sus correspondientes valores ASCII. Con DEBUG
este problema no existe.
- Rangos: Son dos direcciones separadas por una coma; o bien una direccin, la letra 'L' y un
valor numrico que indica el nmero de bytes a partir de la direccin.
El DEBUG del MS-DOS 5.0 y el SYMDEB poseen una ayuda invocable con el comando ?, en la
que se resumen las principales rdenes. A continuacin se listan las ms interesantes:
Los saltos inter-segmento deben especificarse como FAR (ej., CALL FAR [100]) a no ser que
sea evidente que lo son (ej. CALL 1234:5678).
* E <direccin> [<lista>] (enter): permite consultar y modificar la memoria, byte a byte. Por
ejemplo, con E 230 1,2,3 se introduciran los bytes 1, 2 y 3 a partir de DS:230. Si no se indica
<lista>, se visualizar la memoria byte a byte, pudindose modificar los bytes deseados, avanzar
al siguiente (barra espaciadora) o retroceder al anterior (signo -). Para acabar se pulsa RETURN.
* R [<registro>] (register): permite visualizar y modificar el valor de los registros. Por ejemplo,
si se ejecuta la orden 'rip', se solicitar un nuevo valor para IP; con RF se muestran los flags y se
permite modificar alguno:
Flag Activo Borrado
Desbordamiento OV NV
Direccin DN (v) UP (^)
Interrupcin EI DI
Signo NG (<0) PL (>0)
Cero ZR (=0) NZ (!=0)
Acarreo auxiliar AC NA
Paridad PE (par) PO (impar)
Acarreo CY NC
* G [=<direccin> [,<direccin>,...]] (go): ejecuta cdigo desde CS:IP (a menos que se
indique una direccin concreta). Si se trabaja sobre memoria ROM no debe indicarse la segunda
direccin. Para que el flujo del programa se detenga en la 2 direccin o posteriores debe pasar
necesariamente por ella(s). Se puede indicar hasta 10 direcciones donde debe detenerse.
* T [<veces>] (trace): ejecuta una instruccin del programa (a partir de CS:IP) mostrando a
continuacin el estado de los registros y la siguiente instruccin. Ejecutar T10 equivaldra a
ejecutar 16 veces el comando T. Si la instruccin es CALL o INT, se ejecutar como tal
introducindose en la subrutina o servidor de interrupciones correspondiente (SYMDEB no entra
en los INT 21h).
* S <rango> <lista> (search): busca una cadena de bytes por la memoria. Para buscar la cadena
"PEPE" terminada por cero en un rea de 512 bytes desde DS:100 se har a: S 100 L 200
"PEPE",0 (por defecto se busca en DS:). No se encontrara sin embargo "pepe" (en min sculas).
* F <rango> <lista> (fill): llena la zona de memoria especificada con repeticiones de la lista de
bytes indicada. Por ejemplo, para rellenar cdigos 0AAh 100h bytes a partir de 9800h:0 se
ejecutara F 9800:0 L 100 AA; en vez de AA se podra haber indicado una lista de bytes o
cadenas de caracteres.
* M <rango> <direccin> (move): Ms que mover, copia una zona de memoria en otra de
manera inteligente (controlando los posibles solapamientos de los bloques).
Con SYMDEB pueden adems colocarse, con suma facilidad, puntos de ruptura (breakpoints);
con DEBUG se pueden implementar con la orden G (indicando ms de una direccin hasta un
mximo de 10, donde debe detenerse el programa si pasa por ellas) aunque es ms inc modo.
En SYMDEB se pueden definir con BP direccin, borrarse con BC num_breakpoint, habilitarse
con BP num_breakpoint (necesario antes de emplearlos), deshabilitarse con BD num_breakpoint y
listar los definidos con BL. Adems, SYMDEB puede visualizar datos en coma flotante de 32, 64
y 80 bits con el comando D (DS, DL y DT).
El cdigo de la BIOS, almacenado en las memorias ROM del ordenador, constituye la primera
capa de software de los ordenadores compatibles. La BIOS accede directamente al hardware,
liberando a los programas de usario de las tareas ms complejas. Parte del cdigo de la BIOS es
actualizado durante el arranque del ordenador, con los ficheros que incluye el sistema operativo. El
sistema operativo o DOS propiamente dicho se instala despus: el DOS no realiza ning n acceso
directo al hardware, en su lugar se apoya en la BIOS, constituyendo una segunda capa de software.
El DOS pone a disposicin de los programas de usuario unas funciones muy evolucionadas para
acceder a los discos y a los recursos del ordenador. Por encima del DOS se suele colocar
habitualmente al COMMAND.COM, aunque realmente el COMMAND no constituye capa alguna
de software: es un simple programa de utilidad, como cualquier otro, ejecutado sobre el DOS y que
adems no pone ninguna funcin a disposicin del sistema (al menos, documentada), su nica
misin es cargar otros programas.
FUNCIONES DE LA BIOS
Las funciones de la BIOS se invocan, desde los programas de usuario, ejecutando una
interrupcin software con un cierto valor inicial en los registros. La BIOS emplea un cierto rango
de interrupciones, cada una encargada de una tarea especfica:
INT 10h: Servicios de Vdeo (texto y grficos).
INT 11h: Informe sobre la configuracin del equipo.
INT 12h: Informe sobre el tamao de la memoria convencional.
INT 13h: Servicios de disco (muy elementales: pistas, sectores, etc.).
INT 14h: Comunicaciones en serie.
INT 15h: Funciones casette (PC) y servicios especiales del sistema (AT).
INT 16h: Servicios de teclado.
INT 17h: Servicios de impresora.
INT 18h: Llamar a la ROM del BASIC (slo mquinas IBM).
INT 19h: Reinicializacin del sistema.
INT 1Ah: Servicios horarios.
INT 1Fh: Apunta a la tabla de los caracteres ASCII 128-255 (8x8 puntos).
La mayora de las interrupciones se invocan solicitando una funcin determinada (que se
indica en el registro AH al llamar) y se limitan a devolver un resultado en ciertos registros,
realizando la tarea solicitada. En general, slo resultan modificados los registros que devuelven
algo, aunque BP es corrompido en los servicios de vdeo de las mquinas ms obsoletas.
El DOS emplea varias interrupciones, al igual que la BIOS; sin embargo, cuando se habla de
funciones del DOS, todo el mundo sobreentiende que se trata de llamar a la INT 21h, la
interrupcin ms importante con diferencia.
INT 20h: Terminar programa (tal vez en desuso).
INT 21h: Servicios del DOS.
INT 22h: Control de finalizacin de programas.
INT 23h: Tratamiento de Ctrl-C.
INT 24h: Tratamiento de errores crticos.
INT 25h: Lectura absoluta de disco (sectores lgicos).
INT 26h: Escritura absoluta en disco (sectores lgicos).
INT 27h: Terminar dejando residente el programa (en desuso).
INT 28h: Idle (ejecutada cuando el ordenador est inactivo).
INT 29h: Impresin rpida en pantalla (no tanto).
INT 2Ah: Red local MS NET.
INT 2Bh-2Dh: Uso interno del DOS.
INT 2Eh: Procesos Batch.
INT 2Fh: Interrupcin Multiplex.
INT 30h-31h: Compatibilidad CP/M-80.
INT 32h: Reservada.
Las funciones del DOS se invocan llamando a la INT 21h e indicando en el registro AH el
nmero de funcin a ejecutar. Slo modifican los registros en que devuelven los resultados,
devolviendo normalmente el acarreo activo cuando se produce un error (con un cdigo de error en
el acumulador). Muchas funciones de los lenguajes de programacin frecuentemente se limitan a
llamar al DOS.
En general, se debe intentar emplear siempre las funciones que requieran la menor versi n
posible del DOS; sin embargo, no es necesario buscar la compatibilidad con el DOS 1.0: esta
versin no soporta subdirectorios, y el sistema de ficheros se basa en el horroroso m todo FCB.
Los FCB ya no estn soportados siquiera en la ventana de compatibilidad DOS de OS/2, siendo
recomendable ignorar su existencia y trabajar con los handles, al estilo del UNIX, que consisten en
unos nmeros que identifican a los ficheros cuando son abiertos. Existen 5 handles predefinidos
permanentemente abiertos: 0 (entrada estndar -teclado-), 1 (salida estndar -pantalla-), 2 (salida
de error estndar -tambin pantalla-), 3 (entrada/salida por puerto serie) y 4 (salida por
impresora): la pantalla, el teclado, etc. pueden ser manejados como simples ficheros.
Las funciones precedidas de un asterisco son empleadas o mencionadas en este libro, y pueden
consultarse en el apndice al efecto al final del mismo.
ENTRADA/SALIDA DE CARACTERES
GESTION DE FICHEROS
MANEJO DE DISCO
0D -- DOS 1+ - DISK
RESET ..........................................................................
REINICIALIZAR EL DISCO
0E -- DOS 1+ - SELECT DEFAULT
DRIVE ......................................................... ESTABLECER
UNIDAD POR DEFECTO
19 -- DOS 1+ - GET CURRENT DEFAULT
DRIVE ............................................. OBTENER LA UNIDAD ACTUAL POR
DEFECTO
1B -- DOS 1+ - GET ALLOCATION INFORMATION FOR DEFAULT DRIVE ........ OBTENER
INFORMACION DE ESPACIO EN EL DISCO POR DEFECTO
1C -- DOS 1+ - GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE .......... OBTENER
INFORMACION DE ESPACIO EN EL DISCO INDICADO
2E -- DOS 1+ - SET VERIFY
FLAG ..................................................... ESTABLECER EL
BANDERIN DE VERIFICACION
*36 -- DOS 2+ - GET FREE DISK
SPACE ...................................................... OBTENER EL ESPACIO
LIBRE EN DISCO
54 -- DOS 2+ - GET VERIFY
FLAG ........................................................ OBTENER EL
BANDERIN DE VERIFICACION
CONTROL DE PROCESOS
00 -- DOS 1+ - TERMINATE
PROGRAM ........................................................................
TERMINAR PROGRAMA
26 -- DOS 1+ - CREATE NEW PROGRAM SEGMENT
PREFIX ................................................................ CREAR
PSP
*31 -- DOS 2+ - TERMINATE AND STAY
RESIDENT ................................................ TERMINAR Y PERMANECER
RESIDENTE
*4B -- DOS 2+ - "EXEC" - LOAD AND/OR EXECUTE
PROGRAM .......................................... CARGAR Y/O EJECUTAR PROGRAMA
*4C -- DOS 2+ - "EXIT" - TERMINATE WITH RETURN
CODE ................................ TERMINAR PROGRAMA CON CODIGO DE RETORNO
4D -- DOS 2+ - GET RETURN
CODE .................................................................. OBTENER
CODIGO DE RETORNO
*50 -- DOS 2+ internal - SET CURRENT PROCESS ID (SET PSP
ADDRESS) ...................... ESTABLECER DIRECCION DEL PSP ACTUAL
*51 -- DOS 2+ internal - GET CURRENT PROCESS ID (GET PSP
ADDRESS) ......................... OBTENER DIRECCION DEL PSP ACTUAL
*62 -- DOS 3+ - GET CURRENT PSP
ADDRESS ................................................... OBTENER DIRECCION
DEL PSP ACTUAL
GESTION DE MEMORIA
FUNCIONES MISCELANEAS
Son seales enviadas a la CPU para que termine la ejecucin de la instrucci n en curso y
atienda una peticin determinada, continuando ms tarde con lo que estaba haciendo.
Cada interrupcin lleva asociado un nmero que identifica el tipo de servicio a realizar. A
partir de dicho nmero se calcula la direccin de la rutina que lo atiende y cuando se retorna se
contina con la instruccin siguiente a la que se estaba ejecutando cuando se produjo la
interrupcin. La forma de calcular la direccin de la rutina es multiplicar por cuatro el valor de la
interrupcin para obtener un desplazamiento y, sobre el segmento 0, con dicho desplazamiento, se
leen dos palabras: la primera es el desplazamiento y la segunda el segmento de la rutina deseada.
Por tanto, en el primer kilobyte de memoria fsica del sistema, existe espacio suficiente para los
256 vectores de interrupcin disponibles.
Interrupciones hardware: Son las generadas por la circuitera del ordenador en respuesta
a algn evento. Las ms importantes son:
INT 8: Se produce con una frecuencia peridica determinada por el canal 0 del chip
temporizador 8253/8254 (en la prctica, unas 18,2 veces por segundo). Como desde
esta interrupcin se invoca a su vez a INT 1Ch -porque as lo dispuso IBM-, es
posible ligar un proceso a INT 1Ch para que se ejecute peridicamente.
INT 9: generada al pulsar o soltar una tecla.
INT 0Ah, 0Bh, 0Ch, 0Dh, 0Eh, 0Fh: Puertos serie, impresora y controladores de
disquete.
INT 70h, 71h, 72h, 73h, 74h, 75h, 76h, 77h: Generadas en los AT y mquinas
superiores por el segundo chip controlador de interrupciones.
2. El ps: es menos seguro y compatible (ningn programa que emplea esta tcnica
corre en OS/2) y consiste en hacer casi lo que hace el DOS pero sin llamarle. Es adem s
mucho ms incmodo y largo, pero muy usado por programadores despistados:
MOV BL,vector*4 ; vector a cambiar en BL
MOV BH,0 ; ahora en BX
MOV AX,0
PUSH DS ; preservar DS
MOV DS,AX ; apuntar al segmento 0000
LEA DX,rutina ; CS:DX nueva rutina de gestin
CLI ; evitar posible interrupcin
MOV [BX],DX ; cambiar vector (offset)
MOV [BX+2],CS ; cambiar vector (segmento)
STI ; permitir interrupciones
POP DS ; restaurar DS
Los puertos de entrada y salida (E/S) permiten a la CPU comunicarse con los perif ricos. Los
80x86 utilizan los buses de direcciones y datos ordinarios para acceder a los perif ricos, pero
habilitando una lnea que distinga el acceso a los mismos de un acceso convencional a la memoria
(si no existieran los puertos de entrada y salida, los perifricos deberan interceptar el acceso a la
memoria y estar colocados en algn rea de la misma). Para acceder a los puertos E/S se emplean
las instrucciones IN y OUT. Vase el apndice IV.
Cuando la pantalla est en modo de texto, si est activo un adaptador de vdeo monocromo,
ocupa 4 Kb a partir del segmento 0B000h. Con un adaptador de color, son 16 Kb a partir del
segmento 0B800h. Un mtodo para averiguar el tipo de adaptador de vdeo es consultar a la
BIOS el modo de vdeo activo: ser 7 para un adaptador monocromo (tanto MDA como la EGA
y VGA si el usuario las configura as) y un valor entre 0 y 4 para un adaptador de color. Los
modos 0 y 1 son de 40 columnas y el 2 y 3 de 80. Los modos 0 y 2 son de color suprimido ,
aunque en muchos monitores salen tambin en color (y no en tonos de gris). Cada carcter en la
pantalla (empezando por arriba a la izquierda) ocupa dos bytes consecutivos: en el primero se
almacena el cdigo ASCII del carcter a visualizar y en el segundo los atributos de color.
Obviamente, en un modo de 80x25 se utilizan 4000 bytes (los 96 restantes hasta los 4096 de los 4
Kb se desprecian). En los adaptadores de color, como hay 16 Kb de memoria para texto, se pueden
definir entre 4 pginas de texto (80 columnas) y 8 (40 columnas). La pgina activa puede
consultarse tambin llamando a la BIOS, con objeto de conocer el segmento real donde empieza la
pantalla (B800 ms un cierto offset). En el 97,5% de los casos slo se emplea la p gina 0, lo
que no quiere decir que los buenos programas deban asumirla como la nica posible. La BIOS
utiliza la interrupcin 10h para comunicarse con el sistema operativo y los programas de usuario.
El byte de atributos permite definir el color de fondo de los caracteres (0-7) con los bits 4-6, el
de la tinta (0-15) con los bits 0-3 y el parpadeo con el bit 7. La funci n de este ltimo bit puede
ser redefinida para indicar el brillo de los caracteres de fondo (existiendo entonces tambin 16
colores de fondo), aunque en CGA es preciso para ello un acceso directo al hardware. En el
adaptador monocromo, y para la tinta, el color 0 es el negro; el 1 es subrayado normal , del 1 al
7 son colores normales; el 8 es negro, el 9 es subrayado brillante y del 10 al 15 son
brillantes. Para el papel todos los colores son negros menos el 7 (blanco), no obstante para
escribir en vdeo inverso es necesario no slo papel 7 sino adems tinta 0 (al menos, en los
autnticos adaptadores monocromos). El bit 7 siempre provoca parpadeo en este adaptador. En el
adaptador de color no se pueden subrayar caracteres con los cdigos de color (aunque s en la
EGA y VGA empleando otros mtodos). Tabla de colores:
0 - Negro 4 - Rojo 8 - Gris 12 - Rojo claro
1 - Azul 5 - Magenta 9 - Azul claro 13 - Magenta claro
2 - Verde 6 - Marrn 10 - Verde claro 14 - Amarillo
3 - Cian 7 - Blanco 11 - Cian claro 15 - Blanco brillante
Conviene tener cuidado con la tinta azul (1 y 9) ya que, en estos colores, los adaptadores
monocromos subrayan -lo que puede ser un efecto indeseable-. Cuando se llama al DOS para
imprimir, ste invoca a su vez a la BIOS, por lo que la escritura puede ser acelerada llamando
directamente a este ltimo, que adems permite escribir en color. De todas maneras, lo mejor en
programas de calidad es escribir directamente sobre la memoria de pantalla para obtener una
velocidad mxima, aunque con ciertas precauciones -para convivir mejor con entornos pseudo-
multitarea y CGA's con nieve-.
Las pantallas de 132 columnas no son estndar y varan de unas tarjetas grficas a otras, por
lo que no las trataremos. Lo que s se puede hacer -con cualquier EGA y VGA- es llamar a la
BIOS para que cargue el juego de caracteres 8x8, lo que provoca un aumento del n mero de
lneas a 43 (EGA) o 50 (VGA), as como un lgico aumento de la memoria de v deo
requerida (que como siempre, empieza en 0B800h).
En las variables de la BIOS (apndice III) los bytes 49h-66h estn destinados a controlar la
pantalla; su consulta puede ser interesante, como demostrar este ejemplo: el siguiente programa
comprueba el tipo de pantalla, para determinar su segmento, llamando a la BIOS (v ase el
apndice de las funciones del DOS y de la BIOS). Si no es una pantalla de texto estndar no
realiza nada; en caso contrario la recorre y convierte todos sus caracteres a maysculas, sin alterar
el color:
mays SEGMENT
ASSUME CS:mays, DS:mays
ORG 100h ; programa .COM ordinario
inicio:
MOV AH,15 ; funcin para obtener modo de vdeo
INT 10h ; llamar a la BIOS
MOV BX,0B000h ; segmento de pantalla monocroma
MOV CX,2000 ; tamao (caracteres) de la pantalla
CMP AL,7 ; es realmente modo monocromo?
JE datos_ok ; en efecto
MOV BX,0B800h ; segmento de pantalla de color
CMP AL,3 ; es modo de texto de 80 columnas?
JE pant_color ; en efecto
CMP AL,2 ; es modo de texto de 80 columnas?
JE pant_color ; en efecto
MOV CX,1000 ; tamao (caract.) pantalla 40 col.
CMP AL,1 ; es modo texto de 40 columnas?
JBE pant_color ; as es
MOV AL,1 ; pantalla grfica o desconocida:
JMP final ; fin de programa (errorlevel=1)
mays ENDS
END inicio
Las tarjetas grficas son muy distintas entre s a nivel de hardware, por la manera en que
gestionan la memoria de vdeo. Las tarjetas SuperVGA complican an ms el panorama. En
general, un programa que desee aprovechar al mximo el ordenador deber apoyarse en drivers o
subprogramas especficos, uno para cada tarjeta de vdeo del mercado. Esto es as porque
aunque la BIOS del sistema (o el de la tarjeta) soporta una serie de funciones est ndar para
trabajar con grficos, existen bastantes problemas. En primer lugar, su ineficiente diseo lo hace
extremadamente lento para casi cualquier aplicacin seria. Bastara con que las funciones que
implementa la BIOS (pintar y leer puntos de la pantalla) fueran rpidas, slo eso!, para lo que
tan slo hace falta una rutina especfica para cada modo de pantalla, que la BIOS deber a
habilitar nada ms cambiar de modo; casi todas las dems operaciones realizadas sobre la
pantalla se apoyan en esas dos y ello no requerira software adicional para mantener la
compatibilidad entre tarjetas. Sin embargo, los programas comerciales no tienen ms remedio que
incluir sus propias rutinas rpidas para trazar puntos y lneas en drivers apropiados (y de paso
aaden alguna funcin ms compleja). Adems, y por desgracia, no existe NI UNA SOLA
funcin oficial en la BIOS que informe a los programas que se ejecutan de cosas tan elementales
como los modos grficos disponibles (con sus colores, resolucin, etc.); esto no slo es
problemtico en las tarjetas grficas: la anarqua y ausencia de funciones de informaci n
tambin se repite con los discos, el teclado, ... aunque los programadores ya estamos
acostumbrados a realizar la labor del detective para averiguar la informacin que los programas
necesitan. Sin embargo, con los grficos no podemos y nos vemos obligados a preguntar al usuario
qu tarjeta tiene, de cuntos colores y resolucin, en qu modo... y lo que es peor: la
inexistencia de funciones de informacin se agrava con el hecho de que las VGA de los dems
fabricantes hayan asignado de cualquier manera los nmeros de modo. De esta manera, por
ejemplo, una tarjeta Paradise en el modo 5Fh tiene de 640x400 puntos con 256 colores, mientras
que una Trident tiene, en ese mismo modo, 1024x768 con 16 colores. En lo nico que coinciden
todas las tarjetas es en los primeros modos de pantalla, definidos inicialmente por IBM. Muchas
SuperVGA tienen funciones que informan de sus modos, colores y resoluciones, lo que sucede es
que en esto no se han podido poner de acuerdo los fabricantes y la funcin de la BIOS de la VGA
a la que hay que invocar para obtener informacin, difiere de unas tarjetas a otras!.
Afortunadamente, existe un estndar industrial en tarjetas SuperVGA, el estndar VESA, que
aunque ha llegado demasiado tarde, mltiples VGA lo soportan y a las que no, se les puede
aadir soporte con un pequeo driver residente. Hablaremos de l ms tarde.
No conviene seguir adelante sin mencionar antes la tarjeta grfica Hrcules. Se trata de una
tarjeta que apareci en el mercado muy poco despus que la CGA de IBM, con el doble de
resolucin y manteniendo la calidad MDA en modo texto. Esta tarjeta no est soportada por la
BIOS (manufacturada por IBM) y los fabricantes de SuperVGA tampoco se han molestado en
soportarla por software, aunque s por hardware. Est muy extendida en las mquinas antiguas,
pero hoy en da no se utiliza y su programacin obliga a acceder a los puertos de entrada y salida
de manera directa al ms bajo nivel.
La VGA soporta todos los modos grficos estndar de las tarjetas anteriores, resumidos en la
figura 7.4.3.1, si bien los correspondientes a la CGA (320x200 en 4 colores y 640x200 monocromo)
son inservibles para prcticamente cualquier aplicacin grfica actual.
FIGURA 7.4.3.1: MODOS GRFICOS DE VIDEO
Modo (hex) Resolucin Colores Segmento Organizacin Adaptador
4y5 320 x 200 4 B800 entrelazado CGA
6 640 x 200 2 B800 entrelazado CGA
0Dh 320 x 200 16 A000 planos de bit EGA
0Eh 640 x 200 16 A000 planos de bit EGA
0Fh 640 x 350 2 A000 planos de bit EGA
10h 640 x 350 4 A000 planos de bit EGA
10h 640 x 350 16 A000 planos de bit EGA (128K)
11h 640 x 480 2 A000 lineal VGA/MCGA
12h 640 x 480 16 A000 planos de bit VGA
13h 320 x 200 256 A000 lineal VGA/MCGA
La organizacin de la memoria (entrelazado, planos de bit o lineal) es la manera en que se
direcciona la memoria de vdeo por parte de la CPU. Por ejemplo, en el modo 6, cada pixel de la
pantalla est asociado a un bit (8 pixels por byte) a partir de la direccin B800:0000; sin
embargo, cuando se recorren 80 bytes en la memoria (640 bits o pixels, primera lneacompleta)
no se pasa a la segunda lnea de la pantalla sino unas cuantas ms abajo, en una arquitectura
relativamente compleja debida a las limitaciones del hardware de la CGA. Esto ha sido superado en
las siguientes tarjetas, en las que las lneas estn consecutivas de manera lgica en una
organizacin lineal, si bien el lmite de 64 Kb de memoria que puede direccionar en un segmento
el 8086 ha obligado al truco de los planos de bit. Para establecer el modo de vdeo se puede
emplear una funcin del lenguaje de programacin que se trate o bien llamar directamente a la
BIOS, si no se desea emplear la librer a grfica del compilador: la funci n 0 (AH=0) de
servicios de vdeo de la BIOS (INT 10h) establece el modo de v deo solicitado en AL. En Turbo
C sera, por ejemplo:
#include <dos.h>
main()
{
struct REGPACK r;
7.4.3.2 - EL COLOR.
La CGA puede generar 16 colores diferentes, utilizando un solo bit por componente de color
ms un cuarto que indica la intensidad. Sin embargo, la EGA emplea dos bits por cada una de las
tres componentes de color, con lo que obtiene 26=64 colores diferentes. Para asociar estos 64
colores a los no ms de 16 que puede haber en un momento determinado en la pantalla, se
emplean los 16 registros de paleta del controlador de atributos: En cada uno de estos registros, de 6
bits significativos, se definen los 16 colores posibles. La BIOS de la EGA y la VGA carga los
registros de paleta adecuadamente para emular los mismos colores de la CGA. As , por ejemplo,
en los modos de texto el color 0 es el negro y el 15 el blanco brillante, si bien se puede alterar esta
asignacin. Un cambio en un registro de paleta afecta instantneamente a todo el rea de
pantalla pintado de ese color. El valor binario almacenado en los registros de paleta tiene el formato
xxrgbRGB, siendo rgb los bits asociados a las componentes roja, verde y azul de baja intensidad, y
RGB sus homlogos en alta intensidad. As, el valor 010010b se corresponde con el verde ms
brillante.
Los pixels en los modos grficos de 16 colores pueden parpadear, si bien es una tcnica poco
empleada: para ello, basta con cambiar un bit de un registro del controlador de atributos, aunque
existe una funcin de la BIOS que realiza dicha tarea (llamar a la INT 10h con AX=1003h y BX=1
para activar el parpadeo -situacin por defecto en los modos de texto- BX=0 para desactivarlo).
#include <dos.h>
#include <graphics.h>
void main()
{
struct REGPACK r;
int gdrv, gmodo, coderr, i, x, color, pixel;
char paleta[17];
r.r_es=FP_SEG(paleta); r.r_dx=FP_OFF(paleta);
r.r_ax=0x1002; intr (0x10, &r); /* establecer paleta y borde */
getch(); closegraph();
}
Para establecer la paleta se puede llamar a la BIOS (INT 10h) con AX=1002h y ES:DX
apuntando a un buffer de 17 bytes: uno para cada registro de paleta ms otro final para el color del
borde de la pantalla. El Turbo C permite cambiar la paleta con instrucciones de alto nivel; sin
embargo, quienes no deseen aprender las particularidades de cada compilador, siempre pueden
recurrir a la BIOS, que cambiando la paleta es bastante solvente. Echemos un vistazo al ejemplo de
la figura 7.4.3.3 (para ejecutar este programa hay que tener en cuenta que el fichero EGAVGA.BGI
del compilador ha de estar en el directorio de trabajo). Al principio se trazan unas bandas verticales
con la funcin line() que sern coloreadas con los 16 colores por defecto, aunque cambiarn
instantneamente al modificar la paleta. Al definir la paleta, los 4 primeros registros son asignados
con los 4 posibles tonos de rojo, ms bien 3 (el primero es el negro absoluto): rojo, rojooscuro y
rojo brillante. Todos los dems registros y el borde de la pantalla son puestos a 0 (negro) por lo
que en la pantalla quedan visibles slo las tres bandas verticales citadas. El cambio de la paleta es
instantneo, lo que permite hacer efectos especiales. En la VGA, recurdese que los valores de la
paleta son simples punteros al DAC y no los colores reales. Lo que sucede es que los registros del
DAC son inicializados al cambiar el modo de pantalla de tal manera que emulan los colores que se
obtendra en una EGA... a menos que se cambien los valores de dichos registros.
Para ello, nada mejor que llamar de nuevo a la INT 10h con AX=1012h, indicando en BX el
primer elemento del DAC a cambiar (tpicamente 0) y en CX el n mero de elementos a
modificar (a menudo los 256 posibles). Tambin se pasa en ES:DX la direcci n de la tabla de
768 bytes que contiene la informacin: 3 bytes consecutivos para cada elemento del DAC (rojo,
verde y azul) aunque solo son significativos los 6 bits de menor orden de cada byte. Existe
tambin otra funcin bastante interesante, invocable con AX=1013h y que consta de dos
subservicios: el primero se selecciona poniendo un 0 en BL, e indicando en BH si se desean 4
pginas de 64 elementos en el DAC (BH=0) 16 pginas de 16 elementos (BH=1). El segundo
servicio se indica llamando con BL=1, y permite seleccionar la pgina del DAC activa en BH (0-3
0-15, segn cmo est estructurado). Obviamente, esta funcin no est disponible en el
modo 13h de 256 colores, en el que no interviene la paleta (s lo el DAC y entero, no a trocitos).
La figura 7.4.3.4 contiene un nuevo programa completo de demostracin, desarrollado a partir del
anterior, que requiere ya un autntico adaptador VGA. Lo primero que se hace es seleccionar el
modo de 16 pginas en el DAC, estableciendo la pgina 2 como activa (exclusivamente por
antojo mio). Ello significa que se emplearn los elementos 32..47 del DAC (la pgina 0
apuntara a los elementos 0..15, la 1 hubieran sido los elementos 16..31 y as sucesivamente).
Los registros de paleta, simples ndices en el DAC, toman los valores 0,1,...,15 (excepto el 17
byte, color del borde, puesto a 0 para seleccionar el negro). A continuacin, basta programar los
registros 32..47 del DAC con los colores deseados, entre los 262.144 posibles. Como cada
componente puede variar entre 0 y 63, elegimos 16 valores espaciados proporcionalmente (0, 4,
8,..., 60) y los asignamos a las componentes roja y verde (rojo+verde=amarillo), apareciendo en la
pantalla una escala de 16 amarillos (el primero, negro absoluto) de intensidad creciente. Si bien 16
colores son pocos, son suficientes para representar con relativa precisi n algunas imgenes,
especialmente en las que predomina un color determinado (los ficheros grficos se ven
normalmente tan mal en los modos de 16 colores debido a que respetan la paleta de la EGA, en la
VGA sera otra historia).
FIGURA 7.4.3.4:
/*********************************************************************
* EJEMPLO DE CAMBIO DE LA PALETA DE 16 COLORES Y REPROGRAMACION DEL *
* DAC DE LA VGA POR EL BIOS PARA ELEGIR LOS 16 COLORES ENTRE 262.144 *
*********************************************************************/
#include <dos.h>
#include <graphics.h>
void main()
{
struct REGPACK r;
int gdrv, gmodo, coderr, pagina, i, x, color, pixel;
char paleta[17], dac[256][3];
r.r_es=FP_SEG(paleta); r.r_dx=FP_OFF(paleta);
r.r_ax=0x1002; intr (0x10, &r); /* establecer paleta y borde */
getch();
closegraph();
}
Por supuesto, existen ms funciones que stas, entre ellas las que permiten cambiar s lo un
registro de paleta o un elemento del DAC (y no un bloque); sin embargo, son ms lentas cuando se
va a cambiar un conjunto de registros. En cualquier caso, el lector puede consultarlas en el fichero
INTERRUP.LST si lo desea. Tambin existen en la VGA las funciones inversas (obtener paletas y
registros del DAC). El acceso por medio de la BIOS para cambiar la paleta es a menudo m s
cmodo que emplear funciones del lenguaje de programacin y garantiza en ocasiones un mayor
nivel de independencia respecto a la evolucin futura del hardware (aunque si la librera grfica
llama a la BIOS...). Sin embargo, para otras aplicaciones, es mejor no usar la BIOS. Por ejemplo, el
programa de la figura 7.4.3.5 accede directamente a los registros de la VGA para modificar la paleta
en dos bucles, en el primero disminuyendo la luminosidad de la pantalla (hasta dejarla negra) y en
el segundo restaurndola de nuevo. Este efecto cinematogrfico hubiera sido imposible a travs
de la BIOS por razones de velocidad: el acceso directo al hardware, con precauciones (en este caso,
esperar el retrazado vertical para evitar interferencias) es a veces inevitable. El programa de
ejemplo funciona tambin en monitores monocromos, aunque en la prctica slo acte en ellos
sobre la componente verde. El lector deber consultar bibliografa especializada para realizar
este tipo de programacin.
FIGURA 7.4.3.5:
/*********************************************************************
* EFECTO CINEMATOGRAFICO DE DESVANECIMIENTO Y POSTERIOR *
* REAPARICION DE LA PANTALLA CON ACCESO DIRECTO AL HARDWARE VGA. *
*********************************************************************/
#include <dos.h>
void main()
{
unsigned char dac[256][3];
register i, j;
for (i=0; i<256; i++) { /* anotar la paleta activa */
disable();
outportb (0x3C7, i);
dac [i][0] = inportb (0x3C9); /* R */
dac [i][1] = inportb (0x3C9); /* G */
dac [i][2] = inportb (0x3C9); /* B */
enable();
}
/* claridad descendente desde el
64/64-avo al 0/64-avo de intensidad */
for (i=64; i>=0; i--) {
while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */
while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */
for (j=0; j<256; j++) {
disable();
outportb (0x3C8, j);
outportb (0x3C9, dac[j][0]*i >> 6);
outportb (0x3C9, dac[j][1]*i >> 6);
outportb (0x3C9, dac[j][2]*i >> 6);
enable();
}
}
/* claridad ascendente desde el
0/64-avo al 64/64-avo de intensidad */
for (i=0; i<=64; i++) {
while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */
while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */
for (j=0; j<256; j++) {
disable();
outportb (0x3C8, j);
outportb (0x3C9, dac[j][0]*i >> 6);
outportb (0x3C9, dac[j][1]*i >> 6);
outportb (0x3C9, dac[j][2]*i >> 6);
enable();
}
}
}
#include <dos.h>
void main()
{
struct REGPACK r;
char dac[256][3], far *vram;
register x, y;
int i,ii;
Modos de 16 colores.
Para direccionar puntos en los modos de 16 colores, en los que actan interrelacionados los
registros de paleta y el DAC de la manera descrita con anterioridad, es necesario un acceso directo
al hardware por cuestiones de velocidad. Los lectores que no vayan a emplear las funciones del
lenguaje de programacin debern consultar bibliografa especializada en grficos.
Y nada ms.
La nica diferencia de la VGA respecto a la EGA, de hecho, se debe a su peculiar manera de
gestionar el color, as como a la inclusin del modo de 320x200 con 256 colores (el modo de
640x480 es idntico en funcionamiento al de 640x350 de la EGA, solo cambia la altura de la
pantalla). Existe tambin la posibilidad de colocar la VGA en dos modos de 256 colores
alternativos al 13h y basados en el mismo; en uno se alcanzan 320x240 puntos y en el otro
320x400. La bibliografa especializada en grficos explica los pasos a realizar para conseguir
esto, factible en la totalidad de las tarjetas VGA del mercado. Sin embargo, estos modos requieren
un cambio en el modo de direccionamiento de los pixels, que pasa a ser ms complejo -aunque
ms potente para algunas aplicaciones-.
Este programa ejemplo accede a la pantalla empleando las funciones de la BIOS para trazar
puntos (ver apndice sobre funciones de la BIOS). Utiliza el modo CGA de 640x200 puntos,
aunque se puede configurar para cualquier otro modo. El programa dibuja una conocida red en las
cuatro esquinas de la pantalla, trazando lneas. El algoritmo empleado es el de Bresseham con
clculo incremental de puntos (aunque al estar separada la rutina que traza el punto esta
caracterstica no se aprovecha, pero es fcil de implementar si en vez de llamar a la BIOS para
pintar se emplea una rutina propia mezclada con la que traza la recta). La velocidad del algoritmo es
muy elevada, sobre todo con las lneas largas, mxime teniendo en cuenta que se trata
posiblemente de una de sus implementaciones ms optimizada (slo usa una variable y mantiene
todos los dems valores en los 7 registros de datos de la CPU, sin emplear demasiado la pila y
duplicando cdigo cuando es preciso en los puntos crticos). No entrar en explicaciones
matemticas del mtodo, del que hay pautas en su listado. Existen versiones de este mtodo que
consideran de manera especial las lneas verticales y horizontales para pintarlas de manera m s
rpida, aunque yo personalmente prefiero rutinas independientes para esas tareas con objeto de no
ralentizar el trazado de rectas normales.
; ********************************************************************
; * *
; * RED.ASM - Demostracin de grfica en CGA utilizando BIOS *
; * *
; ********************************************************************
red SEGMENT
ASSUME CS:red, DS:red
ORG 100h
inicio:
MOV AX,modo
INT 10h ; modo de pantalla
MOV AL,max_color-1 ; color visible
MOV BX,0 ; contador para eje Y
MOV BP,0 ; contador para eje X
otras_cuatro: MOV CX,0
MOV DX,BX
MOV SI,BP
MOV DI,max_y-1
CALL recta ; primera recta
MOV CX,max_x-1
MOV SI,max_x-1
SUB SI,BP
CALL recta ; segunda
MOV CX,BP
MOV DX,0
MOV SI,0
MOV DI,max_y-1
SUB DI,BX
CALL recta ; tercera
MOV CX,max_x-1
SUB CX,BP
MOV SI,max_x-1
CALL recta ; cuarta
ADD BX,6
ADD BP,14
CMP BX,max_y
JB otras_cuatro
MOV AH,0
INT 16h ; esperar pulsacin de tecla
MOV AX,3
INT 10h ; volver a modo texto
INT 20h ; fin de programa
recta PROC
PUSH AX ; de (CX,DX) a (SI,DI) color AL
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
MOV color,AL
MOV AX,SI
SUB AX,CX ; AX = X2-X1
JNC absx2x1
NEG AX
XCHG CX,SI
XCHG DX,DI
absx2x1: MOV BX,DI ; AX = ABS(X2-X1) = dx
SUB BX,DX
MOV BP,1 ; BP = 1 = yincr si Y2>Y1
JNC absy2y1
NEG BP ; BP = -1 = yincr si Y2<=Y1
NEG BX
absy2y1: CMP AX,BX ; BX = ABS(Y2-Y1) = dy
PUSHF
JA noswap ; ABS(pendiente) menor de 1
XCHG AX,BX
noswap: SHL BX,1 ; BX = dy * 2
MOV SI,BX
SUB SI,AX ; SI = dy * 2 - dx = d
MOV DI,BX
SUB DI,AX
SUB DI,AX ; DI = dy*2-dx*2 = incr2
POPF
JBE penmay1 ; pendiente mayor de 1
penmen1: PUSH AX
MOV AL,color
CALL punto ; en (CX, DX) = (x, y)
POP AX
INC CX ; x++
AND SI,SI ; (SI>0) ? -> d > 0 ?
JS noincy
ADD SI,DI ; d > 0 : d = d + incr2
ADD DX,BP ; y = y + yincr
DEC AX ; dx--
JNZ penmen1
JMP fin
noincy: ADD SI,BX ; d < 0 : d = d + incr1
DEC AX
JNZ penmen1
JMP fin
penmay1: PUSH AX
MOV AL,color
CALL punto ; en (CX, DX) = (x, y)
POP AX
ADD DX,BP ; y = y + yincr
AND SI,SI ; (SI>0) ? -> d > 0 ?
JS noincx
ADD SI,DI ; d > 0 : d = d + incr2
INC CX ; x++
DEC AX ; dx--
JNZ penmay1
JMP fin
noincx: ADD SI,BX ; d = d + incr1
DEC AX ; dx--
JNZ penmay1
fin: POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
RET
color DB 0
recta ENDP
punto PROC
PUSH BX ; preservar registros (salvo AX)
PUSH CX
PUSH DX
PUSH BP
PUSH SI
PUSH DI
MOV AH,0Ch ; trazar punto usando BIOS
XOR BX,BX
INT 10h
POP DI
POP SI
POP BP
POP DX
POP CX
POP BX
RET
punto ENDP
red ENDS
END inicio
Quiz el lector opine que RED.ASM no es tan rpido. Y tiene razn: la culpa es de la BIOS,
que consume un alto porcentaje del tiempo de proceso. Sustituyendo la rutina punto por una
rutina de trazado de puntos propia, como la que se lista a continuacin, la velocidad puede llegar a
quintuplicarse en un hipottico RED2.ASM que la invocara.
punto640x200_C PROC ; en (CX, DX) de color AL (CGA 640x200)
PUSH DS ; slo se corrompe AX
PUSH BX
PUSH CX
PUSH DX
MOV BX,0B800h ; segmento de pantalla CGA
MOV DS,BX
MOV AH,CL ; preservar parte baja de cx
XCHG BX,CX ; BX = cx
MOV CL,3
SHR BX,CL ; BX = cx / 8
SHR DX,1 ; DX = int (cy / 2)
JNC no_add
ADD BX,8192 ; BX = cx / 8 + (cy MOD 2) * 8192
no_add: INC CL ; CL = 4
SHL DX,CL ; DX = (cy / 2) * 16
ADD BX,DX ; BX = BX + (cy / 2) * 16
SHL DX,1
SHL DX,1 ; DX = (cy / 2) * 64
ADD BX,DX ; BX = BX + (cy / 2) * 80
MOV CL,AH ; recuperar parte baja de cx
AND CL,7 ; dejar n de bit a pintar (0..7)
XOR CL,7 ; invertir orden de numeracin
MOV AH,1 ; bit a borrar de la pantalla en AH
SHL AX,CL ; AH = bit a borrar, AL = bit a pintar
NOT AH
AND [BX],AH ; borrar punto anterior
OR [BX],AL ; ubicar nuevo punto (1/0)
POP DX
POP CX
POP BX
POP DS
RET
punto640x200_C ENDP
Para estudiar el funcionamiento de la pantalla CGA el lector puede hacer un programa que
recorra la memoria de vdeo para comprender la manera en que est organizada, un tanto
peculiar pero no demasiado complicada. Sin embargo, con EGA y VGA no es tan sencillo realizar
operaciones sobre la pantalla debido a la presencia de planos de bit; salvo contadas excepciones
como la del siguiente apartado.
Si se sustituye la rutina punto, que traza el punto, por otra que lo haga llamando a la BIOS,
en una VGA Paradise (BIOS de 14/7/88) se emplean 4 segundos y 8 centsimas en generar la
imagen, mientras que tal y como est el programa lo dibuja en 40,4 cent simas (10,1 veces m s
rpido); todos estos datos cronometrados con precisin sobre un 386-25 sin memoria cach
teniendo instalada la opcin de SHADOW ROM (la lenta ROM copiada en RAM, incluida la
BIOS de la VGA, por tanto no compite con desventaja).
oviseg SEGMENT
ASSUME CS:oviseg, DS:oviseg
ORG 100h
inicio:
MOV AX,modo
INT 10h
CALL paleta_verde
MOV CX,max_x
SHR CX,1 ; CX = max_x / 2
MOV DX,max_y
SHR DX,1 ; DX = max_y / 2
MOV BX,DX
SHR BX,1 ; BX = ma_y / 4
CALL ovillo ; en (CX, DX) de radio BX
MOV AH,0
INT 16h ; esperar pulsacin de tecla
MOV AX,3
INT 10h ; volver a modo texto
INT 20h ; fin de programa
paleta_verde PROC
MOV CX,256 ; los 256 registros
MOV DX,3C8h
otro_reg: MOV AL,CL
OUT DX,AL ; registro a programar
INC DX
XOR AL,AL
OUT DX,AL ; componente roja
MOV AL,CL
REPT max_x/320
SHR AL,1
ENDM
OUT DX,AL ; componente verde
XOR AL,AL
OUT DX,AL ; componente azul
DEC DX
LOOP otro_reg
RET
paleta_verde ENDP
oviseg ENDS
END inicio
Actualmente, las principales tarjetas soportan la norma VESA. Las ms antiguas pueden
tambin soportarla gracias a pequeos programas residentes que el usuario puede instalar
opcionalmente. Para desarrollar una aplicacin profesional, es una buena norma soportar algn
modo estndar de la VGA y, para obtener ms prestaciones, algn modo VESA para los
usuarios que estn equipados con dicho soporte. Intentar acceder directamente al hardware o a las
funciones BIOS propias de cada tarjeta del mercado por separado, salvo para aplicaciones muy
concretas, es ciertamente poco menos que imposible.
Modos grficos.
El estndar VESA soporta multitud de modos grficos, numerados a partir de 100h, si bien
algunos de los ms avanzados (con 32000 o 16 millones de colores) s lo estn soportados por
las versiones ms recientes de la norma. Entre 100h y 107h se definen los modos m s comunes
de 16 y 256 colores de todas las SuperVGA, aunque el modo 6Ah tambin es VESA
(800x600x16) al estar soportado por mltiples tarjetas.
Una de las grandes ventajas del estndar VESA es la enorme informaci n que pone a
disposicin del programador. Es posible conocer todos los modos y qu caractersticas de
resolucin, colores y arquitectura tienen. Adems, hay funciones adicionales muy tiles para
guardar y recuperar el estado de la tarjeta, de especial utilidad para programas residentes: as ,
estos pueden fcilmente conmutar a modo texto (con la precaucin de preservar antes los 4
primeros Kbytes de la RAM de vdeo empleados para definir los caracteres) y volver al modo
grfico original dejando la pantalla en el estado inicial.
El programa de ejemplo.
En el apndice donde se resumen las funciones del DOS y la BIOS aparecen tambin las
funciones VESA de vdeo. Estas funciones se invocan va INT 10h, con AX tomando valores
por lo general desde 4F00h hasta 4F08h. Para realizar programas que utilicen la norma, el lector
deber consultar dicha informacin. Sin embargo, se expone aqu un sencillo programa de
demostracin que recoge prcticamente todos los pasos necesarios para trabajar con un modo
VESA.
El primer paso consiste en detectar la presencia de soporte VESA en el sistema, tarea que realiza
la funcin testvesa(). La funcin getbest256() se limita a buscar el modo de mayor resolucin
de 256 colores soportado por la tarjeta grfica de ese equipo, barriendo sistem ticamente todos
los modos de pantalla desde el "mejor" hasta el "peor". Para comprobar la existencia de un
determinado modo grfico, existe_modo() invoca tambin a la BIOS VESA. La funcin
setmode() establece un modo grfico VESA, devolviendo adems dos informaciones
interesantes: la direccin de memoria de la rutina de conmutacin de bancos (ya veremos para
qu sirve) y el segmento de memoria de vdeo, que ser normalmente 0A000h. Finalmente,
getinfo() devuelve informacin sobre cualquier modo grfico. En principio, los modos utilizados
por este programa de demostracin son conocidos. Sin embargo, la lista de modos de v deo
puede ser mayor en algunas tarjetas, sobre todo en el futuro. Por tanto, un esquema alternativo
podra consistir no en buscar ciertos modos concretos sino en ir recorriendo todos y elegir el que
cumpla ciertas caractersticas de resolucin o colores, entre todos los disponibles.
#include <dos.h>
#include <alloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned
testvesa (void), /* Detectar soporte VESA */
existe_modo (unsigned), /* Comprobar si un modo es soportado */
getbest256 (void); /* Obtener mejor modo de 256c */
void
setbank (long, unsigned), /* Conmutar banco de memoria */
setmode (unsigned, long *, /* Establecer modo VESA */
unsigned *),
getinfo (unsigned, /* Obtener informacin del modo */
unsigned *,
unsigned *, unsigned *, unsigned *);
/* DEMOSTRACION */
void main()
{
struct REGPACK r;
long
ConmutaBanco; /* direccin FAR del conmutador de banco */
unsigned
video_seg, /* direccin del segmento de vdeo */
far *pantalla,
i, modo, max_x, max_y, vram, bancos, banco, limite;
if (!testvesa()) {
printf ("\nNecesario soporte VESA para este programa.\n");
exit (1);
}
modo = getbest256();
setmode (modo, &ConmutaBanco, &video_seg);
getinfo (modo, &max_x, &max_y, &vram, &bancos);
for (banco=0; banco<bancos; banco++) {
setbank (ConmutaBanco, banco); /* direccionar banco */
pantalla=MK_FP(video_seg, 0); /* normalmente 0xA000:0 */
if (banco!=bancos-1)
limite=32768; /* todo el segmento de 64 Kb */
else
limite=(vram-banco*64)*512; /* palabras ltimo banco */
unsigned testvesa(void)
{
struct REGPACK r;
char far *mem;
unsigned vesa;
7.5. - EL TECLADO.
En este apartado se estudiar a fondo el funcionamiento del teclado en los ordenadores
compatibles, a tres niveles: bajo, intermedio y alto. En el captulo 12 se documenta el
funcionamiento del hardware del teclado, interesante para ciertas aplicaciones concretas, aunque
para la mayor parte de las labores de programacin no es necesario llegar a tanto.
Bajo el sistema DOS, el teclado del AT es idntico al del XT en los c digos de rastreo y
comportamiento, debido a la traduccin que efecta el 8042 en el primero. No obstante, el
teclado del AT posee unos comandos adicionales para controlar los LEDs. En otros sistemas
operativos (normalmente UNIX) el teclado del AT es programado para trabajar en modo AT y pierde
la compatibilidad con el del XT (los cdigos de rastreo son distintos y al soltar una tecla se
producen dos interrupciones) pero bajo DOS esto no sucede en ningn caso y la compatibilidad es
casi del 100%.
Las teclas expandidas -las que han sido aadidas al teclado estndar de 83/84 teclas- tienen un
comportamiento especial, ya que pueden generar hasta 4 interrupciones consecutivas (con un
intervalo de unos 1,5 milisegundos, 3 ms en los cdigos dobles que convierte en uno el 8042)
con objeto de emular, aunque bastante mal, ciertas combinaciones de las teclas no expandidas; en
general es bastante deficiente la emulacin por hardware y el controlador del teclado (KEYB)
tiene que tratarlas de manera especial en la prctica. As, por ejemplo, cuando est inactivo
NUM LOCK y se pulsa el cursor derecho expandido, se generan dos interrupciones consecutivas:
en la primera aparece un valor 0E0h en el puerto del teclado que indica que es una tecla expandida;
en la segunda interrupcin aparece el valor 4Dh: el mismo que hubiera aparecido pulsando el '6'
del teclado numrico. Sin embargo, si NUM LOCK est activo, en un teclado normal de 83
teclas hay que pulsar el '6' del teclado numrico junto con shift para que el cursor avance. Esto se
simula en el teclado expandido por medio de 4 interrupciones: En las dos primeras puede aparecer
la secuencia 0E0h-2Ah bien 0E0h-36h (2Ah y 36h son los cdigos de las teclas shift normales):
con esto se simula que est pulsado shift aunque ello no sea realmente cierto (las BIOS m s
antiguas ignoran la mayora de los bytes mayores de 128, entre ellos el 0E0h); despu s aparecen
otras dos interrupciones con los valores 0E0h-4Dh (con objeto de simular que se pulsa el '6' del
teclado numrico): como el estado NUM LOCK est activo y en teora se ha pulsado shift y el
6 del teclado numrico, el cursor avanza a la derecha; al soltar la tecla aparecer la secuencia de
interrupciones 0E0h-CDh-0E0h-0AAh, o en su defecto la secuencia equivalente 0E0h-CDh-0E0h-
0B6h. En general, estos cdigos shift fantasma dan problemas cuando las teclas de SHIFT
adquieren otro significado diferente que el de conmutar el estado NUM LOCK, lo que sucede en
casi todos los editores de texto de los modernos compiladores. Por ello, la BIOS o el KEYB tratan
de manera especial las teclas expandidas; en los ordenadores ms antiguos (con BIOS -o al menos
su tecnologa- anterior a Noviembre de 1985), si no se carga el KEYB, el teclado expandido
funcionar mal, incluso en Estados Unidos -aunque las teclas estn bien colocadas-. Cuando se
lee un valor 0E0h en una interrupcin de teclado, el KEYB o la BIOS activan el bit 1 (el que vale
2) de la posicin de memoria 0040h:0096h; en la siguiente interrupcin ese bit se borra y ya se
sabe que el cdigo ledo es el de una tecla expandida. El bit 0 de esa misma posicin de
memoria indica si se ley un byte 0E1h en lugar de 0E0h (la tecla expandida pause o
pausa es un caso especial -por fortuna, el nico- y genera un prefijo 0E1h en vez del 0E0h
habitual; de hecho, esta tecla no genera cdigos al ser soltada, pero al pulsarla aparece la secuencia
E1-1D-45-E1-9D-C5).
Cuando se pulsa una tecla normal, la rutina que gestiona INT 9 deposita en un buffer dos bytes
con su cdigo ASCII y el cdigo de rastreo, para cuando el programa principal decida explorar el
teclado -lo har siempre consultando el buffer-. Si el cdigo ASCII depositado es cero 0E0h,
se trata de una tecla especial (ALT-x, cursor, etc.) y el segundo byte indica cul (son los
denominados cdigos secundarios). El cdigo ASCII 0E0h slo es generado en los teclados
expandidos por las teclas expandidas (marcadas como 'Ex' en la tabla de cdigos de rastreo del
apndice V), aunque las funciones estndar de la BIOS y del DOS que informan del teclado lo
convierten en cero para compatibilizar con teclados no expandidos. As mismo, el cdigo ASCII
0F0h est reservado para indicar las combinaciones de ALT-tecla que no fueron consideradas
inicialmente en el software de soporte de los teclados no expandidos, pero s actualmente (de esta
manera, las rutinas de la BIOS saben si deben informar de estas teclas o no seg n se est
empleando una funcin avanzada u obsoleta, para compatibilizar). En todo caso, las secuencias
introducidas por medio de ALT-teclado_numrico llevan asociado un cdigo de rastreo 0, por lo
que el usuario puede generar los caracteres ASCII 0E0h y 0F0h sin que se confundan con
combinaciones especiales; adems, segn IBM, si el cdigo ASCII 0 va acompaado de un
cdigo de rastreo 3 los programas deberan interpretarlo como un autntico cdigo ASCII 0
(esta secuencia se obtiene con Ctrl-2) lo que permite recuperar ese cdigo perdido en indicar
combinaciones especiales.
Es importante sealar que aunque el buffer (organizado como cola circular) normalmente est
situado entre 0040h:001Eh y 0040h:003Eh, ello no siempre es as; realmente el offset del inicio y
el fin del buffer respecto al segmento 0040h lo determinan las variables (tamao palabra) situadas
en 0040h:0080h y 0040h:0082h en todos los ordenadores posteriores a 1981. Por ello, la inmensa
mayora de las pequeas utilidades de las revistas y los ejemplos de los libros son, por desgracia,
incorrectos: la manera correcta de colocar un valor en el buffer -para simular, por ejemplo, la
pulsacin de una tecla- o extraerlo del mismo es comprobando adecuadamente los
desbordamientos de los punteros teniendo en cuenta las variables mencionadas. El puntero al inicio
del buffer es una variable tamao palabra almacenada en la posicin 0040h:001Ah y el fin otra
ubicada en 0040h:001Ch. El siguiente ejemplo introduce un carcter de cdigo ASCII AL y
cdigo de rastreo AH (es cmodo y vlido hacer AH=0) en el buffer del teclado:
MOV BX,40h ; meter carcter AX en el buffer del
teclado
MOV DS,BX
CLI ; evitar conflictos con interrupciones
MOV BX,DS:[1Ch] ; puntero a la cola del buffer
MOV CX,BX
ADD CX,2 ; apuntar CX al siguiente dato
CMP CX,DS:[82h] ; ms all del fin del buffer
JB no_desb
MOV CX,DS:[80h] ; inicio de la cola circular
no_desb: CMP CX,DS:[1Ah] ; puntero al inicio del buffer
JE fin_rutina ; ZF = 1 --> buffer lleno
MOV DS:[BX],AX ; introducir carcter ASCII (AL) en el
buffer
MOV DS:[1Ch],CX ; actualizar puntero al final del
buffer
CMP SP,0 ; ZF=0 (SP siempre <> 0) --> buffer no
lleno
fin_rutina: STI
El valor 0 para el cdigo de rastreo es usado para introducir tambin algunos caracteres
especiales, como las vocales acentuadas, etc., aunque por lo general no es demasiado importante su
valor (de hecho, los programas suelen comprobar preferentemente el cdigo ASCII; de lo
contrario, en un teclado espaol y otro francs, la tecla Z tendra distinto cdigo!). No
estara de ms en este ejemplo comprobar si las variables 40h:80h y 40h:82h son distintas de
cero por si el ordenador es demasiado antiguo, medida de seguridad que de hecho toma el KEYB
del DR-DOS (en estas mquinas adems no es conveniente ampliar el tamao del buffer
cambindolo de sitio, por ejemplo; lo normal es que est entre 40h:1Eh y 40h:3Eh). En el
apndice V se listan los cdigos secundarios: son el segundo byte (el ms significativo) de la
palabra depositada en el buffer del teclado por la BIOS o el KEYB.
He aqu un ejemplo de una subrutina que intercepta la interrupcin del teclado apoy ndose
en el controlador habitual y limitndose a detectar las teclas pulsadas, espiando lo que sucede pero
sin alterar la operacin normal del teclado:
nueva_int9: STI ; permitir interrupcin peridica
PUSH AX ; preservar registros modificados
IN AL,60h ; cdigo de la tecla pulsada
PUSHF ; preparar la pila para IRET
CALL CS:anterior_int9 ; llamar a la INT 9 original
. . . ; hacer algo con esa tecla
POP AX ; restaurar registros modificados
IRET ; volver al programa principal
Si se implementa totalmente el control de una tecla en una rutina que gestione INT 9 -sin llamar
al principio o al final al anterior gestor-, en los XT hay que enviar una seal de reconocimiento al
teclado poniendo a 1 y despus a 0 el bit 7 del puerto de E/S 61h (en AT no es necesario, aunque
tampoco resulta perjudicial hurgar en ese bit en las mquinas fabricadas hasta ahora); es
importante no enviar ms de una seal de reconocimiento, algo innecesario por otra parte, de
cara a evitar anomalas importantes en el teclado de los XT. Adems, tanto en XT como AT hay
que enviar en este caso una seal de fin de interrupcin hardware (EOI) al 8259 (con un simple
MOV AL,20h; OUT 20h,AL) al igual que cuando se gestiona cualquier otra interrupci n
hardware. El ejemplo anterior quedara como sigue:
nueva_int9: STI
PUSH AX
IN AL,60h ; cdigo de la tecla pulsada
CMP AL,tecla ; es nuestra tecla?
JNE fin ; no
PUSH AX ; vamos a manchar AX
IN AL,61h
OR AL,10000000b
OUT 61h,AL
AND AL,01111111b
OUT 61h,AL ; seal de reconocimiento enviada
POP AX ; AL = tecla pulsada
. . . ; gestionarla
MOV AL,20h
OUT 20h,AL ; EOI al 8259
POP AX ; AX del programa principal
IRET ; volver al programa principal
fin: POP AX ; AX del programa principal
JMP CS:anterior_int9 ; saltar al gestor previo de INT 9
Como se puede observar, esta rutina gestiona una tecla y las dems se las deja al KEYB o la
BIOS. Slo en el caso de que la gestione l es preciso enviar una seal de reconocimiento y un
EOI al 8259. En caso contrario, se salta al controlador previo a esta rutina con un JMP largo
(segmento:offset); ahora no es preciso el PUSHF, como en el caso del CALL, por razones obvias.
La instruccin STI del principio habilita las interrupciones, siempre inhibidas al principio de una
interrupcin -valga la redundancia-, lo que es conveniente para permitir que se produzcan ms
interrupciones -por ejemplo, la del temporizador, que lleva nada menos que la hora interna del
ordenador-. En el ejemplo, el EOI es enviado justo antes de terminar de gestionar esa tecla; ello
significa que mientras se la procesa, las interrupciones hardware de menor prioridad -todas, menos
el temporizador- estn inhibidas por mucho que se haga STI; el programador ha de decidir pues si
es preciso enviar antes o no el EOI (vase la documentacin sobre el controlador de
interrupciones 8259 de los captulos posteriores), aunque si la rutina es corta no habr demasiada
prisa.
Es habitual en los controladores de teclado de AT (tanto la BIOS como el KEYB del MS-DOS)
deshabilitar el teclado mientras se procesa la tecla recin leda, habilitndolo de nuevo al final,
por medio de los comandos 0ADh y 0AEh enviados al 8042. Sin embargo, la mayor a de las
utilidades residentes no toman estas precauciones tan sofisticadas (de hecho, el KEYB del DR-DOS
tampoco). Lgicamente slo se pueden enviar comandos al 8042 cuando el registro de entrada
del mismo est vaco, lo que puede verificarse chequeando el bit 1 del registro de estado: no es
conveniente realizar un bucle infinito que dejara colgado el ordenador de fallar el 8042, de ah
que sea recomendable un bucle que repita slo durante un cierto tiempo; en el ejemplo se utiliza la
temporizacin del refresco de la memoria dinmica de los AT para no emplear ms de 15 ms
esperando al 8042. Adems las interrupciones han de estar inhibidas en el momento crtico en
que dura el envo del comando, aunque cuidando de que sea durante el menor tiempo posible:
nueva_int9: STI ; breve ventana para interrupciones
PUSH AX
CALL espera
MOV AL,0ADh
OUT 60h,AL ; inhibir teclado
CALL espera
IN AL,60h ; tecla?
STI ; permitir rpidamente interrupciones
... ; procesar tecla y enviar EOI al 8259
CALL espera
MOV AL,0AEh
OUT 60h,AL ; desinhibir teclado
POP AX
IRET ; no merece la pena hacer STI
espera: PUSH AX
PUSH CX
MOV CX,995 ; constante para 15 ms
CLI
testref: IN AL,61h
AND AL,10h ; mtodo vlido solo en AT
CMP AL,AH
JZ testref
MOV AH,AL
IN AL,64h ; registro de estado del 8042
TEST AL,2 ; buffer de entrada lleno?
LOOPNZ testref ; as es
POP CX
POP AX
RET
7.5.2. - NIVEL INTERMEDIO.
Consulta de SHIFT, CTRL, ALT, etc ( marcas de teclado).
Estas teclas pueden ser pulsadas para modificar el resultado de la pulsacin de otras. IBM no ha
definido combinaciones con ellas (excepto CTRL-ALT, que sirve para reinicializar el sistema si se
pulsa en conjuncin con DEL) por lo que los programas residentes suelen precisamente emplear
combinaciones de dos o ms teclas de estas para activarse sin eliminar prestaciones al teclado; por
defecto, si se pulsan dos o ms teclas de estas la BIOS o el KEYB asignan prioridades y
consideran slo una de ellas: ALT es la tecla de mayor prioridad, seguida de CTRL y de SHIFT.
Por otra parte, cabe destacar el hecho de que CTRL, ALT y SHIFT (al igual que Num Lock, Caps
Lock, Scroll Lock e Ins) no poseen la caracterstica de autorepeticin de las dems teclas
debido a la gestin que realiza la BIOS o el KEYB.
- Teclado no expandido.
Llamando con AH=2 a la INT 16h (funcin 2 de la BIOS para el teclado), se devuelve en AL
un byte con informacin sobre las teclas de control (SHIFT, CTRL, etc.) que es el mismo byte
almacenado en 0040h:0017h (vase en el apndice III el rea de datos de la BIOS y las
funciones de la BIOS para teclado). En 0040h:0018h, existe otro byte de informacin adicional,
aunque no hay funcin BIOS para consultarlo en los teclados no expandidos, por lo que a menudo
es necesario leerlo directamente. Por lo general es mejor emplear las funciones BIOS, si existen,
que consultar directamente un bit, por razones de compatibilidad. Evidentemente, todas las
funciones para teclados no expandidos pueden usarse tambin con los expandidos.
- Teclado expandido.
A partir de 0040h:0096h hay otros bytes con informacin adicional y especfica sobre el
teclado del AT y los teclados expandidos: parte de esta informacin, as como de la de
0040:0018h, puede ser consultada en los teclados expandidos con la funcin 12h de la BIOS del
teclado expandido, que devuelve en AX una palabra: en AL de nuevo el byte de 0040h:0017h y en
AH otro byte mezcla de diversas posiciones de memoria con informacin til (consultar
funciones de la BIOS para teclado).
Los bits de 40h:96h slo son fiables si est instalado el KEYB del MS-DOS o 99%
compatible; por ejemplo, el KEYB del DR-DOS 5.0/6.0 (excepto en modo KEYB US) no gestiona
correctamente el bit de AltGr, aunque s los dems bits. Antes de usar esta funcin conviene
asegurarse de que est soportada por la BIOS o el KEYB instalado.
Con la funcin 0 de la INT 16h (AH=0 al llamar) se lee una tecla del buffer del teclado,
esperando su pulsacin si es preciso, y se devuelve en AX (AH c digo de rastreo y AL c digo
ASCII); con la funcin 1 (AH=1 al llamar a INT 16h) se devuelve tambin en AX el car cter
del buffer pero sin sacarlo (habr que llamar de nuevo con AH=0), aunque en este caso no se
espera a que se pulse una tecla (si el buffer estaba vaco se retorna con ZF=1 en el registro de
estado). En los equipos con soporte para teclado expandido existen adems las funciones 10h y
11h (correspondientes a la 0 y 1) que permiten detectar alguna tecla ms (como F11 y F12) y
diferenciar entre las expandidas y las que no lo son al no convertir los cdigos 0E0h en 0, as
como la funcin 5 (introducir caracteres en el buffer).
- PAUSE: se obtiene con dicha tecla o bien con CTRL-NUM LOCK (teclados no expandidos);
provoca que el ordenador se detenga hasta que se pulse una tecla no modificadora (ni SHIFT, ni
ALT, etc.), tecla que ser ignorada pero servir para abandonar la pausa. La pausa es interna a la
rutina de control del teclado.
- PTR SCR (SHIFT con el (*) del teclado numrico en teclados no expandidos): vuelca la pantalla
por impresora al ejecutar una INT 5.
- SYS REQ: al pulsarla genera una INT 15h (AX=8500h) y al soltarla otra INT 15h (AX=8501h).
- CTRL-ALT-DEL: el controlador del teclado coloca la palabra 1234h en 0040h:0072h (para evitar
el chequeo de la memoria) y salta a la direccin 0FFFFh:0 reinicializando el ordenador.
Posibilidades avanzadas.
La rutina de la BIOS del AT (y de los KEYB) que lee el buffer del teclado, cuando no hay teclas
y tiene que esperar por las mismas ejecuta de manera regular la funci n 90h (AH=90h) de la
interrupcin 15h indicando una espera de teclado al llamar (AL=2). De esta manera, un
hipottico avanzado sistema operativo podra aprovechar ese tiempo muerto para algo ms
til. As mismo, cuando un carcter acaba de ser introducido en el buffer del teclado, se ejecuta
la funcin 91h para indicar que ya ha finalizado la entrada y hay caracteres disponibles. En
general, estas caractersticas no son tiles en el entorno DOS y, por otra parte, han sido
deficientemente normalizadas. Por ejemplo, al acentuar incorrectamente se generan dos caracteres
(adems del familiar pitido): el KEYB del MS-DOS slo ejecuta una llamada a la INT 15h con la
funcin 91h (pese a haber introducido dos caracteres en el buffer) y el de DR-DOS hace las dos
llamadas...
Consideraciones finales.
Conviene sealar que los teclados de AT pueden generar interrupciones aunque no se pulsen
teclas, normalmente para devolver una seal de reconocimiento cuando alguien les ha enviado
algo -por ejemplo, la BIOS puede enviar un comando para cambiar los led's-; por ello, en el
momento ms insospechado puede producirse una INT 9 con el cdigo de rastreo 0FAh, y la
secuencia de interrupciones generada por las teclas que tienen asociado un led en los AT, debido a
los cdigos 0FAh, no es exactamente idntica a la de los XT, aunque se trata de un detalle poco
relevante -incluso para quienes pretendan hacer algo especial con estas teclas-. Tambin es
conveniente indicar que en los AT se puede leer puerto del teclado, para averiguar la ltima tecla
pulsada o soltada, en casi cualquier momento -por ejemplo, peridicamente desde la interrupcin
del temporizador-. De todas formas, esta prctica tiene efectos secundarios debidos al mal dise o
del software del sistema de los AT (tales como teclas shift que se enganchan, como si se quedaran
pulsadas, numeritos que aparecen al pulsar los cursores expandidos, etc.). Adems, en los XT
slo se obtendr una lectura correcta inmediatamente despus de producirse la interrupcin del
teclado y antes de enviar la correspondiente seal de reconocimiento al mismo -por tanto, no desde
una interrupcin peridica-. Todo esto desaconseja la lectura del puerto del teclado desde
cualquier otro sitio que no sea INT 9, salvo contadas excepciones.
Por ltimo indicar que en los AT se puede modificar el estado de CAPS LOCK, NUM LOCK o
SCROLL LOCK por el simple procedimiento de alterar el bit correspondiente en 40h:17h; dicho
cambio se ver reflejado en los led's cuando el usuario pulse una tecla o el programa lea el teclado
con cualquier funcin -en la prctica, de manera casi instantnea-. Sin embargo, para aplicar
esta tcnica es aconsejable verificar que se trata de un AT porque en los PC/XT el led -si existe- no
se actualiza y pasa a indicar una informacin incorrecta. Realmente, en los XT, el control de los
led lo lleva la propia circuitera del teclado de manera independiente al ordenador.
7.5.3. - ALTO NIVEL.
El acceso al teclado a alto nivel puede realizarse a travs de las funciones 1, 6, 7, 8 y 0Ah del
DOS, considerndolo como dispositivo de entrada estndar. Algunas de estas funciones, si
devuelven un 0, se trata de una tecla especial y la siguiente lectura devuelve el c digo secundario.
El DOS utiliza las funciones BIOS.
Los discos son el principal medio de almacenamiento externo de los ordenadores compatibles.
Pueden ser unidades de disco flexible, removibles, o discos duros -fijos-. Constan bsicamente de
una superficie magntica circular dividida en pistas concntricas, cada una de las cuales se
subdivide a su vez en cierto nmero de sectores de tamao fijo. Como normalmente se emplean
ambas caras de la superficie, la unidad ms elemental posee en la actualidad dos cabezas de
lectura/escritura, una para cada lado del disco. Los tres parmetros comunes a todos los discos
son, por tanto: el nmero de cabezas, el de pistas y el de sectores. El trmino cilindro i hace
referencia a la totalidad de las pistas i de todas las caras. Bajo DOS, los sectores tienen un tamao
de 512 bytes (tanto en discos duros como en disquetes) que es difcil cambiar (aunque no
imposible). Los sectores se numeran a partir de 1, mientras que las pistas y las caras lo hacen desde
0. El DOS convierte esta estructura fsica de tres parmetros a otra: el n mero de sector
lgico, que se numera a partir de 0 (los sectores fsicos les denominaremos a partir de ahora
sectores BIOS para distinguirlos de los sectores lgicos del DOS). Para un disco de SECTPISTA
sectores BIOS por pista y NUMCAB cabezas, los sectores lgicos se relacionan con la estructura
fsica por la siguiente frmula:
Sector lgico = (sector_BIOS - 1) + cara * SECTPISTA + cilindro * SECTPISTA * NUMCAB - X1
Es decir, el DOS recorre el disco empezando la pista 0 (la exterior, la ms alejada del centro) y
por la cara o cabezal 0, recorriendo todos los sectores; luego avanza una cara y recorre de nuevo
todos los sectores; despus pasa al siguiente cilindro... y repite de nuevo el proceso. De esta
manera, varios cabezales podran -hipotticamente- leer bloques de informacin consecutivos
simultneamente. En los disquetes, X1=0, pero en los discos duros se resta un cierto factor de
compensacin X1, ya que stos pueden estar divididos en varias particiones y la que usa el DOS
puede no estar al principio del mismo. En general, un disco duro dividido en varias particiones de
tipo DOS determina varias unidades lgicas de disco, cada una de las cuales dispone de un
conjunto de sectores lgicos numerados a partir de 0 y un factor de compensacin propio para la
frmula. Las siguientes frmulas transforman sectores DOS en sus correspondientes BIOS:
Sector_BIOS = (sector MOD SECTPISTA) + 1
Cara = (sector / SECTPISTA) MOD NUMCAB
Cilindro = sector / (SECTPISTA * NUMCAB) + X2
Como la particin del DOS no suele empezar en el cilindro 0 (reservado en gran parte para la
tabla de particiones) sino ms bien en el 1 en otro posterior (cuando hay ms particiones antes
que la del DOS) ser necesario aadir un cierto valor adicional de compensacin X2 a la
ltima frmula para calcular el cilindro efectivo; esto es as porque en la prctica las
particiones suelen empezar y acabar ocupando cilindros enteros y exactos (aunque en realidad, y
dada la arquitectura de la tabla de particin, podran empezar y acabar no s lo en un
determinado cilindro sino tambin en cierto sector y cara del disco, pero no es frecuente). X1 y X2
se obtienen consultando e interpretando la tabla de particiones o el sector de arranque.
160; Esta tabla comienza en un offset 1BEh del sector (al principio est el c digo ejecutable);
cada particin de las 4 posibles ocupa 16 bytes; al final de las cuatro est la marca 0AA55h,
ubicada en el offset 1FEh, que indica que la tabla es vlida. Los 16 bytes que la forman se
interpretan como indica el cuadro:
+-----------------------------------------------------------------------------+
| byte 0: 0 para particin inactiva, 80h en la de arranque. |
| byte 1: cabeza donde comienza la particin. |
| byte 2: bits 0 al 5: sector de inicio de la particin; 6, 7: parte alta del |
| nmero de cilindro. |
| byte 3: parte baja del nmero de cilindro de inicio de la particin. |
| byte 4: tipo de particin, las ms comunes son 0: No usada; 1: DOS-12 (FAT |
| 12 bits); 4: DOS-16 (FAT 16 bits); 5: DOS Extendida; 6:BIGDOS (ms |
| de 32Mb); 7: OS/2 HPFS WinNT NTFS; 0Ah: OS/2 Boot Manager; 0Bh: |
| 32-bit FAT Win95 (0Ch con LBA); 0Eh y 0Fh (como 06 y 05 pero con |
| LBA); 81h Linux; 82h Linux swap; 83h: Linux native; 0A5h: FreeBSD |
| o BSD/386; 0F2h: particin secundaria (no estudiada en este libro). |
| byte 5: cabeza donde termina la particin. |
| byte 6: bits 0 al 5: sector de fin de la particin; 6, 7: parte alta del |
| nmero de cilindro. |
| byte 7: parte baja del nmero de cilindro de fin de la particin. |
| bytes 8 al 11: Doble palabra que indica el sector relativo (en todo el |
| disco) en que comienza la particin, expresado en sectores. |
| bytes 12 al 15: Doble palabra con el tamao de esa particin en sectores. |
+-----------------------------------------------------------------------------+
Formato de la TABLA DE PARTICIN
Habitualmente, las particiones suelen empezar en el segundo cabezal del cilindro 0, con lo que
toda la primera pista fsica del disco duro est vaca. Lugar ideal para virus, algunos
fabricantes han utilizado esta interesante caracterstica para mejorar el arranque, colocando una
falsa tabla de particin que muestre un men en pantalla y cargue despu s la partici n de
verdad, permitiendo tambin ms de 4 particiones. Sin embargo, estas maniobras suelen reducir
la compatibilidad. Existen tambin cdigo de particiones sofisticado que permite seleccionar una
de las 4 particiones manteniendo pulsada una tecla en el arranque, sin tener que andar ejecutando
FDISK para seleccionar la particin activa... lo que se puede hacer con 400 bytes de c digo!.
Realmente, la arquitectura global de las particiones de un equipo (en particular si tiene m s de 4,
una mezcla de sistemas operativos y/o varios discos duros), puede llegar a ser compleja:
practquese con un buen editor de disco para aprender ms (ej. el DISKEDIT de las Norton
Utilities o las PC-Tools).
Las particiones extendidas llevan su propio sector de particin adicional, en el que no hay
cdigo de programa sino, en su lugar, una lista de dispositivos. Hay dos entradas por cada
dispositivo: la primera indica el tipo (1-FAT12, 4-FAT16); la segunda entrada apunta al siguiente
dispositivo (caso de existir) o es 0 (no hay ms dispositivos). El DOS 4.0 y posteriores eliminaron
la limitacin de los 32 Mb en las particiones y el software actual, ya actualizado, no da problemas
con los discos de ms de 32 Mb. Por ello, en discos de ms de 32 40 Mb lo normal es instalar
DOS 4.0 superior.
En el sector de arranque, adems del sencillo programa de puesta en marcha del sistema, hay
cierta informacin til acerca de las caractersticas del disco o particin. Los primeros 3 bytes
no son significativos: contienen el cdigo de operacin de una instruccin JMP que salta a
donde realmente comienza el cdigo, aunque conviene que dicha instruccin de salto est al
principio del sector de arranque para que algunos sistemas validen dicho sector (es vlido un salto
corto seguido de NOP o un salto completo de 3 bytes). A partir del cuarto (offset 3) se puede
encontrar la informacin vlida. En el sector de arranque del disquete est contenido el BPB
(Bios Parameter Block) que analizaremos ms tarde.
+-------------------------------------------------------------------------------
----------------------------------------+
| offset 3 (8 bytes): Identificacin del sistema (ej., "IBM 3.3")
|
| offset 11 (1 palabra): Bytes por sector, ej. 512.
|
| offset 13 (1 byte): Sectores por cluster (ej. 2)
|
| offset 14 (1 palabra): Sectores reservados al principio (1 en diquettes)
|
| offset 16 (1 byte): Nmero de copias de la FAT (2 normalmente)
|
| offset 17 (1 palabra): Nmero de entradas al directorio raz (112 en discos
de 360 Kb) |
| offset 19 (1 palabra): Nmero total de sectores del disco (0 en discos de
ms de 32 Mb) |
| offset 21 (1 byte): Byte de tipo de disco (vase tabla ms adelante)
|
| offset 22 (1 palabra): Nmero de sectores ocupados por cada FAT
|
| offset 24 (1 palabra): Nmero de sectores por pista
|
| offset 26 (1 palabra): Nmero de cabezas (2 en disquetes de doble cara)
|
| offset 28 (2 palabras): Nmero de sectores especiales reservados. Nota: slo
se debe considerar la primera mitad de |
| esta doble palabra en versiones del sistema 3.30 o
anteriores (no hay problemas con DR-DOS, |
| que en todas sus versiones, hasta la 6.0 incluida,
es un DOS 3.31). El valor de este campo |
| depende de la posicin relativa que ocupe la
particin dentro del disco duro (ser 0 en los |
| disquetes), este valor ha de sumarse al del nmero
de sector del DOS antes de traducirlo a |
| un nmero de sector de la BIOS.
|
| offset 32 (2 palabras): Nmero total de sectores del disco en discos de ms
de 32 Mb (esta informacin slo debe |
| obtenerse de aqu si la palabra ubicada en el offset
19 es cero). |
| offset 36 (1 byte): Nmero de unidad fsica (a partir del DOS 4.0).
|
| offset 37 (1 byte): Reservado.
|
| offset 38 (1 byte): valor 29h desde DOS 4.0 (marca de validacin que
indica que los bytes ubicados desde el |
| offset 36 al offset 61 estn definidos).
|
| offset 39 (2 palabras): Nmero de serie del disco (a partir de DOS 4.0).
|
| offset 43 (11 bytes): Ttulo del disco (desde DOS 4.0); por defecto se
inicializa con "NO NAME ", aunque tanto |
| el DOS 4.0 como el 5.0 y 6.X siguen empleando adems
las tradicionales etiquetas de volumen.|
| offset 54 (8 bytes): Sistema de ficheros (a partir de DOS 4.0): puede ser
"FAT12 " o "FAT16 ". |
+-------------------------------------------------------------------------------
----------------------------------------+
El byte del tipo de disco (offset 21) intenta identificar el tipo de disco, aunque no lo consigue en
muchos casos dada la ilgica utilizacin que se ha hecho de l. La recomendacin es hacer lo
que viene haciendo el DOS desde la 3.30: no hacer caso de lo que dice este byte para identificar los
discos. La nica excepcin tal vez sea el valor 0F8h que identifica a los dispositivos no
removibles:
+---------------------------------------------------------------------+
| 0FEh - discos de 5-160 Kb (1 cara, 8 sectores/pista, 40 pistas) |
| 0FFh - discos de 5-320 Kb (2 caras, 8 sectores/pista, 40 pistas) |
| 0FCh - discos de 5-180 Kb (1 cara, 9 sectores/pista, 40 pistas) |
| 0FDh - discos de 5-360 Kb (2 caras, 9 sectores/pista, 40 pistas) |
| 0F9h - discos de 5-1,2 Mb (2 caras, 15 sectores/pista, 80 pistas) |
| 0F9h - discos de 3-720 Kb (2 caras, 9 sectores/pista, 80 pistas) |
| 0F8h - discos duros y algunos virtuales |
| 0F0h - discos de 3-1,44 Mb (2 caras, 18 sectores/pista, 80 pistas) |
| 0F0h - discos de 3-2,88 Mb (2 caras, 36 sectores/pista, 80 pistas) |
| 0F0h - restantes formatos de disco |
+---------------------------------------------------------------------+
Tipos de Discos
7.6.3. - LA FAT.
Despus del sector de arranque, aparecen en el disco una serie de sectores que constituyen la
Tabla de Localizacin de Ficheros (File Alocation Table o FAT). Consiste en una especie de mapa
que indica qu zonas del disco estn libres, cules ocupadas, dnde estn los sectores
defectuosos, etc. Normalmente hay dos copias consecutivas de la FAT (vase el offset 16 del
sector de arranque), ya que es el rea ms importante del disco de la que dependen todos los
dems datos almacenados en l. No deja de resultar extrao que ambas copias de la FAT estn
fsicamente consecutivas en el disco: si accidentalmente se estropeara una de ellas (por ejemplo,
rayando con un bolgrafo el disco) lo ms normal es que la otra tambi n resultara daada. En
general, muchos programas de chequeo de disco no se molestan en verificar si ambas FAT son
idnticas (empezando por algunas versiones de CHKDSK). Por otra parte, hubiera sido mejor
eleccin haberla colocado en el centro del disco: dada la frecuencia de los accesos a la misma, de
cara a localizar los diferentes fragmentos de los ficheros, ello mejorara notablemente el tiempo de
acceso medio. Aunque cierto es que los cachs de disco y los buffers del config.sys pueden hacer
casi milagros... a costa de memoria.
Como se ve, el primer byte de la primera entrada a la FAT es inicializado con el mismo valor que
el byte de tipo de disco del sector de arranque. Los restantes bits de las dos primeras entradas suelen
estar todos a 1. Para determinar el nmero de clusters del disco, ha de restarse del n mero total
de sectores la cifra correspondiente al nmero de sectores reservados (normalmente 1 en los
disquetes, correspondiente al sector de arranque), los que ocupa la FAT y los empleados por el
directorio raz (que se ver ms adelante); a continuacin se divide ese nmero de sectores
de datos resultante por el nmero de sectores por cluster.
El hecho de emplear FAT's de 12 bits es debido a que con menos bits (ej., un byte) slo podra
haber unos 250 clusters en el disco. En un disco de 1,2 Mb ello significar a que la unidad
mnima de informacin sera 1200/250 = 5 Kb: el fichero ms pequeo (de 1 byte)
ocupara 5 Kb!. Empleando FAT's de 16 bits se podran hacer clusters incluso de tama o
menor que el sector (menos de 512 bytes), aprovechando ms el espacio del disco. Sin embargo,
ello hara que la propia FAT ocupase demasiado espacio en el disco. Por ello, en los disquetes se
emplean FAT's de 12 bits (1 byte y medio): para un programa en cdigo mquina ello no
ralentiza los clculos (aunque al ser humano no se le de muy bien trabajar con medios bytes). En
la prctica, se toman palabras de 16 bits y se desprecian los 4 bits m s significativos en los
clusters pares y los 4 menos significativos en los impares.
A continuacin se listan dos rutinas que permiten acceder a una FAT de 12 bits previamente
cargada en memoria, con objeto de consultar o modificar alguna entrada. Evidentemente, despus
habr que volver a grabar la FAT en disco, tantas veces como copias de la misma existan en ste.
Las rutinas necesitan que la FAT est completamente cargada en memoria, lo cual no es un
requerimiento demasiado costoso, habida cuenta de que no puede ocupar ms de 4085 * 1,5 =
6128 bytes.
; ************ Escribir un elemento en una FAT de 12 bits
; Entrada: AX = posicin de dicho elemento
; DS:BX = FAT completamente cargada en memoria
; DX = nuevo valor de dicho elemento
poke_fat PROC
PUSH AX ; preservar registros
PUSH BX
PUSH DX
ADD BX,AX ; BX = BX + cluster
SHR AX,1 ; AX = cluster / 2
PUSHF ; CF = 1 si impar
ADD BX,AX ; BX = BX + cluster * 1,5
MOV AX,[BX] ; AX = palabra con dato 12 bits
POPF
JC poke_fat_imp
AND AX,1111000000000000b ; preservar la otra entrada
JMP poke_fat_ok
poke_fat_imp: AND AX,0000000000001111b ; preservar la otra entrada
PUSH CX
MOV CL,4
SHL DX,CL ; colocarlo: 4 bits a la izda
POP CX
poke_fat_ok: OR AX,DX ; mezclar
MOV [BX],AX ; nuevo valor en la FAT
POP DX
POP BX
POP AX
RET ; retorno sin alterar registros
poke_fat ENDP
peek_fat PROC
PUSH AX ; preservar registros
PUSH BX
ADD BX,AX ; BX = BX + cluster
SHR AX,1 ; AX = cluster / 2
PUSHF ; CF = 0 si par
ADD BX,AX ; BX = BX + cluster * 1,5
MOV DX,[BX]
POPF
JNC peek_fat_par
PUSH CX
MOV CL,4
SHR DX,CL ; DX=DX/16: si DX=xyz0, DX=0xyz
POP CX
peek_fat_par: AND DH,00001111b ; borrar posible dgito izdo
POP BX
POP AX
RET ; retornar slo DX modificado
peek_fat ENDP
Tal vez, en futuros disquetes de elevada capacidad sea necesario pasar a una FAT de 16 bits,
aparecida con el DOS 3.0, que es la usada por todos los discos duros excepto el de 10 Mb del XT
original de IBM. Con una FAT de 12 bits el n de cluster m s alto posible es 4085, que se
corresponde con un disco de 4084 clusters (numerados de 2 a 4085). En principio, no existe ninguna
manera sencilla de averiguar el tipo de FAT de un disco, ya que el fabricante olvid incluir un byte
de identificacin al efecto. La documentacin publicada es contradictoria en las diversas fuentes
que he consultado, y en todas es por desgracia incorrecta (unos dicen que la FAT 16 comienza a
partir de 4078 clusters, otros que a partir de 4086, otros confunden el nmero de clusters con el
nmero ms alto de cluster...). Sin embargo, todas las versiones del DOS comprobadas (MS-DOS
3.1, 3.3, 4.0, 5.0 y DR-DOS 5.0 y 6.0) operan con una FAT de 16 bits en discos de 4085 clusters
(inclusive) en adelante; esto es, a partir de 4086 como nmero de cluster ms alto. Esto puede
verificarse fcilmente creando discos virtuales con 4084/4085 clusters, copiando algunos ficheros
y mirando la FAT con algn programa de utilidad (a simple vista se distingue si las entradas son de
12 16 bits). Por desgracia, salvo en MS-DOS 3.3 y en DR-DOS 6.0, los comandos CHKDSK del
sistema consideran errneamente que los discos de 4085, 4086 y 4087 clusters poseen una FAT
de 12 bits!, lo cual resulta adems completamente absurdo, dado que 4087 (0FF7h) es la marca de
cluster defectuoso en una FAT de 12 bits y en ningn caso podra ser un n mero de cluster
cualquiera!. Sin embargo, pese a este problema de CHKDSK, los discos con ms de 4084 clusters
han de ser diseados con una FAT de 16 bit, ya que es mucho ms grave tener problemas con el
DOS que con CHKDSK. Otra solucin es procurar no crear discos de ese n mero cr tico de
clusters, o confiar que el usuario no ejecute el casi olvidado CHKDSK sobre ellos. Por fortuna, los
discos normales no estn por ahora en la frontera crtica entre la FAT de 12 y la de 16 bits,
aunque con los discos virtuales s se pueden crear unidades con esos tamaos cr ticos: la casi
totalidad de los discos virtuales del mercado tienen problemas en estos casos. En algunos discos
duros se puede determinar tambin el tipo de FAT consultando la tabla de particiones, aunque no
es el mtodo ms conveniente. Debe tener en cuenta el lector que manipular una FAT sin conocer
su tipo supone destrozar la informacin almacenada en el disco. Sin embargo, tampoco hay que
tener tanto miedo: lo que s puede resultar peligroso es llegar al extremo de preguntar al usuario el
tipo de FAT...
Ahora puede surgir la pregunta: si la FAT mantiene una cadena que indica cmo est
distribuido un fichero en el disco, dnde se almacena el inicio de esa cadena, esto es, la primera
entrada en la FAT del fichero?.
En el byte de atributos, varios bits pueden estar activos a un tiempo. El atributo de sistema no
tiene un significado en particular, es una reliquia heredada del CP/M (los ficheros ocultos del
sistema lo tienen activo). En un mismo disco slo puede haber una entrada con el bit 3 activo;
adems, en este caso se interpretan el nombre y la extensin como un nico conjunto de 11
caracteres. Las entradas de tipo subdirectorio (bit 4 del byte de atributos activo) tienen un valor cero
en el campo de tamao (offset 28): el tamao de un fichero subdirectorio est determinado por
el nmero de entradas que ocupa en la FAT (en la prctica, esto sucede con cualquier otro
fichero, aunque si no es de directorio en el offset 28 esta informacin se indica con precisi n de
bytes).
El nombre del fichero puede comenzar por 0E5h, lo que indica que el fichero que estuvo ah ha
sido borrado. Si empieza por 2Eh (cdigo ASCII del punto (.)) por 2Eh, 2Eh (dos puntos
consecutivos) se trata de una entrada que referencia a un fichero subdirectorio.
Como hemos visto, un subdirectorio en principio puede ser una simple entrada del directorio
raz. El subdirectorio, fsicamente, es a su vez un fichero un tanto especial: contiene datos
binarios ... que son nada ms y nada menos que otras entradas de directorio para otros ficheros, de
32 bytes como siempre. Dentro de cada subdirectorio hay al menos dos entradas especiales: un
fichero con un nombre punto (.) que referencia al propio subdirectorio -que as puede
autolocalizarse- y otro con doble punto (..) que referencia al directorio padre -del que cuelga- siendo
posible, gracias a ello, retroceder cuanto se desee por el rbol de directorios sin necesidad de que
todos los caminos partan del raz. Si la primera entrada en la FAT del fichero (..) es un 0, quiere
decir que ese subdirectorio cuelga del raz, de lo contrario apuntar al primer cluster del fichero
subdirectorio padre.
Dicho sea de paso, tal vez sea una pena que el disco no conste de un nico fichero ra z
privilegiado de directorio, que podramos denominar subdirectorio raz. Ello permitira
tambin un nmero ilimitado de entradas (en vez de 112, 224, etc.) y sera ms lgico que
una ristra de sectores. Sin embargo, esta peculiar circunstancia tambin aparece en otros sistemas
operativos, como el UNIX. Sus motivos tendr.
El BPB (Bios Parameter Block) es una estructura de datos que contiene informacin relativa a
la unidad de disco. El BPB es una pieza vital en los controladores de dispositivo de bloques, como
veremos en un futuro captulo, por lo que a continuacin se expone su contenido (id ntico a
una parte del sector 0):
+---------------------------------------------------------------------------+
| offset 0 DW bytes_por_sector |
| offset 2 DB sectores_por_cluster |
| offset 3 DW sectores_reservados_al_comienzo_del_disco |
| offset 5 DB nmero_de_FATs |
| offset 6 DW nmero_de_entradas_en_el_directorio_raz |
| offset 8 DW nmero_total_de_sectores (0 con n de sector de 32 bits) |
| offset 10 DB byte_descriptor_de_medio |
| offset 11 DW numero_de_sectores_por_FAT |
| -- A partir del DOS 3.0: |
| offset 13 DW sectores_por_pista |
| offset 15 DW nmero_de_cabezas |
| offset 17 DD nmero_de_sectores_ocultos |
| -- A partir del DOS 4.0 (ms bien DOS 3.31) |
| offset 21 DD nmero_de_sectores (unidades con direccionamiento de |
| sector de 32 bits) |
| offset 25 DB 6 DUP (?) (6 bytes no documentados) |
| offset 31 DW nmero_de_cilindros |
| offset 33 DB tipo_de_dispositivo |
| offset 34 DW atributos_del_dispositivo |
+---------------------------------------------------------------------------+
El DOS convierte internamente el BPB en DPB (Drive Parameter Block), una estructura similar
con ms informacin til. Para obtener el DPB de una unidad determinada, puede utilizarse la
funcin 32h del DOS, Get Drive Parameter Block (indocumentada); la cadena de DPBs del DOS
puede recorrerse a partir del primer DPB (obtenido con la funcin 52h del DOS, Get List of Lists,
tambin indocumentada).
Las unidades que soportan estos disquetes, que tambin admiten los de 720K y 1.44M (aunque
a menudo no los de 2.88M) trabajan con controladoras SCSI e incorporan una BIOS propia para dar
soporte a estos dispositivos. El secreto de estos disquetes est en el posicionamiento ptico del
cabezal, lo que permite elevar notablemente el nmero de pistas. Por ejemplo, las unidades de 20
Mb parecen estar equipadas con 753 cilindros y 27 sectores/pista. Aunque en el sector de arranque
indica que posee 251 cilindros y 6 cabezales, el sentido comn nos permite deducir que esto no
puede ser as. Lo de los 27 sectores por pista parece indicar que la velocidad de transferencia de
estos disquetes es exactamente un 50% mayor que la de los convencionales de 1.44M (750 Kbit/seg
frente a 500 Kbit/seg).
El FORMAT del DOS 5.0 y posteriores puede formatear los disquetes floptical, pero lo hace a
bajo nivel, con lo que tarda cerca de 30-45 minutos en inicializarlos. Como ya vienen formateados
de fbrica, en realidad basta con aadirles un sector de arranque e inicializar la FAT y el
directorio raz. Tambin se puede verificar la superficie magntica para detectar posibles
sectores defectuosos. Los programas de utilidad que acompaan estas unidades realizan todas estas
tareas en unos 4 minutos. El tipo de FAT asignado puede ser seleccionado por el usuario (12 16
bits), as como otros parmetros tcnicos (tamao de clusters, etc.).
Las tarjetas controladoras suelen permitir un cierto grado de flexibilidad, de cara a seleccionar la
letra de unidad que se desea asignar al floptical. Configurndolo como A: se puede incluso
arrancar desde un disquete de stos.
Se puede acceder a varios niveles, siendo mejor el ms alto por razones de compatibilidad:
fichnom EQU $
buffer EQU $+80
Sin embargo, si se procede de esta ltima manera convendra asegurarse primero de que
existen 2128 bytes de memoria libres tras el cdigo del programa, ya que de esta manera el DOS
no realiza la comprobacin por nosotros (se limita a cargar cualquier programa que quepa en
memoria). De todas maneras, normalmente suele haber ms de 2128 bytes libres de memoria tras
cargar cualquier programa... Conviene hacer notar que si en lugar de DUP (0) se coloca DUP (?), el
linkador de Borland (TLINK 3.0), al contrario que el LINK de Microsoft, TAMPOCO reserva
espacio efectivo para esas variables. Esto slo sucede, lgicamente, cuando el DUP (?) est al
final del programa y no hay nada ms a continuacin -ni ms cdigo ni datos que no sean
DUP (?)-.
; ********************************************************************
; * *
; * MIRA.ASM - Utilidad para visualizar ficheros de texto. *
; * *
; ********************************************************************
mira SEGMENT
ASSUME CS:mira, DS:mira
mira ENDS
END inicio
La direccin del PSP en los programas COM viene determinada por la de cualquier registro de
segmento (CS=DS=ES=SS) nada ms comenzar la ejecucin del mismo. Sin embargo, en los
programas de tipo EXE slo viene determinada por DS y ES. En cualquier caso, existe una
funcin del DOS para obtener la direccin del PSP, cuyo uso recomienda el fabricante del
sistema en aras de una mayor compatibilidad con futuras versiones del sistema operativo. La
funcin es la 62h y est disponible a partir del DOS 3.0.
En la siguiente informacin, los campos del PSP que ocupen un byte o una palabra han de
interpretarse como tal; los que ocupen 4 bytes deben interpretarse en la forma segmento:offset. En
negrita se resaltan los campos ms importantes.
- offsets 2 al 3: una palabra con la direccin de memoria (segmento) del ltimo prrafo
disponible en el sistema. Teniendo en cuenta dnde acaba la memoria y el punto en que est
cargado nuestro programa, no es difcil saber la memoria que queda libre. Supuesto ES apuntando
al PSP:
MOV AX,ES:[2] ; prrafo ms alto disponible
MOV CX,ES ; segmento del PSP
SUB AX,CX ; AX = prrafos libres
MOV CX,16
MUL CX ; DX:AX bytes libres
- offset 4: no utilizado.
- offsets 5 al 9: salto al despachador de funciones del DOS (en CP/M se ejecutaba un CALL 5, el
MS-DOS tambin lo permite!). No es recomendable llamar al DOS de esta manera. Los PSP
creados por la funcin 4Bh en algunas versiones del DOS no tienen correctamente inicializado
este campo.
- offsets 0Ah al 0Dh: contenido previo del vector de terminacin (INT 22h).
- offsets 0Eh al 11h: contenido previo del vector de Ctrl-Break (INT 23h).
- offsets 12h al 15h: contenido previo del vector de manipulacin de errores cr ticos (INT 24h).
- offsets 18h al 2Bh: tabla de trabajo del sistema con los ficheros (Job File Table o JFT) : un byte
por handle (a 0FFh si cerrado; los primeros son los dispositivos CON, NUL, ... y siempre est n
abiertos). Slo hasta 20 ficheros (si no, vase offset 32h).
- offsets 2Ch al 2Dh: desde el DOS 2.0, una palabra que apunta al segmento del espacio de
entorno, donde se puede encontrar el valor de variables de entorno tan interesantes como PATH,
COMSPEC,... y hasta el nombre del propio programa que se est ejecutando en ese momento y el
directorio de donde se carg (no siempre es el actual; el programa pudo cargarse, apoyndose en
el PATH, en cualquier otro directorio diferente del directorio en curso). Vase el captulo 8 para
ms informacin de las variables de entorno.
- offsets 2Eh al 31h: desde el DOS 2.0, valor de SS:SP en la entrada a la ltima INT 21h invocada.
- offsets 32h al 33h: desde el DOS 3.0, nmero de entradas en la JFT (por defecto, 20).
- offsets 34h al 37h: desde el DOS 3.0, puntero al JFT (por defecto, PSP:18h). Desde el DOS 3.0
puede haber ms de 20 ficheros abiertos a la vez gracias a este campo, que puede ser movido de
sitio. Sin embargo, es slo a partir del DOS 3.3 cuando en un PSP hijo (por ejemplo, creado con la
funcin EXEC) se copia la informacin de ms que de los 20 primeros ficheros, si hay m s de
20. Se puede saber si un fichero es remoto (en la MS-net) comprobando si el byte de la JFT est
comprendido entre 80h-0FEh, aunque es mejor siempre acceder antes a las funciones del DOS.
- offsets 38h al 3Bh: desde el DOS 3.0, puntero al PSP previo (por defecto, 0FFFFh:0FFFFh en las
versiones del DOS 3.x); es utilizado por SHARE en el DOS 3.3.
- offsets 40h al 41h: desde el DOS 5.0, versin del sistema a devolver cuando se invoca la
funcin 30h.
- offsets 50h al 52h: cdigo de INT 21h/RETF. No recomendado hacer CALL PSP:5Ch para
llamar al DOS.
- offsets 5Ch al 7Bh: apuntan a los dos FCB's (File Control Blocks) usados anta o para acceder a
los ficheros (uno en 5Ch y el otro en 6Ch). Es una reliquia en desuso, y adem s este rea no se
inicializa si el programa es cargado en memoria superior con el comando LOADHIGH del MS-
DOS 5.0 y posteriores, por lo que no conviene usarlo ni siquiera para captar par metros, al menos
en programas residentes -susceptibles de ser instalados con LOADHIGH-. Si se utiliza el primer
FCB se sobreescribe adems el segundo.
- offsets 80h al 0FFh: es la zona donde aparecen los parmetros suministrados al programa. El
primer byte indica la longitud de los parmetros, despus vienen los mismos y al final un retorno
de carro (ASCII 13) que es un tanto redundante -a fin de cuentas, ya se sabe la longitud de los
parmetros-. Ese retorno de carro, sin embargo, no se cuenta en el byte que indica la longitud.
Tngase en cuenta que no son mayusculizados automticamente (estn tal y como los tecle el
usuario), y adems los parmetros pueden estar separados por uno o ms espacios en blanco o
tabuladores (ASCII 9).
En general, comprobar los valores que recibe el PSP cuando se carga un programa es una tarea
que se realiza de manera sencilla con el programa DEBUG/SYMDEB. Para ello basta una orden tal
como "DEBUG PROGRAMA.COM HOLA /T": al entrar en el DEBUG (o SYMDEB) basta con
hacer D 0 para examinar el PSP de PROGRAMA. Para ver los parmetros (HOLA /T en el
ejemplo) se hara D 80.
Las memorias ROM que incorporan diversas tarjetas (de vdeo, controladoras de disco duro, de
red) pueden estar ubicadas en cualquier punto del rea 0C0000h-0FFFFFh. La ROM BIOS del
ordenador se encarga de ir recorrindolas y entregndolas el control durante la inicializacin,
con objeto de permitirlas desviar vectores de interrupcin y ejecutar otras tareas propias de su
inicializacin.
La BIOS recorre este rea en incrementos de 2 Kb buscando la signatura 55h, 0AAh: estos dos
bytes consecutivos tienen que aparecer al principio para considerar que ah hay una ROM. El
tercer byte, que va detrs de stos, indica el tamao de esa extensin ROM en bloques de 512
bytes. Por razones de seguridad, se realiza una suma de comprobacin de toda la extensi n ROM
y si el resultado es 0 se considera una autntica ROM v lida. En ese caso, se entrega el control
(con un CALL entre segmentos) al cuarto byte de la extensin ROM. Ah habr de estar
ubicado el cdigo de la extensin ROM (habitualmente un salto a donde realmente comienza). Al
final del todo, el cdigo de la extensin ROM debe devolver de nuevo el control a la BIOS del
sistema, por medio de un retorno lejano (RETF).
El cdigo almacenado en estas extensiones ROM puede contener accesos directos al hardware
y llamadas a la ROM BIOS del sistema. Sin embargo, conviene recordar que el DOS no ha sido
cargado an y no se pueden emplear sus funciones. La ventaja de las extensiones ROM es que
aumentan las prestaciones del sistema antes de cargar el DOS. El inconveniente es que en otros
sistemas operativos (UNIX, etc.) que emplean el modo protegido, estas memorias ROM en general
no son accesibles. En la actualidad, con la disponibilidad de memoria superior bajo DOS, resulta
ms conveniente que las extensiones de hardware vengan acompaadas de drivers para DOS,
WINDOWS, OS/2,... que no con una ROM, mucho ms difcil de actualizar. Un ejemplo de
memoria ROM podra ser:
bios DB 55h, 0AAh
DB 32 ; 16 Kb de ROM
JMP inicio
...
...
fin_bios ... ; la suma de todos los bytes = 0
Los primeros ordenadores de IBM incorporaban una memoria ROM con el BASIC. El
COMMAND de aquellas versiones del DOS (desconozco si el actual tambin) era capaz de
ejecutar comandos internos definidos en estas ROM, al igual que un CLS o un DIR, vamos. El
formato era, por ejemplo:
bios_basic DB 55h, 0AAh
DB 64 ; 32 Kb de ROM-BASIC
JMP inicio
DB 5 ; longitud del siguiente comando
DB "BASIC"
JMP basic ; salto al comienzo del BASIC
DB 6 ; longitud del siguiente comando
DB "BASICA"
JMP basic ; salto al comienzo (el mismo del BASIC)
DB 0 ; no ms comandos
basic ...
...
fin_bios ... ; la suma de todos los bytes = 0
Si esto le parece una tontera al lector, es que no ha visto lo que vamos a ver ahora. Resulta que
tambin se pueden almacenar programas en BASIC (el cdigo fuente, aunque tokenizado) en las
BIOS. S, un listado en ROM!:
mortgagebas DB 55h, 0AAh
DB 48 ; 24 Kb de contabilidad
RETF ; nada que hacer
DB 0AAh, 55h ; esto es un listado BASIC
... ; aqu, el programa
fin_bios ... ; la suma de todos los bytes = 0
Los ficheros EXE constan de una cabecera, seguida de los segmentos de cdigo, datos y pila;
esta cabecera se carga en un buffer auxiliar y no formar parte de la imagen definitiva del
programa en memoria. A continuacin se explica el contenido de los bytes de la cabecera:
Offset 0 (2 bytes): Valores fijos 4Dh y 5Ah (en ASCII, 'MZ') 5Ah y 4Dh ('ZM'); esta
informacin indica que el fichero es realmente de tipo EXE y no lleva esa extensi n por antojo
de nadie.
Offset 6 (1 palabra): Nmero de reubicaciones a realizar. Indica cuntas veces se hace referencia
a un segmento absoluto: el montador del sistema operativo tendr que relocalizar en memoria
todas las referencias a segmentos absolutos segn en qu direccin se cargue el programa para
su ejecucin. En el ejemplo slo hay 1 (correspondiente a la instruccin MOV AX,datos).
Offset 8 (1 palabra): Tamao de esta cabecera del fichero EXE. La cabecera que estamos
analizando y que precede al cdigo y datos del programa ser ms o menos larga en funci n
del tamao de la tabla de reubicaciones, como luego veremos. En el ejemplo son 200h (=512)
bytes, el tamao mnimo, habida cuenta que slo hay una reubicacin (de hecho, a n
cabran muchas ms).
Offset 0Ah (1 palabra): Mnima cantidad de memoria requerida por el programa, en prrafos, en
adicin al tamao del mismo. En el ejemplo es 0 (el programa se conforma con lo que ocupa en
disco).
Offset 0Eh (2 palabras): Valores para inicializar SS (offset 0Eh) y SP (offset 10h). Evidentemente,
el valor para SS est an sin reubicar (habr de sumrsele el segmento en que se cargue el
programa). En el ejemplo, el SS relativo es 4 y SP = 200h (=512 bytes de tamao de pila definido).
Offset 12h (1 palabra): Suma de comprobacin: son en teora los 16 bits de menos peso de la
negacin de la suma de todas las palabras del fichero. El DOS debe hacer poco caso, porque
TLINK no se molesta ni en inicializarlo (El LINK de Microsoft s ). Olvidar este campo.
Offset 14h (2 palabras): Valores para inicializar CS (offset 16h) e IP (offset 14h). El valor para CS
est an sin reubicar y habr de sumrsele el segmento definitivo en que se cargue el
programa. En el ejemplo, el valor relativo de CS es 2, siendo IP = 0.
Offset 18h (1 palabra): Inicio de la tabla de reubicacin, expresado como offset. En el ejemplo es
3Eh, lo que indica que la tabla comienza en el offset 3Eh. Cada entrada en la tabla ocupa 4 bytes. La
nica entrada de que consta este programa tiene el valor 0002:0005 = 25h, lo que indica que en el
offset 200h+25h (225h) hay una palabra a reubicar -se suma 200h que es el tama o de la
cabecera-. En efecto, en el offset 225h hay una palabra a cero, a la que habr de sum rsele el
segmento donde sea cargado el programa. Esta palabra a cero es el operando de la instruccin
MOV AX,datos (el cdigo de operacin de MOV AX,n es 0B8h).
Daremos un breve repaso a los tipos de memoria asociados a los ordenadores compatibles en la
actualidad. Conviene tambin echar un vistazo al apndice I, donde se describe de manera ms
esquemtica, para completar la explicacin.
Es la memoria RAM comprendida entre los 0 y los 640 Kb; es la memoria utilizada por el DOS
para los programas de usuario. Los 384 Kb restantes hasta completar el megabyte se reservan para
otros usos, como memoria para grficos, BIOS, etc. En muchas mquinas, un buen fragmento de
esta memoria est ocupado por el sistema operativo y los programas residentes, quedando
normalmente no ms de 560 Kb a disposicin del usuario.
Este trmino, de reciente aparicin, designa el rea comprendida entre los 640 y los 1024 Kb
de memoria del sistema. Entre 1989 y 1990 aparecieron programas capaces de gestionar este rea
para aprovechar los huecos de la misma que no son utilizados por la BIOS ni las tarjetas gr ficas.
La memoria superior no se toma de la memoria instalada en el equipo, sino que est en ciertos
chips aparte relacionados con la BIOS, los grficos, etc. Por ello, un AT con 1 Mb de RAM
normalmente posee 640 Kb de memoria convencional y 384 Kb de memoria extendida. Los
segmentos A0000 y B0000 estn reservados para grficos, aunque rara vez se utilizan
simultneamente. El segmento C0000 contiene la ROM del disco duro en XT (en AT el disco duro
lo gestiona la propio BIOS del sistema) y/o BIOS de tarjetas gr ficas. El segmento D0000 es
empleado normalmente para el marco de pgina de la memoria expandida. El segmento E0000
suele estar libre y el F0000 almacena la BIOS del equipo. Los modernos sistemas operativos DOS
permiten (en los equipos 386 386sx y superiores) colocar memoria fsica extendida en el
espacio de direcciones de la memoria superior; con ello es factible rellenar los huecos vacos y
aprovecharlos para cargar programas residentes. Ciertos equipos 286 tambin soportan esta
memoria, gracias a unos chips de apoyo, pero no es frecuente.
Surgi en los PC/XT como respuesta a la necesidad de romper el l mite de los 640 Kb, y se
trata de un sistema de paginacin. Consiste en aadir chips de memoria en una tarjeta de
expansin, as como una cierta circuitera que permita colocar un fragmento de esa memoria
extra en lo que se denomina marco de pgina de memoria expandida, que normalmente es el
segmento D0000 del espacio de direcciones del 8086 (64 Kb). Este marco de pgina est
dividido en 4 bloques de 16 Kb. All se pueden colocar bloques de 16 Kb extrados de esos
chips adicionales por medio de comandos de E/S enviados a la tarjeta de expansin. Para que los
programas no tengan que hacer accesos a los puertos y para hacer ms cmodo el trabajo,
surgi la especificacin LIM-EMS (Lotus-Intel-Microsoft Expanded Memory System) que
consiste bsicamente en un driver instalable desde el config.sys que pone a disposici n de los
programas un amplio abanico de funciones invocables por medio de la interrupcin 67h. La
memoria expandida est dividida en pginas lgicas de 16 Kb que pueden ser colocadas en las
normalmente 4 pginas fsicas del marco de pgina. Los microprocesadores 386 (incluido
obviamente el SX) permiten adems convertir la memoria extendida en expandida, gracias a sus
mecanismos de gestin de memoria: en estas mquinas la memoria expandida es emulada por
EMM386 o algn gestor similar.
Es la memoria ubicada por encima del primer mega en los procesadores 286 y superiores. S lo
se puede acceder a la mayora de esta memoria en modo protegido, por lo que su uso queda
relegado a programas complejos o diversos drivers que la aprovechen (discos virtuales, cachs de
disco duro, etc.). Hace ya bastante tiempo se dise una especificacin para que los programas
que utilicen la memoria extendida puedan convivir sin conflictos: se trata del controlador XMS.
Este controlador implementa una serie de funciones normalizadas que adems facilitan la
utilizacin de la memoria extendida, optimizando las transferencias de bloques en los 386 y
superiores (utiliza automticamente palabras de 32 bits para acelerar el acceso). La
especificacin XMS viene en el programa HIMEM.SYS, HIDOS.SYS y en algunas versiones del
EMM386. El controlador XMS tambin aade funciones normalizadas para acceder a la
memoria superior.
Son 64 bytes de memoria (128 en algunas mquinas) ubicados en el chip del reloj de tiempo
real de la placa base de los equipos AT y superiores. A esta memoria se accede por dos puertos de
E/S y en ella se almacena la configuracin y fecha y hora del sistema, que permanecen tras apagar
el ordenador (gracias a las pilas). Evidentemente no se puede ejecutar cdigo sobre la RAM
CMOS (Ni pueden esconderse virus, al contrario de lo que algunos mal informados opinan. Otra
cosa es que utilicen algn byte de la CMOS para controlar su funcionamiento).
Se trata de los primeros 64 Kb de la memoria extendida (colocados entre los 1024 y los 1088
Kb). Normalmente, cuando se intentaba acceder fuera del primer megabyte (por ejemplo, con un
puntero del tipo FFFF:1000 = 100FF0) un artificio de hardware lo impeda, convirtiendo esa
direccin en la 0:0FF0 por el simple procedimiento de poner a cero la l nea A20 de direcciones
del microprocesador en los 286 y superiores. Ese artificio de hardware lo protagoniza el chip
controlador del teclado (8042) ya que la lnea A20 pasa por sus manos. Si se le insta a que
conecte los dos extremos (enviando un simple comando al controlador del teclado) a partir de ese
momento es el microprocesador quien controla la lnea A20 y, por tanto, en el ejemplo anterior se
hubiera accedido efectivamente a la memoria extendida. Los nuevos sistemas operativos DOS
habilitan la lnea A20 y, gracias a ello, estn disponibles otros 64 Kb adicionales. Para ser
exactos, como el rango va desde FFFF:0010 hasta FFFF:FFFF se puede acceder a un total de 65520
bytes (64 Kb menos 16 bytes) de memoria. Tngase en cuenta que las direcciones FFFF:0000 a la
FFFF:000F estn dentro del primer megabyte. En el HMA se cargan actualmente el DR-DOS
5.0/6.0 y el MS-DOS 5.0 y posteriores; evidentemente siempre que el equipo, adems de ser un
AT, disponga como mnimo de 64 Kb de memoria extendida. En ciertos equipos poco compatibles
es difcil habilitar la lnea A20, por lo que el HIMEM.SYS de Microsoft dispone de un
parmetro que se puede variar probando docenas de veces hasta conseguirlo, si hay suerte
(adems, hay BIOS muy intervencionistas que dificultan el control de A20).
Vamos ahora a conocer con profundidad la manera en que el sistema operativo DOS gestiona la
memoria; un tema poco tratado, ya que esta informacin no est oficialmente documentada por
Microsoft.
Los bloques de memoria en el DOS son agrupaciones de bytes siempre mltiplos enteros de 16
bytes: en realidad son agrupaciones de prrafos. La memoria de un PC -siempre bajo DOS- est ,
por tanto, dividida en grupos de prrafos. Por tanto, una palabra de 16 bits permite almacenar la
direccin del prrafo de cualquier posicin de memoria dentro del megabyte direccionable por
el 8086. Todo bloque de memoria tiene asociado un propietario, que bien puede ser el DOS o un
programa residente que haya solicitado al DOS el control de dicho bloque. Cuando se ejecuta un
programa, el sistema crea dos bloques para el mismo: el bloque de memoria del programa y el
bloque de memoria del entorno.
Todos los bloques de memoria (tanto programa como entorno) vienen precedidos por una
cabecera de un prrafo (16 bytes) que almacena informacin relativa al mismo. Esta cabecera
recibe el nombre tcnico de MCB (Memory Control Block) y tiene la siguiente estructura:
En el offset 0 se sita el byte de marca (4Dh si no es el ltimo MCB de la cadena de MCB's
en memoria, 5Ah si es el ltimo), en el offset 1 hay una palabra que indica el PID del programa
propietario del bloque, en el offset 3 otra palabra indica el tamao (como siempre, p rrafos) del
bloque, sin incluir este prrafo del MCB. Los bytes que van del 5 al 7 est n reservados. Entre el
8 y el 15 se sita el nombre del programa propietario, aunque esta informacin s lo existe en
los bloques de programa y con MS-DOS 4.0 posterior (tambin en DR-DOS 5.0/6.0, aunque
este operativo es aparentemente un DOS 3.31). El nombre acaba con un cero si tiene menos de 8
caracteres (en DR-DOS 5.0 acaba siempre con un cero, trunc ndose el 8 car cter si lo hab a;
esta errata ha sido corregida en DR-DOS 6.0).
Como todos los bloques de memoria estn ubicados unos tras otros, y adems se conoce el
tamao de los mismos, es factible hacer un programita que recorra la cadena de bloques de
memoria hasta que se encuentre uno cuyo byte de marca valga 5Ah ( ltimo MCB), pudi ndose
identificar los programas residentes cargados y la memoria que emplean. La direccin del primer
MCB era al principio un secreto de Microsoft, aunque hoy casi todo el mundo sabe que las
siguientes lneas:
MOV AH,52h
INT 21h
MOV AX,ES:[BX-2]
Los bloques de datos aparecen en raras ocasiones, debido al uso de las funciones del sistema
operativo para localizar bloques de memoria. Cuando un programa se ejecuta, tiene asignada la
mayor parte de la memoria para s, pero es perfectamente factible que solicite al DOS una
reduccin de la memoria asignada (funcin 4Ah) y, con los Kb que haya liberado, puede volver a
llamar al DOS para crear bloques de memoria (funcin 48h) o destruirlos (con la funcin 49h).
Resulta triste ver como algunos sofisticados programas residentes llegan incluso a
autorrelocalizarse en memoria machacando parte del PSP con objeto de economizar algunos bytes;
despus un alto porcentaje de los mismos se olvida de liberar el espacio de entorno, que para nada
utilizan y que suele ocupar incluso ms memoria que todo el PSP.
Adicionalmente, el DOS 5.0 introdujo los bloques denominados UMB que recorren la memoria
superior, en las diferentes reas en que puede estar fragmentada. Acceder a estos bloques de
control de memoria es bastante complicado: el segmento donde empiezan est almacenado en el
offset 1Fh de la tabla de informacin sobre buffers de disco, cuya direccin inicial a su vez se
obtiene en el puntero largo que devuelve en ES:BX+12h la funcin indocumentada Get List of
Lists (52h): normalmente el resultado es el segmento 9FFFh. En general, es ms sencillo ignorar
la memoria superior como una entidad independiente y recorrer toda la memoria sin m s. Sin
embargo, para poder acceder a los bloques de memoria superior stos han de estar ligados a los de
la memoria convencional: para conectarlos, si no lo estn, puede emplearse la funci n,
tradicionalmente indocumentada (aunque recientemente ha dejado de serlo) Get or Set Memory
Allocation Strategy (58h) del DOS: es conveniente preservarla antes y volver a restaurar esta
informacin despus de alterarla. En cualquier caso, el formato de los bloques de control UMB
es el siguiente:
offset 0: Byte con valor 5Ah para el ltimo bloque y 4Dh en otro caso.
offset 1: Palabra con el PID.
offset 3: Palabra con el tamao del bloque en prrafos.
offset 8: 8 Bytes: "UMB" si es el primer bloque UMB y "SM" si es el ltimo.
El programa de ejemplo listado ms abajo recorre toda la memoria sin adentrarse en las
particularidades de ningn sistema operativo. Tan slo se toma la molestia de intentar detectar si
existe memoria superior y, en ese caso, mostrar tambin su contenido. Este algoritmo puede no
ensear todo lo que podra ensear gracias a las ltimas versiones del DOS, pero s gran
parte, y funciona en todas las versiones. Para comprobar si existe memoria superior utiliza una
tcnica muy sencilla: al alcanzar el ltimo bloque de memoria, se comprueba si el siguiente
empezara en el segmento 9FFFh en vez del A000h como cabra esperar en una mquina de
640Kb (slo suelen tener memoria superior las mquinas que al menos tienen 640 Kb). Si esto es
as no se considera que el bloque sea el ltimo y se prosigue con el siguiente, saltando la barrera
de los 640 Kb. En este caso, obviamente, los 16 bytes que faltan para completar los 640 Kb de
memoria son precisamente un MCB. Esta tcnica funciona slo a partir del MS-DOS 5.0; en DR-
DOS 6.0, si la memoria superior est inhibida con MEMMAX -U, no funciona (DR-DOS 6.0 se
encarga de machacar el ltimo MCB de la memoria convencional y no deja ni rastro) aunque s
con MEMMAX +U. Tambin se imprime el nombre de los programas, aunque en DOS 3.30 y
versiones anteriores salga basura. Adems, el PID de tipo 6 se interpreta como un bloque de
memoria superior XMS -que se estudiar en el siguiente apartado de este mismo cap tulo- bajo
DR-DOS 6.0, imprimindose tambin el nombre.
La primera accin de MAPAMEM al ser ejecutado es rebajar la memoria que tiene asignada
hasta el mnimo necesario; por ello en el resultado figura ocupando s lo 1440 bytes y teniendo
tras de s un gran bloque libre. Es conveniente que los programas rebajen al principio la memoria
asignada con objeto de facilitar el trabajo bajo ciertos entornos pseudo-multitarea soportados por el
DOS; de hecho, es norma comn en el cdigo generado por los compiladores realizar esta
operacin al principio. Sin embargo, no todo el mundo se preocupa de ello y, a fin de cuentas,
tampoco es tan importante.
Un ejemplo de la salida que puede producir este programa es el siguiente, tomado de una
mquina con memoria superior y bajo los dos sistemas operativos ms comunes (aunque en los
ejemplos los espacios de entorno han coincidido junto al bloque de programa, ello no siempre
sucede as). Las diferentes ocupaciones de memoria de los programas en ambos sistemas
operativos se deben frecuentemente a que se trata de versiones distintas:
DR-DOS 6.0 MS-DOS 5.0
MAPAMEM 2.2 MAPAMEM 2.2
- Informacin sobre la memoria del sistema. - Informacin sobre la memoria del sistema.
Tipo Ubicacin Tamao PID Propietario Tipo Ubicacin Tamao PID Propietario
-------- --------- ------- ----- --------------- -------- --------- ------- ----- ---------------
Sistema 0000-003F 1.024 Interrupciones Sistema 0000-003F 1.024 Interrupciones
Sistema 0040-004F 256 Datos del BIOS Sistema 0040-004F 256 Datos del BIOS
Sistema 0050-023C 7.888 Sistema Operat. Sistema 0050-0252 8.240 Sistema Operat.
Sistema 023E-02FD 3.072 0008 Sistema 0254-045F 8.384 0008
Programa 02FF-031E 512 02FF COMMAND Sistema 0461-0464 64 0008
Entorno 0320-033F 512 02FF COMMAND Programa 0466-050E 2.704 0466 COMMAND
Datos 0341-0358 384 02FF COMMAND Libre 0510-0513 64 0000 <Nadie>
Programa 035A-03EE 2.384 035A MATAGAME Entorno 0515-0544 768 0466 COMMAND
Entorno 03F0-0408 400 040A KEYRESET Entorno 0546-0567 544 0569 MAPAMEM
Programa 040A-041D 320 040A KEYRESET Programa 0569-05C2 1.440 0569 MAPAMEM
Entorno 041F-0437 400 0439 MAPAMEM Libre 05C4-9FFE 631.728 0000 <Nadie>
Programa 0439-0492 1.440 0439 MAPAMEM Sistema A000-D800 229.392 0008
Libre 0494-9FFE 636.592 0000 <Nadie> Sistema D802-E159 38.272 0008
Sistema A000-DEFF 258.048 0007 Libre E15B-E17F 592 0000 <Nadie>
Sistema DF01-E477 22.384 0008 Programa E181-E18D 208 E181 DOSVER
Sistema E479-E483 176 0008 Programa E18F-E23C 2.784 E18F NLSFUNC
Sistema E485-E48D 144 0008 Programa E23E-E3AF 5.920 E23E GRAPHICS
Sistema E48F-E591 4.144 0008 Programa E3B1-E533 6.192 E3B1 SHARE
Sistema E593-E7DA 9.344 0008 Programa E535-E637 4.144 E535 DOSKEY
Sistema E7DC-E806 688 0008 Programa E639-E7E2 6.816 E639 PRINT
Sistema E808-E810 144 0008 Programa E7E4-E840 1.488 E7E4 RCLOCK
Sistema E812-E81A 144 0008 Programa E842-E862 528 E842 DISKLED
Sistema E81C-E8DE 3.120 0008 Programa E864-ECF0 18.640 E864 DATAPLUS
Programa E8E0-EA51 5.920 E8E0 GRAPHICS Programa ECF2-ED59 1.664 ECF2 HBREAK
Programa EA53-EA60 224 EA53 CLICK Programa ED5B-ED7E 576 ED5B ANSIUP
Programa EA62-EA6E 208 EA62 DOSVER Programa ED80-ED8C 208 ED80 PATCHKEY
Programa EA70-EA7F 256 EA70 ALTDUP Programa ED8E-ED93 96 ED8E TDSK
Area XMS EA81-EA8F 240 0006 B1M92VAC Datos ED95-F6D4 37.888 ED8E TDSK
Programa EA91-EAC0 768 EA91 VSA Libre F6D6-F6FF 672 0000 <Nadie>
Area XMS EAC2-EB17 1.376 0006 RCLOCK
Area XMS EB19-EB30 384 0006 DISKLED
Programa EB32-EDB4 10.288 EB32 VWATCH
Area XMS EDB6-EEEC 4.976 0006 DATAPLUS
Area XMS EEEE-EF4F 1.568 0006 HBREAK
Libre EF51-EFFE 2.784 0000 <Nadie>
Sistema F000-F5FF 24.576 0007
Sistema F601-F6FF 4.080 0008
Listado de MAPAMEM 2.2
Adicionalmente, el controlador XMS aade funciones para gestionar la memoria superior. Los
bloques de memoria superior no son accesibles de manera directa por los programas, a menos que
stos sean expresamente cargados en este rea con HILOAD LOADHIGH. Sin embargo, los
programas pueden solicitar zonas de memoria superior al controlador XMS, que adems de la
memoria extendida gestiona tambin estas reas. Estos bloques de memoria son gestionados de
manera independiente a los de la memoria convencional, existiendo funciones especficas del
controlador XMS para localizar y liberar los bloques. Con DR-DOS 6.0 y algunos gestores de
memoria, en la memoria superior pueden residir tanto bloques de memoria DOS gestionados por el
sistema (normalmente, como consecuencia de un HILOAD para instalar programas residentes),
as como autnticos bloques de memoria XMS. Realmente, las zonas que emplea el DR-DOS no
son sino bloques de este tipo de memoria.
El MS-DOS 5.0 y posteriores, sin embargo, reservan toda la memoria superior para sus propios
usos -cargar programas residentes- cuando se indica DOS=UMB en el CONFIG.SYS; por lo que si
alguna aplicacin solicita memoria superior XMS no la encontrar. Pero se puede emplear la
funcin 58h para conectar la memoria superior y a continuacin, con la misma funcin, cambiar
la estrategia de asignacin de memoria para que el sistema asigne memoria superior en respuesta a
las funciones ordinarias de asignacin de memoria. Despus es conveniente restaurar la
estrategia de asignacin y el estado de la memoria superior a la situaci n inicial (tambi n se
puede consultar previamente con la funcin 58h).
La hecho de que un programa pueda solicitar memoria superior al sistema es una posibilidad
interesante: ello permite a los programas residentes auto-relocalizarse de una manera sencilla a estas
zonas, anticipndose a la actuacin de usuarios inexpertos que podran olvidarse del HILOAD
o el LOADHIGH. Por otra parte, se economiza algo de memoria al poder suprimirse el PSP en la
copia. Con MS-DOS 5.0 y posteriores, no obstante, el programa deber dejar algo residente en
memoria convencional (si no se termina residente, el sistema libera los bloques asignados en
memoria superior) o bien modificar el PID de los bloques en memoria superior para que al terminar
sin quedar residente el DOS no los libere.
Para poder emplear los servicios del controlador XMS hay que verificar primero que est
instalado el programa HIMEM.SYS o alguno equivalente (el EMM386 del DR-DOS 6.0 integra
tambin las funciones del HIMEM.SYS, as como el QEMM386). Para ello se chequea la
entrada 43h en la interrupcin Multiplex, comprobando si devuelve 80h en el registro AL (y no
0FFh como otros programas residentes):
MOV AX,352Fh ; obtener vector de INT 2Fh en
ES:BX
INT 21h
MOV AX,ES
CMP AX,0
JE no_hay_XMS ; en DOS 2.x la INT 2Fh est
indefinida
MOV AX,4300h ; chequear presencia de XMS
INT 2Fh ; interrupcin Multiplex
CMP AL,80h
JE hay_XMS
JNE no_hay_XMS
Antes de llamar a la INT 2Fh se comprueba que esta interrupci n est apuntando a alg n
sitio (con el segmento distinto de 0) ya que en algunas versiones 2.x del DOS est sin inicializar y
el sistema se cuelga si se invoca sin precauciones. Las funciones del controlador XMS no se
invocan por medio de ninguna interrupcin, como sucede con las del DOS o la BIOS. En su lugar,
una vez detectada la presencia del mismo se le debe interrogar preguntndole dnde est
instalado, por medio de la subfuncin 10h:
MOV AX,4310h ; preguntar direccin del
controlador
INT 2Fh
MOV XMS_seg,ES ; almacenarla
MOV XMS_off,BX
Posteriormente, cuando haya que utilizar un servicio o funcin del controlador XMS se
colocar el nmero del mismo en AH y se ejecutar un CALL gestor_XMS. Para utilizar las
llamadas al XMS es preciso que en la pila queden al menos 256 bytes libres. En un ap ndice al
final del libro se listan y documentan todas las funciones XMS.
Si por cualquier motivo fuera necesario en un programa residente interceptar las llamadas al
controlador XMS realizadas por los programas de aplicacin, hay que decir que ello es posible.
Por supuesto, no es tan sencillo como desviar un vector de interrupcin: hay que modificar el
cdigo del propio controlador. Por fortuna, todos los controladores XMS suelen comenzar con una
instruccin de salto larga o corta (JMP XXXX:XXXX, JMP XXXX, JMP SHORT XX) y, si sta
ocupa menos de 5 bytes, los restantes estn cubiertos de instrucciones NOP (cdigo de
operacin 90h). Se pueden modificar los primeros bytes del mismo para poner un salto hacia
nuestra propia rutina, que luego acabe llamando a su vez al controlador previo (el RAMDRIVE de
Microsoft, por ejemplo, realiza esta complicada maniobra).
Para utilizar la memoria expandida hay que invocar la interrupcin 67h. Para detectar la
presencia del controlador hay dos mtodos. El primero consiste en buscar un dispositivo
"EMMXXXX0", ya que el gestor de memoria expandida se carga desde el CONFIG.SYS y define
un controlador de dispositivo de caracteres con ese nombre. Es tan sencillo como intentar abrir un
fichero con ese nombre y comprobar si existe. Desde la lnea de comandos del DOS se puede
hacer as:
IF EXIST EMMXXXX0 ECHO HAY CONTROLADOR EMS
Existe el riesgo de que en lugar de un controlador con ese nombre se trate de un fichero que
algn gracioso haya creado!: para cerciorarse, hay unas funciones de control IOCTL en el DOS
para asegurar que se trata de un dispositivo y no de un fichero. Sin embargo, no es recomendable
este mtodo para detectar el EMM en los programas residentes y en los controladores de
dispositivo: existe otro medio ms conveniente para esos casos, que tambi n puede ser empleado
de manera general en cualquier otra aplicacin. Consiste en buscar la cadena "EMMXXXX0" en
el offset 10 del segmento apuntado por el vector 67h (despreciando el offset de dicho vector)
as de sencillo!.
En los modernos sistemas operativos, la memoria expandida soportada a partir de las versiones
4.0 del EMM (Expanded Memory Manager) cubre un amplio espectro del espacio de direcciones
dentro del megabyte gestionado por el MS-DOS. Aqu, las pginas no han de ser necesariamente
consecutivas; son ms de 4 y tampoco tienen que ser necesariamente de 16 Kb. Sin embargo, por
defecto -y por razones de compatibilidad- las cuatro primeras pginas fsicas estn colocadas
adyacentemente por encima de los 640K y son de 16 Kb, no siendo recomendable modificar esta
especificacin. Por ejemplo, en el sistema 386 en que se escribieron las primeras versiones de este
libro, con un EMM 4.0, las pginas fsicas 0 a la 3 estaban ubicadas a partir de la direcci n
0C8000h; las pginas 4 a la 27h estaban ubicadas entre la direccin 10000h a la 9FFFFh,
cubriendo tambin los primeros 640 Kb (excepto los primeros 64 Kb).
Si alguien est pensando en desviar la interrupcin 67h desde un programa residente, para
interceptar y manipular las llamadas de los programas de aplicacin a esa interrupcin, ya puede
ir olvidndose. La razn es que los 386 y superiores estn en modo virtual 86 con los
controladores EMS instalados. Esto significa que cuando un programa invoca una interrupcin,
como la INT 67h, la CPU -de la manera que est programada- pasa inmediatamente a
continuacin a ejecutar una rutina en modo protegido fuera del espacio de direcciones del MS-
DOS. Con algunos gestores de memoria, como el EMM386 del DR-DOS 6.0, no sucede nada: ese
programa supervisor retorna a la tarea virtual y ejecuta el cdigo ubicado en el espacio de
direcciones del MS-DOS. Sin embargo, con QEMM386, el controlador de memoria est ubicado
fuera de ese espacio de direcciones, y ya no vuelve a l. Si se mira con el DEBUG a donde apunta
la INT 67h en una mquina con QEMM (por ejemplo, traceando una llamada a la interrupci n),
se ver que este vector apunta al siguiente cdigo:
INT 28h
IRET
Evidentemente, ese no es el controlador de memoria!. Para acceder a l hay que ejecutar una
interrupcin de verdad. Supongo que a travs de la especificacin VCPI (Virtual Control
Program Interface) que regula el acceso a los modos extendidos del 386, habr algn medio de
poder acceder al cdigo del controlador EMS, o interceptar las llamadas. Sin embargo, no es tan
fcil como cambiar un vector...
Captulo IX: SUBPROCESOS, RECUBRIMIENTOS Y FILTROS
Para llamar a la funcin EXEC para cargar y ejecutar un programa se pone un 0 en AL. Hay
que apuntar DS:DX a la direccin del nombre del programa (una cadena ASCIIZ, esto es,
terminada por cero) que puede incluir la ruta de directorios y debe incluir la extensin. Tambi n
hay que apuntar en ES:BX a una estructura de datos (bloque de parmetros) que se interpreta de la
siguiente forma:
Segmento donde est el entorno a copiar para crear el del programa cargado. A 0 si es el
offset 0: del programa padre. Los programas hijos siempre accedern a una copia y no al
original.
Doble palabra que apunta a los parmetros del programa a ejecutar (los que ese
offset 2: programa admite, por s solo, en la lnea de comandos). Tiene el mismo formato que
el contenido de PSP:80h.
offset 6: Doble palabra que apunta al primer FCB a copiar en el proceso hijo.
offset 10: Doble palabra que apunta al segundo FCB a copiar en el proceso hijo.
offset 14: Si se carga sin ejecutar, devuelve el SS:SP inicial del subprograma.
offset 18: Si se carga sin ejecutar, devuelve el CS:IP inicial del subprograma.
El subprograma cargado hereda los ficheros abiertos del programa padre. Antes de llamar a esta
funcin, el ordenador debe tener suficiente memoria libre. Cuando se ejecuta un programa COM
ordinario, toda la memoria del sistema est asignada al mismo (el mayor bloque en realidad, lo
que en la prctica significa toda la memoria). Por tanto, un programa COM que desee cargar otros
programas debe primero rebajar la memoria que el DOS le ha asignado y quedarse slo con la que
necesita. Con los programas EXE, la cantidad de memoria que les asigna el DOS inicialmente
depende del compilador y las opciones de compilacin; en ensamblador suele ser tambin toda la
memoria, por lo que es deber de ste liberar la que no necesita. Para ello, se calcula cuanta
memoria necesita el programa y se llama a la funcin del sistema para modificar el tama o del
bloque de memoria del propio programa (funcin 4Ah del DOS, pasando en ES la direcci n del
PSP).
En los programas COM, la pila est apuntando al final del segmento (SP est pr ximo a
0FFFEh). Por ello, si el programa va a ocupar menos de 64 Kb, ser preciso mover SP m s abajo
para que no se salga del futuro bloque de memoria del programa. Si no se toma esta precauci n,
SP apuntar dentro del siguiente bloque de memoria, que es ms que probablemente el que
utilizar EXEC, con lo que el ordenador debera colgarse a no ser que haya mucha suerte.
Tras llamar a la funcin EXEC, en teora todos los registros son destruidos, segn la
documentacin oficial, incluidos SS:SP. Esto significa que antes de llamar a EXEC deben apilarse
los registros que no se desee alterar y guardar en un par de variables SS y SP. Tras llamar a EXEC,
inmediatamente a continuacin y antes de hacer nada se deben recargar SS y SP, para proceder
despus a recuperar de la pila los dems registros. Este comportamiento de EXEC parece romper
la tnica habitual de comportamiento del DOS. Sin embargo, lo cierto es que esto slo suced a
en el DOS 2.X: aunque Microsoft no lo diga oficialmente, las versiones posteriores del sistema
slo corrompen DX y BX al llamar a EXEC.
El siguiente programa de ejemplo, de tipo COM, realiza todas las tareas necesarias para cargar
otro programa. Como ejemplo, he decidido cargar el COMMAND.COM, aunque el programa a
ejecutar podra ser cualquier otro; la ventaja de COMMAND es que crea una nueva sesi n de
intrprete de comandos y permite comprobar con comodidad qu ha sucedido con la memoria.
; ********************************************************************
; * *
; * SHELL.ASM 1.0 - Demostracin de carga de subprograma. *
; * *
; ********************************************************************
shell SEGMENT
ASSUME CS:shell, DS:shell
ORG 100h
inicio:
MOV SP,TAMTOT ; redefinir la pila
MOV AH,4Ah
MOV BX,TAMTOT/16
INT 21h ; redimensionar bloque memoria
LEA DX,hola_txt
MOV AH,9
INT 21h ; mensaje de bienvenida
LEA BX,exec_info
MOV WORD PTR [BX],0
MOV WORD PTR [BX+2],80h ; PSP
MOV WORD PTR [BX+4],CS
MOV WORD PTR [BX+6],5Ch ; FCB 0
MOV WORD PTR [BX+8],CS
MOV WORD PTR [BX+0Ah],6Ch ; FCB 1
MOV WORD PTR [BX+0Ch],CS
LEA DX,nombre
MOV AX,4B00h
INT 21h ; cargar y ejecutar programa
PUSH CS
POP DS ; DS = CS
LEA DX,adios_txt
MOV AH,9
INT 21h ; mensaje de despedida
MOV AX,4C00h
INT 21h ; terminar
shell ENDS
END inicio
C:\COMPILER\86\AREA>mapamem
MAPAMEM 2.2
- Informacin sobre la memoria del sistema.
C:\COMPILER\86\AREA>exit
C:\COMPILER\86\AREA>_
La subfuncin EXEC para cargar un programa sin ejecutarlo se selecciona con AL=1; ES:BX
apunta al bloque de parmetros que se defini para el caso normal de carga+ejecucin. Esta
subfuncin asigna el PID, no obstante, al PSP del subprograma cargado.
La subfuncin de EXEC para cargar un overlay o recubrimiento, se llama con los mismos
valores en los registros que la anterior, exceptuando AL (que ahora vale 3). Sin embargo el bloque
de parmetros apuntado por ES:BX es ahora mucho ms sencillo:
Offset 0: Segmento donde cargar el overlay (la memoria ha de asignarla el programa principal).
Offset 2: Factor de reubicacin, si se trata de un fichero EXE (normalmente el mismo valor que
el anterior, si el subprograma va a correr en el mismo segmento en que es cargado).
El overlay puede haber sido ensamblado, por ejemplo, con un desplazamiento relativo nulo
(ORG 0) de manera que para llamarlo hay que hacer un CALL FAR al segmento donde ha sido
cargado, con un offset 0. Claro que tambin se puede calcular la distancia que hay entre el
segmento del programa principal y el del overlay, multiplicarlo por 16 y utilizarlo como offset en la
llamada al mismo segmento del programa principal. Sin embargo, esto requiere que el overlay sea
ensamblado con cierto offset ... a calcular. Quienes proponen este segundo mtodo -que los hay-
andaban ese da ms bien despistados. En general, la programacin con overlays es compleja, y
ms an si los overlays constan de varios segmentos internos.
9.2. - FILTROS.
El DOS es un sistema operativo que soporta el redireccionamiento. Las posibilidades son, sin
embargo, muy limitadas. La razn es la ineficiencia del sistema en las operaciones de entrada y
salida, que obliga a las aplicaciones a hacer accesos directos al hardware. Por ejemplo: con el
comando interno CTTY, a travs de un puerto serie es factible poner a un PC como servidor
remoto de otro. Esto permite operar en la lnea de comandos desde el terminal remoto ubicado a
varios metros de distancia. Sin embargo, nada ms ejecutar un programa, el teclado del PC con el
emulador de terminal dejar de funcionar y ser preciso utilizar el del propio servidor!: la
razn es que muy pocos programas usan el DOS para leer el teclado; no digamos para escribir en
la pantalla...
Sin embargo, an en la actualidad muchos usuarios de PC trabajan en la l nea de comandos,
donde s es posible, como se ha mencionado, utilizar el DOS como un sistema con dispositivos de
entrada y salida estndar que soportan el redireccionamiento. El redireccionamiento bajo DOS es
empleado sobre todo para procesar ficheros de texto.
Un filtro es un programa normal que lee datos de la entrada estndar (por defecto, el teclado),
los procesa de alguna manera y los deposita en la salida estndar (por defecto, la pantalla). Tanto
la entrada como la salida estndar, popularmente conocidas como STDIN y STDOUT,
respectivamente, as como la salida estndar para errores (STDERR) son dispositivos
permanentemente abiertos en el DOS. Tienen asociados un handle de control, como cualquier
fichero: 0 para STDIN (denominado CON), 1 para STDOUT (tambin conocido por CON), 2 para
STDERR (tambin CON), 3 para la salida serie (denominada AUX) y 4 para la impresora
(conocida por PRN).
Por tanto, un filtro normal debe limitarse a leer, con las funciones de manejo de ficheros
ordinarias, informacin procedente del handle 0; tras procesarla debe escribirla en el handle 1. Si
se produce un error en el proceso, o hay una salida de log que no deba mezclarse con la salida
deseada por el usuario, se puede escribir el mensaje en el handle 2. El redireccionamiento y el
sistema de ficheros por handle fue incluido a partir del DOS 2.0 (en versiones anteriores no hay
siquiera subdirectorios).
Cuando se ejecuta una orden del tipo COMANDO | FILTRO, el intrprete de comandos cierra
la salida estndar y crea un fichero auxiliar (de nombre extrao); a continuaci n abre ese
fichero para salida: como al cerrar la salida estndar se haba liberado el handle 1, ese handle
ser asignado al nuevo fichero. Esto significa que toda la salida de COMANDO no ir a la
pantalla (CON) sino al fichero auxiliar. Cuando se acabe de ejecutar COMANDO, el int rprete de
mandatos cerrar el fichero auxiliar y volver a abrir la salida estndar, restaurando el sistema
al estado normal. Pero la cosa no queda ah, evidentemente: a continuacin se cierra la entrada
estndar y se abre como entrada el fichero auxiliar recin creado, que pasar a ser el nuevo
dispositivo de entrada por defecto. Seguidamente, se carga y ejecuta FILTRO, que tomar los
datos del fichero auxiliar en lugar del teclado. Al final, el fichero auxiliar es cerrado y borrado,
abrindose y restaurndose la entrada por defecto normal. Si se ejecuta DIR | SORT, aparte del
directorio ordenado aparecern dos extraos ficheros con 0 bytes (este era su tamao cuando se
ejecut DIR): el DOS crea dos ficheros auxiliares para sustituir la entrada y salida est ndar,
aunque en este ejemplo slo se emplee uno de ellos. Actuarn los dos si se utilizan filtros
encadenados que obliguen a redireccionar simultneamente tanto la entrada como la salida a
ficheros auxiliares, en una orden del tipo DIR | SORT | MORE. A partir del DOS 5.0, si est
definida la variable de entorno TEMP los ficheros auxiliares se crean donde sta indica y no en el
directorio activo, por lo que a simple vista podran no verse dichos ficheros.
Cuando se utilizan los redirectores habituales ('<', '>', '<<' y '>>') suceden procesos similares,
todos ellos desencadenados por COMMAND.COM, con objeto de alterar la salida y entrada por
defecto para trabajar con un fichero en su lugar. Por tanto, los filtros son programas que no tienen
que preocuparse de cual es la entrada o salida; su codificacin es extremadamente sencilla y puede
realizarse en cualquier lenguaje de alto o bajo nivel. El siguiente programa en C est ndar,
NULL.C, es un filtro nulo que no realiza tarea alguna: se limita a enviar todo lo que recibe (por
tanto, DIR es lo mismo que DIR | NULL):
#include <stdio.h>
void main()
{
int c;
do putchar(c=getchar()); while (c!=EOF);
}
El siguiente filtro, algo ms til, transforma en minsculas todo lo que pasa por l,
teniendo cuidado con los caracteres espaoles (, , , etc.). Lee bloques de medio Kbyte de
una sola vez para reducir el nmero de llamadas al DOS y ganar velocidad. Si se ejecuta sin m s
(sin emplear '|' ni '<' ni ningn smbolo de redireccionamiento o filtro) se limita a leer l neas
del teclado y a reescribirlas en minsculas, hasta que se acaba la entrada estndar (teclear Ctrl-Z
y Return al final).
; ********************************************************************
; * *
; * MIN.ASM 1.0 - Filtro para poner en minsculas ASCII Espaol. *
; * *
; ********************************************************************
segmento SEGMENT
ASSUME CS:segmento, DS:segmento
STDIN EQU 0
STDOUT EQU 1
ORG 100h
inicio:
CALL lee_entrada ; leer de STDIN
JCXZ fin_filtro ; en CX, bytes ledos
PUSHF
CALL pon_minusculas
CALL escribe_salida ; escribir en STDOUT
POPF
JNC inicio
fin_filtro: MOV AX,4C00h ; CF = 1 si fin de fichero
INT 21h
lee_entrada PROC
LEA DX,buffer
MOV CX,512
MOV BX,STDIN
MOV AH,3Fh
INT 21h ; leer
MOV CX,AX
RET
lee_entrada ENDP
escribe_salida PROC
LEA DX,buffer
MOV BX,STDOUT
MOV AH,40h
INT 21h ; escribir
RET
escribe_salida ENDP
pon_minusculas PROC
PUSH CX
LEA BX,buffer
procesa_car: MOV AL,[BX]
CMP AL,'A'
JB car_ok
CMP AL,128
JAE car8
CMP AL,'Z'
JA car_ok
OR AL,32
car_ok: MOV [BX],AL
INC BX
LOOP procesa_car
POP CX
RET
car8: MOV AH,''
CMP AL,''
JE trad_ok
MOV AH,''
CMP AL,''
JE trad_ok
MOV AH,''
CMP AL,''
JE trad_ok
MOV AH,''
CMP AL,''
JE trad_ok
MOV AH,AL
trad_ok: MOV AL,AH
JMP car_ok
pon_minusculas ENDP
segmento ENDS
END inicio
Captulo X: PROGRAMAS RESIDENTES
En este captulo vamos a abordar uno de los temas ms estrechamente relacionados con la
programacin de sistemas: la creacin de programas residentes. El DOS es un sistema
monousuario y monotarea, diseado para atender slo un proceso en un momento dado. Los
programas residentes, aquellos que permanecen en memoria tras ser ejecutados, surgieron como
intento de superar esta limitacin. Algunos de estos programas residentes proporcionan en la
prctica multitarea real (tales como colas de impresin o relojes), pero otros est n muertos a
menos que el usuario los active. A la hora de construir programas residentes el ensamblador es el
lenguaje ms apto: es el ms potente, el programador controla totalmente la mquina sin
depender de facetas ocultas del compilador y, adems, es el lenguaje ms sencillo para crear
programas residentes (en ingls, TSR: Terminate and Stay Resident). Para los programas ms
complejos puede ser necesario, en cambio, utilizar algn lenguaje de alto nivel pr ximo a la
mquina. Sin duda, los programas residentes que pretendan captar gran nmero de usuarios,
deben cumplir dos requisitos: por un lado, ocupar poca memoria; por otro, estar disponibles
rpidamente cuando son requeridos y, tambin, ser fiables y crear pocos conflictos. Esto ltimo
es importante, ya que un programa residente puede funcionar ms o menos bien pero no del todo:
si bien la mquina puede resistirse a colgarse, pueden aparecer anomalas o conflictos con
algunas aplicaciones. En particular, es muy comn la circunstancia de que dos programas
residentes sean incompatibles entre s.
Un programa residente o TSR es un programa normal y corriente que, tras ser cargado,
permanece parcial o totalmente en memoria al finalizar su ejecucin. Ello es posible utilizando una
funcin especfica del sistema operativo. Los programas residentes pueden ser activados
mediante una combinacin de teclas o bien actuar con cierta periodicidad, asociados a la
interrupcin del temporizador. Tambin pueden interceptar funciones del DOS o de la BIOS para
cambiar o modificar su funcionamiento. Al final, casi siempre resulta totalmente inevitable desviar
alguna interrupcin hacia una nueva rutina que la gestione, con objeto de activar el programa
residente. Como en casi todos los aspectos de la programacin, existen unos cuantos principios
fundamentales que conviene respetar:
1. Los programas residentes no deben alterar el funcionamiento normal del resto del ordenador.
Esto significa que deben preservar el estado de todo lo que van a modificar durante su
ejecucin, restaurndolo despus antes de retornar al programa principal, lo cual no se
limita por supuesto a los registros de la CPU, sino que incluye tambin la pantalla, los
discos, el estado de la memoria expandida y extendida, etc. Cuando se produce la
interrupcin que activa el programa residente, los registros de la CPU pueden tener un
valor que hay que interpretar o bien pueden ser aleatorios. Este ltimo es el caso de la
interrupcin peridica del temporizador: el programa residente slo puede fiarse de
CS:IP, los dems registros debern ser inicializados antes de empezar a operar
(lgicamente, habrn de ser primero preservados para ser restaurados al final).
2. No se pueden invocar libremente desde un programa residente los servicios del sistema
operativo. Si el lector es la primera vez que oye esto, quiz se quede extraado. Tal vez se
pregunte qu sucedera si desde un programa residente se llama (pongamos por ejemplo,
una vez cada segundo) a la funcin de impresin del DOS para sacar una 'A' por la
pantalla. Lo que puede suceder -y acabar sucediendo, si no a la primera 'A', a la segunda o
la tercera- es que el ordenador se cuelgue. Esto es debido a que el DOS es un sistema
operativo no reentrante, entre otras razones porque conmuta a una pila propia al ser
invocado. Por ello, si se llama a un servicio del DOS desde un programa residente, es
posible que en ese momento el DOS ya estuviese realizando otra funcin del programa
principal y lo que vamos a conseguir es que se vuelva loco y pierda el control cuando se
acabe la tarea residente (el contenido previo de la pila ha sido destrozado). Para utilizar el
DOS desde un programa residente hay que conocer cmo estn organizadas las pilas del
sistema operativo, as como determinar el estado del DOS para saber si se puede
interrumpir en ese momento o si hay que esperar. Utilizar el DOS es pr cticamente
indispensable a la hora de acceder al disco, por lo que ms adelante en este captulo lo
veremos con detenimiento. Para utilizar el DOS hay que emplear funciones ms o menos
secretas del sistema no documentadas por Microsoft, si bien esto no es peligroso: esta
empresa las utiliza y las ha utilizado siempre profusamente en sus propios programas, por lo
que resulta ms que seguro esperar que futuras versiones del DOS sigan soportndolas.
3. La BIOS no es tampoco completamente reentrante. Por fortuna, la BIOS utiliza la pila del
programa que le llama. Por ello, para utilizar funciones de la BIOS desde un programa
residente basta con asegurar que el sistema no est ya ejecutando una funcin BIOS
incompatible (normalmente, una interrupcin 10h en el caso de las funciones de vdeo o
la 13h en las de disco).
4. El hardware puede ser accedido sin limitaciones desde los programas residentes, si bien el
nivel de uso que puede hacerse est limitado por el sentido comn (puede haber
problemas, por ejemplo, si un programa residente cambia la posicin del cabezal de un
disquete cuando el programa principal estaba ejecutando una funcin del DOS o la BIOS
para acceder al disquete).
5. Los programas residentes tienen una causa que provoca su activacin. Si cuando ya estn
activos, se vuelve a reproducir la causa, estamos ante un problema de reentrada que compete
exclusivamente al programador. Por lo general, se suele denegar una demanda de
activacin cuando el programa residente ya estaba activo (si el programa tiene pila propia
esto es adems obligatorio). Pongamos por caso que se pulsa CTRL-ALT-R para mostrar
un reloj residente en pantalla, qu suceder si se vuelve a pulsar CTRL-ALT-R con el
reloj ya activado?. Para solucionar esto, existen dos caminos: uno de ellos es utilizar una
variable que indique que el programa ya est activo. El otro, es utilizar para desactivar el
programa la misma secuencia de teclas que para activarlo. Lgicamente, los programas que
realicen algo peridicamente (pongamos por caso 18,2 veces por segundo) basta con que se
limiten a no pillarse los dedos, esto es, utilizar menos de 1/18,2 segundos de tiempo de CPU
para sus tareas.
Puede que el lector haya visto antes programas residentes que no toman la precaucin de
monitorizar la interrupcin 10h o la 13h de la BIOS, y tal vez se pregunte si ello es realmente
necesario. La respuesta es tajantemente que s. Como se ver en el futuro en otro programa de
ejemplo, reentrar a la BIOS sin ms puede provocar conflictos.
demores SEGMENT
ASSUME CS:demores, DS:demores
ORG 100h
inicio:
JMP main
controla_int08 PROC
PUSHF
CALL CS:ant_int08 ; llamar al gestor normal de INT 8
STI
CMP CS:in10,0
JNE fin_int08 ; estamos dentro de INT 10h
;
; Colocar aqu el proceso a ejecutar 18,2 veces/seg.
; que puede invocar funciones de INT 10h
fin_int08:
IRET
controla_int08 ENDP
controla_int10 PROC
INC CS:in10 ; indicar entrada en INT 10h
PUSHF
CALL CS:ant_int10
DEC CS:in10 ; fin de la INT 10h
IRET
controla_int10 ENDP
main: PUSH ES
MOV AX,3508h
INT 21h ; obtener vector de INT 8
MOV ant_int08_seg,ES
MOV ant_int08_off,BX
MOV AX,3510h
INT 21h ; obtener vector de INT 10h
MOV ant_int10_seg,ES
MOV ant_int10_off,BX
POP ES
LEA DX,controla_int08
MOV AX,2508h
INT 21h ; nueva rutina de INT 8
LEA DX,controla_int10
MOV AX,2510h
INT 21h ; nueva rutina de INT 10h
PUSH ES
MOV ES,DS:[2Ch] ; direccin del entorno
MOV AH,49h
INT 21h ; liberar espacio de entorno
POP ES
demores ENDS
END inicio
Otro mtodo alternativo es rastrear la cadena de bloques de memoria del sistema operativo
buscando programas residentes y comprobndolos uno por uno. Este mtodo es bastante rpido,
habida cuenta de que no van a existir ms de 20-50 bloques de memoria. Sin embargo, la
organizacin de la memoria en los PCs es a veces tan anrquica que este m todo (que deber a
ser el ms elegante) es un poco peligroso en cuanto a la seguridad, aunque mucho menos que el
anterior. Lo cierto es que puede ser difcil intentar recorrer la memoria superior, habida cuenta del
desigual tratamiento que recibe en las diversas versiones del DOS y con los diversos controladores
de memoria que pueden estar instalados.
Por cierto, la idea de rastrear toda la memoria (1 Mb), buscando desesperadamente una cadena
de identificacin, no es nueva. Sin embargo es tremendamente lenta llevada a la pr ctica. Es
incmoda (hay que considerar el caso de que el propio programa que busca se encuentre a s
mismo, en particular en reas como los buffers de transferencia con disco del DOS) y bastante
salvaje.
Finalmente, existe la posibilidad de utilizar el mismo sistema que emplea el DOS para
comprobar la presencia de sus propios programas residentes (como el KEYB, GRAPHICS,
GRAFTABL, SHARE, PRINT, etc) basado en la interrupcin Multiplex (2Fh). Este sistema es el
ms seguro, aunque un tanto laborioso. Consiste en llamar a la INT 2F con un valor en el registro
AH que indica quin est llamando, y otro valor en AL para decir por qu est llamando
(normalmente 0). Los valores 00-BFh en AH estn reservados para el DOS, y de C0h-FFh para las
aplicaciones. A la vuelta, AL devuelve un valor 0 para indicar que el programa no est instalado
pero est permitida la instalacin, un valor 1 para decir que no est instalado ni tampoco est
permitida la instalacin. Si devuelve FFh, significa que el programa ya estaba instalado. Por
ejemplo, el KEYB del DOS llama a INT 2Fh con AX=AD80h, donde ADh significa que quien
pregunta es el KEYB -y no otro programa- para conocer si ya est instalado o no. En caso de que
lo est (AL=FFh a la vuelta), tambin se devuelve en ES:DI la direccin del KEYB ya residente
(que es lo solicitado con AL=80h). En el caso concreto del KEYB, si a la vuelta AL<>FFh se
interpreta que el programa no est an residente, por lo que se procede a su instalacin (en este
caso, curiosamente incluso aunque AL=1).
Esta tcnica cuenta con la complicacin que supone decidir qu valor emplear en la
interrupcin multiplex. Es evidente que dos programas residentes no pueden utilizar el mismo. Los
programas menos eficientes utilizan un valor fijo predeterminado, con lo que limitan las
posibilidades del usuario. Sin embargo, para solucionarlo existen varias alternativas, que se ver n
ms adelante.
Aviso: Aunque no es frecuente, algunas versiones 2.X del sistema no tienen inicializado el vector
de la INT 2Fh. Por ello, es una buena prctica asegurarse de que esta interrupcin apunta a algo
antes de llamarla (por ejemplo, verificando que el segmento es distinto de cero). Por otro lado, el
comando PRINT del DOS en las versiones 2.X del sistema gestiona de tal manera la INT 2Fh que
ninguna otra aplicacin puede emplearla. Por ello, el mtodo de la interrupcin Multiplex est
ms bien reservado para versiones 3.0 o superiores (tambin la 2.X si el usuario prescinde de
PRINT).
10.4. - EXPULSIN DE UN PROGRAMA RESIDENTE DE LA MEMORIA
Se trata de una tarea bastante sencilla en s, aunque hay que tener en cuenta una serie de
factores. En primer lugar, el programa debe restaurar todos los vectores de interrupcin que
haba interceptado. Ello significa que si ha sido instalado tras l otro programa residente que
modifica uno de los vectores que l interceptaba, ya no es posible restaurarlo. Por ello, un primer
requisito para permitir la desinstalacin es que sea el ltimo programa residente cargado que
utiliza un vector de interrupcin dado. Esto es fcil de verificar, basta con comprobar que todas
las interrupciones interceptadas siguen apuntando a una copia de l. Si esta prueba es superada
satisfactoriamente, puede procederse a restaurar los vectores de interrupcin y liberar la memoria
ocupada de una de las dos siguientes maneras:
1. Pasando en ES el segmento donde est cargado el programa y llamando a la funcin 49h
del DOS para liberar el bloque de memoria.
2. Liberando directamente el bloque de memoria al colocar una palabra a cero en los bytes del
MCB que identifican al propietario del bloque. Este mtodo puede ser ms seguro si
est instalado un gestor de memoria expandida extrao, aunque es menos elegante y
quiz menos recomendable.
Por lo general, no tiene mucho sentido que un usuario elimine un programa residente despu s
de haber cargado otro -aunque ello sea posible- ya que se origina un hueco en la memoria que
normalmente no se utilizar para nada -el DOS asigna siempre el mayor bloque disponible al
cargar cualquier aplicacin-, aunque esto es realmente problema exclusivo del usuario.
Como se ver despus, ciertos programas residentes sofisticados permiten ser desinstalados
an sin ser los ltimos instalados; sin embargo, estos programas residentes tienen que tener algo
en comn: comportarse de la misma manera y actuar tambin de una manera definida. Ello
significa que si entre dos programas residentes que cumplen el mismo convenio el usuario instala
un programa que no lo respeta, se pierden todas las posibilidades.
El convenio anterior adolece de un defecto importante: ya puestos a determinar con tanto detalle
el fabricante, nombre y versin del programa, por qu no colocar ms informacin til?.
Por ejemplo, sera interesante disponer de informacin sobre los contenidos previos de los
vectores de interrupcin que el programa ha desviado, lo cual permitira su desinstalaci n
aunque no sea el ltimo cargado, ser desinstalado por parte de otros programas o incluso emplear
ciertas tcnicas de relocalizacin en memoria para evitar la fragmentacin de la misma cuando
es desinstalado. Con objeto de aumentar la eficacia, el autor de este libro desarroll un mtodo
nuevo, extensin del expuesto en el apartado anterior, que permitiera sacar mayor partido de la
interrupcin Multiplex. Al igual que el anterior, el nuevo convenio tambin est publicado en el
INTERRUP.LST, lo que garantiza su difusin y la inversin de quienes decidan emplearlo.
El valor ubicado en ES:DI-14 puede ser til de cara a deducir el tamao de la parte del PSP
que permanece residente, ya que se considera que la ubicacin del programa comienza en el offset
0 relativo al segmento definido en ES:DI-16 y, por tanto, el tama o del programa definido en
ES:DI-12 es relativo tambin con offset 0 a ese segmento. Si bien se puede opinar que son
demasiados campos, son slo poco ms de 16 bytes los que se aaden al programa residente.
Adems, muchas de las variables anteriores han de estar definidas necesariamente: por qu no
juntarlas de una manera convenida?. En la tabla anterior se define un puntero a una estructura con
informacin sobre los vectores interceptados. No se respeta sin embargo el formato de los
encabezamientos de interrupcin propuesto en la BIOS del PS/2 (la intencin de IBM es buena,
pero ha llegado demasiado tarde).
Formato de la tabla area_vectores:
Offset Tamao Descripcin
-1 BYTE nmero de vectores interceptados por el TSR
00h BYTE nmero del primer vector
01h DWORD puntero al primer vector antes de instalar el TSR
05h BYTE nmero del segundo vector
06h DWORD puntero al segundo vector antes de instalar el TSR
. . (y as sucesivamente). Notar que el TSR debe usar ESTAS
variables para
invocar las anteriores rutinas de control de esas
interrupciones, ya que un
. . agente externo podra actualizarlas.
En las primeras versiones de este convenio ya no existan ms reglas. Sin embargo, al final
comprend la necesidad de ampliar las prestaciones. Por ello, el convenio fue ampliado con dos
tablas ms, opcionales, que es conveniente rellenar incluso tambin en aquellos TSR m s
sencillos que ocupan menos de 64 Kb y son totalmente reubicables (no contienen referencias
absolutas a segmentos). Estas tablas permitiran a un hipottico sistema operativo mover los
programas residentes para evitar la fragmentacin de la memoria, tarea que mientras tanto puede
realizar algn programa de utilidad. Aquellos TSR que contengan referencias en su propio cdigo
o datos cambiando el segmento (slo puede ocurrir normalmente en los programas EXE) el
convenio establece que deben soportar el parmetro /SR: ante l, al ser recargados en memoria
desde disco (necesario para la reubicacin) deben instalarse silenciosamente sin chitar,
autoinhibindose a continuacin. En general, la mayora de los programas residentes escritos
en ensamblador son relocalizables, as como los elaborados en el modelo Tiny del C, por lo que
no es muy complejo realizar esta tarea. La nica pega que se puede poner es que, por desgracia,
pocos programas usan este convenio!.
Formato de la tabla area_extra (opcional):
Offset Tamao Descripcin
00h WORD offset a la tabla control_externo (0 si no soportada)
02h WORD reservado para futuro uso (0)
A continuacin se listan dos rutinas que habr de incorporar todo programa que desee emplear
este convenio (u otras equivalentes). Las rutinas las he denominado mx_get_handle y
mx_find_tsr. La primera permite buscar un valor para la interrupcin Multiplex an no
empleado por otra tarea residente, tanto si sta es del convenio como si no. La segunda sirve para
que el programa residente se busque a s mismo en la memoria. En esta segunda rutina se indica el
tamao de la cadena de identificacin (la que contiene el nombre del fabricante, programa y
versin) en CX. Si no se encuentra el programa residente en la memoria, puede repetirse la
bsqueda con CX indicando slo el tamao del nombre del fabricante y el programa, sin incluir
el de la versin: as se podra advertir al usuario que tiene instalada ya otra versin distinta.
; ------------ Buscar entrada no usada en la interrupcin Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta tcnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
mx_no_hueco: STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
mx_find_tsr PROC
MOV AH,0C0h
mx_rep_find: PUSH AX
PUSH CX
PUSH SI
PUSH DS
PUSH ES
PUSH DI
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl ; no hay TSR ah
CLD
PUSH DI
REP CMPSB ; comparar identificacin
POP DI
JE mx_tsr_found ; programa buscado hallado
mx_skip_hndl: POP DI
POP ES
POP DS
POP SI
POP CX
POP AX
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4 ; sacar ES y DI de la pila
POP DS
POP SI
POP CX
POP AX
CLC
RET
mx_find_tsr ENDP
La rutina mx_unload desinstala un programa residente que verifique el convenio; basta con
indicar el nmero de interrupcin Multiplex que emplea el TSR. El proceso de desinstalaci n
falla si se ha instalado despus un TSR que no verifica el convenio y tiene alguna interrupci n en
comn, ya que la rutina no puede en ese caso recorrer la cadena de vectores para modificarla
anulando la tarea residente. Para que un TSR se auto-desinstale basta con que suministre a esta
rutina su propio nmero de identificacin. El mtodo empleado por la rutina para cambiar los
vectores de interrupcin no es muy ortodoxo, pero simplifica el algoritmo y posee un nivel de
seguridad razonable. Esta rutina da dos pasadas: el objeto de la primera es s lo asegurar que el
TSR puede ser desinstalado antes de empezar a cambiar ningn vector. En la segunda, se cambian
los enlaces entre los vectores y se libera la memoria, bien llamando al DOS o al controlador XMS
(segn quin la haya asignado). Hay una maniobra ms o menos complicada para hacer que el
vector 2Fh sea el ltimo restaurado, con objeto de poder seguir la cadena de interrupciones hasta el
propio TSR invocando la INT 2Fh.
; ------------ Eliminar TSR del convenio si es posible. A la entrada,
; en AH se indica la entrada Multiplex; a la salida, CF=1
; si fue imposible y CF=0 si se pudo. Se corrompen todos
; los registros salvo los de segmento. En caso de fallo
; al desinstalar, AL devuelve el vector culpable.
mx_unload PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX ; BP=entrada Multiplex del TSR
MOV CX,2
mx_ul_pasada: PUSH CX ; siguiente pasada
LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0 ; CX = n vectores
mx_ul_masvect: POP AX
PUSH AX ; pasada en curso
DEC AL
PUSH CX
mx_ul_2f: MOV AL,ES:[SI] ; vector en curso
JNZ mx_ul_pasok
CMP CX,1 ; ltimo vector?
JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL ; INT 2Fh?
JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f
mx_ul_noult: CMP AL,2Fh ; restaurar INT 2Fh?
JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f
mx_ul_pasok: PUSH ES
PUSH AX
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h ; vector en ES:BX
POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX ; INT xx en DX (aprox.)
MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI
PUSH ES:[DI-12]
MOV DI,ES:[DI-8] ; offset a la tabla de vectores
MOV CL,ES:[DI-1]
MOV CH,0 ; nmero de vectores en CX
mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect ; este TSR usa vector analizado
ADD DI,5
LOOP mx_ul_buscav
ADD SP,4 ; no lo usa
JMP mx_ul_otro
mx_ul_usavect: POP CX ; tamao del TSR
POP BX ; segmento del TSR
CMP DX,BX
JB mx_ul_otro ; la INT xx no le apunta
ADD BX,CX
CMP DX,BX
JA mx_ul_otro ; la INT xx le apunta
PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP ; es el propio TSR?
POP AX
JNE mx_ul_chain ; no
POP ES ; s: posible reponer vector!
POP CX
POP BX
PUSH BX
PUSH CX
PUSH ES
DEC BX
JNZ mx_ul_norest ; no es la segunda pasada
POP ES ; segunda pasada...
PUSH ES
PUSH DS
MOV BX,CS:mx_ul_tsroff ; restaurar INT's
MOV DS,CS:mx_ul_tsrseg
CLI
MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: POP ES
POP CX
ADD SI,5 ; siguiente vector
DEC CX
JZ mx_unloadable ; no ms, desinstal-ar/ado!
JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direccin
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX ; INT xx en DX (aprox.)
MOV AH,0BFh
mx_ul_otro: INC AH ; a por otro TSR
JZ mx_ul_exitnok ; se acabaron!
JMP mx_ul_masmx
mx_ul_exitnok: ADD SP,6 ; equilibrar pila
POP ES
STC
RET ; imposible desinstalar
mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok ; desinstalado
JMP mx_ul_pasada ; 1 pasada exitosa: por la 2
mx_ul_exitok: TEST ES:info_extra,111b ; tipo de instalacin?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml ; cargado en RAM convencional
CMP xms_ins,1
JNE mx_ul_freeml ; no hay controlador XMS (?)
MOV DX,ES
MOV AH,11h
CALL gestor_XMS ; liberar memoria superior
POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h ; liberar bloque de memoria ES:
POP ES
CLC
RET
mx_ul_tsrcv?: PUSH AX ; es TSR del convenio?...
PUSH ES
PUSH DI
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4 ; CF=0
POP AX
RET
mx_ul_ncvexit: POP DI ; ...no es TSR del convenio
POP ES
POP AX
STC ; CF=1
RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload ENDP
Los dos programas siguientes constituyen dos pequeas utilidades de apoyo a los TSR de este
convenio. TSRLIST lista los TSR del convenio que estn instalados en el ordenador, con
informacin detallada; TSRKILL permite eliminar uno o todos los TSR que est n instalados en
cualquier orden, no slo necesariamente el ltimo que fue cargado. Lgicamente, si entre varios
programas que respetan el convenio hay uno que lo viola, TSRKILL puede no ser capaz de
desinstalar un TSR del convenio. En ese caso, se informa de qu vector ha sido el culpable.
Ejemplo de salida de TSRLIST /V:
TSRLIST 1.3 (c) Febrero 1994 CiriSOFT.
Listado de tareas residentes normalizadas:
La entrada multiplex 210 (0D2h) de que informa TSRLIST es utilizada por QEMM386;
TSRLIST tambin informa de las entradas que estn siendo utilizadas por programas que no
respetan el convenio, aunque lgicamente no da ms informacin.
/********************************************************************/
/* */
/* TSRLIST 1.3 - Utilidad de listado de TSR's normalizados - BC++ */
/* */
/********************************************************************/
#include <dos.h>
#include <string.h>
void cabecera(),
listar_tsr(),
obtener_item();
if (raro) {
printf("\n- ID de programas residentes que incumplen convenio: ");
for (entrada=0; entrada<64; entrada++)
if (tsr_raro[entrada]) printf("%2d; ", entrada+0xc0);
if (vect) printf("\n");
}
if (!vect) printf("\n- Ejecute con /V para listado de vectores.\n");
}
r.r_ax=entrada << 8;
r.r_es=0x1492; r.r_di=0x1992;
intr (0x2f, &r);
return ((r.r_ax==0xFFFF) &&
(peek(r.r_es,r.r_di-4)==9002) && (peek(r.r_es,r.r_di-2)==10787));
}
printf("\n");
}
######################################################################
/********************************************************************/
/* */
/* TSRKILL 1.3 - Utilidad de desinstalacin de TSRs normalizados. */
/* Compilar en el modelo Large de Borland C. */
/* */
/********************************************************************/
#include <dos.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct tsr_info {
unsigned segmento_real;
unsigned offset_real;
unsigned ltsr;
unsigned char info_extra;
unsigned char multiplex_id;
unsigned vectores_id;
unsigned extension_id;
unsigned long validacion;
char autor_nom_ver[80];
};
int tsr_convenio(),
mx_unload(),
existe_xms();
void liberar_umb(),
desinstalar();
if (mxid==-1) {
for (mxid=0xc0; mxid<=0xFF; mxid++)
if (tsr_convenio(mxid, &tsr)) desinstalar (mxid);
}
else
desinstalar (mxid);
}
if (correcto || (vector<0x100)) {
strcpy (cadaux, nombre); p=cadaux;
while (*p) if ((*p++)==':') *(p-1)=0; p=cadaux;
while (*p++); strcpy (cadena, p); /* nombre programa */
strcat (cadena, " ");
while (*p++); strcat (cadena, p); /* versin */
strcat (cadena, " de ");
strcat (cadena, cadaux); /* autor */
}
if (correcto)
printf(" - Desinstalado el %s\n", cadena);
else {
if (vector==0x100)
printf (" - No hay TSR %u o no es del convenio.\n", mxid);
else if (vector==0x101)
printf (" - HBREAK es demasiado fuerte para TSRKILL.\n");
else if (vector==0x102)
printf (" - 2MGUI es demasiado fuerte para TSRKILL.\n");
else {
printf (" - El %s no se puede desinstalar: ", cadena);
printf ("fallo en el vector %02X.\n", vector);
}
}
}
*interrupcin = vector;
*tsrnombre = tsr->autor_nom_ver;
if (strstr(*tsrnombre, "HBREAK")!=NULL) {
posible=0; *interrupcin=0x101; }
if (strstr(*tsrnombre, "2MGUI")!=NULL) {
posible=0; *interrupcin=0x102; }
if (posible) {
for (i=0; i<numvect; i++) {
vector = peekb(FP_SEG(tsr), tsr->vectores_id+5*i);
sgm = peek(FP_SEG(tsr), tsr->vectores_id+5*i+3);
ofs = peek(FP_SEG(tsr), tsr->vectores_id+5*i+1);
if ((tablaptr[i][0]==0) && (tablaptr[i][1]==0)) {
interr=MK_FP(sgm, ofs);
setvect (vector, interr);
}
else {
asm cli
poke (tablaptr[i][0], tablaptr[i][1], ofs);
poke (tablaptr[i][0], tablaptr[i][1]+2, sgm);
asm sti
}
}
r.r_ax=entrada << 8;
r.r_es=0x1492; r.r_di=0x1992;
intr (0x2f, &r);
*info = MK_FP(r.r_es, r.r_di-16);
return ((r.r_ax==0xFFFF) &&
(peek(r.r_es,r.r_di-4)==9002) && (peek(r.r_es,r.r_di-2)==10787));
}
int existe_xms ()
{
struct REGPACK r;
asm {
push es; push si; push di;
mov ax,4310h
int 2Fh
mov word ptr controlador,bx
mov word ptr controlador+2,es
mov ah,11h
mov dx,segmento
call controlador
pop di; pop si; pop es;
}
}
Los programas que emplean la INT 2Dh deben interceptarla e implementar una serie de
funciones. Como luego veremos, no es necesario que soporten todas las que propone el convenio. A
la hora de llamar a la INT 2Dh se indicar en AH, tal como se hac a con la interrupci n
Multiplex, el nmero de entrada y en AL la funcin. Todo el funcionamiento se basa en invocar
funciones en el programa residente. El inconveniente de ejecutar cdigo en la copia residente es
que ocupa algo ms de memoria, y la necesidad de implementar dichas funciones. La ventaja de
ejecutar cdigo en la copia residente es que sta puede, en donde sea procedente, restaurar el
estado del sistema de manera ms completa o realizar tareas especficas que sean necesarias. Por
citar un ejemplo, TSRKILL no puede desinstalar las conocidas utilidades HBREAK o 2MGUI, que,
en cambio, con la propuesta AMIS podran haber soportado una funcin de desinstalacin
accesible por cualquier agente externo. Existen las siguientes funciones:
La rutina de control de interrupcin respeta este formato, propuesto por IBM en las BIOS de
PS/2:
Offset 0 (2 bytes): Salto corto a donde realmente empieza la rutina de control (10EBh).
Offset 2 (4 bytes): Direccin previa de ese vector de interrupcin.
Offset 6 (2 bytes): Valor 424Bh (consejo de IBM).
Offset 8 (1 byte): Bandern de EOI, 0 si es interrupcin software o controlador secundario de
la interrupcin hardware, 80h si es el controlador primario de la interrupcin hardware (debe
enviar un comando EOI al controlador de interrupciones 8259).
Offset 9 (2 bytes): Salto corto a la rutina de reset hardware (que retornar con RETF).
Offset 0Bh (7 bytes): Reservados (a 0).
Offset 12h: Rutina que controla la interrupcin.
- Funciones 5 y siguientes: Reservadas para futuras versiones del convenio, devuelven 0 al no
estar implementadas.
Por supuesto, los programas que cumplan la propuesta AMIS deben asignar dinmicamente el
nmero de entrada que van a utilizar en la INT 2Dh, buscando uno libre. Para chequear su
instalacin han de emplear los 16 bytes que indican el nombre del fabricante y el programa. Como
dije al principio, no es preciso que un programa soporte todas estas funciones: para cumplir con la
versin 3.4 de la especificacin basta con implementar las funciones 0, 2 (sin obligacin de
disponer de rutina de desinstalacin) y la 4 (devolviendo un valor 4).
Cualquiera de los tres mtodos expuestos es vlido para lograr una correcta localizacin del
programa residente en memoria. El ms sencillo es el primero (aunque ES:DI puede estar asignado
de la manera que el lector considere oportuna, por supuesto). Sin embargo, son los dos ltimos los
ms recomendables, por las prestaciones que ofrecen. El ms completo es la propuesta AMIS.
Es cierto que estas tcnicas, con programas que se mueven a si mismos dando vueltas por la
memoria, automodificndose ... no son consideradas elegantes por los programadores
conservadores, y no se pueden hacer estas salvajadas en entornos con proteccin de memoria
(UNIX, etc.); de hecho, Niklaus Wirth se llevara sin duda las manos a la cabeza. Sin embargo el
DOS y el 8086 las permiten y pueden ser bastante tiles, en especial para los programadores de
sistemas. Adems, escondiendo bien los fuentes, lo ms probable es que nadie se entere de ello...
Con DR-DOS y, en general, con ciertos controladores de memoria (tales como QEMM) la
memoria superior es gestionada por la especificacin de memoria extendida XMS (v ase
apartado 8.3). Para utilizar la memoria superior en estos sistemas hay que detectar la presencia del
controlador XMS y pedirle la memoria (tambin habr que llamarle despus para liberarla).
Con MS-DOS 5.0 y posteriores slo existe memoria superior XMS si NO se indica DOS=UMB en
el CONFIG.SYS; sin embargo, la mayora de los usuarios suelen indicar esta orden con objeto de
que el MS-DOS permita emplear LOADHIGH y DEVICEHIGH. Por desgracia, con MS-DOS,
cuando el DOS gestiona la memoria superior, se la roba toda al controlador XMS. Por tanto, habr
que pedrsela al DOS. Con MS-DOS, el procedimiento general es el siguiente: Primero, preservar
el estado de la estrategia de asignacin de memoria y el estado de los bloques de memoria superior
(si estn o no conectados con los de la memoria convencional). A continuaci n, se conectan los
bloques de memoria superior con los de la convencional, por si no lo estaban. Seguidamente, se
modifica la estrategia de asignacin de memoria, estableciendo -por ejemplo- un best fit en
memoria superior. Finalmente, se asigna memoria utilizando la funcin convencional de
asignacin (48h). Tras estas operaciones, habr de ser restaurada la estrategia de asignacin de
memoria y el estado de los bloques de memoria superior.
Es conveniente intentar primero asignar memoria superior XMS: si falla, se puede comprobar si
la versin del DOS es 5 (o superior) y aplicar el mtodo propio que requiere este sistema. De esta
manera, los TSR podrn asignar memoria superior sea cual sea el sistema operativo, controlador
de memoria o configuracin del sistema activos. Sin embargo, con el mtodo propio del DOS 5.0
hay un inconveniente: al acabar la ejecucin del cdigo de instalacin del TSR, el DOS libera
el bloque de memoria que se asign con la funcin 48h!. Para evitar esto, hay dos m todos:
uno, consiste en terminar residente (aunque sea dejando slo los primeros 96 bytes del PSP) con
objeto de que el sistema respete el bloque de memoria creado. Si no se desea este ligero derroche de
memoria convencional, hay un mtodo ms contundente. Consiste en engaar al DOS y, tras
asignar el bloque de memoria, modificar en su correspondiente bloque de control la informacin
del propietario (PID), hacindole apuntar -por ejemplo- a s mismo. De esta manera, al acabar el
programa, el DOS recorrer la cadena de bloques de memoria y no encontrar ninguno que
pertenezca al programa que finaliza... conviene tambin, en este caso, que los dos primeros bytes
del bloque de memoria superior contengan la palabra 20CDh (ubicada al inicio de los PSP), con
objeto de que algunos programas de diagnstico lo confundan con un programa (no obstante, el
comando MEM del DOS no requiere este detalle y lo tomara directamente por un programa).
Tambin hay que crear el nombre del programa en los 8 ltimos bytes del MCB manipulado. Las
siguientes rutinas asignan memoria superior XMS (UMB_alloc) o memoria superior DOS 5
(UPPER_alloc):
; ------------ Reservar bloque de memoria superior del n prrafos AX,
; devolviendo en AX el segmento donde est. CF=1 si no
; est instalado el gestor XMS (AX=0) o hay un error (AL
; devuelve el cdigo de error del controlador XMS).
UMB_alloc PROC
PUSH BX
PUSH CX
PUSH DX
CMP xms_ins,1
JNE no_umb_disp ; no hay controlador XMS
MOV DX,AX ; nmero de prrafos
MOV AH,10h ; solicitar memoria superior
CALL gestor_XMS
CMP AX,1 ; ha ido todo bien?
MOV AX,BX ; segmento UMB/cdigo de error
JNE XMS_fallo ; fallo
POP DX ; ok
POP CX
POP BX
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo: POP DX
POP CX
POP BX
STC
RET
UMB_alloc ENDP
UPPER_alloc PROC
PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin ; necesario DOS 5.0 mnimo
UPPER_existe: PUSH AX ; preservar prrafos...
MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL ; preservar estado UMB
MOV AX,5803h
MOV BX,1
INT 21h ; conectar cadena UMB's
MOV AX,5801h
MOV BX,41h
INT 21h ; High Memory best fit
POP BX ; ...prrafos requeridos
MOV AH,48h
INT 21h ; asignar memoria
PUSHF
PUSH AX ; guardado el resultado
MOV AX,5801h
MOV BX,alloc_strat
INT 21h ; restaurar estrategia
MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h ; restaurar estado cadena UMB
POP AX
POPF
JC UPPER_fin ; hubo fallo
PUSH DS
DEC AX
MOV DS,AX
INC AX
MOV WORD PTR DS:[1],AX ; manipular PID
MOV WORD PTR DS:[16],20CDh ; simular PSP
PUSH ES
MOV CX,DS
MOV ES,CX
MOV CX,CS
DEC CX
MOV DS,CX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB ; copiar nombre de programa
POP ES
POP DS
CLC
UPPER_fin: RET
UPPER_alloc ENDP
La rutina UMB_alloc requiere una variable (xms_ins) que indique si est instalado el
controlador de memoria extendida, as como otra (gestor_XMS) con la direccin del mismo. La
rutina UPPER_alloc necesita una variable de palabra (alloc_strat) y otra de tipo byte (umb_state) en
que apoyarse. El mtodo expuesto consiste en modificar el PID para evitar que el DOS desasigne
la memoria al acabar la ejecucin del programa; tambin se coloca oportunamente la palabra
20CDh para simular un PSP y se asigna al nuevo bloque de programa el mismo nombre que el del
bloque de programa real. Los programas con autoinstalacin en memoria superior deberan tener
un parmetro (al estilo del /ML de los de DR-DOS) para forzar la instalaci n en memoria
convencional si el usuario as lo requiere.
El DR-DOS 6.0 fue el primer sistema operativo DOS que permita instalar programas
residentes en los primeros 64 Kb de la memoria extendida, zona comnmente conocida por HMA.
La ventaja de cargar aqu las utilidades residentes es que no ocupan memoria, dicho entre
comillas (al menos, no memoria convencional ni superior). El inconveniente principal es que este
rea es bastante limitada (en la prctica, algo menos de 20 Kb libres) y la instalaci n un tanto
compleja. Ciertos programas del sistema (COMMAND, KEYB, NLSFUNC, SHARE, TASKMAX)
se pueden cargar en esta zona -algunos incluso lo hacen automticamente-. Otro inconveniente es
la complejidad de la instalacin: normalmente los programas se cargarn en el segmento 0FFFEh
con un offset variable y dependiente de la zona en que sean instalados. Por ello, el primer requisito
que han de cumplir es el de ser relocalizables: en la prctica, la rutina de instalaci n habr de
montar el cdigo en memoria asignando posiciones absolutas a ciertos modos de
direccionamiento.
El MS-DOS 5.0 tambin utiliza el HMA para cargar programas residentes; sin embargo no
est tan normalizado como en el caso del DR-DOS y es probable que en futuras versiones cambie
el mtodo. De una manera torpe, Microsoft eligi a DISPLAY.SYS para ocupar parte del rea
que el propio DOS deja libre en el HMA tras instalarse. Este fichero es utilizado en la
conmutacin de pginas de cdigos (factible en mquinas con EGA y VGA) para adaptar el
juego de caracteres a ciertas lenguas. Hubiera sido mucho ms inteligente elegir el KEYB y otros
programas similares que casi todo el mundo tiene instalados.
Por consiguiente, limitaremos el estudio al caso del DR-DOS. La informaci n que viene a
continuacin fue obtenida por la labor investigadora del autor de este libro, que la envi
posteriormente a Ralf Brown para incluirla en el Interrupt List. Conviene hacer ahora hincapi en
que esta manera de gestionar el HMA, a nivel de bloques de memoria, es propia del DR-DOS 6.0, y
no de otras versiones anteriores de este sistema, aunque probablemente s de las posteriores. Para
comprobar que en una mquina est presente el DR-DOS puede verificarse la presencia de una
variable de entorno del tipo OS=DRDOS y otra VER=X.XX con la versin. En todo
caso, es mucho ms seguro utilizar una funcin del sistema al efecto:
MOV AX,4452h ; funcin exclusiva del DR-DOS
INT 21h
JC no_es_drdos ; probablemente es MS-DOS
CMP AX,1063h
JE drdos341
CMP AX,1065h
JE drdos5
CMP AX,1067h
JE drdos6
JA drdos_futuro
El DR-DOS 6.0 implementa un nuevo servicio para gestionar la carga de programas en el HMA.
Con las siguientes lneas:
MOV AX,4458h
INT 21h
MOV SI,ES:[BX+10h] ; variable exclusiva de DR-DOS
MOV DI,ES:[BX+14h] ; otra variable de DR-DOS
En el HMA los bloques de memoria forman una cadena pero mucho ms simple que en los
dems tipos de memoria. En concreto, tienen una cabecera de slo 5 bytes: los dos primeros
apuntan al offset del siguiente bloque de memoria (cero si ste era el ltimo) y los dos siguientes
el tamao de este bloque. Tngase en cuenta que los bloques no han de estar necesariamente
seguidos, por lo que la informacin del tamao no debe emplearse para direccionar al siguiente
bloque: para algo estn los primeros dos bytes!. El quinto byte puede tomar un valor entre 0 y 5
para indicar el tipo de programa, por este orden: System, KEYB, NLSFUNC, SHARE, TaskMAX,
COMMAND. Como se ve, no se almacena el nombre en formato ASCII sino con un c digo. Los
programas creados por el usuario pueden utilizar cualquiera de los cdigos, aunque quiz el
ms recomendable sea el 0 (de todas maneras, puede haber varios bloques con el mismo c digo).
Para cargar un programa residente aqu, primero se recorre la cadena de bloques libres hasta
encontrar uno del tamao suficiente -si lo hay, claro est-. A continuacin, se rebaja el tamao
de este bloque modificando su cabecera. Despus, se crea una cabecera para el nuevo bloque (que
se sita al final del bloque libre empleado, siempre tendiendo hacia direcciones altas) y se consulta
la variable del DOS que indica el primer bloque ocupado: el nuevo bloque creado habr de
apuntarle; a su vez, esta variable del DOS ha de ser actualizada ya que desde ahora el primer bloque
ocupado (bueno, en realidad el ltimo) es el recin creado. Ha de tenerse en cuenta que si lo que
sobra del bloque libre que va a ser utilizado son menos de 16 bytes, se le debe desechar -porque
as lo establece el sistema-, eliminndolo de la lista encadenada por el simple procedimiento de
hacer apuntar su predecesor a su sucesor. Lgicamente, si el bloque no tena predecesor -si era
el primer bloque- lo que hay que hacer es modificar la variable del DOS que indica el primer bloque
libre para que apunte a su sucesor. En general, se trata de gestionar una lista encadenada, lo que
ms que un problema de ensamblador lo es de sentido comn. No eliminar los posibles bloques
libres de menos de 16 bytes es saltarse una norma del sistema operativo y podra tener
consecuencias imprevisibles con futuros programas cargados.
Una vez reservado espacio para el nuevo programa, habr de copiarse este desde la memoria
convencional hacia el HMA, con una simple instruccin de transferencia. All -o antes de
realizar la transferencia- habr de relocalizarse el cdigo. Lo normal en los programas del
sistema -y, por consiguiente, lo ms recomendable- es que nuestras aplicaciones corran en la
direccin 0FFFEh:XXXX y no la 0FFFFh:XXXX como en principio podra suponerse, aunque
quiz se trate de un detalle irrelevante. Por ltimo, se han de desviar los correspondientes
vectores de interrupcin a las nuevas rutinas del programa residente. Obviamente, el programa
principal instalador deber acabar normalmente -y no residente-.
En general, la gestin del HMA es engorrosa porque el sistema realiza poco trabajo sucio,
delegndoselo al programa que quiera emplear este rea.
Es posible ejecutarlo cuando ya est instalado con objeto de cambiar sus parmetros o
programar la alarma. Si las coordenadas elegidas estn fuera de la pantalla -ej., al cambiar a un
modo de menos columnas o filas- el resultado puede ser decepcionante (esto no sucede si /X=72).
Si se produce un cambio de modo de pantalla o una limpieza de la misma, el reloj seguir
apareciendo correctamente casi al instante -se refresca su impresin 4 veces por segundo-.
Una vez cargado, se puede controlar la presencia o no en pantalla pulsado Ctrl-Alt-R o AltGr-R
(sin necesidad de volver a ejecutar el programa con los parmetros ON u OFF). Cuando se expulsa
el reloj de la pantalla, se restaura el contenido anterior a la aparici n del reloj. Por ello, si se han
producido cambios en el monitor desde que apareci el reloj, el fragmento de pantalla restaurado
puede quedar feo, aunque tambin quedara feo de todas maneras si se rellenara de espacios en
blanco. De hecho, esto ltimo es lo que sucede cuando se trabaja con pantallas grficas.
Cuando comienza a sonar la alarma, estando o no el reloj en pantalla, se puede pulsar Ctrl-Alt-R
o AltGr-R para cancelarla; de lo contrario avisar durante 15 segundos. Este es el nico caso en
que AltGr-R o Ctrl-Alt-R no servir para activar o desactivar el reloj (una posterior pulsaci n,
s). Despus de haber sonado, la alarma quedar desactivada y no volver a actuar, ni siquiera
al cabo de 24 horas.
El programa utiliza el convenio CiriSOFT para detectar su presencia en memoria, por lo que es
desinstalable incluso aunque no sea el ltimo programa residente cargado, siempre que tras l se
hayan instalado slo programas del convenio (o al menos otros que no utilicen las mismas
interrupciones). Posee su propia rutina de desinstalacin (opcin /U), con lo que no es necesario
utilizar la utilidad general de desinstalacin. Tambin est equipado con las rutinas que asignan
memoria superior XMS o, en su defecto, memoria superior solicitada al DOS 5.0: por ello, aunque
el fichero ejecutable ocupa casi 6 Kb, slo hacen falta 1,5 Kb libres de memoria superior para
instalarlo en este rea, lo que se realiza automticamente en todos los entornos operativos que
existen en la actualidad. Evidentemente, tambin se instala en memoria convencional y sus
requerimientos mnimos son un PC/XT y (recomendable) DOS 3.0 o superior.
El trmino no reentrante que se aplica al DOS significa que no puede ser empleado
simultneamente por dos procesos, sin embargo se trata de un cdigo serialmente reusable
como veremos. El DOS posee tres pilas internas: la pila de E/S (I/O Stack), la pila de disco (Disk
Stack) y la pila auxiliar (Auxiliary Stack). Las funciones 0 a la 0Ch utilizan la pila de E/S; las
restantes utilizan la pila de disco. Si se llama al DOS durante un error cr tico (por ejemplo, DIR
B: cuando no hay disquete en la unidad) se utiliza la pila auxiliar. La existencia de estas pilas
locales significa que si el DOS es llamado cuando ya estaba ejecutando una funcin (y ya haba
conmutado a la pila interna correspondiente) volver a inicializar el puntero de pila y en la nueva
reentrada se cargar el contenido previo de la pila. Si estaba ejecutando una funcin 0-0Ch y se
le llama solicitando una 0Dh o superior, no habr problemas, ya que hay dos pilas separadas para
cada caso; sin embargo no suele haber tanta suerte. Algunas funciones del DOS son tan simples que
ste no conmuta a ninguna pila interna: la 33h, 50h, 51h, 62h y 64h: con ellas s es reentrante;
con las dems (que adems son la mayora y las ms interesantes) por desgracia no lo es.
Para solucionar este problema hay dos mtodos: interrumpir al DOS slo cuando no est
ejecutando alguna funcin; esto es, cuando no est dentro de una INT 21h. Alternativamente, el
programa residente puede salvar todo el contexto del DOS, incluyendo las tres pilas internas, para
restaurarlas despus de haber realizado su tarea. En este libro trataremos especialmente el primer
mtodo, tradicionalmente el ms empleado y el ms probado.
Para detectar si el ordenador est ejecutando cdigo del DOS (si est dentro de una INT 21h)
se podra desviar esta interrupcin y colocar una nueva rutina que incrementara una variable
indicativa al principio, llamara a la INT 21h original y despus volviera a decrementar la variable
antes de retornar. As, por ejemplo, desde una interrupcin de teclado o peridica, se podr a
comprobar si el DOS ya est trabajando antes de llamarle (variable distinta de cero). Sin embargo,
ms que una variable habra que tener dos (una para indicar que la pila E/S est en uso y otra
para la pila de disco). Por otro lado, la rutina debera ser algo m s sofisticada todav a, ya que
hay funciones del DOS que no retornan (las de terminar programa: la 0, 31h y 4Ch) y esto, si no se
tiene cuidado, significara no decrementar como es debido la variable que indica que se ha
abandonado la INT 21h. Adems, para liar an ms el asunto, qu hacer con los errores
crticos?. Y, para colmo, todava hay ms: si el DOS est dentro de la INT 21h, funcin 0Ah
(entrada en buffer por teclado), nuestra variable dira que no es posible usar el DOS en ese
momento, ya que est ya en uso, cuando est cientficamente demostrado que en este caso s
es reentrante si se utiliza una funcin 0Dh o superior (en la lnea de comandos, el DOS est
ejecutando precisamente esa funcin de entrada por teclado).
Por fortuna, el DOS viene aqu en nuestro socorro: no ser preciso disear la compleja
rutina propuesta, ya que el propio sistema posee una variable interna que indica si en ese momento
puede ser interrumpido. Se trata de la variable no documentada InDOS. Existe una funcin
secreta del DOS para obtener la direccin de esta variable, de un byte, que valdr 0 en el caso de
que el DOS est libre y pueda ser llamado desde un programa residente. Esa variable se
incrementa automtica y adecuadamente con las llamadas a la INT 21h, y se decrementa al salir.
No hay mejor manera de aprender a construir programas residentes fiables y eficientes que
espiar cmo lo hace el fabricante del sistema operativo con los suyos propios. El comando PRINT
del DOS, cuando se queda residente, desva un montn de interrupciones, entre ellas la 1Ch
(equivalente a la 8) y la 28h. La interrupcin 28h (Idle) es invocada por el DOS en las operaciones
de entrada por teclado, cuando se encuentra libre de otras tareas, para permitir a los programas
residentes aprovechar ese tiempo muerto de CPU. Desde dentro de una INT 28h se puede usar el
DOS incluso aunque InDOS sea igual a 1. El comando PRINT, cuando entra en acci n, realiza
adems una serie de tareas adicionales: preserva el DTA activo (rea de transferencia a disco), el
PSP del programa interrumpido, los vectores de INT 1Bh (Ctrl-Break), INT 23h (Ctrl-C), INT 24h
(manipulador de errores crticos); desva esos vectores hacia unas rutinas propias; a
continuacin establece un DTA y un PSP propios. Tras enviar los caracteres a la impresora,
leyndolos del disco (con las funciones del DOS, por supuesto) vuelve a restaurar todo lo salvado.
Pero vayamos ms despacio.
Para obtener la direccin de InDOS se puede emplear la funcin 34h del DOS, que devuelve
un puntero en ES:BX a dicha variable. La direccin de InDOS es constante, por lo que se puede
inicializar al instalar el programa residente (no cambiar de lugar en toda la sesin de trabajo).
Como luego nos ser de utilidad, conviene decir aqu ahora que el Bandern de Errores
Crticos del DOS est situado justo despus de InDOS en las versiones 2.x y justo antes en la
3.0 (en la 3.1 y siguientes, la funcin 5D06h permite obtener su direcci n en DS:SI). Por tanto,
desde los programas residentes bastar, en principio, comprobar que InDOS es igual a cero antes
de llamar al DOS (y, de paso, que el Bandern de Errores Crticos es tambin cero). En caso
contrario, se puede inicializar una variable que indique que el programa residente tiene a n
pendiente su ejecucin: desde la interrupcin peridica se puede comprobar si est pendiente
la activacin del programa residente y se puede verificar el estado del DOS hasta que ste est
listo para ser llamado, lo que suceder tarde o temprano. Adems de la interrupcin peridica,
tambin se puede desviar la INT 28h: desde esta interrupcin se puede llamar al DOS, como dije
antes, incluso aunque InDOS sea igual a 1 (pero no mayor) siempre que la funcin del DOS a
ejecutar sea superior a la 0Ch (lo ms normal). Sin embargo, cuando sea seguro llamar al DOS,
habr que hacer algunas cosas ms antes de empezar a realizar la labor propia del programa
residente.
En el PSP se almacena mucha informacin vital para la ejecucin de los programas. Una de
las reas ms importantes es el JFT (Job File Table) que contiene informacin referida a los
ficheros del programa que se ejecuta. No es conveniente, desde un programa residente, modificar el
PSP del programa principal. Por tanto, habr que anotar la direccin del PSP actual y conmutar al
del programa residente; al final del trabajo se proceder a restaurar el PSP del programa principal.
Si no se toma esta precaucin, podra suceder de todo. Por ejemplo: si el programa residente abre
un fichero usando el PSP del programa principal, cuando ste termine (el programa principal) ese
fichero ser probablemente cerrado sin que el programa residente se entere. Para obtener la
direccin del PSP activo se puede utilizar la funcin Get PSP (50h; la 62h, totalmente
equivalente) que devuelve en BX su segmento; la funcin Set PSP (51h) permite establecer un
nuevo PSP indicando en BX el segmento. Si se desea mantener la compatibilidad con el DOS 2.x,
hay que tener en cuenta adems un error de este sistema operativo. La errata consiste en que las
funciones 50h y 51h no operan bien en el DOS 2.x a menos que el sistema use la pila de errores
crticos. Por tanto, con esta versin del sistema se puede forzar el Bander n de Errores
Crticos a un valor 0FFh antes de llamar a las funciones 50h y 51h, para volverlo a poner a cero
despus: as, el DOS cree que el sistema est en medio de un error y usa la pila que queremos.
Adems del PSP se debe cambiar el DTA (Disk Transfer Area) que utiliza el DOS para acceder
al disco: este rea est normalmente en el offset 80h del PSP (sobrescribe el campo de
parmetros de la lnea de comandos cuando el programa accede a disco) y ocupa 128 bytes.
Basta con preservar el DTA del programa principal, cuya direccin se obtiene en ES:BX con la
funcin Get DTA (2Fh), y activar un nuevo DTA (por ejemplo, en el offset 80h del PSP de
programa residente) utilizando la funcin Set DTA (1Ah), pasando su direccin en DS:DX.
Como complemento, si se van a emplear las funciones de acceso a disco del DOS, tambi n es
conveniente monitorizar la INT 13h para evitar un acceso a disco cuando no ha finalizado el
anterior (aunque el DOS est en posicin correcta). Si se van a emplear las INT 25h/26h,
convendra monitorizarlas; as como la INT 10h si se utilizan servicios de vdeo (aunque sean
del DOS). Por monitorizar se entiende interceptar esa interrupcin e instalar una rutina de control
que incremente y decremente una variable cada vez que empieza o termina una de esas
interrupciones, con objeto de saber cundo se est dentro de ellas. En general, los programas
residentes que accedan demasiado intensivamente al disco (en una especie de multitarea) deber an
monitorizar no slo INT 13h sino tambin INT 25h e INT 26h.
10.10.3. - RESUMIENDO, NO ES TAN DIFICIL!.
El procedimiento a seguir, por tanto, para activar un programa residente respondiendo por
ejemplo a la pulsacin de una combinacin de teclas, es el siguiente:
- Desde la interrupcin del teclado, y una vez detectada la combinacin de teclas, intentar
activar el programa residente. Ser posible activarlo si: no estaba ya activo, no hay una INT 13h
en curso, InDOS=0 y el Bandern de Errores Crticos tambin es igual a 0.
- Por si falla, desde la interrupcin del temporizador se puede comprobar si est pendiente
an la activacin del programa residente (por si no se pudo cuando se pulsaron las teclas); en ese
caso, volverlo a intentar de nuevo, con los mismos pasos que en el caso anterior.
- Como mnimo habrn de existir dos variables de control: Una que indica si el programa
residente ya est activo (y se deben rechazar o posponer nuevas activaciones, ya que ste se
supone no reentrante). Otra, que indique si el programa residente va a ser activado en breve (en
cuanto el DOS nos deje). Ambas variables son semforos que conviene tratar con cuidado, para
evitar reentradas en el programa residente: cuando desde una interrupcin son comprobadas (ej.,
desde una INT 28h) podra producirse otra interrupcin (como INT 8) lo que complica
ligeramente la programacin. Aunque no lo he dicho antes, todos los programas residentes que
usan el DOS deben definir una pila propia, ya que la del programa interrumpido puede no ser
suficientemente grande. Por el hecho de definir una pila propia, los programas residentes que usan
funciones del DOS no son reentrantes; lo cual no es, por lo general, una limitaci n muy
importante.
- Por supuesto, antes de ejecutar su cdigo propiamente dicho, el programa residente deber
preservar el DTA, el PSP y la informacin extendida de errores, as como los vectores de INT
1Bh/23h/24h. Despus deber desviar las INT 1Bh e INT 23h hacia un IRET (para evitar un Ctrl-
Break Ctrl-C) y la INT 24h, para implementar una gestin propia de los errores cr ticos. Al
final, deber restaurar todo de nuevo.
Toda la informacin vertida hasta ahora procede de la versin original del libro
Undocumented DOS, citado en la bibliografa. Sin embargo, en mi experiencia personal con los
programas residentes he sacado la conclusin de que es conveniente tambin desviar la INT 21h
e intentar desde la misma activar el programa residente, tal como si se tratara de una interrupci n
peridica ms. El motivo es que desde la INT 8 la INT 1Ch hay que tener bastante suerte para
que el DOS est desocupado cuando se producen, ya que estas interrupciones slo suceden 18
veces cada segundo. Esto significa que, por ejemplo, mientras se formatea un disco y se intenta
activar el programa residente, puede que ste no responda hasta haberse formateado medio disco
o, incluso, hasta finalizar el formateo. Sin embargo, mientras se formatea el disco, se producen
miles de llamadas a la INT 21h: cuando InDOS sea cero tras acabar una sola de estas llamadas,
podremos darnos cuenta; sin embargo, utilizando slo la interrupcin peridica estaremos a
merced de la suerte. Desviar la INT 21h e intentar activar el programa residente desde ella permite
por ejemplo que ste acte, en medio de un formateo de disco, de manera casi instant nea
cuando se le requiere. Otro ejemplo: con el mtodo normal, sin controlar la INT 21h, mientras se
saca un directorio por pantalla y se intenta activar el programa residente, cada cierto n mero de
lneas ste responde; controlando la INT 21h, responde cada dos o tres caracteres impresos. Es
evidente que la INT 21h pone a nuestra disposicin un mtodo mucho ms efectivo a menudo
que la interrupcin peridica; sin embargo, tampoco es conveniente prescindir de esta ltima ya
que la INT 21h slo funciona cuando alguien llama al DOS (y no siempre alguien lo est
llamando). En general, conviene utilizar las dos interrupciones a la vez: si bien interceptar la INT
21h no est recomendado en ningn sitio excepto en este libro, puedo asegurar que he tenido
bastantes ocasiones de comprobar que es completamente fiable.
Hasta ahora hemos visto el mtodo ms comn para poder emplear el DOS desde un
programa residente. Sin embargo, este mtodo depende de la molesta variable InDOS. Esto limita
la efectividad de los programas residentes, que no pueden ser activados por ejemplo cuando se
ejecuta un comando TYPE. La solucin alternativa que se apuntaba al principio de este apartado
consiste en salvar el contexto del DOS y restaurarlo despus, algo factible desde el DOS 3.0. Esto
supone bastantes diferencias respecto al mtodo estudiado hasta ahora. En lugar de chequear
InDOS se debe verificar que el DOS no est en una seccin crtica (que por fortuna es lo m s
normal) como luego veremos; y esto tanto desde la interrupcin del teclado como desde la
peridica o desde la INT 28h. Al comienzo del cdigo del programa residente, se debe salvar el
estado del DOS: esto significa que hay que pedir memoria al sistema (o tenerla reservada de
antemano en cantidad suficiente) para contener esa informacin. Tambin hay que instalar las
nuevas rutinas de control de INT 1Bh, 23h y 24h; no es necesario preservar el PSP activo (ya
incluido en el rea salvada): lo que s es preciso es activar el PSP propio. Tampoco es preciso
preservar el DTA ni la informacin extendida de errores: aunque se debe establecer un nuevo
DTA, al restaurar el estado del DOS ms tarde ste ser tambin automticamente
restablecido. Y bien, en qu consiste el estado o contexto del DOS?: se basa en un rea de
datos, el SDA (Swappable Data Area), cuyo tamao oscila entre 24 bytes y 2 Kbytes. Este rea
almacena el PSP activo y las tres pilas del DOS, as como la direccin del DTA...
Para manipular el SDA se puede emplear la funcin del sistema Get Address of DOS
Swappable Data Area (5D06h), que devuelve en DS:SI un puntero al SDA, en DX el n mero
mnimo de bytes a preservar cuando el DOS est libre y en CX el nmero de bytes a preservar
cuando el DOS est ocupado (InDOS distinto de cero). Desde la versin 4.0 del DOS se debe
utilizar en su lugar la funcin Get DOS Swappable Data Areas (5D0Bh), ya que este sistema no
posee un nico rea de datos sino mltiples. El procedimiento general consistir, simplemente,
en salvar el SDA al principio y restaurarlo al final.
Como se dijo antes, el SDA slo puede ser accedido cuando el DOS no est en un momento
crtico. Cuando el DOS entra y sale de los momentos crticos, llama a la INT 2Ah con
AX=8000h (inicio de momento crtico) o bien AX=8100h o AX=8200h (fin de momento
crtico). Se debe interceptar la INT 2Ah e incrementar/decrementar una variable que indique las
entradas/salidas del DOS en fase crtica.
Este mtodo para gestionar los programas residentes requiere algo ms de memoria: en
especial, si se quiere asegurar la compatibilidad con futuras versiones del sistema, habr que
reservar mucho ms de 2Kb para almacenar el SDA (intentar utilizar memoria convencional puede
fallar, ya que el programa principal puede tenerla toda asignada) aunque este problema es menor en
mquinas con memoria expandida o extendida. No hay que olvidar que el SDA no se puede grabar
en disco (para eso hay que usar el DOS, y el DOS no se puede emplear hasta no haber salvado el
SDA). Tambin es quiz algo ms complejo. Sin embargo, aade algo ms de potencia a los
programas residentes, ya que pueden ser activados casi en cualquier momento y prcticamente en
cualquier circunstancia. El autor de este libro nunca ha empleado este mtodo.
10.10.5.- METODOS MENOS ORTODOXOS.
Hay programadores que utilizan mtodos muy curiosos para emplear los servicios del DOS
desde los programas residentes. Un ejemplo, expuesto por Douglas Boling en su artculo de la
revista RMP (Ed. Anaya, Marzo-Abril de 1992) consiste en activar el Bander n de Errores
Crticos antes de llamar a las funciones ordinarias del DOS: de esta manera, se utiliza la pila de
errores crticos en lugar de la de disco, con lo que no hay conflictos. Esto, por supuesto, sin que el
DOS estuviera antes en estado crtico (en caso de estarlo hay que esperar). El inconveniente de
este mtodo es que slo un programa residente de este tipo puede estar activo en un momento
dado en el ordenador. Evidentemente, tambin hay que desviar la INT 24h para controlar un
posible error crtico de verdad.
Para visualizar las pantallas capturadas puede utilizarse la utilidad SCRVER.C, que admite
comodines para poder ver cualquier conjunto de ficheros. Con SCR2TXT.C se convierten las
pantallas capturadas (de 40/80/94/100/120/132 160 columnas) a modo texto: se suprimen los
colores, se eliminan la mayora de los cdigos de control, se quitan los espacios en blanco al
final de las lneas y se aaden retornos de carro para separarlas. Esto ltimo provoca, en
pantallas que ocupan justo las 80 columnas, que al emplear el TYPE del DOS las l neas queden
separadas por una lnea extra en blanco (si tuvieran 79 columnas o si se carga desde un editor de
texto, no habr problemas).
Listados de SCRVER 1.0 y SCR2TXT 1.0
En los modos estndar de IBM (y en general tambin en los no estndar) cuando se solicita a
la BIOS que establezca el modo de vdeo (vanse las funciones de la BIOS en los apndices) si
el bit ms significativo del modo se pone a 1, al cambiar de modo no se limpia la pantalla. Esta
caracterstica est disponible slo en mquinas con tarjeta EGA o VGA (tanto XT como AT).
Se trata de una posibilidad muy interesante, que permite a los programas residentes activar
momentneamente una pantalla de texto, preservar el fragmento de la misma que van a emplear y,
al final, restaurarlo y volver al modo grfico como si no hubiera sucedido nada, sin necesidad de
preservar ni restaurar zonas grficas. Tambin habrn de preservar la posicin inicial del
cursor y la pgina de vdeo activa inicialmente (que habrn de restaurar junto con el modo de
vdeo), as como las paletas de la EGA y VGA, tareas stas que puede simplificar la BIOS.
Por ejemplo: si la pantalla estaba en modo 12h (VGA 640x480 con 16 colores) se puede activar
el modo 83h (el 3 con el bit 7 activo) de texto de 80x25 y, cuando halla que restaurarla, activar el
modo 92h (el 12h con el bit 7 activo). Evidentemente, despus habr que engaar de alguna
manera a la BIOS para que crea que la pantalla est en modo 12h y no 92h (sutil diferencia,
no?) y ello se consigue borrando el bit ms significativo de la posicin 40h:87h (la variable de
la BIOS 40h:49h indica siempre el nmero de modo de pantalla con el bit m s significativo
borrado: este bit se almacena separadamente en 40h:87h). Esta operacin es segura, ya que la
diferencia entre el modo 12h y el 92h es slo a nivel de software y no de hardware. Un programa
residente elegante, adems, se tomar la molestia de dejar activo el bit de 40h:87h si as lo
estaba al principio, antes de restaurar el modo grfico (poco probable, pero posible -sobre todo
cuando el usuario activa ms de un programa residente de manera simultnea-).
Esta tcnica presenta, sin embargo, una ligera complicacin al trabajar en el modo 13h de la
VGA (320x200 con 256 colores) o en la mayora de los modos SuperVGA. El problema consiste
en que, al pasar a modo texto, la BIOS define el juego de caracteres -que en la EGA/VGA es
totalmente programable- utilizando una cierta porcin de la memoria de vdeo de la tarjeta. Por
desgracia, esa porcin de la memoria de la tarjeta grfica es parte de la pantalla en el modo 13h y
en los modos SuperVGA. La solucin no es muy complicada, aunque s un poco engorrosa. Ante
todo, recordar que esto slo es necesario en modos de pantalla avanzados o en el 13h. Una posible
solucin consiste en preservar la zona que va a ser manchada (8 Kb) en un buffer, pasar a modo
texto y, antes de volver al modo grfico, redefinir el juego de caracteres de texto de tal manera que
al volver a modo grfico ya est restaurada la zona manchada. Este orden de operaciones no es
caprichoso y lo he elegido para reducir los accesos al hardware, como se ver . El problema
principal radica en el hecho de que la arquitectura de la pantalla en los modos gr ficos y de texto
vara de manera espectacular. Por ello, no hay un algoritmo sencillo para acceder a la zona de
memoria de grficos que hay que preservar. Para no desarrollar complicadas rutinas -por si fuera
poco, una para cada modo grfico- es ms cmodo programar el controlador de grficos para
configurar de manera cmoda la memoria de vdeo y preservar sin problemas los 8 Kb deseados.
Despus, no hace falta restaurar el estado de ningn controlador de vdeo, ya que la BIOS lo
reprogramar correctamente al pasar a modo texto. Por ltimo, y estando an en modo texto, se
redefinir el juego de caracteres con los 8 Kb preservados. Como inmediatamente despus se
vuelve al modo grfico, el usuario no notar la basura que aparezca en la pantalla durante breves
instantes y, de nuevo, la BIOS reprogramar adecuadamente el controlador de grficos. El
siguiente ejemplo prctico parte de la suposicin de que nos encontramos en el modo 13h:
CALL def_car_on ; habilitar acceso a tabla de caracteres
CALL preservar8k ; guardar 8 Kb de A000:0000 en un buffer
MOV AX,83h
INT 10h ; pasar a modo texto 80x25
; ... operar en modo texto ...
CALL def_car_on ; habilitar acceso a tabla de caracteres
CALL restaurar8k ; copiar el buffer de 8 Kb en A000:0000
MOV AX,93h ; 13h + 80h
INT 10h ; restaurar de nuevo el modo grfico
Las rutinas preservar8k y restaurar8k son tan obvias que, evidentemente, no las comentar. Sin
embargo, la rutina que prepara el sistema de vdeo de tal manera que se pueda redefinir el juego
de caracteres de texto, requiere conocimientos acerca de la arquitectura de las tarjetas grficas
EGA y VGA a bajo nivel. Esta informacin puede obtenerse en libros especializados sobre
grficos (consltese la bibliografa) aunque a continuacin expongo el listado de def_car_on;
eso s, sin entrar en detalles tcnicos acerca de su funcionamiento:
def_car_on PROC
MOV DX,3C4h ; puerto del secuenciador
LEA SI,car_on ; cdigos a enviarle
MOV CX,4
CLD
CLI ; precauciones
def_on_1: LODSW
OUT DX,AX ; programar registro
LOOP def_on_1
STI ; no ms precauciones
MOV DL,0CEh ; 3CEh = puerto del controlador de
grficos
MOV CX,3
def_on_2: LODSW
OUT DX,AX ; programarlo
LOOP def_on_2
RET
car_on DW 100h, 402h, 704h, 300h, 204h, 5, 6 ; datos
def_car_on ENDP
El otro problema est relacionado con la multitarea de Windows. Si se abren varios procesos
DOS desde este entorno y se activa el programa residente en ms de uno de ellos, pueden aparecer
problemas de reentrada (la segunda ejecucin estropear los datos de la primera). La solucin
ms sencilla consiste en no permitir la invocacin del programa residente desde ms de una
tarea; sin embargo, en algunos TSR (tales como utilidades de macros de teclado, etc.) esto supone
una grave e intolerable restriccin. Otra solucin sencilla consiste en obligar al usuario a instalar
el TSR en cada sesin de DOS abierta, con lo que todo el entorno de operacin ser local a
dicha sesin. Para los casos en que no sea recomendable esto ltimo, se puede quemar el ltimo
y ms efectivo cartucho: comunicar el TSR con el conmutador de tareas de Windows para emplear
memoria instantnea. El nico inconveniente es que Windows slo facilita memoria
instantnea en el modo extendido 386, no en el modo estndar ni -en el caso de la versi n 3.0-
en el real. Sin embargo, con la versin 3.1 de Windows, en el modo est ndar se puede emplear el
conmutador de tareas del DOS 5.0, que es el que utiliza dicho modo. No deja de ser una pena tener
que utilizar un mtodo diferente para el modo estndar que para el extendido, aunque la
recompensa para quien implemente soporte en sus TSR para los dos mtodos es que les har
compatibles tambin con el conmutador de tareas del MS-DOS 5.0. Se puede interceptar el
arranque de Windows y comprobar si lo hace en modo real, en cuyo caso se puede abortar su
ejecucin y emitir un mensaje de error para solicitar al usuario que no desinstale el TSR antes de
entrar en ese modo de Windows.
Cuando Windows arranca, llama a la INT 2Fh con AX=1605h: un TSR puede interceptar esta
llamada (como en cualquier otra interrupcin, llamando primero al controlador previo) y
comprobar si el bit 0 de DX est a cero (en ese caso se estar ejecutando en modo extendido): si
se desea abortar la ejecucin de Windows bastar cargar un valor distinto de 0 en CX antes de
retornar.
Si el TSR necesita reas de datos locales a cada sesin en el modo extendido, puede
indicrselo a Windows con un puntero a un rea de datos denominado SWSTARTUPINFO en
ES:BX. Para ello, y teniendo en cuenta que puede haber varios TSR que intercepten las llamadas a
la INT 2Fh con AX=1605h, este rea ha sido diseada para almacenar una cadena de referencias
entre todos ellos; por ello es preciso almacenar primero el ES:BX inicial de la rutina en dicha
estructura y cargar ES:BX apuntndola antes de retornar. El formato de SWSTARTUPINFO es el
siguiente:
DW 3 ; versin de la estructura
DD ? ; puntero a la prxima estructura SWSTARTUPINFO (ES:BX
inicial)
DD 0 ; puntero al nombre ASCIIZ del dispositivo virtual ( 0)
DD 0 ; datos de referencia del dispositivo virtual (si tiene
nombre)
DD ? ; puntero a la tabla de registros de datos locales ( 0)
El formato de la tabla de registros de datos locales, que define las estructuras de datos que
sern locales a cada sesin, es el siguiente:
DD ? ; direccin de memoria de la estructura
DW ? ; tamao de la estructura
. . .
. . .
DD 0 ; estructura NULL
DW 0 ; (fin de lista)
En los momentos crticos en que el TSR deba evitar una conmutacin de tareas, puede
emplear las funciones BeginCriticalSection (llamar a INT 2Fh con AX=1681h) y
EndCriticalSection (llamar a INT 2Fh con AX=1682h); el TSR debe estar poco tiempo en fase
crtica para no ralentizar Windows.
Para detectar la presencia del conmutador de tareas del MS-DOS 5.0 se debe llamar a la INT 2Fh
con AX=4B02h: si a la vuelta AX es 0, significa que est cargado y ES:DI apunta a la rutina de
servicio del mismo, que pone varias funciones a disposicin de los TSR: los TSR deber n
ejecutar la funcin AX=4 (Conectar a la cadena de Notificacin) al instalarse en memoria y la
funcin AX=5 (Desconectar de la Cadena de Notificacin) al ser desinstalados, para informar al
conmutador. Una vez enganchado, el TSR ser llamado por el conmutador de tareas para ser
informado de todo lo interesante que suceda (de cosas tales como la creacin y destruccin de
sesiones, suspensin del conmutador, etc.) por medio de la ejecucin de la rutina de
notificacin del mismo, pudiendo el TSR permitir o no, por ejemplo, la suspensin de la
sesin... el aviso de inicio de sesin es fundamental para los TSR que tienen reas de datos
temporales que inicializar al comienzo de cada sesin. El procedimiento general lo inicia el
conmutador de tareas llamando a la INT 2Fh con AX=4B01h: los TSR ser n invocados unos tras
otros (pasndose mutuamente el control). Para gestionar esto existe una estructura de datos
denominada SWCALLBACKINFO (apuntada por ES:BX al llamar a INT 2Fh con AX=4B01h):
DD ? ; puntero a la estructura SWCALLBACKINFO anterior
DD ? ; puntero a la rutina de notificacin del TSR
DD ? ; rea reservada
DD ? ; puntero a la lista de estructuras SWAPINFO
Las funciones que debe soportar la rutina de notificacin, apuntada por la estructura
SWCALLBACKINFO, son las siguientes:
0000h inicializacin del conmutador
Devuelve: AX = 0000h si permitido
= no cero si no permitir iniciar el conmutador
0001h pregunta de suspensin del conmutador
BX = Identificacin de sesin
Devuelve: AX = 0000h si permitir conmutacin (el TSR no est en
regin crtica)
= 0001h si no
0002h suspensin del conmutador
BX = Identificacin de sesin
interrupciones inhibidas
Devuelve: AX = 0000h si permitido conmutar de sesin
= 0001h si no
0003h activando conmutador
BX = Identificacin de sesin
CX = banderines de estado de la sesin
bit 0: activo si primera activacin de la sesin
bits 1-15: reservado (0)
interrupciones inhibidas
Devuelve: AX = 0000h
0004h sesin activa del conmutador
BX = Identificacin de sesin
CX = banderines de estado de la sesin
bit 0: activo si primera activacin de la sesin
bits 1-15: reservado (0)
Devuelve: AX = 0000h
0005h crear sesin del conmutador
BX = Identificacin de sesin
DEVUELVE: AX = 0000h si permitido
= 0001h si no
0006h destruir sesin
BX = Identificacin de sesin
Devuelve: AX = 0000h
0007h salida del conmutador
BX = banderines
bit 0: activo si el conmutador que llama es el nico cargado
bits 1-15: reservados (0)
Devuelve: AX = 0000h
Captulo XI: CONTROLADORES DE DISPOSITIVO
11.1. - INTRODUCCIN.
Los controladores de dispositivo han sido tradicionalmente programas binarios puros, similares a
los COM aunque ensamblados con un ORG 0, a los que se les colocaba una extensi n SYS. Sin
embargo, no hay razn para que ello sea as ya que un controlador de dispositivo puede estar
incluido dentro de un programa EXE, con la condicin de que el cdigo del controlador sea el
primer segmento de dicho programa. El EMM386.EXE del MS-DOS 5.0 sorprendi a m s de
uno en su da, ya que llamaba la atencin observar cmo se poda cargar con DEVICE: lo
cierto es que esto es factible incluso desde el DOS 2.0 (pese a lo que pueda indicar alg n libro),
pero ha sido mantenido casi en secreto. Actualmente es relativamente frecuente encontrar
programas de este tipo. La ventaja de un controlador de dispositivo de tipo EXE es que puede ser
ejecutado desde el DOS para modificar sus condiciones de operacin, sin complicar su uso por
parte del usuario con otro programa adicional. Adems, un controlador de dispositivo EXE puede
superar el lmite de los 64 Kb, ya que el DOS se encarga de relocalizar las referencias absolutas a
segmentos como en cualquier programa EXE ordinario. Por cierto, el RAMDRIVE.SYS de
WINDOWS 3.1 (no el de MS-DOS 5.0) y el VDISK.SYS de DR-DOS 6.0 son realmente programas
EXE, aunque renombrados a SYS (aviso: no recomiendo a nadie ponerles extensin EXE y
ejecutarlos despus).
11.2.- ENCABEZAMIENTO Y PALABRA DE ATRIBUTOS.
Todo controlador de dispositivo de bloques comienza con una cabecera estndar, mostrada a
continuacin:
+-------------------------------------------------------------------------------
-------+
| CABECERA DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES
|
+-------------------------------------------------------------------------------
-------+
| offset 0 DD 0FFFFFFFFh ; doble palabra de valor -1
|
| offset 4 DW 0 ; palabra de atributos (ejemplo arbitrario)
|
| offset 6 DW estrategia ; desplazamiento de la rutina de estrategia
|
| offset 8 DW interrupcion ; desplazamiento de la rutina de interrupcin
|
| offset 10 DB 1 ; nmero de discos definidos: 1 por ejemplo
|
| offset 11 DB 7 DUP (0) ; 7 bytes no usados
|
+-------------------------------------------------------------------------------
-------+
Al principio, una doble palabra con el valor 0FFFFFFFFh (-1 en complemento a 2) ser
modificada posteriormente por el DOS para enlazar el controlador de dispositivo con los dem s
que haya en el sistema, formando una cadena. No fue una ocurrencia muy feliz elegir precisamente
ese valor inicial como obligatorio para la copia en disco, dado que la instruccin de c digo de
operacin 0FFFFh es ilegal y bloquea la CPU si es ejecutada. Esto significa que un controlador de
dispositivo binario puro no puede ser renombrado a COM y ejecutado tambin desde el DOS
(habr de ser necesariamente de tipo EXE). A continuacin, tras esta doble palabra viene una
palabra de atributos, cuyo bit ms significativo est borrado en los dispositivos de bloques para
diferenciarlos de los dispositivos de caracteres. Tras ello, aparecen los offsets a las rutinas de
estrategia e interrupcin, nicas de las que consta el controlador. Por ltimo, un byte indica
cuntas nuevas unidades de disco se definen y detrs hay 7 bytes reservados -ms bien no
utilizados-.
+-------------------------------------------------------------------------------
-------+
| PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES
|
+-------------------------------------------------------------------------------
-------+
| bit 15: borrado para indicar dispositivo de bloques
|
| bit 14: activo si se soporta IOCTL
|
| bit 13: activo para indicar disco de formato no-IBM
|
| bit 12: reservado
|
| bit 11: en DOS 3+ activo si soportadas rdenes OPEN/CLOSE y REMOVE
|
| bit 10: reservados
|
| bit 9: no documentado. Al parecer, el DRIVER.SYS del DOS 3.3 lo emplea para
|
| indicar que no est permitida una E/S directa en las unidades
nuevas |
| bit 8: no documentado. El DRIVER.SYS del DOS 3.3 lo pone activo para las
|
| unidades nuevas
|
| bit 7: en DOS 5+ activo si soportada orden 19h (CHECK GENERIC IOCTL
SUPPORT) |
| bit 6: en DOS 3.2+ activo si soportada orden 13h (GENERIC IOCTL)
|
| bits 5-2: reservados
|
| bit 1: activo si el driver soporta direccionamientos de sector de 32 bits
|
| (unidades de ms de 65536 sectores y, por ende, ms de 32 Mb).
|
| bit 0: reservado
|
+-------------------------------------------------------------------------------
-------+
Aunque en el ejemplo aparece AUX, ello es un ejemplo de lo que no se debe hacer, a no ser que
sea lo que realmente se desea hacer (se est creando un dispositivo AUX que ya existe, con lo que
se sobrescribe y anula el puerto serie original). En general, adems de los nombres de los
dispositivos del sistema, no deberan utilizarse los que crean ciertos programas (como el
EMMXXXX0 del controlador EMS, etc.). Conviene decir aqu que muchos de los controladores
de dispositivo de caracteres instalados en el ordenador no lo son tal realmente, sino que se trata de
simples programas residentes que se limitan a dar error a quien intenta acceder a ellos (pruebe el
lector a ejecutar la orden COPY *.* EMMXXXX0: con el controlador de memoria expandida
instalado) aunque algunos implementan ciertas funciones va IOCTL.
La tabla anterior resume las rdenes que puede soportar un controlador de dispositivo; en
general no ser preciso implementar todas: de hecho, incluso para un disco virtual basta con
algunas de las primeras 16. Todas las rdenes devuelven una palabra de estado al sistema
operativo, cuyo formato puede consultarse a continuacin. En general, las ordenes no soportadas
pueden originar un error o bien ser sencillamente ignoradas (en ese sentido, crear un dispositivo
NUL es tarea realmente sencilla).
+-------------------------------------------------------------------------------
--------+
| FORMATO DE LA PALABRA DE ESTADO
|
+-------------------------------------------------------------------------------
--------+
| bit 15: Activo si hay error, en ese caso los bits 0-7 indican el tipo de
error |
| bits 14-10: Reservados
|
| bit 9: Activo si el controlador de dispositivo no est listo. En las
operaciones |
| de entrada est listo si hay un carcter en el buffer de entrada o
si tal |
| buffer no existe; en las de salida cuando el buffer an no est
lleno. |
| bit 8: Activo si el controlador de dispositivo ha acabado de ejecutar la
orden. |
| Hasta el DOS 5.0 al menos, esto es siempre as (en un hipottico
sistema |
| multitarea, una orden podra ejecutarse en varias rfagas de CPU).
|
| bits 7-0: Cdigo de error, si el bit 15 est activo:
|
| 00h disco protegido contra escritura
|
| 01h unidad desconocida
|
| 02h unidad no preparada
|
| 03h orden desconocida
|
| 04h error de CRC
|
| 05h longitud invlida de la cabecera de peticin
|
| 06h fallo en el posicionamiento del cabezal
|
| 07h medio fsico desconocido
|
| 08h sector no encontrado
|
| 09h impresora sin papel
|
| 0Ah error de escritura
|
| 0Bh error de lectura
|
| 0Ch anomala general
|
| 0Dh reservado
|
| 0Eh (CD-ROM) medio fsico no disponible
|
| 0Fh cambio de disco no permitido
|
+-------------------------------------------------------------------------------
--------+
La construccin de rutinas de gestin para las diversas rdenes que han de soportarse no es
un proceso muy complicado, pese a que est envuelto en una leyenda negra. Sin embargo, puede
que parte de la explicacin que viene a continuacin sobre dichas rdenes sea dif cil de
entender al lector poco iniciado. No hay que olvidar que los controladores de dispositivo respetan
unas normas de comportamiento definidas por el fabricante del DOS, y ms que de intentar
comprender por qu una cosa es de una manera determinada, de lo que se trata es de obedecer. En
general, lo que no se entienda puede ser pasado por alto ya que probablemente no es estrictamente
necesario conocerlo. Adems, casi ningn controlador necesita soportar todas las rdenes, como
se ver al final en los programas de ejemplo.
Esta es la primera de todas las rdenes y se ejecuta siempre una vez cuando el dispositivo es
cargado en memoria, con objeto de que ste se inicialice. Aqu s se pueden emplear
libremente las funciones del DOS (en el resto de las rdenes no: el driver es un programa residente
ms). En su inicializacin el driver decide qu cantidad de memoria se queda residente y puede
analizar la lnea de comandos del CONFIG.SYS para comprobar los parmetros del usuario. En
los dispositivos de bloque se indica tambin al sistema el nmero de unidades definidas por el
controlador y la direccin de una tabla de punteros a estructuras BPB, ya que existe una de estas
estructuras para cada unidad lgica. El BPB (BIOS Parameter Block) es una estructura que
contiene informacin sobre las unidades; puede consultarse en el captulo 7. Aunque el BPB ha
sido ampliado en las ltimas versiones del DOS, para construir discos de menos de 65536 sectores
solo hace falta completar los primeros campos (solo hasta los relacionados con el DOS 2.0 o, como
mucho, el 3.0).
En los discos de tipo IBM, los ms comunes, el DOS intenta cooperar con el controlador de
dispositivo en los cambios de disco. Por ello, se las apaa para leer el primer sector de la FAT y se
lo pasa al driver, que as tiene ms fcil la tarea de detectar el tipo de disco y suministrar al
DOS el BPB adecuado, ya que el primer byte de la FAT contiene el tipo de disco (byte descriptor de
medio). En los discos que no son de tipo IBM es el driver quien, por sus propios medios, ha de
aparselas para detectar el tipo de disco introducido en la unidad correspondiente: por ejemplo,
leyendo el sector de arranque. En algunos casos puede resultar til indicar que el disco es de tipo
no IBM; por ejemplo en un controlador para un soporte fsico que necesite detectar el medio
introducido para poder acceder al mismo. Por ejemplo en una disquetera: al introducir un nuevo
disco de densidad diferente al anterior, el intento por parte del DOS de leer la FAT en los discos tipo
IBM provocara un fallo (si esto no sucede con el controlador del propio sistema para las
disqueteras es porque la BIOS suplanta al DOS, realizando quiz algunas tareas ms de las que
debera tener estrictamente encomendadas al detectar un cambio de disco).
+-------------------------------------------------------------------------------
-------+
| CABECERA DE PETICIN DE SOLICITUD PARA LA ORDEN 2 (BUILD BPB)
|
+-------------------------------------------------------------------------------
-------+
| offset 0 13 BYTES: Ya vistos con anterioridad.
|
| offset 13 BYTE: A la entrada, el DOS indica el descriptor del
soporte. |
| (solo en dispositivos de bloque)
|
| offset 14 DWORD: A la entrada, el DOS apunta a un buffer que
contiene el |
| primer sector de la FAT (cuyo 1 byte es el
descriptor de |
| soporte) si el disco es de tipo IBM; de lo
contrario el |
| buffer est vaco y puede emplearse para otro
propsito. |
| offset 18 DWORD: A la vuelta, el driver devuelve aqu la direccin
del BPB |
| del nuevo disco (no la de ninguna tabla de
punteros). |
+-------------------------------------------------------------------------------
-------+
Solo debe ser soportada por los dispositivos de caracteres. Es anloga a INPUT, con la
diferencia de que no se avanza el puntero interno al buffer de entrada de datos tras leer el car cter.
Por ello, tras utilizar esta orden ser preciso emplear despus la 4 para leer realmente el
carcter. La principal utilidad de esto es que el sistema puede saber si el dispositivo tiene ya un
nuevo carcter disponible antes de llamarle, para evitar que ste se quede parado hasta que le
llegue. El bit 9 de la palabra de estado devuelta indica, si est activo, que el dispositivo est
ocupado (sin caracteres).
Solo disponible en dispositivos de caracteres, vaca el buffer del dispositivo. Lo que ste
suele hacer es sencillamente igualar los punteros al buffer de entrada interno (el puntero al ltimo
dato recibido del perifrico y el puntero al prximo carcter a enviar al sistema cuando se lo
pida).
Es otra de las rdenes ms importantes, anloga a INPUT pero actuando al revs. Permite
al sistema enviar datos al dispositivo, bien sean caracteres o sectores completos, segn el tipo de
dispositivo.
Es anloga a OUTPUT, con la salvedad de que el dispositivo efect a, tras escribir, una lectura
inmediata hacia un buffer auxiliar, con la correspondiente comprobacin de que lo escrito es
correcto al comparar ambos buffers. Resulta totalmente absurdo implementarla en un disco virtual
(el 11% de la memoria del sistema podra estar ya destinada a detectar un fallo en cualquier byte
de la misma, y adems es igual de probable el error durante la escritura que durante la
verificacin) por lo que en este caso debe comportarse igual que la orden anterior. En los discos
fsicos de verdad, sin embargo, conviene tomarla en serio.
Es similar a INPUT STATUS y, como sta, propia de los dispositivos de caracteres. Su misi n
es anloga, pero relacionada con el buffer de salida en vez del buffer de entrada.
Solo implementada desde el DOS 3.0 y superior, indica que el dispositivo o un fichero
almacenado en l ha sido abierto. El controlador se limita a incrementar un contador. Esta orden y
las dos siguientes no han de estar necesariamente soportadas.
Solo implementada desde el DOS 3.0 y superior, indica que el dispositivo o un fichero
almacenado en l ha sido cerrado. El controlador se limita a decrementar un contador: si ste
llega a cero, se reinicializan los buffers internos, si los hay, para permitir por ejemplo un posible
cambio de disco.
Solo implementada tambin desde el DOS 3.0 y superior, indica al sistema si el dispositivo es
removible o no, apoyndose en los resultados de las dos rdenes anteriores.
Solo es admitida en dispositivos de caracteres y a partir del DOS 3.0; sirve para enviar m s de
un carcter al perifrico. En concreto, se envan todos los que sean posibles (de la cantidad
solicitada) hasta que el perifrico est ocupado: entonces se retorna. Aqu no se considera un
error no haber podido transferir todo. Esta funcin es til para acelerar el proceso de salida.
Las rdenes 11h, 12h, 14h, 15h y 16h no han sido an definidas, ni siquiera en el DOS 5.0.
La orden 13h o GENERIC IOCTL, disponible desde el DOS 3.2 permite un mecanismo ms
sofisticado de comunicacin IOCTL. Tambin en el DOS 3.2 han sido definidas las rdenes 17h
(GET LOGICAL DEVICE) y 18h (SET LOGICAL DEVICE). El DOS 5.0 aade una nueva: la
19h (CHECK GENERIC IOCTL SUPPORT). Por cierto, las ordenes 80h y superiores estn
destinadas a la comunicacin con los dispositivos CD-ROM...
// DRV 1.0
// Utilidad para listar los controladores de dispositivo instalados.
#include <dos.h>
#include <stdio.h>
struct REGPACK r;
unsigned long huge *siguiente;
unsigned char huge *disp;
int i, disco, dosver;
void main()
{
r.r_ax=0x3000; intr (0x21, &r); /* obtener versin del DOS */
dosver=(r.r_ax << 8) | (r.r_ax >> 8);
if ((dosver & 0xFF00)==0x200) i=0x17; /* DOS 2.XX */
else if ((dosver>0x2FF) && (dosver<0x30A)) i=0x28; /* DOS 3.0X */
else i=0x22; /* otra versin */
siguiente=MK_FP(r.r_es, r.r_bx+i);
printf("\n+==== DRV 1.0 === LISTA DE DISPOSITIVOS DEL SISTEMA ===
(c) 1992 CiriSOFT ====+\n");
printf("| Direccin Tipo Nombre Estrat. Interr.
Atributo Programa Tamao |\n");
printf("| --------- -------- ------------- -------- --------
-------- -------- ------ ");
while (FP_OFF(siguiente)!=0xffff) {
disp = (unsigned char huge *) siguiente;
printf("|\n| %04X:%04X ", FP_SEG(disp), FP_OFF(disp));
if (disp[5] & 0x80) {
printf("Carcter ");
for (i=10; i<18; i++) printf("%c",disp[i]); printf(" ");
}
else {
printf("Bloque ");
if (disp[10]==1)
printf("Unidad %c: ", disco--);
else {
printf("Unidades %c:-%c:",disco-disp[10]+1, disco);
disco-=disp[10];
}
}
printf(" %04X %04X %04X ", disp[6] | (disp[7]<<8),
disp[8] | (disp[9]<<8), disp[4] | (disp[5]<<8));
Utilizando COPY en vez de TYPE, al enviar varios ficheros con los comodines el COMMAND
suele encadenarles en uno solo y el offset es relativo al primero enviado (esto depende de la
versin del intrprete de comandos). Aunque se supone que el DOS va a enviar los caracteres de
uno en uno, el dispositivo se toma la molestia de prever que esto pueda no ser as , procesando en
un bucle todos los que se le indiquen. Para imprimir se utiliza la INT 29h del DOS (fast console
OUTPUT), ms recomendable que llamar a un servicio del sistema operativo (que a fin de cuentas
va a parar a esta interrupcin). No hay que olvidar que los controladores de dispositivo son
tambin programas residentes a todos los efectos, con las mismas limitaciones. Sin embargo,
desde los programas normales no es recomendable utilizar la INT 29h, entre otras razones porque
esos programas, adems de imprimir a poca velocidad, no soportaran redireccionamiento en la
salida (la INT 29h no es precisamente rpida, aunque s algo ms que llamar al DOS).
El dispositivo HEX$ slo acta en salida, imprimiendo en pantalla lo que recibe. Si se intenta
leer desde l devuelve una condicin de error (por ejemplo, al realizar COPY HEX$ FICH.TXT).
Para visualizar ficheros binarios que puedan contener la marca de fin de fichero (^Z) no basta hacer
TYPE o COPY a secas: en estos casos se debe emplear COPY /B FICHERO.EXT HEX$, la
opcin /B sirve para que la salida no se detenga ante el ^Z. La operaci n de impresi n en
pantalla se supone siempre exitosa; por ello el dispositivo no modifica la variable que indica el
nmero de caracteres a procesar: al devolverla precisamente como estaba al principio indica que se
han procesado sin problemas todos los solicitados. En la instalacin se comprueba la versin del
DOS, para cerciorarse de la presencia de un 3.0 o superior. Este driver de ejemplo s lo consume
464 bytes de memoria bajo MS-DOS 5.0. Tras ensamblarlo y linkarlo hay que aplicar EXE2BIN
para pasarlo de EXE a SYS (TLINK /t slo opera cuando hay un ORG 100h).
Como se puede verificar observando el listado, las nicas rdenes realmente soportadas por el
dispositivo son, aparte de OPEN, CLOSE y REMOVE, las rdenes WRITE y WRITE VERIFY.
Todas las dems, en este controlador que no depende del hardware tpico de entrada/salida, son
innecesarias. Como el proceso de escritura en pantalla se supone siempre con xito, WRITE
VERIFY es idntica a WRITE, sin realizar verificacin alguna. Las rdenes no soportadas
pueden ser ignoradas o bien desembocar en un error, segn sea el caso.
Otra ventaja es que es mucho ms flexible que los discos virtuales que acompaan al sistema
operativo, permitiendo definir con mayor libertad los parmetros e incluyendo uno nuevo (el
tamao de cluster). Los usuarios avanzados nunca estuvieron contentos con los discos del sistema
que abusaban demasiado del ajuste de parmetros. Aunque una eleccin torpe de parmetros de
TURBODSK puede crear un disco prcticamente intil, e incluso incompatible con algunas
versiones del DOS, tambin es cierto que los usuarios con menos conocimientos pueden dejar a
ste que elija los parmetros por ellos, con excepcin del tamao del disco. Los usuarios ms
informados, en cambio, no tendrn ahora trabas.
Sin embargo, la pretensin inicial de hacer TURBODSK ms rpido que los discos del
sistema, de la que hereda su peculiar nombre, ha tenido que enfrentarse a la elevada eficiencia de
RAMDRIVE. Las ltimas versiones de este disco ya apuran bastante el rendimiento del sistema,
por lo que superarle slo ha sido posible con un truco en la memoria expandida/convencional y en
mquinas 386DX y superiores: TURBODSK detecta estas CPU y aprovechar su bus de 32 bits
para realizar las transferencias de bloques de memoria. La velocidad es sin duda el factor ms
importante de un disco virtual, con mucho, por lo que no se deben ahorrar esfuerzos para
conseguirla.
Para calcular la velocidad de los discos virtuales se ha utilizado el programa KBSEC.C listado
ms abajo. Los resultados de KBSEC pueden variar espectacularmente en funcin del fabricante
del controlador de memoria o del sistema operativo. Este programa de test es til para analizar el
rendimiento de un disco virtual en fase de desarrollo o para que el usuario elija la memoria m s
rpida segn la configuracin de su equipo. Dicho programa bloquea todas las interrupciones
excepto IRQ 0 (INT 8), la cual a su vez desva con objeto de aumentar la precisi n del c lculo;
por ello es exclusivo para la comprobacin de discos virtuales y no flexibles. Debe ser ejecutado
sin tener instalado ningn cach. KBSEC fuerza el buffer de transferencia a una direccin de
memoria determinada, con objeto de no depender aleatoriamente de la velocidad dispar de la
memoria y los controladores XMS/EMS en funcin del segmento que sea utilizado. La fiabilidad
de KBSEC est avalada por el hecho de que siempre da exactamente el mismo resultado al ser
ejecutado en las mismas condiciones. Para hacerse una idea de la potencia de los discos virtuales,
conviene tener en cuenta que un disco fijo con 19 ms de tiempo de acceso e interface IDE, en un
386-25 puede alcanzar una velocidad de transferencia de casi un megabyte, 17 veces menos que la
mejor configuracin de disco virtual -que adems posee un tiempo de acceso pr cticamente
nulo- en esa misma mquina.
+-------------------------------------------------------------------------------
-------+
| Velocidad del disco bajo MS-DOS 5.0, calculada por KBSEC, con los
buffers que |
| establece el DOS por defecto (aunque esto no influye en KBSEC) y con slo
KEYB y |
| DOSKEY instalados. Para evaluar la memoria convencional no estaba instalado
ningn |
| controlador de memoria; para la memoria XMS estaba instalado slo HIMEM.SYS y
para |
| la EMS, tanto HIMEM.SYS como EMM386.EXE a la vez (los resultados varan
bastante |
| en funcin de la gestin de memoria del sistema). Datos en Kb/segundo.
|
+-------------------------------------------------------------------------------
-------+
| VDISK RAMDRIVE
TURBODSK |
| 8088-8 MHz:
|
| - Memoria convencional: 563 573
573 |
| 286-12 Mhz (sin estados de espera):
|
| - Memoria extendida/XMS: 1980 4253
4253 |
| - Memoria convencional: 4169 4368
4368 |
| 386-25 MHz (sin cach):
|
| - Memoria extendida/XMS: 6838 17105
17095 |
| - Memoria expandida EMS: 1261 8308
14937 |
| - Memoria convencional: 7297 6525
14843 |
| 486-25 MHz sin cach externa:
|
| - Memoria extendida/XMS: 7370 10278
10278 |
| - Memoria expandida EMS: 2533 7484
9631 |
| - Memoria convencional: 8256 8454
11664 |
+-------------------------------------------------------------------------------
-------+
/*********************************************************************
* *
* KBSEC 1.2 - Utility to calc with high precision the data transfer *
* rate (the read data transfer read) in a ramdisk. *
* *
* (C) 1992-1995 Ciriaco Garca de Celis *
* *
* - Do not run this program with a cache program loaded; compile *
* it in LARGE memory model with Test stack overflow option *
* disabled. Use Borland C. This program has english messages. *
* *
*********************************************************************/
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
prep_hw(); ti=tiempo=vueltas=0;
while (ti==tiempo); /* esperar pulso del reloj */ ti+=TIEMPO;
rest_hw(TIEMPO); clrscr();
LA CABECERA DE TURBODSK
A continuacin vienen las variables de TURBODSK, la mayora de las cuales son intuitivas.
Sin embargo, las dos primeras son algo especiales. La primera (cs_tdsk) est destinada a
almacenar el valor del registro CS, que indica dnde reside el disco virtual. Aunque en principio
puede parecer redundante, esta operacin es necesaria para lograr la compatibilidad con algunos
gestores de memoria, como QEMM, que pueden cargar la cabecera del dispositivo en memoria
convencional y el resto del mismo en la superior: a nosotros nos interesa conocer la direcci n
donde reside todo el dispositivo, con objeto de acceder a l para ulteriores modificaciones de sus
condiciones de operacin. Cuando se utiliza el LOADHI de QEMM, el dispositivo es cargado en
memoria superior, pero despus QEMM se encarga de copiar la cabecera en memoria
convencional, pasando la cadena de controladores de dispositivo del DOS por dicha memoria.
Como nosotros buscaremos a un posible TURBODSK residente siguiendo esa cadena, gracias a la
variable cs_tdsk podemos saber la direccin real del disco virtual. QEMM crea adems unas
falsas rutinas de estrategia e interrupcin en memoria convencional que luego llaman a las de la
memoria superior. Sin embargo, esto no es relevante para nosotros. Por fortuna, QEMM 6.0
tambin soporta el DEVICEHIGH del DOS, en cuyo caso la totalidad del dispositivo es cargado
en memoria superior; sin embargo, no est de ms tomar precauciones para los casos en que no
sea as.
Existe otra variable importante, tipo_soporte, que indica en todo momento el estado del disco.
En general, las variables ms importantes de TURBODSK han sido agrupadas al principio y el
autor del programa se ha comprometido a no moverlas en futuras versiones. Esto significa que otros
programas podrn detectar la presencia de TURBODSK e influir en sus condiciones de
operacin.
Ms adelante hay otras variables internas al programa: por un lado, la tabla de saltos para las
rutinas que controlan el dispositivo; por otro, un BPB con informacin vlida (si no fuera
correcto, el DOS se podra estrellar al cargar el dispositivo desde el CONFIG). Este BPB ser
modificado cuando se defina el disco, se defina ste desde el CONFIG o no (esto ltimo es lo
ms normal y recomendable). En el BPB solo se han completado los campos correspondientes al
DOS 2.x; la razn es que los dems no son necesarios ni siquiera para el DOS 5.0: la
informacin adicional de las ltimas versiones de los BPB es empleada por las rutinas de m s
bajo nivel del sistema operativo, aquellas que se relacionan con la BIOS y el hardware; sin
embargo, estas nuevas variables no son relevantes para la interfaz del DOS con el controlador de
dispositivo.
Veremos ahora las principales rutinas de TURBODSK. Para empezar, la rutina de estrategia de
TURBODSK no merece ningn comentario, pero s la de interrupcin. Es bastante parecida a
la de los discos del sistema, pero con una diferencia: si el disco no est a n preparado y no se ha
reservado memoria para l (esto sucede con la variable tipo_soporte igual a cero) hay que
rechazar todos los accesos al disco devolviendo un cdigo de unidad no preparada, algo as
como decir que no hay disquete dentro de la disquetera virtual. En cualquier otro caso, y
valindose de la tabla de saltos, llamamos a la subrutina adecuada que gestiona cada orden. Estas
subrutinas devuelven en AX la palabra de estado que hay que devolver al sistema, por lo que al final
se realiza esta operacin. En el caso de un error de transferencia (debido al fallo de alg n
controlador de memoria o a un intento de acceso fuera de los lmites del disco), se indica al DOS
que se han transferido 0 sectores; de lo contrario, esta variable de la cabecera de peticin queda
como estaba al principio, indicando que se han transferido tantos sectores como fueron solicitados.
Las rdenes READ NOWAIT, INPUT STATUS, INPUT FLUSH, OUTPUT STATUS,
OUTPUT FLUSH, IOCTL OUTPUT, OPEN y CLOSE no est n realmente soportadas. Sin
embargo, si el DOS las invoca, TURBODSK se limita a terminar como si nada hubiera sucedido,
devolviendo una palabra de estado 100h que indica funcin terminada. A la orden IOCTL INPUT,
en cambio, se responde con un error (orden no soportada) ya que TURBODSK no est preparado
para enviar cadenas IOCTL a nadie (una cosa es no hacer caso de las que envan, pero cuando
adems las solicitan!); en general, el comportamiento hasta el momento es 100% idntico al de
RAMDRIVE.
Sin embargo, la orden MEDIA CHECK es totalmente diferente de la de los discos virtuales del
DOS. A la pregunta de ha habido cambio de disco?, tanto VDISK como RAMDRIVE responden
siempre que no. En cambio, TURBODSK puede haber sido modificado por el usuario, debido a la
asignacin dinmica de memoria que soporta. En estos casos, el programa que formatea el disco
virtual (el propio TURBODSK cuando el usuario define un disco) colocar la variable cambiado
a un valor 0FFh. Este valor es el que se devolver la primera vez al DOS, indicando que se ha
producido un cambio de disco. Las siguientes veces, TURBODSK no volver a cambiar (no hasta
otro formateo), motivo por el cual la variable se redefine a 1.
En el momento en que el disco es cambiado, el DOS ejecuta la orden BUILD BPB, con la que se
le suministra la direccin del nuevo BPB (la misma de siempre, pero con un BPB actualizado).
La orden REMOVE se limita a devolver una condicin de controlador ocupado. No estaba muy
claro qu haba que hacer con ella, por lo que se opt por imitar el funcionamiento de
RAMDRIVE. Lo cierto es que hay rdenes que casi nunca sern empleadas, o que no tiene
sentido que sean utilizadas, pero conviene considerarlas en todo caso.
Las ltimas rdenes que implementa TURBODSK son las de lectura y escritura o escritura
con verificacin. En estas rdenes simplemente se inicializa un flag (el registro BP) que indica si
se trata de leer o escribir: si BP es 0 es una escritura, si es 1 una lectura. Finalmente, se salta a la
rutina Init_io que se encarga de preparar los registros para la lectura o escritura, consultando el
encabezamiento de peticin de solicitud para estas rdenes.
Ms o menos mezclada con estas rdenes est la rutina que gestiona la interrupcin 19h.
Esta interrupcin es necesario desviarla para mejorar la convivencia con algunos entornos
multitarea basados en el modo virtual del 386. En principio, cuando una tarea virtual es cancelada
(debido a un CTRL-ALT-DEL o a un cuelgue de la misma) el sistema operativo deber a
desasignar todos los recursos ligados a ella, incluida la memoria expandida o extendida que tuviera
a su disposicin. Sin embargo, parece que existen entornos no muy eficientes en los que al anular
una tarea no se recupera la memoria que ocupaba. Por tanto, es deber de la propia tarea, antes de
morir, el devolver la memoria a los correspondientes controladores. La interrupcin 19h se ejecuta
en estos momentos crticos, por lo que TURBODSK aprovecha para liberar la memoria
EMS/XMS ocupada y, tras restaurar el vector previo de INT 19h (para mejorar la compatibilidad)
contina el flujo normal de la INT 19h. La mayor a de los discos virtuales no desv an la INT
19h; sin embargo, RAMDRIVE s y TURBODSK no quera ser menos... aunque, en el caso de
utilizar memoria convencional no se realiza ninguna tarea (RAMDRIVE ejecuta una misteriosa y
complicada rutina).
Otro asunto es controlar el tamao absoluto del rea a transferir: en ningn caso debe rebasar
los 64 Kb, aunque no est muy claro si los puede alcanzar o no. RAMDRIVE opera con palabras
de 16 bits, permitiendo un mximo de 8000h (exactamente 64 Kb), excepto en el caso de trabajar
con memoria extendida: al pasar el n de palabras a bytes, unidad de medida del controlador
XMS, el 8000h se convierte en 0 (se desborda el registro de 16 bits al multiplicar por 2): con este
tipo de memoria RAMDRIVE no soporta transferencias de 64 Kb exactos (por ello, KBSEC.C
emplea un buffer de 63 y no de 64 Kb). En TURBODSK se decidi transferir 64 Kb inclusive
como lmite mximo, en todos los casos. En memoria expandida y convencional, por otro lado,
existe el riesgo de que el offset del buffer sea impar y, debido al tamao del mismo, se produzca un
acceso de 16 bits en la direccin 0FFFFh, ilegal en 286 y superiores. Esto provoca un mensaje
fatal del controlador de memoria, preguntando si se desea seguir adelante o reinicializar el sistema
(QEMM386), o simplemente se cuelga el ordenador (con el EMM386 del MS-DOS 5.0 o en
mquinas 286). Por ejemplo, pruebe el lector a leer justo 32 Kb en un buffer que comience en
8001h con RAMDRIVE en memoria EMS: RAMDRIVE no pierde el tiempo comprobando estas
circunstancias crticas, aunque VDISK parece que s. En TURBODSK se opt tambi n por
ser tolerante a los fallos del programa que accede al disco: adems de limitar el acceso m ximo a
64 Kbytes, y de transferir slo lo que se pueda antes del desbordamiento del segmento, puede que
todava se transfiera entre uno y tres bytes menos, ya que se redondea por truncamiento la cuenta
de palabras que faltan para el final del segmento para evitar un direccionamiento ilegal en el offset
0FFFFh (estas circunstancias crticas deben evaluarse utilizando las interrupciones 25h/26h, ya
que al abrir ficheros ordinarios el DOS es siempre suficientemente cauto para no poner a prueba la
tolerancia a fallos de las unidades de disco).
Las rutinas que gestionan los diversos tipos de memoria tienen los mismos parmetros de
entrada (obtenidos de Init_io) y sirven para leer/escribir en el disco segn lo que indique BP, as
como para liberar la memoria asignada en respuesta a una interrupcin 19h. Retornan devolviendo
en AX el resultado de la operacin, que ser normalmente exitoso. En caso de fallo de alg n
controlador de memoria, devolveran un cdigo de error de anomala general.
En el caso de VDISK, el algoritmo es muy poco eficiente: este disco virtual realiza un bucle, con
una vuelta para cada sector, donde hace todas estas tareas: preservar el contexto del mapa de
pginas, calcular las direcciones, transferir a un buffer auxiliar, recuperar el contexto del mapa de
pginas y transferir del buffer auxiliar hacia donde solicita el DOS. Ello significa que, para
transferir 32 Kb en sectores de 0,5 Kb, se salva y restaura 64 veces! el contexto del mapa de
pginas. No digamos si los sectores son ms pequeos, adems del hecho (mucho m s
grave) de que transfiere dos veces y de la cantidad de veces que calcula las direcciones. Cierto es
que salvar el contexto del mapa de pginas y volverlo a restaurar es necesario, de cara a que el
disco virtual (un programa residente a todos los efectos) no afecte al programa de usuario que se
est ejecutando, por si ste utiliza tambin memoria expandida. La pregunta es, por qu no
sacaron los autores de VDISK esas operaciones fuera del bucle?, y por qu utilizar un buffer
auxiliar?. Lgicamente hay una respuesta. Piense el lector qu suceder si el buffer donde leer o
escribir que suministra el programa principal, est en memoria expandida: se solapa con el
disco virtual!. Para solucionar este posible solapamiento, VDISK se ve obligado a realizar esas
operaciones con objeto de permitir una transferencia de la memoria expandida a la propia memoria
expandida, a travs de un buffer auxiliar. Este algoritmo provoca que VDISK sea pr cticamente
tan lento como un buen disco duro cuando trabaja con memoria expandida y sectores de 512 bytes,
y bastante ms lento si se utilizan los sectores de 128 bytes que suele establecer por defecto!.
Adems, el buffer del tamao de un sector incrementa el consumo de memoria en 512 bytes.
+-------------------------------------------------------------------------------
-------------------+
| ESQUEMA DE FUNCIONAMIENTO DE LA RUTINA DE GESTIN DE MEMORIA EMS DE
TURBODSK |
+-------------------------------------------------------------------------------
-------------------+
| Analizaremos el caso ms conflictivo:
|
| Cuando el rea a transferir ocupa los 16 Kbytes mximos.
|
|
|
|
|
| | | |
| |
| - - - - -+---------------+- - - - - - - - - - - - - -+---------------
+- - - - - |
| | | M |
| |
| | Pgina 3 | E | Pgina 3
| |
| +---------------+ M +---------------
+-- ^ |
| | | O |
| 16 |
| | Pgina 2 | R | Pgina 2
| Kb |
| +---------------+ I +---------------
+-- v |
| | | A |
| |
| | Pgina 1 | | Pgina 1
| |
| +---------------+ E +---------------
+ <----- caso B |
| | | M |
| |
| | Pgina 0 | S | Pgina 0
| |
| - - - - -+---------------+- - - - - - - - - - - - - -+---------------
+- - - - - |
| | | |
| |
| | | |
| |
| +---------------+ <----- caso A +---------------
+ |
|
|
|
|
| Resulta evidente, en el caso A, que si el buffer donde leer/escribir los
datos comienza por |
| debajo de la direccin marcada por la flecha (o justo en esa direccin) no
colisionar con la |
| pgina 0, ya que no excede de 16 Kb de longitud. Como al convertir la
direccin segmentada a |
| prrafos se pierde precisin, TURBODSK se asegura que la direccin est
401h prrafos (16 Kb |
| ms 1 prrafo) por debajo del inicio de la pgina 0.
|
|
|
| En el caso B, el buffer est en memoria expandida pero comienza justo
detrs de la pgina 0 |
| y, por lo que no hay colisin con esta pgina. Una vez ms, por razones de
redondeo, TURBODSK |
| comprueba que el buffer comience al menos 401h prrafos por encima del
inicio de la pgina 0. |
| En realidad, bastara con comprobar si dista al menos 400h bytes, ya
que el redondeo al |
| convertir la direccin segmentada se hace truncando.
|
|
|
| Conclusin: para que no haya colisin, el buffer ha de estar a 401h
prrafos de distancia |
| (expresada en valor absoluto) del inicio de la pgina 0. Qu sucede si hay
colisin?. Pues que |
| no se puede emplear la pgina 0, que se solapa con el buffer. En ese caso,
bastara con elegir |
| la pgina 2 ya que si el buffer empieza justo donde apunta la flecha del caso
B, como su tamao |
| es de no ms de 16 Kb, no puede invadir... s, s puede invadir la pgina
2, aunque slo un |
| prrafo! (no olvidar que si empieza por encima de la flecha no colisiona con
la pgina 0). Por |
| tanto, tenemos que utilizar la pgina 3. En general, en un sistema con
memoria EMS 4.0 donde |
| las pginas pueden ser definidas por el usuario en la direccin que desee
(parmetros /Pn= del |
| EMM386 del MS-DOS 5.0), basta con asegurarse que la pgina alternativa a la
0, para los casos |
| en que hay colisin, est alejada al menos 48 Kb de la pgina 0 (esto es,
que entre ambas |
| pginas hay una distancia absoluta de 32 Kb).
|
|
|
| Se comprende ahora la necesidad de restaurar el contexto del mapa de
pginas antes de pasar |
| utilizar una nueva pgina para las transferencias: el hecho de necesitar una
nueva pgina viene |
| determinado porque la hasta entonces utilizada se solapa con el buffer y es
preciso restaurar |
| el contenido del buffer!. Adems, hay que volver a salvar el contexto de
manera inmediata para |
| que quede salvado para otra ocasin (o para cuando se acabe el acceso al
disco y haya de ser |
| restaurado).
|
+-------------------------------------------------------------------------------
-------------------+
Para terminar con el anlisis de la gestin de este tipo de memoria, hablaremos algo acerca de
la manera de comunicarse con el controlador de memoria. En principio, lo ms normal es cargar
los registros e invocar la INT 67h, analizando el valor en AH para determinar si ha habido error. Sin
embargo, se ha constatado que RAMDRIVE, ante un cdigo de error 82h (EMM ocupado)
vuelve a reintentar de manera indefinida la operacin, excepto en el caso de la funcin 40h
(obtener el estado del gestor) utilizada en la instalacin, en la que hay slo 32768 intentos. Este
comportamiento parece estar destinado a mejorar la convivencia con entornos multitarea, en los que
en un momento dado el controlador de memoria puede estar ocupado pero algo ms tarde puede
responder. Por tanto, tambin se incorpor esta tcnica a TURBODSK.
Un ltimo aspecto a considerar est relacionado con el uso de instrucciones de 32 bits en las
rutinas de TURBODSK: en principio han sido cuidadosamente elegidas con el objetivo de
economizar memoria. Por ello, la instruccin PUSHAD (equivalente a PUSHA, pero con los
registros de 32 bits) vena muy bien para apilar de una sola vez todos los registros de prop sito
general. Sin embargo, la correspondiente instruccin POPAD no opera correctamente, por
desgracia, en la mayora de los 386, aunque el fallo fue corregido en las ltimas versiones de
este procesador (los 386 de AMD tambin lo tienen, qu curioso!). Se trata de un fallo
conocido por los fabricantes de software de sistemas, pero poco divulgado, aunque tampoco es muy
grave: bsicamente, el problema reside en que EAX no se restaura correctamente. El fallo de esta
instruccin, al parecer descubierto por Jeff Prothero est ligado a las instrucciones que vienen
inmediatamente a continuacin, y est demostrado que poniendo un NOP detrs -entre otros-
nunca falla. En las rutinas de TURBODSK se observa tambin que los registros de 32 bits
empleados en la transferencia son enmascarados para que no excedan de 0FFFFh, ya que podr an
tener la parte alta distinta de 0 y ello provocara una trgica excepcin del controlador de
memoria al intentar un acceso -por otra parte, de manera incorrecta- fuera de los segmentos de
64Kb.
Hay sin embargo un detalle curioso que comentar: RAMDRIVE instala una rutina que intercepta
las llamadas al controlador XMS. Hacer esto es realmente complicado, teniendo en cuenta que el
controlador XMS no se invoca por medio de una interrupcin, como los dem s controladores,
sino con un CALL inter-segmento. Por ello, es preciso modificar parte del cdigo ejecutable del
propio controlador de memoria. Esto es posible porque el controlador XMS siempre empieza
tambin por una instruccin de salto lejana de cinco bytes (o una corta de dos o tres, seguida de
NOP's, considerando RAMDRIVE todas estas diferentes posibilidades). RAMDRIVE intercepta la
funcin 1 (asignar el HMA), pero comprobando tambin si AL vale 40h: esto significa que est
intentando detectar la llamada de algn programa en concreto, ya que el valor de AL es irrelevante
para el controlador XMS. En ese caso, en lugar de continuar el flujo normal, determina la memoria
extendida libre y hace unas comprobaciones, pudiendo a consecuencia de ello retornar con un error
91h (el HMA ya est asignado). Todo parece destinado a mejorar la compatibilidad con algn
programa, probablemente tambin de Microsoft, aunque ningn otro disco virtual -TURBODSK
entre ellos- realiza estas extraas maniobras. Esta forma de trabajar es lo que podramos
denominar programacin a nivel de cloacas, usando cdigo basura para tapar la suciedad de otros
programas previos.
En memoria convencional hay pocas diferencias entre todos los discos virtuales. Como no hay
controladores de memoria por el medio, la operacin del disco siempre resultar exitosa. La
diferencia de TURBODSK frente a RAMDRIVE y VDISK es que en los 386 y superiores utiliza de
nuevo transferencias de 32 bits. Sin embargo, esto no es demasiado importante, ya que estas
mquinas suelen tener la memoria convencional destinada a cosas ms tiles que un disco. En
los PC/XT el rendimiento de todos los discos virtuales suele ser muy similar, excepto alg n
despistado de dominio pblico que mueve palabras de 8 bits. La rutina Procesa_con ubicada al
final de TURBODSK se encarga de gestionar esta memoria.
LA SINTAXIS DE TURBODSK.
TURBODSK slo necesita que se indique el tamao del disco, ajustando los dems
parmetros de la manera ms aconsejable. De lo expuesto anteriormente se deduce que es
sencillo crear discos que no operen correctamente, si no se tienen en cuenta las limitaciones de los
diversos sistemas operativos, aunque esto es responsabilidad del usuario y el programa no limita su
libertad. Con /E se fuerza la utilizacin de memoria extendida, aunque es un parmetro un tanto
redundante (TURBODSK utiliza por defecto esta memoria). /A y /X sirven, indistintamente, para
utilizar memoria expandida.
El procedimiento Main es muy similar al Init, la principal diferencia radica en que en el caso de
utilizar memoria convencional hay que terminar residente, para que el DOS respete el bloque de
memoria creado para contener el disco. Sin embargo, se dejan residentes s lo los primeros 96
bytes del PSP. Tambin desde Main puede ser necesario desalojar la memoria de un disco previo,
si se indica uno nuevo. Es preciso, as mismo, considerar ciertas circunstancias nuevas que no
podan darse desde el CONFIG: una versin del DOS anterior a la 2.0, que el driver no haya sido
instalado antes desde el CONFIG, que se indique una letra de unidad que no se corresponda con un
driver TURBODSK, que el tamao de sector exceda el mximo que permite la configuraci n
del DOS, que se solicite memoria expandida y no se halla reservado espacio para la rutina que la
soporta o que se intente redefinir el disco desde WINDOWS. Este ltimo aspecto se consider a
raiz de los riesgos que conlleva. Supongamos, por ejemplo, que el usuario abre una sesi n DOS
desde WINDOWS y define un disco de media mega en memoria convencional, volviendo despu s
a WINDOWS: WINDOWS recupera toda la memoria convencional que haba asignado para su
propio uso, pero TURBODSK no puede darse cuenta de esta circunstancia y, si el usuario intenta
grabar algo en el disco virtual, el sistema se estrellar . La memoria virtual de WINDOWS
tambin da problemas al crear discos en memoria expandida o extendida. Por tanto, las
definiciones del disco han de hacerse antes de entrar en WINDOWS. Tampoco conviene definir el
disco desde DESQVIEW, aunque si se anula de nuevo antes de abandonar DESQVIEW no habr
problemas, por lo que TURBODSK s permite modificar el disco desde el interior de este entorno.
Tanto Init como Main leen la lnea de parmetros indicados por el usuario y ejecutan
ordenadamente los procedimientos necesarios para definir el disco, si sto es preciso.
La rutina Gestionar_ram, ejecutada slo desde la lnea de comandos del DOS, rebaja la
memoria asignada al TDSK.EXE en ejecucin a 96 bytes. Esto se hace as para poder utilizar
despus las funciones estndar del sistema para asignar memoria. Esta acrobacia provoca la
creacin de un bloque de control de memoria (MCB) en el offset 96 del PSP, lo cual es inocuo;
tambin se libera el espacio de entorno por si acaso se fuera a terminar residente.
En la rutina TestWin se comprueba si Windows est activo, para evitar en ese caso una
modificacin del disco por parte del usuario. Por desgracia, hay que chequear en dos
interrupciones distintas las presencia de Windows. Antes de llamar a la INT 2Fh se comprueba que
esta interrupcin est apuntando a algn sitio: en el sistema DOS 2.11 en que se prob
TURBODSK esa interrupcin estaba apuntando a 0000:0000 y el ordenador se colgaba si no se
tomaba esta precaucin.
La rutina Inic_letra, ejecutada desde el CONFIG, calcula la letra que el sistema asignar a la
unidad, con objeto de informar en el futuro al usuario. Desde el DOS 3.0, el encabezamiento de
peticin de solicitud de la orden INIT almacena este dato. Dado que DR-DOS 6.0 no inicializa
correctamente el tamao del encabezamiento de solicitud de esta orden, es ms seguro verificar
la versin del DOS que comprobar si este dato est definido o no, en funcin de las longitudes,
que sera lo normal. En el caso del DOS 2.X, no hay ms remedio que crear una tabla con los
dispositivos de bloque del sistema y contarlos (a que ya sabe por qu RAMDRIVE y VDISK no
informan o informan incorrectamente de la letra de unidad al instalarse en estas versiones del
DOS?).
El procedimiento Lista_discos, como dije con anterioridad, crea una tabla con todos los
dispositivos de bloque del sistema. Para ello utiliza la valiosa funci n indocumentada 52h (Get
List of Lists) del DOS. Por desgracia, la manera de acceder a la cadena de controladores de
dispositivo vara segn la versin del DOS, por lo que TURBODSK tiene en cuenta los tres
casos posibles (DOS 2.X, 3.0 y versiones posteriores). En la tabla creada, con cuatro bytes por
dispositivo: los dos primeros indican el segmento donde reside, el segundo el nmero de unidades
que controla y el tercero puede valer 1 0 para indicar si se trata de una unidad TURBODSK o no.
El final de la tabla se delimita con un valor de segmento igual a cero. En el caso de un dispositivo
TURBODSK no se anota el segmento donde reside sino la variable cs_tdsk del mismo, que indica
la direccin real incluso en el caso de que el dispositivo haya sido relocalizado por QEMM a la
memoria superior.
La rutina Desinstala libera la memoria que ocupa un disco residente con anterioridad,
inhabilitando el driver. En el caso de la memoria convencional hay que liberar tanto el segmento
que ocupaba el disco como el del PSP previamente residente.
que devuelve el nmero de cluster ms alto del disco (se aade uno ya que los clusters se
numeran desde dos; por ejemplo, 100 clusters se numeraran entre 2 y 101 inclusive). Si el
resultado es mayor o igual que 4086, la FAT no puede ser de 12 bits, por lo que se debe recalcular la
frmula sustituyendo el 1,5 por 2 y definiendo una FAT de 16 bits. Hay casos crticos en que una
FAT de 12 bits no alcanza, pero al definirla de 16 el tamao adicional que ella misma ocupa hace
que el nmero de cluster ms alto baje de 4086: en estos casos se reserva espacio para una FAT
de 16 bits que luego ser realmente de 12; sin embargo, se trata de una circunstancia muy puntual
y poco probable. En principio, con los tamaos de cluster y sector que TURBODSK asigna por
defecto, la FAT ser de 12 bits a menos que el disco exceda los 8 Mb.
Conviene hacer hincapi en que los discos con 4085 clusters o ms (con n mero de cluster
ms alto 4086 o superior) tienen una FAT de 16 bits. Por desgracia, casi todos los libros
consultados (y ya es mala suerte) tienen esta informacin incorrecta: para unos, la FAT16 empieza
a partir de 4078 clusters; para otros, a partir de 4086; otros, no distinguen entre n de clusters y
n ms alto de cluster... hay un autntico caos ya que las fuentes de informacin se
contradicen. Al final, lo ms sencillo es crear discos virtuales con 4084/4085 clusters y espiar
qu hace el DOS. Es muy fcil: se graban algunos ficheros y se mira la FAT con alg n
programa de utilidad (PCTOOLS, DISKEDIT). A simple vista se deduce si el DOS asigna una FAT
de 12 o de 16 bits. Tanto el MS-DOS 3.1 como el 3.3, 4.0 y 5.0; as como el DR-DOS 3.41, 5.0 y
6.0 asignan FAT's de 16 bits a partir de 4085 clusters inclusive. Por fortuna, todas las versiones del
DOS parecen comportarse igual. Asignar el tipo de FAT correcto es vital por muchos motivos; entre
otros por que si fuera excesivamente pequea el disco funcionara mal. Sin embargo, los
CHKDSK de casi todas las versiones del DOS (excepto el del MS-DOS 3.30 y el de DR-DOS 6.0),
incluido el de MS-DOS 5.0, poseen una errata por la que suponen que los discos de 4085 a 4087
clusters tienen una FAT de 12 bits, con lo que pueden estropear el disco si el usuario ejecuta un
CHKDSK/F. Esto es un fallo exclusivo de CHKDSK que debera ser corregido en el futuro, por lo
que no se ha evitado estos tamaos de disco (casi nadie ejecuta CHKDSK sobre un disco virtual, y
en ese caso no va a tener tan mala suerte). Resulta curioso este fallo de CHKDSK, teniendo en
cuenta que es un programa que accede a la FAT y que 4087 (0FF7h) es precisamente la marca de
cluster defectuoso en una FAT de 12 bits, nunca un nmero de cluster cualquiera!. Por ejemplo,
con un comando del tipo TDSK 527 128 0 1 /E (no vale la memoria expandida, ya que
redondeara a 528 Kb), se puede crear un disco de 4087 clusters en el que los CHKDSK de las
versiones del DOS sealadas informen incorrectamente de la presencia de errores (si decide hacer
pruebas, retoque el nmero de entradas del directorio para variar ligeramente el nmero de
clusters).
Una vez definidos los parmetros bsicos de la estructura del disco, el procedimiento
Preparar_bpb inicializa el BPB, actualizndolo al nuevo disco; tambin se indica que ha habido
cambio de disco. El procedimiento Prep_driver se encarga de copiar el BPB recin creado sobre
el del driver residente en memoria, as como de actualizar las variables de la copia residente en
memoria, copiando simplemente las del TDSK.EXE en ejecucin. Tambin se instala la rutina
necesaria para gestionar el disco, segn el tipo de memoria a emplear por el mismo: esta rutina se
instala por partida doble, tanto en la copia residente como en el propio cdigo del TDSK.EXE que
se ejecuta (la rutina de gestin de memoria ser accedida directamente al formatear el disco
virtual).
En el caso de emplear memoria convencional, antes de formatear el disco hay que tomar
precauciones. El motivo radica en el hecho de que el disco probablemente comience en el offset 96
del PSP. Por tanto, si se inicializa sin ms el sector de arranque, la FAT y el directorio ra z (en
eso consiste simplemente el formateo) el propio TDSK.EXE se autodestruir. Para evitarlo,
TDSK.EXE se copia a s mismo en esos 128 Kb libres que siempre hay, incluso en el peor de los
casos, pasando a ejecutarse en ese nuevo destino por medio de una instruccin RETF que carga CS
al retornar (procedimiento Relocalizar). Se copia todo, pila incluida (se actualiza tambin SS). No
habr problemas, ya que TDSK.EXE es realmente un programa COM disfrazado de EXE, que
carece de referencias absolutas a segmentos. Se toma la precaucin de relocalizar TDSK.EXE (que
no ocupa ms de 12 Kb) justo a la mitad de ese rea de 128 Kb, para evitar solapamientos
consigo mismo en casos crticos. Se puede llegar a sobreescribir parte de la zona transitoria del
COMMAND.COM, lo cual provoca simplemente su recarga desde disco. Ciertamente, no es muy
ortodoxo que un programa en ejecucin vaya dando paseos por la memoria del PC, pero estas
cosas se pueden hacer en MS-DOS y nadie puede cuestionar la efectividad del m todo. Los
programadores ms conservadores han tenido suerte de que el adaptador de vdeo monocromo
cuente con slo 4 Kb.
+-------------------------------------------------------------------------------
-------------------+
| ESQUEMA DE LA AUTORELOCALIZACIN DE TDSK.EXE (UN CASO
CONCRETO) |
+-------------------------------------------------------------------------------
-------------------+
|
|
| Casi todas las cifras son arbitrarias, a modo de ejemplo prctico.
|
|
|
|
|
| 1 Mb +-------------------------+ 1 Mb
+-------------------------+ |
| | | |
| |
| | | |
| |
| | | |
| |
| | | |
| |
| 640 Kb +-------------------------+ 640 Kb
+-------------------------+<-+ |
| | | |
| | |
| | | |
| | |
| | | aprox. 588 Kb +-
>+-------------------------+ | |
| | | | | nueva pila de
TDSK.EXE | | |
| | | | + - - - - - - - - -
- - - + | |
| | | +--------> | |
| | 128 Kb |
| | | | | | TDSK.EXE
| | |
| | | | | |
| | |
| | | | |
+-------------------------+ | |
| | | | | | PSP TDSK.EXE (256
bytes)| | |
| | | | 576 Kb +-
>+-------------------------+ | |
| | | | | 64 Kb libres
(rea de | | |
| | | | | seguridad)
| | |
| | | | 512 Kb
+-------------------------+<-+ |
| | . . . | |
| . . . | |
| . . .
| . . . |
| . . .
| . . . |
| | | | |
| |
| +-------------------------+<-+ | | Futuros
programas | |
| | pila de TDSK.EXE | | |
+-------------------------+ |
| + - - - - - - - - - - - - + | | |
| |
| | | | ----+ | rea de
almacenamiento | |
| | TDSK.EXE | | | del disco
virtual | |
| | | | |
| |
| +-------------------------+ |
+-------------------------+ |
| | PSP TDSK.EXE (256 bytes)| | | PSP TDSK.EXE (96
bytes) | |
| +-------------------------+<-+
+-------------------------+ |
| | DOS/BIOS | | DOS/BIOS
| |
| 0 Kb +-------------------------+ 0 Kb
+-------------------------+ |
| Antes Despus
|
|
|
|
|
| En este esquema se muestra la autorelocalizacin de TDSK.EXE en
memoria en el caso de |
| definirse el disco en memoria convencional. No estn reflejados los
bloques de control de |
| memoria ni otros detalles. Si la memoria est suficientemente fragmentada
(por haber instalado |
| programas residentes tras definir algn disco) puede que no fuera
estrictamente necesario |
| respetar 128 Kb al final del bloque que nos asigna el DOS ni tampoco quiz
relocalizar TDSK.EXE;|
| sin embargo, el programa no est optimizado hasta ese extremo. El hecho de
relocalizar TDSK |
| hacia la frontera de los 576 Kb en lugar de los 512 se debe a evitar
problemas de colisiones en |
| casos crticos de cantidad de memoria libre y tamao de disco solicitado por
el usuario. |
+-------------------------------------------------------------------------------
-------------------+
Hablando de acceso directo al disco, otra ventaja de no utilizar INT 25h/INT 26h es que
Windows 95 no permite un uso directo de estas funciones. Los programas que acceden a estas
interrupciones son considerados inadecuados. TURBODSK puede funcionar bajo Windows 95, sin
obligar al usuario a reconfigurar nada, gracias entre otros motivos a que no utiliza INT 26h.
Con MS-DOS 2.11 y 3.1 hubo bastantes problemas, ya que estos sistemas no detectan muy bien
el cambio de disco aunque la rutina MEDIA CHECK del controlador de dispositivo se lo indique:
son versiones del DOS muy desconfiadas que adems comprueban el byte descriptor de medio. Es
de suponer que cuando el disco informa que ha habido cambio, estas versiones invalidarn los
buffers asociados a l; sin embargo, si creen que se trata de un disco del mismo tipo no se
molestan en actualizar el BPB. Por ello, con estas versiones, tras el formateo TURBODSK hace dos
cambios de disco consecutivos, con modificacin del byte descriptor de medio entre ambos. El
hecho de hacer un segundo cambio se debe al inters de restaurar el byte descriptor de medio
inicial. Adems, el DOS 2.11 probado necesitaba dos cambios en cualquier caso: si no, no se
tomaba en serio el cambio de disco. Entre cambio y cambio, se pregunta al sistema el espacio libre
en disco para forzar un acceso al mismo.
AMPLIACIONES DE TURBODSK
Despus de esta completa exposicin sobre las rutinas que componen TURBODSK, espero
que el lector est suficientemente preparado para entender en conjunto el funcionamiento del
programa y para crear unidades de disco por su cuenta. Una posible mejora de TURBODSK ser a
evitar la prdida de datos al redefinir el disco, tratndose por ejemplo de aumentar su capacidad.
Es complejo aadir esta optimizacin, ya que la arquitectura del nuevo disco puede cambiar
demasiado (nuevo tamao de FAT e incluso tipo de la misma). Adems, el usuario iba a tener
muchos problemas siempre, ya que sera muy frecuente que cuando tratase de reducir el tamao
del disco ste estuviera demasiado lleno. En general, los discos virtuales redimensionables que
soportan una redefinicin sin prdida de datos, suelen permitir esto de manera limitada y bajo
circunstancias concretas. Lo que s sera ms interesante es crear un disco virtual con
asignacin de memoria en tiempo real: cuando el usuario pretende crear un fichero, habilitar el
espacio suficiente. Sin embargo, esto significa unir las complicaciones anteriores a otras nuevas,
complicaciones que restaran velocidad al disco virtual, adems de la dificultad de
implementarlas que desanima al programador ms audaz. Por otra parte, no est muy claro que el
MS-DOS sea un sistema adecuado para soportar tal disco: al final, el proyecto podr a quedar
descartado en la fase de anlisis (si es que alguien acepta el reto).
El control IOCTL (que permite separar el flujo de datos con el dispositivo de la informaci n de
control) se ejerce por medio de la funcin 44h del DOS, siendo posible lo siguiente:
- Averiguar los atributos de un controlador de dispositivo, a partir del nombre. Esto permite,
entre otras cosas, distinguir entre un dispositivo real y un fichero con el mismo nombre. Seguro que
el lector ha construido alguna vez un programa que abre un fichero de salida de datos con el nombre
que indica el usuario: hay usuarios muy pillines que en lugar del clsico PEPE.TXT prefieren
indicar, por ejemplo, CON, estropeando la bonita pantalla que tanto trabajo hab a costado pintar.
Una solucin consiste, antes de abrir el fichero de salida, en asegurarse de que es realmente un
fichero.
- Leer del controlador o enviarle una tira de caracteres de control. Esto slo es posible si el
controlador soporta IOCTL. Por ejemplo, un driver encargado de gestionar un puerto serie especial
podra admitir cadenas del tipo "9600,n,8,1" para fijar la velocidad de transmisin, paridad, etc.
El trabajo que requiere codificar la rutina IOCTL OUTPUT, encargada de recibir estos datos, puede
en muchos casos merecer la pena.
Para obtener informacin detallada acerca de la funcin 44h del DOS hay que consultar,
lgicamente, la bibliografa al respecto (recomendable el INTERRUP.LST).
Captulo XII: EL HARDWARE DE APOYO AL MICROPROCESADOR
Nota: Por limitaciones tcnicas, al describir los circuitos integrados las se ales que
son activas a nivel bajo no tendrn la tradicional barra negadora encima; en su lugar
aparecern precedidas del signo menos: -CS, -WR, -MEMR, ...
En algunos casos, acceder directamente a los chips no es necesario: en general, es mejor dejar el
trabajo al DOS, o en su defecto a la BIOS. Sin embargo, hay casos en que es estrictamente
necesario hacerlo: por ejemplo, para programar temporizaciones, hacer sonidos, comunicaciones
serie por interrupciones, acceso a discos de formato no estndar, etc. Algunas veces bastar con
la informacin que aparece en el apartado donde se describe la relacin del chip con los PC; sin
embargo, a menudo ser necesario consultar la informacin tcnica del apartado ubicado
inmediatamente antes, para lo que bastan unos conocimientos razonables de los sistemas digitales.
Los ordenadores modernos normalmente no llevan los integrados explicados en este captulo; sin
embargo, poseen circuitos equivalentes que los emulan por completo.
Resulta interesante tener una idea global de las conexiones del 8086 con el exterior de cara a
entender mejor la manera en que interacciona con el resto de los elementos del ordenador. Se ha
elegido el 8088 por ser el primer procesador que tuvo el PC; a efectos de entender el resto del
captulo es suficiente con el 8088.
El 8088 puede trabajar en dos modos: mnimo (pequeas aplicaciones) y mximo (sistemas
multiprocesador). Los requerimientos de conexin con el exterior cambian en funcin del modo
que se decida emplear, aunque una parte de las seales es comn en ambos.
LNEAS COMUNES AL MODO MXIMO Y MNIMO DEL 8088.
Address Data Bus. Son lneas multiplexadas, que pueden actuar como bus de datos o de direcciones,
AD7..0:
evidentemente en tiempos distintos.
A15..8: Address Bus. En todo momento almacenan la parte media del bus de direcciones.
Address/Status. Parte alta del bus de direcciones, multiplexada: cuando no salen direcciones, la l nea S5
A19..16/S
indica el estado del bandern de interrupciones; las lneas S4:S3 informan del registro de segmento
6..3:
empleado para realizar el acceso a memoria: 00-ES, 01-SS, 10-CS, 11-DS; S6 no se usa.
-RD: Read. Indica una lectura de memoria o de un dispositivo de entrada/salida.
READY: Ready. Lnea de entrada que indica el final de la operacin de memoria o E/S.
Interrupt Request. Lnea de peticin de interrupciones enmascarables; el 8088 la observa
INTR:
peridicamente.
Test. En respuesta a la instruccin mquina WAIT (no TEST!), el 8088 se para a comprobar esta
-TEST:
lnea hasta que se ponga a 0.
NMI: Non-maskable Interrupt. Lnea de peticin de la interrupcin de tipo 2, que no puede ser enmascarada.
RESET: Provoca una inicializacin interna que culmina saltando a FFFF:0.
MN/-MX: Esta lnea indica si se trata de un sistema mnimo o mximo.
-RQ/- Request/Grant. Estas patillas bidireccionales permiten a los dems procesadores conectados al bus forzar al
GT0..1: 8088 a que libere el bus al final del ciclo en curso.
Lock. Lnea que sirve al 8088 para prohibir el acceso al bus a otros procesadores (se activa tras la
instruccin mquina LOCK y dura mientras se ejecuta la siguiente instruccin -la que sigue a LOCK,
-LOCK:
que es realmente un prefijo-). Tambin se activa automticamente en los momentos crticos de un ciclo
de interrupcin.
QS1/QS0
Queue Status. Permite determinar el estado de la cola de instrucciones del 8088.
:
DIFERENCIAS IMPORTANTES CON EL 8086.
El 8086 cambia el patillaje sensiblemente, aunque la mayora de las seales son similares. En
lugar de 8 lneas de datos y direcciones multiplexadas (AD0..7) el 8086 posee 16, ya que el bus de
datos es de 16 bits. Existe una lnea especialmente importante en el 8086, -BHE/S7 (Bus High
Enables/Status), que normalmente indica si se accede a la parte alta del bus de datos o no
(operaciones 8/16 bits). El 8086 posee una cola de instrucciones de 6 bytes, en lugar de 4.
El cdigo de operacin ocupa 6 bits; el bit D indica si es el operando fuente (=0) el que est
en el campo registro (REG) o si lo es el operando destino (=1): la razn es que el 8086 s lo
admite un operando a memoria, como mucho (o el fuente, o el destino, no los dos a la vez). El bit
W indica el tamao de la operacin (byte/palabra). MOD indica el modo de direccionamiento:
00-sin desplazamiento (no existe campo de desplazamiento), 01-desplazamiento de 8 bits, 10-
desplazamiento de 16 bits y 11-registro (tanto fuente como destino estn en registro). El campo
REG indica el registro involucrado en la instruccin, que puede ser de 8 16 bits (seg n
indique W): 0-AX/AL, 1-CX/CL, 2-DX/DL, 3-BX/BL, 4-SP/AH, 5-BP/CH, 6-SI/DH, 7-DI/BH; en
el caso de registros de segmento slo son significativos los dos bits de menor peso: 00-ES, 01-CS,
10-SS, 11-DS. El campo R/M, en el caso de modo registro (MOD=11) se codifica igual que el
campo REG; en caso contrario se indica la forma en que se direcciona la memoria: 0:
[BX+SI+desp], 1: [BX+DI+desp], 2: [BP+SI+desp], 3: [BP+DI+desp], 4: [SI+desp], 5: [DI+desp],
6: [BP+desp], 7: [BX+desp].
Para comprender este captulo, basta tener unos conocimientos razonables de C estndar.
Aqu se explicarn las funciones de librera necesarias para acceder al ms bajo nivel, as
como la manera de integrar el ensamblador y el C.
Aunque pueden parecer demasiadas, algunas son idnticas (caso de inp() e inport()) y otras se
diferencian slo ligeramente en el tipo de los datos devueltos, lo cual es irrelevante si se tiene en
cuenta que el dato devuelto es descartado (caso de outp() y outport()). En general, lo normal es
emplear inport() e inportb() para la entrada, as como outport() y outportb() para la salida. Por
ejemplo, para enviar el EOI al final de una interrupcin hardware se puede ejecutar:
outportb(0x20, 0x20);
Las funciones peek(), peekb(), poke() y pokeb() tienen una utilidad evidente de cara a consultar
y modificar las posiciones de memoria. Cuando se necesita saber el segmento y/o el offset de una
variable del programa, las macros FP_OFF y FP_SEG devuelven dicha informacin. Por ltimo,
con MK_FP es posible asignar una direccin de memoria absoluta a un puntero far. Por ejemplo, si
se declara una variable:
char far *pantalla_color;
se puede hacer que apunte a la memoria de vdeo del modo texto de los adaptadores de color con:
pantalla_color = MK_FP (0xB800, 0);
y despus se podra limpiar la pantalla con un bucle: for (i=0; i<4000; i++) *pantalla_color+
+=0;
Para llamar a las interrupciones es conveniente conocer antes ciertas estructuras y uniones.
struct WORDREGS {
unsigned int ax, bx, cx, dx, si, di, cflag, flags;
};
struct BYTEREGS {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
struct SREGS {
unsigned int es; unsigned int cs; unsigned int ss;
unsigned int ds;
};
struct REGPACK {
unsigned r_ax, r_bx, r_cx, r_dx;
unsigned r_bp, r_si, r_di, r_ds, r_es, r_flags;
};
Las dos primeras funciones se basan en la declaracin de dos uniones: una para entrada y otra
para salida, que simbolizan los valores iniciales (antes de llamar a la interrupcin) y finales (tras la
llamada) en los registros. Si se desea que la misma unin que indica los valores iniciales devuelva
los finales, se puede indicar por duplicado:
union REGS regs;
regs.h.ah = 0;
regs.h.al = 0x13; /* VGA 320x200 - 256 colores
*/
int86 (0x10, ®s, ®s); /* cambiar modo de vdeo */
La diferencia entre int86() e int86x() reside en que la ltima permite trabajar con los registros
de segmento (la estructura SREGS se puede inicializar con los valores que tienen que tener los
registros de segmento antes de llamar a la interrupcin; a la vuelta, dicha estructura habr sido
modificada para indicar el valor devuelto en los registros de segmento tras la interrupcin).
Hay quien prefiere trabajar con REGPACK, que con una sola estructura permite tambin
operar con los registros de segmento y la emplea tanto para enviar como para recibir los resultados.
El inconveniente, poco relevante, es que slo admite registros de 16 bits, lo que suele obligar a
hacer desplazamientos y forzar el empleo de mscaras para trabajar con las mitades necesarias:
struct REGPACK bios;
int main()
{
vieja_rutina = getvect (5); /* almacenar direccin de INT 5 (activada
con Print Screen) */
setvect (5, nueva_rutina); /* desviar INT 5 a nuestra propia rutina
de control */
. . .
. . . /* resto del programa */
. . .
setvect (5, vieja_rutina); /* restaurar rutina inicial de INT 5 */
}
La funcin anterior, basada en el servicio 31h del DOS, permite a un programa realizado en C
quedar residente en la memoria. Adems del cdigo de retorno, es preciso indicar el tama o del
rea residente (en prrafos). Es difcil determinar con precisin la memoria que ocupa un
programa en C. Sin embargo, en muchos casos la siguiente frmula puede ser vlida:
keep (0, (_SS + ((_SP + area_de_seguridad)/16) - _psp));
En los casos en que no lo sea, se le puede hacer que vuelva a serlo aumentando el tama o del
rea de seguridad (que en los programas menos conflictivos ser 0). Tanto _psp como _SS y _SP
estn definidas ya por el compilador, por lo que la lnea anterior es perfectamente vlida (sin
ms) al final de un programa.
De estas variables predefinidas, las ms tiles son quiz las que devuelven la versin del
DOS, lo que ahorra el esfuerzo que supone averiguarlo llamando al DOS o empleando la funci n
de librera correspondiente. Tambin es til _psp, que permite un acceso a este rea del
programa de manera inmediata.
Por medio de _ _emit_ _() se puede colocar cdigo mquina de manera directa dentro del
programa en C. No es conveniente hacerlo as porque as, ya que alterar directamente los
registros de la CPU acabar alterando el funcionamiento esperado del compilador y haciendo
fallar el programa. Sin embargo, en un procedimiento dedicado exclusivamente a almacenar
cdigo inline (en lnea), es seguro este mtodo, sobre todo si se tiene cuidado de no alterar los
registros SI y DI (empleados muy a menudo por el compilador como variables de tipo register). Por
medio de geninterrupt() se puede llamar directamente a una interrupci n: geninterrupt (interr) es
exactamente lo mismo que _ _emit_ _(0xCD, interr) ya que 0xCD es el c digo de operaci n de
INT. Por ejemplo, para volcar la pantalla por impresora se puede ejecutar geninterrupt(5). Con los
smbolos _AX, _AL, _AH, _BX, _BL, _BH, _CX, _CL, _CH, _DX, _DL, _DH, _SI, _DI, _BP,
_SP, _CS, _DS, _ES, _SS y _FLAGS se puede acceder directamente a los registros de la CPU. Hay
que tomar tambin precauciones para evitar efectos laterales (una asignacin tipo _DS=0x40 no
afectar slo a DS).
Los modelos de memoria constituyen las diversas maneras de acceder a la memoria por parte de
los compiladores de C. En el caso del Turbo C se pueden distinguir los siguientes:
TINY: Se emplea en los programas donde es preciso apurar el consumo de memoria hasta el
ltimo byte. Los 4 registros de segmento (CS, DS, ES, SS) estn asignados a la misma
direccin, por lo que existe un total de 64 Kb donde se mezclan c digo, datos y pila. Los
programas de este tipo pueden convertirse a formato COM.
SMALL: Se utiliza en aplicaciones pequeas. Los segmentos de cdigo y datos son diferentes y
no se solapan. Por ello, hay 64 kb para cdigo y otros 64 Kb a repartir entre datos y pila.
Segmentos Punteros
Modelo Cdigo Datos Pila Cdigo Datos
Tiny 64 Kb near near
Small 64 Kb 64 Kb near near
Medium 1 Mb 64 Kb far near
Compact 64 Kb 1 Mb near far
Large 1 Mb 1 Mb far far
1 Mb
Huge 1 Mb far far
(Bloques > 64 Kb)
MEDIUM: Este modelo es ideal para programas largos que no manejan demasiados datos. Se
utilizan punteros largos para el cdigo (que puede extenderse hasta 1 Mb) y cortos para los datos:
la pila y los datos juntos no pueden exceder de 64 Kb.
COMPACT: Al contrario que el anterior, este modelo es el apropiado para los programas
pequeos que emplean muchos datos. Por ello, el programa no puede exceder de 64 Kb aunque los
datos que controla pueden alcanzar el Mb, ya que los punteros de datos son de tipo far por defecto.
LARGE: Empleado en las aplicaciones grandes y tambin por los programadores de sistemas que
no tienen paciencia para andar forzando continuamente el tipo de los punteros (para rebasar el
lmite de 64 Kb). Tanto los datos como el cdigo pueden alcanzar el Mb, aunque no se admite
que los datos estticos ocupen ms de 64 Kb. Este modo es el que menos problemas da para
manejar la memoria, no siendo quiz tan lento y pesado como indica el fabricante.
HUGE: Similar al anterior, pero con algunas ventajas: por un lado, todos los punteros son
normalizados automticamente y se admiten datos estticos de ms de 64 Kb. Por otro, y
gracias a esto ltimo, es factible manipular bloques de datos de ms de 64 Kb cada uno, ya que
los segmentos de los punteros se actualizan correctamente. Sin embargo, este modelo es el m s
costoso en tiempo de ejecucin de los programas.
LA SENTENCIA ASM
La sentencia asm permite incluir cdigo ensamblador dentro del programa C, utilizando los
mnemnicos normales del ensamblador. Sin embargo, el uso de esta posibilidad est m s o
menos limitado segn la versin del compilador. En Turbo C 2.0, los programas que utilizan este
mtodo es necesario salir a la lnea de comandos para compilarlos con el tradicional compilador
de lnea, lo cual resulta poco atractivo. En Turbo C++ 1.0, se puede configurar adecuadamente el
compilador para que localice el Turbo Assembler y lo utilice automticamente para ensamblar, sin
necesidad de salir del entorno integrado. Sin embargo, es a partir del Borland C++ cuando se puede
trabajar a gusto: en concreto, la versin Borland C++ 2.0 permite ensamblar sin rodeos cdigo
ensamblador incluido dentro del listado C. El nico inconveniente es la limitaci n del hardware
disponible: para un PC/XT, el Turbo C 2.0 es el nico compilador aceptablemente r pido. Sin
embargo, en un 286 es ms recomendable el Turbo C++, mientras que en un 386 modesto (o
incluso en un 286 potente) resulta ms interesante emplear el Borland C++ 2.0: las versiones 3.X
de este compilador son las ms adecuadas para un 486 o superior (bajo DOS).
Esta es la nica sintaxis soportada por el Turbo C 2.0; sin embargo, en las versiones m s
modernas del compilador se admiten las llaves '{' y '}' para agrupar varias sentencias asm:
asm {
push ax; push cx;
mov cx,dato1
mov ax,0h }
mult: asm {
add ax,dato2
loop mult
mov resultado,ax
pop cx; pop ax;
}
SUBRUTINAS EN ENSAMBLADOR
Cuando las rutinas a incluir son excesivamente largas, resulta ms conveniente escribirlas como
ficheros independientes y ensamblarlas por separado, incluyndolas en un fichero de proyecto
(*.PRJ) seleccionable en los mens del compilador.
Para escribir este tipo de rutinas hay que respetar las mismas definiciones de segmentos que
realiza el compilador. Hoy en da existe algo ms de flexibilidad; sin embargo, aqu se expone
el mtodo general para mezclar cdigo de ensamblador con C.
main()
{
int a=21930; char b='Z';
La variable variable es una variable global del programa a la que no se asigna valor alguno en el
momento de definirla. Tanto a como b son variables locales del procedimiento main() y son
asignadas con un cierto valor inicial; funcion() no aparece por ningn sitio, ya que ser
codificada en ensamblador en un fichero independiente. A dicha funcin se le pasan 3
parmetros. La manera de hacerlo es colocndolos en la pila (empezando por el ltimo y
acabando por el primero). Por ello, el compilador meter primero en la pila el valor 1234h y luego
el 5678h (necesita dos palabras de pila porque es un dato de tipo long). Luego coloca en la pila el
carcter almacenado en la variable b: como los valores que se apilan son siempre de 16 bits, la
parte alta est a 0. Finalmente, deposita el dato entero a. Seguidamente, llama a la funcin
funcion() con un CALL que puede ser de dos tipos: corto (CALL/RET en el mismo segmento) o
largo (CALL/RETF entre distintos segmentos). Esta llamada a la funcin, por tanto, provoca un
almacenamiento adicional de 2 bytes (modelos TINY, SMALL y COMPACT) o 4 (en los restantes
modelos de memoria, que podramos llamar largos).
_TEXT ENDS
END
Como se puede observar, se respetan ciertas convenciones en cuanto a los nombres de los
segmentos y grupos. En el segmento _DATA se definen las variables inicializadas (las que tienen
un valor inicial): _dato podra haber sido accedida perfectamente desde el programa en C, ya que
es declarada como pblica. Por otro lado, en el segmento _BSS se definen o declaran las variables
que no son inicializadas con un valor inicial (como es el caso de la variable _variable del programa
C, que fue definida simplemente como int variable: en el listado ensamblador se la declara como
externa ya que est definida en el programa C). El compilador de C precede siempre de un
subrayado a todas las variables y funciones cuando compila, motivo por el cual hay que hacer lo
propio en el listado ensamblador. Al tratarse de un modelo de memoria pequeo, _BSS y _DATA
estn agrupados. En el segmento _TEXT se almacena el cdigo, es decir, las funciones
definidas: en nuestro caso, slo una (el procedimiento _funcion). Como es de tipo NEAR, slo se
podr emplear con programas C compilados en un modelo de memoria TINY, SMALL o
COMPACT (para los dems modelos hay que poner FAR en lugar de NEAR). Esta funci n de
ejemplo en ensamblador no utiliza ninguna variable, pero tanto _variable (la variable del programa
C) como, por supuesto, _info o _dato son plenamente accesibles.
A la hora de acceder a las variables, hay que tener en cuenta el modelo de memoria: como no
emplea ms de 64 Kb para cdigo (modelos TINY, SMALL o COMPACT), el compilador slo
ha colocado en la pila el offset de la direccin de retorno (registro IP). Nosotros apilamos
despus BP (ya que lo vamos a manchar) por lo que el ltimo dato que apil el programa C
antes de llamar a la rutina en ensamblador habr de ser accedido en [BP+4]. La ventaja de
inicializar BP es que luego se pueden introducir datos en la pila sin perder la posibilidad de acceder
a los parmetros de la rutina que llama. Si el procedimiento fuera de tipo FAR (modelos
MEDIUM, LARGE y HUGE), todos los accesos indexados sobre la pila se incrementar an en dos
unidades (por ejemplo, [BP+6] en vez de [BP+4] para acceder a la variable a) debido a que
tambin se habra almacenado CS en la llamada. Como se puede observar, la rutina no preserva
ni restaura todos los registros que va a emplear: slo es necesario devolver intactos DS, SS, BP y
(por si se emplean variables register) SI y DI; los dems registros pueden ser libremente alterados.
Como la funcin es de tipo entero, devuelve el resultado en AX; si fuera de tipo long lo
devolvera en DX:AX.
El modelo de memoria tambin cuenta en los parmetros que son pasados a la rutina en
ensamblador cuando no son pasados por valor (es decir, cuando se pasan punteros). En el ejemplo,
podramos haber pasado un puntero que podra ser de tipo corto (para cargarlo en BX, por
ejemplo, y efectuar operaciones tipo [BX]). Sin embargo, si se pasan punteros a variables de tipo far
(o si se emplea un modelo de memoria COMPACT, LARGE o HUGE) es necesario cargar la
direccin con una instruccin LES de 32 bits.
Esta rutina de ejemplo en ensamblador es slo demostrativa, por lo que no debe el lector
intentar encontrar alguna utilidad prctica, de ah que incluso ni siquiera emplee todas las
variables que define.
Desde las rutinas en ensamblador tambin se puede llamar a las funciones del compilador,
apilando adecuadamente los parmetros en la pila (empezando por el ltimo) y haciendo un
CALL al nombre de la funcin precedido de un subrayado: no olvidar nunca al final sumar a SP la
cantidad necesaria para reequilibrar la pila.
Las teclas marcadas con 'Ex' son exclusivas de teclados expandidos; generan los mismos
cdigos de rastreo que sus correspondientes teclas no expandidas, aunque precedidos de un
cdigo de rastreo adicional 0E0h como mnimo, por lo general (consultar el apartado 5.2 del
captulo 7 para ms detalles).
Cdigos secundarios.
Ha de tenerse en cuenta que la BIOS modifica en ocasiones el valor ledo del buffer del
teclado, aunque en la siguiente tabla hay pautas para detectar esta circunstancia si fuera necesario.
En primer lugar, cuando se invoca a la BIOS con las funciones 0 y 1, ste se encarga de simular
las teclas normales con las expandidas, as como de ocultar las combinaciones exclusivamente
expandidas. Aquellos cdigos precedidos de (*) en la tabla son ocultados por la BIOS (como si no
se hubiera pulsado las teclas) al emplear las funciones 0 y 1, sacndolos del buffer e
ignorndolos. En concreto, estos cdigos son almacenados con un cdigo ASCII 0F0h en el
buffer del teclado. Lgicamente, para las funciones 10h y 11h s existen, aunque la BIOS
devuelve un 0 en AL (y no un 0F0h). A los cdigos precedidos por (#) les sucede lo mismo: s lo
existen para las funciones 10h y 11h, al emplear dichas funciones la BIOS devuelve en AL el valor
0 (el autntico contenido del buffer en esta ocasin, sin necesidad de transformarlo). Por ltimo,
los cdigos precedidos por (@) existen tanto para las funciones 0 y 1 como para la 10h y la 11h: la
ventaja de usar las dos ltimas es que devuelven en AL el autntico cdigo ASCII del buffer
(0E0h), permitiendo diferenciar entre la pulsacin de una tecla normal y su correspondiente
expandida.
En general, quien no desee complicarse la vida con este galimatas (debido a una evidente falta
de previsin en el diseo del primer teclado) puede limitarse a emplear las combinaciones
normales (las no marcadas con #, # ni *). Por otra parte, para emplear las combinaciones sealadas
con (#), (@) o (*) hay que asegurarse previamente de que la BIOS soporta teclado expandido
(vase captulo 7, apartado 5.3).
Para diferenciar las teclas repetidas, en la tabla siguiente, las teclas entrecomilladas se suponen
expandidas o, en su defecto, ubicadas en el teclado numrico. Por ejemplo: "5" es el 5 del teclado
numrico, "<-" es el cursor izquierdo expandido y <- a secas el normal (esto es, la tecla 4 del
teclado numrico con Num Lock inactivo). Se emplea la notacin anglosajona: Ctrl (Control),
Alt (Alt o AltGr), Shift (Mays), Ins (Insert), Del (Supr), Home (Inicio), End (Fin), PgUp (ReP g),
PgDn (AvPg).
Excepciones:
Hay un par de teclas que sin tener un cdigo ASCII 0, 0E0h ni 0F0h reciben un tratamiento
especial por parte de la BIOS, que provoca que el cdigo secundario no sea el de rastreo
acostumbrado: el Intro del teclado numrico genera un cdigo ASCII 0Dh, como cabra
esperar, pero su cdigo secundario es 0E0h; lo mismo sucede con el '/' del teclado num rico. Las
funciones 0 y 1 de la BIOS traducen este 0E0h al valor correspondiente a la tecla Intro principal y al
'-' del teclado principal (tecla que ocupa la posicin del '/' en los teclados norteamericanos), para
compatibilizar con los teclados no expandidos.
Apndice VI - TAMAOS Y TIEMPOS DE EJECUCIN DE LAS INSTRUCCIONES
En la tabla de esta pgina se listan las instrucciones del ensamblador por orden alfab tico,
indicndose el nmero de bytes consumidos al ser ensambladas as como los tiempos tericos
de ejecucin en 8088, 286, 386 y 486. Estos tiempos son tericos y no deber an ser utilizados
para temporizaciones exactas. Por otra parte son diferentes de un procesador a otro. Los tiempos se
expresan en estados de mquina (1 MHz equivale a 1.000.000 de estados o ciclos de reloj) estando
la capacidad de ejecucin de instrucciones lgicamente en funcin de los MHz del equipo que
se trate. Estos tiempos se aplican suponiendo que se cumplen las siguientes hiptesis:
La instruccin ya ha sido extrada de la memoria y decodificada.
Los datos, si los hay, estn alineados (a palabra o doble palabra).
No hay estados de espera en la placa principal del ordenador.
Nadie ha sustraido el control del bus a la CPU (el DMA no debe estar actuando y no han de
producirse ciclos de refresco de la memoria).
No se produce ninguna interrupcin o excepcin durante la ejecucin.
Evidentemente, es casi imposible que los tiempos tericos sean los reales, teniendo en cuenta
todos estos factores. Cuanto menos potente es la mquina, mucho ms lentos son los tiempos
reales; por el contrario, en ordenadores con cach y procesador avanzado los tiempos efectivos
pueden ser en ocasiones mejores que los tericos!. Por ejemplo, el 486 emplea ya la tecnolog a
pipeline, lo que le permite simultanear la ejecucin de una instruccin con la decodificacin de
la siguiente y la lectura de memoria de la posterior as como almacenar el resultado de la anterior.
Esto, con las lgicas limitaciones de un procesador CISC, permite en la prctica ejecutar un alto
nmero de instrucciones en un solo ciclo (cada una de ellas, claro). Por tanto, para lo que s
sirven las tablas es para decidir qu instrucciones emplear en ciertos procesos en que el tiempo de
ejecucin o la memoria consumida son crticos, especialmente en las mquinas menos potentes.
Como muestra de lo sumamente tericos que son estos tiempos, a continuacin se listan dos
rutinas con las que he probado experimentalmente los tiempos de ejecucin en diversos
microprocesadores. Ambas rutinas constan de un bucle que se repite cierto nmero de veces;
mientras tanto las interrupciones estn inhibidas, por lo que se cronometran a mano:
Ciclos tericos
RutinaA: CLI
MOV AX,1000h
bucle: XOR CX,CX 3 2 2
1
repite: LOOP repite 17 5 8+m 4 (m=2) 11+m (m=2) 6
2
DEC AX 2 2 2
1
JNZ bucle 16 4 7+m 3 (m=2) 7+m 3 (m=2) 3
1
STI
RutinaB: CLI
XOR CX,CX
bucle1: MOV AX,BX 2 2 2
1
bucle2: MOV AX,BX 2 2 2
1
... . . .
.
bucle16384: MOV AX,BX 2 2
2 .
DEC CX 2 2 2
1
JNZ fin 16 4 7+m 3 7+m 3 (m=1) 3
1
JMP bucle1 15 7+m 7+m (m=2)
3
fin: STI
Por ejemplo, la rutina B ejecuta 16384 instrucciones del tipo MOV AX,BX (2 ciclos cada una)
as como un decremento (2 ciclos) un salto que no se realiza -salvo al final del todo- (4 ciclos en
8088) y otro salto absoluto (15 ciclos en 8088). Se emplea este rodeo ya que los saltos
condicionales, como conocer el lector, slo pueden desviar algo ms de 100 bytes el flujo del
programa (y este bucle ocupa nada menos que 32 Kb). En total, 32787 ciclos que, repetidos 65536
veces, suponen 2.148.728.832 ciclos. Con un 8088 corriendo a 8 MHz (8 millones de ciclos)
cabra esperar una demora de 268,59 segundos. Sin embargo, mi reloj de pulsera dice que son
nada menos que 1194!, unas 4,44 veces ms de lo que los tiempos tericos de Intel sugieren.
De hecho, esto implica que cada MOV tarda casi 9 ciclos reales en un 8088, y no 2. Sin embargo,
en el caso de la rutina A apenas hay diferencia entre el tiempo terico y el real: el tiempo que
emplea la instruccin LOOP es bastante alto en comparacin con lo que se tarda en traer dicha
instruccin de la memoria, por lo que la diferencia porcentual se reduce notablemente.
RUTINA A RUTINA B
Teric Teric
Efectivo Efectivo
o o
8088-4.77 956,71 1014,00 450,47 1946,00
V20-8 570,43 623,30 268,59 1194,00
286-12 223,70 254,00 179,02 188,25
386-25* 139,59 135,20 85,93 93,50
486-25* 64,42 75,50 42,96 69,10
(*) El 386 careca de memoria cach y el 486
slo posea los 8 Kb de cach incluidos en el chip.
El 8088, bastante menos potente que el 286, vara enormemente la velocidad de ejecucin de
las instrucciones en funcin del modo de direccionamiento, hay que aadir adems dos ciclos
de reloj en este procesador cuando se usa un prefijo de registro de segmento. En la siguiente tabla se
indica el nmero de ciclos de reloj adicionales que deben considerarse en el 8086/8088 para
calcular la direccin de memoria efectiva (EA, Efective Address) en la tabla de tiempos, segn el
tipo de direccionamiento:
Componentes Operandos valor EA
(a) Base o ndice [BX], [BP], [SI], [DI] 5
(b) Desplazamiento desp 6
(c1) Base + ndice [BX+SI], [BX+DI] 7
(c2) Base + ndice [BP+SI], [BP+DI] 8
(d) Desplaz.+ base/ndice [BX+desp], [BP+desp], [DI+desp], [SI+desp] 9
(e) Desplaz.+ base +
[BX+SI+desp], [BX+DI+desp] 11
ndice
Los datos entre parntesis en el 8088 indican el tiempo empleado por las palabras de 16 bits,
fuera del parntesis hacen referencia a 8 bits (los 8086 y superiores no son ms lentos con datos
de 16 que con los de 8 bits, siempre lgicamente que stos estn en una posicin de memoria
par). Aunque el 286 y 386 no penalizan tanto los modos de direccionamiento complejos, a los
tiempos marcados con (#) hay que aadir un ciclo si en el offset participan tres elementos (ej.,
BP+DI+desp). La letra {m} se refiere al nmero de bytes totales de la siguiente instrucci n que
se va a ejecutar. Cuando aparecen dos opciones en las instrucciones de salto condicional, el menor
tiempo de ejecucin se verifica cuando el salto no se realiza. Todas las instrucciones especficas
de 386 ocupan, bajo DOS, un byte ms de lo que indican las tablas debido a que se utiliza un
prefijo para forzar el modo 32 bit en segmentos de 16. En los tiempos del 386, los datos entre
parntesis se aplican cuando la CPU est en modo virtual 86; en general, los tiempos de
ejecucin corresponden al modo real (en modo protegido, podran variar).
Inst. Operandos Bytes Ciclos 8088 Ciclos
286 Ciclos 386 Ciclos 486
------ ----------------------------------- ------- ---------------
------------ ------------ ------------
AAA 1 8 3
4 3
AAD 2 60 14
19 14
AAM 2 83 16
17 15
AAS 1 8 3
4 3
ADC registro, registro 2 3 2
2 1
ADC registro, memoria 2-4 9(13)+EA 7 #
6 2
ADC memoria, registro 2-4 16(24)+EA 7 #
7 3
ADC registro, inmediato 3-4 4 3
2 1
ADC memoria, inmediato 3-6 17(25)+EA 7 #
7 3
ADC acumulador, inmediato 2-3 4 3
2 1
ADD registro, registro 2 3 2
2 1
ADD registro, memoria 2-4 9(13)+EA 7 #
6 2
ADD memoria, registro 2-4 16(24)+EA 7 #
7 3
ADD registro, inmediato 3-4 4 3
2 1
ADD memoria, inmediato 3-6 17(25)+EA 7 #
7 3
ADD acumulador, inmediato 2-3 4 3
2 1
AND registro, registro 2 3 2
2 1
AND registro, memoria 2-4 9(13)+EA 7 #
6 2
AND memoria, registro 2-4 16(24)+EA 7 #
7 3
AND registro, inmediato 3-4 4 3
2 1
AND memoria, inmediato 3-6 17(25)+EA 7 #
7 3
AND acumulador, inmediato 2-3 4 3
2 1
BOUND registro16, memoria16 2-4 (no existe) 13 #
10 7
BOUND registro32, memoria32 2-6 (no existe) (no
existe) 10 7
BSF registro16, registro16 3 (no existe) (no
existe) 10+3*n 6-42
BSF registro16, memoria16 5-7 (no existe) (no
existe) 10+3*n 7-43
BSF registro32, registro32 3 (no existe) (no
existe) 10+3*n 6-42
BSF registro32, memoria32 5-7 (no existe) (no
existe) 10+3*n 7-43
BSR registro16, registro16 3 (no existe) (no
existe) 10+3*n 6-42
BSR registro16, memoria16 5-7 (no existe) (no
existe) 10+3*n 7-43
BSR registro32, registro32 3 (no existe) (no
existe) 10+3*n 6-42
BSR registro32, memoria32 5-7 (no existe) (no
existe) 10+3*n 7-43
BT registro16, registro16 3 (no existe) (no
existe) 3 3
BT memoria16, registro16 5-7 (no existe) (no
existe) 12 8
BT registro32, registro32 3 (no existe) (no
existe) 3 3
BT memoria32, registro32 5-7 (no existe) (no
existe) 12 8
BT registro16, inmediato8 4 (no existe) (no
existe) 3 3
BT memoria16, inmediato8 6-8 (no existe) (no
existe) 6 3
BT registro32, inmediato8 4 (no existe) (no
existe) 3 3
BT memoria32, inmediato8 6-8 (no existe) (no
existe) 6 3
BTC registro16, registro16 3 (no existe) (no
existe) 6 6
BTC memoria16, registro16 5-7 (no existe) (no
existe) 13 13
BTC registro32, registro32 3 (no existe) (no
existe) 6 6
BTC memoria32, registro32 5-7 (no existe) (no
existe) 13 13
BTC registro16, inmediato8 4 (no existe) (no
existe) 6 6
BTC memoria16, inmediato8 6-8 (no existe) (no
existe) 8 8
BTC registro32, inmediato8 4 (no existe) (no
existe) 6 6
BTC memoria32, inmediato8 6-8 (no existe) (no
existe) 8 8
BTR registro16, registro16 3 (no existe) (no
existe) 6 6
BTR memoria16, registro16 5-7 (no existe) (no
existe) 13 13
BTR registro32, registro32 3 (no existe) (no
existe) 6 6
BTR memoria32, registro32 5-7 (no existe) (no
existe) 13 13
BTR registro16, inmediato8 4 (no existe) (no
existe) 6 6
BTR memoria16, inmediato8 6-8 (no existe) (no
existe) 8 8
BTR registro32, inmediato8 4 (no existe) (no
existe) 6 6
BTR memoria32, inmediato8 6-8 (no existe) (no
existe) 8 8
BTS registro16, registro16 3 (no existe) (no
existe) 6 6
BTS memoria16, registro16 5-7 (no existe) (no
existe) 13 13
BTS registro32, registro32 3 (no existe) (no
existe) 6 6
BTS memoria32, registro32 5-7 (no existe) (no
existe) 13 13
BTS registro16, inmediato8 4 (no existe) (no
existe) 6 6
BTS memoria16, inmediato8 6-8 (no existe) (no
existe) 8 8
BTS registro32, inmediato8 4 (no existe) (no
existe) 6 6
BTS memoria32, inmediato8 6-8 (no existe) (no
existe) 8 8
CALL procedimiento near (intrasegmento) 3 23 7+m
7+m 3
CALL procedimiento far (intersegmento) 5 36 13+m
17+m 18
CALL intrasegmento indirecto a memoria 2-4 29+EA 11+m
10+m 5
CALL intrasegmento indirecto a registro 2 24 7+m
7+m 5
CALL intersegmento indirecto a memoria 2-4 57+EA 16+m
22+m 17
CBW 1 2 2
3 3
CDQ 1 (no existe) (no
existe) 2 3
CLC 1 2 2
2 2
CLD 1 2 2
2 2
CLI 1 2 3
3 5
CMC 1 2 2
2 2
CMP registro, registro 2 3 2
2 1
CMP registro, memoria 2-4 9(13)+EA 6 #
6 2
CMP memoria, registro 2-4 9(13)+EA 7 #
5 2
CMP registro, inmediato 3-4 4 3
2 1
CMP memoria, inmediato 3-6 10(14)+EA 6 #
5 2
CMP acumulador, inmediato 2-3 4 3
2 1
CMPS 1 22(30) 8
10 8
CMPS (REP) 1 9+22(30)*n 5+9*n
5+9*n 5 (CX=0) 7+7*n
CWD 1 5 2
2 3
CWDE 1 (no existe) (no
existe) 3 3
DAA 1 4 3
4 2
DAS 1 4 3
4 2
DEC registro byte 2 3 2
2 1
DEC registro palabra 1 2 2
2 1
DEC memoria 2-4 15(23)+EA 7 #
6 3
DIV registro byte 2 80-90 14
14 16
DIV registro palabra 2 144-162 22
22 24
DIV registro32 2 (no existe) (no
existe) 38 40
DIV byte de memoria 2-4 86-96+EA 17 #
17 16
DIV palabra de memoria 2-4 154-172+EA 25 #
25 24
DIV palabra32 de memoria 2-6 (no existe) (no
existe) 41 40
ENTER constante16, 0 4 (no existe) 11
10 14
ENTER constante16, 1 4 (no existe) 15
12 17
ENTER constante16, nivel 4 (no existe) 12+4*(n-
1) 15+4*(n-1) 17+3*n
ESC inmediato, memoria 2-4 8(12)+EA 9-20 #
(ver coproc.) (ver coproc.)
ESC inmediato, registro 2 2 2
(ver coproc.) (ver coproc.)
HLT 1 2 2
5 4
IDIV registro byte 2 101-112 17
19 19
IDIV registro palabra 2 165-185 25
27 27
IDIV registro32 2 (no existe) (no
existe) 43 43
IDIV byte de memoria 2-4 107-118+EA 20 #
19 20
IDIV palabra de memoria 2-4 175-194+EA 28 #
27 28
IDIV palabra32 de memoria 2-6 (no existe) (no
existe) 43 44
IMUL registro byte 2 80-98 13
9-14 13-18
IMUL registro palabra 2 128-154 21
9-22 13-26
IMUL registro32 2 (no existe) (no
existe) 9-38 13-42
IMUL byte de memoria 2-4 86-104+EA 16
12-17 13-18
IMUL palabra de memoria 2-4 138-164+EA 24 #
12-25 13-26
IMUL palabra32 de memoria 2-6 (no existe) (no
existe) 12-41 13-42
IMUL registro16 destino, constante 3-4 (no existe) 21
9-22 13-26
IMUL registro16 destino, memoria 5-7 (no existe) (no
existe) 12-25 13-26
IMUL registro32 destino, memoria 5-7 (no existe) (no
existe) 12-41 13-42
IMUL registro destino, registro, cte. 2-4 (no existe) 21
9-22 13-26
IMUL registro destino, memoria, cte. 3-4 (no existe) 24 #
12-25 13-26
IN acumulador, puerto fijo 2 10(14) 5
12(26) 14(27)
IN acumulador, DX 1 8(12) 5
13(27) 14(27)
INC registro byte 2 3 2
2 1
INC registro palabra 1 2 2
2 1
INC memoria 2-4 15(23)+EA 7 #
6 3
INS 1 (no existe) 5
15(29) 17(30)
INS (REP) 2 (no existe) 5+4*n
13(27)+6*n 16(29)+8*n
INT 3 1 52 23+m
33 26
INT inmediato 2 51 23+m
37 30
INTO 1 53 4 24+m
3 35 3 28 3
IRET 1 32 17+m
22 15
JCXZ 2 18 6 8+m
4 9+m 5 3 1
JECXZ 2 (no existe) (no
existe) 9+m 5 3 1
JMP short 2 15 7+m
7+m 3
JMP near (intrasegmento) 3 15 7+m
7+m 3
JMP far (intersegmento) 5 15 11+m
12+m 17
JMP intrasegmento indirecto a memoria 2-4 18+EA 11+m
# 10+m 5
JMP intrasegmento indirecto a registro 2 11 7+m
7+m 5
JMP intersegmento indirecto a memoria 2-4 24+EA 15+m
17+m 13
Jxxx inmediato8 2 16 4 7+m
3 7+m 3 3 1
Jxxx inmediato32 6 (no existe) (no
existe) 7+m 3 3 1
LAHF 1 4 2
2 3
LDS 2-4 24+EA 7 #
7 6
LEA 2-4 2+EA 3 #
2 1
LEAVE 1 (no existe) 5
4 5
LES 2-4 24+EA 7 #
7 6
LFS 2-4 (no existe) (no
existe) 7 6
LGS 2-4 (no existe) (no
existe) 7 6
LSS 2-4 (no existe) (no
existe) 7 6
LOCK 1 2 0
0 1
LODS 1 12(16) 5
5 5
LODS (REP) 1 9+13(17)*n 5+4*n
5+6*n 5 (CX=0) 7+4*n
LOOP 2 17 5 8+m
4 11+m 2 6
LOOPE 2 18 6 8+m
4 11+m 9 6
LOOPNE 2 19 5 8+m
4 11+m 9 6
LOOPZ 2 18 6 8+m
4 11+m 9 6
LOOPNZ 2 19 5 8+m
4 11+m 9 6
MOV memoria, acumulador 3 10(14) 3
2 1
MOV acumulador, memoria 3 10(14) 5
4 1
MOV registro, registro 2 2 2
2 1
MOV registro, memoria 2-4 8(12)+EA 5 #
4 1
MOV memoria, registro 2-4 9(13)+EA 3 #
2 1
MOV registro, inmediato 2-3 4 2
2 1
MOV memoria, inmediato 3-6 10(14)+EA 3 #
2 1
MOV registro de segmento, registro 2 2 2
2 3
MOV registro, registro de segmento 2 2 2
2 3
MOV registro de segmento, memoria 2-4 8(12)+EA 5 #
5 9
MOV memoria, registro de segmento 2-4 9(13)+EA 3 #
2 3
MOVS 1 18(26) 5
7 7
MOVS (REP) 1 9+17(25)*n 5+4*n
5+4*n 5 (CX=0) 12+3*n
MOVSX registro16, registro8 3 (no existe) (no
existe) 3 3
MOVSX registro16, memoria8 5-7 (no existe) (no
existe) 6 3
MOVSX registro32, registro8 3 (no existe) (no
existe) 3 3
MOVSX registro32, memoria8 5-7 (no existe) (no
existe) 6 3
MOVSX registro32, registro16 3 (no existe) (no
existe) 3 3
MOVSX registro32, memoria16 5-7 (no existe) (no
existe) 6 3
MOVZX registro16, registro8 3 (no existe) (no
existe) 3 3
MOVZX registro16, memoria8 5-7 (no existe) (no
existe) 6 3
MOVZX registro32, registro8 3 (no existe) (no
existe) 3 3
MOVZX registro32, memoria8 5-7 (no existe) (no
existe) 6 3
MOVZX registro32, registro16 3 (no existe) (no
existe) 3 3
MOVZX registro32, memoria16 5-7 (no existe) (no
existe) 6 3
MUL registro byte 2 70-77 13
9-14 13
MUL registro palabra 2 118-133 21
9-22 13
MUL registro32 2 (no existe) (no
existe) 9-38 13
MUL byte de memoria 2-4 76-83+EA 16 #
12-27 18
MUL palabra de memoria 2-4 128-143+EA 24 #
12-25 26
MUL palabra32 de memoria 2-6 (no existe) (no
existe) 12-41 42
NEG registro 2 3 2
2 1
NEG memoria 2-4 16(24)+EA 7 #
6 3
NOP 1 3 3
3 1
NOT registro 2 3 2
2 1
NOT memoria 2-4 16(24)+EA 7 #
6 3
OR registro, registro 2 3 2
2 1
OR registro, memoria 2-4 9(13)+EA 7 #
6 3
OR memoria, registro 2-4 16(24)+EA 7 #
7 3
OR registro, inmediato 3-4 4 3
2 1
OR memoria, inmediato 3-6 17(25)+EA 7 #
7 3
OR acumulador, inmediato 2-3 4 3
2 1
OUT puerto fijo, acumulador 2 10(14) 3
10(24) 16(29)
OUT DX, acumulador 1 8(12) 3
11(25) 16(29)
OUTS byte o palabra 1 (no existe) 5
14(28) 17(30)
OUTS (REP) 2 (no existe) 5+4*n
12(26)+5*n 17(31)+5*n
POP registro normal 1 12 5
4 4
POP registro de segmento 1 12 5
7 3
POP memoria 2-4 25+EA 5 #
5 6
POPA 1 (no existe) 19
24 9
POPAD 1 (no existe) (no
existe) 24 9
POPF 1 12 5
5 9
POPFD 1 (no existe) (no
existe) 5 9
PUSH registro 1 14 3
2 1
PUSH memoria 2-4 24+EA 5 #
5 4
PUSH inmediato 2-3 (no existe) 3
2 1
PUSHA 1 (no existe) 17
18 11
PUSHAD 1 (no existe) (no
existe) 18 11
PUSHF 1 14 3
4 4
PUSHFD 1 (no existe) (no
existe) 4 4
RCL registro,1 2 2 2
9 3
RCL registro,CL 2 8+4*bits 5
9 8-30
RCL registro, contador 3 (no existe) 5
9 8-30
RCL memoria, contador 3-6 (no existe) 8 #
10 9-31
RCL memoria,1 2-4 15(23)+EA 7 #
10 4
RCL memoria,CL 2-4 20(28)+EA+4*bits 8 #
10 9-31
RCR registro,1 2 2 2
9 3
RCR registro,CL 2 8+4*bits 5
9 8-30
RCR registro, contador 3 (no existe) 5
9 8-30
RCR memoria, contador 3-6 (no existe) 8 #
10 9-31
RCR memoria,1 2-4 15(23)+EA 7 #
10 4
RCR memoria,CL 2-4 20(28)+EA+4*bits 8 #
10 9-31
REP 1 2 0
0 0
REPE 1 2 0
0 0
REPNE 1 2 0
0 0
REPZ 1 2 0
0 0
REPNZ 1 2 0
0 0
RET intrasegmento 1 20 11+m
10+m 5
RET intrasegmento con SP+inmediato 3 24 11+m
10+m 5
RET intersegmento 1 32 15+m
18+m 13
RET intersegmento con SP+inmediato 3 31 15+m
18+m 14
ROL registro,1 2 2 2
3 3
ROL registro,CL 2 8+4*bits 5
3 3
ROL registro, contador 3 (no existe) 5
3 2
ROL memoria, contador 3-6 (no existe) 8 #
7 4
ROL memoria,1 2-4 15(23)+EA 7 #
7 4
ROL memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
ROR registro,1 2 2 2
3 3
ROR registro,CL 2 8+4*bits 5
3 3
ROR registro, contador 3 (no existe) 5
3 2
ROR memoria, contador 3-6 (no existe) 8 #
7 4
ROR memoria,1 2-4 15(23)+EA 7 #
7 4
ROR memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
SAHF 1 4 2
3 2
SAL registro,1 2 2 2
3 3
SAL registro,CL 2 8+4*bits 5
3 3
SAL registro, contador 3 (no existe) 5
3 2
SAL memoria, contador 3-6 (no existe) 8 #
7 4
SAL memoria,1 2-4 15(23)+EA 7 #
7 4
SAL memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
SAR registro,1 2 2 2
3 3
SAR registro,CL 2 8+4*bits 5
3 3
SAR registro, contador 3 (no existe) 5
3 2
SAR memoria, contador 3-6 (no existe) 8 #
7 4
SAR memoria,1 2-4 15(23)+EA 7 #
7 4
SAR memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
SBB registro, registro 2 3 2
2 1
SBB registro, memoria 2-4 9(13)+EA 7 #
6 2
SBB memoria, registro 2-4 16(24)+EA 7 #
7 3
SBB registro, inmediato 3-4 4 3
2 1
SBB memoria, inmediato 3-6 17(25)+EA 7 #
7 3
SBB acumulador, inmediato 2-3 4 3
2 1
SCAS 1 15(19) 7
7 6
SCAS (REP) 1 9+15(19)*n 5+8*n
5+8*n 5 (CX=0) 7+5*n
SETcc registro8 2 (no existe) (no
existe) 4 4
SETcc memoria8 4-6 (no existe) (no
existe) 5 3
SHL registro,1 2 2 2
3 3
SHL registro,CL 2 8+4*bits 5
3 3
SHL registro, contador 3 (no existe) 5
3 2
SHL memoria, contador 3-6 (no existe) 8 #
7 4
SHL memoria,1 2-4 15(23)+EA 7 #
7 4
SHL memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
SHLD registro16, registro16, inmediato8 4 (no existe) (no
existe) 3 2
SHLD memoria16, registro16, inmediato8 6-8 (no existe) (no
existe) 7 3
SHLD registro32, registro32, inmediato8 4 (no existe) (no
existe) 3 2
SHLD memoria32, registro32, inmediato8 6-8 (no existe) (no
existe) 7 3
SHLD registro16, registro16, CL 3 (no existe) (no
existe) 3 2
SHLD memoria16, registro16, CL 5-7 (no existe) (no
existe) 7 3
SHLD registro32, registro32, CL 3 (no existe) (no
existe) 3 2
SHLD memoria32, registro32, CL 5-7 (no existe) (no
existe) 7 3
SHR registro,1 2 2 2
3 3
SHR registro,CL 2 8+4*bits 5
3 3
SHR registro, contador 3 (no existe) 5
3 2
SHR memoria, contador 3-6 (no existe) 8 #
7 4
SHR memoria,1 2-4 15(23)+EA 7 #
7 4
SHR memoria,CL 2-4 20(28)+EA+4*bits 8 #
7 4
SHRD registro16, registro16, inmediato8 4 (no existe) (no
existe) 3 2
SHRD memoria16, registro16, inmediato8 6-8 (no existe) (no
existe) 7 3
SHRD registro32, registro32, inmediato8 4 (no existe) (no
existe) 3 2
SHRD memoria32, registro32, inmediato8 6-8 (no existe) (no
existe) 7 3
SHRD registro16, registro16, CL 3 (no existe) (no
existe) 3 2
SHRD memoria16, registro16, CL 5-7 (no existe) (no
existe) 7 3
SHRD registro32, registro32, CL 3 (no existe) (no
existe) 3 2
SHRD memoria32, registro32, CL 5-7 (no existe) (no
existe) 7 3
STC 1 2 2
2 2
STD 1 2 2
2 2
STI 1 2 2
3 5
STOS 1 11(15) 3
4 5
STOS (REP) 1 9+10(14)*n 4+3*n
5+5*n 5 (CX=0) 7+4*n
SUB registro, registro 2 3 2
2 1
SUB registro, memoria 2-4 9(13)+EA 7 #
6 2
SUB memoria, registro 2-4 16(24)+EA 7 #
7 3
SUB registro, inmediato 3-4 4 3
2 1
SUB memoria, inmediato 3-6 17(25)+EA 7 #
7 3
SUB acumulador, inmediato 2-3 4 3
2 1
TEST registro, registro 2 3 2
2 1
TEST registro, memoria 2-4 9(13)+EA 6 #
5 2
TEST memoria, registro 2-4 16(24)+EA 6 #
5 2
TEST registro, inmediato 3-4 4 3
2 1
TEST memoria, inmediato 3-6 17(25)+EA 6 #
5 2
TEST acumulador, inmediato 2-3 4 3
2 1
WAIT 1 3 3
6 1-3
XCHG AX,registro16 1 3 3
3 3
XCHG registro, registro 2 4 3
3 3
XCHG memoria, registro 2-4 17(25)+EA 5 #
5 5
XLAT 1 11 5
5 4
XOR registro, registro 2 3 2
2 1
XOR registro, memoria 2-4 9(13)+EA 7 #
6 2
XOR memoria, registro 2-4 16(24)+EA 7 #
7 3
XOR registro, inmediato 3-4 4 3
2 1
XOR memoria, inmediato 3-6 17(25)+EA 7 #
7 3
XOR acumulador, inmediato 2-3 4 3 2
1
Apndice VII - SEALES DEL SLOT DE EXPANSIN ISA
El slot de expansin del XT, de 8 bits, consta de 62 terminales en un conector hembra, 31 por
cada cara. La cara A es la de los componentes; por la B slo hay pistas. Viendo las tarjetas por
arriba (por la cara de componentes) y con los conectores exteriores a la derecha, la numeraci n
comienza de derecha a izquierda. En los AT el slot de 16 bits consta de 36 terminales m s,
distribuidos en grupos de 18 en dos nuevas caras (C y D). La mayor a de las m quinas AT
poseen slots de 8 y 16 bits, aunque lo ideal sera que todos fueran de 16 (en los de 16 bits se
pueden insertar tambin tarjetas de 8 bits, dejando la otra mitad al aire).
Las seales en la parte de 8 bits son idnticas en XT y AT, si se excepta la lnea IRQ2 que
en los AT es realmente IRQ9 (IRQ2 es empleada en la placa base para conectar en cascada el
segundo controlador de interrupciones; por compatibilidad con los XT, cuando se produce una
IRQ9 -normalmente una INT 71h- se invoca por software la INT 0Ah).
En el siguiente esquema, las lneas activas en alto van precedidas de un signo (+); las activas
en estado lgico bajo (-). Los smbolos I (Input) y O (Output) indican si las l neas son de
entrada, salida o bidireccionales.
El slot de expansin de los PC contiene bsicamente las principales se ales del 8086
demultiplexadas, as como otras de interrupciones, DMA, control de E/S, etc. Las se ales
presentes en el slot de expansin de 8 bits son:
(Oscilator) Seal de reloj de casi 70 ns (14,31818 MHz) que est la mitad del
OSC:
perodo en estado alto y la otra mitad en estado bajo.
(Address Latch Enable) Indica en su flanco de bajada que el latch de direcciones se ha
ALE:
cargado con una direccin vlida procedente del microprocesador.
TC: (Terminal Count) Indica el final de la cuenta en algn canal de DMA.
DRQ1- (DMA Request) Lneas asncronas de peticin de DMA (1 mayor prioridad, 3
DRQ3: menor). Esta lnea debe activarse hasta que DACK (activo a nivel bajo) suba.
DACK1- (DMA Acknowledge) Indica que ha sido atendida la peticin de DMA y que debe
DACK3: bajarse el correspondiente DRQ.
IRQ2- (Interrupt request) Indica una peticin de interrupcin (2 mayor prioridad, 7 menor).
IRQ7: La seal debe mantenerse activa hasta que la interrupcin acabe de ser procesada.
(Input/Output Read) Seala al dispositivo de E/S que se va a leer el bus de datos; esta
IOR:
lnea la controla la CPU o el DMA.
(Input/Output Write) Seala al dispositivo de E/S que se va a escribir en el bus de
IOW:
datos; esta lnea la controla tambin la CPU o el DMA.
(Memory Read) Indica que se va a efectuar una lectura de la memoria en la direccin
MEMR:
contenida en el bus de direcciones. La activa la CPU o el DMA.
(Memory Write) Indica que se va a efectuar una escritura en memoria en la direccin
MEMW:
contenida en el bus de direcciones. La activa la CPU o el DMA.
(Reset drive) Avisa de que el sistema est en proceso de reinicializacin, para que
RESET
todos los dispositivos conectados se inicialicen. Se activa en el flanco de bajada de la
DRV:
seal del reloj.
(Address) Bus de direcciones comn a la memoria y a la E/S, controlado por la CPU
A0-A19:
o el DMA.
D0-D7: (Data) Bus de datos que conecta el microprocesador y los dems componentes.
(Address Enable) Valida la direccin almacenada en A0-A19. Esto permite inhibir la
AEN: CPU y los dems dispositivos, pudiendo el DMA tomar el control. Los perifricos
deben decodificar la direccin comprobando que AEN est en estado bajo.
(I/O Channel Ready) Esta lnea se pone momentneamente en estado bajo por los
I/O CH perifricos lentos (no durante ms de 10 ciclos de reloj) cuando detectan una
RDY: direccin vlida en una operacin de E/S, con objeto de poder sincronizarse con la
CPU, que genera estados de espera.
(I/O Channel Check) Indica si se ha producido un error de paridad en la memoria o en
I/O CH CK:
los dispositivos E/S.
En los AT, las lneas adicionales completan fundamentalmente la nueva longitud de los buses
de datos y direcciones, permitiendo acceder tambin al resto del nuevo hardware:
Nuevas lneas de peticin/reconocimiento de DMA para los canales 5, 6 y 7, as
DRQ y
como el 0 (realmente el 4) que en los XT no estaba disponible al ser empleado por el
DACK:
refresco de memoria.
Nuevos niveles de interrupcin: 10, 11, 12, 13, 14 y 15. IRQ8 es interna a la placa
IRQ:
base y no est presente en el slot; IRQ9 se utiliza para emular IRQ2.
I/O CS 16: Indica un acceso de 16 bits en los puertos E/S.
MEM CS
Indica un acceso de 16 bits en la memoria.
16:
D8-D15: Parte alta del bus de datos.
A17-A23: Parte alta del bus de direcciones.
Apndice VIII - FUNCIONES DEL SISTEMA, LA BIOS
Y EL DOS ALUDIDAS EN ESTE LIBRO
Lgicamente, las funciones del DOS y la BIOS podran llenar varios libros de mayor
tamao que ste. Por ello, se listarn exclusivamente las funciones que se utilizan en los
programas ejemplo y en las explicaciones. Toda la informacin ha sido obtenida del
INTERRUPT.LST, en su mayora de la versin 39 del mismo (ver bibliografa), en este libro se
recoge menos de un 8% de las lneas de dicho fichero. Todas las funciones recogidas en el
INTERRUPT tienen el siguiente formato:
--------V-1000-------------------------------
INT 10 - VIDEO - SET VIDEO MODE
AH = 00h
AL = mode (see below)
Return: AL = video mode flag (Phoenix BIOS)
20h mode > 7
30h modes 0-5 and 7
3Fh mode 6
AL = CRT controller mode byte (Phoenix 386 BIOS v1.10)
Al principio de la funcin, en la lnea de guiones, suele haber uno, dos o tres n meros
hexadecimales de 8 bits (pegados unos a otros) que indican, por orden de aparicin: nmero de la
interrupcin, valor de llamada en AH, valor de llamada en AL. En el ejemplo superior se trata de la
INT 10h, a la que hay que llamar con AH=0. Si fueran necesarios m s valores en otros registros
normalmente se indicar de manera explcita en la cabecera. Esta cabecera es til, ya que un
fichero de varios megas no es operativo consultarlo con TYPE (y muchos editores de texto no
pueden cargarlo): lo normal es emplear una de esas pequeas utilidades para ver ficheros de texto,
que permiten moverse arriba y abajo con las teclas de los cursores (como README.COM que
acompaa a los compiladores de Borland): esos programas suelen tener opciones de bsqueda de
texto; de esta manera, buscando la cadena "-210A" se podra encontrar rpidamente la funcin
0Ah del DOS (INT 21h).
--------!---
FLAGS------------------------------------------------
-
The use of -> instead of = signifies that the indicated register or
register
pair contains a pointer to the specified item, rather than the item
itself.
One or more letters may follow the interrupt number; they have the
following
meanings: U - undocumented function, u - partially documented function,
P - available only in protected mode, R - available only in real or V86
mode,
C - callout or callback (usually hooked rather than called),
O - obsolete (no longer present in current versions)
--------!---
CATEGORIES-------------------------------------------
-
The ninth column of the divider line preceding an entry usually contains a
classification code (the entry has not been classified if that character
is
a dash). The codes currently in use are:
A - applications, a - access software (screen readers, etc),
B - BIOS, b - vendor-specific BIOS extensions,
C - CPU-generated, c - caches/spoolers,
D - DOS kernel, d - disk I/O enhancements,
E - DOS extenders, e - electronic mail, F - FAX,
f - file manipulation, G - debuggers/debugging tools,
H - hardware, h - vendor-specific hardware,
I - IBM workstation/terminal emulators, i - system info/monitoring
J - Japanese, j - joke programs,
K - keyboard enhancers, k - file compression,
l - shells/command interpreters,
M - mouse/pointing device, m - memory management,
N - network, n - non-traditional input devices,
O - other operating systems,
P - printer enhancements, p - power management,
Q - DESQview/TopView and Quarterdeck programs,
R - remote control/file access, r - runtime support,
S - serial I/O, s - sound/speech,
T - DOS-based task switchers/multitaskers, t - TSR libraries
U - resident utilities, u - emulators,
V - video, v - virus/antivirus,
W - MS Windows, X - expansion bus BIOSes,
y - security, * - reserved (and not otherwise classified)
--------C-
00---------------------------------------------------
---
INT 00 - CPU-generated - DIVIDE ERROR
Desc: generated if the divisor of a DIV or IDIV instruction is zero or
the
quotient overflows the result register; DX and AX will be
unchanged.
Notes: on an 8086/8088, the return address points to the following
instruction
on an 80286+, the return address points to the divide instruction
an 8086/8088 will generate this interrupt if the result of a
division
is 80h (byte) or 8000h (word)
SeeAlso: INT 04
--------C-
01---------------------------------------------------
---
INT 01 - CPU-generated - SINGLE STEP
Desc: generated after each instruction if TF (trap flag) is set; TF is
cleared on invoking the single-step interrupt handler
Notes: interrupts are prioritized such that external interrupts are
invoked
after the INT 01 pushes CS:IP/FLAGS and clears TF, but before
the
first instruction of the handler executes
used by debuggers for single-instruction execution tracing, such
as
MS-DOS DEBUG's T command
SeeAlso: INT 03
--------H-
02---------------------------------------------------
---
INT 02 - external hardware - NON-MASKABLE INTERRUPT
Desc: generated by the CPU when the input to the NMI pin is asserted
Notes: return address points to start of interrupted instruction on
80286+
on the 80286+, further NMIs are disabled until the next IRET
instruction, but one additional NMI is remembered by the
hardware
and will be serviced after the IRET instruction reenables NMIs
maskable interrupts may interrupt the NMI handler if interrupts
are
enabled
although the Intel documentation states that this interrupt is
typically used for power-failure procedures, it has many other
uses
on IBM-compatible machines:
Memory parity error: all except Jr, CONV, and some
machines
without memory parity
Breakout switch on hardware debuggers
Coprocessor interrupt: all except Jr and CONV
Keyboard interrupt: Jr, CONV
I/O channel check: CONV, PS50+
Disk-controller power-on request: CONV
System suspend: CONV
Real-time clock: CONV
System watch-dog timer, time-out interrupt: PS50+
DMA timer time-out interrupt: PS50+
Low battery: HP 95LX
Module pulled: HP 95LX
--------C-
03---------------------------------------------------
---
INT 03 - CPU-generated - BREAKPOINT
Desc: generated by the one-byte breakpoint instruction (opcode CCh)
Notes: used by debuggers to implement breakpoints, such as MS-DOS DEBUG's
G
command
also used by Turbo Pascal versions 1,2,3 when {$U+} specified
return address points to byte following the breakpoint instruction
SeeAlso: INT 01
--------C-
04---------------------------------------------------
---
INT 04 - CPU-generated - INTO DETECTED OVERFLOW
Desc: the INTO instruction will generate this interrupt if OF (Overflow
Flag)
is set; otherwise, INTO is effectively a NOP
Note: may be used for convenient overflow testing (to prevent errors
from
propagating) instead of JO or a JNO/JMP combination
SeeAlso: INT 00
--------B-
05---------------------------------------------------
---
INT 05 - PRINT SCREEN
Desc: dump the current text screen to the first printer
Notes: normally invoked by the INT 09 handler when PrtSc key is pressed,
but
may be invoked directly by applications
byte at 0050h:0000h contains status used by default handler
00h not active
01h PrtSc in progress
FFh last PrtSc encountered error
default handler is at F000h:FF54h in IBM PC and 100%-compatible
BIOSes
SeeAlso: INT 10/AH=12h/BL=20h
--------C-
05---------------------------------------------------
---
INT 05 - CPU-generated (80186+) - BOUND RANGE
EXCEEDED
Desc: generated by BOUND instruction when the value to be tested is less
than
the indicated lower bound or greater than the indicated upper
bound.
Note: returning from this interrupt re-executes the failing BOUND
instruction
--------C-
06---------------------------------------------------
---
INT 06 - CPU-generated (80286+) - INVALID OPCODE
Desc: this interrupt is generated when the CPU attempts to execute an
invalid opcode (most protected-mode instructions are considered
invalid in real mode) or a BOUND, LDS, LES, or LIDT instruction
which specifies a register rather than a memory address
Notes: return address points to beginning of invalid instruction
with proper programming, this interrupt may be used to emulate
instructions which do not exist; many 386 BIOSes emulate the
80286
undocumented LOADALL instruction which was removed from the
80386+
generated by the 80386+ when the LOCK prefix is used with
instructions
other than BTS, BTR, BTC, XCHG, XADD (486), CMPXCHG (486), INC,
DEC,
NOT, NEG, ADD, ADC, SUB, SBB, AND, OR, or XOR, or any
instruction
not accessing memory.
SeeAlso: INT 0C"CPU",INT 0D"CPU"
--------C-
07---------------------------------------------------
---
INT 07 - CPU-generated (80286+) - PROCESSOR EXTENSION
NOT AVAILABLE
Desc: this interrupt is automatically called if a coprocessor
instruction is
encountered when no coprocessor is installed
Note: can be used to emulate a numeric coprocessor in software
SeeAlso: INT 09"MATH UNIT PROTECTION"
--------H-
08---------------------------------------------------
---
INT 08 - IRQ0 - SYSTEM TIMER
Desc: generated 18.2 times per second by channel 0 of the 8254 system
timer,
this interrupt is used to keep the time-of-day clock updated
Notes: programs which need to be invoked regularly should use INT 1C
unless
they need to reprogram the timer while still keeping the time-
of-day
clock running at the proper rate
default handler is at F000h:FEA5h in IBM PC and 100%-compatible
BIOSes
may be masked by setting bit 0 on I/O port 21h
SeeAlso: INT 1C,INT 4A,INT 50"DESQview",INT 58"DoubleDOS",INT 70,INT
78"GO32"
SeeAlso: INT D8"Screen Thief"
--------C-
08---------------------------------------------------
---
INT 08 - CPU-generated (80286+) - DOUBLE EXCEPTION
DETECTED
Desc: called when multiple exceptions occur on one instruction, or an
exception occurs in an exception handler
Notes: called in protected mode if an interrupt above the defined limit
of
the interrupt vector table occurs
return address points at beginning of instruction with errors or
the
beginning of the instruction which was about to execute when the
external interrupt caused the exception
if an exception occurs in the double fault handler, the CPU goes
into
SHUTDOWN mode (which circuitry in the PC/AT converts to a
reset);
this "triple fault" is a faster way of returning to real mode on
many 80286 machines than the standard keyboard controller reset
--------H-
09---------------------------------------------------
---
INT 09 - IRQ1 - KEYBOARD DATA READY
Desc: this interrupt is generated when data is received from the
keyboard.
This is normally a scan code (from either a keypress *or* a key
release), but may also be an ACK or NAK of a command on AT-class
keyboards.
Notes: this IRQ may be masked by setting bit 1 on I/O port 21h
if the BIOS supports an enhanced (101/102-key) keyboard, it calls
INT 15/AH=4Fh after reading the scan code from the keyboard and
before further processing; all further processing uses the scan
code returned from INT 15/AH=4Fh
the default interrupt handler is at F000h:E987h in 100%-compatible
BIOSes
the interrupt handler performs the following actions for certain
special keystrokes:
Ctrl-Break clear keyboard buffer, place word 0000h in
buffer,
invoke INT 1B, and set flag at 0040h:0071h
SysRq invoke INT 15/AH=85h
Ctrl-Numlock place system in a tight wait loop until next INT
09
Ctrl-Alt-Del jump to BIOS startup code (either F000h:FFF0h or
the
destination of the jump at that address)
Shift-PrtSc invoke INT 05
DRDOS hooks this interrupt to control the cursor shape
(underscore/
half block) for overwrite/insert mode
DR Multiuser DOS hooks this interrupt for cursor shape control and
to
control whether Ctrl-Alt-Del reboots the current session or the
entire system
SeeAlso: INT 05,INT 0B"HP 95LX",INT 15/AH=4Fh,INT 15/AH=85h,INT 16,INT 1B
SeeAlso: INT 2F/AX=A901h,INT 51"DESQview",INT 59"DoubleDOS",INT 79"GO32"
(Table 0067)
Values for VESA SuperVGA memory model type:
00h text
01h CGA graphics
02h HGC graphics
03h 16-color (EGA) graphics
04h packed pixel graphics
05h "sequ 256" (non-chain 4) graphics
06h direct color (HiColor, 24-bit color)
07h YUV (luminance-chrominance, also called YIQ)
08h-0Fh reserved for VESA
10h-FFh OEM memory models
--------V-104F02-----------------------------
INT 10 - VESA SuperVGA BIOS - SET SuperVGA VIDEO MODE
AX = 4F02h
BX = mode
bit 15 set means don't clear video memory
Return: AL = 4Fh function supported
AH = status
00h successful
01h failed
SeeAlso: AX=4E03h,AX=4F01h,AX=4F03h
(Table 0068)
Values for VESA video mode:
00h-FFh OEM video modes (see #0009 at AH=00h)
100h 640x400x256
101h 640x480x256
102h 800x600x16
103h 800x600x256
104h 1024x768x16
105h 1024x768x256
106h 1280x1024x16
107h 1280x1024x256
108h 80x60 text
109h 132x25 text
10Ah 132x43 text
10Bh 132x50 text
10Ch 132x60 text
---VBE v1.2---
10Dh 320x200x32K
10Eh 320x200x64K
10Fh 320x200x16M
110h 640x480x32K
111h 640x480x64K
112h 640x480x16M
113h 800x600x32K
114h 800x600x64K
115h 800x600x16M
116h 1024x768x32K
117h 1024x768x64K
118h 1024x768x16M
119h 1280x1024x32K
11Ah 1280x1024x64K
11Bh 1280x1024x16M
Index: video modes
(Table 0069)
Values for S3 OEM video mode:
201h 640x480x256
202h 800x600x16
203h 800x600x256
204h 1024x768x16
205h 1024x768x256
206h 1280x960x16
208h 1280x1024x16
211h 640x480x64K (Diamond Stealth 24)
212h 640x480x16M (Diamond Stealth 24)
301h 640x480x32K
Note: these modes are only available on video cards using S3's VESA
driver
Index: video modes
--------V-104F03-----------------------------
INT 10 - VESA SuperVGA BIOS - GET CURRENT VIDEO MODE
AX = 4F03h
Return: AL = 4Fh function supported
AH = status
00h successful
BX = video mode (see #0068,#0069)
01h failed
SeeAlso: AH=0Fh,AX=4E04h,AX=4F02h
--------V-104F04-----------------------------
INT 10 - VESA SuperVGA BIOS - SAVE/RESTORE SuperVGA
VIDEO STATE
AX = 4F04h
DL = subfunction
00h get state buffer size
Return: BX = number of 64-byte blocks needed
01h save video states
ES:BX -> buffer
02h restore video states
ES:BX -> buffer
CX = states to save/restore (see #0070)
Return: AL = 4Fh function supported
AH = status
00h successful
01h failed
Format of DOS 3.1-3.3x, DR-DOS 5.0-6.0 system file tables and FCB tables:
Offset Size Description
00h DWORD pointer to next file table (offset FFFFh if last)
04h WORD number of files in this table
06h 35h bytes per file
Offset Size Description
00h WORD number of file handles referring to this file
02h WORD file open mode (see AH=3Dh)
bit 15 set if this file opened via FCB
04h BYTE file attribute (see AX=4301h)
05h WORD device info word (see AX=4400h)
bit 15 set if remote file
bit 14 set means do not set file date/time on
closing
bit 12 set means don't inherit on EXEC
bits 5-0 drive number for disk files
07h DWORD pointer to device driver header if character
device
else pointer to DOS Drive Parameter Block (see
AH=32h)
0Bh WORD starting cluster of file
0Dh WORD file time in packed format (see AX=5700h)
not used for character devices in DR-DOS
0Fh WORD file date in packed format (see AX=5700h)
not used for character devices in DR-DOS
11h DWORD file size
---system file table---
15h DWORD current offset in file (may be larger than size of
file; INT 21/AH=42h does not check new position)
---FCB table---
15h WORD counter for last I/O to FCB
17h WORD counter for last open of FCB
(these are separate to determine the times of the
latest I/O and open)
---
19h WORD relative cluster within file of last cluster
accessed
1Bh WORD absolute cluster number of last cluster accessed
0000h if file never read or written???
1Dh WORD number of sector containing directory entry
1Fh BYTE number of dir entry within sector (byte offset/32)
20h 11 BYTEs filename in FCB format (no path/period, blank-
padded)
2Bh DWORD (SHARE.EXE) pointer to previous SFT sharing same
file
2Fh WORD (SHARE.EXE) network machine number which opened
file
(Windows Enhanced mode DOSMGR uses the virtual
machine
ID as the machine number; see INT 2F/AX=1683h)
31h WORD PSP segment of file's owner (see AH=26h) (first
three
entries for AUX/CON/PRN contain segment of IO.SYS
startup code)
33h WORD offset within SHARE.EXE code segment of
sharing record (see above) 0000h = none
Format of DOS 4.01 (from UR 25066 Corrctive Services Disk on) disk buffer
info:
Offset Size Description
00h DWORD pointer to array of disk buffer hash chain heads (see
below)
04h WORD number of disk buffer hash chains (referred to as NDBCH
below)
06h DWORD pointer to lookahead buffer, zero if not present
0Ah WORD number of lookahead sectors, else zero (the y in
BUFFERS=x,y)
0Ch BYTE 01h, possibly to distinguish from pre-UR 25066 format
0Dh WORD ??? EMS segment for BUFFERS (only with /XD)
0Fh WORD ??? EMS physical page number of EMS seg above (only
with /XD)
11h WORD ??? EMS segment for ??? (only with /XD)
13h WORD ??? EMS physical page number of above (only with /XD)
15h BYTE ??? number of EMS page frames present (only with /XD)
16h WORD segment of one-sector workspace buffer allocated in main
memory
if BUFFERS/XS or /XD options in effect, possibly to
avoid DMA
into EMS
18h WORD EMS handle for buffers, zero if not in EMS
1Ah WORD EMS physical page number used for buffers (usually 255)
1Ch WORD ??? appears always to be 0001h
1Eh WORD segment of EMS physical page frame
20h WORD ??? appears always to be zero
22h BYTE 00h if /XS, 01h if /XD, FFh if BUFFERS not in EMS
Format of DOS 4.x disk buffer hash chain head (array, one entry per
chain):
Offset Size Description
00h WORD EMS logical page number in which chain is resident, -1 if
not
in EMS
02h DWORD pointer to least recently used buffer header. All buffers
on
this chain are in the same segment.
06h BYTE number of dirty buffers on this chain
07h BYTE reserved (00h)
Notes: buffered disk sectors are assigned to chain N where N is the
sector's
address modulo NDBCH, 0 <= N <= NDBCH-1
each chain resides completely within one EMS page
this structure is in main memory even if buffers are in EMS
--------m-
2F4300-----------------------------------------------
---
INT 2F - EXTENDED MEMORY SPECIFICATION (XMS) v2+ -
INSTALLATION
CHECK
AX = 4300h
Return: AL = 80h XMS driver installed
AL <> 80h no driver
Notes: XMS gives access to extended memory and noncontiguous/nonEMS
memory
above 640K
this installation check DOES NOT follow the format used by other
software
SeeAlso: AX=4310h
Index: installation check;XMS version 2+
--------m-
2F4310-----------------------------------------------
---
INT 2F - EXTENDED MEMORY SPECIFICATION (XMS) v2+ -
GET DRIVER
ADDRESS
AX = 4310h
Return: ES:BX -> driver entry point
Note: HIMEM.SYS v2.77 chains to previous handler if AH is not 00h or 10h
SeeAlso: AX=4300h
Perform a FAR call to the driver entry point with AH set to the function
code
AH function
00h Get XMS version number
Return: AX = XMS version (in BCD, AH=major, AL=minor)
BX = internal revision number
DX = 0001h if HMA (1M to 1M + 64K) exists
0000h if HMA does not exist
01h Request High Memory Area (1M to 1M + 64K)
DX = memory in bytes (for TSR or device drivers)
FFFFh if application program
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,90h,91h,92h) (see
below)
02h Release High Memory Area
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,90h,93h) (see below)
03h Global enable A20, for using the HMA
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,82h) (see below)
04h Global disable A20
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,82h,94h) (see below)
05h Local enable A20, for direct access to extended memory
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,82h) (see below)
06h Local disable A20
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,82h,94h) (see below)
07h Query A20 state
Return: AX = 0001h enabled
= 0000h disabled
BL = error code (00h,80h,81h) (see below)
08h Query free extended memory, not counting HMA
BL = 00h (some implementations leave BL unchanged on success)
Return: AX = size of largest extended memory block in KB
DX = total extended memory in KB
BL = error code (00h,80h,81h,A0h) (see below)
09h Allocate extended memory block
DX = Kbytes needed
Return: AX = 0001h success
DX = handle for memory block
= 0000h failure
BL = error code (80h,81h,A0h) (see below)
0Ah Free extended memory block
DX = handle of block to free
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,A2h,ABh) (see below)
0Bh Move extended memory block
DS:SI -> EMM structure (see below)
Note: if either handle is 0000h, the corresponding offset is
considered to be an absolute segment:offset address in
directly addressable memory
Return: AX = 0001h success
= 0000h failure
BL = error code (80h-82h,A3h-A9h) (see below)
0Ch Lock extended memory block
DX = handle of block to lock
Return: AX = 0001h success
DX:BX = 32-bit linear address of locked block
= 0000h failure
BL = error code (80h,81h,A2h,ACh,ADh) (see
below)
Note: MS Windows 3.x rejects this function for handles
allocated
after Windows started
0Dh Unlock extended memory block
DX = handle of block to unlock
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,A2h,AAh) (see below)
0Eh Get handle information
DX = handle for which to get info
Return: AX = 0001h success
BH = block's lock count
BL = number of free handles left
DX = block size in KB
= 0000h failure
BL = error code (80h,81h,A2h) (see below)
BUG: MS Windows 3.10 acts as though unallocated handles are
in use
Note: MS Windows 3.00 has problems with this call
0Fh Reallocate extended memory block
DX = handle of block
BX = new size of block in KB
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,81h,A0h-A2h,ABh) (see
below)
10h Request upper memory block (nonEMS memory above 640K)
DX = size of block in paragraphs
Return: AX = 0001h success
BX = segment address of UMB
DX = actual size of block
= 0000h failure
BL = error code (80h,B0h,B1h) (see below)
DX = largest available block
11h Release upper memory block
DX = segment address of UMB to release
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,B2h) (see below)
12h (XMS v3.0) Reallocate upper memory block
DX = segment address of UMB to resize
BX = new size of block in paragraphs
Return: AX = 0001h success
= 0000h failure
BL = error code (80h,B0h,B2h) (see below)
DX = maximum available size (RM386)
34h (QEMM 5.11 only, undocumented) ???
44h (QEMM 5.11 only, undocumented) ???
80h (Netroom RM386 v6.00) Reallocate upper memory block
this function is identical to function 12h
81h (Netroom RM386 v6.00) re-enable HMA allocation
Return: AX = 0001h (success)
82h (Netroom RM386 v6.00) Cloaking API
DX = XMS handle of block containing protected-mode code
CL = code size (00h 16-bit, else 32-bit)
ESI, EDI = parameters to pass to protected-mode code
Return: AX = status
0001h success
0000h failed
BL = error code (A2h,B0h) (see below)
Note: this calls offset 0 in the XMS memory block with
EBX = physical address of block's start
CS = code selector for XMS block at EBX (16-bit or 32-bit)
DS = data selector for XMS block, starting at EBX
ES = selector for V86 memory access to full real-mode
1088K
GS = selector for full flat address space
ESI, EDI from V86 mode
83h (Netroom RM386 v6.00) Create new UMB entry
BX = segment of high-memory block
DX = first page of start of block
CX = number of consecutive pages in block
DI = start of UMB in block
Return: AX = 0001h (success)
DI = segment of first high-DOS block
Note: the new UMB is not linked into the high-memory chain
84h (Netroom RM386 v6.00) Get all XMS handles info
CX = size of buffer for handle info
ES:DI -> buffer for handle info (see below)
Return: AX = 0001h (success)
DX = current number of allocated XMS handles
88h (XMS v3.0) Query free extended memory
Return: EAX = largest block of extended memory, in KB
BL = status
00h success
80h not implemented (i.e. on a 286 system)
81h VDISK detected
A0h all extended memory allocated
ECX = physical address of highest byte of memory
(valid even on error codes 81h and A0h)
EDX = total Kbytes of extended memory (0 if status
A0h)
89h (XMS v3.0) Allocate any extended memory
EDX = Kbytes needed
Return: AX = 0001h success
DX = handle for allocated block (free with
AH=0Ah)
= 0000h failure
BL = status (80h,81h,A0h,A1h,A2h) (see below)
8Eh (XMS v3.0) Get extended EMB handle information
DX = handle
Return: AX = 0001h success
BH = block's lock count
CX = number of free handles left
EDX = block size in KB
= 0000h failure
BL = status (80h,81h,A2h) (see below)
BUG: DOS 6.0 HIMEM.SYS leaves CX unchanged
8Fh (XMS v3.0) Reallocate any extended memory block
DX = unlocked handle
EBX = new size in KB
Return: AX = 0001h success
= 0000h failure
BL = status (80h,81h,A0h-A2h,ABh) (see below)
Notes: HIMEM.SYS requires at least 256 bytes free stack space
the XMS driver need not implement functions 10h through 12h to be
considered compliant with the standard
BUG: HIMEM v3.03-3.07 crash on an 80286 machine if any of the 8Xh
functions
are called
Ralf Brown.
Ralf Brown publica peridicamente un fichero (en ingls) con informacin muy detallada
sobre interrupciones (INTERRUP.LST), muy superior a la de cualquier libro. Contiene todas las
funciones de la BIOS, con informacin de mquinas y marcas concretas, as como de casi todas
las tarjetas (por ejemplo, de vdeo) del mercado. Tambin estn todas las interrupciones y
funciones del DOS, tanto las documentadas como las secretas o indocumentadas. Aqu se pueden
encontrar las funciones (va llamada a interrupciones) de los controladores de memoria expandida
y extendida, del ratn, de las extensiones CD-ROM, de Desqview, de Windows,... en resumen: de
casi todos los programas importantes del mercado. Adems, se trata de un fichero de dominio
pblico. Peridicamente es actualizado con la informacin que altruistamente le envan
personas de todo el mundo. La versin 55 (mediados de 1997) ocupa unos 5 Mbytes, tras
descomprimir y juntar los diversos ficheros en que viene repartido. Se puede conseguir en Internet y
en las principales BBS.
Michael Tischer.
PC Interno. Programacin de sistema.
Editorial Marcombo - Data Becker, 1993. 1404 pginas + disco.
Este gigantesco libro rene en una sola obra un ingente volumen de informaci n til,
relacionada con la programacin de sistemas de los PC. La primera parte constituye una especie
de introduccin a la programacin de sistemas (100 pginas). La segunda parte (600 pginas)
describe los grficos mejor que muchos otros libros especializados en la materia, explica el
teclado, los disquetes y discos duros, los puertos paralelo y serie, la programaci n del rat n y el
joystick, el reloj de tiempo real, las memorias EMS y XMS, la creaci n de sonido, la detecci n
del tipo de microprocesador... La tercera parte (250 pginas) comenta la estructura del sistema
operativo DOS, las formatos COM y EXE, la gestin de archivos, la gestin de memoria, los
controladores de dispositivo,... La cuarta parte (100 pginas) trata de la creacin de programas
residentes, del acceso al modo protegido, los extensores del DOS, DMPI, VPCI,... Por ltimo, la
quinta parte consta de 14 apndices (del A al N) con un resumen de las principales funciones de la
BIOS, el DOS, EMS, XMS, ratn... (300 pginas).
Este libro resulta imprescindible para el programador, al reunir en una sola obra un elevado
volumen de informacin. Tiene puntos dbiles, como las escasas 5 pginas que dedica al puerto
serie; en otros aspectos, como en materia de discos, la informacin es bastante buena sin ser muy
profunda (no toca para nada la programacin directa de la controladora de disquetes); lo mismo
sucede en temas como la creacin de programas residentes (informacin correcta pero muy, muy
justa). Sin embargo, en otras reas, como en grficos, arrasa en comparacin con otros muchos
libros del mercado que se dedican solo a este tema. El libro viene con cientos de listados de
programas de ejemplo en C, Pascal, QuickBasic y ensamblador (con letra muy peque a a dos
columnas) que ejemplifican la aplicacin de lo que se explica y estn incluidos en el disquete que
acompaa -lo que es una garanta de que funcionan-.
Aunque pudiera parecer que PC Interno es el rival de la obra que tiene el lector entre sus
manos, no es realmente as: PC Interno abarca muchas ms reas de programacin y a todos
los niveles, si bien en el acceso directo a los chips se queda muy escaso, aspecto que aqu es el
ms relevante. Pero hay que tener en cuenta que no se pueden tratar las cosas con tanta
profundidad cuando son tantos los temas que se abarcan, ni siquiera con 1400 pginas.
Michael Tischer.
PC Interno 2.0. Programacin de sistema.
Editorial Marcombo - Data Becker, 1996.
Versin ms reciente del libro anterior, sustituye el disquete por un CD conteniendo todo el
libro (incluso con ms captulos que los impresos). Ms completo y actualizado.
Andrew Schulman, Raymond J. Michels, Jim Kyle, Tim Paterson, David Maxey y Ralf
Brown.
Undocumented DOS.
Editorial Addison-Wesley, USA. 694 pginas + 2 discos.
Este libro contiene casi todas las funciones indocumentadas del sistema operativo, esas que
utilizan los mejores programas comerciales para dejarnos sorprendidos. Indispensable para el
programador avanzado que sepa ensamblador y/o C e ingls. Viene con dos discos de 1,2 Mb. Al
principio se centra en la gestin de recursos del DOS, explicando a fondo la gestin de memoria
del sistema al ms bajo nivel y la gestin de procesos y dispositivos. A continuaci n trata en
profundidad la gestin del sistema de ficheros, exponiendo una informacin valiossima.
Despus hay casi 100 pginas destinadas a explicar la creacin de programas residentes que
utilicen servicios del DOS, explicando detalladamente todos los pasos y la solucin de los
problemas. Finalmente, estudia el intrprete de comandos del DOS y aspectos relacionados con la
creacin de depuradores de cdigo. Contiene un apndice donde se listan slo las funciones
indocumentadas del DOS (extrado del INTERRUP.LST). La edicin que comento es de Junio
de 1991 y, aunque en portada indica lo contrario, no es cierta la afirmaci n de que cubre el DOS
5.0. Sin embargo, es un libro casi indispensable para los programadores de sistemas bajo DOS.
Andrew Schulman, Ralf Brown, David Maxey, Raymond J. Michels, Jim Kyle.
El DOS no documentado.
Editorial Addison-Wesley/Daz de Santos. - 1995. 1043 pginas.
Versin en castellano del libro anterior, en una edicin ms moderna que cubre tambin
aspectos relacionados con Windows, DR-DOS, Netware y MVDM de OS/2 y Windows NT.
A. Cattania.
80386: Arquitectura y programacin.
Grupo editorial Jackson. 300 pginas.
Este libro describe profundamente el procesador 80386, con un cap tulo especfico para el
diseo de sistemas hardware basados en el mismo. Al final, reproduce el conjunto de instrucciones
aunque no es la obra ms recomendable para consultar esta informacin.
Intel.
80386: Gua del programador de sistemas.
Anaya Multimedia - Intel. 175 pginas.
Libro oficial de Intel sobre el 386; describe los aspectos relacionados con su programacin a
nivel de sistemas, con un profundo tratamiento de la gestin de memoria, las tareas, las
interrupciones, las llamadas al sistema, la entrada/salida, el coprocesador, la compatibilidad con el
286 y 8086. Al final, culmina con un ejemplo de posible implementacin del sistema UNIX.
Carece por completo, sin embargo, de informacin acerca de las instrucciones del procesador.
80386: Guia del programador y manual de referencia.
Anaya Multimedia - Intel. 492 pginas.
Otro libro oficial relacionado con el 386 pero no especializado en la programacin de
sistemas sino en la programacin general. Describe en una 1 parte la programacin de
aplicaciones, mostrando la organizacin de la memoria y todo el conjunto de instrucciones del
386, una a una. En una 2 parte, aparecen captulos destinados a la programacin de sistemas,
con la gestin de memoria, la proteccin, la programacin multitarea, la entrada/salida, las
excepciones e interrupciones, el coprocesador y el modo de depuracin de programas del 386. En
la 3 parte del libro se trata el tema de la compatibilidad con procesadores anteriores, el modo
virtual-86 y la mezcla de cdigos de 16 y 32 bits. Al final, la 4 parte resume exhaustivamente
todo el juego de instrucciones, con los tiempos de ejecucin... Este libro es el complemento del
anterior.
Leo J. Scanlon.
80286: Programacin ensamblador en entorno MS-DOS.
Anaya Multimedia, S.A. - 1987. 368 pginas.
Aunque su ttulo parezca indicar lo contrario, se trata de un gran libro para aprender
ensamblador del 8086. Las instrucciones exclusivas del 286 (muy pocas y slo las del modo real)
se distinguen con claridad de las estndar del 8086. Realmente, no es un libro sobre el 286.
Empieza desde un nivel bsico y ensea progresivamente la sintaxis del lenguaje y el manejo del
programa ensamblador hasta unos niveles bastante aceptables. Es el libro que con ms sencillez,
claridad y profundidad describe las instrucciones del ensamblador. Tiene un captulo dedicado a la
aritmtica de 32 bits; otro al manejo de estructuras de datos; otro a los recursos del DOS (poco
profundo) y otro relacionado con las macros (muy superficial).
B. Kernigham / D. Ritchie.
El lenguaje de programacin C.
Ed. prentice-Hall.
Libro clsico sobre la programacin en C, escrito por los creadores originales del lenguaje.
No est actualizado, sin embargo, sobre las ltimas revisiones ANSI. Imprescindible para
cualquier programador en C. Juez inapelable sobre cmo deben hacerse las cosas en C.
B. Costales.
Introduccin al Lenguaje C.
Editorial Gustavo Gili, S.A. - 1987. 291 pginas.
El ttulo original de la obra (C from A to Z) es ms descriptivo de su contenido. Se trata de
un extraordinario libro para aprender C y llegar hasta un nivel de conocimientos respetable. Es un
libro fcil de seguir y contiene numerosas referencias sobre cmo han de ser los programas en C
para ser realmente portables entre distintos ordenadores. A mi juicio, mucho ms didctico y
til que el famoso Kernigham & Ritchie. Adems, su precio es asequible. Su punto d bil es no
estar especializado en los PC, para lo que harn falta ms libros de apoyo.
Richard Wilton.
Sistemas de Vdeo.
Editorial Anaya Multimedia, S.A., 1990. 568 pginas + disco.
Describe con profundidad todos los sistemas de vdeo estndar de los PC's, desde la
Hrcules a la VGA (pasando por la Incolor, HGC+, etc.); orientado al programador en C y/o
ensamblador. Trata la programacin directa del hardware de vdeo, los modos alfanumricos
(incluyendo los aspectos avanzados de EGA y VGA) y grficos, las tcnicas para trazar puntos,
lneas, circunferencias, rellenar superficies, etc.; se trata de una de las obras ms extensas sobre
el tema. Sin embargo, en programacin a bajo nivel cuesta encontrar a veces la informaci n -que
est bastante mal organizada- o no se encuentra (registros que se mencionan pero no se describen,
etc.). Digamos que es til en el tema de los algoritmos de trazado de lneas, crculos, rutinas
rpidas en ensamblador... pero se rinde ante PC Interno en todo lo dem s (aunque tambi n
puede echar una mano).
IBM corp.
IBM AT Technical Reference. - 1984. 600 pginas.
Libro oficial de IBM que describe la organizacin interna del AT, incluyendo el listado
fuente de la ROM-BIOS de la mquina. Resulta til para obtener esa informaci n que no se
puede encontrar en otros sitios; aunque sta es poco exhaustiva en cuanto a especificaciones
tcnicas de los integrados se refiere, conviene no olvidar que casi todos los dems libros sobre
programacin avanzada del PC se basan siempre de una u otra forma en ste. Publicado en
ingls y relativamente difcil de conseguir, como evidencia la fecha de la versin que comento.
Harris.
Digital Product Data Book.
Se trata de la lnea de datasheets tcnicos del conocido fabricante, editada en ingls.
Puede conseguirse en tiendas de electrnica de ciudades importantes, o directamente en Internet
(en el WEB de Harris). Harris fabrica chips CMOS compatibles con el 8088, 286, 8255, 8253/4,
8237, 8259 ... en resumen: casi todos los que podamos encontrar dentro de un PC, descritos a fondo
(por venir, vienen hasta las instrucciones del 286 y se explica el funcionamiento del modo protegido
del integrado). Aunque la obra est destinada en parte a los fabricantes de hardware, tal vez entre
los ms beneficiados de su lectura estn aquellos programadores a bajo nivel, tanto de
ensamblador como de C, que desean saber TODA la informacin acerca de un chip, sin errores y
sin olvidos; de manera clara, concisa y completa.