Está en la página 1de 372

EL UNIVERSO DIGITAL

DEL IBM PC, AT Y PS/2
Edición 4.0
(4ª edición)

Versión impresa del original electrónico ubicado en:

http://www.gui.uva.es/udigital

Limitación de garantía: Pese a que todos los programas e ideas incluidas en el libro han sido probados, el autor y el editor no se responsabilizan de los daños que su funcionamiento pueda ocasionar bajo ninguna circunstancia ni están obligados a corregir el contenido del libro.

Marcas registradas: IBM PCjr, PC, XT, AT, PS/2, OS/2 y Microchannel son marcas registradas de International Business Machines. MS-DOS, WINDOWS, Microsoft C y Microsoft Macro Assembler son marcas registradas de Microsoft Corporation. DR-DOS es marca registrada de Digital Research Inc. QEMM y Desqview son marcas registradas de Qarterdeck Corporation. UNIX es marca registrada de AT&T Bell Laboratories. Intel es marca registrada de Intel Corporation. Motorola es marca registrada de Motorola Inc. Turbo Assembler, Turbo C, Turbo Debugger y Borland C++ son marcas registradas de Borland International Inc.

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Ciriaco García de Celis Edición 4.0

Ediciones Grupo Universitario de Informática (Valladolid)

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2 - v4.0 Ciriaco García de Celis.
Grupo Universitario de Informática, 1992-1997.

Publica: Asociación Grupo Universitario de informática, 1992-1997. Apartado de correos 6062, Valladolid. Internet: http://www.gui.uva.es Autor: Ciriaco García de Celis (http://www.gui.uva.es/~ciri) Registro de propiedad Intelectual nº 1121; Madrid, 1993. Versión electrónica en Internet: http://www.gui.uva.es/udigital Imprimió, durante la etapa impresa: Servicio de Reprografía de la Universidad de Valladolid. Casa del Estudiante, avda. Real de Burgos s/n. [Actualmente no se edita impreso; absténganse de contactar con ellos]. Tirada, durante la etapa impresa: Más de 1200 ejemplares. Licencia de uso y distribución: Ver página 11.

ÍNDICE

5

ÍNDICE
1PRÓLOGO DE LA EDICIÓN 4.0 ELECTRÓNICA . . . . . . . . . . . . . . . . . . . PRÓLOGO DE LA TERCERA EDICIÓN (1994) . . . . . . . . . . . . . . . . . . . . INTRODUCCIÓN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 - Números binarios, octales y hexadecimales . . . . . . . . . . . . . . . . . 1.2 - Cambio de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 - Estructura elemental de la memoria . . . . . . . . . . . . . . . . . . . . . . 1.4 - Operaciones aritméticas sencillas en binario . . . . . . . . . . . . . . . . 1.5 - Complemento a dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6 - Agrupaciones de bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.7 - Representación de datos en memoria . . . . . . . . . . . . . . . . . . . . . 1.8 - Operaciones lógicas en binario . . . . . . . . . . . . . . . . . . . . . . . . . . ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES . . . . . . . 2.1 - Arquitectura Von Neuman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 - El microprocesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 - Breve historia del ordenador personal y el DOS . . . . . . . . . . . . . . MICROPROCESADORES 8086/88, 286, 386, 486 y Pentium . . . . . . . . . . 3.1 - Características generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 - Registros del 8086 y del 286 . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 - Registros del 386 y procesadores superiores . . . . . . . . . . . . . . . . 3.4 - Modos de direccionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5 - La pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6 - Un programa de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JUEGO DE INSTRUCCIONES 80x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 - Descripción completa de las instrucciones . . . . . . . . . . . . . . . . . . 4.1.1 - De carga de registros y direcciones . . . . . . . . . . . . . . . . 4.1.2 - De manipulación del registro de estado . . . . . . . . . . . . . 4.1.3 - De manejo de la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.4 - De transferencia de control . . . . . . . . . . . . . . . . . . . . . . 4.1.5 - De entrada/salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.6 - Aritméticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Suma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Multiplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . División . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conversiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.7 - Manipulación de cadenas . . . . . . . . . . . . . . . . . . . . . . . 4.1.8 - Operaciones lógicas a nivel de bit . . . . . . . . . . . . . . . . . 4.1.9 - De control del procesador . . . . . . . . . . . . . . . . . . . . . . . 4.1.10 - De rotación y desplazamiento . . . . . . . . . . . . . . . . . . . 4.2 - Resumen alfabético de las instrucciones y banderines. Índice. . . . 4.3 - Instrucciones específicas del 286, 386 y 486 en modo real . . . . . . 4.3.1 - Diferencias en el comportamiento global respecto al 8086 4.3.2 - Instrucciones específicas del 286 . . . . . . . . . . . . . . . . . . 4.3.3 - Instrucciones propias del 386 y 486 . . . . . . . . . . . . . . . . 4.3.4 - Detección de un sistema AT o superior . . . . . . . . . . . . . 4.3.5 - Evaluación exacta del microprocesador instalado . . . . . . 4.3.6 - Modo plano (flat) del 386 y superiores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 17 21 21 22 22 23 23 23 23 24 25 25 26 27 31 31 33 36 36 38 39 41 41 41 43 45 46 49 49 49 51 53 54 55 55 58 59 60 63 64 64 65 66 68 68 70

2-

3-

4-

6

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

5 - EL LENGUAJE ENSAMBLADOR DEL 80x86 . . . . . . . . . . . . . . . . . . . . 5.1 - Sintaxis de una línea en ensamblador . . . . . . . . . . . . . . . . . . . . 5.2 - Constantes y operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 - Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.2 - Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . 5.2.3 - Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.4 - Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . 5.2.5 - Operadores de retorno de valores . . . . . . . . . . . . . . . . 5.2.6 - Operadores de atributos . . . . . . . . . . . . . . . . . . . . . . . 5.3 - Principales directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 - De definición de datos . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 - De definición de símbolos . . . . . . . . . . . . . . . . . . . . . . 5.3.3 - De control del ensamblador . . . . . . . . . . . . . . . . . . . . . 5.3.4 - De definición de segmentos y procedimientos . . . . . . . 5.3.5 - De referencias externas . . . . . . . . . . . . . . . . . . . . . . . 5.3.6 - De definición de bloques . . . . . . . . . . . . . . . . . . . . . . . 5.3.7 - Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.8 - De listado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4 - Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 - Definición y borrado de las macros . . . . . . . . . . . . . . . 5.4.2 - Ejemplo de una macro sencilla . . . . . . . . . . . . . . . . . . 5.4.3 - Parámetros formales y parámetros actuales . . . . . . . . . 5.4.4 - Etiquetas dentro de macros. Variables locales. . . . . . . . 5.4.5 - Operadores de macros . . . . . . . . . . . . . . . . . . . . . . . . 5.4.6 - Directivas útiles para macros . . . . . . . . . . . . . . . . . . . . 5.4.7 - Macros avanzadas con número variable de parámetros 5.5 - Programación modular y paso de parámetros . . . . . . . . . . . . . . 6 - EL ENSAMBLADOR EN ENTORNO DOS . . . . . . . . . . . . . . . . . . . . . . . 6.1 - Tipos de programas ejecutables bajo DOS . . . . . . . . . . . . . . . . 6.2 - Ejemplo de programa de tipo COM . . . . . . . . . . . . . . . . . . . . . . 6.3 - Ejemplo de programa de tipo EXE . . . . . . . . . . . . . . . . . . . . . . 6.4 - Proceso de ensamblaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.5 - La utilidad DEBUG/SYMDEB . . . . . . . . . . . . . . . . . . . . . . . . . . 6.6 - Las funciones del DOS y de la BIOS . . . . . . . . . . . . . . . . . . . . . 7 - ARQUITECTURA DEL PC, AT y PS/2 BAJO DOS . . . . . . . . . . . . . . . . . 7.1 - Las interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 - La memoria. Los puertos de entrada y salida. . . . . . . . . . . . . . . 7.3 - La pantalla en modo texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4 - La pantalla en modo gráfico . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4.1 - Modos gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4.2 - Detección de la tarjeta gráfica instalada . . . . . . . . . . . . 7.4.3 - Introducción al estándar gráfico VGA . . . . . . . . . . . . . . 7.4.4 - Ejemplo de gráficos empleando la BIOS . . . . . . . . . . . 7.4.5 - Ejemplo de gráficos a nivel hardware . . . . . . . . . . . . . . 7.4.6 - El estándar gráfico VESA . . . . . . . . . . . . . . . . . . . . . . 7.5 - El teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.5.1 - Bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.5.2 - Nivel intermedio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.5.3 - Alto nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6 - Los discos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6.1 - Estructura física . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6.2 - Cabeza 0. Pista 0. Sector 1. . . . . . . . . . . . . . . . . . . . . 7.6.3 - La FAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6.4 - El directorio raíz . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71 71 72 72 72 73 73 73 73 75 75 75 76 76 78 78 80 80 81 81 82 82 83 84 85 87 88 91 91 91 92 94 96 99 103 103 105 105 106 106 108 108 114 115 116 119 119 122 125 125 125 126 127 129

ÍNDICE

7

7.6.5 - Los subdirectorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6.6 - El BPB y el DPB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6.7 - La BIOS y los disquetes . . . . . . . . . . . . . . . . . . . . . . . 7.6.8 - Disquetes floptical 3½ de 20 Mb . . . . . . . . . . . . . . . . . 7.6.9 - Ejemplo de acceso al disco a alto nivel . . . . . . . . . . . . 7.6.10 - Ejemplo de acceso al disco a bajo nivel . . . . . . . . . . . 7.7 - El PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.8 - El proceso de arranque del PC . . . . . . . . . . . . . . . . . . . . . . . . . 7.9 - Formato de las extensiones ROM . . . . . . . . . . . . . . . . . . . . . . . 7.10 - Formato físico de los ficheros EXE . . . . . . . . . . . . . . . . . . . . . 8 - LA GESTIÓN DE MEMORIA DEL DOS . . . . . . . . . . . . . . . . . . . . . . . . . 8.1 - Tipos de memoria en un PC . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2 - Bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.1 - El bloque de memoria del programa . . . . . . . . . . . . . . 8.2.2 - El bloque del entorno . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.3 - Los bloques de control de memoria (MCB’s) . . . . . . . . 8.2.4 - La cadena de los bloques de memoria . . . . . . . . . . . . . 8.2.5 - Relación entre bloque de programa y de entorno . . . . . 8.2.6 - Tipos de bloques de memoria . . . . . . . . . . . . . . . . . . . 8.2.7 - Liberar el espacio de entorno en programas residentes . 8.2.8 - Peculiaridades del MS-DOS 4.0 y 5.0 . . . . . . . . . . . . . 8.2.9 - Cómo recorrer los bloques de memoria. Ejemplo. . . . . . 8.3 - Memorias extendida y superior XMS . . . . . . . . . . . . . . . . . . . . . 8.4 - Memoria expandida EMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 - SUBPROCESOS, RECUBRIMIENTOS Y FILTROS . . . . . . . . . . . . . . . . . 9.1 - Llamada a subprocesos y recubrimientos u overlays . . . . . . . . . 9.2 - Construcción de filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 - PROGRAMAS RESIDENTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1 - Principios básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 - Un ejemplo sencillo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 - Localización de un programa residente . . . . . . . . . . . . . . . . . . 10.3.1 - Método de los vectores de interrupción . . . . . . . . . . . 10.3.2 - Método de la cadena de bloque de memoria . . . . . . . 10.3.3 - Método de la interrupción Multiplex . . . . . . . . . . . . . . 10.4 - Expulsión de un programa residente de la memoria . . . . . . . . . 10.5 - Gestión avanzada de la interrupción Multiplex . . . . . . . . . . . . . 10.5.1 - El convenio BMB Compuscience . . . . . . . . . . . . . . . . 10.5.2 - El convenio CiriSOFT . . . . . . . . . . . . . . . . . . . . . . . . 10.5.3 - La propuesta AMIS . . . . . . . . . . . . . . . . . . . . . . . . . . 10.5.4 - Comparación entre métodos . . . . . . . . . . . . . . . . . . . 10.6 - Métodos especiales para economizar memoria . . . . . . . . . . . . 10.7 - Programas autoinstalables en memoria superior . . . . . . . . . . . . 10.8 - Programas residentes en memoria extendida con DR-DOS 6.0 . 10.9 - Ejemplo de programa residente que utiliza la BIOS . . . . . . . . . 10.10 - Uso sin límites de servicios del DOS en programas residentes 10.10.1 - Una primera aproximación . . . . . . . . . . . . . . . . . . . . 10.10.2 - Pasos a realizar para usar el DOS . . . . . . . . . . . . . . 10.10.3 - Resumiendo, ¡no es tan difícil! . . . . . . . . . . . . . . . . . 10.10.4 - Un método alternativo: el SDA . . . . . . . . . . . . . . . . . 10.10.5 - Métodos menos ortodoxos . . . . . . . . . . . . . . . . . . . . 10.11 - Ejemplo de programa residente que utiliza el DOS . . . . . . . . . 10.12 - Programas residentes invocables en modos gráficos . . . . . . . 10.13 - Programas residentes en entorno WINDOWS 3 . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

130 131 131 132 132 133 137 139 139 140 143 143 145 145 145 146 146 147 147 148 148 149 152 153 157 157 159 161 161 162 163 163 163 164 164 165 165 165 170 172 172 173 174 176 184 185 186 187 188 189 189 197 199

. . . .3 . . . . . 12. . .7. . . . . . . . . . . . .6.4 . . . . . . . . . .Formato de la primera pista . . . . .3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .El 8237 en el ordenador . . . . .4. . . . . . 11. . .Descripción del FDC (Floppy Disk Controller) 765 . . . . . . . . . .7. . . . . . . 12. 11. . . . . . . . . . . . .6. . . . . . . . . . . . . . . . .5 . . .5 . . .6 .El controlador de disquetes NEC 765 . . . . . . . . . . . .Ensamblando TURBODSK . . . . . .5. .3 . . . . . . . . . . .7. . . . . . . . . . . . . .5 . . . . . . . . . . .7. .2 . . . . . . . . . . . . . . . . . . . .Temporización . . . . . . . . . . . . 11. . . . . . . .1 . . . . . . . . 12. .7. 12. . . 12. . . . 12. . . 12. . . 12. . . . . . . . . . . . .2. . . . . . . . . . .5. . . . . .2 . . . . . . . . . . . . . . . . . . .4. . . .6. . 12. . . . . . . . . .6. . . .Cómo y por qué de las interrupciones . . . 12. . . . . . . . . . . . . 12 . . . . . . . . . . . . .8 . .2 . 12. . . . . . . . .7. . . .6. . 12. . .1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Descripción del programa de formateo (2MF) para 2M . . . . . . . . . . . . . . . . . . . . .Introducción . . . . .3 . . . . . . . .Ralentizar un equipo AT con el DMA . . . . . . . . . . . . . . . . . . . 12. . . 203 203 203 205 205 210 212 214 214 216 216 244 245 245 247 247 248 248 249 249 255 256 258 261 261 261 267 269 270 270 270 279 281 283 284 284 286 294 294 297 305 309 311 315 316 330 338 340 341 341 342 345 346 346 . . . . . . .6.Acerca de las páginas de DMA . . .0 . . . . . . . . . .2 . . MFM. . .La opción BIOS de 2M: 2M-ABIOS y 2M-XBIOS . . .2 .6. . . .1 . .2 .El 8259 dentro del ordenador . .El disco duro del AT (IDE. . . .Ejemplo: cambio de la base de las interrupciones . . . . . . . . .El chip DMA 8237 . . . . . .3. . . . . .Disco virtual TURBODSK: Características .Puntualizaciones sobre el formato de máxima capacidad 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .EL HARDWARE DE APOYO AL MICROPROCESADOR . . . . . . . . .6 . .7. . . 12. . . . . .1 . . .5. . . .Un método para averiguar la configuración del PC/XT . . . . . Bus Local) . . . . . . . . . . . . . . . . . .6. .El 8255 en el PC . . . . 12. . .Programación avanzada del controlador de disquetes: 2M 3. . . . . . . .Ejemplo de controlador de dispositivo de caracteres . . . . .000.5. . . . . . . 11. . 12. . .Descripción del integrado . 12. . . . . .Los controladores de dispositivo y el DOS .Síntesis de sonido . . . . . . . . . 11.Descripción del integrado 8259 . . . . . . .Ordenes a soportar por el controlador de dispositivo . . . . . . . . . .El 8254 en el ordenador . . . . . . . . . . . . . .La tecnología de grabación en disco . . . . . . . . . 12. . . .4. . . . . . . . . . . . . . . . . . . . . . . .La cadena de controladores de dispositivo instalados . .2. . .5 . . . . . . . . . . 12. . . .8 EL UNIVERSO DIGITAL DEL IBM PC. . . . . . . 12. . . . .Uso de 2M 3. . .1 . . . . . . . . .8 . . .6 . . .1 . . . . . .El interface . .3 . . . . . . . . . . . . . . . . 12. . . . . . .2 . . . . . . . . . . . 11. . . . . 11. . . 11. . . .Lectura y escritura de sectores de disco sin DMA . . . 12. . . . . . . . .Descripción de funcionamiento del soporte residente .2 . . .Ejemplo de controlador de dispositivo de bloques .1 .4 . . . . . . . .7. . . . . . . . . . . . . . . . .7. . . . . . .6. . .Rutinas de estrategia e interrupción .Encabezamiento y palabra de atributos . . . . . . . . . .3 .6. . . . . . . . . . . . . . . . .Descripción del integrado . . . . . . . . .4 . .3 . . . . .La versión para PC/XT de 2M: 2MX . . . . . . . . . . . . . .1 . . . .7. . . . 12.El 765 dentro del ordenador .9 . . . . . . . . . . . . . . . . . AT Y PS/2 11 . . .7 . . . . . .3. . . . . . . 11. . . . . . . .El acceso directo a memoria . . .4 . . . . . . . .6. . . . . . . . . . . 12. . . . . . . . . . . . .4. . . . . . . . . . .6 . . . . . . . . . . . . . .0 en OS/2 2. . . . 12. . . . . . . . . . .2. .El interfaz de periféricos 8255 . . 12. .5. . .6. . 12. . . . . . . .El controlador de interrupciones 8259 . . . . . . . . 12. . . . . . . .4 . . .7 . . . . . . . . . . . . 12. .10 . . . . . . . . . . . . . 12. . . . . . . . .Un programa para medir el rendimiento de los disquetes 12. . . 12. . . . . . . . . .Descripción del integrado 8237 .La arquitectura del ordenador compatible . . . . . . . . . . . . . . . . . . . . . . . . . . .3 . . . . 12. . . . . . . . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . . . .6. . . . . . . . . . . . .000 de bytes en 3½: 2MGUI . . . .6.7. . . 12. . . . . . . . . . . . .6. . . . . .CONTROLADORES DE DISPOSITIVO . . . . . . .7. .Análisis detallado del listado de TURBODSK . . . 12. . . . . . . . . . .7 . . . . . . . .La utilidad 2MDOS . . . .1 . . . . . . . . 11. . . . . . . .3 . . . . . . . . . . . . . . . .7. . . . . . .6. . . .3.1 . . . .Cómo superar los 2. . . . . . . . . .3. . . . . . . . 12. . .El temporizador 8253 u 8254 .4 . . . . . . . . . . . .1 . . 11. . 12. .7. . . . . . . . 12. . .Densidades de disco y formatos estándar . . . . . 12. . . . . . .5 . .Acceso a disco con DMA . . . .4 . . . .7 . 12. . . . . . . . . .2 . . . . . . . . . . . .

. . . . . . . . . . . . . . .. . .Integración de módulos en ensamblador . . . . . . . 12. . . . .1 .El 8250 en el ordenador . . . . . . . . . . . . . . . . . .8 .1 . . . . .1. . . . 13. . . Tabla de interrupciones del sistema . . . . 12. . . . . . . . . . . . . . . . . Tabla de variables de la BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13. . . . . . .1 . . . .3 . . . .3 . . . . . . . Juego de caracteres ASCII extendido . . . . .El reloj de tiempo real del AT: Motorola MC146818 . .1. 12. . . . . . . 12. . . . . . . . . . . . . . .El MC146818 dentro del ordenador . . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . . . 13. . . . . . . . .Programación de la controladora . . . . . . . . . . . . . . . .El teclado del AT .1. . . . . . . . . . . .Cambio de vectores de interrupción . . . . . Señales del slot de expansión ISA . . . . . . .Envío de caracteres . . . . . . . 13. . . . . . . . . . . . . . . . .10.Interfaz C (Borland/Microsoft) . . . . . . . . . . . . . . . . . . . . . 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . . . 346 349 351 351 352 352 355 356 356 363 364 365 365 365 366 367 368 368 370 371 373 373 373 373 374 374 374 375 375 375 375 376 376 376 APÉNDICES: I II III IV V VI VII VIII IX X XI Mapa de memoria .Programas residentes . . . . . . . . . . . . . . . . . . Tamaños y tiempos de ejecución de las instrucciones . . . . . . . . . . . . . . . . .12. . . .11 . . . . . .ÍNDICE 9 12. . . . . Bibliografía . . . . . . 12. . . . . . . . . . . . . . . . . 13. .5 . . . . . . . .8. . . . .3 . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . . . . . . . .Comunicación teclado CPU . . . . . . . . . . . . . . . . . . . . . . . . 13. . . . . . . . 13. . . . . . . . . . . .1. . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . . . . . . . . . . . . .7 . . . . . . . .Ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . . . . . . . . . . .12. . . . .1 . . . . . . . . . . .El puerto serie: UART 8250 . . . . . . .Variables globales predefinidas interesantes . . . . . . . .2 . . . . . . . . . . . . . . . . . ..Las palabras clave interrupt y asm .9 . . . . . . Puertos de E/S . . . . . . . . . . . . . . . . .9. . . . . . . . . . . . . . 381 383 385 389 391 393 399 401 423 427 429 . . . . . . . . 12. . . . . . . . . . . . . . . .Modelos de memoria . . . . . . . . . . . .12 . . . . . . . . . . .1 . . .El ratón . 13. . . . . .1. 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Control de interrupciones . . . . . . . . .1 . . . . . . . . . . . . . . . . . . . . . . . . . 12. .Ejemplo: autodiagnóstico del 8250 . . . .1. . . . . . . . . . . .Cable NULL-MODEM para conectar dos ordenadores . . . . . . . 12. . . . . . .1 . . . . . . . . . . . . . . .Los registros del puerto paralelo . . . . . .10. . . . . . . . . . . . . . . . . . . .2 . . . . . . Códigos de rastreo del teclado . . . 12. . . .Un método para averiguar la configuración del AT y PS/2 13 . . .El controlador del teclado: 8042 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . .Descripción del integrado . . . . . . . . . .2 . . . 12. . . . . . . . . . 12. . . . . . . .4 . . . . . . . . .6 . . . . . . . . Funciones del sistema. . . .2 . . . . . . . . . . .Acceso a los puertos de E/S . . . . . . . 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12. . . . . . . . . .4 . . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . 13. . . . . 13. . . . . . . . . . . . . .8. . .9. . . . . . . .Llamada a interrupciones .2. .2 . . . . . . . . . . . .2 . . . . . . . . .10. . . . . . . . . . .El 8042 . .Ejemplo práctico de programación .. . . . . . . . . . . . . . . . . . . . . . . . la BIOS y el DOS aludidas en este libro Especificaciones XMS y EMS: Todas sus funciones . . . . . . .2 . .3 . . . . . . .Descripción del integrado . . . . . . . .Comunicación CPU teclado . . 12. . . . . . .Inserción de código en línea . . . . . . . .10 . . . . 12. . . . . . . .8. .8 . . . .El puerto de la impresora . 13.2. . . . .7. . . . . . . . . . .3 . . . . . . .Acceso a la memoria . . .Uso del Turbo C y Borland C a bajo nivel . . . .9 . . . . . .EL ENSAMBLADOR Y EL LENGUAJE C . . . . . .8. . . . . . .9. . . . . . . . . . . . . . . . . .1. . . . . . . . .2 . . . . . . . .3 . . . .

.

es/udigital).0 ELECTRÓNICA 11 PRÓLOGO DE LA EDICIÓN 4. con cualquier texto. lo que hasta ahora me resultaba algo más sencillo.es/udigital Nota: Pudiendo haber discrepancias entre sucesivas ediciones de estas normas. copia y distribución entre particulares. en especial a partir de este momento. difusión.uva. la versión de referencia válida e inapelable será la ubicada en todo momento en la red.0 (4ª edición) de El Universo Digital del IBM PC. Por supuesto. aunque lo recibiría con agrado. Quienes decidan utilizarlo deberán registrarse por vía electrónica una sola vez. La edición 4. los datos o direcciones indicadas por los usuarios nunca serán divulgados por mí.gui. a la siguiente dirección: Ciriaco García de Celis Apartado 6105 47080 Valladolid España Indicando claramente que el motivo es registrar el Universo Digital.uva. de libre uso. incluso si ha pasado bastante tiempo (pero si lo compraron por correo no deben registrarse: conservo su pedido). Los que hayan comprado la versión impresa en persona no necesitan registrarse. Me gustaría conocer en alguna medida la difusión de la obra. por razones de ética (http://www. Licencia de uso y distribución para particulares.gui.0 ELECTRÓNICA* (*) http://www. AT y PS/2 es un libro electrónico/impreso de dominio público. en la dirección electrónica arriba indicada o cualquier otra que pudiera sucederla. También es posible hacerlo enviando una carta o postal ordinaria (mejor en un sobre) al autor. . en cualquier soporte.PRÓLOGO DE LA EDICIÓN 4.

Sin embargo. texto. asociaciones y organizaciones. en el soporte impreso. Editando revistas (no libros) la distribución está permitida en cualquier formato digital (HTML.) en que se publique. electrónico u online de algún libro que vayan a publicar. rebasó ligeramente las 300 páginas. La primera edición de El Universo Digital.que abarcan un espectro mucho más amplio que el de la programación de los ordenadores. AT Y PS/2 Licencia de uso para empresas. Posteriormente se incrementaría aún algo más. Modificaciones. WordPerfect. PostScript. hasta las 420 de la 3ª edición que ha mantenido durante la mayor parte del tiempo. por cortesía. Orígenes de El Universo Digital. cuando su denominación era aún la de Curso de Ensamblador. . Siendo el formato una revista impresa sólo se permiten fragmentos que no totalicen más del 75% de la obra en los sucesivos números publicados. Tratándose de empresas editoriales u otras cualesquiera que planeen incluirlo. Licencia de distribución para empresas. Su objetivo inicial fue dotar de un manual de apoyo al Curso de Lenguaje Ensamblador. El Universo Digital no nació tras una decisión premeditada. que centraliza la generación de nuevas versiones actualizadas. Una buena parte de dicho material y del añadido después ha sido además de cosecha propia. con la excepción de que se recomienda un único registro electrónico o una sola carta o postal en representación de todos los posibles usuarios de la entidad. asociaciones y organizaciones. o cualesquiera otros) tanto en fragmentos como toda la obra completa. Mi única sugerencia es que la empresa me envíe una copia del soporte (CD. el autor siguió recopilando material interesante y añadiéndolo al curso. y buena bibliografía especializada.12 EL UNIVERSO DIGITAL DEL IBM PC. en el marco de unos Cursos de Introducción a la Informática -para los alumnos y personal en general de la Universidad. que ofrece todos los años la asociación Grupo Universitario de Informática de la Universidad de Valladolid. La distribución por empresas que cobren una cierta cantidad por el soporte es libre. Es necesario citar la procedencia. eliminación de contenidos o reemplazamiento de los mismos) es competencia exclusiva del autor. La primera versión ocupaba 116 páginas. Quien realizara alguna modificación sin consentimiento habría de destinar la obra resultante para uso personal e intransferible. etc. editada no mucho tiempo después del manual del curso. entero o por fragmentos. en una época en la que era difícil encontrar información. Se aplican exactamente las mismas condiciones que para usuarios particulares. deberían contactar primero conmigo para negociar una nueva versión (que en todo caso no implicaría la desaparición de ésta en su estatus actual). La realización de cambios (añadidos.

como es el DOS (que deja todo el control de la máquina a cada tarea). como ya se venía haciendo. Las ventajas de una edición oficial sería su no engorrosa distribución (uno de los motivos por los que siempre ha costado poco es porque nuestra Asociación y el propio autor ha puesto su mano de obra gratis). para los que va destinado este libro. solapamiento en contenidos con «obras publicadas o en fase de publicación». Se trata . Algo había que hacer. Una de ellas aún no ha respondido. Puesto en contacto con cuatro prestigiosas editoriales. y mucho menos al nivel que desarrolla este libro. y ello pese a que incluso Windows 95 corre aún en alguna parte sobre DOS. así como su mayor difusión. máxime si lo hace una editorial importante (si no. Por no mencionar las aplicaciones especializadas. los controladores para dar soporte a los dispositivos en los diversos sistemas operativos. evidentemente. aunque hace dos o tres años sí me lo planteé un poco en serio. Actualmente. pues la distribución gratuita del libro llevaba mucho tiempo. no es sólo para los programadores de alto nivel. El futuro de la programación. nuestra Asociación tendría que comprar en la librería los ejemplares para nuestros cursos). como ejemplo práctico de arquitectura interna de un ordenador.0 ELECTRÓNICA 13 El DOS en la actualidad. desde máquinas industriales al microprocesador de las sondas espaciales (que. Los inconvenientes de su publicación por una editorial serían el importante aumento de precio. las que han respondido han valorado muy positivamente la obra. Mi decisión final ya la había acariciado con anterioridad. ha sido la considerable cantidad de pedidos que hemos recibido desde países de hispanoamérica. la programación en DOS ya no es importante. El Universo Digital en Internet. comportamiento que irá reduciéndose hasta la eliminación en próximas versiones. Mis contactos con editoriales. Uno de los motivos que han terminado empujándome a esta decisión. la ventaja de la publicación para facilitar la difusión popular es obvia. la publicidad la harían los lectores lentamente. no corre bajo Windows). y desde hace algún tiempo. Mi objetivo inicial no fue publicarlo. Aunque algunos contenidos muy relacionados con el DOS siguen presentes en esta obra. Sin embargo. los propios usuarios pueden y podrán cada vez en mayor medida hacer sus propios programas incluso sin darse cuenta. Es para los programadores de sistemas. en la época que vivimos. y sobre todo.PRÓLOGO DE LA EDICIÓN 4. el lector habrá de tener en cuenta si es pertinente profundizar en ellos o no. sin embargo. y la distribución sería incluso más limitada pese al recurso a los baratos servicios de reprografía por parte de los usuarios). o simplemente «falta de interés comercial»). sin embargo la han rechazado aduciendo otros motivos («sobrecarga del programa editorial». En alguna manera. Que podrán practicar en un entorno cómodo para este tipo de programación. y para aquellos que necesitan o quieren saber cómo funciona el PC por dentro. siempre hay alguien que tiene que construir los sistemas operativos. Sin embargo. no aparecería en todas las estanterías. y mi renuncia a los derechos de distribución (en particular.

Pido desde aquí disculpas a todos los que lo han solicitado desde fuera de España. mayores además si no he contestado el E-Mail por no haber tomado aún una decisión al respecto. Fin de la distribución impresa. y la mayoría eran de una sola unidad (pese a que se penalizaba su envío con 100 pts adicionales). la práctica imposibilidad de comerciar a pequeña escala con esos países (no existe el envío contrarreembolso.). ya no estoy obligado a venderlo impreso (medida tomada únicamente para mantener el copyright). Cada envío llevaba una media de 20 minutos de tiempo total de mano de obra. finalmente habría que añadir incluso mi temor inconsciente a un aumento incontrolado de la demanda. me permiten tomar esa decisión sin temer el enfado de quienes lo habían comprado. aunque ese inconveniente disminuirá exponencialmente con el tiempo (con el mismo exponente con que crezca la red).14 EL UNIVERSO DIGITAL DEL IBM PC. donándolo al dominio público. No se harán envíos a otras organizaciones. disquete. El coste de impresión de los últimos números en la reprografía oficial de la Universidad (rechazamos opciones más baratas de menor calidad). sobre todo desde México. desgraciadamente. Aunque en general no se harán más envíos. que además podrán seguir usando la versión manuscrita utilizando una impresora. El inconveniente es que no todos tienen igual acceso a estas redes y medios. sólo en la primera ocasión lo he enviado (a Perú). pero beneficio a otros muchos. Renuncio con ello a facilitar su difusión a los lectores menos introducidos en las redes telemáticas. contabilizando la preparación de los libros (transporte físico. las enormes demoras del envío por superficie (el coste del envío aéreo supera el del propio libro) y las complicadas gestiones de pago e injustas comisiones bancarias (aunque las pague el usuario final). Sin embargo. Obtención de ejemplares impresos. AT Y PS/2 de ciudadanos que conocen el índice del libro a través del Web y lo piden. en los ejemplares que no llegaban a su destino. Realmente.. el coste del envío y la devolución lo pagábamos nosotros. lo que perjudicaría a un amplio conjunto potencial de usuarios. Subrayamos que El Universo Digital impreso tiene el carácter legal de apuntes técnicos impresos y no de libro. El libro (realmente. haber facturado sólo aproximadamente el coste de impresión y distribución. apuntes técnicos fotocopiados) se vendía a 2100 pts más gastos de envío. no tenemos tiempo ni medios para atender la demanda actual: aunque es una medida dura de imponer. Naturalmente. la única excepción corresponderá a los pedidos realizados desde bibliotecas (universitarias o no universitarias). ni a librerías o a particulares. podrá ser accedido desde cualquier lugar del mundo. cuando ya había 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). que tal vez no tengan la impresora adecuada o tiempo para reproducirlo. lamento renunciar a realizar más envíos de ejemplares impresos. por ejemplo). encuadernación y disquete era de 1900 pts. los motivos son. Por otro lado. una vez que he renunciado a mis derechos sobre el libro. El Universo Digital de dominio público en formato electrónico. y en cualquier CD de los kioscos. ya que por ejemplo. gestión del pedido. Ese margen de beneficios era más bien de maniobra. .. El precio de los más de 1200 Universos Digitales vendidos ha tenido un crecimiento nominal cero en los cinco años de difusión impresa.

por su colaboración a todos los niveles. además de debidamente firmada por quien corresponda. en general. o los de sospechosa procedencia. y estos son muy expresivos en ocasiones al llegar al destino. Además del nombre completo. Por otro lado. 47080 Valladolid.PRÓLOGO DE LA EDICIÓN 4. porque también volverán mañana). En puntualidad. Agradecimientos. Tampoco es comprensible que sólo Argentaria sea aún la única entidad financiera con el privilegio de gestionar las denominadas Cuentas Corrientes Postales. sumando al final el coste exacto del envío y los disquetes. La dirección es: Grupo Universitario de Informática. cuando hay un 2% de inflación nacional). Nos reservamos el derecho de rechazar aquellos pedidos que no cumplan alguno de estos requisitos.0 ELECTRÓNICA 15 Los pedidos de ejemplares impresos serán admitidos sólo desde España. aunque por debajo. Ciriaco García de Celis Valladolid. Habrán de realizarse exclusivamente por carta impresa. El trato que reciben los clientes no se diferencia mucho del de los paquetes. no tiene la obligación de tratar bien a sus clientes. Mis agradecimientos también a las diversas instituciones de la Universidad de Valladolid. no siendo ellos los encargados de su distribución. que han recibido en ocasión la presión de la demanda a través de incorrectas llamadas telefónicas solicitando el libro. y algunas normas de la empresa (como el plomo adherido a los paquetes postales) no se han simplificado desde finales del siglo XIX. Apartado 6062. Es conveniente que figure el teléfono de la biblioteca o en su defecto de la conserjería del centro. la cantidad de papeles que hay que rellenar en cada envío. Además de que el servicio de correos es caro en la realidad (esto es. El precio por ejemplar será el que figure en la factura que realizará el propio servicio de reprografía (unas 2000 pts/unidad). a los que tardan quince en ir de Valladolid a Madrid) el tiempo promedio podría aproximarse. también al Grupo Universitario de Informática. No puedo decir lo mismo de los funcionarios de Correos: aunque algunos son amables. Noviembre de 1997 . el esmero puesto durante tanto tiempo en la reproducción y encuadernación de cada número durante la etapa impresa. 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 dirección incorrecta) es próxima al 100%: los envíos no suelen perderse. el funcionamiento de esa institución es el que cabía esperar de un monopolio no sometido a la libre competencia en envíos postales ordinarios (y que. ubicado en la Casa del Estudiante. Agradezco desde aquí al servicio de Reprografía de la Universidad. a lo que afirma la empresa. cuando se incluye lo que pagamos en impuestos para cubrir las pérdidas de la compañía) se mantiene el viejo vicio de indexar las tarifas anuales (aumento del 8% en 1997. por tanto. dirección y NIF. por buena que sea. al menos los de los reembolsos. aunque hay extremos de gran aleatoriedad (desde paquetes que llegan en tres días a un pueblo perdido en la otra punta del país. Sin embargo. Cualquier pequeño problema de calidad se ha debido siempre a los fallos inevitables que en ocasiones presenta toda máquina. que deberá estar compulsada por el sello y en su caso papel oficial de la biblioteca que hace el pedido.

.

Para aquellas personas que necesitan comprender el funcionamiento de un ordenador. Respecto a la primera edición. íntimo aliado del ensamblador en la programación de sistemas. También se describe la sintaxis del lenguaje ensamblador. para las que es conveniente un conocimiento elevado del funcionamiento interno del ordenador en general y del MS-DOS en particular. al menos un dominio básico del sistema operativo MS-DOS. siendo más que recomendable conocer algún lenguaje de programación. aunque este último aspecto está extensamente documentado. pese a que la programación continúa alejándose cada vez más del bajo nivel de las máquinas.PRÓLOGO DE LA TERCERA EDICIÓN (1994) 17 PRÓLOGO DE LA TERCERA EDICIÓN (1994) Ha pasado un año desde la publicación de la primera edición de esta obra. ha continuado la expansión de los interfaces gráficos de usuario y los sistemas operativos avanzados para PC. aunque dejando de lado algunas instrucciones del 286 y 386 que se salen del entorno MS-DOS. Sin embargo. Aunque sería conveniente describir el lenguaje C. Este libro pretende cubrir una importante laguna en la bibliografía disponible actualmente sobre la programación a nivel de sistemas de los ordenadores compatibles. Los ordenadores compatibles poseen numerosas aplicaciones en el campo industrial. sobre todo. corrigiéndose además algunos errores. se han incrementado los contenidos en una proporción equivalente al 20% de lo que ya existía. creando los suyos propios. sin embargo. se presupone que el lector tiene unos mínimos conocimientos de informática. ello se deja por razones de espacio para otras publicaciones. Aunque el libro comience con una introducción a la aritmética binaria que pueda indicar todo lo contrario. las máquinas compatibles constituyen una interesante oportunidad y punto de partida. . los programadores de sistemas en el entorno del PC siguen existiendo y son muchos más que los que trabajan para las empresas punteras en el desarrollo de los sistemas operativos. Desde entonces. Seguidamente se explica el lenguaje ensamblador de la serie 80x86 de Intel separando claramente las instrucciones de los diversos procesadores. los lectores que no conozcan el lenguaje ensamblador de ningún microprocesador habrán de trabajar considerablemente leyendo multitud de listados hasta adquirir la soltura necesaria y.

LST. por lo que se insta al lector a conseguir el impresionante fichero de dominio público INTERRUPT. sin embargo. AT Y PS/2 El libro describe con profundidad la arquitectura de los ordenadores compatibles. para permitir al programador de sistemas un control completo del equipo. como Windows. el controlador de DMA 8237 (para acceso a disco). Se dan pistas sobre la manera de conmutar los modos de vídeo sin alterar el contenido de la pantalla. Las memorias extendida XMS y expandida EMS son descritas con cierto detenimiento. después integrándolos en el ordenador y documentando profusamente su uso. Los ejemplos en este capítulo experimentan una importante potenciación respecto a la edición anterior. ya que por sí solo necesitaría de un buen libro más grueso que este. en lo relacionado con el controlador de disquetes se puede considerar que la información vertida es prácticamente casi toda la existente. con ejemplos probados. dada su presencia en todos los ordenadores modernos y su importancia. Existe un capítulo muy próximo al hardware en el que se describen a fondo y sin omisiones todos los chips del ordenador. Existen apéndices que describen todas las funciones del DOS. de manera especial en lo referente a la organización interna de la memoria (actualizada hasta el MS-DOS 6. el controlador de interrupciones 8259. no están ni muchísimo menos todas las interrupciones necesarias. a las técnicas más avanzadas para economizar memoria. Se consideran el interfaz de periféricos 8255 (útil para averiguar la configuración de los PC/XT). en particular el ejemplo de disco virtual: un completo ejemplo de controlador redimensionable que soporta memoria convencional.0). Sin embargo. así como la totalidad de las funciones XMS y EMS. complemento ideal de este libro (ver bibliografía). el controlador de disquetes 765 (acceso directo a los sectores). de la BIOS y del sistema usadas en las rutinas y programas desarrollados. XMS y EMS. el UART 8250 (empleado en las comunicaciones serie) y el reloj de tiempo real MC146818 (configuración de AT y programación de alarmas y temporizaciones). bien la de nuevas unidades de disco añadidas a las del sistema. El apartado de los gráficos se repasa sólo superficialmente. el temporizador 8253/8254 (para temporización y síntesis de sonido). no relacionados con el PC sino como tales circuitos. desde los dos posibles enfoques de su uso: bien sea la creación de controladores de dispositivo de caracteres. Para asimilar este capítulo hace falta cierta formación previa en los sistemas digitales. pasando por el uso de funciones del DOS de manera concurrente al programa principal. la controladora de disco duro de los AT (IDE. Los chips de apoyo al microprocesador son descritos de manera total: primero. en ambos casos se incluyen ejemplos reales de controladores completos y comprobados. así como técnicas 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. existiendo pautas suficientes para que el . los discos y el teclado. en particular. el controlador del teclado del AT (8042). aspecto que resulta de especial interés para los programas residentes.18 EL UNIVERSO DIGITAL DEL IBM PC. Los programas residentes reciben un tratamiento especialmente profundo: desde los métodos más eficientes para que detecten su propia presencia en memoria. Este libro también trata los controladores de dispositivo o device drivers. MFM ó Bus Local). y la posibilidad de ser activados desde pantallas gráficas. los ejemplos que siguen a la información técnica aclaran las explicaciones previas y pueden ser aprovechados de manera inmediata incluso sin entender todo lo anterior.0 y el DR-DOS 6.

También se comenta en un capítulo el funcionamiento al más bajo nivel del ratón. Existen también capítulos que describen el funcionamiento y programación de la impresora. . formatos de alta capacidad. sin olvidos ni omisiones. sí se suministra información común a todas. se incluye un capítulo que describe la manera de comunicar el ensamblador con el lenguaje C. un 286. etc. protecciones de disco. Resumiendo. Dada la importancia del lenguaje C en la programación en general y en la programación de sistemas en particular. Este capítulo requiere un dominio elemental del lenguaje C por parte del lector. el libro pretende reunir en una sola obra la mayoría de la información necesaria para el programador de sistemas. pese a que todas las rutinas y programas han sido probados debidamente en un 8088. aspecto que habitualmente no suele ser considerado. En todo caso. Este afán de información completa es el responsable del título del libro. sin entrar en aspectos particulares relativos a los modelos de las diversas marcas. con objeto de superar las limitaciones de este lenguaje en los puntos críticos de la programación de sistemas. también se pretende explicar las técnicas más avanzadas de creación de programas residentes. aunque probablemente sólo sea útil para aquellos que lo conocen más o menos.el autor del libro no se responsabiliza de su correcto funcionamiento en todas las circunstancias. aunque en el caso de los programas completos debe citarse la procedencia y dejar bien claro en las versiones modificadas quién las ha alterado. Todos los listados de ejemplo se suponen de dominio público y las rutinas pueden ser incluidas por los lectores libremente en sus propios programas. tanto en la actualidad como durante los próximos años. exponiendo toda la información y no sólo lo imprescindible. un 386 o un 486 -bajo varios sistemas operativos y con diferentes configuraciones del hardware.PRÓLOGO DE LA TERCERA EDICIÓN (1994) 19 lector cree sus propios programas copiones.

.

Cada posición 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 Explícitamente. El sistema de numeración utilizado habitualmente es la base 10. En este último tenemos. utilizando el 0 y el 1.INTRODUCCIÓN 21 Capítulo I: INTRODUCCIÓN 1. es necesario conocer otros sistemas de numeración como pueden ser el octal (base 8) y el hexadecimal (base 16). por su importancia y utilidad. letras -normalmente en mayúsculas. Llegar a un número 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. además de los números del 0 al 9.q d h Base 2 8 10 16 Ejemplos 01101010b 175o 789d 6A5h En caso de que no aparezca el sufijo. se utilizarán una serie de sufijos para determinar el sistema de numeración empleado: Sufijo b o. En un ordenador el sistema de numeración es binario -en base 2. . es decir. . Análogamente a la base 10. ordenados de izquierda a derecha y de mayor a menor. consta de 10 dígitos (0-9) que podemos colocar en grupos. basta convertir cada dígito 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. el número se considera decimal.1.de la A a la F. es decir. se indica la base de numeración como 135710. en base 10.NUMEROS BINARIOS.hecho propiciado por ser precisamente dos los estados estables en los dispositivos digitales que componen una computadora. cada posición tiene un valor de 2n donde n es la posición contando desde la derecha y empezando por 0: 1012 = 1 x 22 + 0 x 21 + 1 x 20 Además. OCTALES Y HEXADECIMALES.

1. contracción de «binary digit» en inglés. .3.3. El paso de cualquier base a base 10 lo vimos antes: 6A5h = 6 x 162 + 10 x 161 + 5 x 160 Inversamente. . 8 9 A B C D E F Decimal 8 9 10 11 12 13 14 15 . Cualquiera de estas unidades de información se denomina BIT. . existe un sistema general para realizar el cambio de una base a otra.22 EL UNIVERSO DIGITAL DEL IBM PC.2.1.BYTE. Cada grupo de cuatro bits de un byte constituye un nibble. Los bits en un byte se numeran de derecha a izquierda y de 0 a 7.ESTRUCTURA ELEMENTAL DE LA MEMORIA. El nibble tiene gran utilidad debido a que cada uno almacena un dígito hexadecimal: Binario 0000 0001 0010 0011 0100 0101 0110 0111 Hex. AT Y PS/2 1. 1.3. Es la unidad de almacenamiento en memoria. .BIT. que representamos matemáticamente por 0 y 1. Pese a que las conversiones entre base 2 y base 8 y 16 son prácticamente directas.3. representar 256 estados (de 0 a 255) según la combinación de bits que tomemos. si queremos pasar de base 10 a cualquier otra habrá que realizar sucesivas divisiones por la base y tomar los restos: 1234 114 2 16 77 13 16 4 1234d = 4D2h donde 4 es el último cociente (menor que la base) y los restantes dígitos son los restos en orden inverso. correspondiendo con los exponentes de las potencias de 2 que reflejan el valor de cada posición.3. Toda la memoria del ordenador se compone de dispositivos electrónicos que pueden adoptar únicamente dos estados. Gigabytes (1 Gb = 1024 Mb).NIBBLE. por tanto. Un byte nos permite. Terabytes (1 Tb = 1024 Gb) o Petabytes (1 Pb = 1024 Tb). Cada grupo de 8 bits se conoce como byte u octeto.2. . la cual está constituida por un elevado número de posiciones que almacenan bytes. 1. en Megabytes (1 Mb = 1024 Kb). 0 1 2 3 4 5 6 7 Decimal 0 1 2 3 4 5 6 7 Binario 1000 1001 1010 1011 1100 1101 1110 1111 Hex. 1.CAMBIO DE BASE. 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). La cantidad de memoria de que dispone un sistema se mide en Kilobytes (1 Kb = 1024 bytes).

. cuyo complemento a dos es él mismo. salvo en el caso de 1 + 1 = 102 .AGRUPACIONES DE BYTES. positivo.709. se sigue el mismo proceso que en base 10: 1010 1010b + 0011 1100b 1110 0110b Podemos observar que la suma se desarrolla de la forma tradicional. es decir. que consiste en cambiar los unos por ceros y los ceros por unos en su notación binaria.6. si era negativo (bit más significativo activo) la parte que se añade por la izquierda son bits a 1. se considera negativo (-128) y el número 00h. Este fenómeno.OPERACIONES ARITMÉTICAS SENCILLAS EN BINARIO.551. el resultado es 0 (observar cómo el 1 más significativo subrayado es ignorado). Sin embargo. Kbytes 1. se define como valor negativo de un número el que necesitamos sumarlo para obtener 00h. .COMPLEMENTO A DOS. dos dígitos hexadecimales.5.7. Para sumar números. 1. En general.744. 1. Otro factor a considerar es cuando se pasa de operar con un número de cierto tamaño (ej.073.1. . a continuación se le suma una unidad para calcular el complemento a dos.295 18.. . el bit 7 se considera como de signo y. tanto en base 2 como hexadecimal. se puede resumir en que el bit más significativo se copia en todos los añadidos: es lo que se denomina la extensión del signo: los dos siguientes números son realmente el mismo número (el -310): 11012 (4 bits) y 111111012 (8 bits). Por esta razón. 8 bits) a otro mayor (pongamos de 16 bits). es decir: sumamos normalmente. en cuya demostración matemática no entraremos. Normalmente. Tipo Palabra Doble palabra Cuádruple palabra Párrafo Página Segmento 2 2 4 16 256 64 Definición bytes contiguos palabras contiguas (4 bytes) palabras contiguas (8 bytes) bytes bytes.294. la parte que se añade por la izquierda son bits a 0.REPRESENTACIÓN DE LOS DATOS EN MEMORIA.446. En general. en cuyo caso tenemos un acarreo de 1 (lo que nos llevamos). 1. Luego FFh=-1. por ejemplo: FFh + 01h 100h Como en un byte solo tenemos dos nibbles. etc.NUMEROS BINARIOS: máximo número representable: Tipo 1 2 4 8 byte bytes bytes bytes Sin signo 255 65. Si el número es positivo. .967. para hallar el complemento a dos de un número cualquiera basta con calcular primero su complemento a uno. la operación es más sencilla: el complemento a dos de un número A de n bits es 2n-A. 16 Kb. el número 80h.INTRODUCCIÓN 23 1.7.615 .4.535 4. si está activo (a 1) el número es negativo. Con una calculadora.

Esto permite trabajar con números de muy elevado tamaño -según el exponente. con símbolos de puntuación y algunos caracteres de control de la transmisión. 1.4.036.y con una mayor o menor precisión en función de los bits empleados para codificar la mantisa.S.I. 1.483. (American Standard Code for Information Interchange) es un convenio adoptado para asignar a cada carácter un valor numérico.775. . 45h. 1.7. 1. experimenta un considerable auge. Para ciertas nacionalidades se han diseñado otras páginas específicas que requieren de un software externo. su origen está en los comienzos de la Informática tomando como muestra algunos códigos de la transmisión de información de radioteletipo. La notación BCD ocupa cuatro bits -un nibble.372. Se realizan a nivel de bit y pueden ser de uno o dos operandos: x 0 1 NOT (x) 1 0 x 0 0 1 1 y 0 1 0 1 x AND y 0 0 0 1 x OR y 0 1 1 1 x XOR y 0 1 1 0 . .7.8. de modo equivalente a la notación científica. Con posterioridad. AT Y PS/2 Tipo 1 2 4 8 byte bytes bytes bytes Positivo 127 32. Se puede consultar al final de este libro. Consiste en emplear cuatro bits para codificar los dígitos del 0 al 9 (desperdiciando las seis combinaciones que van de la 1010 a la 1111). la ampliación del código ASCII realizada por esta marca a 8 bits.854.807 Negativo -128 -32.483.036.NUMEROS EN PUNTO FLOTANTE. . o empaquetados. 01h. de forma que en el formato desempaquetado el nibble superior siempre es 0. almacenando dos dígitos por byte (para construir los números que van del 00 al 99).854.NUMEROS BINARIOS CODIFICADOS EN DECIMAL (BCD). que resulta inmediata. La ventaja es la simplicidad de conversión a/de base 10. Son grupos de bytes en los que una parte se emplea para guardar las cifras del número (mantisa) y otra para indicar la posición del punto flotante (exponente).808 Los números binarios de más de un byte se almacenan en la memoria en los procesadores de Intel en orden inverso: 01234567h se almacenaría: 67h.648 -9. en cuyo caso cada byte contiene un dígito BCD (Binary-Coded Decimal).647 9. siendo en la actualidad muy utilizada y recibiendo la denominación oficial de página de códigos 437 (EEUU). .CÓDIGO ASCII.768 -2. Es habitualmente la única página soportada por las BIOS de los PC. con capacidad para 128 símbolos adicionales.372. Se trata de un código de 7 bits con capacidad para 128 símbolos que incluyen todos los caracteres alfanuméricos del inglés. Los números BCD pueden almacenarse desempaquetados.24 EL UNIVERSO DIGITAL DEL IBM PC.147.7.147. El código A.223.OPERACIONES LÓGICAS EN BINARIO. 23h. esta tabla cubre todas las necesidades del idioma.775. con la aparición de los microordenadores y la gran expansión entre ellos de los IBM-PC y compatibles.223.por cifra. En las lenguas del estado español y en las de la mayoría de los demás países de la UE.C.3.I.767 2.2.

C. más conocida por sus siglas en inglés (CPU). vamos a realizar primero un somero repaso a la arquitectura interna de un microordenador. . U. De este forma la velocidad de proceso experimenta un considerable incremento. si bien no es la primera en aparecer.. Von Newman propuso dos conceptos básicos que revolucionarían la incipiente informática: a) La utilización del sistema de numeración binario. que indudablemente sí influye sobre la elección del lenguaje de programación a utilizar en el desarrollo de una determinada rutina. .ARQUITECTURA VON NEWMAN. Simplificaba enormemente los problemas que la implementación electrónica de las operaciones y funciones lógicas planteaban.1. fácilmente accesible. y dada la aparición de nuevos compiladores de lenguajes de alto nivel que optimizan el código generado a niveles muy próximos a los que un buen programador es capaz de realizar en ensamblador. pero siempre hay una parte en la que el ensamblador se hace casi insustituible bajo DOS y es la programación de los drivers para los controladores de dispositivos. Claro es que está siendo desplazada por otra que permiten una mayor velocidad de proceso. La Memoria Interna.P. recordemos que anteriormente una instrucción o un dato estaban codificados en una ficha en el mejor de los casos. ME. a la vez proporcionaba una mayor inmunidad a los fallos (electrónica digital). no es la única razón para su utilización. Memoria masiva Externa. fundamentalmente las operaciones de entrada/salida en las que es preciso actuar directamente sobre los demás chips que acompañan al microprocesador. con sistemas de numeración decimal. E/S. sí que lo hizo prácticamente desde el comienzo de los ordenadores y se sigue desarrollando actualmente. Tomando como modelo las máquinas que aparecieron incorporando las anteriores características. Por ello y porque las instrucciones del lenguaje ensamblador están íntimamente ligadas a la máquina. la RISC. Unidad de Entrada y Salida. el ordenador se puede considerar compuesto por las siguientes partes: La Unidad Central de Proceso. especialmente C. una electrónica sumamente complicada muy susceptible a fallos y un sistema de programación cableado o mediante fichas. por la traducción directa de los mnemónicos a instrucciones maquina. b) Almacenamiento de la secuencia de instrucciones de que consta el programa en una memoria interna. 2. solucionando situaciones en las que los tiempos de ejecución constituye el factor principal para que el proceso discurra con la suficiente fluidez. Centrándonos en los ordenadores sobre los que vamos a trabajar desarrollaré a grandes rasgos la arquitectura Von Newman que. MI. Esta situación. permite realizar aplicaciones rápidas.ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES 25 Capítulo II: ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES El ensamblador es un lenguaje de programación que. junto con los datos que referencia. Es sobradamente conocido que los actuales sistemas operativos son programados en su mayor parte en lenguajes de alto nivel. En los primeros tiempos de los ordenadores. relacionados con las tareas de más bajo nivel de una máquina.

not.La Unidad Central de Proceso (CPU) viene a ser el cerebro del ordenador y tiene por misión efectuar las operaciones aritmético-lógicas y controlar las transferencias de información a realizar. Existe un registro especial llamado de indicadores. La forma de operar del ordenador en su conjunto es direccionar una posición de la memoria en busca de una instrucción mediante el bus de direcciones. Comenzaba así la gran carrera en busca de lo más rápido. . estas tres partes principales de que consta el ordenador deben estar íntimamente conectadas. Es también donde se almacenan temporalmente las variables del mismo. En la CPU la instrucción se decodifica. resta. Aunque cuando entremos en la descripción de los microprocesadores objeto de nuestro estudio lo ampliaremos. y a veces producto y división) y lógicas (and. AT Y PS/2 Realicemos a continuación una descripción de lo que se entiende por cada una de estas partes y cómo están relacionadas entre si: . . or.De direcciones: como vimos. marcando la secuencia de la transferencia el bus de control. todos los datos que se precisan y todos los resultados que devuelve. por ellas se realiza la transferencia de datos entre todos sus elementos. . interpretando qué operandos necesita: si son de memoria. es necesario llevarles a la CPU. etc. llevar la instrucción a la unidad central de proceso -CPUpor medio del bus de datos. Se distinguen tres tipos de bus: . haré un pequeño comentario de las partes del microprocesador: . aparece en este momento el concepto de bus: el bus es un conjunto de líneas que enlazan los distintos componentes del ordenador. una vez que la operación es realizada. . También direcciona los puertos de E/S. si es preciso se devuelve el resultado a la memoria. fluyen los datos entre las distintas partes del ordenador.Unidad aritmético-lógica: Es donde se efectúan las operaciones aritméticas (suma. rápidamente el mundo del ordenador empezó a ser accesible a pequeñas empresas e incluso a nivel doméstico: es el boom de los microordenadores personales.EL MICROPROCESADOR.De datos: por él. . más pequeño.Bloque de registros: Los registros son celdas de memoria en donde queda almacenado un dato temporalmente. Como es de suponer. proporcionando al operador una forma de introducir al ordenador tanto los programas como los datos y obtener los resultados.Decodificador de instrucciones: Allí se interpretan las instrucciones que van llegando y que componen el programa. Un salto importante en la evolución de los ordenadores lo introdujo el microprocesador: se trata de una unidad central de proceso contenida totalmente en un circuito integrado. . de forma bidireccional.Unidades de entrada y salida (E/S) o Input/Output (I/O): son las encargadas de la comunicación de la máquina con el exterior.2. . la memoria está dividida en pequeñas unidades de almacenamiento que contienen las instrucciones del programa y los datos.). El bus de direcciones consta de un conjunto de líneas que permite seleccionar de qué posición de la memoria se quiere leer su contenido. . 2. estado o flags. .26 EL UNIVERSO DIGITAL DEL IBM PC. que refleja el estado operativo del microprocesador.La Memoria Interna (MI) contiene el conjunto de instrucciones que ejecuta la CPU en el transcurso de un programa. también las que marcan la secuencia de los pasos a seguir para dicha transferencia.Bloque de control de buses internos y externos: supervisa todo el proceso de transferencias de información dentro del microprocesador y fuera de él.De control: forman parte de él las líneas que seleccionan desde dónde y hacia dónde va dirigida la información.

con mejores posibilidades gráficas y unos 64 Kb de memoria. basados aún en el 6502 y. como la pantalla vertical. El equipamiento de serie consistía en 16 Kbytes de . Durante finales de los 70 aparecieron muchos otros ordenadores. supuso el primer paso hacia el logro de un PC personal. El Apple-I apareció en 1976. Fue sucedido en 1977 por el Apple-II. Alguna innovación. el centro de investigación de Xerox en Palo Alto desarrolló un equipo informático con el aspecto externo de un PC personal actual. de poco sirvieron. mucho antes de que otros los reinventaran. . Estaban basados en un microprocesador sucesor del 8085 de Intel: el Z80 (desarrollado por la empresa Zilog. Por ello. Sucesores de este procesador fueron el 8008 y el 8080.ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES 27 2.BREVE HISTORIA DEL ORDENADOR PERSONAL Y EL DOS. de 8 bits. Y es que IBM también fabricó su propio ordenador personal con vocación profesional: el 12 de agosto de 1981 presentó el IBM PC. Se utilizaban cintas de casete como almacenamiento. en aquel entonces un recién aparecido aunque casi 10 veces más barato que el 8080 de Intel.3. Sir Clive Sinclair lanzó el ZX-80. la ULA. El desarrollo del primer microprocesador por Intel en 1971. con un chip propio para gestión de gráficos y otras tareas. haremos una pequeña introducción sobre la evolución de los ordenadores personales. No olvidemos los rudimentos de la época: el Apple-II tenía un límite máximo de 48 Kbytes de memoria. de 16 bits. aunque comenzaron a aparecer las unidades de disquete de 5¼. Sin embargo. fruto de la explosión inicial del microprocesador. como se dieron en llamar. Los intentos de rebasar este límite manteniendo aún esos chips por parte de la plataforma MSX (supuesto estándar mundial con la misma suerte que ha corrido el Esperanto) o los CPC de Amstrad. que permitió rebajar su coste y multiplicó su difusión por europa. la tecnología del momento no permitió alcanzar todas las intenciones. En 1973. disponía de un artefacto similar al ratón. aunque esta máquina no tenía teclado ni pantalla (sólo interruptores y luces). abarcando toda la historia (ya que no es muy larga). al reducir drásticamente la circuitería adicional necesaria. era una arquitectura abierta (conocida por todo el mundo) y cuyas tarjetas se conectaban a la placa principal a través de 100 terminales. posteriormente. Commodore irrumpió con sus VIC-20 y. Además de pantalla y teclado. El microprocesador. Algunos términos manejados en este libro podrían ser desconocidos para los lectores más jóvenes. En 1980. algunos de los conceptos universalmente aceptados hoy en día. todos los ordenadores domésticos de la época. el Commodore 64. El IBM PC. en general. Los micros de los 80. Commodore sacó su PET con 8 Kbytes. que más tarde terminarían convirtiéndose en el bus estándar S-100 de la industria. y los procesadores de textos no muestran legiblemente una hoja en vertical completa incluso en monitores de 20 pulgadas. La premonición. este último. el 4004 (de 4 bits). La trepidante evolución del mundo informático podría provocar que algún recién llegado a este libro no sepa exactamente qué diferencia a un ordenador "AT" del viejo "XT" inicial de IBM. cuyas instrucciones serán las que usemos en este libro. estaban basados en procesadores de 8 bits y tenían el límite de 64 Kb de memoria. Su competidor fue el ZX-Spectrum de Sinclair. Sin embargo. creada por un ex-ingeniero de Intel). seguido muy poco después del ZX-81. En el mismo año. y en particular por España. Ed Roberts construyó en 1975 el Altair 8800 basándose en el 8080. basado en el microprocesador de 8 bits 6502. también basado en el Z80. Estaba basado en el microprocesador 8088. de formato similar a una hoja de papel (que desearían algunos actuales internautas para los navegadores) aún no ha sido adoptada: nuestros PC’s siguen pareciendo televisores con teclas. este aparato (denominado Alto) introdujo. ya que todos los procesadores posteriores son básicamente (en MS-DOS) versiones mucho más rápidas del mismo.

que no permitían obtener todo el rendimiento. básicamente es una prolongación de las patillas de la CPU a las ranuras de expansión. con su bus PCI. En general. así como ranuras de expansión de 16 bits (el bus ISA de 16 bits) en contraposición con las de 8 bits del PC y el XT (bus ISA de 8 bits). sin embargo. La razón es que la propia IBM tenía que respetar la compatibilidad con lo anterior. [Por cierto. desarrollaron con la norma VESA las placas con bus local (VESA Local Bus). también de 32 bits pero compatible con la ISA anterior. Una manera sencilla de comprender la evolución de los PC es observar la evolución de las sucesivas versiones del DOS y los sistemas que le han sucedido. Finalmente. etc). desarrolló el MS-DOS. así que los fabricantes orientales. la jugada del PC-DOS/MS-DOS se repetiría en alguna manera pocos años después con el OS/2-Windows]. Un año más tarde aparecería el IBM PC-AT. Esta arquitectura de bus se popularizó mucho con los procesadores 486.1. que fue presentado en 1980.. En 1979. le gustara o no a IBM. EISA también era caro. La insolente respuesta de la competencia fue la arquitectura EISA. . Sin embargo.2 Mbytes. aunque si se hace distinción entre un PC y un AT en la misma frase. que traía como novedad un disco duro de 10 Mbytes. que con el Pentium se ha convertido finalmente en el único estándar de bus de 32 bits. IBM desarrolló demasiado tarde.3. en 1987. el desarrollo de los microprocesadores posteriores ha estado totalmente condicionado por el MS-DOS. eran caras) y la incluyó en su gama de ordenadores PS/2 (alguno de cuyos modelos era aún realmente ISA). es hoy en día mucho más general.28 EL UNIVERSO DIGITAL DEL IBM PC. El primer problema vino con la aparición de los procesadores 386: los demás fabricantes se adelantaron a IBM y lanzaron máquinas con ranuras de expansión aún de 16 bits. todos los equipos con procesador 286 o superior pueden catalogarse dentro de la categoría AT.. no obstante. se perdió la ventaja de la normalización (no hay dos tarjetas superiores a la VGA que funcionen igual). a partir de ahí sucedió un fenómeno similar y los demás fabricantes se adelantaron a finales de los 80 con mejores tarjetas y más baratas. además incorporaba un disco duro de 20 Mbytes y disquetes de 5¼ pero con 1. Estas máquinas aún admiten no obstante las viejas tarjetas ISA. con bus de 32 bits pero cerrada e incompatible con tarjetas anteriores (aunque se desarrollaron nuevas tarjetas. Incluso. cruzada ya la barrera de los años 90. por PC (a secas) se entiende cualquiera de ambos. menos potente. suficientes para algunas aplicaciones de baja velocidad (modems. el almacenamiento externo se hacía en cintas de casete. introduciendo el microprocesador 286. el término XT hace referencia al 8088/8086 y similares. Cuando aparecieron máquinas compatibles con el PC de IBM. Seatle Computer necesitaba apoyar de alguna manera a sus incipientes placas basadas en el 8086. IBM fue paulatinamente dejando de tener la batuta del mercado del PC. y en ese terreno no tenía más facilidades para innovar que la competencia. Alrededor del PC se estaba construyendo un imperio de software más importante que el propio hardware: estamos hablando del sistema operativo PC-DOS. AT Y PS/2 memoria ampliables a 64 en la placa base (y a 256 añadiendo tarjetas). desarrolló su propio sistema: el QDOS 0. El término PC ya digo. al final el estándar que se ha impuesto ha sido el propuesto por el propio fabricante de las CPU: Intel. aunque pronto aparecieron las unidades de disco de 5¼ pulgadas y simple cara (160/180 Kb por disco) o doble cara (320/360 Kb). tenían que respetar la compatibilidad con ese sistema. referenciando habitualmente a cualquier ordenador personal. Como Digital Research estaba tardando demasiado en convertir el CP/M-80 a CP/M-86. A partir de 1986. Antes de finales de año apareció QDOS 0. la arquitectura Microchannel. La evolución del MS-DOS. cuyo copyright era de IBM). lo que fue sencillo (ya que Microsoft. lo que permite tarjetas rápidas de 32 bits pero muy conflictivas entre sí. En 1983 apareció el IBM PC-XT. Otro ejemplo: si IBM gobernó los estándares gráficos hasta la VGA.. por PC se sobreentiende un XT. compatible con el PC-DOS pero que no requería la BIOS del ordenador original.

MS-DOS 3.).2: Soporte para disquetes de 720K (3½-DD).0 original.0 aunque en principio parecía tener menos futuro que el CP/M. En abril de 1981 aparecieron las primeras versiones de CP/M-86 de Digital. Controlador de memoria EMM386. el 86-DOS. subdirectorios. MS-DOS 3. MS-DOS 2. Las funciones del DOS (en INT 21h) sólo llegaban hasta la 1Fh (¡la 30h no estaba implementada!). Así que IBM optó por Bill Gates. que pasó a denominarse PC-DOS 1. hay una única excepción: la versión 7. MS-DOS 3. MS-DOS 2. mejoras en el sistema de ficheros (FAT.0. Soporte internacional: páginas de códigos.0 (no confundir MS-DOS 7.11: eliminación de errores. El MS-DOS 7. separación de los controladores de dispositivo del sistema.01: Corrige las erratas de la 4. MS-DOS 3. Microsoft adquiría todos los derechos del 86-DOS. En Julio. sin embargo.0. Julio de 1988.25.3 a finales de 1980.0: Añade soporte para disquetes de 1. dueño de Microsoft.0: Soporte para memoria superior. No sería necesaria una nueva versión del DOS para cada nuevo formato de disco si el controlador integrado para A:. Agosto de 1981. A continuación se expone la evolución hasta la versión 5. las versiones siguientes no añaden ninguna característica interna nueva destacable (aunque a nivel de interfaz con el usuario y utilidades incluidas haya más cambios). Noviembre de 1988. Las versiones de PC-DOS no dependientes de la ROM BIOS de IBM se denominarían MS-DOS. Bill Gates ya había hecho la primera demostración mundial de BASIC corriendo en un 8086 en las placas de Seatle Computer (en julio de 1979) y había firmado un contrato de distribución no exclusiva para el QDOS 0. que irrumpe en el mundo del DOS una década más tarde (con DR-DOS). MS-DOS 1. MS-DOS 2.2). Octubre de 1983. Aunque PC-DOS y MS-DOS siembre han caminado paralelos. Diciembre de 1985. .0: Soporte para discos duros de más de 32 Mb (cambio radical interno que forzó la reescritura de muchos programas de utilidad) hasta 2 Gb. B: y C: lo hubieran hecho flexible algún día.3: Soporte para disquetes de 1. Marzo de 1983. Permite particiones secundarias en los discos duros. Marzo de 1985..ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES 29 Bill Gates. que acababa de adquirir un sistema operativo. Abril de 1987. pero no es frecuente su uso aislado o independiente de Windows 95.0 ó 6. Digital Research no ocupa actualmente el lugar de Microsoft porque en 1981 era una compañía demasiado importante como para cerrar un acuerdo con IBM sin imponer sus condiciones para cederle los derechos del sistema operativo CP/M.44M (3½-HD).01: soporte de juegos de caracteres internacionales.2M y discos duros de 20 Mb. de momento sólo poseía una versión de lenguaje BASIC para 8086 no orientada a ningún sistema operativo particular. el equivalente al MS-DOS 5. a la vez que QDOS se renombraba a 86-DOS 1. obliga a Microsoft a incluir ayuda online y a ocuparse un poco más de los usuarios. que le gustó a algún directivo de IBM. realmente.0 sobre el que corre Windows 95 sí tiene bastantes retoques internos. Marzo de 1982. añadiendo soporte para disquetes de doble cara. Mayo de 1983..0 con PC-DOS 7. MS-DOS 5. MS-DOS 4. Agosto de 1984. Presentación del MS-DOS 1.0: este último es. Junio de 1991. La competencia de Digital Research.0. MS-DOS 4. término que ha terminado siendo más popular.1: Soporte para redes locales..0 introducido con el XT: reescritura del núcleo en C. Precipitada salida al mercado.

En ese caso.0 (cuando cedió los derechos a Novell) se pueden considerar prácticamente 100% compatibles. Caminamos hacia la integración de los diversos Windows en uno sólo. según el modo de funcionamiento y la versión): el MS-DOS no lo escribió inicialmente Microsoft. Con tanto analista en el paro. Naturalmente. no vamos por buen camino. no tengo nada contra Microsoft. Probablemente. La prueba evidente son los procesadores de Intel. es que ve mucho la tele. y salta a la vista que sus programadores. El DOS y el Windows actuales no son ningún invento maravilloso de Microsoft.0 de este sistema.0 y 6. que esperemos que algún día sea suficientemente abierto para que le surjan competidores.). Si se queda con la segunda opción. al forzar a Microsoft a mejorar la interacción del sistema operativo con los usuarios (documentación en línea.-) . pero Windows sí. ni de hardware ni de software. Si en el futuro hubiera un sólo sistema operativo soportado por Microsoft. Si hay alguien que puede competir con Windows es Unix. la insuficiente normalización actual la corregiría pronto el propio mercado.2 ha sido necesario intercambiar tres veces el disquete origen y el destino durante la copia de un disquete normal de 1. ciertos detalles. ¿O por el contrario es de los que piensan que Bill Gates es un genio?. y finalmente consiguió lanzar al mercado su sistema DR-DOS. En cierto modo.. ¿Tiene usted Linux instalado en casa y lo utiliza al menos para conectarse a Internet por Infovía.. para cometer semejante despiste.30 EL UNIVERSO DIGITAL DEL IBM PC. 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.44M.. 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. se sentaron delante del teclado antes de hacer un análisis de la aplicación a desarrollar. la prepotencia de Microsoft con el MS-DOS a principios de los noventa era similar a la de Digital Research a principios de los 80 con el CP/M. pero opino que el poder adquirido durante una década. Somos prisioneros. con objeto de que algún organismo de normalización internacional las recogiera sin ambigüedades para permitir la libre competencia de otros fabricantes. hasta el MS-DOS 6. programas de utilidad. o quizá le gustaría hacerlo algún día?. construidos desde hace tiempo para dar servicio al sistema operativo del PC. El futuro. a menos que no esté informado de la actualidad. Exactamente lo mismo le ha sucedido a las primeras versiones de Windows (hay varios chequeos distintos para detectarlas. Y en Unix no dependemos de ningún fabricante concreto.. aunque evidentemente tiene razón: y cuantos más como usted.. El resto de la historia de los sistemas operativos de PC ya la conoce el lector. Las versiones 5. o de un Windows cerrado íntimamente ligado al DOS (de quien sólo Microsoft tiene el código fuente) no legitima a ninguna empresa a tener tanto poder. el MS-DOS 1. por poner un ejemplo.. . así como el Novell DOS 7. sería de agradecer que algún juez les obligara a publicar una especificación completa de las funciones y protocolos del sistema. El efecto del DR-DOS fue positivo. más genio que será. Del mismo modo que Windows seguirá lento o colgándose mientras Unix no tenga más aplicaciones comerciales. gracias a la exclusiva de los derechos sobre un sistema operativo sin ayuda en la línea de comandos. usuarios obligados de Microsoft.0 carecía de función para identificar la versión del sistema. igual que lo hubiera hecho alguien que hubiera aprendido a programar con unos fascículos comprados en el kiosco. Por poner un ejemplo. AT Y PS/2 Digital Research trabajó arduamente para lograr una compatibilidad total con MS-DOS.

77 MHz en la versión 8086. incluso sin memoria caché externa es capaz de ejecutar entre 1. sólo el proceso supervisor -normalmente el sistema operativo. los programas de usuario tienen un acceso limitado al juego de instrucciones. de esta manera. lo cual le hace más lento que su hermano el 8086. Tienen una capacidad de direccionamiento en puertos de entrada y salida de hasta 64K (65536 puertos). 286. Entre esas instrucciones. Los microprocesadores Intel 8086 y 8088 se desarrollan a partir de un procesador anterior.8 y 30 millones de estas instrucciones por segundo). coprocesador matemático de coma flotante. La memoria virtual permite al ordenador usar más memoria de la que realmente tiene. por lo que las máquinas construidas entorno a estos microprocesadores no suelen emplear la entrada/salida por mapa de memoria. se produce una interrupción y el sistema operativo se encarga de acceder al disco y traerla. en sus diversas encarnaciones -incluyendo el Zilog Z-80. Recuérdese que un MHz son un millón de ciclos de reloj. El microprocesador Intel 80286 se caracteriza por poseer dos modos de funcionamiento completamente diferenciados: el modo real en el que se encuentra nada más ser conectado a la corriente y el modo protegido en el que adquiere capacidad de proceso multitarea y almacenamiento en memoria virtual. como veremos. Esto es así para evitar que los programas de usuario puedan campar a sus anchas y entrar en conflictos unos con otros. Disponen de 92 tipos de instrucciones. Además. que pueden ejecutar con hasta 7 modos de direccionamiento.ha sido la CPU de 8 bits de mayor éxito. pues éste es capaz de cargar una palabra ubicada en una dirección par en un solo ciclo de memoria mientras el 8088 debe realizar dos ciclos leyendo cada vez un byte.5 millones de instrucciones por segundo. Poseen una arquitectura interna de 16 bits y pueden trabajar con operandos de 8 y 16 bits. ya que mientras un proceso está esperando a que un periférico complete una operación. cuando acceden a una parte de la memoria lógica que no existe físicamente. por lo que disponen de diversos coprocesadores como el 8089 de E/S y el 8087. 3. el 8088 se diseñó con un bus de datos de 8 bits. una capacidad de direccionamiento de 20 bits (hasta 1 Mb) y comparten el mismo juego de instrucciones.CARACTERÍSTICAS GENERALES.MICROPROCESADORES 8086/88. las más rápidas se ejecutan en 2 ciclos teóricos de reloj y unos 9 reales (se trata del movimiento de datos entre registros internos) y las más lentas en 206 (división entera con signo del acumulador por una palabra extraída de la memoria). se puede atender otro proceso diferente. El proceso multitarea consiste en realizar varios procesos de manera aparentemente simultánea. aunque un error software provoque el cuelgue de un proceso.77 MHz puede ejecutar de 20. los programas creen tener a su disposición más memoria de la que realmente existe. Cuando la CPU está en modo protegido. . con la ayuda del sistema operativo para conmutar automáticamente de uno a otro optimizando el uso de la CPU.1.está capacitado para realizar ciertas tareas.000 a unos 0. 486 y Pentium. almacenando parte de ella en disco: de esta manera. De acuerdo a esta filosofía y para permitir la compatibilidad con los anteriores sistemas de 8 bits. 286. 8 MHz en la versión 8086-2 y 10 MHz en la 8086-1. por lo que un PC estándar a 4. el 8080. los demás pueden seguir funcionando . 386 Y 486 31 Capítulo III: Microprocesadores 8086/88. según la complejidad de las mismas (un 486 a 50 MHz. en materia de recursos como memoria o periféricos. que. La filosofía de diseño de la familia del 8086 se basa en la compatibilidad y la creación de sistemas informáticos integrados. Las frecuencias internas de reloj típicas son 4. 386.

de esta forma una vez que concluye la instrucción en curso ya tiene internamente la que le sigue). También poseen una cola de prebúsqueda mayor (cuando el microprocesador está ejecutando una instrucción. llegando a superar casi en tres veces la velocidad de los originales en algunas instrucciones aritméticas. no se puede decir lo mismo del 386 respecto al 286: no hubiera sido necesario añadir un nuevo modo protegido si hubiera sido mejor construido el del 286 apenas un par de años atrás. sino en la ausencia del 387 (que puede ser añadido externamente). Ambos son compatibles Hardware y Software. Por ello. casi 15 veces más que un 8086 medio a 8 MHz. Un 286 de categoría media (16 MHz) podría ejecutar más de medio millón de instrucciones de estas en un segundo. 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. el primer fabricante de estos chips. el modo protegido (relativamente compatible con el del 286). Versiones mejoradas de los Intel 8086 y 8088 se encuentran también en los procesadores NEC-V30 y NEC-V20 respectivamente. Normalmente. Además.romper la barrera de los tradicionales segmentos y el modo «virtual 86».con lo que de entrada se está tirando a la basura un 50% de la capacidad de proceso del chip. un modo protegido propio que permite -¡por fin!. los 386 suelen operar en modo real (debido al DOS) por lo que no se aprovechan las posibilidades multitarea ni de gestión de memoria. al igual que el 80186 y el 80188. en el que puede emular el funcionamiento simultáneo de varios 8086. transfiriendo datos entre registros la diferencia de un procesador a otro se reduce notablemente. aunque se pueden emplear los registros de 32 bits en modo real. El 486 se diferencia del 386 en la integración en un solo chip del coprocesador 387. todos los modos son incompatibles entre sí y requieren de un sistema operativo específico: si se puede perdonar al fabricante la pérdida de compatibilidad del modo avanzados del 286 frente al 8086. La versión 486sx no se diferencia en el tamaño del bus. También se ha mejorado la velocidad de operación: la versión de 25 MHz dobla en términos reales a un 386 a 25 MHz equipado con el mismo tamaño de memoria caché. Por otra parte. también de 32 bits. posee 25 instrucciones más que el 8086 y admite 8 modos de direccionamiento. En modo virtual permite direccionar hasta 1 Gigabyte.32 EL UNIVERSO DIGITAL DEL IBM PC. aunque existen versiones a 20 y 25 MHz. un bus de direcciones de 24 bits (16 Mb). Las frecuencias de trabajo típicas son de 12 y 16 MHz. Básicamente. Por su parte. aunque el fabricante lo evitó probablemente por razones comerciales. en este libro sólo trataremos el modo real. aunque es compatible en software. el 286 en modo protegido pierde absolutamente toda la compatibilidad con los procesadores anteriores. AT Y PS/2 normalmente. carga en una cola FIFO de unos pocos bytes las posiciones posteriores a la que está procesando. ha sido Cyrix. por lo que es difícil diseñar un sistema multitarea para el mismo y casi imposible conseguir que sea realmente operativo. los NEC V20 y V30 disponen de las mismas instrucciones adicionales del 286 en modo real. es un 386 con un bus de datos de sólo 16 bits -más lento. debido a la lógica evolución tecnológica. También existen versiones de 486 con buses de 16 bits. solo que emplea 29 ciclos de reloj en lugar de 206. si no hace uso de los buses externos. aunque veremos alguna instrucción extra que también se puede emplear en modo real. podría haber sido diseñado perfectamente para mantener una compatibilidad hardware con el 286. Las características generales del 286 son: tiene un bus de datos de 16 bits. El 8086 no posee ningún mecanismo para apoyar la multitarea ni la memoria virtual desde el procesador. Por desgracia. único disponible bajo DOS. con la ventaja de que el procesado de instrucciones está optimizado. y el sistema operativo podría abortar el proceso colgado. denominados 486SLC. aunque por fortuna estos procesadores suelen trabajar a frecuencias de 16/20 MHz (obsoletas) y normalmente de 33 y hasta 40 MHz. Una tendencia iniciada por el 486 fue la de duplicar la velocidad del reloj interno (pongamos por caso . el 386 dispone de una arquitectura de registros de 32 bits. Sin embargo. Aquí. Obviamente. Una vez más.significa la caída inmediata de todo el sistema. con un bus de direcciones también de 32 bits (direcciona hasta 4 Gigabytes = 4096 Mb) y más modos posibles de funcionamiento: el modo real (compatible 8086). aunque el 286 es más rápido y no sólo gracias a los MHz adicionales. al tener que dar dos pasadas para un dato de 32 bits-. ello no suele hacerse -para mantener la compatibilidad con procesadores anteriores. El 386sx es una variante del 386 a nivel de hardware. la instrucción más lenta es la misma que en el caso del 8086. De hecho.

el rendimiento general del sistema se puede considerar prácticamente el doble. Una caché de tamaño razonable puede doblar la velocidad efectiva de proceso de la CPU. pero no se suele emplear bajo DOS). La mecánica básica de funcionamiento de un programa consiste en cargar los registros con datos de la memoria o de un puerto de E/S. Estos procesadores disponen de 14 registros de 16 bits (el 286 alguno más. lo que agiliza los accesos a memoria) y en un elevadísimo nivel de optimización y segmentación que le permite. Obviamente. simultanear en muchos casos la ejecución de dos instrucciones consecutivas. y que un disco duro moderno transfiere datos 10 veces más deprisa que la memoria de aquel IBM PC original. El Pentium. Son los chips DX2 (también hay una variante a 50 MHz: 25 x 2). A continuación se describen los registros del 8086. Y a una fracción del coste (un millón de pts de aquel entonces que equivale a unos 2. Por desgracia.REGISTROS DEL 8086 Y DEL 286. se diferencia respecto al 486 en el bus de datos (ahora de 64 bits. ya que los accesos a memoria son mucho más lentos que los accesos a los registros. Comenzó en 60/90 MHz hasta los 166/200/233 MHz de las últimas versiones (Pentium Pro y MMX). 386 Y 486 33 de 33 a 66 MHz) aunque en las comunicaciones con los buses exteriores se respeten los 33 MHz. se agilizaba ya un tanto la velocidad de proceso al poder ejecutar una instrucción al mismo tiempo que iba leyendo la siguiente. el software no ha mejorado el rendimiento. 3. un Pentium básico sólo es unas 2 veces más rápido que un 486 a la misma frecuencia de reloj. El 8088 carecía de memoria caché.. Ello agiliza la ejecución de las instrucciones más largas: bajo DOS. 286. que junto a diversos clones de otros fabricantes. en esa proporción: es la factura pasada por las técnicas de programación cada vez a un nivel más alto (aunque nadie discute sus ventajas).. ésta ya se encuentra en la caché y el acceso es muy rápido. bajo DOS. 200 veces más grande el disco duro. No todos los registros sirven para almacenar datos. Además.MICROPROCESADORES 8086/88. Cuando sea necesario acceder a la instrucción siguiente del programa.5 millones de hoy en día). último procesador de Intel en el momento de escribirse estas líneas. pero esto no es todavía posible actualmente. Para hacerse una idea. Aunque no hay que olvidar la revolución del resto de los componentes: 100 veces más memoria (central y de vídeo). Posee dos cachés internas. procesar los datos y devolver el resultado a la memoria o a otro puerto de E/S. Una característica de los microprocesadores a partir del 386 es la disponibilidad de memorias caché de alta velocidad de acceso -muy pocos nanosegundos. cierta circuitería de control se encarga de ir depositando el contenido de esa posición y el de las posiciones inmediatamente consecutivas en la memoria caché. . Todos los equipos Pentium emplean las técnicas DX.2. empleando compiladores optimizados. tiene capacidad para predecir el destino de los saltos y la unidad de coma flotante experimenta elevadas mejoras. algunos están especializados en apuntar a las direcciones de memoria. pero sí estaba equipado con una unidad de lectura adelantada de instrucciones con una cola de prebúsqueda de 4 bytes: de esta manera. AX BX CX DX Registros de datos SP BP SI DI Registros punteros de pila e índices CS DS SS ES Registros de segmento IP flags Registro puntero de instrucciones y flags . La culminación de esta tecnología viene de la mano de los DX4 a 75/100 MHz (25/33 x 3). en coma flotante la diferencia aumenta incluso algunos órdenes más de magnitud. mejoran aún más el rendimiento. por unas 200000 pts de 1997 un equipo Pentium MMX a 233 MHz es cerca de 2000 veces más rápido en aritmética entera que el IBM PC original de inicios de la década de los 80. es preferible realizar la operación directamente sobre la memoria. hay ciertas operaciones que sólo se pueden realizar sobre los registros. Lo ideal sería que toda la memoria del equipo fuera caché. Sin embargo. ni remotamente. ya que las placas base típicas corren a 60 MHz.que almacenan una pequeña porción de la memoria principal. si ello es posible. Cuando la CPU accede a una posición de memoria. si un dato sólo va a experimentar un cambio. La misión de estos registros es almacenar las posiciones de memoria que van a experimentar repetidas manipulaciones.

Es el registro principal. Los programas de más de 64 Kb requieren cambiar CS periódicamente. por ejemplo. la suma y la resta sobre cualquier registro de datos. Estas áreas pueden solaparse total o parcialmente. . entre las que se pueden intercambiar datos. AT Y PS/2 . Se usa como registro base para referenciar direcciones de memoria con direccionamiento indirecto. DX = Datos. Es extraordinariamente útil actuando en conjunción con DS: con ambos se puede definir dos zonas de 64 Kb. Usado en conjunción con AX en las operaciones de multiplicación y división que involucran o generan datos de 32 bits. Contiene la dirección del segmento con las instrucciones del programa. CX = Contador. así como en ciertas operaciones de carácter específico como entrada.34 EL UNIVERSO DIGITAL DEL IBM PC. 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 según queramos referirnos a la parte alta o baja respectivamente. CX. Segmento del área de datos del programa. manteniendo la dirección de la base o comienzo de tablas o matrices. BX = Base. habrá de moverse alguno. Evidentemente. no necesariamente el acumulador. No es posible acceder a una posición de memoria no definida por algún segmento: si es preciso. En las instrucciones de desplazamiento y rotación se utiliza como contador de 8 bits. De esta manera. Segmento de ampliación para zona de datos. salida y traducción. En las de entrada y salida se emplea para especificar la dirección del puerto E/S. tan alejadas como se desee en el espacio de direcciones. DS = Registro de segmento de datos (data segment). SS = Registro de segmento de pila (stack segment). haciendo avanzar de unidad en unidad a BX. no es preciso indicar una posición de memoria fija.Registros de segmento: Definen áreas de 64 Kb dentro del espacio de direcciones de 1 Mb del 8086. ¡cualquier cambio sobre AH o AL altera AX!: valga como ejemplo que al incrementar AH se le están añadiendo 256 unidades a AX. AX se descompone en AH (parte alta) y AL (parte baja).Registros de datos: AX. . Se utiliza comúnmente como contador en bucles y operaciones repetitivas de manejo de cadenas. Obsérvese que el 8086 es suficientemente potente para realizar las operaciones lógicas. ES = Registro de segmento extra (extra segment). BX. sino la número BX (así. es utilizado en las instrucciones de multiplicación y división y en algunas instrucciones aritméticas especializadas. Por ejemplo. CS = Registro de segmento de código (code segment). AX = Acumulador. Segmento de pila. se puede ir accediendo a un gran bloque de memoria en un bucle).

Registro de estado o de indicadores (flags). . Indicador de atrape (ejecución paso a paso). también se emplea para guardar un valor de desplazamiento en operaciones de cadenas. Para ajuste en operaciones BCD. .MICROPROCESADORES 8086/88. . Apunta a la cabeza de la pila. Indicador de dirección. Indicador de paridad. 6. Indicador de interrupciones: puesto a 1 están permitidas.Registros índices: SI = Índice fuente (source index). Su valor más habitual es lo que nos llevamos en una suma o resta. . Es automáticamente modificado con la lectura de una instrucción. que apunta a una zona dentro de la pila dedicada al almacenamiento de datos (variables locales y parámetros de las funciones en los programas compilados). Indicador de resultado 0 o comparación igual. 4. que reflejan los resultados de operaciones del programa. 386 Y 486 35 . Estos indicadores pueden ser comprobados por las instrucciones de salto condicional. Manipulando bloques de memoria. los bits del 8 al 10 son indicadores de control y el resto no se utilizan. BP = Puntero base (base pointer). 15 14 13 12 11 OF CF (Carry Flag) OF (Overflow Flag) ZF (Zero Flag) SF (Sign Flag) PF (Parity Flag) AF (Auxiliary Flag) DF (Direction Flag) IF (Interrupt Flag) TF (Trap Flag) 10 DF 9 IF 8 TF 7 SF 6 ZF 5 4 AF 3 2 PF 1 0 CF Indicador de acarreo.Puntero de instrucciones o contador de programa: IP = Puntero de instrucción (instruction pointer). 286. Utilizado como registro de índice en ciertos modos de direccionamiento indirecto. 7 y 11 son indicadores de condición. Se activa tras algunas operaciones aritmético-lógicas para indicar que el número de bits a uno resultante es par. Indicador de desbordamiento. lo que permite variar el flujo secuencial del programa según el resultado de las operaciones. Es un registro de 16 bits de los cuales 9 son utilizados para indicar diversas situaciones durante la ejecución de un programa. Indica que el resultado de una operación no cabe en el tamaño del operando destino. Indicador de resultado o comparación negativa. Es un puntero de base.Registros punteros de pila: SP = Puntero de pila (stack pointer). Los bits 0. Utilizado en las instrucciones de manejo de la pila. Marca el desplazamiento de la instrucción en curso dentro del segmento de código. DI = Índice destino (destination index). Se usa en determinados modos de direccionamiento indirecto y para almacenar un desplazamiento en operaciones con cadenas. 2. indica el sentido de avance (ascendente/descendente).

36 EL UNIVERSO DIGITAL DEL IBM PC. Los 386 y superiores disponen de muchos más registros de los que vamos a ver ahora. Cada grupo se asocia con un registro de segmento. sí sería posible configurar estos procesadores para poder direccionar más memoria bajo DOS con los registros de 32 bits. 3D00h:0300h es equivalente a 3D30:0000h. AX EAX BX EBX CX ECX DX EDX EDI ESI DI SS GS EBP SI ES FS BP DS flags SP CS IP Se amplía el tamaño de los registros de datos (que pueden ser accedidos en fragmentos de 8.REGISTROS DEL 386 Y PROCESADORES SUPERIORES. hay por tanto que recurrir a algún artificio para direccionar toda la memoria. Por ello.4. Es importante resaltar que no se puede acceder a más de 64 Kb en un segmento de datos. en los procesadores 386 y superiores no se deben emplear registros de 32 bit para generar direcciones (bajo DOS). Esto equivale a concebir el mecanismo de generación de la dirección absoluta. Hay instrucciones. una misma dirección puede expresarse de más de una manera: por ejemplo. como si se tratase de que los registros de segmento tuvieran 4 bits a 0 (imaginarios) a la derecha antes de sumarles el desplazamiento: dirección = segmento * 16 + offset En la práctica. obteniéndose una dirección efectiva de 20 bits. . e incluso ninguno: INSTRUCCIÓN DESTINO Como ejemplos. en cambio. ya que pondremos alguna en los ejemplos: INSTRUCCIÓN DESTINO. Como ya sabemos. pero bajo sistema operativo DOS no pueden ser empleados de manera directa. echaremos un vistazo a la sintaxis general de las instrucciones. sin embargo. . como la siguiente. la dirección se compone de 20 bits con capacidad para 1Mb. una dirección se indica con la notación SEGMENTO:OFFSET. aunque para los cálculos pueden ser interesantes (no obstante. Sin embargo. aunque no resulta por lo general práctico). 3. AT Y PS/2 3. aunque no hemos visto aún las instrucciones utilizaremos un par de ellas: la de copia o movimiento de datos (MOV) y la de suma (ADD). 3. Dicho artificio consiste en la segmentación: se trata de dividir la memoria en grupos de 64K. por lo que no les consideraremos. . que sólo tienen un operando. que constituyen básicamente una extensión a 32 bits de los registros originales del 8086. el desplazamiento (offset) dentro de ese segmento lo proporciona otro registro de 16 bits. Algunos de los registros aquí mostrados son realmente de 32 bits (como EIP en vez de IP). además.MODOS DE DIRECCIONAMIENTO.ORGANIZACIÓN DE DIRECCIONES: SEGMENTACIÓN. los microprocesadores 8086 y compatibles poseen registros de un tamaño máximo de 16 bits que direccionarían hasta 64K. . bajo el sistema operativo DOS sólo se suelen emplear los que veremos. La dirección absoluta se calcula multiplicando por 16 el valor del registro de segmento y sumando el offset. Antes de ver los modos de direccionamiento.3.1. Son los distintos modos de acceder a los datos en memoria por parte del procesador. FUENTE Donde destino indica dónde se deja el resultado de la operación en la que pueden participar (según casos) FUENTE e incluso el propio DESTINO.4. 16 ó 32 bits) y se añaden dos nuevos registros de segmento multipropósito (FS y GS).

0fffh El número hexadecimal 0fffh es la constante numérica que en el direccionamiento inmediato se le sumará al registro AX. AX = «dirección de memoria» de dato Porque hay que tener en cuenta que cuando traduzcamos a números el símbolo podría quedar: 17F3:0A11 DW FFF MOV AX.AX MOV AH. Sin embargo. variable del programa En el primer ejemplo se transfiere a AX el valor contenido en la dirección apuntada por la etiqueta dato sobre el segmento de datos (DS) que se asume por defecto. Al trabajar con ensambladores.Direccionamiento inmediato: El operando es una constante situada detrás del código de la instrucción.0A11 . se pueden definir símbolos constantes (ojo.MICROPROCESADORES 8086/88. La dirección efectiva se calcula de la forma ya vista con anterioridad: Registro de segmento * 16 + desplazamiento_de_dato (este desplazamiento depende de la posición al ensamblar el programa). también se trata de un caso de direccionamiento inmediato: dato DW 0fffh MOV AX. 286.dato MOV AX. símbolo constante Si se referencia a la dirección de memoria de una variable de la siguiente forma. relativa al segmento que se trate: MOV AX.Direccionamiento de registro: Los operandos.ES:dato dato DW 1234h .ES:[429Ch] Esta sintaxis (quitando la ’h’ de hexadecimal) sería la que admite el programa DEBUG (realmente habría que poner.2. .dato .[57D1h] MOV AX. . necesariamente de igual tamaño.AL . en el segundo ejemplo se indica de forma explícita el segmento tratándose del segmento ES.OFFSET dato .4. las variables en memoria se pueden referenciar con etiquetas simbólicas: MOV AX. ES: en una línea y el MOV en otra). ahora es una variable . 386 Y 486 37 3.MODOS DE DIRECCIONAMIENTO. ADD AX.Direccionamiento directo o absoluto: El operando está situado en la dirección indicada en la instrucción. Al trabajar con ensambladores. en el segundo caso. . como registro destino no se puede indicar uno de segmento (habrá que utilizar uno de datos como paso intermedio). están contenidos en los registros indicados en la instrucción: MOV DX. no variables) y es más intuitivo: dato EQU 0fffh MOV AX.

[BP] MOV ES:[DI]. SI o DI y un desplazamiento de 8 ó 16 bits.BX ó ó ADD MOV AX. un registro de índice. la pila avanza hacia direcciones decrecientes. con todos estos modos se puede considerar que hay más que suficiente. Existen unos segmentos asociados por defecto a los registros de desplazamiento (IP.ES:desp[BX][DI] MOV CS:desp[BX][SI]. La siguiente tabla relaciona las posibles combinaciones de los registros de segmento y los de desplazamiento: CS IP SP BP BX SI DI Sí No con prefijo con prefijo con prefijo con prefijo SS No Sí por defecto con prefijo con prefijo con prefijo DS No No con prefijo por defecto por defecto por defecto ES No No con prefijo con prefijo con prefijo con prefijo(1) (1) También por defecto en el manejo de cadenas. Por ahora. AT Y PS/2 .CX ó ó MOV AX. SI). después de conocer todas las instrucciones del 8086. el ensamblador genera un byte adicional (a modo de prefijo) para indicar cuál es el segmento referenciado. . primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de pila). (Nota: BP actúa por defecto con SS). SP.AX .[DI+DESP] ADD [SI+DESP].38 EL UNIVERSO DIGITAL DEL IBM PC.LA PILA. sólo es necesario declarar el segmento cuando no coincide con el asignado por defecto. Los 386 y superiores admiten otros modos de direccionamiento más sofisticados. es decir.CX Combinaciones de registros de segmento y desplazamiento. hay casos en los que se indica explícitamente el registro de segmento a usar para acceder a los datos. BX. BP. . La pila es un bloque de memoria de estructura LIFO (Last Input First Output: último en entrar. uno de base.Indirecto con índice o indexado: El operando se encuentra en una dirección determinada por la suma de un registro de segmento*16.Indirecto con base e índice o indexado a base: El operando se encuentra en una dirección especificada por la suma de un registro de segmento*16. DI. AX = [SS*16+BP] .Direccionamiento indirecto: El operando se encuentra en una dirección señalada por un registro de segmento*16 más un registro base (BX/BP) o índice (SI/DI). MOV AX. 3. 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.BX . y cada vez que se introduce algo en ella por medio de las instrucciones de manejo de pila (PUSH y POP). En ese caso. que se verán en el próximo capítulo. Como se ve en los modos de direccionamiento. Todos los datos que se almacenan en la pila son de longitud palabra. el puntero se decrementa en dos.desp[DI] desp[SI]. algunos se utilizan en muy contadas ocasiones.ES:[BX+DI+DESP] MOV CS:[BX+SI+DESP]. Ejemplos: MOV AX.5. [ES*16+DI] = AX . De hecho. uno de índice y opcionalmente un desplazamiento de 8 ó 16 bits: MOV AX.

Ejemplo de operación sobre la pila (todos los datos son arbitrarios): Memoria 66h 91h F3h 21h Situación inicial AX = 1234h BX = 9D33h <-. En estas operaciones conviene tener cuidado.sin necesidad de desapilarlos para consultarlos. la B800:0002. 386 Y 486 39 El registro BP suele utilizarse normalmente para apuntar a una cierta posición de la pila y acceder indexadamente a sus elementos -generalmente en el caso de variables. El programa quedaría en memoria de esta manera: La primera columna indica la dirección de memoria donde está el programa que se ejecuta (CS=14D3h e IP=7A10h al principio). Esta última es. Aunque las instrucciones del procesador no serán vistas hasta el próximo capítulo.6.14C0:D020 SS:SP Memoria 66h 91h 12h 34h después de POP BX AX = 1234h BX = 1234h <-. y alguna otra más como la instrucción INC (incrementar). 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. ya que la pila en los 8086 es común al procesador y al usuario. ó 20h en hexadecimal). DEC (disminuir una unidad) y JNZ (saltar si el resultado no es cero).14C0:D022 SS:SP 3. otras dos ó tres (las hay de más). la pantalla de texto comienza en B800:0000 (no es más que una zona de memoria). la más importante de sus funciones. aunque nosotros nos referiremos a ellos con unos símbolos que faciliten entenderlos. comenzando arriba a la izquierda. Así.UN PROGRAMA DE EJEMPLO. ya conocida. sin modificar el color que hubiera antes. por lo que se almacenan en ella también las direcciones de retorno de las subrutinas. La pila es utilizada frecuentemente al principio de una subrutina para preservar los registros que no se desean modificar. También es interesante este ejemplo para afianzar el concepto de registro de segmento. al retornar de la subrutina se extrae de la pila la dirección a donde volver. con el código ASCII del carácter y el segundo con el color. Lo que vamos a hacer es rellenar los 2000 caracteres (80 columnas x 25 líneas) con espacios en blanco (código ASCII 32. Como el ordenador es un PC con monitor en color. En este programa sólo vamos a emplear las instrucciones MOV. con objeto de ayudar a la imaginación del lector elaboraremos un primer programa de ejemplo en lenguaje ensamblador. La tercera columna contiene el nombre de las instrucciones. 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. Algunas instrucciones ocupan un byte de memoria. Por ello. La segunda columna constituye el código máquina que interpreta el 8086. que serán las de la siguiente instrucción que provocó la llamada a la subrutina. La utilidad de este programa es dejar patente que lo único que entiende el 8086 son números. a partir de la dirección B800:0000 tenemos dos bytes: el primero. 286.. una norma básica es que se debe desapilar siempre todo lo apilado para evitar una pérdida de control inmediata del ordenador.14C0:D022 SS:SP Memoria 66h 91h 12h 34h después de PUSH AX AX = 1234h BX = 9D33h <-. al final de la subrutina basta con recuperarlos en orden inverso al que fueron depositados.MICROPROCESADORES 8086/88. la B800:0004. Esto es. y así sucesivamente. Por cada carácter que hay en dicha pantalla. Los compiladores de los lenguajes de alto nivel la emplean también para pasar los parámetros de los procedimientos y para generar en ella las variables automáticas -variables locales que existen durante la ejecución del subprograma y se destruyen inmediatamente después-. algo mucho más legible para los humanos que los números: .. se trata de poner el valor 32 en la dirección B800:0000. . de hecho.

. Normalmente. CX = 7D0h (2000 decimal = 7D0 hexadecimal) segmento de la memoria de pantalla apuntar segmento de datos a la misma apuntar al primer carácter ASCII de la pantalla se pone BYTE PTR para indicar que 32 es de 8 bits BX=BX+1 .queda un carácter menos si CX no es 0.0 BYTE PTR [BX].40 EL UNIVERSO DIGITAL DEL IBM PC. A medida que se van haciendo programas. En el ejemplo cargamos el valor 0B800h en DS apoyándonos en AX como intermediario. que tampoco son demasiadas. El valor 0F8h del código máquina de la última instrucción es el complemento a dos (número negativo) del valor 8. como hemos hecho en este ejemplo.32 equivale a decir: «poner en la dirección de memoria apuntada por BX (DS:[BX] para ser más exactos) el byte de valor 32».AX BX.apuntar al siguiente carácter ASCII CX=CX-1 .7D0H AX. saltar 8 bytes atrás (a 14D3:7A1B) Como se puede ver. la mejor manera de aprender ensamblador es no olvidando la estrecha relación de cada línea de programa con la CPU y la memoria. . . . el ensamblador da mensajes de error cuando se encuentra con estos fallos y permite ir aprendiendo con facilidad las normas. . casi nunca habrá que ensamblar a mano consultando unas tablas. Sin embargo. . .32 BX BX CX -8 .apuntar al byte de color BX=BX+1 . La instrucción MOV BYTE PTR [BX]. AT Y PS/2 14D3:7A10 14D3:7A13 14D3:7A16 14D3:7A18 14D3:7A1B 14D3:7A1E 14D3:7A1F 14D3:7A20 14D3:7A21 B9 B8 8E BB C6 43 43 49 75 D0 00 D8 00 07 07 B8 00 20 F8 MOV MOV MOV MOV MOV INC INC DEC JNZ CX. ya que la anterior que ocupaba 3 bytes comenzaba en 7A10h. . 0 y 0B8h colocados en posiciones consecutivas) está colocada a partir del desplazamiento 7A13h. El motivo es que los registros de segmento no admiten el direccionamiento inmediato.0B800h DS. . la segunda instrucción (bytes de código máquina 0B8h.

offset dato En el último ejemplo. Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere datos de longitud byte o palabra del operando origen al operando destino. Existen ciertas limitaciones. Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguiente notación: . .DESCRIPCIÓN COMPLETA DE LAS INSTRUCCIONES. como que los registros de segmento no admiten el direccionamiento inmediato: es incorrecto MOV DS. SI es un puntero a «dato» pero no es «dato».INSTRUCCIONES DE CARGA DE REGISTROS Y DIRECCIONES.1.AX o MOV DS.es:[si] si.VARIABLE. pero no lo es por ejemplo MOV DS.4000h. Al hacer MOV hacia un registro de segmento.1. En el próximo capítulo se verá cómo se declaran las variables. las interrupciones quedan inhibidas hasta después de ejecutarse la siguiente instrucción (8086/88 de 1983 y procesadores posteriores).ax bx.1. con la única condición de que origen y destino tengan la misma dimensión. XCHG (intercambiar) Sintaxis: XCHG destino. Pueden ser operando origen y operando destino cualquier registro o posición de memoria direccionada de las formas ya vistas. En otras palabras.AX aunque pueda admitirlo algún ensamblador). No es posible. origen. origen Indicadores: OF DF IF TF SF ZF AF PF CF - . Ejemplos: mov mov mov ds. .JUEGO DE INSTRUCCIONES 80x86 41 Capítulo IV: JUEGO DE INSTRUCCIONES 80x86 4. no se coloca en SI el valor de la variable «dato» sino su dirección de memoria o desplazamiento respecto al segmento de datos. así mismo. utilizar CS como destino (es incorrecto hacer MOV CS.bit no modificado ? desconocido o indefinido x modificado según el resultado de la operación 1 puesto siempre a 1 0 puesto siempre a 0 4. MOV (transferencia) Sintaxis: MOV dest.

offset tabla al. Sin embargo. Otras instrucciones pueden a continuación utilizar el registro como desplazamiento para acceder a los datos que constituyen el objetivo. origen Indicadores: OF DF IF TF SF ZF AF PF CF - Traslada un puntero de 32 bits (dirección completa de memoria compuesta por . No pueden utilizarse registros de segmentos como operandos. Ejemplo: xchg xchg bl.bx XLAT (traducción) Sintaxis: XLAT tabla Indicadores: OF DF IF TF SF ZF AF PF CF - Se utiliza para traducir un byte del registro AL a un byte tomado de la tabla de traducción.4 LEA (carga dirección efectiva) Sintaxis: LEA destino. donde bx es un puntero a el comienzo de la tabla y AL es un índice. Los datos se toman desde una dirección de la tabla correspondiente a BX + AL. Ejemplo: mov mov xlat bx. LEA es en algunos casos más potente que MOV al permitir indicar registros de índice y desplazamiento para calcular el offset: lea dx. origen Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere el desplazamiento del operando fuente al operando destino.si LDS (carga un puntero utilizando DS) Sintaxis: LDS destino. esta instrucción es equivalente a «MOV destino.offset datos dx. el valor depositado en DX es el offset de la etiqueta «datos» más el registro SI.datos[si] En el ejemplo de arriba.ch mem_pal.42 EL UNIVERSO DIGITAL DEL IBM PC. En general. AT Y PS/2 Intercambia el contenido de los operandos origen y destino. Indicar «tabla» al lado de xlat es sólo una redundancia opcional. El operando destino no puede ser un registro de segmento.OFFSET fuente» y de hecho los buenos ensambladores (TASM) la codifican como MOV para economizar un byte de memoria. Esa sola instrucción es equivalente a estas dos: mov add dx.

Ejemplo: punt dd lds 12345678h si. 4.INSTRUCCIONES DE MANIPULACIÓN DEL REGISTRO DE ESTADO.JUEGO DE INSTRUCCIONES 80x86 43 segmento y desplazamiento).2. SAHF (copia AH en los indicadores) Sintaxis: SAHF Indicadores: OF DF IF TF SF x ZF x AF x PF x CF x Transfiere el contenido de los bits 7. en DS:SI se hace referencia a la posición de memoria 1234h:5678h. LAHF (carga AH con los indicadores) Sintaxis: LAHF Indicadores: OF DF IF TF SF ZF AF PF CF - Carga los bits 7. con los otros dos. . El contenido de los demás bits queda sin definir. pero utilizando ES en lugar de DS. A partir de la dirección indicada por el operando origen. ZF. otra en DS. ’dd’ sirve para definir una variable larga de 4 bytes (denominada «punt» en el ejemplo) y será explicado en el capítulo siguiente. 2 y 0 a los indicadores SF. al destino indicado y a DS. el procesador toma 4 bytes de la memoria: con los dos primeros forma una palabra que deposita en «destino» y. 6. ZF. CLC (baja el indicador de acarreo) Sintaxis: CLC Indicadores: OF DF IF TF SF ZF AF PF CF 0 Borra el indicador de acarreo (CF) sin afectar a ninguno otro. origen Esta instrucción es análoga a LDS. LES (carga un puntero utilizando ES) Sintaxis: LES destino. 2 y 0 del registro AH con el contenido de los indicadores SF. 4. 4. PF Y CF respectivamente. AF.1. PF y CF respectivamente.punt Como resultado de esta instrucción. AF. CLD (baja el indicador de dirección) Sintaxis: CLD . 6.

CMC (complementa el indicador de acarreo) Sintaxis: CMC Indicadores: OF DF IF TF SF ZF AF PF CF x Complementa el indicador de acarreo CF invirtiendo su estado. AT Y PS/2 Indicadores: OF - DF 0 IF - TF - SF - ZF - AF - PF - CF - Pone a 0 el indicador de dirección DF. lo que desactiva las interrupciones enmascarables. por lo que los registros SI y/o DI se autodecrementan en las operaciones de cadenas. . Véase STD. o antes de cambiar un vector de interrupción sin el apoyo del DOS. Generalmente las interrupciones sólo se inhiben por breves instantes en momentos críticos. STD (pone a uno el indicador de dirección) Sintaxis: STD Indicadores: OF DF 1 IF TF SF ZF AF PF CF - Pone a 1 el indicador de dirección DF. sin afectar al resto de los indicadores. Véase también CLD. Es NECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce con seguridad el estado de DF. Es muy conveniente hacer esto antes de modificar la pareja SS:SP en los 8086/88 anteriores a 1983 (véase comentario en la instrucción MOV). Es NECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce con seguridad el valor de DF. por lo que los registros SI y/o DI se autoincrementan en las operaciones de cadenas. Véase también STI. sin afectar al resto de los indicadores. CLI (baja indicador de interrupción) Sintaxis: CLI Indicadores: OF DF IF 0 TF SF ZF AF PF CF - Borra el indicador de activación de interrupciones IF.44 EL UNIVERSO DIGITAL DEL IBM PC. STC (pone a uno el indicador de acarreo) Sintaxis: STC Indicadores: OF DF IF TF SF ZF AF PF CF 1 Pone a 1 el indicador de acarreo CF sin afectar a ningún otro indicador.

no funciona correctamente en los 286 y superiores. Una interrupción pendiente no es reconocida. al contrario de lo que afirman algunas publicaciones. PUSHF (introduce los indicadores en la pila) . Ejemplo: push cs POPF (extrae los indicadores de la pila) Sintaxis: POPF Indicadores: OF x DF x IF x TF x SF x ZF x AF x PF x CF x Traslada al registro de los indicadores la palabra almacenada en la cima de la pila. Véase también CLI. La instrucción POP CS. 4.1. e incrementa en dos el registro SP.JUEGO DE INSTRUCCIONES 80x86 45 STI (pone a uno el indicador de interrupción) Sintaxis: STI Indicadores: OF DF IF 1 TF SF ZF AF PF CF - Pone a 1 la bandera de desactivación de interrupciones IF y activa las interrupciones enmascarables. POP (extraer de la pila) Sintaxis: POP destino Indicadores: OF DF IF TF SF ZF AF PF CF - 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.3. hasta después de ejecutar la instrucción que sigue a STI. sin embargo. a continuación el puntero de pila SP se incrementa en dos. El registro CS aquí sí se puede especificar como origen. .INSTRUCCIONES DE MANEJO DE LA PILA. poco útil. Ejemplos: pop pop ax pepe PUSH (introduce en la pila) Sintaxis: PUSH origen Indicadores: OF DF IF TF SF ZF AF PF CF - Decrementa el puntero de pila (SP) en 2 y luego transfiere la palabra especificada en el operando origen a la cima de la pila.

Ejemplos: dir call dd call proc1 0f000e987h dword ptr dir En el segundo ejemplo. 4.INSTRUCCIONES DE TRANSFERENCIA DE CONTROL. sin que el programador tenga que ocuparse de poner «short».o FAR) o indirecta.1. De esta última manera -conociendo su dirección. para economizar memoria. indicando la dirección donde se encuentra el puntero. . porque la rutina de interrupción retornará (con IRET en vez de con RETF) sacándolos.46 EL UNIVERSO DIGITAL DEL IBM PC.4. o larga. Los buenos ensambladores (como TASM) cuando dan dos pasadas colocan allí donde es posible un salto corto. la variable dir almacena la dirección a donde saltar. pero además puede ser corta (tipo SHORT) con un desplazamiento comprendido entre -128 y 127. La bifurcación puede ser también directa o indirecta como anteriormente vimos. para poder volver a ella una vez ejecutado el procedimiento. Según la llamada sea cercana o lejana. Si se hace un JMP SHORT y no llega el salto (porque está demasiado alejada esa etiqueta) el ensamblador dará error. salvando previamente en la pila la dirección de la instrucción siguiente. A su vez la llamada puede ser directa a una etiqueta (especificando el tipo de llamada NEAR -por defecto. se puede indicar con «far» que es largo (salto a otro segmento). AT Y PS/2 Sintaxis: PUSHF Indicadores: OF DF IF TF SF ZF AF PF CF - Decrementa en dos el puntero de pila y traslada a la cima de la pila el contenido de los indicadores. que permite desplazamientos de 64 Kb en la memoria sigue siendo insuficiente. Incondicional CALL (llamada a subrutina) Sintaxis: CALL destino Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere el control del programa a un procedimiento. El procedimiento puede estar en el mismo segmento (tipo NEAR) o en otro segmento (tipo FAR). con un desplazamiento de dos bytes con signo. JMP (salto) Sintaxis: JMP dirección o JMP SHORT dirección Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere el control incondicionalmente a la dirección indicada en el operando. se almacena en la pila una dirección de retorno de 16 bits o dos palabras de 16 bits indicando en este último caso tanto el offset (IP) como el segmento (CS) a donde volver. guardando previamente los flags en la pila (PUSHF). . Si el salto de dos bytes.puede llamarse también a un vector de interrupción.

si CF=0 y ZF=0. si SF<>OF. si ZF=0. si CF=0. si paridad par. si SF=0. «Valor». y se indican en la tabla como ’±’ (-128 a +127 ó -32768 a +32767). Salto si mayor o igual (above or equal). En negrita se realzan las condiciones más empleadas. si paridad impar. + + + + ± ± ± ± Gestión de bucle LOOP (bucle) Sintaxis: LOOP desplazamiento Indicadores: OF DF IF TF SF ZF AF PF CF - . si esta instrucción es colocada dentro de un bloque PROC-ENDP (como se verá en el siguiente capítulo) el ensamblador sabe el tipo de retorno que debe hacer. si OF=1. También se puede retornar de una interrupción con RETF 2. Condicional Las siguientes instrucciones son de transferencia condicional de control a la instrucción que se encuentra en la posición IP+desplazamiento (desplazamiento comprendido entre -128 y +127) si se cumple la condición. Algunas condiciones se pueden denotar de varias maneras. si ZF=0 y SF=0. lo que es frecuente en el código generado por los compiladores para retornar de una función con parámetros. Salto si no igual. si cero (zero). si ZF=0 y SF<>OF. Salto si no desbordamiento. se puede forzar que el retorno sea de tipo FAR con la instrucción RETF. Salto si menor (less). si SF=0. si acarreo. si no menor (not less).JUEGO DE INSTRUCCIONES 80x86 47 Ejemplos: jmp jmp etiqueta far ptr etiqueta RET / RETF (retorno de subrutina) Sintaxis: RET [valor] o RETF [valor] Indicadores: OF DF IF TF SF ZF AF PF CF - Retorna de un procedimiento extrayendo de la pila la dirección de la siguiente dirección. Salto si CX=0. Salto si paridad. Salto si mayor (greater). si no menor o igual (not below or equal). si no superior ni igual (not above or equal). si CF=0. Salto si menor o igual (less or equal). si CF=1. si no mayor (not above). Donde interviene SF se consideran con signo los operandos implicados en la última comparación u operación aritmetico-lógica. si es indicado permite sumar una cantidad «valor» en bytes a SP antes de retornar. si no mayor ni igual (not greater or equal). si SF=1. si no menor (not below). indicados como ’+’. si no cero. si no menor ni igual (not less or equal). si PF=1. Salto si signo. se consideran sin signo (0 a 255 ó 0 a 65535): JA/JNBE JAE/JNB JB/JNAE/JC JBE/JNA JCXZ JE/JZ JG/JNLE JGE/JNL JL/JNGE JLE/JNG JNC JNE/JNZ JNO JNP/JPO JNS JO JP/JPE JS Salto si mayor (above). si CF=1 ó ZF=1. si PF=0. Salto si menor (below). Salto si desbordamiento. Todos los saltos son cortos y si no alcanza hay que apañárselas como sea. Salto si no acarreo. en los demás casos. según el procedimiento sea NEAR o FAR. si no mayor (not greater). Salto si menor o igual (not below or equal). para que devuelva el registro de estado sin restaurarlo de la pila. si OF=0. Salto si mayor o igual (greater or equal). Salto si no signo. Salto si igual (equal). Se extraerá el registro de segmento y el desplazamiento en un procedimiento de tipo FAR (dos palabras) y solo el desplazamiento en un procedimiento NEAR (una palabra). si ZF=1. Salto si no paridad. si positivo. En cualquier caso.

Véase también IRET y el apartado 1 del capítulo VII. De lo contrario se continúa con la instrucción siguiente. si cero.. Véase también INT. AT Y PS/2 Decrementa el registro contador CX. ejecuta la siguiente instrucción. esta interrupción se utiliza para poner puntos de ruptura en los programas.. Z=1 y CX<>0 LOOPNE/LOOPNZ Bucle si no igual. al ensamblarla el ensamblador genera un sólo byte en vez de los dos habituales.. Ejemplo: int 21h INTO (interrupción por desbordamiento) Sintaxis: INTO Indicadores: OF DF IF 0 TF 0 SF ZF AF PF CF - Genera una interrupción de tipo 4 (INT 4) si existe desbordamiento (OF=1)... INT 3 es un caso especial de INT. .10 bucle Con las mismas características que la instrucción anterior: LOOPE/LOOPZ Bucle si igual... . Z=0 y CX<>0 Interrupciones INT (interrupción) Sintaxis: INT n (0 <= n <= 255) Indicadores: OF DF IF 0 TF 0 SF ZF AF PF CF - Inicializa un procedimiento de interrupción de un tipo indicado en la instrucción. se sacan las 3 palabras que fueron colocadas en la pila cuando se produjo la interrupción. Ejemplo: mov bucle: .. loop cx. El desplazamiento debe estar comprendido entre -128 y +127.48 EL UNIVERSO DIGITAL DEL IBM PC. si CX es cero. IRET (retorno de interrupción) Sintaxis: IRET Indicadores: OF x DF x IF x TF x SF x ZF x AF x PF x CF x Devuelve el control a la dirección de retorno salvada en la pila por una interrupción previa y restaura los indicadores que también se introdujeron en la pila.. En la pila se introduce al llamar a una interrupción la dirección de retorno formada por los registros CS e IP y el estado de los indicadores.... si no cero. En total. en caso contrario transfiere el control a la dirección resultante de sumar a IP + desplazamiento.

5. dependiendo de la longitud byte o palabra respectivamente. AF se pone a 1. . se suma 6 a AL.dx OUT (salida) Sintaxis: OUT puerto. IN (entrada) Sintaxis: IN acumulador.1. 1 a AH. . . acumulador Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere un byte o palabra del registro AL o AX a un puerto de salida. El puerto puede especificarse mediante una constante (0 a 255) o a través del valor contenido en DX (0 a 65535). Ejemplo: out out 12h.1. el resultado (por medio de AAA) sigue siendo un número BCD no empaquetado.ax dx.INSTRUCCIONES DE ENTRADA SALIDA (E/S).0fh al. *** SUMA *** AAA (ajuste ASCII para la suma) Sintaxis: AAA Indicadores: OF ? DF IF TF SF ? ZF ? AF x PF ? CF x Convierte el contenido del registro AL en un número BCD no empaquetado. CF se iguala a AF y AL pone sus cuatro bits más significativos a 0. Ejemplo: add aaa al. Si los cuatro bits menos significativos de AL son mayores que 9 ó si el indicador AF está a 1. Ejemplo: in in ax.INSTRUCCIONES ARITMÉTICAS. puerto. Indicadores: OF DF IF TF SF ZF AF PF CF - Transfiere datos desde el puerto indicado hasta el registro AL o AX.al 4.bl En el ejemplo.JUEGO DE INSTRUCCIONES 80x86 49 4. tras la suma de dos números BCD no empaquetados colocados en AL y BL.6. El puerto puede especificarse con un valor fijo entre 0 y 255 ó a través del valor contenido en el registro DX (de 0 a 65535).

si AL y CL contenían dos números BCD empaquetados. Ejemplo: adc ax. Se activa el acarreo si se desborda el registro destino durante la suma. origen Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF x Suma los operandos origen.dh DAA (ajuste decimal para la suma) Sintaxis: DAA Indicadores: OF ? DF IF TF SF x ZF x AF x PF x CF x Convierte el contenido del registro AL en un par de valores BCD: si los cuatro bits menos significativos de AL son un número mayor que 9. el indicador CF se pone a 1 y se suma 60h a AL. en varios pasos. origen Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF x Suma los operandos origen y destino almacenando el resultado en el operando destino. de más de 16 bits. Ejemplos: add add ax. . Ejemplo: add daa al. INC (incrementar) Sintaxis: INC destino Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF - Incrementa el operando destino.50 EL UNIVERSO DIGITAL DEL IBM PC. DAA hace que el resultado de la suma (en AL) siga siendo también un BCD empaquetado. si los cuatro bits más significativos de AL tras la operación anterior son un número mayor que 9.bx ADD (suma) Sintaxis: ADD destino.bx cl. Se utiliza normalmente para sumar números grandes. De igual forma. AT Y PS/2 ADC (suma con acarreo) Sintaxis: ADC destino. considerando lo que nos llevamos (el acarreo) de la suma anterior.cl En el ejemplo anterior. el indicador AF se pone a 1 y se suma 6 a AL. destino y el valor del indicador de acarreo (0 ó 1) y el resultado lo almacena en el operando destino. El operando destino puede ser byte o palabra.

Ejemplos: inc inc inc inc al es:[di] ss:[bp+4] word ptr cs:[bx+di+7] *** RESTA *** AAS (ajuste ASCII para la resta) Sintaxis: AAS Indicadores: OF ? DF IF TF SF ? ZF ? AF x PF ? CF x Convierte el resultado de la sustracción de dos operandos BCD no empaquetados para que siga siendo un número BCD no empaquetado. Ejemplo: sub aas al. El resultado se guarda en AL con los bits de 4 a 7 puestos a 0. se decrementa AH. CMP (comparación) Sintaxis: CMP destino. AF se pone a 1. convirtiéndolo también en un valor BCD empaquetado. de AL se resta 6. origen Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF x Resta origen de destino sin retornar ningún resultado. Si el nibble inferior tiene un valor mayor que 9 o AF es 1. el resultado (por medio de AAS) sigue siendo un número BCD no empaquetado. Los operandos quedan inalterados. Los operandos pueden ser de tipo byte o palabra pero ambos de la misma dimensión. entonces se resta 60h a AL y se activa después CF. mem_pal ch. paro los indicadores pueden ser consultados mediante instrucciones de bifurcación condicional.bl En el ejemplo. AF se pone a 1 y CF se iguala a AF. tras la resta de dos números BCD no empaquetados colocados en AL y BL. .JUEGO DE INSTRUCCIONES 80x86 51 Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF). Si el nibble mas significativo es mayor que 9 ó CF está a 1. a AL se le resta 6.cl DAS (ajuste decimal para la resta) Sintaxis: DAS Indicadores: OF DF IF TF SF x ZF x AF x PF x CF x Corrige el resultado en AL de la resta de dos números BCD empaquetados. Si el nibble inferior de AL tiene un valor mayor que 9. Ejemplo: cmp cmp bx.

Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF).dh SUB (resta) Sintaxis: SUB destino. origen . considerando lo que nos llevamos (el acarreo) de la resta anterior. Ejemplo: sbb sbb ax. de más de 16 bits. AT Y PS/2 Ejemplo: sub das al. origen Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF x Resta el operando origen del operando destino y el resultado lo almacena en el operando destino. DEC (decrementar) Sintaxis: DEC destino Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF - Resta una unidad del operando destino. DAS hace que el resultado de la resta (en AL) siga siendo también un BCD empaquetado. El operando puede ser byte o palabra.52 EL UNIVERSO DIGITAL DEL IBM PC. Se utiliza normalmente para restar números grandes. en varios pasos. Si está a 1 el indicador de acarreo además resta una unidad más.ax ch. Ejemplo: dec dec ax mem_byte NEG (negación) Sintaxis: NEG destino Indicadores: OF x DF IF TF SF x ZF x AF x PF x CF x Calcula el valor negativo en complemento a dos del operando y devuelve el resultado en el mismo operando. Ejemplo: neg al SBB (resta con acarreo) Sintaxis: SBB destino. si AL y BL contenían dos números BCD empaquetados. Los operandos pueden ser de tipo byte o palabra.bl En el ejemplo anterior.

un número BCD no empaquetado. colocando el resultado en el operando destino.JUEGO DE INSTRUCCIONES 80x86 53 Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Resta el operando destino al operando origen. Ejemplo: mul aam bl En el ejemplo. convirtiéndolo en un valor BCD también no empaquetado. En AH sitúa el cociente de AL/10 quedando en AL el resto de dicha operación.dx *** MULTIPLICACION *** AAM (ajuste ASCII para la multiplicación) Sintaxis: AAM Indicadores: OF ? DF IF TF SF x ZF x AF ? PF x CF ? Corrige el resultado en AX del producto de dos números BCD no empaquetados. Ejemplos: sub sub al. CF y OF son activados. IMUL (multiplicación entera con signo) Sintaxis: IMUL origen (origen no puede ser operando inmediato en 8086.bl dx. Si las mitades más significativas son distintas de cero. tras el producto de dos números BCD no empaquetados colocados en AL y BL. Ejemplo: imul imul bx ch MUL (multiplicación sin signo) Sintaxis: MUL origen (origen no puede ser operando inmediato) Indicadores: OF x DF IF TF SF ? ZF ? AF ? PF ? CF x Multiplica el contenido sin signo del acumulador por el operando origen. Si el . si «origen» es una palabra el resultado es devuelto en DX (parte alta) y AX (parte baja). independientemente del signo. byte o palabra. siendo necesario que sean del mismo tipo. sí en 286) Indicadores: OF x DF IF TF SF ? ZF ? AF ? PF ? CF x Multiplica un operando origen con signo de longitud byte o palabra por AL o AX respectivamente. Si «origen» es un byte el resultado se guarda en AH (byte más significativo) y en AL (menos significativo). el resultado (por medio de AAA) sigue siendo. Los operandos pueden tener o no signo. en AX.

Esta instrucción es necesaria ANTES de la operación de dividir. Cuando el operando origen es de longitud palabra el acumulador es AX quedando el resultado sobre DX y AX. la instrucción de dividir genera un resultado correcto. al contrario que AAM. DIV (división sin signo) Sintaxis: DIV origen (origen no puede ser operando inmediato) Indicadores: OF ? DF IF TF SF ? ZF ? AF ? PF ? CF ? Divide. un número contenido en el acumulador y su extensión (AH. si el valor de DX es distinto de cero los indicadores CF y OF se activan. Ejemplo: aad div bl En el ejemplo.54 EL UNIVERSO DIGITAL DEL IBM PC. cociente y resto quedan indefinidos produciéndose una interrupción 0. si el contenido de AH es distinto de 0 activa los indicadores CF y OF. AT Y PS/2 operando destino es un byte el acumulador es AL guardando el resultado en AH y AL. DX o AH deben ser cero antes de la operación. AL si el operando es de tipo byte o DX. AX si el operando es palabra) entre el operando fuente. El cociente se guarda en AL o AX y el resto en AH o DX según el operando sea byte o palabra respectivamente. sin considerar el signo. En caso de que las partes más significativas del cociente tengan un valor distinto de cero se activan los indicadores CF y OF. Cuando el cociente es mayor que el resultado máximo que puede almacenar. Tras la operación AH queda a cero. Ejemplo: mul mul mul byte ptr ds:[di] dx cl *** DIVISION *** AAD (ajuste ASCII para la división) Sintaxis: AAD Indicadores: OF ? DF IF TF SF x ZF x AF ? PF x CF ? Convierte dos números BCD no empaquetados contenidos en AH y AL en un dividendo de un byte que queda almacenado en AL. Ejemplo: div div bl mem_pal IDIV (división entera) Sintaxis: IDIV origen (origen no puede ser operando inmediato) Indicadores: OF ? DF IF TF SF ? ZF ? AF ? PF ? CF ? . tras convertir los dos números BCD no empaquetados (en AX) en un dividendo válido.

Si se indica un registro de segmento. El cociente se almacena en AL o AX según el operando sea byte o palabra y de igual manera el resto en AH o DX.JUEGO DE INSTRUCCIONES 80x86 55 Divide.INSTRUCCIONES DE MANIPULACIÓN DE CADENAS. Ejemplo: . CMPS/CMPSB/CMPSW (compara cadenas) Sintaxis: CMPS cadena_destino. Ejemplo: idiv idiv bl bx *** CONVERSIONES*** CBW (conversión de byte en palabra) 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. 4. «Cadena origen» y «cadena destino» son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar. 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. o cuando el cociente es negativo e inferior al valor mínimo que puede almacenarse (81h u 8001h) entonces cociente y resto quedan indefinidos. cadena_origen CMPSB (bytes) CMPSW (palabras) OF x DF IF TF SF x ZF x AF x PF x CF x Indicadores: Compara dos cadenas restando al origen el destino. Ninguno de los operandos se alteran. copiando el bit más significativo de AH en todo DX. es decir. éste sustituirá en la cadena origen al DS ordinario. Cuando el cociente es positivo y superior al valor máximo que puede almacenarse (7fh ó 7fffh). . es más cómodo colocar CMPSB o CMPSW para indicar bytes/palabras. considerando el signo. lo que también sucede si el divisor es 0. pero los indicadores resultan afectados.1. Los registros DI y SI se autoincrementan o autodecrementan según el valor del indicador DF (véanse CLD y STD) en una o dos unidades. CWD (conversión de palabra a doble palabra) Sintaxis: CWD Indicadores: OF DF IF TF SF ZF AF PF CF - Expande el signo del registro AX sobre el registro DX.7. DX o AH deben ser cero antes de la operación. dependiendo de si se trabaja con bytes o con palabras. expande el signo de AL a AX como paso previo a una operación de 16 bits. un número contenido en el acumulador y su extensión entre el operando fuente. generándose una interrupción 0.

destino cmpsb LODS/LODSB/LODSW (cargar cadena) Sintaxis: LODS cadena_origen LODSB (bytes) LODSW (palabras) OF DF IF TF SF ZF AF PF CF - Indicadores: Copia en AL o AX una cadena de longitud byte o palabra direccionada sobre el segmento de datos (DS) con el registro SI. SI se incrementa o decrementa según el indicador DF (véanse CLD y STD) en una o dos unidades.56 EL UNIVERSO DIGITAL DEL IBM PC.destino movsw SCAS/SCASB/SCASW (explorar cadena) Sintaxis: SCAS cadena_destino SCASB (bytes) SCASW (palabras) OF x DF IF TF SF x ZF x AF x PF x CF x Indicadores: . incrementando o decrementando a continuación los registros SI y DI según el valor de DF (véanse CLD y STD) en una o dos unidades. cadena_origen MOVSB (bytes) MOVSW (palabras) OF DF IF TF SF ZF AF PF CF - Indicadores: Transfiere un byte o una palabra de la cadena origen direccionada por DS:SI a la cadena destino direccionada por ES:DI. es más cómodo colocar MOVSB o MOVSW para indicar bytes/palabras. AT Y PS/2 lea si. Ejemplo: lea si. dependiendo de si se trabaja con bytes o con palabras. éste sustituirá en la cadena origen al DS ordinario. es más cómodo colocar LODSB o LODSW para indicar bytes/palabras. «Cadena_origen» es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar.origen lea di. «Cadena origen» y «cadena destino» son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar. según se estén manejando bytes o palabras. Si se indica un registro de segmento.origen lea di. Tras la transferencia. Ejemplo: cld lea lodsb si.origen MOVS/MOVSB/MOVSW (mover cadena) Sintaxis: MOVS cadena_destino.

DI se incrementa o decrementa según el indicador DF (véanse CLD y STD) para apuntar al siguiente elemento de la cadena.69 SCASB encontrado . «Cadena_destino» es un operando redundante que sólo indica el tipo del dato (byte o palabra). STOS CMPS. es más cómodo colocar SCASB o SCASW para indicar bytes/palabras.para apuntar al siguiente elemento de la cadena. Ejemplo: lea di. SCAS Ejemplos: 1) Buscar el byte 69 entre las 200 primeras posiciones de «tabla» (se supone «tabla» en el segmento ES): LEA MOV MOV CLD REPNE JE DI.tabla CX.50 STOS/STOSB/STOSW (almacena cadena) Sintaxis: STOS cadena_destino STOSB (bytes) STOSW (palabras) OF DF IF TF SF ZF AF PF CF - Indicadores: Transfiere el operando origen almacenado en AX o AL. es más cómodo colocar STOSB o STOSW para indicar bytes/palabras.destino mov ax. al destino direccionado por el registro DI sobre el segmento extra. El número de veces se indica en CX. Tras la operación. «Cadena_destino» es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar.200 AL. Ejemplo: lea mov scasb di.1991 stosw REP/REPE/REPZ/REPNE/REPNZ (repetir) REP REPE/REPZ REPNE/REPNZ repetir operación de cadena repetir operación de cadena si igual/si cero repetir operación de cadena si no igual (si no 0) Estas instrucciones se pueden colocar como prefijo de otra instrucción de manejo de cadenas.JUEGO DE INSTRUCCIONES 80x86 57 Resta de AX o AL una cadena destino direccionada por el registro DI sobre el segmento extra. con objeto de que la misma se repita un número determinado de veces incondicionalmente o hasta que se verifique alguna condición. Ninguno de los valores es alterado pero los indicadores se ven afectados.destino al. Por sentido común sólo deben utilizarse las siguientes combinaciones: Prefijo ----------REP REPE/REPZ REPNE/REPNZ Función ------------------------------Repetir CX veces Repetir CX veces mientras ZF=1 Repetir CX veces mientras ZF=0 Instrucciones ---------------MOVS. SCAS CMPS. DI se incrementa o decrementa según el valor de DF (véanse CLD y STD) en una o dos unidades -según se esté trabajando con bytes o palabras.

0B800h DS. Ejemplo: or ax.buffer SI.0 CX. . AND (y lógico) Sintaxis: AND destino.1. segmento de pantalla en DS destino en ES:DI copiar desde DS:0 2000 palabras hacia adelante copiar CX palabras 4. .0 CX. .2000 MOVSW . Ejemplos: and and ax.byte ptr es:[si+10h] NOT (no lógico) Sintaxis: NOT destino Indicadores: OF DF IF TF SF ZF AF PF CF - Realiza el complemento a uno del operando destino. . Los indicadores no resultan afectados.2500 STOSW 3) Copiar la memoria de pantalla de texto (adaptador de color) de un PC en un buffer (se supone «buffer» en el segmento ES): MOV MOV LEA MOV MOV CLD REP CX. almacenándose después el resultado en el operando destino.datos AX. AT Y PS/2 2) Rellenar de ceros 5000 bytes de una tabla colocada en «datos» (se supone «datos» en el segmento ES): LEA MOV MOV CLD REP DI. origen Indicadores: OF 0 DF IF TF SF x ZF x AF ? PF x CF 0 Realiza una operación de Y lógico entre el operando origen y destino quedando el resultado en el destino.CX DI.8.INSTRUCCIONES DE OPERACIONES LÓGICAS A NIVEL DE BIT. .58 EL UNIVERSO DIGITAL DEL IBM PC. invirtiendo cada uno de sus bits.bx bl. . origen Indicadores: OF 0 DF IF TF SF x ZF x AF ? PF x CF 0 Realiza una operación O lógico a nivel de bits entre los dos operandos. . Son válidos operandos byte o palabra.bx . Ejemplo: not ax OR (O lógico) Sintaxis: OR destino. pero ambos del mismo tipo.

Ejemplo: test al. Los indicadores son afectados con la operación.ax 4. origen Indicadores: OF 0 DF IF TF SF x ZF x AF ? PF x CF 0 Realiza una operación Y lógica entre los dos operandos pero sin almacenar el resultado. y abre al dispositivo externo el acceso a las direcciones y operandos requeridos. NOP (operación nula) Sintaxis: NOP Indicadores: OF DF IF TF SF ZF AF PF CF - Realiza una operación nula.INSTRUCCIONES DE CONTROL DEL PROCESADOR.bh XOR (O exclusivo) Sintaxis: XOR destino. es decir.1.9.AX. tales como los coprocesadores de coma flotante o de E/S. Ejemplo: xor di. . origen Indicadores: OF 0 DF IF TF SF x ZF x AF ? PF x CF 0 Operación OR exclusivo a nivel de bits entre los operandos origen y destino almacenándose el resultado en este último.ax HLT (parada hasta interrupción o reset) Sintaxis: HLT . Realmente se trata de la instrucción XCHG AX. origen Indicadores: OF DF IF TF SF ZF AF PF CF - Se utiliza en combinación con procesadores externos. el microprocesador decodifica la instrucción y pasa a la siguiente. Ejemplo: esc 21.JUEGO DE INSTRUCCIONES 80x86 59 TEST (comparación lógica) Sintaxis: TEST destino. ESC (salida a un coprocesador) Sintaxis: ESC código_operación. Al mnemónico ESC le siguen los códigos de operación apropiados para el coprocesador así como la instrucción y la dirección del operando necesario.

10.60 EL UNIVERSO DIGITAL DEL IBM PC. en bytes. Como en los PC se producen normalmente 18. Suele preceder a ESC para sincronizar las acciones del procesador y coprocesador. el método no es preciso y puede fallar con ciertos controladores de memoria. cuando el copro ha terminado una operación e indica su finalización. Ocurre. RCL (rotación a la izquierda con acarreo) Sintaxis: RCL destino.INSTRUCCIONES DE ROTACIÓN Y DESPLAZAMIENTO.1. Cuando una instrucción va precedida por LOCK. No es conveniente que CL sea mayor de 7. el procesador bloquea inmediatamente el bus. en caso contrario el valor debe cargarse en CL y especificar CL como segundo operando. en palabras. introduciendo una señal por la patilla LOCK.2 interrupciones de tipo 8 por segundo (del temporizador) algunos programadores utilizan HLT para hacer pausas y bucles de retardo. se puede especificar directamente.1 .1 al. Sin embargo. ó 15. Si el número de bits a desplazar es 1. .cl di. por ejemplo. contador Indicadores: OF x DF IF TF SF ZF AF PF CF x Rotar a la izquierda los bits del operando destino junto con el indicador de acarreo CF el número de bits especificado en el segundo operando. AT Y PS/2 Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - El procesador se detiene hasta que se restaura el sistema o se recibe una interrupción. LOCK (bloquea los buses) Sintaxis: LOCK Indicadores: OF DF IF TF SF ZF AF PF CF - Es una instrucción que se utiliza en aplicaciones de recursos compartidos para asegurar que no accede simultáneamente a la memoria más de un procesador. CF alto bajo RCL Ejemplos: rcl rcl rcl ax. 4. WAIT (espera) Sintaxis: WAIT Indicadores: OF DF IF TF SF ZF AF PF CF - Provoca la espera del procesador hasta que se detecta una señal en la patilla TEST.

contador Indicadores: OF x DF IF TF SF ZF AF PF CF x Rotar a la derecha los bits del operando destino junto con el indicador de acarreo CF el número de bits especificado en el segundo operando.cl ah. en caso contrario debe ponerse a través de CL. en caso contrario su valor debe cargarse en CL y especificar CL como segundo operando: alto bajo CF RCR Ejemplos: rcr rcr bx.1 ax. que puede ser 1 ó CL previamente cargado con el valor del número de veces. Si el número de bits es 1 se puede poner directamente.cl bx.cl .JUEGO DE INSTRUCCIONES 80x86 61 RCR (rotación a la derecha con acarreo) Sintaxis: RCR destino. Si el número de bits es 1 se puede especificar directamente. contador Indicadores: OF x DF IF TF SF ZF AF PF CF x Rota a la derecha los bits del operando destino el número de bits especificado en el segundo operando.1 ROR (rotación a la derecha) Sintaxis: ROR destino. contador Indicadores: OF x DF IF TF SF ZF AF PF CF x Rota a la izquierda los bits del operando destino el número de bits especificado en el segundo operando. CF alto bajo ROL Ejemplos: rol rol dx. alto bajo CF ROR Ejemplos: ror ror cl.1 ROL (rotación a la izquierda) Sintaxis: ROL destino.

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: 0 alto bajo CF SHR Ejemplos: shr shr ax. contador Indicadores: OF x DF IF TF SF x ZF x AF ? PF x CF x Desplaza a la derecha los bits del operando destino el número de los bits especificados en el segundo operando. si es mayor se especifica a través de CL.cl cl.1 SHR (desplazamiento lógico a la derecha) Sintaxis: SHR destino. Los bits de la izquierda se llena con cero.cl bp. AT Y PS/2 SAL/SHL (desplazamiento aritmético a la izquierda) Sintaxis: SAL/SHL destino.1 .cl bajo 0 SAL/SHL SAR (desplazamiento aritmético a la derecha) Sintaxis: SAR destino. contador Indicadores: OF x DF IF TF SF x ZF x AF ? PF x CF x Desplaza a la derecha los bits del operando destino el número de bits especificado en el segundo operando. CF Ejemplos: alto shl sal dx. Los bits de la izquierda se rellenan con el bit de signo del primer operando.1 bx. contador Indicadores: OF x DF IF TF SF x ZF x AF ? PF x CF x Desplaza a la izquierda los bits del operando el número de bits especificado en el segundo operando que debe ser CL si es mayor que 1 los bits desplazados.62 EL UNIVERSO DIGITAL DEL IBM PC. alto bajo CF SAR Ejemplos: sar sar ax. Si el número de bits a desplazar es 1 se puede especificar directamente.

.x x x x x 0 .fnt LOCK LODS/LODSB/ LODSW cfnt LOOP LOOPcc (LOOPE..fnt LES dst....fnt cdst. ÍNDICE.fnt ADD dst......fnt CMPS/CMPSB CMPSW cdst.JUEGO DE INSTRUCCIONES 80x86 63 4.fnt fnt fnt acum..... 49 54 53 51 50 50 58 46 55 43 43 44 44 51 55 55 50 51 52 54 59 59 54 53 49 50 48 48 48 47 46 47 43 42 42 43 60 56 47 48 41 56 53 52 59 58 58 49 45 45 45 45 dst.x x x x x x .) JMP JCXZ dsp LAHF LDS dst..RESUMEN ALFABÉTICO DE LAS INSTRUCCIONES Y BANDERINES.x x ? x ? ? .fnt mem dsp dsp dst..fnt dst. .cfnt MUL fnt NEG dst NOP NOT dst OR dst..fnt port..) MOV dst....? ? x ? x ? ..acum dst dst .x x ? x ? ? .fnt dst.x x ...x x ? x 0 ..0 .....0 ....port INC dst INT interrup INTO IRET Jcc (JA.0 .port dst interrup dsp dsp dsp dst.fnt dsp dst.fnt AND dst..cfnt CWD DAA DAS DEC dst DIV fnt ESC opcode...... Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguiente notación: ... JBE.....fnt cdst.2...bit no modificado ? desconocido o indefinido x modificado según el resultado de la operación 1 puesto siempre a 1 0 puesto siempre a 0 Instrucción AAA AAD AAM AAS ADC dst...cfnt dst dst opcode...acum POP dst POPF PUSH dst PUSHF Sintaxis AAA AAD AAM AAS ADC ADD AND CALL CBW CLC CLD CLI CMC CMP CMPS CWD DAA DAS DEC DIV ESC HLT IDIV IMUL IN INC INT INTO IRET Jcc JMP JCXZ LAHF LDS LEA LES LOCK LODS LOOP LOOPcc MOV MOVS MUL NEG NOP NOT OR OUT POP POPF PUSH PUSHF Efecto sobre los flags OF DF IF TF SF ZF AF PF CF ? ..cfnt fnt fnt dst dst.fnt LEA dst....fnt dst....fnt HLT IDIV fnt IMUL fnt IN acum..fnt CALL dsp CBW CLC CLD CLI CMC CMP dst..? ? x ? x x .fnt dst..x x x x x x ? x ? ? x x x x x 0 x x x 0 0 x x 0 0 x x x x x x ? ? ? x x ? x x x x x x x ? ? ? x x ? x x x x x x x ? ? ? x x ? x ? x x x x x ? ? ? x x ? x x x x x x ? ? x x x x 0 x pág..fnt MOVS/MOVSB/ MOVSW cdst.fnt OUT port..

fnt SCAS/SCASB/ SCASW cdst SHR dst.Códigos de operación indefinidos. se ejecuta.fnt dst.3.. . si es una instrucción con sentido para estos procesadores. 386 y 486 EN MODO REAL.cnt SAR dst. en el 8086 es el de SP después del PUSH (dos unidades menos).cnt Efecto sobre los flags OF DF IF TF SF ZF AF PF CF x .fnt 4.cnt dst. En el 286 y superiores.. .cnt SBB dst.fnt WAIT XCHG dst. ..x x x x x x x x x 0 0 1 1 x x x x x x x x x x x x x x x x x x x ? ? x x ? x ? ? x x x x x x x x x x x x x x x x x 1 x 0 0 pág.cnt ROR dst. 4. se . Por tanto.INSTRUCCIONES ESPECIFICAS DEL 286.x x . En el 286 y superiores se produce una excepción 6 (INT 6) o.cnt RCR dst.. .3.1. provocan que en la pila se almacene el valor de CS:IP para la siguiente instrucción en el 8086.Desplazamientos y rotaciones.cnt dst.. debidas a una división por cero o a un cociente excesivamente grande. En el 286 y superiores se almacena el CS:IP de la propia instrucción que causa la excepción. Un acceso de 16 bits en el offset 0FFFFh en el 8086 provoca un acceso a los bytes ubicados en las posiciones 0FFFFh y 0 (se da la vuelta alrededor del segmento). .cnt dst. Las instrucciones tienen una longitud ilimitada en el 8086..DIFERENCIAS EN EL COMPORTAMIENTO GLOBAL RESPECTO AL 8086.fnt TEST dst.. El valor que introduce en la pila en el 286 y superiores es el de SP antes del PUSH. .64 EL UNIVERSO DIGITAL DEL IBM PC.Prefijos redundantes.cnt dst.fnt tfnt dst.fnt Sintaxis RCL RCR REP RET RETF ROL ROR SAHF SAL SAR SBB SCAS SHR STC STD STI STOS SUB TEST WAIT XCHG XLAT XOR dst.fnt cdst dst.fnt XLAT tfnt XOR dst.. AT Y PS/2 Instrucción RCL dst.Excepciones de división: Las excepciones INT 0. .cnt [val] [val] dst. en el 286 y superiores no pueden exceder de 15 bytes. en el 286 y superiores se toma módulo 32 (sólo se consideran los 5 bits menos significativos).Accesos al límite del segmento.. .cnt SAHF SAL/SHL dst.cnt dst...cnt STC STD STI STOS/STOSB/ STOSW cdst SUB dst.cnt REP/REPE/REPZ/ REPNE/REPNZ RET [val] RETF [val] ROL dst. 60 61 57 47 47 61 61 43 62 62 52 56 62 44 44 45 57 52 59 60 41 42 59 cdst dst.Valor de PUSH SP. El valor de desplazamiento en las operaciones de manipulación de bits del 8086 es una constante de 8 bits (indicada en CL). los prefijos redundantes pueden producir excepciones de código de operación no válido.fnt dst.. El 8086 se estrella.

al final del primer megabyte se vuelve a comenzar por las posiciones más bajas de la memoria. La prioridad de la excepción paso a paso en el 286 y superiores es más alta que la de una interrupción externa. En el 8086 la memoria es circular. así como con los procesadores superiores al 286. . si existe. También se admiten tres operandos: IMUL r1.2. las interrupciones externas no pueden ser traceadas. ENTER crea una estructura de pila para un procedimiento de alto nivel. Al producirse una excepción de error de coprocesador. . . Difiere algo en los bits 12 al 15 en todos los procesadores. pero puede ser solventado). 4. devolviendo un resultado palabra (CF=1 si no cabe en 16 bits). . en el 8086 cualquier vector. una NMI no puede interrumpir una rutina de tratamiento NMI. En el 286 y superiores.Error del coprocesador.3. Los límites de la matriz los definen dos palabras consecutivas en la memoria apuntadas por mem16. Si está fuera de los límites. imm.25. en el 286 y superiores sí. siendo específicas de la conmutación de procesos y tratamiento de la memoria virtual y no pueden emplearse directamente bajo DOS. r2.Ejecución paso a paso.LOCK. por ejemplo. En el 386 y superiores su uso está restringido a determinadas instrucciones. mem16: Comprueba si el registro de 16 bits indicado como primer operando está dentro de los límites de una matriz.Interrupción NMI. por tanto. Las instrucciones del modo protegido se dirigen especialmente a la multiprogramación y el tiempo compartido. se produce una interrupción 5 en la que el IP apilado queda apuntando a la instrucción BOUND (¡no se incrementa!). IMUL puede multiplicar cualquier registro de 16 bits por una constante inmediata. . BOUND r16. Las instrucciones PUSH permiten meter valores inmediatos a la pila: es válido hacer PUSH 40h. Esta instrucción no está limitada de ninguna manera en el 8086 y en el 286. Esto se cumple tanto para accesos a datos en memoria como a instrucciones del programa en esos puntos críticos.Instrucciones de cadena repetitivas.JUEGO DE INSTRUCCIONES 80x86 65 produce una excepción de violación de límites. En el 286 y superiores se utiliza el vector 16. A continuación se describen las instrucciones adicionales que incorporan los 286 en modo real. El CS:IP grabado en el 8086 no incluye el prefijo.INSTRUCCIONES ESPECIFICAS DEL 286. . . que también pueden ser consideradas cuando trabajamos con los microprocesadores compatibles V20 y V30. Desde el 286 y superiores.Prefijos de las instrucciones del coprocesador. En este caso. . el 386 dispone además de un registro de flags de 32 bits.Registro de FLAGS. En el 386 y superiores se produce también en accesos de 32 bits en las posiciones 0FFFDh a la 0FFFFh. se accede a la memoria extendida (un artificio hardware en los PC lo impide al forzar A20 a estado bajo. en el 8086 se almacena un CS:IP que no incluye prefijos -si los había-.Límite del primer megabyte. se multiplica r2 por el valor inmediato . . es válido IMUL CX. al contrario que en el 286 y superiores.

En cambio. en caso contrario se almacena en el primer operando la posición relativa de ese bit: MOV AX. PUSHA/POPA: Introduce en la pila y en este orden los registros AX. es válida la instrucción MOV AL. Sin embargo no deben intentarse direccionamientos por encima de los 64K. Sintaxis (ejemplo sobre BT): . tales como EAX. Además. se pueden utilizar para acelerar las operaciones pero no para acceder a más memoria. no se saltará. ECX. 4. Por ejemplo. ES. Tanto r1 como r2 han de ser de 16 bits.[EDX+EAX*8]. Ideal en el manejo de interrupciones y muy usada en las BIOS de 286 y 386. Se comienza a explorar por el bit 0 (BSF) o por el más significativo (BSR) del segundo operando: si no aparece ningún bit activo (a 1) el indicador ZF se activa. BOUND r32. estos procesadores cuentan con dos segmentos más: además de DS.AX JZ ax_es_0 . comprobación y puesta a 1. EBX. CS y SS se pueden emplear también FS y GS. el segundo puede estar multiplicado por 2. Los desplazamientos en el direccionamiento indexado con registros de 32 bits pueden ser de 8 y también de 32 bits. RCR/RCL. el 386 y el 486 permiten utilizar cualquier registro de 32 bits de propósito general en todos los modos de funcionamiento. EDI. En otras palabras. comprobación y puesta a 0. reg ó ó BSF reg.8 BSF BX. ROR/ROL. por ejemplo. con los de 32 se puede utilizar en el direccionamiento indirecto cualquier registro: es válida. además BX = 3 BT/BTC/BTR/BTS: Operaciones sobre bits: comprobación. la instrucción MOV AX.[EBX] tendría un resultado impredecible. LEAVE abandona los procedimientos de alto nivel (equivale a MOV SP. Por supuesto.3. por ejemplo. EDX.6 hay un ejemplo de ello. Cuando dos registros deben sumarse para calcular la dirección efectiva. Con los registros de 16 bits sólo están disponibles los modos tradicionales. BSF/BSR: Exploración de bits hacia adelante y atrás. reg BSR reg. DX. SP.3. EBP. SAL/SAR y SHL/SHR admiten una constante de rotación distinta de 1. [memoria] BSR reg. AT Y PS/2 (8/16 bits) y el resultado se almacena en r1. mem32: Se admiten ahora operandos de 32 bits. 4 u 8. SI y DI -o los saca en orden inverso-. Nota: No es del todo cierto que el 386 y el 486 no permitan acceder a más de 64 Kb en modo real: en la sección 4. bajo DOS hay que asegurarse siempre que el resultado de todas las operaciones que determinan la dirección efectiva no excede de 0FFFFh (0FFFEh si se accede a palabras y 0FFFCh en accesos a dobles palabras en memoria). Aviso: parece ser que en algunos 386 fallan ocasionalmente las instrucciones de multiplicar de 32 bits. Además de todas las posibilidades adicionales del 286. respectivamente.BP / POP BP). OUTS (salida de cadenas) e INS (entrada de cadenas) repetitivas (equivalente a MOVS y LODS).[EAX]. CX.66 EL UNIVERSO DIGITAL DEL IBM PC. ESI. incluido el modo real. comprobación y complementación. BX. .[ECX] o MOV EDX. BP. [memoria] Donde reg puede ser de 16 ó 32 bits.3. si EBX > 0FFFFh. una instrucción del tipo MOV AX. La sintaxis es: BSF reg. Los modos de direccionamiento aumentan notablemente su flexibilidad en el 386 y superiores.INSTRUCCIONES PROPIAS DEL 386 Y 486.

386 en los programas en que se desee mantener la compatibilidad con procesadores anteriores.reg y los recíprocos: acceso a registros de control y depuración. Si el primer operando es de 16 bits. Toma el segundo operando. el segundo puede ser un registro u operando en memoria (nunca inmediato): MOV EAX.reg / MOV DRx.[dato] INSD: Similar a INSW pero empleando ESI. resultado: EAX = 0FFFF7FFFh MOVSX EAX.16 BTC AX.7FFFh . imm8 Donde reg puede ser de 16 ó 32 bits. Jcc: Los saltos condicionales ahora pueden ser de ¡32 bits!. JECXZ se utiliza en vez de JCXZ (mismo código de operación). resultado: CF = 1 y AX = 0 CDQ: Similar a CWD.4 . el segundo sólo puede ser de 8. LGS: similar a LDS o LES pero con esos registros de segmento. A continuación no le hacen nada a ese bit (BT). EDI. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. IMUL: Ahora se admite un direccionamiento a memoria en el 2º operando: IMUL CX. EDI. Se puede emplear bajo DOS siempre que ESI y EDI (usando REP también ECX) no rebasen 0FFFFh. Operando sobre la memoria de vídeo sólo se obtiene ventaja si la tarjeta es realmente de 32 bits. el operando inmediato es necesariamente de 8. LODSD: Similar a LODSW pero empleando ESI. reg ó BT reg.AX . Se puede emplear bajo DOS para acelerar las transferencias siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. . LFS. Prefijos FS: y GS: en los accesos a memoria. lo complementan (BTC). ECX y comparando datos de 32 bits. Estas instrucciones copian el número de bit del primer operando que indique el segundo operando (entre 0 y 31) en el acarreo. Mucho cuidado con la directiva .0FFFFFFFFh MOV AX. LSS. MOVSD: Similar a MOVSW pero empleando ESI. ECX y leyendo datos de 32 bits. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. EDI.JUEGO DE INSTRUCCIONES 80x86 67 BT reg. EDI y ECX y cargando datos de 32 bits en EAX. EDI. MOV CRx. referenciando a esos segmentos. 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 primero es de 32 bits el segundo puede ser de 8 ó 16. Ejemplo: MOV AX. resultado: EAX = 000007FFFh OUTSD: Similar a OUTSW pero empleando ESI. ECX y enviando datos de 32 bits. extiende el signo de EAX a EDX:EAX. lo borran (BTR) o lo activan (BTS). CMPSD: Similar a CMPSW pero empleando ESI. MOVSX / MOVZX: carga con extensión de signo o cero. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. El primer operando debe ser un registro. CWDE: Extiende el signo de AX a EAX. ECX y moviendo datos de 32 bits.

Para averiguar el procesador de un ordenador puede emplearse el siguiente programa de utilidad. EDI. a 0). reg16. SCASD: Similar a SCASW pero empleando ESI. borrar nibble más significativo AX . el segundo controlador de interrupciones) que de antemano se sabe que sólo equipan máquinas AT o superiores.5678h SHLD AX. La instrucción POPAD falla en la mayoría de los 386. Una vez desplazado. resultado: AX=2345h. CL SHLD regmem32. ECX y buscando datos de 32 bits. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. de cara a evitar ciertas instrucciones que podrían bloquear un PC o XT. reg32. imm8 ó SHLD regmem32. seguirán valiendo 1 excepto en un 80286 o superior . ECX y almacenando EAX.0Fh . aunque sólo sea para decir al usuario que se compre una máquina más potente antes de abortar la ejecución del programa. Para solventar el fallo (que consiste en que EAX no se restaura correctamente) basta colocar un NOP inmediatamente detrás de POPAD.3.4 .1234h MOV BX. MOV AX.5. con el acarreo activo. Lo ideal es verificar directamente si está instalado un 286 o superior. además. La sintaxis es (ejemplo sobre SHLD): SHLD regmem16. Por ejemplo. PUSHFD/POPFD introducen y sacan de la pila los flags de 32 bits. reg32. . se desplaza el primer operando a la izquierda tanto como indique el tercer operando (contador). incluidos los de AMD.EVALUACIÓN EXACTA DEL MICROPROCESADOR INSTALADO. BX=5678h STOSD: Similar a STOSW pero empleando ESI. SHRD es análogo pero al revés. AX = flags AH. . es 286 o superior 4.0F0h no_es_AT si_es_AT . la evolución futura que tengan es impredecible. Hay casos en los que es necesario determinar si una máquina es AT o superior: no ya de cara a emplear instrucciones propias del 286 en modo real (también disponibles en los V20/V30 y 80188/80186) sino debido a la necesidad de acceder a ciertos chips (por ejemplo. SETC AL pone a 1 el registro AL. PUSHF POP AND PUSH POPF PUSHF POP AND CMP JE JMP AX . indistintamente. Es importante por tanto determinar la presencia de un AT. AT Y PS/2 PUSHAD / POPAD: Similares a PUSHA y POPA pero con los registro de 32 bits. se pone a 1 el byte de memoria o registro de 8 bits indicado (si no. No se debe en estos casos comprobar los bytes de la ROM que identifican el equipo: a veces no son correctos y.DETECCIÓN DE UN SISTEMA AT O SUPERIOR. 4. SETcc reg8 ó mem8: Si se cumple la condición cc. . En el caso de SHLD. del tamaño indicado. intentar poner a 0 los 4 bits más significativos de los flags AX AH.BX. basado en el procedimiento procesador? que devuelve en AX un código numérico entro 0 y 8 distinguiendo entre los 9 procesadores más difíciles de identificar de los ordenadores compatibles. Se puede emplear bajo DOS siempre que ESI y EDI (usando REP también ECX) no rebasen 0FFFFh.0F0h AH. EDI.68 EL UNIVERSO DIGITAL DEL IBM PC. los bits menos significativos se rellenan con los más significativos del segundo operando.3.4. CL Donde regmem es un registro u operando en memoria. que no resulta alterado. reg16. Sobra decir que las instrucciones avanzadas deben ser utilizadas con la previa comprobación del tipo de procesador. SHLD / SHRD: Desplazamiento de doble precisión a la izquierda/derecha. imm8 ó SHLD regmem16.

3-NEC V30. en un 8086/80186/V30 (y no en un INC CX . Esta instrucción está documentada. supuesto un V20 . 2/3-NEC V20/V30 ENDP . .. un 80286 o superior JE ni286ni_super PUSHF .i286.0FFFFh STI DB 0F3h.7000h . intentar cambiar este bit PUSHFD POP EAX . imprimirlo tipo de procesador en AX guardarlo para el final tabla de nombres-2 número de iteración-1 tipo_bus_proc: cpu inicio: LEA MOV INT CALL PUSH LEA MOV INC ADD MOV CALL CMP JNE LEA CALL LEA CALL CMP JBE LEA CALL MOV INT otro_proc: . es un 386 (DL=7) . por ejemplo en alguno de los ficheros que acompañan al Interrupt List. .9 21h procesador? AX BX.JUEGO DE INSTRUCCIONES 80x86 69 Nota: el 486 no tiene que tener coprocesador necesariamente (el 486sx carece de él)." 13. borrar parte alta de ESP AND ESP. borrar nibble más significativo PUSH AX POPF . es 486: DL=8 (bit 18 cambió) JMP cpu_hallada MOV DL.. ya que hasta los siguientes capítulos no será explicada la sintaxis del lenguaje.386 ORG 100h DX. durante la rutina se guardará MOV ES. 1-8086.ESP . opcode de STI MOV CX.AX . ¿procesador del equipo? . calcular bus (V20/V30) XOR DL.Valladolid * * * * Este programa determina el tipo de microprocesador del equipo * * y devuelve un código ERRORLEVEL indicándolo: * * * * 0-8088. .10.CS MOV DS.9. 286 pone bits 12. últimos caracteres . ECX conserva el bit inicial XOR EAX. poder emplear dicha instrucción.7000h . En ese caso. ."$" inicio .i186. .DL . supuesto un 286 (DL=6) . preservar ESP en EDX AND ESP. intentar activar bit 12.40000h . De este modo.0FFFCh .apuntador_txt print DX. . fin de programa no_es_este: procesador? PROC . mover bit 18 a bit 0 AND EAX.10 13. el bus ya era supuesto de 8 bits INC DL ." El microprocesador de este " "equipo es compatible:". .i86. 8088/80188/V20) porque está en la STI . . resulta que es de 16 MOV AL.9 21h CX BX AX print cpus_indice i88 i86 v20 v30 i188 i186 i286 i386 i486 apuntador_txt texto_ini i88.BYTE PTR DS:tipo_bus_byte . guardar flags en pila (32 bits) POP EAX .DL XOR AH.separador_txt print CX. 13 y 14 a cero JZ cpu_hallada .10. bit 18 de EAX a 1 si cambió SHR EAX. Algunas versiones de procesador 486 y todos los procesadores posteriores soportan la instrucción CPUID que permite identificar la CPU.10.tipo_bus_dest MOV AL. transferencias hacia arriba LEA DI. 8-486 * * * * Aviso: Utilizar TASM 2.4 ..AH POP SI POP DI POP DX POP CX POP ES POP DS POPF RET . retornar código errorlevel AL .0FFFFh CX BX.7 otro_proc DX. PUSHF POP AX . DS:cpu . Es normal que el lector recién iniciado en el ensamblador no entienda absolutamente nada de este programa. ******************************************************************** * * * CPU v2. . 2-NEC V20.0FFFFh . . calcular bus (188/186) MOV DL. .v20. . opcode de REPZ LODSB ES: JCXZ tipo_bus_proc . con STOSB pero aún se ejecutará NOP .0ACh .i386. restaurar bit 18 de los flags MOV ESP.. cola de lectura adelantada. . no lo es.9. * * * ******************************************************************** ni286ni_super: SEGMENT ASSUME CS:cpu.[BX] print CX. . resulta trivial detectar el Pentium o cualquier procesador posterior que aparezca. nombre del primer procesador .26h.AX no_es_este DX.EDX . momento crítico MOV EDX..CL . significativos de los flags POP AX AND AX.i188. AX = CPU: 0/1-8088/86. 6-286. AX = flags AND AX. lo es. es 286 o superior POP AX OR AX. .texto_ini AH.3 CLI REP STOSB .2 . de momento PUSH DX CLI . transferir tres bytes CLD NOP . sí lo es: indicarlo tipo_bus_byte: tipo_bus_dest: . recordar que las instrucciones específicas del 286 en modo real también están disponibles en los V20/V30 de NEC y la serie 80188/80186 de Intel. 7-386. ya sólo puede ser un 8088/8086 STD .0FFFFh MOV CL. .1 . permitir interrupciones de nuevo POP DX . 6-286. es 386: DL=7 (bit 18 no cambió) INC DL .cpus_indice-2 CX.6 . . 4/5-80188/186. Para los propósitos de este libro no es preciso en general detectar más allá del 386..ECX . MOV CX.."CPU Test v2. . devolver el tipo de microprocesador en AX PUSHF PUSH DS PUSH ES PUSH CX PUSH DX PUSH DI PUSH SI MOV AX. 5-80186.AX . recuperar flags en EAX MOV ECX.10.. ...9..v30. número de CPUs tratadas-1 cpu_hallada: . MOV AX. 7-386. el INC CX (1 byte) será machacado NOP .EAX XOR EAX. 8-486 PROC PUSH PUSH PUSH MOV INT POP POP POP RET ENDP DW DB DB DB DB DB DB DB DB DB DB LABEL DB DB DB DB DB DB ENDS END AX BX CX AH.texto_fin print AH. es un 286 (DL=6) INC DL .12h .0 JE cpu_hallada . recuperar tipo de CPU en DL CMP AX.. el tipo de procesador en DL: MOV DL. restaurar ESP STI .0FFFh . conmutar bit 18 PUSH EAX procesador? print POPFD . dejar sólo ese bit PUSH ECX POPFD .4Ch 21h . seguirán valiendo 1 excepto en CMP AX. intentar poner a 0 los 4 bits más PUSHF . en ese caso.2 " "(c) Septiembre 1992 Ciriaco García de Celis. * * 4-80188. (80188/80186 toman CL mod 32) JNZ tipo_bus_proc .2 DX. máxime si no tiene previsto trabajar con otras instrucciones que no sean las del 8086. .0F000h . puede saltarse este ejemplo y continuar en el capítulo siguiente.0 o compatible exclusivamente. STI JCXZ cpu_hallada .2 (c) Septiembre 1992 CiriSOFT * * (c) Grupo Universitario de Informática . Basta comprobar un bit del registro de estado para saber si está soportada y. 13 ó 14 PUSH AX POPF PUSHF POP AX AND AX. Por último.i486 "Intel 8088 $" "Intel 8086 $" " NEC V20 $" " NEC V30 $" "Intel 80188$" "Intel 80186$" "Intel 80286$" "Intel 80386$" "Intel 80486$" " < $" separador_txt texto_fin cpu BYTE 13. texto de saludo . supuesto un 80188 . forzar ESP a múltiplo de 4 PUSHFD .0F000h ."$" 13.33 SHL AX. lo es.

Consúltese al efecto la bibliografía recomendada del apéndice. el bit 21º (de los 32 bits de la dirección de memoria) suele estar forzado a 0 por defecto al arrancar. AT Y PS/2 4. GDT SEGMENT USE16 ASSUME CS:segmento.0ffh. -----------. . Arrancando sin controlador de memoria (excepto HIMEM) no habrá problema alguno.OFFSET gdt CS:[gd2].0B8000h .0cfh.6. cargar tabla global de descriptores .0. El problema es que pasar a modo protegido no es sencillo cuando la máquina ya está en modo protegido emulando al modo real (el conocido como modo virtual 86). . activa la línea A20 (necesario hacerlo directamente .EAX SHORT $+2 BX. . . desplazamiento de GDT . . dicho de otro modo.0. No solo es factible de este modo saltar la restricción de 64 Kb. . borrar cola de prebúsqueda .BX AL. .SS EAX.MODO PLANO (FLAT) DEL 386 Y SUPERIORES.0. . El problema es que al encender el ordenador.0.BX ES. dirección de vídeo absoluta CX. las áreas de memoria extendida afectadas).CS EAX.CR0 AL.BX FS.0.2000 BYTE PTR [EBX]. QEMM) o dentro de Windows 3. Para acceder a la memoria de vídeo esto no es problema. No se . a toda la memoria por encima del primer megabyte. sería relativamente sencillo habilitar la línea A20 directamente o a través de una función del controlador XMS.’A’ EBX BYTE PTR [EBX].0 $-OFFSET gdt .0. Por tanto.EAX CS:[gdtr] EAX. Naturalmente. Entonces se consigue el llamado modo flat o plano.9fh.EAX AX.AX DS. desde el modo real.3. .x. TASM flat386 /m5 TLINK flat386 /t /32 . DS:segmento ORG 100h flat386 . flat386 PROC PUSH PUSH PUSH PUSH PUSH MOV XOR MOV SHL ADD MOV CLI LGDT MOV OR MOV JMP MOV MOV MOV MOV MOV MOV AND MOV JMP MOV STI POP POP POP POP POP RET DS ES EAX BX CX CX. . .0 0ffh.EAX SHORT $+2 SS. .0.CX CX BX EAX ES DS . fin de programa prueba: CALL XOR MOV MOV MOV MOV INC MOV INC LOOP INT prueba llena_pant: . sino que además se puede acceder directamente. datos para cargar en GDTR 0.70 EL UNIVERSO DIGITAL DEL IBM PC.0 $-OFFSET gdt 0ffh. El programa de ejemplo se limita a llenar la pantalla de texto (empleando ahora la dirección absoluta 0B8000h a través de EBX) de letras ’A’. dirección lineal de segmento CS .386p segmento .93h. volver a modo real . . el siguiente programa de ejemplo no funciona si está cargado un controlador de memoria expandida (EMM386. De todos modos.1 CR0. el 386 tiene definidos por defecto dichos límites de 64 Kb. se sale de los objetivos de este libro describir el modo protegido o explicar los pasos que realiza esta rutina de demostración.15 EBX llena_pant 20h . o a través de algún servicio XMS antes de acceder a .11111110b CR0.4 EAX. Otra restricción de este programa de ejemplo es que no activa la línea A20 de direcciones.0.AX EBX.0. ampliar el límite y volver a modo real.0.0. se puede pasar un momento a modo protegido. sólo para 386 o superior gdtr gd1 gd2 gdt gcod gcodl gdat gdtl flat386 segmento LABEL QWORD DW gdtl-1 DD ? DB DB EQU DB EQU ENDP ENDS END . . guardar dirección lineal de GDT .0cfh.0ffh. Como ya se comentó.0. pero por encima del primer megabyte podría haber problemas según a qué dirección se pretenda acceder. manera directa (necesita la CPU en modo real).BX GS. no es estrictamente cierto que no se pueda rebasar el límite de 64 Kb en los segmentos en modo real. bit de modo protegido pasar a modo protegido borrar cola de prebúsqueda índice de descriptor en BX cargar registro de segmento DS ES SS FS GS .gcodl DS. Sin embargo. activar modo flat AX. .Esta rutina pasa momentáneamente a modo protegido de . . Rutina para activar el modo flat del 386 y superiores (acceso a 4 Gb en modo real).BX SS.

Para facilitar estas operaciones se utilizan las directivas que indican al ensamblador qué debe hacer con las instrucciones y los datos. al igual que el propio ensamblador en ocasiones: leer todo una vez primero -aunque no se entienda del todo. TASM genera además un código más reducido y optimizado. Por otra parte. debido a la complejidad de la sintaxis del lenguaje ensamblador ideada por el fabricante (que no la del microprocesador).0 de Borland (TASM). Por otro lado. También es propenso a generar errores de fase y otros similares al tratar con listados un poco grandes.1 (aparecida sospechosa e inusualmente muy poco tiempo después) haya corregido dichos fallos.0. Respecto a MASM 6. Muchos programas han sido ensamblados una vez con MASM.1. «_». Los programas de ejemplo de este libro y la sintaxis de ensamblador tratada son las del MASM de Microsoft y el ensamblador de IBM. Ello por otra parte resulta inevitable también en algunos libros más básicos. 5. Conviene decir aquí que este capítulo es especialmente arduo para aquellos que no conocen el lenguaje ensamblador de ninguna máquina.0 no permite cambiar (aunque sí la 6. El formato de una sentencia de instrucción es el siguiente: [etiqueta] nombre_instrucción [operandos] [comentario] Los corchetes. Las instrucciones se aplican en tiempo de ejecución. Por ello. Es el nombre simbólico de la primera posición de una instrucción. «. Consta de hasta 31 caracteres que pueden ser las letras de la A a la Z.que aún no han sido definidos. por lo que con frecuencia se utilizan unos elementos -para explicar otros. Reglas: . con números en binario o hexadecimalaún resultaría tedioso tener que realizar los cálculos de los desplazamientos en los saltos a otras partes del programa en las transferencias de control. las posibilidades adicionales de TASM no han sido empleadas por lo general. algo vital para mantener la compatibilidad con procesadores anteriores. Campo de etiqueta. pero las directivas sólo son utilizadas durante el ensamblaje. puntero o dato. Si bien se realiza un gran avance al introducir los mnemónicos respecto a programar directamente en lenguaje maquina -es decir. indican que lo especificado entre ellos es opcional.SINTAXIS DE UNA LÍNEA EN ENSAMBLADOR. los números del 0 al 9 y algunos caracteres especiales como «@».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 líneas donde se desea emplearlas. dependiendo de la situación que se trate. etc. compatible con el clásico MASM 5. como es normal al explicar instrucciones en informática. aunque es probable que la versión 6.y volverlo a leer después más despacio. reservar espacio de memoria dentro de un programa para almacenar datos. es un buen consejo actuar a dos pasadas. ..» y «$». La razón es que la información está organizada a modo de referencia. MASM 5..EL LENGUAJE ENSAMBLADOR DEL 80x86 71 Capítulo V: EL LENGUAJE ENSAMBLADOR DEL 80x86 Hasta ahora hemos visto los mnemónicos de las instrucciones que pasadas a su correspondiente código binario ya puede entender el microprocesador. para asegurar que éste puede ensamblarlos. Un programa fuente en ensamblador contiene dos tipos de sentencias: las instrucciones y las directivas.0 de Microsoft pero más potente y al mismo tiempo mucho más rápido y flexible. intolerables en un ensamblador. todos los programas han sido desarrollados con el Turbo Assembler 2. No obstante. el autor de este libro encontró que en ocasiones calcula incorrectamente el valor de algunos símbolos y etiquetas.

calcúlese el complemento a dos).OPERADORES ARITMÉTICOS. Las sentencias fuente -tanto instrucciones como directivas. mov ax.CONSTANTES Y OPERADORES. Campo de nombre.1. esto es. 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 .fuente. 5. 1 ó 2. Por defecto. que se verá más adelante.No se pueden utilizar los nombres de instrucciones o registros como nombres de etiquetas. hexadecimales (ej. Pueden emplearse libremente (+). como las directivas de definición de datos por ejemplo. la siguiente línea en ensamblador (que se apoya en la directiva DW. (-). 10010b). Cuando es referenciada en una transferencia de control se carga el puntero de instrucciones IP y el segmento de código CS (llamadas intersegmento).2. Sólo se puede poner el signo (-) en las decimales (en las demás. 5. 0E0h) u octales (ej. Téngase en cuenta que hablamos de instrucciones. es:[di] ax es:[di] destino origen Campo de comentarios. Es válida. . o bien una directiva de las que veremos más adelante. . las etiquetas son de tipo NEAR cuando el campo de etiqueta finaliza con dos puntos (:).) todo lo que sigue en la línea es un comentario que realiza aclaraciones sobre lo que se está haciendo en ese programa. se considera cercana: quiere esto decir que cuando realizamos una llamada sobre dicha etiqueta el ensamblador considera que está dentro del mismo segmento de código (llamadas intrasegmento) y el procesador sólo carga el puntero de instrucciones IP. si el primer dígito no es numérico hay que poner un 0. también las hay de cadena (ej. (*) y (/) -en este último caso la división es siempre entera-. Indica cuales son los datos implicados en la operación. 21o ó 21q). Pueden ser binarias (ej.2. decimales (ej.2. . para reservar memoria para una palabra de 16 bits): dato DW 12*(numero+65)/7 También se admiten los operadores MOD (resto de la división) y SHL/SHR (desplazar a la izquierda/derecha cierto número de bits). en el caso de que sean dos al 1º se le llama destino y al 2º -separado por una coma. En las hexadecimales. AT Y PS/2 . Puede haber 0. resulta de gran utilidad de cara a realizar futuras modificaciones al mismo.Si se utiliza el punto «. "juan") e incluso con comillas dentro de comillas de distinto tipo (como ’hola. Campo de operandos.72 EL UNIVERSO DIGITAL DEL IBM PC.El primer carácter no puede ser un dígito. . Obviamente. no llevan los dos puntos y sin embargo son cercanas. las numéricas están en base 10 si no se indica lo contrario con una directiva (poco recomendable como se verá)."amigo"’).» éste debe colocarse como primer carácter de la etiqueta. las etiquetas empleadas antes de las directivas. Cuando en una línea hay un punto y coma (. 34d).pueden contener constantes y operadores. ’pepe’.CONSTANTES. Contiene el mnemónico de las instrucciones vistas en el capítulo anterior. por ejemplo. . Las etiquetas son de tipo FAR si el campo de etiqueta no termina con los dos puntos: en estas etiquetas la instrucción a la que apunta no se encuentra en el mismo segmento de código sino en otro.2. 5.

BX = 100 Operadores MASK y WIDTH: informan de los campos de un registro de bits (véase RECORD). El bit 5 indica si la expresión es local (0 si está definida externamente o indefinida).(255 AND 128) XOR 128 .TYPE: devuelve el modo de la expresión indicada en un byte. Pueden ser el AND.SEG tabla_datos Operador OFFSET: devuelve el desplazamiento de la variable o etiqueta en su segmento: MOV AX. Operador SEG: devuelve el valor del segmento de la variable o etiqueta. Pueden ser: EQ (igual).4. Realizan las operaciones lógicas en las expresiones. OR. QWORD. 5.dato EQ 99 . LT (menor que). Este operador es útil sobre todo en las macros para determinar el tipo de los parámetros: info .SIZE matriz BX.OFFSET DS:variable también es válido: MOV Operador . Operador PTR: redefine el atributo de tipo (BYTE. El TASM utiliza también el bit 3 para indicar algo que desconozco. respectivamente. si ambos bits están inactivos significa modo absoluto. de la variable indicada (definida obligatoriamente con DUP): matriz DW MOV MOV 100 DUP (12345) AX. AL = 0FFh (cierto) . . .OPERADORES LÓGICOS.indica si es lejana o FAR (0FFFEh) o cercana o NEAR (0FFFFh).2.5. BL = 0 5. DWORD.2.6.OPERADORES RELACIONALES. GT (mayor que).: MOV BL. AH = 0 (falso) 5. NE (no igual). XOR y NOT.OFFSET variable Si se desea obtener el offset de una variable respecto al grupo (directiva GROUP) de segmentos en que está definida y no respecto al segmento concreto en que está definida: MOV AX. LE (menor o igual que).OPERADORES DE RETORNO DE VALORES. Ej.3.2. «dato» vale 100 .LENGTH matriz . GE (mayor o igual que). . No válido en variables DUP: kilos DW MOV 76 AX. Devuelven condiciones de cierto (0FFFFh ó 0FFh) o falso (0) evaluando una expresión. El bit 0 indica modo «relativo al código» y el 1 modo «relativo a datos».dato GE 10 AH. AX = 200 . el bit 7 indica si la expresión contiene una referencia externa. TBYTE) o el de .EL LENGUAJE ENSAMBLADOR DEL 80x86 73 5.OPERADORES DE ATRIBUTOS.OFFSET nombre_grupo:variable AX.TYPE kilos . Ejemplo: dato EQU MOV MOV 100 AL.2. sólo se puede emplear en programas de tipo EXE: MOV AX. AX = 2 Tratándose de etiquetas -en lugar de variables.TYPE variable Operador TYPE: devuelve el tamaño (bytes) de la variable indicada. Operadores SIZE y LENGTH: devuelven el tamaño (en bytes) o el nº de elementos. WORD. .

sin embargo. se supone DS para los registros BX. Operador ’$’: indica la posición del contador de posiciones («Location Counter») utilizado por el ensamblador dentro del segmento para llevar la cuenta de por dónde se llega ensamblando.[0] Para solucionarlo hay que indicar en qué segmento está el dato (incluso aunque éste sea DS): MOV AL. Lo que desea el programador debe indicárselo en este caso explícitamente al ensamblador de la siguiente manera: MOV AL. Por defecto. ya que midato está declarado en el segmento de datos y el ensamblador lo sabe: MOV AL. 10 palabras a 0 Para colocar en AL el primer byte de la misma. PTR puede redefinir una etiqueta NEAR de uno de ellos para convertirla en FAR desde el otro. Sin embargo. El ensamblador TASM. de tipo NEAR. en el siguiente ejemplo no es necesario. DS:. Operador SHORT: indica que la etiqueta referenciada. Si al acceder a un dato éste no se encuentra en el segmento por defecto.DS:[0] En este último ejemplo el ensamblador no generará el byte adicional ya que las instrucciones MOV operan por defecto sobre DS (como casi todas). pondrá o no el prefijo adecuado según sea conveniente. sí se emplean con bastante frecuencia.. Cuando se referencia una dirección fija hay que indicar el segmento. AT Y PS/2 distancia (NEAR o FAR) de un operando de memoria. Es responsabilidad exclusiva del programador inicializar los registros de segmento al principio de los procedimientos para que el ASSUME no se quede en tinta mojada. Por ejemplo. para economizar memoria (el MASM no). Por ejemplo. con objeto de poder llamarla..BYTE PTR tabla Trabajando con varios segmentos. Muy útil: frase longitud DB EQU "simpático" $-OFFSET frase . ya que el ensamblador no conoce en qué segmento está la variable. el ensamblador añadirá el byte adicional de manera automática. 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.ES:variable En el ejemplo. DI o SI (o sin registros de base o índice) y SS para SP y BP.tabla es incorrecta. variable se supone ubicada en el segmento extra. si se tiene una tabla definida de la siguiente manera: tabla DW 10 DUP (0) . la instrucción MOV AL. Sin embargo. la siguiente línea dará un error al ensamblar: MOV AL.74 EL UNIVERSO DIGITAL DEL IBM PC. coloca automáticamente instrucciones SHORT allí donde es posible. el programador puede forzar también esta circunstancia: MOV AL. pero ha sido necesario indicar DS para que el ensamblador nos entienda.midato Por lo general no es muy frecuente la necesidad de indicar explícitamente el segmento: al acceder a una variable el ensamblador mira en qué segmento está declarada (véase la directiva SEGMENT) y según como estén asignados los ASSUME. es uno de los pocos casos en que debe indicarse. si se solicitan dos pasadas. ya que tabla (una cadena 10 palabras) no cabe en el registro AL. los prefijos CS en las rutinas que gestionan interrupciones (ya que CS es el único registro de segmento que apunta en principio a las mismas. hasta que se cargue DS u otro). puede alcanzarse con un salto corto (-128 a +127 posiciones) desde la actual situación del contador de programa. Operadores CS:.

DIRECTIVAS DE DEFINICIÓN DE SÍMBOLOS. . de doble precisión (8 bytes) con DQ y «reales temporales» (10 bytes) con DT.PRINCIPALES DIRECTIVAS. Para que el ensamblador interprete el número como real ha de llevar el punto decimal: temperatura DD espanoles91 DQ 29. La sintaxis de «nombre» es análoga a la de la «etiqueta» de las líneas de instrucciones.1. asignándolas un valor inicial: anno mes numerazo texto DW DB DD DB 1991 12 12345678h "Hola".EL LENGUAJE ENSAMBLADOR DEL 80x86 75 En el ejemplo.13. .HIGH dato .3. . DQ (definir cuádruple palabra). para asignar 100 bytes a cero y 25 palabras de contenido indefinido (no importa lo que el ensamblador asigne): ceros basura DB DW 100 DUP (0) 25 DUP (?) Se admiten también los anidamientos.edad . DD (definir doble palabra). los campos han de estar separados por al menos un espacio en blanco.DIRECTIVAS DE DEFINICIÓN DE DATOS. El campo de comentario cumple también las mismas normas.2.3. 3.2. DB (definir byte). aunque falta alguna que otra y las explicadas no lo están en todos los casos con profundidad. 2 DUP (7)) 5. La sintaxis de una sentencia directiva es muy similar a la de una sentencia de instrucción: [nombre] nombre_directiva [operandos] [comentario] Sólo es obligatorio el campo «nombre_directiva». DW (definir palabra).LOW dato AH. 2.3. Se trata de un operador muy flexible.10 Se pueden definir números reales de simple precisión (4 bytes) con DD.7.3. longitud tomará el valor 9. AL = 1 .9E6 Con el operando DUP pueden definirse estructuras repetitivas. AH = 4 5. Por ejemplo.72 38. A continuación se explican las directivas empleadas en los programas ejemplo de este libro y alguna más. Es válido hacer: edad EQU MOV [BX+DI+8] AX. todos ellos con el formato empleado por el coprocesador. Operadores HIGH y LOW: devuelven la parte alta o baja. EQU (EQUivalence): Asigna el valor de una expresión a un nombre simbólico fijo: olimpiadas EQU 1992 Donde olimpiadas ya no podrá cambiar de valor en todo el programa. respectivamente (8 bits) de la expresión: dato EQU MOV MOV 1025 AL. El siguiente ejemplo crea una tabla de bytes donde se repite 50 veces la secuencia 1. DT (definir 10 bytes): sirven para declarar las variables.7: tabla DB 50 DUP (1. aunque nunca se pone el sufijo «:». 5.

opción por defecto) y PAGE (comienza en dirección múltiplo de 256).3. el tipo de alineamiento. nombre ENDS Se pueden definir unos segmentos dentro de otros (el ensamblador los ubicará unos tras otros). . .3. no en 8088) es dos veces más rápido el acceso a palabras en posición par: EVEN dato_rapido DW 0 . Puede omitirse en los programas EXE si éstos constan de un sólo módulo. etc. DWORD (comienza en posición múltiplo de 4). En los COM es preciso indicarla y.8086 se fuerza a que de nuevo sólo se reconozcan instrucciones del 8086 (modo por defecto). que indica el offset donde se deposita la instrucción o dato. de tipo COM. SEGMENT-ENDS: SEGMENT indica el comienzo de un segmento (código. donde se indique. . El alineamiento puede ser BYTE (ninguno). la expresión -realmente una etiqueta. . opcionalmente.DIRECTIVAS DE DEFINICIÓN DE SEGMENTOS Y PROCEDIMIENTOS. datos.» inicial.4.3. WORD (el segmento comienza en posición par). el 386 y del 8087.DIRECTIVAS DE CONTROL DEL ENSAMBLADOR. Si se incluye. intercalando un byte con la instrucción NOP si es preciso.debe estar inmediatamente después del ORG 100h. En los programas COM (que se cargan en memoria con un OFFSET 100h) es necesario colocar al principio un ORG 100h. que a menudo obliga además a iniciar los números por 0 para distinguirlos de las etiquetas). Bastante desaconsejable dada la notación elegida para indicar las bases por parte de IBM/Microsoft (si se cambia la base por defecto a 16.76 EL UNIVERSO DIGITAL DEL IBM PC. 5. . Muy usada en macros (sobre todo con REPT). necesita la declaración de un segmento (común para datos.286. alternativamente se puede ubicar fuera de los segmentos (obligatorio en MASM) y definir éstos explícitamente como de 16 bits con USE16.386 Y .RADIX n: cambia la base de numeración por defecto. código y pila). En buses de 16 ó más bits (8086 y superiores. Junto a SEGMENT puede aparecer.) y ENDS su final. expresión indica el punto donde arranca el programa. También debe ponerse el «. Con . . La combinación puede ser: . la combinación.386 puede ser colocada dentro de un segmento (entre las directivas SEGMENT/ENDS) con el ensamblador TASM. y un ORG 0 en los controladores de dispositivo (aunque si se omite se asume de hecho un ORG 0). AT Y PS/2 = (signo ’=’): asigna el valor de la expresión a un nombre simbólico variable: Análogo al anterior pero con posibilidad de cambiar en el futuro. El programa más simple. num = 19 num = pepe + 1 dato = [BX+3] dato = ES:[BP+1] 5. pila. EVEN: fuerza el contador de posiciones a una posición par. La directiva . lo que permite emplear instrucciones de 386 con segmentos de 16 bits. el uso y la clase: nombre SEGMENT [alineamiento] [combinación] [uso] [’clase’] . ¡los números no pueden acabar en ’d’ ya que se confundirían con el sufijo de decimal!: lo ideal sería emplear un prefijo y no un sufijo. PARA (comienza en una dirección múltiplo de 16. . además. ORG (ORiGin): pone el contador de posiciones del ensamblador.8087 obligan al ensamblador a reconocer instrucciones específicas del 286. END [expresión]: indica el final del fichero fuente.

El programa más sencillo necesita que se «suponga» CS como mínimo para el segmento de código. además el Linkador de Borland (TLINK 4.0) exige obligatoriamente que la clase de éste sea también ’STACK’.ES:p_serie0 . segmento . .EL LENGUAJE ENSAMBLADOR DEL 80x86 77 . vectores de interrupción. las variables declaradas han de serlo en el mismo orden y tamaño. debe existir uno en los programas de tipo EXE. Todos los segmentos PUBLIC de igual nombre y clase tienen una base común y son colocados adyacentemente unos tras otros. al emplear la directiva . siendo conveniente nombrar la clase del segmento de pila con ’STACK’.PUBLIC: usado especialmente cuando se trabaja con segmentos definidos en varios ficheros que se ensamblan por separado o se compilan con otros lenguajes. Téngase en cuenta que el linkador no soporta esta característica. no para ensamblar sino para declarar variables (inicializadas siempre con ’?’) de cara a acceder con comodidad a zonas de ROM. inicializar ES .variables_bios ES. También se puede indicar el nombre de un grupo o emplear «SEG variable» o «SEG etiqueta» en vez de nombre_segmento: ASSUME reg_segmento:nombre_segmento[. etc. por lo demás. con el LINK de Microsoft no siempre es necesario indicar la clase del segmento de pila.AT: asocia un segmento a una posición de memoria fija. Similar. El uso indica si el segmento es de 16 bits o de 32. Si se definen varios segmentos de este tipo el ensamblador acepta el primero y trata a los demás como COMMON. ’clase’ es un nombre opcional que empleará el linkador para encadenar los módulos.COMMON: similar. Se puede indicar ASSUME NOTHING para cancelar un ASSUME anterior. por ello debe declararse un nombre entre comillas simples -’clase’. pero son lógicamente independientes: cada uno tiene su propia base y sus propios offsets relativos. Por ello. por lo que emplear MEMORY es equivalente a todos los efectos a utilizar COMMON.AX AX..MEMORY: segmento que el linkador ubicará al final de todos los demás. ASSUME (Suponer): Indica al ensamblador el registro de segmento que se va a utilizar para direccionar cada segmento dentro del módulo. . la dirección del primer puerto serie puede obtenerse de esta manera (por ejemplo): MOV MOV MOV AX. aunque ahora los segmentos de igual nombre y clase se solapan. marcando con claridad su inicio y su fin. .STACK: segmento de pila.(No indicada): los segmentos se colocan unos tras otros físicamente.para ayudar al linkador. Esta instrucción va normalmente inmediatamente después del SEGMENT. También conviene hacer un assume del registro de segmento DS hacia el segmento de datos.. lo que permite emplear algunas instrucciones del 386 en el modo real del microprocesador y bajo el sistema operativo DOS. incluso en el caso de que éste sea el mismo que el de código: si no. a PUBLIC.] PROC-ENDP permite dar nombre a una subrutina.. el ensamblador colocará un byte de prefijo adicional en todos los accesos a memoria para forzar que éstos sean sobre CS. de lo contrario el ensamblador empezará a protestar un montón al no saber que registro de segmento asociar al código generado.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. Ejemplo: vars_bios p_serie0 vars_bios SEGMENT AT 40h DW ? ENDS De esta manera. . siendo el offset relativo al primer segmento cargado. Por último. lo que permitiría saber dónde acaba el programa. . Olvídate de MEMORY.

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 serán. Observar que la etiqueta nunca termina con dos puntos.. 5. en el punto en que aparece el INCLUDE. del nombre del propio fichero fuente. codigo. NAME nombre_modulo_objeto: indica el nombre del módulo objeto.. 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. cls cls PROC . Si la directiva EXTRN se coloca dentro de un segmento. NEAR o FAR.78 EL UNIVERSO DIGITAL DEL IBM PC.5. Ejemplo: superseg codigo codigo GROUP datos. var_x PROC FAR ENDP DW 0 Declara la variable var_x y el procedimiento proc1 como accesibles desde el exterior por medio de la directiva EXTRN.DIRECTIVAS DE DEFINICIÓN DE BLOQUES. en su defecto.. Es exactamente lo mismo que mezclar ambos ficheros con un editor de texto.. Evidentemente. . al linkar habrá que enlazar este módulo con el que define los elementos externos. es muy recomendable para estructurar los programas. Por ejemplo: proc1 proc1 var_x PUBLIC proc1.6. de 32 bits). Si el símbolo está en otro segmento. se emplea además ABS para las constantes numéricas): EXTRN proc1:FAR. AT Y PS/2 Aunque es redundante. 5. además. GROUP segmento1.DIRECTIVAS DE REFERENCIAS EXTERNAS. WORD o DWORD. Ahorra trabajo en fragmentos de código que se repiten en varios programas (como quizá una librería de macros). EXTRN: Permite acceder a símbolos definidos en otro fichero objeto (resultante de otro ensamblaje o de una compilación de un lenguaje de alto nivel). No se recomiendan INCLUDE’s anidados.. Necesario para programación modular e interfaces con lenguajes de alto nivel. se supone el símbolo dentro del mismo.indicados. pila SEGMENT ENDS . segmento2. Si no se incluye NAME. permite agrupar dos o más segmentos lógicos en uno sólo de no más de 64 Kb totales (ojo: el ensamblador no comprueba este extremo. .3. se tomará de la directiva TITLE o.3. INCLUDE nombre_fichero: Añade al fichero fuente en proceso de ensamblaje el fichero indicado. al final. aunque sí el enlazador).var_x. es necesario también indicar el tipo del dato o procedimiento (BYTE. var_x:WORD En el ejemplo se accede a los símbolos externos proc1 y var_x (ver ejemplos de PUBLIC) y a continuación sería posible hacer un CALL proc1 o un MOV CX. PUBLIC: permite hacer visibles al exterior (otros ficheros objeto resultantes de otros listados en ensamblador u otro lenguaje) los símbolos -variables y procedimientos.

DW..175 DB 0 DB 10 DUP(0) DD ? ENDS . correcto La ventaja de agrupar segmentos es poder crear programas COM y SYS que contengan varios segmentos.telefono BX. DD. es posible acceder a sus elementos utilizando un (. DWORD. NEAR o FAR).101. WORD. para acceder de una manera más elegante a los campos de una información con cierta estructura. STRUC . Permite definir una estructura determinada de byte o palabra para operar con comodidad. Tras crear la estructura. con MOV AX. Sintaxis: . DT) y pueden ser modificables o no en función de si son simples o múltiples.) para separar el nombre del campo: MOV LEA MOV AX. equivale a [BX+12] RECORD: similar a STRUC pero operando con campos de bits.supersegmento:dato .EL LENGUAJE ENSAMBLADOR DEL 80x86 79 datos dato datos pila pila SEGMENT DW 1234 ENDS SEGMENT STACK ’STACK’ DB 128 DUP (?) ENDS Cuando se accede a un dato definido en algún segmento de un grupo y se emplea el operador OFFSET es preciso indicar el nombre del grupo como prefijo. Ejemplo: palabra byte_bajo byte_alto LABEL DB DB WORD 0 0 En el ejemplo. creándose la estructura ’felipe’ que ocupa 27 bytes.251244> En el ejemplo se definen los campos modificables (los únicos definibles) dejando sin definir (comas consecutivas) los no modificables. En todo caso. respectivamente: alumno mote edadaltura peso otros telefono alumno STRUC DB ’0123456789’ DB 20. .palabra se accederá a ambos bytes a la vez (el empleo de MOV AX.ENDS: permite definir registros al estilo de los lenguajes de alto nivel..dato AX. ¡incorrecto! .peso . . Estos campos pueden componerse de cualquiera de los tipos de datos simples (DB. téngase en cuenta aún en ese caso que no pueden emplearse todas las características de la programación con segmentos (por ejemplo. no se puede utilizar la directiva SEG ni debe existir segmento de pila). la cual ha de hacerse expresamente utilizando los ángulos ’<’ y ’>’: felipe alumno <’Gordinflas’.OFFSET felipe. siendo factible redefinir el tipo. El TASM es más flexible y permite definir también el primer elemento de los campos múltiples sin dar error. DQ. modificable no modificable modificable no modificable modificable La anterior definición de estructura no lleva implícita la reserva de memoria necesaria. LABEL: Permite referenciar un símbolo con otro nombre. . de lo contrario el ensamblador no generará el desplazamiento correcto ¡ni emitirá errores!: MOV MOV AX.byte_bajo daría error: no se puede cargar un sólo byte en un registro de 16 bits y el ensamblador no supone que realmente pretendíamos tomar dos bytes consecutivos de la memoria). La sintaxis es: nombre LABEL tipo (tipo = BYTE. . Las cadenas de caracteres son rellenadas con espacios en blanco al final si no alcanzan el tamaño máximo de la declaración.felipe CL.[BX].

] . por ejemplo. por defecto son 66 líneas por página (modificable entre 10 y 255) y 80 columnas (seleccionable de 60 a 132).DIRECTIVAS DE LISTADO.DIRECTIVAS CONDICIONALES... AL = 2 (anchura de A) 5. ELSE .. Donde nombre permitirá referenciar la estructura en el futuro. el B los bits 1 al 4 y el C el bit 0: 6 5 1 1 4 3 2 1 0 1 0 1 0 ? La reserva de memoria se realiza. por ejemplo. según el modelo de memoria). de cara a generar código para varios ordenadores: pueden existir ciertos símbolos definidos que indiquen en un momento dado si hay que ensamblar ciertas zonas del listado o no de manera condicional.8. ensamble o no ciertas zonas de código. Está dividida en tres campos que ocupan los 7 bits menos significativos del byte: el campo A ocupa los bits 6 y 5. «PAGE +» indica capítulo nuevo (y se incrementa el número). registro RECORD a:2=3.. . de la siguiente manera: reg1 registro <2. nombre_de_campo identifica los distintos campos. según ellas. a los que se asigna un tamaño (en bits) y opcionalmente un valor por defecto. menos significativo de A) . AL = 5 (desplazamiento del bit . AL = 01100000b (máscara de A) . PAGE num_lineas.80 EL UNIVERSO DIGITAL DEL IBM PC.1> Quedando reg1 con el valor binario 1001011 (el campo B permanece inalterado y el A y C toman los valores indicados). c:1 La estructura registro totaliza 7 bits. requiere ’<’ y ’>’) 5. En los fragmentos en ensamblador del código que generan los compiladores también aparecen con frecuencia (para actuar de manera diferente. b:4=5.. MASK A AL. Se emplean para que el ensamblador evalúe unas condiciones y.3. A AL. Ejemplos de operaciones soportadas: MOV MOV MOV AL. Es frecuente. num_columnas: Formatea el listado de salida. el ELSE es opcional expresion expresión símbolo símbolo <argumento> <argumento> <arg1>. Sintaxis: IFxxx . requiere ’<’ y ’>’) (arg1 distinto de arg2./arg.. . por lo que ocupa un byte. AT Y PS/2 nombre RECORD nombre_de_campo:tamaño[=valor]. . PAGE salta de página e incrementa su número.7.3. Es interesante también la posibilidad de definir un símbolo que indique que el programa está en fase de pruebas y ensamblar código adicional en ese caso con objeto de depurarlo. TITLE título: indica el título que aparece en la 1ª línea de cada página (máximo 60 caracteres).. por ejemplo. también es obligado poner ’<’ y ’>’) (arg1 idéntico a arg2.. según la máquina. <arg2> <arg1>. xxx es la condición . ENDIF IF IFE IF1 IF2 IFDEF IFNDEF IFB IFNB IFIDN IFDIF [símbolo/exp. <arg2> (expresión distinta de cero) (expresión igual a cero) (pasada 1 del ensamblador) (pasada 2 del ensamblador) (símbolo definido o declarado como externo) (símbolo ni definido ni declarado como externo) (argumento en blanco en macros -incluir ’<’ y ’>’-) (lo contrario. WIDTH A .

excepto cuando el listado es por pantalla y no en fichero.CREF: Restaurar listado de referencias cruzadas. . La macro se define por medio de la directiva MACRO. Esta es la misión de las macros.XALL: Listar sólo las macros que generan código objeto. . con objeto de economizar memoria. Cuando un conjunto de instrucciones en ensamblador aparecen frecuentemente repetidas a lo largo de un listado. duplicándose tantas veces como se use la macro. %OUT mensaje: escribe en la consola el mensaje indicado durante la fase de ensamblaje y al llegar a ese punto del listado.SFCOND: suprimir dicho listado. Una macro puede llamar a otra. de cara a facilitar la depuración). por el hecho de soportarlas el ensamblador eleva su categoría a la de macroensamblador. Por ello.DEFINICIÓN Y BORRADO DE LAS MACROS. aquellas tareas que puedan ser realizadas con subrutinas siempre será más conveniente realizarlas con las mismas. cada vez que se referencia a una macro. Sin embargo. las macros se colocan juntas en un fichero independiente y luego se mezclan en el programa principal con la directiva INCLUDE: .SALL: No listar las macros ni sus expansiones. . el conjunto de instrucciones aparece una sola vez en todo el programa y luego se invoca con CALL. . Con frecuencia. . 5. .1.4. .XCREF: Suprimir listado de referencias cruzadas (listado alfabético de símbolos junto al nº de línea en que son definidos y referenciados. No conviene confundir las macros con subrutinas: es estas últimas.4. que generarían programas gigantescos con menos de un 1% de velocidad adicional. el delimitador (primer carácter no blanco ni tabulador que sigue al COMMENT) indica el inicio e indicará más tarde el final del comentario. . .XLIST: Suprimir el listado ensamblador desde ese punto.TFCOND: Invertir el modo vigente de listado de los bloques asociados a una condición falsa.LIST: Restaurar de nuevo la salida de listado ensamblador. es conveniente agruparlas bajo un nombre simbólico que las sustituirá en aquellos puntos donde aparezcan. . 60 caracteres). ¡No olvidar cerrar el comentario!. . COMMENT delimitador comentario delimitador: Define un comentario que puede incluso ocupar varias líneas. 5.LALL: Listar las macros y sus expansiones. es absurdo e irracional realizar ciertas tareas con macros que pueden ser desarrolladas mucho más eficientemente con subrutinas: es una pena que en muchos manuales de ensamblador aún se hable de macros para realizar operaciones sobre cadenas de caracteres. . Es cierto que las macros son algo más rápidas que las subrutinas (se ahorra un CALL y un RET) pero la diferencia es tan mínima que en la práctica es despreciable en el 99.LFCOND: Listar los bloques de código asociados a una condición falsa (IF). Es necesario definir la macro antes de utilizarla. Por ello. el código que ésta representa se expande en el programa definitivo.99% de los casos.MACROS.EL LENGUAJE ENSAMBLADOR DEL 80x86 81 SUBTTL subtítulo: Ídem con el subtítulo (máx. al ser las macros una herramienta muy cotizada por los programadores.

para compatibilizar.4. falta directiva END»). y así ya no consumirá espacio en las tablas de macros que mantiene en memoria el ensamblador al ensamblar-..PARÁMETROS FORMALES Y PARÁMETROS ACTUALES. por lo que en la práctica es indiferente declarar cientos que ninguna macro: nombre_simbólico MACRO [parámetros] . tenemos a nuestra disposición una nueva instrucción máquina (SUPERPUSH) que puede ser usada con libertad dentro de los programas.. En realidad. A partir de la definición de esta macro. sacando los registros en orden inverso.3. Conviene hacer hincapié en que la definición de la macro no consume memoria. 5. y se construye casi con las mismas reglas que los nombres de las variables y demás símbolos. similar aquí a los procedimientos de los lenguajes de alto nivel. para restaurar el significado original del símbolo. Para borrarlas. Sin embargo.. No es necesario borrar las macros antes de redefinirlas. . la sintaxis es la siguiente: PURGE nombre_simbólico[. si alguna vez se redefiniera una instrucción máquina o directiva. la macro puede ser borrada -o simplemente porque ya no va a ser usada a partir de cierto punto del listado.0. ello provocaría un error un tanto curioso y extraño por parte del ensamblador (algo así como «Fin del fichero fuente inesperado.EJEMPLO DE UNA MACRO SENCILLA. Desde el 286 existe una instrucción muy cómoda que introduce en la pila 8 registros.2. instrucciones de la macro El nombre simbólico es el que permitirá en adelante hacer referencia a la macro. La macro puede contener parámetros de manera opcional. la directiva ENDM señala el final de la macro. finalmente. No se debe repetir el nombre simbólico junto a la directiva ENDM. puede crear unas macros que simulen estas instrucciones en los 8086: SUPERPUSH MACRO PUSH PUSH PUSH PUSH PUSH PUSH PUSH PUSH ENDM AX CX DX BX SP BP SI DI La creación de SUPERPOP es análoga. El orden elegido no es por capricho y se corresponde con el de la instrucción PUSHA original. .4. como a las variables. AT Y PS/2 IF1 INCLUDE fichero. para acelerar el ensamblaje y evitar que aparezcan en el listado (generado en la segunda fase). El ensamblador dará además un aviso de advertencia si se emplea una instrucción o directiva como nombre de macro. y a diferencia de lo que sucede con los demás símbolos... . Para quien no haya tenido relación previa con algún lenguaje estructurado de alto nivel.nombre_simbólico. aunque tolerará la operación. al menos con MASM 5.82 EL UNIVERSO DIGITAL DEL IBM PC. y otra que los saca (PUSHA y POPA).] 5.ext ENDIF La sentencia IF1 asegura que el ensamblador lea el fichero fuente de las macros sólo en la primera pasada. la instrucción o directiva machacada pierde su significado original. Quien esté acostumbrado a emplearlas. . el nombre de una macro puede coincidir con el de una instrucción máquina o una directiva del ensamblador: a partir de ese momento. ENDM . Normalmente se las asignará nombres normales.. A continuación vienen las instrucciones que engloba y.0 y TASM 2. haré un breve comentario acerca de lo que son los parámetros formales y actuales en una macro..

no. negativos. separándolas por comas.dato1 AX. En general. Estos parámetros se denominan parámetros actuales. la macro sólo podría ser empleada una vez en todo el programa para evitar que dicha etiqueta aparezca duplicada. . no es necesario que esto sea así pero es una buena costumbre de programación para evitar que los programas hagan cosas raras.positivos AX.b MOV total.a ADD AX. En la definición de la macro. Considerar el siguiente ejemplo: SUMAR MACRO a. «a».ETIQUETAS DENTRO DE MACROS. «b» y «total» son los parámetros formales y «positivos».b. al invocar la macro dos veces el ensamblador no generará la etiqueta «ya_esta» sino las etiquetas ??0000. VARIABLES LOCALES. El código que genera el ensamblador al expandir la macro será el siguiente: PUSH MOV ADD MOV POP AX AX.BX. ¿es dato1 el menor? . el TASM es algo más rígido y podría dar un error. se trata de situaciones atípicas que deben ser evitadas. se ignorarán los restantes.AX POP AX ENDM . si faltan.AX resultado . se comportan de manera independiente. Cuando el ensamblador expanda la macro al ensamblar. ya_esta AX. . Si se pone una etiqueta a donde saltar. En general. También puede darse el caso de que no sea posible expandir la macro. El parámetro formal «total» ha coincidido en el ejemplo y por casualidad con su correspondiente actual. cuya única misión es permitir distinguir unos parámetros de otros e indicar en qué orden son entregados: son los parámetros formales. Son necesarias normalmente para los saltos condicionales que contengan las macros más complejas.AX sería ilegal. «negativos» y «total» son los parámetros actuales.total PUSH AX MOV AX.dato2 resultado. los parámetros formales serán sustituidos por sus correspondientes parámetros actuales. La solución está en emplear la directiva LOCAL que ha de ir colocada justo después de la directiva MACRO: MINIMO MACRO LOCAL MOV CMP JB MOV MOV ENDM dato1.negativos total. 5. sin embargo. Si se indican más parámetros de los que una macro necesita. etiquetas. En el ejemplo. dentro de la macro.dato2 ya_esta AX. el MASM asumirá que son nulos (0) y dará un mensaje de advertencia.EL LENGUAJE ENSAMBLADOR DEL 80x86 83 Cuando se llama a una macro se le pueden pasar opcionalmente un cierto número de parámetros de cierto tipo.4.. La directiva LOCAL no sólo es útil para los saltos condicionales en las macros.DL porque DL es de 8 bits y la instrucción MOV DL. SUMAR positivos.. . Tanto «a» como «b» pueden ser variables.AX AX Las instrucciones PUSH y POP sirven para no alterar el valor de AX y conseguir que la macro se comporte como una caja negra. ??0001. En cambio. sí ... dichos parámetros aparecen asociados a ciertos nombres arbitrarios. las macros de este tipo no deberían alterar los registros y. dato2. también permite declarar variables internas a los mismos. es dato2 ya_esta: En el ejemplo. Se puede indicar un número casi indefinido de etiquetas con la directiva LOCAL. en otro punto del programa. no hubiera sido posible ejecutar SUMAR AX. etc. y así sucesivamente. si los cambian.4. hay que tener muy claro cuáles. total En el ejemplo..

OPERADORES DE MACROS. la expresión debe ser una constante (no relocalizable).84 EL UNIVERSO DIGITAL DEL IBM PC. Indica que lo que viene a continuación es un comentario que no debe aparecer al expansionar la macro. <1. 2> i Si se invoca MEMORIA ET se produce el error de "etiqueta ETi repetida".).5.).. sin embargo sólo aparecerán los comentarios normales que comiencen por (. Es necesario para lograr que el ensamblador sustituya un parámetro dentro de una cadena de caracteres o como parte de un símbolo: SALUDO MACRO MOV etiqueta&c: CALL ENDM c AL. Operador & Utilizado para concatenar texto o símbolos. <1. las macros suelen aparecer expandidas en los puntos en que se invocan.a un número. 2> i Lo que con MEMORIA ET generará correctamente las líneas: ET1 ET2 DB 1 DB 2 Operador ! o <> Empleado para indicar que el carácter que viene a continuación debe ser interpretado literalmente y no como un símbolo. Cuando al ensamblar se genera un listado del programa."A" imprimir Si no se hubiera colocado el & se hubiera expandido como MOV AL."&c" imprimir Al ejecutar SALUDO A se producirá la siguiente expansión: etiquetaA: MOV CALL AL. . ya que el ensamblador se come un & al hacer la primera sustitución.."c" Cuando se utilizan estructuras repetitivas REPT.). Los comentarios relacionados con el funcionamiento interno de la macro deberían ir con (. y no conviene desperdiciar memoria. es equivalente a <..>. Sólo se emplea en los argumentos de macros. los relativos al uso y sintaxis de la misma con (. IRP o IRPC (que se verán más adelante) existe un problema adicional al intentar crear etiquetas.. Esto es además conveniente porque durante el ensamblaje son mantenidos en memoria los comentarios de macros (no los del resto del programa) que comienzan por (. Dada la macro siguiente: . Operador % Convierte la expresión que le sigue -generalmente un símbolo.). !.4. como se ejemplifica a continuación: MEMORIA x&&i MACRO IRP DB ENDM ENDM x i. AT Y PS/2 5. Por ello. Operador . generando la misma etiqueta a menos que se duplique el operador &: MEMORIA x&i MACRO IRP DB ENDM ENDM x i. que se puede salvar añadiendo tantos ’&’ como niveles de anidamiento halla en las estructuras repetitivas empleadas.

AL Esta secuencia se transformará. El bloque de instrucciones se delimita con ENDM (no confundirlo con el final de una macro).AL Empleando símbolos definidos con (=) y apoyándose además en las macros se puede llegar a crear pseudo-instrucciones muy potentes: SUCESION MACRO n num = 0 REPT n DB num num = num + 1 ENDM ENDM . invocando la macro de la siguiente manera (con %): PSUM < SIM1 + SIM2 = >. %(SIM1+SIM2) Se produce la expansión deseada: %OUT * SIM1 + SIM2 = 620 * 5. suma * mensaje. (SIM1+SIM2) Se produce la siguiente expansión: %OUT * SIM1 + SIM2 = (SIM1+SIM2) * Sin embargo. suma * (Evidentemente. aumentando la comodidad de la programación.AL DX.6. fin de macro La sentencia SUCESION 3 provocará la siguiente expansión: DB DB DB 0 1 2 .DIRECTIVAS ÚTILES PARA MACROS. aunque abundan especialmente dentro de las macros. el % que precede a OUT forma parte de la directiva y no se trata del % operador que estamos tratando) Supuesta la existencia de estos símbolos: SIM1 SIM2 EQU EQU 120 500 Invocando la macro con las siguientes condiciones: PSUM < SIM1 + SIM2 = >. al ensamblar.. fin de REPT . Por ejemplo: REPT OUT ENDM 2 DX. REPT veces .EL LENGUAJE ENSAMBLADOR DEL 80x86 85 PSUM MACRO %OUT ENDM mensaje. ENDM (Repeat) Permite repetir cierto número de veces una secuencia de instrucciones.. Estas directivas pueden ser empleadas también sin las macros. . en lo siguiente: OUT OUT DX.4.

17 se obtendrá: PUSH PUSH MOV MOV OUT MOV OUT MOV OUT MOV OUT POP POP AX DX AL. <arg1. Por ejemplo. fin de macro Se comprende la necesidad de utilizar los ángulos: . Los ángulos (<) y (>) son obligatorios. 318h DX. Lógicamente. 2D1h. <1. 17 DX. 3. para no confundirlas con elementos independientes. valor AX DX AL. arg_n> . . 1C9h... supuesta la macro INCD: INCD MACRO IRP INC ENDM DEC ENDM lista. fin de IRP DX AX . p3. AL DX. El símbolo de control va tomando sucesivamente los valores (no necesariamente numéricos) arg1.3> 0.86 EL UNIVERSO DIGITAL DEL IBM PC.valor cn. 1. 1C9h DX. p2. p3.) dentro de los ángulos no se interpreta como el inicio de un comentario sino como un elemento más.AL DX AX Cuando se pasan listas como parámetros hay que encerrarlas entre ’<’ y ’>’ al llamar.. <lista> i . <p1. 9 Nota: Todo lo encerrado entre los ángulos se considera un único parámetro. 4 0.. p4. al emplear macros anidadas. 1A4h DX. AT Y PS/2 IRP simbolo_control. fin de macro Al ejecutar TETRAOUT 318h... AL DX. AL . p4> DX. p2. . AL DX. deben indicarse tantos símbolos angulares ’<’ y ’>’ consecutivos como niveles de anidamiento existan. Un (.. arg2. Por otra parte. 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 DB ENDM i. cn DX. ENDM (Indefinite repeat) Es relativamente similar a la instrucción FOR de los lenguajes de alto nivel. i*i Al expansionarse. fin de IRP p . arg2. este conjunto de instrucciones se convierte en lo siguiente: DB DB DB 0. i. 1A4h. 1 0. dentro de una macro también resulta bastante útil la estructura IRP: TETRAOUT MACRO PUSH PUSH MOV IRP MOV OUT ENDM POP POP ENDM p1.2. 2D1h DX. 2. p i.

byte de fin de bloque ENDM 5. d iter. c. 1. <c1c2 . por cierto. fin de macro Al ejecutar INICIALIZA 7..MACROS AVANZADAS CON NUMERO VARIABLE DE PARÁMETROS. abandonar REPT ENDIF maximo = maximo . destinada . Como ejemplo.EL LENGUAJE ENSAMBLADOR DEL 80x86 87 INCD AX. cn> . CX>. colocando un byte 255 al final del bloque reservado: MALLOC MACRO n maximo=100 REPT n IF maximo EQ 0 .. con una salvedad: los elementos situados entre los ángulos (<) y (>) -ahora opcionales. CX. BX. Normalmente se utiliza apoyándose en una directiva condicional (IF.. <&a&b&c&d> iter . Como ejemplo. CX y DX se ignoran (4 parámetros) INCD <AX..7. IRP ó IRPC.ELSE. DX se expandirá: INC INC INC DEC AX BX CX DX . se pasa al nivel inmediatamente superior (que puede ser otro bloque de estos). 0 se produce la siguiente expansión: DB DB DB DB 7 1 4 0 EXITM Sirve para abortar la ejecución de un bloque MACRO. REPT. b. Al salir del bloque.ENDIF). existe la posibilidad de chequear condicionalmente la presencia de un parámetro por medio de IFNB.1 DB 0 . Como se vio al estudiar la directiva IF. fin de IRPC .. (2 parámetros) IRPC simbolo_control. Uniendo esto a la potencia de IRP es posible crear macros extraordinariamente versátiles.4. <813> i El bloque anterior generará al expandirse: DB DB DB 8 1 3 Ejemplo de utilización dentro de una macro (en combinación con el operador &): INICIALIZA MACRO IRPC DB ENDM ENDM a. o su ausencia con IFB. ENDM (Indefinite repeat character) Esta directiva es similar a la anterior. . ¿ya van 100? EXITM ... 4. reservar byte ENDM DB 255 . BX. la siguiente macro reserva n bytes de memoria a cero hasta un máximo de 100.son caracteres ASCII y no van separados por comas: IRPC DB ENDM i. DX se expandirá: INC DEC AX BX .. valga la siguiente macro.

personalmente considero que cada uno es muy libre de hacer lo que desee.R10> IFNB <reg> PUSH reg ENDIF ENDM . aunque no generan código son bastante útiles para dejar bien claro dónde empieza y acaba un módulo. la instrucción: XPUSH AX. Sin embargo. sí es conveniente leerlo en 2 ó 3 minutos para observar ciertas reglas muy sencillas que ayudarán a hacer programas seguros y eficientes. <lista> PUSH i ENDM ENDM MACRO lista IRP i.DS.VAR1 AX AX DS ES VAR1 Se expandirá en: PUSH PUSH PUSH PUSH PUSH El ejemplo anterior es ilustrativo del mecanismo de comprobación de presencia de parámetros. fin de XPUSH Por ejemplo. Se pueden implementar en ensamblador con las directivas PROC y ENDP que. AT Y PS/2 a introducir en la pila un número variable de parámetros (hasta 10): es especialmente útil en los programas que gestionan interrupciones: XPUSH MACRO R1. Reglas para la buena programación: .R9.ES.R7.R3. Un ejemplo de uso puede ser el siguiente: XPUSH XPOP <AX.R4.R6. BX.R2.R2. BX. por otra parte.PROGRAMACIÓN MODULAR Y PASO DE PARÁMETROS. este ejemplo puede ser optimizado notablemente empleando una lista como único parámetro: XPUSH MACRO lista IRP i. <lista> POP i ENDM ENDM XPOP La ventaja es el número indefinido de parámetros soportados (no sólo 10).R7.R10 IRP reg. en muchos casos no se pueden cumplir los principios de la programación elegante -especialmente en ensamblador. CX> <CX.por lo que detesto aquellos profesionales de la informática que se entrometen con la manera de programar de sus colegas o alumnos. AX> AX BX CX CX BX AX Que al expandirse queda: PUSH PUSH PUSH POP POP POP 5.R5. Sin embargo.R4.R6. .R9.BX. obligándolos a hacer las cosas a su gusto. lo que reduce el tiempo de programación y aumenta la fiabilidad del código.R5.R3.88 EL UNIVERSO DIGITAL DEL IBM PC. <R1. Aunque lo que viene a continuación no es indispensable para programar en ensamblador.5. La programación modular consiste en dividir los problemas más complejos en módulos separados con unas ciertas interdependencias.R8. fin de IRP ENDM .R8.

. El tipo de los parámetros habrá de estar debidamente documentado al principio de los módulos. o bien indirectamente por referencia o dirección.4 NEAR BP BP. los parámetros son apilados antes de llamar al módulo que los va a recoger. si es preciso perdiendo eficiencia. parte baja del dato moduloA .Paso de parámetros por la pila. Tampoco deben depender de ejecuciones anteriores.Excepto en los puntos en que la velocidad o la memoria son críticas (la experiencia demuestra que son menos del 1%) debe codificarse el programa con claridad.Una sola entrada y salida en cada módulo: un módulo sólo debe llamar al inicio de otro (con CALL) y éste debe retornar al final con un único RET. Los registros serán preservados preferiblemente en la pila (con PUSH) y recuperados de la misma (con POP en orden inverso). Los parámetros pueden pasarse además de dos maneras: directamente por valor. los módulos son reentrantes y pueden ser llamados de manera múltiple soportando. Este tipo de módulos no son reentrantes y hasta que no acaben de procesar una llamada no se les debe llamar de nuevo en medio de la faena. entre otras características. se requerirá también que las variables locales se generen sobre la pila). la recursividad (sin embargo.Los módulos han de ser «cajas negras» y no deben modificar el entorno exterior. Este es el método empleado por el DOS y la BIOS en la mayoría de las ocasiones para comunicarse con quien los llama.[BP+4] AX.[BP+6] BP . parte alta del dato . . Para el paso de parámetros entre módulos existen varios métodos que se exponen a continuación. llamada . Esto significa que no deben actuar sobre variables globales ni modificar los registros (excepto aquellos registros y variables en que devuelven los resultados. . no debiendo existir más puntos de salida y no siendo recomendable alterar la dirección de retorno. salvo excepciones en que la propia claridad del programa obligue a lo contrario (por ejemplo. La ventaja del paso de parámetros por la pila es el prácticamente ilimitado número de parámetros admitido. los generadores de números aleatorios pueden depender de la llamada anterior). apilar parámetros . Ese 1% documentarlo profusamente como se haría para que lo lea otra persona. En el primer caso se envía el valor del parámetro y en el segundo la dirección inicial de memoria a partir de la que está almacenado. para equilibrar el puntero de pila al final antes de retornar (método de los compiladores de lenguaje Pascal) o en caso contrario el programa que llama deberá encargarse de esta operación (lenguaje C). equilibrar pila . . Un ejemplo puede ser el siguiente: dato datoL datoH LABEL DW DW PUSH PUSH CALL ADD moduloA PROC PUSH MOV MOV MOV POP RET ENDP DWORD ? ? datoL datoH moduloA SP.SP DX.Dividir los problemas en módulos pequeños relacionados sólo por un conjunto de parámetros de entrada y salida. han de ser preservados al principio del módulo y restaurados al final.Paso de parámetros a través de un área común: se utiliza una zona de memoria para la comunicación. En este método. . Este debe conocer el número y tamaño de los mismos. por lo cual. de cómodo acceso. . y que los módulos siguen siendo reentrantes.Paso de parámetros en los registros: Los módulos utilizan ciertos registros muy concretos para comunicarse. de esta manera. lo que debe documentarse claramente al principio del módulo).EL LENGUAJE ENSAMBLADOR DEL 80x86 89 . Todos los demás registros han de permanecer inalterados. si son empleados internamente.

Se carga BP con SP debido a que el 8086 no permite el direccionamiento indexado sobre SP. AT Y PS/2 En el ejemplo. Como la instrucción CALL se dirige a una dirección cercana (NEAR). Si MODULOA fuera un procedimiento lejano (FAR) la variable estaría en [BP+6] y [BP+8]. empezando por el último: de esta manera se accede correctamente a los primeros N parámetros que se necesiten). Sin embargo. Al final. el cual comienza por preservar BP (lo usará posteriormente) para respetar la norma de caja negra. debido a que al llamar al módulo se habría guardado también en la pila el CS del programa que llama. en la pila se almacena sólo el registro IP. que es el caso más complejo (variables de 32 bits). tenemos la variable dato de 32 bits dividida en dos partes de 16. El lenguaje Pascal hubiera retornado con RET 4. A continuación se llama a MODULOA. haciendo innecesario que el programa que llama equilibre la pila. el método del lenguaje C expuesto es más eficiente porque no requiere que el módulo llamado conozca el número de parámetros que se le envían: éste puede ser variable (de hecho. Dicha variable es cargada en DX:AX antes de proceder a usarla (también deberían apilarse AX y DX para conservar la estructura de caja negra). . en [BP+2] el registro IP del programa que llama y en [BP+4] y [BP+6] la variable enviada. en [BP+0] está el BP del programa que llama. 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. el C apila los parámetros antes de llamar en orden inverso.90 EL UNIVERSO DIGITAL DEL IBM PC. Dicha variable es colocada en la pila empezando por la parte menos significativa. Por tanto.

. estos programas tienen un formato especial en disco. Los programas COM no hacen referencias a datos o direcciones separados más de 64 Kb. Además de la cantidad de memoria disponible y los posibles parámetros suministrados del programa. Por ello.CS e IP toman los valores del punto de arranque del programa (directiva END etiqueta). tanto COM como EXE.SS apunta al segmento de pila y SP = tamaño de la pila definida. En realidad. no se extrañe el lector de haber visto alguna vez ficheros EXE de más de 640 Kb: evidentemente.COM pase el control al programa que se pretende ejecutar.CS apunta al PSP e IP=100h (el programa empieza tras el PSP).0) que acaba cualquier programa sin problemas y sin ningún tipo de requerimientos adicionales. Los programas de tipo COM se cargan en memoria tal y como están en disco. Salvo excepciones. En los de tipo EXE: . 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». 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. . la dirección de retorno en caso de Ctrl-Break y en caso de errores críticos. no se cargan enteros en memoria aunque lo parezca. aunque el ensamblador da algo más de comodidad al programador en los mismos. aunque esto al usuario no le importe. Por ello. y su imagen en memoria no se corresponde realmente con lo que está grabado en el disco.SS apunta al PSP y SP toma la dirección más alta dentro del segmento del PSP. otra manera de acabar es por medio de la función 4Ch del sistema (disponible desde el DOS 2. se almacenan en disco «semiensamblados». entregándoseles el control. .TIPOS DE PROGRAMAS EJECUTABLES BAJO DOS. Cuando el programa toma el control. . calculando las direcciones de memoria absolutas.2. Los de tipo EXE. 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). No obstante. el DOS tiene que realizar la última fase de montaje. cuya descripción detallada se verá en el próximo capítulo.EJEMPLO DE PROGRAMA DE TIPO COM. se crea un bloque de 256 bytes llamado PSP (Program Segment Prefix). DS y ES apuntan al PSP. Si el programa es COM podemos terminarlo con la interrupción 20h (INT 20h). que pueden llegar a manejar múltiples segmentos de código de hasta 64 Kb.EL ENSAMBLADOR EN ENTORNO DOS 91 Capítulo VI: EL ENSAMBLADOR EN ENTORNO DOS 6. por lo que no es estrictamente necesario trabajar con programas EXE realizados en ensamblador. controladores de dispositivos o rutinas de apoyo a programas hechos en otros lenguajes. generado por los ensambladores y compiladores. En general. En él aparecen datos tales como la dirección de retorno al dos cuando finalice el programa. la mayoría de los programas desarrollados en este libro serán de tipo COM ya que los EXE ocuparían algo más. por cuenta y riesgo del programador. El siguiente ejemplo escribe una cadena en pantalla llamando a uno de los servicios estándar de impresión del DOS (función 9 de INT 21h): .1. al ser cargados en memoria. 6. Antes de que el COMMAND. Tipos de programas: En los de tipo COM: . la programación en ensamblador está hoy en día relegada a pequeños programas residentes.

La directiva ASSUME asocia cada registro de segmento con su correspondiente segmento.0) y el PUSH AX mete ese 0 en la pila. constante de retorno de carro . lo que también ayuda a estructurar más el programa. consumiendo más memoria.cr. e incluso para otros mucho más complejos. segmento común a CS. la dirección del texto a imprimir se coloca en DS:DX (CS=DS=ES=SS en un programa COM recién ejecutado) y se llama al DOS. fin del segmento . El límite máximo está en 64 Kb.3. Se pueden añadir los demás registros de segmento en el ASSUME. . con medio kilobyte basta para programas tan sencillos como el del ejemplo.lf. DS. consta de un único segmento.9 21h 20h . aunque es redundante. En general. todo lo anterior son directivas. es necesario preparar «a mano» la dirección de retorno al sistema.EJEMPLO DE PROGRAMA DE TIPO EXE. 6.92 EL UNIVERSO DIGITAL DEL IBM PC.texto AH. El carácter ’$’ delimita la cadena a imprimir. el XOR AX. programa de tipo COM . En primer lugar. lo cual es una herencia del CP/M (sería más interesante que fuera el 0 el delimitador) por razones históricas. de tipo COM. El segmento de código se define como procedimiento FAR. al volver . ya que estos programas serán cargados en memoria en la posición CS:100h. dirección de texto a imprimir función de impresión llamar al DOS volver al sistema operativo texto programa cr. ES.AX coloca un cero en AX (esta instrucción gasta un byte menos que MOV AX. aunque en realidad en los programas COM el punto indicado (en el ejemplo. Obsérvese que no se genera código hasta llegar a la línea «inicio:». en las primeras lineas las directivas EQU definen dos constantes para el preprocesador del compilador: cr=13 y lf=10. el ensamblador añadiría el prefijo del segmento CS a la instrucción al no estar seguro de que DS apunta a los datos. .». Con ello. . Téngase en cuenta que en la pila se almacenan las direcciones de retorno de las subrutinas y al llamar a una función de la BIOS la pila es usada con intensidad. Los programas EXE (listado en la página siguiente) requieren algo más de elaboración. son definidos por separado el segmento de código. Al final. La directiva ASSUME indica que. las instrucciones máquina se ensamblarán para el registro CS en este segmento (lo más lógico.lf. pila y datos. constante de salto de línea . Se acaba el programa con INT 20h. El punto de arranque es indicado con la directiva END. si hubiera que acceder a una variable. entre otras razones para que el ensamblador ensamble el RET del final (con el que se vuelve al DOS) como un RETF.". El segmento de pila se nombra siempre STACK y con el TLINK de Borland es necesario indicar también la clase ’STACK’."Grupo Universitario de Informática. SEGMENT ASSUME CS:programa. también conviene asumir el registro DS. El PUSH DS del principio coloca el segmento del PSP en la pila. Como puede observarse al principio del programa. es necesario definir una pila y reservar espacio para la misma. de lo contrario. SS."$" . Al contrario que los programas COM (cuya pila se sitúa al final del segmento compartido también con el código y los datos) esta característica obliga a definir un tamaño prudente en función de las necesidades del programa. El ORG 100h es obligatorio en programas COM. fin del programa y punto de inicio Programa tipo COM Olvidándonos de los comentarios que comienzan por «. «inicio») debe estar forzosamente al principio del programa. El programa. Como se ve. por defecto. por otra parte). AT Y PS/2 cr lf programa EQU EQU 13 10 . DS:programa ORG inicio: LEA MOV INT INT DB ENDS END inicio 100h DX. .

en el proceso de montaje y en función de cuál sea la primera posición de memoria libre. Segmento de datos datos texto datos SEGMENT DB cr."Texto a imprimir".cr.datos . ya que en los programas EXE por defecto no apunta a los datos.AX AX . AX = 0 .EL ENSAMBLADOR EN ENTORNO DOS 93 al DOS con RET (RETF en realidad) el control pasará a DS:0. DS:datos.AX . SS:pila . Aunque pueda parecer un tanto lioso. inicializar DS ."$" ENDS . cuando el programa sea cargado en memoria. direccionar segmento de datos con DS MOV MOV AX. se le asignará un valor determinado por el montador del sistema operativo. segmento del PSP . AX = dirección del segmento de datos DS. poner STACK es obligatorio . a la primera instrucción del PSP (INT 20h). En general. esto es.lf. DS:DX = dirección del texto . cr lf EQU EQU 13 10 .0 es más aconsejable terminar el programa con la función 4Ch del DOS. que después acaban con RET. por curiosidad.9 21h . Segmento de pila pila pila SEGMENT STACK ’STACK’ DB 128 dup (’pila’) ENDS . Segmento de código codigo ejemplo SEGMENT PROC FAR ASSUME CS:codigo.texto AH. reservados 512 bytes . ¿qué valdrá «datos»?: datos tiene un valor relativo asignado por el ensamblador. También debe observarse cómo se inicializa DS.lf. escribir texto LEA MOV INT DX. es un juego de niños y estas tres instrucciones consecutivas (PUSH DS / XOR AX. en realidad. a partir del DOS 2. RETF (PROC FAR) . desplazamiento 0 al PSP . poner dirección de retorno al DOS en la pila: PUSH XOR PUSH DS AX.AX / PUSH AX) son la manera de empezar de cientos de programas EXE. volver al DOS RET ejemplo codigo ENDP ENDS END ejemplo . fin del código . Ahora puede preguntarse el lector. punto de arranque del programa Programa EXE . que no requiere que CS apunte al PSP ni precisa de preparación alguna en la pila y además permite retornar un código de ERRORLEVEL en AL: en los programas futuros esto se hará con bastante frecuencia.

4. /Cp en lugar de /ml y /Cu en vez de /mu. 6.merece la pena utilizar el parámetro /m3.X): /a y /s /c Seleccionan un orden alfabético o secuencial de los segmentos.X. la sintaxis del TASM y MASM es más o menos equivalente: en el primero se obtiene ayuda con /H y en el segundo con /HELP. para ensamblar los listados de este libro hay que indicar la opción /Zm para mantener la compatibilidad con las versiones anteriores del ensamblador. Permite indicar el directorio donde el ensamblador debe de buscar los ficheros indicados en el programa fuente con INCLUDE. lenguaje máquina en el que sólo faltan las referencias a rutinas externas. o bien incluye el listado de referencias cruzadas directamente dentro del listado del programa (caso de TASM). Indica el nivel de advertencias: /w0 ninguna. Genera un listado de referencias cruzadas en un fichero de extensión CRF listo para ser procesado por CREF (MASM) añadiendo además números de línea al listado. apoyándose en una librería al efecto. con objeto de que de dos/tres pasadas y optimize más el código. En general. finalmente. El montador o linkador permite combinar varios módulos objeto.X se obtiene .X se emplea /Cx en lugar de /mx. AT Y PS/2 6.PROCESO DE ENSAMBLAJE. variables). Incluye sólo la información del número de línea.2. Con TASM. MASM 4. Aunque /d (en minúsculas) es un obsoleto parámetro de MASM para obtener un listado de la primera pasada del ensamblador.4. /mx sólo con los símbolos globales y /mu hace que se mayusculicen todos los símbolos globales. Por su lado. Lista las condiciones falsas (ensamblaje condicional). La sintaxis es (tanto para TASM como MASM): TASM fichero_fuente. .asm A continuación se listan los parámetros comunes a TASM 2.0 (y posterior) y MASM 4.ASM por defecto). bastará con hacer TASM nombre_programa (se supone la extensión . /w1 sólo las serias y /w2 sólo consejos. Cuando se emplea MASM 6. realizando las conexiones entre ellos y. Suprime las tablas de símbolos en el listado. cuya presencia puede comprobarse en el programa con una directiva IF (es útil para definir externamente un símbolo que indique que el programa está en fase de depuración. /D /e /Iruta /l[a] /m /n /p /t /w /X /z /Zi /Zd 6.4. indicando los números de línea del mismo en que son definidos y referenciados. Genera información simbólica para los depuradores de código. o si éste es corto -o el ordenador rápido. El fichero final tiene extensión OBJ.0/5. Al ensamblar módulos para usar desde lenguaje C hay que indicar por lo menos /mx. Con /m se indica el nivel de preservación del sentido de mayúsculas y minúsculas en los símbolos: /ml hace que se consideres diferentes mayúsculas de minúsculas en todos los símbolos. de cara a ensamblar cierto código adicional). es decir. Verifica que el código generado para el modo protegido es correcto (al emplear la directiva para generar instrucciones de modo protegido). La sintaxis quedaría: ML /Zm fihero_fuente. Es el programa que convierte nuestro listado fuente en código objeto.0 es capaz de darse cuenta de que se pretende definir un símbolo con /d a menos que se indique solo /d.0 (NO la 6.94 EL UNIVERSO DIGITAL DEL IBM PC. siendo además obligatorio indicar la extensión. Las referencias cruzadas son un listado de todos los símbolos del programa. . De la manera /Dsímbolo[=valor] permite crear el símbolo indicado. En MASM 6. Emula las instrucciones de punto flotante del 80x87. Con /l se genera un listado de ensamblaje y con /la un listado expandido. Permite la obtención de listados de código y de referencias cruzadas (símbolos. En general. cuando se va a obtener la versión definitiva del programa. etiquetas.TASM/MASM. entre 1 y 63. fichero_referencias_cruzadas Se puede omitir el fichero de listado y el de referencias cruzadas. fichero_listado. MASM presenta estadísticas adicionales si se indica /v y se puede cambiar con /Btamaño el nº de Kb de memoria que destina al fichero fuente. como se genera directamente el fichero EXE hay que indicar /c si se desea evitar esto (si no se quiere que linke). Suprime los mensajes si el ensamblaje es correcto.1. los convierte en módulo ejecutable de tipo EXE (empleando el ML de MASM 6. . Visualiza la línea del error y no sólo el número de la misma.TLINK/LINK.

por lo que es necesaria la ayuda de EXE2BIN para convertir el programa EXE en SYS. es algo más corto y más sencillo de desensamblar. .EL ENSAMBLADOR EN ENTORNO DOS 95 directamente el fichero EXE ya que invoca automáticamente al linkador). Los parámetros de TLINK son sensibles a mayúsculas y minúsculas. Alternativamente se puede indicar TLINK @fichero para que tome los parámetros del fichero de texto FICHERO. Al contrario de lo que algunos opinaron en su día. 6. EXE2BIN preguntará el segmento en que va a correr (algunas versiones permiten indicarlo de la manera /Ssegmento): esto permite generar código para ser ejecutado en un segmento determinado de la memoria (como pueda ser una memoria EPROM o ROM). reemplaza el módulo existente en la librería alternativamente *-. TLINK. Los comandos son de la forma <simbolo>nombre_de_módulo y pueden ser los siguientes: + * -+ -* añade el módulo objeto indicado a la librería borra el módulo indicado de la librería saca el módulo de la librería sin borrarlo (extrae fichero OBJ) alternativamente +-. será necesario este programa (al igual que cuando se utiliza LINK). En este libro no se desarrollan programas tan complejos que justifiquen su utilización. Puede obtenerse ayuda ejecutándolo sin parámetros. fichero_listado Si no se indican comandos se obtiene simplemente información del contenido de la librería en el fichero de listado (que puede ser CON para listado por pantalla).debe realizar una última operación de «montaje». el tiempo ha demostrado que nunca llegarían a ser directamente compatibles con los actuales entornos multitarea. en el caso de que estos sean demasiados y sea incómodo teclearlos cada vez que se linka.obj con la librería math.obj y el prog2.com) Si el programa no contiene ORG 100h. la sintaxis genérica de ambos es: TLINK fich_obj(s). sino que el DOS -tras cargarlos. El linkador permite el uso de librerías de funciones y rutinas. Un programa COM en memoria es una copia del fichero del disco.EXE basta con ejecutar TLINK prog1+prog2. fich_map. fich_exe. Cuando se crean programas SYS (que se diferencian de los COM básicamente en que no tienen ORG 100h) no se puede ejecutar TLINK /t.. Sintaxis: EXE2BIN fich.EXE2BIN.exe fich.4. EXE2BIN permite transformar un fichero EXE en COM siempre que el módulo ocupe menos de 64K y que esté ensamblado con ORG 100h. . fich_def Los ficheros no necesarios se pueden omitir (o indicar NUL): para linkar el fichero prog1. Los ficheros de texto de extensión MAP contienen información útil para el programador sobre la distribución de memoria de los segmentos.4. 6.4.lib generando PROG1. fich_libreria. permite generar un fichero de tipo COM directamente de un OBJ si se indica el parámetro /t. Los ficheros EXE generados por TLINK o LINK no son copia exacta de lo que aparece en la memoria. En cualquier caso. EXE2BIN genera un fichero binario puro de extensión BIN.exe (a veces hay que indicar EXE2BIN fich. Aunque los parámetros de uno y otro son bastante distintos. El gestor de librerías permite reunir módulos objeto en un único fichero para poder tomar de él las rutinas que se necesiten en cada caso.. extrae el módulo de la librería y lo borra de ella . por lo que /T no es lo mismo que /t.3. a diferencia de LINK. Si además existen referencias absolutas a segmentos. lo que agiliza aún más el proceso. Con LINK se obtiene ayuda indicando /HELP.TLIB/LIB.math. la sintaxis es la siguiente: TLIB fichero_libreria comandos. Si no se indicó el parámetro /t en TLINK.

TCREF/CREF.LIB se ejecutaría: TLIB libreria +quick-slow-+sort Si la lista es muy larga se puede incluir en un fichero y ejecutar TLIB @fichero para que la lea del mismo (si no cabe en una línea del fichero. los cuales también son admitidos en su mayoría por Codeview.ext) lo que permite conmutar entre la pantalla de depuración y la de ejecución pulsando la tecla ’\’. Además. en la práctica.. Sintaxis general: DEBUG [programa.LA UTILIDAD DEBUG/SYMDEB. La utilidad DEBUG incluída en los sistemas MS-DOS. aunque si se indica el nombre del fichero de referencias cruzadas genera un fichero de extensión XRF. la opción /c de TASM lo incluye en el listado.4. . lo que puede no ser deseable en algunos casos muy concretos. 6. Sólo es recomendable para programas grandes. por lo que el tiempo invertido en aprenderlos será útil no sólo para conocer el clásico y mítico DEBUG. divididos en módulos.OBJ. indicando los números de línea del mismo en que son referenciados (la línea en que son definidos se marca con #). para añadir el módulo QUICK. como ayuda a la depuración. Ej. Evidentemente. Esta utilidad genera listados en orden alfabético de los símbolos.. Esta utilidad se apoya en unos ficheros especiales.5.96 EL UNIVERSO DIGITAL DEL IBM PC. listos para su ejecución.OBJ por una nueva versión en LIBRERIA. Con el MASM la opción /c crea un fichero de referencias cruzadas de extensión CRF (respondiendo afirmativamente cuando pregunta por el mismo o indicándolo explícitamente en la línea de comandos). en el caso de los primeros se les cargará ya montados y con los registros inicializados. CREF y TCREF interpretan respectivamente los ficheros CRF y XRF generando un fichero de texto con extensión REF que contiene el listado de referencias cruzadas. También admite las instrucciones adicionales del 286 y los NEC V20/V30. . borrar el SLOW. viendo las modificaciones que sufren los registros y banderas.0 se admite el parámetro /S (con SYMDEB /S nomfich.6.5. conviene hacer referencia al programa SYMDEB que acompaña al MASM de Microsoft: se trata de un DEBUG mejorado. pero en algunos casos es más útil. con ayuda. 6. Su diferencia principal es que al abandonarlo para volver al DOS restaura los vectores de interrupción. 6. Es útil para depurar programas grandes y complejos. además. Veremos ahora los principales comandos del DEBUG. desde la versión 4.ext [parámetros] ] Los programas pueden ser de tipo EXE o COM. más rápido e inteligente (indica el tipo de función del sistema cuando al tracear un programa éste llama al DOS) y. los programas COM también se cargan con los registros inicializados y el correspondiente PSP preparado. AT Y PS/2 Por ejemplo. estos números de línea son relativos al listado de ensamblaje del programa (y no al fichero fuente). en los que MAKE chequea la fecha y hora para ensamblar sólo las partes que hayan sido modificadas. ejecutar programas paso a paso. Se trata de un programa menos complejo.OBJ y reemplazar el SORT. al estilo de los BAT del DOS.4. es 99% compatible. .: TASM fichero. cómodo y potente que depuradores de código como Turbo Debugger (de Borland) o Codeview (Microsoft).MAKE. de cara a automatizar el proceso de ensamblaje.fichero TCREF fichero Las referencias cruzadas son un listado de todos los símbolos del programa. puede escribirse & al final antes de pasar a la siguiente). Antes de empezar con ellos. así como con IP=100h. es una herramienta para depuración de programas muy interesante que permite desensamblar los módulos y.

. Entonces se pueden teclear órdenes que constarán generalmente de una sola letra. registros y.. CALL 1234:5678). Al entrar.Cadenas de caracteres: Encerradas entre comillas simples o dobles. byte a byte." La cadena ’ES:’ no será bien traducida a sus correspondientes valores ASCII. que normalmente irán separados por comas. Por ejemplo: XLAT MOV CS: XLAT ES: MOV CS: .Rangos: Son dos direcciones separadas por una coma. además: . o bien una dirección. . Estos parámetos pueden ser números hexadecimales de hasta dos o cuatro dígitos. en la que se resumen las principales órdenes. El texto puede a su vez encerrar fragmentos entrecomillados. y esto también WORD PTR [100]. 0A. SYMDEB permite además visualizarla en palabras (DW). error en DEBUG (sí vale con SYMDEB) . empleando comillas distintas a las más exteriores. Sería válida la dirección DS:BX+AX+104. coma flotante . ’$’ El DEBUG del MS-DOS 5. También se pueden cargar otros ficheros de cualquier extensión o simplemente entrar en el programa sin cargar ningún fichero. A [<dirección>] (assemble): permite ensamblar a partir de CS:IP si no se indica una dirección concreta.0 y el SYMDEB poseen una ayuda invocable con el comando ?. bien emsamblado con ambos . ’Curso de "8086"’ Con SYMDEB debe tenerse cuidado de no colocar el nombre de un registro de segmento en mayúsculas y seguido de dos puntos. A continuación se listan las más interesantes: Q (Quit): permite abandonar el programa y volver al DOS. E <dirección> [<lista>] (enter): permite consultar y modificar la memoria.. mal ensamblado con DEBUG (no así con SYMDEB) WORD PTR ES:[100]. D [<dirección> [numbytes]] (dump): visualiza el contenido de la memoria. Con DEBUG este problema no existe. aparecerá el prompt particular del DEBUG: un guión (-). "Otra ’cadena’ más". con DEBUG hay que ponerlas en una sola línea. CS:100. Ejemplo: "Cadena de caracteres". ya que no se interpretará correctamente: "ESTO ES: ESTA CADENA SERA MAL TRADUCIDA. aunque el offset siempre será numérico: 1E93:AD21. "Texto de ejemplo". 0D. dobles palabras (DD)..Listas: Son secuencias de bytes y/o cadenas separadas por comas: AC. Se admiten las directivas DB y DW del ensamblador. ES:19AC El depurador SYMDEB es mucho más flexible y permite también emplear registros de propósito general en el offset.EL ENSAMBLADOR EN ENTORNO DOS 97 Los parámetros opcionales no son los de el DEBUG o SYMDEB sino los que normalmente se suministrarían al programa a depurar. CALL FAR [100]) a no ser que sea evidente que lo son (ej. la letra ’L’ y un valor numérico que indica el número de bytes a partir de la dirección.1234 . . Por . Las instrucciones que requieran indicar un registro de segmento.Direcciones: Pueden expresarse con sus correspondientes valores numéricos o bien apoyándose en algún registro de segmento.1234 Los saltos inter-segmento deben especificarse como FAR (ej. La mayoría de las mismas admiten parámetros.

hay que renombrarles para cambiarles la extensión. Si no se indica <lista>.. Se trata de sectores lógicos del DOS y no los sectores físicos de la BIOS. Las versiones antiguas de SYMDEB dan errores en particiones de disco duro de más de 32 Mb. B. DEBUG desensamblará ese número de bytes y SYMDEB ese número de líneas.<dirección>. Las versiones antiguas de SYMDEB dan errores en particiones de más de 32 Mb. Por defecto. W <dirección> <unidad> <primer_sector> <num_sectores> (write): graba sectores de la memoria a disco en la unidad 0.. (A.. se ejecutará como tal introduciéndose en la subrutina o servidor de interrupciones correspondiente (SYMDEB no entra en los INT 21h).. 1.. B. pudiéndose modificar los bytes deseados. . Por ejemplo. (A... con RF se muestran los flags y se permite modificar alguno: Flag Desbordamiento Dirección Interrupción Signo Cero Acarreo auxiliar Paridad Acarreo Activo OV DN EI NG ZR AC PE CY (↓) (<0) (=0) (par) Borrado NV UP DI PL NZ NA PO NC (↑) (>0) (!=0) (impar) G [=<dirección> [. Por defecto se emplea CS: como registro de segmento. Si no se indica la dirección.3 se introducirían los bytes 1. si se ejecuta la orden ’rip’. W [<dirección>] (write): graba el contenido de una zona de memoria a disco. U E000:1940 .]] (go): ejecuta código desde CS:IP (a menos que se indique una dirección concreta). se solicitará un nuevo valor para IP. P [<veces>] (proceed): similar al comando T. . . L <dirección> <unidad> <primer_sector> <num_sectores> (load): carga sectores de la unidad 0. N <especificacion_fichero> (name): se asigna un nombre al programa que está siendo creado o modificado. con E 230 1.. Se trata de sectores lógicos del DOS y no los sectores físicos de la BIOS.98 EL UNIVERSO DIGITAL DEL IBM PC. la dirección es CS:100h. S <rango> <lista> (search): busca una cadena de bytes por la memoria. Para buscar la cadena . . avanzar al siguiente (barra espaciadora) o retroceder al anterior (signo -). Para acabar se pulsa RETURN.. 2 y 3 a partir de DS:230. Ejecutar T10 equivaldría a ejecutar 16 veces el comando T. Se puede indicar hasta 10 direcciones donde debe detenerse.. Si se trabaja sobre memoria ROM no debe indicarse la segunda dirección.. ¡esto último falla al tracear sobre memoria ROM!). aunque de esta manera no serán montados al cargarlos). Para que el flujo del programa se detenga en la 2ª dirección o posteriores debe pasar necesariamente por ella(s). Si es ejecutable lo prepara adecuadamente para su inmediata ejecución. se graba desde CS:100h hasta CS:100h+número_bytes. pero al encontrarse un CALL o INT lo ejecuta de golpe sin entrar en su interior (ojo. Como ejemplos válidos: U ES:100. si se indica rango..) a memoria.). Si la instrucción es CALL o INT. Si se trata de un EXE no se permitirá grabarlo (para modificarlos. U [<direccion> [<rango>]] (unassemble): desensambla la memoria. En BX:CX queda depositado el tamaño del fichero (BX=0 para ficheros de menos de 64 Kb).2. 1. se visualizará la memoria byte a byte.. el número de bytes se indica en BX:CX (no es una dirección segmentada sino un valor de 32 bits). R [<registro>] (register): permite visualizar y modificar el valor de los registros. T [<veces>] (trace): ejecuta una instrucción del programa (a partir de CS:IP) mostrando a continuación el estado de los registros y la siguiente instrucción. L [<dirección>] (load): carga el fichero de nombre indicado con el comando N. AT Y PS/2 ejemplo. Se puede indicar la trayectoria de directorios.

puntos de ruptura (breakpoints). H <valor1> <valor2> (hexaritmetic): muestra la suma y resta de valor1 y valor2. donde debe detenerse el programa si pasa por ellas) aunque es más incómodo.TXT. La BIOS accede directamente al hardware. para comparar 5 bytes de DS:100 y DS:200 se hace: C 100 L 5 200. borrarse con BC num_breakpoint. En SYMDEB se pueden definir con BP dirección. 64 y 80 bits con el comando D (DS. DL y DT). Una posibilidad interesante de DEBUG y SYMDEB es que admiten el redireccionamiento del sistema operativo. M <rango> <dirección> (move): Más que mover. para rellenar códigos 0AAh 100h bytes a partir de 9800h:0 se ejecutaría F 9800:0 L 100 AA. con suma facilidad. Parte del código de la BIOS es actualizado durante el arranque del ordenador. crear ficheros ASCII con órdenes y después suministrárselas al programa. El sistema operativo o DOS .LAS FUNCIONES DEL DOS Y DE LA BIOS.no actúa). Ello permite. como en el siguiente ejemplo: DEBUG < ORDENES. lógicamente. 6. También es versátil la posibilidad de redireccionar la salida.TXT. habilitarse con BP num_breakpoint (necesario antes de emplearlos). Por supuesto. Por ejemplo. en vez de AA se podría haber indicado una lista de bytes o cadenas de caracteres. de lo contrario no se devolvería el control al DOS ni se podría parar el programa (la entrada por defecto -el teclado. También existen comandos en DEBUG para acceder a la memoria expandida: XS (obtener el estado de la memoria expandida). Además. C <rango> <dirección> (compare): compara dos zonas de memoria mostrando las diferencias. XA npag (localizar npag páginas). Por ejemplo. SYMDEB puede visualizar datos en coma flotante de 32. No se encontraría sin embargo "pepe" (en minúsculas). La última orden de este fichero deberá ser Q (quit). con los ficheros que incluye el sistema operativo. se puede teclear un comando para desensamblar (U) y otro para salir (Q): en el disco aparecerá el fichero con los datos del desensamblaje (se teclea a ciegas. Por ejemplo. XD handle (desalojar el handle indicado) y XM pagina_logica pagina_fisica handle (mapear páginas). SYMDEB es realmente un depurador simbólico (SYMbolic DEBugger) que permite mostrar información adicional y depurar con mayor comodidad los programas que han sido ensamblados con información de depuración. liberando a los programas de usario de las tareas más complejas. que tampoco excede los 16 bits). copia una zona de memoria en otra de manera inteligente (controlando los posibles solapamientos de los bloques). Con SYMDEB pueden además colocarse. almacenado en las memorias ROM del ordenador. tras DEBUG > SALIDA. con DEBUG se pueden implementar con la orden G (indicando más de una dirección hasta un máximo de 10. ambos operandos de un máximo de 16 bits (si hay desbordamiento se trunca el resultado. también es posible redireccionar entrada y salida a un tiempo: DEBUG < ORDENES. O <puerto> <valor> (output): envia un valor a un puerto de E/S.6 . deshabilitarse con BD num_breakpoint y listar los definidos con BL. I <puerto> (input): visualiza la lectura del puerto de E/S indicado.EL ENSAMBLADOR EN ENTORNO DOS 99 "PEPE" terminada por cero en un área de 512 bytes desde DS:100 se haría: S 100 L 200 "PEPE". F <rango> <lista> (fill): llena la zona de memoria especificada con repeticiones de la lista de bytes indicada. El código de la BIOS. constituye la primera capa de software de los ordenadores compatibles. porque la salida por pantalla ha sido redireccionada al fichero).0 (por defecto se busca en DS:).TXT > SALIDA. por ejemplo.

sectores. Reservada.100 EL UNIVERSO DIGITAL DEL IBM PC. Servicios del DOS. La BIOS emplea un cierto rango de interrupciones. Compatibilidad CP/M-80. Red local MS NET. Procesos Batch.COM. . Tratamiento de Ctrl-C. Llamar a la ROM del BASIC (sólo máquinas IBM). como cualquier otro. Escritura absoluta en disco (sectores lógicos). Interrupción Multiplex. El DOS pone a disposición de los programas de usuario unas funciones muy evolucionadas para acceder a los discos y a los recursos del ordenador. cuando se habla de funciones del DOS. Idle (ejecutada cuando el ordenador está inactivo). realizando la tarea solicitada. FUNCIONES DEL DOS El DOS emplea varias interrupciones. Servicios de impresora. Lectura absoluta de disco (sectores lógicos). Reinicialización del sistema. Por encima del DOS se suele colocar habitualmente al COMMAND. Servicios de teclado. etc. Uso interno del DOS. ejecutando una interrupción software con un cierto valor inicial en los registros. en su lugar se apoya en la BIOS. Tratamiento de errores críticos. ejecutado sobre el DOS y que además no pone ninguna función a disposición del sistema (al menos. al igual que la BIOS. documentada). Apunta a la tabla de los caracteres ASCII 128-255 (8x8 puntos). AT Y PS/2 propiamente dicho se instala después: el DOS no realiza ningún acceso directo al hardware. Funciones casette (PC) y servicios especiales del sistema (AT). Servicios de disco (muy elementales: pistas. la interrupción más importante con diferencia. aunque BP es corrompido en los servicios de vídeo de las máquinas más obsoletas. Servicios horarios. En general. Terminar dejando residente el programa (en desuso).). Control de finalización de programas. Informe sobre el tamaño de la memoria convencional. desde los programas de usuario. aunque realmente el COMMAND no constituye capa alguna de software: es un simple programa de utilidad. Impresión rápida en pantalla (no tanto). sin embargo. Informe sobre la configuración del equipo. cada una encargada de una tarea específica: INT INT INT INT INT INT INT INT INT INT INT INT 10h: 11h: 12h: 13h: 14h: 15h: 16h: 17h: 18h: 19h: 1Ah: 1Fh: Servicios de Vídeo (texto y gráficos). La mayoría de las interrupciones se invocan solicitando una función determinada (que se indica en el registro AH al llamar) y se limitan a devolver un resultado en ciertos registros. su única misión es cargar otros programas. todo el mundo sobreentiende que se trata de llamar a la INT 21h. FUNCIONES DE LA BIOS Las funciones de la BIOS se invocan. sólo resultan modificados los registros que devuelven algo. constituyendo una segunda capa de software. Comunicaciones en serie. INT 20h: INT 21h: INT 22h: INT 23h: INT 24h: INT 25h: INT 26h: INT 27h: INT 28h: INT 29h: INT 2Ah: INT 2Bh-2Dh: INT 2Eh: INT 2Fh: INT 30h-31h: INT 32h: Terminar programa (tal vez en desuso).

ESCRIBIR CARACTER EN LA IMPRESORA . . . . . . . . . . . . . . . . ESCRITURA ALEATORIA DE REGISTRO EMPLEANDO FCB . OBTENER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE . . . . . . . . . . . . . . . BORRAR FICHERO EMPLEANDO HANDLE . . . . . . . . REDIRECCIONAR EL HANDLE . PASAR DE E/S SECUENCIAL A ALEATORIA EMPLEANDO FCB . . . . . . . . . "OPEN" . . . OBTENER TAMAÑO DE FICHERO EMPLEANDO FCB . ESCRITURA SECUENCIAL EN FICHERO EMPLEANDO FCB . . . . . . . LECTURA ALEATORIA DE BLOQUE EMPLEANDO FCB . . . . . . . . "DUP" . . . . . . . . . que consisten en unos números que identifican a los ficheros cuando son abiertos. . . . SIN IMPRESION . . . . . CREATE TEMPORARY FILE . . . . . . . . . . . . . . . . ESTABLECER MAXIMO NUMERO DE HANDLES PARA LA TAREA EN CURSO . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO FCB . . . . . . . . . . . . . .3+ OPEN FILE USING FCB . BUFFERED INPUT . . . . . . . . . . . . . . Se indica también la versión del DOS a partir de la que están disponibles. . . . . . . . . . se las referencia aún como indocumentadas). . . . WRITE CHARACTER TO STDAUX . . . . . . . y pueden consultarse en el apéndice al efecto al final del mismo. . . . . . . . . . . . . . . . . . . . . . Todos los valores mostrados a continuación son hexadecimales. . . CREAR NUEVO FICHERO SIN MACHACARLO SI EXISTIA EMPLEANDO HANDLE . . . . . . . . . . . el de la izquierda es el número de función (lo que hay que cargar en AH antes de llamar). . . . . . . GET DISK TRANSFER AREA ADDRESS . . . APERTURA DE FICHERO EMPLEANDO FCB . . . . . . . . . . . . . . . . . . . . . . y el sistema de ficheros se basa en el horroroso método FCB. .READ FROM FILE OR DEVICE "WRITE" . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN LA SALIDA ESTANDAR . . . . . algunas funciones del DOS se dividen a su vez en subfunciones. LECTURA SECUENCIAL DE FICHERO EMPLEANDO FCB . . . . . . . . . . . . . . . . . . "FINDNEXT" . . . . . . LIMPIAR BUFFER Y LEER DE LA ENTRADA ESTANDAR GESTION DE FICHEROS 0F 10 11 12 13 16 17 23 29 *3C *3D *3E 41 43 43 45 46 4E 4F 56 57 57 5A 5B 67 68 -------------00 01 -----00 01 ----DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 2+ 3+ 3+ 3. . . . . . . . . . . . . . . . . . . . . READ CHARACTER FROM STDAUX . . BUSCAR PRIMER FICHERO EMPLEANDO FCB . . . . . . . . . . . . . se debe intentar emplear siempre las funciones que requieran la menor versión posible del DOS. CHARACTER INPUT WITHOUT ECHO . . . . . . . . . ESCRIBIR CADENA EN LA SALIDA ESTANDAR . . . . . . . . . . . . . . . . . no es necesario buscar la compatibilidad con el DOS 1. . . RANDOM BLOCK WRITE TO FCB FILE . . . el teclado. . . . . . . . . . DELETE FILE USING FCB . . . . . . . . . . . . . . CREAR/TRUNCAR FICHERO EMPLEANDO FCB . . . . . . . . . . . . . . . . . . . . . . . . . aunque Microsoft desclasificó casi todas ellas a partir del MS-DOS 5. . . . . . . . 2 (salida de error estándar -también pantalla-). RENOMBRAR FICHERO EMPLEANDO HANDLE . . . . . . . . . . WRITE CHARACTER TO PRINTER . . . . . . . . . . . . . . . FIND FIRST MATCHING FILE USING FCB . . . . . . . GET FILE SIZE FOR FCB . . . . . "CLOSE" . . . . . . . . . .COMMIT FILE . "CHMOD" . . . . "RENAME" . . . . . ESCRITURA ALEATORIA DE BLOQUE EMPLEANDO FCB OBTENER LA DIRECCION DEL AREA DE TRANSFERENCIA A DISCO . ESTABLECER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE . . . . . . . . . . . "READ" . . . . BUSCAR PRIMER FICHERO EMPLEANDO HANDLE . . . WITHOUT ECHO . . SIN IMPRESION . ENTRADA/SALIDA DE CARACTERES AH AL Versión 01 *02 03 04 05 06 06 07 08 *09 *0A 0B 0C -------------DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ Nombre original READ CHARACTER FROM STANDARD INPUT. . . . . . . . . ESCRIBIR CARACTER EN EL PUERTO SERIE . . . . . escritas con anterioridad. . . . . . . . . . . FLUSH BUFFER AND READ STANDARD INPUT . . 1 (salida estándar -pantalla-). . . . . . . . . . . . . siendo recomendable ignorar su existencia y trabajar con los handles. . . . . . . . . . . . . . . ENTRADA DESDE TECLADO POR BUFFER . . . . . . . . CERRAR FICHERO EMPLEANDO FCB . . CREATE OR TRUNCATE FILE USING FCB . . . ."FFLUSH" . . . . . . . . . . . . . . . . . . .0 (en muchas secciones de este libro. . . . . . . .DELETE FILE . GET FILE ATTRIBUTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .WRITE TO FILE OR DEVICE . . . . . OBTENER ESTADO DE LA ENTRADA ESTANDAR . . . BORRAR FICHERO EMPLEANDO FCB . . . . . . . . . . . . . . . . . .3+ 3. . . . . . . . . . . . . . . 3 (entrada/salida por puerto serie) y 4 (salida por impresora): la pantalla. . . . . . . . LECTURA ALEATORIA DE REGISTRO EMPLEANDO FCB . . . . . READ RANDOM RECORD FROM FCB FILE WRITE RANDOM RECORD TO FCB FILE . . "DUP2". .CLOSE FILE . . . CREAR/TRUNCAR FICHERO EMPLEANDO HANDLE . . . . . . . pueden ser manejados como simples ficheros. . . . . . . . . . . . . . . . . . . . . FIND NEXT MATCHING FILE USING FCB . . . . . . . . . ABRIR FICHERO EXISTENTE EMPLEANDO HANDLE . . . . . . . ESTABLECER EL AREA DE TRANSFERENCIA A DISCO . . . .SET HANDLE COUNT . . . . . . . al estilo del UNIX. WITH ECHO WRITE CHARACTER TO STANDARD OUTPUT .CREATE OR TRUNCATE FILE . . . . . . . . . . . .FIND FIRST MATCHING FILE . . . . . . . . . . . . . . . . . . . . . CERRAR FICHERO EXISTENTE EMPLEANDO HANDLE . . . . . . . . . . . . CREATE NEW FILE . SEQUENTIAL WRITE TO FCB FILE . . CREAR FICHERO TEMPORAL EMPLEANDO HANDLE . PARSE FILENAME INTO FCB . . Los FCB ya no están soportados siquiera en la ventana de compatibilidad DOS de OS/2. . etc. .RENAME FILE . . sin embargo. En general. . . . . . . OBTENER ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE . . . . . . GET STDIN STATUS . . . . . . . . . . . . Existen 5 handles predefinidos permanentemente abiertos: 0 (entrada estándar -teclado-). . . . Traducción LEER CARACTER DE LA ENTRADA ESTANDAR. . . . DIRECT CONSOLE INPUT . . . . . . .EL ENSAMBLADOR EN ENTORNO DOS 101 Las funciones del DOS se invocan llamando a la INT 21h e indicando en el registro AH el número de función a ejecutar. . .OPEN EXISTING FILE . . . . . . . seleccionables mediante AL (segundo valor numérico. . DIRECT CONSOLE OUTPUT . . . . . Las funciones marcadas con U> fueron históricamente indocumentadas. . . . . . . . . . . . . LEER CARACTER DEL PUERTO SERIE . .SET FILE ATTRIBUTES . . LECTURA DE CARACTERES. . . . SET RANDOM RECORD NUMBER FOR FCB RANDOM BLOCK READ FROM FCB FILE . .FIND NEXT MATCHING FILE . . . . .DUPLICATE FILE HANDLE . . . . . . . CLOSE FILE USING FCB . . . .0: esta versión no soporta subdirectorios. . . . RENAME FILE USING FCB . . . . . WRITE STRING TO STANDARD OUTPUT . . RENOMBRAR FICHERO EMPLEANDO FCB . . VOLCAR BUFFERS INTERNOS A DISCO OPERACIONES SOBRE FICHEROS 14 15 *1A 21 22 24 27 28 *2F *3F *40 -----------DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS DOS 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 2+ 2+ 2+ SEQUENTIAL READ FROM FCB FILE . . . . . . . . . . SET FILE’S DATE AND TIME . . . . . . . . . . . . . . . . . . . . . . . . . en los casos en que aparece). ENTRADA DIRECTA POR CONSOLA . . . . "FORCEDUP" . SET DISK TRANSFER AREA ADDRESS . . . . . . . . . DUPLICAR EL HANDLE HANDLE . . . . . . . . . . . . . ESCRIBIR EN UN FICHERO EMPLEANDO HANDLE . . . . . LECTURA DIRECTA DE CARACTER. . . . . . . EXPANDIR EL NOMBRE DEL FICHERO EMPLEANDO FCB . . . devolviendo normalmente el acarreo activo cuando se produce un error (con un código de error en el acumulador). . Sólo modifican los registros en que devuelven los resultados. . . . . . . CON IMPRESION . . . . . . "UNLINK" . MODIFICAR ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE . . . . . . . . . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO HANDLE . . . "CREAT" . SALIDA DIRECTA A CONSOLA . . . . . . . . . . . . . . . . . . . . . . . . . . . .FORCE DUPLICATE FILE "FINDFIRST" . . . . . . Muchas funciones de los lenguajes de programación frecuentemente se limitan a llamar al DOS. . . . . . . . . . . . . . Las funciones precedidas de un asterisco son empleadas o mencionadas en este libro. . . . . . . . LEER DE UN FICHERO EMPLEANDO HANDLE . DIRECT CHARACTER INPUT. GET FILE’S DATE AND TIME . . .

SELECT DEFAULT DRIVE . . . . . . . . . . . . .CREATE CHILD PSP .GET LIST OF LISTS . ESTABLECER DIRECCION DEL PSP ACTUAL . CONTROL E/S: ESTABLECER INFORMACION DEL DISPOSITIVO DOS 2+ . . . CONTROL E/S: ESCRIBIR EN CANAL CONTROL DISP. .SET DEVICE DRIVER LOOKAHEAD FLAG . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M DOS 1+ . . . . . .SET GLOBAL CODE PAGE TABLE . . .READ FROM BLOCK DEVICE CONTROL CHANNEL . . . . ESTABLECER EL CARACTER INDICADOR DE PARAMETROS DOS 2. . . . . . . . . . . . . . .0 . . OBTENER ASIGNACION DE UNIDADES LOGICAS DOS 3. . . . . . . . . . . . . . . . . . . . . . .3+ . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA ENTRADA DOS 2+ . . . . . . . . . .ALLOCATE MEMORY . . . . . . . . . . . .GET EXTENDED ERROR INFORMATION .0 . . . .GET RETURN CODE . ."EXIT" . . . . .1+ . CONTROLAR EL NIVEL DE DETECCION DE CTRL-BREAK DOS 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .GET TRUE VERSION NUMBER . . . OBTENER DIRECCION DEL PSP ACTUAL . . . . . . .TERMINATE AND STAY RESIDENT . . . . . . . . . . . . . . . . . . ESTABLECER LA PAGINA DE CODIGOS GLOBAL U> DOS 4+ internal . . . . . . . . . . . . . . . . . . . . . . . . . . . .RESIZE MEMORY BLOCK . . . . . . . . . . . . OBTENER VERSION REAL DEL DOS DOS 2+ . . .1+ . . .TERMINATE WITH RETURN CODE . . .TERMINATE PROGRAM . . .GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE . . . . . . .SPECIFY \DEV\ PREFIX USE . . . . . . . . . ."AVAILDEV" . . . . . OBTENER LA UNIDAD ACTUAL POR DEFECTO OBTENER INFORMACION DE ESPACIO EN EL DISCO POR DEFECTO .SET CURRENT FILE POSITION . . . . . . . . . . . . . . . . . . BORRAR SUBDIRECTORIO . . . . . . . . . .DOS 3+ . . . . . . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE CARACTERES DOS 3. . .NULL FUNCTION FOR CP/M COMPATIBILITY . OBTENER DIRECCION DEL PSP ACTUAL GESTION DE MEMORIA *48 *49 *4A *58 *58 -----DOS DOS DOS DOS DOS 2+ .GET DOS SWAPPABLE DATA AREAS . . . . . . . GET FREE DISK SPACE . .3+ only .IOCTL . . . . . OBTENER DIRECCION DEL AREA INTERCAMBIABLE DEL DOS DOS 3. .x only internal . . . OBTENER INFORMACION EXTENDIDA DE ERRORES U> DOS 3. . . . . . . .GET/SET DISK SERIAL NUMBER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .GET INTERRUPT VECTOR . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M DOS 1+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . .GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE . . . . . . . . . . . . 3+ . . . . . OBTENER LA DIRECCION DE INDOS DOS 2+ . . . . . .SET INTERRUPT VECTOR . . . . . . . . . . . . . . .SET SWITCH CHARACTER . . TERMINAR Y PERMANECER RESIDENTE . . . . INDICAR/OBTENER NIVEL DETECCION CTRL-BREAK DOS 4+ . . . .FREE MEMORY . . . . ."SWITCHAR" . . . .2+ .IOCTL . . . . . . . . . . . . . .EXTENDED BREAK CHECKING . . .SET COUNTRY CODE . . . . . . . . . . . .GET GLOBAL CODE PAGE TABLE . . . . . . . . . . OBTENER EL ESPACIO LIBRE EN DISCO . . . . . . . . . . . . CARAC. . . . . .GET BOOT DRIVE .IOCTL . . . . . .SET DEVICE INFORMATION . . . . . . . . . . . . . .GET ADDRESS OF INDOS FLAG . . . OBTENER EL LISTADO DE LAS LISTAS DEL SISTEMA DOS 2+ internal . . .CREATE SUBDIRECTORY . . . . . . . . . . . . .CHECK IF HANDLE IS REMOTE . . . . . . . . . . ESTABLECER UNIDAD POR DEFECTO . . . OBTENER/ESTABLECER EL NUMERO DE SERIE DE UN DISCO U> DOS 5. . .GET DOS VERSION . . TRADUCIR BPB A DPB DOS 2+ internal . . . . . . . . . . . . . . . . . .1+ . . . . . . . . . . . . . . . . . . . .2+ internal . . . . . . . . . . . . CARGAR Y/O EJECUTAR PROGRAMA TERMINAR PROGRAMA CON CODIGO DE RETORNO . .NULL FUNCTION FOR CP/M COMPATIBILITY .GET DEVICE INFORMATION . .REMOVE SUBDIRECTORY . . . . . . . . .UNUSED . . . . . .SET LOGICAL DRIVE MAP . . . . . . . . . internal . . . . . . . . . . . . . .IOCTL . . . . . . . .IOCTL . . . . . . . . . . TERMINAR PROGRAMA . . . . . . . . . . . .DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONS . . . . . . . . . . . . . . . . . . . CONTROL E/S: COMPROBAR SI EL DISP. . . . . NO USADA AUN DOS 3. . . . . . . . . . . . .NULL FUNCTION . . . . . . . . . OBTENER EL DPB DE LA UNIDAD POR DEFECTO DOS 1+ . . . . . . DE BLOQUE ES REMOTO DOS 3. . . . . . . . . . . . . . . . . . . .x and 3. . . . . . . . .LOAD AND/OR EXECUTE PROGRAM . . . . . . . . . .2+ . . . . . . . . . . . . . . . . . . . . . . . .IOCTL .IOCTL . OBTENER LA DIRECCION DE UN VECTOR DE INTERRUPCION DOS 2+ . . . . ."SYSVARS" . . . . . . . . . . . .NULL FUNCTION FOR CP/M COMPATIBILITY . . . ESTABLECER VECTOR DE INTERRUPCION DOS 2+ . . . . . . . CONTROL E/S: ESCRIBIR EN CANAL CONTROL DISP. . . . . . OBTENER/ESTABLECER LA ESTRATEGIA DE ASIGNACION DE MEMORIA OBTENER/ESTABLECER EL ESTADO DE CONEXION DE LA MEMORIA SUPERIOR CONTROL DE FECHA Y HORA *2A 2B *2C 2D ----DOS DOS DOS DOS 1+ 1+ 1+ 1+ GET SET GET SET SYSTEM SYSTEM SYSTEM SYSTEM DATE DATE TIME TIME . . CONTROL E/S: OBTENER INFORMACION DEL DISPOSITIVO DOS 2+ . . . . .SET CURRENT PROCESS ID (SET PSP ADDRESS) internal . . . . . OBTENER EL BANDERIN DE VERIFICACION CONTROL DE PROCESOS 00 26 *31 *4B *4C 4D *50 *51 *62 ---------DOS DOS DOS DOS DOS DOS DOS DOS DOS 1+ 1+ 2+ 2+ 2+ 2+ 2+ 2+ 3+ . . . . . . . . . . . . .GET CURRENT PSP ADDRESS . . . . . . . . OBTENER INFORMACION RELATIVA AL PAIS DOS 3+ . . . . . .COUNTRY-DEPENDENT FILENAME CAPITALIZATION . . . . . CREAR PSP HIJO DOS 3+ . . . . . .0+ internal . . .CHECK IF BLOCK DEVICE REMOTE . . . . . REINICIALIZAR EL DISCO . .x+ internal .GET EXTENDED COUNTRY INFORMATION . . . . . . . . . . . . . . . . DOS 2+ . . .GET OR SET UMB LINK STATE . . . .3+ . . . . . . . . . . . OBTENER AREAS INTERCAMBIABLES DEL DOS DOS 3. . . . . . .IOCTL . . . DETERMINAR UNIDAD DE ARRANQUE DOS 5. . . . . . DETERMINAR SI UNA LETRA INDICA SI O NO U> DOS 4+ internal . . . . . . . . . . . . . . . . . . . . . DE BLOQUE ES REMOVIBLE DOS 3. . . . . . . . . . . . . . . . . . . . . . . ."EXEC" . . . . . . . . . . . . . . . . . . . . .GET LOGICAL DRIVE MAP . . . . . . . . . .GENERIC CHARACTER DEVICE REQUEST . . . . .GET COUNTRY-SPECIFIC INFORMATION . . . . . . . . . ."FLOCK" . . .102 EL UNIVERSO DIGITAL DEL IBM PC. . . . . . . . . . . . . . . . . . . . . . .IOCTL . . . . BLOQUE DOS 2+ . . .EXTENDED OPEN/CREATE .SET EXTENDED ERROR INFORMATION . OBTENER INFORMACION DE ESPACIO EN EL DISCO INDICADO . . . . . . . . . . . . . . . ."SWITCHAR" . . . . . . . . . . . . . . . . . . . .GET OUTPUT STATUS . . . . . CREAR PSP . . FUNCION NULA PARA COMPATIBILIDAD CP/M DOS 1+ . . . GET CURRENT DEFAULT DRIVE . . . . . OBTENER LA PAGINA DE CODIGOS GLOBAL DOS 3. . . . . CAMBIAR EL DIRECTORIO ACTIVO . . . . . . . . LIBERAR MEMORIA . . . . . . . ESTABLECER INFORMACION EXTENDIDA DE ERRORES U> DOS 4. . . . . .GET OR SET MEMORY ALLOCATION STRATEGY 5. . . . . .IOCTL . . CONTROLAR EL USO DEL PREFIJO \DEV\ DOS 2+ . . . . . . . . . . . . . . . . .0+ . . . .IOCTL . . . . .0+ . . . . . . . . . . . . . CARAC. . . . . . . . . . . . ."LSEEK" . . . FUNCION NULA PARA COMPATIBILIDAD CP/M DOS 1+ . . . . . . CONTROL E/S: DEFINIR NUMERO DE REINTENTOS EN MODO DE COMPARTICION DOS 3. . . . . . . . . . ESTABLECER EL BANDERIN DE VERIFICACION . . . . . . OBTENER LA HORA . . . . . . . MOVER EL PUNTERO RELATIVO EN EL FICHERO EMPLEANDO HANDLE 5C -. .3+ . . . . OBTENER EL DPB DE LA UNIDAD INDICADA DOS 2+ . . . . . . . . . . . . . . . . . . ESTABLECER LA FECHA . . . . . . . . . . . . .SET SHARING RETRY COUNT . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE BLOQUE DOS 3. . . OBTENER EL DIRECTORIO ACTUAL MANEJO DE DISCO 0D 0E 19 1B 1C 2E *36 54 --------DOS DOS DOS DOS DOS DOS DOS DOS 1+ 1+ 1+ 1+ 1+ 1+ 2+ 2+ DISK RESET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .IOCTL .0 . CONTROL E/S: COMPROBAR SI EL DISP. . . . . . CREAR SUBDIRECTORIO . . . . . MODIFICAR EL TAMAÑO DE UN BLOQUE DE MEMORIA ASIGNADA . . . OBTENER LA FECHA . . . EXPANDIR NOMBRE DE FICHERO A ESPECIFICACION COMPLETA DE DIRECTORIOS DOS 3+ . . . . . . . . . . . CONTROL E/S: COMPROBAR SI UN HANDLE ES REMOTO DOS 3. . .TRANSLATE BIOS PARAMETER BLOCK TO DRIVE PARAM BLOCK . AT Y PS/2 42 -. . . . . . . . . . . . . . . . . .GET ADDRESS OF DOS SWAPPABLE DATA AREA .RECORD LOCKING . . . . .IOCTL . . . . . . . . . . . . .1+ . . . . .DOS 2+ . . . .SET CURRENT DIRECTORY GET CURRENT DIRECTORY . .2+ . . . . . . . . . . . . . . . . . . . CONTROL E/S: LEER DE CANAL CONTROL DISP. . . . . . . . . . . . . . . . . .GET INPUT STATUS . . . . . . . . . . . . . . . ESTABLECER LA HORA DEL DEL DEL DEL SISTEMA SISTEMA SISTEMA SISTEMA FUNCIONES MISCELANEAS 18 1D 1E 1F 20 *25 *30 32 33 33 33 33 *34 *35 37 37 37 *38 38 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 *52 53 55 *59 *5D *5D *5D 60 61 64 65 65 65 66 66 69 6B 6C ---------02 05 06 --00 01 ---00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ----06 0A 0B ----23 -01 02 --00 DOS 1+ . . . . .GENERIC BLOCK DEVICE REQUEST . . . . . . . . 2+ . . . . . . . . . . . . . . . .READ FROM CHARACTER DEVICE CONTROL CHANNEL . . . .2+ . . . . . . . . . . . .CHECK IF BLOCK DEVICE REMOVABLE . . . . . . . . . . . . . . . . . . . . CONTROL E/S: LEER DE CANAL CONTROL DISP. . . . . . . . . . . BLOQUE DOS 2+ . . . . . . . . DOS 2+ . . . . . . . . . . . . . .IOCTL . BLOQUEAR/DESBLOQUER UNA ZONA DEL FICHERO EMPLEANDO HANDLE OPERACIONES CON DIRECTORIOS 39 3A 3B 47 ----DOS DOS DOS DOS 2+ 2+ 2+ 2+ "MKDIR" "RMDIR" "CHDIR" "CWD" . . . . . . . . . .WRITE TO CHARACTER DEVICE CONTROL CHANNEL . . . FUNCION NULA DOS 4+ . . . . . . . . . . .IOCTL . . . . . . . . . .GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE . . . OBTENER EL CARACTER INDICADOR DE PARAMETROS DOS 2+ . . MAYUSCULIZACION DE NOMBRE DEPENDIENTE DEL PAIS DOS 3. . . ESTABLECER BANDERIN DE LECTURA ADELANTADA DE DISPOSITIVO DOS 3. . . . . . . . . . . . . . OBTENER CODIGO DE RETORNO . .GET CURRENT PROCESS ID (GET PSP ADDRESS) . . . . . DEFINIR ASIGNACION DE UNIDADES LOGICAS U> DOS 2+ internal . . . . OBTENER INFORMACION EXTENDIDA DEL PAIS U> DOS 4+ internal . . . . . . GET VERIFY FLAG . . ASIGNAR MEMORIA . . . . . . . . . . . . . APERTURA/CREACION DE FICHEROS EXTENDIDA EMPLEANDO HANDLE . 2+ . . GET ALLOCATION INFORMATION FOR DEFAULT DRIVE GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE SET VERIFY FLAG . . . .GET SWITCH CHARACTER . . .WRITE TO BLOCK DEVICE CONTROL CHANNEL . . . OBTENER VERSION DEL DOS DOS 2+ . .CANONICALIZE FILENAME OR PATH . . . . . . . . . . . . . . . . . . ESTABLECER EL CODIGO DEL PAIS DOS 2+ . . .NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . .CREATE NEW PROGRAM SEGMENT PREFIX . . . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA SALIDA DOS 3. . . .IOCTL . . . . . . . . . . . . . . . .

en la pila se almacena el CS:IP de la instrucción ilegal.ARQUITECTURA DEL PC.LAS INTERRUPCIONES Son señales enviadas a la CPU para que termine la ejecución de la instrucción en curso y atienda una petición determinada. Hay una sutil diferencia de comportamiento ante esta interrupción según el tipo de procesador: el 8088/8086 y los NEC V20 y V30 almacenan en la pila. existe espacio suficiente para los 256 vectores de interrupción disponibles. la dirección de la instrucción que sigue a la que causó la excepción. AT Y PS/2 BAJO DOS 7. tiene prioridad absoluta y se produce incluso aunque estén inhibidas las interrupciones (con CLI) para indicar un hecho muy urgente (fallo en la alimentación o error de paridad en la memoria). se leen dos palabras: la primera es el desplazamiento y la segunda el segmento de la rutina deseada. INT 7: dispositivo no disponible (sólo a partir del 286). generada automáticamente cuando el cociente no cabe en el registro o el divisor es cero. Cada interrupción lleva asociado un número que identifica el tipo de servicio a realizar. INT 4: desbordamiento. IBM se saltó olímpicamente la especificación de Intel que reserva las interrupciones 0-31 para el procesador. en el primer kilobyte de memoria física del sistema. INT 5: rango excedido en la instrucción BOUND (sólo 286 y superiores). Por tanto. . Por desgracia. se dispara cuando se ejecuta un INTO y había desbordamiento. Sólo puede ser generada mediante DIV o IDIV. como cabría esperar. INT 6: código de operación inválido (sólo a partir del 286). Sin embargo. debido a que es una instrucción de un solo byte muy cómoda de utilizar.Interrupciones internas o excepciones: Las genera la propia CPU cuando se produce una situación anormal o cuando llega el caso. Hay tres tipos básicos de interrupciones: . sobre el segmento 0. Ha sido incorrectamente empleada por IBM para volcar la pantalla por impresora. con dicho desplazamiento. Se produce al ejecutar una instrucción indefinida. A partir de dicho número se calcula la dirección de la rutina que lo atiende y cuando se retorna se continúa con la instrucción siguiente a la que se estaba ejecutando cuando se produjo la interrupción. AT Y PS/2 BAJO DOS 103 Capítulo VII: ARQUITECTURA DEL PC. . INT 2: interrupción no enmascarable.1. INT 0: error de división. INT 1: paso a paso. continuando más tarde con lo que estaba haciendo. el 286 y superiores almacenan la dirección del DIV o IDIV que causa la excepción. INT 3: utilizada para poner puntos de ruptura en la depuración de programas. La forma de calcular la dirección de la rutina es multiplicar por cuatro el valor de la interrupción para obtener un desplazamiento y. se produce tras cada instrucción cuando el procesador está en modo traza (utilizada en depuración de programas).

que es lo más normal. como se verá más adelante. Para cambiar un vector de interrupción existen cuatro métodos: 1) «El elegante»: es además el más cómodo y compatible. podría quedar residente en memoria. Como desde esta interrupción se invoca a su vez a INT 1Ch -porque así lo dispuso IBM-. INT 0Ah.vector*4 BH. como enviar una señal fin de interrupción hardware al chip controlador de interrupciones. es posible ligar un proceso a INT 1Ch para que se ejecute periódicamente. . . en resumen: conviene documentarse debidamente antes de intentar hacer nada.2 veces por segundo). pero muy usado por programadores despistados: MOV MOV MOV PUSH MOV LEA CLI MOV MOV STI POP BL. algunos programas de DOS funcionan también bajo OS/2 si han sido diseñados con esta técnica. hay que enviar una señal de reconocimiento al mismo . ahora en BX .rutina [BX].104 EL UNIVERSO DIGITAL DEL IBM PC.AX DX. . unas 18.0 AX. . La BIOS y el DOS utilizan algunas interrupciones a las que se puede llamar con determinados valores en los registros para que realicen ciertos servicios. servicio para cambiar vector entre 0 y 255 DS:DX nueva rutina de gestión llamar al DOS 2) El «psé»: es menos seguro y compatible (ningún programa que emplea esta técnica corre en OS/2) y consiste en hacer casi lo que hace el DOS pero sin llamarle. De hecho.Interrupciones hardware: Son las generadas por la circuitería del ordenador en respuesta a algún evento. 71h. También existe alguna que otra interrupción que se limita simplemente a apuntar a modo de puntero a una tabla de datos.vector DX.25h AL. 75h. Todos estos problemas se evitan si la nueva rutina que controla la interrupción llama al principio (o al final) al anterior gestor de la misma. 74h. hay que realizar una serie de tareas adicionales. consiste en cambiar el vector «de un tirón» (cambiar a la vez segmento y offset con un REP MOVS) con objeto de evitar una posible interrupción no enmascarable que se pueda producir en ese momento crítico en que ya se ha cambiado el offset pero todavía no el segmento (CLI no inhibe la interrupción no enmascarable). . Si se reprograma por completo una interrupción y ésta es de tipo hardware. Es además mucho más incómodo y largo. 73h. . preservar DS apuntar al segmento 0000 CS:DX nueva rutina de gestión evitar posible interrupción cambiar vector (offset) cambiar vector (segmento) permitir interrupciones restaurar DS 3) El «método correcto» es similar al «psé». . . Los vectores de interrupción pueden ser desviados hacia un programa propio que. . 72h. .. 0Dh.0 DS DS. Si se trata además de la interrupción del teclado del PC o XT. INT 9: generada al pulsar o soltar una tecla.CS DS .. . 0Ch. Las más importantes son: INT 8: Se produce con una frecuencia periódica determinada por el canal 0 del chip temporizador 8253/8254 (en la práctica.DX [BX+2]. 0Fh: Puertos serie. 0Eh. además. vector a cambiar en BL .rutina 21h . 77h: Generadas en los AT y máquinas superiores por el segundo chip controlador de interrupciones. Basta con llamar al servicio 25h del DOS (INT 21h) y decirle qué interrupción hay que desviar y a dónde: MOV MOV LEA INT AH. AT Y PS/2 . pero es el mejor y es el que utiliza el DOS en el método (1). 76h. .Interrupciones software: Producidas por el propio programa (instrucción INT) para invocar ciertas subrutinas. Este sistema es todavía algo más engorroso. impresora y controladores de disquete. 0Bh. INT 70h.

En . son 16 Kb a partir del segmento 0B800h.2 veces por segundo. ocupa 4 Kb a partir del segmento 0B000h. si está activo un adaptador de vídeo monocromo. con el riesgo de que se produzca una interrupción cuando se ha cambiado sólo medio vector. el color 0 es el negro. etc. Un método para averiguar el tipo de adaptador de vídeo es consultar a la BIOS el modo de vídeo 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. Con un adaptador de color. 7. Los modos 0 y 1 son de 40 columnas y el 2 y 3 de 80. el de la tinta (0-15) con los bits 0-3 y el parpadeo con el bit 7. Los peores programadores lo emplean sobre todo para cambiar INT 8 ó INT 1Ch. el 1 es «subrayado normal». En los adaptadores de color. los primeros 1024 bytes están ocupados por la tabla de vectores de interrupción. El bit 7 siempre provoca parpadeo en este adaptador. En el adaptador monocromo. los periféricos deberían interceptar el acceso a la memoria y estar colocados en algún área de la misma). dejando más memoria libre al usuario dentro de los primeros 640 Kb. . Los modos 0 y 2 son de «color suprimido». La función de este último bit puede ser redefinida para indicar el brillo de los caracteres de fondo (existiendo entonces también 16 colores de fondo). De C0000h a EFFFFh aparecen las extensiones de la ROM (añadidas por las tarjetas gráficas. en un modo de 80x25 se utilizan 4000 bytes (los 96 restantes hasta los 4096 de los 4 Kb se desprecian). Es similar al «psé» sólo que sin inhibir las interrupciones mientras se cambia el vector. En el 97. con objeto de conocer el segmento real donde empieza la pantalla (B800 más un cierto offset).LA PANTALLA EN MODO TEXTO. aunque en muchos monitores salen también en color (y no en tonos de gris). el propio sistema operativo se sitúa (en 286 y superiores) en los primeros 64 Kb de la memoria extendida (HMA) que pueden ser direccionados desde el DOS. Cuando la pantalla está en modo de texto. Los 80x86 utilizan los buses de direcciones y datos ordinarios para acceder a los periféricos. En A0000h comienza el área de expansión de memoria de pantalla (EGA y VGA). 7. pero habilitando una línea que distinga el acceso a los mismos de un acceso convencional a la memoria (si no existieran los puertos de entrada y salida. el 9 es «subrayado brillante» y del 10 al 15 son «brillantes». La BIOS utiliza la interrupción 10h para comunicarse con el sistema operativo y los programas de usuario. AT Y PS/2 BAJO DOS 105 4) El «método incorrecto» es muy usado por los malos programadores. Los modernos sistemas operativos (DR-DOS y MS-DOS 5.) y en F0000h suele estar colocada la BIOS del sistema (a veces tan sólo 8 Kb a partir de FE000h). Dentro del megabyte que puede direccionar un 8086. se pueden definir entre 4 páginas de texto (80 columnas) y 8 (40 columnas). Para el papel todos los colores son negros menos el 7 (blanco). De 600h a 9FFFFh está la memoria del usuario (casi 640 Kb). Cada carácter en la pantalla (empezando por arriba a la izquierda) ocupa dos bytes consecutivos: en el primero se almacena el código ASCII del carácter a visualizar y en el segundo los atributos de color.. como hay 16 Kb de memoria para texto. puede consultarse el apéndice I y el capítulo 8. En B0000h comienzan otros 64 Kb de los adaptadores de texto MDA y gráficos (CGA). Los puertos de entrada y salida (E/S) permiten a la CPU comunicarse con los periféricos.ARQUITECTURA DEL PC. el 8 es negro. Véase el apéndice IV. discos duros. Para acceder a los puertos E/S se emplean las instrucciones IN y OUT. en los auténticos adaptadores monocromos). no obstante para escribir en vídeo inverso es necesario no sólo papel 7 sino además tinta 0 (al menos. y para la tinta. De hecho. Obviamente. aunque en CGA es preciso para ello un acceso directo al hardware. lo que no quiere decir que los buenos programas deban asumirla como la única posible. Para más información. Esta zona de memoria sirve para cargar programas residentes.3. El byte de atributos permite definir el color de fondo de los caracteres (0-7) con los bits 4-6.5% de los casos sólo se emplea la página 0. LOS PUERTOS DE ENTRADA Y SALIDA. del 1 al 7 son colores «normales». La página activa puede consultarse también llamando a la BIOS. A continuación existen 256 bytes de datos de la BIOS y otros tantos para el BASIC y el DOS. que se producen con una cadencia de 18.2.LA MEMORIA.0 y posteriores) permiten colocar RAM en huecos «vacíos» por encima de los 640 Kb en las máquinas 386 (y algún 286 con cierto juego especial de chips).

Tabla de colores: 0 1 2 3 Negro Azul Verde Cian 4 5 6 7 Rojo Magenta Marrón Blanco 8 9 10 11 Gris Azul claro Verde claro Cian claro 12 13 14 15 Rojo claro Magenta claro Amarillo Blanco brillante Conviene tener cuidado con la tinta azul (1 y 9) ya que.2 . en caso contrario la recorre y convierte todos sus caracteres a mayúsculas. poner en mayúsculas apuntar siguiente carácter repetir con los CX caracteres no_minuscula: DS. . como demostrará este ejemplo: el siguiente programa comprueba el tipo de pantalla. . 7.4 .BX . .2 pant_color CX.2000 AL. sin alterar el color: mays inicio: MOV INT MOV MOV CMP JE MOV CMP JE CMP JE MOV CMP JBE MOV JMP pant_color: MOV MOV MOV AH. Las pantallas de 132 columnas no son estándar y varían de unas tarjetas gráficas a otras.COM ordinario SHR SHR SHR SHR ADD MOV XOR CMP JB CMP JA AND ADD LOOP MOV MOV INT ENDS END AX. AL. . .3 pant_color AL.1 AX. considerar página activa<>0 DS.106 EL UNIVERSO DIGITAL DEL IBM PC.4Ch 21h inicio . Las tarjetas EGA y posteriores vienen acompañadas de una extensión ROM que parchea la BIOS normal del sistema para añadir soporte al nuevo sistema de vídeo. . BYTE PTR [BX]. De todas maneras. sólo se tratará de una manera general el tema.1000 AL. y que de hecho llenan varias estanterías en las librerías. Trident y Genoa. EGA y VGA. . su consulta puede ser interesante. lo mejor en programas de calidad es escribir directamente sobre la memoria de pantalla para obtener una velocidad máxima. Se considerarán los estándares más comunes. que además permite escribir en color. .DS:[4Eh] . .AX .40h . DS:mays ORG 100h . . éste invoca a su vez a la BIOS. . Dada la inmensidad de estándares gráficos existentes para los ordenadores compatibles. aunque con ciertas precauciones -para convivir mejor con entornos pseudo-multitarea y CGA’s con nieve-.1 BX. BYTE PTR [BX]. . .’a’.’z’. función para obtener modo de vídeo llamar a la BIOS segmento de pantalla monocroma tamaño (caracteres) de la pantalla ¿es realmente modo monocromo? en efecto segmento de pantalla de color ¿es modo de texto de 80 columnas? en efecto ¿es modo de texto de 80 columnas? en efecto tamaño (caract. Cuando se llama al DOS para imprimir. Lo que sí se puede hacer -con cualquier EGA y VGA. llamando a la BIOS (véase el apéndice de las funciones del DOS y de la BIOS). . empieza en 0B800h). En los modos de vídeo que precisan más de 64 Kb se recurre a técnicas especiales.1. así como un lógico aumento de la memoria de vídeo requerida (que como siempre.) pantalla 40 col. . BX. No se consideran las peculiaridades del PCJr.0B800h AL. así como en las SuperVGA Paradise.15 10h BX. no_minuscula .1 pant_color AL. lo que provoca un aumento del número de líneas a 43 (EGA) o 50 (VGA).1 final .es llamar a la BIOS para que cargue el juego de caracteres 8x8. AT Y PS/2 el adaptador de color no se pueden subrayar caracteres con los códigos de color (aunque sí en la EGA y VGA empleando otros métodos).MODOS GRÁFICOS. desplazamiento de la página activa 7. . tales como planos de bits para los diferentes colores. por lo que la escritura puede ser acelerada llamando directamente a este último. Las tarjetas gráficas tradicionales administran normalmente entre 16 Kb y 1 Mb de memoria de vídeo. Si no es una pantalla de texto estándar no realiza nada. los adaptadores monocromos subrayan -lo que puede ser un efecto indeseable-. con algunos ejemplos de programación de la pantalla gráfica CGA con la BIOS y programando la VGA directamente para obtener la velocidad y potencia del ensamblador. otra_letra . CGA. por lo que no las trataremos. programa . En las variables de la BIOS (apéndice III) los bytes 49h-66h están destinados a controlar la pantalla. no_minuscula . para determinar su segmento. . DS = 40h (variables de la BIOS) AX.0 AH.7 datos_ok BX. que sucedieron al primer adaptador que sólo soportaba texto (MDA). A continuación se listan los principales modos gráficos disponibles en MDA. o bien dividir la pantalla en pequeños fragmentos que se seleccionan en un puerto E/S.1 AX. desplazamiento / 2 desplazamiento / 4 desplazamiento / 8 desplazamiento / 16 (párrafos) segmento de vídeo efectivo DS = segmento de pantalla BX = 0 (primer carácter) ¿código ASCII menor que ’a’? luego no puede ser minúscula ¿código ASCII mayor de ’z’? luego no puede ser minúscula . .0B000h CX. .0DFh BX.4. ¿es modo texto de 40 columnas? así es pantalla gráfica o desconocida: fin de programa (errorlevel=1) datos_ok: otra_letra: SEGMENT ASSUME CS:mays.1 AX. .BX . en el segmento 0B800h las CGA/Hércules y en 0A000h las VGA.LA PANTALLA EN MODO GRÁFICO. fin programa (errorlevel=0) final: mays AX. en estos colores. BYTE PTR [BX]. .AX .

. Esto es así porque aunque la BIOS del sistema (o el de la tarjeta) soporta una serie de funciones estándar para trabajar con gráficos. esto no sólo es problemático en las tarjetas gráficas: la anarquía y ausencia de funciones de información también se repite con los discos. VGA EGA. Bastaría con que las funciones que implementa la BIOS (pintar y leer puntos de la pantalla) fueran rápidas. VGA EGA. en ese mismo modo. por la manera en que gestionan la memoria de vídeo. por ejemplo.. una tarjeta Paradise en el modo 5Fh tiene de 640x400 puntos con 256 colores. los programas comerciales no tienen más remedio que incluir sus propias rutinas rápidas para trazar puntos y líneas en drivers apropiados (y de paso añaden alguna función más compleja). VGA EGA.ARQUITECTURA DEL PC. MCGA. Sin embargo. casi todas las demás operaciones realizadas sobre la pantalla se apoyan en esas dos y ello no requeriría software adicional para mantener la compatibilidad entre tarjetas. En general. el teclado. su ineficiente diseño lo hace extremadamente lento para casi cualquier aplicación seria. no existe NI UNA SOLA función oficial en la BIOS que informe a los programas que se ejecutan de cosas tan elementales como los modos gráficos disponibles (con sus colores. para lo que tan sólo hace falta una rutina específica para cada modo de pantalla. ¡sólo eso!. EGA. VGA CGA. Además. MCGA VGA VGA. MCGA. mientras que una Trident tiene. MCGA Genoa Genoa Genoa Genoa Genoa Genoa Genoa Paradise VGA Paradise VGA Trident TVGA Genoa 6400 Trident TVGA Genoa 6400 Trident TVGA Paradise VGA Trident 8900 Genoa 6400 Paradise VGA Trident TVGA Genoa 6400 Trident TVGA Trident TVGA Genoa 6400 Genoa Genoa 100x75 100x75 100x75 80x25 80x30 80x25 80x30 96x64 8800. etc. existen bastantes problemas. AT Y PS/2 BAJO DOS 107 Modo 04h 05h 05h 06h 0Dh 0Eh 0Fh 10h 10h 11h 12h 13h 27h 29h 2Dh 2Eh 2Fh 30h 37h 58h 59h 5Bh 5Bh 5Ch 5Ch 5Dh 5Eh 5Eh 5Eh 5Fh 5Fh 5Fh 61h 62h 6Ah 7Ch 7Dh Texto 40x25 40x25 40x25 80x25 40x25 80x25 80x25 80x25 80x25 80x30 80x30 40x25 Resolución 320x200 320x200 320x200 640x200 320x200 640x200 640x350 640x350 640x350 640x480 640x480 320x200 720x512 800x600 640x350 640x480 720x512 800x600 1024x768 800x600 800x600 800x600 640x350 640x400 640x480 640x480 640x400 800x600 800x600 640x480 1024x768 1024x768 768x1024 1024x768 800x600 512x512 512x512 Colores 4 4 grises 4 2 16 16 2 4 16 2 16/256k 256/256k 16 16 256/256k 256/256k 256 256/256k 16 16/256k 2 16/256k 256 256 256 256 256 256 256 256 16/256k 16 16/256k 256 16 16 256 Segmento B800 B800 B800 B800 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 A000 Tarjeta CGA. VGA EGA con 64K EGA con 256K. EGA CGA. uno para cada tarjeta de vídeo del mercado.). Las tarjetas SuperVGA complican aún más el panorama. VGA CGA. resolución. En primer lugar.. de cuántos colores y resolución. VGA VGA. en qué modo. EGA. y por desgracia. Sin embargo.. un programa que desee aprovechar al máximo el ordenador deberá apoyarse en drivers o subprogramas específicos. . con los gráficos no podemos y nos vemos obligados a preguntar al usuario qué tarjeta tiene. que la BIOS debería habilitar nada más cambiar de modo. y lo que es peor: la inexistencia de funciones de información se agrava con el hecho de que las VGA de los demás fabricantes hayan asignado de cualquier manera los números de modo. 8900 8800 8800 (512K) A000 A000 (512K) 8800 (512K) 8800 (512K) 8900 Las tarjetas gráficas son muy distintas entre sí a nivel de hardware. En lo único que coinciden todas las tarjetas es en los primeros . De esta manera. 1024x768 con 16 colores. aunque los programadores ya estamos acostumbrados a realizar la labor del detective para averiguar la información que los programas necesitan.

Hablaremos de él más tarde.12h 10h . Tampoco se considerará la MCGA. es EGA mono tarjeta_ok BL. Se trata de una tarjeta que apareció en el mercado muy poco después que la CGA de IBM.INTRODUCCIÓN AL ESTÁNDAR GRÁFICO VGA. un híbrido entre EGA y VGA que solo equipa a los PS/2-30 de IBM. aunque sí se indicará qué parte de lo expuesto se puede aplicar también a la EGA.8 . 2 si hay CGA.. 7. tipo_tarjeta PROC PUSH MOV INT CMP JE MOV MOV MOV MOV INT CMP JE MOV TEST JNZ MOV OR JZ INC JMP MOV CMP JE DEC POP RET ENDP DS AX. 4 ó 5 si existe una EGA. haciendo un especial hincapié en el tema menos claramente explicado por lo general: el color.4. En este apartado estudiaremos la forma básica de programar sus modos gráficos. es MDA BL. con el doble de resolución y manteniendo la calidad MDA en modo texto. supuesto MDA BYTE PTR DS:[87h]. función soportada (hay VGA) AX. estado del control de vídeo tarjeta_ok . lo que sucede es que en esto no se han podido poner de acuerdo los fabricantes y la función de la BIOS de la VGA a la que hay que invocar para obtener información. ¡difiere de unas tarjetas a otras!. Devuelve un valor en BL que es el mismo que retorna la INT 10h al llamarla con AX=1A00h (ver funciones de la BIOS en los apéndices): 0 ó 1 para indicar que no hay gráficos. colores y resoluciones. Retorna 255 si la tarjeta es desconocida (muy raro).2 . Afortunadamente. bastante incompatible además con la EGA y la VGA.1 . Está muy extendida en las máquinas antiguas. solicitar información EGA a la BIOS BL. así es BL . supuesto EGA color BH.AX BL. BL = tipo de tarjeta tarjeta_ok . No conviene seguir adelante sin mencionar antes la tarjeta gráfica Hércules. no es EGA BL.4. pero hoy en día no se utiliza y su programación obliga a acceder a los puertos de entrada y salida de manera directa al más bajo nivel. El siguiente procedimiento es uno de tantos para evaluar la tarjeta gráfica instalada en el ordenador. 6 si detecta una PGA. Muchas SuperVGA tienen funciones que informan de sus modos.108 EL UNIVERSO DIGITAL DEL IBM PC.BH tarjeta_ok . La tarjeta VGA es el estándar actual en ordenadores personales. solicitar información VGA a la BIOS AL. supuesto CGA WORD PTR DS:[63h]. con o sin tarjetas gráficas instaladas y del tipo que sean. se les puede añadir soporte con un pequeño driver residente. existe un estándar industrial en tarjetas SuperVGA. es MDA DS no_ega: tarjeta_ok: tipo_tarjeta 7. definidos inicialmente por IBM. así es BL . . 11 ó 12 si existe MCGA. 7 u 8 si hay VGA o superior y 10. Esta tarjeta no está soportada por la BIOS (manufacturada por IBM) y los fabricantes de SuperVGA tampoco se han molestado en soportarla por software. múltiples VGA lo soportan y a las que no.40h DS. siendo el sistema de vídeo mínimo que incluye la máquina más asequible. La rutina funciona en todos los ordenadores.10h no_ega . aunque sí por hardware. que aunque ha llegado demasiado tarde.2.3D4h . 3. Se ignorarán por completo las tarjetas CGA y Hércules.1Ah .DETECCIÓN DE LA TARJETA GRÁFICA INSTALADA. AT Y PS/2 modos de pantalla. de momento.4 .3. .1A00h 10h .10h AH. base del CRT tarjeta_ok . el estándar VESA.

El controlador de atributos gestiona la paleta de 16 colores y el color del borde.3. La parte del león son los ¡768 registros! de 6 bits que almacenan la intensidad en las componentes roja. se definen los 16 colores posibles.1 . Esto ha sido superado en las siguientes tarjetas. El chip VGA consta de varios módulos internos. con lo que obtiene 26=64 colores diferentes.EL COLOR. Para establecer el modo de vídeo se puede emplear una función del lenguaje de programación que se trate o bien llamar directamente a la BIOS. en las que las líneas están consecutivas de manera lógica en una organización lineal. Por un lado está el secuenciador. aunque en la VGA pueden ser tanto escritos como leídos. AL=12h */ /* ejecutar INT 10h */ 7.4.EL HARDWARE DE LA VGA. Sin embargo.3.1. si bien el límite de 64 Kb de memoria que puede direccionar en un segmento el 8086 ha obligado al truco de los planos de bit. cuando se recorren FIGURA 7. se emplean los 16 registros de paleta del controlador de atributos: En cada uno de estos registros. intr (0x10. en el modo 6.1: MODOS GRÁFICOS DE VIDEO 80 bytes en la memoria (640 bits o pixels.2 .4. encargado del tráfico de información entre la CPU. que definen conjuntos de registros direccionables en el espacio E/S del 80x86. Por otro lado tenemos el controlador de gráficos. la memoria de vídeo y el controlador de atributos. encargado de la temporización necesaria para el acceso a la memoria de vídeo. &r). La . en una arquitectura relativamente compleja debida a las limitaciones del hardware de la CGA. verde y azul de cada color. Para asociar estos 64 colores a los no más de 16 que puede haber en un momento determinado en la pantalla. si bien los correspondientes a la CGA (320x200 en 4 colores y 640x200 monocromo) son inservibles para prácticamente cualquier aplicación gráfica actual. por ejemplo: Modo (hex) Resolución Colores Segmento Organización Adaptador #include <dos. sin 13h 320 x 200 256 A000 lineal VGA/MCGA embargo.144 colores que se pueden visualizar en pantalla.3. resumidos en la figura 7.4. primera línea completa) no se pasa a la segunda línea de la pantalla sino unas cuantas más abajo. de los 256 que como mucho puede haber simultáneamente en la pantalla (256*3=768). si no se desea emplear la librería gráfica del compilador: la función 0 (AH=0) de servicios de vídeo de la BIOS (INT 10h) establece el modo de vídeo solicitado en AL. el DAC o Digital to Analog Converter se encarga en la VGA (no dispone de él la EGA) de gestionar los 262. 4 y 5 320 x 200 4 B800 entrelazado CGA planos de bit o lineal) es la 6 640 x 200 2 B800 entrelazado CGA manera en que se direcciona 0Dh 320 x 200 16 A000 planos de bit EGA la memoria de vídeo por 0Eh 640 x 200 16 A000 planos de bit EGA parte de la CPU. La organización de la memoria (entrelazado. La CGA puede generar 16 colores diferentes.ARQUITECTURA DEL PC. En la EGA eran de sólo escritura. de 6 bits significativos. En Turbo C sería. Por 0Fh 640 x 350 2 A000 planos de bit EGA ejemplo. AT Y PS/2 BAJO DOS 109 La VGA soporta todos los modos gráficos estándar de las tarjetas anteriores. Por último. cada 10h 640 x 350 4 A000 planos de bit EGA pixel de la pantalla está 10h 640 x 350 16 A000 planos de bit EGA (128K) asociado a un bit (8 pixels 11h 640 x 480 2 A000 lineal VGA/MCGA por byte) a partir de la 12h 640 x 480 16 A000 planos de bit VGA dirección B800:0000.3. utilizando un solo bit por componente de color más un cuarto que indica la intensidad.4. la EGA emplea dos bits por cada una de las tres componentes de color. 7. consta de 9 registros cuya programación es necesaria para trazar puntos a gran velocidad en los modos de 16 colores.h> main() { struct REGPACK r. } /* AH = 00. r.r_ax=0x0012.

. basta con cambiar un bit de un registro del controlador de atributos. Conviene resaltar que el color del borde de la pantalla se define en la EGA y en la VGA en una especie de registro que sigue a los 16 registros de paleta: en la VGA no interviene el DAC en la generación del color del borde.. que es quien realmente contiene el color. El valor binario almacenado en los registros de paleta tiene el formato xxrgbRGB.47 página (0. por ejemplo.. en los modos de texto el color 0 es el negro y el 15 el blanco brillante. el valor 010010b se corresponde con el verde más brillante. del que solo existen por consiguiente 64 tonos (si bien el borde suele estar en color negro y su tamaño reducido y variable lo hace inservible para nada).15 16. debido a la presencia del DAC: una matriz de 256 elementos que constan cada uno de 3 registros de 6 bits. si bien es una técnica poco empleada: para ello. la BIOS emplea los elementos 0.15 CASO 16 x 16 elemento del DAC : : 224..255 Elementos del DAC 0.. solo se consideran los 4 bits menos significativos de los mismos). verde y azul de baja intensidad.. cambiando la página o subpaleta activa a cierta velocidad se puede hacer que la imagen se encienda y apague rítmica y suavemente..31 32. por ejemplo. y RGB sus homólogos en alta intensidad. Por supuesto. AT Y PS/2 BIOS de la EGA y la VGA carga los registros de paleta adecuadamente para emular los mismos colores de la CGA. la BIOS establece 4 páginas de 64 elementos en el DAC.. Los pixels en los modos gráficos de 16 colores pueden parpadear..191 192. si bien se puede alterar esta asignación. suma el 30% del valor rojo.3. Los monitores monocromos VGA solo admiten 64 tonos y se limitan siempre a presentar la componente verde del DAC. al 128-191 ó al 192-255): por defecto. también se pueden obtener efectos similares alterando directamente los registros del DAC. Cada uno de los registros de paleta apunta a un elemento del DAC. siendo rgb los bits asociados a las componentes roja. La figura 7.3. Se pueden definir.. de manera que se puede dividir el DAC en 16 bloques de 16 elementos o bien en 4 bloques de 64 elementos: en un momento dado.63 del DAC que programa para emular los 64 colores de la EGA.ó BX=0 para desactivarlo). puede resultar más interesante disponer de 16 subpaletas de 16 elementos para conseguir determinados efectos gráficos: en este caso no tiene sentido que los registros de paleta almacenen valores fuera del rango 0-15 (de hecho. las 16 subpaletas en tonos ascendentes de azul y.110 EL UNIVERSO DIGITAL DEL IBM PC. Sin embargo. Por defecto.. Modos de 16 colores en VGA. sólo uno de los bloques (denominado página de color del DAC) está activo.2 expresa gráficamente la manera en que se genera el color..127 valor 0. Un cambio en un registro de paleta afecta instantáneamente a todo el área de pantalla pintado de ese color. Existen dos maneras diferentes de indexar en el DAC los registros de paleta. lo que sucede es que los registros del DAC son programados por la BIOS para emular los 64 colores de la EGA. al 64-127. de manera que valores en el rango 0-63 en los 16 registros de paleta referencien a posiciones distintas en el DAC (al área 0-63.15) valor 0..3) seleccionable (0 por defecto) FIGURA 7.63 CASO 4 X 64 64. aunque es mucho más lento que conmutar entre varias paletas ya definidas. En la VGA el tema del color en los modos de pantalla de 16 colores (tanto gráficos como de texto) se complica algo más. Así.4.15) seleccionable página (0.255 color en pantalla (0. En concreto. Lo que sucede es que la BIOS ajusta la intensidad de la señal verde para emular la presencia de las otras dos. pudiéndose activar una u otra libremente con una función de la BIOS de la VGA. aunque existe una función de la BIOS que realiza dicha tarea (llamar a la INT 10h con AX=1003h y BX=1 para activar el parpadeo -situación por defecto en los modos de texto. el 59% del verde y el 11% del azul .63 elemento del DAC 128..239 240..2: OBTENCIÓN DEL COLOR EN LOS MODOS DE 16 COLORES (VGA) 16 Registros de paleta El truco del mono. Así. Esto significa que se pueden crear 16 ó 4 subpaletas.4. 0.

/* establecer paleta y borde */ asignados con los 4 posibles tonos de rojo. El cambio de la paleta es instantáneo. if (((gdrv!=EGA) && (gdrv!=VGA)) || (coderr!=grOk)) pueden recurrir a la BIOS. dada la absurda paleta propuesta por la BIOS. gmodo. Sin embargo. si se ponen la roja y la azul en 32 y la verde en 0. x++) { EGAVGA. Para definir un color en el DAC. color++) for (pixel=0. intr (0x10. si están a 63 (valor máximo) saldrá un blanco brillante. /* __100100 = 36 --> ambas: rojo brillante */ for (i=4. si bien lo normal aquí es esperar que existan 16 colores (caso de la VGA. char paleta[17].. En algunos casos. i. coderr=graphresult().4. color. exit(1). Otra ventaja de emplear la BIOS es que ésta hace automáticamente las conversiones necesarias para lograr la mejor visualización posible en pantallas monocromas. ya que un acceso directo al hardware sin más precauciones puede provocar interferencias con algunas tarjetas VGA. Modo de 256 colores. los 4 primeros registros son r. Al principio se trazan unas /* DEFINIR NUEVA PALETA */ bandas verticales con la función line() que serán paleta[0]=0.16 COLORES */ particularidades de cada compilador. AT Y PS/2 BAJO DOS 111 y el resultado lo fuerza al rango 0-63. Se puede realizar un bucle y llenar los primeros 64 elementos del DAC con valores crecientes en una componente de color. i<17. exit(1). ENTRE LOS 64 POSIBLES DE LA * * EGA (POR DEFECTO EMULADOS POR EL DAC DE LA VGA). color<16. r.\n".3. basta con un poco de imaginación: si las tres componentes están a cero.} ejemplo de la figura 7. saldrá un morado de oscuridad mediana.BGI del compilador ha de estar en el setcolor (color). En el modo 13h de 320x200 con 256 colores. 1. sin int gdrv. Cómo definir la paleta y los registros del DAC. } directorio de trabajo). 0. line (x. saldrá el negro. coderr. siendo más sencillo el proceso de generación del color. embargo. los registros de paleta del controlador de atributos no se emplean en este modo. que cambiando la paleta { printf("\nNecesaria tarjeta EGA o VGA. este sería un aspecto a considerar. &gmodo. o incluso de la EGA con 128K). initgraph(&gdrv. más getch(). recuérdese que los valores de la paleta son simples punteros al DAC y no los colores reales. lo cual no es nuestro caso. ""). getmaxy()). decir que en el modo de 4 colores y 350 líneas. solo se emplean los registros de paleta 0. poniendo a 0 las demás: de esa manera. las paletas que define por defecto la BIOS al establecer el modo de pantalla son apropiadas.r_es=FP_SEG(paleta). grapherrormsg(coderr)). closegraph(). FIGURA 7. Al definir la paleta. se genera una paleta óptima para hacer degradados (escalas de intensidad) de un color puro. Por tanto. puede ser útil cambiarlas para lograr un degradado atractivo en los modos de 16 colores y casi obligatorio en el modo de 256 colores. la generación del color se aparta de lo estudiado hasta ahora para los demás modos gráficos y los de texto. El Turbo C permite cambiar void main() { struct REGPACK r. x.3 (para ejecutar este /* DIBUJAR BANDAS VERTICALES DE EJEMPLO */ programa hay que tener en cuenta que el fichero for (x=color=0. Si se accediera directamente al hardware sin ayuda de la BIOS. * *********************************************************************/ . pixel++. /********************************************************************* * EJEMPLO DE CAMBIO DE LA PALETA DE 16 COLORES (EGA/VGA) LLAMANDO AL * * BIOS PARA ELEGIR LOS COLORES DESEADOS. Echemos un vistazo al if (coderr!=grOk) { printf("Error gráfico: %s.ARQUITECTURA DEL PC.r_ax=0x1002. &r). /* resto colores y borde negros */ cambiarán instantáneamente al modificar la paleta. rojo oscuro y rojo brillante.. Conviene también emplear las funciones que cambian de una sola vez un conjunto de registros del DAC. coderr=graphresult(). } gmodo=EGAHI. r. pixel.3: 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 #include <dos. x. i++) paleta[i]=0. Por último. lo cual simula aproximadamente la intensidad que percibiría el ojo humano con los colores reales. pixel<getmaxx()/16. A la hora de cambiar la paleta es conveniente emplear funciones de la BIOS o del lenguaje de programación. siempre detectgraph (&gdrv. a menos que se cambien los valores de dichos registros. lo que permite hacer efectos especiales. &gmodo).4. 4 y 5. Todos los demás registros y el borde de la pantalla son puestos a 0 (negro) por lo que en la pantalla quedan visibles sólo las tres bandas verticales citadas. En la VGA. es bastante solvente. aunque paleta[2]=4*8.3.h> borde de la pantalla. ya que hacerlo uno por uno es demasiado lento.h> registro de paleta más otro final para el color del #include <graphics. ya que solo interviene el DAC: el byte de memoria de vídeo asociado a cada punto de la pantalla apunta directamente a un elemento del DAC. /* __000100 = 4 --> componente roja normal */ coloreadas con los 16 colores por defecto. /* __rgbRGB = 0 --> negro */ paleta[1]=4. la paleta con instrucciones de alto nivel. } bien 3 (el primero es el negro absoluto): rojo. quienes no deseen aprender las /* ESTABLECER MODO EGA/VGA 640x350 .r_dx=FP_OFF(paleta).\n"). 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 obtendría en una EGA. /* __100000 = 32 --> componente roja oscura */ paleta[3]=4*8+4.

(0.. initgraph(&gdrv.47 del DAC con los } colores deseados. con precauciones (en este caso. i. /* color del borde */ la pantalla una escala de 16 amarillos (el primero. /* valores crecientes 0. que requiere ya un auténtico /********************************************************************* * 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. También existen en la VGA las funciones inversas (obtener paletas y registros del DAC).h> #include <graphics. Lo primero que se hace es *********************************************************************/ seleccionar el modo de 16 páginas en el DAC.60 de verde */ especialmente en las que predomina un color dac[i][2]=0. /* valores crecientes 0. i<16. es mejor no usar la BIOS. x. simples gmodo=VGAHI. esperar el retrazado vertical para evitar interferencias) es a veces inevitable. /* número de elementos a definir */ normalmente tan mal en los modos de 16 colores r.. sin embargo.3. closegraph(). &gmodo.4: a partir del anterior. Como /* SELECCIONAR 16 BLOQUES DE 16 ELEMENTOS EN EL DAC */ cada componente puede variar entre 0 y 63. La figura 7. verde y azul) aunque solo son significativos los 6 bits de menor orden de cada byte. exit(1). #include <dos. r. son suficientes para representar /* LLENAR ELEMENTOS 32.r_ax=0x1013.. programar los registros 32. gmodo. existen más funciones que éstas. &r). getmaxy()).r_ax=0x1012. apareciendo en for (i=0. el programa de la figura 7. /* establecer paleta y borde */ colores son pocos. intr (0x10..144 * adaptador VGA. intr (0x10. en el primero disminuyendo la luminosidad de la pantalla (hasta dejarla negra) y en el segundo restaurándola de nuevo. El programa de ejemplo funciona también en monitores monocromos. dac[256][3].3. Este efecto cinematográfico hubiera sido imposible a través de la BIOS por razones de velocidad: el acceso directo al hardware. 8.. x. según cómo esté estructurado). El acceso por medio de la BIOS para cambiar la paleta es a menudo más cómodo que emplear funciones del lenguaje de programación y garantiza en ocasiones un mayor nivel de independencia respecto a la evolución futura del hardware (aunque si la librería gráfica llama a la BIOS..LST si lo desea. 0. puesto a 0 /* DIBUJAR BANDAS VERTICALES DE EJEMPLO */ para seleccionar el negro).r_ax=0x1013. .4. r. } Por supuesto.60 de rojo */ dac[i][1]=i*4.5 accede directamente a los registros de la VGA para modificar la paleta en dos bucles. &gmodo). /* primer elemento del DAC */ r. ""). intr (0x10. esta función no está disponible en el modo 13h de 256 colores. /* PAGINA 2: LA PALETA SE APOYARA EN ELEMENTOS 32... pixel++.} índices en el DAC. El segundo servicio se indica llamando con BL=1.. int gdrv. Si bien 16 r. basta for (x=color=0. pixel. r. /* programar elementos del DAC */ debido a que respetan la paleta de la EGA. r. Existe también otra función bastante interesante.4. AT Y PS/2 Para ello. i++) { con relativa precisión algunas imágenes.. en el que no interviene la paleta (sólo el DAC y entero.47 DEL DAC DE AMARILLOS CRECIENTES */ for (i=32.47 del DAC (la char paleta[17].4.15 (excepto el 17º byte. grapherrormsg(coderr)). r. coderr=graphresult(). También se pasa en ES:DX la dirección de la tabla de 768 bytes que contiene la información: 3 bytes consecutivos para cada elemento del DAC (rojo. VGA sería otra historia). exit(1). negro absoluto) de intensidad creciente.). paleta[16]=0. line (x. r. r.r_dx=FP_OFF(paleta). En cualquier caso. /* ESTABLECER MODO VGA 640x480 . aunque en la práctica sólo actúe en ellos sobre la componente verde. no a trocitos). coderr=graphresult()..1. el lector puede consultarlas en el fichero INTERRUP. /* sin componente azul */ } determinado (los ficheros gráficos se ven r. if (coderr!=grOk) { printf("Error gráfico: %s. Por ejemplo.. color++) for (pixel=0.\n"). Los registros de paleta. indicando en BX el primer elemento del DAC a cambiar (típicamente 0) y en CX el número de elementos a modificar (a menudo los 256 posibles).r_bx=0x0100. r. 60) y los asignamos a las componentes /* APUNTAR REGISTROS DE PALETA A ELEMENTOS CONSECUTIVOS DEL DAC */ roja y verde (rojo+verde=amarillo).. desarrollado FIGURA 7. &r)..r_cx=16.r_bx=(pagina<<8) | 1. e indicando en BH si se desean 4 páginas de 64 elementos en el DAC (BH=0) ó 16 páginas de 16 elementos (BH=1). entre ellas las que permiten cambiar sólo un registro de paleta o un elemento del DAC (y no un bloque). toman los valores 0. hubieran sido los elementos 16. &r).r_dx=FP_OFF(dac[32]). la 1 detectgraph (&gdrv.r_es=FP_SEG(dac[32]).31 y así if ((gdrv!=VGA) || (coderr!=grOk)) { printf("\nNecesaria tarjeta VGA. y permite seleccionar la página del DAC activa en BH (0-3 ó 0-15.\n".h> estableciendo la página 2 como activa void main() { (exclusivamente por antojo mio). Obviamente. coderr. Sin embargo.r_es=FP_SEG(paleta).15. invocable con AX=1013h y que consta de dos subservicios: el primero se selecciona poniendo un 0 en BL. intr (0x10.112 EL UNIVERSO DIGITAL DEL IBM PC. se emplearán los elementos 32. &r).47 DEL DAC */ elegimos 16 valores espaciados proporcionalmente pagina=2. color del borde.4 contiene un nuevo programa completo de demostración. i++) paleta[i]=i.. A continuación...144 posibles.16 COLORES */ página 0 apuntaría a los elementos 0. color.r_ax=0x1002. pixel<getmaxx()/16. Ello significa que struct REGPACK r.r_bx=32. entre los 262. para otras aplicaciones. i<48. 4. El lector deberá consultar bibliografía especializada para realizar este tipo de programación.3. x++) { setcolor (color). dac[i][0]=i*4. son más lentas cuando se va a cambiar un conjunto de registros.. } sucesivamente). pagina. color<16. nada mejor que llamar de nuevo a la INT 10h con AX=1012h. en la getch().

} coordenada Y. La página será normalmente la 0. i--) { while (!((inportb(0x3DA) & 8)==8)). ya que existen básicamente sólo #include <dos. /* LLENAR LA PANTALLA CON LINEAS HORIZONTALES DE COLOR 0. &r). Por ejemplo. /* esperar retrazo vertical */ trazar un punto en el modo 1024x768x256 de una while (!((inportb(0x3DA) & 8)==0)). j<256. se cargan CX y DX con sus coordenadas y BH con la página.199.4. outportb (0x3C8. &r). dac[j][0]*i >> 6).r_bx=0. outportb (0x3C8. y los siguientes 100 elementos . /* definir naranjas */ punto está asociado a un byte.ARQUITECTURA DEL PC. haciendo AH=0Dh antes de llamar a la INT 10h. r.3. j. El fallo reside.6 hay un nuevo listado de ejemplo. intr (0x10.199 del DAC de la siguiente manera: los primeros 100 en tonos ascendentes de azul.. i<200. char dac[256][3]. A continuación define los elementos 0. La causa de este problema no FIGURA 7. { unsigned char dac[256][3]. un programa puede fácilmente for (i=0.r_dx=FP_OFF(dac). dac[j][1]*i >> 6). /* definir azules */ presenta complicación alguna: los pixels se suceden dac[i][2]=i >> 1. 4. 0).3.. como es costumbre. r. dac[j][2]*i >> 6). outportb (0x3C9. /* B */ enable(). aplicaciones comerciales. en AL el color. /* R */ dac [i][1] = inportb (0x3C9). i++) { Este modo. Se puede en estas circunstancias visualizar una register x. /* esperar su fin */ for (j=0. /* DEFINIR PALETA EN EL DAC */ Modo 13h de 256 colores.3 . i++) { ii=200-i. más conveniente es utilizar los recursos del } /* claridad descendente desde el 64/64-avo al 0/64-avo de intensidad */ lenguaje de programación o. r. se coloca en CX la coordenada X. en DX la enable(). emplea el algoritmo más lento posible que existe dac [i][0] = inportb (0x3C9). intr (0x10.h> tres tipos de arquitectura de pantalla (modos CGA. simplemente. j<256. j++) { subrutinas en ensamblador.3. } referencia directamente a un elemento del DAC. 5 y hasta 10 veces más tiempo del necesario para trazar los puntos.r_es=FP_SEG(dac). j). Este es el disable(). Cada dac[i][0]=ii >> 1. página cualquiera mientras se trabaja en las otras. Lo dac [i][2] = inportb (0x3C9). far *vram. procedimiento seguido por la mayoría de las outportb (0x3C9. Turbo C. la BIOS enable(). Así.5: /********************************************************************* reside en que empleen rutinas multipropósito para * EFECTO «CINEMATOGRAFICO» DE DESVANECIMIENTO Y POSTERIOR * * REAPARICION DE LA PANTALLA CON ACCESO DIRECTO AL HARDWARE VGA. La existencia de varias páginas de vídeo se produce cuando en el segmento de 64 Kb de la FIGURA 7.h> etc. outportb (0x3C9. y++) for (x=0. y. AT Y PS/2 BAJO DOS 113 7. i<100.r_ax=0x1012. Sin embargo. arriba a abajo.3. /* ESTABLECER MODO DE PANTALLA */ que mientras tanto permanecen ocultas a los ojos r.r_ax=0x13. * imagen completa (caso por ejemplo del modo *********************************************************************/ 640x350x16): existen entonces varias páginas (2. dac[i][1]=0. i). &r). /* G */ para trazar puntos en los modos de 16 colores.4. Para pintar pixels en la pantalla y para consultar su color. y<200. del usuario. register i. outportb (0x3C9. aunque en los modos de vídeo que soportan varias páginas ésta se puede seleccionar con la función 5 de la INT 10h. i++) { while (!((inportb(0x3DA) & 8)==8)). for (i=0. void main() 16 colores y 256 colores).. i<=64.. outportb (0x3C7. dac[j][2]*i >> 6). En r. void main() { struct REGPACK r. en que han sido desarrollados sin pensar en la velocidad. sean muchos más de uno. Para consultar el color de un punto en la pantalla. x<320. } en la memoria de vídeo de izquierda a derecha y de for (i=100.6: memoria de vídeo se puede almacenar más de una /********************************************************************* * EJEMPLO DE USO DEL MODO DE 320x200 CON 256 COLORES * * SIN EMPLEAR LA LIBRERIA GRAFICA DEL COMPILADOR. dac[i][1]=ii >> 2.r_ax=3. i>=0. porque como disable().4. la cual devuelve el color del pixel en AL. vram=MK_FP(0xA000..199 */ for (y=0. a partir del segmento A000. acceder for (i=64. la BIOS for (i=0. a la INT 10h. de organización lineal. no dac[i][0]=0. outportb (0x3C9. Para trazar un punto outportb (0x3C9. } tiene la ventaja de que permite normalizar el acceso } /* claridad ascendente desde el 0/64-avo al 64/64-avo de intensidad */ a la pantalla. El problema es que las BIOS emplean 4. dac[j][1]*i >> 6). #include <dos. /* esperar su fin */ for (j=0.4.) que se reparten el segmento a partes iguales. /* esperar retrazo vertical */ directamente a la memoria de pantalla con while (!((inportb(0x3DA) & 8)==0)). en este caso sin emplear la librería gráfica del } getch(). El programa se limita a activar este modo de pantalla pintando las 200 líneas con los valores 0. A continuación se llama. int i. i<256. no mejorado tampoco por las VGA clónicas. dac[j][0]*i >> 6). La razón estriba en el mal diseño de la BIOS inicial de IBM.DIRECCIONAMIENTO DE PIXELS. existen funciones de la BIOS de uso no recomendado. x++) *vram++=y. r.r_cx=200. intr (0x10.). i++) { /* anotar la paleta activa */ disable(). * *********************************************************************/ todos los modos. j). mejor aún. cuyo valor (0-255) dac[i][2]=0. j++) { SuperVGA (y nunca mejor dicho. la figura 7.ii. r. en BH la página y } } en AH el valor 0Ch.

BX = «dy» * 2 . se debe a su peculiar manera de gestionar el color.0 16h AX.AL AX.0 BP.BP DX.255 del DAC. . Este programa ejemplo accede a la pantalla empleando las funciones de la BIOS para trazar puntos (ver apéndice sobre funciones de la BIOS).max_y-1 recta CX.max_y otras_cuatro AH. . del que hay pautas en su listado.max_x-1 SI.BX recta CX. ABS(pendiente) menor de 1 . como el amarillo es a su vez rojo más verde. Utiliza el modo CGA de 640x200 puntos. sobre todo con las líneas largas. Conseguir el naranja no es complicado: basta sumar rojo con amarillo. . . .4.BX SI. El algoritmo empleado es el de Bresseham con cálculo incremental de puntos (aunque al estar separada la rutina que traza el punto esta característica no se aprovecha.DI) color AL SEGMENT ASSUME CS:red. Y nada más.CX absx2x1 AX CX. Sin embargo.BX BX. solo cambia la altura de la pantalla). lo que divide automáticamente la pantalla en dos zonas con la estructura citada.3 10h 20h AX BX CX DX SI DI BP color. AX = ABS(X2-X1) = «dx» .SI DX.BP DI.«dx» = «d» . Los elementos 200. volver a modo texto . Existe también la posibilidad de colocar la VGA en dos modos de 256 colores alternativos al 13h y basados en el mismo. es necesario un acceso directo al hardware por cuestiones de velocidad.SI AX. ******************************************************************** * * * RED.BP recta CX. no empleados en este ejemplo.modo 10h AL. podrían ser definidos con otros colores para dibujar alguna otra cosa.BP SI. primera recta absx2x1: . aunque yo personalmente prefiero rutinas independientes para esas tareas con objeto de no ralentizar el trazado de rectas normales. La velocidad del algoritmo es muy elevada..BX SI.DX BP.max_x-1 . que pasa a ser más complejo -aunque más potente para algunas aplicaciones-. estos modos requieren un cambio en el modo de direccionamiento de los pixels. segunda absy2y1: .1 absy2y1 BP BX AX. máxime teniendo en cuenta que se trata posiblemente de una de sus implementaciones más optimizada (sólo usa una variable y mantiene todos los demás valores en los 7 registros de datos de la CPU. BX = ABS(Y2-Y1) = «dy» .BX noswap AX. de (CX. Existen versiones de este método que consideran de manera especial las líneas verticales y horizontales para pintarlas de manera más rápida. . BP = -1 = «yincr» si = «yincr» si Y2>Y1 Y2<=Y1 .AX . en uno se alcanzan 320x240 puntos y en el otro 320x400. Para direccionar puntos en los modos de 16 colores. SI = «dy» * 2 . así como a la inclusión del modo de 320x200 con 256 colores (el modo de 640x480 es idéntico en funcionamiento al de 640x350 de la EGA. factible en la totalidad de las tarjetas VGA del mercado. sin emplear demasiado la pila y duplicando código cuando es preciso en los puntos críticos). El programa dibuja una conocida red en las cuatro esquinas de la pantalla.max_x-1 SI. DS:red ORG 100h AX. No entraré en explicaciones matemáticas del método. cuarta modo max_x max_y max_color red . modo de vídeo CALL ADD ADD CMP JB MOV INT MOV INT INT recta PROC PUSH PUSH PUSH PUSH PUSH PUSH PUSH MOV MOV SUB JNC NEG XCHG XCHG MOV SUB MOV JNC NEG NEG CMP PUSHF JA XCHG SHL MOV SUB recta BX. fin de programa .4.max_color-1 BX.14 BX.ASM Demostración de gráfica en CGA utilizando BIOS * * * ******************************************************************** EQU EQU EQU EQU 6 640 200 2 . AT Y PS/2 en tonos descendentes de naranja. el naranja se obtiene sumando dos cantidades de rojo por cada una de verde. trazando líneas.DX) a (SI. La bibliografía especializada en gráficos explica los pasos a realizar para conseguir esto.1 SI. BP = 1 .0 DI.max_y-1 DI. tercera noswap: . . . 7. AX = X2-X1 .114 EL UNIVERSO DIGITAL DEL IBM PC.0 SI.0 CX.DI BX. . pero es fácil de implementar si en vez de llamar a la BIOS para pintar se emplea una rutina propia mezclada con la que traza la recta). en los que actúan interrelacionados los registros de paleta y el DAC de la manera descrita con anterioridad. aunque se puede configurar para cualquier otro modo. de hecho. Los lectores que no vayan a emplear las funciones del lenguaje de programación deberán consultar bibliografía especializada en gráficos. modo de pantalla color visible contador para eje Y contador para eje X inicio: MOV INT MOV MOV MOV MOV MOV MOV MOV CALL MOV MOV SUB CALL MOV MOV MOV MOV SUB CALL MOV SUB MOV otras_cuatro: .EJEMPLO DE GRÁFICOS EMPLEANDO LA BIOS.6 BP.0 DX.max_x-1 CX. Modos de 16 colores. esperar pulsación de tecla .DI BX. La única diferencia de la VGA respecto a la EGA.

«x»++ . segmento de pantalla CGA . sólo se corrompe AX SHL SHL ADD MOV AND XOR MOV SHL NOT AND OR POP POP POP POP RET punto640x200_C ENDP DX. incluida la BIOS de la VGA.cy) es 320*cy+cx. 7. «y») .color punto AX DX.BX AX penmen1 fin AX AL. «d» > 0 : «d» = «d» + «incr2» .1 AX. como la que se lista a continuación. Si se sustituye la rutina «punto». en (CX. Michener.4.BX AH.BP SI. «d» = «d» + «incr1» BX CX DX BP SI DI AH. Dibuja un vistoso ovillo basado en circunferencias con centro ubicado en una circunferencia base imaginaria.AX DI.ASM no es tan rápido. Sustituyendo la rutina «punto» por una rutina de trazado de puntos propia. Recordar que la fórmula para calcular el desplazamiento para un punto (cx. ubicar nuevo punto (1/0) Para estudiar el funcionamiento de la pantalla CGA el lector puede hacer un programa que recorra la memoria de vídeo para comprender la manera en que está organizada.SI noincx SI. DX) de color AL (CGA 640x200) .8192 CL DX.. . que consume un alto porcentaje del tiempo de proceso. «dx»-- . que traza el punto. con EGA y VGA no es tan sencillo realizar operaciones sobre la pantalla debido a la presencia de planos de bit. DI = «dy»*2-«dx»*2 = «incr2» . provoca nieve con algunas tarjetas). Sin embargo. se define previamente una paleta con apoyo directo en el hardware (el método empleado es sencillo pero no recomendable.DX .ASM que la invocara. DX = int («cy» / 2) .SI noincy SI. por tanto no compite con desventaja).DX CL. preservar parte baja de «cx» . DX) = («x». aprovechando los 256 colores de la VGA estándar en el modo 320x200.BX . . «y» = «y» + «yincr» .color punto AX CX SI. . borrar punto anterior .BP AX penmen1 fin SI. (SI>0) ? -> «d» > 0 ? fin: color recta punto DEC JNZ POP POP POP POP POP POP POP RET DB ENDP PROC PUSH PUSH PUSH PUSH PUSH PUSH MOV XOR INT POP POP POP POP POP POP RET ENDP ENDS END AX penmay1 BP DI SI DX CX BX AX 0 . un tanto peculiar pero no demasiado complicada. BX = «cx» / 8 . Y tiene razón: la culpa es de la BIOS. trazar punto usando BIOS punto red inicio Quizá el lector opine que RED. La VGA en modo 13h asocia cada punto de pantalla a un byte. en una VGA Paradise (BIOS de 14/7/88) se emplean 4 segundos y 8 centésimas en generar la imagen. . AL = bit a pintar . Se emplea el color verde. en (CX. en (CX. mientras que tal y como está el programa lo dibuja en 40. . . La versión que incluyo genera circunferencias en pantallas de . .7 CL. quien se basó a su vez en otro de J.5.ARQUITECTURA DEL PC.0Ch BX. El siguiente programa de ejemplo accede directamente al segmento de vídeo de la VGA (0A000h) para trazar los puntos.CL AH [BX].CL BX. Como la paleta establecida por defecto es poco interesante. «y» = «y» + «yincr» . BX = «cx» .BX 10h DI SI BP DX CX BX .7) invertir orden de numeración bit a borrar de la pantalla en AH AH = bit a borrar. preservar registros (salvo AX) .1 DX. «d» < 0 : «d» = «d» + «incr1» . todos estos datos cronometrados con precisión sobre un 386-25 sin memoria caché teniendo instalada la opción de «SHADOW ROM» (la lenta ROM copiada en RAM.AX penmay1 AX AL. «y») . .DI DX.AL DX CX BX DS . . .7 AH. punto640x200_C PROC PUSH PUSH PUSH PUSH MOV MOV MOV XCHG MOV SHR SHR JNC ADD no_add: INC SHL ADD DS BX CX DX BX. pendiente mayor de 1 .CX CL. AT Y PS/2 BAJO DOS 115 penmen1: noincy: penmay1: noincx: MOV SUB SUB POPF JBE PUSH MOV CALL POP INC AND JS ADD ADD DEC JNZ JMP ADD DEC JNZ JMP PUSH MOV CALL POP ADD AND JS ADD INC DEC JNZ JMP ADD DI. El algoritmo empleado para trazar la circunferencia es de J.CL BX. «d» > 0 : «d» = «d» + «incr2» . Bresseham desarrollado para plotter.AH [BX]. por otra que lo haga llamando a la BIOS.3 BX. «x»++ . DX = («cy» / 2) * 64 BX = BX + («cy» / 2) * 80 recuperar parte baja de «cx» dejar nº de bit a pintar (0.DI CX AX penmay1 fin SI.EJEMPLO DE GRÁFICOS ACCEDIENDO AL HARDWARE. salvo contadas excepciones como la del siguiente apartado.BX DI.CL DX. único visualizable en monitores monocromos (aunque cambiando la paleta con las funciones de la BIOS no hubiera sido necesario). la velocidad puede llegar a quintuplicarse en un hipotético RED2. (SI>0) ? -> «d» > 0 ? . DX) = («x».AH CL.1 veces más rápido).4 centésimas (10. «dx»-.1 BX. «dx»-. BX CL DX BX = = = = «cx» / 8 + («cy» MOD 2) * 8192 4 («cy» / 2) * 16 BX + («cy» / 2) * 16 . por lo que la pantalla es una matriz de 64000 bytes en el segmento 0A000h.1 no_add BX.0B800h DS.

.DI CX.SI .SI circunferencia .DI DX.DI BX.DI BP. en (x-DI.DI punto CX. CX = «cy» * 320 + «cx» .0 circunf_decx BX.SI CX.DI DX.SI circunferencia .DX BX. en (x+SI. . DS:oviseg ORG 100h AX. circunferencia completada .SI CX.AL BX.1 AX. circunferencia de circunferencias BP.1 BX.DI punto CX.SI DX.AL DX otro_reg inicio: MOV INT CALL MOV SHR MOV SHR MOV SHR CALL MOV INT MOV INT INT paleta_verde otro_reg: PROC MOV MOV MOV OUT INC XOR OUT MOV REPT SHR ENDM OUT XOR OUT DEC LOOP RET ENDP PROC MOV MOV MOV XOR SHL SUB NEG CMP JG ADD ADD CALL INC SUB SUB CALL INC SUB SUB CALL INC ADD ADD CALL INC SUB ADD ADD ADD CALL INC SUB SUB CALL INC SUB SUB CALL INC ADD ADD CALL INC SUB ADD CMP JG ADD ADD ADD ADD ADD JMP . y+SI) . .1 DX. en (x-DI. DX) con radio BX y color AL AL. BX = offset pintar el punto restaurar BX restaurar demás registros punto oviseg inicio 7. CX = x.SI ovillo_ok . en (CX.DI DX. y-SI) .SI circunferencia .DI BP.DX BX.CL max_x/320 AL.1 BX. y+DI) .1 DX.1 AX.DI circunferencia .1 BP. en (x+DI. CX = x.0 ovillo_decx BP.DI punto CX.1 BX.3 BX DI.EL ESTÁNDAR GRÁFICO VESA. DX) de radio BX . DX) con color AL .Demostración de gráfica en VGA utilizando hardware * * * ******************************************************************** EQU EQU EQU EQU 13h 320 200 256 . CX = max_x / 2 .SI DX.DX DX.DI DX.DI BP. .AL AL. BX = ma_y / 4 .SI AX.3 BP .DI circunferencia . y-SI) . y-DI) . trazar punto en 320x200 con 256 col. y+SI) AL DX.AX AX BP. esperar pulsación de tecla . en (x+DI.3C8h AL. y+SI) AL CX.DI circunferencia .AL DX.DI circunferencia .DI CX.DI BX.DI DX.0 SI.DI BX. y-SI) AL CX.0A000h DS.BX .AL AL.DI BX. Genoa.1 CX. los 256 registros . en (CX. . en (x+SI..SI DX. ovillo completado CX. en (x-SI. en (CX. . en (x-SI.SI BX.116 EL UNIVERSO DIGITAL DEL IBM PC.AL DX AL. en (x-DI. de 640 x 200) produciría elipses.SI circunf_ok CX. componente verde .DI DX.SI DX. No entraré en su demostración matemática. El resultado .0 16h AX. en otras (ej. Debido a la anarquía reinante en el mundo de las tarjetas gráficas.1 DX. ******************************************************************** * * * OVILLO. DX = «cy» * 256 .max_x CX. DX = «cy» * 64 .SI CX. en (x-SI.SI punto DX.AL DX. y-DI) AL CX.modo 10h paleta_verde CX.max_y DX. fin de programa . .DI punto DX. DX = max_y / 2 . en (x+DI.SI AX.6 ovillo_incy circunferencia PROC PUSH PUSH PUSH PUSH PUSH MOV XOR SHL SUB NEG circunf_acaba: CMP JG ADD ADD CALL SUB SUB CALL SUB SUB CALL ADD ADD CALL SUB ADD ADD ADD CALL SUB SUB CALL SUB SUB CALL ADD ADD CALL SUB ADD CMP JG ADD ADD ADD ADD ADD JMP circunf_decx: DEC PUSH MOV SUB SHL SHL ADD POP ADD circunf_incy: INC JMP circunf_ok: POP POP POP POP POP RET circunferencia ENDP punto PROC PUSH PUSH PUSH XCHG ADD SHR SHR ADD MOV MOV XCHG MOV XCHG POP POP POP RET ENDP ENDS END .DI BP.DI CX.1 ovillo AH.CL DX.SI DX. AT Y PS/2 relación de aspecto 1:1. que nada tiene que ver con el ensamblador. y+DI) AL DX.AX AX BX.DI CX.3 10h 20h CX. DX = y BP.ASM . en (x+SI. modo de vídeo ovillo_incy: ovillo_ok: ovillo ovillo_decx: DEC PUSH MOV SUB SHL SHL ADD POP ADD INC JMP RET ENDP SI AX AX. componente roja . y-DI) AL CX.BX DI. .2 * BX . baste decir que la rutina se basa exclusivamente en la aritmética entera calculando un solo octante de la circunferencia (los demás los obtiene por simetría). y+DI) . segmento VGA preservar BX en CX.6. Intel. en (x+DI. BX = 3 .CX DX CX DS . etc) para intentar crear una norma común. . en 1989 se reunieron un grupo importante de fabricantes (ATI.DI CX.DL CX. DX = y DS CX DX DH.DI BP. y+SI) . registro a programar . CX = «cy» * 256 + «cx» . .10 DI ovillo_acaba modo max_x max_y max_color oviseg SEGMENT ASSUME CS:oviseg.10 DI circunf_acaba DI SI DX CX BX .SI punto CX.256 DX.1 BP.SI punto CX. y-DI) .SI circunferencia .CX [BX]. y+DI) AL CX.BX DI.DI AX. en (x-DI. en (CX.SI CX.DX DX.4. en (x+SI.DI AX.DX) con radio BX y color AL BX CX DX SI DI SI. .DI BX. y-SI) AL CX.SI punto CX. BP = 3 .DI CX. volver a modo texto . componente azul paleta_verde ovillo ovillo_acaba: .SI DX.6 circunf_incy SI AX AX.2 * BX DI. en (x-SI. Paradise.

Una de las grandes ventajas del estándar VESA es la enorme información que pone a disposición del programador. Las más antiguas pueden también soportarla gracias a pequeños programas residentes que el usuario puede instalar opcionalmente. barriendo sistemáticamente todos los modos de pantalla desde el "mejor" hasta el "peor". La función setmode() establece un modo gráfico VESA. En principio. si bien algunos de los más avanzados (con 32000 o 16 millones de colores) sólo están soportados por las versiones más recientes de la norma. Estas funciones se invocan vía INT 10h. Para realizar programas que utilicen la norma. devolviendo además dos informaciones interesantes: la dirección de memoria de la rutina de conmutación de bancos (ya veremos para qué sirve) y el segmento de memoria de vídeo. el inicio de la ventana lógica sobre el total de la memoria de vídeo. Este estándar define una interface software común a todas las BIOS para permitir a los programadores adaptarse con facilidad a las diversas tarjetas sin tener en cuenta sus diferencias de hardware. La función getbest256() se limita a buscar el modo de mayor resolución de 256 colores soportado por la tarjeta gráfica de ese equipo. entre todos los disponibles. estos pueden fácilmente conmutar a modo texto (con la precaución de preservar antes los 4 primeros Kbytes de la RAM de vídeo empleados para definir los caracteres) y volver al modo gráfico original dejando la pantalla en el estado inicial. Sin embargo. Para desarrollar una aplicación profesional. mejor aún: podemos llamar directamente a una subrutina que realiza . con AX tomando valores por lo general desde 4F00h hasta 4F08h. Además. sólo uno de los 4 bancos puede estar direccionado en el segmento de memoria de vídeo. Actualmente. algún modo VESA para los usuarios que estén equipados con dicho soporte. De toda la información que devuelve getinfo() es particularmente interesante el número de bancos que necesita ese modo de vídeo. El estándar VESA soporta multitud de modos gráficos. que será normalmente 0A000h. la lista de modos de vídeo puede ser mayor en algunas tarjetas. AT Y PS/2 BAJO DOS 117 de la misma fue el estándar VESA. es una buena norma soportar algún modo estándar de la VGA y. una imagen de 640x480 con 256 colores utiliza unos 256 Kb de RAM. Por tanto. salvo para aplicaciones muy concretas. Es posible conocer todos los modos y qué características de resolución. El programa de ejemplo. Modos gráficos.ARQUITECTURA DEL PC. En el apéndice donde se resumen las funciones del DOS y la BIOS aparecen también las funciones VESA de vídeo. los modos utilizados por este programa de demostración son conocidos. dividida en 4 bancos. de especial utilidad para programas residentes: así. las principales tarjetas soportan la norma VESA. getinfo() devuelve información sobre cualquier modo gráfico. Intentar acceder directamente al hardware o a las funciones BIOS propias de cada tarjeta del mercado por separado. En un momento dado. aunque nuestro ejemplo es una simplificación) existe una función de la BIOS VESA o. existe_modo() invoca también a la BIOS VESA. para obtener más prestaciones. un esquema alternativo podría consistir no en buscar ciertos modos concretos sino en ir recorriendo todos y elegir el que cumpla ciertas características de resolución o colores. Sin embargo. es ciertamente poco menos que imposible. Para comprobar la existencia de un determinado modo gráfico. De esta manera. colores y arquitectura tienen. por ejemplo. numerados a partir de 100h. Finalmente. el lector deberá consultar dicha información. Para elegir el banco activo (más bien. se expone aquí un sencillo programa de demostración que recoge prácticamente todos los pasos necesarios para trabajar con un modo VESA. tarea que realiza la función testvesa(). El primer paso consiste en detectar la presencia de soporte VESA en el sistema. hay funciones adicionales muy útiles para guardar y recuperar el estado de la tarjeta. aunque el modo 6Ah también es VESA (800x600x16) al estar soportado por múltiples tarjetas. Hay que tener en cuenta que todos los modos de 256 colores de más de 320x200 ocupan más de 64 Kb de memoria. sobre todo en el futuro. Entre 100h y 107h se definen los modos más comunes de 16 y 256 colores de todas las SuperVGA.

r_es = FP_SEG (mem). intr (0x10. unsigned).r_di = FP_OFF (mem). *bancos = *vram / 64. } . banco++) { setbank (ConmutaBanco. &bancos). } /* CONMUTAR DE BANCO CON LA MAXIMA VELOCIDAD */ void setbank (long direccion. &r). /* Obtener mejor modo de 256c */ void setbank (long. mem = farmalloc (256L). char far *mem. *vram = (unsigned) ( (long) mem[8] * mem[10] / 1024L). r.r_bx=modo. intr (0x10. if (!testvesa()) { printf ("\nNecesario soporte VESA para este programa. farfree (mem). } /* COMPROBAR LA EXISTENCIA DE UN MODO GRAFICO */ unsigned existe_modo (unsigned modo) { struct REGPACK r. 0). &video_seg). el interface VESA evita que tengamos que hacer accesos directos al hardware.r_es = FP_SEG (mem). while ((*array!=0xFFFF) && (*array!=modo)) array++. &max_x. r. farfree (mem). } unsigned testvesa (void).r_di = FP_OFF (mem).r_ax=0x4F02. /********************************************************************* * * * ESTANDAR GRAFICO VESA: EJEMPLO DE USO DEL MEJOR MODO DE 256 * * COLORES EN CUALQUIER SUPERVGA. /* direccionar banco */ /* normalmente 0xA000:0 */ /* OBTENER INFORMACION SOBRE UN MODO GRAFICO VESA */ void getinfo (unsigned modo. r. unsigned *vram. } /* COMPROBAR QUE EXISTE SOPORTE VESA */ unsigned testvesa(void) { struct REGPACK r. unsigned *bancos) { struct REGPACK r. if (banco!=bancos-1) limite=32768. r. if (existe_modo (M640x480x256)) return (M640x480x256). r. i++) *pantalla++=0x55AA. max_x. i<=limite. AT Y PS/2 rápidamente esa tarea (sin tener que utilizar interrupciones) cuya dirección nos devolvió setmode(). r. bancos. for (banco=0. por fortuna.r_ax = 0x4F01. max_x.h> <string. De esta manera. r. unsigned *max_y. /* dirección FAR del conmutador de banco */ unsigned video_seg.r_cx = modo. } setbank (ConmutaBanco. i. getinfo (modo. entre los normales de las SuperVGA. if (existe_modo (M800x600x256)) return (M800x600x256). if (*vram % 64) (*bancos)++. /* Detectar soporte VESA */ existe_modo (unsigned). limite. r.h> <alloc. &r). intr (0x10. r. mem = farmalloc (256L). modo. &r). far *array. mem = farmalloc (256L). Finalmente. /* Comprobar si un modo es soportado */ getbest256 (void). *max_y = mem[10]. *videoseg = *(mem+2). /* Establecer modo VESA */ unsigned *). printf ("Modo de %dx%dx256 con %d Kb\n\n". /* dirección del segmento de vídeo */ far *pantalla.r_di = FP_OFF (mem). *max_x = mem[9].118 EL UNIVERSO DIGITAL DEL IBM PC. r.r_di = FP_OFF (mem). unsigned vesa. &vram.h> M640x400x256 M640x480x256 M800x600x256 M1024x768x256 M1280x1024x256 0x100 0x101 0x103 0x105 0x107 /* modos VESA normales de 256c */ /* BUSCAR EL MODO DE 256 COLORES DE MAYOR RESOLUCION */ unsigned getbest256 (void) { if (existe_modo (M1280x1024x256)) return (M1280x1024x256). } /* ESTABLECER UN MODO GRAFICO VESA Y DEVOLVER LA DIRECCION DE */ /* LA RUTINA DE CONMUTACION DE BANCOS Y EL SEGMENTO DE VIDEO */ void setmode (unsigned modo.h> <stdlib. vram. long *conmutar. farfree (mem). r. setmode (modo. banco<bancos. pantalla=MK_FP(video_seg.r_es = FP_SEG (mem). r. if (strcmp (mem. unsigned *max_x. exit (1). activarlo e ir recorriendo todos los bancos que componen la memoria de vídeo (excepto el último. banco. else vesa=0. array = MK_FP (mem[8]. unsigned *). mem = farmalloc (256L).\n"). unsigned *. /* Obtener información del modo */ unsigned *. intr (0x10. r. r. vram). return (*array==modo). long *.r_ax = 0x4F01. intr (0x10. return (vesa). /* DEMOSTRACION */ void main() { struct REGPACK r.r_es = FP_SEG (mem). if (existe_modo (M640x400x256)) return (M640x400x256). return (0). * * * *********************************************************************/ #include #include #include #include #include #define #define #define #define #define <dos. max_y. banco). unsigned banco) { asm { mov ax. long ConmutaBanco. El único cometido de este programa de demostración es buscar el mejor modo de 256 colores. unsigned far *mem. &ConmutaBanco. 0).4f02h mov dx. } modo = getbest256(). antes de terminar. &r). else limite=(vram-banco*64)*512. *conmutar = *(mem+3). unsigned *.r_ax=0x4F00. esta modalidad de llamada no tiene por qué estar soportada por todas las BIOS VESA (en cuyo caso devuelven una dirección 0000:0000 para el CALL) aunque la inmensa mayoría. getinfo (unsigned.banco mov bx. que podría estar incompleto) para llenar la pantalla con bytes de valor 55h y 0AAh. unsigned *videoseg) { struct REGPACK r. lo soportan.0 call dword ptr direccion } } /* todo el segmento de 64 Kb */ /* palabras último banco */ /* pintar */ for (i=0. long far *mem. "VESA")==0) vesa=1. if (existe_modo (M1024x768x256)) return (M1024x768x256). &max_y. r. La rutina setbank() se limita a cargar el registro DX con el banco necesario antes de ejecutar el CALL. unsigned far *mem. farfree (mem). /* Conmutar banco de memoria */ setmode (unsigned. mem[7]). De todas maneras. &r).r_cx = modo. se imprime la resolución y cantidad de memoria consumida por ese modo.h> <stdio. r. max_y. mem[4]=0.r_ax = 0x4F00.

5 milisegundos. debido a la traducción que efectúa el 8042 en el primero. Funcionamiento general del teclado. en la segunda interrupción aparece el valor 4Dh: el mismo que hubiera aparecido pulsando el ’6’ del teclado numérico. el KEYB o la BIOS activan el bit 1 (el que vale 2) de la posición de memoria 0040h:0096h. No obstante. 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 códigos 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 mayoría de los bytes mayores de 128. por ejemplo. En general. El bit 0 de esa misma posición 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. después aparecen otras dos interrupciones con los valores 0E0h-4Dh (con objeto de simular que se pulsa el ’6’ del teclado numérico): como el estado NUM LOCK está activo y en teoría se ha pulsado shift y el 6 del teclado numérico. intermedio y alto. Bajo el sistema DOS. se generan dos interrupciones consecutivas: en la primera aparece un valor 0E0h en el puerto del teclado que indica que es una tecla expandida. 7. aunque bastante mal. . 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 códigos de rastreo son distintos y al soltar una tecla se producen dos interrupciones) pero bajo DOS esto no sucede en ningún caso y la compatibilidad es casi del 100%.ARQUITECTURA DEL PC.tienen un comportamiento especial. Al pulsar una tecla se genera una interrupción 9 (IRQ 1) y el código de rastreo que identifica la tecla pulsada puede leerse en el puerto de E/S 60h. Las teclas expandidas -las que han sido añadidas al teclado estándar de 83/84 teclas. Por ejemplo. entre ellos el 0E0h). donde se listan los códigos de rastreo del teclado). el teclado del AT posee unos comandos adicionales para controlar los LEDs. el único. aunque para la mayor parte de las labores de programación no es necesario llegar a tanto. la BIOS o el KEYB tratan de manera especial las teclas expandidas. en un teclado normal de 83 teclas hay que pulsar el ’6’ del teclado numérico junto con shift para que el cursor avance. tanto en XT como en AT (se corresponde en los AT con el registro de salida del 8042). Así.y genera un prefijo 0E1h en vez del 0E0h habitual. pero al pulsarla aparece la secuencia E1-1D-45-E1-9D-C5). ciertas combinaciones de las teclas no expandidas. el teclado expandido funcionará mal. lo que sucede en casi todos los editores de texto de los modernos compiladores. En el capítulo 12 se documenta el funcionamiento del hardware del teclado. si no se carga el KEYB. a tres niveles: bajo. ya que pueden generar hasta 4 interrupciones consecutivas (con un intervalo de unos 1. . el cursor avanza a la derecha.BAJO NIVEL. en los ordenadores más antiguos (con BIOS -o al menos su tecnología. En este apartado se estudiará a fondo el funcionamiento del teclado en los ordenadores compatibles.EL TECLADO. en la siguiente interrupción ese bit se borra y ya se sabe que el código leído es el de una tecla expandida. esta tecla no genera códigos al ser soltada. al soltar la ’A’ se generará otra INT 9 y se podrá leer el byte 9Eh del puerto del teclado (véase la tabla del apéndice V. estos códigos shift fantasma dan problemas cuando las teclas de SHIFT adquieren otro significado diferente que el de conmutar el estado NUM LOCK. el teclado del AT es idéntico al del XT en los códigos de rastreo y comportamiento. de hecho.1. si se pulsa la ’A’ se generará una INT 9 y aparecerá en el puerto del teclado (60h) el byte 1Eh. si NUM LOCK está activo. al soltar la tecla aparecerá la secuencia de interrupciones 0E0h-CDh-0E0h-0AAh. interesante para ciertas aplicaciones concretas. incluso en Estados Unidos -aunque las teclas estén bien colocadas-. Sin embargo.anterior a Noviembre de 1985). en general es bastante deficiente la emulación por hardware y el controlador del teclado (KEYB) tiene que tratarlas de manera especial en la práctica. Cuando se lee un valor 0E0h en una interrupción de teclado. cuando está inactivo NUM LOCK y se pulsa el cursor derecho expandido.5. si se suelta la tecla se produce otra interrupción y se genera el mismo código de rastreo+128 (bit 7 activo). AT Y PS/2 BAJO DOS 119 7. . ó 3 ms en los códigos dobles que convierte en uno el 8042) con objeto de emular.5. o en su defecto la secuencia equivalente 0E0h-CDh-0E0h-0B6h. Por ello.

puntero a la cola del buffer . inicio de la cola circular puntero al inicio del buffer ZF = 1 --> buffer lleno introducir carácter ASCII (AL) en el buffer actualizar puntero al final del buffer ZF=0 (SP siempre <> 0) --> buffer no lleno no_desb: fin_rutina: El valor 0 para el código de rastreo es usado para introducir también algunos caracteres especiales. el código 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.o extraerlo del mismo es comprobando adecuadamente los desbordamientos de los punteros teniendo en cuenta las variables mencionadas. las rutinas de la BIOS saben si deben informar de estas teclas o no según se esté empleando una función avanzada u obsoleta. lo normal es que esté entre 40h:1Eh y 40h:3Eh). para cuando el programa principal decida explorar el teclado -lo hará siempre consultando el buffer-.) y el segundo byte indica cuál (son los denominados códigos secundarios). AT Y PS/2 El buffer del teclado. más allá del fin del buffer . espiando lo que sucede pero sin alterar la . las secuencias introducidas por medio de ALTteclado_numérico llevan asociado un código de rastreo 0.BX BX. Gestión de la interrupción del teclado. de lo contrario. Es importante señalar que aunque el buffer (organizado como cola circular) normalmente está situado entre 0040h:001Eh y 0040h:003Eh. cursor. El siguiente ejemplo introduce un carácter de código ASCII AL y código de rastreo AH (es cómodo y válido hacer AH=0) en el buffer del teclado: MOV MOV CLI MOV MOV ADD CMP JB MOV CMP JE MOV MOV CMP STI BX.40h DS. El puntero al inicio del buffer es una variable tamaño palabra almacenada en la posición 0040h:001Ah y el fin otra ubicada en 0040h:001Ch. He aquí un ejemplo de una subrutina que intercepta la interrupción del teclado apoyándose en el controlador habitual y limitándose a detectar las teclas pulsadas. . pero sí actualmente (de esta manera.DS:[1Ch] CX. si el código ASCII 0 va acompañado de un código de rastreo 3 los programas deberían interpretarlo como un auténtico código ASCII 0 (esta secuencia se obtiene con Ctrl-2) lo que permite recuperar ese código perdido en indicar combinaciones especiales. Si el código ASCII depositado es cero ó 0E0h. Así mismo.DS:[80h] CX.. medida de seguridad que de hecho toma el KEYB del DR-DOS (en estas máquinas además no es conveniente ampliar el tamaño del buffer cambiándolo de sitio.DS:[82h] no_desb CX.DS:[1Ah] fin_rutina DS:[BX]. En el apéndice V se listan los códigos secundarios: son el segundo byte (el más significativo) de la palabra depositada en el buffer del teclado por la BIOS o el KEYB. meter carácter AX en el buffer del teclado .BX CX. por desgracia.AX DS:[1Ch]. según IBM. por lo que el usuario puede generar los caracteres ASCII 0E0h y 0F0h sin que se confundan con combinaciones especiales. por ejemplo. en un teclado español y otro francés. por ejemplo. además. . apuntar CX al siguiente dato . evitar conflictos con interrupciones . Cuando se pulsa una tecla normal. aunque las funciones estándar de la BIOS y del DOS que informan del teclado lo convierten en cero para compatibilizar con teclados no expandidos. se trata de una tecla especial (ALT-x. El código ASCII 0E0h sólo es generado en los teclados expandidos por las teclas expandidas (marcadas como ’Ex’ en la tabla de códigos de rastreo del apéndice V). No estaría de más en este ejemplo comprobar si las variables 40h:80h y 40h:82h son distintas de cero por si el ordenador es demasiado antiguo. la inmensa mayoría de las pequeñas utilidades de las revistas y los ejemplos de los libros son.2 CX. Por ello.0 . incorrectos: la manera correcta de colocar un valor en el buffer -para simular. etc. realmente el offset del inicio y el fin del buffer respecto al segmento 0040h lo determinan las variables (tamaño palabra) situadas en 0040h:0080h y 0040h:0082h en todos los ordenadores posteriores a 1981. la pulsación de una tecla. los programas suelen comprobar preferentemente el código ASCII. para compatibilizar). como las vocales acentuadas. ello no siempre es así. ¡la tecla Z tendría distinto código!). En todo caso. . aunque por lo general no es demasiado importante su valor (de hecho. . la rutina que gestiona INT 9 deposita en un buffer dos bytes con su código ASCII y el código de rastreo.120 EL UNIVERSO DIGITAL DEL IBM PC. .CX SP. etc.

habilitándolo de nuevo al final. . En general. 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-. Es necesario hacer PUSHF antes de llamar porque la subrutina invocada va a retornar con IRET y no con RETF. . EOI al AX del volver AX del saltar 8259 programa principal al programa principal programa principal al gestor previo de INT 9 fin: Como se puede observar. . .ARQUITECTURA DEL PC. . En caso contrario. ahora no es preciso el PUSHF. menos el temporizador. gestionarla . . dado que puede producirse en el momento más insospechado y no debe afectar a la marcha del programa principal. el programador ha de decidir pues si es preciso enviar antes o no el EOI (véase la documentación sobre el controlador de interrupciones 8259 de los capítulos posteriores).20h. Sólo en el caso de que la gestione él es preciso enviar una señal de reconocimiento y un EOI al 8259. como en cualquier otra interrupción hardware. anterior_int9 es una variable de 32 bits que contiene la dirección de la interrupción del teclado antes de instalar la nueva rutina. el duo PUSHF/CALL es una manera alternativa de simular una instrucción INT. como en el caso del CALL. que lleva nada menos que la hora interna del ordenador-. se salta al controlador previo a esta rutina con un JMP largo (segmento:offset). lo que es conveniente para permitir que se produzcan más interrupciones -por ejemplo. la del temporizador. Además. AT Y PS/2 BAJO DOS 121 operación normal del teclado: nueva_int9: STI PUSH AX IN AL. . .60h AL. la mayoría de las utilidades residentes no toman . por razones obvias.están inhibidas por mucho que se haga STI. señal de reconocimiento enviada . . . tanto en XT como AT hay que enviar en este caso una señal de fin de interrupción hardware (EOI) al 8259 (con un simple MOV AL. es necesario preservar y restaurar todos los registros modificados. código de la tecla pulsada ¿es nuestra tecla? no vamos a «manchar» AX .01111111b 61h. permitir interrupción periódica preservar registros modificados código de la tecla pulsada preparar la pila para IRET llamar a la INT 9 original hacer algo con esa tecla restaurar registros modificados volver al programa principal Evidentemente. esta rutina gestiona una tecla y las demás se las deja al KEYB o la BIOS. algo innecesario por otra parte.AL AL. por medio de los comandos 0ADh y 0AEh enviados al 8042. En el ejemplo. en los XT hay que enviar una señal de reconocimiento al teclado poniendo a 1 y después a 0 el bit 7 del puerto de E/S 61h (en AT no es necesario.61h AL. El ejemplo anterior quedaría como sigue: nueva_int9: STI PUSH IN CMP JNE PUSH IN OR OUT AND OUT POP MOV OUT POP IRET POP JMP AX AL. . es importante no enviar más de una señal de reconocimiento. el EOI es enviado justo antes de terminar de gestionar esa tecla.10000000b 61h. .tecla fin AX AL.AL AX AL.AL AX AX CS:anterior_int9 . aunque tampoco resulta perjudicial hurgar en ese bit en las máquinas fabricadas hasta ahora). 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 recién leída.20h 20h. La instrucción STI del principio habilita las interrupciones. siempre inhibidas al principio de una interrupción -valga la redundancia-. AL = tecla pulsada .AL) al igual que cuando se gestiona cualquier otra interrupción hardware. aunque si la rutina es corta no habrá demasiada prisa. las interrupciones hardware de menor prioridad todas. OUT 20h.60h PUSHF CALL CS:anterior_int9 POP IRET AX . ello significa que mientras se la procesa. de cara a evitar anomalías importantes en el teclado de los XT. Sin embargo. . .

método válido solo en AT testref: . por defecto. por lo que a menudo es necesario leerlo directamente. CALL MOV OUT POP IRET PUSH PUSH MOV CLI IN AND CMP JZ MOV IN TEST LOOPNZ POP POP RET . así es 7. ALT y SHIFT (al igual que Num Lock.AL AL.60h espera AL. se devuelve en AL un byte con información sobre las teclas de control (SHIFT. En 0040h:0018h. aunque cuidando de que sea durante el menor tiempo posible: nueva_int9: STI PUSH CALL MOV OUT CALL IN STI .) que es el mismo byte almacenado en 0040h:0017h (véase en el apéndice III el área de datos de la BIOS y las funciones de la BIOS para teclado). ¿buffer de entrada lleno? . Por lo general es mejor emplear las funciones BIOS.Teclado no expandido. que sirve para reinicializar el sistema si se pulsa en conjunción con DEL) por lo que los programas residentes suelen precisamente emplear combinaciones de dos o más teclas de estas para activarse sin eliminar prestaciones al teclado. registro de estado del 8042 . breve ventana para interrupciones AX espera AL. permitir rápidamente interrupciones . . inhibir teclado . Por otra parte. .5. cabe destacar el hecho de que CTRL. por razones de compatibilidad. que consultar directamente un bit. IBM no ha definido combinaciones con ellas (excepto CTRL-ALT. lo que puede verificarse chequeando el bit 1 del registro de estado: no es conveniente realizar un bucle infinito que dejaría colgado el ordenador de fallar el 8042. no merece la pena hacer STI espera: .2. AT Y PS/2 estas precauciones tan sofisticadas (de hecho..995 AL.AH testref AH. Consulta de SHIFT. etc (marcas de teclado). si se pulsan dos o más teclas de estas la BIOS o el KEYB asignan prioridades y consideran sólo una de ellas: ALT es la tecla de mayor prioridad. el KEYB del DR-DOS tampoco). ¿tecla? . si existen.10h AL.64h AL. Evidentemente. Scroll Lock e Ins) no poseen la característica de autorepetición de las demás teclas debido a la gestión que realiza la BIOS o el KEYB. . Llamando con AH=2 a la INT 16h (función 2 de la BIOS para el teclado). Además las interrupciones han de estar inhibidas en el momento crítico en que dura el envío del comando. Lógicamente sólo se pueden enviar comandos al 8042 cuando el registro de entrada del mismo está vacío. de ahí que sea recomendable un bucle que repita sólo durante un cierto tiempo. CTRL.AL espera AL.0AEh 60h. desinhibir teclado .61h AL. CTRL. constante para 15 ms . Caps Lock. todas las funciones para teclados no expandidos pueden usarse también con los expandidos.0ADh 60h. en el ejemplo se utiliza la temporización del refresco de la memoria dinámica de los AT para no emplear más de 15 ms esperando al 8042.. ALT.122 EL UNIVERSO DIGITAL DEL IBM PC. existe otro byte de información adicional.NIVEL INTERMEDIO. procesar tecla y enviar EOI al 8259 .AL AX AX CX CX. aunque no hay función BIOS para consultarlo en los teclados no expandidos. Estas teclas pueden ser pulsadas para modificar el resultado de la pulsación de otras. etc. seguida de CTRL y de SHIFT.2 testref CX AX .

Lectura de teclas ordinarias. . provoca que el ordenador se detenga hasta que se pulse una tecla no modificadora (ni SHIFT. se hace 40h:19h=0. AT Y PS/2 BAJO DOS 123 . El controlador del teclado almacena en 40h:19h el número en proceso de formación: cada vez que llega un nuevo dígito multiplica el contenido anterior por 10 y se lo suma. con la función 1 (AH=1 al llamar a INT 16h) se devuelve también en AX el carácter del buffer pero sin sacarlo (habrá que llamar de nuevo con AH=0). detectan BREAK y abortan el programa en curso).ALT-teclado_numérico: manteniendo pulsada ALT se puede teclear en el teclado numérico un valor numérico en decimal. Detección de soporte para teclado expandido. el KEYB del DR-DOS 5. el bit 4 de 0040h:0096h indica si el teclado es expandido. . Los bits de 40h:96h sólo son fiables si está instalado el KEYB del MS-DOS o 99% compatible. Combinaciones especiales de teclas.PTR SCR (SHIFT con el (*) del teclado numérico en teclados no expandidos): vuelca la pantalla por impresora al ejecutar una INT 5. sin embargo es suicida fiarse de esto . . El controlador del teclado introduce una palabra a cero en el buffer e invoca la interrupción 1Bh. Normalmente no será necesario distinguir entre un teclado expandido o estándar. aunque sí los demás bits. La pausa es interna a la rutina de control del teclado. Los programas pueden interceptar esta interrupción para realizar ciertas tareas críticas antes de terminar su ejecución (ciertas rutinas del DOS. Al soltar ALT.0/6. ni ALT.SYS REQ: al pulsarla genera una INT 15h (AX=8500h) y al soltarla otra INT 15h (AX=8501h). etc. así como la función 5 (introducir caracteres en el buffer). esperando su pulsación si es preciso. Antes de usar esta función conviene asegurarse de que está soportada por la BIOS o el KEYB instalado. por ejemplo. así como de la de 0040:0018h. puede ser consultada en los teclados expandidos con la función 12h de la BIOS del teclado expandido.BREAK: se obtiene pulsando CTRL-PAUSE en los teclados expandidos (CTRL-SCROLL LOCK en los no expandidos).ARQUITECTURA DEL PC. A partir de 0040h:0096h hay otros bytes con información adicional y específica sobre el teclado del AT y los teclados expandidos: parte de esta información. y se devuelve en AX (AH código de rastreo y AL código ASCII). aunque en este caso no se espera a que se pulse una tecla (si el buffer estaba vacío se retorna con ZF=1 en el registro de estado). tecla que será ignorada pero servirá para abandonar la pausa. aunque en algunos casos habrá que tener en cuenta la posible pulsación de una tecla expandida y su código 0E0h asociado.0 (excepto en modo KEYB US) no gestiona correctamente el bit de AltGr.). . . . En todo caso. al soltar ALT el código ASCII que representa se introducirá en el buffer. 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 información útil (consultar funciones de la BIOS para teclado). En los equipos con soporte para teclado expandido existen además las funciones 10h y 11h (correspondientes a la 0 y 1) que permiten detectar alguna tecla más (como F11 y F12) y diferenciar entre las expandidas y las que no lo son al no convertir los códigos 0E0h en 0. básicamente las de impresión por pantalla.PAUSE: se obtiene con dicha tecla o bien con CTRL-NUM LOCK (teclados no expandidos).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 dirección 0FFFFh:0 reinicializando el ordenador. Con la función 0 de la INT 16h (AH=0 al llamar) se lee una tecla del buffer del teclado.Teclado expandido.

aunque se trata de un detalle poco relevante -incluso para quienes pretendan hacer algo especial con estas teclas-. En general.124 EL UNIVERSO DIGITAL DEL IBM PC. las funciones de teclado expandido no están soportadas. puede verificarse la presencia del programa KEYB.. La rutina de la BIOS del AT (y de los KEYB) que lee el buffer del teclado. en caso de estar implementada no podría devolver 1200h porque ello significaría una contradicción entre AH y AL. periódicamente desde la interrupción del temporizador-. En teoría. lo ideal es chequear la presencia de estas funciones por otros procedimientos. estas características no son útiles en el entorno DOS y. activan el acarreo y ejecutan inmediatamente la función 4Fh de la INT 15h para permitir que alguien se de por enterado de la tecla y opcionalmente aproveche para manipular AL y simular que se ha pulsado otra tecla: ese alguien puede devolver además el acarreo borrado para indicar al KEYB que no continúe procesando esa tecla y que la ignore (en caso contrario se procedería a interpretarla normalmente). Consideraciones finales. cuando un carácter acaba de ser introducido en el buffer del teclado. numeritos que aparecen al pulsar los cursores expandidos. por otra parte. De todas formas. para averiguar la última tecla pulsada o soltada. venga equipada o no con teclado expandido. tras leer el código de rastreo en AL. invocar función teclado expandido AX. Por ello. cuando no hay teclas y tiene que esperar por las mismas ejecuta de manera regular la función 90h (AH=90h) de la interrupción 15h indicando una espera de teclado al llamar (AL=2). no es exactamente idéntica a la de los XT. los de XT a partir del 10 de enero de 1986 soportan la 10h y la 11h. etc. También es conveniente indicar que en los AT se puede leer puerto del teclado. normalmente para devolver una señal de reconocimiento cuando alguien les ha enviado algo -por ejemplo. lo que también permite emplear esta función en los PC/XT. Alternativamente. si la función no está implementada no devuelve el acarreo activo para indicar el error.. esta práctica tiene efectos secundarios debidos al mal diseño del software del sistema de los AT (tales como teclas shift que se enganchan. Pero hay un truco: si el resultado sigue siendo AX=1200h. por ello. función soportada Posibilidades avanzadas. Para verificar si esta función está disponible en la BIOS basta con ejecutar la función 0C0h de la INT 15h que devuelve un puntero en ES:BX y comprobar que el bit 4 de la posición direccionada por ES:[BX+5] está activo. Conviene señalar que los teclados de AT pueden generar interrupciones aunque no se pulsen teclas. han sido deficientemente normalizadas. nadie ha cambiado el valor de AX: además. Por ejemplo. en los XT sólo se obtendrá una lectura correcta inmediatamente después de producirse la interrupción del teclado y antes de enviar la correspondiente señal . Para detectar la presencia del KEYB del MS-DOS en memoria basta con llamar a la interrupción 2Fh con AX=0AD80h y comprobar que devuelve AL=0FFh (esta función devuelve la versión del KEYB en BX y un puntero a un área de datos en ES:DI). Por desgracia. Esto se debe a que al no estar implementada la función. al acentuar incorrectamente se generan dos caracteres (además del familiar pitido): el KEYB del MS-DOS sólo ejecuta una llamada a la INT 15h con la función 91h (pese a haber introducido dos caracteres en el buffer) y el de DR-DOS hace las dos llamadas. AT Y PS/2 y es más seguro chequear por otros medios la presencia de funciones de la BIOS para teclado expandido antes de usarlas. la BIOS puede enviar un comando para cambiar los led’s-. como si se quedaran pulsadas. debido a los códigos 0FAh. las BIOS de AT del 15 de noviembre de 1985 en adelante soportan las funciones 5. un hipotético avanzado sistema operativo podría aprovechar ese tiempo muerto para algo más útil. De esta manera. Lo que sí puede resultar más interesante es la función de intercepción de código del teclado: las BIOS de AT no demasiado antiguas y el programa KEYB. aunque es más arriesgado. Así mismo.1200h 16h . 10h y 11h. [DR-DOS usa AX=0AD00h]. se ejecuta la función 91h para indicar que ya ha finalizado la entrada y hay caracteres disponibles. en casi cualquier momento -por ejemplo. MOV INT CMP JE JMP AX. Por ejemplo: llamar a la función 12h con AL=0. y la secuencia de interrupciones generada por las teclas que tienen asociado un led en los AT. función no soportada si_expandido . en la práctica todas ellas normalmente están disponibles también en cualquier máquina más antigua si tiene instalado un KEYB eficiente.). Además.1200h no_expandido . Sin embargo. en el momento más insospechado puede producirse una INT 9 con el código de rastreo 0FAh.

El DOS convierte esta estructura física de tres parámetros a otra: el número de sector lógico. 6.6. los sectores lógicos se relacionan con la estructura física por la siguiente fórmula: Sector lógico = (sector_BIOS . . una para cada lado del disco.. dicho cambio se verá reflejado en los led’s cuando el usuario pulse una tecla o el programa lea el teclado con cualquier función -en la práctica. el DOS recorre el disco empezando la pista 0 (la exterior. Los discos son el principal medio de almacenamiento externo de los ordenadores compatibles. por tanto: el número de cabezas.LOS DISCOS. 8 y 0Ah del DOS. varios cabezales podrían -hipotéticamente. o discos duros -fijos-. después pasa al siguiente cilindro. 7. El DOS utiliza las funciones BIOS. los sectores tienen un tamaño de 512 bytes (tanto en discos duros como en disquetes) que es difícil cambiar (aunque no imposible). De esta manera.5. 7. El término cilindro i hace referencia a la totalidad de las pistas i de todas las caras. Constan básicamente de una superficie magnética circular dividida en pistas concéntricas. Los tres parámetros comunes a todos los discos son.1. que se numera a partir de 0 (los sectores físicos les denominaremos a partir de ahora sectores BIOS para distinguirlos de los sectores lógicos del DOS). . NUM LOCK o SCROLL LOCK por el simple procedimiento de alterar el bit correspondiente en 40h:17h. Por último indicar que en los AT se puede modificar el estado de CAPS LOCK.ARQUITECTURA DEL PC. 7. salvo contadas excepciones.leer bloques de información consecutivos simultáneamente. Bajo DOS. podrían empezar y . para aplicar esta técnica es aconsejable verificar que se trata de un AT porque en los PC/XT el led -si existe. Pueden ser unidades de disco flexible. El acceso al teclado a alto nivel puede realizarse a través de las funciones 1. no desde una interrupción periódica-.ESTRUCTURA FISICA. Realmente. cada una de las cuales se subdivide a su vez en cierto número de sectores de tamaño fijo. considerándolo como dispositivo de entrada estándar.no se actualiza y pasa a indicar una información incorrecta. Como normalmente se emplean ambas caras de la superficie. esto es así porque en la práctica las particiones suelen empezar y acabar ocupando cilindros enteros y exactos (aunque en realidad.6. si devuelven un 0.1) + cara * SECTPISTA + cilindro * SECTPISTA * NUMCAB . el control de los led lo lleva la propia circuitería del teclado de manera independiente al ordenador. . luego avanza una cara y recorre de nuevo todos los sectores.ALTO NIVEL. AT Y PS/2 BAJO DOS 125 de reconocimiento al mismo -por tanto.. Los sectores se numeran a partir de 1. Todo esto desaconseja la lectura del puerto del teclado desde cualquier otro sitio que no sea INT 9. Sin embargo. se trata de una tecla especial y la siguiente lectura devuelve el código secundario. Algunas de estas funciones. removibles. cada una de las cuales dispone de un conjunto de sectores lógicos numerados a partir de 0 y un factor de compensación propio para la fórmula. el de pistas y el de sectores. En los disquetes. ya que éstos pueden estar divididos en varias particiones y la que usa el DOS puede no estar al principio del mismo. Para un disco de SECTPISTA sectores BIOS por pista y NUMCAB cabezas. 7. En general. y repite de nuevo el proceso. recorriendo todos los sectores. de manera casi instantánea-. pero en los discos duros se resta un cierto factor de compensación X1.3.X1 Es decir. y dada la arquitectura de la tabla de partición. la unidad más elemental posee en la actualidad dos cabezas de lectura/escritura. Las siguientes fórmulas 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 partición del DOS no suele empezar en el cilindro 0 (reservado en gran parte para la tabla de particiones) sino más bien en el 1 ó en otro posterior (cuando hay más particiones antes que la del DOS) será necesario añadir un cierto valor adicional de compensación X2 a la última fórmula para calcular el cilindro efectivo. X1=0. un disco duro dividido en varias particiones de tipo DOS determina varias unidades lógicas de disco. en los XT. la más alejada del centro) y por la cara o cabezal 0. mientras que las pistas y las caras lo hacen desde 0.

con lo que toda la primera pista física del disco duro está vacía. 6:BIGDOS (más de 32Mb). lo carga y le entrega el control. COMMAND. Realmente. en terminos absolutos. la segunda entrada apunta al siguiente dispositivo (caso de existir) o es 0 (no hay más dispositivos). que indica que la tabla es válida. Lugar ideal para virus. 7. expresado en sectores. 6. El DOS 4. byte 7: parte baja del número de cilindro de fin de la partición. no siendo preciso particionarlos para compartirlos con varios sistemas operativos. 0Ah: OS/2 Boot Manager. El programa ubicado en el sector de arranque busca el fichero oculto del sistema IBMBIO. 82h Linux swap. 80h en la de arranque. 4-FAT16). hay cierta información útil acerca de las características del disco o partición. byte 2: bits 0 al 5: sector de inicio de la partición. byte 4: tipo de partición. SECTOR 1. ubicada en el offset 1FEh. al final de las cuatro está la marca 0AA55h. 7: parte alta del número de cilindro.SYS.CABEZA 0. puede llegar a ser compleja: practíquese con un buen editor de disco para aprender más (ej. ese programa realiza una tarea muy sencilla: consulta la tabla de particiones ubicada en ese mismo sector.. Sin embargo.. o bien el código de la tabla de particiones de los discos duros. 83h: Linux native.126 EL UNIVERSO DIGITAL DEL IBM PC. byte 6: bits 0 al 5: sector de fin de la partición. la arquitectura global de las particiones de un equipo (en particular si tiene más de 4. pero no es frecuente).0 ó superior. El programa contenido en este fichero cargará a su vez IBMDOS. 5: DOS Extendida. AT Y PS/2 acabar no sólo en un determinado cilindro sino también en cierto sector y cara del disco. Formato del sector de arranque: En el sector de arranque.COM o IO. Existen también código de particiones sofisticado que permite seleccionar una de las 4 particiones manteniendo pulsada una tecla en el arranque. 4: DOS-16 (FAT 16 bits).SYS. a continuación carga el sector lógico 0 de esa partición (sector de arranque) y lo ejecuta.COM o MSDOS. En los disquetes no existe este paso intermedio: el sector físico 0 del disquete. determina cuál es la partición activa y dónde empieza y acaba.0 y posteriores eliminaron la limitación de los 32 Mb en las particiones y el software actual. colocando una falsa tabla de partición que muestre un menú en pantalla y cargue después la partición de verdad. bytes 12 al 15: Doble palabra con el tamaño de esa partición en sectores. bytes 8 al 11: Doble palabra que indica el sector relativo (en todo el disco) en que comienza la partición. Los 16 bytes que la forman se interpretan como indica el cuadro de la derecha: byte 0: 0 para partición inactiva. además del sencillo programa de puesta en marcha del sistema. permitiendo también más de 4 particiones. en el que no hay código de programa sino. 0A5h: FreeBSD o BSD/386. 0Bh: 32-bit FAT Win95 (0Ch con LBA). 7: OS/2 HPFS ó WinNT NTFS. no da problemas con los discos de más de 32 Mb. es ya el sector de arranque y no el de partición. Por ello. Esto es así porque los disquetes contienen poca información y son baratos. byte 1: cabeza donde comienza la partición. El primer sector físico de todos los discos contiene información especial (el sector_BIOS 1 del cilindro 0 y cabezal 0). byte 5: cabeza donde termina la partición. algunos fabricantes han utilizado esta interesante característica para mejorar el arranque. estas maniobras suelen reducir la compatibilidad. byte 3: parte baja del número de cilindro de inicio de la partición. las más comunes son 0: No usada. contiene un pequeño programa que se encarga de poner en marcha el ordenador: es el sector de arranque de los disquetes. una lista de dispositivos. una mezcla de sistemas operativos y/o varios discos duros).COM). en su lugar. Tanto en disquetes como en discos duros. Las particiones extendidas llevan su propio sector de partición adicional. el cual a su vez cargará finalmente el intérprete de comandos (normalmente. Los primeros 3 bytes no son significativos: contienen el código de operación de una instrucción JMP que salta a donde realmente comienza el código. . cada partición de las 4 posibles ocupa 16 bytes. 81h Linux. Formato de la tabla de partición de los discos duros: Esta tabla comienza en un offset 1BEh del sector (al principio está el código ejecutable). Hay dos entradas por cada dispositivo: la primera indica el tipo (1-FAT12. X1 y X2 se obtienen consultando e interpretando la tabla de particiones o el sector de arranque. 0Eh y 0Fh (como 06 y 05 pero con LBA). en discos de más de 32 ó 40 Mb lo normal es instalar DOS 4. En este último caso. PISTA 0. Formato de la TABLA DE PARTICIÓN Habitualmente.2. 6. 0F2h: partición secundaria (no estudiada en este libro).6. 1: DOS-12 (FAT 12 bits). ¡lo que se puede hacer con 400 bytes de código!. . 7: parte alta del número de cilindro. el DISKEDIT de las Norton Utilities o las PC-Tools). ya actualizado. las particiones suelen empezar en el segundo cabezal del cilindro 0. sin tener que andar ejecutando FDISK para seleccionar la partición activa.

3. 40 pistas) discos de 5¼-1.ARQUITECTURA DEL PC. 512. En el sector de arranque del disquete está contenido el BPB (Bios Parameter Block) que analizaremos más tarde. Normalmente consta de varios sectores (ver offset 13 del sector de arranque): dos en un disquete de 360 Kb. dónde están los sectores defectuosos. 9 sectores/pista. a costa de memoria. hasta la 6. 40 pistas) discos de 5¼-320 Kb (2 caras. conviene hacer un pequeño paréntesis y explicar el concepto de cluster: un cluster es la unidad mínima de información a la que accede el DOS.0): puede ser "FAT12 " o "FAT16 ". 40 pistas) discos de 5¼-180 Kb (1 cara. 9 sectores/pista. Sectores por cluster (ej. 36 sectores/pista. ej.0 y 6. AT Y PS/2 BAJO DOS 127 aunque conviene que dicha instrucción de salto esté al principio del sector de arranque para que algunos sistemas validen dicho sector (es válido un salto corto seguido de NOP o un salto completo de 3 bytes). 2) Sectores reservados al principio (1 en diquettes) Número de copias de la FAT (2 normalmente) Número de entradas al directorio raíz (112 en discos de 360 Kb) Número total de sectores del disco (0 en discos de más de 32 Mb) Byte de tipo de disco (véase tabla más adelante) Número de sectores ocupados por cada FAT Número de sectores por pista Número de cabezas (2 en disquetes de doble cara) Número de sectores especiales reservados.0 (marca de validación que indica que los bytes ubicados desde el offset 36 al offset 61 están definidos). Por otra parte. 9 sectores/pista.0 incluida.0). 80 pistas) discos de 3½-720 Kb (2 caras.0). aunque tanto el DOS 4. valor 29h desde DOS 4. ya que es el área más importante del disco de la que dependen todos los demás datos almacenados en él.31).sys pueden hacer casi milagros. . A partir del cuarto (offset 3) se puede encontrar la información válida.88 Mb (2 caras. Reservado. ello mejoraría notablemente el tiempo de acceso medio. 8 sectores/pista. Título del disco (desde DOS 4. etc. Aunque cierto es que los cachés de disco y los buffers del config. 80 pistas) discos de 3½-2.X siguen empleando además las tradicionales etiquetas de volumen.. Nota: sólo se debe considerar la primera mitad de esta doble palabra en versiones del sistema 3.6. . Número de unidad física (a partir del DOS 4. Número total de sectores del disco en discos de más de 32 Mb (esta información sólo debe obtenerse de aquí si la palabra ubicada en el offset 19 es cero). es un DOS 3. 80 pistas) discos duros y algunos virtuales discos de 3½-1.30: no hacer caso de lo que dice este byte para identificar los discos. aunque no lo consigue en muchos casos dada la ilógica utilización que se ha hecho de él. 80 pistas) restantes formatos de disco Tipos de Discos 7. aparecen en el disco una serie de sectores que constituyen la Tabla de Localización de Ficheros (File Alocation Table o FAT). cuáles ocupadas. que en todas sus versiones.. hubiera sido mejor elección haberla colocado en el centro del disco: dada la frecuencia de los accesos a la misma. 18 sectores/pista. por defecto se inicializa con "NO NAME ". "IBM 3.0 como el 5. 8 sectores/pista. muchos programas de chequeo de disco no se molestan en verificar si ambas FAT son idénticas (empezando por algunas versiones de CHKDSK).LA FAT. En general. Sistema de ficheros (a partir de DOS 4.. Consiste en una especie de mapa que indica qué zonas del disco están libres. 15 sectores/pista.0).2 Mb (2 caras. Después del sector de arranque. No deja de resultar extraño que ambas copias de la FAT estén físicamente consecutivas en el disco: si accidentalmente se estropeara una de ellas (por ejemplo. Antes de seguir adelante. offset offset offset offset offset offset offset offset offset offset offset offset 3 11 13 14 16 17 19 21 22 24 26 28 (8 (1 (1 (1 (1 (1 (1 (1 (1 (1 (1 (2 bytes): palabra): byte): palabra): byte): palabra): palabra): byte): palabra): palabra): palabra): palabras): Identificación del sistema (ej. de cara a localizar los diferentes fragmentos de los ficheros. Número de serie del disco (a partir de DOS 4. El valor de este campo depende de la posición relativa que ocupe la partición dentro del disco duro (será 0 en los disquetes).3") Bytes por sector. Normalmente hay dos copias consecutivas de la FAT (véase el offset 16 del sector de arranque). offset 32 (2 palabras): offset 36 (1 byte): offset 37 (1 byte): offset 38 (1 byte): offset 39 (2 palabras): offset 43 (11 bytes): offset 54 (8 bytes): Formato del SECTOR DE ARRANQUE El byte del tipo de disco (offset 21) intenta identificar el tipo de disco. 40 pistas) discos de 5¼-360 Kb (2 caras. desde el punto de vista lógico. rayando con un bolígrafo el disco) lo más normal es que la otra también resultara dañada.44 Mb (2 caras. La recomendación es hacer lo que viene haciendo el DOS desde la 3. este valor ha de sumarse al del número de sector del DOS antes de traducirlo a un número de sector de la BIOS. La única excepción tal vez sea el valor 0F8h que identifica a los dispositivos no removibles: 0FEh 0FFh 0FCh 0FDh 0F9h 0F9h 0F8h 0F0h 0F0h 0F0h - discos de 5¼-160 Kb (1 cara.30 o anteriores (no hay problemas con DR-DOS.

un byte) sólo podría haber unos 250 clusters en el disco. AT Y PS/2 uno en un disquete de alta densidad. 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. Valor FFD FFF 003 005 FF7 006 FFF 013 . Sin embargo. Los restantes bits de las dos primeras entradas suelen estar todos a 1. cluster no utilizable: valores 0FF5 al 0FF6h (ó 0FFF5 al 0FFF6h).. y entre 4 y 16 -normalmente. Los cluster se numeran a partir de 2. los que ocupa la FAT y los empleados por el directorio raíz (que se verá más adelante). después habrá que volver a grabar la FAT en disco. indicando su estado: cluster libre: valor 0 cluster defectuoso: valores 0FF7h (ó 0FFF7h). en un cierto número de clusters. por ejemplo. ello haría que la propia FAT ocupase demasiado espacio en el disco.en un disco duro. A continuación se listan dos rutinas que permiten acceder a una FAT de 12 bits previamente cargada en memoria. El disco queda dividido.. tantas veces como copias de la misma existan en éste. Por ello. Por ello. con objeto de consultar o modificar alguna entrada. lo cual no es un requerimiento demasiado costoso. correspondiente al sector de arranque). Por tanto. . 3. las dos FAT y -como veremos después. Puede ser válida. la FAT y el directorio. la siguiente FAT de 12 bits habiendo un fichero A que ocupe los clusters 2. Los ficheros en disco no siempre ocupan posiciones contiguas: normalmente están más o menos fragmentados debido a que se aprovechan los huecos dejados por otros ficheros borrados..5 = 6128 bytes.128 EL UNIVERSO DIGITAL DEL IBM PC. en los disquetes se emplean FAT’s de 12 bits (1 byte y medio): para un programa en código máquina ello no ralentiza los cálculos (aunque al ser humano no se le de muy bien trabajar con medios bytes). Empleando FAT’s de 16 bits se podrían hacer clusters incluso de tamaño menor que el sector (menos de 512 bytes). existiendo normalmente un valor 0FFFh ó 0FFFFh en el último cluster para señalar el final (del 0FF8h al 0FFEh y del 0FFF8h al 0FFFEh no se emplean). Interpretación El disco es de tipo 0FDh (despreciar restantes bits) Entrada no utilizada El siguiente cluster del fichero A es el 3 El siguiente cluster del fichero A es el 5 Cluster defectuoso El siguiente cluster del fichero A es el 6 Este es el último cluster del fichero A El siguiente cluster del fichero B es el 013 Como se ve.que inicia una cadena tan larga como la longitud del mismo (expresada en clusters). 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. Los clusters hacen referencia exclusiva a la zona de datos: el área que va detrás del sector de arranque. En la práctica.2 Mb ello significaría que la unidad mínima de información sería 1200/250 = 5 Kb: el fichero más pequeño (de 1 byte) ocuparía ¡5 Kb!.por cada cluster. por tanto. último cluster del fichero: valor 0FF8 al 0FFFh (ó 0FFF8h al 0FFFFh).. hay 354 clusters (numerados de 2 a 355) y los 6 Kb misteriosos que faltan son el sector de arranque. otro valor: puntero al siguiente cluster del fichero. habida cuenta de que no puede ocupar más de 4085 * 1.el directorio raíz. 5 y 6: Elemento de la FAT 0 1 2 3 4 5 6 7 . Evidentemente. ya que las dos primeras entradas en la FAT están reservadas para el sistema. Las rutinas necesitan que la FAT esté completamente cargada en memoria. En un disco de 1. a continuación se divide ese número de sectores de datos resultante por el número de sectores por cluster. cada fichero consta de un cluster inicial indicado en la entrada del directorio -como se verá. así como qué zonas están aún disponibles y cuáles son defectuosas en el mismo. con clusters de 1 Kb y 354 Kb libres para datos. ha de restarse del número total de sectores la cifra correspondiente al número de sectores reservados (normalmente 1 en los disquetes. Consultando la FAT se puede determinar la ubicación de los fragmentos en que están físicamente divididos los ficheros en los discos. aprovechando más el espacio del disco. Para determinar el número de clusters del disco. La FAT es realmente un mapa que contiene 12 ó 16 bits -como veremos. en un disquete de 360 Kb. El hecho de emplear FAT’s de 12 bits es debido a que con menos bits (ej. de ahí el auge de los programas que compactan los discos con objeto de acelerar el acceso a los datos..

4 DX.00001111b BX AX . en futuros disquetes de elevada capacidad sea necesario pasar a una FAT de 16 bits.. Esto puede verificarse fácilmente creando discos virtuales con 4084/4085 clusters.0) operan con una FAT de 16 bits en discos de 4085 clusters (inclusive) en adelante. . esto es. En los discos de 360 Kb. preservar registros . ..0 y 6. ************ Leer un elemento de una FAT de 12 bits . Otra solución es procurar no crear discos de ese número crítico de clusters. En algunos discos duros se puede determinar también el tipo de FAT consultando la tabla de particiones. Por desgracia. preservar la otra entrada CX CL. . El tamaño y ubicación del directorio pueden obtenerse del sector de arranque. esto es. salvo en MS-DOS 3. ya que el fabricante olvidó incluir un byte de identificación al efecto. que es la usada por todos los discos duros excepto el de 10 Mb del XT original de IBM. retorno sin alterar registros . los comandos CHKDSK del sistema consideran erróneamente que los discos de 4085.6. preservar registros .0 y DR-DOS 5.EL DIRECTORIO RAÍZ. 4. DS:BX = FAT completamente cargada en memoria .1 BX. BX AX CF BX AX = = = = = BX + cluster cluster / 2 1 si impar BX + cluster * 1. Inmediatamente después de la FAT y su(s) réplica(s) de seguridad viene el directorio raíz.[BX] peek_fat_par CX CL. retornar sólo DX modificado peek_fat_par: peek_fat poke_fat Tal vez. Sin embargo. Debe tener en cuenta el lector que manipular una FAT sin conocer su tipo supone destrozar la información almacenada en el disco.). DX = nuevo valor de dicho elemento poke_fat PROC PUSH PUSH PUSH ADD SHR PUSHF ADD MOV POPF JC AND JMP AND PUSH MOV SHL POP OR MOV POP POP POP RET ENDP AX BX DX BX.AX AX. ************ Escribir un elemento en una FAT de 12 bits . . ya que es mucho más grave tener problemas con el DOS que con CHKDSK.3..4. DX=DX/16: si DX=xyz0.DX . copiando algunos ficheros y mirando la FAT con algún programa de utilidad (a simple vista se distingue si las entradas son de 12 ó 16 bits). 4086 y 4087 clusters ¡poseen una FAT de 12 bits!. pese a este problema de CHKDSK. colocarlo: 4 bits a la izda CX AX. Sin embargo. .CL . .AX AX. DS:BX = FAT completamente cargada en memoria . Sin embargo. El directorio consta de 32 bytes por cada fichero/subdirectorio (los subdirectorios no son más que un tipo especial de fichero). aparecida con el DOS 3. 3.3 y en DR-DOS 6.1111000000000000b . Detrás de éste ya vienen los clusters conteniendo la información del disco propiamente dicha. los discos normales no están por ahora en la frontera crítica entre la FAT de 12 y la de 16 bits. como se vio al principio. borrar posible dígito izdo . .5 poke_fat_imp: poke_fat_ok: poke_fat_imp AX. ¿dónde se almacena el inicio de esa cadena.0. o confiar que el usuario no ejecute el casi olvidado CHKDSK sobre ellos. En principio. y en todas es por desgracia incorrecta (unos dicen que la FAT 16 comienza a partir de 4078 clusters. el directorio se extiende a lo largo de 7 sectores (3584 bytes = 112 entradas como máximo).CL CX DH. aunque no es el método más conveniente. 5. no existe ninguna manera sencilla de averiguar el tipo de FAT de un disco. que se corresponde con un disco de 4084 clusters (numerados de 2 a 4085)..1.0. BX AX CF BX = = = = BX + cluster cluster / 2 0 si par BX + cluster * 1.AX AX. La información almacenada en los 32 bytes es la siguiente: .AX . tampoco hay que tener tanto miedo: lo que sí puede resultar peligroso es llegar al extremo de preguntar al usuario el tipo de FAT. otros confunden el número de clusters con el número más alto de cluster. preservar la otra entrada poke_fat_ok AX.. dado que 4087 (0FF7h) es la marca de cluster defectuoso en una FAT de 12 bits y ¡en ningún caso podría ser un número de cluster cualquiera!.1 BX. lo cual resulta además completamente absurdo. 7.0. aunque con los discos virtuales sí se pueden crear unidades con esos tamaños críticos: la casi totalidad de los discos virtuales del mercado tienen problemas en estos casos. AT Y PS/2 BAJO DOS 129 .[BX] . por ejemplo.4 DX. Ahora puede surgir la pregunta: si la FAT mantiene una cadena que indica cómo está distribuido un fichero en el disco. los discos con más de 4084 clusters han de ser diseñados con una FAT de 16 bit. la primera entrada en la FAT del fichero?.5 palabra con dato 12 bits . Por fortuna.0000000000001111b .ARQUITECTURA DEL PC. Salida: DX = valor de dicho elemento peek_fat PROC PUSH PUSH ADD SHR PUSHF ADD MOV POPF JNC PUSH MOV SHR POP AND POP POP RET ENDP AX BX BX. otros que a partir de 4086. nuevo valor en la FAT DX BX AX . La documentación publicada es contradictoria en las diversas fuentes que he consultado. Entrada: AX = posición de dicho elemento . todas las versiones del DOS comprobadas (MS-DOS 3. Con una FAT de 12 bits el nº de cluster más alto posible es 4085. a partir de 4086 como número de cluster más alto. DX=0xyz . «mezclar» [BX].AX DX. Entrada: AX = posición de dicho elemento .

físicamente.) y el (. Sus motivos tendrá. Dentro de cada subdirectorio hay al menos dos entradas especiales: un fichero con un nombre punto (. etc. El subdirectorio..)) de tamaño cero que paradójicamente llenarían el disco (recordar que cada entrada al directorio ocupa 32 bytes).) que referencia al propio subdirectorio -que así puede autolocalizarse. que son nada más y nada menos que otras entradas de directorio para otros ficheros. . en un disco de 360 Kb (354 Kb libres) se puede crear un subdirectorio y en él se pueden introducir. de lo contrario apuntará al primer cluster del fichero subdirectorio padre. la capacidad del disco-. que podríamos denominar «subdirectorio raíz». .. esto sucede con cualquier otro fichero. tal vez sea una pena que el disco no conste de un único «fichero raíz» privilegiado de directorio.6. en un subdirectorio puede haber una gran cantidad de ficheros (muchos más de 112 ó 500) sin problemas. 224.) es un 0. Considerando el nombre completo de un fichero. pero en la práctica el DOS no se ocupa de estas pequeñeces.) y sería más lógico que una ristra de sectores. Por ello. borrando el anterior para recuperar el espacio libre). retroceder cuanto se desee por el árbol de directorios sin necesidad de que todos los caminos partan del raíz. varios bits pueden estar activos a un tiempo. AT Y PS/2 offset offset offset offset offset offset offset offset 0 (8 bytes): 8 (3 bytes): 11 (1 byte): 12 (10 bytes): 22 (2 bytes): 24 (2 bytes): 26 (2 bytes): 28 (4 bytes): Nombre del fichero Extensión del nombre del fichero Byte de atributos Reservado (PASSWORD cifrada DR-DOS) Hora*2048 + minutos*32 + segundos/2 (año-1980)*512 + mes*32 + día Primera entrada en la FAT Tamaño del fichero en bytes ENTRADA DE DIRECTORIO activo si el fichero es de sólo lectura activo si el fichero es oculto activo si el fichero es de sistema activo si esa entrada de directorio es la etiqueta de volumen bit 4: activo si es un subdirectorio bit 5: bit de archivo usado por BACKUP y RESTORE bits 6. Normalmente nadie suele cometer esos excesos. el proceso a seguir para localizarlo en el disco es ir recorriendo los ficheros subdirectorio de uno en uno.. En un mismo disco sólo puede haber una entrada con el bit 3 activo. en caso extremo. Cada fichero que se crea en un subdirectorio aumenta el tamaño del fichero subdirectorio en 32 bytes. es a su vez un fichero un tanto especial: contiene datos binarios . en la posición correspondiente. Ello permitiría también un número ilimitado de entradas (en vez de 112. Sin embargo. de 32 bytes como siempre. un subdirectorio en principio puede ser una simple entrada del directorio raíz. 2Eh (dos puntos consecutivos) se trata de una entrada que referencia a un fichero subdirectorio. Las entradas de tipo subdirectorio (bit 4 del byte de atributos activo) tienen un valor cero en el campo de tamaño (offset 28): el tamaño de un fichero subdirectorio está determinado por el número de entradas que ocupa en la FAT (en la práctica. como el UNIX. el tamaño del fichero subdirectorio debería reducirse. Si empieza por 2Eh (código ASCII del punto (. Por ello..5. quiere decir que ese subdirectorio cuelga del raíz. obtener su punto de entrada en la FAT. lo que indica que el fichero que estuvo ahí ha sido borrado. en este caso se interpretan el nombre y la extensión como un único conjunto de 11 caracteres. 11326 ficheros (más el (. evidentemente. Si en un subdirectorio había demasiados ficheros y se borra una buena parte de los mismos.)) ó por 2Eh.LOS SUBDIRECTORIOS. aunque si no es de directorio en el offset 28 esta información se indica con precisión de bytes). El nombre del fichero puede comenzar por 0E5h.7: no utilizados BYTE DE ATRIBUTOS bit bit bit bit 0: 1: 2: 3: En el byte de atributos. es una reliquia heredada del CP/M (los ficheros ocultos del sistema lo tienen activo). Dicho sea de paso. Como hemos visto.y otro con doble punto (. 7. El tamaño de un fichero subdirectorio es ilimitado -sin exceder. hasta llegar al fichero subdirectorio donde está registrado el fichero y.. con toda la trayectoria de directorios.siendo posible.) que referencia al directorio padre -del que cuelga. El atributo de sistema no tiene un significado en particular. Si la primera entrada en la FAT del fichero (. esta peculiar circunstancia también aparece en otros sistemas operativos. además.130 EL UNIVERSO DIGITAL DEL IBM PC. gracias a ello. habida cuenta de que los ficheros subdirectorio son unos pequeños islotes en el gran océano disco (los usuarios más tacaños siempre pueden optar por crear un nuevo subdirectorio y mover todos los ficheros a él.

Esta variable almacena. la verdad) y si soporta varias velocidades de transferencia. entre otros. Estado de los motores.ARQUITECTURA DEL PC.EL BPB Y DPB. 7 bytes almacenan el resultado de la última operación de disquete o disco duro. Para obtener el DPB de una unidad determinada. La BIOS se apoya en varias variables ubicadas en el segmento 40h de la memoria. según se haya logrado determinar el tipo de soporte). El BPB es una pieza vital en los controladores de dispositivo de bloques. Número de cilindro en curso en la unidad A. Estado de la última operación: se actualiza tras cada acceso al disco.A partir del DOS 4.0: offset 13 DW sectores_por_pista offset 15 DW número_de_cabezas offset 17 DD número_de_sectores_ocultos -. por lo que a continuación 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 número_de_FATs offset 6 DW número_de_entradas_en_el_directorio_raíz offset 8 DW número_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 -. establecimiento del tipo de soporte para formateo. El funcionamiento del disquete se controla a través de funciones de la INT 13h. AT Y PS/2 BAJO DOS 131 7. algunas de estas últimas funciones no están disponibles en las máquinas PC/XT. En esta variable se indica. el único que estaba girando) son detenidos. ya que las aplicaciones desarrolladas bajo DOS de una u otra manera habrán de cooperar con la BIOS por razones de compatibilidad (o al menos respetar ciertas especificaciones). Get Drive Parameter Block (indocumentada). escritura y verificación de sectores. Cuenta para la detención del motor. y el resultado de los intentos de la BIOS (la velocidad puede ser correcta o no.LA BIOS Y LOS DISQUETES. Estado del soporte en la unidad B al inicio de la operación. Estas variables son las siguientes (para más información. Get List of Lists.6. formateo de pistas. Control del soporte (AT). envío del comando specify y recalibramiento del cabezal). Resulta interesante conocer el comportamiento de la BIOS en relación a los disquetes. Las funciones soportadas por esta interrupción son: reset del sistema de disco (reset del controlador de disquetes. consulta del estado del disco (obtener resultado de la última operación). obtención de información del disco y las disqueteras. una estructura similar con más información útil. El BPB (Bios Parameter Block) es una estructura de datos que contiene información relativa a la unidad de disco.0 (más bien DOS 3.A partir del DOS 3. Estado del soporte en la unidad A al inicio de la operación. Se indica la velocidad de transferencia a emplear en el disquete introducido en esta unidad. también indocumentada).. puede utilizarse la función 32h del DOS. la última velocidad de transferencia seleccionada. . Esta variable indica varias cosas: si se ha producido una interrupción de disquete.31) offset 21 DD número_de_sectores (unidades con direccionamiento de sector de 32 bits) offset 25 DB 6 DUP (?) (6 bytes no documentados) offset 31 DW número_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). cuando llega a 0 todos los motores de las disqueteras (realmente. aunque esta interrupción por lo general acaba llamando a la INT 40h que es quien realmente gestiona el disco en las BIOS modernas de AT. Dejar el motor girando unos segundos tras la última operación evita tener que esperar a que el motor acelere antes de la siguiente (si esta llega poco después). indicando los errores producidos (0 = ninguno).. . pero para la unidad B. o si es preciso recalibrar alguna disquetera debido a un reset anterior. la cadena de DPBs del DOS puede recorrerse a partir del primer DPB (obtenido con la función 52h del DOS. la última unidad que fue seleccionada y la operación en curso sobre la misma. la BIOS utiliza también una tabla de parámetros apuntada por la INT 1Eh. Información del controlador de disquete (AT). Este byte es decrementado por la interrupción periódica del temporizador. Se trata de los 7 bytes que devuelve el NEC765 tras los principales comandos.7. detección del cambio de disco. Estado del soporte en la unidad A.6. Número de cilindro en curso en la unidad B. lectura. como veremos en un futuro capítulo. además del estado de los motores de las 4 posibles disqueteras (si están encendidos o no). Lo mismo que el byte anterior. Se indica si la unidad soporta 80 cilindros (pues sí.6. A partir de esta dirección. 7. Byte 40h:41h Bytes 40h:42h Byte 40h:8Bh Byte 40h:8Fh Byte 40h:90h Byte Byte Byte Byte Byte 40h:91h 40h:92h 40h:93h 40h:94h 40h:95h Además de estas variables. Los valores para programar ciertas características del FDC según el tipo de disco pueden variar. consultar el apéndice al final del libro): Byte 40h:3Eh Byte 40h:3Fh Byte 40h:40h Estado de recalibramiento del disquete. si precisa o no saltos dobles del cabezal (caso de los disquetes de 40 cilindros en unidades de 80). aunque .

de cara a seleccionar la letra de unidad que se desea asignar al floptical.44M (aunque a menudo no los de 2. 0Fh = 240 ó 480 ms). Configurándolo como A: se puede incluso arrancar desde un disquete de éstos. Llamando al DOS para leer un sector lógico determinado en la unidad que se le indique. Una excepción quizá la constituye el valor de GAP empleado al formatear. byte 4: byte 5: byte byte byte byte byte Sectores por pista. en la posición 0F000h:0EFC7h de todas las BIOS compatibles (prácticamente el 100%). byte 1: byte 2: byte 3: El tiempo de estabilización del cabezal es el tiempo que hay que esperar tras mover el cabezal al cilindro adecuado. Llamando al DOS para acceder a un fichero por su nombre y ruta. Por ejemplo. Del mismo modo. aunque el DOS suele desviarla a la RAM para poder actualizarla. Es el byte 2 del comando ’Specify’: los bits 7.0 y posteriores puede formatear los disquetes floptical. lo que permite elevar notablemente el número de pistas.EJEMPLO DE ACCESO AL DISCO A ALTO NIVEL. la flexibilidad del sistema de disco es extraordinaria y suele responder favorablemente con unos altísimos niveles de tolerancia en las temporizaciones. Las tarjetas controladoras suelen permitir un cierto grado de flexibilidad.6. 8: Byte de relleno al formatear (normalmente 0F6h).. con lo que tarda cerca de 30-45 minutos en inicializarlos. Dicha tabla está inicialmente en la ROM. Tics de reloj (pulsos de la interrupción 8) que transcurren tras el acceso hasta que se para el motor. las unidades de 20 Mb parecen estar equipadas con 753 cilindros y 27 sectores/pista. . 10: Tiempo de aceleración del motor (en unidades de 1/8 de segundo). El tipo de FAT asignado puede ser seleccionado por el usuario (12 ó 16 bits). siendo mejor el más alto por razones de compatibilidad: 1) 2) 3) 4) Programando directamente el controlador de disquetes/disco duro para acceder a sectores físicos. así como otros parámetros técnicos (tamaño de clusters.DISQUETES FLOPTICAL 3½ DE 20 MB. Los programas de utilidad que acompañan estas unidades realizan todas estas tareas en unos 4 minutos. al ser un parámetro demasiado importante. Longitud del GAP entre sectores (normalmente 2Ah en unidades de 5¼ y 1Bh en las de 3½). . que indica el step rate (el tiempo de acceso cilindro-cilindro. nada más ponerlo en marcha. Como ya vienen formateados de fábrica.8. 2=512. En cualquier caso. hasta que éste se asiente.44M (750 Kbit/seg frente a 500 Kbit/seg). 9: Tiempo de estabilización del cabezal en ms. de cierta cara y cierto cilindro.9. Se puede acceder a varios niveles. 7. que también admiten los de 720K y 1. Las unidades que soportan estos disquetes. 84 en 5¼-HD y 108 en 3½-HD). . Llamando a la BIOS para leer cierto sector. AT Y PS/2 algunos son comunes. 3=1024). 1=256. etc. En general. el tiempo de aceleración del motor (byte 10) es el tiempo que se espera a que el motor adquiera la velocidad de rotación correcta. esta breve pausa es establecida en 25 milisegundos en la BIOS del PC original. pese a estos valores usuales. También se puede verificar la superficie magnética para detectar posibles sectores defectuosos. 7. el sentido común nos permite deducir que esto no puede ser así. Bytes por sector (0=128. Esta tabla determina las principales características de operación del disco. en realidad basta con añadirles un sector de arranque e inicializar la FAT y el directorio raíz. El formato de la misma es: byte 0: Se corresponde con el byte 1 del comando ’Specify’ del 765. con objeto de garantizar el éxito de las operaciones futuras. pero lo hace a bajo nivel. es norma general intentar tres veces el acceso a disco (con resets de por medio) hasta considerar que un error es real. El FORMAT del DOS 5. 7: Longitud del GAP 3 al formatear (80 en 5¼ y 3½-DD.132 EL UNIVERSO DIGITAL DEL IBM PC.88M) trabajan con controladoras SCSI e incorporan una BIOS propia para dar soporte a estos dispositivos. 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.1 indican el head load time (normalmente 01h = 2 ó 4 ms) y el bit 0 suele estar a 0 para indicar modo DMA. aunque otras BIOS y el propio DOS suelen bajarlo a 15. a menudo es 0Dh = 3 ó 6 ms) y el head unload time (normalmente. Aunque en el sector de arranque indica que posee 251 cilindros y 6 cabezales. El secreto de estos disquetes está en el posicionamiento óptico del cabezal. 6: Longitud de sector (ignorado si el byte 3 no es 0).).6.

0 . . . las opciones (3) y (4) son las más cómodas e interesantes. handle de control del fichero 13.13.0 cerrar 20h .3Dh 21h error handle.9 . . . .2048 trocito BX. al final del programa se definen dos buffers de datos de 80 y 2048 bytes. bytes leídos realmente no hay nada que imprimir preservarlos imprimir buffer . llamar al DOS DX. por razones de eficiencia se trabaja con fracciones para evitar el empleo de coma flotante. . Esto sólo sucede.AX BX. mensaje AH.2048 DX. * * * ******************************************************************** SEGMENT ASSUME CS:mira. Si no se desea que estos buffers alarguen el tamaño del programa ejecutable. el (2) es útil para acceder a otras particiones de otros sistemas operativos o a disquetes formateados por otros sistemas operativos.10.60 .2 21h BX imprime AX AX. TAMPOCO reserva espacio efectivo para esas variables. En general. al contrario que el LINK de Microsoft. carácter a carácter ir llamando al servicio 2 del DOS para imprimir en pantalla siguiente carácter acabar caracteres recuperar nº de bytes leídos ¿leidos 2048 bytes? sí. cargándolo por fragmentos y apoyándose en las funciones del DOS que se comentan en el apéndice que resume las funciones del sistema operativo.10 . buffer para leer desde el teclado 2048 DUP (0) . . ******************************************************************** * * * MIRA. El funcionamiento del programa se basa en acceder a la FAT y crear una imagen gráfica de la misma. Muchas veces el ensamblador no es suficiente para asegurar la velocidad: la primera versión del programa tardaba 18 segundos en dibujar un mapa en un 386-25. leer otro trocito más código de acceso al fichero cerrar fichero llamar al DOS CF = 1 --> error fin del programa mensaje de error función de impresión llamar al DOS ¿fichero abierto? sí: cerrarlo fin del programa imprime: mira error: . el acceso se realiza a alto nivel pese a tratarse de un programa en ensamblador.EJEMPLO DE ACCESO AL DISCO A BAJO NIVEL."*** Error ***".10. . .0). . . AT Y PS/2 BAJO DOS 133 El método (1) es apropiado para realizar formateos especiales en sistemas de protección anticopia. no más de 60 caracteres AH. pueden definirse de la siguiente manera: fichnom buffer EQU EQU $ $+80 Sin embargo.[fichnom+1] .ASM . . Conviene hacer notar que si en lugar de DUP (0) se coloca DUP (?). llamar al DOS BL.buffer DL. . programa de tipo . ."Nombre del fichero: $" 13.handle AH. el linkador de Borland (TLINK 3. Aunque este número no es entero."$" 80 DUP (0) .buffer AH. . " " " " el disco inicio 7. Para ello. . offset a cadena ASCIIZ nombre modo de lectura función para abrir fichero llamar al DOS CF=1 --> error código de acceso al fichero código de acceso al fichero número de bytes a leer dirección del buffer función para leer del fichero llamar al DOS CF=1 --> error cerrar: 100h .[BX] AH. . Además. -----------. normalmente suele haber más de 2128 bytes libres de memoria tras cargar cualquier programa. .0 AH. . .10. .handle CX. A continuación se muestra un programa de ejemplo que solicita el nombre de un fichero y lo visualiza por pantalla. . Como se puede observar.fichnom . .fallo_txt AH.input_txt .Utilidad para visualizar ficheros de texto. . poner un cero al final DX..AX cerrar AX BX. un disco virtual no existe en absoluto para la BIOS).6. El programa de ejemplo desarrollado requiere un adaptador VGA ya que utiliza el modo de 640 por 480 con 16 colores para obtener una representación gráfica de alta calidad del contenido del disco. . . de lo contrario se pierde la posibilidad de acceder a ciertas unidades (por ejemplo. lógicamente.OFFSET fichnom . apuntar al final BYTE PTR [BX+2].3Fh 21h error . longitud efectiva tecleada BH. en lugar de la tradicional y pobre representación habitual en modo texto. . .0 .fichnom+2 AL. . De todas maneras.datos y variables handle input_txt fallo_txt fichnom buffer mira DW DB DB DB DB ENDS END 0 . dirección para el «input» BYTE PTR [fichnom].10. en la medida de lo posible es conveniente no bajar del nivel (3).ARQUITECTURA DEL PC.9 21h handle. con una rutina escrita en su mayor parte en ensamblador.3Eh 21h error 20h DX. . .COM MOV JCXZ PUSH LEA MOV MOV INT INC LOOP POP CMP JE MOV MOV INT JC INT LEA MOV INT CMP JNE INT CX.10. . calcula cuantos puntos de pantalla debe trazar por cada cluster de disco (utiliza una ventana de 636x326 = 207336 puntos).. se redujo a menos . Paradójicamente. ya que de esta manera el DOS no realiza la comprobación por nosotros (se limita a cargar cualquier programa que quepa en memoria). . . función de impresión 21h . . cuando el DUP (?) está al final del programa y no hay nada más a continuación -ni más código ni datos que no sean DUP (?)-. . DS:mira ORG inicio: LEA MOV INT LEA MOV MOV INT MOV MOV ADD MOV LEA MOV MOV INT JC MOV trocito: MOV MOV LEA MOV INT JC DX. Tras mejorar el algoritmo y optimizar el código en la zona crítica donde se trazan los puntos.. en BX BX. se reprograman los registros de paleta y el DAC de la VGA para elegir colores más atractivos. . . si se procede de esta última manera convendría asegurarse primero de que existen 2128 bytes de memoria libres tras el código del programa. . función de entrada de teclado 21h ..

clusters_malos). int scr_ok. init_video().66 segundos el tiempo necesario (¡314000 puntos por segundo a 25 MHz!). if ((bitfat=farmalloc((long)numclusters))==NULL) salida_error (2).4096). ya que posee una errata por la que falla con unidades de más de 32767 clusters.0x62).peek(0x40. intr (0x10. aviso_espera(). En su lugar. r. /* índices correctos */ /* borde negro */ int existe_vga() /* devolver condición cierta si hay VGA */ { struct REGPACK r. prepara_punto(). Se lee de tres veces para evitar que en un sólo acceso a disco. if (((*modo<=3)||(*modo==7))&&((*scrbuf=farmalloc(4096L))!=NULL)) { *scr_ok=1. *scr_ok=0. leyendas (numclusters.4096). /* VGA naranja */ { 0.0. preservar_pantalla (&scrbuf. &r). r. 16. marco(). dec2str(). if (!getch()) getch(). intr (0x10. &r). 40. &r). huge *fat. &sectfat. &r). char **argv) { sp=HablaSp(). /* VGA amarillo */ { 0. } void init_video() { struct REGPACK r. 0x10. /* VGA verde oscuro */ { 0. carga_fat (fat.r_ax & 0xFF)==0x1A). &clusters_datos. &numsect. r. AT Y PS/2 de 0. 0. else unidad=getdisk(). if ((boot=farmalloc(2048L))==NULL) salida_error (2). int sp. cb. int colorbits) { struct REGPACK r. else movedata(0xb800. int *colorbits) { } /* parámetro /I */ void prepara_paleta() { struct REGPACK r. &numclusters. *colorbits=peek(0x40. &r). if (*modo==7) movedata(0xb000.&scr_ok. int existe_vga(). 63. unsigned long numsect. /********************************************************************/ /* */ /* DMAP 2. 28.134 EL UNIVERSO DIGITAL DEL IBM PC.4096). *cy=peekb(0x40. leyendas(). int *cx. marco(). 0x10) & 0xFFCF | colorbits). 1.r_ax=0x1012. se interpreta la FAT (según sea de 12 ó 16 bits) y se crea otra matriz de tamaño equivalente al número de clusters del disco. intr (0x10. r.r_es=FP_SEG(dac). /* DAC: 16 bloques de 16 elementos */ r. genera_bitfat(). sectfat.cb). &r). 0x49). cur_x.r_ax=0x1A00. 0}. scr_ok=0.r_ax=0x1013. if (!strcmp(strupr(argv[argc-1]). *cx=peekb(0x40. info_disco(). r.r_ax=MODO. 48.&pag. void main(int argc. /* programar elementos del DAC */ } . */ /* */ /********************************************************************/ #include #include #include #include #include #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define <string. /* VGA negro */ {63. 0}. 32. Se imprime todo lo necesario antes de dibujar ya que para trazar los puntos es preciso programar el adaptador de vídeo de una manera diferente a la que emplea la BIOS (por razones de velocidad): después de ejecutar prepara_punto(). } void preservar_pantalla(char far **scrbuf. pinta_fat (bitfat. 0}. utilizando la función de impresión de la BIOS. &clusters_malos). HablaSp().cur_x. r.peek(0x40.h> <alloc.&cur_x. tsect. if (!existe_vga()) salida_error (1).h> C_PACIENCIA C_PACIENCIAM C_NEGRO C_CABECERA C_TITULOS C_INFO C_LEYENDA C_MARCO C_OCUPADA C_LIBRE C_ERRONEA MODO MIN_X MAX_X MIN_Y MAX_Y 78 9 0 1 2 3 4 5 6 7 8 0x12 2 637 152 477 /* colores */ /* /* /* /* /* /* /* /* /* VGA VGA VGA VGA VGA VGA VGA VGA VGA negro */ oro */ rojo */ naranja */ azul claro */ amarillo */ verde oscuro */ verde claro */ verde muy oscuro */ /* modo de vídeo */ /* ventana de dibujo de FAT */ } void preservar_pantalla(). return ((r. r.r_ax=0x1013. se rebasen los 64 Kb permitidos si la FAT ocupa más de 64 Kb (el puntero al buffer apunta al inicio del segmento al ser de tipo HUGE). restaurar_pantalla().scr_ok. 63}. la BIOS no es capaz de escribir en pantalla. r. if (argc>cb+1) unidad=(*argv[1] | 0x20)-’a’. numsect.&modo. /* determinar idioma del país */ cb=0. 0}. analiza_fat().r_ax=0x200. peek (0x40. } else { r. 0}. analiza_fat (bitfat.0x6c)) { r. &tsect)) salida_error(5). int cy. ya que no ocupa más de 128 Kb en el peor de los casos."/I")) cb++. 0}. clusters_datos. El programa también imprime información general sobre el disco. far *scrbuf. &tamcluster.cur_y. numclusters. &r). /* VGA azul claro */ {63. tamfat. sectfat.r_bx=1. 0}. 0x10) & 0xFFCF | 0x20). /* establecer paleta y borde */ r.r_ax=modo. huge *bitfat.FP_OFF(*scrbuf).0. escribir(). int modo.r_dx=FP_OFF(paleta). &r).r_bx=0. La inclusión de ensamblador en los programas en C se verá con detalle en un capítulo posterior.FP_SEG(*scrbuf). &tamfat.r_ax=0x1002. i<16. r. if ((fat=farmalloc(tamfat))==NULL) salida_error (2). carga_fat(). numclusters). } r.r_dx=cy<<8+cx. int cx. int pag. cur_y.0xb000. boot)!=0) salida_error (3).&cur_y. unidad. */ /* */ /* Compilar con Borland C++ en modelo large con */ /* la opción «Jump optimization» desactivada. /* establecer modo 640x480x16 */ r. /* primer elemento del DAC */ r. bitfat.1 . intr (0x10. int *scr_ok. modo. La FAT se lee en una matriz. una rutina en ensamblador se encarga de llamar a la interrupción 25h teniendo cuidado con el tipo de disco (particiones de más de 32 Mb o de menos de esa cantidad). if (!info_disco (boot. vía INT 25h. prepara_punto(). prepara_paleta().r_bx=0x0100.h> <dos. intr (0x10.0. *pag=peekb(0x40.r_ax=modo. r. /* supuesto que no va a ser posible */ *modo=peekb(0x40.r_ax=0x500+pag..r_cx=9.h> <conio. porc2str().0x51+(*pag)*2). inifat.0x4e).r_dx=FP_OFF(dac). } } void restaurar_pantalla(char far *scrbuf.15 del DAC */ for (i=0. peek(0x40. intr (0x10. paleta[16]=0. prepara_paleta(). sp^=cb. if (scr_ok) { if (modo!=peekb(0x40.r_es=FP_SEG(paleta). clusters_malos). unsigned numclusters. intr (0x10. &r). clusters_malos. informe_disco (unidad. tamcluster. &inifat. pag. clusters_datos. punto(). init_video(). 63.0x4e). genera_bitfat (fat. tsect).FP_OFF(*scrbuf). /* restaura página activa */ if (modo==7) movedata(FP_SEG(scrbuf). while (kbhit()) getch(). 0x10) & 0x30. 0L. int *cy.0x50+(*pag)*2). FP_SEG(*scrbuf). i++) paleta[i]=i. /* VGA rojo */ {63. r. aviso_espera(). salida_error().FP_OFF(scrbuf). clusters_datos. */ /* */ /* (c) Julio 1994 Ciriaco García de Celis. /* VGA verde claro */ { 0. static unsigned char dac[][3] = { /* R G B */ { 0. informe_disco(). char i. restaurar_pantalla (scrbuf. poke (0x40. /* VGA oro */ {63.4096). Esta última matriz -que indica los clusters libres. intr (0x10.pag. /* forzar modo color */ poke (0x40. 42.h> <dir. boot. paleta[17]. pinta_fat().FP_OFF(scrbuf). &r). int *modo.&cb). /* página 0: paleta en elementos 0.Utilidad de información gráfica de discos. A continuación. numclusters). int *pag. 0} /* VGA verde muy oscuro */ }. else movedata(FP_SEG(scrbuf). intr (0x10. if (leesect(unidad. unsigned char huge *boot. 0x10. } /* imposible reponer pantalla */ r.modo. intr (0x10. 0xb800. Para leer los sectores del disco no se utiliza la función absread() del Borland C 2. farfree(scrbuf).r_bx=pag<<8. leesect(). ocupados y defectuososes la que se volcará en pantalla adecuadamente. /* número de elementos a definir */ r. inifat.

&r). C_TITULOS. escribir (26. (cy << 8) + cx). sp?"Sectores/FAT: ":"Sectors/FAT: "). sectcluster = boot[0x0D]. numcaras. not ready. unsigned long *inifat. escribir (43. i<numclusters+2. Informe unidad Drive A: cursor_x = MK_FP (0x40. unsigned numclusters. i<numclusters. break. i++. 0x50 + (pagina <<1) ). escribir (0. while (*p) { r. (*cursor_x)++. sp?"Sectores/cluster:":"Sectors/cluster: "). break. int *sectfat. } for (i=0. div=10000L. escribir (43. id). C_TITULOS. sectpista. HPFS or network drive.1 (c) Julio 1994 CiriSOFT ": " DMAP 2. escribir (cx.1 (c) July 1994 CiriSOFT report "). C_PACIENCIA. int i. escribir (15. C_INFO. C_INFO. (numsect/sectpista/numcaras*256+255) >> 8. } } int info_disco (unsigned char *boot. /* retorno con error */ else { nsect=*numsect . 0.r_bx = (pagina << 8) | color. 4. parte2. r. C_TITULOS. if (modo>1) cx=25. C_TITULOS. *clusters_datos=numclusters-libres-(*clusters_malos). intr (0x10. C_TITULOS. pagina = peekb(0x40. unsigned datos. r. int cy. 1. i++) if ((elemento=bitfat[i])==C_LIBRE) libres++. 3.\n")." } void carga_fat (unsigned char huge *fat. int longitud) { unsigned long div. id). 14. nclus = nsect / boot[0x0D]. id[8]=0. impossible to analyze drive. C_PACIENCIAM.cb). if (numclusters>4084) fat16++. 6).cur_y. /* la FAT se carga de tres veces */ if (parte1) if (leesect(unidad. sectpista. i++) id[i-3]=boot[i]. 3.\n"). tamsect.(*inifat) . escribir (0. 3. id[1]=c.’)) && (*(cadena+1))) *cadena++=’ ’. C_INFO. escribir (sp?68:61. *tamsect = boot[0x0B] | ((int) boot[0x0C] << 8). unsigned numclusters) { unsigned int fat16=0. inifat+parte1+parte2.r_cx=1. } cadena[i]=num/div+’0’. unsigned char far *cursor_x. escribir (0. 2. C_TITULOS. p++. unsigned i. /* retorno correcto */ } } void salida_error(int error) { restaurar_pantalla (scrbuf. unsigned char *p. i++) if (fat16) { elemento = fat[(long)i<<1] | (fat [((long)i<<1)|1] << 8). *sectfat=boot[0x16] | (int) boot[0x17] << 8. break. cadena[1]=num/1000 | ’0’. 2. 6. sp?"Pistas: ":"Tracks: "). if (c>’9’) c+=7. escribir (15. i. /* cluster libre */ else if (elemento == 0xFF7) bitfat[i-2]=C_ERRONEA.scr_ok. parte3=sectfat-parte1-parte2. C_PACIENCIA. pos. 0x50 + (pagina << 1). 4. 6). if (cadena[0]==’0’) { cadena[0]=’ ’.\n": "\n Boot record damaged. int tamsect. parte2=(sectfat-parte1)/2. int *tamcluster. escribir (cx. dec2str (id. long inifat. 1. i<11.r_dx=0xFF00. nsect. void porc2str (char *cadena. 12. AT Y PS/2 BAJO DOS 135 void aviso_espera() { int cx. numsect/sectpista. else if (elemento == C_ERRONEA) (*clusters_malos)++. id). *bytesfat=(long) (*sectfat) * (*tamsect). 13. unsigned char *boot. escribir (0. "). case 2: printf (sp?"\n Memoria insuficiente. elemento. i<longitud. if (!*numsect) *numsect=(long) boot[0x20] | (long) boot[0x21]<<8 | (long) boot[0x22]<<16 | (long) boot[0x23]<<24. C_TITULOS. case 4: printf (sp?"\n Sólo soportados sistemas FAT12/FAT16. intr (0x10. while (((*cadena==’0’) || (*cadena==’. sectpista = boot[0x18] | (int) boot[0x19] << 8. cadena[2]=num/100 | ’0’. sp?"ID sistema: ":"System ID: "). sectcluster. 6). poke (0x40. C_TITULOS. sp?"Caras: ":"Sides: "). /* cluster ocupado */ } } void analiza_fat (unsigned char huge *bitfat. break. } exit (error). return (1).C_PACIENCIAM. escribir (cx+1. unsigned malos) { char id[17]. C_INFO. dec2str (id. else cx=4. cadena[4]=num/10 | ’0’.’. C_TITULOS. if (parte3) if (leesect(unidad. dec2str (id. if (i & 1) elemento = (fat[pos] >> 4) | (fat[pos+1L] << 4). C_TITULOS. unsigned long *bytesfat. int color. else cadena[3]=’. /* cluster libre */ else if (elemento == 0xFFF7) bitfat[i-2]=C_ERRONEA. C_CABECERA. switch (longitud) { case 13: coma=1. r.15. HPFS o de red. if (sp) cadena[3]=’. if (!elemento) bitfat[i-2]=C_LIBRE. sectcluster. elemento. /* eliminar cursor de la pantalla */ "). escribir (0. parte1. if (!elemento) bitfat[i-2]=C_LIBRE. num%=1000. 14. 0. parte3. if (parte2) if (leesect(unidad. sp?" ANALIZANDO AREAS DEL SISTEMA ": " PROCESSING SYSTEM AREAS "). *inifat=boot[0x0E] | (int) boot[0x0F] << 8. imposible informar.\n"). } void escribir (int cx. C_INFO. sp?"Sectores/pista:":"Sectors/track: "). id). 0x62). parte1=(sectfat+2)/3. coma. dec2str (id. div/=10L) { .(*sectfat) * boot[0x10] (boot[0x11] | (int) boot[0x12] << 8) * 32 / *tamsect. escribir (43. id[2]=0." escribir (cx. case 5: printf (sp?"\n Sector de arranque dañado. id).r_bx = (pagina << 8). 2.\n").\n": "\n Incorrect. no preparada. 6). A: id[0]=(char) unidad + ’A’. escribir (26. libres=0. } cadena[i]=0.modo. sp?"Byte de Medio: ":"Media byte: "). fat)!=0) salida_error (3). *numsect = boot[0x13] | ((unsigned long) boot[0x14] << 8). unsigned *clusters_datos. sp? " DMAP 2. sectfat. pagina. escribir (0. C_INFO. int num) { cadena[0]=num/10000 | ’0’. /* cluster ocupado */ } else /* FAT12 */ { pos = (i*3L) >> 1. case 6: coma=2. id). else elemento = fat[pos] | ((fat[pos+1L] & 0x0F) << 8). int tsect) { int parte1. C_INFO. escribir (15. dec2str (id. for (i=0.\n").\n": "\n Only supported FAT12/FAT16 filesystems. escribir (15. inifat+parte1. C_INFO. num%=div.\n": "\n This program requires VGA adaptor. " "). &r). fat + (unsigned long) (parte1+parte2) * tsect)!=0) salida_error (3). case 3: printf (sp?"\n Unidad incorrecta. unsigned long num. escribir (0. 6. break. for (i=3. sp?"Cilindros: ":"Cylinders: "). if ((*tamsect<32) || (numsect==0) || (boot[0x0D]==0) || (*sectfat==0)) return (0). if (nclus>65535L) salida_error (4). /* cluster defectuoso */ else bitfat[i-2]=C_OCUPADA. id). numcaras = boot[0x1A] | (int) boot[0x1B] << 8. parte2. id[0]=c. sectfat = boot[0x16] | (int) boot[0x17] << 8." escribir (cx+32. switch (error) { case 1: printf (sp?"\n Este programa requiere adaptador VGA. 13.\n": "\n Insufficient memory.r_ax=0x900 | *p. numcaras. unsigned *clusters_malos) { unsigned i. if (i==coma) { cadena[i]=’.pag. sp?"Número de FATs:":"Number of FATs:"). unsigned long *numsect. fat + (unsigned long) parte1 * tsect)!=0) salida_error (3). for (i=2. coma+=4. parte3. 6). id). dec2str (id. id[1]=0. break. c=boot[0x15] >> 4 | ’0’.’. dec2str (id. C_PACIENCIAM. escribir (26. 3. if (cadena[1]==’0’) cadena[1]=’ ’. " "). 6). escribir (26. i++. C_CABECERA. } "). } } void genera_bitfat (unsigned char huge *fat. break. r. } void informe_disco (int unidad.cur_x. *numclusters = nclus. id). p=cadena. } void dec2str (char *cadena. escribir (15.ARQUITECTURA DEL PC. unsigned char huge *bitfat. escribir (cx+32. 6). sectfat. 2. tamsect = boot[0x0B] | (int) boot[0x0C] << 8. /* cluster defectuoso */ else bitfat[i-2]=C_OCUPADA. 5. C_INFO. r. 5.’. id). 1. c=boot[0x15] & 0x0F | ’0’. num%=100. c.r_ax=0x200. if (c>’9’) c+=7. escribir (19. int *tamsect) { unsigned long nclus. 4. num%=10000. unsigned char *cadena) { struct REGPACK r. int sectfat. unsigned *numclusters. C_PACIENCIA. div=1000000000L. inifat. unsigned long numsect. "Bytes/sector: "). 1. *tamcluster = (*tamsect) * boot[0x0D].

if (unidad!=anterior_unidad) /* ahorrar tiempo si mismo disco */ { getfat(unidad+1. escribir (52. " "). escribir (26. ant_pixel_h=0. dec2str (id.al mov ah.1 /* DX:AX = DX:AX:SI / 16384 = pixel */ mov si. sp?"Sectores reserv.coord_x mov dx.es:[bx-1] mov bx. y. psectl. escribir (43.al pop dx. mov cl.3 shr bx. C_MARCO). unsigned x.cx /* BX = cx.si /* DI --> número de cluster hasta donde avanzar */ push si mov ax.3CEh mov ax. escribir (sp?19:18.ant_pixel_h /* SI:DI = nº de pixels a pintar */ mov ant_pixel_l. C_INFO. pop ds } } int leesect(int unidad. C_MARCO). int nsect. coord_y=MIN_Y*80.80h shr ah. 13).1 rcl ax.cl /* AH = bit a pintar en su sitio */ mov dx. strcpy (id.bx sub di.ax pop cx pop bx pop si jcxz fin_proc jmp proc_fat } fin_proc: asm { pop es. i++) escribir (i<<4.coord_y xchg bx. porc).80 mov bp. tipo_disco. boot[0x0E] | (int) boot[0x0F] << 8.ax mov ant_pixel_h. 4. porc2str (cad. 2 lect. punto } } void pinta_fat (unsigned char huge *bitfat.cl /* CL = 3 */ add bx. unsigned long psect. dec2str (id. pop si. C_TITULOS. mov cx. escribir (52. x++) { punto (x. pop dx.0 /* DX:AX:SI producto */ shl si. boot[0x10]. coord_x=2. id).(boot[0x0E] | (int) boot[0x0F] << 8) (sectfat) * boot[0x10] (boot[0x11] | (int) boot[0x12] << 8) * 32 / tamsect. static anterior_unidad=0xFFFF. C_MARCO). sp?"Area defectuosa (":"Damaged area ("). id). C_MARCO).bitfat mov si. (long)datos*sectcluster*tamsect. pop bx. BP = cy*80 */ and cl. sp?"Bytes ocupados:":"Bytes used: "). C_INFO. 8. C_LEYENDA.80 out dx. push cx. " "). pop bx. 13).ant_pixel_l sbb si.dx /* BX = cy * 80 + cx / 8 */ and al. C_TITULOS. push bp. C_INFO. C_TITULOS. pop bx. C_LEYENDA. id). pop cx. escribir (sp?43:42. 5.136 EL UNIVERSO DIGITAL DEL IBM PC. buffer_o. C_INFO.es:[bx] } cuenta: asm { inc bx cmp al. ant_pixel_l=0. push si. sp?"Area ocupada (":"Used area ("). 0 cambiar AH para hacer OR/XOR/AND */ void punto (int coord_x. 8. char *cad="100. } void prepara_punto() { asm { push ax. /* unidad de más de 65535 sectores */ else tipo_disco=0.205h /* out dx. push cx. MIN_Y-1. cad). pop cx. sp?"Sectores: ":"Sectors: ").ax pop dx. C_MARCO). asm { push ax.7 mov ah. "Clusters: "). 8. dec2str (id.bp /* BX = cy*80+cx/8 */ push si mov si. pop ax } } /* preparar la VGA para punto() */ */ registro de modo (5): escr.ax sub di.MIN_Y*80 mov cl. escribir (sp?72:69.cx mov cl. y. DX = cy */ mov cx. x<=MAX_X+2.80h shr ah. jmp fin_proc } /* tecla pulsada */ dec_msb: asm { pop si sub si. numsect = numsect . C_MARCO). pop ax. 13).cl add bx. factor=(long) (MAX_X-MIN_X+1)*(MAX_Y-MIN_Y+1). y. if (((unsigned)fatdisco. " "). C_LEYENDA. pop di. /* unidad de menos de 65536 sectores */ .3CEh mov ax. 2. 5. id). MIN_Y-2. id). escribir (67.[bx] /* acceso en lectura */ mov ax.1 rcl dx. pop ax.ch /* pintar punto */ sub di.1 rcl dx. push bx. 4. 6). 8. sp?"Bytes libres:":"Bytes free: "). punto punto (MAX_X+1.dx /* DI:SI producto parcial */ mul word ptr [factor+2] /* DX:AX segundo producto parcial */ add ax. 6.1 /* siguiente pixel en el eje X */ out dx. escribir (52.ax mov al. pop ds.0A000h mov ds. C_LIBRE. pop cx. escribir (28.si cmp bp. y. C_TITULOS.cl add bx. C_TITULOS. push es. porc2str (cad. C_OCUPADA. (long)numclusters*tamsect*sectcluster.bp pop bp pop ds mov coord_x. id).bp /* BX = cy*80+cx/8 */ push ax mov ah. 3.dx shl ax. porc2str (cad. 13). id). escribir (52. escribir (43.1 /* CX = cy * 64 */ add dx. push ds. C_ERRONEA. pop si. 7. C_INFO.ax mov al. unsigned malos) { int porc. escribir (43.coord_x mov bp. 6). dec2str (id. dec2str (id.fi_nclus * (unsigned long)fatdisco.numclusters les bx. 6.di mov di. porc).[bx] /* acceso en lectura */ mov [bx]. 6.es:[bx] loope cuenta mov di. C_INFO. escribir (67. for (i=0. cad).1 shl ax. 3.ax /* DX = cy * 80 */ mov al. escribir (26. i<5.ax mov ax. (((long)numsect/sectcluster-datos-malos) *tamsect*sectcluster).ax mov ax. pop bx. y. 8. C_CABECERA. porc=(numclusters-datos-malos)*10000L/numclusters+5.1 jc dec_msb } /* evitar salto la mayoría de las veces */ incy: asm { add bx.ax } pinta_mas: asm { mov cl. 8. mov cx. escribir (52.bl /* BX = cx. push cx. porc).3 /* out dx. escribir (67. C_LEYENDA. punto punto (x. push dx. sp?"Bytes erróneos:":"Bytes damaged: ").dx mov di. C_MARCO). y<=MAX_Y. escribir (67. 6). C_MARCO). boot[0x11] | (int) boot[0x12] << 8. push di. numsect/sectcluster. for (y=MIN_Y. 8.80 jnc incy pop si pop bx mov ax.di adc dx.coord_y mov dx. unsigned numclusters) { unsigned long factor.ax pop si pop bx inc bx push bx push si mov si. punto } for (x=MIN_X-2.bx mov coord_y.0%)".dx push bx. C_TITULOS.1 int 16h pop ax jz pinta_mas pop si. 13). MAX_Y+1.0A000h mov ds.:":"Reserved sectors:").si add bp. escribir (52.3 shr bx. pop bp. escribir (31. (x. C_LEYENDA. C_INFO.3CEh /* registro de direcciones */ mov al.bx } /* SI --> posición del primer cluster */ proc_fat: asm { mov al. 5.1 shl si. 2. porc=malos*10000L/numclusters+5.(MAX_Y+1)*80 jb pinta_mas ror ah. escribir (sp?5:7. push dx mov dx.fi_sclus) > 0xFFFFL) tipo_disco=1. 6.1 rcl ax.8 out dx. 8.1 push si mov si. "Total bytes:"). porc=datos*10000L/numclusters+5. mov ch. &fatdisco). MAX_Y+2. C_LEYENDA. sp?"Area libre (":"Free area ("). 5. C_INFO. escribir (67. escribir (sp?2:4. " "). numsect.7 mov cl.word ptr factor mul di mov si. C_TITULOS. id). 8. id). flags. int coord_y. AT Y PS/2 dec2str (id. factor=factor*16384L/numclusters. (x. push dx. dec2str (id. unsigned datos. C_TITULOS.cl /* DX = cy * 16 */ mov ax. escribir (55. escribir (67. } } void marco() { int x. cad). push bx. dec2str (id. (long)malos*sectcluster*tamsect. y. psecth. id). escribir (52. 4. } void leyendas (unsigned numclusters. C_INFO. 1. void *buffer) { struct fatinfo fatdisco.color mov [bx]. 13). (MAX_X+2.cl /* AH = bit a pintar en su sitio */ push bx mov cl. sp?"Entradas en raiz:":"Root dir entries:"). 1.4 shl dx.bl dec cl shr bx. y++) { punto (MIN_X-2.8 (MIN_X-1. int color) { asm { /* rutina rápida sólo para modos de 640x???x16 */ push ds push ax. dec2str (id. unsigned buffer_s.

Como se vio en el capítulo anterior.unidad /* unidad */ mov bx.offset 4: no utilizado. r.ARQUITECTURA DEL PC. los que ocupen 4 bytes deben interpretarse en la forma segmento:offset. } buffer_o=FP_OFF(buffer). while (spl[i++]) if (spl[i-1]==r.x. 593. idioma=0. pop dx. push bp. &r. En cualquier caso. cuya descripción detallada se da a continuación. La dirección del PSP en los programas COM viene determinada por la de cualquier registro de segmento (CS=DS=ES=SS) nada más comenzar la ejecución del mismo. antes de que el COMMAND. push bx. struct SREGS s.ds=FP_SEG(info). /* supuesto el inglés */ if (_osmajor>=3) { r. AT Y PS/2 BAJO DOS 137 anterior_unidad=unidad. 34. 3. no es difícil saber la memoria que queda libre. di. spl[]={54.sp mov dx. push cx. int i. . se crea un bloque de 256 bytes llamado PSP (Program Segment Prefix). push ds.ss mov ds. 57. pop si. mov mov mov mov mov int pushf pop add pop pop } ax. 508. . if (tipo_disco) /* unidades con más de 65535 sectores */ asm { push ax. 505.offsets 2 al 3: una palabra con la dirección de memoria (segmento) del último párrafo disponible en el sistema. intdosx (&r. 0}.dx=FP_OFF(info). Supuesto ES apuntando al PSP: MOV MOV SUB MOV MUL AX.EL PSP. push cx. push dx. pop bp.0. Sin embargo. pop ax } else /* unidades con menos de 65536 sectores */ asm { push ax. un programa COM ¡también!. s.CX CX. 58. push bx. psectl=psect & 0xFFFF. pop cx.psectl cx. .offsets 0 al 1: palabra 20CDh. párrafo más alto disponible . 52. pop bp. 502. push si. 503. pop di.ES AX. char info[64].nsect bx. el MS-DOS ¡también lo permite!). push dx. En CP/M se podía terminar un programa ejecutando un salto a la posición 0. DX:AX bytes libres . . . 598. push di. 507. 51.x. 56.dx /* DS:BX = SS:SP */ mov cx. 591. .12 /* equilibrar pila */ pop ds. En MS-DOS.x. AX = párrafos libres .2 /* equilibrar pila */ ds. 506. pop ax } return (flags & 1). correspondiente a la instrucción INT 20h. } 7. buffer_s=FP_SEG(buffer). pop dx. push buffer_s /* segmento del buffer */ push buffer_o /* offset */ push nsect /* número de sectores */ push psecth /* sector inicial (parte alta) */ push psectl /* (parte baja) */ mov ax. los campos del PSP que ocupen un byte o una palabra han de interpretarse como tal. .offsets 12h al 15h: contenido previo del vector de manipulación de errores críticos (INT 24h). pop cx. 504. segmento del PSP . En la siguiente información. Los PSP creados por la función 4Bh en algunas versiones del DOS no tienen correctamente inicializado este campo. En negrita se resaltan los campos más importantes. 63.offsets 0Ah al 0Dh: contenido previo del vector de terminación (INT 22h).buffer_s 25h /* /* /* /* /* /* unidad */ sector inicial */ número de sectores */ offset del buffer */ segmento */ acceso al disco */ flags /* resultado de la operación */ sp.ES:[2] CX. cuyo uso recomienda el fabricante del sistema en aras de una mayor compatibilidad con futuras versiones del sistema operativo. . existe una función del DOS para obtener la dirección del PSP. &s). int HablaSp() /* devolver 1 si mensajes en castellano */ { union REGS r. push si. 212. Teniendo en cuenta dónde acaba la memoria y el punto en que está cargado nuestro programa. 595. pop bx. idioma.unidad dx. push ds. } return (idioma).buffer_o ds.ax=0x3800. i=0. La función es la 62h y está disponible a partir del DOS 3. pop si. push bp. No es recomendable llamar al DOS de esta manera.0ffffh /* sectores de 32 bits */ int 25h /* acceso al disco */ pushf pop flags /* resultado de la operación */ add sp.bx) idioma=1.COM pase el control al programa que se pretende ejecutar.offsets 5 al 9: salto al despachador de funciones del DOS (en CP/M se ejecutaba un CALL 5. pop bx. 80.offsets 0Eh al 11h: contenido previo del vector de Ctrl-Break (INT 23h). push di. psecth=psect >> 16.7.16 CX . en los programas de tipo EXE sólo viene determinada por DS y ES.

0.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).offsets 32h al 33h: desde el DOS 3.offsets 7Ch al 7Fh: no usados hasta ahora.offsets 50h al 52h: código de INT 21h/RETF.0 puede haber más de 20 ficheros abiertos a la vez gracias a este campo. . NUL. Véase el capítulo 8 para más información de las variables de entorno. Es una reliquia en desuso. ya se sabe la longitud de los parámetros-.offsets 40h al 41h: desde el DOS 5. Ese retorno de carro. . número de entradas en la JFT (por defecto. que puede ser movido de sitio. una palabra que apunta al segmento del espacio de entorno.offsets 3Ch al 3Fh: no usados hasta ahora. versión del sistema a devolver cuando se invoca la función 30h. es utilizado por SHARE en el DOS 3. al menos en programas residentes -susceptibles de ser instalados con LOADHIGH-.offsets 80h al 0FFh: es la zona donde aparecen los parámetros suministrados al programa. y además los parámetros pueden estar separados por uno o más espacios en blanco o tabuladores (ASCII 9).. El primer byte indica la longitud de los parámetros..0. . .offsets 38h al 3Bh: desde el DOS 3.offsets 34h al 37h: desde el DOS 3.offsets 2Eh al 31h: desde el DOS 2. PSP:18h). véase offset 32h). y además este área no se inicializa si el programa es cargado en memoria superior con el comando LOADHIGH del MS-DOS 5. . si hay más de 20.. aunque es mejor siempre acceder antes a las funciones del DOS. Se puede saber si un fichero es remoto (en la MS-net) comprobando si el byte de la JFT está comprendido entre 80h-0FEh.3. Sólo hasta 20 ficheros (si no. . el programa pudo cargarse. Sin embargo.0. puntero al PSP previo (por defecto.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.3 cuando en un PSP hijo (por ejemplo. 0FFFFh:0FFFFh en las versiones del DOS 3. No recomendado hacer CALL PSP:5Ch para llamar al DOS. . . Desde el DOS 3.offsets 49h al 4Fh: no usados hasta ahora.0 y posteriores.0. Si se utiliza el primer FCB se sobreescribe además el segundo. en cualquier otro directorio diferente del directorio en curso). valor de SS:SP en la entrada a la última INT 21h invocada. los primeros son los dispositivos CON. . Téngase en cuenta que no son mayusculizados automáticamente (están tal y como los tecleó el usuario).. 20). creado con la función EXEC) se copia la información de más que de los 20 primeros ficheros.offset 48h: desde Windows 3. .offsets 16h al 17h: segmento del PSP padre.offsets 2Ch al 2Dh: desde el DOS 2.x). . . .. . . . AT Y PS/2 . no «se cuenta» en el byte que indica la longitud. apoyándose en el PATH. y siempre están abiertos). por lo que no conviene usarlo ni siquiera para captar parámetros. donde se puede encontrar el valor de variables de entorno tan interesantes como PATH. . puntero al JFT (por defecto. COMSPEC.138 EL UNIVERSO DIGITAL DEL IBM PC. el bit 0 está activo si la aplicación es no-Windows. después vienen los mismos y al final un retorno de carro (ASCII 13) que es un tanto redundante -a fin de cuentas. .offsets 53h al 5Bh: no usados hasta ahora. sin embargo.offsets 42h al 47h: no usados hasta ahora. es sólo a partir del DOS 3.0. 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.0.

EXE.8.COM aunque no hay razón para que ello tenga que ser así necesariamente (pruebe el lector a poner en CONFIG. existe un salto a donde realmente comienza el código de la BIOS.EL PROCESO DE ARRANQUE DEL PC. se procede igualmente con el primer disco duro (en los PC de IBM. se intenta acceder a la primera unidad de disquetes: si no hay disquete. Por último. controladoras de disco duro. se carga el intérprete de mandatos: por defecto es COMMAND.COM.SYS la orden SHELL C:\DOS\QBASIC. en la que hay ROM. 7. en vez de bloquear el ordenador). El nombre de los ficheros del sistema depende de si éste es PC-DOS (o DR-DOS) o MS-DOS.ARQUITECTURA DEL PC. Se carga el primer sector en la dirección 0:7C00h y se entrega el control a la misma. En las versiones más recientes del DOS. se detecta la configuración del sistema. A continuación.). etc. se entrega el control sucesivamente a las posibles memorias ROM adicionales que existan (la de la VGA. el sistema puede residir en memoria superior o en el HMA: en ese caso. DMA.) con objeto de que desvíen los vectores que necesiten. se detiene el sistema)..FORMATO DE LAS EXTENSIONES ROM. Ese fichero es como una capa que se interpone entre la BIOS del PC y el código del sistema operativo contenido en MSDOS.SYS (o IBMDOS. aunque si se abandona QBASIC algunas versiones modernas del DOS son aún capaces de cargar el COMMAND por sus propios medios.. se inicializan los principales chips (interrupciones. 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. donde también está direccionada la ROM (todos los microprocesadores arrancan en modo real). se establecen los vectores de interrupción y se chequea la memoria RAM si el contenido de la dirección 40h:72h es distinto de 1234h (el contenido de la memoria es aleatorio inicialmente). Al final del todo. El programa del sector de arranque busca el fichero del sistema IO.SYS ó IBMBIO. . entregándole el control (programa SYSINIT) o mostrando un mensaje de error si no lo encuentra.COM comience en el primer cluster de datos del disco. AT Y PS/2 BAJO DOS 139 En general. temporizador. Las memorias ROM que incorporan diversas tarjetas (de vídeo. después del error pertinente.COM. En esa posición de memoria. el proceso de arranque se complica ya que es necesario localizar el DOS en esa zona después de cargar los controladores de memoria. Teniendo en cuenta que el MSDOS y el PC-DOS son prácticamente idénticos desde la versión 2. Este último fichero es el encargado de inicializar los vectores 20h-2Fh y completar las tablas de datos internas del sistema.SYS para instalar los controladores de dispositivo que den soporte a las características peculiares de la configuración del ordenador. Para ver los parámetros (HOLA /T en el ejemplo) se haría «D 80». la existencia de las dos versiones se explica sólo por razones comerciales.SYS o IBMBIO.0 (PC-DOS funciona en máquinas no IBM).9. El programa de la ROM inicialmente se limita a chequear los registros de la CPU. Para ello basta una orden tal como "DEBUG PROGRAMA. Este salto suele ser de tipo largo (segmento:offset) con objeto de cargar en CS un valor que referencie al primer mega de memoria. Puede que también se cargue al principio el fichero MSDOS.SYS o IBMDOS. Las versiones más modernas del DOS no requieren que IO. como todos los PC compatibles son casi idénticos a nivel hardware (salvo algunas de las primeras máquinas que intentaron imitar al PC) en la práctica es el fabricante del DOS (Microsoft o Digital Research) quien entrega dicho fichero. Finalmente. . Ese sector cargado será el sector de arranque del disquete o la tabla de partición del disco duro (el código que contiene se encargará de cargar el sector de arranque del propio disco duro. el disco duro en XT. 0FFFFF0h en 286 y 0FFFFFFF0h en 386 y superiores). primero el de estado y luego los demás (en caso de fallo. 7.SYS o IBMBIO. Sin embargo. de red) pueden estar ubicadas en cualquier punto del área 0C0000h-0FFFFFh.COM en teoría debería ser entregado por el vendedor del ordenador: este fichero provee soporte a las diferencias específicas que existen en el hardware de las diferentes máquinas.COM HOLA /T": al entrar en el DEBUG (o SYMDEB) basta con hacer «D 0» para examinar el PSP de PROGRAMA. Al conectar el PC éste comienza a ejecutar código en los 16 últimos bytes de la memoria (dirección 0FFFF0h en PC/XT. La ROM BIOS del ordenador se . También se interpreta el CONFIG.COM en PC-DOS) y lo carga. accediendo directamente a los puertos de E/S y también consultando los switches de configuración de la placa base (PC/XT) o la CMOS (AT). según la partición activa). El fichero IO. aunque sí que se encuentre en el directorio raíz.COM) o bien puede que el encargado de cargar dicho fichero sea el propio IO.SYS (o IBMBIO. si no hay disco duro ni disquete se ejecuta la ROM BASIC).

Un ejemplo de memoria ROM podría ser: bios DB DB JMP . que va detrás de éstos. 0AAh 32 inicio . OS/2. . al igual que un CLS o un DIR.. . vamos. el código de la extensión ROM debe devolver de nuevo el control a la BIOS del sistema. WINDOWS. etc. al contrario que los COM..FORMATO FÍSICO DE LOS FICHEROS EXE. El COMMAND de aquellas versiones del DOS (desconozco si el actual también) era capaz de ejecutar comandos internos definidos en estas ROM. . 0AAh 48 0AAh. se realiza una suma de comprobación de toda la extensión ROM y si el resultado es 0 se considera una auténtica ROM válida. que requiere modificar un fichero ejecutable ya ensamblado o compilado... En la actualidad. por ejemplo: bios_basic DB DB JMP DB DB JMP DB DB JMP DB . . se entrega el control (con un CALL entre segmentos) al cuarto byte de la extensión ROM. 0AAh 64 inicio 5 "BASIC" basic 6 "BASICA" basic 0 . no más comandos . . . 0AAh: estos dos bytes consecutivos tienen que aparecer al principio para considerar que ahí hay una ROM.. ¡Sí.. El inconveniente es que en otros sistemas operativos (UNIX.. El código almacenado en estas extensiones ROM puede contener accesos directos al hardware y llamadas a la ROM BIOS del sistema. Por razones de seguridad. En ese caso.. 24 Kb de contabilidad nada que hacer esto es un listado BASIC aquí.... El tercer byte. la suma de todos los bytes = 0 basic fin_bios Si esto le parece una tontería al lector. . .140 EL UNIVERSO DIGITAL DEL IBM PC. un listado en ROM!: mortgagebas DB DB RETF DB . . que no con una ROM. Ahí habrá de estar ubicado el código de la extensión ROM (habitualmente un salto a donde realmente comienza). salto al comienzo del BASIC .. como por ejemplo la creación de antivirus -y también la de virus-. 32 Kb de ROM-BASIC . salto al comienzo (el mismo del BASIC) . mucho más difícil de actualizar. 55h. aunque tokenizado) en las BIOS. es que no ha visto lo que vamos a ver ahora.. con objeto de permitirlas desviar vectores de interrupción y ejecutar otras tareas propias de su inicialización. longitud del siguiente comando . 55h .10. indica el tamaño de esa extensión ROM en bloques de 512 bytes. Es conveniente conocer esta estructura para ciertas tareas. La ventaja de las extensiones ROM es que aumentan las prestaciones del sistema antes de cargar el DOS. Resulta que también se pueden almacenar programas en BASIC (el código fuente. 16 Kb de ROM fin_bios . . Al final del todo.... Los ficheros EXE poseen una estructura en el disco distinta de su imagen en memoria. la suma de todos los bytes = 0 Los primeros ordenadores de IBM incorporaban una memoria ROM con el BASIC. por medio de un retorno lejano (RETF). conviene recordar que el DOS no ha sido cargado aún y no se pueden emplear sus funciones. resulta más conveniente que las extensiones de hardware vengan acompañadas de drivers para DOS. .) que emplean el modo protegido. 55h. el programa la suma de todos los bytes = 0 fin_bios 7. longitud del siguiente comando . Sin embargo. La BIOS recorre este área en incrementos de 2 Kb buscando la signatura 55h. El formato era... con la disponibilidad de memoria superior bajo DOS. AT Y PS/2 encarga de ir recorriéndolas y entregándolas el control durante la inicialización. estas memorias ROM en general no son accesibles. 55h..

el programa se cargará lo más abajo posible en la memoria -lo más normal-. .. 6C 00-20 00-3E 00-00 00-00 00-00 00-00 .. 00 78 24 B8 00 70 .. 00-00 20-61 00-00 8E-D8 00-00 61-70 . 00 74 00 00 00 69 . pilapilapilapila pilapilapilapila Los ficheros EXE constan de una cabecera.>. A continuación se lista un volcado del fichero ejecutable a estudiar......Texto a imprim ir.. como en el ejemplo.. . el valor para SS está aún sin reubicar (habrá de sumársele el segmento en que se cargue el programa)... 70 5A 02 72 00 00 00 .......datos).... 6C 00 30 00 00 00 00 .. 61-70 00 00 00 00 00 00 .4... 00 69 00 CD 00 6C .. en adición a l tamaño del mismo.... Indica cuántas veces se hace referencia a un segmento absoluto: el montador del sistema operativo tendrá que relocalizar en memoria todas las referencias a segmentos absolutos según en qué dirección se cargue el programa para su ejecución..... pero para eso está la palabra menos significativa (offset 2) que indica que el último sector sólo tiene ocupados los primeros 40h bytes. 6C 00 00 00 00 00 00 .... datos y pila. .. ’MZ’) ó 5Ah y 4Dh (’ZM’).. AT Y PS/2 BAJO DOS 141 Analizaremos como ejemplo de programa EXE el del capítulo 6... porque TLINK no se molesta ni en inicializarlo (El LINK de Microsoft sí).. 6C 00 00 00 00 00 00 .. se repite la línea de arriba tantas veces como sea preciso: 0000 0010 0020 0030 0040 0050 . seguida de los segmentos de código.. 00 6D 00 21 00 61 ... habida cuenta que sólo hay una reubicación (de hecho. En el ejemplo sólo hay 1 (correspondiente a la instrucción MOV AX. Offset 2 (2 palabras): Tamaño del fichero en el disco. Offset 0Ch (1 palabra): Máxima cantidad de memoria requerida (párrafos).... el SS relativo es 4 y SP = 200h (=512 bytes de tamaño de pila definido). Por tanto. 0430 4D 00 6A 00 02 00 . 69 40 00 00 00 00 00 . 00 6D 00 00 00 61 ... 00 54 0D C0 00 6C . 70 FF 00 00 00 00 00 . 00 6F 00 00 00 6C . ... 61 FF 01 00 00 00 00 . .. Offset 12h (1 palabra): Suma de comprobación: son en teoría los 16 bits de menos peso de la negación de la suma de todas las palabras del fichero... 01F0 0200 0210 0220 0230 0240 ....... En el ejemplo.... esta información indica que el fichero es realmente de tipo EXE y no lleva esa extensión por antojo de nadie. Si es 0. 69 01 02 00 00 00 00 . 00 70 00 B4 00 70 ..3@P8.. 69 04 FB 00 05 00 00 . La palabra más significativa (offset 4) da el número total de sectores que ocupa: 3 en este caso (3 * 512 = 1536).. Offset 0Ah (1 palabra): Mínima cantidad de memoria requerida por el programa. El tercer sector no está totalmente lleno.. 69 00 00 00 00 00 00 ..... .... en párrafos.. 00 69 00 00 00 6C .{0 jr.... si es 0FFFFh......X:... la columna de la izquierda es el offset del primer byte de la línea...... como luego veremos. el tamaño efectivo del fichero es de 1024 + 64 = 1088 bytes. Se comentarán los principales bytes que componen el fichero ejecutable en el disco (1088 en total). . . 00 72 00 09 00 69 . el tamaño mínimo.. En el ejemplo son 200h (=512) bytes.. 70 00 00 00 00 00 00 . Olvidar este campo.. El DOS debe hacer poco caso. esta cabecera se carga en un buffer auxiliar y no formará parte de la imagen definitiva del programa en memoria.. ..M! K.. Todos los datos están en hexadecimal (parte central) y ASCII (derecha). 00 65 0A 50 00 61 .... Donde hay puntos suspensivos.. Offset 8 (1 palabra): Tamaño de esta cabecera del fichero EXE. A continuación se explica el contenido de los bytes de la cabecera: Offset 0 (2 bytes): Valores fijos 4Dh y 5Ah (en ASCII.. ... 61 MZ@... el programa se cargará lo más alto posible en la memoria (opción /H del LINK de Microsoft)... Offset 6 (1 palabra): Número de reubicaciones a realizar. . que reúne las principales características necesarias para nuestro estudio.. lo que se corresponde con la realidad... En el ejemplo es 0 (el programa se conforma con lo que ocupa en disco). 00 0A 72 33 00 69 ...ARQUITECTURA DEL PC.. 00 20 00 BA 00 69 .. La cabecera que estamos analizando y que precede al código y datos del programa será más o menos larga en función del tamaño de la tabla de reubicaciones.... 00 0D 69 1E CB 70 . Offset 0Eh (2 palabras): Valores para inicializar SS (offset 0Eh) y SP (offset 10h)..$. 61 03 00 00 00 00 00 .... Evidentemente.. aún cabrían muchas más)..

Offset 1Ah (1 palabra): Número de overlay (0 en el ejemplo. Cada entrada en la tabla ocupa 4 bytes. En el ejemplo. es un programa principal).142 EL UNIVERSO DIGITAL DEL IBM PC. AT Y PS/2 Offset 14h (2 palabras): Valores para inicializar CS (offset 16h) e IP (offset 14h). Offset 18h (1 palabra): Inicio de la tabla de reubicación. . Esta palabra a cero es el operando de la instrucción MOV AX. 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. siendo IP = 0.n es 0B8h).datos (el código de operación de MOV AX. El valor para CS está aún sin reubicar y habrá de sumársele el segmento definitivo en que se cargue el programa. Offset 1Ch al 3Dh: Valores desconocidos (dependientes de la versión de LINK o TLINK). a la que habrá de sumársele el segmento donde sea cargado el programa. el valor relativo de CS es 2. En el ejemplo es 3Eh. La única entrada de que consta este programa tiene el valor 0002:0005 = 25h. en el offset 225h hay una palabra a cero. lo que indica que la tabla comienza en el offset 3Eh. expresado como offset.

4. como memoria para gráficos. las tarjetas más avanzadas tienen paginada su memoria y con una serie de puertos de E/S se indica qué fragmento del total de la memoria de vídeo está siendo direccionado (en la VGA. En muchas máquinas. . gracias a unos chips de apoyo.Memoria de vídeo. . Surgió en los PC/XT como respuesta a la necesidad de romper el límite de los 640 Kb.LA GESTIÓN DE MEMORIA DEL DOS 143 Capítulo VIII: LA GESTIÓN DE MEMORIA DEL DOS 8. 8. El segmento E0000 suele estar libre y el F0000 almacena la BIOS del equipo. donde se describe de manera más esquemática.1. que normalmente es el segmento D0000 del espacio de direcciones del 8086 . . de reciente aparición. 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. Conviene también echar un vistazo al apéndice I. aunque rara vez se utilizan simultáneamente. Los modernos sistemas operativos DOS permiten (en los equipos 386 ó 386sx y superiores) colocar memoria física extendida en el espacio de direcciones de la memoria superior. Los segmentos A0000 y B0000 están reservados para gráficos. La memoria superior no se toma de la memoria instalada en el equipo. El segmento D0000 es empleado normalmente para el marco de página de la memoria expandida. etc. VGA (256 Kb) y SVGA (hasta 2 Mb).1.1. sino que está en ciertos chips aparte relacionados con la BIOS.2. Consiste en añadir chips de memoria en una tarjeta de expansión. 8. . Después han ido apareciendo la CGA (16 Kb). . un AT con 1 Mb de RAM normalmente posee 640 Kb de memoria convencional y 384 Kb de memoria extendida.3. y se trata de un sistema de paginación. Es la memoria RAM comprendida entre los 0 y los 640 Kb. BIOS.TIPOS DE MEMORIA EN UN PC. 8. El primer adaptador de vídeo de IBM era sólo para texto y empleaba 4 Kb. sólo 64 Kb en A0000). con ello es factible rellenar los huecos vacíos y aprovecharlos para cargar programas residentes.1. pero no es frecuente. 8. Los 384 Kb restantes hasta completar el megabyte se reservan para otros usos.Memoria superior. Como sólo hay 128 Kb reservados para gráficos en el espacio de direcciones del 8086.Memoria convencional. Ciertos equipos 286 también soportan esta memoria. un buen fragmento de esta memoria está ocupado por el sistema operativo y los programas residentes. quedando normalmente no más de 560 Kb a disposición del usuario. los gráficos.1.Memoria expandida. Este término. designa el área comprendida entre los 640 y los 1024 Kb de memoria del sistema. es la memoria utilizada por el DOS para los programas de usuario. Por ello. así como una cierta circuitería que permita colocar un fragmento de esa memoria extra en lo que se denomina marco de página de memoria expandida. etc. EGA (64-256 Kb).1. 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. Daremos un breve repaso a los tipos de memoria asociados a los ordenadores compatibles en la actualidad. para completar la explicación.

expandida o extendida) empleada por un controlador de dispositivo (driver) para almacenar las partes del disco de más frecuente uso. Este marco de página está dividido en 4 bloques de 16 Kb. Hace ya bastante tiempo se diseñó una especificación para que los programas que utilicen la memoria extendida puedan convivir sin conflictos: se trata del controlador XMS.SYS y en algunas versiones del EMM386.5. 8.Memoria extendida. La shadow RAM normalmente son 384 Kb que reemplazan cualquier fragmento de ROM ubicado entre los 640-1024Kb de RAM durante el proceso de arranque (boot) del sistema. Para que los programas no tengan que hacer accesos a los puertos y para hacer más cómodo el trabajo. También incorporan memorias caché algunos controladores de disco duro.Memoria CMOS RAM. .Memoria caché. surgió la especificación LIM-EMS (Lotus-Intel-Microsoft Expanded Memory System) que consiste básicamente en un driver instalable desde el config.SYS. Evidentemente no se puede ejecutar código sobre la RAM CMOS (Ni pueden esconderse virus. que permanecen tras apagar el ordenador (gracias a las pilas). especialmente con los programas que se apoyan en la BIOS. 8. A nivel hardware. AT Y PS/2 (64 Kb). También los chipset de la placa base pueden añadir soporte para esta característica. Es la memoria ubicada por encima del primer mega en los procesadores 286 y superiores. Desde el punto de vista del software. Con ello. gracias a sus mecanismos de gestión de memoria: en estas máquinas la memoria expandida es emulada por EMM386 o algún gestor similar.144 EL UNIVERSO DIGITAL DEL IBM PC. .Memoria shadow RAM. 8. En ocasiones.1. Sólo se puede acceder a la mayoría de esta memoria en modo protegido. existen unos mecanismos de gestión de memoria virtual que permiten colocar RAM en el espacio lógico de direcciones de la ROM. . .1. 8. la memoria caché es una pequeña RAM ultrarrápida que acompaña a los microprocesadores más avanzados. Este controlador implementa una serie de funciones normalizadas que además facilitan la utilización de la memoria extendida. Los microprocesadores 386 (incluido obviamente el SX) permiten además convertir la memoria extendida en expandida. La especificación XMS viene en el programa HIMEM. La memoria expandida está dividida en páginas lógicas de 16 Kb que pueden ser colocadas en las normalmente 4 páginas físicas del marco de página. por lo que su uso queda relegado a programas complejos o diversos drivers que la aprovechen (discos virtuales. optimizando las transferencias de bloques en los 386 y superiores (utiliza automáticamente palabras de 32 bits para acelerar el acceso). por ello es frecuente que un 486 a 66 MHz tenga una BIOS de sólo 8 bits a 8 Mhz. Los chips de ROM no han evolucionado tanto como las memorias RAM. HIDOS. .1.). con objeto de acelerar el acceso a la información. etc. cachés de disco duro. los programas no tienen que ocuparse de la misma.1. Son 64 bytes de memoria (128 en algunas máquinas) ubicados en el chip del reloj de tiempo real de la placa base de los equipos AT y superiores.7.8. A partir de los procesadores 386 (también 386sx) y superiores. Allí se pueden colocar bloques de 16 Kb extraídos de esos chips adicionales por medio de comandos de E/S enviados a la tarjeta de expansión. El controlador XMS también añade funciones normalizadas para acceder a la memoria superior. al contrario de lo que algunos mal informados opinan. es memoria (convencional. Otra cosa es que utilicen algún byte de la CMOS para controlar su funcionamiento).sys que pone a disposición de los programas un amplio abanico de funciones invocables por medio de la interrupción 67h. el usuario puede optar entre 384 Kb de shadow ó 384 Kb más de memoria extendida en el programa SETUP de su ordenador. A esta memoria se accede por dos puertos de E/S y en ella se almacena la configuración y fecha y hora del sistema. es factible copiar la ROM en RAM y acelerar sensiblemente el rendimiento del sistema.6. aunque se trata básicamente de memoria normal y corriente para acelerar los accesos.

Se trata de los primeros 64 Kb de la memoria extendida (colocados entre los 1024 y los 1088 Kb).2. en el ejemplo anterior se hubiera accedido efectivamente a la memoria extendida. hay BIOS muy intervencionistas que dificultan el control de A20). Los nuevos sistemas operativos DOS habilitan la línea A20 y. Por ello. En ciertos equipos poco compatibles es difícil habilitar la línea A20. .El bloque del entorno.2. Este área recibe el nombre de bloque de programa o segmento de programa. Por tanto. Todo bloque de memoria tiene asociado un propietario. ya que esta información no está oficialmente documentada por Microsoft. disponga como mínimo de 64 Kb de memoria extendida. 8. cuando un programa finaliza. dividida en grupos de párrafos. Esto se corresponde con el código de operación de la instrucción ensamblador INT 20h (o INT 27h).0 y el MS-DOS 5. Téngase en cuenta que las direcciones FFFF:0000 a la FFFF:000F están dentro del primer megabyte. el DOS busca el mayor bloque de memoria disponible (convencional o superior. evidentemente siempre que el equipo. los primeros dos bytes del PSP) que contienen la palabra 20CDh (ó 27CDh en algunos casos). El otro campo del PSP que nos interesa es el offset 2Ch: en él hay una palabra que indica el párrafo donde comienza el bloque de entorno asociado al programa. . el sistema crea dos bloques para el mismo: el bloque de memoria del programa y el bloque de memoria del entorno.1. un tema poco tratado. En los primeros 256 bytes de este área el DOS crea el PSP ya conocido -256 bytes. por tanto. identificador de proceso).COM es el bloque de entorno del COMMAND. 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. cuando se intentaba acceder fuera del primer megabyte (por ejemplo. En el HMA se cargan actualmente el DR-DOS 5.formado por varios campos de información relacionada con el programa.está. Tras el PSP viene el código del programa ejecutable. aunque normalmente el programador ejecuta directamente el INT 20h que es más seguro. Para ser exactos. según sea el caso) y se lo asigna -y no el bloque más cercano a la dirección 0. La memoria de un PC -siempre bajo DOS.SYS de Microsoft dispone de un parámetro que se puede variar probando docenas de veces hasta conseguirlo.El bloque de memoria del programa.Memoria alta o HMA. con un puntero del tipo FFFF:1000 = 100FF0) un artificio de hardware lo impedía. Cuando se ejecuta un programa. Los bloques de memoria en el DOS son agrupaciones de bytes siempre múltiplos enteros de 16 bytes: en realidad son agrupaciones de párrafos. Ese artificio de hardware lo protagoniza el chip controlador del teclado (8042) ya que la línea A20 pasa por sus manos. Vamos ahora a conocer con profundidad la manera en que el sistema operativo DOS gestiona la memoria. puede hacerlo con un salto al inicio del PSP (un JMP 0 en los programas COM) donde se ejecuta el INT 20h. Para los objetivos de este capítulo basta con conocer dos campos del PSP: el primero está en su offset 0 y son dos bytes (por tanto. 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. una palabra de 16 bits permite almacenar la dirección del párrafo de cualquier posición de memoria dentro del megabyte direccionable por el 8086. están disponibles otros 64 Kb adicionales. 8.0 y posteriores. además de ser un AT. por tanto.1. Normalmente. como algunos afirman-. convirtiendo esa dirección 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.COM (que podemos considerar como un programa residente). . esto es así por razones históricas heredadas del CP/M. si hay suerte (además.2. El espacio de entorno del COMMAND. 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 línea A20 y. Es una zona de memoria donde se almacenan las variables . 8.BLOQUES DE MEMORIA.2. gracias a ello.LA GESTIÓN DE MEMORIA DEL DOS 145 8.0/6. por lo que el HIMEM. La dirección del primer párrafo del mismo es de suma importancia y se denomina PID (Process ID.9. .

sin incluir este párrafo del MCB.. Cuando un programa finaliza su ejecución. el PRINT. Los buenos programas residentes suelen liberar el bloque de memoria del entorno antes de terminar.lo que permite a los programas saber su propio nombre y desde qué directorio están siendo ejecutados y. etc. Por ejemplo. Como todos los bloques de memoria están ubicados unos tras otros. aunque hoy casi todo el mundo sabe que las siguientes líneas: .. El nombre acaba con un cero si tiene menos de 8 caracteres (en DR-DOS 5. en muchos casos. APPEND. 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). las variables de entorno sirven para crear información que puedan usar múltiples programas. SHARE.COM. 8 15 Nombre del propietario (sólo en bloque de programa y MS-DOS ≥4. normalmente el DOS libera su bloque de memoria y de entorno. 5Ah si es el último). Los bytes que van del 5 al 7 están reservados. pudiéndose identificar los programas residentes cargados y la memoria que emplean. de esta manera. con objeto de economizar una memoria que normalmente no usan (entre otras razones porque tiene un tamaño variable e impredecible). .0. Sin embargo. GRAFTABL.Los bloques de control de memoria (MCB’s). AT Y PS/2 de entorno definidas con el mandato SET del sistema. NLSFUNC.31). aunque los usuarios suelen añadir el KEYB y. que contiene una información muy útil: la especificación completa del nombre del programa que está siendo ejecutado -incluida la unidad y ruta de directorios. y además se conoce el tamaño de los mismos. en el offset 1 hay una palabra que indica el PID del programa propietario del bloque.0. Se trata de una vulgar copia del espacio de entorno del COMMAND. .0/6.3. este añadido del DOS 3.0 ó DRDOS ≥5. 8.La cadena de los bloques de memoria. truncándose el 8º carácter si lo había. hasta que se les desinstale o se reinicialice el equipo. Las variables de entorno se almacenan en formato ASCIIZ ordinario (esto es. Cuando un programa es cargado. el programa en ejecución tiene acceso a las variables de entorno del sistema aunque no las puede modificar (estaría modificando una mera copia). GRAPHICS. PROMPT. así como con algunos comandos como PATH. Todos los bloques de memoria (tanto programa como entorno) vienen precedidos por una cabecera de un párrafo (16 bytes) que almacena información relativa al mismo.0 y posteriores parece no estar definido. 8.146 EL UNIVERSO DIGITAL DEL IBM PC. Esta cabecera recibe el nombre técnico de MCB (Memory Control Block) y tiene la siguiente estructura: offset 0 byte de marca 1 PID propietario 3 Tamaño 5 .0) En el offset 0 se sitúa el byte de marca (4Dh si no es el último MCB de la cadena de MCB’s en memoria. etc. viene una palabra que indica el número de cadenas ASCIIZ especiales que vienen a continuación: normalmente 1. la orden PATH C:\DOS es análoga a SET PATH=C:\DOS. aunque este operativo es aparentemente un DOS 3. además del bloque de memoria del programa se crea el bloque del entorno. En el espacio de entorno del COMMAND.COM. Tras la última de las variables hay otro byte más a cero para indicar el final. aunque esta información sólo existe en los bloques de programa y con MS-DOS 4. párrafos) del bloque. y sólo a partir del DOS 3. Después de esto.2. los programas residentes permanecen con el bloque de memoria y de entorno en la RAM del sistema.0 ó posterior (también en DR-DOS 5. dónde deben abrir sus ficheros (por educación no es conveniente hacerlo en el directorio raíz o en el actual). La dirección del primer MCB era al principio un secreto de Microsoft. Las variables de entorno pueden consultarse con SET (sin parámetros). Entre el 8 y el 15 se sitúa el nombre del programa propietario. esta errata ha sido corregida en DR-DOS 6.2.4. por tanto. terminadas por un byte a cero) y tienen una sintaxis del tipo VARIABLE=SU VALOR. en el offset 3 otra palabra indica el tamaño (como siempre.0). aunque se usan poco en la realidad. Como mínimo existen dos programas residentes en todo momento: el núcleo (kernel) del sistema operativo y el COMMAND.0 acaba siempre con un cero.

del sistema. Los dos primeros ya han sido ampliamente explicados..Tipos de bloques de memoria. si se sigue el siguiente orden de evaluación el resultado será siempre correcto: en primer lugar. 8.. normalmente tienen su PID como 0008. Bloque del entorno 1DB7 1DB8 Marca 4Dh PID 316F Tamaño 000B 00 variable 2 (reservados) 00 variable 3 última variable 00 00 00 variable 1 .2. Los bloques de datos aparecen en raras ocasiones. tiene asignada la mayor parte de la memoria para sí. .. debido al uso de las funciones del sistema operativo para localizar bloques de memoria.. En los nuevos sistemas operativos y en las máquinas donde la cadena de bloques de memoria puede avanzar por encima de los 640 Kb. las zonas correspondientes a RAM de vídeo y extensiones BIOS suelen tener un PID 0007 en DR-DOS (que indica área excluida) ó 0008 (MS-DOS 5. de entorno. puede volver a llamar al DOS para crear bloques de memoria (función 48h) o destruirlos (con la función 49h). aunque sólo sea para saltarlos de alguna manera. Los bloques del sistema se corresponden con el kernel o núcleo del sistema operativo o los dispositivos instalables. .. (offset 0) 20CDh (offset 2Ch) 1DB8 8.LA GESTIÓN DE MEMORIA DEL DOS 147 MOV INT MOV AH.6. se trata de un bloque del programa (recuérdese que el MCB lo precede inmediatamente). (reservados) (nombre propietario) P R O G R A M A . Cuando un programa se ejecuta. Si el PID no apunta a un PSP (no apunta a un área que empieza por 20CDh ó 27CDh) se trata entonces de un bloque del sistema. El siguiente esquema aclarará la relación existente entre el bloque de programa y el de entorno.Relación entre bloque de programa y de entorno. pero es perfectamente factible que solicite al DOS una reducción de la memoria asignada (función 4Ah) y.2. Los bloques libres tienen un PID 0000. (más variables terminadas en 0) . Básicamente existen cinco tipos de bloques de memoria: bloques de programa.. utilizando la función indocumentada 52h del sistema operativo. A la hora de recorrer la cadena de bloques de memoria.ES:[BX-2] devuelven en AX la dirección del primer MCB de la cadena. Los valores numéricos que figuran son arbitrarios (pero correctos)..0) y son consideradas como bloques de memoria ordinarios. 00 0001 C:\UTIL\VARIOS\PROGRAMA.. bloques de datos y bloques libres.EXE Bloque del programa 316E 316F Marca 4Dh PID 316Fh Tamaño 1C70 .52h 21h AX. Si el PID apunta a un PSP en cuyo offset 2Ch una palabra apunta al . si aparece un PID 0000 significa que es un bloque libre. Si el PID apunta al MCB+1.5. con los Kb que haya liberado. El PID 0006 (sólo aparece en DR-DOS) indica que se trata de un bloque de memoria superior XMS.

código del . desde el DOS 4. bloque destruido Alternativamente. después un alto porcentaje de los mismos se olvida de liberar el espacio de entorno.148 EL UNIVERSO DIGITAL DEL IBM PC. obtener dirección del PSP en BX . A partir del DOS 4.0. no es válida para DR-DOS aunque algunos aspectos concretos puedan ser comunes. Si ES apunta al PSP: MOV DEC MOV MOV AX.62h 21 ES.FCBS= (área de almacenamiento de estas estructuras) "C" .controlador de dispositivo "E" .STACKS= (zona de código y datos de las pilas del sistema) "T" .IFS (Installable File System) driver "F" . .2. por eliminación ha de ser un bloque de datos. liberar bloque (PID=0) 8.0 y posteriores. que para nada utilizan y que suele ocupar incluso más memoria que todo el PSP.2. el primer bloque de memoria es un segmento de datos del sistema. los bloques propiedad del sistema tienen el nombre "SC" (System Code.LASTDRIVE= (área de almacenamiento de las CDS) "S" . que contiene los drivers instalados desde el CONFIG. La información siguiente explica las particularidades de los bloques de memoria con MS-DOS 4.BX ES. se trata del bloque del entorno de ese PSP.INSTALL= (área transitoria de este mandato) Palabra. nombre del fichero que cargó el driver.ES:[2Ch] AH.0 como mínimo si se obtiene la dirección del PSP utilizando la función 62h): MOV INT MOV MOV MOV INT AH.0 . dirección del espacio de entorno . puede despreciarse y tomar el que viene inmediatamente a continuación (párrafo siguiente) para recorrer los subsegmentos conectados.49h 21h . indica el tipo de subsegmento: "D" . .7. Si no es ninguno de estos últimos bloques. La manera de liberar el espacio de entorno antes de que un programa quede residente es la siguiente (necesario DOS 3. se puede liberar directamente el bloque de memoria del entorno poniendo directamente un 0 en su PID.8.FILES= (área de almacenamiento de estas estructuras. una vez localizado el primer MCB. dirección del espacio de entorno AX . función para liberar bloque .AX WORD PTR ES:[1]. cada uno de ellos precedidos de un bloque de control de memoria con el siguiente formato: offset 0: Byte.SYS. indica el tamaño del subsegmento (en párrafos) 8 bytes: en los tipos "D" e "I". AT Y PS/2 MCB+1. Resulta triste ver como algunos sofisticados programas residentes llegan incluso a autorrelocalizarse en memoria machacando parte del PSP con objeto de economizar algunos bytes. Desde el MS-DOS 3.0 y posteriores.1.ES:[2Ch] . offset 1: offset 3: offset 8: Por tanto. este bloque de memoria está dividido en subbloques. si FILES>5) "X" . En el DOS 5.BUFFERS= (área de buffers) "L" .BUFFERS= /X (área de buffers en memoria expandida) "B" . 8.0 y siguientes. apuntar a su MCB ES.Peculiaridades del MS-DOS 4. indica dónde comienza el subsegmento (normalmente a continuación) Palabra.extensión de controlador de dispositivo "I" . aunque es menos elegante.Liberar el espacio de entorno en programas residentes.0.

tampoco es tan importante. Desde la versión 5. se comprueba si el siguiente empezaría en el segmento 9FFFh en vez del A000h como cabría esperar en una máquina de 640Kb (sólo suelen tener memoria superior las máquinas que al menos tienen 640 Kb).LA GESTIÓN DE MEMORIA DEL DOS 149 sistema o áreas de memoria superior excluidas) o bien "SD" (System Data.). imprimiéndose también el nombre.Cómo recorrer los bloques de memoria.0. 8 Bytes: "UMB" si es el primer bloque UMB y "SM" si es el último. el DOS 5. saltando la barrera de los 640 Kb.0. no todo el mundo se preocupa de ello y. con controladores de dispositivo. puede emplearse la función. y funciona en todas las versiones. en ese caso. es norma común en el código generado por los compiladores realizar esta operación al principio. pero sí gran parte. Si esto es así no se considera que el bloque sea el último y se prosigue con el siguiente. 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. si no lo están. este área puede estar desconectada por completo de los primeros 640 Kb. Para comprobar si existe memoria superior utiliza una técnica muy sencilla: al alcanzar el último bloque de memoria. de hecho. por ello en el resultado figura ocupando sólo 1440 bytes y teniendo tras de sí un gran bloque libre. en DR-DOS 6. las cosas pueden cambiar un poco en esta zona de memoria: si tienen instalado algún gestor de memoria extraño. En líneas generales. El programa de ejemplo listado más abajo recorre toda la memoria sin adentrarse en las particularidades de ningún sistema operativo. Sin embargo. La organización de la memoria varía según la versión del sistema operativo instalada.0 del DOS.9.0 se encarga de machacar el último MCB de la memoria convencional y no deja ni rastro) aunque sí con MEMMAX +U. Esta técnica funciona sólo a partir del MS-DOS 5. 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 información sobre buffers de disco. para poder acceder a los bloques de memoria superior éstos han de estar ligados a los de la memoria convencional: para conectarlos. Sin embargo. aunque en DOS 3. es más sencillo ignorar la memoria superior como una entidad independiente y recorrer toda la memoria sin más. Además. estos bloques "SD" contienen subbloques con las mismas características que los del DOS 4. mostrar también su contenido.30 y versiones anteriores salga basura. 8. En general. Este algoritmo puede no enseñar todo lo que podría enseñar gracias a las últimas versiones del DOS. Palabra con el tamaño del bloque en párrafos. Adicionalmente. . a fin de cuentas. cuya dirección inicial a su vez se obtiene en el puntero largo que devuelve en ES:BX+12h la función indocumentada Get List of Lists (52h): normalmente el resultado es el segmento 9FFFh.0. obviamente.0 existen funciones específicas del sistema para estas tareas. . todo lo comentado hasta ahora -excepto lo del apartado anterior. desde el MS-DOS 5. si la memoria superior está inhibida con MEMMAX -U. no funciona (DR-DOS 6.bajo DR-DOS 6. La primera acción de MAPAMEM al ser ejecutado es rebajar la memoria que tiene asignada hasta el mínimo necesario. en las diferentes áreas en que puede estar fragmentada.es válido para cualquier versión del DOS. Sin embargo. los 16 bytes que faltan para completar los 640 Kb de memoria son precisamente un MCB.0.2. En este caso. 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. el formato de los bloques de control UMB es el siguiente: offset offset offset offset 0: 1: 3: 8: Byte con valor 5Ah para el último bloque y 4Dh en otro caso. Tan sólo se toma la molestia de intentar detectar si existe memoria superior y. en las máquinas que tienen memoria superior. Con DR-DOS el usuario puede utilizar el comando MEMMAX para habilitar o inhibir el acceso a la memoria superior. etc. En cualquier caso. Palabra con el PID.0 introdujo los bloques denominados UMB que recorren la memoria superior. 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 información después de alterarla. También se imprime el nombre de los programas.

supuesta zona del sistema DS DS. Tipo -------Sistema Sistema Sistema Sistema Programa Entorno Datos Programa Entorno Programa Entorno Programa Libre Sistema Sistema Sistema Sistema Sistema Sistema Sistema Sistema Sistema Sistema Programa Programa Programa Programa Area XMS Programa Area XMS Area XMS Programa Area XMS Area XMS Libre Sistema Sistema Ubicación --------0000-003F 0040-004F 0050-023C 023E-02FD 02FF-031E 0320-033F 0341-0358 035A-03EE 03F0-0408 040A-041D 041F-0437 0439-0492 0494-9FFE A000-DEFF DF01-E477 E479-E483 E485-E48D E48F-E591 E593-E7DA E7DC-E806 E808-E810 E812-E81A E81C-E8DE E8E0-EA51 EA53-EA60 EA62-EA6E EA70-EA7F EA81-EA8F EA91-EAC0 EAC2-EB17 EB19-EB30 EB32-EDB4 EDB6-EEEC EEEE-EF4F EF51-EFFE F000-F5FF F601-F6FF Tamaño PID Propietario ------.072 0008 512 02FF COMMAND 512 02FF COMMAND 384 02FF COMMAND 2. puntero al siguiente MCB no.Utilidad para listar los bloques de memoria.576 0007 4.024 Interrupciones 256 Datos del BIOS 7.120 0008 5.tam_mapmem . imprime_rango .384 0008 176 0008 144 0008 4.BX AX.cabx_txt print BX.240 Sistema Operat.272 0008 592 0000 <Nadie> 208 E181 DOSVER 2. ¿es el último? . printAL AL.2 .640 E864 DATAPLUS 1.440 0439 MAPAMEM 636.784 0000 <Nadie> 24. programa tipo COM no_tipo_sys: mapamem mapa otro_mcb: PROC MOV MOV INT LEA CALL MOV INT MOV MOV DEC CALL INC SUB MOV MUL MOV CALL LEA CALL MOV MOV CMP JE MOV CMP JE MOV PUSH MOV MOV MOV POP CMP tipo_ok: BX.I.B.--------------1. supuesta zona libre (tipo DL) BX.920 E8E0 GRAPHICS 224 EA53 CLICK 208 EA62 DOSVER 256 EA70 ALTDUP 240 0006 B1M92VAC 768 EA91 VSA 1. segmento del primer M.ES .384 035A MATAGAME 400 040A KEYRESET 320 040A KEYRESET 400 0439 MAPAMEM 1.----.920 E23E GRAPHICS 6.WORD PTR DS:[0] .144 0008 9.16 DX .50h DX. .AX AX print16hex . AT Y PS/2 Un ejemplo de la salida que puede producir este programa es el siguiente.tabla_tipos AL. Tipo -------Sistema Sistema Sistema Sistema Sistema Programa Libre Entorno Entorno Programa Libre Sistema Sistema Libre Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Datos Libre Ubicación --------0000-003F 0040-004F 0050-0252 0254-045F 0461-0464 0466-050E 0510-0513 0515-0544 0546-0567 0569-05C2 05C4-9FFE A000-D800 D802-E159 E15B-E17F E181-E18D E18F-E23C E23E-E3AF E3B1-E533 E535-E637 E639-E7E2 E7E4-E840 E842-E862 E864-ECF0 ECF2-ED59 ED5B-ED7E ED80-ED8C ED8E-ED93 ED95-F6D4 F6D6-F6FF Tamaño PID Propietario ------. CX. pasar párrafos a bytes CL. 3.27CDh tipo_ok .DL imprime_tipo . lo es (PID = 6) DL. tomado de una máquina con memoria superior y bajo los dos sistemas operativos más comunes (aunque en los ejemplos los espacios de entorno han coincidido junto al bloque de programa.cabecera_txt print AH.0 MAPAMEM 2. imprimir dónde acaba el DOS AX AX.384 0008 64 0008 2. AX.976 0006 DATAPLUS 1.Información sobre la memoria del sistema. ejecutar función del DOS DX.AX otro_mcb . .ES AX BX.BX tipo. supuesto bloque XMS de DR-DOS BX.BX otro_mcb AX.----. CX = [PID:002C] DS AX. DL.816 E639 PRINT 1.888 Sistema Operat.--------------1.tipo .Información sobre la memoria del sistema.AX tipo_ok DL .13 .10 . (Process ID) DL.888 ED8E TDSK 672 0000 <Nadie> .C.144 E535 DOSKEY 6. lo es (PID = 0) DL.440 0569 MAPAMEM 631.392 0008 38.048 0007 22. AX 12h BX. imprime_pid imprime_nombre AL.64 BX AX BX. ello no siempre sucede así).ES:[3] .150 EL UNIVERSO DIGITAL DEL IBM PC. P.6 tipo_ok . pid.4C00h 21h es un PSP no es un PSP supuesta zona de programa ¿PID=MCB+1? lo es supuesta zona de entorno por eliminación zona de datos tipo del bloque ubicación y tamaño retorno de carro salto de línea MCB ya tratado tamaño del bloque .344 0008 688 0008 144 0008 144 0008 3. AX BYTE PTR ES:[0].4 . AX = [PID:0000] CX.5Ah ES. modificar memoria asignada 21h .024 Interrupciones 256 Datos del BIOS 8. no era el último . DS:mapamem ORG 100h .592 0000 <Nadie> 258. 8. tipo_ok .WORD PTR ES:[1] . imprimir tamaño zona del DOS DX.704 0466 COMMAND 64 0000 <Nadie> 768 0466 COMMAND 544 0569 MAPAMEM 1. . printAL AX.192 E3B1 SHARE 4. AX.WORD PTR DS:[2Ch] .20CDh JE CMP JNE MOV MOV INC CMP JE MOV CMP JE INC MOV MOV CALL CALL CALL CALL MOV CALL MOV CALL MOV ADD INC CMP MOV JNE PUSH INT MOV MUL DEC MOV POP CMP JE MOV INT ENDP PROC LEA MOV no_tipo_sys .AX .0 tipo_ok .52h . ¿hay RAM superior (DOS 5)? .1 .664 ECF2 HBREAK 576 ED5B ANSIUP 208 ED80 PATCHKEY 96 ED8E TDSK 37.0 . ES. AX.288 EB32 VWATCH 4.080 0008 MS-DOS 5.AX AX AX.2 .376 0006 RCLOCK 384 0006 DISKLED 10.2 .0 MAPAMEM 2. fin del programa mapa imprime_tipo SI. ******************************************************************** * * * MAPAMEM 2. función "Get List of Lists" 21h AX. así es .ES:[BX-2] .8+16 print_32 .488 E7E4 RCLOCK 528 E842 DISKLED 18. apuntar al siguiente MCB .784 E18F NLSFUNC 5. tamaño de este programa AH.4Ah . 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.2 .728 0000 <Nadie> 229. . * * * ******************************************************************** SEGMENT ASSUME CS:mapamem.568 0006 HBREAK 2.D. DL.3 .

fracc_pr32 acabar_pr32 BYTE PTR [BX-1]. último dígito ent_frac_pr32.3 imprimir nibble más significativo restaurar AL y preservarlo de nuevo dejar nibble menos significativo e imprimirlo poner_pr32: limpiar_pr32: .4 AL.DI . DXAX = DXAX + DISI CL. transferencias hacia atrás BX .0 .AX DI.CL CX . . No requiere ningún registro de segmento apuntándola. Si se intenta.’9’ no_sup9 AL. DXAX = DXAX .16 . . AX = tipo * 2 .ent_frac_pr32 CX.ES:[3] print16hex AX. . longitud obtenida del número . .’A’-’9’-1 printAL AX no_frac_pr32: entera_pr32: . byte del formato de impresión BX. ningún registro modificado. CX se recuperará más tarde CL.DI AX. .2 JE nombre_ok MOV BX.CL print4hex AX AX AL.SI DX. imprimir final del bloque .AL JZ nombre_ok CMP AL. . Si se especifican. . imprimir guión . dirección del mensaje . . cero a la izda --> poner " " AL.BX .’0’ AL.’?’ cod_normal: CALL printAL LOOPNZ otra_letra nombre_ok: POP ES RET imprime_nombre ENDP print PROC PUSH PUSH MOV INT POP POP RET ENDP PROC PUSH PUSH MOV MOV INT POP POP RET ENDP PROC PUSH ADD CMP JBE ADD CALL POP RET ENDP PROC PUSH PUSH MOV SHR CALL POP PUSH AND CALL POP POP RET ENDP PROC PUSH MOV CALL POP CALL RET ENDP AX CX AH. evitar códigos raros en DOS < 4.8 otra_letra: INC BX MOV AL.CH SI. . -------------------------.. . registros usados preservados función de impresión del DOS carácter a imprimir llamar al sistema .DX .ES AX.1 SI.formato_pr32 AL.fracc_pr32 [DI]. . DISI * 8 SI. se apoya en .principio_pr32 .DISI rep_sub_pr32 .CL .’ ’ printAL printAL imprime_pid imprime_nombre PROC PUSH ES LEA DX. pasar bits 4. . cadena arriba (hacer hueco) AL. pasar bytes a párrafos . . imprimir cadena en DS:DX con . . . . pocos dígitos en la parte entera (=demasiados en la fraccional) no tiene sentido imprimir el separador de millares. recuperar registros .SI DI MOVSB . PROC PUSHF PUSH PUSH PUSH PUSH PUSH PUSH PUSH PUSH MOV MOV MOV MOV MOV MOV PUSH PUSH PUSH XOR MOV DEC JCXZ SAL RCL MOV MOV SAL RCL SAL RCL ADD ADC LOOP POP POP MOV INC SUB SBB JNC ADD ADC ADD MOV POP INC LOOP STD DEC MOV MOV MOV MOV SHR AND JZ MOV XOR MOV MOV INC REP INC MOV MOV MOV MOV TEST JZ MOV SUB ADD MOV MOV INC REP MOV MOV INC MOV SUB CMP JAE MOV MOV MOV MOV MOV CMP JE CMP JE CMP JNE MOV DEC MOV AND XOR MOV SUB INC AND JNZ MOV print_32 imprime_rango imprime_pid AL.1 .AX format_pr32 . imprimir palabra hexadecimal (AX) AX AL.SI . no es letra lo es imprimir dígito hexadecimal restaurar AX printAL print4hex no_sup9: print4hex print8hex AX AL. Entradas: Si bit 4 = 1 --> se imprimirán signos separadores de millar bits 0-3 = nº total de dígitos (incluyendo separadores de millar y parte fraccional) bits 5-7 = nº de dígitos de la parte fraccional (cuantos dígitos de DXAX."$" .SI DI MOVSB .1 . interpretar el formato poner_pr32 .1 DI.DI . CX se recupera ahora BX digit_pr32 .millares_pr32 [DI].final_pr32 .SI SI. ninguno CL.SI . imprimir como tal CX. imprimir carácter en AL AX DX AH. DISI=DISI*(10^(CX-1)) DX AX . próximo dígito del número .’0’ . apuntar al MCB . CL. . imprimir tamaño del bloque . . la rutina «print» para imprimir la cadena DS:DX delimitada por ’$’.final_pr32 DX. preservar registros BX CX DX SI DI DS ES BX. .’ ’ printAL printAL AX. .’0’ blanco_pr32 .00001111b AH.libre_txt CMP tipo.pid print16hex AL. . imprimir parte alta . próximo separador BX.AX DX.8+16 print_32 . Subrutina para imprimir nº decimal de 32 bits en DXAX formateado. preservar AX . imprimirlo .final_pr32 DI. indicar nueva frontera AL. . imprimir AL. DISI=DISI*8+DISI*2=DISI*10 factor_pr32 .1 DI.5 AL. la rutina podría colgarse porque no valida el formato.9 21h CX AX digit_pr32: .OFFSET tabla_pr32 SI. . inicio de cadena AL. empezando por la derecha.ES:[3] DX. usar la variable como puntero SI.formato_pr32 AL. restar factor cuanto se pueda AX. bloque XMS: nombre de ES:8 a ES:16 .3 entera_pr32 .BX formato_pr32. subsanar el desbordamiento: DX. pasar binario a ASCII .BX .20 Tener cuidado al especificar la plantilla para que ésta se adapte al número a imprimir.1 JE nombre_listo CMP tipo.7 a 0.ES:[1] DEC BX MOV ES.0 JNE no_libre CALL print JMP nombre_ok no_libre: CMP tipo.1 hecho_pr32 SI.SI .0FFh CL AX.BX ES. . retornar .ES:[BX] AND AL.LA GESTIÓN DE MEMORIA DEL DOS 151 imprime_tipo imprime_rango XOR SHL ADD MOV CALL RET ENDP PROC MOV INC CALL MOV CALL MOV ADD CALL MOV MOV MUL MOV CALL RET ENDP PROC MOV CALL CALL MOV CALL MOV CALL CALL RET ENDP AH. e irán precedidos del correspondiente separador) Salidas: nº impreso. . CX .’0’ .16 DX CL. imprimir byte hexadecimal en AL CX AX CL. pasar binario a ASCII [BX]. delimitador fin de cadena BX.’ ’ JAE cod_normal MOV AL.[SI] print . por ej.BX . segmento del PSP dueño del bloque .millares_pr32 . AL = nº de decimales AL.final_pr32 BYTE PTR [BX+1]. . cadena arriba (hacer hueco) final_pr32 AL.final_pr32 DI. si DXAX=9384320 y CL=010 1 1011 se imprimirá ( ’_’ representa un espacio en blanco ): __93.AL . DX = offset ’principio’ AX. no . a por otro (8 como máximo) hecho_pr32: rep_sub_pr32: factor_pr32: . . imprimirlo AX.. reponer 0 antes de la coma principio_pr32 AL. máximo tamaño del nombre .1 -------------------------.ES AX print16hex AL.3 SI.’-’ printAL AX. longitud solicitada DX. imprimir parte baja acabar_pr32: print8hex print16hex print16hex . . imprimirlo .AH DX. imprimir carácter hexadecimal (AL) .AH print8hex AX print8hex . .10 CX AX DX DI.OFFSET tabla_pr32 CX. .CL .AX DX .7 MOV CX. nombre del propietario desconocido .. separador de parte fraccional ent_frac_pr32. AX .2 DL.843. DISI * 2 DX. imprimir inicio del bloque .AH AX. DISI = 1 CX .1111b print4hex AX CX . es cero: fin del nombre .BX nombre_listo: MOV BX.DI SI.1 SI. * Ejemplo. BX apunta al último dígito final_pr32. . poner separador de millares final_pr32 ent_frac_pr32.AL .CS DS.1 DI. añadir separadores de millar CX. nombre de ES:BX+1 a ES:BX+9 .[BX] AL.1 . carácter del nombre .PRINT-32 v3. . separador millares a la izda blanco_pr32 AL.OFFSET tabla_pr32 principio_pr32.AL CH. . se consideran parte fraccional. el final delimitado por un ’$’ print printAL . .AL no_frac_pr32 . frontera parte entera/fracc. ¿bloque libre? .AL 21h DX AX .formato_pr32 .

Datos cabecera_txt LABEL BYTE DB 13. área de trabajo .---------------" DB 13. sin embargo.3. para asignar dicha memoria a los programas que la solicitan (a los que devuelve un handle de control.13. Digamos que la memoria extendida XMS es como un gran banco o almacén de memoria torpe. Con MS-DOS 5. liberarla. Los bloques de memoria superior no son accesibles de manera directa por los programas.Información sobre la memoria del sistema. Sin embargo."$" . offset último byte a imprimir ." .’ 0 0 0 . .2" DB 13. Estos bloques de memoria son gestionados de manera independiente a los de la memoria convencional.10.". El MS-DOS 5. offset frontera entero-fracc.152 EL UNIVERSO DIGITAL DEL IBM PC."Sistema 0040-004F 256 Datos del BIOS" DB 13.10. el programa deberá dejar algo residente en memoria convencional (si no se termina residente. 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. tipo_datos "Libre $" "Area XMS $" "Sistema $" "Programa $" "Entorno $" "Datos $" "<Nadie>$" 0 0 ($-OFFSET mapamem)/16+1 mapa . salida del procedimiento . -----------. sustituyendo por espacios tipo_libre. separador de millares . de la manera más óptima y rápida según el tipo de CPU que se trate. restaurar todos los registros . igual que cuando se abre un fichero).10. a menos que éstos sean expresamente cargados en este área con HILOAD ó LOADHIGH.cuando se indica DOS=UMB en el CONFIG. para averiguar la memoria extendida disponible. tamaño de MAPAMEM 8.000. El controlador XMS implementa una serie de funciones para acceder de manera sencilla a la memoria extendida.--------. que además de la memoria extendida gestiona también estas áreas. hay funciones para asignar y liberar el HMA (frecuentemente ya estará ocupado por el sistema operativo). bien sea entre zonas de la memoria extendida o entre la memoria convencional y la extendida. tipo_sistema tipo_programa.10 DB "-------. tipo_xms. Después es conveniente restaurar la estrategia de asignación y el estado de la memoria superior a la situación inicial (también se puede consultar previamente con la función 58h). tipo_libre tipo_xms tipo_sistema tipo_programa tipo_entorno tipo_datos libre_txt tipo pid tam_mapmem mapamem . reservan toda la memoria superior para sus propios usos -cargar programas residentes. es el número 0.0 y posteriores."Sistema 0050-$" cabx_txt tabla_tipos DB DW DW DB DB DB DB DB DB DB DB DW EQU ENDS END " Sistema Operat. se economiza algo de memoria al poder suprimirse el PSP en la copia.10 DB "Tipo Ubicación Tamaño PID Propietario"."Sistema 0000-003F 1.10. quitar 0 / separador millares . devolver la dirección física para quien desee realizar transferencias directas y lo más interesante: para mover bloques."MAPAMEM 2. imprimir cadena en DS:DX .BX SHORT acabar_pr32 0 5 DUP (’ ’) 0 0.0 y algunos gestores de memoria.10. por lo que si alguna aplicación solicita memoria superior XMS no la encontrará. " parte fraccional . el controlador XMS añade funciones para gestionar la memoria superior. Adicionalmente. 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. " " primer " " " . Por otra parte. anticipándose a la actuación de usuarios inexpertos que podrían olvidarse del HILOAD o el LOADHIGH. los programas pueden solicitar zonas de memoria superior al controlador XMS.13. tipo_entorno.13.10. no obstante.0 y posteriores. imprimir . cambiar la estrategia de asignación de memoria para que el sistema asigne memoria superior en respuesta a las funciones ordinarias de asignación de memoria.------. con la misma función.000. Realmente.SYS. AT Y PS/2 CALL POP POP POP POP POP POP POP POP POPF RET blanco_pr32: MOV INC INC CMP JB MOV JMP formato_pr32 DB DB tabla_pr32 DT DW millares_pr32 EQU fracc_pr32 EQU final_pr32 DW principio_pr32 DW ent_frac_pr32 DW print_32 ENDP format_pr32: print ES DS DI SI DX CX BX AX BYTE PTR [BX].’ ’. . existiendo funciones específicas del controlador XMS para localizar y liberar los bloques. En principio. en la memoria superior pueden residir tanto bloques de memoria DOS gestionados por el sistema (normalmente. las zonas que emplea el DR-DOS no son sino bloques de este tipo de memoria.’ ’ BX principio_pr32 BX.----.MEMORIAS EXTENDIDA Y SUPERIOR XMS. del que podemos traer o llevar datos y nada más. así como auténticos bloques de memoria XMS.10.final_pr32 limpiar_pr32 DX.0 ’. Con DR-DOS 6.024 Interrupciones" DB 13. como consecuencia de un HILOAD para instalar programas residentes). para controlar la línea A20 (en la actualidad suele estar permanentemente habilitada).". Pero se puede emplear la función 58h para conectar la memoria superior y a continuación.00X .

SYS o alguno equivalente (el EMM386 del DR-DOS 6. realiza esta complicada maniobra). Se pueden modificar los primeros bytes del mismo para poner un salto hacia nuestra propia rutina. es factible acceder simultáneamente a cuatro páginas lógicas entre todas las disponibles.ES AX. cualquier página lógica puede ser mapeada sobre una de las cuatro páginas físicas. chequear presencia de XMS . por medio de la subfunción 10h: MOV INT MOV MOV AX. los restantes están cubiertos de instrucciones NOP (código de operación 90h).x la INT 2Fh está indefinida .BX . Para utilizar las llamadas al XMS es preciso que en la pila queden al menos 256 bytes libres. Si por cualquier motivo fuera necesario en un programa residente interceptar las llamadas al controlador XMS realizadas por los programas de aplicación. como sucede con las del DOS o la BIOS. todos los controladores XMS suelen comenzar con una instrucción de salto larga o corta (JMP XXXX:XXXX.ES XMS_off. así como cierto número de páginas lógicas asociado al mismo. a veces otras como 0C8000h. si ésta ocupa menos de 5 bytes. JMP SHORT XX) y. JMP XXXX. interrupción Multiplex 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. Por supuesto. Ese segmento se denomina marco de página de la memoria expandida. Para ello se chequea la entrada 43h en la interrupción Multiplex. como se comentó al principio del capítulo. en DOS 2. Las funciones del controlador XMS no se invocan por medio de ninguna interrupción. Por fortuna.4. se le asigna un handle de control (un número de 16 bits) que la referencia.0 no_hay_XMS AX.) dividido en cuatro páginas adyacentes de 16 Kb.LA GESTIÓN DE MEMORIA DEL DOS 153 Para poder emplear los servicios del controlador XMS hay que verificar primero que está instalado el programa HIMEM. hay que decir que ello es posible. esta extensión consiste en un segmento de memoria de 64 Kb (en la dirección 0D0000h o 0E0000h.0 integra también las funciones del HIMEM.80h hay_XMS no_hay_XMS . Las cuatro páginas son las páginas físicas numeradas entre 0 y 3. Hasta la versión 3 del controlador de memoria expandida. que luego acabe llamando a su vez al controlador previo (el RAMDRIVE de Microsoft. En su lugar. una vez detectada la presencia del mismo se le debe interrogar preguntándole dónde está instalado.x del DOS está sin inicializar y el sistema se cuelga si se invoca sin precauciones. es una técnica de paginación para solventar la limitación de 640 Kb de memoria de los PC.SYS.. preguntar dirección del controlador . comprobando si devuelve 80h en el registro AL (y no 0FFh como otros programas residentes): MOV INT MOV CMP JE MOV INT CMP JE JNE AX. En un apéndice al final del libro se listan y documentan todas las funciones XMS. por ejemplo. así como el QEMM386).352Fh 21h AX. etc. 8. De este modo. obtener vector de INT 2Fh en ES:BX . almacenarla donde XMS_seg y XMS_off es una estructura del tipo: gestor_XMS XMS_off XMS_seg LABEL DWORD DW 0 DW 0 Posteriormente. Por . La memoria expandida. A partir de ese momento.MEMORIA EXPANDIDA EMS. cuando haya que utilizar un servicio o función del controlador XMS se colocará el número del mismo en AH y se ejecutará un CALL gestor_XMS.4310h 2Fh XMS_seg. Cuando un programa solicita memoria expandida. no es tan sencillo como desviar un vector de interrupción: hay que modificar el código del propio controlador.4300h 2Fh AL.

hay unas funciones de control IOCTL en el DOS para asegurar que se trata de un dispositivo y no de un fichero. Desde la línea 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 algún gracioso haya creado!: para cerciorarse. 45h . 4Dh . aunque algunos equipos 286 ya la tienen integrada en la placa base. 42h . 47h .2) son: 40h . si bien en muchos casos no son necesarias. 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!. la CPU puede ser colocada en modo virtual 86.Obtener información de todos los handles que hay y las páginas que tienen asignadas. El primero consiste en buscar un dispositivo "EMMXXXX0".Preguntar el número de páginas libres que aún no están asignadas. Para utilizar la memoria expandida hay que invocar la interrupción 67h. sin necesidad de una extensión hardware. Es tan sencillo como intentar abrir un fichero con ese nombre y comprobar si existe. igual que cuando se abre un fichero). aunque es un tanto absurdo.154 EL UNIVERSO DIGITAL DEL IBM PC. DFFFF 16 Kb DC000 2 D8000 1 D4000 0 D0000 MARCO DE PÁGINA DE MEMORIA EXPANDIDA (PÁGINAS FÍSICAS) 3 A B C D E F G PÁGINAS DE MEMORIA EXPANDIDA ASIGNABLES (PÁGINAS LÓGICAS) En este ejemplo se ha solicitado al EMM 8 páginas (numeradas en el gráfico A-G) y cualquiera de ellas puede ser «colocada» (paginada) en cualquiera de las 4 páginas físicas. a elegir. Algunos sistemas de memoria expandida real (no emulada) pueden soportar incluso una reinicialización del PC sin perder el contenido de esa memoria. Las principales funciones (soportadas por EMS 3.3).Salvar el contexto del mapa de páginas (usado por los TSR para no alterar el marco de página).0 del controlador..Liberar las páginas asignadas.Asignar páginas (esta función devuelve un handle de control. una variante del modo protegido en la que la memoria expandida puede ser emulada por las técnicas de memoria virtual de este microprocesador..Mapear páginas (colocar una cierta página lógica 0. .Obtener el segmento del marco de página (no tiene por qué se 0D000h ni 0E000h). 48h . AH normalmente valdrá 0 para indicar que todo ha ido bien. La memoria expandida se implementa con una extensión del hardware.SYS y define un controlador de dispositivo de caracteres con ese nombre. Estas funciones se numeran a partir de 40h. 44h . 46h . ya que el gestor de memoria expandida se carga desde el CONFIG. aunque desde la 4Fh sólo están disponibles a partir de la versión 4. En los 386 y superiores. que también puede ser empleado de manera general en cualquier otra aplicación. AT Y PS/2 ello es posible incluso asignar la misma página lógica a más de una página física. Para detectar la presencia del controlador hay dos métodos.N en una de las físicas 0. 41h . no es recomendable este método para detectar el EMM en los programas residentes y en los controladores de dispositivo: existe otro medio más conveniente para esos casos. En un apéndice al final del libro se listan y documentan todas las funciones EMS. Las funciones del EMM se invocan colocando en AH el número de función y ejecutando la INT 67h: a la vuelta. para que puedan usarlas futuros programas (¡es vital!).Preguntar la versión del controlador de memoria expandida. La principal utilidad de la memoria expandida es de cara a almacenar grandes estructuras de datos evitando en lo posible un acceso a disco.Obtener el estado del controlador (ver si es operativo y la memoria EMS puede funcionar bien).Restaurar el contexto del mapa de páginas (usado por los TSR para no alterar el marco de página). Sin embargo. 43h .

seleccionando el chip de RAM que se utiliza. dicho entre comillas. ¡ese no es el controlador de memoria!. como la INT 67h. habrá algún medio de poder acceder al código del controlador EMS. una rapidísima función coloca en el espacio de direcciones del 8086 la memoria que va a ser accedida: allí mismo puede ser procesada sin necesidad de movimiento físico. en cualquier caso se va a producir un movimiento físico (¿qué mas da que sea hacia la EMS que hacia la XMS?).0. Si se mira con el DEBUG a donde apunta la INT 67h en una máquina con QEMM (por ejemplo. el controlador de memoria está ubicado fuera de ese espacio de direcciones. en el sistema 386 en que se escribieron las primeras versiones de este libro. no es tan fácil como cambiar un vector. Sin embargo. la memoria expandida soportada a partir de las versiones 4. Para acceder a él hay que ejecutar una interrupción de verdad.LA GESTIÓN DE MEMORIA DEL DOS 155 La memoria expandida. cubriendo también los primeros 640 Kb (excepto los primeros 64 Kb). no siendo recomendable modificar esta especificación. Supongo que a través de la especificación VCPI (Virtual Control Program Interface) que regula el acceso a los modos extendidos del 386. la EMS no aumenta el rendimiento: por ejemplo. habrá que transferir datos desde la memoria convencional a la XMS ó la EMS. Sin embargo.. la CPU -de la manera que está programada. por defecto -y por razones de compatibilidad.pasa inmediatamente a continuación a ejecutar una rutina en modo protegido fuera del espacio de direcciones del MS-DOS. sin embargo. lejos de ser sólo un invento obsoleto para superar los 640K en los viejos ordenadores.las cuatro primeras páginas físicas están colocadas adyacentemente por encima de los 640K y son de 16 Kb. Esto significa que cuando un programa invoca una interrupción. Por ejemplo. En los modernos sistemas operativos. con la memoria expandida EMS. es una de las memorias más versátiles disponibles bajo DOS.. Sin embargo. Si alguien está pensando en desviar la interrupción 67h desde un programa residente. o interceptar las llamadas. como el EMM386 del DR-DOS 6.0 del EMM (Expanded Memory Manager) cubre un amplio espectro del espacio de direcciones dentro del megabyte gestionado por el MS-DOS. las páginas no han de ser necesariamente consecutivas. son más de 4 y tampoco tienen que ser necesariamente de 16 Kb. al construir un disco virtual. procesarla y volverla a copiar a la memoria extendida. hay que traerla (copiarla) a la memoria convencional. con un EMM 4. sin existir movimiento físico de datos. Aquí. Con algunos gestores de memoria. traceando una llamada a la interrupción). se verá que este vector apunta al siguiente código: INT 28h IRET Evidentemente. Sin embargo. las páginas 4 a la 27h estaban ubicadas entre la dirección 10000h a la 9FFFFh. no sucede nada: ese programa supervisor retorna a la tarea virtual y ejecuta el código ubicado en el espacio de direcciones del MS-DOS. para interceptar y manipular las llamadas de los programas de aplicación a esa interrupción. con la memoria extendida. con QEMM386. En algunos casos. ya puede ir olvidándose. las páginas físicas 0 a la 3 estaban ubicadas a partir de la dirección 0C8000h. . La razón es que. Esto es debido a que la conmutación páginas de memoria expandida se hace. Muchos programas pueden ver incrementado notablemente el rendimiento si se desarrollan empleando esta memoria en lugar de la XMS.0. y ya no vuelve a él. La razón es que los 386 y superiores están en modo virtual 86 con los controladores EMS instalados.

.

un programa COM que desee cargar otros programas debe primero rebajar la memoria que el DOS le ha asignado y quedarse sólo con la que necesita. por lo que es deber de éste liberar la que no necesita. Con los programas EXE.. lo que en la práctica significa toda la memoria).LLAMADA A SUBPROCESOS Y RECUBRIMIENTOS U OVERLAYS. RECUBRIMIENTOS Y FILTROS 9.0 (sí ha sido documentada desde dicha versión). SP apuntará dentro del siguiente bloque de memoria. esto es. RECUBRIMIENTOS Y FILTROS 157 Capítulo IX: SUBPROCESOS. offset 10: Doble palabra que apunta al segundo FCB a copiar en el proceso hijo. offset 6: Doble palabra que apunta al primer FCB a copiar en el proceso hijo. Si no existiera la función EXEC. devuelve el SS:SP inicial del subprograma. También hay que apuntar en ES:BX a una estructura de datos (bloque de parámetros) que se interpreta de la siguiente forma: offset 0: Segmento donde está el entorno a copiar para crear el del programa cargado. offset 18: Si se carga sin ejecutar. 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. Cuando se ejecuta un programa COM ordinario. incluidos SS:SP. devuelve el CS:IP inicial del subprograma. offset 2: Doble palabra que apunta a los parámetros del programa a ejecutar (los que ese programa admite. pasando en ES la dirección del PSP). Por tanto. Si no se toma esta precaución. para proceder después a recuperar de la pila los demás . así como la carga de subrutinas de un mismo programa desde disco (overlays). se calcula cuanta memoria necesita el programa y se llama a la función del sistema para modificar el tamaño del bloque de memoria del propio programa (función 4Ah del DOS.. el proceso sería arduo: habría que reservar memoria. relocalizarlo si es de tipo EXE. la cantidad de memoria que les asigna el DOS inicialmente depende del compilador y las opciones de compilación. Antes de llamar a esta función. el ordenador debe tener suficiente memoria libre. que es la posibilidad de cargar un programa sin ejecutarlo. . Por ello. cargar el fichero ejecutable en memoria. en teoría todos los registros son destruidos.1. en la línea de comandos). terminada por cero) que puede incluir la ruta de directorios y debe incluir la extensión. por fortuna. según la documentación oficial. si el programa va a ocupar menos de 64 Kb. en ensamblador suele ser también toda la memoria. Para ello. Hay que apuntar DS:DX a la dirección del nombre del programa (una cadena ASCIIZ. Tras llamar a EXEC. la función EXEC se ocupa de todo ello. será preciso mover SP más abajo para que no se salga del futuro bloque de memoria del programa. Para llamar a la función EXEC para cargar y ejecutar un programa se pone un 0 en AL. que es más que probablemente el que utilizará EXEC. Tras llamar a la función EXEC. etc). la pila está apuntando al final del segmento (SP está próximo a 0FFFEh). La función EXEC del DOS (4Bh) es el pilar que sustenta la ejecución de programas desde dentro de otros programas. inmediatamente a continuación y antes de hacer nada se deben recargar SS y SP.SUBPROCESOS. Los programas hijos siempre accederán a una copia y no al original. En los programas COM. El subprograma cargado hereda los ficheros abiertos del programa padre. offset 14: Si se carga sin ejecutar. crear su PSP y demás áreas de datos (entorno. por sí solo. A 0 si es el del programa padre. esta función posee una característica no documentada hasta el DOS 5. con lo que el ordenador debería colgarse a no ser que haya mucha suerte. toda la memoria del sistema está asignada al mismo (el mayor bloque en realidad. Además. Tiene el mismo formato que el contenido de PSP:80h. lo cual puede ser interesante de cara a la creación de depuradores de código.

872 0000 <Nadie> 229. he decidido cargar el COMMAND.COM. FCB 0 WORD PTR [BX+8].Demostración de carga de subprograma. PSP WORD PTR [BX+4].0 .024 Interrupciones 256 Datos del BIOS 45. redimensionar bloque memoria DX.CS WORD PTR [BX+0Ah].X: aunque Microsoft no lo diga oficialmente.4B00h 21h . * * * ******************************************************************** EQU 1024 .640 ED3B DATAPLUS 1.13.CS DX.COM C:\COMPILER\86\AREA>_ .COM".exec_info WORD PTR [BX]. las versiones posteriores del sistema sólo corrompen DX y BX al llamar a EXEC. . cargar y ejecutar programa CS DS . FCB 1 WORD PTR [BX+0Ch]. DS = CS DX.10 "Estás dentro de SHELL.392 0008 38.. .hola_txt AH. .10 ".4C00h 21h .816 EB0F PRINT 1.0 WORD PTR [BX+2].COM en C:\DOS (es más cómodo que andar buscando la variable de entorno COMSPEC).520 E5B9 FILES 1.512 0008 4.920 E714 GRAPHICS 6. y suponiendo que el ordenador tenga el COMMAND."$" inicio TAMTOT shell SEGMENT ASSUME CS:shell.TAMTOT .192 E887 SHARE 4. mensaje de despedida AX.COM".". redefinir la pila AH.10.COM .Información sobre la memoria del sistema. C:\COMPILER\86\AREA>mapamem MAPAMEM 2.--------------1.nombre AX.4Ah BX. terminar "C:\DOS\COMMAND. 6. la ventaja de COMMAND es que crea una nueva sesión de intérprete de comandos y permite comprobar con comodidad qué ha sucedido con la memoria.80h . Como ejemplo.158 EL UNIVERSO DIGITAL DEL IBM PC.216 Sistema Operat.344 0FA7 MAPAMEM 589. mensaje de bienvenida BX. AT Y PS/2 registros.5Ch . .COM: C:\COMPILER\86\AREA>shell Estás dentro de SHELL. El siguiente programa de ejemplo. Este comportamiento de EXEC parece romper la tónica habitual de comportamiento del DOS.ASM 1.0 .. en la que la utilidad MAPAMEM permite verificar la estructura de la memoria tras la ejecución de SHELL.2 . DS:shell ORG 100h SP.272 0008 496 0000 <Nadie> 208 E17B DOSVER 17.576 0000 <Nadie> C:\COMPILER\86\AREA>exit . programa a ejecutar 22 DUP (0) 13. de tipo COM..."$" 13.9 21h .024 0E6F SHELL 400 0ECA COMMAND 2.9 21h . lo cierto es que esto sólo sucedía en el DOS 2. MOV MOV MOV MOV LEA MOV INT PUSH POP LEA MOV INT MOV INT nombre exec_info hola_txt adios_txt shell DB DB DB DB DB DB ENDS END WORD PTR [BX+6].10. aunque el programa a ejecutar podría ser cualquier otro. Acabas de abandonar SHELL.784 E665 NLSFUNC 5..664 F1C9 HBREAK 576 F232 ANSIUP 96 F257 TDSK 16.13.200 E619 LASTDRIV 2. . Acabas de abandonar SHELL.00 (C)Copyright Microsoft Corp 1981-1991.144 EA0B DOSKEY 6. realiza todas las tareas necesarias para cargar otro programa. ******************************************************************** * * * SHELL.adios_txt AH. Microsoft(R) MS-DOS(R) Versión 5.COM .136 E189 BUFFERS 1..TAMTOT/16 21h .----.768 0CF3 COMMAND 64 0000 <Nadie> 768 0CF3 COMMAND 416 0E6F SHELL 1.6Ch . este programa y su pila caben en 1 Kb..704 0ECA COMMAND 384 0ECA COMMAND 400 0FA7 MAPAMEM 1.CS inicio: MOV MOV MOV INT LEA MOV INT LEA MOV MOV MOV Al ejecutar el programa anterior. Tipo -------Sistema Sistema Sistema Sistema Programa Libre Entorno Entorno Programa Datos Programa Entorno Entorno Programa Libre Sistema Sistema Libre Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Programa Datos Libre Ubicación --------0000-003F 0040-004F 0050-0B59 0B5B-0CF1 0CF3-0E1C 0E1E-0E21 0E23-0E52 0E54-0E6D 0E6F-0EAE 0EB0-0EC8 0ECA-0F72 0F74-0F8B 0F8D-0FA5 0FA7-0FFA 0FFC-9FFE A000-D800 D802-E159 E15B-E179 E17B-E187 E189-E5B7 E5B9-E617 E619-E663 E665-E712 E714-E885 E887-EA09 EA0B-EB0D EB0F-ECB8 ECBA-ED17 ED19-ED39 ED3B-F1C7 F1C9-F230 F232-F255 F257-F25C F25E-F65D F65F-F6FF Tamaño PID Propietario ------. Sin embargo. se puede generar una sesión de trabajo como la que se muestra a continuación.384 F257 TDSK 2..504 ECBA RCLOCK 528 ED19 DISKLED 18.

el teclado del PC con el emulador de terminal dejará de funcionar y será preciso utilizar ¡el del propio servidor!: la razón es que muy pocos programas usan el DOS para leer el teclado. 9. utilizar el DOS como un sistema con dispositivos de entrada y salida estándar que soportan el redireccionamiento.. y más aún si los overlays constan de varios segmentos internos. 2 para STDERR (también CON). respectivamente. Sin embargo. aún en la actualidad muchos usuarios de PC trabajan en la línea de comandos. En general. se puede . Por ejemplo: con el comando interno CTTY. multiplicarlo por 16 y utilizarlo como offset en la llamada al mismo segmento del programa principal. Claro que también se puede calcular la distancia que hay entre el segmento del programa principal y el del overlay. Al llamar a la función 4Dh. la pantalla). Las posibilidades son. muy limitadas.. El redireccionamiento bajo DOS es empleado sobre todo para procesar ficheros de texto. 2 (terminación por error crítico) ó 3 (terminación residente).andaban ese día más bien despistados. como se ha mencionado.. si se trata de un fichero EXE (normalmente el mismo valor que el anterior. La razón es la ineficiencia del sistema en las operaciones de entrada y salida. popularmente conocidas como STDIN y STDOUT. Sin embargo. Tanto la entrada como la salida estándar. sin embargo. un filtro normal debe limitarse a leer. 1 para STDOUT (también conocido por CON). que devuelve en AH: 0 (terminación normal). a través de un puerto serie es factible poner a un PC como servidor remoto de otro. RECUBRIMIENTOS Y FILTROS 159 La subfunción EXEC para cargar un programa sin ejecutarlo se selecciona con AL=1. por ejemplo.2. no digamos para escribir en la pantalla.FILTROS. Si se produce un error en el proceso. 3 para la salida serie (denominada AUX) y 4 para la impresora (conocida por PRN). se borra la información que devuelve (sólo funciona la primera llamada). con las funciones de manejo de ficheros ordinarias. En AL se devuelve el valor que retorna el programa que finaliza (valor de ERRORLEVEL). Quienes proponen este segundo método -que los hay. Tienen asociados un handle de control. el teclado). Por tanto.SUBPROCESOS. como cualquier fichero: 0 para STDIN (denominado CON). exceptuando AL (que ahora vale 3). no obstante. a calcular. Esta subfunción asigna el PID. Esto permite operar en la línea de comandos desde el terminal remoto ubicado a varios metros de distancia. se puede utilizar la función 4Dh del DOS (Obtener código de retorno). . información procedente del handle 0. nada más ejecutar un programa. o hay una salida de log que no deba mezclarse con la salida deseada por el usuario. que obliga a las aplicaciones a hacer accesos directos al hardware. Un filtro es un programa normal que lee datos de la entrada estándar (por defecto. La subfunción de EXEC para cargar un overlay o recubrimiento. la programación con overlays es compleja. Offset 2: Factor de reubicación. al PSP del subprograma cargado. si el subprograma va a correr en el mismo segmento en que es cargado). 1 (programa abortado por Ctrl-Break). Sin embargo el bloque de parámetros apuntado por ES:BX es ahora mucho más sencillo: Offset 0: Segmento donde cargar el overlay (la memoria ha de asignarla el programa principal). así como la salida estándar para errores (STDERR) son dispositivos permanentemente abiertos en el DOS. donde sí es posible. tras procesarla debe escribirla en el handle 1. El overlay puede haber sido ensamblado. con un desplazamiento relativo nulo (ORG 0) de manera que para llamarlo hay que hacer un CALL FAR al segmento donde ha sido cargado. se llama con los mismos valores en los registros que la anterior. El DOS es un sistema operativo que soporta el redireccionamiento. esto requiere que el overlay sea ensamblado con cierto offset . Para conocer si la función EXEC se ha realizado correctamente o ha fracasado.. con un offset 0. ES:BX apunta al bloque de parámetros que se definió para el caso normal de carga+ejecución. Sin embargo. los procesa de alguna manera y los deposita en la salida estándar (por defecto.

aparte del directorio ordenado aparecerán dos extraños ficheros con 0 bytes (este era su tamaño cuando se ejecutó DIR): el DOS crea dos ficheros auxiliares para sustituir la entrada y salida estándar. ’<<’ y ’>>’) suceden procesos similares.’A’ car_ok AL. Si se ejecuta sin más (sin emplear ’|’ ni ’<’ ni ningún símbolo de redireccionamiento o filtro) se limita a leer líneas del teclado y a reescribirlas en minúsculas. es un filtro nulo que no realiza tarea alguna: se limita a enviar todo lo que recibe (por tanto. el intérprete de comandos cierra la salida estándar y crea un fichero auxiliar (de nombre extraño). . do putchar(c=getchar()).STDOUT AH.32 [BX]. Al final.h> void main() { int c.’ñ’ AL. Actuarán los dos si se utilizan filtros encadenados que obliguen a redireccionar simultáneamente tanto la entrada como la salida a ficheros auxiliares.160 EL UNIVERSO DIGITAL DEL IBM PC.AL BX procesa_car CX AH. Lee bloques de medio Kbyte de una sola vez para reducir el número de llamadas al DOS y ganar velocidad. DIR es lo mismo que DIR | NULL): #include <stdio.buffer BX. .COM.buffer CX. A partir del DOS 5.’ü’ AL. ******************************************************************** * * * MIN. si está definida la variable de entorno TEMP los ficheros auxiliares se crean donde ésta indica y no en el directorio activo.0 (en versiones anteriores no hay siquiera subdirectorios).’É’ trad_ok AH. los filtros son programas que no tienen que preocuparse de cual es la entrada o salida. su codificación es extremadamente sencilla y puede realizarse en cualquier lenguaje de alto o bajo nivel. transforma en minúsculas todo lo que pasa por él.[BX] AL.’é’ AL.’Ü’ trad_ok AH. teniendo cuidado con los caracteres españoles (Ñ. etc. que pasará a ser el nuevo dispositivo de entrada por defecto. con objeto de alterar la salida y entrada por defecto para trabajar con un fichero en su lugar.C.Filtro para poner en minúsculas ASCII Español. Esto significa que toda la salida de COMANDO no irá a la pantalla (CON) sino al fichero auxiliar. Ç. en una orden del tipo DIR | SORT | MORE. el fichero auxiliar es cerrado y borrado. hasta que se acaba la entrada estándar (teclear Ctrl-Z y Return al final). * * * ******************************************************************** SEGMENT ASSUME CS:segmento. . ’>’. Cuando se ejecuta una orden del tipo COMANDO | FILTRO. Cuando se acabe de ejecutar COMANDO.3Fh 21h CX.ASM 1. El redireccionamiento y el sistema de ficheros por handle fue incluido a partir del DOS 2. . NULL. El siguiente programa en C estándar. que tomará los datos del fichero auxiliar en lugar del teclado. aunque en este ejemplo sólo se emplee uno de ellos. el intérprete de mandatos cerrará el fichero auxiliar y volverá a abrir la salida estándar. leer de STDIN . while (c!=EOF). AT Y PS/2 escribir el mensaje en el handle 2. por lo que a simple vista podrían no verse dichos ficheros. se carga y ejecuta FILTRO. DS:segmento EQU 0 EQU 1 ORG inicio: CALL JCXZ PUSHF CALL CALL POPF JNC MOV INT PROC LEA MOV MOV MOV INT MOV RET ENDP lee_entrada fin_filtro pon_minusculas escribe_salida inicio AX. Si se ejecuta DIR | SORT.40h 21h . Ü.AL AL.AX . Pero la cosa no queda ahí. restaurando el sistema al estado normal.).0 .’Ñ’ trad_ok AH. evidentemente: a continuación se cierra la entrada estándar y se abre como entrada el fichero auxiliar recién creado. escribir en STDOUT .buffer AL. abriéndose y restaurándose la entrada por defecto normal.4C00h 21h DX. ese handle será asignado al nuevo fichero. CF = 1 si fin de fichero 100h escribe_salida ENDP pon_minusculas PROC PUSH LEA procesa_car: MOV CMP JB CMP JAE CMP JA OR car_ok: MOV INC LOOP POP RET car8: MOV CMP JE MOV CMP JE MOV CMP JE MOV CMP JE MOV trad_ok: MOV JMP pon_minusculas ENDP buffer segmento .’Ç’ trad_ok AH.128 car8 AL.’Z’ car_ok AL. . bytes leídos .512 BX. Cuando se utilizan los redirectores habituales (’<’.STDIN AH. algo más útil.AH car_ok 512 DUP (?) inicio segmento STDIN STDOUT fin_filtro: lee_entrada . Seguidamente. en CX. Por tanto. leer lee_entrada escribe_salida PROC LEA MOV MOV INT RET DX. a continuación abre ese fichero para salida: como al cerrar la salida estándar se había liberado el handle 1. todos ellos desencadenados por COMMAND.0.’ç’ AL. } El siguiente filtro. escribir DB ENDS END CX BX.

los demás registros deberán ser inicializados antes de empezar a operar (lógicamente. lo cual no se limita por supuesto a los registros de la CPU. existen unos cuantos principios fundamentales que conviene respetar: 1) Los programas residentes no deben alterar el funcionamiento normal del resto del ordenador. si no a la primera ’A’. si se llama a un servicio del DOS desde un programa residente. . además. entre otras razones porque conmuta a una pila propia al ser invocado. Los programas residentes pueden ser activados mediante una combinación de teclas o bien actuar con cierta periodicidad. Lo que puede suceder -y acabará sucediendo. quizá se quede extrañado. surgieron como intento de superar esta limitación. Esto significa que deben preservar el estado de todo lo que van a modificar durante su ejecución. Un programa residente o TSR es un programa normal y corriente que. Ello es posible utilizando una función específica del sistema operativo. pero otros están muertos a menos que el usuario los active. permanece parcial o totalmente en memoria al finalizar su ejecución. Esto último es importante. TSR: Terminate and Stay Resident). Sin duda. etc. Esto es debido a que el DOS es un sistema operativo no reentrante. habrán de ser primero preservados para ser restaurados al final). Los programas residentes. ya que un programa residente puede funcionar más o menos bien pero no del todo: si bien la máquina puede resistirse a colgarse. estar disponibles rápidamente cuando son requeridos y. Por ello. aquellos que permanecen en memoria tras ser ejecutados. Tal vez se pregunte qué sucedería si desde un programa residente se llama (pongamos por ejemplo. tras ser cargado.1. es muy común la circunstancia de que dos programas residentes sean incompatibles entre sí. los registros de la CPU pueden tener un valor que hay que interpretar o bien pueden ser aleatorios. Para los programas más complejos puede ser necesario. en cambio.PRINCIPIOS BÁSICOS. casi siempre resulta totalmente inevitable desviar alguna interrupción hacia una nueva rutina que la gestione. asociados a la interrupción del temporizador. el programador controla totalmente la máquina sin depender de facetas ocultas del compilador y. El DOS es un sistema monousuario y monotarea. restaurándolo después antes de retornar al programa principal. Algunos de estos programas residentes proporcionan en la práctica multitarea real (tales como colas de impresión o relojes). Al final. En particular. utilizar algún lenguaje de alto nivel próximo a la máquina. 10. También pueden interceptar funciones del DOS o de la BIOS para cambiar o modificar su funcionamiento. el estado de la memoria expandida y extendida. 2) No se pueden invocar libremente desde un programa residente los servicios del sistema operativo. A la hora de construir programas residentes el ensamblador es el lenguaje más apto: es el más potente. es el lenguaje más sencillo para crear programas residentes (en inglés. a la segunda o la tercera. es posible que en ese momento el . Como en casi todos los aspectos de la programación. Si el lector es la primera vez que oye esto. por otro.PROGRAMAS RESIDENTES 161 Capítulo X: PROGRAMAS RESIDENTES En este capítulo vamos a abordar uno de los temas más estrechamente relacionados con la programación de sistemas: la creación de programas residentes. los discos. también. Este último es el caso de la interrupción periódica del temporizador: el programa residente sólo puede fiarse de CS:IP. una vez cada segundo) a la función de impresión del DOS para sacar una ’A’ por la pantalla. Cuando se produce la interrupción que activa el programa residente. con objeto de activar el programa residente.es que el ordenador se cuelgue. deben cumplir dos requisitos: por un lado. diseñado para atender sólo un proceso en un momento dado. los programas residentes que pretendan captar gran número de usuarios. ocupar poca memoria. pueden aparecer anomalías o conflictos con algunas aplicaciones. sino que incluye también la pantalla. ser fiables y crear pocos conflictos.

Sin embargo. . ya que los servicios de vídeo de la BIOS no utilizan el registro de estado para devolver ninguna condición.2. es utilizar para desactivar el programa la misma secuencia de teclas que para activarlo. Puede que el lector haya visto antes programas residentes que no toman la precaución de monitorizar la interrupción 10h o la 13h de la BIOS.162 EL UNIVERSO DIGITAL DEL IBM PC. y tal vez se pregunte si ello es realmente necesario. Para utilizar el DOS hay que emplear funciones más o menos secretas del sistema no documentadas por Microsoft. 3) La BIOS no es tampoco completamente reentrante. utilizar menos de 1/18. una interrupción 10h en el caso de las funciones de vídeo o la 13h en las de disco). esto es. El otro. se limita a llamar al anterior vector de la INT 8 y a comprobar que no se está ejecutando ninguna función de vídeo de la BIOS (que no se ha interrumpido la ejecución de una INT 10h). ¿qué sucederá si se vuelve a pulsar CTRL-ALT-R con el reloj ya activado?. Lógicamente. Por cierto. si se fija el lector en la manera de controlar la INT 10h verá que al final se retorna al programa principal con IRET: los flags devueltos son los del propio programa que llamó y no los de la INT 10h real. Pongamos por caso que se pulsa CTRL-ALT-R para mostrar un reloj residente en pantalla. se vuelve a reproducir la causa. Utilizar el DOS es prácticamente indispensable a la hora de acceder al disco. existen dos caminos: uno de ellos es utilizar una variable que indique que el programa ya está activo. Finalmente. por lo que resulta más que seguro esperar que futuras versiones del DOS sigan soportándolas. por lo que más adelante en este capítulo lo veremos con detenimiento. con objeto de no dejarlo residente y economizar memoria. Para utilizar el DOS desde un programa residente hay que conocer cómo están organizadas las pilas del sistema operativo. En principio. si un programa residente cambia la posición del cabezal de un disquete cuando el programa principal estaba ejecutando una función del DOS o la BIOS para acceder al disquete). Esto significa que el lector podrá utilizar libremente los servicios de vídeo de la BIOS. por ejemplo. Por fortuna. 5) Los programas residentes tienen una causa que provoca su activación. Con la INT 10h se puede hacer esto.2 veces por segundo: de la manera que está. la BIOS utiliza la pila del programa que le llama. así como determinar el estado del DOS para saber si se puede interrumpir en ese momento o si hay que esperar. 10. AT Y PS/2 DOS ya estuviese realizando otra función 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). 4) El hardware puede ser accedido sin limitaciones desde los programas residentes. por supuesto además que no se puede llamar al DOS en este TSR (no se puede hacer INT 21h directamente desde el código residente). si bien esto no es peligroso: esta empresa las utiliza y las ha utilizado siempre profusamente en sus propios programas. para utilizar funciones de la BIOS desde un programa residente basta con asegurar que el sistema no está ya ejecutando una función BIOS incompatible (normalmente. El procedimiento CONTROLA_INT8 puede ser modificado por el lector para que el programa realice una tarea útil cualquiera 18. con otras interrupciones BIOS (ej.UN EJEMPLO SENCILLO. si bien el nivel de uso que puede hacerse está limitado por el sentido común (puede haber problemas. También se instala una rutina de control de la interrupción 10h. Si cuando ya están activos. se suele denegar una demanda de activación cuando el programa residente ya estaba activo (si el programa tiene pila propia esto es además obligatorio). Por ello. el código de instalación está colocado al final. los programas que realicen algo periódicamente (pongamos por caso 18. La respuesta . La rutina de instalación (MAIN) se encarga de preservar el vector de la interrupción periódica y desviarlo para que apunte a la futura rutina residente. estamos ante un problema de reentrada que compete exclusivamente al programador.2 segundos de tiempo de CPU para sus tareas. tan sólo es una demostración de la manera general de proceder para crear un programa residente.2 veces por segundo) basta con que se limiten a no pillarse los dedos. El siguiente programa residente no realiza tarea alguna. Por lo general. Para solucionar esto. 16h) o las del DOS habría que actuar con más cuidado para que la rutina de control no altere nada el funcionamiento normal. si bien para utilizar por ejemplo los de disquetes habría que desviar y monitorizar también INT 13h. se libera el espacio de entorno para economizar memoria y se termina residente.

nueva rutina de INT 8 DX.3100h 21h inicio . Por cierto.PROGRAMAS RESIDENTES 163 es tajantemente que sí.49h 21h ES DX.DS:[2Ch] AH. Como se verá en el futuro en otro programa de ejemplo. demores SEGMENT ASSUME CS:demores. la organización de la memoria en los PCs es a veces tan anárquica que este método (que debería ser el más elegante) es un poco peligroso en cuanto a la seguridad. sólo el último cargado será capaz de encontrarse a sí mismo en memoria. Dejar residente hasta aquí. Lo cierto es que puede ser difícil intentar recorrer la memoria superior. 10. buscando desesperadamente una cadena de identificación.BX AX. redondeo a párrafo . Sin embargo es tremendamente lenta llevada a la práctica. basta con mirar a dónde apunta dicha interrupción y comprobar un grupo de bytes o alguna identificación que permita determinar si el programa que la gestiona es ya una copia de él mismo.15 CL. Puede que el programa sea de éstos que se cargan una sola vez y carecen de parámetros.controla_int10 AX. si una recarga posterior puede provocar un cuelgue del sistema o. la idea de rastrear toda la memoria (1 Mb).MÉTODO DE LA CADENA DE BLOQUES DE MEMORIA. Otro método alternativo es rastrear la cadena de bloques de memoria del sistema operativo buscando programas residentes y comprobándolos uno por uno. Por ejemplo. estamos dentro de INT 10h DX. Un programa residente que ya está instalado en memoria puede volver a ser cargado desde disco y esto hay que tenerlo en cuenta. el programa tiene opciones y se pretende modificar los parámetros de la copia ya residente.ES ant_int10_off.3510h 21h ant_int10_seg.ES ant_int08_off.3508h 21h ant_int08_seg.CL AX. entonces se hace necesario que el programa tenga capacidad para buscarse en memoria y encontrarse a sí mismo en el caso de que ya estuviera cargado.2 veces/seg. Sin embargo.2. Este método es bastante rápido. mayor de 0 si hay INT 10h .y consiste en apoyarse en los vectores de interrupción. En ese caso. DS:demores ORG inicio: JMP main controla_int08 PROC PUSHF CALL CS:ant_int08 STI CMP CS:in10. El inconveniente de este método.1 .0 JNE fin_int08 100h main: PUSH MOV INT MOV MOV MOV INT MOV MOV POP LEA MOV INT LEA MOV INT PUSH MOV MOV INT POP LEA ADD MOV SHR MOV INT demores ENDS END ES AX. no sucederá nada porque sea creada en memoria una nueva copia del mismo: es problema del usuario.2508h 21h . obtener vector de INT 8 . Es incómoda (hay que considerar el caso de que el propio programa que busca se encuentre a sí mismo. .3.main DX.3. . bytes -> párrafos . dirección del entorno . simplemente. fin del código residente .LOCALIZACIÓN DE UN PROGRAMA RESIDENTE. El método más simple es también el más simplón -inútil. si el programa quedó residente interceptando la interrupción 9.4 DX.3. terminar residente . 10. Colocar aquí el proceso a ejecutar 18. habida cuenta de que no van a existir más de 20-50 bloques de memoria. . liberar espacio de entorno . nueva rutina de INT 10h ES ES. habida cuenta del desigual tratamiento que recibe en las diversas versiones del DOS y con los diversos controladores de memoria que pueden estar instalados. aunque mucho menos que el anterior. no es nueva. reentrar a la BIOS sin más puede provocar conflictos. .controla_int08 AX. que puede invocar funciones de INT 10h fin_int08: IRET controla_int08 ENDP controla_int10 PROC INC CS:in10 PUSHF CALL CS:ant_int10 DEC CS:in10 IRET controla_int10 ENDP in10 ant_int08 ant_int08_off ant_int08_seg ant_int10 ant_int10_off ant_int10_seg DB LABEL DW DW LABEL DW DW 0 DWORD ? ? DWORD ? ? . 10. en particular en áreas como los buffers de transferencia con disco del DOS) y bastante salvaje. fin de la INT 10h . es que si se carga más de un programa residente que emplee la INT 9.BX ES . indicar entrada en INT 10h .2510h 21h . Sin embargo. obtener vector de INT 10h . llamar al gestor normal de INT 8 . .MÉTODO DE LOS VECTORES DE INTERRUPCIÓN. fácil de deducir.

Si esta prueba es superada satisfactoriamente. Esto es fácil de verificar. Por otro lado. verificando que el segmento es distinto de cero). que se verán más adelante. el KEYB del DOS llama a INT 2Fh con AX=AD80h.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 aplicación-. Es evidente que dos programas residentes no pueden utilizar el mismo. aunque un tanto laborioso. . Los valores 00-BFh en AH están reservados para el DOS. Aviso: Aunque no es frecuente. A la vuelta. sin embargo. ciertos programas residentes sofisticados permiten ser desinstalados aún sin ser los últimos instalados.MÉTODO DE LA INTERRUPCIÓN MULTIPLEX.X del sistema gestiona de tal manera la INT 2Fh que ninguna otra aplicación puede emplearla. por lo que se procede a su instalación (en este caso. Este sistema es el más seguro. AT Y PS/2 10. Por lo general. con lo que limitan las posibilidades del usuario.EXPULSIÓN DE UN PROGRAMA RESIDENTE DE LA MEMORIA Se trata de una tarea bastante sencilla en sí. Por ello. también se devuelve en ES:DI la dirección del KEYB ya residente (que es lo solicitado con AL=80h). no tiene mucho sentido que un usuario elimine un programa residente después de haber cargado otro -aunque ello sea posible.3. Los programas menos eficientes utilizan un valor fijo predeterminado. existe la posibilidad de utilizar el mismo sistema que emplea el DOS para comprobar la presencia de sus propios programas residentes (como el KEYB. Esta técnica cuenta con la complicación que supone decidir qué valor emplear en la interrupción multiplex. si a la vuelta AL<>FFh se interpreta que el programa no está aún residente. Como se verá después. Si devuelve FFh. PRINT. etc) basado en la interrupción Multiplex (2Fh). aunque hay que tener en cuenta una serie de factores. Ello significa que si ha sido instalado tras él otro programa residente que modifica uno de los vectores que él interceptaba. Por ello. 10. . En caso de que lo esté (AL=FFh a la vuelta). En primer lugar.4. significa que el programa ya estaba instalado. y otro valor en AL para decir por qué está llamando (normalmente 0).164 EL UNIVERSO DIGITAL DEL IBM PC. Este método puede ser más seguro si está instalado un gestor de memoria expandida extraño. 2) Liberando directamente el bloque de memoria al colocar una palabra a cero en los bytes del MCB que identifican al propietario del bloque. el programa debe restaurar todos los vectores de interrupción que había interceptado. estos programas residentes tienen que tener algo en común: .para conocer si ya está instalado o no.X del sistema no tienen inicializado el vector de la INT 2Fh. Consiste en llamar a la INT 2F con un valor en el registro AH que indica quién está llamando. GRAFTABL. curiosamente incluso aunque AL=1).X si el usuario prescinde de PRINT). Por ello. GRAPHICS. basta con comprobar que todas las interrupciones interceptadas siguen apuntando a una copia de él. aunque esto es realmente problema exclusivo del usuario. SHARE. Finalmente. el método de la interrupción Multiplex está más bien reservado para versiones 3.0 o superiores (también la 2. puede procederse a restaurar los vectores de interrupción 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 función 49h del DOS para liberar el bloque de memoria. aunque es menos elegante y quizá menos recomendable. es una buena práctica asegurarse de que esta interrupción apunta a algo antes de llamarla (por ejemplo. el comando PRINT del DOS en las versiones 2. un valor 1 para decir que no está instalado ni tampoco está permitida la instalación. y de C0h-FFh para las aplicaciones. Por ejemplo. algunas versiones 2. un primer requisito para permitir la desinstalación es que sea el último programa residente cargado que utiliza un vector de interrupción dado. para solucionarlo existen varias alternativas. Sin embargo. AL devuelve un valor 0 para indicar que el programa no está instalado pero está permitida la instalación. donde ADh significa que quien pregunta es el KEYB -y no otro programa.3. ya no es posible restaurarlo. En el caso concreto del KEYB.

Ello significa que si entre dos programas residentes que cumplen el mismo convenio el usuario instala un programa que no lo respeta. todo sucede como es costumbre en los programas que utilizan la interrupción Multiplex. lo que garantiza su difusión y la inversión de quienes decidan emplearlo. llamando a continuación a la INT 2Fh.. 10. El método es similar al anterior.LST de Ralf Brown. Hasta ahora. con AH=0C0h. el programa residente debe interceptar también INT 2Fh y devolver (cuando alguien pregunta por él) un valor FFh en AL y. modifica ES y DI para apuntar a una tabla con la siguiente información: Offset -16 -14 -12 Tamaño WORD WORD WORD Descripción segmento donde realmente comienza el código del TSR (CS en programas con PSP. además de devolver un 0FFFFh en AX. segmento de memoria superior XMS si instalado como UMB. Por ello. lo cual permitiría su desinstalación aunque no sea el último cargado. Con objeto de aumentar la eficacia. que expongo a continuación. Lo de emplear 0EBEBh y 0BEBEh constituye un mecanismo similar a un password. .PROGRAMAS RESIDENTES 165 comportarse de la misma manera y actuar también de una manera definida. ¿por qué no colocar más información útil?.5. se incrementa AH y si AH es menor o igual a 0FFh se repite el proceso. Sin embargo. memoria empleada por el TSR (en párrafos).GESTIÓN AVANZADA DE LA INTERRUPCIÓN MULTIPLEX. como siempre. por ejemplo. Tras llamar. el programa residente sabe que quien lo llama es alguien que respeta el convenio. y FFh para decir que ya está instalado.LST. si devuelve un 0 significa que está libre y que puede ser utilizada..COM y 0 en TSR’s en memoria superior).1. nombre y versión del programa.EL CONVENIO CiriSOFT. Si se comprueba que ese programa no es el buscado. De este bucle puede salirse de dos maneras: encontrando el programa buscado (y su ubicación en memoria) o sin encontrarle. con la diferencia de que en ES:DI está almacenado en el momento de llamar el valor 1492h:1992h. ¿quién?: un programa cuyo nombre de fabricante abreviado (MMMM). en cuyo caso también se habrá localizado algún valor de AH aún no utilizado por ninguna tarea residente (a no ser que el usuario haya instalado ya 64 programas residentes con esta técnica). El convenio anterior adolece de un defecto importante: ya puestos a determinar con tanto detalle el fabricante.5. nombre de producto (PPPPPPPP) y versión (NNNN) están en ES:DI de la forma "BMB MMMMPPPPPPPPvNNNN". . Para ello se empieza. Conociendo la memoria que emplea el TSR es posible determinar si los vectores que intercepta están . por el hecho de haber llamado con ES:DI=1492h:1992h. que permitiera sacar mayor partido de la interrupción Multiplex. se pierden todas las posibilidades. extensión del expuesto en el apartado anterior. 10. La idea consiste en asignar dinámicamente el valor del registro AH empleado al llamar a la interrupción Multiplex. un 1 para señalar además que no se debe instalar. Por ejemplo. ser desinstalado por parte de otros programas o incluso emplear ciertas técnicas de relocalización en memoria para evitar la fragmentación de la misma cuando es desinstalado. sería interesante disponer de información sobre los contenidos previos de los vectores de interrupción que el programa ha desviado. si AL devuelve un 1 ó un 0FFh significa que esa entrada ya está empleada. el nuevo convenio también está publicado en el INTERRUP. Se coloca un 0 en AL para solicitar chequeo de instalación y se hace que los registros ES:DI valgan 0EBEBh:0BEBEh (porque sí). Al igual que el anterior. los señores de BMB Compuscience Canada pensaron un buen sistema. 10.EL CONVENIO BMB COMPUSCIENCE. A la vuelta se devuelve en 0 en AL para indicar programa no instalado. En AH se indica.5. el autor de este libro desarrolló un método nuevo. para evitar que al programa que llama a INT 2Fh se le modifique ES:DI sin que lo sepa. Lógicamente. el número de entrada de la interrupción Multiplex y en AL se coloca un 0 solicitando chequeo de instalación.2... si además el que preguntaba llamaba con ES:DI=0EBEBh:0BEBEh entonces debe devolver en ES:DI la información antes mencionada. Para solucionar el problema de que dos programas residentes no pueden utilizar el mismo valor de identificación en la interrupción Multiplex.) offset donde realmente comienza el código del TSR (frecuentemente 100h en programas *. publicado en el INTERRUP..

el carácter ’:’ se utiliza como delimitador). El valor ubicado en ES:DI-14 puede ser útil de cara a deducir el tamaño de la parte del PSP que permanece residente. Notar que el TSR debe usar ESTAS variables para invocar las anteriores rutinas de control de esas interrupciones. AT Y PS/2 -10 BYTE -9 -8 -6 -4 00h BYTE WORD WORD 4 BYTEs ??? aún apuntándolo (y si es seguro el proceso de desinstalación). así como los elaborados en el modelo Tiny del C. offset a la tabla area_vectores (se verá después) offset a la tabla area_extra (ver bit 7 en offset -10) "*##*" (asegurar que el TSR verifica el convenio) "AUTOR:NOMBRE_DEL_PROGRAMA:VERSION". . al final comprendí la necesidad de ampliar las prestaciones. Si bien se puede opinar que son demasiados campos. Además.166 EL UNIVERSO DIGITAL DEL IBM PC.0 (longitud variable. Notar que el TSR debe usar ESTA variable en su rutina de control de INT 2Fh. pero ha llegado demasiado tarde). La única pega que se puede poner es que. (y así sucesivamente). Aquellos TSR que contengan referencias en su propio código o datos cambiando el segmento (sólo puede ocurrir normalmente en los programas EXE) el convenio establece que deben soportar el parámetro /SR: ante él. por desgracia. autoinhibiéndose a continuación. 0Bh DWORD puntero a la última variable (todas están en el mismo bloque). Sin embargo. opcionales. No se respeta sin embargo el formato de los encabezamientos de interrupción propuesto en la BIOS del PS/2 (la intención de IBM es buena.SYS para liberar la memoria al desinstalar) 010 device driver (*. el convenio fue ampliado con dos tablas más. En la tabla anterior se define un puntero a una estructura con información sobre los vectores interceptados. por lo que no es muy complejo realizar esta tarea. ya que se considera que la ubicación del programa comienza en el offset 0 relativo al segmento definido en ES:DI-16 y.SYS) 011 device driver en formato EXE 1xx otros (reservados) bits 3-6 reservados bit 7 activo si tabla_extra definida y soportada número de entrada en la interrupción Multiplex (redefinible por un agente externo). tarea que mientras tanto puede realizar algún programa de utilidad. la mayoría de los programas residentes escritos en ensamblador son relocalizables. ya que un . Estas tablas permitirían a un hipotético sistema operativo mover los programas residentes para evitar la fragmentación de la memoria. Por ello. que es conveniente rellenar incluso también en aquellos TSR más sencillos que ocupan menos de 64 Kb y son totalmente reubicables (no contienen referencias absolutas a segmentos). muchas de las variables anteriores han de estar definidas necesariamente: ¿por qué no juntarlas de una manera convenida?. de características bits 0-2: 000 programa normal (con PSP) 001 bloque de memoria superior XMS (se necesita función de HIMEM. de la tabla area_vectores: Tamaño Descripción BYTE número de vectores interceptados por el TSR BYTE número del primer vector DWORD puntero al primer vector antes de instalar el TSR BYTE número del segundo vector DWORD puntero al segundo vector antes de instalar el TSR . ¡pocos programas usan este convenio!. En general. . Formato Offset 00h 02h de la tabla area_extra (opcional): Tamaño Descripción WORD offset a la tabla control_externo (0 si no soportada) WORD reservado para futuro uso (0) Formato de la tabla control_externo (opcional): Offset Tamaño Descripción 00h BYTE bit 0: activo si el TSR es relocalizable (sin referencias a segmentos) 01h WORD offset a una variable que puede inhibir o activar el TSR ---Si el bit 0 en el offset 00h está a 0: 03h DWORD puntero a cadena ASCIIZ con el nombre del fichero ejecutable que soporta el parámetro /SR (instalación e inhibición silenciosa) 07h DWORD puntero a la primera variable a inicializar en la copia recargada de disco desde el TSR aún residente. son sólo poco más de 16 bytes los que se añaden al programa residente. En las primeras versiones de este convenio ya no existían más reglas. por tanto. Formato Offset -1 00h 01h 05h 06h . el tamaño del programa definido en ES:DI-12 es relativo también con offset 0 a ese segmento. al ser recargados en memoria desde disco (necesario para la reubicación) deben instalarse silenciosamente sin chitar. agente externo podría actualizarlas. este área es empleada de cara a determinar si el TSR está ya residente y su versión.

0 AX AX AL CX AL.tabla_vectores CL.) . . Hay una maniobra más o menos complicada para hacer que el vector 2Fh sea el último restaurado.AL AH. pero simplifica el algoritmo y posee un nivel de seguridad razonable. DS:SI cadena de identificación del programa . En la segunda. -----------. 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 ningún vector.0 CX 2Fh CX AL.0FFh AX mx_si_hueco AH mx_busca_hndl mx_skip_hndl: .AL BP.5 mx_ul_busca2f AL. siguiente pasada . si el TSR ya está instalado. entrada. A la . pasada en curso .BX . CX = nº vectores .Buscar entrada no usada en la interrupción Multiplex. CF=1 si no hay hueco (ya hay 64 programas .ES:[SI-1] CH. sin incluir el de la versión: así se podría advertir al usuario que tiene instalada ya otra versión distinta. mx_find_tsr mx_rep_find: PROC MOV PUSH PUSH PUSH AH. AH.CL DX. A la entrada. AX CL. mismo. . . (CX bytes) y ES:DI protocolo de búsqueda (normalmente . La segunda sirve para que el programa residente se busque a sí mismo en la memoria. se . no hay TSR ahí . los registros salvo los de segmento.AL . mx_get_handle PROC MOV mx_busca_hndl: PUSH MOV INT CMP POP JNE INC JNZ mx_no_hueco: STC RET mx_si_hueco: CLC RET mx_get_handle ENDP AH. A la salida. Para que un TSR se auto-desinstale basta con que suministre a esta rutina su propio número de identificación. Si CF=0.4 BX. programa buscado hallado . residentes instalados con esta técnica). AX AX AH.ES DX. A continuación se listan dos rutinas que habrá de incorporar todo programa que desee emplear este convenio (u otras equivalentes).0 . «sacar» ES y DI de la pila La rutina mx_unload desinstala un programa residente que verifique el convenio.2Fh . basta con indicar el número de interrupción Multiplex que emplea el TSR. CF=0 y ES:DI apunta a la cadena de identificación del . A la salida. AL devuelve el vector «culpable».1 mx_ul_noult AL.2 CX SI.5 mx_ul_2f ES AX AH. CF=1 y ningún registro alterado. BP=entrada Multiplex del TSR .0FFh mx_skip_hndl DI CMPSB DI mx_tsr_found DI ES DS SI CX AX AH mx_rep_find SP. . al desinstalar.AX CX. vector en curso .Eliminar TSR del convenio si es posible.0C0h AX CX SI mx_tsr_found: mx_find_tsr PUSH PUSH PUSH MOV PUSH INT POP CMP JNE CLD PUSH REP POP JE POP POP POP POP POP POP INC JNZ STC RET ADD POP POP POP POP CLC RET ENDP DS ES DI AL.4 DS SI CX AX .ES:[SI] mx_ul_pasok CX. -----------.AX CS:mx_ul_tsrseg. Las rutinas las he denominado mx_get_handle y mx_find_tsr.1 AX. bien llamando al DOS o al controlador XMS (según quién la haya asignado). El método empleado por la rutina para cambiar los vectores de interrupción no es muy ortodoxo. 1492h:1992h). se cambian los enlaces entre los vectores y se libera la memoria. Si no.0 2Fh AL.tabla_vectores ES:[SI].0C0h AX AL. puede repetirse la búsqueda con CX indicando sólo el tamaño del nombre del fabricante y el programa. . mx_unload PROC PUSH CALL JNC POP RET mx_ul_able: XOR XCHG MOV MOV mx_ul_pasada: PUSH LEA MOV MOV mx_ul_masvect: POP PUSH DEC PUSH mx_ul_2f: MOV JNZ CMP JNE MOV ES mx_ul_tsrcv? mx_ul_able ES AL. La primera permite buscar un valor para la interrupción Multiplex aún no empleado por otra tarea residente. ya que la rutina no puede en ese caso recorrer la cadena de vectores para modificarla anulando la tarea residente.35h 21h . En esta segunda rutina se indica el tamaño de la cadena de identificación (la que contiene el nombre del fabricante. CF=1 . comparar identificación . El proceso de desinstalación falla si se ha instalado después un TSR que no verifica el convenio y tiene alguna interrupción en común. mx_ul_pasok SI. En caso de fallo .1 AX CS:mx_ul_tsroff.Buscar un TSR por la interrupción Multiplex. tanto si ésta es del convenio como si no.2Fh . -----------. Se corrompen todos . ¿último vector? LEA mx_ul_busca2f: CMP JE ADD JMP mx_ul_noult: CMP JNE ADD JMP mx_ul_pasok: PUSH PUSH MOV SHL SHL DEC MOV MOV POP PUSH MOV INT POP MOV SHR MOV ADD MOV mx_ul_masmx: CALL JNC SI.PROGRAMAS RESIDENTES 167 La variable que activa o inhibe el TSR permite paralizarlo momentáneamente antes de realizar ciertas tareas críticas. con objeto de poder seguir la cadena de interrupciones hasta el propio TSR invocando la INT 2Fh. devuelve en AH un valor de entrada libre en la INT 2Fh. Si no se encuentra el programa residente en la memoria. si bien no está pensada su utilización de cara a relocalizarlo en memoria o a desinstalarlo.0 AX. a la salida. si fue imposible y CF=0 si se pudo. mx_ul_pasok SI.0C0h mx_ul_tsrcv? mx_ul_tsrcv ¿INT 2Fh? ¿restaurar INT 2Fh? apuntar a tabla vectores vector en ES:BX INT xx en DX (aprox. programa y versión) en CX. en AH se indica la entrada Multiplex.

/* /* /* /* /* para rastrear entradas de INT 0x2F */ a 1 si se detecta parámetro /V */ a 0 cuando no lo sea */ a 1 si detectado TSR no del convenio */ flags de TSRs que no respetan el convenio */ if ((argc>1) && (!strcmp(strupr(argv[1]).. a por otro TSR . liberar bloque de memoria ES: AX .ES:[DI+1] CL. número de vectores en CX AL. .h> #include <string.) .ES:[DI+3] DX. segmento real del bloque .0 E15B:0000 208 193 09 2F DISKLED 2. printf("\nTSRLIST 1. CF=1 Los dos programas siguientes constituyen dos pequeñas utilidades de apoyo a los TSR de este convenio. entrada++) { tsr_raro[entrada-0xc0]=0. aunque lógicamente no da más información.4 E91F:0060 18640 195 09 2F ANSIUP 1. segunda pasada. no sólo necesariamente el último que fue cargado."*#" mx_ul_ncvexit SP.. restaurar INT’s DS.1 mx_ul_freeml . primera_vez=1...ES:segmento_real mx_ul_freeml xms_ins. ES:info_extra.\n").4 . ES AH. . segmento del TSR DX.0 EDAD:0060 576 196 29 2F HBREAK 4. ID Vectores interceptados -------. la INT xx no le apunta BX.4 . ¡desinstal-ar/ado! mx_ul_masvect CS:mx_ul_tsroff. for (entrada=0xc0. no más. CF=0 AX DI ES AX 0 0 . Dirección Tamaño Mx. INT xx en DX (aprox.49h 21h ES . este TSR usa vector analizado DI.DI .CX AH. ES:DI almacena la dirección CS:mx_ul_tsrseg.--------.0BFh AH mx_ul_exitnok mx_ul_masmx SP. char *argv[]) { int entrada."#*" mx_ul_ncvexit WORD PTR ES:[DI-2]. obtener_item(). DX.3 (c) Febrero 1994 CiriSOFT.ES:[DI-1] CH.ES:[SI+1] [BX+1].no es TSR del convenio . TSRLIST también informa de las entradas que están siendo utilizadas por programas que no respetan el convenio.11h gestor_XMS . vect=0. mx_ul_pasada . TSRLIST lista los TSR del convenio que están instalados en el ordenador. printf(" Listado de tareas residentes normalizadas:\n\n"). En ese caso.CX CX. no ES .h> void cabecera(). Listado de tareas residentes normalizadas: Programa Ver.BP . AT Y PS/2 mx_ul_tsrcv: JMP PUSH PUSH MOV MOV MOV mx_ul_buscav: CMP JE ADD LOOP ADD JMP mx_ul_usavect: POP POP CMP JB ADD CMP JA PUSH XOR XCHG CMP POP JNE POP POP POP PUSH PUSH PUSH DEC JNZ POP PUSH PUSH MOV MOV CLI MOV MOV MOV MOV STI POP mx_ul_norest: POP POP ADD DEC JZ JMP mx_ul_chain: MOV MOV MOV MOV SHR mx_ul_otro ES:[DI-16] . listar_tsr().3 E8A3:0000 1424 192 08 09 10 2F KEYBFIX 1.0 . si entre varios programas que respetan el convenio hay uno que lo viola.ES:[DI] mx_ul_usavect . con información detallada.0 F23E:0100 2144 198 08 09 13 28 2F .ID de programas residentes que incumplen convenio: 210.CX DS ES CX SI.DI DI.CX DX.6 ES .ES . TSRKILL permite eliminar uno o todos los TSR que estén instalados en cualquier orden.0FFFFh mx_ul_ncvexit WORD PTR ES:[DI-4]. /********************************************************************/ /* */ /* TSRLIST 1. entrada<=0xff.168 EL UNIVERSO DIGITAL DEL IBM PC.. no lo usa mx_ul_otro CX .----. ¿es el propio TSR? AX mx_ul_chain . sí: ¡posible reponer vector! CX BX BX CX ES BX mx_ul_norest . raro=0. if (hay_tsr(entrada)) { if (tsr_convenio (entrada)) { if (primera_vez) cabecera(vect). void main (int argc. ¿tipo de instalación? . equilibrar pila imposible desinstalar desinstalado 1ª pasada exitosa: por la 2ª . offset a la tabla de vectores CL.CS:mx_ul_tsroff . ES DS BX. ES DI DI. ¡se acabaron! . La entrada multiplex 210 (0D2h) de que informa TSRLIST es utilizada por QEMM386.CL MOV ADD MOV mx_ul_otro: INC JZ JMP mx_ul_exitnok: ADD POP STC RET mx_unloadable: POP DEC JZ JMP mx_ul_exitok: TEST MOV JZ CMP JNE MOV MOV CALL POP CLC RET mx_ul_freeml: MOV INT POP CLC RET mx_ul_tsrcv?: PUSH PUSH PUSH MOV MOV MOV INT CMP JNE CMP JNE CMP JNE ADD POP RET mx_ul_ncvexit: POP POP POP STC RET mx_ul_tsroff DW mx_ul_tsrseg DW mx_unload ENDP CX..1 EDD2:0000 1584 197 08 09 20 21 27 2F 70 SCRCAP 1.1492h ES.. Lógicamente. Ejemplo de salida de TSRLIST /V: TSRLIST 1. tamaño del TSR BX ..ES:[SI+3] [BX+3].-------. char tsr_raro[64].------------------------------------RCLOCK 2.1 E8FD:0060 528 194 08 09 13 2F DATAPLUS 2.4 DX. cargado en RAM convencional no hay controlador XMS (¿?) liberar memoria superior . siguiente vector CX mx_unloadable ."/V"))) vect=1.BX mx_ul_otro . la INT xx le apunta AX AL.CS:mx_ul_tsrseg CX.Utilidad de listado de TSR’s normalizados . se informa de qué vector ha sido el culpable.AL AH.AL AX.BX mx_ul_otro .5 mx_ul_buscav SP. de la variable vector DX.-----. TSRKILL puede no ser capaz de desinstalar un TSR del convenio. no es la segunda pasada ES .111b ES.1992h 2Fh AX. ¿es TSR del convenio?.TSR del convenio en ES:DI ES:[DI-12] DI.3 .ES AH. /* encabezamiento */ . CX CX mx_ul_exitok .3 (c) Febrero 1994 CiriSOFT.5 .ES:[DI-8] .BC++ */ /* */ /********************************************************************/ #include <dos.

&r).Utilidad de desinstalación de TSRs normalizados. obtener_item (1. } if (posible) { for (i=0. r. /* TSR no del convenio */ } } if (raro) { printf("\n. /* elemento 1: nombre */ printf("%-8s". &tsrx)) { iniciotsr=tsrx->segmento_real. "2MGUI")!=NULL) { posible=0.r. " de "). peek(r. peekb(r. intr (0x2f.r_es=0x1492. int tsr_convenio(). cad[i]=cad[max_long]=0.r_ax=entrada << 8. vx. unsigned int base. } } } int mx_unload (int mxid. "HBREAK")!=NULL) { posible=0. } else { asm cli poke (tablaptr[i][0]. r. intr (0x2f. info.r_es. base+cont*5)). r. 8. for (i=0.r_es. &tsr)) desinstalar (mxid). while (posible && nofincadena) { if (tsr_convenio (mx. else { printf (" . i++) tablaptr[i][0]=tablaptr[i][1]=0. existe_xms().r_di-9) & 0xff). " "). unsigned offset_real. } }.r_es=0x1492. ofs. posible && (vx<numvect). p=cadaux. vect). else if (vector==0x101) printf (" . unsigned intptr. while (*p++). &vector. info. void liberar_umb().----. obtener_item (2. for (i=0.-----. tablaptr[i][1]. */ /* Compilar en el modelo «Large» de Borland C. poke (tablaptr[i][0].3 . ID "). tsr->vectores_id+5*vx). strcat (cadena. ". iniciotsr. char *cad) { int i. 37.h> <string. if (strstr(*tsrnombre. } void desinstalar (int mxid) { int vector.r_ax==0xFFFF) && (peek(r. cad). char huge *info. cont++) { if (!(cont % 12) && cont) /* excesivos vectores: otra línea */ printf ("\n ").\n"). p). return ((r.r_di). printf ("fallo en el vector %02X.r_di=0x1992. unsigned char multiplex_id. posible.No hay TSR %u o no es del convenio. } } else /* imprimir autor */ { obtener_item (0. r. r. void main (int argc. tsr->vectores_id-1). printf (" (-1 todos los TSR). cad). tablaptr[vx][1]=tsrx->vectores_id+5*(i-1)+1. setvect (vector.r_ax & 0xff)==0xff). char huge *info. nofincadena=1. nombre). void cabecera(int vect) { printf("Programa Ver. cont. char far *nombre. Dirección Tamaño Mx. i<numvect. char far **tsrnombre) { int mx. if (i && (intptr>=iniciotsr)&&(intptr<=iniciotsr+tsrx->ltsr)) if (mx==mxid) nofincadena=0. tablaptr[i][1]+2. if (!tsr_convenio (mxid. else printf (" Autor/fabricante\n").Desinstalado el %s\n". r. } int tsr_convenio (int entrada) { struct REGPACK r. interr). ###################################################################### /********************************************************************/ /* */ /* TSRKILL 1. tsr->vectores_id+5*i). i<posicion. unsigned long validacion.HBREAK es «demasiado fuerte» para TSRKILL.r_es. cadena).tablaptr[vx][1]) >>4). vector. } void obtener_item (int posicion. r. info. *tsrnombre = tsr->autor_nom_ver.--------.\n". entrada+0xc0). printf("-------. sgm. /* fin de cadena y controlar tamaño */ } if (correcto || (vector<0x100)) { strcpy (cadaux.r_ax=entrada << 8. /* nombre programa */ strcat (cadena.r_di-14)). cad). int *interrupción.r_es. &tsr)) { *interrupción=0x100.3\n"). ID (TSRLIST) entre 192 y 255"). vx=0. unsigned ltsr. i++) while ((*info++)!=’:’). unsigned vectores_id. printf("-----------------------------------\n"). peekb(r. for (posible=1. i++) { vector = peekb(FP_SEG(tsr). mx=0xBF. while ((peekb(FP_SEG(tsrx). } else desinstalar (mxid). struct tsr_info far *tsr. char **argv) { int mxid. correcto. r. . strcat (cadena. sgm). /* compensar incremento posterior */ } } if (mx==0xFF) posible=0.\n"). if (vect) printf (" Vectores interceptados\n"). int vect) { struct REGPACK r. p). strcpy (cadena. if ((tablaptr[i][0]==0) && (tablaptr[i][1]==0)) { interr=MK_FP(sgm. info=MK_FP(r. i<256. *interrupción=0x102. cad). r. if (vect) printf("\n"). printf("%6u %03u ".Indicar número Mx. char numvect. ofs = peek(FP_SEG(tsr).\n").\n"). } if (!vect) printf("\n. peek(r. intptr = FP_SEG(getvect(vector)) + (FP_OFF(getvect(vector)) >> 4).-------. unsigned extension_id. entrada++) if (tsr_raro[entrada]) printf("%2d.ID de programas residentes que incumplen convenio: "). tablaptr[256][2]. return ((r. &r). } printf("\n"). /* elemento 0: autor */ printf("%s". exit (1). 3. void interrupt (*interr)(). if ((((mxid=atoi(argv[1]))<0xc0) || (mxid>0xFF)) && (mxid!=-1)) { printf (" . else if (vector==0x102) printf (" . mx_unload(). for (cont=0. tsr->vectores_id+5*i+3). } if (strstr(*tsrnombre. r. /* versión */ strcat (cadena. printf ("\nTSRKILL 1. intptr=peek(tablaptr[vx][0].r_di-4)==9002) && (peek(r. return (0). /* elemento 2: versión */ printf(" %-4s %04X:%04X ". entrada<64.r_di=0x1992. &r). char autor_nom_ver[80]. cont<peekb(r.r_di-16). struct REGPACK r.h> struct tsr_info { unsigned segmento_real. far *tsrx.r_di-2)==10787)). else { tablaptr[vx][0]=FP_SEG(tsrx). i=0.r_es. } numvect = peekb(FP_SEG(tsr). ofs). *interrupción=0x101. base-1). } void listar_tsr (int entrada. if (vect) /* listado de vectores */ { base=peek(r. cad). r. ofs). vector). p=cadaux. mxid++) if (tsr_convenio(mxid.r_di-8). printf("%02X "."). } else tsr_raro[entrada-0xc0]=raro=1. cadena). mx=0xC0. tsrx->vectores_id-1). peek(r. cadaux[80]. cadena [80].r_es. vx++) { vector = peekb(FP_SEG(tsr). else mx++. nofincadena. r. tsr->vectores_id+5*i+1).r_es. intr (0x2f. } } *interrupción = vector. int max_long.r_ax=entrada << 8.El %s no se puede desinstalar: ". */ /* */ /********************************************************************/ #include #include #include #include <dos. mxid). /* autor */ } if (correcto) printf(" . struct tsr_info far *tsr. correcto=mx_unload (mxid.r_es. } if (mxid==-1) { for (mxid=0xc0. i. sgm = peek(FP_SEG(tsr).r_es. /* el OFFSET se desprecia */ i=peekb(FP_SEG(tsrx). while ((*info!=’:’) && (*info)) cad[i++]=*info++. desinstalar(). /* informar del TSR */ primera_vez=0. else { if (vector==0x100) printf (" .tsrx->vectores_id+5*(i-1))!=vector) && i) i--.PROGRAMAS RESIDENTES 169 listar_tsr (entrada. &nombre). } int hay_tsr (int entrada) /* función booleana: 1 si hay TSR */ { struct REGPACK r. r. *p. cadaux).tablaptr[vx][1]+2) + ((unsigned) peek(tablaptr[vx][0].h> <stdlib. unsigned char info_extra. while (*p++).h> <stdio. while (*p) if ((*p++)==’:’) *(p-1)=0. for (entrada=0.r_es. char cad[40].\n". mxid<=0xFF.2MGUI es «demasiado fuerte» para TSRKILL.Ejecute con /V para listado de vectores.r. cad.r_di-12)*16.

Por tanto. intr (0x21. tal como se hacía con la interrupción Multiplex. *info = MK_FP(r. algunos no llegan ni a eso. con objeto de solucionar estos casos. La interrupción Multiplex presenta un elevado nivel de polución debido al gran número de programas que la utilizan incorrectamente. pop si. Habida cuenta de que las principales empresas desarrolladoras de software de sistemas ojean el INTERRUP. intr (0x2f. Esta interrupción no ha sido empleada hasta ahora por el DOS ni por ninguna aplicación importante. A la hora de llamar a la INT 2Dh se indicará en AH. Como llamar a la INT 2Dh puede ser relativamente lento (debido al elevado número de programas residentes que puede haber instalados) con esta función se solicita al TSR un punto de entrada alternativo para poder llamarlo de una manera más directa sin la INT 2Dh. &r). son tan malos que casi nadie los emplea.es ah. } int tsr_convenio (int entrada.4 de la especificación.ha desarrollado un método alternativo basado en la interrupción 2Dh. Offset 8 (8 bytes): Nombre del programa (rellenado con espacios si hace falta). La información que expongo a continuación se corresponde con la versión 3. Todo el funcionamiento se basa en invocar funciones en el programa residente. en donde sea procedente.bx word ptr controlador+2. La propuesta AMIS (Alternate Multiplex Interrupt Specification) implementa un sistema estandarizado de interface con los programas residentes. } int existe_xms () { struct REGPACK r.Función 0: Chequeo de instalación.LST antes de utilizar una interrupción. } void liberar_umb (unsigned segmento) { long controlador.r_di=0x1992. Si . en cambio. CX=340h para la v3. con el siguiente formato: Offset 0 (8 bytes): Nombre del fabricante (rellenado con espacios al final). AT Y PS/2 asm sti } } switch (tsr->info_extra & 3) { case 0: r. &r). Existen las siguientes funciones: . no es muy arriesgado seguir este convenio. Sin embargo.r_di-16). case 1: if (existe_xms()) liberar_umb (tsr->segmento_real). en CX se devuelve además el número de versión del interface AMIS que soporta el TSR (ej.r_es. r.r_ax==0xFFFF) && (peek(r. con la propuesta AMIS podrían haber soportado una función de desinstalación accesible por cualquier agente externo. En caso contrario se devuelve un 0FFh en AL. break.r_di-2)==10787)). r. En algunos casos se soluciona el problema instalando primero los programas conflictivos y después los que trabajan bien. Lo mínimo que se puede exigir a un programa residente que utilice esta interrupción es que soporte el chequeo de instalación (la llamada con AL=0) y devuelva una señal de reconocimiento afirmativo (AL=0FFh) si está empleando esa entrada en cuestión. } } return (posible). Sin embargo. El inconveniente de ejecutar código en la copia residente es que ocupa algo más de memoria. Ralf Brown -autor del INTERRUP. que.4310h 2Fh word ptr controlador. ax. push si. TSRKILL no puede desinstalar las conocidas utilidades HBREAK o 2MGUI. este campo puede constar simplemente de un cero si no se desea inicializarlo. r. . asm { push mov int mov mov mov mov call pop } } es.r. es de esperar que la propia Microsoft no utilice tampoco la INT 2Dh para sus propósitos en futuras versiones del DOS. en DX:DI se entrega la dirección de la cadena de identificación.segmento controlador di. y la necesidad de implementar dichas funciones. 10. Por citar un ejemplo. r.r. struct tsr_info far **info) { struct REGPACK r.170 EL UNIVERSO DIGITAL DEL IBM PC. intr (0x2F. return ((r.r_di-4)==9002) && (peek(r. r. Los programas que emplean la INT 2Dh deben interceptarla e implementar una serie de funciones. return ((r.r_es.5. &r). Como luego veremos. Offset 16 (hasta 64 bytes): Cadena ASCIIZ (terminada en 0) con la descripción del producto. break.r_ax=0x4300. Por fortuna.r_ax=entrada << 8. restaurar el estado del sistema de manera más completa o realizar tareas específicas que sean necesarias.r_ax & 0xFF)==0x80).3. no es necesario que soporten todas las que propone el convenio.4). push di.LA PROPUESTA AMIS. para evitar conflictos entre aplicaciones.r_es=tsr->segmento_real.r_ax=0x4900.LST. Si no hay un TSR utilizando ese número se devuelve un 0 en AL.Función 1: Obtener punto de entrada. La ventaja de ejecutar código en la copia residente es que ésta puede..r_es. pop es. r.11h dx. el número de entrada y en AL la función.r_es=0x1492.

3).No es seguro desinstalar ahora. lo más normal es que el TSR devuelva un valor 4 sin hacer caso del valor de BL (de lo contrario. En BX se devuelve la causa genérica del fallo: 0-Desconocido. Esto en principio significa que el TSR puede hacer casi lo que le da la gana cuando le preguntan qué interrupciones controla. La rutina de control de interrupción respeta este formato. 2 .Imposible determinar. el TSR no es de tipo POP-UP. significa que el TSR debe ser invocado obligatoriamente vía INT 2Dh.Se devuelve en DX:BX la lista de interrupciones interceptadas. en CX se devuelve un código de error exclusivo de la aplicación que se trate. 3 .El TSR fue correctamente POP-UPado y posteriormente abandonado por el usuario.Función 3: Solicitud de POP-UP. TSR desinstalado: retorna con AX corrompido a la dirección DX:BX.Todo ha ido bien. pero el TSR no dispone de rutina al efecto. .La interrupción indicada ha sido interceptada. Además. 3 . un 1 para indicar que el TSR fue descargado por el usuario y los valores 2 al 0FFh están reservados para futuros usos. el autor del convenio avisa que no serán quizá soportados en otras versiones. el TSR lo reintentará en breve.Función 4: Determinar los vectores interceptados. Los valores 100h al 0FFFFh en BX están a disposición del programa que se trate. intentar solicitud más tarde. el programa que llama tendría que hacer un molesto bucle comprobando todas las interrupciones). 4 . El TSR está inhibido y devuelve en BX el segmento del bloque de memoria donde reside.Función no implementada. Si devuelve un 0FFh en AL ello implica que soporta una llamada directa. 1 .Imposible hacer POP-UP. se indica al TSR en DX:BX el punto donde deberá saltar tras su autodesinstalación (si la soporta). El valor que devuelve en AL se interpreta: 0 . A la vuelta.Es seguro desinstalar. el TSR lo intentará cuando pueda. Sería una lástima que un TSR devolviera un valor 0.Función no implementada. 1 . 0FFh . BX entrega un 0 para no indicar nada. A la entrada se indica en BL el número de la interrupción (excepto 2Dh). 2 . 0FFh .Función 2: Desinstalación. . AL devuelve un código: 0 . 2 . 1-La cadena de interrupciones se solapa con memoria que debe ser desalojada para el POP-UP. propuesto por IBM en las BIOS de PS/2: . se requiere intervención del usuario. 5 .El TSR ya está POP-UPado. Por tanto.Es seguro desinstalar. 2-Fallo en las operaciones de swapping necesarias para el POP-UP.No es posible el POP-UP ahora. pero el TSR no dispone de rutina al efecto. A la vuelta.No es posible el POP-UP en este preciso instante. El formato de la lista de interrupciones interceptadas es: Offset 0 (1 bytes): Número del vector (el último de la lista es siempre 2Dh). 0FFh .Función no implementada. Los valores 1 al 3 sólo están definidos por compatibilidad con versiones anteriores de la especificación (v3. cuyo punto de entrada devuelve en DX:BX. el TSR devuelve un código en AL que se interpreta: 0 .No es posible desinstalar ahora.Esa interrupción no ha sido interceptada. Offset 1 (2 bytes): Offset a la rutina de control de interrupción. Esta función está diseñada sólo para los programas residentes que muestran menús en pantalla al ser activados (normalmente con una combinación de teclas). 3 . 1 .La interrupción indicada ha sido interceptada. A la entrada. 4 . El TSR está aún habilitado y devuelve en BX el segmento del bloque de memoria donde reside. . Intentar de nuevo más tarde. 4 .PROGRAMAS RESIDENTES 171 devuelve un 0 en AL. DX:BX apunta a la rutina que la gestiona.Fallo. A la vuelta.

No obstante. 0 si es interrupción software o controlador secundario de la interrupción hardware. con los programas COM y EXE normales también se pueden tomar una serie de medidas para reducir la ocupación de memoria: la primera y más efectiva es no dejar residente el inservible espacio de entorno. devuelven 0 al no estar implementadas.4.172 EL UNIVERSO DIGITAL DEL IBM PC. por supuesto. ya que es un segmento previo al programa (de hecho. El más completo es la propuesta AMIS.MÉTODOS ESPECIALES PARA ECONOMIZAR MEMORIA. Por supuesto. esto último sólo debe hacerse después de finalizada la ejecución del programa -después de haber entregado el control al sistema-. Offset 9 (2 bytes): Salto corto a la rutina de reset hardware (que retornará con RETF). . se respetarán los primeros 96 bytes.por lo que el único área que es obligatorio respetar es la zona 00-54h: 85 bytes (incluso este área podría ser también casi totalmente ocupada. los 7 bytes anteriores corresponden al FCB extendido -circunstancia que poco suelen poner de relieve los libros técnicos.5. por supuesto). justo 6 párrafos: moviendo el programa hacia atrás un número entero de párrafos. Offset 2 (4 bytes): Dirección previa de ese vector de interrupción. De cara a aumentar el número potencial de usuarios de un programa residente es fundamental considerar el aspecto de la ocupación de memoria. Si el programa utiliza pocos datos como para cubrir el PSP. tipo COM. estos programas sólo pueden ser ejecutados una vez en el momento de arranque del sistema. AT Y PS/2 Offset 0 (2 bytes): Salto corto a donde realmente empieza la rutina de control (10EBh). 80h si es el controlador primario de la interrupción hardware (debe enviar un comando EOI al controlador de interrupciones 8259). Otra de ellas consiste en emplear el PSP para almacenar datos. El más sencillo es el primero (aunque ES:DI puede estar asignado de la manera que el lector considere oportuna. . Offset 0Bh (7 bytes): Reservados (a 0). al terminar residente hay que añadir el tamaño del PSP) y sería complicada la reubicación. por las prestaciones que ofrecen. no es preciso que un programa soporte todas estas funciones: para cumplir con la versión 3. para lo cual el programa puede auto-relocalizarse hacia atrás en la memoria. en el offset 5Ch comienza el primer FCB. Cualquiera de los tres métodos expuestos es válido para lograr una correcta localización del programa residente en memoria. El método más sencillo es implementar el programa como falso controlador de dispositivo (se verán en el capítulo siguiente) con objeto de evitar el PSP. los programas que cumplan la propuesta AMIS deben asignar dinámicamente el número de entrada que van a utilizar en la INT 2Dh. al final resulta sencillo desviar los vectores de interrupción decrementando su segmento en 6 unidades menos antes de desviarlos. pero después de finalizar la ejecución del programa). Como dije al principio.. buscando uno libre. Offset 12h: Rutina que controla la interrupción.COMPARACIÓN ENTRE MÉTODOS. Sin embargo. Offset 6 (2 bytes): Valor 424Bh (consejo de IBM). Offset 8 (1 byte): Banderín de EOI.4 de la especificación basta con implementar las funciones 0. son los dos últimos los más recomendables. . cabe la posibilidad de colocar código en el mismo. ya que el PSP es utilizado por el DOS al terminar la ejecución. como se vio en capítulos anteriores. 10. Por comodidad. machacando los 171 últimos bytes del PSP que no son vitales para el sistema: en efecto. En todo caso conviene respetar al menos los dos primeros bytes (y a ser posible también los dos situados en el offset 2Ch) con objeto de que no se vuelvan locos los programas del sistema que informan sobre el estado de la memoria (fundamentalmente el comando MEM). Esta treta sólo es factible. 2 (sin obligación de disponer de rutina de desinstalación) y la 4 (devolviendo un valor 4). Los de tipo EXE normalmente dejarán residente todo el PSP. sin embargo. 10.6.Funciones 5 y siguientes: Reservadas para futuras versiones del convenio. como se dijo antes. en programas de un solo segmento. Para chequear su instalación han de emplear los 16 bytes que indican el nombre del fabricante y el programa.

como los COM. habrá que pedírsela al DOS. conviene también. con objeto de que algunos programas de diagnóstico lo confundan con un programa (no obstante. preservar el estado de la estrategia de asignación de memoria y el estado de los bloques de memoria superior (si están o no conectados con los de la memoria convencional). escondiendo bien los fuentes. haciéndole apuntar -por ejemplo.0 hay un inconveniente: al acabar la ejecución del código de instalación del TSR. Si se utiliza LOADHIGH o HILOAD. no es problema alguno realizar la operación de copia. Las siguientes rutinas asignan memoria superior XMS (UMB_alloc) o memoria superior DOS 5 (UPPER_alloc): . el DOS ¡libera el bloque de memoria que se asignó con la función 48h!. los TSR podrán asignar memoria superior sea cual sea el sistema operativo. Por desgracia. se modifica la estrategia de asignación de memoria. Además. con lo que lo cargará en memoria convencional. por varios motivos.a sí mismo. Sin embargo. con ciertos controladores de memoria (tales como QEMM) la memoria superior es gestionada por la especificación de memoria extendida XMS (véase apartado 8. etc. que los dos primeros bytes del bloque de memoria superior contengan la palabra 20CDh (ubicada al inicio de los PSP). se puede comprobar si la versión del DOS es 5 (o superior) y aplicar el método propio que requiere este sistema. Para evitar esto. Sin embargo. con el método propio del DOS 5. Los TSR más eficientes deben detectar la presencia de memoria superior e instalarse automáticamente en ella. el sistema intenta reservar memoria para poder cargar el fichero desde disco. habrá de ser restaurada la estrategia de asignación de memoria y el estado de los bloques de memoria superior. Con DR-DOS y. hay dos métodos: uno. sin embargo. al acabar el programa. tras asignar el bloque de memoria. en especial para los programadores de sistemas. Esto significa que puede haber casos en que no tenga suficiente memoria para cargar el programa.0 y posteriores sólo existe memoria superior XMS si NO se indica DOS=UMB en el CONFIG.un best fit en memoria superior. . no son consideradas elegantes por los programadores conservadores.. También hay que crear el nombre del programa en los 8 últimos bytes del MCB manipulado. Por un lado.7.PROGRAMAS AUTOINSTALABLES EN MEMORIA SUPERIOR. De esta manera. Con MS-DOS. el comando MEM del DOS no requiere este detalle y lo tomaría directamente por un programa). estableciendo -por ejemplo. la mayoría de los usuarios suelen indicar esta orden con objeto de que el MS-DOS permita emplear LOADHIGH y DEVICEHIGH. cuando el DOS gestiona la memoria superior.. Para utilizar la memoria superior en estos sistemas hay que detectar la presencia del controlador XMS y pedirle la memoria (también habrá que llamarle después para liberarla).).3). controlador de memoria o configuración del sistema activos. modificar en su correspondiente bloque de control la información del propietario (PID). Por otro. se asigna memoria utilizando la función convencional de asignación (48h). lo más probable es que nadie se entere de ello. el DOS recorrerá la cadena de bloques de memoria y no encontrará ninguno que pertenezca al programa que finaliza. Por tanto. de hecho. por si no lo estaban. se la roba toda al controlador XMS. en este caso.. A continuación. un programa residente puede ocupar mucho más espacio en disco que lo que luego ocupará en memoria. Tratándose de programas de un solo segmento real. automodificándose . Si no se desea este ligero derroche de memoria convencional. De esta manera. Es conveniente intentar primero asignar memoria superior XMS: si falla.SYS. Consiste en engañar al DOS y. se mejora el rendimiento en aquellas máquinas con usuarios inexpertos que no emplean el HILOAD o el LOADHIGH del sistema. Finalmente.PROGRAMAS RESIDENTES 173 Es cierto que estas técnicas. Seguidamente. en general. Con MS-DOS 5. Niklaus Wirth se llevaría sin duda las manos a la cabeza. y no se pueden hacer estas salvajadas en entornos con protección de memoria (UNIX. se conectan los bloques de memoria superior con los de la convencional.. consiste en terminar residente (aunque sea dejando sólo los primeros 96 bytes del PSP) con objeto de que el sistema respete el bloque de memoria creado. Tras estas operaciones. 10. ese TSR tal vez hubiera cabido en la memoria superior: si es el propio TSR el que se auto-relocaliza (copiándose a sí mismo) hacia la memoria superior. este problema desaparece. con MS-DOS. hay un método más contundente.. Sin embargo el DOS y el 8086 las permiten y pueden ser bastante útiles. el procedimiento general es el siguiente: Primero. con programas que se mueven a si mismos dando vueltas por la memoria..

30h 21h AL. aprovechando el modo protegido en que está el ordenador con el controlador de memoria expandida instalado. está instalado el gestor XMS (AX=0) o hay un error (AL . en caso contrario devuelve el segmento en AX. El método expuesto consiste en modificar el PID para evitar que el DOS desasigne la memoria al acabar la ejecución del programa.umb_state BH.0 fue el primer sistema operativo DOS que permitía instalar programas residentes en los primeros 64 Kb de la memoria extendida. .1 no_umb_disp DX.5803h BL.0 El auténtico empleo de memoria extendida para instalar programas residentes.PROGRAMAS RESIDENTES EN MEMORIA EXTENDIDA CON DR-DOS 6.1 AX. Microsoft eligió a DISPLAY.AX . El MS-DOS 5. guardado el resultado . Este fichero es utilizado en la conmutación de páginas de códigos (factible en .174 EL UNIVERSO DIGITAL DEL IBM PC. KEYB.5801h BX. Si no hay bastante CF=1. . El inconveniente principal es que este área es bastante limitada (en la práctica. De una manera torpe. UMB_alloc PROC PUSH PUSH PUSH CMP JNE MOV MOV CALL CMP MOV JNE POP POP POP CLC RET MOV POP POP POP STC RET ENDP BX CX DX xms_ins. algo menos de 20 Kb libres) y la instalación un tanto compleja. .5 AX UPPER_existe UPPER_fin AX AX. en los primeros 64 Kb de memoria extendida accesibles desde DOS. devuelve el código de error del controlador XMS). -----------. solicitar memoria superior . TASKMAX) se pueden cargar en esta zona -algunos incluso lo hacen automáticamente-. hubo fallo DS AX DS. Los programas con autoinstalación en memoria superior deberían tener un parámetro (al estilo del /ML de los de DR-DOS) para forzar la instalación en memoria convencional si el usuario así lo requiere. la rutina de instalación habrá de montar el código en memoria asignando posiciones absolutas a ciertos modos de direccionamiento.1 21h AX.41h 21h BX AH. con DOS 5. copiar nombre de programa La rutina UMB_alloc requiere una variable (xms_ins) que indique si está instalado el controlador de memoria extendida. Ciertos programas del sistema (COMMAND.. sin embargo no está tan normalizado como en el caso del DR-DOS y es probable que en futuras versiones cambie el método. La rutina UPPER_alloc necesita una variable de palabra (alloc_strat) y otra de tipo byte (umb_state) en que apoyarse.BX XMS_fallo DX CX BX AX. restaurar estrategia .AX . preservar estrategia UPPER_existe: MOV INT MOV MOV MOV INT MOV MOV INT POP MOV INT PUSHF PUSH MOV MOV INT MOV MOV XOR INT POP POPF JC PUSH DEC MOV INC MOV MOV PUSH MOV MOV MOV DEC MOV MOV MOV MOV CLD REP POP POP CLC RET ENDP AX.0 también utiliza el HMA para cargar programas residentes. preservar estado UMB . preservar párrafos. 10. . número de párrafos . necesario DOS 5.5801h BX.CX MOVSB ES DS .5802h 21h umb_state.CX CX. simular PSP ES CX.5803h BX. el primer requisito que han de cumplir es el de ser relocalizables: en la práctica.. -----------. no será tratado en este libro.5800h 21h alloc_strat.AL AX. algún emulador de coprocesador para 386 emplea esas técnicas. UPPER_alloc PROC PUSH MOV INT CMP POP JAE STC JMP PUSH MOV INT MOV AX AH.DS ES.0 mínimo . también 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. no hay controlador XMS . . asignar memoria .BH 21h AX .10h gestor_XMS AX.. La ventaja de cargar aquí las utilidades residentes es que no ocupan memoria. no memoria convencional ni superior).Reservar bloque de memoria superior del nº párrafos AX. NLSFUNC.48h 21h AX AX. UPPER_fin: UPPER_alloc . manipular PID WORD PTR DS:[16]. del tamaño . así como otra (gestor_XMS) con la dirección del mismo. CF=1 si no .CS CX DS.CX DI.20CDh .8. conectar cadena UMB’s . En particular.AX AH.0 DX CX BX .8 SI.. Por ello. restaurar estado cadena UMB UPPER_fin . solicitado (AX párrafos). El DR-DOS 6.párrafos requeridos . High Memory best fit . AT Y PS/2 .AX AX WORD PTR DS:[1]. .alloc_strat 21h AX.CX CX. .Reservar memoria superior. devolviendo en AX el segmento donde está. ¿ha ido todo bien? segmento UMB/código de error fallo ok no_umb_disp: XMS_fallo: UMB_alloc .SYS para ocupar parte del área que el propio DOS deja libre en el HMA tras instalarse. zona comúnmente conocida por HMA. Otro inconveniente es la complejidad de la instalación: normalmente los programas se cargarán en el segmento 0FFFEh con un offset variable y dependiente de la zona en que sean instalados. SHARE.0. Aquí nos limitaremos a un objetivo más modesto. dicho entre comillas (al menos.

. es mucho más seguro utilizar una función del sistema al efecto: MOV INT JC CMP JE CMP JE CMP JE JA AX.PROGRAMAS RESIDENTES 175 máquinas con EGA y VGA) para adaptar el juego de caracteres a ciertas lenguas. Por consiguiente. No eliminar los posibles bloques libres de menos de 16 bytes es saltarse una norma del sistema operativo y podría tener consecuencias imprevisibles con futuros programas cargados. otra variable de DR-DOS se obtiene en SI el offset al primer bloque libre de memoria en el HMA (ubicado en 0FFFFh:SI). En general. con lo que no es posible instalar programas en el HMA. 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. por este orden: System. TaskMAX. Como se ve. Hubiera sido mucho más inteligente elegir el KEYB y otros programas similares que casi todo el mundo tiene instalados.lo que hay que hacer es modificar la variable del DOS que indica el primer bloque libre para que apunte a su sucesor. aunque probablemente sí de las posteriores.ES:[BX+14h] . si el bloque no tenía predecesor -si era el primer bloque. eliminándolo de la lista encadenada por el simple procedimiento de hacer apuntar su predecesor a su sucesor. COMMAND. y no de otras versiones anteriores de este sistema. esta variable del DOS ha de ser actualizada ya que desde ahora el primer bloque ocupado (bueno.4458h 21h SI. En todo caso. NLSFUNC. variable exclusiva de DR-DOS DI.4452h 21h no_es_drdos AX. Conviene hacer ahora hincapié en que esta manera de gestionar el HMA. significa que el DR-DOS no está instalado en el HMA o que no está instalado el EMM386. probablemente es MS-DOS El DR-DOS 6. En concreto. a nivel de bloques de memoria. y en DI el offset al primer bloque ocupado de memoria en el HMA (en 0FFFFh:DI). tienen una cabecera de sólo 5 bytes: los dos primeros apuntan al offset del siguiente bloque de memoria (cero si éste era el último) y los dos siguientes el tamaño de este bloque. no se almacena el nombre en formato ASCII sino con un código. En el HMA los bloques de memoria forman una cadena pero mucho más simple que en los demás tipos de memoria. Para comprobar que en una máquina está presente el DRDOS puede verificarse la presencia de una variable de entorno del tipo «OS=DRDOS» y otra «VER=X. SHARE. se rebaja el tamaño de este bloque modificando su cabecera. Téngase en cuenta que los bloques no han de estar necesariamente seguidos. KEYB. se trata de gestionar una lista encadenada. aunque quizá el más recomendable sea el 0 (de todas maneras. claro está-.0.1063h drdos341 AX. Los programas creados por el usuario pueden utilizar cualquiera de los códigos. Después.0 implementa un nuevo servicio para gestionar la carga de programas en el HMA. en realidad el último) es el recién creado.SYS. Lógicamente.ES:[BX+10h] . se crea una cabecera para el nuevo bloque (que se sitúa al final del bloque libre empleado. primero se recorre la cadena de bloques libres hasta encontrar uno del tamaño suficiente -si lo hay.1067h drdos6 drdos_futuro .1065h drdos5 AX. limitaremos el estudio al caso del DR-DOS. Para cargar un programa residente aquí. por lo que la información del tamaño no debe emplearse para direccionar al siguiente bloque: ¡para algo están los primeros dos bytes!. a su vez. función exclusiva del DR-DOS . es propia del DR-DOS 6. La información que viene a continuación fue obtenida por la labor investigadora del autor de este libro. puede haber varios bloques con el mismo código). para compartir la memoria con el sistema operativo. A continuación. Sólo si el kernel del DR-DOS reside en el HMA se puede utilizar esta técnica. El quinto byte puede tomar un valor entre 0 y 5 para indicar el tipo de programa. Ha de tenerse en cuenta que si lo que sobra del bloque libre que va a ser utilizado son menos de 16 bytes.XX» con la versión. se le debe desechar -porque así lo establece el sistema-. Si el offset al primer bloque de memoria libre es 0. lo que más que un problema de ensamblador lo es de sentido común. que la envió posteriormente a Ralf Brown para incluirla en el Interrupt List. Con las siguientes líneas: MOV INT MOV MOV AX.

el reloj no aparecerá realmente en esa coordenada sino lo más a la derecha posible en cada tipo de pantalla activa. Lo normal en los programas del sistema -y. Los parámetros opcionales X e Y permiten colocarlo en la posición deseada dentro de la pantalla: si /X=72 (valor por defecto). 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 parámetros ON u OFF). por sí solos. lo más recomendable. Con /C se puede modificar el valor del byte de atributos empleado para colorear el reloj. se emplean para controlar la aparición en pantalla o no del reloj -por defecto aparece nada más ser instalado-. por lo que es desinstalable incluso aunque no sea el último programa residente cargado. 10. aunque también quedaría feo de todas maneras si se rellenara de espacios en blanco. 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. Si las coordenadas elegidas están fuera de la pantalla -ej. .habrá de relocalizarse el código. de lo contrario avisará durante 15 segundos. por consiguiente. delegándoselo al programa que quiera emplear este área. Después de haber sonado.. El programa de ejemplo es un completo reloj-alarma residente. Admite la siguiente sintaxis: RCLOCK [/A=hh:mm:ss | OFF] [ON|OFF] [/T=n] [/X=nn] [/Y=nn] [/C=nn] [/ML] [/U] [/?|H] La opción /A permite indicar una hora concreta para activar la alarma sonora o bien desactivar una alarma (/A=OFF) previamente programada -por defecto. Allí -o antes de realizar la transferencia. no hay alarma definida-. al cambiar a un modo de menos columnas o filas.es que nuestras aplicaciones corran en la dirección 0FFFEh:XXXX y no la 0FFFFh:XXXX como en principio podría suponerse. Obviamente. siempre que tras él se hayan instalado sólo programas del convenio (o al menos otros que no utilicen las mismas interrupciones). el programa principal instalador deberá acabar normalmente -y no residente-. si se han producido cambios en el monitor desde que apareció el reloj. Posee su propia rutina de desinstalación (opción /U). el fragmento de pantalla restaurado puede quedar feo. pero es sencillo y muy económico en cuanto a consumo de memoria se refiere. la gestión del HMA es engorrosa porque el sistema realiza poco trabajo sucio. Cuando comienza a sonar la alarma. en su defecto. ni siquiera al cabo de 24 horas. aunque quizá se trate de un detalle irrelevante. El programa utiliza el convenio CiriSOFT para detectar su presencia en memoria. con una simple instrucción de transferencia. En general. con /U se puede desinstalar de la memoria. el reloj seguirá apareciendo correctamente casi al instante -se refresca su impresión 4 veces por segundo-. Por ello. si vale 0 no se harán señales de ninguna clase. De hecho. estando o no el reloj en pantalla. 4 para pitar a los cuartos y 5 para avisar cada cinco minutos. esto último es lo que sucede cuando se trabaja con pantallas gráficas. habrá de copiarse este desde la memoria convencional hacia el HMA. /ML fuerza la instalación en memoria convencional. Por último. Una vez cargado. No posee intuitivas ventanas de configuración ni cientos de opciones. .176 EL UNIVERSO DIGITAL DEL IBM PC. También está equipado con las rutinas que asignan memoria superior XMS o. Es posible ejecutarlo cuando ya está instalado con objeto de cambiar sus parámetros o programar la alarma. sí).el resultado puede ser decepcionante (esto no sucede si /X=72). Cuando se expulsa el reloj de la pantalla. la alarma quedará desactivada y no volverá a actuar. Por último. se puede pulsar Ctrl-Alt-R o AltGr-R para cancelarla. 2 para avisar a las medias. en los casos en que sea posible. El parámetro /T puede tomar un valor 1 para activar la señal horaria -por defecto-. Si se produce un cambio de modo de pantalla o una limpieza de la misma. Los parámetros ON y OFF. se han de desviar los correspondientes vectores de interrupción a las nuevas rutinas del programa residente. con lo que no es necesario utilizar la utilidad general de desinstalación. AT Y PS/2 Una vez reservado espacio para el nuevo programa. se restaura el contenido anterior a la aparición del reloj.EJEMPLO DE PROGRAMA RESIDENTE QUE UTILIZA LA BIOS.9.

Se utiliza la función de impresión en pantalla de la BIOS.2 veces por segundo tampoco se notaría un retraso perceptible. es preciso desviar la INT 10h con objeto de detectar su invocación y no llamarla cuando ya se está dentro de ella (el reloj funciona ligado a la interrupción periódica y es impredecible el estado de la máquina cuando ésta se produce). 1 y 2-> 000: normal. sólo hacen falta 1. en teoría. o crear una utilidad de música residente por interrupciones para amenizar el uso del PC. 011: *. según la nota que se trate.Identificación estandarizada del programa program_id LABEL BYTE . sin embargo se toma la precaución de llamar justo al principio al anterior controlador de la interrupción. INT 8 DWORD . lo que se realiza automáticamente en todos los entornos operativos que existen en la actualidad.Macros de propósito general XPUSH MACRO RM IRP reg. ya que sería difícil utilizar la coma flotante al efecto. dirección original 0 0 9 .* RCLOCK v2. aunque el fichero ejecutable ocupa casi 6 Kb. campo reservado . número de vectores de interrupción usados $ 8 .5 Kb libres de memoria superior para instalarlo en este área. es sencillo modificar las melodías que suenan.0/6.SYS formato EXE . por ejemplo.0: por ello. aunque conviene ser precavidos.********************************************************************* . . un parámetro /A=000020:3:48 para programar la alarma a las 20:03:48. -----------. Evidentemente. <RM> POP reg ENDM ENDM segmento_real offset_real longitud_total info_extra DW DW DW DB multiplex_id vectores_id extension_id autor_nom_ver DB DW DW DB DB . 001: bloque UMB XMS . se tiene la precaución de comprobar si al pulsar Ctrl-Alt-R o AltGr-R la BIOS o el KEYB han colocado un código Alt-R en el buffer. INT 10h DWORD .Programa rclock SEGMENT ASSUME CS:rclock. se obtienen de una tabla donde están ya calculados. bit 7 a 1: «extension_id» definida 0 . -----------.********************************************************************* . la parte más engorrosa del programa es la interpretación de los parámetros en la línea de comandos. 010: *.3 (c) Septiembre 1992 CiriSOFT * . ese carácter se saca del buffer para que no lo detecte el programa principal (si se sacara sin cerciorarse de que realmente está. . EQU 100h $ DB tabla_vectores EQU DB ant_int08 LABEL ant_int08_off DW ant_int08_seg DW DB ant_int09 LABEL ant_int09_off DW ant_int09_seg DW DB ant_int10 LABEL ant_int10_off DW ant_int10_seg DW DB ant_int2F LABEL ant_int2F_off DW ant_int2F_seg DW tabla_extra **************************************** * * * D A T O S R E S I D E N T E S * * * **************************************** JMP main LABEL BYTE DW ctrl_exterior . INT 2Fh DWORD .0 (excepto en modo KEYB US). Sin duda.Tabla de períodos de las notas . Aún así. la BIOS es dura como una roca (no se cuelga el ordenador. el uso del ensamblador para este tipo de programas es más que recomendable: además de aumentar la fiabilidad del código.3". con PSP . El método utilizado para detectar la pulsación de AltGr en los teclados expandidos no funciona con el KEYB de DR-DOS 5. En los modos de pantalla normales no habría tanta conflictividad. . cuando se hace DIR) e incluso cuando se imprime.* * .PROGRAMAS RESIDENTES 177 memoria superior solicitada al DOS 5. Al leer el teclado.* »»» Utilidad de reloj-alarma residente ««« * .0 o superior.* * . tarea incómoda en ensamblador. DS:rclock ORG ini_residente . a Alt a secas). offset real " " " . Esto suele suceder a menos que el KEYB no sea demasiado compatible (Ctrl-Alt equivale. dirección original 0 0 2Fh . -----------. permitido control exterior DW 0 . Si así es. . el consumo de memoria es más que asequible. Por ello. en los modos gráficos SuperVGA de elevada resolución aparecen fuertes anomalías al deslizarse la pantalla (por ejemplo. Sin embargo. incluso en máquinas modestas. -----------. dirección original 0 0 10h . segmento real donde será cargado . con lo cual el reloj se imprime también en las pantallas gráficas (incluida SuperVGA). . número Multiplex de este TSR tabla_vectores tabla_extra "*##*" "CiriSOFT:RCLOCK:2.* (c) Grupo Universitario de Informática . La impresión del reloj se produce sólo 4 veces por segundo para no ralentizar el ordenador. el programa es bastante flexible y se puede indicar. La interrupción periódica es empleada no sólo para imprimir el reloj sino también para hacer sonar la música. también se instala en memoria convencional y sus requerimientos mínimos son un PC/XT y (recomendable) DOS 3. INT 9 DWORD . programa 100% reubicable inicio: ctrl_exterior LABEL BYTE reubicabilidad DB 1 activacion DW visibilidad . enviando las notas adecuadamente al temporizador a medida que se van produciendo las interrupciones. No se utiliza INT 1Ch porque la considero menos segura y fiable que INT 8. De la manera que está diseñado el programa. aunque se realizara 18.SYS . en cualquier caso). dirección original 0 0 0 0 0 80h XPOP . sin embargo.Valladolid * . en caso de no estar el ordenador se quedaría esperando una pulsación de tecla). .* * .0 4 . zona de memoria ocupada (párrafos) . aunque esto es un fallo exclusivo de dicho controlador. Los valores para programar el temporizador. Si se anula la rutina que controla INT 10h. <RM> PUSH reg ENDM ENDM MACRO RM IRP reg. bits 0.

57 20.57. para almacenar el contenido previo .3216. AL.3.CS:multiplex_id preguntan CS:ant_int2F .10. . un par de dobles pitidos: musica_5min DB 57.1. .56. No se imprimirá en pantalla .2. CX.3408. .1608.1.37. SI. -----------.37.1 AX. sí: operacion. Datos para el período de las 89 notas. .8105. DI. CS DS AH.52.1. ¿"callar" durante 1 segundo? . consultar estado del buffer no se colocó Alt-R en buffer este KEYB es más compatible: sacar código Alt-R del buffer .20425.255 .52. antes de que un 255. .56.1912 1805. . .54. típica música de las iglesias: musica_horas DB DB 61.6072. BP.22926.1 musica_sonando.1.379.14442.2 sg.255 no_sonando: musica_cuartos DB 52.56. aunque las de código 0 al 6 son «silenciosas».7.52.1. DS.1.358.255 ret_int09: .CS DS. saltar al gestor de INT 2Fh no llama alguien del convenio no llama alguien del convenio sí llama: darle información "entrada multiplex en uso" preguntan: 41 .1.Rutina de control INT 10h.AX ES..40h DS.57. .1 51.13h .1518.5106. . no hay sonido . . . -----------.Controlar la generación de señales sonoras avisos_sonoros PROC CMP JE DEC JMP avisos_on: CMP JNE DEC JNZ CMP JE MOV MOV MOV CALL parando. entonces tomar el contenido bios_scr_proc .3610.AH cont_refresco.1704. .568.10820. reponer contenido de pantalla <ES.638. de la pantalla (sólo modo texto) fin_int08: ges_int08 <AX.15301. .1013.255 no_hay_alt_r: fin_int09ds: fin_int09: ges_int09 .51.Cadenas para imprimir hora_actual horasH horasL minutosH minutosL segundosH segundosL restaurar LABEL DB DB DB DB DB DB DB DB DB DB DB BYTE 0 0 ":" 0 0 ":" 0 0 0 8 DUP (’ ’) 8 DUP (7) . no . DI.57. . BX.59. tomando como base un reloj de 1.2146. .56. .3+128.319. fin_int09 .54.2. .1 49.refresco cont_refresco. si al byte de duración se se produce una pausa de 1/18. SI. .AX avisos_sonoros . 5 -> cada 5 min.2. .19318 MHz (el del 8253). acelerar presencia/ausencia .4052.Parámetros básicos del reloj alarm_enable hora_alarma alarm_h alarm_m alarm_s visibilidad tipo_aviso c_x c_y color refresco DB LABEL DW DB DW DB DW DB DB DB DB DB DB 0 BYTE "0 " ":" "00" ":" "00" 1 1 72 0 14+4*16 4 .59.3.2.10. 0 -> sin señal coordenada X para el reloj coordenada Y tinta amarilla y fondo rojo cada 4/18.1 . .AH no_sonando AH parando.1 .3+128. 2 -> a las medias 4 -> a los cuartos.902.1432. BX. .56. El final se indica con sirven para le suma 128. -----------.1276.2. fin de la INT 10h ges_int10 .1 49. . .8 .AX AL. . recargar cuenta CS:in10.37.49. . tres pitidos ascendentes: .3825. -----------.2. previo de la pantalla gestiona_fondo .37. -----------. . CX. -----------.9098.1. indicar entrada en INT 10h .2409.0 contador_nota.2. reloj oculto ¿recientemente? fin_int08 . . .61.676. DX.54.3.56.19279 18197.1.9639.0FFFFh .1.10.12867.759 716.Rutina de gestión de INT 2Fh ges_int2F PROC STI CMP JE JMP CMP JNE MOV CMP JNE PUSH POP LEA MOV IRET ENDP FAR AH. ES> AX. WORD 37.3. . reloj aparece 1 -> señal horaria. llamar al controlador previo . alarma OFF .3036 2865.12145 11463.61.9 bios_scr_proc .AL .956.52. sí . .) Las primeras 7 notas son inaudibles y hacer pausas.2.ES AX.13632.52. .37. .301 284 ges_int2F . CX. .25734.506.3.60h formato de la música: número de nota (0-88).1. .3. .5731.10.Rutina de gestión de INT 9 ges_int09 PROC PUSH IN PUSHF CALL CMP JNE PUSH MOV MOV MOV XOR TEST JZ TEST JZ STI PUSH POP MOV CMP JNE DEC MOV MOV MOV CALL JMP XOR MOV XPUSH MOV INT JZ MOV INT XPOP POP POP IRET ENDP FAR AX AL. .56.57. AX. -----------.2. .59. *************************************** * * * C O D I G O R E S I D E N T E * * * *************************************** .1 47. no más notas parar música desactivar alarma silenciar altavoz . flag contador de entradas en INT 10h contador de INT’s 8 a «saltar» página de vídeo activa modo de vídeo activo (valor imposible para provocar inicialización) 8/9 para preservar/restaurar la zona de pantalla ocupada por el reloj 1 si el reloj está en pantalla coordenada X real del reloj a 1 si música sonando apunta a la siguiente nota musical que va a sonar INT’s 8 que le quedan por sonar a la nota que está en curso a 1 si se procesa la nota separadora de notas contador para detener el sonido scr_getted: restaurar?: .12 ctrl_alt . .52.2.1.7650 7221.54.6433. BX> DS AX invertir bits de Ctrl y Alt pulsado Ctrl-Alt no pulsado AltGr ctrl_alt: .17175.7.1 16h no_hay_alt_r AH.2553.47. . .54. no visible.852.8 fin_int09ds . Los datos (para notas mayores de 6) se han calculado con la fórmula: 1193180/(36. . . 40 42 44 45 47 49 51 52 54 56 57 59 61 63 43 46 48 50 53 55 58 60 62 .1 . . no hay sonido en curso . en 1 segundo.57.1.3+128. CX. .2.0 fin_int08 . darlos si es necesario cont_refresco . AL. BYTE PTR DS:[96h]. -----------. AX> .27264.1.0 otra_nota turno_blanco.1.2 segundos suene otra nota. .1073. .7.AH alarm_enable.10.16211.57. ges_int10 PROC INC PUSHF CALL DEC IRET ENDP FAR CS:in10 CS:ant_int10 CS:in10 . .804.2. ya había desaparecido visible.24290.1. . .478 451. por defecto.426.Rutina de gestión de INT 8 .2 seg. por defecto.1 255 CS:ant_int09 . no han pasado las suficientes AL.2. Las notas están ordenadas ascendentemente como las de un piano. sí.1204 1137.3.56.2274.52.1 51. no.2.3.3.59.1492h ret_no_info . tres pitidos descendentes musica_medias DB 47.2026.10.1 no_mas_notas contador_nota misma_nota turno_blanco.21640.autor_nom_ver AX. . no visible.AH <BX. ¿reloj visible? restaurar? .0 .3. . . DS AX.1 . BP.4293.7. duración (en 1/18. .7.7.10. en efecto: es preciso operacion.1. espiar código de rastreo llamar al KEYB ¿tecla «R»? no . . ret_no_info: tabla_periodos LABEL DW DW DW DW DW DW DW DW DW DW DW DW .0 avisos_on parando fin_avisos musica_sonando.1352. DI.1992h ret_no_info .54.2705.7.2.2.5410.3.37. CS ES . sólo una vez y durante una interrupción período inaudible .402. . sigue sonando todavía la nota ¿pausa entre notas? no sí. .6816. .52.12 .8*(2^(1/12))^(nota-6)) .30603 28885.61. . crear cadena con la hora visibilidad. ¿acaba de aparecer? scr_getted .536.0 16h <BP.178 EL UNIVERSO DIGITAL DEL IBM PC.59. estamos dentro de INT 10h obtiene_hora .51.Variables de control general in10 cont_refresco pagina modo_video operacion visible c_xx musica_sonando puntero_notas contador_nota turno_blanco parando DW DB DB DB DB DB DB DB DW DB DB DB 0 1 0 255 0 1 0 0 0 0 0 0 . .2.2.10212.20. contador de INTs 8 a «saltar» fin_int08 .602.338. . cuando se ejecute una INT 10h para no reentrar al BIOS.4819 4549.1. DX. . detectar cambio en pantalla print_reloj .2. .8.AH chiton ret_int09 visibilidad. -----------. imprimir reloj fin_int08 visible. . . BP> AH. fragmento del preludio 924 de Bach: musica_alarma DB DB DB DB DB DB DB 47.59. DI. AT Y PS/2 .0 programar_8253 . invertir visibilidad reloj .19 musica_sonando.2.Sonido . .3+128.2.2.DS:[17h] AL. se reimprime el reloj ges_int08 PROC PUSHF CALL STI XPUSH MOV MOV MOV CALL DEC JNZ MOV MOV CMP JNE CALL CMP JNE CMP JE MOV MOV CALL CALL CALL JMP CMP JNE MOV MOV CALL XPOP IRET ENDP FAR CS:ant_int08 .8587. DS.

pasar BCD a ASCII . ¿avisar a las medias? fin_avisando .0 .[BX] BX AL. pasar binario a BCD ."00" .c_y BH. inicio de la melodía contador_nota. minutos múltiplo exacto de 5 CL.[BX+tabla_periodos] . ¿se acabaron las notas? sonar .127 .72 dejar_c_x BL. .Avanzar cursor a la derecha cursor_derecha PROC MOV MOV INT INC MOV MOV INT RET cursor_derecha ENDP BH.2065 = seg. no es la hora de la alarma AX. primer carácter a imprimir . sí alarm_enable.AX AX.AL . .Controlar posible cambio de modo de pantalla o página . .’ ’ horasH. -----------. .hora_actual DI.AH AX CL.5 .0FCh SHORT $+2 SHORT $+2 61h. en efecto SI. BX = posición en la tabla BX.BH AL.8 . desactivar alarma chiton . no.AX . .1 .BX .7 . período del sonido programar_8253 fin_avisos alarm_enable.WORD PTR minutosH DI. ¿avisar a las horas? fin_avisando ."00" segundosH.2 10h . guardarlas para restaurarlas . -----------. SI = segundos restantes . ahora el menos significativo turno_blanco.Detener sonido por el altavoz chiton PROC IN AND JMP JMP OUT RET ENDP AL. posible desbordamiento) .PROGRAMAS RESIDENTES 179 misma_nota: otra_nota: JMP MOV INC INC MOV MOV MOV AND ROL MOV AND CMP JNE MOV MOV CALL JMP sonar: INC MOV XOR SHL MOV CALL JMP no_mas_notas: CMP JE LEA LEA MOV CLD REP JNE LEA JMP no_alarma: MOV MOV MOV CMP JNE CMP JNE LEA CMP JAE media?: CMP JNE CMP JNE LEA CMP JAE cuarto?: CMP JE CMP JNE cuar_quiza?: CMP JNE LEA CMP JAE cinco_min?: CMP JE CMP JNE cinc_quiza?: CMP JNE LEA CMP JB fin_avisando: MOV MOV MOV fin_avisos: RET avisos_sonoros ENDP fin_avisos BX.Preparar la producción de sonido programar_8253 PROC PUSH MOV OUT POP JMP JMP OUT MOV JMP JMP OUT JMP JMP IN OR JMP JMP OUT RET programar_8253 ENDP AX AL. altavoz silenciado chiton . coordenadas del cursor en DX . sí: preservar área pantalla CX.1 . .pagina AH.musica_cuartos-2 . acallar altavoz no_mas_notas BH contador_nota.3 . ¿hora en punto? media? DI.pagina 10h DX AH. compensar futuro decremento musica_sonando. DXDISI = DXDISI / 19663 . .61h AL.0 no_alarma .AL . siguiente nota AL. la tabla es de palabras AX.AH . .AL pagina. evitar cero a la izda en hora no_cero_izda: . incrementar X (sin controlar .c_xx DH. ¿minutos múltiplos de 5? cinc_quiza? minutosL.1 .10 CL AX.AX DI. ¿hora actual = hora alarma? no_alarma .’0’ fin_avisos DI. ¿30 minutos exactos? cuarto? DI. y atributos blancos BX fondo_clr_ar .AH .Crear cadena de caracteres con la hora actual obtiene_hora PROC PUSH XOR MOV MOV MOV POP MOV CALL MOV CALL PUSH PUSH MOV CALL MOV MOV DIV OR CMP JNE MOV MOV MOV MOV MUL POP POP SUB SBB MOV MOV DIV PUSH MOV MOV DIV OR MOV MOV POP MOV MUL SUB MOV MOV DIV OR MOV MOV RET ENDP DS AX. activar sonido .15 10h AL. . 30 minutos exactos CL.AL minutosL.pagina 10h .255 ."00" cuarto? AX.color CX.AL AL.pagina clr_fondo? modo_video.pagina AH.AL fin_print BX AH.WORD PTR segundosH SI.3 10h DL AH.pagina 10h BX.DX AX. -----------.2 . aislar bit más significativo AL. que afectan al fragmento de . siguiente carácter .AX SI. alarma desactivada SI.8 CMPSB . .19663 divi48x15 DI SI AX.8 bios_scr_proc .8 .AL segundosL.7 .1 . ubicar cursor cadena a imprimir imprimir reloj recuperar posición del cursor y página activa .60 CL AX AH. -----------.0 CL. ¿modo de texto monocromo? get_fondo .3600 SI SI DI SI. acabar buffer operacion. BX.0 .128 . pasar binario a BCD . pues no puntero_notas.musica_alarma-2 ."00" cinco_min? AX. activar música dejar_c_x: SUB MOV CMP JBE CMP JE MOV LEA fondo_clr_ar: MOV MOV INC LOOP RET get_fondo: MOV CALL RET gestiona_fondo ENDP BL. ¿avisar a los cuartos? fin_avisando . DISI = tics/18.hora_alarma CX. de visualización activa. coordenada X real AL. -----------.modo_video clr_fondo? BH.9 BH.3600 divi48x15 AX. -----------.musica_medias-2 .pagina BL. AX = SI = horas .AL AX SHORT $+2 SHORT $+2 42h. a la memoria de vídeo. luego tocar esta nota musica_sonando. puntero a la siguiente nota BX BX puntero_notas. INT’s 8 que dura esa nota BH. sí: preservar área pantalla AL. restaurar posición del cursor print_reloj . función de impresión . contador de hora del BIOS . modos gráficos y en cualquier tarjeta (incluído SVGA). pantalla preservado antes de imprimir el reloj. en efecto SI."00" AL.10 CL AX. actualizarlo BX. AL = minutos . .tipo_aviso SI.3 BH. .’5’ ."51" . en efecto minutosL.BH . sí lo es fin_avisando CL.hora_actual bios_print DX BH. el resto de BH es la duración BL.Imprimir reloj en pantalla print_reloj PROC MOV MOV INT PUSH MOV MOV MOV MOV INT LEA CALL POP MOV MOV INT RET ENDP AH. hora en punto CL.c_x BL. canal #2 del 8253 programado bios_print PROC MOV INC AND JZ PUSH MOV MOV MOV MOV INT CALL POP JMP RET ENDP AL.2 BH.AH SHORT $+2 SHORT $+2 42h."00" fin_avisos AX. . sería más rápido acceder .4 .BH .SI CL. . coordenadas del reloj . DXAX = horas*3600 .AL .DS:[46Eh] DS AX.DS:[46Ch] DI.AX DS."54" cinco_min? DI. DXDISI = DISI * 1080 . preparar canal 2 obtiene_hora .SI CL.1080 mult32x16 AX.BH BL. DISI = segundos+minutos*60 . preservar zona de la pantalla . pero así también funciona en los .10 CL AX. 15 ó 45 minutos exactos CL. modo de vídeo AL y página BH ¿ha cambiado modo de vídeo? en efecto ¿ha cambiado la página? así es no ha cambiado nada actualizar nuevos parámetros coordenada X teórica ¿es la 72? no: se deja como tal sí: ajustar posición lo más . pasar BCD a ASCII . modo gráfico: no preservar."03" .AL horasL.1 10h cursor_derecha BX bios_print . DX = coordenadas actuales . número de caracteres .puntero_notas .60 CL SI.musica_horas-2 . gestiona_fondo PROC MOV INT CMP JNE CMP JNE RET clr_fondo?: MOV MOV MOV CMP JNE MOV fin_print: bios_print AH. pasar BCD a ASCII . cubrir con espacios en blanco BYTE PTR DS:[BX].[BX] . bit de separación entre notas BH. pasar a BCD no empaquetado .AL SHORT $+2 SHORT $+2 AL. avanzar cursor . ¿15 ó 45 minutos exactos? cuar_quiza? SI.musica_5min-2 ."00" minutosH. .’ ’ BYTE PTR DS:[BX+8].3 SHORT $+2 SHORT $+2 61h.Imprimir en color usando BIOS. actualizar posición cursor . La cadena ASCIIZ se entrega en DS:BX. -----------.2 DL."00" media? AX.restaurar .182 43h.1 . byte 0 -> fin de cadena . . . ¿avisar cada 5 minutos? fin_avisos . a la derecha posible c_xx.’0’ no_cero_izda AL. ¿modo de texto de color? get_fondo .BL .61h AL.SI CL.AH AX.

DH. buffer y si es 9 se hará la operación inversa." " . .1 divi48_15_cmp AX.********************************************************* . pasar a minúsculas . obtener entrada Multiplex . 8 caracteres . . otro_pmt .********************************************************* . . -----------. AL. . DI.1 10h operacion. 8 ->preservar.AL [SI+8]. ."no" pmt_off? visibilidad. ¿parámetro ON? mult32x16 .8 CX AH.nocabe_txt print fin_noresid multiplex_id.’x’ pmt_X CL.* * . . preservar resultado parcial . apuntar a zona de parámetros saltar delimitadores quedan más parámetros no más parámetros parámetro precedido por ’/’ código de «error» para ayuda «error» de solicitud de ayuda .4C00h 21h . CX . . activar_ints . WORD PTR [BX].1 BX.1 divi48_resto BX. . AX (parte alta) * SI --> DXAX parte baja del resultado parte media del resultado acumular resultado intermedio arrastrar posible acarreo CALL JMP CMP JNE LEA CALL JMP CALL JNC LEA CALL JMP MOV LEA CALL CALL CMP JNE MOV CALL JNC MOV CALL JC STC MOV MOV CALL CALL CALL JMP STC MOV CALL CALL CALL CALL MOV ADD MOV INT MOV INT ENDP error_version fin_noresid param_u. . no_ml param_ml. params_ok . error de versión incompatible . no quedan entradas . bios_scr_proc PROC MOV MOV INT PUSH MOV MOV MOV MOV INT LEA MOV PUSH MOV MOV MOV MOV MOV INT CMP JNE MOV MOV CALL INC POP LOOP POP MOV MOV INT RET ENDP AH. .’c’ DH. DI. reubicar_prog .128 .[BX] .0 AX main . guardar carácter leído y su atributo siguiente posición próximo carácter handle_ok: proximo_car: instalar_umb: opcont: .’y’ pmt_Y CL. . fin_noresid .1 visible. DX. y preservarla para el final . SI.1 .1 param_onoff.parrafos_resid UPPER_alloc instalar_ml . residente? . fin_noresid ES. código de error . mal_proc_pmt .6 . SI. ¿parámetro OFF? letra del parámetro ¿fin de mandatos? falta parámetro código de «error» para ayuda poner en minúsculas $ .[SI+8] AL. no_residente .1 DX.DI SI SI DI DI.3100h 21h . AX. fin del área residente fin_residente-ini_residente (bytes_resid+15)/16 parrafos_resid EQU ."lm" . lo piden.Procesar fragmento de pantalla empleado por el reloj: .’a’ pmt_no_A pmt_A . No se comprueba si .c_y BH. ¡no rotar el resto al final! . pasar a minúsculas .0 .rclock_txt . AL. convencional inicializar identificación reubicar programa a ES:DI interceptar vectores liberar espacio de entorno tamaño zona residente. mover cursor . inicializa_id .128 . print obtener_param .7 . . y reponer posición del cursor instalar_ml: bios_scr_proc . AL. . coordenadas del reloj . print fin_noresid AX. no lo piden . parámetros procesados param_u. multiplicador en SI .AX SI.1 . no residente: ¿desinstalar? .BX CX. instalable .BX CX BX . DH.* * . .2 10h instalable: instalar: . .pagina 10h DX AH.0 instalar_ml AX." " WORD PTR [BX]. DX.instalado_txt print preservar_ints param_ml.’?’ DH. fin_noresid inic_XMS . AL. ¿parámetro OFx? . -----------. desinst .AH DX.1 instalar DX. . . AL. . recuperar coordenadas . .tsr_seg rclock_off AH. print_err .0 mal_proc_pmt BYTE PTR [BX+2].parrafos_resid .’u’ pmt_U SI.* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION * .parrafos_resid UMB_alloc instalar_umb AX.13 . bits generando resultado de 48 bits: DISI * AX = DXDISI fin_noresid: mult32x16 PROC PUSH XCHG MUL PUSH PUSH MOV MUL POP POP ADD ADC POP RET ENDP AX SI.BX divi48_nosub BX. ¡serán despistados! .0 .60h .’h’ mal_proc_pmt AL.AH cursor_derecha SI CX proximo_car DX BH.0 mal_proc_pmt . obtener posición del cursor ."fo" DH.8 opcont [SI].pagina BL. rotar 49 veces . .pagina AH. AL. .2 DL. DX.AX DX.2 .des_no_ok_txt . si «operacion» es 8 se copiará de la pantalla a un .1 BX otro_pmt_mas mens_ok: no_residente: . acabar caracteres . .2 otro_pmt_mas AX get_num . -----------. tomar nota de vectores ¿se indicó parámetro /ML? en efecto párrafos de memoria precisos pedir memoria superior XMS hay la suficiente pedir memoria superior DOS 5 no hay la suficiente indicar que usa memoria DOS segmento del bloque UMB ES:0 zona a donde reubicar inicializar identificación reubicar el programa a ES:DI interceptar vectores programa instalado «arriba» indicar que usa memoria DOS instalación mem. .180 EL UNIVERSO DIGITAL DEL IBM PC. bits sin desbordamientos y con cociente de 48 bits. param_u. free_environ . mensaje de instalación . .1 CX. pmt_nobarrado mal_proc_pmt .1 BX. . No se modifican otros registros.’ ’ BYTE PTR [BX+2]. CL. saltar_esp . . nombre del programa analizar posibles parámetros son correctos no: informar del error/ayuda considerar presencia de XMS ¿programa ya residente? todavía no sí: ¿solicitan desinstalarlo? así es parámetros en copia residente parámetro /A=hh:mm:ss|ON|OFF ¿parámetro de dos caracteres? mayusculizar ¿parámetro /ML? en efecto main params_ok: obtener valor del parámetro CL tipo de parámetro código de error parámetro incorrecto desinst: código de error error en parámetro(s) desinstalarlo: ha sido posible es imposible ¿reside una versión distinta? no: se admite instalación . el divisor es cero o excede los 15 bits. inicializa_id .pagina 10h SI. ES.operacion BH.[SI] CX.des_ok_txt mens_ok . entrada multiplex para RCLOCK . reubicar_prog . mal_proc_pmt AL.imp_desins_txt print fin_noresid mx_get_handle handle_ok DX.ES:multiplex_id mx_unload . pmt_C . DXDISI/AX --> cociente en DXDISI y resto en AX. AX. . .81h .3 BH.Rutina para dividir números de 48 por números de 15 . BX. 9 ->restaurar . desde PSP:60h bytes (6 párrafos) terminar residente terminar no residente .c_xx DH. ***************************** * * * I N S T A L A C I O N * * * ***************************** PROC LEA CALL CALL JNC CALL JMP CALL CALL JC CMP JE CALL JMP MOV CALL MOV CALL LEA JNC LEA CALL JMP CMP JE DX. activar_ints .0 visible.’f’ mal_proc_pmt visibilidad. DX. preparar BL por si AH=9 preparar AL por si AH=9 preparar CX por si AH=9 leer/escribir carácter ¿se trataba de leer? no sí.3 otro_pmt_mas BX AL.’?’ DH.’ ’ .0 param_onoff. dirección del buffer . -----------.AX .Admitir posibles parámetros en la línea de comandos obtener_param otro_pmt_mas: PROC MOV CALL JNC JMP otro_pmt: CMP JE CMP MOV JNE JMP pmt_nobarrado: OR CMP JNE MOV MOV MOV ADD JMP pmt_off?: CMP MOV JNE OR CMP JNE MOV MOV MOV ADD JMP pmt_barrado: INC MOV CMP MOV JE CMP MOV JE OR CMP JE CMP JNE JMP pmt_no_A: CMP JE MOV OR CMP JNE MOV ADD JMP no_ml: PUSH CALL POP MOV JC CMP JE CMP JE CMP JE CMP MOV JE mal_proc_pmt: STC RET fin_proc_pmt: CLC RET pmt_U: MOV INC JMP BX. AX (parte baja) * SI --> DXAX .’t’ pmt_T CL. divi48x15 PROC PUSH PUSH XOR MOV divi48_15_cmp: CMP JA SUB STC divi48_nosub: RCL RCL RCL PUSHF CMP JE POPF RCL PUSHF divi48_resto: POPF LOOP MOV POP POP RET divi48x15 ENDP fin_residente bytes_resid EQU EQU BX CX BX. adaptar_param .49 AX. AT Y PS/2 .[BX] .AX SI DX AX AX.Rutina para multiplicar números de 32 por números de 16 .’/’ pmt_barrado .1 DI. fin_proc_pmt .restaurar CX.2 otro_pmt_mas WORD PTR [BX].

AH BX get_num mal_pmtA AX.3 mal_proc_pmt AL.’=’ ok_num . delimitador: fin de número CL. AX*10 no se desbordará AX. recuperar código y parámetro .1 c_y. sí: obtener su dirección .AL AH. admitir hasta 132 columnas DH."fo" . espacio en blanco error_version . pasar a minúsculas WORD PTR [BX].1 otro_pmt_mas DH.4300h 2Fh AL.[BX] obtener_num err_sintax BX . revisión err_sintax: delimit_ok: error_version .’.’0’ .6 .1 . fin número (otro dato) fin_num BX AL.mal_ver_txt2 print ES fin_param: . ¿parámetro OFx? DH. delimitador: fin de número CL. «sacar» BX de la pila AX.10 DX .’=’ delimit_ok AL. 9 AX .’f’ .Saltar espacios.PROGRAMAS RESIDENTES 181 MOV MOV CMP MOV JA CMP JE JMP pmt_X: MOV MOV CMP MOV JA JMP pmt_Y: MOV MOV CMP MOV JA JMP pmt_C: MOV MOV CMP MOV JA JMP pmt_A: PUSH CALL JNC POP ADD OR CMP JNE MOV MOV ADD JMP pmt_A_off?: CMP MOV JNE OR CMP JNE MOV MOV ADD JMP bien_pmt_A: MOV ADD CMP JA MOV DIV ADD CMP JNE MOV no_cero_izda2: MOV MOV DEC CALL JC CMP JA MOV DIV ADD MOV MOV DEC CALL JC CMP JA MOV DIV ADD MOV MOV MOV JMP mal_pmtA: MOV mal_proc_pm: JMP obtener_param ENDP pmt_T: param_t.6 mal_proc_pmt otro_pmt_mas BX get_num bien_pmt_A BX BX.255 SCASB SCASB DL.3 otro_pmt_mas param_a.[BX] BX AL.32 fin_num .[BX] BX AL. el . a la izda sólo permitir ceros DX .fin_err_txt print .2 21h DL. reequilibrar pila BX. -----------. número mayor de 65535 . -----------. error: DH código de error no_ayuda: .32 saltar_esp AL.2 21h DX.AH BX.’:’ delimit_ok .4 mal_proc_pmt otro_pmt_mas param_y.SI AX. tabla de mensajes de error .124 .’:’ . pasar BCD a ASCII BYTE PTR alarm_s.. pasar ASCII a binario CH.0Dh fin_num AL. pobre usuario DI.59 mal_pmtA CL.[BX] CL.Considerar presencia de controlador XMS . condición de Ok.0 . dirección del texto . fin de zona de parámetros . puntero (BX) apuntará al final del número y CF=1 si el .’0’ mal_num CL.AH AL.’00’ . AX = AX elevado a la (N+1) DX otro_car AX .Obtener número chequeando delimitadores /= y /: get_num: INC MOV INC CMP JE CMP JE STC RET MOV CALL JC INC RET BX AL. pasar binario a BCD AX.1 mal_proc_pmt fin_num: otro_car: no_millar: multiplica: potencia: mal_num_pop: mal_num: ok_num: obtener_num CMP JE CMP JE CMP JE INC MOV JMP MOV DEC XOR MOV DEC MOV CMP JE CMP JE CMP JNE CMP JE JMP CMP JB CMP JA SUB MOV PUSH AND JNZ AND JNZ PUSH MUL POP JC ADD JC POP CMP JNE MOV JMP MOV PUSH MUL POP JMP POP MOV STC RET MOV MOV CLC RET ENDP AL.ES:[DI+2] AH. pasar binario a BCD AX.0 .. y hasta 60 líneas AX. resultado .ES:[DI] AH.AL BYTE PTR alarm_m+1. CL=parámetro en errores 1.CL DX. código de error mal_proc_pm BYTE PTR [BX+2].tabla_err AX AL.AL BYTE PTR alarm_h+1.5 DH. separador millar descolocado CL.DX .10 CL . sintaxis incorrecta AL. buscando un parámetro saltar_esp: MOV INC CMP JE CMP JE CMP JE DEC CLC RET STC RET AL." " ."no" ..1 SP.’:’ CL.1 color.DH AL.AL BYTE PTR alarm_s+1. saltar los puntos de millar AX.1 c_x.BX SI DX.’/’ .Ya está instalada otra versión distinta del programa . chequear presencia XMS .9 . al final.1 param_a_onoff. fin número fin_num AL.Imprimir errores en los parámetros print_err PROC CMP JNE LEA JMP MOV MOV LEA CALL LEA PUSH MOV SHL XOR ADD MOV CALL POP CMP JBE MOV MOV INT LEA CALL RET ENDP DH.’ AH.2 21h DL.AL AX. . la izda .’00’ . fin número (otro parámetro) fin_num AL.3 mal_proc_pmt otro_pmt_mas param_x. no instalado .[BX] print AX AH.AL AX.2 WORD PTR [BX]. AX = 10 elevado a la N AX. evitar cero a la izda.’ ’ ."00" . número correcto . -----------.10 CL .2 21h DX.AX .. fin número inic_XMS PROC MOV INT CMP JNE PUSH MOV INT AX.ini_err_txt print BX.9 saltar_esp AL.1000 otro_car mal_num . pasar BCD a ASCII AL.AH alarm_enable. hay parámetro .Extraer nº de 16 bits y depositarlo en AX. -----------.0 . AX = AL .mal_ver_txt1 print DI. número era incorrecto.’ no_millar .ayuda_txt pr_ret AH.SI . como próximo dígito<>0 a otro_car .128 no_ayuda DX.’ ’ .AX DX.’. puntero al primer carácter . AX = 10 elevado a la 0 = 1 BX .2 . fin zona parámetros y número .AX multiplica CL. tras completar 5º dígito CX DX mal_num_pop DX. condición de error BX.’9’ mal_num CL. error 0 ó 1 .1 BX.1 no_pr_pmt DL.0Dh fin_param BX no_pr_pmt: pr_ret: print_err .1 AH.80h XMS_ausente ES AX. pasar BCD a ASCII BYTE PTR alarm_m. ¿parámetro ON? pmt_A_off? alarm_enable.. no hay parámetro PROC PUSH LEA CALL LES MOV MOV CLD REPNE REPNE MOV MOV INT MOV MOV INT MOV MOV INT LEA CALL POP RET ENDP ES DX. ¿parámetro OFF? mal_proc_pm alarm_enable.2 otro_pmt_mas WORD PTR [BX]. próximo carácter a procesar CL. imprimir letra del parámetro .’0’ no_cero_izda2 AL. -----------.AL . tabuladores.’:’ ok_num .59 mal_pmtA CL.AH BX get_num mal_pmtA AX. CX = 0 .10000 potencia .0 param_a_onoff. obtener_num PROC CMP JE CMP JE AL. pasar binario a BCD AX. hora BYTE PTR alarm_h.1 BX.CL mal_num_pop . número de versión .59 DH.255 DH. -----------.1 tipo_aviso. . carácter tabulador . no manchar DX al multiplicar DI .[BX] obtener_num SI. AL = AL * 2 .4310h 2Fh .tsr_dir AL.23 mal_pmtA CL..AL AX. pasar a minúsculas BYTE PTR [BX+2].DX AX.10 CL .5 mal_proc_pmt otro_pmt_mas param_c. DX = DX + digito (CX) * 10 ^ N (AX) mal_num_pop AX AX.

High Memory best fit .refresco CH.0 AL. AX=0 -> no residente .5800h 21h alloc_strat. param_c.1 param_y? AL. preservar estado UMB . identificación de la copia residente. AX=1 -> sí: otra vers. param_x.0 DX CX BX .0 CL.0 .61h . redondear hacia arriba .1 ES xms_ins. adaptar visibilidad del reloj .Liberar espacio de entorno free_environ PROC PUSH MOV MOV INT POP RET ENDP ES ES.0 . ES:color. free_environ resid_ok: residente? .1 param_t? AL.DS:[6Ch] AX.AL .visibilidad ES:visibilidad.Adaptar parámetros de un RCLOCK ya instalado. programar nueva alarma no_umb_disp: XMS_fallo: parámetro /A=ON o /A=OFF: . Sólo se adaptan los indicados. . espera_reloj ..5801h BX.1492h ES.40h DS. eliminarlo de la pantalla ES:musica_sonando.0 espera_reloj . Si no hay bastante CF=1.1 param_adapted AL. versión distinta (AX=1). adaptar_param PROC LEA CALL MOV CMP JNE MOV MOV param_a?: CMP JNE LEA MOV MOV CLD REP param_aonoff?: CMP JNE MOV MOV param_t?: CMP JNE MOV MOV param_x?: CMP JNE MOV MOV CALL MOV MOV MOV MOV param_y?: CMP JNE MOV MOV CALL MOV MOV MOV param_c?: CMP JNE MOV MOV param_adapted: RET adaptar_param ENDP DX.ES AX resid_ok ES ES DI. . . param_y.tsr_seg param_onoff. identificación del programa espera_reloj .’:’ CL.0 .DI AX.. solicitado (AX párrafos).0 CX DI AH. CX vectores interceptados .ES:visibilidad .DI AL. tamaño autor+programa+versión . -----------.49h 21h ES .BH 21h .5802h 21h umb_state.BX XMS_seg.ya_install_txt print ES. ES:visibilidad. necesario DOS 5.autor_nom_ver SI. ES:tipo_aviso.alloc_strat 21h AX. solicitar memoria superior .alarm_enable DI. ¿ha ido todo bien? segmento UMB/código de error fallo ok . preservar párrafos.AL .AH .AL param_a..Comprobar si el programa ya reside en memoria.SI CX. -----------.DS:[2Ch] AH.ES xms_ins. dirección del entorno .1 no_umb_disp DX.[DI-1] CH.48h 21h AX AX.AX AH. preservar estrategia .0 resid_ok AX.1 param_x? AL. -----------.10h gestor_XMS AX. testeando la variable . ES:visibilidad. .41h 21h BX AH. . en caso contrario devuelve el segmento en AX.AX DI. ES:DI protocolo de búsqueda buscar si está en memoria anotar dirección del programa por si instalada otra versión .BX XMS_fallo DX CX BX AX.color .1 ES DI SI CX .ES:visibilidad .182 EL UNIVERSO DIGITAL DEL IBM PC. programa no reside aún (AX=0) o reside pero en otra .Eliminar el RCLOCK de la pantalla rclock_off PROC MOV CALL MOV IN AND JMP JMP OUT RET ES:visibilidad. .ES DI. restaurar estrategia .35h AL. A la . obtener vector de INT xx . número de párrafos .Reservar memoria superior.[DI] 21h DI CX [DI+1].0 mínimo .2 AX. .SI CX.1492h ES. Si CF=1.DI AL. «tsr_off» inicializadas apuntando a la cadena de . del tamaño .AH . espera_reloj PROC PUSH PUSH PUSH MOV MOV ADD MOV MOV STI MOV CMP JE LOOP POP POP POP RET ENDP DS AX CX CL. en pantalla si ésta -la impresión.alarm_enable .AL param_t. CF=0 si programa ya reside.DS:[6Ch] espera_tic espera_tics CX AX DS .5801h BX.umb_state BH. UPPER_alloc PROC PUSH MOV INT CMP POP JAE STC JMP PUSH MOV INT MOV MOV INT MOV MOV MOV INT MOV MOV INT POP MOV INT PUSHF PUSH MOV MOV INT MOV MOV XOR INT AX AH. CF=1. .5 AX UPPER_existe UPPER_fin AX AX. con DOS 5. tamaño autor+programa . -----------.está habilitada.DI AX.BX [DI+3].DI tsr_seg.1 21h AX.0 CX.9 . asignar memoria . repetir con los restantes . .AX AX. ES:c_xx.AX DI. actualizar estado alarma UMB_alloc parámetro /T: actualizar byte parámetro /X: eliminar reloj de pantalla esperar a que se vaya actualizar coordenada X restaurar visibilidad parámetro /Y: eliminar reloj de pantalla esperar a que se vaya actualizar coordenada Y restaurar visibilidad parámetro /C: actualizar byte de atributos UPPER_existe: MOVSB param_a_onoff.autor_nom_ver SI.c_y ES:c_y. ES:visibilidad. -----------. liberar espacio de entorno . UMB_alloc PROC PUSH PUSH PUSH CMP JNE MOV MOV CALL CMP MOV JNE POP POP POP CLC RET MOV POP POP POP STC RET ENDP BX CX DX xms_ins. -----------. CF=1. parámetros ON u OFF: . que indica si se han especificado. parámetro /A=hh:mm:ss .AL . .SI CX. no hay controlador XMS . AT Y PS/2 XMS_ausente: inic_XMS MOV MOV MOV POP RET MOV RET ENDP XMS_off. ES:alarm_enable. el .0. .AX AX. aparezca en pantalla .Reservar bloque de memoria superior del nº párrafos AX.AL . .Preservar vectores de interrupción previos preservar_INTs PROC PUSH PUSH LEA MOV MOV otro_vector: PUSH PUSH MOV MOV INT POP POP MOV MOV ADD LOOP POP POP RET preservar_INTs ENDP ES DI DI. ES:DI protocolo de búsqueda buscar si está en memoria anotar la dirección programa por si estaba instalado . .AL .5803h BL. devolviendo en AX el segmento donde está.ES AX. salida.5803h BX. -----------. .párrafos requeridos .255 SCASB DI. con «tsr_seg» y .Esperar una INT 8 que refresque la impresión del reloj .1992h mx_find_tsr tsr_off.1 AX. nº tics suficientes para que . AH.0FCh SHORT $+2 SHORT $+2 61h. residente? PROC PUSH PUSH PUSH PUSH PUSH LEA MOV MOV MOV CLD REPNE SUB MOV MOV MOV MOV CALL MOV MOV POP JNC POP PUSH LEA MOV MOV MOV REPNE REPNE SUB MOV MOV MOV MOV CALL MOV MOV MOV JC MOV STC POP POP POP POP RET ENDP espera_tics: espera_tic: CX SI DI ES AX DI. y preservarla rclock_off ENDP .5 otro_vector DI ES . AH.1992h mx_find_tsr tsr_off. CF=1 si no . parar posible sonido AL.c_x ES:c_x. guardado el resultado .tabla_vectores CL. está instalado el gestor XMS (AX=0) o hay un error (AL . restaurar estado cadena UMB . CF=0 -> programa ya residente .255 SCASB SCASB DI.DI tsr_seg. anotar donde apunta . conectar cadena UMB’s .AL AX. -----------..tipo_aviso .AH ES:visibilidad. devuelve el código de error del controlador XMS).30h 21h AL.1 param_a? AL. .1 param_aonoff? SI.1 param_c? AL. espera_reloj .

PROGRAMAS RESIDENTES

183

UPPER_fin: UPPER_alloc

POP POPF JC PUSH DEC MOV INC MOV MOV PUSH MOV MOV MOV DEC MOV MOV MOV MOV CLD REP POP POP CLC RET ENDP

AX UPPER_fin ; hubo fallo DS AX DS,AX AX WORD PTR DS:[1],AX ; manipular PID WORD PTR DS:[16],20CDh ; simular PSP ES CX,DS ES,CX CX,CS CX DS,CX CX,8 SI,CX DI,CX MOVSB ES DS ; copiar nombre de programa mx_tsr_found:

mx_skip_hndl:

; ------------ Inicializar área «program_id» del programa residente. ; A la entrada, ES:DI = seg:off a donde será reubicado ; y CF=1 si se utiliza memoria superior XMS. mx_find_tsr inicializa_id PROC PUSHF MOV MOV MOV MOV MOV SHR ADD MOV POPF JNC DEC OR RET ENDP segmento_real,ES ; anotar segmento del bloque offset_real,DI ; ídem con el offset longitud_total,parrafos_resid CL,4 AX,DI AX,CL longitud_total,AX ; consumirá desde offset=0 AL,1 ; CF=0: usar memoria UMB XMS info_ok AL ; usar memoria convencional info_extra,AL

PUSH MOV PUSH INT POP CMP JNE CLD PUSH REP POP JE POP POP POP POP POP POP INC JNZ STC RET ADD POP POP POP POP CLC RET ENDP

DI AL,0 CX 2Fh CX AL,0FFh mx_skip_hndl DI CMPSB DI mx_tsr_found DI ES DS SI CX AX AH mx_rep_find SP,4 DS SI CX AX

; no hay TSR ahí ; comparar identificación ; programa buscado hallado

; «sacar» ES y DI de la pila

; ------------ 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 CALL JNC POP RET mx_ul_able: XOR XCHG MOV MOV mx_ul_pasada: PUSH LEA MOV MOV mx_ul_masvect: POP PUSH DEC PUSH mx_ul_2f: MOV JNZ CMP JNE MOV LEA mx_ul_busca2f: CMP JE ADD JMP mx_ul_noult: CMP JNE ADD JMP mx_ul_pasok: PUSH PUSH MOV SHL SHL DEC MOV MOV POP PUSH MOV INT POP MOV SHR MOV ADD MOV mx_ul_masmx: CALL JNC JMP mx_ul_tsrcv: PUSH PUSH MOV MOV MOV mx_ul_buscav: CMP JE ADD LOOP ADD JMP mx_ul_usavect: POP POP CMP JB ADD CMP JA PUSH XOR XCHG CMP POP JNE POP POP POP PUSH PUSH PUSH DEC JNZ POP PUSH PUSH MOV MOV CLI ES mx_ul_tsrcv? mx_ul_able ES AL,AL AH,AL BP,AX ; BP=entrada Multiplex del TSR CX,2 CX ; siguiente pasada SI,tabla_vectores CL,ES:[SI-1] CH,0 ; CX = nº vectores AX AX ; pasada en curso AL CX AL,ES:[SI] ; vector en curso mx_ul_pasok CX,1 ; ¿último vector? mx_ul_noult AL,2Fh SI,tabla_vectores ES:[SI],AL ; ¿INT 2Fh? mx_ul_pasok SI,5 mx_ul_busca2f AL,2Fh ; ¿restaurar INT 2Fh? mx_ul_pasok SI,5 mx_ul_2f ES AX AH,0 AX,1 AX,1 AX CS:mx_ul_tsroff,AX CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores AX AX AH,35h 21h ; vector en ES:BX AX CL,4 BX,CL DX,ES DX,BX ; INT xx en DX (aprox.) AH,0C0h mx_ul_tsrcv? mx_ul_tsrcv mx_ul_otro ES:[DI-16] ; ...TSR del convenio en ES:DI ES:[DI-12] DI,ES:[DI-8] ; offset a la tabla de vectores CL,ES:[DI-1] CH,0 ; número de vectores en CX AL,ES:[DI] mx_ul_usavect ; este TSR usa vector analizado DI,5 mx_ul_buscav SP,4 ; no lo usa mx_ul_otro CX ; tamaño del TSR BX ; segmento del TSR DX,BX mx_ul_otro ; la INT xx no le apunta BX,CX DX,BX mx_ul_otro ; la INT xx le apunta AX AL,AL AH,AL AX,BP ; ¿es el propio TSR? AX mx_ul_chain ; no ES ; sí: ¡posible reponer vector! CX BX BX CX ES BX mx_ul_norest ; no es la segunda pasada ES ; segunda pasada... ES DS BX,CS:mx_ul_tsroff ; restaurar INT’s DS,CS:mx_ul_tsrseg

info_ok: inicializa_id

; ------------ Reubicar programa residente a su dirección definitiva. reubicar_prog PROC PUSH LEA MOV CLD ADD ADD SUB REP POP RET ENDP DI SI,ini_residente CX,bytes_resid SI,2 DI,2 CX,2 MOVSB DI ; no copiar primera palabra ; respetar primera palabra

reubicar_prog

; ------------ Desviar vectores de interrupción a las nuevas rutinas. ; Se tendrá en cuenta que está ensambladas para correr en ; un offset inicial (100h) y que el offset real en que ; han sido instaladas está en DI. Por ello, CS ha de ; desplazarse (100h-DI)/16 unidades atrás (DI se supone ; múltiplo de 16). El segmento inicial es ES. activar_INTs PROC PUSH PUSH MOV SUB MOV SHR MOV SUB MOV LEA MOV ADD MOV MOV MOV INT ADD LOOP POP POP RET ENDP CX DS AX,100h AX,DI CL,4 AX,CL CX,ES CX,AX DS,CX SI,offsets_ints CX,CS:[SI] SI,2 AL,CS:[SI] DX,CS:[SI+1] AH,25h 21h SI,3 desvia_otro DS CX

; preservar DS para el retorno ; AX = 100h-DI ; AX = (100h-DI)/16

; CX vectores a desviar ; número del vector en curso ; obtener offset ; desviar INT xx a DS:DX

desvia_otro:

activar_INTs

; ------------ Buscar entrada no usada en la interrupción Multiplex. ; A la salida, CF=1 si no hay hueco (ya hay 64 programas ; residentes instalados con esta técnica). Si CF=0, se ; devuelve en AH un valor de entrada libre en la INT 2Fh. mx_get_handle PROC MOV mx_busca_hndl: PUSH MOV INT CMP POP JNE INC JNZ mx_no_hueco: STC RET mx_si_hueco: CLC RET mx_get_handle ENDP AH,0C0h AX AL,0 2Fh AL,0FFh AX mx_si_hueco AH mx_busca_hndl

; ------------ Buscar un TSR por la interrupción Multiplex. A la ; entrada, DS:SI cadena de identificación del programa ; (CX bytes) y ES:DI protocolo de búsqueda (normalmente ; 1492h:1992h). A la salida, si el TSR ya está instalado, ; CF=0 y ES:DI apunta a la cadena de identificación del ; mismo. Si no, CF=1 y ningún registro alterado. mx_find_tsr mx_rep_find: PROC MOV PUSH PUSH PUSH PUSH PUSH AH,0C0h AX CX SI DS ES

184

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

MOV MOV MOV MOV STI POP mx_ul_norest: POP POP ADD DEC JZ JMP mx_ul_chain: MOV MOV MOV MOV SHR MOV ADD MOV mx_ul_otro: INC JZ JMP mx_ul_exitnok: ADD POP STC RET mx_unloadable: POP DEC JZ JMP mx_ul_exitok: TEST MOV JZ CMP JNE MOV MOV CALL POP CLC RET mx_ul_freeml: MOV INT POP CLC RET mx_ul_tsrcv?: PUSH PUSH PUSH MOV MOV MOV INT CMP JNE CMP JNE CMP JNE ADD POP RET mx_ul_ncvexit: POP POP POP STC RET mx_ul_tsroff DW mx_ul_tsrseg DW mx_unload ENDP

CX,ES:[SI+1] [BX+1],CX CX,ES:[SI+3] [BX+3],CX DS ES CX SI,5 ; siguiente vector CX mx_unloadable ; no más, ¡desinstal-ar/ado! mx_ul_masvect CS:mx_ul_tsroff,DI ; ES:DI almacena la dirección CS:mx_ul_tsrseg,ES ; de la variable vector DX,ES:[DI+1] CL,4 DX,CL CX,ES:[DI+3] DX,CX ; INT xx en DX (aprox.) AH,0BFh AH ; a por otro TSR mx_ul_exitnok ; ¡se acabaron! mx_ul_masmx SP,6 ; equilibrar pila ES ; CX CX mx_ul_exitok ; mx_ul_pasada ; ES:info_extra,111b ES,ES:segmento_real mx_ul_freeml xms_ins,1 mx_ul_freeml ; DX,ES AH,11h gestor_XMS ; ES AH,49h 21h ES imposible desinstalar desinstalado 1ª pasada exitosa: por la 2ª ; ¿tipo de instalación? ; segmento real del bloque ; cargado en RAM convencional no hay controlador XMS (¿?) liberar memoria superior mal_ver_txt1 mal_ver_txt2 ; liberar bloque de memoria ES: des_ok_txt des_no_ok_txt AX ; ¿es TSR del convenio?... ES DI DI,1492h ES,DI DI,1992h 2Fh AX,0FFFFh mx_ul_ncvexit WORD PTR ES:[DI-4],"#*" mx_ul_ncvexit WORD PTR ES:[DI-2],"*#" mx_ul_ncvexit SP,4 ; CF=0 AX DI ES AX 0 0 ; ...no es TSR del convenio ; CF=1 ini_err_txt err0_txt err1_txt err2_txt err3_txt err4_txt err5_txt err6_txt err7_txt fin_err_txt

DB DW DB DW DB DW param_ml param_u param_onoff param_a param_a_onoff param_t param_x param_y param_c rclock_txt instalado_txt DB DB DB DB DB DB DB DB DB DB DB

9 ges_int09 10h ges_int10 2Fh ges_int2F 0 0 0 0 0 0 0 0 0 ; ; ; ; ; ; ; ; ; a a a a a a a a a 1 1 1 1 1 1 1 1 1 si si si si si si si si si se se se se se se se se se indicó indicó indicó indicó indicó indicó indicó indicó indicó /ML /U ON u OFF /A /A=ON o /A=OFF /T /X /Y /C

13,10,"

RCLOCK v2.3$"

" instalado.",13,10,"$" " ya instalado.",13,10 " - Parámetros indicados actualizados." 13,10,"$" err0_txt, err1_txt, err2_txt, err3_txt err4_txt,err5_txt, err6_txt, err7_txt 13,10," - Error: $" "sintaxis incorrecta$" "hora de alarma incorrecta$" "parámetro no admitido: /$" "parámetro distinto de 0, 1, 2, 4 ó 5: /$" "parámetro fuera del rango 0..124: /$" "parámetro fuera del rango 0..59: /$" "parámetro fuera del rango 0..255: /$" "necesario numéro en el parámetro /$" 13,10 " Ejecute RCLOCK /? para obtener ayuda." 13,10,7,"$" " - Error: ya está instalada la versión $" " de este programa.",13,10,7,"$" " desinstalado.",13,10,"$" 13,10," - Desinstalación imposible (se ha " "instalado después un programa" 13,10," que no respeta el convenio y tiene " "alguna interrupción común).",13,10,7,"$" 13,10," - Programa aún no instalado: " "imposible desinstalarlo.",13,10,"$" ": Instalación imposible.",13,10 " Ya hay 64 programas residentes con la " "misma técnica.",13,10,"$"

ya_install_txt DB DB DB tabla_err DW DW DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB

imp_desins_txt DB DB nocabe_txt DB DB DB

; ------------ imprimir cadena en DS:DX delimitada por un ’$’ print PROC PUSH MOV INT POP RET ENDP AX AH,9 21h AX

print ; ; ; ; ;

*********************************************** * * * D A T O S N O R E S I D E N T E S * * * *********************************************** DB LABEL DW DW DW DB 0 DWORD 0 0 0 0 ; a 1 si presente controlador XMS ; dirección del controlador XMS

xms_ins gestor_XMS XMS_off XMS_seg alloc_strat umb_state tsr_dir tsr_off tsr_seg offsets_ints

; estrategia asignación (DOS 5) ; estado de bloques UMB (DOS 5) ; dirección de la copia residente

LABEL DWORD DW 0 DW 0 DW DB DW 4 8 ges_int08

ayuda_txt LABEL BYTE DB 13,9,9,"RCLOCK v2.3 - Utilidad de reloj-alarma residente.",13,10 DB " (c) 1992 CiriSOFT, (c) Grupo Universitario de Informática - " DB "Valladolid.",13,10,10 DB " RCLOCK [/A=hh:mm:ss|OFF|ON] [ON|OFF] [/T=] [/X=] [/Y=] [/C=] " DB "[/U] [/ML] [/?|H]",13,10,10 DB " /A Indica una hora de alarma y activa la misma; con /A=ON o " DB "/A=OFF se puede",13,10 DB " controlar a posteriori la habilitación de la alarma. Tras " DB "sonar, quedará",13,10 DB " desactivada (hasta un posterior /A=ON o bien /A=hh:mm:ss). " DB "Se puede can-",13,10 DB " celar siempre el sonido pulsando Ctrl-Alt-R o AltGr-R " DB "durante el mismo.",13,10 DB " ON y OFF Controlan la aparición del reloj en pantalla. " DB "Equivalente a pulsar",13,10 DB " AltGr-R ó Ctrl-Alt-R con el reloj ya instalado y sin " DB "sonido en curso.",13,10 DB " /T Indica el nivel de avisos sonoros del reloj: 0 ninguno; 1 " DB "señal horaria;",13,10 DB " 2, a las medias; 4 a los cuartos y 5 cada cinco minutos. " DB "Cada uno de los",13,10 DB " niveles incluye a su vez a los anteriores. Por defecto, " DB "/T=1.",13,10 DB " /X e /Y Indican las coordenadas de pantalla donde se " DB "imprimirá el reloj; su",13,10 DB " valor varía según el modo de pantalla. Las coordenadas son " DB "siempre refe-",13,10 DB " ridas al modo texto, aunque la pantalla esté en modo " DB "gráfico. Para /X=72",13,10 DB " (valor por defecto) el reloj no se imprimirá realmente en " DB "la columna 72,",13,10 DB " sino lo más a la derecha posible según el modo de vídeo " DB "activo.",13,10 DB " /C Indica los atributos de color en que aparece el reloj." DB 13,10 DB " /U Permite desinstalar el programa de la memoria si ello es " DB "posible.",13,10 DB " /ML Fuerza la instalación en memoria convencional -por defecto " DB "se cargará en",13,10 DB " memoria superior XMS o en su ausencia en la administrada " DB "por el DOS 5.0-",13,10,"$" rclock ENDS END inicio

; número de vectores interceptados ; tabla de offsets de los vectores ; de interrupción interceptados

10.10. - USO SIN LIMITES DE SERVICIOS DEL DOS EN PROGRAMAS RESIDENTES. Como se dijo al principio del capítulo, desde un programa residente no se pueden emplear directamente los servicios del DOS. Si se salta esta norma se pueden crear programas que funcionen bajo determinadas circunstancias, pero nada robustos. Por ejemplo, una utilidad para volcar la pantalla a un fichero en disco al pulsar una cierta combinación de teclas, podría funcionar correctamente si es ejecutada desde la línea de comandos, o desde dentro de un editor de texto. Sin embargo, si es invocada mientras se ejecuta un

PROGRAMAS RESIDENTES

185

comando DIR o mientras el programa principal está accediendo al disco o, simplemente, ejecutando cualquier función del DOS tal como consultar la fecha, nuestra utilidad dejaría de funcionar correctamente. Y el fallo no consiste en que la pantalla no se vuelque en disco, o se vuelque mal: el problema es que el ordenador se cuelga, siendo preciso reinicializarlo. Aunque es fácil y, en ocasiones más cómodo y recomendable acceder directamente a la pantalla y al teclado, el DOS es la herramienta más potente para acceder al disco y su utilidad en este campo es prácticamente insustituíble. Para la BIOS o el hardware no existen los discos virtuales ni las unidades de disco en red; por otra parte, el DOS constituye un soporte básico que permite a los programas ignorar la evolución futura de las unidades de almacenamiento. Por consiguiente, poder utilizar el DOS desde los programas residentes es algo más que interesante. Con este objetivo, la propia Microsoft tuvo que enfrentarse a las limitaciones del sistema para desarrollar el comando PRINT desde la versión 2.0; en la actualidad es casi universalmente conocido lo que hay que hacer para emplear el DOS desde un programa residente, aunque una gran mayoría de los libros aún no expliquen estas técnicas. Algunos de ellos, incluso muestran programas residentes que llaman descaradamente al DOS, sin tomar precauciones de ninguna clase ¡por algo no los he incluido en la bibliografía!. El término no reentrante que se aplica al DOS significa que no puede ser empleado simultáneamente por dos procesos, sin embargo se trata de un código 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 función (y ya había 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 función 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 demás (que además son la mayoría y las más interesantes) por desgracia no lo es. Para solucionar este problema hay dos métodos: interrumpir al DOS sólo cuando no esté ejecutando alguna función; 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 después de haber realizado su tarea. En este libro trataremos especialmente el primer método, tradicionalmente el más empleado y el más probado. 10.10.1. - UNA PRIMERA APROXIMACION. Para detectar si el ordenador está ejecutando código del DOS (si está dentro de una INT 21h) se podría desviar esta interrupción y colocar una nueva rutina que incrementara una variable indicativa al principio, llamara a la INT 21h original y después volviera a decrementar la variable antes de retornar. Así, por ejemplo, desde una interrupción de teclado o periódica, se podría comprobar si el DOS ya está trabajando antes de llamarle (variable distinta de cero). Sin embargo, más que una variable habría 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 debería 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, significaría no decrementar como es debido la variable que indica que se ha abandonado la INT 21h. Además, para liar aún más el asunto, ¿qué hacer con los errores críticos?. Y, para colmo, todavía hay más: si el DOS está dentro de la INT 21h, función 0Ah (entrada en buffer por teclado), nuestra variable diría que no es posible usar el DOS en ese momento, ya que está ya en uso, cuando está científicamente demostrado que en este caso sí es reentrante si se utiliza una función 0Dh o superior (en la línea de comandos, el DOS está ejecutando precisamente esa función de entrada por teclado). Por fortuna, el DOS viene aquí en nuestro socorro: no será preciso diseñar la compleja rutina propuesta, ya que el propio sistema posee una variable interna que indica si en ese momento puede ser

186

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

interrumpido. Se trata de la variable no documentada InDOS. Existe una función secreta del DOS para obtener la dirección 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 automática 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 cómo lo hace el fabricante del sistema operativo con los suyos propios. El comando PRINT del DOS, cuando se queda residente, desvía un montón de interrupciones, entre ellas la 1Ch (equivalente a la 8) y la 28h. La interrupción 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 además 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 críticos); desvía esos vectores hacia unas rutinas propias; a continuación establece un DTA y un PSP propios. Tras enviar los caracteres a la impresora, leyéndolos del disco (con las funciones del DOS, por supuesto) vuelve a restaurar todo lo salvado. Pero vayamos más despacio. 10.10.2. - PASOS A REALIZAR PARA USAR EL DOS. Para obtener la dirección de InDOS se puede emplear la función 34h del DOS, que devuelve un puntero en ES:BX a dicha variable. La dirección de InDOS es constante, por lo que se puede inicializar al instalar el programa residente (no cambiará de lugar en toda la sesión de trabajo). Como luego nos será de utilidad, conviene decir aquí ahora que el Banderín de Errores Críticos del DOS está situado justo después de InDOS en las versiones 2.x y justo antes en la 3.0 (en la 3.1 y siguientes, la función 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 Banderín de Errores Críticos es también cero). En caso contrario, se puede inicializar una variable que indique que el programa residente tiene aún pendiente su ejecución: desde la interrupción periódica se puede comprobar si está pendiente la activación 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. Además de la interrupción periódica, también se puede desviar la INT 28h: desde esta interrupción se puede llamar al DOS, como dije antes, incluso aunque InDOS sea igual a 1 (pero no mayor) siempre que la función del DOS a ejecutar sea superior a la 0Ch (lo más normal). Sin embargo, cuando sea seguro llamar al DOS, habrá que hacer algunas cosas más antes de empezar a realizar la labor propia del programa residente. En el PSP se almacena mucha información vital para la ejecución de los programas. Una de las áreas más importantes es el JFT (Job File Table) que contiene información 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 dirección 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 precaución, podría 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 dirección del PSP activo se puede utilizar la función Get PSP (50h; ó la 62h, totalmente equivalente) que devuelve en BX su segmento; la función 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 además 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 críticos. Por tanto, con esta versión del sistema se puede forzar el Banderín de Errores Críticos a un valor 0FFh antes de llamar a las funciones 50h y 51h, para volverlo a poner a cero después: así, el DOS cree que el sistema está en medio de un error y usa la pila que queremos. Además 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 parámetros de la línea

PROGRAMAS RESIDENTES

187

de comandos cuando el programa accede a disco) y ocupa 128 bytes. Basta con preservar el DTA del programa principal, cuya dirección se obtiene en ES:BX con la función Get DTA (2Fh), y activar un nuevo DTA (por ejemplo, en el offset 80h del PSP de programa residente) utilizando la función Set DTA (1Ah), pasando su dirección en DS:DX. La información extendida de errores es otro punto a tener en consideración. Supongamos que el programa principal comete un error y el DOS genera la correspondiente información extendida de errores (a partir de la versión 3.0). Si en ese momento se activa el programa residente, puede que realice alguna función del DOS con éxito y el DOS sobrescribirá la condición de error previa. Por tanto, es deber del programa residente preservar y restaurar la información extendida de errores antes de actuar. La función Get Extended Error Information (59h) devuelve en AX, BX y CX la información extendida de errores. Con la función Set Extended Error Information (5D0Ah), en DS:DX se suministra al DOS la dirección de una tabla que contiene el AX, BX y CX con la información extendida de errores a establecer. 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 posición correcta). Si se van a emplear las INT 25h/26h, convendría monitorizarlas; así como la INT 10h si se utilizan servicios de vídeo (aunque sean del DOS). Por monitorizar se entiende interceptar esa interrupción 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 cuándo 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 sólo INT 13h sino también 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 pulsación de una combinación de teclas, es el siguiente: - Desde la interrupción del teclado, y una vez detectada la combinación 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 Banderín de Errores Críticos también es igual a 0. - Por si falla, desde la interrupción del temporizador se puede comprobar si está pendiente aún la activación 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. - Desde la interrupción 28h comprobar si está pendiente aún la activación del programa residente: en ese caso, si no estaba ya activo e InDOS<=1 y el Banderín de Errores Críticos es igual a 0 se puede proceder a activar el programa residente. - Como mínimo habrán 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 semáforos que conviene tratar con cuidado, para evitar reentradas en el programa residente: cuando desde una interrupción son comprobadas (ej., desde una INT 28h) podría producirse otra interrupción (como INT 8) lo que complica ligeramente la programación. 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 código propiamente dicho, el programa residente deberá preservar el DTA, el PSP y la información extendida de errores, así como los vectores de INT 1Bh/23h/24h. Después 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 gestión propia de los errores críticos. Al final, deberá restaurar todo de nuevo.

188

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

Toda la información vertida hasta ahora procede de la versión original del libro Undocumented DOS, citado en la bibliografía. Sin embargo, en mi experiencia personal con los programas residentes he sacado la conclusión de que es conveniente también desviar la INT 21h e intentar desde la misma activar el programa residente, tal como si se tratara de una interrupción periódica más. 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 sólo 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 sólo la interrupción periódica estaremos a merced de la suerte. Desviar la INT 21h e intentar activar el programa residente desde ella permite por ejemplo que éste actúe, en medio de un formateo de disco, de manera casi instantánea cuando se le requiere. Otro ejemplo: con el método 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 líneas éste responde; controlando la INT 21h, responde cada dos o tres caracteres impresos. Es evidente que la INT 21h pone a nuestra disposición un método mucho más efectivo a menudo que la interrupción periódica; sin embargo, tampoco es conveniente prescindir de esta última ya que la INT 21h sólo 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 ningún sitio excepto en este libro, puedo asegurar que he tenido bastantes ocasiones de comprobar que es completamente fiable. 10.10.4.- UN METODO ALTERNATIVO: EL SDA. Hasta ahora hemos visto el método más común para poder emplear el DOS desde un programa residente. Sin embargo, este método 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 solución alternativa que se apuntaba al principio de este apartado consiste en salvar el contexto del DOS y restaurarlo después, algo factible desde el DOS 3.0. Esto supone bastantes diferencias respecto al método estudiado hasta ahora. En lugar de chequear InDOS se debe verificar que el DOS no está en una sección crítica (que por fortuna es lo más normal) como luego veremos; y esto tanto desde la interrupción del teclado como desde la periódica o desde la INT 28h. Al comienzo del código 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 información. También 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 información extendida de errores: aunque se debe establecer un nuevo DTA, al restaurar el estado del DOS más tarde éste será también automáticamente 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 tamaño oscila entre 24 bytes y 2 Kbytes. Este área almacena el PSP activo y las tres pilas del DOS, así como la dirección del DTA... Para manipular el SDA se puede emplear la función del sistema Get Address of DOS Swappable Data Area (5D06h), que devuelve en DS:SI un puntero al SDA, en DX el número mínimo de bytes a preservar cuando el DOS está libre y en CX el número de bytes a preservar cuando el DOS está ocupado (InDOS distinto de cero). Desde la versión 4.0 del DOS se debe utilizar en su lugar la función Get DOS Swappable Data Areas (5D0Bh), ya que este sistema no posee un único área de datos sino múltiples. El procedimiento general consistirá, simplemente, en salvar el SDA al principio y restaurarlo al final. Como se dijo antes, el SDA sólo puede ser accedido cuando el DOS no está en un momento crítico. Cuando el DOS entra y sale de los momentos críticos, llama a la INT 2Ah con AX=8000h (inicio de momento crítico) o bien AX=8100h o AX=8200h (fin de momento crítico). Se debe interceptar la INT 2Ah e incrementar/decrementar una variable que indique las entradas/salidas del DOS en fase crítica. Este método para gestionar los programas residentes requiere algo más de memoria: en especial, si se quiere asegurar la compatibilidad con futuras versiones del sistema, habrá que reservar mucho más de 2Kb

PROGRAMAS RESIDENTES

189

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 máquinas 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). También es quizá algo más complejo. Sin embargo, añade algo más de potencia a los programas residentes, ya que pueden ser activados casi en cualquier momento y prácticamente en cualquier circunstancia. El autor de este libro nunca ha empleado este método. 10.10.5.- METODOS MENOS ORTODOXOS. Hay programadores que utilizan métodos muy curiosos para emplear los servicios del DOS desde los programas residentes. Un ejemplo, expuesto por Douglas Boling en su artículo de la revista RMP (Ed. Anaya, Marzo-Abril de 1992) consiste en activar el Banderín de Errores Críticos antes de llamar a las funciones ordinarias del DOS: de esta manera, se utiliza la pila de errores críticos en lugar de la de disco, con lo que no hay conflictos. Esto, por supuesto, sin que el DOS estuviera antes en estado crítico (en caso de estarlo hay que esperar). El inconveniente de este método es que sólo un programa residente de este tipo puede estar activo en un momento dado en el ordenador. Evidentemente, también hay que desviar la INT 24h para controlar un posible error crítico de verdad.

10.11. - EJEMPLO DE PROGRAMA RESIDENTE QUE UTILIZA EL DOS. El programa propuesto de ejemplo (SCRCAP) es el tradicional capturador de pantallas, en este caso de texto. El método que emplea es el clásico de comprobar la variable InDOS. Al pulsar Alt-SysReq (combinación por defecto) comienza a actuar. Emite un sonido ascendente que precede la grabación y otro descendente que la sucede, para confirmar que ha grabado. Los ficheros que genera tienen por nombre SCRxx-nn.SCR, donde xx es la anchura de la pantalla en columnas (en hexadecimal) y nn el número de fichero, entre 00 y 99. Los ficheros se crean a partir de 00 cuando se instala el programa, sobrescribiendo otros existentes con anterioridad. Al almacenar en el nombre del fichero la anchura del modo de vídeo, es fácil después procesar la imagen al conocer sus dimensiones. El programa no comprueba el modo de vídeo, por lo que en pantallas gráficas se obtienen resultados desconcertantes. Sin embargo, la ventaja de ello es que de esta manera puede salvar pantallas extrañas no estándar (como 132x60, etc.) que pueden poseer ciertas tarjetas. El fichero es creado en el directorio activo por defecto; si se invoca la utilidad mientras se ejecuta un DIR, el fichero podría crearse en el directorio visualizado (algunas versiones del COMMAND cambian el directorio activo momentáneamente). Como cabía esperar, el programa se autoinstala automáticamente en memoria superior y tiene opción de desinstalación, siendo también configurables las teclas de activación. Entre los aspectos técnicos, decir que se desvía la INT 21h como se comentó con anterioridad. En ese sentido, SCRCAP puede ser invocado con éxito mientras se formatea un disquete (bueno, pero tampoco para grabar precisamente sobre ese disquete). Se define una pila interna de 0,75 Kbytes, suficiente para el programa que graba la pantalla y para dar cabida a todas las interrupciones hardware que puedan anidarse durante el proceso (examinando la memoria con DEBUG se puede observar qué cantidad máxima de pila es consumida tras un rato de trabajo, ya que los caracteres ’PILA’ permanecen en la zona de la misma aún no empleada). Desde la rutina de control de INT 8 e INT 9 se llama a una subrutina, proceso_tsr, que toma la decisión de activar el programa residente si el DOS está preparado, o lo pospone en caso contrario. Desde la INT 28h se hace la comprobación más relajada de InDOS (basta con que sea no mayor de 1) y se toma también la decisión de activar el programa residente o seguir esperando: en el primer caso se llama a proceso_tsr con una variable (in28) que indica que ya no hay que hacer más comprobaciones. En proceso_tsr se comprueba la variable activo para evitar una reentrada al programa residente: como es un semáforo, es preciso inhibir las interrupciones con objeto de que entre su consulta y ulterior hipotética modificación no pueda ser modificado por nadie (por otro proceso lanzado por interrupciones). Al final, la rutina tarea_TSR es el auténtico programa residente. Simplemente modificando esta rutina se pueden crear programas residentes que realicen cualquier función, pudiendo llamar para ella al DOS.

INT 8 . -----------. INT 13h . DI. con objeto de que apunte correctamente al nuevo segmento en que reside el PSP. 0.Macros de propósito general XPUSH MACRO RM IRP reg.autor_nom_ver AX. Por defecto. DI. por supuesto.SCR". -----------.Rutina de gestión de INT 2Fh ges_int2F PROC STI CMP JE JMP CMP JNE MOV CMP JNE PUSH POP LEA MOV IRET ENDP FAR AH. 011: *.Identificación estandarizada del programa program_id segmento_real offset_real longitud_total info_extra LABEL DW DW DW DB BYTE 0 . y producir múltiples programas en tiempo récord. programa 100% reubicable . 010: *. DX.Programa scrcap SEGMENT ASSUME CS:scrcap. alguna subrutina de la instalación y. a diferencia de programas anteriores.1992h ret_no_info .DS:[17h] DS . con PSP . bit 7 a 1: «extension_id» definida 0 . DS:scrcap ORG ini_residente EQU 100h $ . -----------. etc. .CS:cod_rastreo . Si no se tomara esta precaución. INT 2Fh . zona de memoria ocupada (párrafos) 80h . ******************************************************************** * * * SCRCAP 1.60h CS:ant_int09 AL. . DS.0FFFFh .0". dirección original .Variables internas dosver ega activo inminente marcas DW DB DB DB DB ? ON OFF OFF 8 . . . vectores de interrupción interceptados . Alt.SysReq (PetSys) . dirección original . Sólo cambia la ayuda.. -----------. SI. nueva dirección . dirección original ret_no_info: .0 6 $ 8 DWORD 0 0 9 DWORD 0 0 13h DWORD 0 0 21h DWORD 0 0 28h DWORD 0 0 2Fh DWORD 0 0 . versión del DOS . Esto permite trabajar con comodidad. ¿tecla de activación? fin_09 AX. INT 23h . INT 9 .SYS formato EXE . algún parámetro. las subrutinas que componen ambos programas son lo suficientemente generales como para acomodar múltiples soluciones informáticas: se puede considerar que ambos programas son una especie de plantillas para crear utilidades residentes. dirección original . INT 28h .AX AL. bits 0. AT Y PS/2 SCRCAP termina residente dejando en memoria todo el PSP. el código residente. no hay ejecución pendiente .ON exit_08 proceso_tsr . INT 1Bh .190 EL UNIVERSO DIGITAL DEL IBM PC. AX. . En general. nueva dirección . hay que actualizar un campo en el PSP relocalizado (rutina reubicar_prog): se trata del campo que apunta a la JFT (offset 36h del PSP). . INT 24h .SYS . permitido control exterior DW 0 .0 ? 3 1Bh ges_int1B DWORD 0 0 23h ges_int23 DWORD 0 0 24h ges_int24 DWORD 0 0 . DB tabla_vectores EQU DB ant_int08 LABEL ant_int08_off DW ant_int08_seg DW DB ant_int09 LABEL ant_int09_off DW ant_int09_seg DW DB ant_int13 LABEL ant_int13_off DW ant_int13_seg DW DB ant_int21 LABEL ant_int21_off DW ant_int21_seg DW DB ant_int28 LABEL ant_int28_off DW ant_int28_seg DW DB ant_int2F LABEL ant_int2F_off DW ant_int2F_seg DW tabla_extra saltar al gestor de INT 2Fh no llama alguien del convenio no llama alguien del convenio sí llama: darle información "entrada multiplex en uso" . PSP del programa principal .. Para hacer nuevos programas residentes que hagan otras tareas. 001: bloque UMB XMS . dirección original exit_08: ges_int08 ges_int2F DB DW DW LABEL DW DW LABEL DW DW DW DW DW LABEL DW DW LABEL DW DW DW DW DW DW DW DB EQU DB DW DW DB DW LABEL DW DW DB DW LABEL DW DW DB DW LABEL DW DW 54h 0 0 DWORD ? ? DWORD ? ? ? ? ? DWORD ? ? DWORD ? ? ? 8 DUP (0) ? ? ? 192 DUP ("PILA") $ "SCRxx-00. dirección original preguntan: . el lector comprobará que tienen común cerca del 50% de las líneas. * * * ******************************************************************** cod_rastreo in13 in28 indos indos_off indos_seg crit_err crit_err_off crit_err_seg ant_pila_off ant_pila_seg mainpsp maindta maindta_off maindta_seg errinfo errinfo_ax errinfo_bx errinfo_cx ret_off ret_seg ret_flags pila_ini fich_nom fich_handle local_ints inicio: JMP main ant_int1B ant_int1B_off ant_int1B_seg ant_int23 ant_int23_off ant_int23_seg ant_int24 ant_int24_off ant_int24_seg .1492h ret_no_info . 1 y 2-> 000: normal.ES AX.75 Kb de pila multiplex_id vectores_id extension_id autor_nom_ver DB DW DW DB DB . INT 21h . Extended error information . campo reservado LABEL DB DW DW BYTE 1 act 1 . a ON si EGA o superior . -----------. DTA del programa principal . . -----------. dirección original . nueva dirección . DI. ejecutar TSR si es posible LABEL BYTE DW ctrl_exterior . no se accedería al disco correctamente.. . número Multiplex de este TSR tabla_vectores tabla_extra "*##*" "CiriSOFT:SCRCAP:1. . segmento real donde será cargado 0 . Si se compara el listado de SCRCAP con el de RCLOCK.Rutina de gestión de INT 8 ges_int08 PROC PUSHF CALL STI CMP JNE CALL IRET ENDP CS:ant_int08 CS:inminente. CS ES .CS:multiplex_id preguntan CS:ant_int2F . <RM> POP reg ENDM ENDM . pese a tratarse del lenguaje ensamblador. del programa principal . <RM> PUSH reg ENDM ENDM MACRO RM IRP reg.40h DS DS.. Conviene ahora hacer un pequeño apunte importante: cuando el programa es relocalizado a la memoria superior. Los últimos 128 bytes del PSP se dejan residentes porque serán empleados como área de transferencia a disco (DTA).0 * * * * Utilidad residente de captura de pantallas de texto. offset real " " " 0 . dirección original . ES. dirección original . XPOP .Rutina de gestión de INT 9 ges_int09 PROC STI PUSH IN PUSHF CALL CMP JNE MOV PUSH MOV MOV POP ctrl_exterior reubicabilidad activacion act AX AL. -----------. basta con cambiar sólo la parte residente y poco más.

.mainpsp 21h .[SI+2] AH. restaurar vectores INT 1Bh/23h/24h DS SI.59h BX. interceptar INT 1Bh/23h/24h ES SI.0 21h errinfo_ax..semáforo comprobado ya atendida la petición . DX.[BX] AL. SI.2Fh 21h maindta_off. BX = PSP activo (DOS 3+) BX AH.Rutina de control de ejecución del TSR proceso_tsr PROC CMP JNE CMP JA XPUSH LDS MOV LDS OR AND XPOP JZ MOV RET CLI CMP JE MOV STI MOV MOV MOV CLI MOV MOV LEA STI XPUSH XPUSH XPOP CALL CALL CALL CALL CALL CALL CALL CALL CALL CALL XPOP CLI MOV MOV . ejecutar TSR si se puede CS:in28. -----------. dentro de INT 28h . BX> BX. almacenar DTA activo .PROGRAMAS RESIDENTES 191 fin_09: ges_int09 AND CMP JNE CALL POP IRET ENDP AL. -----------. mucho cuidado con los flags . CX. INT xx desviada SI.SP CS:ant_pila_seg.0 no_proceder <DS. flags de retorno .segmento_real 21h <ES. DS.300h ret_int24 AX.ES .dosver AH. BX.CS:indos BYTE PTR [BX]..[SI] CX AL. errores críticos . CS:ant_pila_off. restaurar PSP programa principal DS AX. gestionar INT 24h pop_ints AX. -----------. activar nuevo PSP BYTE PTR [DI].x pop_otro: ret_int24: ges_int24 . BX. ejecutar TSR si es posible .0 proceder CS:in13.maindta_off DS.0 exit_28 <DS.50h BX.CS:crit_err AL.1 <BX. ES> . ES> <CS. DI.AX errinfo_bx. activo .BX [SI+7]. CS:activo. ges_int1B ges_int23 ges_int23 ges_int24 EQU PROC IRET ENDP PROC STI MOV CMP JAE XOR IRET ENDP THIS BYTE . retornar sin alterar flags .CS:[SI+2] AH. esperar próxima INT 8/28h a comprobar semáforo. DS y ES apuntan al TSR pushset_ints pushset_psp pushset_dta push_crit_err kbuff_limp tarea_TSR . . DS. AX> SP. BX.BX maindta_seg. establecer nuevo DTA <AX. se cumple que ambos a 0 . ejecutar código del TSR . necesario DOS 3.ES AH. no hay ejecución pendiente .CS:[SI+5] DS. exit_proceso . ¿marcas de activación? exit_proceso: . fuera de INT 28h pushset_psp pop_psp .[BX] BX.ON . AX> BX.ON exit_28 CS:in13.AX . CS> <DS. BP.3 CS:dosver. DS> proceder CS:inminente. nueva pila habilitada pop_dta pushset_dta . activar nuevo PSP BX mainpsp.OFF .BX exit_28: ges_int28 .ON . ejecutar TSR si es posible proceso_tsr MOV MOV STI RET ENDP SP. -----------. dentro de INT 28h . indicar entrada en INT 13h . ES> AH.maindta_seg 21h DS .pila_ini <DS.. anular error crítico psp_poped AH.[SI] CX AL. TSR ya activo . ejecutar proceso residente pop_crit_err pop_dta pop_psp pop_ints <ES. INT 13h en curso . ¿ya estaba activo? evitar reentrada ahora sí.AL <AX. DS> exit_28 <DS. restaurar PSP DS no_proceder: proceder: .x .CS:mainpsp 21h . dejar sólo segmento:offset pop_ints pushset_psp . no hay que activarlo getpsp3: . en DOS 2. restaurar DTA pop_dta push_crit_err dosver. DI. gestionar INT 13h phst_otro: .0+ .SP SP. ¿error crítico? psp_ok: . forzar error crítico BX AH.SS SP.CS:segmento_real 21h . -----------.15 AL.25h DX.x) BX AH.dosver AH.0FFh .x . INT xx restaurada SI. en DOS 2.segmento_real 21h . salida de INT 13h . BX> BX.OFF .Rutina de gestión de INT 28h ges_int28 PROC STI CMP JE CMP JNE CMP JA XPUSH LDS CMP XPOP JNE XPUSH LDS CMP XPOP JA INC CALL DEC JMP ENDP . CS:activo.50h BX.crit_err BYTE PTR [DI]. CS:inminente. retornar sin tocar flags ges_int13 . anular error crítico BX DS psp_ok AH.25h 21h .CS:crit_err BYTE PTR [BX]. DS> ..local_ints CX. DI.300h push_crit_fin AH. pila restaurada .2 getpsp3 DS . SI. gestionar INTs 1Bh/23h .0 .[SI+2] AH.7 CX pop_otro DS exit_21: ges_int21 .Subrutinas de apoyo pushset_ints PROC PUSH LEA MOV PUSH MOV MOV INT MOV MOV MOV MOV MOV INT ADD POP LOOP POP RET ENDP PROC PUSH LEA MOV PUSH MOV MOV MOV MOV INT ADD POP LOOP POP RET ENDP PROC MOV CMP JA PUSH LDS MOV MOV INT PUSH MOV MOV INT MOV POP POP JMP MOV INT PUSH MOV MOV INT POP MOV RET ENDP PROC PUSH MOV CMP JA LDS MOV PUSH MOV MOV INT POP MOV JMP MOV MOV INT POP RET ENDP PROC XPUSH MOV INT MOV MOV MOV MOV MOV INT XPOP RET ENDP PROC PUSH MOV MOV MOV INT POP RET ENDP PROC CMP JB MOV MOV INT MOV MOV MOV .[SI+3] AL.Rutina de gestión de INT 21h ges_int21 PROC POP POP POP PUSH PUSH PUSH CALL PUSHF CMP JNE CALL POPF RET ENDP FAR CS:ret_off CS:ret_seg CS:ret_flags CS:ret_seg CS:ret_off CS:ret_flags CS:ant_int21 CS:inminente. preservar información de . BX..2 setpsp3 BX.1Ah DX.1Ah DX.CS:[SI+7] 21h .50h .62h 21h . DX.0 <BX.. INT xx preservada DX.CS:marcas fin_09 proceso_tsr AX .Rutina de gestión de INT 13h ges_int13 PROC STI PUSHF INC CALL PUSHF DEC POPF RET ENDP FAR CS:in13 CS:ant_int13 CS:in13 2 . DS> exit_28 CS:in28 proceso_tsr CS:in28 CS:ant_int28 .CX .CS:indos AL.0 . segmento de retorno . ¿Indos>1? .CS:ant_pila_seg SS.ON . preservar PSP y activar el nuevo AX.Rutinas de gestión de INT 1Bh.local_ints CX. forzar error crítico AH. BYTE PTR [BX]. restaurar PSP BX BYTE PTR [BX]. crit_err OR indos pop_psp .51h 21h . offset de retorno ..50h BX. gestionar INT 28h CS:activo.80h DS. ignorar Ctrl-C y Ctrl-Break pushset_ints .0FFh . 0 en DOS 2. función de fallo .CS SS. BP.SP DS AH. -----------. 23h y 24h.35h 21h [SI+5].7 CX phst_otro ES . CX.BX errinfo_cx.CS:ant_pila_off CS:activo.crit_err . BX = PSP activo (DOS 2. preservar pila pushset_dta .ON exit_21 proceso_tsr . INT 13h en curso setpsp3: psp_poped: . DOS 3+ BX.ON exit_28 CS:inminente.

-----------.3 SHORT $+2 SHORT $+2 61h. activar sonido .40h DS. detectarEGA obtener_param .4 AX.Inicializar nombre de fichero con anchura de pantalla.DS:[49h] BX. -----------. -----------. desactivar sonido sonidoOFF .AX AL.AL WORD PTR fich_nom+3.’00’ .15 AX. -----------. preparar futuro nombre PROC PUSH IN OR JMP JMP OUT MOV JMP JMP OUT POP RET ENDP AX AL.CS:fich_handle DX.errinfo 21h sonar_arriba: .Pausa de 55 milisegundos espera55ms .30 sonar_abajo sonidoOFF sonar_abajo: kbuff_limpio: kbuff_limp . DS> AX.Sonido descendente AH.0 16h kbuff_limp sonidoDown PROC CALL CALL MOV MOV CALL CALL ADD LOOP CALL RET ENDP espera55ms sonidoON AX.’9’ inc_ok AH.DS:[6Ch] espera_tic <DS.Inhibir sonido sonidoOFF PROC PUSH IN AND JMP JMP OUT POP RET ENDP AX AL. binario -> hex AL. y apuntar DS a la misma .BX .Programar la nota AX en el temporizador sonidoAX PROC PUSH OUT MOV JMP JMP OUT POP RET ENDP AX 42h.1 CX. -----------.AL AX al_es_hex: ah_es_hex: init_nomfich . 25 líneas .25 CS:ega.’9’ ah_es_hex AH.ES:multiplex_id mx_unload .30 sonar_arriba sonidoOFF .AH AL. limpiar buffer del teclado sonidoUp CALL CALL MOV MOV CALL CALL SUB LOOP CALL RET ENDP espera55ms sonidoON AX.AX .’A’-’9’-1 AH.Activar sonido sonidoON .255-3 SHORT $+2 SHORT $+2 61h. número de bytes .Sonido ascendente sonidoUp PROC .0+ pop_crit_fin: pop_crit_err kbuff_limp .’9’ al_es_hex AL. adaptador de color .182 SHORT $+2 SHORT $+2 43h.5D0Ah BX. AX = tamaño buffer de vídeo sonidoAX .CL BX. DX.fich_handle AH.1 AH.AX .ya_install_txt print info_ya_ins .AL AL. . desinst . canal 2 del 8253 programado . -----------. por si acaso AL.61h AL.DX AH.AX dscx_eq_video BX.AX params_ok: desinst: desinstalarlo: ha sido posible no es posible ¿reside una versión distinta? no: se admite instalación error de versión incompatible inc_ok: inc_nombre no_pesame: no_residente: . preparar canal 2 . fin_noresid residente? . print fin_noresid AX. tarjeta modesta . devolver CX = tamaño pantalla . AT Y PS/2 push_crit_fin: RET push_crit_err ENDP pop_crit_err PROC CMP JB MOV MOV LEA INT RET ENDP PROC MOV INT JZ MOV INT JMP RET ENDP dosver.1 AH.AL AL. info_err_param . anchura de pantalla . abrir fichero PROC XPUSH MOV MOV STI MOV CMP JE XPOP RET ENDP <AX.fich_nom AX.AL AX tarea_err: tarea_TSR .1 AL.0B000h CX. no_residente .DS:[84h] AL WORD PTR DS:[4Ah] AX.des_no_ok_txt .’9’ [BX+6].Fin del área residente fin_residente bytes_resid EQU EQU $ fin_residente-ini_residente (bytes_resid+15)/16 parrafos_resid EQU .40h DS.3Ch 21h tarea_err fich_handle.1 16h kbuff_limpio AH.0B800h AX.2400 CX.DS:[4Eh] CL.0 AH.’A’-’9’-1 AH.tsr_seg AH.AL AX . error_version .ON modo_ok AH. supuesto adaptador monocromo . restaurar información de . DX.[BX+6] AH AH. grabar pantalla espera55ms .des_ok_txt no_pesame .40h 21h tarea_err CS DS BX. anchura de pantalla DS AH. necesario DOS 3.AX AX. -----------. -----------. mensaje inicial inicializar ciertas variables analizar posibles parámetros son correctos no: informar del error/ayuda ¿programa ya residente? aún no ¿se solicita desinstalarlo? así es parámetros en copia residente informar de teclas activación main modo_ok: video_ok: dscx_eq_video .’9’ inc_ok AL. bytes -> párrafos .1 . -----------. errores críticos .40h DS.DS:[4Ah] .AX DS.4000 AL.7 video_ok BX. params_ok .AX AX. fin_noresid ES. .fich_nom CX.AL AH. AX = líneas EGA/VGA .Proceso residente que puede emplear el DOS tarea_TSR PROC CALL CALL LEA MOV MOV INT JC MOV CALL MOV XOR MOV INT JC PUSH POP MOV MOV INT JC CALL CALL RET PUSH POP RET ENDP sonidoDown sonidoUp init_nomfich DX. . instalable . init_nomfich PROC PUSH MOV MOV MOV POP MOV SHR SHR SHR SHR AND ADD CMP JBE ADD CMP JBE ADD XCHG MOV RET ENDP sonidoON DS AX.Obtener segmento de vídeo y tamaño de la pantalla dscx_eq_video PROC MOV MOV MOV MOV MOV CMP JE MOV MOV MOV SHR ADD MOV CMP JNE XOR MOV INC MUL SHL MOV MOV RET ENDP AX.192 EL UNIVERSO DIGITAL DEL IBM PC.3Eh 21h tarea_err inc_nombre sonidoDown CS DS . DX. cerrar fichero .18 sonidoAX espera55ms AX. param_u.Incrementar número de fichero para siguiente vez inc_nombre PROC LEA MOV INC CMP JBE MOV INC CMP JBE MOV MOV RET ENDP BX. -----------. -----------.’0’ AL AL.DS:[6Ch] AL. print inic_general .AH SHORT $+2 SHORT $+2 42h.3000 CX.1 AH. offset de la página activa . .0 . modo de pantalla .61h AL. ***************************** * * * I N S T A L A C I O N * * * ***************************** PROC LEA CALL CALL CALL CALL JNC CALL JMP CALL JC CMP JE CALL LEA CALL CALL JMP MOV MOV CALL LEA JNC LEA CALL JMP CMP JE CALL DX.300h pop_crit_fin AX. líneas*columnas = caracteres .0 DX. segmento de vídeo efectivo . AX> espera_tic: . adaptar_param .scrcap_txt .18 sonidoAX espera55ms AX.

instalado_txt print info_ya_ins preservar_ints param_ml.AH DX.1 get_num mal_proc_pmt marcas. falta parámetro .’ ’ AL. separador millar descolocado CL.’?’ pmt_hlp AL. JMP CMP JNE LEA CALL JMP MOV ADD MOV CALL JNC LEA CALL JMP MOV LEA CALL CALL CALL CMP JNE MOV CALL JNC MOV CALL JC STC MOV MOV CALL CALL CALL JMP STC MOV CALL CALL CALL CALL MOV MOV INT MOV INT ENDP fin_noresid param_u. resultado .’9’ mal_num CL. terminar no residente ************************************* * * * SUBRUTINAS PARA LA INSTALACION * * * ************************************* no_millar: .2 21h DL. informar teclas activación tomar nota de vectores ¿se indicó parámetro /ML? en efecto párrafos de memoria precisos pedir memoria superior XMS hay la suficiente pedir memoria superior DOS 5 no hay la suficiente indicar que usa memoria DOS segmento del bloque UMB ES:256 zona a donde reubicar inicializar identificación reubicar el programa a ES:DI interceptar vectores programa instalado «arriba» instalación mem. CX = 0 . -----------.1 BX otro_pmt_mas param_ml. . no residente: ¿desinstalar? .10 DX . como próximo dígito<>0 a otro_car .Extraer nº de 16 bits y depositarlo en AX.[BX] BX AL. .’:’ ok_num .’0’ . . parámetro /S= . carácter tabulador .’0’ mal_num CL. delimitador: fin de número CL. letra del parámetro .13 mal_proc_pmt AL.9 saltar_esp AL. área residente .’u’ pmt_U SI.SI . 256 bytes de PSP (completo) .err_tec_txt print DX. el . .’.ayuda_txt print DX. número era incorrecto.[BX] obtener_num SI.1 get_num cod_rastreo. obtener_num PROC CMP JE CMP JE CMP JE CMP JE CMP JE INC MOV JMP MOV DEC XOR MOV DEC MOV CMP JE CMP JE CMP JNE CMP JE JMP CMP JB CMP JA SUB MOV PUSH AND JNZ AND JNZ PUSH MUL POP JC ADD JC POP CMP JNE MOV JMP MOV PUSH MUL POP JMP POP MOV STC RET MOV MOV CLC RET ENDP AL.’?’ pmt_hlp mal_proc_pmt BX AL. tras completar 5º dígito CX DX mal_num_pop DX. mayusculizar .PROGRAMAS RESIDENTES 193 instalable: instalar: handle_ok: instalar_umb: instalar_ml: fin_noresid: main . fin zona parámetros y número fin_num AL.32 . al final.15 fuera_rango AL.9 .1 mal_proc_pmt param_s.2 otro_pmt_mas . poner en minúsculas potencia: . puntero (BX) apuntará al final del número y CF=1 si el .0Dh fin_param BX .’/’ pmt_barrado AL.2 21h . fin número fin_num AL.SI AX. en efecto error_version PROC PUSH LEA CALL LES MOV MOV CLD REPNE REPNE MOV MOV INT MOV MOV INT MOV MOV INT ES DX. puntero al primer carácter . -----------.nocabe_txt print fin_noresid multiplex_id.ES:[DI+2] AH. fin número (otro dato) fin_num BX AL. no hay parámetro . -----------.[BX] AL. .1 . no quedan entradas .AL otro_pmt_mas param_u. . -----------.’.32 saltar_esp AL. ¡serán despistados! . fin de zona de parámetros .[BX] SI.’t’ pmt_T AL. -----------. apuntar a zona de parámetros saltar delimitadores quedan más parámetros no más parámetros multiplica: . parámetro /T= mal_num_pop: mal_num: . AX = 10 elevado a la 0 = 1 BX . . AX = 10 elevado a la N AX.’:’ delimit_ok . parámetros procesados ok.’ AH. .. -----------. fin_num: otro_car: . revisión fin_param: .AL fuera_rango otro_pmt_mas marcas.’h’ pmt_hlp AL.. .. . AX = AX elevado a la (N+1) DX otro_car AX . . saltar los puntos de millar AX. .3100h 21h AX.’ no_millar . ¿parámetro /ML? . .ES:[DI] AH.[BX] CL.’:’ CL. .255 mal_proc_pmt param_t.memoria AX.1 BX. próximo carácter a procesar CL.’=’ ok_num . .AL AX.DX AX. sintaxis incorrecta AL.4C00h 21h RET . DX = DX + digito (CX) * 10 ^ N (AX) mal_num_pop AX AX.16 memoria.256 inicializa_id reubicar_prog activar_ints fin_noresid DI. fin número fin_num AL. . 9 AX .0 . .’s’ pmt_S AL. . parámetro precedido por ’/’ pmt_barrado: .0Dh .81h saltar_esp otro_pmt fin_proc_pmt AL.Extraer posibles parámetros de la línea de comandos obtener_param otro_pmt_mas: otro_pmt: PROC MOV CALL JNC JMP CMP JE CMP JE JMP INC MOV CMP JE CMP JE OR CMP JE CMP JE CMP JE CMP JE MOV OR CMP JE STC RET CLC RET MOV JMP MOV CALL JC MOV CMP JA AND JZ JMP MOV JMP MOV CALL MOV JMP MOV INC JMP MOV ADD JMP ENDP BX. error en parámetro(s) . .. «error» de ayuda info_err_param PROC CMP JNE LEA CALL RET otro_error: LEA CMP JNE LEA err_ok: CALL LEA CALL RET info_err_param ENDP param_ayuda.’:’ . número correcto . hay parámetro . número mayor de 65535 .imp_desins_txt print fin_noresid AX. a la izda sólo permitir ceros DX . terminar residente .Obtener número chequeando delimitadores /= y /: get_num: INC MOV INC CMP JE CMP JE STC RET MOV CALL JC INC RET BX AL. pobre usuario DI. .’=’ delimit_ok AL. no manchar DX al multiplicar DI .Mensajes de error / ayuda .[BX] BX AL. convencional inicializar identificación reubicar programa a ES:DI interceptar vectores liberar espacio de entorno tamaño zona residente err_sintax: delimit_ok: . pasar ASCII a binario CH. tabuladores. delimitador: fin de número CL.parrafos_resid AX.[BX] obtener_num err_sintax BX . ¿fin de mandatos? .’/’ .1 instalar DX.DX .AX mx_get_handle handle_ok DX.255 err_ok DX. fin número (otro parámetro) fin_num AL.AX multiplica CL.1000 otro_car mal_num . reequilibrar pila BX.Saltar espacios.255 SCASB SCASB DL." " SI.err_sintax_txt marcas.256 inicializa_id reubicar_prog activar_ints free_environ DX. obtener_num ok_num: mal_proc_pmt: fin_proc_pmt: pmt_hlp: pmt_S: fuera_rango: pmt_T: pmt_U: pmt_ML: obtener_param param_ayuda.2 21h DL. .1 otro_error DX.Ya está instalada otra versión distinta del programa . AX*10 no se desbordará AX. . obtener entrada Multiplex . buscando un parámetro saltar_esp: MOV INC CMP JE CMP JE CMP JE DEC CLC RET STC AL. . la izda .tsr_dir AL. ¿parámetro de dos caracteres? .mal_ver_txt1 print DI.AX . mensaje de instalación . . condición de error BX.0 . entrada multiplex para SCRCAP .err_sintax_fin print .CL mal_num_pop .BX SI DX. no lo piden .AX DI. ."lm" pmt_ML . ..memoria UMB_alloc instalar_umb AX. número de versión . . . lo piden.10000 potencia .memoria UPPER_alloc instalar_ml ES. espacio en blanco . .0 instalar_ml AX. condición de Ok.

ES AX resid_ok ES ES DI. -----------.marcas AH.marcas ES:marcas. ídem con el offset AX.0 CL.act_ctrl print_ AL.ES ES . AX=1 -> sí: otra vers.DI AX. residente? PROC PUSH PUSH PUSH PUSH PUSH LEA MOV MOV MOV CLD REPNE SUB MOV MOV MOV MOV CALL MOV MOV POP JNC POP PUSH LEA MOV MOV MOV REPNE REPNE SUB MOV MOV MOV MOV CALL MOV MOV MOV JC MOV STC POP POP POP POP RET ENDP CX SI DI ES AX DI.4310h 2Fh XMS_off. no instalado . CF=0: usar memoria UMB XMS info_ok AL .10h gestor_XMS ega_ini: detectarEGA .AX AH.Reservar bloque de memoria superior del nº párrafos AX. .1 c_ok AL.Preservar vectores de interrupción previos preservar_INTs PROC PUSH PUSH LEA MOV MOV otro_vector: PUSH PUSH MOV MOV INT POP POP MOV MOV ADD LOOP POP POP RET preservar_INTs ENDP ES DI DI. -----------. A la entrada.0 . info_ok: inicializa_id resid_ok: segmento_real.AL .1992h mx_find_tsr tsr_off.cod_rastreo ES:cod_rastreo. -----------.1 no_umb_disp DX.Inicializar área «program_id» del programa residente.AX DI.AL ES . está instalado el gestor XMS (AX=0) o hay un error (AL . CF=0 -> programa ya residente c_ok: adaptar_param . SI> <BX.DI tsr_seg. anotar segmento del bloque offset_real.x .80h XMS_ausente ES AX.BX XMS_seg. programa no reside aún (AX=0) o reside pero en otra . tamaño autor+programa .DI .194 EL UNIVERSO DIGITAL DEL IBM PC.mal_ver_txt2 print ES . CF=0 si programa ya reside. CX vectores interceptados .AX AL.tsr_seg AL.AL residente? . ES:DI protocolo de búsqueda buscar si está en memoria anotar dirección del programa por si instalada otra versión .Detectar EGA o tarjeta superior free_environ detectarEGA PROC MOV MOV INT CMP MOV JE MOV MOV RET ENDP BL. CF=1. .DS:[2Ch] AH.AX DI. el .act_shift_der print_ cod_rastreo. CF=1 si no .0 CX DI AH.1 ES xms_ins. ES:DI protocolo de búsqueda buscar si está en memoria anotar la dirección programa por si estaba instalado s_ok: PROC PUSH MOV CMP JNE MOV MOV CMP JNE MOV MOV POP RET ENDP ES ES.act_shift_izq print_ AL. usar memoria convencional info_extra.255 SCASB DI. dirección de ese flag .5D06h 21h <DS. .OFF ega_ini AL. inicializa_id PROC PUSHF MOV MOV MOV MOV MOV POPF JNC DEC OR RET ENDP .tsr_seg param_s.300h crit_ok AX. CF=1.Liberar espacio de entorno .2 21h AX .0 resid_ok AX. .1 fin DX.4300h 2Fh AL. tamaño autor+programa+versión adaptar_param .act_otra_txt print_ DX. versión del DOS .12h 10h BL.10h AL.cod_rastreo DS DX. AT Y PS/2 error_version LEA CALL POP RET ENDP DX. obtener vector de INT xx .SI CX. liberar espacio de entorno crit_ok: inic_general .ES xms_ins.AL dosver.memoria longitud_total. Si CF=1. -----------.ES BX dosver.AX inic_XMS AH. dirección de InDOS . «tsr_off» inicializadas apuntando a la cadena de . Critical Error detrás en 2.1 s_ok AL.300h crit_ok BX.1 ES DI SI CX .54h act_ok DX. devuelve el código de error del controlador XMS). . ** .’:’ CL. AX=0 -> no residente . .act_teclas_txt print AL. UMB_alloc PROC PUSH PUSH PUSH CMP JNE MOV MOV CALL BX CX DX xms_ins. solicitar memoria superior . identificación de la copia residente. * free_environ .BX indos_seg.1992h mx_find_tsr tsr_off. -----------.2 shift_der? DX.8 shift_izq? DX. y preservarla XMS_ausente: inic_XMS .act_c_txt AH. ES:DI = seg:off a donde será reubicado . número de párrafos .BX [DI+3].act_fin_txt print print AX DL.DI AL.4 alt? DX. pedir información EGA al BIOS .ON ega. .49h 21h ES . devolviendo en AX el segmento donde está.AL param_t.DI AL.act_alt print_ AL. -----------.10h AH.[DI] 21h DI CX [DI+1].’-’ AH.0 .30h 21h AH. con «tsr_seg» y . no hay controlador XMS . Critical Error antes en 3. -----------.DI AX. A la . ES> DS crit_err_off.ES .1492h ES.35h AL.ES AX.34h 21h indos_off. sí: obtener su dirección .0 no_mas_teclas DX.autor_nom_ver SI. -----------.2 dosver.255 SCASB SCASB DI.Comprobar si el programa ya reside en memoria.SI CX.1492h ES.1 . -----------. identificación del programa PUSH CALL JC MOV tec_no_res: MOV MOV POP LEA CALL TEST JZ LEA CALL alt?: TEST JZ LEA CALL shift_izq?: TEST JZ LEA CALL shift_der?: TEST JZ LEA CALL fin: CMP JE LEA CMP JE LEA act_ok: CALL no_mas_teclas: LEA CALL RET print_: CALL PUSH MOV MOV INT POP RET info_ya_ins ENDP DS residente? tec_no_res DS.BX crit_err_seg.autor_nom_ver SI.Adaptar parámetros de un SCRCAP ya instalado en memoria . anotar donde apunta . DS> AH.tabla_vectores CL. -----------. repetir con los restantes .5 otro_vector DI ES .[DI-1] CH. y CF=1 si se utiliza memoria superior XMS.DI tsr_seg.Inicializar ciertas variables inic_general PROC XPUSH MOV INT XCHG MOV CALL MOV INT MOV MOV INC CMP JB SUB CMP JE MOV INT XPUSH XPOP POP MOV MOV POP RET ENDP <ES. versión distinta (AX=1). no es EGA . detectar controlador XMS .Informar de las teclas que activan SCRCAP info_ya_ins PROC .ES DI. * PROC PUSH MOV MOV INT POP RET ENDP ES ES. dirección del entorno .Considerar presencia de controlador XMS inic_XMS PROC MOV INT CMP JNE PUSH MOV INT MOV MOV MOV POP RET MOV RET ENDP AX. salida. chequear presencia XMS . .

mx_unload PROC PUSH CALL JNC POP RET mx_ul_able: XOR XCHG MOV MOV mx_ul_pasada: PUSH LEA MOV MOV mx_ul_masvect: POP PUSH DEC PUSH mx_ul_2f: MOV JNZ CMP JNE MOV LEA mx_ul_busca2f: CMP JE ADD JMP mx_ul_noult: CMP JNE ADD JMP mx_ul_pasok: PUSH PUSH MOV SHL SHL DEC MOV MOV POP PUSH MOV INT POP MOV SHR MOV ADD MOV mx_ul_masmx: CALL JNC JMP mx_ul_tsrcv: PUSH PUSH MOV ES mx_ul_tsrcv? mx_ul_able ES AL. un offset inicial (100h) y que el offset real en que .CX CX. .5 mx_ul_busca2f AL.AL .0C0h mx_ul_tsrcv? mx_ul_tsrcv mx_ul_otro ES:[DI-16] . Por ello. UPPER_fin: UPPER_alloc . CF=0 y ES:DI apunta a la cadena de identificación del . AL CX AL. necesario DOS 5.AX AX WORD PTR DS:[1].DI CX.256 MOVSB DI ES:[36h].DS ES. guardado el resultado .CS:[SI] DX.ES CX. -----------. programa buscado hallado mx_skip_hndl: .bytes_resid MOVSB SI.1 AX.) . copiar nombre de programa mx_tsr_found: . Se tendrá en cuenta que está ensambladas para correr en .alloc_strat 21h AX. mx_ul_noult AL.ES:[DI-8] .AX . del tamaño . hubo fallo DS AX DS. Si no hay bastante CF=1.ES BP=entrada Multiplex del TSR siguiente pasada CX = nº vectores pasada en curso vector en curso ¿último vector? . restaurar estado cadena UMB UPPER_fin .5802h 21h umb_state. AX CL.0 CX 2Fh CX AL.tabla_vectores ES:[SI].0.AX CS:mx_ul_tsrseg. A la salida.8 SI.. ES:[DI-12] DI. . entrada. número del vector en curso . reubicar_prog PROC PUSH LEA MOV CLD REP XOR XOR MOV REP POP MOV RET ENDP DI SI. DS:SI cadena de identificación del programa . .CS:[SI+1] AH. AX = 100h-DI . si el TSR ya está instalado.Reservar memoria superior.0 DX CX BX . han sido instaladas está en DI. los registros salvo los de segmento.PROGRAMAS RESIDENTES 195 no_umb_disp: XMS_fallo: UMB_alloc CMP MOV JNE POP POP POP CLC RET MOV POP POP POP STC RET ENDP AX.CS:[SI] SI.Reubicar programa residente a su dirección definitiva. Si no.5800h 21h alloc_strat. CF=1 y ningún registro alterado.5 mx_ul_2f ES AX AH. solicitado (AX párrafos).offsets_ints CX. .ES:[SI-1] CH. -----------. .2Fh . preservar párrafos. mx_get_handle PROC MOV mx_busca_hndl: PUSH MOV INT CMP POP JNE INC JNZ mx_no_hueco: STC RET mx_si_hueco: CLC RET mx_get_handle ENDP AH.CX DI. múltiplo de 16).5803h BL. si fue imposible y CF=0 si se pudo. comparar identificación .35h 21h . en caso contrario devuelve el segmento en AX.1 AX.25h 21h SI.AL BP. preservar DS para el retorno . .0FFh AX mx_si_hueco AH mx_busca_hndl . preservar estado UMB .0C0h AX AL.BX XMS_fallo DX CX BX AX. A la . 1492h:1992h). AL devuelve el vector «culpable».0 . A la entrada.DI CL. a la salida.TSR del convenio en ES:DI offset a la tabla de vectores activar_INTs .umb_state BH. -----------.AX AX.CL CX. A la salida. CF=1 si no hay hueco (ya hay 64 programas . En caso de fallo .30h 21h AL. desviar INT xx a DS:DX desvia_otro: INT xx en DX (aprox. al desinstalar. residentes instalados con esta técnica). . con DOS 5. simular PSP ES CX. no hay TSR ahí .41h 21h BX AH.. nuevo segmento de la JFT reubicar_prog ¿INT 2Fh? . CS ha de .AX .ES:[SI] .1 21h AX.Buscar entrada no usada en la interrupción Multiplex.Buscar un TSR por la interrupción Multiplex.. Se copia también el PSP. -----------. -----------.2Fh SI..0C0h AX CX SI DS ES DI AL.ES DX.SI DI.0 AX.0FFh mx_skip_hndl DI CMPSB DI mx_tsr_found DI ES DS SI CX AX AH mx_rep_find SP.100h AX.Eliminar TSR del convenio si es posible..párrafos requeridos . (CX bytes) y ES:DI protocolo de búsqueda (normalmente . mismo. AX = (100h-DI)/16 apuntar a tabla vectores vector en ES:BX . AH.ini_residente CX.2 CX . mx_ul_pasok CX. asignar memoria .AX DS. -----------. .20CDh .5801h BX.48h 21h AX AX.0 2Fh AL. .BH 21h AX . ¿ha ido todo bien? segmento UMB/código de error fallo ok .CS CX DS. mx_find_tsr mx_rep_find: PROC MOV PUSH PUSH PUSH PUSH PUSH PUSH MOV PUSH INT POP CMP JNE CLD PUSH REP POP JE POP POP POP POP POP POP INC JNZ STC RET ADD POP POP POP POP CLC RET ENDP AH.3 desvia_otro DS CX ¿restaurar INT 2Fh? . preservar estrategia . AX AX AH. Si CF=0. El segmento inicial es ES.5803h BX.CX CX.AL AX.CX SI.CL DX.Desviar vectores de interrupción a las nuevas rutinas.CX MOVSB ES DS . UPPER_alloc PROC PUSH MOV INT CMP POP JAE STC JMP PUSH MOV INT MOV MOV INT MOV MOV MOV INT MOV MOV INT POP MOV INT PUSHF PUSH MOV MOV INT MOV MOV XOR INT POP POPF JC PUSH DEC MOV INC MOV MOV PUSH MOV MOV MOV DEC MOV MOV MOV MOV CLD REP POP POP CLC RET ENDP AX AH. mx_ul_pasok SI.. conectar cadena UMB’s .tabla_vectores CL.BX . High Memory best fit .5801h BX. desplazarse (100h-DI)/16 unidades atrás (DI se supone .5 AX UPPER_existe UPPER_fin AX AX.4 DS SI CX AX UPPER_existe: .AL AH. «sacar» ES y DI de la pila mx_find_tsr .1 AX CS:mx_ul_tsroff. restaurar estrategia . CX vectores a desviar . SI.0 mínimo . obtener offset .2 AL.4 BX. . en AH se indica la entrada Multiplex. manipular PID WORD PTR DS:[16]. Se corrompen todos . devuelve en AH un valor de entrada libre en la INT 2Fh. se . .4 AX. activar_INTs PROC PUSH PUSH MOV SUB MOV SHR MOV SUB MOV LEA MOV ADD MOV MOV MOV INT ADD LOOP POP POP RET ENDP CX DS AX. CF=1 . CX. mx_ul_pasok SI.1 . AX AX .0 .

las pantallas gráficas generan ficheros " DB "inservibles.CS:mx_ul_tsroff . Se salvan también pantallas de texto no estándar " DB "(más de 25 líneas". ¿tipo de instalación? .13.13.10 DB " El parámetro /S permite elegir la combinación de teclas de " DB "activación (se". a por otro TSR mx_ul_exitnok .AL fin_print DL. no más.13.13."." SCRCAP 1.ES AH.DI DI.AL AX.49h 21h ES desinstalado 1ª pasada exitosa: por la 2ª .ES:segmento_real mx_ul_freeml xms_ins.196 EL UNIVERSO DIGITAL DEL IBM PC. Lo que".13.0 13.0 .0 "ShiftDer".10 DB " u 80 columnas).13. ES:DI almacena la dirección CS:mx_ul_tsrseg. ¿es TSR del convenio?.13.13.0 13. a 1 0 .ES:[DI] mx_ul_usavect ." SCRCAP 1." para activarlo. con /T puede".Programa aún no instalado: " "imposible desinstalarlo. no ES ..".10 " Ya hay 64 programas residentes con la " "misma técnica. CX." .10." DB "Valladolid.13. a 1 si presente controlador XMS .10. ¡se acabaron! mx_ul_masmx SP.Texto scrcap_txt instalado_txt DB DB 13.5 . a 1 0 .10.". cargado en RAM convencional no hay controlador XMS (¿?) liberar memoria superior .10 DB " utilidad. CF=1 . -----------.". ya que. dirección de la copia residente LABEL DWORD DW 0 DW 0 DW DW DB DW DB DW DB DW DB DW DB DW DB DW DB DB DB DB DB 0 6 8 ges_int08 9 ges_int09 13h ges_int13 21h ges_int21 28h ges_int28 2Fh ges_int2F 0 . 4-Ctrl.".Parámetro /S fuera de rango.10.ES:[DI-1] CH.[BX] AL..0 .11h gestor_XMS .ES .0FFFFh mx_ul_ncvexit WORD PTR ES:[DI-4].13. " DB "8-Alt)." . (c) Grupo Universitario de Informática .0 13. Se puede " DB "desinstalar con /U.no es TSR del convenio .10.0 "Ctrl".10 DB " salvará en disco con nombre SCRxx-nn.ES:[DI+1] CL. AT Y PS/2 mx_ul_buscav: mx_ul_usavect: mx_ul_norest: mx_ul_chain: mx_ul_otro: mx_ul_exitnok: mx_unloadable: mx_ul_exitok: mx_ul_freeml: mx_ul_tsrcv?: mx_ul_ncvexit: mx_ul_tsroff mx_ul_tsrseg mx_unload MOV MOV CMP JE ADD LOOP ADD JMP POP POP CMP JB ADD CMP JA PUSH XOR XCHG CMP POP JNE POP POP POP PUSH PUSH PUSH DEC JNZ POP PUSH PUSH MOV MOV CLI MOV MOV MOV MOV STI POP POP POP ADD DEC JZ JMP MOV MOV MOV MOV SHR MOV ADD MOV INC JZ JMP ADD POP STC RET POP DEC JZ JMP TEST MOV JZ CMP JNE MOV MOV CALL POP CLC RET MOV INT POP CLC RET PUSH PUSH PUSH MOV MOV MOV INT CMP JNE CMP JNE CMP JNE ADD POP RET POP POP POP STC RET DW DW ENDP CL.9.10 DB " instalación en memoria convencional. siguiente vector CX mx_unloadable .". la INT xx le apunta AX AL.Error: ya está instalada la versión ".CX CX.10.13.13.0".0 " desinstalado.10.10.13." .CX .10 DB 9.ES:[DI+3] DX.7. CF=0 AX DI ES AX 0 0 . tamaño del TSR BX ..13.10.AL AH.10 DB " (c) 1992 CiriSOFT." SCRCAP [/ML] [/S=marcas] [/T=codigo de rastreo] [/U] [/?|H]" DB 13.CS:mx_ul_tsrseg CX."*#" mx_ul_ncvexit SP.10.10 DB " cambiarse opcionalmente la tecla de activación..5 mx_ul_buscav SP.0 " de este programa.Imprimir cadena en DS:DX delimitada por un 0 print print_mas: PROC XPUSH MOV MOV AND JZ MOV MOV PUSH INT POP INC JMP XPOP RET ENDP <AX.BP . BX.".13. este TSR usa vector analizado DI. Consumo: 2208 bytes (2. número de vectores interceptados .0 8.". segmento del TSR DX.10.1492h ES. AX> ayuda_txt LABEL BYTE DB 13.10.0 " instalado.Parámetro(s) incorrecto(s).13. .0 ": Instalación imposible.Pulse ".Desinstalación imposible (se ha " "instalado después un programa" 13.4 .0): con " DB "/ML se fuerza la".13.13.13.". de la variable vector DX. estrategia asignación (DOS 5) . no es la segunda pasada ES . mx_ul_pasada . a 1 . ES AH.". dirección del controlador XMS ON OFF xms_ins gestor_XMS XMS_off XMS_seg alloc_strat umb_state tsr_dir tsr_off tsr_seg memoria offsets_ints . párrafos que ocupará SCRCAP ..0 13. estado de bloques UMB (DOS 5) .10 DB " obtiene sumando: 1-shift derecho.10 DB " la captura va precedida y sucedida de un sonido de aviso " DB "durante 1 segundo. sí: ¡posible reponer vector! CX BX BX CX ES BX mx_ul_norest .DX AL. al pulsar Alt-SysReq (Alt-PetSis) la " DB "pantalla actual se".".CX DX.BX mx_ul_otro ." . BX. INT xx en DX (aprox.10 DB " Por defecto se instala residente en memoria superior (si la " DB "hay) de manera".10.0 fin_prog scrcap EQU ENDS END $ inicio fin_print: print .13.BX mx_ul_otro .7.0 ya_install_txt DB act_teclas_txt act_ctrl act_alt act_shift_der act_shift_izq act_c_txt act_otra_txt act_fin_txt nocabe_txt DB DB DB DB DB DB DB DB DB DB DB err_sintax_txt DB err_tec_txt DB err_sintax_fin DB DB mal_ver_txt1 mal_ver_txt2 des_ok_txt des_no_ok_txt DB DB DB DB DB DB DB DB .10.SCR.13. a 1 0 . restaurar INT’s DS." Ejecute SCRCAP /? para obtener " "ayuda.13.10 DB " (incluso sin indicar DOS=UMB en el CONFIG del DOS 5. DX> BX."#*" mx_ul_ncvexit WORD PTR ES:[DI-2].0 13." .ES:[SI+1] [BX+1].AL AH. CX.7." DB 13.10. segmento real del bloque . imposible desinstalar CX CX mx_ul_exitok . equilibrar pila ES .0 13.10.10 DB " Una vez instalado. de interrupción interceptados param_ml param_s param_t param_u param_ayuda si si si si si se se se se se indicó parámetro /ML indicó parámetro /S indicó parámetro /T indicó parámetro /U indicaron parámetros /? /H ó ? . donde xx es la " DB "anchura hexadecimal". la INT xx no le apunta BX.10.0 8. -----------.4 .". ¿es el propio TSR? AX mx_ul_chain . segunda pasada.10 DB " de la misma (en columnas) y nn el número de fichero.0 "Alt".10 DB " tras instalar el programa.CX DS ES CX SI. ES DS BX.) AH.". a 1 0 .4 DX. ES:info_extra.0 13.13.10 " . " DB "partiendo de 00". liberar bloque de memoria ES: imp_desins_txt DB DB AX .10. sea cual sea la versión del sistema o el " DB "controlador de memoria".111b ES. .DI . no lo usa mx_ul_otro CX .10 DB " automática..10.6 .2 BX 21h BX BX print_mas <DX.".13.0 "SysReq". número de vectores en CX AL.10. 2-shift izdo.10 DB " siendo a menudo posible incluso aunque no sea el último TSR " DB "instalado. ********************************** * * * DATOS PARA LA INSTALACION * * * ********************************** EQU EQU DB LABEL DW DW DW DB 1 0 0 DWORD 0 0 0 0 .".0 " ya instalado. .1992h 2Fh AX.ES:[SI+3] [BX+3].". DX. ¡desinstal-ar/ado! mx_ul_masvect CS:mx_ul_tsroff.0BFh AH .".1 mx_ul_freeml .0 "ShiftIzq".Utilidad de captura de pantallas de texto." que no respeta el convenio y tiene " "alguna interrupción común). . constantes booleanas .CL CX. tabla de offsets de los vectores .". se crean sucesivamente cada vez " DB "que se invoca la".16 " DB "Kb). ES DI DI." y la tecla elegida". .13.10 DB " se almacena en los ficheros es exactamente el contenido del " DB "buffer de vídeo.

0x10). if (!*ext) strcpy (ext. char **argv) { int handler. ancho<<1)==ancho<<1) { for (il = (ancho<<1)-2. rutar). aunque presenta alguna dificultad en ciertos modos de la VGA. 1).\n"). il-=2). disco. (il>=0) && buffer[il]==’ ’. buffer. *(p+2)=’X’. } buffer=MK_FP((peekb(0x40. O_RDONLY | O_BINARY. __emit__(0xcd.C se convierten las pantallas capturadas (de 40/80/94/100/120/132 ó 160 columnas) a modo texto: se suprimen los colores. } _AX=3. buffer. FA_ARCH|FA_HIDDEN|FA_RDONLY). fnmerge (rutar. p+=2. ruta[MAXPATH]. exit(1). FA_ARCH|FA_HIDDEN|FA_RDONLY).12. } if ((handler=open(rutar. direct[MAXDIR].’)) p++.*"). exit(1). */ /* */ /********************************************************************/ #include #include #include #include #include <dos.h> exit (1).h> <dir. void far *buffer. if (il>9) il-=’A’-’9’-1. &fichero. ancho=(ih<<4)+il. il=fichero. __emit__(0xcd.\n"). if ((ancho!=40) && (ancho!=80) && (ancho!=94) && (ancho!=100) && (ancho!=114) && (ancho!=120) && (ancho!=132) && (ancho!=160)) { printf(" . while ((*p) && (*p!=’.PROGRAMAS RESIDENTES 197 Para visualizar las pantallas capturadas puede utilizarse la utilidad SCRVER. son totalmente estándar y más rápidas. 0x10). que al emplear el TYPE del DOS las líneas queden separadas por una línea extra en blanco (si tuvieran 79 columnas o si se carga desde un editor de texto. no habrá problemas). *p++=0x0A. ext).CASO GENERAL. if (argc<2) { printf("Indique el(los) fichero(s) a convertir. ultimo=findfirst (ruta. struct ffblk fichero. close(handle). 0)) == -1) { printf("Error al abrir fichero de salida. ext[MAXEXT]. if (fichero.PROGRAMAS RESIDENTES INVOCABLES EN MODOS GRÁFICOS. strcpy (rutaw. 0)) == -1) { printf("Error al abrir fichero de entrada. } } void main(int argc. fnsplit (argv[1]. close (handlew).ff_name[3]==’2’) { _AX=1. *p. if (argc<2) { printf("\nIndique el(los) fichero(s) a visualizar. /* modo 80x25 */ } void main(int argc. O_RDONLY | O_BINARY. exit (1). fich. se eliminan la mayoría de los códigos de control. ""). ""). . direct.\n").h> <fcntl. */ /* */ /********************************************************************/ #include #include #include #include #include <dos.0 . ext). struct ffblk fichero. } read(handle. } printf("Procesando %s\n". direct[MAXDIR]. p.ff_name[4]-’0’. direct. que admite comodines para poder ver cualquier conjunto de ficheros.h> fnmerge (rutar.SCR\n".ff_name. __emit__(0xcd.Utilidad para convertir pantallas capturadas por */ /* SCRCAP a modo texto. En la práctica. direct. printf("\n"). buffer. *p++=0x0D. disco.\n").Utilidad para visualizar pantallas 80x25 y 40x25 */ /* capturadas por SCRCAP.ff_name. } if ((handlew=_creat(rutaw. if (ih>9) ih-=’A’-’9’-1. rutaw[MAXPATH].h> <conio. 0x10). ih<=il. ultimo=(getch()==27) || findnext (&fichero). fnmerge (ruta. 2). rutar[MAXPATH].h> <string. il. ultimo=findfirst (rutar. ".ff_name[3]-’0’. if (ultimo) { printf("Nombre de fichero incorrecto. ultimo=findnext (&fichero). 10. } close(handler).0x49)==7 ? 0xB000: 0xB800).1 . exit(1).h> <string. if (!*ext) strcpy (ext. /* carácter control */ write (handlew.*"). La mayoría de los programas residentes prefieren operar con pantallas de texto: ocupan menos memoria. exit(1). ". direct. *(p+4)=0. disco. *(p+1)=*(p+3)=’T’. ext). /********************************************************************/ /* */ /* SCRVER 1. ext).\n"). exit(1). al cambiar de modo no se limpia la pantalla. char buffer[512]. disco. 30000). exit(1). Sin embargo. disco[MAXDRIVE].12. fichero. direct. while (read(handler.h> <dir. p=buffer. /********************************************************************/ /* */ /* SCR2TXT 1. *p=0. } /* modo de 40x25 */ else { _AX=3. fich[MAXFILE]. ultimo. ancho. Esta característica está disponible sólo . *(p-5)=*(p-4)=*(p-3)=’0’. En los modos estándar de IBM (y en general también en los no estándar) cuando se solicita a la BIOS que establezca el modo de vídeo (véanse las funciones de la BIOS en los apéndices) si el bit más significativo del modo se pone a 1. Con SCR2TXT. fich. } while (!ultimo) { 10. fich. existe una técnica sencilla que permite simplificar este proceso. Borland C en modo "Large". rutar). rutar). 0). for (ih=0. direct. char **argv) { int handle. fichero. se quitan los espacios en blanco al final de las líneas y se añaden retornos de carro para separarlas. write (handlew. } /* modo 80x25 */ if ((handle=open(ruta.h> <fcntl.\n"). } p=buffer.h> <conio. ih+=2) { if (((*p>6) && (*p<32)) || !*p) *p=’ ’. fich. handlew. } fnsplit (argv[1]. ih=fichero. ultimo. Esto último provoca. ih. 0)) == -1) { printf("Error al abrir fichero de entrada. Borland C en modo "Large". fich[MAXFILE].\n").Error: el fichero %s no es del tipo SCRxx-nn. en pantallas que ocupan justo las 80 columnas. la dificultad asociada al proceso de preservar el contenido de una pantalla gráfica y después restaurarla lleva a muchos programas residentes a no dejarse activar cuando la pantalla está en modo gráfico.0 . disco.C. } while (!ultimo) { fnmerge (ruta. if (ultimo) { printf("\nNombre de fichero incorrecto. siendo operativa en todos los modos de la EGA y VGA estándar. p=rutaw. disco. &fichero. char disco[MAXDRIVE]. ext[MAXEXT].

se tomará la molestia de dejar activo el bit de 40h:87h si así lo estaba al principio. habilitar acceso a tabla de caracteres . Ante todo. evidentemente. se redefinirá el juego de caracteres con los 8 Kb preservados. Sin embargo.. .12. Por ello. antes de volver al modo gráfico. una ligera complicación al trabajar en el modo 13h de la VGA (320x200 con 256 colores) o en la mayoría de los modos SuperVGA. ya que la diferencia entre el modo 12h y el 92h es sólo a nivel de software y no de hardware.93h 10h . tareas éstas que puede simplificar la BIOS. Por último. esa porción de la memoria de la tarjeta gráfica es parte de la pantalla en el modo 13h y en los modos SuperVGA. como se verá. También habrán de preservar la posición inicial del cursor y la página de vídeo activa inicialmente (que habrán de restaurar junto con el modo de vídeo).es más cómodo programar el controlador de gráficos para configurar de manera cómoda la memoria de vídeo y preservar sin problemas los 8 Kb deseados. activar el modo 92h (el 12h con el bit 7 activo). así como las paletas de la EGA y VGA. antes de restaurar el modo gráfico (poco probable. El problema consiste en que. sin embargo. Como inmediatamente después se vuelve al modo gráfico. al pasar a modo texto. requiere conocimientos acerca de la arquitectura de las tarjetas gráficas EGA y VGA a bajo nivel. pero posible -sobre todo cuando el usuario activa más de un programa residente de manera simultánea-).83h 10h def_car_on restaurar8k AX. no hace falta restaurar el estado de ningún controlador de vídeo. al final. Se trata de una posibilidad muy interesante. AT Y PS/2 en máquinas con tarjeta EGA o VGA (tanto XT como AT). Esta técnica presenta. La solución no es muy complicada. además. 10. después habrá que engañar de alguna manera a la BIOS para que crea que la pantalla está en modo 12h y no 92h (sutil diferencia. cuando halla que restaurarla. y estando aún en modo texto. no las comentaré. Esta información puede obtenerse en libros especializados sobre gráficos (consúltese la bibliografía) aunque a continuación expongo el listado de def_car_on. que permite a los programas residentes activar momentáneamente una pantalla de texto. aunque sí un poco engorrosa. la BIOS define el juego de caracteres -que en la EGA/VGA es totalmente programableutilizando una cierta porción de la memoria de vídeo de la tarjeta. la rutina que prepara el sistema de vídeo de tal manera que se pueda redefinir el juego de caracteres de texto. el usuario no notará la basura que aparezca en la pantalla durante breves instantes y. sin necesidad de preservar ni restaurar zonas gráficas. no hay un algoritmo sencillo para acceder a la zona de memoria de gráficos que hay que preservar.. El siguiente ejemplo práctico parte de la suposición de que nos encontramos en el modo 13h: CALL CALL MOV INT CALL CALL MOV INT def_car_on preservar8k AX. . pasar a modo texto 80x25 . . . recordar que esto sólo es necesario en modos de pantalla avanzados o en el 13h. una para cada modo gráfico. pasar a modo texto y. Un programa residente elegante. . 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. eso sí. Por desgracia.2 . redefinir el juego de caracteres de texto de tal manera que al volver a modo gráfico ya esté restaurada la zona manchada.. Evidentemente.. Después. Este orden de operaciones no es caprichoso y lo he elegido para reducir los accesos al hardware. la BIOS reprogramará adecuadamente el controlador de gráficos. El problema principal radica en el hecho de que la arquitectura de la pantalla en los modos gráficos y de texto varía de manera espectacular.198 EL UNIVERSO DIGITAL DEL IBM PC. guardar 8 Kb de A000:0000 en un buffer . habilitar acceso a tabla de caracteres copiar el buffer de 8 Kb en A000:0000 13h + 80h restaurar de nuevo el modo gráfico Las rutinas preservar8k y restaurar8k son tan obvias que. preservar el fragmento de la misma que van a emplear y. de nuevo. Una posible solución consiste en preservar la zona que va a ser manchada (8 Kb) en un buffer. ¿no?) y ello se consigue borrando el bit más significativo de la posición 40h:87h (la variable de la BIOS 40h:49h indica siempre el número de modo de pantalla con el bit más significativo borrado: este bit se almacena separadamente en 40h:87h). operar en modo texto . sin entrar en detalles técnicos acerca de su funcionamiento: . Para no desarrollar complicadas rutinas -por si fuera poco. Esta operación es segura.CASO DEL MODO 13H DE LA VGA Y MODOS SUPERVGA. ya que la BIOS lo reprogramará correctamente al pasar a modo texto. restaurarlo y volver al modo gráfico como si no hubiera sucedido nada.

La solución adoptada consistió en dar un paso intermedio: antes de llamar a def_car_on se puede poner la pantalla en un modo no conflictivo y que sea gráfico para evitar que la BIOS defina el juego de caracteres (como el 13h+80h=93h). Las máquinas que no tengan disco duro aumentarán el consumo de memoria del programa residente en 8/16 Kb.car_on . En la aplicación práctica de las rutinas expuestas se han detectado algunos problemas de compatibilidad con algunas tarjetas. Los más avanzados pueden grabar los 8 Kb en disco duro. así como toda la memoria de pantalla CGA (unos modestos 16 Kb) en las máquinas que no están dotadas de EGA o VGA y no pueden conmutar el modo de pantalla sin borrar la misma. El tema de los programas residentes de DOS funcionando bajo Windows no es demasiado importante ya que. 300h. si la máquina está dotada del mismo.3 . desde dentro de Windows no es necesario tener instalados programas residentes.0CEh .PROGRAMAS RESIDENTES EN ENTORNO WINDOWS 3. programarlo LOOP def_on_2 RET DW 100h. aunque sería incluso más recomendable ocuparse antes de la Hércules.4 .AX . no más precauciones MOV DL. se colgaba el ordenador al ejecutar def_car_on. El método propuesto es ciertamente sencillo. Sin embargo.12. 10. en este modo sí se puede ejecutar def_car_on. Tiene requerimientos (como el buffer de 8 Kb) que no están quizá al alcance de los programas residentes menos avanzados. los más perfeccionistas siempre pueden consultar bibliografía especializada en gráficos para tratar de manera especial este adaptador de vídeo. 6 . antes de pasar al modo texto.ALGUNOS PROBLEMAS. Otro premio reservado para estos perfeccionistas será la posibilidad de conmutar los modos de pantalla accediendo al hardware y sin apoyo de la BIOS. aunque se complique un poco más en algunos modos de la VGA. Por cierto. aunque ¡peor sería tener que preservar hasta 1 Mb de memoria de vídeo!. el estándar VESA posee también funciones para preservar y restaurar el estado del adaptador de vídeo. El más grave se produjo con una OAK SuperVGA: en algunos modos de 800 y 1024 puntos. el lector podría encontrar interesante documentarse acerca de ello. de cara a no tener que desarrollar una versión específica no residente para este entorno. anulando los TSR que se activan por . aunque afortunadamente esta tarjeta está poco extendida (sólo acompaña al PS/2-30. datos ENDP 10. 10. La razón es que Windows reemplaza totalmente al controlador del DOS. en teoría. El problema está en las tarjetas no compatibles VGA: mucho cuidado al utilizar la rutina def_car_on (hay que detectar antes la presencia de una auténtica EGA/VGA. en sus primeros modelos un compatible XT). 204h. . programar registro LOOP def_on_1 STI . para que no borre la pantalla en las CGA.AX .3C4h .3 LODSW OUT DX.PROGRAMAS RESIDENTES 199 def_car_on def_on_1: def_on_2: car_on def_car_on PROC MOV DX.12.CONSIDERACIONES FINALES. puede ser interesante en ocasiones crear programas residentes que también operen bajo Windows. 5.4 CLD CLI . 402h. Un problema importante de los programas residentes consiste en la dificultad para leer el teclado. códigos a enviarle MOV CX. 704h. ¡no vale la MCGA!). al tratarse de un entorno multitarea que permite tener varios programas activos en pantalla a la vez. 3CEh = puerto del controlador de gráficos MOV CX. Téngase en cuenta que esta operación sería mucho más delicada en las EGA y VGA (es más difícil restaurar todos los parámetros hardware del modo gráfico activo inicialmente) en las que además habría que definir un juego de caracteres de texto.13. precauciones LODSW OUT DX. puerto del secuenciador LEA SI. En MCGA no se puede aplicar def_car_on en el modo 13h.

bloque numérico. de cara a permitir la convivencia de varios programas residentes (problema que se puede solventar permitiendo al usuario elegir las teclas de activación). La solución más sencilla consiste en no permitir la invocación del programa residente desde más de una tarea. puede indicárselo a Windows con un puntero a un área de datos denominado SWSTARTUPINFO en ES:BX.0. por ello es preciso almacenar primero el ES:BX inicial de la rutina en dicha estructura y cargar ES:BX apuntándola antes de retornar. 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 ejecución de Windows bastará cargar un valor distinto de 0 en CX antes de retornar. llama a la INT 2Fh con AX=1605h: un TSR puede interceptar esta llamada (como en cualquier otra interrupción. sin embargo. en cuyo caso se puede abortar su ejecución y emitir un mensaje de error para solicitar al usuario que no desinstale el TSR antes de entrar en ese modo de Windows. que define las estructuras de datos que serán locales a cada sesión.0. . pueden aparecer problemas de reentrada (la segunda ejecución estropeará los datos de la primera).. El otro problema está relacionado con la multitarea de Windows. .200 EL UNIVERSO DIGITAL DEL IBM PC. . Se puede interceptar el arranque de Windows y comprobar si lo hace en modo real. en algunos TSR (tales como utilidades de macros de teclado.en el real. En los AT se puede leer el puerto del teclado en cualquier momento (fuera de la INT 9) aunque no es recomendable porque la práctica reiterada de este método provoca anomalías en el mismo (tales como aparición de números en los cursores. se puede quemar el último y más efectivo cartucho: comunicar el TSR con el conmutador de tareas de Windows para emplear memoria instantánea. estado de Shift que se engancha. 0 0 . este área ha sido diseñada para almacenar una cadena de referencias entre todos ellos. consiste en comprobar las variables de la BIOS que indican el estado de mayúsculas.) debido a las limitaciones del hardware. El único problema es la limitación de combinaciones posibles que se pueden realizar con estas teclas. Otra solución sencilla consiste en obligar al usuario a instalar el TSR en cada sesión de DOS abierta.1 de Windows. shift.) esto supone una grave e intolerable restricción. con la versión 3. en el modo estándar se puede emplear el conmutador de tareas del DOS 5. dirección de memoria de la estructura tamaño de la estructura estructura NULL (fin de lista) En los momentos críticos en que el TSR deba evitar una conmutación de tareas. es el siguiente: DD DW . . DD DW ? ? . aunque la recompensa para quien implemente soporte en sus TSR para los dos métodos es que les hará compatibles también con el conmutador de tareas del MS-DOS 5. . versión de la estructura puntero a la próxima estructura SWSTARTUPINFO (ES:BX inicial) puntero al nombre ASCIIZ del dispositivo virtual (ó 0) datos de referencia del dispositivo virtual (si tiene nombre) puntero a la tabla de registros de datos locales (ó 0) El formato de la tabla de registros de datos locales. puede emplear las funciones BeginCriticalSection (llamar a INT 2Fh con AX=1681h) y EndCriticalSection (llamar a INT 2Fh con AX=1682h). . Sin embargo. no en el modo estándar ni -en el caso de la versión 3.0. aunque menos potente. Si el TSR necesita áreas de datos locales a cada sesión en el modo extendido. que es el que utiliza dicho modo. . AT Y PS/2 teclado. Si se abren varios procesos DOS desde este entorno y se activa el programa residente en más de uno de ellos. y teniendo en cuenta que puede haber varios TSR que intercepten las llamadas a la INT 2Fh con AX=1605h. etc. . No deja de ser una pena tener que utilizar un método diferente para el modo estándar que para el extendido. Para los casos en que no sea recomendable esto último.. . Para ello. . etc. . el TSR debe estar poco tiempo en fase crítica para no ralentizar Windows. ya que estas variables son correctamente actualizadas desde dentro de Windows. El formato de SWSTARTUPINFO es el siguiente: DW DD DD DD DD 3 ? 0 0 ? . Cuando Windows arranca. con lo que todo el entorno de operación será local a dicha sesión. . Un método más recomendable. El único inconveniente es que Windows sólo facilita memoria instantánea en el modo extendido 386. .

apuntada por la estructura SWCALLBACKINFO. identificador del API (1-NETBIOS. 2-802. 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 (pasándose mutuamente el control). son las siguientes: 0000h inicialización del conmutador Devuelve: AX = 0000h si permitido = no cero si no permitir iniciar el conmutador 0001h pregunta de suspensión del conmutador BX = Identificación de sesión Devuelve: AX = 0000h si permitir conmutación (el TSR no está en región crítica) = 0001h si no 0002h suspensión del conmutador BX = Identificación de sesión interrupciones inhibidas Devuelve: AX = 0000h si permitido conmutar de sesión = 0001h si no 0003h activando conmutador BX = Identificación de sesión CX = banderines de estado de la sesión bit 0: activo si primera activación de la sesión bits 1-15: reservado (0) interrupciones inhibidas Devuelve: AX = 0000h 0004h sesión activa del conmutador BX = Identificación de sesión CX = banderines de estado de la sesión bit 0: activo si primera activación de la sesión bits 1-15: reservado (0) Devuelve: AX = 0000h 0005h crear sesión del conmutador BX = Identificación de sesión DEVUELVE: AX = 0000h si permitido = 0001h si no . aunque algunas podrían fallar). la suspensión de la sesión. . por ejemplo. significa que está cargado y ES:DI apunta a la rutina de servicio del mismo. la misma en ambos casos. el aviso de inicio de sesión es fundamental para los TSR que tienen áreas de datos temporales que inicializar al comienzo de cada sesión. el TSR será llamado por el conmutador de tareas para ser informado de todo lo interesante que suceda (de cosas tales como la creación y destrucción de sesiones. Las funciones que debe soportar la rutina de notificación. . 2-soporte a nivel API (el TSR impide la conmutación de tareas si las peticiones son importantes). . por fortuna.2. 3Compatibilidad de conmutación (se permite conmutar de tarea incluso con peticiones importantes.. ejecuta una INT 2Fh con AX=4D05h para tomar nota de los bloques de datos locales a cada sesión. llamada que los TSR deberán detectar del mismo modo que cuando comprobaban la ejecución de Windows en modo extendido: la estructura de datos es además. longitud de la estructura . Para gestionar esto existe una estructura de datos denominada SWCALLBACKINFO (apuntada por ES:BX al llamar a INT 2Fh con AX=4B01h): DD DD DD DD ? ? ? ? . número de la mayor versión del API soportada . etc.. 4-Sin compatibilidad (se permite siempre la conmutación).0 se debe llamar a la INT 2Fh con AX=4B02h: si a la vuelta AX es 0. para informar al conmutador. 5-NetWare IPX) . suspensión del conmutador. nivel de soporte: 1-mínimo (el TSR impide la conmutación de la tarea incluso tras finalizar sus funciones). puntero a la estructura SWCALLBACKINFO anterior puntero a la rutina de notificación del TSR área reservada puntero a la lista de estructuras SWAPINFO La lista de estructuras SWAPINFO tiene a su vez el siguiente formato: DW DW DW DW DW 10 ? ? ? ? .PROGRAMAS RESIDENTES 201 Para detectar la presencia del conmutador de tareas del MS-DOS 5. 3-TCP/IP. Cuando el conmutador de tareas arranca. número de la menor versión del API soportada . Una vez enganchado.) por medio de la ejecución de la rutina de notificación del mismo. pudiendo el TSR permitir o no. 4-Tuberías LanManager. que pone varias funciones a disposición de los TSR: los TSR deberán ejecutar la función AX=4 (Conectar a la cadena de Notificación) al instalarse en memoria y la función AX=5 (Desconectar de la Cadena de Notificación) al ser desinstalados.

202 EL UNIVERSO DIGITAL DEL IBM PC. AT Y PS/2 0006h destruir sesión BX = Identificación de sesión 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 .

0 sorprendió a más de uno en su día. . Sin embargo.EXE del MS-DOS 5. un controlador de dispositivo EXE puede superar el límite de los 64 Kb. mostrada a continuación: CABECERA DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES offset 0 offset 4 offset 6 offset 8 offset 10 offset 11 DD DW DW DW DB DB 0FFFFFFFFh 0 estrategia interrupcion 1 7 DUP (0) .0. Además. sin complicar su uso por parte del usuario con otro programa adicional. del sistema) o bien orientados a bloques. bloques de cierta longitud en bytes (sectores). No fue una ocurrencia muy feliz elegir precisamente ese valor inicial como obligatorio para la copia en disco. permiten añadir nuevos componentes al ordenador sin necesidad de rediseñar el sistema operativo. doble palabra de valor -1 palabra de atributos (ejemplo arbitrario) desplazamiento de la rutina de estrategia desplazamiento de la rutina de interrupción número de discos definidos: 1 por ejemplo 7 bytes no usados Al principio. . Los controladores de dispositivo han sido tradicionalmente programas binarios puros. . . dado que la instrucción de código de operación 0FFFFh es ilegal y bloquea la CPU si es ejecutada. concebidos inicialmente para gestionar periféricos y dispositivos especiales.2. en cambio. Los controladores de dispositivo pueden ser de dos tipos: orientados a caracteres (tales como los dispositivos NUL. Todo controlador de dispositivo de bloques comienza con una cabecera estándar. El EMM386. etc. ya que llamaba la atención observar cómo se podía cargar con DEVICE: lo cierto es que esto es factible incluso desde el DOS 2. Por cierto.1. los controladores de dispositivo de bloques procesan. no hay razón para que ello sea así ya que un controlador de dispositivo puede estar incluido dentro de un programa EXE. el RAMDRIVE. pero ha sido mantenido casi en secreto.CONTROLADORES DE DISPOSITIVOS 203 Capítulo XI: CONTROLADORES DE DISPOSITIVO 11. aunque renombrados a SYS (aviso: no recomiendo a nadie ponerles extensión EXE y ejecutarlos después). como su propio nombre indica. PRN. ya que el DOS se encarga de relocalizar las referencias absolutas a segmentos como en cualquier programa EXE ordinario. AUX. a los que se les colocaba una extensión SYS. con la condición de que el código del controlador sea el primer segmento de dicho programa.INTRODUCCIÓN.ENCABEZAMIENTO Y PALABRA DE ATRIBUTOS. similares a los COM aunque ensamblados con un ORG 0. Actualmente es relativamente frecuente encontrar programas de este tipo.SYS de WINDOWS 3. Esto significa que un controlador de dispositivo binario puro no puede ser renombrado a . . .. Los controladores de dispositivo.SYS de DR-DOS 6. 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.1 (no el de MS-DOS 5. 11.0) y el VDISK. La diferencia fundamental entre ambos tipos de controladores es que los primeros reciben o envían la información carácter a carácter. La ventaja de un controlador de dispositivo de tipo EXE es que puede ser ejecutado desde el DOS para modificar sus condiciones de operación. formando una cadena.0 son realmente programas EXE. Los controladores de dispositivo (device drivers en inglés) son programas añadidos al núcleo del sistema operativo.0 (pese a lo que pueda indicar algún libro). aparecidos con el DOS 2. constituyendo las conocidas unidades de disco.

Al parecer. aparecen los offsets a las rutinas de estrategia e interrupción. La palabra de atributos del controlador de dispositivo de caracteres también cambia respecto al de bloques. no deberían utilizarse los que crean ciertos programas (como el EMMXXXX0 del controlador EMS. 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 *. únicas de las que consta el controlador.). . Conviene decir aquí que muchos de los controladores de dispositivo de caracteres instalados en el ordenador no lo son tal realmente. En general.SYS del DOS 3. la cabecera del controlador de dispositivo cambia ligeramente para indicar cuál es el nombre del dispositivo: CABECERA DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES offset 0 offset 4 offset 6 offset 8 offset 10 DD DW DW DW DB 0FFFFFFFFh 8000h estrategia interrupcion "AUX " . más de 32 Mb). el DRIVER. además de los nombres de los dispositivos del sistema.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. bit 0: reservado 15: 14: 13: 12: 11: 10: 9: En la palabra de atributos. . tras esta doble palabra viene una palabra de atributos. el bit 15 indicaba si el dispositivo es de bloques o caracteres: en este último caso. un byte indica cuántas nuevas unidades de disco se definen y detrás hay 7 bytes reservados -más bien no utilizados-.SYS del DOS 3. El DRIVER. PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES bit bit bit bit bit bit bit borrado para indicar dispositivo de bloques activo si se soporta IOCTL activo para indicar disco de formato no-IBM reservado en DOS 3+ activo si soportadas órdenes OPEN/CLOSE y REMOVE reservados no documentado. por ende.X y anteriores) Este dispositivo poco conocido es útil para consultar o establecer en cualquier momento la hora del sistema con la siguiente secuencia de 6 bytes: DW dias_transcurridos_desde_1980 DB minutos DB horas DB centésimas de segundo DB segundos activo si es el dispositivo NUL activo si es el dispositivo de salida estándar activo si es el dispositivo de entrada estándar bit 2: bit 1: bit 1: .3 lo emplea para indicar que no está permitida una E/S directa en las unidades «nuevas» bit 8: no documentado. doble palabra de valor -1 palabra de atributos (ejemplo arbitrario) desplazamiento de la rutina de estrategia desplazamiento de la rutina de interrupción nombre del dispositivo (8 caracteres) Aunque en el ejemplo aparece AUX. ello es un ejemplo de lo que no se debe hacer. Tras ello. Por último. etc. con lo que se sobrescribe y anula el puerto serie original). AT Y PS/2 COM y ejecutado también desde el DOS (habrá de ser necesariamente de tipo EXE). a no ser que sea lo que realmente se desea hacer (se está creando un dispositivo AUX que ya existe.2+ activo si soportada orden 13h (GENERIC IOCTL) reservado activo si el dispositivo es «especial» y utiliza la INT 29h (llamada por el DOS para imprimir e carácter ubicado en AL).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 más de 65536 sectores y. pero sustancialmente: PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES bit 15: bit 14: bit 13: bit 12: bit 11: bits 10-8: bit 7: bit 6: bit 5: bit 4: bit 3: activo para indicar dispositivo de caracteres activo si se soporta IOCTL en DOS 3+ activo si se soporta orden 10h (OUTPUT UNTIL BUSY) reservado en DOS 3+ activo si soportadas órdenes OPEN/CLOSE y REMOVE) reservados en DOS 5+ activo si soportada orden 19h (CHECK GENERIC IOCTL SUPPORT) en DOS 3. cuyo bit más significativo está borrado en los dispositivos de bloques para diferenciarlos de los dispositivos de caracteres. . activo si es el dispositivo CLOCK$ (CLOCK en MS-DOS 2. . A continuación.204 EL UNIVERSO DIGITAL DEL IBM PC.* EMMXXXX0: con el controlador de memoria expandida instalado) aunque algunos implementan ciertas funciones vía IOCTL.

y se recogen ciertos resultados. en vez de con IRET. casi todos los controladores de dispositivo se comportan de esta manera.2+) GENERIC IOCTL 14h-16h no usadas 17h (DOS 3. Hay que recordar que el paso del MS-DOS 1. de manera secuencial.ES .2+) GET LOGICAL DEVICE 18h (DOS 3. La razón de la existencia separada de las rutinas de estrategia e interrupción se inspira en la filosofía de diseño del UNIX y su arquitectura multitarea. los primeros 13 bytes son: CABECERA DE PETICIÓN DE SOLICITUD (13 PRIMEROS BYTES) COMÚN A TODAS LAS ÓRDENES offset offset offset offset offset offset 0 1 2 3 5 9 DB DB DB DW DD DD longitud_bloque num_disco orden palabra_estado pun_dos encadenamiento . longitud total de la cabecera disco implicado (sólo en disp.CONTROLADORES DE DISPOSITIVOS 205 11. ya que son prácticamente idénticas en todos los controladores de dispositivo: RUTINA DE ESTRATEGIA estrategia PROC MOV MOV RET ENDP FAR CS:pcab_pet_desp. bloques) orden solicitada por el sistema donde devolver la palabra de estado apuntador usado por el DOS usado por el DOS para encadenar En general. es un área de datos que el DOS utiliza para comunicarse con el controlador de dispositivo.0 al 2. . por lo que nunca podrá ser invocada por una interrupción hardware. . la rutina de interrupción suele multiplicar por dos el número de la orden (almacenada en el offset 2 de la cabecera de petición).4. para así acceder indexadamente a una tabla de palabras que contiene los desplazamientos a las rutinas que procesan las diversas órdenes: aunque esto no ha de ser necesariamente así. .3.RUTINAS DE ESTRATEGIA E INTERRUPCIÓN. . NO WAIT (dispositivos de caracteres) 06h INPUT STATUS (dispositivos de caracteres) 07h INPUT FLUSH (dispositivos de caracteres) 08h OUTPUT 09h OUTPUT WITH VERIFY 0Ah OUTPUT STATUS (dispositivos de caracteres) 0Bh OUTPUT FLUSH (dispositivos de caracteres) 0Ch IOCTL OUTPUT 0Dh (DOS 3+) DEVICE OPEN 0Eh (DOS 3+) DEVICE CLOSE 0Fh (DOS 3+) REMOVABLE MEDIA (dispositivos de bloques) 10h (DOS 3+) OUTPUT UNTIL BUSY (dispositivos de caracteres) 11h-12h no usada 13h (DOS 3. de tipo FAR estrategia pcab_peticion pcab_pet_desp pcab_pet_segm LABEL DWORD DW 0 DW 0 ¿Para qué sirve la cabecera de petición de solicitud?: sencillamente. aunque para el DOS hubiera sido suficiente una sola rutina.2+) SET LOGICAL DEVICE 19h (DOS 5. la rutina de estrategia tiene como única misión recoger la dirección de la cabecera de petición de solicitud que el DOS envía al driver. De hecho.0 supuso una emigración de la filosofía del CP/M a la del UNIX. No es realmente una rutina de interrupción ya que retorna con RETF. Las 3 líneas de código siguientes constituyen una rutina de estrategia. Por medio de este área se envían las órdenes y los parámetros que el dispositivo soporta. además de preservar todos los registros que va a alterar para restaurarlos al final. se encarga de consultar la dirección de la cabecera de petición de solicitud que almacenó la rutina de estrategia y comprobar qué le está pidiendo el DOS. Cuando el DOS va a acceder a un dispositivo (debido a una petición de un programa de usuario) ejecuta. . .0+) CHECK GENERIC IOCTL SUPPORT . en ES:BX. 00h INIT 01h MEDIA CHECK (dispositivos de bloque) 02h BUILD BPB (dispositivos de bloque) 03h IOCTL INPUT 04h INPUT 05h NONDESTRUCTIVE INPUT. 11. que son de tipo FAR.ORDENES A SOPORTAR POR EL CONTROLADOR DE DISPOSITIVO.BX CS:pcab_pet_segm. . las rutinas de estrategia e interrupción. La rutina de interrupción del dispositivo. Aunque según la orden a procesar el tamaño de la cabecera de petición de solicitud puede variar.

en general no será preciso implementar todas: de hecho. Aquí sí se pueden emplear libremente las funciones del DOS (en el resto de las órdenes no: el driver es un programa residente más). cuyo formato puede consultarse a continuación. con objeto de que éste se inicialice. En su inicialización el driver decide qué cantidad de memoria se queda residente y puede analizar la línea de comandos del CONFIG. A la salida se indica al DOS la dirección de la tabla de apuntadores a estructuras BPB (esto último sólo en los dispositivos de bloques).Orden 0 o INIT. A la vuelta. y más que de intentar comprender por qué una cosa es de una manera determinada. CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 0 (INIT) offset 0 offset 0Dh offset 0Eh: 13 BYTES: BYTE: DWORD: Ya vistos con anterioridad. puede que parte de la explicación que viene a continuación sobre dichas órdenes sea difícil de entender al lector poco iniciado. lo que no se entienda puede ser pasado por alto ya que probablemente no es estrictamente necesario conocerlo. para no quedar residente basta indicar un offset 0 (el segmento es vital inicializarlo con CS).SYS. crear un dispositivo NUL es tarea realmente sencilla).0. casi ningún controlador necesita soportar todas las órdenes. en las de salida cuando el buffer aún no está lleno. las ordenes no soportadas pueden originar un error o bien ser sencillamente ignoradas (en ese sentido.4. Desde el DOS 3.0 al menos. 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 inválida de la cabecera de petición 06h fallo en el posicionamiento del cabezal 07h medio físico desconocido 08h sector no encontrado 09h impresora sin papel 0Ah error de escritura 0Bh error de lectura 0Ch anomalía general 0Dh reservado 0Eh (CD-ROM) medio físico no disponible 0Fh cambio de disco no permitido La construcción de rutinas de gestión para las diversas órdenes que han de soportarse no es un proceso muy complicado. A la vuelta. número de discos lógicos existentes hasta ese momento ej. . incluso para un disco virtual basta con algunas de las primeras 16. de lo que se trata es de obedecer. ya que . offset 12h: DWORD: offset 16h: BYTE: Esta es la primera de todas las órdenes y se ejecuta siempre una vez cuando el dispositivo es cargado en memoria. bits 7-0: Código de error. el DOS indica dónde comienza la línea de parámetros del CONFIG. A la entrada. esto es siempre así (en un hipotético sistema multitarea. FORMATO DE LA PALABRA DE ESTADO bit 15: Activo si hay error. Todas las órdenes devuelven una palabra de estado al sistema operativo. No hay que olvidar que los controladores de dispositivo respetan unas normas de comportamiento definidas por el fabricante del DOS. bit 8: Activo si el controlador de dispositivo ha acabado de ejecutar la orden. 11. En general. una orden podría ejecutarse en varias ráfagas de CPU). indica el último byte residente con un puntero largo de 32 bits.206 EL UNIVERSO DIGITAL DEL IBM PC. Si el dispositivo no se instala ante algún fallo. En las operaciones de entrada está listo si hay un carácter en el buffer de entrada o si tal buffer no existe. 3 para A: B: y C: (solo en los dispositivos de bloque). Además. Sin embargo.SYS para comprobar los parámetros del usuario. En los dispositivos de bloque se indica también al sistema el número de unidades definidas por el controlador y la dirección de una tabla de punteros a estructuras BPB.0. pese a que está envuelto en una leyenda negra. indicar al DOS el nº de unidades de disco definidas (solo en dispositivos de bloque). como se verá al final en los programas de ejemplo. En general. 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. AT Y PS/2 La tabla anterior resume las órdenes que puede soportar un controlador de dispositivo. Hasta el DOS 5.

0 se recomienda anotar la etiqueta de volumen del disco cuando se ejecuta esta orden para detectar un posible cambio ilegal del mismo. Aviso: tras el nombre/ruta del fichero. En general.sys como la siguiente: DEVICE \DOS\VDISK.Orden 1 o MEDIA CHECK.ES:[BX+12h] tiene como resultado alterar el valor de ES:BX para que ahora apunte a la zona de parámetros. si se ha cambiado el disquete de la disquetera. para construir discos de menos de 65536 sectores solo hace falta completar los primeros campos (solo hasta los relacionados con el DOS 2.aunque quizá en algunas versiones del DOS podría estar indicado el final de la cadena por un salto de línea -ASCII 10. Las versiones más antiguas del DOS (2. ya que es seguro que nadie puede haberlos cambiado. con la misma etiqueta.0 se mejora este asunto con los números de serie. al tratarse de un área de datos del sistema. Los parámetros en la línea de comandos del CONFIG. el DOS sacará la información de sus buffers internos evitando en lo posible un acceso al disco. No se debe modificar la línea de parámetros: además de improcedente puede ser peligroso.x) necesitan que cambie el byte descriptor de soporte para detectar el cambio de disco.4.CONTROLADORES DE DISPOSITIVOS 207 existe una de estas estructuras para cada unidad lógica. Si no ha cambiado. Desde el DOS 3. desde el DOS 4. De nuevo. puede consultarse en el capítulo 7. Por ejemplo. 11. aunque como se observa en el cuadro anterior su dirección se obtiene en el puntero de 32 bits ubicado en el offset 12h de la cabecera de petición de solicitud. Esta orden sólo es preciso implementarla en los dispositivos de bloques. el driver indica el resultado: 0FFh si se ha producido un cambio.en lugar del retorno de carro. el 3.0 o. . CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 1 (MEDIA CHECK) offset 0 offset 13 offset 14 13 BYTES: BYTE: BYTE: Ya vistos con anterioridad. El DOS necesita entonces averiguar las características del nuevo soporte. para una línea de config. como mucho. 11. Por ello.SYS son similares a los de un programa ordinario.Orden 2 o BUILD BPB.). el nombre y ruta del programa están separados de sus parámetros por uno o más delimitadores (espacios en blanco o tabuladores -ASCII 9-). En ella. el DOS invalida y libera todos los buffers en memoria relacionados con el mismo.4.2. . En caso de que el soporte haya cambiado. pero pocos drivers se molestan en comprobarlos. . el mismo campo donde se obtiene la dirección de los parámetros ha de ser empleado para devolver al DOS la dirección de los punteros a los BPB: el sentido común indica que primero debe leerse la dirección de los parámetros y después puede modificarse dicho campo. aunque lo cierto es que este método es bastante ineficiente (discos sin etiquetar. 0 si se desconoce (lo que equivale al primer caso) y 1 si no ha habido cambio. para lo que pide al driver que le suministre un BPB con información.1. A la entrada. Es ejecutada por el sistema si la respuesta a la orden MEDIA CHECK es afirmativa (cambio de soporte). no requieren que el byte descriptor cambie para aceptar el cambio y confían en la información que suministra MEDIA CHECK. sirve para que el sistema pregunte al controlador si se ha producido un cambio en el soporte: por ejemplo. En los dispositivos de bloque.. aparece todo lo que había después del ’=’ o el ’ ’ que seguía al DEVICE. las versiones más antiguas del DOS colocan un byte a cero. esta orden solo ha de implementarse en los dispositivos de bloques. la instrucción LES BX. si ES:BX apunta a dicha cabecera. El BPB (BIOS Parameter Block) es una estructura que contiene información sobre las unidades. Las versiones actuales. los discos fijos y virtuales suelen responder que no. en los disquetes suele responderse que sí (ante la duda). lógicamente-.SYS 128’ -sin incluir las comillas..0). Aunque el BPB ha sido ampliado en las últimas versiones del DOS. habida cuenta del caos de bytes de identificación comunes para disquetes diferentes.SYS 128 el contenido de la zona de parámetros sería ’\DOS\VDISK. al final se encuentra el código de retorno de carro -ASCII 13. Como se puede observar. el DOS indica el descriptor del soporte (solo en dispositivos de bloque) A la vuelta.

se las apaña para leer el primer sector de la FAT y se lo pasa al driver. solo debe considerarse este campo si la longitud de la cabecera de petición (byte 0) es mayor de 1Ah. por sus propios medios. de lo contrario el buffer está vacío y puede emplearse para otro propósito. por ejemplo en un controlador para un soporte físico que necesite detectar el medio introducido para poder acceder al mismo. 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. el DOS indica el descriptor del soporte. . dirección del área de transferencia a memoria En entrada. Es frecuente que no esté soportada en los dispositivos más simples. Puede ser soportada tanto por los dispositivos de caracteres como por los de bloque. ha de apañárselas para detectar el tipo de disco introducido en la unidad correspondiente: por ejemplo. . Esta orden es una de las más importantes. Por ello. Por ejemplo en una disquetera: al introducir un nuevo disco de densidad diferente al anterior. espera un tiempo razonable a que le lleguen antes de "fallar"). AT Y PS/2 En los discos de tipo IBM. (solo en dispositivos de bloque) A la entrada. el intento por parte del DOS de leer la FAT en los discos tipo IBM provocaría un fallo (si esto no sucede con el controlador del propio sistema para las disqueteras es porque la BIOS suplanta al DOS. En los discos que no son de tipo IBM es el driver quien. En cualquier caso. A la entrada. el driver devuelve aquí la dirección del BPB del nuevo disco (no la de ninguna tabla de punteros). 11. los almacenará en un buffer de entrada a medida que le van llegando del periférico y los enviará en respuesta a esta orden (si no los tiene. número de sectores (dispositivos de bloques) o bytes (dispositivos de caracteres) a transferir. por medio de esta función. el DOS indica el descriptor del soporte.Orden 4 o INPUT. leyendo el sector de arranque.208 EL UNIVERSO DIGITAL DEL IBM PC.4. A la salida.3. los programas de usuario solicitan información al controlador (subfunciones 2 y 4 de la función 44h del DOS) sin tener que emplear el canal normal por el que se envían los datos. A la vuelta. realizando quizá algunas tareas más de las que debería tener estrictamente encomendadas al detectar un cambio de disco). sectores/bytes realmente transferidos. el DOS intenta cooperar con el controlador de dispositivo en los cambios de disco. (solo en dispositivos de bloque) En entrada. el sistema solo la utiliza si así se le indicó en la palabra de atributos del dispositivo (bit 14). CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 2 (BUILD BPB) offset 0 offset 13 offset 14 13 BYTES: BYTE: DWORD: Ya vistos con anterioridad. los más comunes. Si el dispositivo es de caracteres. Número de sector de comienzo en discos de más de 32Mb (ver bit 1 de palabra de atributos). que así tiene más fácil la tarea de detectar el tipo de disco y suministrar al DOS el BPB adecuado. offset 18 DWORD: 11. no se envían bytes sino sectores completos. El IOCTL es un mecanismo genérico de comunicación de las aplicaciones con el controlador de dispositivo.0 se devuelve al DOS un puntero a la etiqueta de volumen del disco en el caso de un error 0Fh. .Orden 3 o IOCTL INPUT. Si el dispositivo es de bloque. ya que el primer byte de la FAT contiene el tipo de disco (byte descriptor de medio). Número de sector de comienzo (solo en los dispositivos de bloques y de menos de 32 Mb) En las órdenes 4 y 8 y desde el DOS 3.4. Sirve para que el sistema lea los datos almacenados en el dispositivo. La cabecera de petición de solicitud de esta orden y de varias de las que veremos a continuación es la siguiente: CABECERA DE PETICIÓN DE SOLICITUD PARA LAS ÓRDENES: 3 (IOCTL INPUT) 4 (INPUT) 8 (OUTPUT) 9 (OUTPUT VERIFY) 10h (OUTPUT UNTIL BUSY) offset 0 offset 13 offset 14 offset 18 offset 20 offset 22 offset 26 13 BYTES: BYTE: DWORD: WORD: WORD: DWORD: DWORD: Ya vistos con anterioridad.4. En algunos casos puede resultar útil indicar que el disco es de tipo no IBM.

Resulta totalmente absurdo implementarla en un disco virtual (el 11% de la memoria del sistema podría estar ya destinada a detectar un fallo en cualquier byte de la misma. lo más normal es que el DOS solicite transferir sólo 1 en cada vez. El bit 9 de la palabra de estado devuelta indica.Orden 5 o NONDESTRUCTIVE INPUT. Es análoga a OUTPUT. si está activo. 11.11.4.12. . y además es igual de probable el error durante la escritura que durante la verificación) por lo que en este caso debe comportarse igual que la orden anterior.Orden 0Ah u OUTPUT STATUS. 11. que el dispositivo está ocupado (sin caracteres). con la salvedad de que ni siquiera se envía el siguiente carácter del buffer de entrada.Orden 0Ch o IOCTL OUTPUT. Su misión es análoga. sin tener que mezclarla con los datos que se le . vaciándose el buffer de salida en lugar de el de entrada. Es otra de las órdenes más importantes. La principal utilidad de esto es que el sistema puede saber si el dispositivo tiene ya un nuevo carácter disponible antes de llamarle. Sólo sirve para determinar el estado del controlador. Solo debe ser soportada por los dispositivos de caracteres.4. aunque en teoría podría solicitar cualquier cantidad. es equivalente a INPUT FLUSH. vacía el buffer del dispositivo. También exclusiva de dispositivos de caracteres.4. Por ello. con la correspondiente comprobación de que lo escrito es correcto al comparar ambos buffers. . pero relacionada con el buffer de salida en vez del buffer de entrada. . conviene tomarla en serio. En los discos físicos de verdad. tras escribir. 11. 11.CONTROLADORES DE DISPOSITIVOS 209 En los dispositivos de caracteres.Orden 8 u OUTPUT.Orden 9 u OUTPUT VERIFY. .4. 11.5. 11. bien sean caracteres o sectores completos. Permite al sistema enviar datos al dispositivo. Es análoga a INPUT.7. . como ésta. análoga a INPUT pero actuando al revés.Orden 0Bh u OUTPUT FLUSH. propia de los dispositivos de caracteres. .4.10. una lectura inmediata hacia un buffer auxiliar. .4. Es complementaria de la orden IOCTL INPUT: se pueden enviar cadenas de información a través de la función 44h del DOS (subfunciones 3 y 5). En el caso de los dispositivos de bloque esta orden es ejecutada por el DOS cuando se accede a disco vía INT 25h/26h. Lo que éste suele hacer es sencillamente igualar los punteros al buffer de entrada interno (el puntero al último dato recibido del periférico y el puntero al próximo carácter a enviar al sistema cuando se lo pida). tras utilizar esta orden será preciso emplear después la 4 para leer realmente el carácter.Orden 6 o INPUT STATUS.6. según el tipo de dispositivo. 11. con la salvedad de que el dispositivo efectúa. 11.8. Solo disponible en dispositivos de caracteres. indagando si tiene caracteres disponibles o no.4. sin embargo. con la diferencia de que no se avanza el puntero interno al buffer de entrada de datos tras leer el carácter.9.Orden 7 o INPUT FLUSH. . Es similar a INPUT STATUS y. Es totalmente análoga a NONDESTRUCTIVE INPUT. Es útil para lograr una comunicación de ciertas informaciones con el controlador a través de otro canal.4. para evitar que éste se quede parado hasta que le llegue.

0.Orden 0Dh o DEVICE OPEN.Orden 10h u OUTPUT UNTIL BUSY. 15h y 16h no han sido aún definidas. disponible desde el DOS 3.0. se envían todos los que sean posibles (de la cantidad solicitada) hasta que el periférico esté ocupado: entonces se retorna.0 y superior. 11.4.16. sirve para enviar más de un carácter al periférico.Orden 0Eh o DEVICE CLOSE.13.Orden 0Fh o REMOVABLE MEDIA. Solo implementada desde el DOS 3. También en el DOS 3. El programa TURBODSK que veremos más adelante utiliza la cadena de controladores de dispositivo para buscarse a sí mismo en memoria e identificar todas las posibles unidades . las ordenes 80h y superiores están destinadas a la comunicación con los dispositivos CD-ROM. 11. para permitir por ejemplo un posible cambio de disco.2 permite un mecanismo más sofisticado de comunicación IOCTL. saltando de una a la otra múltiples veces. Algunos gestores de memoria. Por tanto. quedan de tal manera que los últimos cargados apuntan a los predecesores. se reinicializan los buffers internos. . indica que el dispositivo o un fichero almacenado en él ha sido cerrado.17.4. Esta función es útil para acelerar el proceso de salida.0 añade una nueva: la 19h (CHECK GENERIC IOCTL SUPPORT). indica al sistema si el dispositivo es removible o no..LA CADENA DE CONTROLADORES DE DISPOSITIVO INSTALADOS. Esto quiere decir que para acceder al código o datos internos del dispositivo conviene tomar precauciones. averiguando la dirección del dispositivo NUL y siguiendo la cadena de apuntadores obtenida en los primeros 4 bytes de cada uno (en la forma segmento:offset) se puede recorrer la lista de dispositivos (ya sean de caracteres o de bloque) en orden inverso al que fueron instalados en memoria.4. 11.0 y superior. ni siquiera en el DOS 5. .2 han sido definidas las órdenes 17h (GET LOGICAL DEVICE) y 18h (SET LOGICAL DEVICE). Solo es admitida en dispositivos de caracteres y a partir del DOS 3. indica que el dispositivo o un fichero almacenado en él ha sido abierto.Otras órdenes. si los hay. .4. 12h.210 EL UNIVERSO DIGITAL DEL IBM PC. aunque luego instalen el mismo en memoria superior. El DOS 5. 11.SYS (en lugar del DEVICEHIGH del DOS) colocan la cadena de dispositivos en memoria convencional. Al final. Los controladores de dispositivo forman una cadena en la memoria. . .5. de cara a averiguar la dirección donde realmente reside. El controlador se limita a incrementar un contador.15. La orden 13h o GENERIC IOCTL. una lista conectada por los 4 primeros bytes de la cabecera utilizados a modo de puntero. AT Y PS/2 envían. el sistema operativo apunta el dispositivo NUL al último dispositivo instalado. La lista de controladores de dispositivo puede pasar por la memoria convencional o por la superior. Solo implementada desde el DOS 3. Por cierto. . instalados como falsos controladores de dispositivo de caracteres soportan ciertos comandos vía IOCTL. Algunos programas residentes. El controlador se limita a decrementar un contador: si éste llega a cero..0 y superior. evitando a las aplicaciones acceder directamente a la zona de memoria donde está instalado el controlador para modificar sus variables. Solo implementada también desde el DOS 3. Aquí no se considera un error no haber podido transferir todo.4. como QEMM cuando se utiliza LOADHI. apoyándose en los resultados de las dos órdenes anteriores. A medida que se van instalando en memoria. Las órdenes 11h. 11.14. En concreto. Esta orden y las dos siguientes no han de estar necesariamente soportadas. El último de ellos estará apuntando a XXXX:FFFF. colocándose NUL al final de la cadena. 11. 14h.

antes del listado del programa. A continuación. /* contar discos */ siguiente = (unsigned long huge *) *siguiente. con objeto de indicar el consumo de memoria de los mismos y el nombre del fichero ejecutable.0 Utilidad para listar los controladores de dispositivo instalados. r. 0116:0048 E279:0000 E22B:0000 E1A7:0000 E103:0000 E0E6:0000 E0BE:0000 E013:0000 E003:0000 DFD8:0000 DD90:0000 DD85:0000 DD7C:0000 0316:0000 D803:0000 0255:003F 0255:0000 0070:0023 0070:0035 0070:0047 0070:0059 0070:006B 0070:007B 0070:008D 0070:009F 0070:00B8 0070:00CA 0070:00DC 0070:00EE Carácter Bloque Bloque Bloque Bloque Bloque Bloque Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Carácter Bloque Carácter Carácter Carácter Carácter Carácter Carácter Carácter NUL Unidad I: Unidad H: Unidad G: Unidad F: Unidad E: Unidad D: CON ALTDUP$ KEYBSP50 gmouse ACCESOS$ &FDREAD2 KEYBUF21 SMARTAAR QEMM386$ EMMXXXX0 CON AUX PRN CLOCK$ Unidades A:-C: COM1 LPT1 LPT2 LPT3 COM2 COM3 COM4 0DC6 00CB 00CB 0086 0086 005A 005A 0078 00C2 0012 0012 0013 0012 0012 00A2 0051 0051 06F5 06F5 06F5 06F5 06F5 06F5 06F5 06F5 06F5 06F5 06F5 06F5 0DCC 00D6 00D6 0091 0091 0065 0065 0083 00CD 0018 0021 001A 0012 0018 00AD 007D 0064 0700 0721 0705 0739 073E 0721 070C 0713 071A 0727 072D 0733 (c) 1992 CiriSOFT Atributo Programa Tamaño 8004 0800 0800 0800 0800 0800 0800 8013 8000 8000 8000 8000 8000 8000 C800 C000 C000 8013 8000 A0C0 8008 08C2 8000 A0C0 A0C0 A0C0 8000 8000 8000 RAMDRIVE RAMDRIVE VDISK VDISK TDSK TDSK ZANSI ALTDUP KEYBSP GMOUSE ACCESOS FDREAD KEYBUFF SMARTDRV QEMM386 1184 1232 2096 2608 448 624 2720 240 672 9328 160 128 160 22400 3072 // // DRV 1. /* DOS 3.disp[i]). } siguiente=MK_FP(r. /* "Get List of Lists" */ while (FP_OFF(siguiente)!=0xffff) { disp = (unsigned char huge *) siguiente. dosver. printf("\n DRV 1. if ((dosver & 0xFF00)==0x200) i=0x17. Adicionalmente. /* otra versión */ r. DRV. disp[8] | (disp[9]<<8). 28h para la 3. con MS-DOS 4. int i. for (i=1.C accede a los bloques de control de memoria que preceden a los dispositivos que están ubicados en un offset 0 respecto al segmento. i<18. printf(" \n"). disp[6] | (disp[7]<<8). Interr. else printf(" "). Por desgracia. printf(" "). } printf(" \n "). if (disp[5] & 0x80) { printf("Carácter "). Ese offset es 17h para las versiones 2. disco--). disco=’A’-1. printf(" %6u ". aunque solo ligeramente. } else { printf("Bloque ").r_ax >> 8). por supuesto.0X */ else i=0x22.disp[i]).0 no se informa correctamente del nombre. /* DOS 2.XX */ else if ((dosver>0x2FF) && (dosver<0x30A)) i=0x28. disco-=disp[10].(disp[-13] | (disp [-12] << 8)) << 4).r_bx+i). } siguiente=MK_FP(r.r_ax=0x5200. unsigned char huge *disp.0 LISTA DE DISPOSITIVOS DEL SISTEMA Dirección Tipo Nombre Estrat. } } printf(" %04X %04X %04X ".0X y 22h para todas las demás. i<0.r_ax << 8) | (r. DRV 1. la manera de obtener la dirección del dispositivo NUL varía de unas versiones del DOS a otras. i++) printf(" "). } else printf(" "). if (!(disp[5] & 0x80)) disco+=disp[10]. #include <dos. informando de ellos. i++) printf("%c". printf(" ").0 ni. void main() { r.0 ó posterior. r. habidas y por haber. disco).0 (por supuesto.disco-disp[10]+1. while (FP_OFF(siguiente)!=0xffff) { disp = (unsigned char huge *) siguiente. disco.r_bx+i). for (i=10. excepto en las versiones más antiguas del DOS. disp[4] | (disp[5]<<8)).h> #include <stdio. La utilidad DRV. ni tampoco del tamaño (excepto si el dispositivo está instalado en memoria superior). no un puntero al mismo). Hay que utilizar la función indocumentada Get List of Lists (servicio 52h del DOS) e interpretar la información que devuelve: En ES:BX más un cierto offset comienza la cabecera del dispositivo NUL (el propio dispositivo. else { printf("Unidades %c:-%c:". no recomiendo a nadie instalar tantos discos virtuales). if (disp[10]==1) printf("Unidad %c: ". i++) if (disp[i]>=’ ’) printf("%c". &r). /* obtener versión del DOS */ dosver=(r. Atributo Programa Tamaño \n"). printf(" Dirección Tipo Nombre Estrat. siguiente = (unsigned long huge *) *siguiente.r_es. se muestra un ejemplo de salida del mismo bajo MS-DOS 5.X del DOS. intr (0x21. FP_SEG(disp). if ((!FP_OFF(disp)) && (dosver>0x31E)) { for (i=-8. Interr.CONTROLADORES DE DISPOSITIVOS 211 que controla. FP_OFF(disp)).r_ax=0x3000.h> struct REGPACK r. . Con DR-DOS 5. &r).0 LISTA DE DISPOSITIVOS DEL SISTEMA (c) 1992 CiriSOFT \n").C listada más abajo recorre los dispositivos instalados. unsigned long huge *siguiente. printf(" \n %04X:%04X ".r_es. no hay problemas sin embargo con DR-DOS 6. intr (0x21. i<78.

0. El controlador propuesto de ejemplo crea un dispositivo HEX$ que imprime en pantalla y en hexadecimal todo lo que recibe. Para imprimir se utiliza la INT 29h del DOS (fast console OUTPUT). en este controlador que no depende del hardware típico de entrada/salida. De hecho. son innecesarias. más recomendable que llamar a un servicio del sistema operativo (que a fin de cuentas va a parar a esta interrupción). AT Y PS/2 11. el hecho de realizar un volcado hexadecimal complica bastante el asunto. sin realizar verificación alguna. según sea el caso. Este driver de ejemplo sólo consume 464 bytes de memoria bajo MS-DOS 5. la opción /B sirve para que la salida no se detenga ante el ^Z. las únicas órdenes realmente soportadas por el dispositivo son. S ólo sirve para p robar. Si se intenta leer desde él devuelve una condición de error (por ejemplo. entre otras razones porque esos programas. Como se puede verificar observando el listado. La orden Close sirve para detectar el final de la operación: ante ella se escriben los espacios necesarios y se vuelcan los códigos ASCII acumulados hasta el momento (entre 0 y 15) que restasen por ser imprimidos. se imprimen de nuevo pero en ASCII (sustituyendo por puntos los códigos de control). 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. Sin embargo.. Además. además de imprimir a poca velocidad. WRITE VERIFY es idéntica a WRITE. aunque sí algo más que llamar al DOS). La operación de impresión en pantalla se supone siempre exitosa. 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 versión del intérprete de comandos). las órdenes WRITE y WRITE VERIFY. Tras ensamblarlo y linkarlo hay que aplicar EXE2BIN para pasarlo de EXE a SYS (TLINK /t sólo opera cuando hay un ORG 100h). procesando en un bucle todos los que se le indiquen. Es preciso implementar la orden Open para detectar el inicio de la transferencia. inicializando a cero el contador de offset relativo de la izquierda.EXT HEX$. Por emplear Open y Close este controlador de dispositivo necesita DOS 3. Aunque se supone que el DOS va a enviar los caracteres de uno en uno. imprimiendo en pantalla lo que recibe. también se eliminarían todas las demás subrutinas de apoyo. con las mismas limitaciones.65 20 70 61 72 61 20 70 0D Este es un fiche ro de pruebas. Close o Remove. Como el proceso de escritura en pantalla se supone siempre con éxito.bin > hex$ 00000000 00000010 00000020 00000030 45 72 A2 72 73 6F 6C 6F 74 20 6F 62 65 64 20 61 20 65 73 72 65 20 69 2E 73 70 72 0A 20 . desde los programas normales no es recomendable utilizar la INT 29h.75 6E 20 66 69 63 68 65 72 . En la instalación se comprueba la versión del DOS.75 65 62 61 73 2E 20 53 76 .212 EL UNIVERSO DIGITAL DEL IBM PC. sino al resto. la mayor parte de la complejidad del listado no se debe al controlador de dispositivo. el dispositivo se toma la molestia de prever que esto pueda no ser así. Sin embargo. Utilizando COPY en vez de TYPE. Todas las demás. aunque ello no tendría utilidad alguna. Las órdenes no soportadas pueden ser ignoradas o bien desembocar en un error. aparte de OPEN. sería un programa mucho más simple si se limitara a imprimir los caracteres que recibe. las órdenes Open. En principio. para cerciorarse de la presencia de un 3.TXT). Por supuesto. CLOSE y REMOVE.0 o superior.0 o superior. El dispositivo HEX$ sólo actúa en salida. Para empezar. al realizar COPY HEX$ FICH. el programa se instala en el CONFIG. No hay que olvidar que los controladores de dispositivo son también programas residentes a todos los efectos. .6. por ello el dispositivo no modifica la variable que indica el número de caracteres a procesar: al devolverla precisamente como estaba al principio indica que se han procesado sin problemas todos los solicitados. .SYS.SYS con una orden del tipo DEVICE=HEX. Los caracteres se imprimen unos tras otros en hexadecimal (con un guión separador tras el octavo) y se van almacenando en un buffer hasta completar 16: entonces.EJEMPLO DE CONTROLADOR DE DISPOSITIVO DE CARACTERES. en un hipotético dispositivo que simplemente sacara por pantalla lo que recibe están de más. El listado hexadecimal que se obtiene es similar al siguiente: C:\WP51\TEXTOS>type prueba. la rutina que procesa los caracteres (procesa_AL) se limitaría a imprimirles.. no soportarían redireccionamiento en la salida (la INT 29h no es precisamente rápida.

BX. en AL AX AH.0Dh print_AL AL.DX. .[BX+2] . CX> DI otro_car retorno_ok . conjunto de órdenes con .p_rutinas SI. byte bajo palabra alta . imprimir en ASCII 16 bytes .AX .AL . puntero a la cabecera de petición WORD .ES> BX. -----------. imprimir desplazamiento AX AL.’ ’ print_AL CX.DS.CX. orden = orden * 2 SI.’ ’ print_AL AX exit_interr: interrupción imprimir_sep imprimir_asc . <RM> PUSH reg ENDM ENDM MACRO RM IRP reg.DI.300h .puntero [BX]. imprimirlo en ASCII . recibidos de 16 en 16.’ ’ print_AL print_AL AX.8102h exit_interr AX. registros (de 16 bits).100h .ini_buffer .DX.ES .CX. byte alto palabra alta .AL print_8hex AH.0 (c) 1992 Ciriaco García de Celis. .8103h .puntero CX.Rutina de interrupción. acabado el buffer: puntero. byte bajo palabra baja .1 .AL CL. -----------.[BX] AL. CX = CX * 3 BX. DS: -> HEX$ .AX> imprimir_desp imprimir_sep orden_ok: . buffer para contener los caracteres .CX .’ ’ asc_ok AL. apilar lista de registros XPOP .ini_buffer .[BX+0Eh] AX. "HEX$ " . indicar . . . . * * * * Controlador de dispositivo para volcado hexadecimal en salida. offset relativo a cero retorno_ok AL.0Ah print_AL .OFFSET med_buffer tam_ok CX. imprimir guión separador AX AL.SI.BP.1 .16 AL. salto de línea . no imprimir los de control . -----------. AL = orden . incluídos los de segmento.BX> [BX+3]. imprimir desplazamiento BX. input_status: output_status: input_flush: output_flush: ioctl_output: retorno_ok: MOV RET media_check: build_bpb: read: read_nowait: ioctl_input: open . inicializa puntero CS:dirl. otro_car: pcab_peticion pcab_pet_desp pcab_pet_segm p_rutinas LABEL DWORD DW 0 DW 0 LABEL DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW EQU DB EQU DB EQU DW DW DW .Macros de propósito general XPUSH MACRO RM IRP reg. bytes a transferir .’ print_AL BX asc_dump AL.CS DS.BX. imprimirlo en ASCII retorno_ok . «controlador ocupado» DW DW DB . INC no afecta al acarreo dirh. estrategia .CS:pcab_peticion AL.[BX+12h] DI. imprimir byte en hexadecimal AL.BP. . ignorar orden . interrupción .dirh AH. .dirl AH. -----------.ES:[DI] <CX. interrupción PROC XPUSH LDS MOV CBW CMP JBE MOV JMP SHL LEA ADD XPUSH CALL XPOP MOV XPOP RET ENDP FAR <AX. espacio separador .OFFSET fin_buffer fin_buff BYTE PTR [BX]. orden correcta AX. -----------.CX CX. . . procesar carácter . encadenamiento con otros drivers palabra de atributo: bit 15 a 1: dispositivo caracteres bit 14 a 0: sin control IOCTL bit 11 a 1: soportados Open/Close y Remove (DOS 3.OFFSET med_buffer no_sep . fichero o canal en proceso AL_procesado: procesa_AL imprimir_desp .0 .AX . AX = orden (AH = 0) AL.4 AL. write procesa_AL no_direcc: no_sep: ini_buffer med_buffer fin_buffer puntero dirl dirh . dos espacios separadores estrategia .BX imprimir_asc . dos espacios al principio . guardar carácter puntero BX.DS> CS:[SI] . para un párrafo CX.’-’ print_AL AL. devolver palabra de estado <ES. DS:HEXSEG DD DW -1 8800h .CS DS.puntero BX. sólo soportada la salida MOV RET PROC MOV MOV MOV JMP AX.OFFSET ini_buffer no_direcc .’ ’ BX limpia_buffer BX.2 .’ ’ print_AL . tabla de rutinas del controlador init media_check build_bpb ioctl_input read read_nowait input_status input_flush write write_verify output_status output_flush ioctl_output open close remove $ 8 DUP (0) $ 8 DUP (0) $ ini_buffer 0 0 . fin de transferencia: AX. byte alto palabra baja .Variables y tablas de datos globales fijas. espacio separador puntero. dirección inicial .CONTROLADORES DE DISPOSITIVOS 213 . no es inicio de «párrafo» imprimir_desp . DI> procesa_AL <DI. tratamiento idéntico asc_dump: asc_ok: AX.AL print_8hex AL. * * * ******************************************************************** open close ENDP .BX CS:pcab_pet_segm. HEXSEG SEGMENT ASSUME CS:HEXSEG. retorno de carro .AX AL.BX imprimir_asc .AX <BX.’ ’ print_AL print_AL AX .CL print_4hex . acabado el buffer: puntero. permitido corromper registros BX. ejecutar orden <DS.OFFSET ini_buffer .AL print_8hex AH. órdenes no soportadas imprimir_asc print_8hex . inicio de transferencia: CS:puntero. .0Fh orden_ok .0 CS:dirh. siempre Ok. ******************************************************************** * * * HEX$ 1. ************ Inicio del área residente.’ ’ esp_escr print_AL escr_esp BX.DS. aún no alzanzada la mitad imprimir_sep dirl. imprimir byte hexad. a partir de DS:BX .Las rutinas que controlan el dispositivo devuelven AX .’. dos espacios de separación AL. <RM> POP reg ENDM ENDM .DI. incrementada dirección print_8hex . Pueden cambiar todos los .SI. estrategia PROC MOV MOV RET ENDP FAR CS:pcab_pet_desp.OFFSET fin_buffer AL_procesado BX.0 . . offset relativo del carácter del . puntero al buffer . CX caracteres faltan AX. otro carácter .Rutina de estrategia. no hay error. desapilar lista de registros . CX. con la palabra de estado.AL print_8hex AX.AX CX.fin_buffer BX.0+) rutina de estrategia rutina de interrupción nombre del dispositivo PROC MOV MOV LEA MOV SUB MOV ADD ADD CMP JA ADD tam_ok: MOV JCXZ escr_esp: CALL LOOP esp_escr: MOV limpia_buffer: CMP JAE MOV INC JMP fin_buff: LEA MOV CALL JMP close ENDP remove remove write write_verify: PROC MOV RET ENDP PROC MOV LES MOV MOV MOV XPUSH CALL XPOP INC LOOP JMP ENDP PROC MOV MOV INC CMP JNE CALL CMP JNE CALL ADD ADC CALL MOV CALL CMP JB LEA MOV CALL RET ENDP PROC PUSH MOV CALL CALL MOV XCHG CALL XCHG CALL MOV XCHG CALL XCHG CALL MOV CALL CALL POP RET ENDP PROC PUSH MOV CALL MOV CALL POP RET ENDP PROC MOV CALL MOV MOV CMP JAE MOV CALL INC LOOP MOV CALL MOV CALL RET ENDP PROC PUSH MOV MOV SHR CALL tipo_drive AX.BX .

se ha preferido crear un disco completo que pueda competir al mismo nivel que los del sistema.’0’ AL. expandida o convencional que ocupaba. sin quedar residente AX. Sin embargo.1) y el VDISK de DR-DOS 6. permitiendo definir con mayor libertad los parámetros e incluyendo uno nuevo (el tamaño de cluster).OFFSET init [BX+10h].7. se le formatea.3 dos_ok WORD PTR [BX+0Eh]."Error: HEX$ necesita DOS 3.CS:[BX] DL. de un segmento de 64 Kb. tampoco implementado ya en RAMDRIVE.00001111b print_4hex AX dos_ok: . en la práctica. por lo que no se deben ahorrar esfuerzos para conseguirla. e incluso incompatible con algunas versiones del DOS.10.0 11.100h . Por el contrario." 13. también es cierto que los usuarios con menos conocimientos pueden dejar a éste que elija los parámetros por ellos. 11.AH AL. este disco ha sido dotado de varias comodidades adicionales no disponibles en los discos del DOS.instalado_txt print AX.mal_dos_txt print AX. Los usuarios más informados. es posible modificar su tamaño una vez que ha sido instalado. después de ser usado. .0 o superior. en cambio. Los usuarios avanzados nunca estuvieron contentos con los discos del sistema que abusaban demasiado del ajuste de parámetros. ha tenido que enfrentarse a la elevada eficiencia de RAMDRIVE.SYS o algunas versiones del EMM386. no tendrán ahora trabas.0 .EJEMPLO DE CONTROLADOR DE DISPOSITIVO DE BLOQUES. comparándolo con los discos virtuales del sistema: RAMDRIVE en representación del MS-DOS 5. imprimir cadena en CS:BX DL.1. con objeto de recoger todas las circunstancias posibles que implica su desarrollo. Al final.CS . indicado área residente BX. se puede desasignar la memoria extendida. Las últimas versiones de este disco ya apuran bastante el rendimiento del sistema. instalación siempre Ok. por lo que superarle sólo ha sido posible con un truco en la memoria expandida/convencional y en máquinas 386DX y superiores: TURBODSK detecta estas CPU y aprovechar su bus de 32 bits para realizar las transferencias de bloques de memoria. .10. con excepción del tamaño del disco. de la que hereda su peculiar nombre.30h 21h . se va a ejecutar WINDOWS a continuación y ya no se necesita el disco virtual.SYS init PROC PUSH MOV INT POP CMP JAE MOV fin_print: print BX AH. Esto último es más que recomendable si.’A’-’9’-1 print_AL AX hex_AL: print_4hex print_AL print_AL init print .’9’ hex_AL AL. Esta asignación dinámica de la memoria significa que. ************ Instalación invocada desde el CONFIG. El disco virtual propuesto no es el clásico minidisco de ejemplo. es factible tener instalado el controlador sin reservar memoria: cuando es preciso utilizar el disco."Dispositivo HEX$ instalado. la única característica que TURBODSK no presenta es el soporte de memoria extendida vía INT 15h de VDISK.10.0 13. . Aunque una elección torpe de parámetros de TURBODSK puede crear un disco prácticamente inútil.CS BX. Otra ventaja es que es mucho más flexible que los discos virtuales que acompañan al sistema operativo. .2 BX 21h BX BX print 29h . A continuación se resumen las características de TURBODSK. sin necesidad de arrancar de nuevo el ordenador. Como puede observarse. Por un lado.DL fin_print AH. imprimir ASCII en AL MOV LEA CALL MOV RET LEA MOV MOV MOV LEA CALL MOV RET ENDP PROC MOV AND JZ MOV PUSH INT POP INC JMP RET ENDP DB DB DB ENDS END [BX+10h]. en AL AX AL.13.0 (aunque se incluye una versión más reciente que viene con WINDOWS 3.DISCO VIRTUAL TURBODSK: CARACTERÍSTICAS. ya que en la actualidad es difícil encontrar máquinas con memoria extendida que no tengan instalada la especificación XMS que implementa HIMEM. imprimir nibble hexad.0. por ejemplo. con mucho. obtener versión del DOS BX AL. anular rutina INIT WORD PTR [BX+0Eh].10. La velocidad es sin duda el factor más importante de un disco virtual. OFFSET 0: terminar instalado_txt mal_dos_txt HEXSEG 13.". AT Y PS/2 print_8hex print_4hex MOV AND CALL POP RET ENDP PROC PUSH ADD CMP JBE ADD CALL POP RET ENDP PROC INT RET ENDP AL.7.retorno_ok CS:p_rutinas. El motivo es simplificar el programa.100h . la pretensión inicial de hacer TURBODSK más rápido que los discos del sistema.AX .214 EL UNIVERSO DIGITAL DEL IBM PC.

salvo timer */ void rest_hw (unsigned long tiempo_transcurrido_con_reloj_parado) . nuevaIRQ0). Los resultados de KBSEC pueden variar espectacularmente en función del fabricante del controlador de memoria o del sistema operativo.Do not run this program with a cache program loaded. vueltas.SYS como EMM386.CONTROLADORES DE DISPOSITIVOS 215 CARACTERÍSTICAS RAMDRIVE VDISK TURBODSK (WINDOWS 3.C listado más abajo. } MAXBUF 64512L /* 63 Kb (no sobrepasar 64 Kb en un acceso) TIEMPO 110L /* 6 segundos * 18. con objeto de no depender aleatoriamente de la velocidad dispar de la memoria y los controladores XMS/EMS en función del segmento que sea utilizado.Utility to calc with high precision the data transfer * * rate (the read data transfer read) in a ramdisk.2 . la cual a su vez desvía con objeto de aumentar la precisión del cálculo. en un 386-25 puede alcanzar una velocidad de transferencia de casi un megabyte. Para evaluar la memoria convencional no estaba instalado ningún controlador de memoria. VDISK 8088-8 MHz: . calculada por KBSEC.h> */ */ */ */ unsigned long ti. * * * * (C) 1992-1995 Ciriaco García de Celis * * * * . KBSEC fuerza el buffer de transferencia a una dirección de memoria determinada.Memoria extendida/XMS: .SYS y para la EMS.2 /* cadencia de interrupciones del temporizador HORA_BIOS MK_FP(0x40. This program has english messages. por ello es exclusivo para la comprobación de discos virtuales y no flexibles. conviene tener en cuenta que un disco fijo con 19 ms de tiempo de acceso e interface IDE.en esa misma máquina. far *cbios.Memoria convencional: 486-25 MHz sin caché externa: . far *pantalla.Memoria convencional: 386-25 MHz (sin caché): .h> <dos. Para hacerse una idea de la potencia de los discos virtuales. 0xfe). int unidad.EXE a la vez (los resultados varían bastante en función de la gestión de memoria del sistema).3 Capacidad máxima: Soporte de memoria convencional: Soporte de memoria EMS: Soporte de memoria extendida INT 15h: Soporte de memoria extendida XMS: Tamaño de sector soportado: Ficheros en directorio raíz: Asignación dinámica de la memoria: Tamaño de cluster definible: Memoria convencional consumida (MS-DOS 5. unsigned segmento. void interrupt (*viejaIRQ0)(). outportb (0x21.Memoria convencional: 286-12 Mhz (sin estados de espera): . con los buffers que establece el DOS por defecto (aunque esto no influye en KBSEC) y con sólo KEYB y DOSKEY instalados. periódica */ /* instalar nueva rutina de control */ /* inhibir todas las int.Memoria convencional: 563 1980 4169 6838 1261 7297 7370 2533 8256 RAMDRIVE 573 4253 4368 17105 8308 6525 10278 7484 8454 TURBODSK 573 4253 4368 17095 14937 14843 10278 9631 11664 /********************************************************************* * * * KBSEC 1. Use Borland C.Memoria expandida EMS: . tamsect. unsigned char far *sbuffer.2 ≈ 110 tics (error < 1%) TM 18.0.h> <conio. /* EOI al controlador de interrupciones */ } void prep_hw (void) { viejaIRQ0=getvect(8). Velocidad del disco bajo MS-DOS 5.h> <stdlib. setvect (8. compile * * it in LARGE memory model with «Test stack overflow» option * * disabled. Dicho programa bloquea todas las interrupciones excepto IRQ 0 (INT 8). 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 rápida según la configuración de su equipo. Debe ser ejecutado sin tener instalado ningún caché.Memoria extendida/XMS: .Memoria extendida/XMS: . /* incrementar nuestro contador de hora */ outportb (0x20. void interrupt nuevaIRQ0 () /* rutina ejecutada cada 55 ms */ { tiempo++. para la memoria XMS estaba instalado sólo HIMEM. static unsigned tiempo. tanto HIMEM. Datos en Kb/segundo. 17 veces menos que la mejor configuración de disco virtual -que además posee un tiempo de acceso prácticamente nulo.1) (DR-DOS 6.0): 32 Mb Sí Sí No Sí 128-1024 4-1024 No No 1184-1232 32 Mb Sí Sí Sí No 128-512 4-512 No No 2096-2608 64 Mb Sí Sí No Sí 32-2048 1-65534 Sí Sí 448-624 Para calcular la velocidad de los discos virtuales se ha utilizado el programa KBSEC.Memoria expandida EMS: .0x20).0) v2. 0x6c) /* variable de hora del BIOS /* preservar vector de int. * * * *********************************************************************/ #include #include #include #include #define #define #define #define <stdio. La fiabilidad de KBSEC está avalada por el hecho de que siempre da exactamente el mismo resultado al ser ejecutado en las mismas condiciones.

A lo largo de la explicación aparecen numerosas alusiones al comportamiento de RAMDRIVE y VDISK. 1. por lo que es un tanto difusa e incompleta. aunque existen por ahí discos virtuales de tipo «no-IBM». clrscr().". esta operación es necesaria para lograr la compatibilidad con algunos gestores de memoria. Se describirán paso a paso todas las peculiaridades del programa. el dispositivo es cargado en memoria superior.7. sbuffer)!=0) { rest_hw(ti-tiempo).0.’a’. TIEMPO/TM). ti=tiempo=vueltas=0. while (ti >= tiempo) if (absread (unidad. Finalmente se optó por seguir la corriente de los discos del DOS. pantalla=MK_FP((peekb(0x40. } sbuffer=MK_FP((segmento+0x100) & 0xff00 | 0x80. } void main(int argc.386 dentro de los segmentos. /* "imprimir" */ rest_hw(TIEMPO).MAXBUF/1024. 0). sbuffer)!=0)) { printf ("\nChoose drive C or above with less than 32 Mb.ENSAMBLANDO TURBODSK. hoy por hoy da lo mismo cómo esté este bit de la palabra de atributos.2. /* 2Kb+n*4Kb */ if (argc<2) { printf("\nChoose the drive to test. como QEMM. 0). tan sólo existe una sutil diferencia en la orden BUILD BPB. los detalles referidos a RAMDRIVE o VDISK se refieren exclusivamente a la versión de los mismos que acompaña a Windows 3. . viejaIRQ0). La primera (cs_tdsk) está destinada a almacenar el valor del registro CS.0f sec. clrscr(). las dos primeras son algo especiales. A continuación vienen las variables de TURBODSK.. *cbios+=tiempo_transcurrido_con_reloj_parado. . printf ("\nError reading the disk. printf ("\nComputing speed (wait %2. respectivamente. } tamsect = sbuffer[11] | (sbuffer[12]<<8). básicamente porque no permiten emplear la directiva . Como nosotros buscaremos a un posible TURBODSK residente siguiendo esa cadena..0*vueltas/(TIEMPO/TM)). Versiones de MASM anteriores a la citada no tienen potencia suficiente. 0L. En principio. exit(255). unidad+’A’. } textmode (C80). para que no emita errores).).\n").\n").0x49)==7 ? 0xB000:0xB800).216 EL UNIVERSO DIGITAL DEL IBM PC. /* autorizar todas las interrupciones */ setvect (8. /* restaurar vector de int.ANÁLISIS DETALLADO DEL LISTADO DE TURBODSK.7. AT Y PS/2 { outportb (0x21. MAXBUF / tamsect. 11. MAXBUF/1024. en cualquier caso.3. periódica */ cbios=HORA_BIOS. gracias a la variable cs_tdsk . Por supuesto. char **argv) { if (allocmem ((unsigned) ((MAXBUF+0x1800) >> 4). 0L.0). la información sobre ambos no ha sido obtenida escribiendo al fabricante para solicitarle el listado fuente. aunque preferiblemente por el segundo. &segmento)!=-1) { printf("\nInsufficient memory. Cuando se utiliza el LOADHI de QEMM. por lo que el listado debería ser comprensible prácticamente al 100%.X (con el parámetro de compatibilidad con versiones anteriores) o por TASM. exit(254). if ((unidad<2) || (absread (unidad.\n". /* esperar pulso del reloj */ ti+=TIEMPO.\n"). que indica dónde reside el disco virtual. LA CABECERA DE TURBODSK El inicio de TURBODSK es el clásico de todos los controladores de dispositivo de bloques. exit (3).\n").1 y a DR-DOS 6. 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. Este programa puede ser perfectamente ensamblado de manera indistinta por MASM 6. pasando la cadena de controladores de dispositivo del DOS por dicha memoria.2: Effective data transfer rate on drive %c:\ %6. exit(1). ti = (long) tamsect * ((sbuffer[0x14] << 8) | sbuffer[0x13]). exit (2). Aunque en principio puede parecer redundante. Evidentemente. } unidad=(argv[1][0] | 0x20) . } else if (!(vueltas++ & 7)) *pantalla++=0xf07. aunque sí suficiente para complementar la explicación de TURBODSK y dar una perspectiva más amplia. la mayoría de las cuales son intuitivas. 0x140). prep_hw(). La palabra de atributos es idéntica a la de VDISK o RAMDRIVE. printf("\nKBSEC 1. Con TASM conviene emplear la opción /m5 para que el ensamblador ejecute todas las pasadas necesarias para optimizar el código al máximo (como mínimo habría que solicitar 2.0f Kb to 32 Mb\n". pero después QEMM se encarga de copiar la cabecera en memoria convencional. Sin embargo. if ((ti < MAXBUF) || (ti > 33554431L)) { } printf ("\nNeeds a disk from %2. while (ti==tiempo). 11. Hay que hacer aquí una breve mención al bit 13 que indica si el dispositivo es de tipo IBM o no: la verdad es que en nuestro caso daría igual elegir un tipo que otro (la diferencia es que en los de tipo IBM el DOS accede a la FAT antes que al propio sector de arranque para verificar el tipo de disco). El listado fuente de TURBODSK consta de un único fichero que ha de ser ensamblado sin demasiados parámetros especiales. El listado completo de TURBODSK puede consultarse al final de este apartado. no siendo necesariamente aplicable a otras anteriores o futuras de dichos programas.0f Kb/sec. con objeto de acceder a él para ulteriores modificaciones de sus condiciones de operación.

algo así como decir que no hay disquete dentro de la disquetera virtual. A la orden IOCTL INPUT. Es bastante parecida a la de los discos del sistema. se indica al DOS que se han transferido 0 sectores. no está de más tomar precauciones para los casos en que no sea así. estas nuevas variables no son relevantes para la interfaz del DOS con el controlador de dispositivo. INPUT FLUSH. Este BPB será modificado cuando se defina el disco. la rutina de estrategia de TURBODSK no merece ningún comentario. 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 límites del disco). de alguna manera. de lo contrario. indicando que se han transferido tantos sectores como fueron solicitados. OPEN y CLOSE no están realmente soportadas. En general. que indica en todo momento el estado del disco. llamamos a la subrutina adecuada que gestiona cada orden. OUTPUT STATUS. la orden MEDIA CHECK es totalmente diferente de la de los discos virtuales del DOS. Las órdenes READ NOWAIT. La segunda variable es id_tdsk y su utilidad es fundamental: sirve para certificar que el controlador de dispositivo es TURBODSK. Sin embargo. las variables más importantes de TURBODSK han sido agrupadas al principio y el autor del programa se ha comprometido a no moverlas en futuras versiones. LAS RUTINAS QUE CONTROLAN EL DISPOSITIVO. Estas subrutinas devuelven en AX la palabra de estado que hay que devolver al sistema.x. indicando además la versión. ha normalizado el uso de la memoria superior. 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 envían. ¡pero cuando además las solicitan!). esto no es relevante para nosotros. QEMM 6. pero sí la de interrupción. por otro. el DOS se podría estrellar al cargar el dispositivo desde el CONFIG). un BPB con información válida (si no fuera correcto. el comportamiento hasta el momento es 100% idéntico al de RAMDRIVE.0 también soporta el DEVICEHIGH del DOS. y valiéndose de la tabla de saltos. Sin embargo. 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 código de unidad no preparada. TURBODSK se limita a terminar como si nada hubiera sucedido. esta variable de la cabecera de petición queda como estaba al principio. la tabla de saltos para las rutinas que controlan el dispositivo. En cualquier otro caso.0 que. se defina éste desde el CONFIG o no (esto último es lo más normal y recomendable). Para empezar. TURBODSK puede haber sido modificado por el usuario. tanto VDISK como RAMDRIVE responden siempre que no. Veremos ahora las principales rutinas de TURBODSK. si el DOS las invoca. por lo que no habría riesgo alguno de provocar un desastre. debido a la asignación dinámica de . En cambio. Por fortuna. Esta variable está ubicada en los primeros 18 bytes de la cabecera. Por fortuna. por lo que al final se realiza esta operación.0: la información adicional de las últimas versiones de los BPB es empleada por las rutinas de más bajo nivel del sistema operativo. IOCTL OUTPUT. Más adelante hay otras variables internas al programa: por un lado. en cuyo caso la totalidad del dispositivo es cargado en memoria superior. sin embargo. Sin embargo. Si algún gestor de memoria extraño realizara la misma maniobra de QEMM y copiase menos de 18 bytes en memoria convencional. estas complicadas argucias de los controladores de memoria tienden a desaparecer desde la aparición del DOS 5. Existe otra variable importante. en cambio. la razón es que los demás no son necesarios ni siquiera para el DOS 5. INPUT STATUS. A la pregunta de ¿ha habido cambio de disco?. QEMM crea además unas falsas rutinas de estrategia e interrupción en memoria convencional que luego llaman a las de la memoria superior. en general. no pasaría nada: TURBODSK sería incapaz de hallarse a sí mismo residente en la memoria superior. que son los que QEMM copia en memoria convencional. sin embargo. devolviendo una palabra de estado 100h que indica función terminada. En el BPB solo se han completado los campos correspondientes al DOS 2. aquellas que se relacionan con la BIOS y el hardware. Esto significa que otros programas podrán detectar la presencia de TURBODSK e influir en sus condiciones de operación. tipo_soporte.CONTROLADORES DE DISPOSITIVOS 217 podemos saber la dirección real del disco virtual. OUTPUT FLUSH.

se salta a la rutina Init_io que se encarga de preparar los registros para la lectura o escritura. RAMDRIVE sí y TURBODSK no quería ser menos. Más o menos mezclada con estas órdenes está la rutina que gestiona la interrupción 19h. Esta interrupción es necesario desviarla para mejorar la convivencia con algunos entornos multitarea basados en el modo virtual del 386. En principio. AT Y PS/2 memoria que soporta. 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. Por tanto. el manual de referencia para programadores de Microsoft dice que el dispositivo solo está obligado a transferir cuanto pueda sin cambiar de segmento. Se controla aquí que el primer y último sector a ser accedido estén dentro del disco: en caso contrario se devuelve un error de sector no encontrado. En TURBODSK se prefirió limitar la transferencia al máximo posible antes de que se desborde el segmento: hay que tener en cuenta que un desbordamiento en el segmento de datos puede llegar a afectar al de código. pero conviene considerarlas en todo caso. el DOS ejecuta la orden BUILD BPB. es deber de la propia tarea. excepto en el caso de trabajar con memoria extendida: al pasar el nº de palabras a bytes. La razón es que si el último sector está dentro del disco ¡como no lo va a estar también el primero!. Este valor es el que se devolverá la primera vez al DOS. si es 1 una lectura. sin embargo. En principio. antes de morir. En estos casos. pero a mi juicio es mejor no poner las cosas todavía peor. indicando que se ha producido un cambio de disco. por lo que se optó por imitar el funcionamiento de RAMDRIVE. 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. Pongamos por ejemplo que un programa pretende leer del disco virtual 48 Kb de datos en la dirección DS:A000h. La mayoría de los discos virtuales no desvían la INT 19h. el devolver la memoria a los correspondientes controladores. pero sí en la convencional y expandida). ya que al llegar al final de un segmento se comienza por el principio del mismo otra vez (esto no es así en el caso de emplear memoria extendida. incluida la memoria expandida o extendida que tuviera a su disposición.. el problema reside en la dirección donde depositar o leer los datos. el RAMDRIVE de Microsoft no considera esta circunstancia. al contrario que la mayoría de los discos virtuales. La interrupción 19h se ejecuta en estos momentos críticos.. unidad de medida del controlador XMS. con la que se le suministra la dirección del nuevo BPB (la misma de siempre.218 EL UNIVERSO DIGITAL DEL IBM PC. Finalmente. tras restaurar el vector previo de INT 19h (para mejorar la compatibilidad) continúa el flujo normal de la INT 19h. En realidad. Las siguientes veces. Otro asunto es controlar el tamaño absoluto del área a transferir: en ningún caso debe rebasar los 64 Kb. La rutina Init_io se ejecuta inmediatamente antes de una lectura o escritura en el disco. Cierto es que un acceso incorrecto a disco es una circunstancia crítica de la que no se puede responsabilizar al mismo. o que no tiene sentido que sean utilizadas. En el momento en que el disco es cambiado. con todo lo que ello implica. Lo cierto es que hay órdenes que casi nunca serán empleadas. pero con un BPB actualizado). consultando el encabezamiento de petición de solicitud para estas órdenes. por lo que si un programa intenta hacer un acceso ilegal de este tipo se corromperá también una parte indeseada del segmento de datos. Sin embargo. aunque no está muy claro si los puede alcanzar o no. permitiendo un máximo de 8000h (exactamente 64 Kb). en el caso de utilizar memoria convencional no se realiza ninguna tarea (RAMDRIVE ejecuta una misteriosa y complicada rutina). por lo que TURBODSK aprovecha para liberar la memoria EMS/XMS ocupada y. En concreto. aunque. RAMDRIVE opera con palabras de 16 bits. motivo por el cual la variable se redefine a 1. el programa que formatea el disco virtual (el propio TURBODSK cuando el usuario define un disco) colocará la variable cambiado a un valor 0FFh. para ahorrar memoria. TURBODSK no comprueba si el primer sector está en el disco. Sin embargo. preparando los registros. La orden REMOVE se limita a devolver una condición de controlador ocupado. Las últimas órdenes que implementa TURBODSK son las de lectura y escritura o escritura con verificación. el 8000h se convierte . También hay que tener en cuenta la histórica leyenda de los 64 Kb. parece que existen entornos no muy eficientes en los que al anular una tarea no se recupera la memoria que ocupaba. No estaba muy claro qué había que hacer con ella. TURBODSK no volverá a cambiar (no hasta otro formateo).

ya que solo queda residente el código necesario. calcular las direcciones. De esta manera. 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 críticas. Este aspecto no es tenido en cuenta por ningún disco virtual de dominio público con soporte de memoria EMS que yo conozca. en todos los casos. En el caso de una operación de disco. Al final de Init_io hay una instrucción para borrar el acarreo. ilegal en 286 y superiores. Por ejemplo.0 o en máquinas 286). El acarreo sirve aquí para discernir si estamos ante una operación normal de disco o ante una inicialización del sistema. debido al tamaño del mismo. TURBODSK soporta también memoria extendida XMS y convencional: cuando se utilizan estas memorias. Esto es así porque la rutina que gestiona el disco puede ser accedida. BP indica además si es lectura o escritura. TURBODSK y RAMDRIVE (que también comete esta inmoralidad) economizan memoria. desde el gestor de la interrupción 19h. El hecho de que por defecto esté colocada la rutina de memoria expandida es debido a que es. es frecuente en la programación de sistemas bajo MS-DOS. Trabajando con memoria EMS. con diferencia. por otro lado. Las rutinas que gestionan los diversos tipos de memoria tienen los mismos parámetros de entrada (obtenidos de Init_io) y sirven para leer/escribir en el disco según lo que indique BP. Esta técnica. un disco virtual que se precie debe soportar transferencias incluso en el caso de que el buffer donde leer/escribir los datos esté también en la memoria expandida y se solape con el propio disco. KBSEC. puede que todavía se transfiera entre uno y tres bytes menos. o simplemente se cuelga el ordenador (con el EMM386 del MS-DOS 5.C emplea un buffer de 63 y no de 64 Kb). Esto provoca un mensaje fatal del controlador de memoria. aunque sí por los del DOS. Ello significa que. Inmediatamente después de la rutina Init_io de TURBODSK está colocada la que gestiona el disco en memoria expandida. donde hace todas estas tareas: preservar el contexto del mapa de páginas. la más larga de todas y así siempre queda hueco para copiar encima las otras. a esto se debe que algunas aplicaciones que trabajan con memoria expandida adviertan que pueden operar mal con ciertos discos virtuales. preguntando si se desea seguir adelante o reinicializar el sistema (QEMM386). Retornan devolviendo en AX el resultado de la operación. La rutina más compleja es la que gestiona la memoria expandida EMS. Además. 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). En el caso de VDISK. así como para liberar la memoria asignada en respuesta a una interrupción 19h. transferir a un buffer auxiliar. En caso de fallo de algún controlador de memoria.CONTROLADORES DE DISPOSITIVOS 219 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. recuperar el contexto del mapa de páginas y transferir del buffer auxiliar hacia donde solicita el DOS. se produzca un acceso de 16 bits en la dirección 0FFFFh. se salva y restaura ¡64 veces! el contexto del mapa de páginas. además de desde Init_io. . con una vuelta para cada sector. no se dejará espacio más que para las rutinas de memoria extendida y convencional. 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 críticas deben evaluarse utilizando las interrupciones 25h/26h. y de transferir sólo lo que se pueda antes del desbordamiento del segmento. la rutina correspondiente sustituye a la de memoria EMS por el simple y efectivo procedimiento de copiarla encima.5 Kb. para transferir 32 Kb en sectores de 0. En TURBODSK se decidió transferir 64 Kb inclusive como límite máximo. si la máquina tiene memoria extendida y no se indica /A. el algoritmo es muy poco eficiente: este disco virtual realiza un bucle. No digamos si los sectores son más pequeños. para economizar más memoria. devolverían un código de error de anomalía general. En TURBODSK se optó también por ser tolerante a los fallos del programa que accede al disco: además de limitar el acceso máximo a 64 Kbytes. En memoria expandida y convencional. ANÁLISIS DE LAS RUTINAS DE GESTIÓN DE MEMORIA. existe el riesgo de que el offset del buffer sea impar y. A la hora de terminar residente. No existe ningún nexo de unión y ambas se ejecutan secuencialmente. que horrorizará a más de un programador. aunque VDISK parece que sí. que será normalmente exitoso.

bastaría con elegir la página 2 ya que si el buffer empieza justo donde apunta la flecha del caso B... ya que el redondeo al convertir la dirección segmentada se hace truncando. como su tamaño es de no más de 16 Kb. ESQUEMA DE FUNCIONAMIENTO DE LA RUTINA DE GESTIÓN DE MEMORIA EMS DE TURBODSK Analizaremos el caso más conflictivo: Cuando el área a transferir ocupa los 16 Kbytes máximos... Lógicamente hay una respuesta... y ¿por qué utilizar un buffer auxiliar?..220 EL UNIVERSO DIGITAL DEL IBM PC.. VDISK se ve obligado a realizar esas operaciones con objeto de permitir una transferencia de la memoria expandida a la propia memoria expandida... por razones de redondeo. en el caso A.. para los casos en que hay colisión. Como al convertir la dirección segmentada a párrafos se pierde precisión. TURBODSK se asegura que la dirección esté 401h párrafos (16 Kb más 1 párrafo) por debajo del inicio de la página 0. el buffer del tamaño de un sector incrementa el consumo de memoria en 512 bytes. AT Y PS/2 además del hecho (mucho más grave) de que transfiere dos veces y de la cantidad de veces que calcula las direcciones. En realidad.. La pregunta es. ya que no excede de 16 Kb de longitud.0). En el caso B.. porque los escasos 247 bytes que ocupa evidencian en qué medida el autor se ha decantado por la eficiencia en detrimento de la claridad al diseñarla.. por lo que no hay colisión con esta página. es más fácil para un programador desarrollar la suya propia que intentar entender la actual: fundamentalmente. en un sistema con memoria EMS 4... Para solucionar este posible solapamiento. ¡sí puede invadir la página 2. ¡y bastante más lento si se utilizan los sectores de 128 bytes que suele establecer por defecto!. bastaría con comprobar si dista al menos 400h bytes. Por tanto. En general. ¿por qué no sacaron los autores de VDISK esas operaciones fuera del bucle?..Página 3 -Página 2 -Página 1 caso B Página 0 . TURBODSK comprueba que el buffer comience al menos 401h párrafos por encima del inicio de la página 0. de cara a que el disco virtual (un programa residente a todos los efectos) no afecte al programa de usuario que se está ejecutando. basta con asegurarse que la página alternativa a la 0. tenemos que utilizar la página 3. no se recomienda a nadie intentar comprender la rutina de TURBODSK para la memoria EMS (Procesa_ems): dada su complejidad. .... aunque sólo un párrafo! (no olvidar que si empieza por encima de la flecha no colisiona con la página 0).. sí. hay que volver a salvar el contexto de manera inmediata para que quede salvado para otra ocasión (o para cuando se acabe el acceso al disco y haya de ser restaurado)..M E M O R I A E M S .. Se comprende ahora la necesidad de restaurar el contexto del mapa de páginas antes de pasar utilizar una nueva página para las transferencias: el hecho de necesitar una nueva página viene determinado porque la hasta entonces utilizada se solapa con el buffer ¡y es preciso restaurar el contenido del buffer!. que se solapa con el buffer. Piense el lector qué sucederá si el buffer donde leer o escribir que suministra el programa principal. Conclusión: para que no haya colisión. el buffer ha de estar a 401h párrafos de distancia (expresada en valor absoluto) del inicio de la página 0.. a través de un buffer auxiliar.16 Kb Resulta evidente. no puede invadir. Cierto es que salvar el contexto del mapa de páginas y volverlo a restaurar es necesario..Página 3 Página 2 Página 1 Página 0 . Sin embargo. el buffer está en memoria expandida pero comienza justo detrás de la página 0 y..... por si éste utiliza también memoria expandida. que entre ambas páginas hay una distancia absoluta de 32 Kb). Una vez más. En ese caso. está en memoria expandida: ¡se solapa con el disco virtual!... las pautas que se darán pueden ser útiles. 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. Pues que no se puede emplear la página 0. Además...caso A .0 donde las páginas pueden ser definidas por el usuario en la dirección que desee (parámetros /Pn= del EMM386 del MS-DOS 5.. TURBODSK utiliza una técnica totalmente diferente a .- . está alejada al menos 48 Kb de la página 0 (esto es. ¿Qué sucede si hay colisión?... que si el buffer donde leer/escribir los datos comienza por debajo de la dirección marcada por la flecha (o justo en esa dirección) no colisionará con la página 0.. En principio.. Además.

como no transfiere con 32 bits. en los 386 y superiores es notablemente más lento que TURBODSK. Estas maniobras que aumentan la complejidad y dificultan posteriores modificaciones del código. Tras la transferencia. además. para volver al estado previo a la entrada en el bucle. . pero totalmente distinto. De hecho. lo que le permitiría en ocasiones ser levemente más rápido que TURBODSK. Estas operaciones hacen que TURBODSK sea ligeramente más lento cuando el buffer de lectura/escritura está en memoria expandida. posee una subrutina encargada de acceder al controlador de memoria que. Esto significa que TURBODSK sólo mapea (y una sola vez) las páginas estrictamente necesarias para la transferencia. cuenta con un algoritmo con un rendimiento similar al de TURBODSK. al transferir 32 bits en los 386 y superiores. también se incorporó esta técnica a TURBODSK. pero probablemente la diferencia no llegue al 1% al caso en que no hay solapamientos. No se realizará esto más veces si no hay solapamientos. el método de TURBODSK es algo más tolerante: no necesita que sean estrictamente contiguas. Por ejemplo. se ha constatado que RAMDRIVE. volviéndose a restaurar al final del todo. hablaremos algo acerca de la manera de comunicarse con el controlador de memoria. además. puede ser necesario transferir un fragmento del final de la primera página mapeada. Esta rutina es además la misma para leer que para escribir: en el caso de la escritura. Para terminar con el análisis de la gestión de este tipo de memoria. En el caso en que haya colisión con la página 0. El inconveniente de la rutina de gestión de memoria EMS en TURBODSK es. elige otra página que diste al menos 32 Kb de la página 0 (bastaría con 16 Kb. si había habido colisión se vuelve de nuevo a restaurar y preservar el contexto. El esquema gráfico lo explica con mayor claridad. para evitar el buffer auxiliar. Al principio se salva una sola vez el contexto de la memoria expandida. pero se hace así para evitar problemas en los redondeos si los buffers no empiezan en posiciones alineadas a párrafo). Por otra parte. excepto en el caso de la función 40h (obtener el estado del gestor) utilizada en la instalación. por lo que no habrá más referencias a ellas. puede ser preciso transferir algunas páginas enteras y. como sólo se utiliza una página de memoria expandida a un tiempo. Sin embargo. por último.0). la cual asigna dinámicamente. están bastante documentadas en el listado. altera la pila para retornar directamente al programa principal y no al procedimiento que la llamó. TURBODSK elige inteligentemente una que no colisione con la del buffer del programa principal a donde enviar/recibir los datos. el bucle no dará nunca más de 5 vueltas (un bloque de disco de 64 Kb puede estar comprendido en 5 páginas EMS). sin embargo. Este algoritmo permite que TURBODSK sea tan rápido como cabría esperar de un disco virtual. por lo que puede resultar de difícil comprensión.CONTROLADORES DE DISPOSITIVOS 221 la de VDISK. entre los principales objetivos estaba reducir el consumo de memoria. ante un código de error 82h (EMM ocupado) vuelve a reintentar de manera indefinida la operación. debido a que TURBODSK transfiere bloques de hasta 16 Kb en cada iteración. RAMDRIVE. Hay que reconocer que por 30 ó 40 bytes más la rutina podría haber sido todo un ejemplo de programación estructurada. en la que hay sólo 32768 intentos. RAMDRIVE necesita que las páginas de memoria expandida sean contiguas (podrían no serlo en EMS 4. basta solo con que entre las 4 primeras haya alguna que diste de la primera al menos 32 Kb. la velocidad que desarrolla en memoria EMS no se queda muy por detrás de la que consigue el controlador de memoria XMS en estas máquinas. una parte inicial de la última página. Por tanto. emitiendo un error de instalación en caso contrario. analizando el valor en AH para determinar si ha habido error. pero cuando se escribió TURBODSK. La principal diferencia es que RAMDRIVE mapea varias páginas consecutivas. El funcionamiento general consiste en ir mapeando las páginas de memoria expandida una a una. En principio. se limita simplemente a intercambiar la pareja DS:SI con la ES:DI antes y después de realizar la transferencia. antes de entrar en el bucle. considerando las tres posibilidades: al principio. también una sola vez. TURBODSK restaura el contexto y lo vuelve a salvar. con objeto de devolver la memoria expandida a la situación inicial y mantener la primera copia que se hizo del contexto. en caso de fallo. del buffer de 512 bytes). no transfiere sector a sector sino el mayor número posible que pueda ser transferido de una sola vez y se evita la necesidad de hacer doble transferencia (con el consiguiente ahorro. Este comportamiento parece estar destinado a mejorar la convivencia con entornos multitarea. la complejidad: está optimizada para reducir en lo posible el tamaño. En principio. lo más normal es cargar los registros e invocar la INT 67h. además. después. incluso al trabajar con memoria EMS. en los que en un momento dado el controlador de memoria puede estar ocupado pero algo más tarde puede responder. como se dijo antes. por su parte.

Por ello. sabiendo dónde reside éste. como los demás controladores. al igual que RAMDRIVE. En el caso de VDISK.fuera de los segmentos de 64Kb. por desgracia. ya que casi todas las máquinas que poseen memoria extendida en la actualidad tienen instalado el controlador XMS. RAMDRIVE intercepta la función 1 (asignar el HMA). donde se hizo la prueba. Es fácil comprobar la pila que el DOS pone a disposición de los drivers: basta hacer un pequeño programa en DEBUG que acceda al disco virtual (por ejemplo. Las que no lo tienen instalado. es extraordinariamente sencillo el proceso: basta crear una estructura con la información del bloque a mover de la memoria convencional hacia/desde la extendida e invocar la función 0Bh. seguida de NOP’s.0). poner un punto de ruptura en algún lugar del mismo con una INT 3. ya que la llamada al controlador XMS puede crear una trama de pila de hasta ¡256 bytes!. Se trata de un fallo conocido por los fabricantes de software de sistemas. se les puede añadir fácilmente (solo requiere al menos DOS 3. ya que en los 386 y superiores utiliza automáticamente instrucciones de transferencia de 32 bits.nunca falla. y está demostrado que poniendo un NOP detrás -entre otros. que se supone suficientemente grande para estos eventos. pero con los registros de 32 bits) venía muy bien para apilar de una sola vez todos los registros de propósito general. de manera incorrecta. La diferencia entre TURBODSK y RAMDRIVE es que el primero crea la estructura sobre la pila (solo son 8 palabras). conmuta a una de sus pilas internas. ¡qué curioso!). decir que debido a que utilizan la misma memoria de la misma manera. antes de acceder a los controladores de dispositivo. aunque hay discos virtuales de dominio público que sí lo hacen. RAMDRIVE no define una pila propia. Hacer esto es realmente complicado. y no es difícil deducir por qué: el DOS. considerando RAMDRIVE todas estas diferentes posibilidades). En las rutinas de TURBODSK se observa también que los registros de 32 bits empleados en la transferencia son enmascarados para que no excedan de 0FFFFh. En TURBODSK se prefirió emigrar a los servicios del controlador XMS (rutina Procesa_xms. sino con un CALL inter-segmento. la instrucción PUSHAD (equivalente a PUSHA. Trabajando con memoria XMS. por otro lado así no hace falta reservar el buffer para la estructura. La ventaja de ello es que las instrucciones PUSH consumen mucha menos memoria que las MOV. La memoria extendida vía XMS. se decidió no incorporar una pila a TURBODSK. el problema reside en que EAX no se restaura correctamente. se emplea el tradicional método de la INT 15h de la BIOS para transferir bloques en memoria extendida.0. Hablando de pila: todos los programas residentes que utilizan servicios XMS suelen definir una pila interna. y puede convivir satisfactoriamente con WINDOWS y con los programas que soportan la especificación XMS debido a que toma las precauciones necesarias. TURBODSK y RAMDRIVE desarrollan velocidades prácticamente idénticas al operar en memoria extendida. El fallo de esta instrucción. implementada por HIMEM.SYS y algún controlador de memoria expandida. teniendo en cuenta que el controlador XMS no se invoca por medio de una interrupción. aunque tampoco es muy grave: básicamente. pero poco divulgado. la velocidad es bastante elevada. Esto es posible porque el controlador XMS siempre empieza también por una instrucción de salto lejana de cinco bytes (o una corta de dos o tres.0 es una versión moderna del legendario controlador. mostrando los registros. al final del listado).222 EL UNIVERSO DIGITAL DEL IBM PC. ya que podrían tener la parte alta distinta de 0 y ello provocaría una trágica excepción del controlador de memoria al intentar un acceso -por otra parte. En MS-DOS 5. Sin embargo. el VDISK de DR-DOS 6. Pese a ello. Al ejecutar el programa en DEBUG. AT Y PS/2 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. todavía quedaban más de 2 Kb de pila en el momento del acceso al disco virtual (el tamaño de la pila es el valor de SP). pero comprobando también si AL vale 40h: esto significa que está intentando detectar la llamada de algún programa en concreto. Por otro. aunque el fallo fue corregido en las últimas versiones de este procesador (los 386 de AMD también lo tienen. vía INT 25h) y. Por ello. es preciso modificar parte del código ejecutable del propio controlador de memoria. en la mayoría de los 386. es notablemente más sencilla de manejar que la expandida. Las ventajas del controlador XMS son múltiples. la correspondiente instrucción POPAD no opera correctamente. Por un lado. ya que el . al parecer descubierto por Jeff Prothero está ligado a las instrucciones que vienen inmediatamente a continuación. Sin embargo. Hay sin embargo un detalle curioso que comentar: RAMDRIVE instala una rutina que intercepta las llamadas al controlador XMS. el control volverá al DEBUG al llegar al punto de ruptura del disco virtual. Por el mismo motivo. Finalmente.

esto no es demasiado importante. En ese caso. Tsect es el tamaño de sector. DR-DOS no opera correctamente con sectores de menos de 128 bytes. desde el DOS: TDSK [U:] [tamaño [tsect [nfich [tclus]]]] [/E] [/A|X] [/C] [/M] [/F] El tamaño del disco ha de estar entre 8 y 65534 Kb (para exceder de 32 Mb hacen falta sectores de al menos 1024 bytes). En memoria convencional hay pocas diferencias entre todos los discos virtuales. El tamaño de cluster (sectores/cluster) es el último parámetro numérico.EXE [tamaño [tsect [nfich [tclus]]]] [/E] [/A|X] [/M] [/F] Alternativamente. Esta forma de trabajar es lo que podríamos denominar programación a nivel de cloacas. La rutina Procesa_con ubicada al final de TURBODSK se encarga de gestionar esta memoria. para cambiar las características de un disco ya definido. la operación del disco siempre resultará exitosa. la sintaxis de TURBODSK es idéntica a la de RAMDRIVE y VDISK. si no se tienen en cuenta las limitaciones de los diversos sistemas operativos. si se exceptúa el parámetro adicional del tamaño de cluster. Trabajando con memoria convencional. Se puede omitir en el CONFIG si no se desea definir el disco en ese momento. Repasaremos la sintaxis que admite antes de proceder a estudiar la instalación del programa: DEVICE=TDSK. En cualquier caso. probablemente también de Microsoft. Con /E se fuerza la utilización de memoria extendida. que por otro lado no soporta sectores de más de 512 bytes (DR-DOS sí). y además en el primer caso de manera repetida. aunque sí el MS-DOS 5. aunque esto es responsabilidad del usuario y el programa no limita su libertad. Los parámetros numéricos intermedios que se desee omitir se pueden poner a cero. Aviso: con sectores de 32 bytes. y desde el DOS si solo se quiere obtener información del disco definido. el programa habrá de ser instalado obligatoriamente en el CONFIG. Sin embargo. Como no hay controladores de memoria por el medio. De lo expuesto anteriormente se deduce que es sencillo crear discos que no operen correctamente.0 toma el nº de entradas del directorio raíz como módulo 256. TURBODSK puede ser ejecutado desde el DOS o el CONFIG. pudiendo a consecuencia de ello retornar con un error 91h (el HMA ya está asignado). debiendo estar comprendido entre 1 y 255. Sin embargo. Sin embargo. ajustando los demás parámetros de la manera más aconsejable. La diferencia de TURBODSK frente a RAMDRIVE y VDISK es que en los 386 y superiores utiliza de nuevo transferencias de 32 bits. en lugar de continuar el flujo normal. determina la memoria extendida libre y hace unas comprobaciones.SYS indistintamente.0. usando código basura para tapar la suciedad de otros programas previos. el MS-DOS 5. Hasta ahora. excepto algún despistado de dominio público que mueve palabras de 8 bits. ya que estas máquinas suelen tener la memoria convencional destinada a cosas más útiles que un disco. aunque por defecto se actúa siempre sobre la primera. para que TURBODSK tome valores por defecto. aunque es un parámetro un tanto redundante (TURBODSK utiliza por defecto esta memoria). Sin embargo. para utilizar memoria expandida. indistintamente. LA SINTAXIS DE TURBODSK. TURBODSK sólo necesita que se indique el tamaño del disco. También se puede indicar /C desde el DOS . En los PC/XT el rendimiento de todos los discos virtuales suele ser muy similar. TURBODSK soporta la presencia de varias unidades instaladas simultáneamente: desde el DOS puede ser preciso indicar también la letra de la unidad a tratar.CONTROLADORES DE DISPOSITIVOS 223 valor de AL es irrelevante para el controlador XMS. Todo parece destinado a mejorar la compatibilidad con algún programa. El número de ficheros del directorio raíz viene a continuación (nfich) y ha de estar comprendido entre 1 y 65534: TURBODSK lo ajusta para aprovechar totalmente los sectores empleados en el directorio. /A y /X sirven.SYS. el MS-DOS no soporta tamaños de cluster que no sean potencia de 2 (DR-DOS sí). aunque ningún otro disco virtual -TURBODSK entre ellosrealiza estas extrañas maniobras. entre 32 y 2048 bytes en potencias de dos.

aunque hay ciertas diferencias lógicas. realizando todas las tareas que cabría esperar de la misma: inicializar el puntero a la tabla de BPB’s (solo uno.Unidad D: Tamaño: Memoria: 512 Kbytes Extendida XMS Tamaño de sector: 512 Nº entradas raiz: 128 Sectores/cluster: 1 1012 clusters (FAT12) EL PROCESO DE INSTALACIÓN DE TURBODSK. Empero. pero TURBODSK soporta la definición de 2 con objeto de permitir la creación de discos idénticos a los estándar del DOS.0. constituyen el programa principal en ambos casos.SYS. la principal diferencia radica en que en el caso de utilizar memoria convencional hay que terminar residente. con un pequeño programa de utilidad es fácil montar ficheros imagen de disquetes (creados con el DISKCOPY de DR-DOS 6. También desde el CONFIG se desvía la INT 19h. AT Y PS/2 para forzar el empleo de memoria convencional en máquinas con memoria expandida y/o extendida. ya que el tamaño de los buffers aumenta y se consume más memoria. el número de unidades (una). desde el CONFIG no se permite definir el disco en memoria convencional. no documentada en la ayuda del programa. instalar el driver sin reservar memoria: para definir el disco se puede ejecutar TURBODSK después desde el DOS. respectivamente. para indicar que no se soportan las órdenes Open/Close/Remove. se ajusta la palabra de atributos. incluso aunque no se cree el disco al indicar un tamaño 0. para evitar una posible fragmentación de la misma (esto depende de la eficacia de los controladores de memoria) aunque sí se permite definir un disco de estos desde el CONFIG. no es recomendable utilizar sectores de más de 512 bytes. /M genera una salida menos espectacular. con DCOPY o con otras utilidades) en un disco virtual de tamaño suficiente. El procedimiento Init se corresponde exactamente con la orden INIT del controlador de dispositivo. Lo normal es trabajar con una FAT. Otro pequeño detalle: si la versión del DOS es anterior a la 3. Sin embargo. Se puede definir el disco desde el CONFIG o. TURBODSK.224 EL UNIVERSO DIGITAL DEL IBM PC. También es vital considerar el parámetro de tamaño de sector que el usuario pueda definir. pero DR-DOS opera satisfactoriamente con sectores de uno o dos Kbytes. Dicho volcado debe hacerse justo tras redefinir el disco y antes de realizar ningún acceso al mismo. así como la memoria que ocupa el programa: al final de Init. Algunas subrutinas concretas actuarán de manera diferente según desde donde sea ejecutado el programa. si no se va utilizar memoria expandida se reserva espacio sólo para las rutinas de memoria convencional y extendida. para que el DOS respete el bloque de memoria creado para contener el disco. Tampoco es muy recomendable reservar memoria extendida o expandida. en la línea de comandos). forzando una salida en color).0 no soporta sectores de más de 512 bytes.X (RAMDRIVE también se toma esta molestia). para aprovechar el hecho de que el DOS va a ser informado de un cambio de soporte. En cualquier caso.3 . así mismo. El procedimiento Main es muy similar al Init. si se indica uno nuevo. considerar ciertas circunstancias nuevas que no podían darse desde el CONFIG: una versión del DOS anterior a la 2. que se indique una letra . Así. ya que si así fuera no se podría desasignar en el futuro. en monocromo y redireccionable (desde el CONFIG se imprime en monocromo por discreción y este conmutador actúa al revés. Sin embargo. La razón es que el DOS asigna el tamaño de sus buffers de disco para poder soportar el sector más grande que defina algún controlador de dispositivo de bloques. se dejan residentes sólo los primeros 96 bytes del PSP. También desde Main puede ser necesario desalojar la memoria de un disco previo. que el driver no haya sido instalado antes desde el CONFIG. ya que cada TURBODSK instalado controla un solo disco).0. sin indicar capacidad o indicando un tamaño 0.0. La opción /F. El funcionamiento del programa es muy similar en los dos casos. Es preciso. Al principio de ambas rutinas se inicializa una variable que indica si estamos en el CONFIG o en el AUTOEXEC (más en general. Ejemplo de lo que puede aparecer en pantalla al definir un disco: TURBODSK 2. gracias a los sectores de más de 512 bytes permitiría operar con discos de más de 32 Mb sin rebasar el límite máximo de 65535 sectores. TURBODSK puede ser ejecutado desde la línea de comandos y desde el CONFIG. con objeto de parecerse lo más posible a un controlador del DOS 2. Casi el 80% del listado de TURBODSK está destinado a instalar y mantener el disco virtual en memoria. e incluso más. los procedimientos Main e Init. permite elegir el número de FATS (1 ó 2). El MS-DOS 5.

Tanto Init como Main leen la línea de parámetros indicados por el usuario y ejecutan ordenadamente los procedimientos necesarios para definir el disco. La rutina Inic_letra. Alternativamente. Veremos ahora con detalle algunas rutinas importantes ejecutadas durante la instalación del disco virtual. También desde el DOS. Dado que DR-DOS 6. no hay más 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?). así como para anotar la dirección donde reside. si ésto es preciso. calcula la letra que el sistema asignará a la unidad. aunque si se anula de nuevo antes de abandonar DESQVIEW no habrá problemas. ejecutada desde el CONFIG. ejecutada sólo desde la línea de comandos del DOS. La rutina Gestionar_ram.0 no inicializa correctamente el tamaño del encabezamiento de solicitud de esta orden. si es así se indica al usuario que ese tamaño de sector debe definirse previamente desde el CONFIG. si el usuario intenta grabar algo en el disco virtual. el sistema se estrellará. rebaja la memoria asignada al TDSK. volviendo después a WINDOWS: WINDOWS recupera toda la memoria convencional que había asignado para su propio uso. Supongamos. En la rutina TestWin se comprueba si Windows está activo. por lo que TURBODSK sí permite modificar el disco desde el interior de este entorno. LAS PRINCIPALES SUBRUTINAS PARA LA INSTALACIÓN. 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. en función de las longitudes. que el tamaño de sector exceda el máximo que permite la configuración del DOS. Esta acrobacia provoca la creación de un bloque de control de memoria (MCB) en el offset 96 del PSP.X. Los procedimientos Errores_Dos y Errores_config comprueban algunos errores que pueden producirse al ejecutar el programa desde la línea de comandos del DOS o desde el CONFIG. el procedimiento Obtener_segm recorre la tabla de discos para asegurarse de que esa letra de unidad es un dispositivo TURBODSK. pero TURBODSK no puede darse cuenta de esta circunstancia y. Desde el DOS 3. En el caso del DOS 2. el encabezamiento de petición de solicitud de la orden INIT almacena este dato. el procedimiento Reside_tdsk? busca la primera unidad TURBODSK residente de todas las que puede haber en la memoria.11 en que se probó TURBODSK esa interrupción estaba apuntando a 0000:0000 y el ordenador se colgaba si no se tomaba esta precaución. La memoria virtual de WINDOWS también da problemas al crear discos en memoria expandida o extendida. es más seguro verificar la versión del DOS que comprobar si este dato está definido o no. para evitar en ese caso una modificación del disco por parte del usuario. Esto se hace así para poder utilizar después las funciones estándar del sistema para asignar memoria. Para ello crea una tabla con todos los dispositivos de bloque del sistema (rutina Lista_discos) y empieza a buscar desde el final hacia atrás (se trata de encontrar la primera unidad TURBODSK y no la última). . Antes de llamar a la INT 2Fh se comprueba que esta interrupción esté apuntando a algún sitio: en el sistema DOS 2. que sería lo normal. Este último aspecto se consideró a raiz de los riesgos que conlleva. también se libera el espacio de entorno por si acaso se fuera a terminar residente.EXE en ejecución a 96 bytes.0. Tampoco conviene definir el disco desde DESQVIEW. lo cual es inocuo. hay que chequear en dos interrupciones distintas las presencia de Windows. las definiciones del disco han de hacerse antes de entrar en WINDOWS. En el procedimiento Max_sector invocado desde Errores_Dos se comprueba si el tamaño de sector indicado excede el máximo que soporta el DOS. que el usuario abre una sesión DOS desde WINDOWS y define un disco de media mega en memoria convencional. si se había indicado una letra de unidad. Por desgracia. por ejemplo. Por tanto.CONTROLADORES DE DISPOSITIVOS 225 de unidad que no se corresponda con un driver TURBODSK. para lo que se utiliza la función 52h (Get List of Lists). con objeto de informar en el futuro al usuario.

por defecto se intenta emplear. en equipos 386 y superiores.226 EL UNIVERSO DIGITAL DEL IBM PC.EXE se autorelocalizará hacia la memoria convencional y permitirá emplear toda la memoria superior libre que quede). hay que tener la precaución de no ocuparla toda y dejar algo libre. A la memoria expandida se le asigna menos prioridad que a la extendida debido a que. el segundo el número de unidades que controla y el tercero puede valer 1 ó 0 para indicar si se trata de una unidad TURBODSK o no.0 y versiones posteriores). con cuatro bytes por dispositivo: los dos primeros indican el segmento donde reside. En el caso de un dispositivo TURBODSK no se anota el segmento donde reside sino la variable cs_tdsk del mismo. El método ordinario suele ser intentar abrir ese dispositivo y después comprobar por IOCTL que no se trata de un fichero con ese nombre. pero el recurso a la memoria convencional se evita siempre. no será preciso solicitar en estos casos. La subrutina Eval_ems detecta la presencia del controlador de memoria expandida buscando un dispositivo "EMMXXXX0".0 del controlador se asigna un nombre al handle con objeto de que los programas de diagnóstico muestren una información más detallada al usuario. Además. inhabilitando el driver. para que el DOS devuelva el segmento en que está disponible. El procedimiento Mem_reserva procede a la efectiva asignación de memoria al disco. hay que pedir sólo exactamente la cantidad de memoria superior disponible en la máquina (o algo menos). cargando TDSK. Seguidamente se procede a pedir justo esa memoria. que indica la dirección real incluso en el caso de que el dispositivo haya sido relocalizado por QEMM a la memoria superior. Por desgracia. Si no se indica el tipo de memoria. 128 Kb de menos para lograr que sea asignada memoria superior (TDSK. dos páginas de memoria expandida (una de ellas la 0) que disten entre sí 32 Kb. en esos 128 Kb que se perdonan será preciso que TDSK. y una vez que ya se había decidido el tipo de memoria a emplear. El final de la tabla se delimita con un valor de segmento igual a cero. en este orden.X. La rutina Desinstala libera la memoria que ocupa un disco residente con anterioridad. memoria extendida. normalmente es memoria extendida que emula por software la expandida: suele ser más rápido dejar directamente al controlador XMS la tarea de realizar las transferencias de bloques de memoria. En principio se procura utilizar la memoria que el usuario indica. crea una tabla con todos los dispositivos de bloque del sistema. como dije con anterioridad. En el caso de que no haya suficiente memoria se rebaja la cantidad solicitada. en el caso de que finalmente éste se instale. con objeto de que éste falle e indique cual es la cantidad máxima de memoria disponible. despreciando longitudes inferiores a 8 Kb que es el tamaño mínimo del disco. 3. por lo que TURBODSK tiene en cuenta los tres casos posibles (DOS 2. la manera de acceder a la cadena de controladores de dispositivo varía según la versión del DOS. Al principio le solicita casi 1 Mb al DOS. con memoria convencional. expandida o convencional. antes de llamar a INT 2Fh se toma una vez más la precaución de comprobar que esta interrupción está apuntado a algo. Al final.SYS no deben acceder a las funciones IOCTL. afortunadamente. El afán de información no se detiene aquí: en el caso de emplear memoria extendida. De lo contrario. al tamaño de ese bloque de memoria se le restan 128 Kb ya que. sin embargo. Para ello utiliza la valiosa función indocumentada 52h (Get List of Lists) del DOS. En el caso de la memoria convencional hay que liberar tanto el segmento que ocupaba el disco como el del PSP previamente residente. volviéndose a liberarla inmediatamente a continuación. El procedimiento Mem_info evalúa la memoria disponible en el sistema y toma la decisión de qué tipo y cantidad de la misma va a ser empleada.0 y posterior hay que buscar. En esta subrutina se comprueba además la versión del controlador: en la 4. generándose un mensaje de advertencia. en el caso de no haber la suficiente extendida (aunque haya algo) se utiliza la expandida. La subrutina Eval_xms chequea la presencia de un controlador de memoria extendida.EXE se autoreubique antes de formatear el disco. de lo contrario el DOS asignará memoria convencional para satisfacer la demanda: dado que normalmente hay más memoria convencional libre que superior. Con MS-DOS 5. la subrutina Eval_con determina la memoria convencional disponible. Si se utiliza memoria expandida. TURBODSK comprueba si la creación de un . sin embargo. En la tabla creada. recuérdese. AT Y PS/2 El procedimiento Lista_discos. Finalmente. por lo que se utiliza el algoritmo alternativo de comprobar si esa cadena está en el offset 10 del vector 67h. desde la versión 4. El procedimiento Mem_info se apoya en tres subrutinas que calculan la cantidad disponible de cada tipo de memoria.EXE con el comando LOADHIGH: sin embargo. los controladores de dispositivo invocados desde el CONFIG. como veremos después.0 se puede crear un disco virtual en memoria superior.

1 como el 3. por lo que no se ha evitado estos tamaños de disco (casi nadie ejecuta CHKDSK sobre un disco virtual. por ejemplo. La subrutina Adaptar_param es una pieza clave dentro del programa: aquí se decide qué parte del disco va a ocupar el directorio. no distinguen entre nº de clusters y nº más alto de cluster. Es muy fácil: se graban algunos ficheros y se mira la FAT con algún programa de utilidad (PCTOOLS. lo más sencillo es crear discos virtuales con 4084/4085 clusters y espiar qué hace el DOS. Por fortuna. con un comando del tipo TDSK 527 128 0 1 /E (no vale la memoria expandida. En el caso de versiones 2. A simple vista se deduce si el DOS asigna una FAT de 12 o de 16 bits. el tipo de FAT. en caso contrario. es factible definir un directorio raíz que consuma la mitad de la capacidad del disco. los CHKDSK de casi todas las versiones del DOS (excepto el del MS-DOS 3. la FAT será de 12 bits a menos que el disco exceda los 8 Mb. Por desgracia.. copiando simplemente las . Por ejemplo. con lo que pueden estropear el disco si el usuario ejecuta un CHKDSK/F. Esto sucede con QEMM y otros controladores de memoria que no distinguen la expandida de la extendida. 100 clusters se numerarían entre 2 y 101 inclusive).. DISKEDIT). actualizándolo al nuevo disco. Sin embargo. Hay casos críticos en que una FAT de 12 bits no alcanza. 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.X del sistema se establece un tamaño de cluster por defecto tal que nunca sea necesaria una FAT de 16 bits (no soportada por estas versiones). libertad total para las decisiones del usuario. ya que redondearía a 528 Kb). evidentemente. poseen una errata por la que suponen que los discos de 4085 a 4087 clusters tienen una FAT de 12 bits.0 asignan FAT’s de 16 bits a partir de 4085 clusters inclusive.CONTROLADORES DE DISPOSITIVOS 227 handle XMS implica la aparición de otro handle EMS. así como el DR-DOS 3. otros.5 que devuelve el número de cluster más alto del disco (se añade uno ya que los clusters se numeran desde dos. 4. todas las versiones del DOS parecen comportarse igual.0 y 6. Al final. 5. clusters de hasta 31 Kbytes. etc. como se dijo..5 bytes adicionales para cada cluster. la FAT no puede ser de 12 bits. con los tamaños de cluster y sector que TURBODSK asigna por defecto.5 por 2 y definiendo una FAT de 16 bits. pero en principio hay. casi todos los libros consultados (y ya es mala suerte) tienen esta información incorrecta: para unos. se trata de una circunstancia muy puntual y poco probable. la FAT16 empieza a partir de 4078 clusters.30 y el de DR-DOS 6. Esto es un fallo exclusivo de CHKDSK que debería ser corregido en el futuro. Una vez definidos los parámetros básicos de la estructura del disco. para otros. El algoritmo para determinar el tipo de FAT del disco consiste en considerar el número de sectores libres que quedan después de descontar el sector de arranque y el directorio raíz. entre otros por que si fuera excesivamente pequeña el disco funcionaría mal.0.3. los que el usuario haya indicado. considerando todas las posibilidades de error.0 y 5. ¡nunca un número de cluster cualquiera!.41. los valores que TURBODSK asigna por defecto suelen ser bastante más operativos. Tanto el MS-DOS 3. y en ese caso no va a tener tan mala suerte). retoque el número de entradas del directorio para variar ligeramente el número de clusters). así como de actualizar las variables de la copia residente en memoria. Por ejemplo. Se toman valores por defecto o. sin embargo. Si el resultado es mayor o igual que 4086. se aplica esta fórmula: número de sectores libres * tamaño de sector + 1 tamaño de cluster + 1. Resulta curioso este fallo de CHKDSK. también se indica que ha habido cambio de disco. el procedimiento Preparar_bpb inicializa el BPB. lo busca y le renombra. pero al definirla de 16 el tamaño adicional que ella misma ocupa hace que el número de cluster más alto baje de 4086: en estos casos se reserva espacio para una FAT de 16 bits que luego será realmente de 12. se puede crear un disco de 4087 clusters en el que los CHKDSK de las versiones del DOS señaladas informen incorrectamente de la presencia de errores (si decide hacer pruebas.0. a partir de 4086. incluido el de MS-DOS 5.0). por lo que se debe recalcular la fórmula sustituyendo el 1. TURBODSK permite un elevado grado de libertad.. Teniendo en cuenta el tamaño de cluster en bytes y que la FAT de 12 bits añade 1. En principio. El procedimiento Prep_driver se encarga de copiar el BPB recién creado sobre el del driver residente en memoria. hay un auténtico caos ya que las fuentes de información se contradicen. Conviene hacer hincapié en que los discos con 4085 clusters o más (con número de cluster más alto 4086 o superior) tienen una FAT de 16 bits. la FAT. Asignar el tipo de FAT correcto es vital por muchos motivos.

EXE que se ejecuta (la rutina de gestión de memoria será accedida directamente al formatear el disco virtual). Por tanto. . antes de formatear el disco hay que tomar precauciones. .EXE (que no ocupa más de 12 Kb) justo a la mitad de ese área de 128 Kb. Se puede llegar a sobreescribir parte de la zona transitoria del COMMAND. Si la memoria está suficientemente fragmentada (por haber instalado programas residentes tras definir algún disco) puede que no fuera estrictamente necesario respetar 128 Kb al final del bloque que nos asigna el DOS ni tampoco quizá relocalizar TDSK.COM. . 1 Mb 1 Mb 640 Kb 640 Kb aprox. para evitar solapamientos consigo mismo en casos críticos. AT Y PS/2 del TDSK.EXE se autodestruirá.EXE en ejecución. Ciertamente. .EXE (256 bytes) DOS/BIOS 0 Kb Antes 0 Kb Después Área de almacenamiento del disco virtual PSP TDSK. incluso en el peor de los casos. .EXE (UN CASO CONCRETO) Casi todas las cifras son arbitrarias.EXE (256 bytes) 576 Kb 64 Kb libres (área de seguridad) 512 Kb . lo cual provoca simplemente su recarga desde disco. tanto en la copia residente como en el propio código del TDSK. la FAT y el directorio raíz (en eso consiste simplemente el formateo) el propio TDSK. . . No están reflejados los bloques de control de memoria ni otros detalles.EXE se copia a sí mismo en esos 128 Kb libres que siempre hay. . Para evitarlo. ESQUEMA DE LA AUTORELOCALIZACIÓN DE TDSK. ya que TDSK. También se instala la rutina necesaria para gestionar el disco. si se inicializa sin más el sector de arranque. 588 Kb nueva pila de TDSK. Se toma la precaución de relocalizar TDSK. . .228 EL UNIVERSO DIGITAL DEL IBM PC. pasando a ejecutarse en ese nuevo destino por medio de una instrucción RETF que carga CS al retornar (procedimiento Relocalizar). . sin embargo.EXE en memoria en el caso de definirse el disco en memoria convencional. . . pila incluida (se actualiza también SS). No habrá problemas. pero estas cosas se pueden hacer en MS-DOS y nadie puede cuestionar la efectividad del método.EXE (96 bytes) DOS/BIOS En este esquema se muestra la autorelocalización de TDSK. .EXE. . a modo de ejemplo práctico.EXE PSP TDSK. . . El motivo radica en el hecho de que el disco probablemente comience en el offset 96 del PSP. que carece de referencias absolutas a segmentos. En el caso de emplear memoria convencional. . según el tipo de memoria a emplear por el mismo: esta rutina se instala por partida doble. Se copia todo. Futuros programas pila de TDSK. TDSK. el programa no está optimizado hasta ese extremo.EXE PSP TDSK. 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 críticos de cantidad de memoria libre y tamaño de disco solicitado por el usuario. no es muy ortodoxo que un programa en ejecución vaya dando paseos por la memoria del PC.EXE 128 Kb TDSK.EXE es realmente un programa COM disfrazado de EXE.EXE TDSK. Los programadores más conservadores han tenido suerte de que el adaptador de vídeo monocromo cuente con sólo 4 Kb.

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 además comprueban el byte descriptor de medio. con modificación del byte descriptor de medio entre ambos. En general. esto significa unir las complicaciones anteriores a otras nuevas. Una posible mejora de TURBODSK sería evitar la pérdida de datos al redefinir el disco. espero que el lector esté suficientemente preparado para entender en conjunto el funcionamiento del programa y para crear unidades de disco por su cuenta. con estas versiones. Además. TURBODSK se toma la molestia de consultar la fecha y hora actuales para inicializar la etiqueta de volumen. Lo que sí sería más interesante es crear un disco virtual con asignación de memoria en tiempo real: cuando el usuario pretende crear un fichero. el usuario iba a tener muchos problemas siempre. sin embargo. TURBODSK puede funcionar bajo Windows 95. El hecho de hacer un segundo cambio se debe al interés de restaurar el byte descriptor de medio inicial. además de la dificultad de implementarlas que desanima al programador más audaz. por ello es preciso acceder directamente al mismo (sin embargo. estas versiones invalidarán los buffers asociados a él. habilitar el espacio suficiente. Basta copiar un sector de arranque y poner a 0 la FAT y el directorio raíz. Es complejo añadir esta optimización.1 hubo bastantes problemas. pero mola.EXE: en el caso de que el disco ocupe memoria convencional/superior. Hablando de acceso directo al disco. una vez terminado el arranque del ordenador no hubiera habido problema alguno). ya que sería muy frecuente que cuando tratase de reducir el tamaño del disco éste estuviera demasiado lleno. el comando MEM del sistema operativo indicará claramente que se trata de TDSK y además qué unidad controla. Es de suponer que cuando el disco informa que ha habido cambio. tratándose por ejemplo de aumentar su capacidad. si creen que se trata de un disco del mismo tipo no se molestan en actualizar el BPB. Además.SYS el DOS no está aún listo para ejecutar la INT 26h ya que el driver aún no está encadenado a la lista de dispositivos. Es una tontería. con la excepción de los primeros 3 bytes de la FAT (4 si es de 16 bits) y los 32 primeros bytes del directorio raíz. sin obligar al usuario a reconfigurar nada. complicaciones que restarían velocidad al disco virtual.11 probado necesitaba dos cambios en cualquier caso: si no. no está muy claro que el MS-DOS sea un sistema adecuado para soportar tal disco: al final. en un disco virtual no es preciso verificar la memoria buscando posibles sectores defectuosos. AMPLIACIONES DE TURBODSK Después de esta completa exposición sobre las rutinas que componen TURBODSK. suelen permitir esto de manera limitada y bajo circunstancias concretas. Entre cambio y cambio. si se reserva memoria desde el CONFIG. . El procedimiento renombrar_mcb cambia el nombre del bloque de memoria de TDSK. que contienen una entrada con la etiqueta de volumen. Los programas que acceden a estas interrupciones son considerados inadecuados.11 y 3. Evidentemente. el proyecto podría quedar descartado en la fase de análisis (si es que alguien acepta el reto). Por ello. gracias entre otros motivos a que no utiliza INT 26h. se pregunta al sistema el espacio libre en disco para forzar un acceso al mismo. ya que la arquitectura del nuevo disco puede cambiar demasiado (nuevo tamaño de FAT e incluso tipo de la misma).CONTROLADORES DE DISPOSITIVOS 229 El procedimiento Formatear_tdsk es extraordinariamente sencillo: se encarga de realizar lo que desde hace algún tiempo ha dado en llamarse formateo rápido. Sin embargo. otra ventaja de no utilizar INT 25h/INT 26h es que Windows 95 no permite un uso directo de estas funciones. Por otra parte. Con MS-DOS 2. el DOS 2. Para grabar los sectores en el disco no se puede emplear el elegante método de llamar a la INT 26h: aunque el driver residente ya está totalmente preparado para operar. tras el formateo TURBODSK hace dos cambios de disco consecutivos. no se tomaba en serio el cambio de disco. los discos virtuales redimensionables que soportan una redefinición sin pérdida de datos.

230

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

; ------------ Variables y tablas de datos globales fijas. Estas ; variables no serán movidas de sitio en otras versiones ; de TURBODSK, con objeto de facilitar un control externo ; del disco virtual por parte de otros programas. Todo lo ; que está dentro del «área a actualizar» será copiado ; sobre el TURBODSK residente al redefinir el disco, para ; inicializar todas las variables precisas. cs_tdsk DW ? ; ; ; ; ; ; ; ; ; ; ; ; ; ; Segmento de TDSK. Con QEMM-386, los drivers pueden ser relocalizados en memoria superior de tal manera que parte de la cabecera queda en memoria convencional, con el dispositivo completo en la memoria superior, en la que es ejecutado. Tras la instalación, QEMM copia en memoria convencional los primeros 18 bytes de la cabecera, entre los que está esta palabra, actualizándola. Pese a que la cadena de dispositivos del sistema pasa por la memoria convencional en este caso, esta variable nos permite conocer la dirección REAL en memoria superior (o en cualquier otra) de TURBODSK, que así es compatible con el LOADHI de QEMM.

Versión 2.3 CONTROLADOR DE DISCO VIRTUAL PARA SISTEMAS DOS Y WINDOWS 3 * * * Programa de Dominio Público * * * (C) 1992-1995 Ciriaco García de Celis. Grupo Universitario de Informática. Facultad de Ciencias. Apartado 6062 - Valladolid (España) id_tdsk Internet Email: FidoNet: ciri@gui.uva.es 2:341/21.8 num_ordenes Mensajes en alemán cortesía de Axel Christoph Frinke Internet Email: acfrinke@uni-bonn.de i_tdsk_ctrl tipo_soporte Aviso: Este programa contiene instrucciones exclusivas de los procesadores 386 y superiores. Debe ser ensamblado como fichero EXE, de la siguiente manera, para asegurar la compatibilidad con los procesadores 8086 y 286: cambiado - Con TASM 2.0: TASM tdsk /m3 TLINK tdsk - Con MASM 6.0 (versiones anteriores de MASM generarían un disco virtual que requeriría un 386 o superior, además habría que mover las directivas que controlan el tipo de procesador y colocarlas con «peligro»): ML /Zm tdsk.asm o alternativamente: ML /c /Zm tdsk.asm TLINK tdsk La ventaja de TLINK frente a LINK es que el fichero ejecutable ocupa 2 Kbytes menos en disco (a la tabla ubicada al final del programa se le asigna memoria en la cabecera del fichero EXE y no ocupando disco). IMPORTANTE: Cualquier cambio realizado en el programa debe ser documentado, indicando claramente en el listado y en el fichero DOC quién lo ha realizado. mem_handle DW DB DB EQU DB DB

"TDS23" ; esto es TURBODSK 2.3 y no otro ; controlador de dispositivo 10h $ 0FFh ; nº de órdenes soportadas ; inicio del área a actualizar ; ; ; ; ; 0: disco no formateado 1: se emplea memoria XMS 2.0+ 2: " " " EMS 3.2+ 3: " " " convencional 0FFh: aún no ejecutada INIT

? ?

; al formatear el disco virtual se pone ; a 0FFh (para indicar cambio de disco) ; para memoria EMS/XMS; si se utiliza ; memoria convencional, apunta al ; segmento donde empieza el disco ; segmento del PSP residente si se ; utiliza memoria convencional ; segmento de página EMS (si se emplea) ; segmento alternativo ; nº de página física alternativa ; dirección del controlador XMS, en el ; caso de emplear memoria XMS. ; a ON si 386 ó superior ; final del área a actualizar ; letra ASCII del disco (’C’, ’D’,...) ; puntero al BPB del disco ; a ON si reservado espacio en ; memoria para la larga rutina de ; gestión de memoria EMS.

tdsk_psp ems_pagina0 ems_paginai ems_pagni xms_driver xms_desp xms_segm cpu386 f_tdsk_ctrl letra_unidad bpb_ptr rutina_larga

DW DW DW DB

? ? ? ?

LABEL DWORD DW ? DW ? DB EQU DB DW DB OFF $ ? bpb OFF

; ------------ Macros de propósito general XPUSH MACRO regmem IRP rm, <regmem> PUSH rm ENDM ENDM MACRO regmem IRP rm, <regmem> POP rm ENDM ENDM ; apilar lista de registros

; ------------ Variables internas de TURBODSK; su ubicación podría ; cambiar en futuras versiones del programa. pcab_peticion pcab_pet_desp pcab_pet_segm p_rutinas LABEL DWORD DW 0 DW 0 LABEL DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW EQU ; puntero a la cabecera de petición

XPOP

; desapilar lista de registros

; ------------ Estructuras de datos cab_PETICION tamano unidad orden estado dos_info cab_PETICION cab_INIT_BBPB num_discos fin_resid_desp fin_resid_segm bpb_cmd_desp bpb_cmd_segm nuevo_disco cab_INIT_BBPB STRUC DB DB DB DW DB ENDS STRUC DB DB DW DW DW DW DB ENDS ? ? ? ? 8 DUP (?) ; parte inicial común a todos ; los comandos de la cabecera ; de petición

; para comandos INIT/BUILD_BPB (TYPE cab_PETICION) DUP (?) ? ; número de unidades definidas ? ; área que quedará residente ? ? ; línea de órdenes del CONFIG ? ; y puntero al BPB ? ; (DOS 3+) (0-A:, 1-B:,...)

WORD ; tabla de rutinas del controlador init media_check build_bpb ioctl_input read read_nowait input_status input_flush write write_verify output_status output_flush ioctl_output open ; DOS 3.0+ close ; DOS 3.0+ remove ; DOS 3.0+ 0FAh ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; byte descriptor de medio utilizado por TURBODSK. No es 0F8h como en los discos virtuales del sistema ya que TURBODSK no es un dispositivo fijo. Este byte no es empleado por los discos estándar del dos y al ser mayor de 0F7h no provoca mensajes extraños con antiguos CHKDSKs. Estos valores del BPB son arbitrarios: se inicializarán si se define el disco al instalar desde el CONFIG; en caso contrario, como son correctos, el DOS no tendrá problemas para realizar sus cálculos internos iniciales al instalar el driver. En concreto, el tamaño de sector influye de manera directa en el tamaño de los buffers de disco del DOS.

media

cab_MEDIACHECK STRUC ; estructura para MEDIA CHECK DB (TYPE cab_PETICION) DUP (?) media_descrip DB ? ; descriptor de medio cambio DB ? ; 1: no cambiado, 0FFh:sí, 0:? cab_MEDIACHECK ENDS cab_READ_WRITE STRUC DB DB transfer_desp DW transfer_segm DW transfer_sect DW transfer_sini DW cab_READ_WRITE ENDS (TYPE cab_PETICION) DUP (?) ? ; descriptor de medio ? ; dirección de transferencia ? ? ; nº de sectores a transferir ? ; primer sector a transferir

bpb bytes_sector sect_cluster sect_reserv num_fats entradas_raiz num_sect media_byte sectores_fat fin_bpb

LABEL DW DB DW DB DW DW DB DW EQU

BYTE 512 1 1 1 128 128 media 4 $

; ------------ Rutina de estrategia del disco virtual. estrategia PROC MOV MOV RET ENDP FAR CS:pcab_pet_desp,BX CS:pcab_pet_segm,ES

; ************ Disco virtual: inicio del área residente. _PRINCIPAL SEGMENT ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL DD DW -1 0800h ; ; ; ; ; ; ; estrategia ; interrupcion ; 1 ; encadenamiento con otros drivers palabra de atributo: bit 15 a 0: dispositivo de bloques bit 14 a 0: sin control IOCTL bit 13 a 0: formato IBM bit 11 a 1: soportados Open/Close y Remove (DOS 3.0+) rutina de estrategia rutina de interrupción número de unidades estrategia

tipo_drive

DW DW DB

; ------------ Rutina de interrupción del disco virtual. TURBODSK, ; al igual que RAMDRIVE o VDISK, no define una pila ; interna. Es responsabilidad del DOS que ésta tenga el ; tamaño adecuado (con el disco en memoria XMS, el ; controlador XMS puede requerir hasta 256 bytes de ; pila). TURBODSK no consume más de 64 bytes de pila en ; ningún momento, y sólo alrededor de 48 antes de llamar ; al controlador XMS cuando se emplea esta memoria. interrupcion PROC FAR

CONTROLADORES DE DISPOSITIVOS

231

orden_ok:

no_test_fmt:

exit_interr: interrupcion

XPUSH LDS MOV MOV CMP JB MOV CMP JNE MOV JMP SHL MOV XPUSH XOR MOV CALL XPOP AND JNS CMP JE MOV MOV XPOP RET ENDP

<AX,BX,CX,DX,SI,DI,BP,DS,ES> BX,CS:pcab_peticion AL,[BX].orden ; AL = orden AH,0 ; AX = orden AL,CS:num_ordenes orden_ok ; orden soportada AL,3 ; " desconocida (IOCTL INPUT) CS:tipo_soporte,AH no_test_fmt ; tipo_soporte distinto de 0 AX,8102h ; disco no formateado: error exit_interr AX,1 ; orden = orden * 2 SI,AX <BX,DS> BP,BP AX,100h CS:[SI+OFFSET p_rutinas] ; ejecutar orden <DS,BX> AH,AH exit_interr ; no hubo error (bit 15 = 0) AL,3 exit_interr ; error de orden desconocida [BX].transfer_sect,0 ; otro: movidos 0 sectores [BX].estado,AX <ES,DS,BP,DI,SI,DX,CX,BX,AX>

; ; ; ; ; ; ; ; ; ; procesa_ems

al menos 32 Kb absolutos de la 0. Para dilucidar si hay solapamiento, se compara la distancia entre direcciones origen y destino antes de la transferencia: si es mayor de 401h párrafos (16400 bytes, 16 para redondeo) no hay problema. Ante un solapamiento se procede a restaurar el contexto de las páginas mapeadas, antes y después de la transferencia, para poder acceder a la memoria expandida donde está el buffer del programa principal.

no_emslib:

procesa_pag:

; ------------ Las rutinas que controlan el dispositivo devuelven AX ; con la palabra de estado. Pueden cambiar todos los ; registros (de 16 bits), incluídos los de segmento. A la ; entrada, BP=0 y AX=100h. media_check: MOV MOV MOV AL,CS:cambiado CS:cambiado,AH [BX].cambio,AL ; condición de «disco cambiado» ; de momento ya no cambiará más rpos: ; conjunto de órdenes con ; tratamiento idéntico no_conflicto:

read_nowait: input_status: input_flush: output_status: output_flush: ioctl_output: open: close: retorno_ok: RET build_bpb: MOV MOV JMP MOV RET MOV RET PROC .286 PUSHA XPUSH XOR MOV CMP JE MOV CALL LEA MOV PUSH POP CLI MOVSW MOVSW XPOP POPA DB DW DW .8086 ENDP INC

; no hay error, ignorar orden [BX].bpb_cmd_desp,OFFSET bpb [BX].bpb_cmd_segm,CS retorno_ok AX,8103h AH,3 ; orden no soportada cx_ok: ; fin de función, indicar ; «controlador ocupado» ; Interceptar reinicialización <DS,ES> ; Esto es una interrupción AX,AX ES,AX AL,CS:tipo_soporte ; ¿Disco formateado? no_lib ; no CS:tipo_soporte,AL ; sí: anularlo procesa_io ; CF=1: liberar memoria EMS/XMS SI,ant19off DI,64h ; desplazamiento de INT 19h CS DS

ioctl_input: remove: nueva_int19

no_lib:

transferido:

<ES,DS> 0EAh ? ? ; código de JMP FAR SEG:OFF trans_16bit: fin_trans:

ant19off ant19seg nueva_int19 read: write: write_verify: init_io

BP

; indicar lectura (BP=1) ; escritura (BP=0)

ahorra_ms:

io_proc: io_no_ok: io_ok?:

io_cx_ok:

init_io

PROC LES LDS MOV MOV ADD JNC MOV RET CMP JA SUB MUL RCR MOV NEG CMC RCR CMP JAE MOV JCXZ MOV MUL CLC ENDP

; preparar registros E/S DI,DWORD PTR [BX].transfer_desp ; * direc. ES:DI AX,DWORD PTR [BX].transfer_sect ; nº sectores AX BX,DS ; 1º sector ¡DS indefinido! SI,CS:bytes_sector AX,BX io_ok? ; último sector < 65536 AX,8108h ; «sector no encontrado» AX,CS:num_sect io_no_ok AX,BX SI AX,1 CX,DI CX CX,1 AX,CX io_cx_ok CX,AX io_no_ok AX,BX SI ; sector final ¡fuera! ; DX(CF):AX = tamaño bloque ; CF:AX/2 -> AX = palabras ; 10000h-CX: CF=1 si CX<>0 ; CF:CX bytes hasta fin de ; segmento = (10000h-DI)/2 ; ; ; ; ; * tamaño: CX palabras CX=0 si DI=0FFFFh (fatal) sector inicial * desplazamiento en DX:AX ¡no reinicializando!

fin_leer:

procesa_ems

PROC JNC no_emslib MOV DH,45h CALL llama_EMM RET MOV SI,DX MOV DH,47h CALL llama_EMM MOV DX,SI MOV BX,4000h DIV BX MOV SI,DX PUSH CX MOV BX,DI MOV CL,4 SHR BX,CL MOV CX,ES ADD BX,CX MOV CX,CS:ems_pagina0 MOV DS,CX XOR DL,DL SUB BX,CX JNC rpos NEG BX CMP BX,401h JAE no_conflicto CALL copia_contexto MOV DS,CS:ems_paginai MOV DL,CS:ems_pagni OR BP,8000h POP CX MOV BX,AX MOV DH,44h CALL llama_EMM XPUSH <CX,SI> SUB SI,4000h NEG SI SHR SI,1 CMP CX,SI JB cx_ok MOV CX,SI POP SI CLD POP BX SUB BX,CX PUSH BX CALL coloca_regs CMP CS:cpu386,ON JNE trans_16bit .386 PUSHAD SHR CX,1 JCXZ transferido XOR EAX,EAX DEC AX AND ECX,EAX AND ESI,EAX AND EDI,EAX REP MOVSD POPAD .8086 NOP ADD CX,CX ADD DI,CX ADD SI,CX JMP fin_trans REP MOVSW CALL coloca_regs AND BP,BP JNS ahorra_ms CALL copia_contexto AND BP,1 POP CX JCXZ fin_leer INC AX XOR SI,SI JMP procesa_pag MOV DH,48h CALL llama_EMM MOV AX,100h RET ENDP

; sistema reinicializando: ; liberar memoria EMS ; preservar DX ; ; ; ; ; ; DH=47h -> salvar contexto EMS recuperar DX tamaño de página (16 Kb) AX = 1ª página EMS a mapear offset relativo en 1ª página **

; bytes del offset -> párrafos ; AX = segmento de datos ; intentar emplear página 0 ; ; ; ; valor absoluto distancia respecto página EMS más de 16 Kb: no solapamiento está CX apilado

; usar página alternativa ; indicar su uso ; * pila totalmente equilibrada ; DL = 0 ó 2 (página física) ; DH = 44h -> mapear página EMS ; ++ ; SI = 4000h - SI: «resto» ; bytes -> palabras ; no ocupada toda la página ; + SI=desplazamiento relativo ; + palabras restantes ; descontar las que se moverán ; * volver a apilar el viejo CX ; ¿386 o superior?

; ; ; ; ;

nº palabras de 32 bit a mover evitar desgracia asegurar no violación de segmento-64K EAX = 0FFFFh

; transferencia ultrarrápida ; POPAD falla en muchos 386 ; arreglar fallo de POPAD ; simular cambio normal de DI ; y de SI ; mover palabras de 16 bit ; ¿se usó página alternativa? ; ; ; ; ; ; está CX apilado de momento, no se usará más ** no quedan más palabras próxima página EMS ahora desde inicio página EMS

; DH=47h restaurar contexto EMS ; no hubo problemas

; ---- ¡Cuidado!: esta rutina debe ser invocada siempre ; con la pila (SP) tal y como estaba al principio ; del procedimiento «procesa_ems», y utilizando ; siempre CALL, para que en el caso de que haya ; errores retorne correctamente al nivel anterior ; (nivel previo a «procesa_ems»). Se corrompe DX ; y, si hay error, AX también (devuelve 810Ch). llama_EMM PROC XPUSH MOV llama_denuevo: MOV XPUSH INT MOV XPOP AND JZ CMP JE llama_ok: XPOP JNE RET ret_atras: POP MOV RET llama_EMM ENDP <AX,BX,CX,BP> AX,DX DX,CS:mem_handle <AX,BX> 67h CL,AH <BX,AX> CL,CL llama_ok CL,82h llama_denuevo <BP,CX,BX,AX> ret_atras AX AX,810Ch

; función en AX ; handle EMS ; llamar al EMM

; además, ZF = 1 ; intentarlo hasta que funcione

; ------------ Area residente dependiente del tipo de memoria empleada ; por el disco. La rutina instalada por defecto es la más ; larga de todas, para «dejar hueco» donde copiar encima ; las otras si se va a utilizar otro tipo de memoria. Si ; se modifican las rutinas, convendría medirlas por si ; acaso la de memoria EMS deja de ser la más larga... procesa_io EQU $

; sacar dirección de retorno ; error de «anomalía general» ; retornar dos niveles atrás

; ---- La rutina de gestión de memoria EMS transfiere ; bloques de hasta 16Kb de una vez. Intenta mapear ; en la página física 0: si no puede, debido a un ; solapamiento con el buffer de transferencia del ; programa principal (si está también en memoria ; EMS), utiliza otra página alternativa que dista

; ---- ¡Cuidado!: esta rutina debe ser invocada siempre ; con CX (y sólo CX) apilado: recarga CX desde la ; pila y corrompe BX dejando aún en la pila CX. copia_contexto PROC XPOP <BX,CX> ; equilibrar pila a llama_EMM

232

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

MOV CALL MOV CALL PUSH JMP copia_contexto ENDP coloca_regs PROC TEST JNZ XCHG XPUSH XPOP RET ENDP EQU

DH,48h llama_EMM DH,47h llama_EMM CX BX

; restaurar contexto EMS ; preservarlo de nuevo ; más rápido que PUSH BX/RET ; ¿invertir sentido?

BP,1 colocados SI,DI <DS,ES> <DS,ES>

; escritura: invertir sentido

colocados: coloca_regs tam_proc_ems

$-OFFSET procesa_ems

; tamaño de esta rutina

; <<< Fin del código residente del disco virtual >>> ; ************ Instalación (invocada desde CONFIG.SYS). init PROC MOV CALL LEA MOV INC MOV CMP JAE AND MOV MOV MOV MOV LEA MOV MOV CALL CALL MOV MOV MOV CALL CALL PUSH POP CMP JNE MOV MOV PUSH INT POP AND JZ LEA NEG JMP CMP JE CALL MOV CMP JBE MOV CALL TEST JNZ CMP JE CALL CMP JE CALL JC CALL CALL CALL CALL CALL CALL CMP JE CMP JE CALL CALL CMP JE CMP JNE MOV MOV JMP MOV MOV CMP JAE XCHG LDS ADD MOV MOV MOV RET ENDP CS:modo,CONFIG ; obtDosVer ; AX,retorno_ok CS:p_rutinas,AX ; CS:tipo_soporte ; CS:cs_tdsk,CS ; CS:dosver,300h ; dos_ok ; CS:tipo_drive,0F7FFh ; CS:num_ordenes,0Dh ; SI,[BX].bpb_cmd_desp ES,[BX].bpb_cmd_segm ; [BX].num_discos,1 ; AX,bpb_ptr [BX].bpb_cmd_desp,AX [BX].bpb_cmd_segm,CS ; desvia_int19 ; inic_letra ; BX,CS DS,BX ; BX,SI ; salta_nombre ; procesar_param ; DS ; ES ; param_b,ON pet_ayuda? AH,8 ; DL,80h ES 13h ; ES DL,3 pet_ayuda? ; AX,procesa_io AX bytes_res_ok ; param_h,ON fin_instalar ; max_sector ; BX,param_tsect BX,AX ; sect_def_ok ; bytes_sector,BX ; errores_config lista_err,ERROR0+ERROR1 fin_instalar ; param_tdisco,0 ; fin_instalar ; mem_info ; tdisco,0 ; fin_instalar ; mem_reserva ; fin_instalar ; test_CPU ; adaptar_param ; preparar_BPB ; prep_driver ; formatear_tdsk ; info_disco ; tipo_soporte,2 res_largo ; param_a,ON res_largo ; eval_xms eval_ems ems_kb,0 res_corto ; xms_kb,0 res_corto ; AX,tam_proc_ems rutina_larga,ON ; bytes_res_ok AX,tam_proc_xms ; BX,tam_proc_con AX,BX bytes_res_ok AX,BX BX,CS:pcab_peticion AX,OFFSET procesa_io [BX].fin_resid_desp,AX ; [BX].fin_resid_segm,CS ; AX,100h ; ejecutando desde CONFIG obtener versión del DOS anular rutina INIT 0: disco no formateado inicializar esa variable ¿DOS inferior al 3.0? DOS 3.0+ ajustar atributos y número de órdenes ES:SI -> parámetros una unidad de disco inicializado puntero BPB controlar INT 19h obtener letra de unidad DS: -> _PRINCIPAL ES:BX -> parámetros buscar inicio parámetros procesar parámetros ES: -> _PRINCIPAL opción /B ¿nº de discos duros? no existe disco duro no quedará residente piden ayuda obtener mayor sector ¿el nuestro es mayor? no sí: ajustar BPB

MOV CMP JNE CMP JE OR cabria_ems: TEST JNZ CMP JNE CMP JE CALL cont_instalar: CALL CMP JE CALL JC CALL CALL CALL CALL CALL CALL exit_instalar: CALL CMP JNE CALL MOV MOV INT fin_no_res: CALL MOV INT main ENDP

ES,segm_tdsk ; param_a,ON cabria_ems ES:rutina_larga,ON cabria_ems ; lista_err,ERROR2 lista_err,ERROR0+ERROR2 exit_instalar ; param_tdiscof,ON exit_instalar ; ES:tipo_soporte,0 cont_instalar ; desinstala ; mem_info ; tdisco,0 ; exit_instalar ; mem_reserva ; exit_instalar ; test_CPU ; adaptar_param ; preparar_BPB ; relocalizar ; prep_driver ; formatear_tdsk ; info_disco ; tipo_soporte,3 ; fin_no_res ; renombrar_mcb ; DX,6 ; AX,3100h 21h ; set_errorlevel ; AH,4Ch 21h ;

ES: --> disco residente

cabe la rutina EMS ; ¿error sintaxis ó EMS? sí: no modificar disco no indicado nuevo tamaño no estaba formateado aún liberar memoria ocupada evaluar memoria del PC ¿se reservará memoria? no: no hay más que hacer reservar memoria fallo reservando memoria detectar 386 ó superior adaptar parámetros disco BPB del nuevo disco autoreubicación de TDSK preparar el driver BOOT, FAT y ROOT informar sobre el disco ¿memoria convencional? no usada cambiar nombre del MCB usada: 96 bytes de PSP terminar residente preparar ERRORLEVEL final normal

dos_ok:

; ------------ Inicializar la variable con la versión del DOS obtDosVer PROC XPUSH MOV INT XCHG MOV XPOP RET ENDP <AX,BX,CX,DX> AH,30h 21h AH,AL CS:dosver,AX <DX,CX,BX,AX>

obtDosVer

; ------------ Determinar segmento del PSP, último segmento de memoria ; y liberar espacio de entorno. Se modifica también el ; bloque de memoria de TDSK reduciéndolo a 96 bytes: esto ; provoca la creación de un bloque de control de memoria ; en el offset 96 del PSP, lo cual no es peligroso. El ; objetivo de esta maniobra es poder asignar memoria al ; disco después (sólo si hace falta memoria convencional) ; usando los servicios estándar del DOS. gestionar_ram PROC MOV MOV MOV PUSH MOV MOV INT POP MOV MOV INT RET ENDP CS:segm_psp,DS AX,DS:[2] CS:top_ram,AX ES ES,DS:[2Ch] AH,49h 21h ES BX,6 AH,4Ah 21h ; indicar segmento del PSP ; segmento más alto ; indicar tope de memoria ; segmento del entorno ; liberar área de entorno ; ES: -> PSP ; hacer creer al DOS que ; TDSK ocupa sólo 96 bytes

pet_ayuda?:

sect_def_ok:

gestionar_ram algún error importante ¿se define disco ahora? no: no hay más que hacer evaluar memoria del PC ¿se reservará memoria? no: no hay más que hacer reservar memoria fallo al reservarla detectar 386 ó superior adaptar parámetros disco BPB del nuevo disco preparar el driver inic. BOOT, FAT y ROOT informar sobre el disco se utiliza memoria EMS se indicó /A

; ------------ Leer los parámetros de la línea de comandos (ES:BX). ; Se inicializan las correspondientes variables. En caso ; de error, se dejan a cero las variables y se acumula en ; «lista_err» un ERROR0 (error de sintaxis). procesar_param PROC CALL JC CALL JC MOV MOV p_param2: CALL JC CALL JC MOV p_param3: CALL JC CALL JC MOV p_param4: CALL JC CALL JC MOV p_param5: CALL JC CALL JC fin_param: CALL RET procesar_param ENDP param_barra PROC CMP JNE MOV JMP CMP JNE MOV JMP CMP JE CMP JNE MOV JMP CMP JNE MOV JMP CMP JE busca_param fin_param param_barra procesar_param param_tdisco,AX param_tdiscof,ON busca_param fin_param param_barra p_param2 param_tsect,AX busca_param fin_param param_barra p_param3 param_tdir,AX busca_param fin_param param_barra p_param4 param_tcluster,AX busca_param fin_param param_barra p_param5 validacion ; ; ; ; ; ; saltar delimitadores no hay más parámetros gestionar parámetro tipo "/A" era parámetro tipo "/A" es numérico: tamaño del disco parámetro de tamaño indicado

fin_instalar:

; tamaño de sector

no hay memoria EMS la hay, pero también XMS dejar sitio a rutina EMS dejar sitio a XMS/conv.

; entradas al directorio

res_largo: res_corto:

; tamaño de cluster ; últimas opciones posibles ; validación de parámetros

bytes_res_ok:

reservar memoria para las rutinas a usar instalación siempre Ok.

init

; ------------ Redefinición (invocada desde el AUTOEXEC.BAT o el DOS). p_exp1?: main PROC MOV CALL CALL MOV MOV MOV CALL CMP JE PUSH POP CALL TEST JNZ FAR CS:modo,AUTOEXEC obtDosVer gestionar_ram AX,_PRINCIPAL DS,AX BX,81h procesar_param param_h,ON exit_instalar DS ES errores_Dos err_grave,0FFFFh exit_instalar ; ; ; ; ; ; ; ejecutando desde el DOS obtener versión del DOS gestión de memoria programa de un segmento DS: -> _PRINCIPAL ES:BX línea de órdenes procesar parámetros p_exp: p_exp2?:

; piden ayuda ; ES: --> _PRINCIPAL ; algún error grave

p_ayuda?: p_ayuda: p_exit?:

AX,"e/" p_exp1? param_e,ON p_barra_exit AX,"a/" p_exp2? param_a,ON p_barra_exit AX,"x/" p_exp AX,"c/" p_ayuda? param_c,ON p_barra_exit AX,"h/" p_exit? param_h,ON p_barra_exit AX,"?/" p_ayuda

; ¿indicado /E?

; ¿indicado /A?

; /A y /X son equivalentes ; ¿indicado /C?

; ¿indicado /H?

; /H y /? son equivalentes

CONTROLADORES DE DISPOSITIVOS

233

CMP JNE MOV JMP param_id?: CMP JNE ADD CMP JE CMP JNE p_id_ok: CALL MOV MOV SUB JMP param_fats?: CMP JNE ADD CMP JE CMP JNE p_f_ok: CALL MOV SUB JMP param_b?: CMP JNE MOV JMP param_unidad?: CMP JNE AND MOV JMP param_num?: CMP JNE param_b_mal: OR param_num: CALL CLC RET p_barra_exit: ADD STC RET param_barra ENDP validacion PROC MOV CMP JE CMP JE CMP JE CMP JE CMP JE CMP JB CMP JA MOV CMP JE CMP JE CMP JE CMP JE CMP JE CMP JE CMP JE CMP JNE CMP JAE CMP JB CMP JBE MOV JMP MOV JMP MOV XOR MOV MOV MOV MOV OR RET ENDP PROC MOV INC CMP JE CMP JE CMP JE CMP JE AND JZ JMP DEC RET ENDP PROC DEC INC MOV CMP JE CMP JE CMP JE

AX,"m/" ; ¿indicado /M? param_id? param_m,ON p_barra_exit AX,"i/" ; ¿indicado /I= o /I:? param_fats? BX,3 BYTE PTR ES:[BX-1],’=’ p_id_ok BYTE PTR ES:[BX-1],’:’ param_b_mal obt_num ; leer código telefónico param_i,ON codigo_tfno,AX BX,2 p_barra_exit AX,"f/" ; ¿indicado /F= o /F:? param_b? BX,3 BYTE PTR ES:[BX-1],’=’ p_f_ok BYTE PTR ES:[BX-1],’:’ param_b_mal obt_num ; leer número de FATs param_f,AX BX,2 p_barra_exit AX,"b/" ; ¿indicado /B? param_unidad? param_b,ON p_barra_exit AH,’:’ ; ¿parámetro de unidad? param_num? AL,255-32 ; poner en mayúsculas param_unidad,AL p_barra_exit AL,’/’ param_num ; puede ser número lista_err,ERROR0 obt_num ; es parámetro numérico: leerlo ; no es parámetro barrado BX,2 ; saltar este parámetro ; es parámetro barrado

p_final: busca_param obt_num otro_digito:

CMP JE OR CLC RET STC RET ENDP PROC XPUSH XOR MOV CMP JB CMP JBE CMP JE CMP JE CMP JE CMP JE CMP JE JMP XOR MOV MUL JC XOR SUB ADD JC INC JMP MOV XPOP RET ENDP

AL,10 p_final AX," "

; poner en minúsculas ; se acabaron los parámetros

no_digito:

digito_ok:

num_incorr: fin_num: obt_num

<CX,DX,SI> AX,AX CL,ES:[BX] CL,’0’ no_digito CL,’9’ digito_ok CL,’ ’ fin_num CL,9 fin_num CL,13 fin_num CL,10 fin_num CL,’/’ fin_num num_incorr DX,DX SI,10 SI num_incorr CH,CH CL,’0’ AX,CX num_incorr BX otro_digito AX,65535 <SI,DX,CX>

; leer número: devolver 65535 ; si hay error ; número en proceso de creación

; posibles delimitadores...

; AX = AX * 10

; AX = AX + dato

; indicar valor incorrecto

; ------------ Detectar errores que se pueden producir sólo en la ; línea de comandos. PROC PUSH CMP JAE OR JMP existe_tdsk?: CALL CMP JNE OR JMP busca_unidad: MOV CMP JE CALL JC disco_defecto: CALL MOV CMP JBE OR MOV fin_err_Dos: CALL CALL POP RET errores_Dos ENDP errores_Dos ES dosver,200h existe_tdsk? err_grave,ERROR0 fin_err_Dos reside_tdsk? segm_tdsk,0 busca_unidad err_grave,ERROR1 fin_err_Dos ES,segm_tdsk param_unidad,0 disco_defecto obtener_segm fin_err_Dos max_sector BX,param_tsect BX,AX fin_err_Dos lista_err,ERROR3 param_tsect,0 test32Mb testWin ES

valida_tsect:

valida_tclus:

pf_a1: sintax_err:

AX,0FFFFh AX,param_tdisco ; sintax_err AX,param_tsect sintax_err AX,param_tdir sintax_err AX,param_tcluster sintax_err param_tdisco,0 valida_tsect ; param_tdisco,8 sintax_err param_tdisco,65534 sintax_err AX,param_tsect AX,0 valida_tclus ; AX,32 valida_tclus AX,64 valida_tclus AX,128 valida_tclus AX,256 valida_tclus AX,512 valida_tclus AX,1024 valida_tclus AX,2048 sintax_err param_tcluster,256 sintax_err ; param_f,1 pf_a1 ; param_f,2 ; fin_validar param_f,2 fin_validar param_f,1 fin_validar param_tdiscof,OFF ; AX,AX param_tdisco,AX param_tsect,AX param_tdir,AX param_tcluster,AX lista_err,ERROR0 ;

; necesario DOS 2.x+ ; error de DOS incorrecto ; ¿instalado TURBODSK? ; ya instalado ; error: TURBODSK no instalado ; ES: -> disco virtual ; ; ; ; no se indicó letra de unidad segmento del TDSK indicado fallo (no es unidad TDSK) obtener mayor sector

¿números correctos?

no indicado tamaño (o 0)

no indicado tamaño de sector

; tamaño de sector correcto ; el tamaño no definible ahora ; ignorar tamaño indicado

; ------------ Detectar errores que se pueden producir sólo desde ; el CONFIG.SYS errores_config PROC CMP JE OR no_unidad: CMP JNE OR fin_err_con: CALL RET errores_config ENDP param_unidad,0 no_unidad lista_err,ERROR1 param_c,ON fin_err_con lista_err,ERROR1 test32Mb

debe estar entre 0..255 /F=1 ó /F=2 exclusivamente si no, forzarlo y perdonar

; ------------ Preparar valor de ERRORLEVEL para el retorno. no definir disco ahora set_errorlevel PROC MOV TEST JNZ DEC TEST JNZ DEC TEST JNZ DEC TEST JNZ CMP JE MOV CMP JNE MOV fin_cod_ok: RET set_errorlevel ENDP AL,255 err_grave,ERROR1 ; ¿TDSK no instalado? fin_cod_ok AL err_grave,ERROR2 ; ¿unidad incorrecta? fin_cod_ok AL err_grave,ERROR3 ; ¿dentro de Windows? fin_cod_ok AL lista_err,ERROR0 ; error de sintaxis fin_cod_ok param_h,ON ; ayuda: handle desconocido fin_cod_ok AL,BYTE PTR ES:mem_handle ; handle XMS/EMS ES:tipo_soporte,0 fin_cod_ok AL,0 ; disco no formateado

aviso de error de sintaxis

fin_validar: validacion salta_nombre

fin_nombre: salta_nombre busca_param p_delimit:

AL,ES:[BX] BX AL,’ ’ fin_nombre AL,9 fin_nombre AL,0Dh fin_nombre AL,0Ah fin_nombre AL,AL fin_nombre salta_nombre BX

; saltar nombre del driver en ; línea de órdenes del CONFIG

; necesario para DOS 2.x

; ------------ Obtener mayor tamaño de sector definido en el sistema. max_sector PROC XPUSH MOV INT ADD CMP JAE INC MOV XPOP RET ENDP <BX,ES> AH,52h 21h BX,10h CS:dosver,30Ah psect_ok BX AX,ES:[BX] <ES,BX>

; Get List of Lists

; saltar delimitadores BX BX AX,ES:[BX] AL,’ ’ p_delimit AL,9 p_delimit AL,13 p_final

psect_ok: ; espacio en blanco ; tabulador ; CR ó LF indican el final max_sector

; DOS anterior al 3.1 ; mayor tamaño de sector ; definido por cualquier disp.

; ------------ Si el disco es de más de 32 Mb, comprobar si el sector

234

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

; test32Mb

es de al menos 1024 bytes. PROC CMP JBE CMP JAE OR MOV RET ENDP param_tdisco,32768 fin32mb param_tsect,1024 fin32mb lista_err,ERROR15 param_tdisco,32768

; ------------ Obtener la letra de la unidad de disco definida. Esta ; rutina se invoca sólo desde CONFIG.SYS con DS:BX ; apuntando a la cabecera de petición de la orden INIT. inic_letra ; sector de menos de 1024 ; evitar fallo posterior PROC XPUSH MOV ADD PUSH POP CMP JAE CALL LEA XOR cuenta_discos: ADD ADD CMP JNE ADD letra_ok: MOV XPOP RET inic_letra ENDP <AX,BX,SI,DS> AL,[BX].nuevo_disco AL,’A’ CS DS dosver,300h letra_ok lista_discos SI,area_trabajo AL,AL AL,[SI+2] SI,4 WORD PTR [SI],0 cuenta_discos AL,’A’ letra_unidad,AL <DS,SI,BX,AX>

; unidad en DOS 3.0+ ; DS -> _PRINCIPAL ; hallar unidad en DOS 2.x ; cuenta de discos

fin32mb: test32Mb

; ------------ Desde Windows, no se permite redefinir el disco. testWin PROC CMP JNE CMP JB MOV INT AND JZ CMP JE OR JMP MOV INT AND JZ RET ENDP param_tdiscof,ON fin_testWin dosver,300h fin_testWin AX,1600h 2Fh AL,AL noWinEnh AL,80h noWinEnh err_grave,ERROR3 fin_testWin AX,4680h 2Fh AX,AX siWin

; no redefinido el disco ; no buscar Windows en DOS 2.x ; ¿Windows en modo extendido? ; ¿Windows en modo extendido? ; estamos dentro de Windows

; guardar letra de unidad

siWin: noWinEnh:

; Windows en modo real/estándar

fin_testWin: testWin

; ------------ Crear una lista de todos los dispositivos de bloque ; del sistema. La lista tiene una entrada de 4 bytes ; para cada dispositivo: los dos primeros indican el ; segmento en que reside, el siguiente el número de ; unidades que controla y el último vale 1 ó 0 para ; indicar si es una unidad TDSK o no. El final de la ; lista lo señaliza un segmento igual a 0. lista_discos PROC XPUSH MOV INT MOV CMP JB MOV CMP JB MOV ADD LEA ADD LES CMP JE TEST JNZ MOV MOV MOV MOV PUSH LEA MOV MOV CLD REP POP JNE MOV MOV INC JMP MOV XPOP RET ENDP <AX,BX,CX,DX,SI,DI,ES> AH,52h ; "Get list of lists" 21h ; obtener puntero en ES:BX CX,17h ; supuesto DOS 2.x dosver,300h pdisp_ok CX,28h ; supuesto DOS 3.0x dosver,30Ah pdisp_ok CX,22h ; versiones del DOS superiores BX,CX DI,area_trabajo-4 ; tabla de dispositivos-4 DI,4 BX,ES:[BX] ; siguiente dispositivo BX,-1 disp_fin BYTE PTR ES:[BX+5],80h disp_skip ; es dispositivo de caracteres CL,ES:[BX+10] ; es de bloques [DI],ES ; anotar dirección [DI+2],CL BYTE PTR [DI+3],0 ; de momento, no es TDSK DI SI,id_tdsk ; identificación de TURBODSK DI,SI CX,5 CMPSB ; ¿es TURBODSK? DI disp_otro ; es de bloques, pero no TDSK AX,ES:cs_tdsk ; segmento real de TDSK [DI],AX ; corregir dirección en tabla BYTE PTR [DI+3] ; indicar dispositivo TDSK disp_otro ; buscar hasta completar tabla WORD PTR [DI],0 ; final de la lista <ES,DI,SI,DX,CX,BX,AX>

; ------------ Verificar la presencia en memoria de TURBODSK. Se ; inicializa «segm_tdsk» y «letra_unidad» indicando dónde ; reside el primer dispositivo TURBODSK de todos los que ; puede haber instalados. La letra de la unidad se halla ; del propio TDSK residente, para evitar conflictos con ; programas que manipulan ilegalmente la lista de ; unidades, del tipo de Stacker o Smartdrive. reside_tdsk? PROC XPUSH CALL LEA ADD CMP JNE SUB CMP JB CMP JNE MOV MOV PUSH MOV MOV POP MOV XPOP RET ENDP <AX, SI> lista_discos SI,area_trabajo-4 SI,4 WORD PTR [SI],0 busca_final ; ir al final de la tabla SI,4 SI,OFFSET area_trabajo fin_busca ; no reside (segm_tdsk = 0) BYTE PTR [SI+3],1 busca_tdsk AX,[SI] ; encontrada unidad TURBODSK segm_tdsk,AX DS DS,AX AL,letra_unidad ; con esta letra de unidad DS letra_unidad,AL <SI, AX>

pdisp_ok: disp_otro: disp_skip:

busca_final: busca_tdsk:

fin_busca: reside_tdsk?

; ------------ Obtener el segmento de la unidad TURBODSK indicada, si ; existe, accediendo a una tabla de dispositivos que se ; crea. A la salida, CF=1 si esa unidad no es TURBODSK. disp_fin: obtener_segm busca_ultimo: recorre_dsks: PROC CALL LEA ADD CMP JNE SUB CMP JB CMP JNE PUSH MOV MOV POP CMP JNE MOV MOV MOV MOV CLC RET OR STC RET ENDP lista_discos SI,area_trabajo-4 SI,4 WORD PTR [SI],0 busca_ultimo ; realmente, el primero SI,4 SI,OFFSET area_trabajo tdsk_no_hay BYTE PTR [SI+3],1 recorre_dsks DS DS,[SI] AL,letra_unidad ; unidad del TDSK residente DS AL,param_unidad ; disco TDSK: ¿es el buscado? recorre_dsks letra_unidad,AL ; inicializar letra de unidad AX,[SI] segm_tdsk,AX ; inicializar segmento ES,AX err_grave,ERROR2 ; unidad indicada no es TDSK lista_discos

; ------------ Liberar la memoria ocupada por un TURBODSK residente. desinstala PROC MOV MOV DEC JZ DEC JZ PUSH MOV MOV INT POP PUSH PUSHF MOV MOV INT PUSHF CMP JA MOV DEC MOV MOV MOV CLD MOV REP POPF JNC POPF POP STC JMP POPF POP JMP MOV CALL CMP JE STC JMP MOV INT CMP JE CMP JE STC MOV JNC OR DX,ES:mem_handle AL,ES:tipo_soporte AL libera_ext ; AL libera_exp ; ES ES,DX AH,49h ; 21h ES ES ; ES,ES:tdsk_psp ; AH,49h 21h dosver,31Eh mcb_ok AX,ES AX ES,AX DI,8 CX,DI AL,’ ’ STOSB lib_con_ok? ES ; ha habido fallo desinstalado ; recuperar condición de error ES desinstalado AH,0Ah ES:xms_driver AX,1 desinstalado desinstalado AH,45h 67h AH,0 desinstalado AH,82h libera_exp

liberar memoria extendida liberar memoria expandida liberar memoria convencional:

condición de error liberar PSP residente

tdsk_no_hay: obtener_segm

; DOS 3.31+: el MCB es correcto

; ------------ Colocar nuevo gestor de INT 19h al instalar TDSK desde ; el CONFIG.SYS. En algunos entornos multitarea basados ; en el modo virtual-86 del 386 y superiores, si no se ; libera la memoria EMS/XMS tras una cancelación de la ; tarea virtual, ésta queda permanentemente ocupada hasta ; un reset «frío» del sistema, sin poder ser aprovechada ; por los demás procesos. La INT 19h se ejecuta cuando la ; tarea en curso va a ser inminentemente cancelada por el ; sistema, y TURBODSK la intercepta para poder liberar la ; memoria EMS/XMS en el último instante. La rutina que ; controla INT 19h contiene código de 286, por lo que se ; chequea la presencia de este procesador. desvia_int19 PROC XPUSH MOV MOV CALL CMP JNE MOV INT MOV MOV LEA MOV INT XPOP RET ENDP <BX,DS,ES> BX,CS DS,BX test_CPU cpu286,ON fin_desvia19 AX,3519h 21h ant19off,BX ant19seg,ES DX,nueva_int19 AX,2519h 21h <ES,DS,BX>

; hasta DOS 3.30 borrar nombre ; liberado correctamente

mcb_ok:

lib_con_ok?: libera_ext:

; éxito al liberar memoria XMS ; fallo

; no es 286 ó superior ; ES:BX anterior INT 19h libera_exp:

; ¿EMM ocupado?

; nueva rutina de control desinstalado:

fin_desvia19: desvia_int19

; fallo al liberar memoria EMS ES:tipo_soporte,0 ; disco «no formateado» desins_ok lista_err,ERROR14 ; fallo al liberar memoria

CONTROLADORES DE DISPOSITIVOS

235

desins_ok: desinstala

STC RET ENDP

; ------------ Determinar la configuración del sistema: tipos de ; memoria y cantidad de la misma. Se indica en «tdisco» ; un valor 0 si no se define ahora el disco, sea cual sea ; el motivo del fallo, y se actualiza la variable que ; indica los mensajes de error y advertencia a imprimir. mem_info PROC MOV CALL CALL CALL MOV CMP JNE MOV AND JNZ OR JMP CMP JBE MOV OR MOV MOV JMP CMP JNE MOV AND JNZ OR JMP CMP JBE MOV OR MOV MOV JMP CMP JNE MOV AND JNZ OR JMP CMP JBE MOV OR MOV MOV JMP CMP JBE CMP JE MOV CMP JNE JMP MOV CMP JA JMP MOV OR JZ OR MOV CMP JAE MOV JMP CMP JE OR RET ENDP tdisco,0 ; eval_xms ; eval_ems ; eval_con ; AX,param_tdisco ; param_a,ON no_ems ; BX,ems_kb ; BX,BX usara_ems lista_err,ERROR7 ; mem_infoado AX,BX usar_ems ; AX,BX lista_err,ERROR4 ; tdisco,AX tipo_soporte,2 ; mem_infoado param_e,ON no_xms ; BX,xms_kb ; BX,BX usara_xms lista_err,ERROR6 ; mem_infoado AX,BX usar_xms ; AX,BX lista_err,ERROR4 ; tdisco,AX tipo_soporte,1 ; mem_infoado param_c,ON no_con ; BX,con_kb ; BX,BX usara_con lista_err,ERROR10 ; mem_infoado AX,BX usar_con ; AX,BX lista_err,ERROR4 ; tdisco,AX tipo_soporte,3 ; mem_infoado AX,xms_kb ; usar_xms ; ES:rutina_larga,ON valdria_ems BX,xms_kb BX,0 ; usara_xms ; usar_con? BX,ems_kb AX,BX nv_ems usar_ems ; BX,ems_kb BX,xms_kb usar_con? ; lista_err,ERROR4 ; AX,xms_kb AX,ems_kb usar_xms ; AX,ems_kb usar_ems ; modo,AUTOEXEC forzar_con ; lista_err,ERROR5 ; ley de Murphy inicializar «xms_kb» inicializar «ems_kb» inicializar «con_kb» cantidad de memoria necesaria no solicitan memoria EMS solicitan memoria EMS... no hay memoria EMS disponible

ems_existe: emm_llama:

emm_fatal: emm_responde:

emm_pag_ok: piden algo razonable rebajado el tamaño indicar memoria expandida no solicitan memoria XMS solicitan memoria XMS... no hay memoria XMS disponible piden algo razonable rebajado el tamaño indicar memoria extendida no solicitan memoria conv. solicitan memoria conv. ... no hay memoria conv. libre piden algo razonable rebajado el tamaño indicar memoria convencional no indicado tipo de memoria intentar emplear memoria XMS bxpositivo: emm_obt_kb: emm_pags_ok: emm_obt_pag:

usara_ems:

usar_ems: no_ems:

usara_xms:

usar_xms: no_xms: forzar_con:

ems_busca_i:

usara_con:

usar_con: no_con:

imposible usar EMS queda algo de XMS emm_kb_ok: ems_ok: emplear memoria EMS eval_ems no hay un ápice de XMS ni EMS rebajado el tamaño solicitado hay más o igual XMS que EMS hay algo de EMS (más que XMS) sólo se puede usar mem. conv. ho hay memoria EMS ni XMS hallada_pag: emm_busca_pag emm_busca_pag emm_otra_pag:

valdria_ems:

nv_ems:

LEA MOV CLD REP JE JMP MOV MOV INT AND JZ CMP LOOPE OR JMP MOV INT AND JZ CMP JE JMP MOV ADD MOV MOV MOV INT CMP JB MOV XPUSH POP MOV LEA INT POP AND JZ CMP JE JMP XOR CALL JC MOV INC CMP JE CALL JC MOV MOV SUB JNC NEG CMP JB MOV INT AND JZ CMP JE JMP MOV SHL MOV POP RET ENDP PROC LEA PUSH LODSW MOV LODSW CMP JE LOOP STC POP RET ENDP

SI,emm_id CX,8 CMPSB ems_existe ems_ok CX,8000h AH,40h 67h AH,AH emm_responde AH,82h emm_llama lista_err,ERROR9 ems_ok AH,41h 67h AH,AH emm_pag_ok AH,82h emm_responde emm_fatal ems_pagina0,BX BX,0C00h ems_paginai,BX ems_pagni,3 AH,46h 67h AL,40h emm_obt_kb ems4,ON <ES,DS> ES AX,5800h DI,area_trabajo 67h ES AH,AH emm_pags_ok AH,82h emm_obt_pag emm_fatal DX,DX emm_busca_pag emm_fatal ems_pagina0,BX DX DX,5 emm_fatal emm_busca_pag emm_fatal ems_paginai,BX ems_pagni,DL BX,ems_pagina0 bxpositivo BX BX,0C00h ems_busca_i AH,42h 67h AH,AH emm_kb_ok AH,82h emm_obt_kb emm_fatal CL,4 BX,CL ems_kb,BX ES ; ¿instalado controlador EMS? ; nº de intentos prudente

; fallo del EMM

; reintentar (EMM ocupado) ; inicializar página EMS ; página alternativa: la 3 ; obtener versión del EMM ; versión anterior a la 4.0

; obtener dirección de páginas

; buscar página 0 ; buscar la siguiente ; la 5ª y siguientes no valen ; ; ; > > <-- pág i ;0C00h 32 ; pá Kb ; rra ; fos > ; ; > <-- pág 0 ; no distan 32 Kb: buscar otra

; páginas EMS disponibles ; Kb EMS disponibles (0,16,...)

; buscar página nº DX (EMS 4.0) SI,area_trabajo CX BX,AX AX,DX hallada_pag emm_otra_pag CX ; BX = segmento de la página ; AX = nº de la página

usar_con?: mem_infoado: mem_info

; ---- Calcular memoria extendida disponible eval_xms PROC PUSH MOV INT MOV AND JZ MOV INT CMP JNE MOV INT MOV MOV MOV CALL AND JNZ CMP JE TEST JZ OR CMP JB MOV POP RET ENDP ES AX,352Fh 21h AX,ES AX,AX xms_ok AX,4300h 2Fh AL,80h xms_ok AX,4310h 2Fh xms_segm,ES xms_desp,BX AH,8 xms_driver AX,AX xms_kb_ok BL,0A0h xms_kb_ok BL,80h xms_kb_ok lista_err,ERROR8 AX,8 xms_ok xms_kb,AX ES ; ---- Calcular el tamaño del mayor bloque de memoria ; convencional disponible. Como mínimo se dejarán ; unos 128 Kb libres en él, para que el usuario ; pueda volver a ejecutar TDSK y el DOS tenga algo ; de memoria libre. A la mitad de esos 128Kb (para ; evitar solapamientos) es donde TURBODSK se ; autorelocalizará antes de formatear el disco. eval_con ; ¿hay controlador XMS? ; obtener su dirección PROC CMP JNE MOV MOV INT MOV MOV SHR SUB JC CMP JB MOV MOV MOV PUSH INT POP XPUSH ADD SUB MOV POP MOV INT POP RET ENDP modo,AUTOEXEC conv_ok AH,48h BX,0FFFFh 21h DX,BX CL,6 BX,CL BX,128 conv_ok BX,8 conv_ok con_kb,BX BX,DX AH,48h BX 21h BX <ES,AX> AX,BX AX,1024/16*64 segm_reubicar,AX ES AH,49h 21h ES ; ¿se ejecuta desde el DOS? ; no, desde el config ; pedir 1 Mb al DOS (fallará) ; tamaño del mayor bloque ; BX = Kb del mayor bloque ; restar 128 Kb ; no quedan ni 128 Kb ; no quedan siquiera 8 Kb ; tamaño del mayor bloque ; localizarlo (AX=segmento) ; ; ; ; ; preservar ES y segmento (AX) añadir longitud restar 64 Kb segmento de autoreubicación recuperar segmento del bloque

; dirección de INT 2Fh en ES:BX ; apunta a 0000:XXXX (DOS 2.x)

; preguntar memoria libre ; no hubo fallo ; asignada ya toda la memoria ; no hay memoria XMS disponible ; fallo real del controlador ; mayor bloque XMS disponible ; mínimo necesario: 8 Kb

xms_kb_ok: xms_ok: eval_xms

; ---- Calcular memoria expandida disponible. Si la ; versión del EMM es 4.0 o superior, las páginas ; de memoria expandida pueden no ser contiguas: ; buscar una que diste 32 Kb de la página 0. eval_ems PROC PUSH MOV INT MOV ES AX,3567h 21h DI,10

; liberarlo ; recuperar ES

conv_ok: eval_con

; vector de INT 67h en ES:BX

; ------------ Reservar la memoria llamando al gestor que la controla. ; Con memoria XMS y existiendo un controlador EMS 4.0+ se ; comprueba si el handle XMS provoca la creacción de otro ; en EMS (caso de QEMM386 y otros emuladores de EMS) y en

236

EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2

; ; mem_reserva

ese caso se le renombra, para mejorar la información de los programas de diagnóstico. AL,tipo_soporte AL mem_r_xms AL mem_r_ems CL,6 BX,tdisco BX,CL AH,48h 21h mem_handle,AX BX,segm_psp tdsk_psp,BX ems4,ON skip_lst_hndl BX,area_trabajo lista_handles AH,9 DX,tdisco xms_driver AX,AX mem_rda_xms lista_err,ERROR8 mem_handle,DX ; preservar condición de error ems4,ON skip_ren_hndl ren_handle BX,tdisco BX,15 BL,11110000b tdisco,BX CL,4 BX,CL AH,43h 67h AH,AH mem_rda_ems lista_err,ERROR9 mem_handle,DX ems4,ON nhandle_ok nombrar_hndl ; en EMS 4.0+ renombrar handle ; tipo de memoria empleada ; 1: memoria extendida XMS ; 2: memoria expandida EMS ; 3: memoria convencional

PROC MOV DEC JZ DEC JZ MOV MOV SHL MOV INT MOV MOV MOV RET mem_r_xms: CMP JNE LEA CALL skip_lst_hndl: MOV MOV CALL AND JNZ OR STC mem_rda_xms: MOV PUSHF CMP JNE CALL skip_ren_hndl: POPF RET mem_r_ems: MOV ADD AND MOV MOV SHR MOV INT AND JZ OR STC RET mem_rda_ems: MOV CMP JNE CALL nhandle_ok: CLC RET mem_reserva ENDP ren_handle PROC XPUSH POP LEA CALL LEA LEA MOV CLD REP JE MOV CALL POP RET ENDP PROC MOV XOR MOV LEA XPUSH INT XPOP CMP JE MOV JMP MOV ADD INC LOOP RET ENDP PROC MOV LEA MOV MOV INT RET ENDP

; segmento del disco virtual ; inicializar esta variable

; EMS 4.0+: listado de handles ; pedir memoria XMS ; fallo del controlador XMS ; indicar error

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; adaptar_param

descontanto el sector de arranque y el directorio raiz; y se aplica la siguiente fórmula, que devuelve el nº de cluster más alto del disco al considerar también la ocupación de la futura FAT (12 bits = 1,5 bytes): nsect * tamsect 2 * nsect * tamsect ------------------ + 1 = --------------------- + 1 tamcluster + 1,5 2 * tamcluster + 3 Al resultado se le suma 1, ya que los clusters se numeran a partir de 2, para calcular el cluster de nº más alto del disco. Si ese número es 4086 o más habrá de utilizarse una FAT de 16 bits, recalculándose la fórmula anterior sustituyendo 1,5 por 2 y 3 por 4. Al final, una vez determinado el tipo de FAT habrá de calcularse con exactitud el número de cluster más alto, ya que hay casos críticos en que una FAT12 no sirve pero al aplicar una FAT16 el número de clusters baja de nuevo de 4085 (debido al mayor consumo de disco de la FAT16) resultado de ello la asignación de una FAT12, pese a que se reserva espacio para la de 16. Hay que considerar además el caso de que el disco tenga 2 FAT. PROC MOV MOV MOV CMP JBE MOV CMP JBE MOV CMP JBE MOV CMP JBE MOV MOV CMP JB MOV CMP JAE CMP JB MOV CMP JB MOV CMP JB MOV MOV MOV MOV MUL MOV AND JNZ MOV CALL JNC OR JMP MOV MOV MOV SHR MOV AND JNZ MOV MOV XOR MOV DIV XCHG XOR DIV AND JZ INC CMP JB OR JMP MOV MUL MOV CALL MOV MOV XOR DIV MOV XOR MUL AND JZ MOV MOV AND JNZ MOV XOR SHL CMP JB OR JMP SHR MOV MUL JC CMP JA MOV MOV MOV MOV MOV MOV SHL SHR CALL CMP AX,tdisco ; en Kb BX,AX ; entradas de directorio propuestas CL,1 ; sectores por cluster propuestos AX,128 ; ¿disco de 128 Kb o menos? prop_ok BX,128 AX,512 ; ¿disco de 512 Kb o menos? prop_ok BX,256 AX,2042 ; ¿disco de casi 2 Mb o menos? prop_ok CL,2 ; evitar FAT16 AX,4084 ; ¿disco de casi 4 Mb o menos? prop_ok CL,4 ; evitar FAT16 hasta 8 Mb BX,384 AX,16384 ; ¿disco de menos de 16 Mb? prop_ok BX,512 dosver,300h prop_valido AX,4084*2 ; en DOS 2.xx evitar FAT16 prop_valido CL,8 AX,4084*4 prop_valido CL,16 AX,4084*8 prop_valido CL,32 tdir,BX tcluster,CL ; inicializar valores recomendados DX,1024 ; AX = tamaño del disco en Kb DX ; DX:AX = bytes totales del disco CX,param_tsect CX,CX tsect_def ; se ha definido tamaño de sector CX,tsect ; tamaño por defecto divCX nsect_ok ; menos de 65536 sectores: correcto lista_err,ERROR11 tsect_rec ; asumir por defecto y recalcular tsect,CX numsect,AX BX,AX BX,1 ; BX = 1/2 del nº total de sectores CX,param_tdir CX,CX tdir_def ; se ha definido nº entradas CX,tdir ; nº por defecto AX,tsect DX,DX SI,32 ; 32 bytes = tamaño entrada direct. SI ; AX nº entradas direct. por sector AX,CX DX,DX ; DX:AX = nº de entradas CX ; CX = entradas en cada sector DX,DX ; AX = nº sectores del ROOT dir_ok? AX ; redondear tamaño de ROOT AX,BX ; BX = 1/2 nº sectores del disco dir_ok lista_err,ERROR12 ; directorio excesivo tdir_rec ; directorio por defecto sdir,AX ts