PCA, PS2 ,IBM y AT

372

Click here to load reader

description

el universo digital de ibm, pc, at y ps2

Transcript of PCA, PS2 ,IBM y AT

Page 1: PCA, PS2 ,IBM y AT

EL UNIVERSODIGITAL

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

Page 2: PCA, PS2 ,IBM y AT

Limitación de garantía:

Pese a que todos los programas e ideas incluidas en el libro hansido probados, el autor y el editor no se responsabilizan de los daños quesu funcionamiento pueda ocasionar bajo ninguna circunstancia ni estánobligados a corregir el contenido del libro.

Marcas registradas:

IBM PCjr, PC, XT, AT, PS/2, OS/2 y Microchannel son marcasregistradas de International Business Machines.MS-DOS, WINDOWS, Microsoft C y Microsoft Macro Assembler sonmarcas 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 marcasregistradas de Borland International Inc.

Page 3: PCA, PS2 ,IBM y AT

EL UNIVERSO DIGITALDEL IBM PC, AT Y PS/2

Ciriaco García de Celis

Edición 4.0

Ediciones Grupo Universitario de Informática (Valladolid)

Page 4: PCA, PS2 ,IBM y AT

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.

Page 5: PCA, PS2 ,IBM y AT

5ÍNDICE

ÍNDICE

PRÓLOGO DE LA EDICIÓN 4.0 ELECTRÓNICA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11PRÓLOGO DE LA TERCERA EDICIÓN (1994) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1 - INTRODUCCIÓN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.1 - Números binarios, octales y hexadecimales . . . . . . . . . . . . . . . . . . . . . . . . . . 211.2 - Cambio de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221.3 - Estructura elemental de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221.4 - Operaciones aritméticas sencillas en binario . . . . . . . . . . . . . . . . . . . . . . . . . 231.5 - Complemento a dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231.6 - Agrupaciones de bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231.7 - Representación de datos en memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231.8 - Operaciones lógicas en binario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

2 - ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES . . . . . . . . . . . . . . . . 252.1 - Arquitectura Von Neuman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252.2 - El microprocesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262.3 - Breve historia del ordenador personal y el DOS . . . . . . . . . . . . . . . . . . . . . . . 27

3 - MICROPROCESADORES 8086/88, 286, 386, 486 y Pentium . . . . . . . . . . . . . . . . . . . 313.1 - Características generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.2 - Registros del 8086 y del 286 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333.3 - Registros del 386 y procesadores superiores . . . . . . . . . . . . . . . . . . . . . . . . . 363.4 - Modos de direccionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363.5 - La pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383.6 - Un programa de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4 - JUEGO DE INSTRUCCIONES 80x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414.1 - Descripción completa de las instrucciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4.1.1 - De carga de registros y direcciones . . . . . . . . . . . . . . . . . . . . . . . . . 414.1.2 - De manipulación del registro de estado . . . . . . . . . . . . . . . . . . . . . . 434.1.3 - De manejo de la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454.1.4 - De transferencia de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464.1.5 - De entrada/salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494.1.6 - Aritméticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

Suma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49Resta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51Multiplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53División . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54Conversiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

4.1.7 - Manipulación de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554.1.8 - Operaciones lógicas a nivel de bit . . . . . . . . . . . . . . . . . . . . . . . . . . 584.1.9 - De control del procesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594.1.10 - De rotación y desplazamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

4.2 - Resumen alfabético de las instrucciones y banderines. Índice. . . . . . . . . . . . . 634.3 - Instrucciones específicas del 286, 386 y 486 en modo real . . . . . . . . . . . . . . . 64

4.3.1 - Diferencias en el comportamiento global respecto al 8086 . . . . . . . . . 644.3.2 - Instrucciones específicas del 286 . . . . . . . . . . . . . . . . . . . . . . . . . . . 654.3.3 - Instrucciones propias del 386 y 486 . . . . . . . . . . . . . . . . . . . . . . . . . 664.3.4 - Detección de un sistema AT o superior . . . . . . . . . . . . . . . . . . . . . . 684.3.5 - Evaluación exacta del microprocesador instalado . . . . . . . . . . . . . . . 684.3.6 - Modo plano (flat) del 386 y superiores . . . . . . . . . . . . . . . . . . . . . . . 70

Page 6: PCA, PS2 ,IBM y AT

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

5 - EL LENGUAJE ENSAMBLADOR DEL 80x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715.1 - Sintaxis de una línea en ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715.2 - Constantes y operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

5.2.1 - Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725.2.2 - Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725.2.3 - Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.2.4 - Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.2.5 - Operadores de retorno de valores . . . . . . . . . . . . . . . . . . . . . . . . . . 735.2.6 - Operadores de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

5.3 - Principales directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755.3.1 - De definición de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755.3.2 - De definición de símbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755.3.3 - De control del ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 765.3.4 - De definición de segmentos y procedimientos . . . . . . . . . . . . . . . . . 765.3.5 - De referencias externas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785.3.6 - De definición de bloques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785.3.7 - Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805.3.8 - De listado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

5.4 - Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815.4.1 - Definición y borrado de las macros . . . . . . . . . . . . . . . . . . . . . . . . . 815.4.2 - Ejemplo de una macro sencilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825.4.3 - Parámetros formales y parámetros actuales . . . . . . . . . . . . . . . . . . . 825.4.4 - Etiquetas dentro de macros. Variables locales. . . . . . . . . . . . . . . . . . 835.4.5 - Operadores de macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845.4.6 - Directivas útiles para macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855.4.7 - Macros avanzadas con número variable de parámetros . . . . . . . . . . 87

5.5 - Programación modular y paso de parámetros . . . . . . . . . . . . . . . . . . . . . . . . 886 - EL ENSAMBLADOR EN ENTORNO DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

6.1 - Tipos de programas ejecutables bajo DOS . . . . . . . . . . . . . . . . . . . . . . . . . . 916.2 - Ejemplo de programa de tipo COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916.3 - Ejemplo de programa de tipo EXE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926.4 - Proceso de ensamblaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946.5 - La utilidad DEBUG/SYMDEB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966.6 - Las funciones del DOS y de la BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

7 - ARQUITECTURA DEL PC, AT y PS/2 BAJO DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . 1037.1 - Las interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1037.2 - La memoria. Los puertos de entrada y salida. . . . . . . . . . . . . . . . . . . . . . . . . 1057.3 - La pantalla en modo texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057.4 - La pantalla en modo gráfico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

7.4.1 - Modos gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067.4.2 - Detección de la tarjeta gráfica instalada . . . . . . . . . . . . . . . . . . . . . . 1087.4.3 - Introducción al estándar gráfico VGA . . . . . . . . . . . . . . . . . . . . . . . . 1087.4.4 - Ejemplo de gráficos empleando la BIOS . . . . . . . . . . . . . . . . . . . . . 1147.4.5 - Ejemplo de gráficos a nivel hardware . . . . . . . . . . . . . . . . . . . . . . . . 1157.4.6 - El estándar gráfico VESA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

7.5 - El teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197.5.1 - Bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197.5.2 - Nivel intermedio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227.5.3 - Alto nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

7.6 - Los discos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257.6.1 - Estructura física . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257.6.2 - Cabeza 0. Pista 0. Sector 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267.6.3 - La FAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277.6.4 - El directorio raíz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Page 7: PCA, PS2 ,IBM y AT

7ÍNDICE

7.6.5 - Los subdirectorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1307.6.6 - El BPB y el DPB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317.6.7 - La BIOS y los disquetes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317.6.8 - Disquetes floptical 3½ de 20 Mb . . . . . . . . . . . . . . . . . . . . . . . . . . . 1327.6.9 - Ejemplo de acceso al disco a alto nivel . . . . . . . . . . . . . . . . . . . . . . 1327.6.10 - Ejemplo de acceso al disco a bajo nivel . . . . . . . . . . . . . . . . . . . . . 133

7.7 - El PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377.8 - El proceso de arranque del PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397.9 - Formato de las extensiones ROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397.10 - Formato físico de los ficheros EXE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

8 - LA GESTIÓN DE MEMORIA DEL DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1438.1 - Tipos de memoria en un PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1438.2 - Bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

8.2.1 - El bloque de memoria del programa . . . . . . . . . . . . . . . . . . . . . . . . 1458.2.2 - El bloque del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1458.2.3 - Los bloques de control de memoria (MCB’s) . . . . . . . . . . . . . . . . . . 1468.2.4 - La cadena de los bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . 1468.2.5 - Relación entre bloque de programa y de entorno . . . . . . . . . . . . . . . 1478.2.6 - Tipos de bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1478.2.7 - Liberar el espacio de entorno en programas residentes . . . . . . . . . . . 1488.2.8 - Peculiaridades del MS-DOS 4.0 y 5.0 . . . . . . . . . . . . . . . . . . . . . . . 1488.2.9 - Cómo recorrer los bloques de memoria. Ejemplo. . . . . . . . . . . . . . . . 149

8.3 - Memorias extendida y superior XMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1528.4 - Memoria expandida EMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

9 - SUBPROCESOS, RECUBRIMIENTOS Y FILTROS . . . . . . . . . . . . . . . . . . . . . . . . . . . 1579.1 - Llamada a subprocesos y recubrimientos u overlays . . . . . . . . . . . . . . . . . . . 1579.2 - Construcción de filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

10 - PROGRAMAS RESIDENTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16110.1 - Principios básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16110.2 - Un ejemplo sencillo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16210.3 - Localización de un programa residente . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

10.3.1 - Método de los vectores de interrupción . . . . . . . . . . . . . . . . . . . . . 16310.3.2 - Método de la cadena de bloque de memoria . . . . . . . . . . . . . . . . . 16310.3.3 - Método de la interrupción Multiplex . . . . . . . . . . . . . . . . . . . . . . . . 164

10.4 - Expulsión de un programa residente de la memoria . . . . . . . . . . . . . . . . . . . 16410.5 - Gestión avanzada de la interrupción Multiplex . . . . . . . . . . . . . . . . . . . . . . . 165

10.5.1 - El convenio BMB Compuscience . . . . . . . . . . . . . . . . . . . . . . . . . . 16510.5.2 - El convenio CiriSOFT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16510.5.3 - La propuesta AMIS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17010.5.4 - Comparación entre métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

10.6 - Métodos especiales para economizar memoria . . . . . . . . . . . . . . . . . . . . . . 17210.7 - Programas autoinstalables en memoria superior . . . . . . . . . . . . . . . . . . . . . . 17310.8 - Programas residentes en memoria extendida con DR-DOS 6.0 . . . . . . . . . . . 17410.9 - Ejemplo de programa residente que utiliza la BIOS . . . . . . . . . . . . . . . . . . . 17610.10 - Uso sin límites de servicios del DOS en programas residentes . . . . . . . . . . 184

10.10.1 - Una primera aproximación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18510.10.2 - Pasos a realizar para usar el DOS . . . . . . . . . . . . . . . . . . . . . . . . 18610.10.3 - Resumiendo, ¡no es tan difícil! . . . . . . . . . . . . . . . . . . . . . . . . . . . 18710.10.4 - Un método alternativo: el SDA . . . . . . . . . . . . . . . . . . . . . . . . . . . 18810.10.5 - Métodos menos ortodoxos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189

10.11 - Ejemplo de programa residente que utiliza el DOS . . . . . . . . . . . . . . . . . . . 18910.12 - Programas residentes invocables en modos gráficos . . . . . . . . . . . . . . . . . 19710.13 - Programas residentes en entorno WINDOWS 3 . . . . . . . . . . . . . . . . . . . . . 199

Page 8: PCA, PS2 ,IBM y AT

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

11 - CONTROLADORES DE DISPOSITIVO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20311.1 - Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20311.2 - Encabezamiento y palabra de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20311.3 - Rutinas de estrategia e interrupción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20511.4 - Ordenes a soportar por el controlador de dispositivo . . . . . . . . . . . . . . . . . . 20511.5 - La cadena de controladores de dispositivo instalados . . . . . . . . . . . . . . . . . . 21011.6 - Ejemplo de controlador de dispositivo de caracteres . . . . . . . . . . . . . . . . . . . 21211.7 - Ejemplo de controlador de dispositivo de bloques . . . . . . . . . . . . . . . . . . . . . 214

11.7.1 - Disco virtual TURBODSK: Características . . . . . . . . . . . . . . . . . . . 21411.7.2 - Ensamblando TURBODSK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21611.7.3 - Análisis detallado del listado de TURBODSK . . . . . . . . . . . . . . . . . 216

11.8 - Los controladores de dispositivo y el DOS . . . . . . . . . . . . . . . . . . . . . . . . . . 24412 - EL HARDWARE DE APOYO AL MICROPROCESADOR . . . . . . . . . . . . . . . . . . . . . 245

12.1 - La arquitectura del ordenador compatible . . . . . . . . . . . . . . . . . . . . . . . . . . . 24512.2 - El interfaz de periféricos 8255 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247

12.2.1 - Descripción del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24712.2.2 - El 8255 en el PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24812.2.3 - Un método para averiguar la configuración del PC/XT . . . . . . . . . . . 248

12.3 - El temporizador 8253 u 8254 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24912.3.1 - Descripción del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24912.3.2 - El 8254 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25512.3.3 - Temporización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25612.3.4 - Síntesis de sonido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258

12.4 - El controlador de interrupciones 8259 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26112.4.1 - Cómo y por qué de las interrupciones . . . . . . . . . . . . . . . . . . . . . . 26112.4.2 - Descripción del integrado 8259 . . . . . . . . . . . . . . . . . . . . . . . . . . . 26112.4.3 - El 8259 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26712.4.4 - Ejemplo: cambio de la base de las interrupciones . . . . . . . . . . . . . . 269

12.5 - El chip DMA 8237 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27012.5.1 - El acceso directo a memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27012.5.2 - Descripción del integrado 8237 . . . . . . . . . . . . . . . . . . . . . . . . . . . 27012.5.3 - El 8237 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27912.5.4 - Ralentizar un equipo AT con el DMA . . . . . . . . . . . . . . . . . . . . . . . 28112.5.5 - Acerca de las páginas de DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

12.6 - El controlador de disquetes NEC 765 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28412.6.1 - La tecnología de grabación en disco . . . . . . . . . . . . . . . . . . . . . . . 28412.6.2 - Descripción del FDC (Floppy Disk Controller) 765 . . . . . . . . . . . . . . 28612.6.3 - El 765 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29412.6.4 - Densidades de disco y formatos estándar . . . . . . . . . . . . . . . . . . . 29412.6.5 - Acceso a disco con DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29712.6.6 - Lectura y escritura de sectores de disco sin DMA . . . . . . . . . . . . . . 30512.6.7 - Programación avanzada del controlador de disquetes: 2M 3.0 . . . . . 309

12.6.7.1 - Formato de la primera pista . . . . . . . . . . . . . . . . . . . . . . . 31112.6.7.2 - Puntualizaciones sobre el formato de máxima capacidad . . 31512.6.7.3 - Descripción de funcionamiento del soporte residente . . . . . 31612.6.7.4 - Descripción del programa de formateo (2MF) para 2M . . . . 33012.6.7.5 - Un programa para medir el rendimiento de los disquetes . . 33812.6.7.6 - La versión para PC/XT de 2M: 2MX . . . . . . . . . . . . . . . . . 34012.6.7.7 - La opción BIOS de 2M: 2M-ABIOS y 2M-XBIOS . . . . . . . . 34112.6.7.8 - La utilidad 2MDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34112.6.7.9 - Cómo superar los 2.000.000 de bytes en 3½: 2MGUI . . . . 34212.6.7.10 - Uso de 2M 3.0 en OS/2 2.1 . . . . . . . . . . . . . . . . . . . . . . 345

12.7 - El disco duro del AT (IDE, MFM, Bus Local) . . . . . . . . . . . . . . . . . . . . . . . . 34612.7.1 - El interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346

Page 9: PCA, PS2 ,IBM y AT

9ÍNDICE

12.7.2 - Programación de la controladora . . . . . . . . . . . . . . . . . . . . . . . . . . 34612.7.3 - Ejemplo práctico de programación . . . . . . . . . . . . . . . . . . . . . . . . . 349

12.8 - El controlador del teclado: 8042 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35112.8.1 - El 8042 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35112.8.2 - El teclado del AT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35212.8.3 - Comunicación CPU teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . 35212.8.4 - Comunicación teclado CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . 355

12.9 - El puerto serie: UART 8250 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35612.9.1 - Descripción del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35612.9.2 - El 8250 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36312.9.3 - Ejemplo: autodiagnóstico del 8250 . . . . . . . . . . . . . . . . . . . . . . . . . 364

12.10 - El puerto de la impresora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36512.10.1 - Los registros del puerto paralelo . . . . . . . . . . . . . . . . . . . . . . . . . 36512.10.2 - Envío de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36512.10.3 - Cable NULL-MODEM para conectar dos ordenadores . . . . . . . . . . 366

12.11 - El ratón . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36712.12 - El reloj de tiempo real del AT: Motorola MC146818 . . . . . . . . . . . . . . . . . . 368

12.12.1 - Descripción del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36812.12.2 - El MC146818 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . 37012.12.3 - Un método para averiguar la configuración del AT y PS/2 . . . . . . . 371

13 - EL ENSAMBLADOR Y EL LENGUAJE C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37313.1 - Uso del Turbo C y Borland C a bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . 373

13.1.1 - Acceso a los puertos de E/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37313.1.2 - Acceso a la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37313.1.3 - Control de interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37413.1.4 - Llamada a interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37413.1.5 - Cambio de vectores de interrupción . . . . . . . . . . . . . . . . . . . . . . . . 37413.1.6 - Programas residentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37513.1.7 - Variables globales predefinidas interesantes . . . . . . . . . . . . . . . . . . 37513.1.8 - Inserción de código en línea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37513.1.9 - Las palabras clave interrupt y asm . . . . . . . . . . . . . . . . . . . . . . . . . 375

13.2 - Interfaz C (Borland/Microsoft) - Ensamblador . . . . . . . . . . . . . . . . . . . . . . . . 37613.2.1 - Modelos de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37613.2.2 - Integración de módulos en ensamblador . . . . . . . . . . . . . . . . . . . . 376

APÉNDICES:I Mapa de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381

II Tabla de interrupciones del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383III Tabla de variables de la BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385IV Puertos de E/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389V Códigos de rastreo del teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391

VI Tamaños y tiempos de ejecución de las instrucciones . . . . . . . . . . . . . . . . . 393VII Señales del slot de expansión ISA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399

VIII Funciones del sistema, la BIOS y el DOS aludidas en este libro . . . . . . . . . 401IX Especificaciones XMS y EMS: Todas sus funciones . . . . . . . . . . . . . . . . . . 423X Juego de caracteres ASCII extendido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427

XI Bibliografía . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429

Page 10: PCA, PS2 ,IBM y AT
Page 11: PCA, PS2 ,IBM y AT

11PRÓLOGO DE LA EDICIÓN 4.0 ELECTRÓNICA

PRÓLOGO

DE LA EDICIÓN 4.0 ELECTRÓNICA*

(*) http://www.gui.uva.es/udigital

Nota: Pudiendo haber discrepancias entre sucesivas ediciones de estas normas, laversión de referencia válida e inapelable será la ubicada en todo momento en lared, en la dirección electrónica arriba indicada o cualquier otra que pudierasucederla.

Licencia de uso y distribución para particulares.

La edición 4.0 (4ª edición) de El Universo Digital del IBM PC, AT y PS/2 es un libroelectrónico/impreso de dominio público; de libre uso, difusión, copia y distribución entreparticulares, en cualquier soporte. Quienes decidan utilizarlo deberán registrarse por víaelectrónica una sola vez, por razones de ética (http://www.gui.uva.es/udigital). También esposible hacerlo enviando una carta o postal ordinaria (mejor en un sobre) al autor, concualquier texto, a la siguiente dirección:

Ciriaco García de CelisApartado 6105

47080 ValladolidEspaña

Indicando claramente que el motivo es registrar el Universo Digital. Los que hayancomprado la versión impresa en persona no necesitan registrarse, aunque lo recibiría conagrado, incluso si ha pasado bastante tiempo (pero si lo compraron por correo no debenregistrarse: conservo su pedido). Me gustaría conocer en alguna medida la difusión de la obra,en especial a partir de este momento, lo que hasta ahora me resultaba algo más sencillo. Porsupuesto, los datos o direcciones indicadas por los usuarios nunca serán divulgados por mí.

Page 12: PCA, PS2 ,IBM y AT

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

Licencia de uso para empresas, asociaciones y organizaciones.

Se aplican exactamente las mismas condiciones que para usuarios particulares, con laexcepción de que se recomienda un único registro electrónico o una sola carta o postal enrepresentación de todos los posibles usuarios de la entidad.

Licencia de distribución para empresas, asociaciones y organizaciones.

Editando revistas (no libros) la distribución está permitida en cualquier formato digital(HTML, PostScript, WordPerfect, texto, o cualesquiera otros) tanto en fragmentos como todala obra completa. Siendo el formato una revista impresa sólo se permiten fragmentos que nototalicen más del 75% de la obra en los sucesivos números publicados. Es necesario citar laprocedencia. La distribución por empresas que cobren una cierta cantidad por el soporte eslibre. Mi única sugerencia es que la empresa me envíe una copia del soporte (CD, etc.) en quese publique, por cortesía.

Tratándose de empresas editoriales u otras cualesquiera que planeen incluirlo, enteroo por fragmentos, en el soporte impreso, electrónico u online de algún libro que vayan apublicar, deberían contactar primero conmigo para negociar una nueva versión (que en todocaso no implicaría la desaparición de ésta en su estatus actual).

Modificaciones.

La realización de cambios (añadidos, eliminación de contenidos o reemplazamiento delos mismos) es competencia exclusiva del autor, que centraliza la generación de nuevasversiones actualizadas. Quien realizara alguna modificación sin consentimiento habría dedestinar la obra resultante para uso personal e intransferible.

Orígenes de El Universo Digital.

El Universo Digital no nació tras una decisión premeditada. Su objetivo inicial fue dotarde un manual de apoyo al Curso de Lenguaje Ensamblador, que ofrece todos los años laasociación Grupo Universitario de Informática de la Universidad de Valladolid, en el marcode unos Cursos de Introducción a la Informática -para los alumnos y personal en general dela Universidad- que abarcan un espectro mucho más amplio que el de la programación de losordenadores.

La primera versión ocupaba 116 páginas, cuando su denominación era aún la de Cursode Ensamblador. Sin embargo, en una época en la que era difícil encontrar información, y buenabibliografía especializada, el autor siguió recopilando material interesante y añadiéndolo alcurso. Una buena parte de dicho material y del añadido después ha sido además de cosechapropia. La primera edición de El Universo Digital, editada no mucho tiempo después delmanual del curso, rebasó ligeramente las 300 páginas. Posteriormente se incrementaría aúnalgo más, hasta las 420 de la 3ª edición que ha mantenido durante la mayor parte del tiempo.

Page 13: PCA, PS2 ,IBM y AT

13PRÓLOGO DE LA EDICIÓN 4.0 ELECTRÓNICA

El DOS en la actualidad.

Actualmente, y desde hace algún tiempo, la programación en DOS ya no es importante,y mucho menos al nivel que desarrolla este libro, y ello pese a que incluso Windows 95 correaún en alguna parte sobre DOS, comportamiento que irá reduciéndose hasta la eliminaciónen próximas versiones.

El futuro de la programación, sin embargo, no es sólo para los programadores de altonivel. En alguna manera, los propios usuarios pueden y podrán cada vez en mayor medidahacer sus propios programas incluso sin darse cuenta. Sin embargo, siempre hay alguien quetiene que construir los sistemas operativos, y sobre todo, los controladores para dar soportea los dispositivos en los diversos sistemas operativos. Por no mencionar las aplicacionesespecializadas, desde máquinas industriales al microprocesador de las sondas espaciales (que,evidentemente, no corre bajo Windows). Es para los programadores de sistemas, y paraaquellos que necesitan o quieren saber cómo funciona el PC por dentro, como ejemplo prácticode arquitectura interna de un ordenador, para los que va destinado este libro. Que podránpracticar en un entorno cómodo para este tipo de programación, como es el DOS (que dejatodo el control de la máquina a cada tarea). Aunque algunos contenidos muy relacionados conel DOS siguen presentes en esta obra, el lector habrá de tener en cuenta si es pertinenteprofundizar en ellos o no, en la época que vivimos.

Mis contactos con editoriales.

Mi objetivo inicial no fue publicarlo, aunque hace dos o tres años sí me lo planteé unpoco en serio.

Las ventajas de una edición oficial sería su no engorrosa distribución (uno de losmotivos por los que siempre ha costado poco es porque nuestra Asociación y el propio autorha puesto su mano de obra gratis), así como su mayor difusión. Puesto en contacto con cuatroprestigiosas editoriales; las que han respondido han valorado muy positivamente la obra, sinembargo la han rechazado aduciendo otros motivos («sobrecarga del programa editorial»,solapamiento en contenidos con «obras publicadas o en fase de publicación», o simplemente «faltade interés comercial»). Una de ellas aún no ha respondido.

Los inconvenientes de su publicación por una editorial serían el importante aumentode precio, y mi renuncia a los derechos de distribución (en particular, nuestra Asociacióntendría que comprar en la librería los ejemplares para nuestros cursos).

Sin embargo, la ventaja de la publicación para facilitar la difusión popular es obvia, máximesi lo hace una editorial importante (si no, no aparecería en todas las estanterías, la publicidad laharían los lectores lentamente, como ya se venía haciendo, y la distribución sería incluso máslimitada pese al recurso a los baratos servicios de reprografía por parte de los usuarios).

El Universo Digital en Internet.

Mi decisión final ya la había acariciado con anterioridad. Algo había que hacer, puesla distribución gratuita del libro llevaba mucho tiempo.

Uno de los motivos que han terminado empujándome a esta decisión, ha sido laconsiderable cantidad de pedidos que hemos recibido desde países de hispanoamérica. Se trata

Page 14: PCA, PS2 ,IBM y AT

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

de ciudadanos que conocen el índice del libro a través del Web y lo piden, sobre todo desdeMéxico. Sin embargo, sólo en la primera ocasión lo he enviado (a Perú); los motivos son,desgraciadamente, la práctica imposibilidad de comerciar a pequeña escala con esos países (noexiste el envío contrarreembolso, por ejemplo); 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 einjustas comisiones bancarias (aunque las pague el usuario final); finalmente habría que añadirincluso mi temor inconsciente a un aumento incontrolado de la demanda, cuando ya habíademasiado trabajo que hacer para atender la de origen nacional (en mi memoria estaba lo queocurrió cuando empezaron a aparecer mensajes y comenzaron a recibirse pedidos porFidoNET). 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 alrespecto.

El Universo Digital de dominio público en formato electrónico, podrá ser accedidodesde cualquier lugar del mundo, y en cualquier CD de los kioscos.

El inconveniente es que no todos tienen igual acceso a estas redes y medios, aunqueese inconveniente disminuirá exponencialmente con el tiempo (con el mismo exponente conque crezca la red).

Fin de la distribución impresa.

Naturalmente, una vez que he renunciado a mis derechos sobre el libro, donándolo aldominio público, ya no estoy obligado a venderlo impreso (medida tomada únicamente paramantener el copyright). Realmente, no tenemos tiempo ni medios para atender la demandaactual: aunque es una medida dura de imponer, lamento renunciar a realizar más envíos deejemplares impresos. Renuncio con ello a facilitar su difusión a los lectores menos introducidosen las redes telemáticas, pero beneficio a otros muchos, que además podrán seguir usando laversión manuscrita utilizando una impresora.

Por otro lado, haber facturado sólo aproximadamente el coste de impresión ydistribución, me permiten tomar esa decisión sin temer el enfado de quienes lo habíancomprado. El coste de impresión de los últimos números en la reprografía oficial de laUniversidad (rechazamos opciones más baratas de menor calidad), encuadernación y disqueteera de 1900 pts. El libro (realmente, apuntes técnicos fotocopiados) se vendía a 2100 pts más gastosde envío. Ese margen de beneficios era más bien de maniobra, ya que por ejemplo, en losejemplares que no llegaban a su destino, el coste del envío y la devolución lo pagábamosnosotros. Cada envío llevaba una media de 20 minutos de tiempo total de mano de obra,contabilizando la preparación de los libros (transporte físico, disquete, gestión del pedido...),y la mayoría eran de una sola unidad (pese a que se penalizaba su envío con 100 ptsadicionales). El precio de los más de 1200 Universos Digitales vendidos ha tenido uncrecimiento nominal cero en los cinco años de difusión impresa.

Obtención de ejemplares impresos.

Aunque en general no se harán más envíos, la única excepción corresponderá a los pedidosrealizados desde bibliotecas (universitarias o no universitarias), que tal vez no tengan la impresoraadecuada o tiempo para reproducirlo, lo que perjudicaría a un amplio conjunto potencial deusuarios. No se harán envíos a otras organizaciones, ni a librerías o a particulares. Subrayamosque El Universo Digital impreso tiene el carácter legal de apuntes técnicos impresos y no de libro.

Page 15: PCA, PS2 ,IBM y AT

15PRÓLOGO DE LA EDICIÓN 4.0 ELECTRÓNICA

Los pedidos de ejemplares impresos serán admitidos sólo desde España. Habrán derealizarse exclusivamente por carta impresa, que deberá estar compulsada por el sello y en sucaso papel oficial de la biblioteca que hace el pedido, además de debidamente firmada porquien corresponda. Es conveniente que figure el teléfono de la biblioteca o en su defecto dela conserjería del centro. Además del nombre completo, dirección y NIF. Nos reservamos elderecho de rechazar aquellos pedidos que no cumplan alguno de estos requisitos, o los desospechosa procedencia. La dirección es: Grupo Universitario de Informática. Apartado 6062.47080 Valladolid. El precio por ejemplar será el que figure en la factura que realizará el propioservicio de reprografía (unas 2000 pts/unidad); sumando al final el coste exacto del envío ylos disquetes.

Agradecimientos.

Agradezco desde aquí al servicio de Reprografía de la Universidad, ubicado en la Casadel Estudiante, el esmero puesto durante tanto tiempo en la reproducción y encuadernaciónde cada número durante la etapa impresa. Cualquier pequeño problema de calidad se hadebido siempre a los fallos inevitables que en ocasiones presenta toda máquina, por buena quesea.

Mis agradecimientos también a las diversas instituciones de la Universidad deValladolid, que han recibido en ocasión la presión de la demanda a través de incorrectasllamadas telefónicas solicitando el libro, no siendo ellos los encargados de su distribución;también al Grupo Universitario de Informática, por su colaboración a todos los niveles.

No puedo decir lo mismo de los funcionarios de Correos: aunque algunos son amables,en general, el funcionamiento de esa institución es el que cabía esperar de un monopolio nosometido a la libre competencia en envíos postales ordinarios (y que, por tanto, no tiene laobligación de tratar bien a sus clientes, porque también volverán mañana). El trato que recibenlos clientes no se diferencia mucho del de los paquetes, y estos son muy expresivos enocasiones al llegar al destino. Por otro lado, la cantidad de papeles que hay que rellenar encada envío, y algunas normas de la empresa (como el plomo adherido a los paquetes postales)no se han simplificado desde finales del siglo XIX. Tampoco es comprensible que sóloArgentaria sea aún la única entidad financiera con el privilegio de gestionar las denominadasCuentas Corrientes Postales. Además de que el servicio de correos es caro en la realidad (estoes, 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, cuando hayun 2% de inflación nacional).

Sin embargo, he de reconocer que la fiabilidad de Correos (entendida en cuanto apaquetes 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, al menos los de los reembolsos. Enpuntualidad, aunque hay extremos de gran aleatoriedad (desde paquetes que llegan en tresdías a un pueblo perdido en la otra punta del país, a los que tardan quince en ir de Valladolida Madrid) el tiempo promedio podría aproximarse, aunque por debajo, a lo que afirma laempresa.

Ciriaco García de Celis

Valladolid, Noviembre de 1997

Page 16: PCA, PS2 ,IBM y AT
Page 17: PCA, PS2 ,IBM y AT

17PRÓLOGO DE LA TERCERA EDICIÓN (1994)

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. Desdeentonces, ha continuado la expansión de los interfaces gráficos de usuario y los sistemasoperativos avanzados para PC. Sin embargo, pese a que la programación continúaalejándose cada vez más del bajo nivel de las máquinas, los programadores de sistemas enel entorno del PC siguen existiendo y son muchos más que los que trabajan para lasempresas punteras en el desarrollo de los sistemas operativos. Los ordenadores compatiblesposeen numerosas aplicaciones en el campo industrial, para las que es conveniente unconocimiento elevado del funcionamiento interno del ordenador en general y del MS-DOSen particular. Para aquellas personas que necesitan comprender el funcionamiento de unordenador, las máquinas compatibles constituyen una interesante oportunidad y punto departida. Este libro pretende cubrir una importante laguna en la bibliografía disponibleactualmente sobre la programación a nivel de sistemas de los ordenadores compatibles.

Respecto a la primera edición, se han incrementado los contenidos en unaproporción equivalente al 20% de lo que ya existía, corrigiéndose además algunos errores.Aunque el libro comience con una introducción a la aritmética binaria que pueda indicartodo lo contrario, se presupone que el lector tiene unos mínimos conocimientos deinformática, al menos un dominio básico del sistema operativo MS-DOS, siendo más querecomendable conocer algún lenguaje de programación. Seguidamente se explica el lenguajeensamblador de la serie 80x86 de Intel separando claramente las instrucciones de losdiversos procesadores, aunque dejando de lado algunas instrucciones del 286 y 386 que sesalen del entorno MS-DOS. También se describe la sintaxis del lenguaje ensamblador; sinembargo, aunque este último aspecto está extensamente documentado, los lectores que noconozcan el lenguaje ensamblador de ningún microprocesador habrán de trabajarconsiderablemente leyendo multitud de listados hasta adquirir la soltura necesaria y, sobretodo, creando los suyos propios. Aunque sería conveniente describir el lenguaje C, íntimoaliado del ensamblador en la programación de sistemas, ello se deja por razones de espaciopara otras publicaciones.

Page 18: PCA, PS2 ,IBM y AT

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

El libro describe con profundidad la arquitectura de los ordenadores compatibles,de manera especial en lo referente a la organización interna de la memoria (actualizadahasta el MS-DOS 6.0 y el DR-DOS 6.0), los discos y el teclado. El apartado de los gráficosse repasa sólo superficialmente, ya que por sí solo necesitaría de un buen libro más gruesoque este. Se dan pistas sobre la manera de conmutar los modos de vídeo sin alterar elcontenido de la pantalla, aspecto que resulta de especial interés para los programasresidentes.

Las memorias extendida XMS y expandida EMS son descritas con ciertodetenimiento, dada su presencia en todos los ordenadores modernos y su importancia.

Existen apéndices que describen todas las funciones del DOS, de la BIOS y delsistema usadas en las rutinas y programas desarrollados, así como la totalidad de lasfunciones XMS y EMS. Sin embargo, no están ni muchísimo menos todas las interrupcionesnecesarias, por lo que se insta al lector a conseguir el impresionante fichero de dominiopúblico INTERRUPT.LST, complemento ideal de este libro (ver bibliografía).

Los programas residentes reciben un tratamiento especialmente profundo: desde losmétodos más eficientes para que detecten su propia presencia en memoria, a las técnicasmás avanzadas para economizar memoria, pasando por el uso de funciones del DOS demanera concurrente al programa principal, así como técnicas de empleo de memoriaextendida y superior para conseguir programas que usen 0 Kb dentro de los primeros 640Kb de la máquina y todo ello sin olvidar la convivencia con los actuales entornosoperativos, como Windows, y la posibilidad de ser activados desde pantallas gráficas.

Este libro también trata los controladores de dispositivo o device drivers, desde losdos posibles enfoques de su uso: bien sea la creación de controladores de dispositivo decaracteres, bien la de nuevas unidades de disco añadidas a las del sistema; en ambos casosse incluyen ejemplos reales de controladores completos y comprobados, en particular elejemplo de disco virtual: un completo ejemplo de controlador redimensionable que soportamemoria convencional, XMS y EMS.

Existe un capítulo muy próximo al hardware en el que se describen a fondo y sinomisiones todos los chips del ordenador, para permitir al programador de sistemas uncontrol completo del equipo. Para asimilar este capítulo hace falta cierta formación previaen los sistemas digitales; sin embargo, los ejemplos que siguen a la información técnicaaclaran las explicaciones previas y pueden ser aprovechados de manera inmediata inclusosin entender todo lo anterior. Los chips de apoyo al microprocesador son descritos demanera total: primero, no relacionados con el PC sino como tales circuitos; despuésintegrándolos en el ordenador y documentando profusamente su uso, con ejemplosprobados. Se consideran el interfaz de periféricos 8255 (útil para averiguar la configuraciónde los PC/XT), el temporizador 8253/8254 (para temporización y síntesis de sonido), elcontrolador de interrupciones 8259, el controlador de DMA 8237 (para acceso a disco), elcontrolador de disquetes 765 (acceso directo a los sectores), la controladora de disco durode los AT (IDE, MFM ó Bus Local); el controlador del teclado del AT (8042); el UART 8250(empleado en las comunicaciones serie) y el reloj de tiempo real MC146818 (configuraciónde AT y programación de alarmas y temporizaciones). Los ejemplos en este capítuloexperimentan una importante potenciación respecto a la edición anterior; en particular, enlo relacionado con el controlador de disquetes se puede considerar que la informaciónvertida es prácticamente casi toda la existente, existiendo pautas suficientes para que el

Page 19: PCA, PS2 ,IBM y AT

19PRÓLOGO DE LA TERCERA EDICIÓN (1994)

lector cree sus propios programas copiones, protecciones de disco, formatos de altacapacidad, etc.

Existen también capítulos que describen el funcionamiento y programación de laimpresora; sin entrar en aspectos particulares relativos a los modelos de las diversasmarcas, sí se suministra información común a todas. También se comenta en un capítuloel funcionamiento al más bajo nivel del ratón, aspecto que habitualmente no suele serconsiderado.

Dada la importancia del lenguaje C en la programación en general y en laprogramación de sistemas en particular, tanto en la actualidad como durante los próximosaños, se incluye un capítulo que describe la manera de comunicar el ensamblador con ellenguaje C, con objeto de superar las limitaciones de este lenguaje en los puntos críticos dela programación de sistemas. Este capítulo requiere un dominio elemental del lenguaje Cpor parte del lector, aunque probablemente sólo sea útil para aquellos que lo conocen máso menos.

Resumiendo, el libro pretende reunir en una sola obra la mayoría de la informaciónnecesaria para el programador de sistemas, exponiendo toda la información y no sólo loimprescindible, sin olvidos ni omisiones; también se pretende explicar las técnicas másavanzadas de creación de programas residentes. Este afán de información completa es elresponsable del título del libro.

Todos los listados de ejemplo se suponen de dominio público y las rutinas puedenser incluidas por los lectores libremente en sus propios programas, aunque en el caso delos programas completos debe citarse la procedencia y dejar bien claro en las versionesmodificadas quién las ha alterado. En todo caso, pese a que todas las rutinas y programashan sido probados debidamente en un 8088, un 286, un 386 o un 486 -bajo varios sistemasoperativos y con diferentes configuraciones del hardware- el autor del libro no seresponsabiliza de su correcto funcionamiento en todas las circunstancias.

Page 20: PCA, PS2 ,IBM y AT
Page 21: PCA, PS2 ,IBM y AT

21INTRODUCCIÓN

Capítulo I: INTRODUCCIÓN

1.1. - NUMEROS BINARIOS, OCTALES Y HEXADECIMALES.

El sistema de numeración utilizado habitualmente es la base 10; es decir, consta de 10 dígitos (0-9)que podemos colocar en grupos, ordenados de izquierda a derecha y de mayor a menor.

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, se indica la base de numeración como 135710.

En un ordenador el sistema de numeración es binario -en base 2, utilizando el 0 y el 1- hechopropiciado por ser precisamente dos los estados estables en los dispositivos digitales que componen unacomputadora.

Análogamente a la base 10, cada posición tiene un valor de 2n donde n es la posición contando desdela derecha y empezando por 0:

1012 = 1 x 22 + 0 x 21 + 1 x 20

Además, por su importancia y utilidad, es necesario conocer otros sistemas de numeración comopueden ser el octal (base 8) y el hexadecimal (base 16). En este último tenemos, además de los números del0 al 9, letras -normalmente en mayúsculas- de la A a la F.

Llegar a un número en estos sistemas desde base 2 es realmente sencillo si agrupamos las cifrasbinarias de 3 en 3 (octal) o de 4 en 4 (hexadecimal):

Base 2 a base 8: 101 0112 = 538

Base 2 a base 16: 0010 10112 = 2B16

A la inversa, basta convertir cada 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, se utilizarán una serie de sufijos para determinar el sistema de numeraciónempleado:

Sufijo Base Ejemplos

b 2 01101010bo,q 8 175od 10 789dh 16 6A5h

En caso de que no aparezca el sufijo, el número se considera decimal; es decir, en base 10.

Page 22: PCA, PS2 ,IBM y AT

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

1.2. - CAMBIO DE BASE.

Pese a que las conversiones entre base 2 y base 8 y 16 son prácticamente directas, existe un sistemageneral para realizar el cambio de una base a otra. El paso de cualquier base a base 10 lo vimos antes:

6A5h = 6 x 162 + 10 x 161 + 5 x 160

Inversamente, si queremos pasar de base 10 a cualquier otra habrá que realizar sucesivas divisionespor la base y tomar los restos:

1234 16

114 77 16 1234d = 4D2h2

13 4

donde 4 es el último cociente (menor que la base) y los restantes dígitos son los restos en orden inverso.

1.3. - ESTRUCTURA ELEMENTAL DE LA MEMORIA.

1.3.1. - BIT.

Toda la memoria del ordenador se compone de dispositivos electrónicos que pueden adoptarúnicamente dos estados, que representamos matemáticamente por 0 y 1. Cualquiera de estas unidades deinformación se denomina BIT, contracción de «binary digit» en inglés.

1.3.2. - BYTE.

Cada grupo de 8 bits se conoce como byte u octeto. Es la unidad de almacenamiento en memoria,la cual está constituida por un elevado número de posiciones que almacenan bytes. La cantidad de memoriade que dispone un sistema se mide en Kilobytes (1 Kb = 1024 bytes), en Megabytes (1 Mb = 1024 Kb),Gigabytes (1 Gb = 1024 Mb), Terabytes (1 Tb = 1024 Gb) o Petabytes (1 Pb = 1024 Tb).

Los bits en un byte se numeran de derecha a izquierda y de 0 a 7, correspondiendo con losexponentes de las potencias de 2 que reflejan el valor de cada posición. Un byte nos permite, por tanto,representar 256 estados (de 0 a 255) según la combinación de bits que tomemos.

1.3.3. - NIBBLE.

Cada grupo de cuatro bits de un byte constituye un nibble, de forma que los dos nibbles de un bytese llaman nibble superior (el compuesto por los bits 4 a 7) e inferior (el compuesto por los bits 0 a 3). Elnibble tiene gran utilidad debido a que cada uno almacena un dígito hexadecimal:

Binario Hex. Decimal Binario Hex. Decimal

0000 0 0 1000 8 80001 1 1 1001 9 90010 2 2 1010 A 100011 3 3 1011 B 110100 4 4 1100 C 120101 5 5 1101 D 130110 6 6 1110 E 140111 7 7 1111 F 15

Page 23: PCA, PS2 ,IBM y AT

23INTRODUCCIÓN

1.4. - OPERACIONES ARITMÉTICAS SENCILLAS EN BINARIO.

Para sumar números, tanto en base 2 como hexadecimal, se sigue el mismo proceso que en base 10:

Podemos observar que la suma se desa-1010 1010b rrolla de la forma tradicional; es decir:

+ 0011 1100b sumamos normalmente, salvo en el caso de1 + 1 = 102 , en cuyo caso tenemos un aca-

1110 0110b rreo de 1 (lo que nos llevamos).

1.5. - COMPLEMENTO A DOS.

En general, se define como valor negativo de un número el que necesitamos sumarlo para obtener00h, por ejemplo:

FFh Como en un byte solo tenemos dos nibbles, es+ 01h decir, dos dígitos hexadecimales, el resultado es

0 (observar cómo el 1 más significativo subrayado100h es ignorado). Luego FFh=-1. Normalmente, el bit 7

se considera como de signo y, si está activo (a 1)el número es negativo.

Por esta razón, el número 80h, cuyo complemento a dos es él mismo, se considera negativo (-128)y el número 00h, positivo. En general, para hallar el complemento a dos de un número cualquiera basta concalcular primero su complemento a uno, que consiste en cambiar los unos por ceros y los ceros por unosen su notación binaria; a continuación se le suma una unidad para calcular el complemento a dos. Con unacalculadora, la operación es más sencilla: el complemento a dos de un número A de n bits es 2n-A.

Otro factor a considerar es cuando se pasa de operar con un número de cierto tamaño (ej., 8 bits) aotro mayor (pongamos de 16 bits). Si el número es positivo, la parte que se añade por la izquierda son bitsa 0. Sin embargo, si era negativo (bit más significativo activo) la parte que se añade por la izquierda son bitsa 1. Este fenómeno, en cuya demostración matemática no entraremos, se puede resumir en que el bit mássignificativo se copia en todos los añadidos: es lo que se denomina la extensión del signo: los dos siguientesnúmeros son realmente el mismo número (el -310): 11012 (4 bits) y 111111012 (8 bits).

1.6. - AGRUPACIONES DE BYTES.

Tipo Definición

Palabra 2 bytes contiguosDoble palabra 2 palabras contiguas (4 bytes)Cuádruple palabra 4 palabras contiguas (8 bytes)Párrafo 16 bytesPágina 256 bytes, 16 Kb, etc.Segmento 64 Kbytes

1.7. - REPRESENTACIÓN DE LOS DATOS EN MEMORIA.

1.7.1. - NUMEROS BINARIOS: máximo número representable:

Tipo Sin signo

1 byte 2552 bytes 65.5354 bytes 4.294.967.2958 bytes 18.446.744.073.709.551.615

Page 24: PCA, PS2 ,IBM y AT

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

Tipo Positivo Negativo

1 byte 127 -1282 bytes 32.767 -32.7684 bytes 2.147.483.647 -2.147.483.6488 bytes 9.223.372.036.854.775.807 -9.223.372.036.854.775.808

Los números binarios de más de un byte se almacenan en la memoria en los procesadores de Intelen orden inverso: 01234567h se almacenaría: 67h, 45h, 23h, 01h.

1.7.2. - NUMEROS BINARIOS CODIFICADOS EN DECIMAL (BCD).

Consiste en emplear cuatro bits para codificar los dígitos del 0 al 9 (desperdiciando las seiscombinaciones que van de la 1010 a la 1111). La ventaja es la simplicidad de conversión a/de base 10, queresulta inmediata. Los números BCD pueden almacenarse desempaquetados, en cuyo caso cada byte contieneun dígito BCD (Binary-Coded Decimal); o empaquetados, almacenando dos dígitos por byte (para construirlos números que van del 00 al 99). La notación BCD ocupa cuatro bits -un nibble- por cifra, de forma queen el formato desempaquetado el nibble superior siempre es 0.

1.7.3. - NUMEROS EN PUNTO FLOTANTE.

Son grupos de bytes en los que una parte se emplea para guardar las cifras del número (mantisa) yotra para indicar la posición del punto flotante (exponente), de modo equivalente a la notación científica. Estopermite trabajar con números de muy elevado tamaño -según el exponente- y con una mayor o menorprecisión en función de los bits empleados para codificar la mantisa.

1.7.4. - CÓDIGO ASCII.

El código A.S.C.I.I. (American Standard Code for Information Interchange) es un convenio adoptadopara asignar a cada carácter un valor numérico; su origen está en los comienzos de la Informática tomandocomo muestra algunos códigos de la transmisión de información de radioteletipo. Se trata de un código de7 bits con capacidad para 128 símbolos que incluyen todos los caracteres alfanuméricos del inglés, consímbolos de puntuación y algunos caracteres de control de la transmisión.

Con posterioridad, con la aparición de los microordenadores y la gran expansión entre ellos de losIBM-PC y compatibles, la ampliación del código ASCII realizada por esta marca a 8 bits, con capacidad para128 símbolos adicionales, experimenta un considerable auge, siendo en la actualidad muy utilizada yrecibiendo la denominación oficial de página de códigos 437 (EEUU). Se puede consultar al final de estelibro. Es habitualmente la única página soportada por las BIOS de los PC. Para ciertas nacionalidades se handiseñado otras páginas específicas que requieren de un software externo. En las lenguas del estado españoly en las de la mayoría de los demás países de la UE, esta tabla cubre todas las necesidades del idioma.

1.8. - OPERACIONES LÓGICAS EN BINARIO.

Se realizan a nivel de bit y pueden ser de uno o dos operandos:

x NOT (x) x y x AND y x OR y x XOR y

0 1 0 0 0 0 01 0 0 1 0 1 1

1 0 0 1 11 1 1 1 0

Page 25: PCA, PS2 ,IBM y AT

25ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES

Capítulo II: ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES

El ensamblador es un lenguaje de programación que, por la traducción directa de los mnemónicosa instrucciones maquina, permite realizar aplicaciones rápidas, solucionando situaciones en las que los tiemposde ejecución constituye el factor principal para que el proceso discurra con la suficiente fluidez. Estasituación, que indudablemente sí influye sobre la elección del lenguaje de programación a utilizar en eldesarrollo de una determinada rutina, y dada la aparición de nuevos compiladores de lenguajes de alto nivelque optimizan el código generado a niveles muy próximos a los que un buen programador es capaz derealizar en ensamblador, no es la única razón para su utilización.

Es sobradamente conocido que los actuales sistemas operativos son programados en su mayor parteen lenguajes de alto nivel, especialmente C, pero siempre hay una parte en la que el ensamblador se hace casiinsustituible bajo DOS y es la programación de los drivers para los controladores de dispositivos, relacionadoscon las tareas de más bajo nivel de una máquina, fundamentalmente las operaciones de entrada/salida en lasque es preciso actuar directamente sobre los demás chips que acompañan al microprocesador. Por ello yporque las instrucciones del lenguaje ensamblador están íntimamente ligadas a la máquina, vamos a realizarprimero un somero repaso a la arquitectura interna de un microordenador.

2.1. - ARQUITECTURA VON NEWMAN.

Centrándonos en los ordenadores sobre los que vamos a trabajar desarrollaré a grandes rasgos laarquitectura Von Newman que, si bien no es la primera en aparecer, sí que lo hizo prácticamente desde elcomienzo de los ordenadores y se sigue desarrollando actualmente. Claro es que está siendo desplazada porotra que permiten una mayor velocidad de proceso, la RISC.

En los primeros tiempos de los ordenadores, con sistemas de numeración decimal, una electrónicasumamente complicada muy susceptible a fallos y un sistema de programación cableado o mediante fichas,Von Newman propuso dos conceptos básicos que revolucionarían la incipiente informática:

a) La utilización del sistema de numeración binario. Simplificaba enormemente los problemasque la implementación electrónica de las operaciones y funciones lógicas planteaban, a la vezproporcionaba una mayor inmunidad a los fallos (electrónica digital).

b) Almacenamiento de la secuencia de instrucciones de que consta el programa en una memoriainterna, fácilmente accesible, junto con los datos que referencia. De este forma la velocidad deproceso experimenta un considerable incremento; recordemos que anteriormente una instrucción oun dato estaban codificados en una ficha en el mejor de los casos.

Tomando como modelo las máquinas que aparecieron incorporando las anteriores características, elordenador se puede considerar compuesto por las siguientes partes:

- La Unidad Central de Proceso, U.C.P., más conocida por sus siglas en inglés (CPU).- La Memoria Interna, MI.- Unidad de Entrada y Salida, E/S.- Memoria masiva Externa, ME.

Page 26: PCA, PS2 ,IBM y AT

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

Realicemos a continuación una descripción de lo que se entiende por cada una de estas partes y cómoestán relacionadas entre si:

- La Unidad Central de Proceso (CPU) viene a ser el cerebro del ordenador y tiene por misiónefectuar las operaciones aritmético-lógicas y controlar las transferencias de información a realizar.

- La Memoria Interna (MI) contiene el conjunto de instrucciones que ejecuta la CPU en el transcursode un programa. Es también donde se almacenan temporalmente las variables del mismo, todos losdatos que se precisan y todos los resultados que devuelve.

- Unidades de entrada y salida (E/S) o Input/Output (I/O): son las encargadas de la comunicación dela máquina con el exterior, proporcionando al operador una forma de introducir al ordenador tantolos programas como los datos y obtener los resultados.

Como es de suponer, estas tres partes principales de que consta el ordenador deben estar íntimamenteconectadas; aparece en este momento el concepto de bus: el bus es un conjunto de líneas que enlazan losdistintos componentes del ordenador, por ellas se realiza la transferencia de datos entre todos sus elementos.

Se distinguen tres tipos de bus:

- De control: forman parte de él las líneas que seleccionan desde dónde y hacia dónde va dirigidala información, también las que marcan la secuencia de los pasos a seguir para dicha transferencia.- De datos: por él, de forma bidireccional, fluyen los datos entre las distintas partes del ordenador.- De direcciones: como vimos, la memoria está dividida en pequeñas unidades de almacenamientoque contienen las instrucciones del programa y los datos. El bus de direcciones consta de un conjuntode líneas que permite seleccionar de qué posición de la memoria se quiere leer su contenido. Tambiéndirecciona los puertos de E/S.

La forma de operar del ordenador en su conjunto es direccionar una posición de la memoria en buscade una instrucción mediante el bus de direcciones, llevar la instrucción a la unidad central de proceso -CPU-por medio del bus de datos, marcando la secuencia de la transferencia el bus de control. En la CPU lainstrucción se decodifica, interpretando qué operandos necesita: si son de memoria, es necesario llevarles ala CPU; una vez que la operación es realizada, si es preciso se devuelve el resultado a la memoria.

2.2. - EL MICROPROCESADOR.

Un salto importante en la evolución de los ordenadores lo introdujo el microprocesador: se trata deuna unidad central de proceso contenida totalmente en un circuito integrado. Comenzaba así la gran carreraen busca de lo más rápido, más pequeño; rápidamente el mundo del ordenador empezó a ser accesible apequeñas empresas e incluso a nivel doméstico: es el boom de los microordenadores personales. Aunquecuando entremos en la descripción de los microprocesadores objeto de nuestro estudio lo ampliaremos, haréun pequeño comentario de las partes del microprocesador:

- Unidad aritmético-lógica: Es donde se efectúan las operaciones aritméticas (suma, resta, y a vecesproducto y división) y lógicas (and, or, not, etc.).- Decodificador de instrucciones: Allí se interpretan las instrucciones que van llegando y quecomponen el programa.- Bloque de registros: Los registros son celdas de memoria en donde queda almacenado un datotemporalmente. Existe un registro especial llamado de indicadores, estado o flags, que refleja elestado operativo del microprocesador.- Bloque de control de buses internos y externos: supervisa todo el proceso de transferencias deinformación dentro del microprocesador y fuera de él.

Page 27: PCA, PS2 ,IBM y AT

27ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES

2.3. - BREVE HISTORIA DEL ORDENADOR PERSONAL Y EL DOS.

La trepidante evolución del mundo informático podría provocar que algún recién llegado a este librono sepa exactamente qué diferencia a un ordenador "AT" del viejo "XT" inicial de IBM. Algunos términosmanejados en este libro podrían ser desconocidos para los lectores más jóvenes. Por ello, haremos unapequeña introducción sobre la evolución de los ordenadores personales, abarcando toda la historia (ya queno es muy larga).

La premonición.

En 1973, el centro de investigación de Xerox en Palo Alto desarrolló un equipo informático con elaspecto externo de un PC personal actual. Además de pantalla y teclado, disponía de un artefacto similar alratón; en general, este aparato (denominado Alto) introdujo, mucho antes de que otros los reinventaran,algunos de los conceptos universalmente aceptados hoy en día. Sin embargo, la tecnología del momento nopermitió alcanzar todas las intenciones. Alguna innovación, como la pantalla vertical, de formato similar auna 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, y los procesadores de textos no muestran legiblementeuna hoja en vertical completa incluso en monitores de 20 pulgadas.

El microprocesador.

El desarrollo del primer microprocesador por Intel en 1971, el 4004 (de 4 bits), supuso el primer pasohacia el logro de un PC personal, al reducir drásticamente la circuitería adicional necesaria. Sucesores de esteprocesador fueron el 8008 y el 8080, de 8 bits. Ed Roberts construyó en 1975 el Altair 8800 basándose enel 8080; aunque esta máquina no tenía teclado ni pantalla (sólo interruptores y luces), era una arquitecturaabierta (conocida por todo el mundo) y cuyas tarjetas se conectaban a la placa principal a través de 100terminales, que más tarde terminarían convirtiéndose en el bus estándar S-100 de la industria.

El Apple-I apareció en 1976, basado en el microprocesador de 8 bits 6502, en aquel entonces unrecién aparecido aunque casi 10 veces más barato que el 8080 de Intel. Fue sucedido en 1977 por elApple-II. No olvidemos los rudimentos de la época: el Apple-II tenía un límite máximo de 48 Kbytes dememoria. En el mismo año, Commodore sacó su PET con 8 Kbytes. Se utilizaban cintas de casete comoalmacenamiento, aunque comenzaron a aparecer las unidades de disquete de 5¼. Durante finales de los 70aparecieron muchos otros ordenadores, fruto de la explosión inicial del microprocesador.

Los micros de los 80.

En 1980, Sir Clive Sinclair lanzó el ZX-80, seguido muy poco después del ZX-81. Estaban basadosen un microprocesador sucesor del 8085 de Intel: el Z80 (desarrollado por la empresa Zilog, creada por unex-ingeniero de Intel). Commodore irrumpió con sus VIC-20 y, posteriormente, el Commodore 64, basadosaún en el 6502 y, este último, con mejores posibilidades gráficas y unos 64 Kb de memoria. Su competidorfue el ZX-Spectrum de Sinclair, también basado en el Z80, con un chip propio para gestión de gráficos yotras tareas, la ULA, que permitió rebajar su coste y multiplicó su difusión por europa, y en particular porEspaña. Sin embargo, todos los ordenadores domésticos de la época, como se dieron en llamar, estabanbasados en procesadores de 8 bits y tenían el límite de 64 Kb de memoria. Los intentos de rebasar este límitemanteniendo aún esos chips por parte de la plataforma MSX (supuesto estándar mundial con la misma suerteque ha corrido el Esperanto) o los CPC de Amstrad, de poco sirvieron.

El IBM PC.

Y es que IBM también fabricó su propio ordenador personal con vocación profesional: el 12 deagosto de 1981 presentó el IBM PC. Estaba basado en el microprocesador 8088, de 16 bits, cuyasinstrucciones serán las que usemos en este libro, ya que todos los procesadores posteriores son básicamente(en MS-DOS) versiones mucho más rápidas del mismo. El equipamiento de serie consistía en 16 Kbytes de

Page 28: PCA, PS2 ,IBM y AT

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

memoria ampliables a 64 en la placa base (y a 256 añadiendo tarjetas); el almacenamiento externo se hacíaen cintas de casete, aunque pronto aparecieron las unidades de disco de 5¼ pulgadas y simple cara(160/180 Kb por disco) o doble cara (320/360 Kb). En 1983 apareció el IBM PC-XT, que traía comonovedad un disco duro de 10 Mbytes. Un año más tarde aparecería el IBM PC-AT, introduciendo elmicroprocesador 286, así como ranuras de expansión de 16 bits (el bus ISA de 16 bits) en contraposición conlas de 8 bits del PC y el XT (bus ISA de 8 bits), además incorporaba un disco duro de 20 Mbytes y disquetesde 5¼ pero con 1.2 Mbytes.

En general, todos los equipos con procesador 286 o superior pueden catalogarse dentro de la categoríaAT; el término XT hace referencia al 8088/8086 y similares. Finalmente, por PC (a secas) se entiendecualquiera de ambos; aunque si se hace distinción entre un PC y un AT en la misma frase, por PC sesobreentiende un XT, menos potente. El término PC ya digo, no obstante, es hoy en día mucho más general,referenciando habitualmente a cualquier ordenador personal.

Alrededor del PC se estaba construyendo un imperio de software más importante que el propiohardware: estamos hablando del sistema operativo PC-DOS. Cuando aparecieron máquinas compatibles conel PC de IBM, tenían que respetar la compatibilidad con ese sistema, lo que fue sencillo (ya que Microsoft,le gustara o no a IBM, desarrolló el MS-DOS, compatible con el PC-DOS pero que no requería la BIOS delordenador original, cuyo copyright era de IBM). Incluso, el desarrollo de los microprocesadores posterioresha estado totalmente condicionado por el MS-DOS. [Por cierto, la jugada del PC-DOS/MS-DOS se repetiríaen alguna manera pocos años después con el OS/2-Windows].

A partir de 1986, IBM fue paulatinamente dejando de tener la batuta del mercado del PC. La razónes que la propia IBM tenía que respetar la compatibilidad con lo anterior, y en ese terreno no tenía másfacilidades para innovar que la competencia. El primer problema vino con la aparición de los procesadores386: los demás fabricantes se adelantaron a IBM y lanzaron máquinas con ranuras de expansión aún de 16bits, que no permitían obtener todo el rendimiento. IBM desarrolló demasiado tarde, en 1987, la arquitecturaMicrochannel, con bus de 32 bits pero cerrada e incompatible con tarjetas anteriores (aunque se desarrollaronnuevas tarjetas, eran caras) y la incluyó en su gama de ordenadores PS/2 (alguno de cuyos modelos era aúnrealmente ISA). La insolente respuesta de la competencia fue la arquitectura EISA, también de 32 bits perocompatible con la ISA anterior.

Otro ejemplo: si IBM gobernó los estándares gráficos hasta la VGA, a partir de ahí sucedió unfenómeno similar y los demás fabricantes se adelantaron a finales de los 80 con mejores tarjetas y másbaratas; sin embargo, se perdió la ventaja de la normalización (no hay dos tarjetas superiores a la VGA quefuncionen igual).

EISA también era caro, así que los fabricantes orientales, cruzada ya la barrera de los años 90,desarrollaron con la norma VESA las placas con bus local (VESA Local Bus); básicamente es unaprolongación de las patillas de la CPU a las ranuras de expansión, lo que permite tarjetas rápidas de 32 bitspero muy conflictivas entre sí. Esta arquitectura de bus se popularizó mucho con los procesadores 486. Sinembargo, al final el estándar que se ha impuesto ha sido el propuesto por el propio fabricante de las CPU:Intel, con su bus PCI, que con el Pentium se ha convertido finalmente en el único estándar de bus de 32 bits.Estas máquinas aún admiten no obstante las viejas tarjetas ISA, suficientes para algunas aplicaciones de bajavelocidad (modems,... etc).

La evolución del MS-DOS.

Una manera sencilla de comprender la evolución de los PC es observar la evolución de las sucesivasversiones del DOS y los sistemas que le han sucedido.

En 1979, Seatle Computer necesitaba apoyar de alguna manera a sus incipientes placas basadas enel 8086. Como Digital Research estaba tardando demasiado en convertir el CP/M-80 a CP/M-86, desarrollósu propio sistema: el QDOS 0.1, que fue presentado en 1980. Antes de finales de año apareció QDOS 0.3.

Page 29: PCA, PS2 ,IBM y AT

29ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES

Bill Gates, dueño de Microsoft, de momento sólo poseía una versión de lenguaje BASIC para 8086no orientada a ningún sistema operativo particular, que le gustó a algún directivo de IBM. Bill Gates ya habíahecho 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.3 a finales de1980. En abril de 1981 aparecieron las primeras versiones de CP/M-86 de Digital, a la vez que QDOS serenombraba a 86-DOS 1.0 aunque en principio parecía tener menos futuro que el CP/M. En Julio, sinembargo, Microsoft adquiría todos los derechos del 86-DOS.

Digital Research no ocupa actualmente el lugar de Microsoft porque en 1981 era una compañíademasiado importante como para cerrar un acuerdo con IBM sin imponer sus condiciones para cederle losderechos del sistema operativo CP/M. Así que IBM optó por Bill Gates, que acababa de adquirir un sistemaoperativo, el 86-DOS, que pasó a denominarse PC-DOS 1.0. Las versiones de PC-DOS no dependientes dela ROM BIOS de IBM se denominarían MS-DOS, término que ha terminado siendo más popular.

A continuación se expone la evolución hasta la versión 5.0; las versiones siguientes no añadenninguna característica interna nueva destacable (aunque a nivel de interfaz con el usuario y utilidadesincluidas haya más cambios). El MS-DOS 7.0 sobre el que corre Windows 95 sí tiene bastantes retoquesinternos, pero no es frecuente su uso aislado o independiente de Windows 95. Aunque PC-DOS y MS-DOSsiembre han caminado paralelos, hay una única excepción: la versión 7.0 (no confundir MS-DOS 7.0 conPC-DOS 7.0: este último es, realmente, el equivalente al MS-DOS 5.0 ó 6.2).

Agosto de 1981. Presentación del MS-DOS 1.0 original.

Marzo de 1982. MS-DOS 1.25, añadiendo soporte para disquetes de doble cara. Las funciones delDOS (en INT 21h) sólo llegaban hasta la 1Fh (¡la 30h no estaba implementada!).

Marzo de 1983. MS-DOS 2.0 introducido con el XT: reescritura del núcleo en C; mejoras en elsistema de ficheros (FAT, subdirectorios,...); separación de los controladores dedispositivo del sistema.

Mayo de 1983. MS-DOS 2.01: soporte de juegos de caracteres internacionales.

Octubre de 1983. MS-DOS 2.11: eliminación de errores.

Agosto de 1984. MS-DOS 3.0: Añade soporte para disquetes de 1.2M y discos duros de 20 Mb. Nosería necesaria una nueva versión del DOS para cada nuevo formato de disco si elcontrolador integrado para A:, B: y C: lo hubieran hecho flexible algún día.

Marzo de 1985. MS-DOS 3.1: Soporte para redes locales.

Diciembre de 1985. MS-DOS 3.2: Soporte para disquetes de 720K (3½-DD).

Abril de 1987. MS-DOS 3.3: Soporte para disquetes de 1.44M (3½-HD). Permite particionessecundarias en los discos duros. Soporte internacional: páginas de códigos.

Julio de 1988. MS-DOS 4.0: Soporte para discos duros de más de 32 Mb (cambio radical internoque forzó la reescritura de muchos programas de utilidad) hasta 2 Gb. Controladorde memoria EMM386. Precipitada salida al mercado.

Noviembre de 1988. MS-DOS 4.01: Corrige las erratas de la 4.0.

Junio de 1991. MS-DOS 5.0: Soporte para memoria superior. La competencia de Digital Research,que irrumpe en el mundo del DOS una década más tarde (con DR-DOS), obliga aMicrosoft a incluir ayuda online y a ocuparse un poco más de los usuarios.

Page 30: PCA, PS2 ,IBM y AT

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

Digital Research trabajó arduamente para lograr una compatibilidad total con MS-DOS, y finalmenteconsiguió lanzar al mercado su sistema DR-DOS. Las versiones 5.0 y 6.0 de este sistema, así como elNovell DOS 7.0 (cuando cedió los derechos a Novell) se pueden considerar prácticamente 100% compatibles.El efecto del DR-DOS fue positivo, al forzar a Microsoft a mejorar la interacción del sistema operativo conlos usuarios (documentación en línea, programas de utilidad, ciertos detalles...); por poner un ejemplo, hastael MS-DOS 6.2 ha sido necesario intercambiar tres veces el disquete origen y el destino durante la copia deun disquete normal de 1.44M. En cierto modo, la prepotencia de Microsoft con el MS-DOS a principios delos noventa era similar a la de Digital Research a principios de los 80 con el CP/M.

El futuro.

El resto de la historia de los sistemas operativos de PC ya la conoce el lector, a menos que no estéinformado de la actualidad. Caminamos hacia la integración de los diversos Windows en uno sólo, queesperemos que algún día sea suficientemente abierto para que le surjan competidores. Si en el futuro hubieraun sólo sistema operativo soportado por Microsoft, no vamos por buen camino.

En ese caso, sería de agradecer que algún juez les obligara a publicar una especificación completade las funciones y protocolos del sistema, con objeto de que algún organismo de normalización internacionallas recogiera sin ambigüedades para permitir la libre competencia de otros fabricantes. El DOS y el Windowsactuales no son ningún invento maravilloso de Microsoft. Por poner un ejemplo, el MS-DOS 1.0 carecía defunción para identificar la versión del sistema. Exactamente lo mismo le ha sucedido a las primeras versionesde Windows (hay varios chequeos distintos para detectarlas, según el modo de funcionamiento y la versión):el MS-DOS no lo escribió inicialmente Microsoft, pero Windows sí, y salta a la vista que sus programadores,para cometer semejante despiste, se sentaron delante del teclado antes de hacer un análisis de la aplicacióna desarrollar, igual que lo hubiera hecho alguien que hubiera aprendido a programar con unos fascículoscomprados en el kiosco. Con tanto analista en el paro...

No olvidemos que el DOS y Windows son el fruto de toda la sociedad utilizando el mismo tipo deordenadores y necesitando la compatibilidad con lo anterior a cualquier precio. La prueba evidente son losprocesadores de Intel, construidos desde hace tiempo para dar servicio al sistema operativo del PC. Somosprisioneros, usuarios obligados de Microsoft. Naturalmente, no tengo nada contra Microsoft, pero opino queel poder adquirido durante una década, gracias a la exclusiva de los derechos sobre un sistema operativo sinayuda en la línea de comandos, o de un Windows cerrado íntimamente ligado al DOS (de quien sóloMicrosoft tiene el código fuente) no legitima a ninguna empresa a tener tanto poder. No lo olvidemos: elMS-DOS ha dado un vuelco hacia la amigabilidad con el usuario cuando Digital Research ha aparecido conel DR-DOS. Del mismo modo que Windows seguirá lento o colgándose mientras Unix no tenga másaplicaciones comerciales.

Si hay alguien que puede competir con Windows es Unix. Y en Unix no dependemos de ningúnfabricante concreto, ni de hardware ni de software. Probablemente, la insuficiente normalización actual lacorregiría pronto el propio mercado. ¿Tiene usted Linux instalado en casa y lo utiliza al menos paraconectarse a Internet por Infovía, o quizá le gustaría hacerlo algún día?. ¿O por el contrario es de los quepiensan que Bill Gates es un genio?. Si se queda con la segunda opción, es que ve mucho la tele, aunqueevidentemente tiene razón: y cuantos más como usted, más genio que será... ;-)

Page 31: PCA, PS2 ,IBM y AT

31MICROPROCESADORES 8086/88, 286, 386 Y 486

Capítulo III: Microprocesadores 8086/88, 286, 386, 486 y Pentium.

3.1. - CARACTERÍSTICAS GENERALES.

Los microprocesadores Intel 8086 y 8088 se desarrollan a partir de un procesador anterior, el 8080,que, en sus diversas encarnaciones -incluyendo el Zilog Z-80- ha sido la CPU de 8 bits de mayor éxito.

Poseen una arquitectura interna de 16 bits y pueden trabajar con operandos de 8 y 16 bits; unacapacidad de direccionamiento de 20 bits (hasta 1 Mb) y comparten el mismo juego de instrucciones.

La filosofía de diseño de la familia del 8086 se basa en la compatibilidad y la creación de sistemasinformáticos integrados, por lo que disponen de diversos coprocesadores como el 8089 de E/S y el 8087,coprocesador matemático de coma flotante. De acuerdo a esta filosofía y para permitir la compatibilidad conlos anteriores sistemas de 8 bits, el 8088 se diseñó con un bus de datos de 8 bits, lo cual le hace más lentoque su hermano el 8086, pues éste es capaz de cargar una palabra ubicada en una dirección par en un solociclo de memoria mientras el 8088 debe realizar dos ciclos leyendo cada vez un byte.

Disponen de 92 tipos de instrucciones, que pueden ejecutar con hasta 7 modos de direccionamiento.Tienen una capacidad de direccionamiento en puertos de entrada y salida de hasta 64K (65536 puertos), porlo que las máquinas construidas entorno a estos microprocesadores no suelen emplear la entrada/salida pormapa de memoria, como veremos.

Entre esas instrucciones, las más rápidas se ejecutan en 2 ciclos teóricos de reloj y unos 9 reales (setrata del movimiento de datos entre registros internos) y las más lentas en 206 (división entera con signo delacumulador por una palabra extraída de la memoria). Las frecuencias internas de reloj típicas son 4.77 MHzen la versión 8086; 8 MHz en la versión 8086-2 y 10 MHz en la 8086-1. Recuérdese que un MHz son unmillón de ciclos de reloj, por lo que un PC estándar a 4,77 MHz puede ejecutar de 20.000 a unos 0,5millones de instrucciones por segundo, según la complejidad de las mismas (un 486 a 50 MHz, incluso sinmemoria caché externa es capaz de ejecutar entre 1,8 y 30 millones de estas instrucciones por segundo).

El microprocesador Intel 80286 se caracteriza por poseer dos modos de funcionamientocompletamente diferenciados: el modo real en el que se encuentra nada más ser conectado a la corriente yel modo protegido en el que adquiere capacidad de proceso multitarea y almacenamiento en memoria virtual.El proceso multitarea consiste en realizar varios procesos de manera aparentemente simultánea, con la ayudadel sistema operativo para conmutar automáticamente de uno a otro optimizando el uso de la CPU, ya quemientras un proceso está esperando a que un periférico complete una operación, se puede atender otro procesodiferente. La memoria virtual permite al ordenador usar más memoria de la que realmente tiene, almacenandoparte de ella en disco: de esta manera, los programas creen tener a su disposición más memoria de la querealmente existe; cuando acceden a una parte de la memoria lógica que no existe físicamente, se produce unainterrupción y el sistema operativo se encarga de acceder al disco y traerla.

Cuando la CPU está en modo protegido, los programas de usuario tienen un acceso limitado al juegode instrucciones; sólo el proceso supervisor -normalmente el sistema operativo- está capacitado para realizarciertas tareas. Esto es así para evitar que los programas de usuario puedan campar a sus anchas y entrar enconflictos unos con otros, en materia de recursos como memoria o periféricos. Además, de esta manera,aunque un error software provoque el cuelgue de un proceso, los demás pueden seguir funcionando

Page 32: PCA, PS2 ,IBM y AT

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

normalmente, y el sistema operativo podría abortar el proceso colgado. Por desgracia, con el DOS el 286 noestá en modo protegido y el cuelgue de un solo proceso -bien el programa principal o una rutina operada porinterrupciones- significa la caída inmediata de todo el sistema.

El 8086 no posee ningún mecanismo para apoyar la multitarea ni la memoria virtual desde elprocesador, por lo que es difícil diseñar un sistema multitarea para el mismo y casi imposible conseguir quesea realmente operativo. Obviamente, el 286 en modo protegido pierde absolutamente toda la compatibilidadcon los procesadores anteriores. Por ello, en este libro sólo trataremos el modo real, único disponible bajoDOS, aunque veremos alguna instrucción extra que también se puede emplear en modo real.

Las características generales del 286 son: tiene un bus de datos de 16 bits, un bus de direcciones de24 bits (16 Mb); posee 25 instrucciones más que el 8086 y admite 8 modos de direccionamiento. En modovirtual permite direccionar hasta 1 Gigabyte. Las frecuencias de trabajo típicas son de 12 y 16 MHz, aunqueexisten versiones a 20 y 25 MHz. Aquí, la instrucción más lenta es la misma que en el caso del 8086, soloque emplea 29 ciclos de reloj en lugar de 206. Un 286 de categoría media (16 MHz) podría ejecutar más demedio millón de instrucciones de estas en un segundo, casi 15 veces más que un 8086 medio a 8 MHz. Sinembargo, transfiriendo datos entre registros la diferencia de un procesador a otro se reduce notablemente,aunque el 286 es más rápido y no sólo gracias a los MHz adicionales.

Versiones mejoradas de los Intel 8086 y 8088 se encuentran también en los procesadores NEC-V30y NEC-V20 respectivamente. Ambos son compatibles Hardware y Software, con la ventaja de que elprocesado de instrucciones está optimizado, llegando a superar casi en tres veces la velocidad de losoriginales en algunas instrucciones aritméticas. También poseen una cola de prebúsqueda mayor (cuando elmicroprocesador está ejecutando una instrucción, si no hace uso de los buses externos, carga en una colaFIFO de unos pocos bytes las posiciones posteriores a la que está procesando, de esta forma una vez queconcluye la instrucción en curso ya tiene internamente la que le sigue). Además, los NEC V20 y V30disponen de las mismas instrucciones adicionales del 286 en modo real, al igual que el 80186 y el 80188.

Por su parte, el 386 dispone de una arquitectura de registros de 32 bits, con un bus de direccionestambién de 32 bits (direcciona hasta 4 Gigabytes = 4096 Mb) y más modos posibles de funcionamiento: elmodo real (compatible 8086), el modo protegido (relativamente compatible con el del 286), un modoprotegido propio que permite -¡por fin!- romper la barrera de los tradicionales segmentos y el modo «virtual86», en el que puede emular el funcionamiento simultáneo de varios 8086. Una vez más, todos los modosson incompatibles entre sí y requieren de un sistema operativo específico: si se puede perdonar al fabricantela pérdida de compatibilidad del modo avanzados del 286 frente al 8086, debido a la lógica evolucióntecnológica, no se puede decir lo mismo del 386 respecto al 286: no hubiera sido necesario añadir un nuevomodo protegido si hubiera sido mejor construido el del 286 apenas un par de años atrás. Normalmente, los386 suelen operar en modo real (debido al DOS) por lo que no se aprovechan las posibilidades multitareani de gestión de memoria. Por otra parte, aunque se pueden emplear los registros de 32 bits en modo real,ello no suele hacerse -para mantener la compatibilidad con procesadores anteriores- con lo que de entradase está tirando a la basura un 50% de la capacidad de proceso del chip, aunque por fortuna estos procesadoressuelen trabajar a frecuencias de 16/20 MHz (obsoletas) y normalmente de 33 y hasta 40 MHz.

El 386sx es una variante del 386 a nivel de hardware, aunque es compatible en software.Básicamente, es un 386 con un bus de datos de sólo 16 bits -más lento, al tener que dar dos pasadas para undato de 32 bits-. De hecho, podría haber sido diseñado perfectamente para mantener una compatibilidadhardware con el 286, aunque el fabricante lo evitó probablemente por razones comerciales.

El 486 se diferencia del 386 en la integración en un solo chip del coprocesador 387. También se hamejorado la velocidad de operación: la versión de 25 MHz dobla en términos reales a un 386 a 25 MHzequipado con el mismo tamaño de memoria caché. La versión 486sx no se diferencia en el tamaño del bus,también de 32 bits, sino en la ausencia del 387 (que puede ser añadido externamente). También existenversiones de 486 con buses de 16 bits, el primer fabricante de estos chips, denominados 486SLC, ha sidoCyrix. Una tendencia iniciada por el 486 fue la de duplicar la velocidad del reloj interno (pongamos por caso

Page 33: PCA, PS2 ,IBM y AT

33MICROPROCESADORES 8086/88, 286, 386 Y 486

de 33 a 66 MHz) aunque en las comunicaciones con los buses exteriores se respeten los 33 MHz. Ello agilizala ejecución de las instrucciones más largas: bajo DOS, el rendimiento general del sistema se puedeconsiderar prácticamente el doble. Son los chips DX2 (también hay una variante a 50 MHz: 25 x 2). Laculminación de esta tecnología viene de la mano de los DX4 a 75/100 MHz (25/33 x 3).

El Pentium, último procesador de Intel en el momento de escribirse estas líneas, se diferenciarespecto al 486 en el bus de datos (ahora de 64 bits, lo que agiliza los accesos a memoria) y en unelevadísimo nivel de optimización y segmentación que le permite, empleando compiladores optimizados,simultanear en muchos casos la ejecución de dos instrucciones consecutivas. Posee dos cachés internas, tienecapacidad para predecir el destino de los saltos y la unidad de coma flotante experimenta elevadas mejoras.Sin embargo, bajo DOS, un Pentium básico sólo es unas 2 veces más rápido que un 486 a la mismafrecuencia de reloj. Comenzó en 60/90 MHz hasta los 166/200/233 MHz de las últimas versiones (PentiumPro y MMX), que junto a diversos clones de otros fabricantes, mejoran aún más el rendimiento. Todos losequipos Pentium emplean las técnicas DX, ya que las placas base típicas corren a 60 MHz. Para hacerse unaidea, por unas 200000 pts de 1997 un equipo Pentium MMX a 233 MHz es cerca de 2000 veces más rápidoen aritmética entera que el IBM PC original de inicios de la década de los 80; en coma flotante la diferenciaaumenta incluso algunos órdenes más de magnitud. Y a una fracción del coste (un millón de pts de aquelentonces que equivale a unos 2,5 millones de hoy en día). Aunque no hay que olvidar la revolución del restode los componentes: 100 veces más memoria (central y de vídeo), 200 veces más grande el disco duro... yque un disco duro moderno transfiere datos 10 veces más deprisa que la memoria de aquel IBM PC original.Por desgracia, el software no ha mejorado el rendimiento, ni remotamente, en esa proporción: es la facturapasada por las técnicas de programación cada vez a un nivel más alto (aunque nadie discute sus ventajas).

Una característica de los microprocesadores a partir del 386 es la disponibilidad de memorias cachéde alta velocidad de acceso -muy pocos nanosegundos- que almacenan una pequeña porción de la memoriaprincipal. Cuando la CPU accede a una posición de memoria, cierta circuitería de control se encarga de irdepositando el contenido de esa posición y el de las posiciones inmediatamente consecutivas en la memoriacaché. Cuando sea necesario acceder a la instrucción siguiente del programa, ésta ya se encuentra en la cachéy el acceso es muy rápido. Lo ideal sería que toda la memoria del equipo fuera caché, pero esto no es todavíaposible actualmente. Una caché de tamaño razonable puede doblar la velocidad efectiva de proceso de laCPU. El 8088 carecía de memoria caché, pero sí estaba equipado con una unidad de lectura adelantada deinstrucciones con una cola de prebúsqueda de 4 bytes: de esta manera, se agilizaba ya un tanto la velocidadde proceso al poder ejecutar una instrucción al mismo tiempo que iba leyendo la siguiente.

3.2. - REGISTROS DEL 8086 Y DEL 286.

Estos procesadores disponen de 14 registros de 16 bits (el 286 alguno más, pero no se suele emplearbajo DOS). La misión de estos registros es almacenar las posiciones de memoria que van a experimentarrepetidas manipulaciones, ya que los accesos a memoria son mucho más lentos que los accesos a los registros.Además, hay ciertas operaciones que sólo se pueden realizar sobre los registros. No todos los registros sirvenpara almacenar datos, algunos están especializados en apuntar a las direcciones de memoria. La mecánicabásica de funcionamiento de un programa consiste en cargar los registros con datos de la memoria o de unpuerto de E/S, procesar los datos y devolver el resultado a la memoria o a otro puerto de E/S. Obviamente,si un dato sólo va a experimentar un cambio, es preferible realizar la operación directamente sobre lamemoria, si ello es posible. A continuación se describen los registros del 8086.

AX SP CS IP

BX BP DS flags

CX SI SS Registropuntero de

DX DI ES instruccionesy flags

Registros Registros Registrosde punteros de de

datos pila e índices segmento

Page 34: PCA, PS2 ,IBM y AT

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

- Registros de datos:

AX, BX, CX, DX: pueden utilizarse bien como registros de 16 bits o como dos registrosseparados de 8 bits (byte superior e inferior) cambiando la X por H o L según queramos referirnosa la parte alta o baja respectivamente. Por ejemplo, AX se descompone en AH (parte alta) y AL(parte baja). Evidentemente, ¡cualquier cambio sobre AH o AL altera AX!: valga como ejemplo queal incrementar AH se le están añadiendo 256 unidades a AX.

AX = Acumulador.

Es el registro principal, es utilizado en las instrucciones de multiplicación y división y enalgunas instrucciones aritméticas especializadas, así como en ciertas operaciones de carácter específicocomo entrada, salida y traducción. Obsérvese que el 8086 es suficientemente potente para realizar lasoperaciones lógicas, la suma y la resta sobre cualquier registro de datos, no necesariamente elacumulador.

BX = Base.Se usa como registro base para referenciar direcciones de memoria con direccionamiento

indirecto, manteniendo la dirección de la base o comienzo de tablas o matrices. De esta manera, noes preciso indicar una posición de memoria fija, sino la número BX (así, haciendo avanzar de unidaden unidad a BX, por ejemplo, se puede ir accediendo a un gran bloque de memoria en un bucle).

CX = Contador.Se utiliza comúnmente como contador en bucles y operaciones repetitivas de manejo de

cadenas. En las instrucciones de desplazamiento y rotación se utiliza como contador de 8 bits.

DX = Datos.Usado en conjunción con AX en las operaciones de multiplicación y división que involucran

o generan datos de 32 bits. En las de entrada y salida se emplea para especificar la dirección delpuerto E/S.

- Registros de segmento:

Definen áreas de 64 Kb dentro del espacio de direcciones de 1 Mb del 8086. Estas áreaspueden solaparse total o parcialmente. No es posible acceder a una posición de memoria no definidapor algún segmento: si es preciso, habrá de moverse alguno.

CS = Registro de segmento de código (code segment).Contiene la dirección del segmento con las instrucciones del programa. Los programas de más

de 64 Kb requieren cambiar CS periódicamente.

DS = Registro de segmento de datos (data segment).Segmento del área de datos del programa.

SS = Registro de segmento de pila (stack segment).Segmento de pila.

ES = Registro de segmento extra (extra segment).Segmento de ampliación para zona de datos. Es extraordinariamente útil actuando en

conjunción con DS: con ambos se puede definir dos zonas de 64 Kb, tan alejadas como se desee enel espacio de direcciones, entre las que se pueden intercambiar datos.

Page 35: PCA, PS2 ,IBM y AT

35MICROPROCESADORES 8086/88, 286, 386 Y 486

- Registros punteros de pila:

SP = Puntero de pila (stack pointer).Apunta a la cabeza de la pila. Utilizado en las instrucciones de manejo de la pila.

BP = Puntero base (base pointer).Es un puntero de base, 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).

- Registros índices:

SI = Índice fuente (source index).Utilizado como registro de índice en ciertos modos de direccionamiento indirecto, también

se emplea para guardar un valor de desplazamiento en operaciones de cadenas.

DI = Índice destino (destination index).Se usa en determinados modos de direccionamiento indirecto y para almacenar un

desplazamiento en operaciones con cadenas.

- Puntero de instrucciones o contador de programa:

IP = Puntero de instrucción (instruction pointer).Marca el desplazamiento de la instrucción en curso dentro del segmento de código. Es

automáticamente modificado con la lectura de una instrucción.

- Registro de estado o de indicadores (flags).

Es un registro de 16 bits de los cuales 9 son utilizados para indicar diversas situacionesdurante la ejecución de un programa. Los bits 0, 2, 4, 6, 7 y 11 son indicadores de condición, quereflejan los resultados de operaciones del programa; los bits del 8 al 10 son indicadores de controly el resto no se utilizan. Estos indicadores pueden ser comprobados por las instrucciones de saltocondicional, lo que permite variar el flujo secuencial del programa según el resultado de lasoperaciones.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

OF DF IF TF SF ZF AF PF CF

CF (Carry Flag) Indicador de acarreo. Su valor más habitual es lo que nos llevamos en unasuma o resta.

OF (Overflow Flag) Indicador de desbordamiento. Indica que el resultado de una operación nocabe en el tamaño del operando destino.

ZF (Zero Flag) Indicador de resultado 0 o comparación igual.SF (Sign Flag) Indicador de resultado o comparación negativa.PF (Parity Flag) Indicador de paridad. Se activa tras algunas operaciones aritmético-lógicas

para indicar que el número de bits a uno resultante es par.AF (Auxiliary Flag) Para ajuste en operaciones BCD.DF (Direction Flag) Indicador de dirección. Manipulando bloques de memoria, indica el sentido

de avance (ascendente/descendente).IF (Interrupt Flag) Indicador de interrupciones: puesto a 1 están permitidas.TF (Trap Flag) Indicador de atrape (ejecución paso a paso).

Page 36: PCA, PS2 ,IBM y AT

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

3.3. - REGISTROS DEL 386 Y PROCESADORES SUPERIORES.

Los 386 y superiores disponen de muchos más registros de los que vamos a ver ahora. Sin embargo, bajo elsistema operativo DOS sólo se suelen emplear los que veremos, que constituyen básicamente una extensión a 32 bitsde los registros originales del 8086.

AX SP CS IP

EAX

BX BP DS flags

EBX EBP

CX SI ES FS

ECX ESI

DX DI SS GS

EDX EDI

Se amplía el tamaño de los registrosde datos (que pueden ser accedidos enfragmentos de 8, 16 ó 32 bits) y se añadendos nuevos registros de segmentomultipropósito (FS y GS). Algunos de losregistros aquí mostrados son realmente de 32bits (como EIP en vez de IP), pero bajosistema operativo DOS no pueden serempleados de manera directa, por lo que noles consideraremos.

3.4. - MODOS DE DIRECCIONAMIENTO.

Son los distintos modos de acceder a los datos en memoria por parte del procesador. Antes de verlos modos de direccionamiento, echaremos un vistazo a la sintaxis general de las instrucciones, ya quepondremos alguna en los ejemplos:

INSTRUCCIÓN DESTINO, FUENTE

Donde destino indica dónde se deja el resultado de la operación en la que pueden participar (segúncasos) FUENTE e incluso el propio DESTINO. Hay instrucciones, sin embargo, que sólo tienen un operando,como la siguiente, e incluso ninguno:

INSTRUCCIÓN DESTINO

Como ejemplos, aunque no hemos visto aún las instrucciones utilizaremos un par de ellas: la de copiao movimiento de datos (MOV) y la de suma (ADD).

3.4.1. - ORGANIZACIÓN DE DIRECCIONES: SEGMENTACIÓN.

Como ya sabemos, los microprocesadores 8086 y compatibles poseen registros de un tamaño máximode 16 bits que direccionarían hasta 64K; en cambio, la dirección se compone de 20 bits con capacidad para1Mb, hay por tanto que recurrir a algún artificio para direccionar toda la memoria. Dicho artificio consisteen la segmentación: se trata de dividir la memoria en grupos de 64K. Cada grupo se asocia con un registrode segmento; el desplazamiento (offset) dentro de ese segmento lo proporciona otro registro de 16 bits. Ladirección absoluta se calcula multiplicando por 16 el valor del registro de segmento y sumando el offset,obteniéndose una dirección efectiva de 20 bits. Esto equivale a concebir el mecanismo de generación de ladirección absoluta, como si se tratase de que los registros de segmento tuvieran 4 bits a 0 (imaginarios) a laderecha antes de sumarles el desplazamiento:

dirección = segmento * 16 + offset

En la práctica, una dirección se indica con la notación SEGMENTO:OFFSET; además, una mismadirección puede expresarse de más de una manera: por ejemplo, 3D00h:0300h es equivalente a 3D30:0000h.Es importante resaltar que no se puede acceder a más de 64 Kb en un segmento de datos. Por ello, en losprocesadores 386 y superiores no se deben emplear registros de 32 bit para generar direcciones (bajo DOS),aunque para los cálculos pueden ser interesantes (no obstante, sí sería posible configurar estos procesadorespara poder direccionar más memoria bajo DOS con los registros de 32 bits, aunque no resulta por lo generalpráctico).

Page 37: PCA, PS2 ,IBM y AT

37MICROPROCESADORES 8086/88, 286, 386 Y 486

3.4.2. - MODOS DE DIRECCIONAMIENTO.

- Direccionamiento inmediato: El operando es una constante situada detrás del código de lainstrucción. Sin embargo, como registro destino no se puede indicar uno de segmento (habrá que utilizar unode datos como paso intermedio).

ADD AX,0fffh

El número hexadecimal 0fffh es la constante numérica que en el direccionamiento inmediatose le sumará al registro AX. Al trabajar con ensambladores, se pueden definir símbolos constantes(ojo, no variables) y es más intuitivo:

dato EQU 0fffh ; símbolo constanteMOV AX,dato

Si se referencia a la dirección de memoria de una variable de la siguiente forma, también setrata de un caso de direccionamiento inmediato:

dato DW 0fffh ; ahora es una variableMOV AX,OFFSET dato ; 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 FFFMOV AX,0A11

- Direccionamiento de registro: Los operandos, necesariamente de igual tamaño, están contenidos enlos registros indicados en la instrucción:

MOV DX,AXMOV AH,AL

- Direccionamiento directo o absoluto: El operando está situado en la dirección indicada en lainstrucción, relativa al segmento que se trate:

MOV AX,[57D1h]MOV AX,ES:[429Ch]

Esta sintaxis (quitando la ’h’ de hexadecimal) sería la que admite el programa DEBUG (realmentehabría que poner, en el segundo caso, ES: en una línea y el MOV en otra). Al trabajar con ensambladores,las variables en memoria se pueden referenciar con etiquetas simbólicas:

MOV AX,datoMOV AX,ES:dato

dato DW 1234h ; variable del programa

En el primer ejemplo se transfiere a AX el valor contenido en la dirección apuntadapor la etiqueta dato sobre el segmento de datos (DS) que se asume por defecto; en elsegundo ejemplo se indica de forma explícita el segmento tratándose del segmento ES. Ladirección efectiva se calcula de la forma ya vista con anterioridad: Registro desegmento * 16 + desplazamiento_de_dato (este desplazamiento depende de la posición alensamblar el programa).

Page 38: PCA, PS2 ,IBM y AT

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

- Direccionamiento indirecto: El operando se encuentra en una dirección señalada por un registro desegmento*16 más un registro base (BX/BP) o índice (SI/DI). (Nota: BP actúa por defecto con SS).

MOV AX,[BP] ; AX = [SS*16+BP]MOV ES:[DI],AX ; [ES*16+DI] = AX

- Indirecto con índice o indexado: El operando se encuentra en una dirección determinada por la sumade un registro de segmento*16, un registro de índice, SI o DI y un desplazamiento de 8 ó 16 bits. Ejemplos:

MOV AX,[DI+DESP] ó MOV AX,desp[DI]ADD [SI+DESP],BX ó ADD desp[SI],BX

- Indirecto con base e índice o indexado a base: El operando se encuentra en una direcciónespecificada por la suma de un registro de segmento*16, uno de base, uno de índice y opcionalmente undesplazamiento de 8 ó 16 bits:

MOV AX,ES:[BX+DI+DESP] ó MOV AX,ES:desp[BX][DI]MOV CS:[BX+SI+DESP],CX ó MOV CS:desp[BX][SI],CX

Combinaciones de registros de segmento y desplazamiento.

Como se ve en los modos de direccionamiento, hay casos en los que se indica explícitamente elregistro de segmento a usar para acceder a los datos. Existen unos segmentos asociados por defecto a losregistros de desplazamiento (IP, SP, BP, BX, DI, SI); sólo es necesario declarar el segmento cuando nocoincide con el asignado por defecto. En ese caso, el ensamblador genera un byte adicional (a modo deprefijo) para indicar cuál es el segmento referenciado. La siguiente tabla relaciona las posibles combinacionesde los registros de segmento y los de desplazamiento:

CS SS DS ES

IP Sí No No No

SP No Sí No No

BP con prefijo por defecto con prefijo con prefijo

BX con prefijo con prefijo por defecto con prefijo

SI con prefijo con prefijo por defecto con prefijo

DI con prefijo con prefijo por defecto con prefijo(1)

(1) También por defecto en el manejo de cadenas.

Los 386 y superiores admiten otros modos de direccionamiento más sofisticados, que se verán en elpróximo capítulo, después de conocer todas las instrucciones del 8086. Por ahora, con todos estos modos sepuede considerar que hay más que suficiente. De hecho, algunos se utilizan en muy contadas ocasiones.

3.5. - LA PILA.

La pila es un bloque de memoria de estructura LIFO (Last Input First Output: último en entrar,primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de pila). Lasposiciones individuales dentro de la pila se calculan sumando al contenido del segmento de pila SS undesplazamiento contenido en el registro puntero de pila SP. Todos los datos que se almacenan en la pila sonde longitud palabra, y cada vez que se introduce algo en ella por medio de las instrucciones de manejo depila (PUSH y POP), el puntero se decrementa en dos; es decir, la pila avanza hacia direcciones decrecientes.

Page 39: PCA, PS2 ,IBM y AT

39MICROPROCESADORES 8086/88, 286, 386 Y 486

El registro BP suele utilizarse normalmente para apuntar a una cierta posición de la pila y accederindexadamente a sus elementos -generalmente en el caso de variables- sin necesidad de desapilarlos paraconsultarlos.

La pila es utilizada frecuentemente al principio de una subrutina para preservar los registros que nose desean modificar; al final de la subrutina basta con recuperarlos en orden inverso al que fuerondepositados. En estas operaciones conviene tener cuidado, ya que la pila en los 8086 es común al procesadory al usuario, por lo que se almacenan en ella también las direcciones de retorno de las subrutinas. Esta últimaes, de hecho, la más importante de sus funciones. La estructura de pila permite que unas subrutinas llamena otras que a su vez pueden llamar a otras y así sucesivamente: en la pila se almacenan las direcciones deretorno, que serán las de la siguiente instrucción que provocó la llamada a la subrutina. Así, al retornar dela subrutina se extrae de la pila la dirección a donde volver. Los compiladores de los lenguajes de alto nivella emplean también para pasar los parámetros de los procedimientos y para generar en ella las variablesautomáticas -variables locales que existen durante la ejecución del subprograma y se destruyeninmediatamente después-. Por ello, una norma básica es que se debe desapilar siempre todo lo apilado paraevitar una pérdida de control inmediata del ordenador.

Ejemplo de operación sobre la pila (todos los datos son arbitrarios):

Memoria SS:SP Memoria SS:SP Memoria SS:SP

66h 66h 66h

91h <-- 14C0:D022 91h 91h <-- 14C0:D022

F3h 12h 12h

21h 34h <-- 14C0:D020 34h

Situación inicial después de PUSH AX después de POP BXAX = 1234h AX = 1234h AX = 1234hBX = 9D33h BX = 9D33h BX = 1234h

3.6. - UN PROGRAMA DE EJEMPLO.

Aunque las instrucciones del procesador no serán vistas hasta el próximo capítulo, con objeto deayudar a la imaginación del lector elaboraremos un primer programa de ejemplo en lenguaje ensamblador.La utilidad de este programa es dejar patente que lo único que entiende el 8086 son números, aunquenosotros nos referiremos a ellos con unos símbolos que faciliten entenderlos. También es interesante esteejemplo para afianzar el concepto de registro de segmento.

En este programa sólo vamos a emplear las instrucciones MOV, ya conocida, y alguna otra más comola instrucción INC (incrementar), DEC (disminuir una unidad) y JNZ (saltar si el resultado no es cero).Suponemos que el programa está ubicado a partir de la dirección de memoria 14D3:7A10 (arbitrariamenteelegida) y que lo que pretendemos hacer con él es limpiar la pantalla. Como el ordenador es un PC conmonitor en color, la pantalla de texto comienza en B800:0000 (no es más que una zona de memoria). Porcada carácter que hay en dicha pantalla, comenzando arriba a la izquierda, a partir de la dirección B800:0000tenemos dos bytes: el primero, con el código ASCII del carácter y el segundo con el color. Lo que vamosa hacer es rellenar los 2000 caracteres (80 columnas x 25 líneas) con espacios en blanco (código ASCII 32,ó 20h en hexadecimal), sin modificar el color que hubiera antes. Esto es, se trata de poner el valor 32 en ladirección B800:0000, la B800:0002, la B800:0004... y así sucesivamente.

El programa quedaría en memoria de esta manera: La primera columna indica la dirección dememoria donde está el programa que se ejecuta (CS=14D3h e IP=7A10h al principio). La segunda columnaconstituye el código máquina que interpreta el 8086. Algunas instrucciones ocupan un byte de memoria, otrasdos ó tres (las hay de más). La tercera columna contiene el nombre de las instrucciones, algo mucho máslegible para los humanos que los números:

Page 40: PCA, PS2 ,IBM y AT

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

14D3:7A10 B9 D0 07 MOV CX,7D0H ; CX = 7D0h (2000 decimal = 7D0 hexadecimal)14D3:7A13 B8 00 B8 MOV AX,0B800h ; segmento de la memoria de pantalla14D3:7A16 8E D8 MOV DS,AX ; apuntar segmento de datos a la misma14D3:7A18 BB 00 00 MOV BX,0 ; apuntar al primer carácter ASCII de la pantalla14D3:7A1B C6 07 20 MOV BYTE PTR [BX],32 ; se pone BYTE PTR para indicar que 32 es de 8 bits14D3:7A1E 43 INC BX ; BX=BX+1 - apuntar al byte de color14D3:7A1F 43 INC BX ; BX=BX+1 - apuntar al siguiente carácter ASCII14D3:7A20 49 DEC CX ; CX=CX-1 - queda un carácter menos14D3:7A21 75 F8 JNZ -8 ; si CX no es 0, saltar 8 bytes atrás (a 14D3:7A1B)

Como se puede ver, la segunda instrucción (bytes de código máquina 0B8h, 0 y 0B8h colocados enposiciones consecutivas) está colocada a partir del desplazamiento 7A13h, ya que la anterior que ocupaba 3bytes comenzaba en 7A10h. En el ejemplo cargamos el valor 0B800h en DS apoyándonos en AX comointermediario. El motivo es que los registros de segmento no admiten el direccionamiento inmediato. Amedida que se van haciendo programas, el ensamblador da mensajes de error cuando se encuentra con estosfallos y permite ir aprendiendo con facilidad las normas, que tampoco son demasiadas. La instrucción MOVBYTE PTR [BX],32 equivale a decir: «poner en la dirección de memoria apuntada por BX (DS:[BX] paraser más exactos) el byte de valor 32». El valor 0F8h del código máquina de la última instrucción es elcomplemento a dos (número negativo) del valor 8.

Normalmente, casi nunca habrá que ensamblar a mano consultando unas tablas, como hemos hechoen este ejemplo. Sin embargo, la mejor manera de aprender ensamblador es no olvidando la estrecha relaciónde cada línea de programa con la CPU y la memoria.

Page 41: PCA, PS2 ,IBM y AT

41JUEGO DE INSTRUCCIONES 80x86

Capítulo IV: JUEGO DE INSTRUCCIONES 80x86

4.1. - DESCRIPCIÓN COMPLETA DE LAS INSTRUCCIONES.

Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguientenotación:

- bit no modificado? desconocido o indefinidox modificado según el resultado de la operación1 puesto siempre a 10 puesto siempre a 0

4.1.1. - INSTRUCCIONES DE CARGA DE REGISTROS Y DIRECCIONES.

MOV (transferencia)

Sintaxis: MOV dest, origen.

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Transfiere datos de longitud byte o palabra del operando origen al operando destino.Pueden ser operando origen y operando destino cualquier registro o posición de memoriadireccionada de las formas ya vistas, con la única condición de que origen y destino tenganla misma dimensión. Existen ciertas limitaciones, como que los registros de segmento noadmiten el direccionamiento inmediato: es incorrecto MOV DS,4000h; pero no lo es porejemplo MOV DS,AX o MOV DS,VARIABLE. No es posible, así mismo, utilizar CS comodestino (es incorrecto hacer MOV CS,AX aunque pueda admitirlo algún ensamblador). Alhacer MOV hacia un registro de segmento, las interrupciones quedan inhibidas hasta despuésde ejecutarse la siguiente instrucción (8086/88 de 1983 y procesadores posteriores).

Ejemplos: mov ds,axmov bx,es:[si]mov si,offset dato

En el último ejemplo, no se coloca en SI el valor de la variable «dato» sino sudirección de memoria o desplazamiento respecto al segmento de datos. En otras palabras, SIes un puntero a «dato» pero no es «dato». En el próximo capítulo se verá cómo se declaranlas variables.

XCHG (intercambiar)

Sintaxis: XCHG destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Page 42: PCA, PS2 ,IBM y AT

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

Intercambia el contenido de los operandos origen y destino. No pueden utilizarseregistros de segmentos como operandos.

Ejemplo: xchg bl,chxchg mem_pal,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 detraducción. Los datos se toman desde una dirección de la tabla correspondiente a BX + AL,donde bx es un puntero a el comienzo de la tabla y AL es un índice. Indicar «tabla» al ladode xlat es sólo una redundancia opcional.

Ejemplo: mov bx,offset tablamov al,4xlat

LEA (carga dirección efectiva)

Sintaxis: LEA destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Transfiere el desplazamiento del operando fuente al operando destino. Otrasinstrucciones pueden a continuación utilizar el registro como desplazamiento para acceder alos datos que constituyen el objetivo. El operando destino no puede ser un registro desegmento. En general, esta instrucción es equivalente a «MOV destino,OFFSET fuente» yde hecho los buenos ensambladores (TASM) la codifican como MOV para economizar unbyte de memoria. Sin embargo, LEA es en algunos casos más potente que MOV al permitirindicar registros de índice y desplazamiento para calcular el offset:

lea dx,datos[si]

En el ejemplo de arriba, el valor depositado en DX es el offset de la etiqueta «datos»más el registro SI. Esa sola instrucción es equivalente a estas dos:

mov dx,offset datosadd dx,si

LDS (carga un puntero utilizando DS)

Sintaxis: LDS destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Traslada un puntero de 32 bits (dirección completa de memoria compuesta por

Page 43: PCA, PS2 ,IBM y AT

43JUEGO DE INSTRUCCIONES 80x86

segmento y desplazamiento), al destino indicado y a DS. A partir de la dirección indicadapor el operando origen, el procesador toma 4 bytes de la memoria: con los dos primerosforma una palabra que deposita en «destino» y, con los otros dos, otra en DS.

Ejemplo: punt dd 12345678hlds si,punt

Como resultado de esta instrucción, en DS:SI se hace referencia a la posición dememoria 1234h:5678h; ’dd’ sirve para definir una variable larga de 4 bytes (denominada«punt» en el ejemplo) y será explicado en el capítulo siguiente.

LES (carga un puntero utilizando ES)

Sintaxis: LES destino, origen

Esta instrucción es análoga a LDS, pero utilizando ES en lugar de DS.

LAHF (carga AH con los indicadores)

Sintaxis: LAHF

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Carga los bits 7, 6, 4, 2 y 0 del registro AH con el contenido de los indicadores SF,ZF, AF, PF Y CF respectivamente. El contenido de los demás bits queda sin definir.

SAHF (copia AH en los indicadores)

Sintaxis: SAHF

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - x x x x x

Transfiere el contenido de los bits 7, 6, 4, 2 y 0 a los indicadores SF, ZF, AF, PFy CF respectivamente.

4.1.2. - INSTRUCCIONES DE MANIPULACIÓN DEL REGISTRO DE ESTADO.

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.

CLD (baja el indicador de dirección)

Sintaxis: CLD

Page 44: PCA, PS2 ,IBM y AT

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

Indicadores: OF DF IF TF SF ZF AF PF CF- 0 - - - - - - -

Pone a 0 el indicador de dirección DF, por lo que los registros SI y/o DI seautoincrementan en las operaciones de cadenas, sin afectar al resto de los indicadores. EsNECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce conseguridad el valor de DF. Véase STD.

CLI (baja indicador de interrupción)

Sintaxis: CLI

Indicadores: OF DF IF TF SF ZF AF PF CF- - 0 - - - - - -

Borra el indicador de activación de interrupciones IF, lo que desactiva lasinterrupciones enmascarables. Es muy conveniente hacer esto antes de modificar la parejaSS:SP en los 8086/88 anteriores a 1983 (véase comentario en la instrucción MOV), o antesde cambiar un vector de interrupción sin el apoyo del DOS. Generalmente las interrupcionessólo se inhiben por breves instantes en momentos críticos. Véase también STI.

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.

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.

STD (pone a uno el indicador de dirección)

Sintaxis: STD

Indicadores: OF DF IF TF SF ZF AF PF CF- 1 - - - - - - -

Pone a 1 el indicador de dirección DF, por lo que los registros SI y/o DI seautodecrementan en las operaciones de cadenas, sin afectar al resto de los indicadores. EsNECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce conseguridad el estado de DF. Véase también CLD.

Page 45: PCA, PS2 ,IBM y AT

45JUEGO DE INSTRUCCIONES 80x86

STI (pone a uno el indicador de interrupción)

Sintaxis: STI

Indicadores: OF DF IF TF SF ZF AF PF CF

- - 1 - - - - - -

Pone a 1 la bandera de desactivación de interrupciones IF y activa las interrupcionesenmascarables. Una interrupción pendiente no es reconocida, sin embargo, hasta después deejecutar la instrucción que sigue a STI. Véase también CLI.

4.1.3. - INSTRUCCIONES DE MANEJO DE LA PILA.

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 porSP) al operando destino que a de ser tipo palabra, e incrementa en dos el registro SP. Lainstrucción POP CS, poco útil, no funciona correctamente en los 286 y superiores.

Ejemplos: pop axpop 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 especificadaen el operando origen a la cima de la pila. El registro CS aquí sí se puede especificar comoorigen, al contrario de lo que afirman algunas publicaciones.

Ejemplo: push cs

POPF (extrae los indicadores de la pila)

Sintaxis: POPF

Indicadores: OF DF IF TF SF ZF AF PF CFx x x x x x x x x

Traslada al registro de los indicadores la palabra almacenada en la cima de la pila;a continuación el puntero de pila SP se incrementa en dos.

PUSHF (introduce los indicadores en la pila)

Page 46: PCA, PS2 ,IBM y AT

46 EL UNIVERSO DIGITAL DEL IBM PC, 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 delos indicadores.

4.1.4. - INSTRUCCIONES DE TRANSFERENCIA DE CONTROL.

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, salvando previamente en lapila la dirección de la instrucción siguiente, para poder volver a ella una vez ejecutado elprocedimiento. El procedimiento puede estar en el mismo segmento (tipo NEAR) o en otrosegmento (tipo FAR). A su vez la llamada puede ser directa a una etiqueta (especificando eltipo de llamada NEAR -por defecto- o FAR) o indirecta, indicando la dirección donde seencuentra el puntero. Según la llamada sea cercana o lejana, se almacena en la pila unadirección de retorno de 16 bits o dos palabras de 16 bits indicando en este último caso tantoel offset (IP) como el segmento (CS) a donde volver.

Ejemplos: call proc1

dir dd 0f000e987hcall dword ptr dir

En el segundo ejemplo, la variable dir almacena la dirección a donde saltar. De estaúltima manera -conociendo su dirección- puede llamarse también a un vector de interrupción,guardando previamente los flags en la pila (PUSHF), porque la rutina de interrupciónretornará (con IRET en vez de con RETF) sacándolos.

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. Labifurcación puede ser también directa o indirecta como anteriormente vimos, pero ademáspuede ser corta (tipo SHORT) con un desplazamiento comprendido entre -128 y 127; o larga,con un desplazamiento de dos bytes con signo. Si se hace un JMP SHORT y no llega el salto(porque está demasiado alejada esa etiqueta) el ensamblador dará error. Los buenosensambladores (como TASM) cuando dan dos pasadas colocan allí donde es posible un saltocorto, para economizar memoria, sin que el programador tenga que ocuparse de poner«short». Si el salto de dos bytes, que permite desplazamientos de 64 Kb en la memoria siguesiendo insuficiente, se puede indicar con «far» que es largo (salto a otro segmento).

Page 47: PCA, PS2 ,IBM y AT

47JUEGO DE INSTRUCCIONES 80x86

Ejemplos: jmp etiquetajmp 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 siguientedirección. Se extraerá el registro de segmento y el desplazamiento en un procedimiento detipo FAR (dos palabras) y solo el desplazamiento en un procedimiento NEAR (una palabra).si esta instrucción es colocada dentro de un bloque PROC-ENDP (como se verá en elsiguiente capítulo) el ensamblador sabe el tipo de retorno que debe hacer, según elprocedimiento sea NEAR o FAR. En cualquier caso, se puede forzar que el retorno sea detipo FAR con la instrucción RETF. «Valor», si es indicado permite sumar una cantidad«valor» en bytes a SP antes de retornar, lo que es frecuente en el código generado por loscompiladores para retornar de una función con parámetros. También se puede retornar de unainterrupción con RETF 2, para que devuelva el registro de estado sin restaurarlo de la pila.

Condicional

Las siguientes instrucciones son de transferencia condicional de control a lainstrucción que se encuentra en la posición IP+desplazamiento (desplazamiento comprendidoentre -128 y +127) si se cumple la condición. Algunas condiciones se pueden denotar devarias maneras. Todos los saltos son cortos y si no alcanza hay que apañárselas como sea.En negrita se realzan las condiciones más empleadas. Donde interviene SF se consideran consigno los operandos implicados en la última comparación u operación aritmetico-lógica, y seindican en la tabla como ’±’ (-128 a +127 ó -32768 a +32767); en los demás casos, indicadoscomo ’+’, se consideran sin signo (0 a 255 ó 0 a 65535):

JA/JNBE Salto si mayor (above), si no menor o igual (not below or equal), si CF=0 y ZF=0. +JAE/JNB Salto si mayor o igual (above or equal), si no menor (not below), si CF=0. +JB/JNAE/JC Salto si menor (below), si no superior ni igual (not above or equal), si acarreo, si CF=1. +JBE/JNA Salto si menor o igual (not below or equal), si no mayor (not above), si CF=1 ó ZF=1. +JCXZ Salto si CX=0.JE/JZ Salto si igual (equal), si cero (zero), si ZF=1.JG/JNLE Salto si mayor (greater), si no menor ni igual (not less or equal), si ZF=0 y SF=0. ±JGE/JNL Salto si mayor o igual (greater or equal), si no menor (not less), si SF=0. ±JL/JNGE Salto si menor (less), si no mayor ni igual (not greater or equal), si SF<>OF. ±JLE/JNG Salto si menor o igual (less or equal), si no mayor (not greater), si ZF=0 y SF<>OF. ±JNC Salto si no acarreo, si CF=0.JNE/JNZ Salto si no igual, si no cero, si ZF=0.JNO Salto si no desbordamiento, si OF=0.JNP/JPO Salto si no paridad, si paridad impar, si PF=0.JNS Salto si no signo, si positivo, si SF=0.JO Salto si desbordamiento, si OF=1.JP/JPE Salto si paridad, si paridad par, si PF=1.JS Salto si signo, si SF=1.

Gestión de bucleLOOP (bucle)

Sintaxis: LOOP desplazamiento

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Page 48: PCA, PS2 ,IBM y AT

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

Decrementa el registro contador CX; si CX es cero, ejecuta la siguiente instrucción,en caso contrario transfiere el control a la dirección resultante de sumar a IP +desplazamiento. El desplazamiento debe estar comprendido entre -128 y +127. Ejemplo:

mov cx,10bucle: .......

.......loop bucle

Con las mismas características que la instrucción anterior:

LOOPE/LOOPZ Bucle si igual, si cero. Z=1 y CX<>0LOOPNE/LOOPNZ Bucle si no igual, si no cero. Z=0 y CX<>0

Interrupciones

INT (interrupción)

Sintaxis: INT n (0 <= n <= 255)

Indicadores: OF DF IF TF SF ZF AF PF CF- - 0 0 - - - - -

Inicializa un procedimiento de interrupción de un tipo indicado en la instrucción. Enla pila se introduce al llamar a una interrupción la dirección de retorno formada por losregistros CS e IP y el estado de los indicadores. INT 3 es un caso especial de INT, alensamblarla el ensamblador genera un sólo byte en vez de los dos habituales; estainterrupción se utiliza para poner puntos de ruptura en los programas. Véase también IRETy el apartado 1 del capítulo VII.

Ejemplo: int 21h

INTO (interrupción por desbordamiento)

Sintaxis: INTO

Indicadores: OF DF IF TF SF ZF AF PF CF- - 0 0 - - - - -

Genera una interrupción de tipo 4 (INT 4) si existe desbordamiento (OF=1). De locontrario se continúa con la instrucción siguiente.

IRET (retorno de interrupción)

Sintaxis: IRET

Indicadores: OF DF IF TF SF ZF AF PF CFx x x x x x x x x

Devuelve el control a la dirección de retorno salvada en la pila por una interrupciónprevia y restaura los indicadores que también se introdujeron en la pila. En total, se sacanlas 3 palabras que fueron colocadas en la pila cuando se produjo la interrupción. Véasetambién INT.

Page 49: PCA, PS2 ,IBM y AT

49JUEGO DE INSTRUCCIONES 80x86

4.1.5. - INSTRUCCIONES DE ENTRADA SALIDA (E/S).

IN (entrada)

Sintaxis: IN acumulador, puerto.

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Transfiere datos desde el puerto indicado hasta el registro AL o AX, dependiendode la longitud byte o palabra respectivamente. El puerto puede especificarse mediante unaconstante (0 a 255) o a través del valor contenido en DX (0 a 65535).

Ejemplo: in ax,0fhin al,dx

OUT (salida)

Sintaxis: OUT puerto, 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 puertopuede especificarse con un valor fijo entre 0 y 255 ó a través del valor contenido en elregistro DX (de 0 a 65535).

Ejemplo: out 12h,axout dx,al

4.1.6. - INSTRUCCIONES ARITMÉTICAS.

* * * S U M A * * *

AAA (ajuste ASCII para la suma)

Sintaxis: AAA

Indicadores: OF DF IF TF SF ZF AF PF CF? - - - ? ? x ? x

Convierte el contenido del registro AL en un número BCD no empaquetado. Si loscuatro bits menos significativos de AL son mayores que 9 ó si el indicador AF está a 1, sesuma 6 a AL, 1 a AH, AF se pone a 1, CF se iguala a AF y AL pone sus cuatro bits mássignificativos a 0.

Ejemplo: add al,blaaa

En el ejemplo, tras la suma de dos números BCD no empaquetados colocados en ALy BL, el resultado (por medio de AAA) sigue siendo un número BCD no empaquetado.

Page 50: PCA, PS2 ,IBM y AT

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

ADC (suma con acarreo)

Sintaxis: ADC destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Suma los operandos origen, destino y el valor del indicador de acarreo (0 ó 1) y elresultado lo almacena en el operando destino. Se utiliza normalmente para sumar númerosgrandes, de más de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo)de la suma anterior.

Ejemplo: adc ax,bx

ADD (suma)

Sintaxis: ADD destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Suma los operandos origen y destino almacenando el resultado en el operandodestino. Se activa el acarreo si se desborda el registro destino durante la suma.

Ejemplos: add ax,bxadd cl,dh

DAA (ajuste decimal para la suma)

Sintaxis: DAA

Indicadores: OF DF IF TF SF ZF AF PF CF? - - - x x x x x

Convierte el contenido del registro AL en un par de valores BCD: si los cuatro bitsmenos significativos de AL son un número mayor que 9, el indicador AF se pone a 1 y sesuma 6 a AL. De igual forma, si los cuatro bits más significativos de AL tras la operaciónanterior son un número mayor que 9, el indicador CF se pone a 1 y se suma 60h a AL.

Ejemplo: add al,cldaa

En el ejemplo anterior, si AL y CL contenían dos números BCD empaquetados, DAAhace que el resultado de la suma (en AL) siga siendo también un BCD empaquetado.

INC (incrementar)

Sintaxis: INC destino

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x -

Incrementa el operando destino. El operando destino puede ser byte o palabra.

Page 51: PCA, PS2 ,IBM y AT

51JUEGO DE INSTRUCCIONES 80x86

Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectarun desbordamiento por este procedimiento (utilícese ZF).

Ejemplos: inc alinc es:[di]inc ss:[bp+4]inc word ptr cs:[bx+di+7]

* * * R E S T A * * *

AAS (ajuste ASCII para la resta)

Sintaxis: AAS

Indicadores: OF DF IF TF SF ZF AF PF CF? - - - ? ? x ? x

Convierte el resultado de la sustracción de dos operandos BCD no empaquetados paraque siga siendo un número BCD no empaquetado. Si el nibble inferior de AL tiene un valormayor que 9, de AL se resta 6, se decrementa AH, AF se pone a 1 y CF se iguala a AF. Elresultado se guarda en AL con los bits de 4 a 7 puestos a 0.

Ejemplo: sub al,blaas

En el ejemplo, tras la resta de dos números BCD no empaquetados colocados en ALy BL, el resultado (por medio de AAS) sigue siendo un número BCD no empaquetado.

CMP (comparación)

Sintaxis: CMP destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Resta origen de destino sin retornar ningún resultado. Los operandos quedaninalterados, paro los indicadores pueden ser consultados mediante instrucciones debifurcación condicional. Los operandos pueden ser de tipo byte o palabra pero ambos de lamisma dimensión.

Ejemplo: cmp bx, mem_palcmp ch,cl

DAS (ajuste decimal para la resta)

Sintaxis: DAS

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - x x x x x

Corrige el resultado en AL de la resta de dos números BCD empaquetados,convirtiéndolo también en un valor BCD empaquetado. Si el nibble inferior tiene un valormayor que 9 o AF es 1, a AL se le resta 6, AF se pone a 1. Si el nibble mas significativoes mayor que 9 ó CF está a 1, entonces se resta 60h a AL y se activa después CF.

Page 52: PCA, PS2 ,IBM y AT

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

Ejemplo: sub al,bldas

En el ejemplo anterior, si AL y BL contenían dos números BCD empaquetados, DAShace que el resultado de la resta (en AL) siga siendo también un BCD empaquetado.

DEC (decrementar)

Sintaxis: DEC destino

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x -

Resta una unidad del operando destino. El operando puede ser byte o palabra.Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectarun desbordamiento por este procedimiento (utilícese ZF).

Ejemplo: dec axdec mem_byte

NEG (negación)

Sintaxis: NEG destino

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Calcula el valor negativo en complemento a dos del operando y devuelve el resultadoen el mismo operando.

Ejemplo: neg al

SBB (resta con acarreo)

Sintaxis: SBB destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Resta el operando origen del operando destino y el resultado lo almacena en eloperando destino. Si está a 1 el indicador de acarreo además resta una unidad más. Losoperandos pueden ser de tipo byte o palabra. Se utiliza normalmente para restar númerosgrandes, de más de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo)de la resta anterior.

Ejemplo: sbb ax,axsbb ch,dh

SUB (resta)

Sintaxis: SUB destino, origen

Page 53: PCA, PS2 ,IBM y AT

53JUEGO DE INSTRUCCIONES 80x86

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Resta el operando destino al operando origen, colocando el resultado en el operandodestino. Los operandos pueden tener o no signo, siendo necesario que sean del mismo tipo,byte o palabra.

Ejemplos: sub al,blsub dx,dx

* * * M U L T I P L I C A C I O N * * *

AAM (ajuste ASCII para la multiplicación)

Sintaxis: AAM

Indicadores: OF DF IF TF SF ZF AF PF CF? - - - x x ? x ?

Corrige el resultado en AX del producto de dos números BCD no empaquetados,convirtiéndolo en un valor BCD también no empaquetado. En AH sitúa el cociente de AL/10quedando en AL el resto de dicha operación.

Ejemplo: mul blaam

En el ejemplo, tras el producto de dos números BCD no empaquetados colocados enAL y BL, el resultado (por medio de AAA) sigue siendo, en AX, un número BCD noempaquetado.

IMUL (multiplicación entera con signo)

Sintaxis: IMUL origen (origen no puede ser operando inmediato en 8086, sí en 286)

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - ? ? ? ? x

Multiplica un operando origen con signo de longitud byte o palabra por AL o AXrespectivamente. Si «origen» es un byte el resultado se guarda en AH (byte más significativo)y en AL (menos significativo), si «origen» es una palabra el resultado es devuelto en DX(parte alta) y AX (parte baja). Si las mitades más significativas son distintas de cero,independientemente del signo, CF y OF son activados.

Ejemplo: imul bximul ch

MUL (multiplicación sin signo)

Sintaxis: MUL origen (origen no puede ser operando inmediato)

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - ? ? ? ? x

Multiplica el contenido sin signo del acumulador por el operando origen. Si el

Page 54: PCA, PS2 ,IBM y AT

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

operando destino es un byte el acumulador es AL guardando el resultado en AH y AL, si elcontenido de AH es distinto de 0 activa los indicadores CF y OF. Cuando el operando origenes de longitud palabra el acumulador es AX quedando el resultado sobre DX y AX, si elvalor de DX es distinto de cero los indicadores CF y OF se activan.

Ejemplo: mul byte ptr ds:[di]mul dxmul cl

* * * D I V I S I O N * * *

AAD (ajuste ASCII para la división)

Sintaxis: AAD

Indicadores: OF DF IF TF SF ZF AF PF CF? - - - x x ? x ?

Convierte dos números BCD no empaquetados contenidos en AH y AL en undividendo de un byte que queda almacenado en AL. Tras la operación AH queda a cero. Estainstrucción es necesaria ANTES de la operación de dividir, al contrario que AAM.

Ejemplo: aaddiv bl

En el ejemplo, tras convertir los dos números BCD no empaquetados (en AX) en undividendo válido, la instrucción de dividir genera un resultado correcto.

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, sin considerar el signo, un número contenido en el acumulador y su extensión(AH, AL si el operando es de tipo byte o DX, AX si el operando es palabra) entre eloperando fuente. El cociente se guarda en AL o AX y el resto en AH o DX según eloperando sea byte o palabra respectivamente. DX o AH deben ser cero antes de la operación.Cuando el cociente es mayor que el resultado máximo que puede almacenar, cociente y restoquedan indefinidos produciéndose una interrupción 0. En caso de que las partes mássignificativas del cociente tengan un valor distinto de cero se activan los indicadores CF yOF.

Ejemplo: div bldiv 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? - - - ? ? ? ? ?

Page 55: PCA, PS2 ,IBM y AT

55JUEGO DE INSTRUCCIONES 80x86

Divide, considerando el signo, un número contenido en el acumulador y su extensiónentre el operando fuente. El cociente se almacena en AL o AX según el operando sea byteo palabra y de igual manera el resto en AH o DX. DX o AH deben ser cero antes de laoperación. Cuando el cociente es positivo y superior al valor máximo que puede almacenarse(7fh ó 7fffh), o cuando el cociente es negativo e inferior al valor mínimo que puedealmacenarse (81h u 8001h) entonces cociente y resto quedan indefinidos, generándose unainterrupción 0, lo que también sucede si el divisor es 0.

Ejemplo: idiv blidiv bx

* * * C O N V E R S I O N E S * * *

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, es decir, expandeel signo de AL a AX como paso previo a una operación de 16 bits.

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, copiando el bit mássignificativo de AH en todo DX.

4.1.7. - INSTRUCCIONES DE MANIPULACIÓN DE CADENAS.

CMPS/CMPSB/CMPSW (compara cadenas)

Sintaxis: CMPS cadena_destino, cadena_origenCMPSB (bytes)CMPSW (palabras)

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Compara dos cadenas restando al origen el destino. Ninguno de los operandos sealteran, pero los indicadores resultan afectados. La cadena origen se direcciona con registroSI sobre el segmento de datos DS y la cadena destino se direcciona con el registro DI sobreel segmento extra ES. Los registros DI y SI se autoincrementan o autodecrementan según elvalor del indicador DF (véanse CLD y STD) en una o dos unidades, dependiendo de si setrabaja con bytes o con palabras. «Cadena origen» y «cadena destino» son dos operandosredundantes que sólo indican el tipo del dato (byte o palabra) a comparar, es más cómodocolocar CMPSB o CMPSW para indicar bytes/palabras. Si se indica un registro de segmento,éste sustituirá en la cadena origen al DS ordinario. Ejemplo:

Page 56: PCA, PS2 ,IBM y AT

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

lea si,origenlea di,destinocmpsb

LODS/LODSB/LODSW (cargar cadena)

Sintaxis: LODS cadena_origenLODSB (bytes)LODSW (palabras)

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Copia en AL o AX una cadena de longitud byte o palabra direccionada sobre elsegmento de datos (DS) con el registro SI. Tras la transferencia, SI se incrementa odecrementa según el indicador DF (véanse CLD y STD) en una o dos unidades, según seestén manejando bytes o palabras. «Cadena_origen» es un operando redundante que sóloindica el tipo del dato (byte o palabra) a cargar, es más cómodo colocar LODSB o LODSWpara indicar bytes/palabras.

Ejemplo: cldlea si,origenlodsb

MOVS/MOVSB/MOVSW (mover cadena)

Sintaxis: MOVS cadena_destino, cadena_origenMOVSB (bytes)MOVSW (palabras)

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Transfiere un byte o una palabra de la cadena origen direccionada por DS:SI a lacadena destino direccionada por ES:DI, incrementando o decrementando a continuación losregistros SI y DI según el valor de DF (véanse CLD y STD) en una o dos unidades,dependiendo de si se trabaja con bytes o con palabras. «Cadena origen» y «cadena destino»son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar,es más cómodo colocar MOVSB o MOVSW para indicar bytes/palabras. Si se indica unregistro de segmento, éste sustituirá en la cadena origen al DS ordinario.

Ejemplo: lea si,origenlea di,destinomovsw

SCAS/SCASB/SCASW (explorar cadena)

Sintaxis: SCAS cadena_destinoSCASB (bytes)SCASW (palabras)

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x x x x

Page 57: PCA, PS2 ,IBM y AT

57JUEGO DE INSTRUCCIONES 80x86

Resta de AX o AL una cadena destino direccionada por el registro DI sobre elsegmento extra. Ninguno de los valores es alterado pero los indicadores se ven afectados. DIse incrementa o decrementa según el valor de DF (véanse CLD y STD) en una o dosunidades -según se esté trabajando con bytes o palabras- para apuntar al siguiente elementode la cadena. «Cadena_destino» es un operando redundante que sólo indica el tipo del dato(byte o palabra), es más cómodo colocar SCASB o SCASW para indicar bytes/palabras.

Ejemplo: lea di,destinomov al,50scasb

STOS/STOSB/STOSW (almacena cadena)

Sintaxis: STOS cadena_destinoSTOSB (bytes)STOSW (palabras)

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Transfiere el operando origen almacenado en AX o AL, al destino direccionado porel registro DI sobre el segmento extra. Tras la operación, DI se incrementa o decrementasegún el indicador DF (véanse CLD y STD) para apuntar al siguiente elemento de la cadena.«Cadena_destino» es un operando redundante que sólo indica el tipo del dato (byte o palabra)a cargar, es más cómodo colocar STOSB o STOSW para indicar bytes/palabras.

Ejemplo: lea di,destinomov ax,1991stosw

REP/REPE/REPZ/REPNE/REPNZ (repetir)

REP repetir operación de cadenaREPE/REPZ repetir operación de cadena si igual/si ceroREPNE/REPNZ repetir operación de cadena si no igual (si no 0)

Estas instrucciones se pueden colocar como prefijo de otra instrucción de manejo decadenas, con objeto de que la misma se repita un número determinado de vecesincondicionalmente o hasta que se verifique alguna condición. El número de veces se indicaen CX. Por sentido común sólo deben utilizarse las siguientes combinaciones:

Prefijo Función Instrucciones----------- ------------------------------- ----------------REP Repetir CX veces MOVS, STOSREPE/REPZ Repetir CX veces mientras ZF=1 CMPS, SCASREPNE/REPNZ Repetir CX veces mientras ZF=0 CMPS, SCAS

Ejemplos:

1) Buscar el byte 69 entre las 200 primeras posiciones de «tabla» (se supone «tabla»en el segmento ES):

LEA DI,tablaMOV CX,200MOV AL,69CLDREPNE SCASBJE encontrado

Page 58: PCA, PS2 ,IBM y AT

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

2) Rellenar de ceros 5000 bytes de una tabla colocada en «datos» (se supone «datos»en el segmento ES):

LEA DI,datosMOV AX,0MOV CX,2500CLDREP 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 CX,0B800h ; segmento de pantallaMOV DS,CX ; en DSLEA DI,buffer ; destino en ES:DIMOV SI,0 ; copiar desde DS:0MOV CX,2000 ; 2000 palabrasCLD ; hacia adelanteREP MOVSW ; copiar CX palabras

4.1.8. - INSTRUCCIONES DE OPERACIONES LÓGICAS A NIVEL DE BIT.

AND (y lógico)

Sintaxis: AND destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF0 - - - x x ? x 0

Realiza una operación de Y lógico entre el operando origen y destino quedando elresultado en el destino. Son válidos operandos byte o palabra, pero ambos del mismo tipo.

Ejemplos: and ax,bxand bl,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, invirtiendo cada uno de sus bits.Los indicadores no resultan afectados.

Ejemplo: not ax

OR (O lógico)

Sintaxis: OR destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF0 - - - x x ? x 0

Realiza una operación O lógico a nivel de bits entre los dos operandos,almacenándose después el resultado en el operando destino.

Ejemplo: or ax,bx

Page 59: PCA, PS2 ,IBM y AT

59JUEGO DE INSTRUCCIONES 80x86

TEST (comparación lógica)

Sintaxis: TEST destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF0 - - - x x ? x 0

Realiza una operación Y lógica entre los dos operandos pero sin almacenar elresultado. Los indicadores son afectados con la operación.

Ejemplo: test al,bh

XOR (O exclusivo)

Sintaxis: XOR destino, origen

Indicadores: OF DF IF TF SF ZF AF PF CF0 - - - x x ? x 0

Operación OR exclusivo a nivel de bits entre los operandos origen y destinoalmacenándose el resultado en este último.

Ejemplo: xor di,ax

4.1.9. - INSTRUCCIONES DE CONTROL DEL PROCESADOR.

NOP (operación nula)

Sintaxis: NOP

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Realiza una operación nula, es decir, el microprocesador decodifica la instrucción ypasa a la siguiente. Realmente se trata de la instrucción XCHG AX,AX.

ESC (salida a un coprocesador)

Sintaxis: ESC código_operación, origen

Indicadores: OF DF IF TF SF ZF AF PF CF- - - - - - - - -

Se utiliza en combinación con procesadores externos, tales como los coprocesadoresde coma flotante o de E/S, y abre al dispositivo externo el acceso a las direcciones yoperandos requeridos. Al mnemónico ESC le siguen los códigos de operación apropiadospara el coprocesador así como la instrucción y la dirección del operando necesario.

Ejemplo: esc 21,ax

HLT (parada hasta interrupción o reset)

Sintaxis: HLT

Page 60: PCA, PS2 ,IBM y AT

60 EL UNIVERSO DIGITAL DEL IBM PC, 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.Como en los PC se producen normalmente 18,2 interrupciones de tipo 8 por segundo (deltemporizador) algunos programadores utilizan HLT para hacer pausas y bucles de retardo.Sin embargo, el método no es preciso y puede fallar con ciertos controladores de memoria.

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 paraasegurar que no accede simultáneamente a la memoria más de un procesador. Cuando unainstrucción va precedida por LOCK, el procesador bloquea inmediatamente el bus,introduciendo una señal por la patilla LOCK.

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.Ocurre, por ejemplo, cuando el copro ha terminado una operación e indica su finalización.Suele preceder a ESC para sincronizar las acciones del procesador y coprocesador.

4.1.10. - INSTRUCCIONES DE ROTACIÓN Y DESPLAZAMIENTO.

RCL (rotación a la izquierda con acarreo)

Sintaxis: RCL destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - - - - - x

Rotar a la izquierda los bits del operando destino junto con el indicador de acarreoCF el número de bits especificado en el segundo operando. Si el número de bits a desplazares 1, se puede especificar directamente, en caso contrario el valor debe cargarse en CL yespecificar CL como segundo operando. No es conveniente que CL sea mayor de 7, en bytes;ó 15, en palabras.

CF alto bajo RCLRCL

Ejemplos: rcl ax,1rcl al,clrcl di,1

Page 61: PCA, PS2 ,IBM y AT

61JUEGO DE INSTRUCCIONES 80x86

RCR (rotación a la derecha con acarreo)

Sintaxis: RCR destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - - - - - x

Rotar a la derecha los bits del operando destino junto con el indicador de acarreo CFel número de bits especificado en el segundo operando. Si el número de bits es 1 se puedeespecificar directamente; en caso contrario su valor debe cargarse en CL y especificar CLcomo segundo operando:

alto bajo CF RCRRCR

Ejemplos: rcr bx,clrcr bx,1

ROL (rotación a la izquierda)

Sintaxis: ROL destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - - - - - x

Rota a la izquierda los bits del operando destino el número de bits especificado enel segundo operando, que puede ser 1 ó CL previamente cargado con el valor del número deveces.

CF alto bajo ROLROL

Ejemplos: rol dx,clrol ah,1

ROR (rotación a la derecha)

Sintaxis: ROR destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - - - - - x

Rota a la derecha los bits del operando destino el número de bits especificado en elsegundo operando. Si el número de bits es 1 se puede poner directamente, en caso contrariodebe ponerse a través de CL.

alto bajo CF RORROR

Ejemplos: ror cl,1ror ax,cl

Page 62: PCA, PS2 ,IBM y AT

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

SAL/SHL (desplazamiento aritmético a la izquierda)

Sintaxis: SAL/SHL destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x ? x x

Desplaza a la izquierda los bits del operando el número de bits especificado en elsegundo operando que debe ser CL si es mayor que 1 los bits desplazados.

CF alto bajo 0 SAL/SHLSAL/SHL

Ejemplos: shl dx,1sal bx,cl

SAR (desplazamiento aritmético a la derecha)

Sintaxis: SAR destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x ? x x

Desplaza a la derecha los bits del operando destino el número de bits especificadoen el segundo operando. Los bits de la izquierda se rellenan con el bit de signo del primeroperando. Si el número de bits a desplazar es 1 se puede especificar directamente, si esmayor se especifica a través de CL.

alto bajo CF SARSAR

Ejemplos: sar ax,clsar bp,1

SHR (desplazamiento lógico a la derecha)

Sintaxis: SHR destino, contador

Indicadores: OF DF IF TF SF ZF AF PF CFx - - - x x ? x x

Desplaza a la derecha los bits del operando destino el número de los bitsespecificados en el segundo operando. Los bits de la izquierda se llena con cero. Si elnúmero de bits a desplazar es 1 se puede especificar directamente en el caso en que noocurra se pone el valor en CL:

0 alto bajo CF SHRSHR

Ejemplos: shr ax,clshr cl,1

Page 63: PCA, PS2 ,IBM y AT

63JUEGO DE INSTRUCCIONES 80x86

4.2. - RESUMEN ALFABÉTICO DE LAS INSTRUCCIONES Y BANDERINES. ÍNDICE.

Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguientenotación:

- bit no modificado? desconocido o indefinidox modificado según el resultado de la operación1 puesto siempre a 10 puesto siempre a 0

Instrucción Sintaxis Efecto sobre los flags pág.

OF DF IF TF SF ZF AF PF CFAAA AAA ? - - - ? ? x ? x 49AAD AAD ? - - - x x ? x ? 54AAM AAM ? - - - x x ? x ? 53AAS AAS ? - - - ? ? x ? x 51ADC dst,fnt ADC dst,fnt x - - - x x x x x 50ADD dst,fnt ADD dst,fnt x - - - x x x x x 50AND dst,fnt AND dst,fnt 0 - - - x x ? x 0 58CALL dsp CALL dsp - - - - - - - - - 46CBW CBW - - - - - - - - - 55CLC CLC - - - - - - - - 0 43CLD CLD - 0 - - - - - - - 43CLI CLI - - 0 - - - - - - 44CMC CMC - - - - - - - - x 44CMP dst,fnt CMP dst,fnt x - - - x x x x x 51CMPS/CMPSBCMPSW cdst,cfnt CMPS cdst,cfnt x - - - x x x x x 55CWD CWD - - - - - - - - - 55DAA DAA ? - - - x x x x x 50DAS DAS - - - - x x x x x 51DEC dst DEC dst x - - - x x x x - 52DIV fnt DIV dst ? - - - ? ? ? ? ? 54ESC opcode,fnt ESC opcode,fnt - - - - - - - - - 59HLT HLT - - - - - - - - - 59IDIV fnt IDIV fnt ? - - - ? ? ? ? ? 54IMUL fnt IMUL fnt x - - - ? ? ? ? x 53IN acum,port IN acum,port - - - - - - - - - 49INC dst INC dst x - - - x x x x - 50INT interrup INT interrup - - 0 0 - - - - - 48INTO INTO - - 0 0 - - - - - 48IRET IRET x x x x x x x x x 48Jcc (JA, JBE...) Jcc dsp - - - - - - - - - 47JMP JMP dsp - - - - - - - - - 46JCXZ dsp JCXZ dsp - - - - - - - - - 47LAHF LAHF - - - - - - - - - 43LDS dst,fnt LDS dst,fnt - - - - - - - - - 42LEA dst,fnt LEA dst,fnt - - - - - - - - - 42LES dst,fnt LES dst,fnt - - - - - - - - - 43LOCK LOCK - - - - - - - - - 60LODS/LODSB/LODSW cfnt LODS mem - - - - - - - - - 56LOOP LOOP dsp - - - - - - - - - 47LOOPcc (LOOPE...) LOOPcc dsp - - - - - - - - - 48MOV dst,fnt MOV dst,fnt - - - - - - - - - 41MOVS/MOVSB/MOVSW cdst,cfnt MOVS cdst,cfnt - - - - - - - - - 56MUL fnt MUL fnt x - - - ? ? ? ? x 53NEG dst NEG fnt x - - - x x x x x 52NOP NOP - - - - - - - - - 59NOT dst NOT dst - - - - - - - - - 58OR dst,fnt OR dst,fnt 0 - - - x x ? x 0 58OUT port,acum OUT port,acum - - - - - - - - - 49POP dst POP dst - - - - - - - - - 45POPF POPF x x x x x x x x x 45PUSH dst PUSH dst - - - - - - - - - 45PUSHF PUSHF - - - - - - - - - 45

Page 64: PCA, PS2 ,IBM y AT

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

Instrucción Sintaxis Efecto sobre los flags pág.

OF DF IF TF SF ZF AF PF CFRCL dst,cnt RCL dst,cnt x - - - - - - - x 60RCR dst,cnt RCR dst,cnt x - - - - - - - x 61REP/REPE/REPZ/REPNE/REPNZ REP - - - - - - - - - 57RET [val] RET [val] - - - - - - - - - 47RETF [val] RETF [val] - - - - - - - - - 47ROL dst,cnt ROL dst,cnt x - - - - - - - x 61ROR dst,cnt ROR dst,cnt x - - - - - - - x 61SAHF SAHF - - - - x x x x x 43SAL/SHL dst,cnt SAL dst,cnt x - - - x x ? x x 62SAR dst,cnt SAR dst,cnt x - - - x x ? x x 62SBB dst,fnt SBB dst,fnt x - - - x x x x x 52SCAS/SCASB/SCASW cdst SCAS cdst x - - - x x x x x 56SHR dst,cnt SHR dst,cnt x - - - x x ? x x 62STC STC - - - - - - - - 1 44STD STD - 1 - - - - - - - 44STI STI - - 1 - - - - - - 45STOS/STOSB/STOSW cdst STOS cdst - - - - - - - - - 57SUB dst,fnt SUB dst,fnt x - - - x x x x x 52TEST dst,fnt TEST dst,fnt 0 - - - x x ? x 0 59WAIT WAIT - - - - - - - - - 60XCHG dst,fnt XCHG dst,fnt - - - - - - - - - 41XLAT tfnt XLAT tfnt - - - - - - - - - 42XOR dst,fnt XOR dst,fnt 0 - - - x x ? x 0 59

4.3. - INSTRUCCIONES ESPECIFICAS DEL 286, 386 y 486 EN MODO REAL.

4.3.1. - DIFERENCIAS EN EL COMPORTAMIENTO GLOBAL RESPECTO AL 8086.

- Excepciones de división:Las excepciones INT 0, debidas a una división por cero o a un cociente excesivamente

grande, provocan que en la pila se almacene el valor de CS:IP para la siguiente instrucción en el8086. En el 286 y superiores se almacena el CS:IP de la propia instrucción que causa la excepción.

- Códigos de operación indefinidos.En el 286 y superiores se produce una excepción 6 (INT 6) o, si es una instrucción con

sentido para estos procesadores, se ejecuta. El 8086 se estrella.

- Valor de PUSH SP.El valor que introduce en la pila en el 286 y superiores es el de SP antes del PUSH; en el

8086 es el de SP después del PUSH (dos unidades menos).

- Desplazamientos y rotaciones.El valor de desplazamiento en las operaciones de manipulación de bits del 8086 es una

constante de 8 bits (indicada en CL); en el 286 y superiores se toma módulo 32 (sólo se consideranlos 5 bits menos significativos).

- Prefijos redundantes.Las instrucciones tienen una longitud ilimitada en el 8086; en el 286 y superiores no pueden

exceder de 15 bytes. Por tanto, los prefijos redundantes pueden producir excepciones de código deoperación no válido.

- Accesos al límite del segmento.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). En el 286 y superiores, se

Page 65: PCA, PS2 ,IBM y AT

65JUEGO DE INSTRUCCIONES 80x86

produce una excepción de violación de límites. En el 386 y superiores se produce también en accesosde 32 bits en las posiciones 0FFFDh a la 0FFFFh. Esto se cumple tanto para accesos a datos enmemoria como a instrucciones del programa en esos puntos críticos.

- LOCK.Esta instrucción no está limitada de ninguna manera en el 8086 y en el 286. En el 386 y

superiores su uso está restringido a determinadas instrucciones.

- Ejecución paso a paso.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; por tanto, las interrupciones externas no pueden ser traceadas.

- Registro de FLAGS.Difiere algo en los bits 12 al 15 en todos los procesadores; el 386 dispone además de un

registro de flags de 32 bits.

- Interrupción NMI.Desde el 286 y superiores, una NMI no puede interrumpir una rutina de tratamiento NMI.

- Error del coprocesador.En el 286 y superiores se utiliza el vector 16; en el 8086 cualquier vector.

- Prefijos de las instrucciones del coprocesador.Al producirse una excepción de error de coprocesador, en el 8086 se almacena un CS:IP que

no incluye prefijos -si los había-, al contrario que en el 286 y superiores.

- Límite del primer megabyte.En el 8086 la memoria es circular; al final del primer megabyte se vuelve a comenzar por

las posiciones más bajas de la memoria. En el 286 y superiores, se accede a la memoria extendida(un artificio hardware en los PC lo impide al forzar A20 a estado bajo, pero puede ser solventado).

- Instrucciones de cadena repetitivas.El CS:IP grabado en el 8086 no incluye el prefijo, si existe; en el 286 y superiores sí.

4.3.2. - INSTRUCCIONES ESPECIFICAS DEL 286.

A continuación se describen las instrucciones adicionales que incorporan los 286 en modo real, quetambién pueden ser consideradas cuando trabajamos con los microprocesadores compatibles V20 y V30, asícomo con los procesadores superiores al 286. Las instrucciones del modo protegido se dirigen especialmentea la multiprogramación y el tiempo compartido, siendo específicas de la conmutación de procesos ytratamiento de la memoria virtual y no pueden emplearse directamente bajo DOS.

BOUND r16, mem16: Comprueba si el registro de 16 bits indicado como primer operando estádentro de los límites de una matriz. Los límites de la matriz los definen dos palabras consecutivasen la memoria apuntadas por mem16. Si está fuera de los límites, se produce una interrupción 5 enla que el IP apilado queda apuntando a la instrucción BOUND (¡no se incrementa!).

ENTER crea una estructura de pila para un procedimiento de alto nivel.

Las instrucciones PUSH permiten meter valores inmediatos a la pila: es válido hacer PUSH 40h.

IMUL puede multiplicar cualquier registro de 16 bits por una constante inmediata, devolviendo unresultado palabra (CF=1 si no cabe en 16 bits); por ejemplo, es válido IMUL CX,25. También seadmiten tres operandos: IMUL r1, r2, imm. En este caso, se multiplica r2 por el valor inmediato

Page 66: PCA, PS2 ,IBM y AT

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

(8/16 bits) y el resultado se almacena en r1. Tanto r1 como r2 han de ser de 16 bits.

LEAVE abandona los procedimientos de alto nivel (equivale a MOV SP,BP / POP BP).

PUSHA/POPA: Introduce en la pila y en este orden los registros AX, CX, DX, BX, SP, BP, SIy DI -o los saca en orden inverso-. Ideal en el manejo de interrupciones y muy usada en las BIOSde 286 y 386.

OUTS (salida de cadenas) e INS (entrada de cadenas) repetitivas (equivalente a MOVS y LODS).

RCR/RCL, ROR/ROL, SAL/SAR y SHL/SHR admiten una constante de rotación distinta de 1.

4.3.3. - INSTRUCCIONES PROPIAS DEL 386 Y 486.

Además de todas las posibilidades adicionales del 286, el 386 y el 486 permiten utilizar cualquierregistro de 32 bits de propósito general en todos los modos de funcionamiento, incluido el modo real,tales como EAX, EBX, ECX, EDX, ESI, EDI, EBP. Sin embargo no deben intentarsedireccionamientos por encima de los 64K. En otras palabras, se pueden utilizar para acelerar lasoperaciones pero no para acceder a más memoria. Por ejemplo, si EBX > 0FFFFh, la instrucciónMOV AX,[EBX] tendría un resultado impredecible. Además, estos procesadores cuentan con dossegmentos más: además de DS, ES, CS y SS se pueden emplear también FS y GS. Aviso: parece serque en algunos 386 fallan ocasionalmente las instrucciones de multiplicar de 32 bits.

Nota: No es del todo cierto que el 386 y el 486 no permitan acceder a más de 64 Kb enmodo real: en la sección 4.3.6 hay un ejemplo de ello.

Los modos de direccionamiento aumentan notablemente su flexibilidad en el 386 y superiores. Conlos registros de 16 bits sólo están disponibles los modos tradicionales. En cambio, con los de 32 sepuede utilizar en el direccionamiento indirecto cualquier registro: es válida, por ejemplo, unainstrucción del tipo MOV AX,[ECX] o MOV EDX,[EAX]. Los desplazamientos en eldireccionamiento indexado con registros de 32 bits pueden ser de 8 y también de 32 bits. Cuando dosregistros deben sumarse para calcular la dirección efectiva, el segundo puede estar multiplicado por2, 4 u 8; por ejemplo, es válida la instrucción MOV AL,[EDX+EAX*8]. Por supuesto, bajo DOS hayque asegurarse siempre que el resultado de todas las operaciones que determinan la dirección efectivano excede de 0FFFFh (0FFFEh si se accede a palabras y 0FFFCh en accesos a dobles palabras enmemoria).

BOUND r32, mem32: Se admiten ahora operandos de 32 bits.

BSF/BSR: Exploración de bits hacia adelante y atrás, respectivamente. La sintaxis es:

BSF reg, reg ó BSF reg, [memoria]BSR reg, reg ó BSR reg, [memoria]

Donde reg puede ser de 16 ó 32 bits. Se comienza a explorar por el bit 0 (BSF) o por el mássignificativo (BSR) del segundo operando: si no aparece ningún bit activo (a 1) el indicador ZF seactiva; en caso contrario se almacena en el primer operando la posición relativa de ese bit:

MOV AX,8BSF BX,AXJZ ax_es_0 ; no se saltará, además BX = 3

BT/BTC/BTR/BTS: Operaciones sobre bits: comprobación, comprobación y complementación,comprobación y puesta a 0, comprobación y puesta a 1. Sintaxis (ejemplo sobre BT):

Page 67: PCA, PS2 ,IBM y AT

67JUEGO DE INSTRUCCIONES 80x86

BT reg, reg ó BT reg, imm8

Donde reg puede ser de 16 ó 32 bits, el operando inmediato es necesariamente de 8. Estasinstrucciones copian el número de bit del primer operando que indique el segundo operando (entre0 y 31) en el acarreo. A continuación no le hacen nada a ese bit (BT), lo complementan (BTC), loborran (BTR) o lo activan (BTS). Ejemplo:

MOV AX,16BTC AX,4 ; resultado: CF = 1 y AX = 0

CDQ: Similar a CWD, extiende el signo de EAX a EDX:EAX.

CMPSD: Similar a CMPSW pero empleando ESI, EDI, ECX y comparando datos de 32 bits. Sepuede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de0FFFFh.

CWDE: Extiende el signo de AX a EAX.

IMUL: Ahora se admite un direccionamiento a memoria en el 2º operando: IMUL CX,[dato]

INSD: Similar a INSW pero empleando ESI, EDI, ECX y leyendo datos de 32 bits. Se puedeemplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh.

Jcc: Los saltos condicionales ahora pueden ser de ¡32 bits!. Mucho cuidado con la directiva .386en los programas en que se desee mantener la compatibilidad con procesadores anteriores. JECXZse utiliza en vez de JCXZ (mismo código de operación).

LODSD: Similar a LODSW pero empleando ESI, EDI y ECX y cargando datos de 32 bits enEAX. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) noexcedan de 0FFFFh.

LSS, LFS, LGS: similar a LDS o LES pero con esos registros de segmento.

MOV CRx,reg / MOV DRx,reg y los recíprocos: acceso a registros de control y depuración.

MOVSD: Similar a MOVSW pero empleando ESI, EDI, ECX y moviendo datos de 32 bits. Sepuede emplear bajo DOS para acelerar las transferencias siempre que ESI y EDI (utilizando REPtambién ECX) no excedan de 0FFFFh. Operando sobre la memoria de vídeo sólo se obtiene ventajasi la tarjeta es realmente de 32 bits.

MOVSX / MOVZX: carga con extensión de signo o cero. Toma el segundo operando, le extiendeadecuadamente el signo (o le pone a cero la parte alta) hasta que sea tan grande como el primeroperando y luego lo carga en el primer operando. Si el primer operando es de 16 bits, el segundosólo puede ser de 8; si el primero es de 32 bits el segundo puede ser de 8 ó 16. El primer operandodebe ser un registro, el segundo puede ser un registro u operando en memoria (nunca inmediato):

MOV EAX,0FFFFFFFFhMOV AX,7FFFh ; resultado: EAX = 0FFFF7FFFhMOVSX EAX,AX ; resultado: EAX = 000007FFFh

OUTSD: Similar a OUTSW pero empleando ESI, EDI, ECX y enviando datos de 32 bits. Se puedeemplear bajo DOS siempre que ESI y EDI (usando REP también ECX) no rebasen 0FFFFh.

Prefijos FS: y GS: en los accesos a memoria, referenciando a esos segmentos.

Page 68: PCA, PS2 ,IBM y AT

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

PUSHAD / POPAD: Similares a PUSHA y POPA pero con los registro de 32 bits. La instrucciónPOPAD falla en la mayoría de los 386, incluidos los de AMD. Para solventar el fallo (que consisteen que EAX no se restaura correctamente) basta colocar un NOP inmediatamente detrás de POPAD.

PUSHFD/POPFD introducen y sacan de la pila los flags de 32 bits.

SCASD: Similar a SCASW pero empleando ESI, EDI, ECX y buscando datos de 32 bits. Se puedeemplear bajo DOS siempre que ESI y EDI (usando REP también ECX) no rebasen 0FFFFh.

SETcc reg8 ó mem8: Si se cumple la condición cc, se pone a 1 el byte de memoria o registro de8 bits indicado (si no, a 0). Por ejemplo, con el acarreo activo, SETC AL pone a 1 el registro AL.

SHLD / SHRD: Desplazamiento de doble precisión a la izquierda/derecha. La sintaxis es (ejemplosobre SHLD):

SHLD regmem16, reg16, imm8 ó SHLD regmem16, reg16, CLSHLD regmem32, reg32, imm8 ó SHLD regmem32, reg32, CL

Donde regmem es un registro u operando en memoria, indistintamente, del tamaño indicado.En el caso de SHLD, se desplaza el primer operando a la izquierda tanto como indique el terceroperando (contador). Una vez desplazado, los bits menos significativos se rellenan con los mássignificativos del segundo operando, que no resulta alterado. SHRD es análogo pero al revés.

MOV AX,1234hMOV BX,5678hSHLD AX,BX,4 ; resultado: AX=2345h, BX=5678h

STOSD: Similar a STOSW pero empleando ESI, EDI, ECX y almacenando EAX. Se puedeemplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh.

4.3.4. - DETECCIÓN DE UN SISTEMA AT O SUPERIOR.

Hay casos en los que es necesario determinar si una máquina es AT o superior: no ya de cara aemplear 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, el segundo controlador de interrupciones)que de antemano se sabe que sólo equipan máquinas AT o superiores. Es importante por tanto determinarla presencia de un AT, de cara a evitar ciertas instrucciones que podrían bloquear un PC o XT. No se debeen estos casos comprobar los bytes de la ROM que identifican el equipo: a veces no son correctos y, además,la evolución futura que tengan es impredecible. Lo ideal es verificar directamente si está instalado un 286o superior.

PUSHFPOP AX ; AX = flagsAND AH,0Fh ; borrar nibble más significativoPUSH AXPOPF ; intentar poner a 0 los 4 bits más significativos de los flagsPUSHFPOP AXAND AH,0F0h ; seguirán valiendo 1 excepto en un 80286 o superiorCMP AH,0F0hJE no_es_ATJMP si_es_AT ; es 286 o superior

4.3.5. - EVALUACIÓN EXACTA DEL MICROPROCESADOR INSTALADO.

Sobra decir que las instrucciones avanzadas deben ser utilizadas con la previa comprobación del tipode procesador, aunque sólo sea para decir al usuario que se compre una máquina más potente antes de abortarla ejecución del programa. Para averiguar el procesador de un ordenador puede emplearse el siguienteprograma de utilidad, basado en el procedimiento procesador? que devuelve en AX un código numéricoentro 0 y 8 distinguiendo entre los 9 procesadores más difíciles de identificar de los ordenadores compatibles.

Page 69: PCA, PS2 ,IBM y AT

69JUEGO DE INSTRUCCIONES 80x86

Nota: el 486 no tiene que tener coprocesador necesariamente (el 486sx carece de él).

Algunas versiones de procesador 486 y todos los procesadores posteriores soportan la instrucciónCPUID que permite identificar la CPU. Basta comprobar un bit del registro de estado para saber si estásoportada y, en ese caso, poder emplear dicha instrucción. De este modo, resulta trivial detectar el Pentiumo cualquier procesador posterior que aparezca. Esta instrucción está documentada, por ejemplo en alguno delos ficheros que acompañan al Interrupt List. Para los propósitos de este libro no es preciso en generaldetectar más allá del 386.

Es normal que el lector recién iniciado en el ensamblador no entienda absolutamente nada de esteprograma, ya que hasta los siguientes capítulos no será explicada la sintaxis del lenguaje. En ese caso, puedesaltarse este ejemplo y continuar en el capítulo siguiente, máxime si no tiene previsto trabajar con otrasinstrucciones que no sean las del 8086. Por último, recordar que las instrucciones específicas del 286 en modoreal también están disponibles en los V20/V30 de NEC y la serie 80188/80186 de Intel.

; ********************************************************************; * *; * CPU v2.2 (c) Septiembre 1992 CiriSOFT *; * (c) Grupo Universitario de Informática - Valladolid *; * *; * Este programa determina el tipo de microprocesador del equipo *; * y devuelve un código ERRORLEVEL indicándolo: *; * *; * 0-8088, 1-8086, 2-NEC V20, 3-NEC V30, *; * 4-80188, 5-80186, 6-286, 7-386, 8-486 *; * *; * Aviso: Utilizar TASM 2.0 o compatible exclusivamente. *; * *; ********************************************************************

cpu SEGMENTASSUME CS:cpu, DS:cpu

.386

ORG 100hinicio:

LEA DX,texto_ini ; texto de saludoMOV AH,9INT 21h ; imprimirloCALL procesador? ; tipo de procesador en AXPUSH AX ; guardarlo para el finalLEA BX,cpus_indice-2 ; tabla de nombres-2MOV CX,0FFFFh ; número de iteración-1

otro_proc: INC CXADD BX,2MOV DX,[BX] ; nombre del primer procesadorCALL printCMP CX,AX ; ¿procesador del equipo?JNE no_es_esteLEA DX,apuntador_txt ; sí lo es: indicarloCALL print

no_es_este: LEA DX,separador_txtCALL printCMP CX,7 ; número de CPUs tratadas-1JBE otro_procLEA DX,texto_fin ; últimos caracteresCALL printMOV AH,4Ch ; retornar código errorlevel ALINT 21h ; fin de programa

procesador? PROC ; devolver el tipo de microprocesador en AXPUSHFPUSH DSPUSH ESPUSH CXPUSH DXPUSH DIPUSH SIMOV AX,CSMOV DS,AX ; durante la rutina se guardaráMOV ES,AX ; el tipo de procesador en DL:MOV DL,6 ; supuesto un 286 (DL=6) ...PUSHFPOP AX ; AX = flagsAND AX,0FFFh ; borrar nibble más significativoPUSH AXPOPF ; intentar poner a 0 los 4 bits másPUSHF ; significativos de los flagsPOP AXAND AX,0F000h ; seguirán valiendo 1 excepto enCMP AX,0F000h ; un 80286 o superiorJE ni286ni_superPUSHF ; es 286 o superiorPOP AXOR AX,7000h ; intentar activar bit 12, 13 ó 14PUSH AXPOPFPUSHFPOP AXAND AX,7000h ; 286 pone bits 12, 13 y 14 a ceroJZ cpu_hallada ; es un 286 (DL=6)INC DL ; es un 386 (DL=7) ... de momentoPUSH DXCLI ; momento críticoMOV EDX,ESP ; preservar ESP en EDXAND ESP,0FFFFh ; borrar parte alta de ESPAND ESP,0FFFCh ; forzar ESP a múltiplo de 4PUSHFD ; guardar flags en pila (32 bits)POP EAX ; recuperar flags en EAXMOV ECX,EAXXOR EAX,40000h ; conmutar bit 18PUSH EAX

POPFD ; intentar cambiar este bitPUSHFDPOP EAX ; ECX conserva el bit inicialXOR EAX,ECX ; bit 18 de EAX a 1 si cambióSHR EAX,12h ; mover bit 18 a bit 0AND EAX,1 ; dejar sólo ese bitPUSH ECXPOPFD ; restaurar bit 18 de los flagsMOV ESP,EDX ; restaurar ESPSTI ; permitir interrupciones de nuevoPOP DX ; recuperar tipo de CPU en DLCMP AX,0JE cpu_hallada ; es 386: DL=7 (bit 18 no cambió)INC DL ; es 486: DL=8 (bit 18 cambió)JMP cpu_hallada

ni286ni_super: MOV DL,4 ; supuesto un 80188 ...MOV AX,0FFFFhMOV CL,33SHL AX,CL ; (80188/80186 toman CL mod 32)JNZ tipo_bus_proc ; ... lo es, calcular bus (188/186)MOV DL,2 ; no lo es, supuesto un V20 ...MOV CX,0FFFFhSTIDB 0F3h,26h,0ACh ; opcode de REPZ LODSB ES:JCXZ tipo_bus_proc ; ... lo es, calcular bus (V20/V30)XOR DL,DL ; ya sólo puede ser un 8088/8086

tipo_bus_proc: STD ; transferencias hacia arribaLEA DI,tipo_bus_destMOV AL,BYTE PTR DS:tipo_bus_byte ; opcode de STIMOV CX,3CLIREP STOSB ; transferir tres bytesCLDNOP ; el INC CX (1 byte) será machacadoNOP ; con STOSB pero aún se ejecutaráNOP ; en un 8086/80186/V30 (y no en unINC CX ; 8088/80188/V20) porque está en la

tipo_bus_byte: STI ; cola de lectura adelantada.tipo_bus_dest: STI

JCXZ cpu_hallada ; el bus ya era supuesto de 8 bitsINC DL ; resulta que es de 16

cpu_hallada: MOV AL,DLXOR AH,AHPOP SIPOP DIPOP DXPOP CXPOP ESPOP DSPOPFRET ; AX = CPU: 0/1-8088/86, 2/3-NEC V20/V30

procesador? ENDP ; 4/5-80188/186, 6-286, 7-386, 8-486

print PROCPUSH AXPUSH BXPUSH CXMOV AH,9INT 21hPOP CXPOP BXPOP AXRET

print ENDP

cpus_indice DW i88,i86,v20,v30,i188,i186,i286,i386,i486i88 DB "Intel 8088 $"i86 DB "Intel 8086 $"v20 DB " NEC V20 $"v30 DB " NEC V30 $"i188 DB "Intel 80188$"i186 DB "Intel 80186$"i286 DB "Intel 80286$"i386 DB "Intel 80386$"i486 DB "Intel 80486$"

apuntador_txt DB " < $"

texto_ini LABEL BYTEDB 13,10,"CPU Test v2.2 "DB "(c) Septiembre 1992 Ciriaco García de Celis."DB 13,10," El microprocesador de este "DB "equipo es compatible:",10

separador_txt DB 13,10,9,9,9,"$"texto_fin DB 13,10,"$"

cpu ENDSEND inicio

Page 70: PCA, PS2 ,IBM y AT

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

4.3.6. - MODO PLANO (FLAT) DEL 386 Y SUPERIORES.

Como ya se comentó, no es estrictamente cierto que no se pueda rebasar el límite de 64 Kb en lossegmentos en modo real. El problema es que al encender el ordenador, el 386 tiene definidos por defectodichos límites de 64 Kb. Sin embargo, se puede pasar un momento a modo protegido, ampliar el límite yvolver a modo real. Entonces se consigue el llamado modo flat o plano. No solo es factible de este modosaltar la restricción de 64 Kb, sino que además se puede acceder directamente, desde el modo real, a toda lamemoria por encima del primer megabyte.

El problema es que pasar a modo protegido no es sencillo cuando la máquina ya está en modoprotegido emulando al modo real (el conocido como modo virtual 86). Por tanto, el siguiente programa deejemplo no funciona si está cargado un controlador de memoria expandida (EMM386, QEMM) o dentro deWindows 3.x. Arrancando sin controlador de memoria (excepto HIMEM) no habrá problema alguno. Elprograma de ejemplo se limita a llenar la pantalla de texto (empleando ahora la dirección absoluta 0B8000ha través de EBX) de letras ’A’.

Otra restricción de este programa de ejemplo es que no activa la línea A20 de direcciones; dicho deotro modo, el bit 21º (de los 32 bits de la dirección de memoria) suele estar forzado a 0 por defecto alarrancar. Para acceder a la memoria de vídeo esto no es problema, pero por encima del primer megabytepodría haber problemas según a qué dirección se pretenda acceder. De todos modos, sería relativamentesencillo habilitar la línea A20 directamente o a través de una función del controlador XMS.

Naturalmente, se sale de los objetivos de este libro describir el modo protegido o explicar los pasosque realiza esta rutina de demostración. Consúltese al efecto la bibliografía recomendada del apéndice.

;; Rutina para activar el modo flat del 386 y superiores (acceso; a 4 Gb en modo real).;; TASM flat386 /m5; TLINK flat386 /t /32;

.386p ; sólo para 386 o superior

segmento SEGMENT USE16ASSUME CS:segmento, DS:segmento

ORG 100hprueba:

CALL flat386 ; activar modo flatXOR AX,AXMOV DS,AXMOV EBX,0B8000h ; dirección de vídeo absolutaMOV CX,2000

llena_pant: MOV BYTE PTR [EBX],’A’INC EBXMOV BYTE PTR [EBX],15INC EBXLOOP llena_pantINT 20h ; fin de programa

; ------------ Esta rutina pasa momentáneamente a modo protegido de; manera directa (necesita la CPU en modo real). No se; activa la línea A20 (necesario hacerlo directamente; o a través de algún servicio XMS antes de acceder a; las áreas de memoria extendida afectadas).

flat386 PROCPUSH DSPUSH ESPUSH EAXPUSH BXPUSH CXMOV CX,SSXOR EAX,EAXMOV AX,CSSHL EAX,4 ; dirección lineal de segmento CSADD EAX,OFFSET gdt ; desplazamiento de GDTMOV CS:[gd2],EAX ; guardar dirección lineal de GDTCLILGDT CS:[gdtr] ; cargar tabla global de descriptoresMOV EAX,CR0OR AL,1 ; bit de modo protegidoMOV CR0,EAX ; pasar a modo protegidoJMP SHORT $+2 ; borrar cola de prebúsquedaMOV BX,gcodl ; índice de descriptor en BXMOV DS,BX ; cargar registro de segmento DSMOV ES,BX ; ESMOV SS,BX ; SSMOV FS,BX ; FSMOV GS,BX ; GSAND AL,11111110bMOV CR0,EAX ; volver a modo realJMP SHORT $+2 ; borrar cola de prebúsquedaMOV SS,CXSTIPOP CXPOP BXPOP EAXPOP ESPOP DSRET

gdtr LABEL QWORD ; datos para cargar en GDTRgd1 DW gdtl-1gd2 DD ?

gdt DB 0,0,0,0,0,0,0,0 ; GDTgcod DB 0ffh,0ffh,0,0,0,9fh,0cfh,0gcodl EQU $-OFFSET gdtgdat DB 0ffh,0ffh,0,0,0,93h,0cfh,0gdtl EQU $-OFFSET gdt

flat386 ENDP

segmento ENDSEND prueba

Page 71: PCA, PS2 ,IBM y AT

71EL LENGUAJE ENSAMBLADOR DEL 80x86

Capítulo V: EL LENGUAJE ENSAMBLADOR DEL 80x86

Hasta ahora hemos visto los mnemónicos de las instrucciones que pasadas a su correspondiente códigobinario ya puede entender el microprocesador. Si bien se realiza un gran avance al introducir los mnemónicosrespecto a programar directamente en lenguaje maquina -es decir, con números en binario o hexadecimal-aún resultaría tedioso tener que realizar los cálculos de los desplazamientos en los saltos a otras partes delprograma en las transferencias de control, reservar espacio de memoria dentro de un programa para almacenardatos, etc... Para facilitar estas operaciones se utilizan las directivas que indican al ensamblador qué debehacer con las instrucciones y los datos.

Los programas de ejemplo de este libro y la sintaxis de ensamblador tratada son las del MASM deMicrosoft y el ensamblador de IBM. No obstante, todos los programas han sido desarrollados con el TurboAssembler 2.0 de Borland (TASM), compatible con el clásico MASM 5.0 de Microsoft pero más potente yal mismo tiempo mucho más rápido y flexible. TASM genera además un código más reducido y optimizado.Por otra parte, MASM 5.0 no permite cambiar (aunque sí la 6.0) dentro de un segmento el modo delprocesador: esto conlleva el riesgo de ejecutar indeseadamente instrucciones de 32 bits al no poder acotarexactamente las líneas donde se desea emplearlas, algo vital para mantener la compatibilidad con procesadoresanteriores. También es propenso a generar errores de fase y otros similares al tratar con listados un pocograndes. Respecto a MASM 6.0, el autor de este libro encontró que en ocasiones calcula incorrectamente elvalor de algunos símbolos y etiquetas, aunque es probable que la versión 6.1 (aparecida sospechosa einusualmente muy poco tiempo después) haya corregido dichos fallos, intolerables en un ensamblador. Porotro lado, las posibilidades adicionales de TASM no han sido empleadas por lo general. Muchos programashan sido ensamblados una vez con MASM, para asegurar que éste puede ensamblarlos.

Conviene decir aquí que este capítulo es especialmente arduo para aquellos que no conocen ellenguaje ensamblador de ninguna máquina. La razón es que la información está organizada a modo dereferencia, por lo que con frecuencia se utilizan unos elementos -para explicar otros- que aún no han sidodefinidos. Ello por otra parte resulta inevitable también en algunos libros más básicos, debido a lacomplejidad de la sintaxis del lenguaje ensamblador ideada por el fabricante (que no la del microprocesador).Por ello, es un buen consejo actuar a dos pasadas, al igual que el propio ensamblador en ocasiones: leer todouna vez primero -aunque no se entienda del todo- y volverlo a leer después más despacio.

5.1. - SINTAXIS DE UNA LÍNEA EN ENSAMBLADOR.

Un programa fuente en ensamblador contiene dos tipos de sentencias: las instrucciones y lasdirectivas. Las instrucciones se aplican en tiempo de ejecución, pero las directivas sólo son utilizadas duranteel ensamblaje. El formato de una sentencia de instrucción es el siguiente:

[etiqueta] nombre_instrucción [operandos] [comentario]

Los corchetes, como es normal al explicar instrucciones en informática, indican que lo especificadoentre ellos es opcional, dependiendo de la situación que se trate.

Campo de etiqueta. Es el nombre simbólico de la primera posición de una instrucción,puntero o dato. Consta de hasta 31 caracteres que pueden ser las letras de la A a la Z, los númerosdel 0 al 9 y algunos caracteres especiales como «@», «_», «.» y «$». Reglas:

Page 72: PCA, PS2 ,IBM y AT

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

- Si se utiliza el punto «.» éste debe colocarse como primer carácter de la etiqueta.- El primer carácter no puede ser un dígito.- No se pueden utilizar los nombres de instrucciones o registros como nombres de etiquetas.

las etiquetas son de tipo NEAR cuando el campo de etiqueta finaliza con dos puntos (:); estoes, se considera cercana: quiere esto decir que cuando realizamos una llamada sobre dicha etiquetael ensamblador considera que está dentro del mismo segmento de código (llamadas intrasegmento)y el procesador sólo carga el puntero de instrucciones IP. Téngase en cuenta que hablamos deinstrucciones; las etiquetas empleadas antes de las directivas, como las directivas de definición dedatos por ejemplo, no llevan los dos puntos y sin embargo son cercanas.

Las etiquetas son de tipo FAR si el campo de etiqueta no termina con los dos puntos: enestas etiquetas la instrucción a la que apunta no se encuentra en el mismo segmento de código sinoen otro. Cuando es referenciada en una transferencia de control se carga el puntero de instruccionesIP y el segmento de código CS (llamadas intersegmento).

Campo de nombre. Contiene el mnemónico de las instrucciones vistas en el capítuloanterior, o bien una directiva de las que veremos más adelante.

Campo de operandos. Indica cuales son los datos implicados en la operación. Puede haber0, 1 ó 2; en el caso de que sean dos al 1º se le llama destino y al 2º -separado por una coma- fuente.

mov ax, es:[di] ax destinoes:[di] origen

Campo de comentarios. Cuando en una línea hay un punto y coma (;) todo lo que sigue enla línea es un comentario que realiza aclaraciones sobre lo que se está haciendo en ese programa,resulta de gran utilidad de cara a realizar futuras modificaciones al mismo.

5.2. - CONSTANTES Y OPERADORES.

Las sentencias fuente -tanto instrucciones como directivas- pueden contener constantes y operadores.

5.2.1. - CONSTANTES.

Pueden ser binarias (ej. 10010b), decimales (ej. 34d), hexadecimales (ej. 0E0h) u octales (ej. 21o ó21q); también las hay de cadena (ej. ’pepe’, "juan") e incluso con comillas dentro de comillas de distinto tipo(como ’hola,"amigo"’). En las hexadecimales, si el primer dígito no es numérico hay que poner un 0. Sólose puede poner el signo (-) en las decimales (en las demás, calcúlese el complemento a dos). Por defecto, lasnuméricas están en base 10 si no se indica lo contrario con una directiva (poco recomendable como se verá).

5.2.2. - OPERADORES ARITMÉTICOS.

Pueden emplearse libremente (+), (-), (*) y (/) -en este último caso la división es siempre entera-. Esválida, por ejemplo, la siguiente línea en ensamblador (que se apoya en la directiva DW, que se verá másadelante, 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 laizquierda/derecha cierto número de bits). Obviamente, el ensamblador no codifica las instrucciones dedesplazamiento (al aplicarse sobre datos constantes el resultado se calcula en tiempo de ensamblaje):

dato DW (12 SHR 2) + 5

Page 73: PCA, PS2 ,IBM y AT

73EL LENGUAJE ENSAMBLADOR DEL 80x86

5.2.3. - OPERADORES LÓGICOS.

Pueden ser el AND, OR, XOR y NOT. Realizan las operaciones lógicas en las expresiones. Ej.:

MOV BL,(255 AND 128) XOR 128 ; BL = 0

5.2.4. - OPERADORES RELACIONALES.

Devuelven condiciones de cierto (0FFFFh ó 0FFh) o falso (0) evaluando una expresión. Pueden ser:EQ (igual), NE (no igual), LT (menor que), GT (mayor que), LE (menor o igual que), GE (mayor o igualque). Ejemplo:

dato EQU 100 ; «dato» vale 100MOV AL,dato GE 10 ; AL = 0FFh (cierto)MOV AH,dato EQ 99 ; AH = 0 (falso)

5.2.5. - OPERADORES DE RETORNO DE VALORES.

Operador SEG: devuelve el valor del segmento de la variable o etiqueta, sólo se puede emplearen programas de tipo EXE:

MOV AX,SEG tabla_datos

Operador OFFSET: devuelve el desplazamiento de la variable o etiqueta en su segmento:

MOV AX,OFFSET variable

Si se desea obtener el offset de una variable respecto al grupo (directiva GROUP) desegmentos en que está definida y no respecto al segmento concreto en que está definida:

MOV AX,OFFSET nombre_grupo:variabletambién es válido:

MOV AX,OFFSET DS:variable

Operador .TYPE: devuelve el modo de la expresión indicada en un byte. El bit 0 indica modo«relativo al código» y el 1 modo «relativo a datos», si ambos bits están inactivos significa modoabsoluto. El bit 5 indica si la expresión es local (0 si está definida externamente o indefinida); el bit7 indica si la expresión contiene una referencia externa. El TASM utiliza también el bit 3 para indicaralgo que desconozco. Este operador es útil sobre todo en las macros para determinar el tipo de losparámetros:

info .TYPE variable

Operador TYPE: devuelve el tamaño (bytes) de la variable indicada. No válido en variables DUP:

kilos DW 76MOV AX,TYPE kilos ; AX = 2

Tratándose de etiquetas -en lugar de variables- indica si es lejana o FAR (0FFFEh) o cercanao NEAR (0FFFFh).

Operadores SIZE y LENGTH: devuelven el tamaño (en bytes) o el nº de elementos,respectivamente, de la variable indicada (definida obligatoriamente con DUP):

matriz DW 100 DUP (12345)MOV AX,SIZE matriz ; AX = 200MOV BX,LENGTH matriz ; BX = 100

Operadores MASK y WIDTH: informan de los campos de un registro de bits (véase RECORD).

5.2.6. - OPERADORES DE ATRIBUTOS.

Operador PTR: redefine el atributo de tipo (BYTE, WORD, DWORD, QWORD, TBYTE) o el de

Page 74: PCA, PS2 ,IBM y AT

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

distancia (NEAR o FAR) de un operando de memoria. Por ejemplo, si se tiene una tabla definida dela siguiente manera:

tabla DW 10 DUP (0) ; 10 palabras a 0

Para colocar en AL el primer byte de la misma, la instrucción MOV AL,tabla es incorrecta,ya que tabla (una cadena 10 palabras) no cabe en el registro AL. Lo que desea el programador debeindicárselo en este caso explícitamente al ensamblador de la siguiente manera:

MOV AL,BYTE PTR tabla

Trabajando con varios segmentos, PTR puede redefinir una etiqueta NEAR de uno de ellospara convertirla en FAR desde el otro, con objeto de poder llamarla.

Operadores CS:, DS:, ES: y SS: el ensamblador genera un prefijo de un byte que indica almicroprocesador el segmento que debe emplear para acceder a los datos en memoria. Por defecto,se supone DS para los registros BX, DI o SI (o sin registros de base o índice) y SS para SP y BP.Si al acceder a un dato éste no se encuentra en el segmento por defecto, el ensamblador añadirá elbyte adicional de manera automática. Sin embargo, el programador puede forzar también estacircunstancia:

MOV AL,ES:variable

En el ejemplo, variable se supone ubicada en el segmento extra. Cuando se referencia unadirección fija hay que indicar el segmento, ya que el ensamblador no conoce en qué segmento estála variable, es uno de los pocos casos en que debe indicarse. Por ejemplo, la siguiente línea dará unerror al ensamblar:

MOV AL,[0]

Para solucionarlo hay que indicar en qué segmento está el dato (incluso aunque éste sea DS):

MOV AL,DS:[0]

En este último ejemplo el ensamblador no generará el byte adicional ya que las instruccionesMOV operan por defecto sobre DS (como casi todas), pero ha sido necesario indicar DS para queel ensamblador nos entienda. Sin embargo, en el siguiente ejemplo no es necesario, ya que midatoestá declarado en el segmento de datos y el ensamblador lo sabe:

MOV AL,midato

Por lo general no es muy frecuente la necesidad de indicar explícitamente el segmento: alacceder a una variable el ensamblador mira en qué segmento está declarada (véase la directivaSEGMENT) y según como estén asignados los ASSUME, pondrá o no el prefijo adecuado según seaconveniente. Es responsabilidad exclusiva del programador inicializar los registros de segmento alprincipio de los procedimientos para que el ASSUME no se quede en tinta mojada... sí se empleancon bastante frecuencia, sin embargo, los prefijos CS en las rutinas que gestionan interrupciones (yaque CS es el único registro de segmento que apunta en principio a las mismas, hasta que se cargueDS u otro).

Operador SHORT: indica que la etiqueta referenciada, de tipo NEAR, puede alcanzarse con unsalto corto (-128 a +127 posiciones) desde la actual situación del contador de programa. Elensamblador TASM, si se solicitan dos pasadas, coloca automáticamente instrucciones SHORT allídonde es posible, para economizar memoria (el MASM no).

Operador ’$’: indica la posición del contador de posiciones («Location Counter») utilizado por elensamblador dentro del segmento para llevar la cuenta de por dónde se llega ensamblando. Muy útil:

frase DB "simpático"longitud EQU $-OFFSET frase

Page 75: PCA, PS2 ,IBM y AT

75EL LENGUAJE ENSAMBLADOR DEL 80x86

En el ejemplo, longitud tomará el valor 9.

Operadores HIGH y LOW: devuelven la parte alta o baja, respectivamente (8 bits) de la expresión:

dato EQU 1025MOV AL,LOW dato ; AL = 1MOV AH,HIGH dato ; AH = 4

5.3. - PRINCIPALES DIRECTIVAS.

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»; los campos han de estar separados por al menosun espacio en blanco. La sintaxis de «nombre» es análoga a la de la «etiqueta» de las líneas de instrucciones,aunque nunca se pone el sufijo «:». El campo de comentario cumple también las mismas normas. Acontinuación se explican las directivas empleadas en los programas ejemplo de este libro y alguna más,aunque falta alguna que otra y las explicadas no lo están en todos los casos con profundidad.

5.3.1. - DIRECTIVAS DE DEFINICIÓN DE DATOS.

DB (definir byte), DW (definir palabra), DD (definir doble palabra), DQ (definir cuádruplepalabra), DT (definir 10 bytes): sirven para declarar las variables, asignándolas un valor inicial:

anno DW 1991mes DB 12numerazo DD 12345678htexto DB "Hola",13,10

Se pueden definir números reales de simple precisión (4 bytes) con DD, de doble precisión(8 bytes) con DQ y «reales temporales» (10 bytes) con DT; todos ellos con el formato empleado porel coprocesador. Para que el ensamblador interprete el número como real ha de llevar el puntodecimal:

temperatura DD 29.72espanoles91 DQ 38.9E6

Con el operando DUP pueden definirse estructuras repetitivas. Por ejemplo, para asignar 100bytes a cero y 25 palabras de contenido indefinido (no importa lo que el ensamblador asigne):

ceros DB 100 DUP (0)basura DW 25 DUP (?)

Se admiten también los anidamientos. El siguiente ejemplo crea una tabla de bytes donde serepite 50 veces la secuencia 1,2,3,7,7:

tabla DB 50 DUP (1, 2, 3, 2 DUP (7))

5.3.2. - DIRECTIVAS DE DEFINICIÓN DE SÍMBOLOS.

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. Se trata de un operadormuy flexible. Es válido hacer:

edad EQU [BX+DI+8]MOV AX,edad

Page 76: PCA, PS2 ,IBM y AT

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

= (signo ’=’): asigna el valor de la expresión a un nombre simbólico variable: Análogo al anteriorpero con posibilidad de cambiar en el futuro. Muy usada en macros (sobre todo con REPT).

num = 19num = pepe + 1dato = [BX+3]dato = ES:[BP+1]

5.3.3. - DIRECTIVAS DE CONTROL DEL ENSAMBLADOR.

ORG (ORiGin): pone el contador de posiciones del ensamblador, que indica el offset donde sedeposita la instrucción o dato, donde se indique. En los programas COM (que se cargan en memoriacon un OFFSET 100h) es necesario colocar al principio un ORG 100h, y un ORG 0 en loscontroladores de dispositivo (aunque si se omite se asume de hecho un ORG 0).

END [expresión]: indica el final del fichero fuente. Si se incluye, expresión indica el punto dondearranca el programa. Puede omitirse en los programas EXE si éstos constan de un sólo módulo. Enlos COM es preciso indicarla y, además, la expresión -realmente una etiqueta- debe estarinmediatamente después del ORG 100h.

.286, .386 Y .8087 obligan al ensamblador a reconocer instrucciones específicas del 286, el 386y del 8087. También debe ponerse el «.» inicial. Con .8086 se fuerza a que de nuevo sólo sereconozcan instrucciones del 8086 (modo por defecto). La directiva .386 puede ser colocada dentrode un segmento (entre las directivas SEGMENT/ENDS) con el ensamblador TASM, lo que permiteemplear instrucciones de 386 con segmentos de 16 bits; alternativamente se puede ubicar fuera delos segmentos (obligatorio en MASM) y definir éstos explícitamente como de 16 bits con USE16.

EVEN: fuerza el contador de posiciones a una posición par, intercalando un byte con la instrucciónNOP si es preciso. En buses de 16 ó más bits (8086 y superiores, no en 8088) es dos veces másrápido el acceso a palabras en posición par:

EVENdato_rapido DW 0

.RADIX n: cambia la base de numeración por defecto. Bastante desaconsejable dada la notaciónelegida para indicar las bases por parte de IBM/Microsoft (si se cambia la base por defecto a 16, ¡losnúmeros no pueden acabar en ’d’ ya que se confundirían con el sufijo de decimal!: lo ideal seríaemplear un prefijo y no un sufijo, que a menudo obliga además a iniciar los números por 0 paradistinguirlos de las etiquetas).

5.3.4. - DIRECTIVAS DE DEFINICIÓN DE SEGMENTOS Y PROCEDIMIENTOS.

SEGMENT-ENDS: SEGMENT indica el comienzo de un segmento (código, datos, pila, etc.) yENDS su final. El programa más simple, de tipo COM, necesita la declaración de un segmento(común para datos, código y pila). Junto a SEGMENT puede aparecer, opcionalmente, el tipo dealineamiento, la combinación, el uso y la clase:

nombre SEGMENT [alineamiento] [combinación] [uso] [’clase’]. . . .

nombre ENDS

Se pueden definir unos segmentos dentro de otros (el ensamblador los ubicará unos trasotros). El alineamiento puede ser BYTE (ninguno), WORD (el segmento comienza en posición par),DWORD (comienza en posición múltiplo de 4), PARA (comienza en una dirección múltiplo de 16,opción por defecto) y PAGE (comienza en dirección múltiplo de 256). La combinación puede ser:

Page 77: PCA, PS2 ,IBM y AT

77EL LENGUAJE ENSAMBLADOR DEL 80x86

- (No indicada): los segmentos se colocan unos tras otros físicamente, pero sonlógicamente independientes: cada uno tiene su propia base y sus propios offsets relativos.

- PUBLIC: usado especialmente cuando se trabaja con segmentos definidos en variosficheros que se ensamblan por separado o se compilan con otros lenguajes, por ello debedeclararse un nombre entre comillas simples -’clase’- para ayudar al linkador. Todos lossegmentos PUBLIC de igual nombre y clase tienen una base común y son colocadosadyacentemente unos tras otros, siendo el offset relativo al primer segmento cargado.

- COMMON: similar, aunque ahora los segmentos de igual nombre y clase sesolapan. Por ello, las variables declaradas han de serlo en el mismo orden y tamaño.

- AT: asocia un segmento a una posición de memoria fija, no para ensamblar sinopara declarar variables (inicializadas siempre con ’?’) de cara a acceder con comodidad azonas de ROM, vectores de interrupción, etc. Ejemplo:

vars_bios SEGMENT AT 40hp_serie0 DW ?vars_bios ENDS

De esta manera, la dirección del primer puerto serie puede obtenerse de estamanera (por ejemplo):

MOV AX,variables_bios ; segmentoMOV ES,AX ; inicializar ESMOV AX,ES:p_serie0

- STACK: segmento de pila, debe existir uno en los programas de tipo EXE; ademásel Linkador de Borland (TLINK 4.0) exige obligatoriamente que la clase de éste sea también’STACK’, con el LINK de Microsoft no siempre es necesario indicar la clase del segmentode pila. Similar, por lo demás, a PUBLIC.

- MEMORY: segmento que el linkador ubicará al final de todos los demás, lo quepermitiría saber dónde acaba el programa. Si se definen varios segmentos de este tipo elensamblador acepta el primero y trata a los demás como COMMON. Téngase en cuenta queel linkador no soporta esta característica, por lo que emplear MEMORY es equivalente atodos los efectos a utilizar COMMON. Olvídate de MEMORY.

El uso indica si el segmento es de 16 bits o de 32; al emplear la directiva .386 se asumenpor defecto segmentos de 32 bits por lo que es necesario declarar USE16 para conseguir que lossegmentos sean interpretados como de 16 bits por el linkador, lo que permite emplear algunasinstrucciones del 386 en el modo real del microprocesador y bajo el sistema operativo DOS.

Por último, ’clase’ es un nombre opcional que empleará el linkador para encadenar losmódulos, siendo conveniente nombrar la clase del segmento de pila con ’STACK’.

ASSUME (Suponer): Indica al ensamblador el registro de segmento que se va a utilizar paradireccionar cada segmento dentro del módulo. Esta instrucción va normalmente inmediatamentedespués del SEGMENT. El programa más sencillo necesita que se «suponga» CS como mínimo parael segmento de código, de lo contrario el ensamblador empezará a protestar un montón al no saberque registro de segmento asociar al código generado. También conviene hacer un assume del registrode segmento DS hacia el segmento de datos, incluso en el caso de que éste sea el mismo que el decódigo: si no, el ensamblador colocará un byte de prefijo adicional en todos los accesos a memoriapara forzar que éstos sean sobre CS. Se puede indicar ASSUME NOTHING para cancelar unASSUME anterior. 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[,...]

PROC-ENDP permite dar nombre a una subrutina, marcando con claridad su inicio y su fin.

Page 78: PCA, PS2 ,IBM y AT

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

Aunque es redundante, es muy recomendable para estructurar los programas.

cls PROC...

cls ENDP

El atributo FAR que aparece en ocasiones junto a PROC indica que es un procedimientolejano y las instrucciones RET en su interior se ensamblan como RETF (los CALL hacia él serán,además, de 32 bits). Observar que la etiqueta nunca termina con dos puntos.

5.3.5. - DIRECTIVAS DE REFERENCIAS EXTERNAS.

PUBLIC: permite hacer visibles al exterior (otros ficheros objeto resultantes de otros listados enensamblador u otro lenguaje) los símbolos -variables y procedimientos- indicados. Necesario paraprogramación modular e interfaces con lenguajes de alto nivel. Por ejemplo:

PUBLIC proc1, var_xproc1 PROC FAR

proc1 ENDPvar_x DW 0

Declara la variable var_x y el procedimiento proc1 como accesibles desde el exterior pormedio de la directiva EXTRN.

EXTRN: Permite acceder a símbolos definidos en otro fichero objeto (resultante de otro ensamblajeo de una compilación de un lenguaje de alto nivel); es necesario también indicar el tipo del dato oprocedimiento (BYTE, WORD o DWORD; NEAR o FAR; se emplea además ABS para lasconstantes numéricas):

EXTRN proc1:FAR, 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,var_x. Si la directiva EXTRNse coloca dentro de un segmento, se supone el símbolo dentro del mismo. Si el símbolo está en otrosegmento, debe colocarse EXTRN fuera de todos los segmentos indicando explícitamente el prefijodel registro de segmento (o bien hacer el ASSUME apropiado) al referenciarlo. Evidentemente, alfinal, al linkar habrá que enlazar este módulo con el que define los elementos externos.

INCLUDE nombre_fichero: Añade al fichero fuente en proceso de ensamblaje el fichero indicado,en el punto en que aparece el INCLUDE. Es exactamente lo mismo que mezclar ambos ficheros conun editor de texto. Ahorra trabajo en fragmentos de código que se repiten en varios programas (comoquizá una librería de macros). No se recomiendan INCLUDE’s anidados.

5.3.6. - DIRECTIVAS DE DEFINICIÓN DE BLOQUES.

NAME nombre_modulo_objeto: indica el nombre del módulo objeto. Si no se incluye NAME, setomará de la directiva TITLE o, en su defecto, del nombre del propio fichero fuente.

GROUP segmento1, segmento2,... permite agrupar dos o más segmentos lógicos en uno sólo deno más de 64 Kb totales (ojo: el ensamblador no comprueba este extremo, aunque sí el enlazador).Ejemplo:

superseg GROUP datos, codigo, pila

codigo SEGMENT

codigo ENDS

Page 79: PCA, PS2 ,IBM y AT

79EL LENGUAJE ENSAMBLADOR DEL 80x86

datos SEGMENTdato DW 1234datos ENDS

pila SEGMENT STACK ’STACK’DB 128 DUP (?)

pila ENDS

Cuando se accede a un dato definido en algún segmento de un grupo y se emplea el operadorOFFSET es preciso indicar el nombre del grupo como prefijo, de lo contrario el ensamblador nogenerará el desplazamiento correcto ¡ni emitirá errores!:

MOV AX,dato ; ¡incorrecto!MOV AX,supersegmento:dato ; correcto

La ventaja de agrupar segmentos es poder crear programas COM y SYS que contengan variossegmentos. En todo caso, téngase en cuenta aún en ese caso que no pueden emplearse todas lascaracterísticas de la programación con segmentos (por ejemplo, no se puede utilizar la directiva SEGni debe existir segmento de pila).

LABEL: Permite referenciar un símbolo con otro nombre, siendo factible redefinir el tipo. Lasintaxis es: nombre LABEL tipo (tipo = BYTE, WORD, DWORD, NEAR o FAR). Ejemplo:

palabra LABEL WORDbyte_bajo DB 0byte_alto DB 0

En el ejemplo, con MOV AX,palabra se accederá a ambos bytes a la vez (el empleo de MOVAX,byte_bajo daría error: no se puede cargar un sólo byte en un registro de 16 bits y el ensambladorno supone que realmente pretendíamos tomar dos bytes consecutivos de la memoria).

STRUC - ENDS: permite definir registros al estilo de los lenguajes de alto nivel, para acceder deuna manera más elegante a los campos de una información con cierta estructura. Estos campospueden componerse de cualquiera de los tipos de datos simples (DB, DW, DD, DQ, DT) y puedenser modificables o no en función de si son simples o múltiples, respectivamente:

alumno STRUCmote DB ’0123456789’ ; modificableedadaltura DB 20,175 ; no modificablepeso DB 0 ; modificableotros DB 10 DUP(0) ; no modificabletelefono DD ? ; modificablealumno ENDS

La anterior definición de estructura no lleva implícita la reserva de memoria necesaria, la cualha de hacerse expresamente utilizando los ángulos ’<’ y ’>’:

felipe alumno <’Gordinflas’,,101,,251244>

En el ejemplo se definen los campos modificables (los únicos definibles) dejando sin definir(comas consecutivas) los no modificables, creándose la estructura ’felipe’ que ocupa 27 bytes. Lascadenas de caracteres son rellenadas con espacios en blanco al final si no alcanzan el tamaño máximode la declaración. El TASM es más flexible y permite definir también el primer elemento de loscampos múltiples sin dar error. Tras crear la estructura, es posible acceder a sus elementos utilizandoun (.) para separar el nombre del campo:

MOV AX,OFFSET felipe.telefonoLEA BX,felipeMOV CL,[BX].peso ; equivale a [BX+12]

RECORD: similar a STRUC pero operando con campos de bits. Permite definir una estructuradeterminada de byte o palabra para operar con comodidad. Sintaxis:

Page 80: PCA, PS2 ,IBM y AT

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

nombre RECORD nombre_de_campo:tamaño[=valor],...

Donde nombre permitirá referenciar la estructura en el futuro, nombre_de_campo identificalos distintos campos, a los que se asigna un tamaño (en bits) y opcionalmente un valor por defecto.

registro RECORD a:2=3, b:4=5, c:1

La estructura registro totaliza 7 bits, por lo que ocupa un byte. Está dividida en tres camposque ocupan los 7 bits menos significativos del byte: el campo A ocupa los bits 6 y 5, el B los bits1 al 4 y el C el bit 0:

6 5 4 3 2 1 0

1 1 0 1 0 1 ?

La reserva de memoria se realiza, por ejemplo, de la siguiente manera:

reg1 registro <2,,1>

Quedando reg1 con el valor binario 1001011 (el campo B permanece inalterado y el A y Ctoman los valores indicados). Ejemplos de operaciones soportadas:

MOV AL, A ; AL = 5 (desplazamiento del bit; menos significativo de A)

MOV AL, MASK A ; AL = 01100000b (máscara de A)MOV AL, WIDTH A ; AL = 2 (anchura de A)

5.3.7. - DIRECTIVAS CONDICIONALES.

Se emplean para que el ensamblador evalúe unas condiciones y, según ellas, ensamble o nociertas zonas de código. Es frecuente, por ejemplo, de cara a generar código para varios ordenadores:pueden existir ciertos símbolos definidos que indiquen en un momento dado si hay que ensamblarciertas zonas del listado o no de manera condicional, según la máquina. En los fragmentos enensamblador del código que generan los compiladores también aparecen con frecuencia (para actuarde manera diferente, por ejemplo, según el modelo de memoria). Es interesante también la posibilidadde definir un símbolo que indique que el programa está en fase de pruebas y ensamblar códigoadicional en ese caso con objeto de depurarlo. Sintaxis:

IFxxx [símbolo/exp./arg.] ; xxx es la condición...ELSE ; el ELSE es opcional...ENDIF

IF expresion (expresión distinta de cero)IFE expresión (expresión igual a cero)IF1 (pasada 1 del ensamblador)IF2 (pasada 2 del ensamblador)IFDEF símbolo (símbolo definido o declarado como externo)IFNDEF símbolo (símbolo ni definido ni declarado como externo)IFB <argumento> (argumento en blanco en macros -incluir ’<’ y ’>’-)IFNB <argumento> (lo contrario, también es obligado poner ’<’ y ’>’)IFIDN <arg1>, <arg2> (arg1 idéntico a arg2, requiere ’<’ y ’>’)IFDIF <arg1>, <arg2> (arg1 distinto de arg2, requiere ’<’ y ’>’)

5.3.8. - DIRECTIVAS DE LISTADO.

PAGE num_lineas, num_columnas: Formatea el listado de salida; por defecto son 66 líneas porpágina (modificable entre 10 y 255) y 80 columnas (seleccionable de 60 a 132). PAGE salta depágina e incrementa su número. «PAGE +» indica capítulo nuevo (y se incrementa el número).

TITLE título: indica el título que aparece en la 1ª línea de cada página (máximo 60 caracteres).

Page 81: PCA, PS2 ,IBM y AT

81EL LENGUAJE ENSAMBLADOR DEL 80x86

SUBTTL subtítulo: Ídem con el subtítulo (máx. 60 caracteres).

.LALL: Listar las macros y sus expansiones.

.SALL: No listar las macros ni sus expansiones.

.XALL: Listar sólo las macros que generan código objeto.

.XCREF: Suprimir listado de referencias cruzadas (listado alfabético de símbolos junto al nº delínea en que son definidos y referenciados, de cara a facilitar la depuración).

.CREF: Restaurar listado de referencias cruzadas.

.XLIST: Suprimir el listado ensamblador desde ese punto.

.LIST: Restaurar de nuevo la salida de listado ensamblador.

COMMENT delimitador comentario delimitador: Define un comentario que puede incluso ocuparvarias líneas, el delimitador (primer carácter no blanco ni tabulador que sigue al COMMENT) indicael inicio e indicará más tarde el final del comentario. ¡No olvidar cerrar el comentario!.

%OUT mensaje: escribe en la consola el mensaje indicado durante la fase de ensamblaje y al llegara ese punto del listado, excepto cuando el listado es por pantalla y no en fichero.

.LFCOND: Listar los bloques de código asociados a una condición falsa (IF).

.SFCOND: suprimir dicho listado.

.TFCOND: Invertir el modo vigente de listado de los bloques asociados a una condición falsa.

5.4. - MACROS.

Cuando un conjunto de instrucciones en ensamblador aparecen frecuentemente repetidas a lo largode un listado, es conveniente agruparlas bajo un nombre simbólico que las sustituirá en aquellos puntos dondeaparezcan. Esta es la misión de las macros; por el hecho de soportarlas el ensamblador eleva su categoría ala de macroensamblador, al ser las macros una herramienta muy cotizada por los programadores.

No conviene confundir las macros con subrutinas: es estas últimas, el conjunto de instruccionesaparece una sola vez en todo el programa y luego se invoca con CALL. Sin embargo, cada vez que sereferencia a una macro, el código que ésta representa se expande en el programa definitivo, duplicándosetantas veces como se use la macro. Por ello, aquellas tareas que puedan ser realizadas con subrutinas siempreserá más conveniente realizarlas con las mismas, con objeto de economizar memoria. Es cierto que las macrosson algo más rápidas que las subrutinas (se ahorra un CALL y un RET) pero la diferencia es tan mínima queen la práctica es despreciable en el 99,99% de los casos. Por ello, es absurdo e irracional realizar ciertastareas con macros que pueden ser desarrolladas mucho más eficientemente con subrutinas: es una pena queen muchos manuales de ensamblador aún se hable de macros para realizar operaciones sobre cadenas decaracteres, que generarían programas gigantescos con menos de un 1% de velocidad adicional.

5.4.1. - DEFINICIÓN Y BORRADO DE LAS MACROS.

La macro se define por medio de la directiva MACRO. Es necesario definir la macro antes deutilizarla. Una macro puede llamar a otra. Con frecuencia, las macros se colocan juntas en un ficheroindependiente y luego se mezclan en el programa principal con la directiva INCLUDE:

Page 82: PCA, PS2 ,IBM y AT

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

IF1INCLUDE fichero.ext

ENDIF

La sentencia IF1 asegura que el ensamblador lea el fichero fuente de las macros sólo en la primerapasada, para acelerar el ensamblaje y evitar que aparezcan en el listado (generado en la segunda fase).Conviene hacer hincapié en que la definición de la macro no consume memoria, por lo que en la práctica esindiferente declarar cientos que ninguna macro:

nombre_simbólico MACRO [parámetros]...... ; instrucciones de la macroENDM

El nombre simbólico es el que permitirá en adelante hacer referencia a la macro, y se construye casicon las mismas reglas que los nombres de las variables y demás símbolos. La macro puede contenerparámetros de manera opcional. A continuación vienen las instrucciones que engloba y, finalmente, ladirectiva ENDM señala el final de la macro. No se debe repetir el nombre simbólico junto a la directivaENDM, ello provocaría un error un tanto curioso y extraño por parte del ensamblador (algo así como «Findel fichero fuente inesperado, falta directiva END»), al menos con MASM 5.0 y TASM 2.0.

En realidad, y a diferencia de lo que sucede con los demás símbolos, el nombre de una macro puedecoincidir con el de una instrucción máquina o una directiva del ensamblador: a partir de ese momento, lainstrucción o directiva machacada pierde su significado original. El ensamblador dará además un aviso deadvertencia si se emplea una instrucción o directiva como nombre de macro, aunque tolerará la operación.Normalmente se las asignará nombres normales, como a las variables. Sin embargo, si alguna vez seredefiniera una instrucción máquina o directiva, para restaurar el significado original del símbolo, la macropuede ser borrada -o simplemente porque ya no va a ser usada a partir de cierto punto del listado, y así yano consumirá espacio en las tablas de macros que mantiene en memoria el ensamblador al ensamblar-. Noes necesario borrar las macros antes de redefinirlas. Para borrarlas, la sintaxis es la siguiente:

PURGE nombre_simbólico[,nombre_simbólico,...]

5.4.2. - EJEMPLO DE UNA MACRO SENCILLA.

Desde el 286 existe una instrucción muy cómoda que introduce en la pila 8 registros, y otra que lossaca (PUSHA y POPA). Quien esté acostumbrado a emplearlas, puede crear unas macros que simulen estasinstrucciones en los 8086:

SUPERPUSH MACROPUSH AXPUSH CXPUSH DXPUSH BXPUSH SPPUSH BPPUSH SIPUSH DIENDM

La creación de SUPERPOP es análoga, sacando los registros en orden inverso. El orden elegido noes por capricho y se corresponde con el de la instrucción PUSHA original, para compatibilizar. A partir dela definición de esta macro, tenemos a nuestra disposición una nueva instrucción máquina (SUPERPUSH)que puede ser usada con libertad dentro de los programas.

5.4.3. - PARÁMETROS FORMALES Y PARÁMETROS ACTUALES.

Para quien no haya tenido relación previa con algún lenguaje estructurado de alto nivel, haré un brevecomentario acerca de lo que son los parámetros formales y actuales en una macro, similar aquí a losprocedimientos de los lenguajes de alto nivel.

Page 83: PCA, PS2 ,IBM y AT

83EL LENGUAJE ENSAMBLADOR DEL 80x86

Cuando se llama a una macro se le pueden pasar opcionalmente un cierto número de parámetros decierto tipo. Estos parámetros se denominan parámetros actuales. En la definición de la macro, dichosparámetros aparecen asociados a ciertos nombres arbitrarios, cuya única misión es permitir distinguir unosparámetros de otros e indicar en qué orden son entregados: son los parámetros formales. Cuando elensamblador expanda la macro al ensamblar, los parámetros formales serán sustituidos por suscorrespondientes parámetros actuales. Considerar el siguiente ejemplo:

SUMAR MACRO a,b,totalPUSH AXMOV AX,aADD AX,bMOV total,AXPOP AXENDM....SUMAR positivos, negativos, total

En el ejemplo, «a», «b» y «total» son los parámetros formales y «positivos», «negativos» y «total»son los parámetros actuales. Tanto «a» como «b» pueden ser variables, etiquetas, etc. en otro punto delprograma; sin embargo, dentro de la macro, se comportan de manera independiente. El parámetro formal«total» ha coincidido en el ejemplo y por casualidad con su correspondiente actual. El código que genera elensamblador al expandir la macro será el siguiente:

PUSH AXMOV AX,positivosADD AX,negativosMOV total,AXPOP AX

Las instrucciones PUSH y POP sirven para no alterar el valor de AX y conseguir que la macro secomporte como una caja negra; no es necesario que esto sea así pero es una buena costumbre deprogramación para evitar que los programas hagan cosas raras. En general, las macros de este tipo nodeberían alterar los registros y, si los cambian, hay que tener muy claro cuáles.

Si se indican más parámetros de los que una macro necesita, se ignorarán los restantes. En cambio,si faltan, el MASM asumirá que son nulos (0) y dará un mensaje de advertencia, el TASM es algo más rígidoy podría dar un error. En general, se trata de situaciones atípicas que deben ser evitadas.

También puede darse el caso de que no sea posible expandir la macro. En el ejemplo, no hubiera sidoposible ejecutar SUMAR AX,BX,DL porque DL es de 8 bits y la instrucción MOV DL,AX sería ilegal.

5.4.4. - ETIQUETAS DENTRO DE MACROS. VARIABLES LOCALES.

Son necesarias normalmente para los saltos condicionales que contengan las macros más complejas.Si se pone una etiqueta a donde saltar, la macro sólo podría ser empleada una vez en todo el programa paraevitar que dicha etiqueta aparezca duplicada. La solución está en emplear la directiva LOCAL que ha de ircolocada justo después de la directiva MACRO:

MINIMO MACRO dato1, dato2, resultadoLOCAL ya_estaMOV AX,dato1CMP AX,dato2 ; ¿es dato1 el menor?JB ya_esta ; síMOV AX,dato2 ; no, es dato2

ya_esta: MOV resultado,AXENDM

En el ejemplo, al invocar la macro dos veces el ensamblador no generará la etiqueta «ya_esta» sinolas etiquetas ??0000, ??0001, ... y así sucesivamente. La directiva LOCAL no sólo es útil para los saltoscondicionales en las macros, también permite declarar variables internas a los mismos. Se puede indicar unnúmero casi indefinido de etiquetas con la directiva LOCAL, separándolas por comas.

Page 84: PCA, PS2 ,IBM y AT

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

5.4.5. - OPERADORES DE MACROS.

Operador ;;Indica que lo que viene a continuación es un comentario que no debe aparecer al expansionar

la macro. Cuando al ensamblar se genera un listado del programa, las macros suelen aparecerexpandidas en los puntos en que se invocan; sin embargo sólo aparecerán los comentarios normalesque comiencen por (;). Los comentarios relacionados con el funcionamiento interno de la macrodeberían ir con (;;), los relativos al uso y sintaxis de la misma con (;). Esto es además convenienteporque durante el ensamblaje son mantenidos en memoria los comentarios de macros (no los del restodel programa) que comienzan por (;), y no conviene desperdiciar memoria...

Operador &Utilizado para concatenar texto o símbolos. 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 cMOV AL,"&c"

etiqueta&c: CALL imprimirENDM

Al ejecutar SALUDO A se producirá la siguiente expansión:

MOV AL,"A"etiquetaA: CALL imprimir

Si no se hubiera colocado el & se hubiera expandido como MOV AL,"c"

Cuando se utilizan estructuras repetitivas REPT, IRP o IRPC (que se verán más adelante)existe un problema adicional al intentar crear etiquetas, ya que el ensamblador se come un & al hacerla primera sustitución, generando la misma etiqueta a menos que se duplique el operador &:

MEMORIA MACRO xIRP i, <1, 2>

x&i DB iENDMENDM

Si se invoca MEMORIA ET se produce el error de "etiqueta ETi repetida", que se puedesalvar añadiendo tantos ’&’ como niveles de anidamiento halla en las estructuras repetitivasempleadas, como se ejemplifica a continuación:

MEMORIA MACRO xIRP i, <1, 2>

x&&i DB iENDMENDM

Lo que con MEMORIA ET generará correctamente las líneas:

ET1 DB 1ET2 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. Por ello, !; es equivalente a <;>.

Operador %Convierte la expresión que le sigue -generalmente un símbolo- a un número; la expresión

debe ser una constante (no relocalizable). Sólo se emplea en los argumentos de macros. Dada lamacro siguiente:

Page 85: PCA, PS2 ,IBM y AT

85EL LENGUAJE ENSAMBLADOR DEL 80x86

PSUM MACRO mensaje, suma%OUT * mensaje, suma *ENDM

(Evidentemente, 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 EQU 120SIM2 EQU 500

Invocando la macro con las siguientes condiciones:

PSUM < SIM1 + SIM2 = >, (SIM1+SIM2)

Se produce la siguiente expansión:

%OUT * SIM1 + SIM2 = (SIM1+SIM2) *

Sin embargo, invocando la macro de la siguiente manera (con %):

PSUM < SIM1 + SIM2 = >, %(SIM1+SIM2)

Se produce la expansión deseada:

%OUT * SIM1 + SIM2 = 620 *

5.4.6. - DIRECTIVAS ÚTILES PARA MACROS.

Estas directivas pueden ser empleadas también sin las macros, aumentando la comodidad de laprogramación, aunque abundan especialmente dentro de las macros.

REPT veces ... ENDM (Repeat)

Permite repetir cierto número de veces una secuencia de instrucciones. El bloque deinstrucciones se delimita con ENDM (no confundirlo con el final de una macro). Por ejemplo:

REPT 2OUT DX,AL

ENDM

Esta secuencia se transformará, al ensamblar, en lo siguiente:

OUT DX,ALOUT DX,AL

Empleando símbolos definidos con (=) y apoyándose además en las macros se puede llegara crear pseudo-instrucciones muy potentes:

SUCESION MACRO nnum = 0REPT nDB numnum = num + 1

ENDM ; fin de REPTENDM ; fin de macro

La sentencia SUCESION 3 provocará la siguiente expansión:

DB 0DB 1DB 2

Page 86: PCA, PS2 ,IBM y AT

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

IRP simbolo_control, <arg1, arg2, ..., arg_n> ... ENDM (Indefinite repeat)

Es relativamente similar a la instrucción FOR de los lenguajes de alto nivel. Los ángulos (<)y (>) son obligatorios. El símbolo de control va tomando sucesivamente los valores (nonecesariamente numéricos) arg1, arg2, ... y recorre en cada pasada todo el bloque de instruccioneshasta alcanzar el ENDM (no confundirlo con fin de macro) sustituyendo simbolo_control por esosvalores en todos los lugares en que aparece:

IRP i, <1,2,3>DB 0, i, i*i

ENDM

Al expansionarse, este conjunto de instrucciones se convierte en lo siguiente:

DB 0, 1, 1DB 0, 2, 4DB 0, 3, 9

Nota: Todo lo encerrado entre los ángulos se considera un único parámetro. Un (;) dentro de losángulos no se interpreta como el inicio de un comentario sino como un elemento más. Porotra parte, al emplear macros anidadas, deben indicarse tantos símbolos angulares ’<’ y ’>’consecutivos como niveles de anidamiento existan.

Lógicamente, dentro de una macro también resulta bastante útil la estructura IRP:

TETRAOUT MACRO p1, p2, p3, p4, valorPUSH AXPUSH DXMOV AL,valorIRP cn, <p1, p2, p3, p4>MOV DX, cnOUT DX, AL

ENDM ; fin de IRPPOP DXPOP AXENDM ; fin de macro

Al ejecutar TETRAOUT 318h, 1C9h, 2D1h, 1A4h, 17 se obtendrá:

PUSH AXPUSH DXMOV AL, 17MOV DX, 318hOUT DX, ALMOV DX, 1C9hOUT DX, ALMOV DX, 2D1hOUT DX, ALMOV DX, 1A4hOUT DX,ALPOP DXPOP AX

Cuando se pasan listas como parámetros hay que encerrarlas entre ’<’ y ’>’ al llamar, parano confundirlas con elementos independientes. Por ejemplo, supuesta la macro INCD:

INCD MACRO lista, pIRP i, <lista>INC i

ENDM ; fin de IRPDEC pENDM ; fin de macro

Se comprende la necesidad de utilizar los ángulos:

Page 87: PCA, PS2 ,IBM y AT

87EL LENGUAJE ENSAMBLADOR DEL 80x86

INCD AX, BX, CX, DX se expandirá:

INC AXDEC BX ; CX y DX se ignoran (4 parámetros)

INCD <AX, BX, CX>, DX se expandirá:

INC AXINC BXINC CXDEC DX ; (2 parámetros)

IRPC simbolo_control, <c1c2 ... cn> ... ENDM (Indefinite repeat character)

Esta directiva es similar a la anterior, con una salvedad: los elementos situados entre losángulos (<) y (>) -ahora opcionales, por cierto- son caracteres ASCII y no van separados por comas:

IRPC i, <813>DB i

ENDM

El bloque anterior generará al expandirse:

DB 8DB 1DB 3

Ejemplo de utilización dentro de una macro (en combinación con el operador &):

INICIALIZA MACRO a, b, c, dIRPC iter, <&a&b&c&d>DB iter

ENDM ; fin de IRPCENDM ; fin de macro

Al ejecutar INICIALIZA 7, 1, 4, 0 se produce la siguiente expansión:

DB 7DB 1DB 4DB 0

EXITMSirve para abortar la ejecución de un bloque MACRO, REPT, IRP ó IRPC. Normalmente se

utiliza apoyándose en una directiva condicional (IF...ELSE...ENDIF). Al salir del bloque, se pasa alnivel inmediatamente superior (que puede ser otro bloque de estos). Como ejemplo, la siguientemacro reserva n bytes de memoria a cero hasta un máximo de 100, colocando un byte 255 al finaldel bloque reservado:

MALLOC MACRO nmaximo=100REPT nIF maximo EQ 0 ; ¿ya van 100?EXITM ; abandonar REPT

ENDIFmaximo = maximo - 1DB 0 ; reservar byte

ENDMDB 255 ; byte de fin de bloqueENDM

5.4.7. - MACROS AVANZADAS CON NUMERO VARIABLE DE PARÁMETROS.

Como se vio al estudiar la directiva IF, existe la posibilidad de chequear condicionalmente lapresencia de un parámetro por medio de IFNB, o su ausencia con IFB. Uniendo esto a la potencia de IRPes posible crear macros extraordinariamente versátiles. Como ejemplo, valga la siguiente macro, destinada

Page 88: PCA, PS2 ,IBM y AT

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

a introducir en la pila un número variable de parámetros (hasta 10): es especialmente útil en los programasque gestionan interrupciones:

XPUSH MACRO R1,R2,R3,R4,R5,R6,R7,R8,R9,R10IRP reg, <R1,R2,R3,R4,R5,R6,R7,R8,R9,R10>IFNB <reg>

PUSH regENDIF

ENDM ; fin de IRPENDM ; fin de XPUSH

Por ejemplo, la instrucción:XPUSH AX,BX,DS,ES,VAR1

Se expandirá en:PUSH AXPUSH AXPUSH DSPUSH ESPUSH VAR1

El ejemplo anterior es ilustrativo del mecanismo de comprobación de presencia de parámetros. Sinembargo, este ejemplo puede ser optimizado notablemente empleando una lista como único parámetro:

XPUSH MACRO listaIRP i, <lista>PUSH i

ENDMENDM

XPOP MACRO listaIRP i, <lista>POP i

ENDMENDM

La ventaja es el número indefinido de parámetros soportados (no sólo 10). Un ejemplo de uso puedeser el siguiente:

XPUSH <AX, BX, CX>XPOP <CX, BX, AX>

Que al expandirse queda:PUSH AXPUSH BXPUSH CXPOP CXPOP BXPOP AX

5.5. - PROGRAMACIÓN MODULAR Y PASO DE PARÁMETROS.

Aunque lo que viene a continuación no es indispensable para programar en ensamblador, sí esconveniente leerlo en 2 ó 3 minutos para observar ciertas reglas muy sencillas que ayudarán a hacerprogramas seguros y eficientes. Sin embargo, personalmente considero que cada uno es muy libre de hacerlo que desee; por otra parte, en muchos casos no se pueden cumplir los principios de la programaciónelegante -especialmente en ensamblador- por lo que detesto aquellos profesionales de la informática que seentrometen con la manera de programar de sus colegas o alumnos, obligándolos a hacer las cosas a su gusto.

La programación modular consiste en dividir los problemas más complejos en módulos separados conunas ciertas interdependencias, lo que reduce el tiempo de programación y aumenta la fiabilidad del código.Se pueden implementar en ensamblador con las directivas PROC y ENDP que, aunque no generan códigoson bastante útiles para dejar bien claro dónde empieza y acaba un módulo. Reglas para la buenaprogramación:

Page 89: PCA, PS2 ,IBM y AT

89EL LENGUAJE ENSAMBLADOR DEL 80x86

- Dividir los problemas en módulos pequeños relacionados sólo por un conjunto deparámetros de entrada y salida.

- 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, no debiendo existir más puntos de saliday no siendo recomendable alterar la dirección de retorno.

- Excepto en los puntos en que la velocidad o la memoria son críticas (la experienciademuestra que son menos del 1%) debe codificarse el programa con claridad, si es preciso perdiendoeficiencia. Ese 1% documentarlo profusamente como se haría para que lo lea otra persona.

- Los módulos han de ser «cajas negras» y no deben modificar el entorno exterior. Estosignifica que no deben actuar sobre variables globales ni modificar los registros (excepto aquellosregistros y variables en que devuelven los resultados, lo que debe documentarse claramente alprincipio del módulo). Tampoco deben depender de ejecuciones anteriores, salvo excepciones en quela propia claridad del programa obligue a lo contrario (por ejemplo, los generadores de númerosaleatorios pueden depender de la llamada anterior).

Para el paso de parámetros entre módulos existen varios métodos que se exponen a continuación. Losparámetros pueden pasarse además de dos maneras: directamente por valor, o bien indirectamente porreferencia o dirección. En el primer caso se envía el valor del parámetro y en el segundo la dirección inicialde memoria a partir de la que está almacenado. El tipo de los parámetros habrá de estar debidamentedocumentado al principio de los módulos.

- Paso de parámetros en los registros: Los módulos utilizan ciertos registros muy concretos paracomunicarse. Todos los demás registros han de permanecer inalterados, por lo cual, si son empleadosinternamente, han de ser preservados al principio del módulo y restaurados al final. Este es el métodoempleado por el DOS y la BIOS en la mayoría de las ocasiones para comunicarse con quien los llama. Losregistros serán preservados preferiblemente en la pila (con PUSH) y recuperados de la misma (con POP enorden inverso); de esta manera, los módulos son reentrantes y pueden ser llamados de manera múltiplesoportando, entre otras características, la recursividad (sin embargo, se requerirá también que las variableslocales se generen sobre la pila).

- Paso de parámetros a través de un área común: se utiliza una zona de memoria para lacomunicación. Este tipo de módulos no son reentrantes y hasta que no acaben de procesar una llamada nose les debe llamar de nuevo en medio de la faena.

- Paso de parámetros por la pila. En este método, los parámetros son apilados antes de llamar almódulo que los va a recoger. Este debe conocer el número y tamaño de los mismos, para equilibrar el punterode pila al final antes de retornar (método de los compiladores de lenguaje Pascal) o en caso contrario elprograma que llama deberá encargarse de esta operación (lenguaje C). La ventaja del paso de parámetros porla pila es el prácticamente ilimitado número de parámetros admitido, de cómodo acceso, y que los módulossiguen siendo reentrantes. Un ejemplo puede ser el siguiente:

dato LABEL DWORDdatoL DW ?datoH DW ?

PUSH datoL ; apilar parámetrosPUSH datoHCALL moduloA ; llamadaADD SP,4 ; equilibrar pila

moduloA PROC NEARPUSH BPMOV BP,SPMOV DX,[BP+4] ; parte alta del datoMOV AX,[BP+6] ; parte baja del dato

POP BPRET

moduloA ENDP

Page 90: PCA, PS2 ,IBM y AT

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

En el ejemplo, tenemos la variable dato de 32 bits dividida en dos partes de 16. Dicha variable escolocada en la pila empezando por la parte menos significativa. A continuación se llama a MODULOA, elcual comienza por preservar BP (lo usará posteriormente) para respetar la norma de caja negra. Se carga BPcon SP debido a que el 8086 no permite el direccionamiento indexado sobre SP. Como la instrucción CALLse dirige a una dirección cercana (NEAR), en la pila se almacena sólo el registro IP. Por tanto, en [BP+0]está el BP del programa que llama, en [BP+2] el registro IP del programa que llama y en [BP+4] y [BP+6]la variable enviada, que es el caso más complejo (variables de 32 bits). Dicha variable es cargada en DX:AXantes de proceder a usarla (también deberían apilarse AX y DX para conservar la estructura de caja negra).Al final, se retorna con RET y el programa principal equilibra la pila aumentando SP en 4 unidades paracompensar el apilamiento previo de dos palabras antes de llamar. Si MODULOA fuera un procedimientolejano (FAR) la variable estaría en [BP+6] y [BP+8], debido a que al llamar al módulo se habría guardadotambién en la pila el CS del programa que llama. El lenguaje Pascal hubiera retornado con RET 4, haciendoinnecesario que el programa que llama equilibre la pila. Sin embargo, el método del lenguaje C expuesto esmá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, el C apila los parámetros antes de llamar en orden inverso, empezando porel último: de esta manera se accede correctamente a los primeros N parámetros que se necesiten).

Page 91: PCA, PS2 ,IBM y AT

91EL ENSAMBLADOR EN ENTORNO DOS

Capítulo VI: EL ENSAMBLADOR EN ENTORNO DOS

6.1. - TIPOS DE PROGRAMAS EJECUTABLES BAJO DOS.

Antes de que el COMMAND.COM pase el control al programa que se pretende ejecutar, se crea unbloque de 256 bytes llamado PSP (Program Segment Prefix), cuya descripción detallada se verá en elpróximo capítulo. En él aparecen datos tales como la dirección de retorno al dos cuando finalice el programa,la dirección de retorno en caso de Ctrl-Break y en caso de errores críticos. Además de la cantidad de memoriadisponible y los posibles parámetros suministrados del programa. Cuando el programa toma el control, DSy ES apuntan al PSP. Tipos de programas:

En los de tipo COM:- CS apunta al PSP e IP=100h (el programa empieza tras el PSP).- SS apunta al PSP y SP toma la dirección más alta dentro del segmento del PSP.

En los de tipo EXE:- CS e IP toman los valores del punto de arranque del programa (directiva END etiqueta).- SS apunta al segmento de pila y SP = tamaño de la pila definida.

Si el programa es COM podemos terminarlo con la interrupción 20h (INT 20h), o simplemente conun RET si la pila no está desequilibrada (apunta a un INT 20h que hay en la posición 0 del PSP); otramanera de acabar es por medio de la función 4Ch del sistema (disponible desde el DOS 2.0) que acabacualquier programa sin problemas y sin ningún tipo de requerimientos adicionales, tanto COM como EXE.

Los programas de tipo COM se cargan en memoria tal y como están en disco, entregándoseles elcontrol. Los de tipo EXE, que pueden llegar a manejar múltiples segmentos de código de hasta 64 Kb, sealmacenan en disco «semiensamblados». En realidad, al ser cargados en memoria, el DOS tiene que realizarla última fase de montaje, calculando las direcciones de memoria absolutas. Por ello, estos programas tienenun formato especial en disco, generado por los ensambladores y compiladores, y su imagen en memoria nose corresponde realmente con lo que está grabado en el disco, aunque esto al usuario no le importe. Por ello,no se extrañe el lector de haber visto alguna vez ficheros EXE de más de 640 Kb: evidentemente, no secargan enteros en memoria aunque lo parezca. Los programas COM no hacen referencias a datos odirecciones separados más de 64 Kb, por lo que todos los saltos y desplazamientos son relativos a losregistros de segmento (no se cambia CS ni DS) con lo que no es necesaria la fase de «montaje». No obstante,un programa COM puede hacer lo que le de la gana con los registros de segmento y acceder a más de 64Kb de memoria, por cuenta y riesgo del programador. En general, la programación en ensamblador está hoyen día relegada a pequeños programas residentes, controladores de dispositivos o rutinas de apoyo aprogramas hechos en otros lenguajes, por lo que no es estrictamente necesario trabajar con programas EXErealizados en ensamblador. Salvo excepciones, la mayoría de los programas desarrollados en este libro seránde tipo COM ya que los EXE ocuparían algo más, aunque el ensamblador da algo más de comodidad alprogramador en los mismos.

6.2. - EJEMPLO DE PROGRAMA DE TIPO COM.

El siguiente ejemplo escribe una cadena en pantalla llamando a uno de los servicios estándar deimpresión del DOS (función 9 de INT 21h):

Page 92: PCA, PS2 ,IBM y AT

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

cr EQU 13 ; constante de retorno de carrolf EQU 10 ; constante de salto de línea

programa SEGMENT ; segmento común a CS, DS, ES, SS.

ASSUME CS:programa, DS:programa

ORG 100h ; programa de tipo COM

inicio: LEA DX,texto ; dirección de texto a imprimirMOV AH,9 ; función de impresiónINT 21h ; llamar al DOSINT 20h ; volver al sistema operativo

texto DB cr,lf,"Grupo Universitario de Informática.",cr,lf,"$"

programa ENDS ; fin del segmento

END inicio ; fin del programa y punto de inicio

Programa tipo COM

Olvidándonos de los comentarios que comienzan por «;», en las primeras lineas las directivas EQUdefinen dos constantes para el preprocesador del compilador: cr=13 y lf=10. El programa, de tipo COM,consta de un único segmento. La directiva ASSUME indica que, por defecto, las instrucciones máquina seensamblarán para el registro CS en este segmento (lo más lógico, por otra parte); también conviene asumirel registro DS, de lo contrario, si hubiera que acceder a una variable, el ensamblador añadiría el prefijo delsegmento CS a la instrucción al no estar seguro de que DS apunta a los datos, consumiendo más memoria.Se pueden añadir los demás registros de segmento en el ASSUME, aunque es redundante. El ORG 100h esobligatorio en programas COM, ya que estos programas serán cargados en memoria en la posición CS:100h.Al final, la dirección del texto a imprimir se coloca en DS:DX (CS=DS=ES=SS en un programa COM reciénejecutado) y se llama al DOS. El carácter ’$’ delimita la cadena a imprimir, lo cual es una herencia del CP/M(sería más interesante que fuera el 0 el delimitador) por razones históricas. Se acaba el programa con INT20h. El punto de arranque es indicado con la directiva END, aunque en realidad en los programas COM elpunto indicado (en el ejemplo, «inicio») debe estar forzosamente al principio del programa. Obsérvese queno se genera código hasta llegar a la línea «inicio:», todo lo anterior son directivas.

6.3. - EJEMPLO DE PROGRAMA DE TIPO EXE.

Los programas EXE (listado en la página siguiente) requieren algo más de elaboración. En primerlugar, es necesario definir una pila y reservar espacio para la misma. 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ísticaobliga a definir un tamaño prudente en función de las necesidades del programa. Téngase en cuenta que enla pila se almacenan las direcciones de retorno de las subrutinas y al llamar a una función de la BIOS la pilaes usada con intensidad. En general, con medio kilobyte basta para programas tan sencillos como el delejemplo, e incluso para otros mucho más complejos. El límite máximo está en 64 Kb. El segmento de pilase nombra siempre STACK y con el TLINK de Borland es necesario indicar también la clase ’STACK’.

Como se ve, son definidos por separado el segmento de código, pila y datos, lo que también ayudaa estructurar más el programa. El segmento de código se define como procedimiento FAR, entre otras razonespara que el ensamblador ensamble el RET del final (con el que se vuelve al DOS) como un RETF. Ladirectiva ASSUME asocia cada registro de segmento con su correspondiente segmento. Como puedeobservarse al principio del programa, es necesario preparar «a mano» la dirección de retorno al sistema. ElPUSH DS del principio coloca el segmento del PSP en la pila; el XOR AX,AX coloca un cero en AX (estainstrucción gasta un byte menos que MOV AX,0) y el PUSH AX mete ese 0 en la pila. Con ello, al volver

Page 93: PCA, PS2 ,IBM y AT

93EL ENSAMBLADOR EN ENTORNO DOS

al DOS con RET (RETF en realidad) el control pasará a DS:0, esto es, a la primera instrucción del PSP (INT20h). Aunque pueda parecer un tanto lioso, es un juego de niños y estas tres instrucciones consecutivas(PUSH DS / XOR AX,AX / PUSH AX) son la manera de empezar de cientos de programas EXE, quedespués acaban con RET. En general, a partir del DOS 2.0 es más aconsejable terminar el programa con lafunción 4Ch del DOS, que no requiere que CS apunte al PSP ni precisa de preparación alguna en la pila yademás permite retornar un código de ERRORLEVEL en AL: en los programas futuros esto se hará conbastante frecuencia.

También debe observarse cómo se inicializa DS, ya que en los programas EXE por defecto no apuntaa los datos. Ahora puede preguntarse el lector, por curiosidad, ¿qué valdrá «datos»?: datos tiene un valorrelativo asignado por el ensamblador; cuando el programa sea cargado en memoria, en el proceso de montajey en función de cuál sea la primera posición de memoria libre, se le asignará un valor determinado por elmontador del sistema operativo.

cr EQU 13lf EQU 10

; Segmento de datos

datos SEGMENTtexto DB cr,lf,"Texto a imprimir",cr,lf,"$"datos ENDS

; Segmento de pila

pila SEGMENT STACK ’STACK’ ; poner STACK es obligatorioDB 128 dup (’pila’) ; reservados 512 bytes

pila ENDS

; Segmento de código

codigo SEGMENTejemplo PROC FAR

ASSUME CS:codigo, DS:datos, SS:pila

; poner dirección de retorno al DOS en la pila:

PUSH DS ; segmento del PSPXOR AX,AX ; AX = 0PUSH AX ; desplazamiento 0 al PSP

; direccionar segmento de datos con DS

MOV AX,datos ; AX = dirección del segmento de datosMOV DS,AX ; inicializar DS

; escribir texto

LEA DX,texto ; DS:DX = dirección del textoMOV AH,9INT 21h

; volver al DOS

RET ; en realidad, RETF (PROC FAR)

ejemplo ENDP

codigo ENDS ; fin del códigoEND ejemplo ; punto de arranque del programa

Programa EXE

Page 94: PCA, PS2 ,IBM y AT

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

6.4. - PROCESO DE ENSAMBLAJE.

6.4.1. - TASM/MASM.

Es el programa que convierte nuestro listado fuente en código objeto, es decir, lenguaje máquina enel que sólo faltan las referencias a rutinas externas. Permite la obtención de listados de código y dereferencias cruzadas (símbolos, etiquetas, variables). En general, bastará con hacer TASM nombre_programa(se supone la extensión .ASM por defecto). El fichero final tiene extensión OBJ. En general, la sintaxis delTASM y MASM es más o menos equivalente: en el primero se obtiene ayuda con /H y en el segundo con/HELP. Con TASM, cuando se va a obtener la versión definitiva del programa, o si éste es corto -o elordenador rápido- merece la pena utilizar el parámetro /m3, con objeto de que de dos/tres pasadas y optimizemás el código. Por su lado, 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, entre 1 y 63. La sintaxis es (tanto paraTASM como MASM):

TASM fichero_fuente, fichero_listado, fichero_referencias_cruzadas

Se puede omitir el fichero de listado y el de referencias cruzadas. Cuando se emplea MASM 6.X,para ensamblar los listados de este libro hay que indicar la opción /Zm para mantener la compatibilidad conlas versiones anteriores del ensamblador, siendo además obligatorio indicar la extensión; como se generadirectamente el fichero EXE hay que indicar /c si se desea evitar esto (si no se quiere que linke). La sintaxisquedaría:

ML /Zm fihero_fuente.asm

A continuación se listan los parámetros comunes a TASM 2.0 (y posterior) y MASM 4.0/5.0 (NO la 6.X):

/a y /s Seleccionan un orden alfabético o secuencial de los segmentos./c 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, o bien incluye el listado de referencias cruzadas directamente dentro dellistado del programa (caso de TASM). Las referencias cruzadas son un listado de todos los símbolos del programa,indicando los números de línea del mismo en que son definidos y referenciados.

/D De la manera /Dsímbolo[=valor] permite crear el símbolo indicado, cuya presencia puede comprobarse en el programa conuna directiva IF (es útil para definir externamente un símbolo que indique que el programa está en fase de depuración, decara a ensamblar cierto código adicional). Aunque /d (en minúsculas) es un obsoleto parámetro de MASM para obtenerun listado de la primera pasada del ensamblador, MASM 4.0 es capaz de darse cuenta de que se pretende definir un símbolocon /d a menos que se indique solo /d.

/e Emula las instrucciones de punto flotante del 80x87, apoyándose en una librería al efecto./Iruta Permite indicar el directorio donde el ensamblador debe de buscar los ficheros indicados en el programa fuente con

INCLUDE./l[a] Con /l se genera un listado de ensamblaje y con /la un listado expandido./m 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, /mx sólo con los símbolos globales y /mu hace quese mayusculicen todos los símbolos globales. Al ensamblar módulos para usar desde lenguaje C hay que indicar por lomenos /mx. En MASM 6.X se emplea /Cx en lugar de /mx, /Cp en lugar de /ml y /Cu en vez de /mu.

/n Suprime las tablas de símbolos en el listado./p Verifica que el código generado para el modo protegido es correcto (al emplear la directiva para generar instrucciones de

modo protegido)./t Suprime los mensajes si el ensamblaje es correcto./w Indica el nivel de advertencias: /w0 ninguna, /w1 sólo las serias y /w2 sólo consejos./X Lista las condiciones falsas (ensamblaje condicional)./z Visualiza la línea del error y no sólo el número de la misma./Zi Genera información simbólica para los depuradores de código./Zd Incluye sólo la información del número de línea.

6.4.2. - TLINK/LINK.

El montador o linkador permite combinar varios módulos objeto, realizando las conexiones entre ellosy, finalmente, los convierte en módulo ejecutable de tipo EXE (empleando el ML de MASM 6.X se obtiene

Page 95: PCA, PS2 ,IBM y AT

95EL ENSAMBLADOR EN ENTORNO DOS

directamente el fichero EXE ya que invoca automáticamente al linkador). El linkador permite el uso delibrerías de funciones y rutinas. TLINK, a diferencia de LINK, permite generar un fichero de tipo COMdirectamente de un OBJ si se indica el parámetro /t, lo que agiliza aún más el proceso. Puede obtenerse ayudaejecutándolo sin parámetros. Los parámetros de TLINK son sensibles a mayúsculas y minúsculas, por lo que/T no es lo mismo que /t. Con LINK se obtiene ayuda indicando /HELP. Aunque los parámetros de uno yotro son bastante distintos, la sintaxis genérica de ambos es:

TLINK fich_obj(s), fich_exe, fich_map, fich_libreria, fich_def

Los ficheros no necesarios se pueden omitir (o indicar NUL): para linkar el fichero prog1.obj y elprog2.obj con la librería math.lib generando PROG1.EXE basta con ejecutar TLINK prog1+prog2,,,math.Alternativamente se puede indicar TLINK @fichero para que tome los parámetros del fichero de textoFICHERO, en el caso de que estos sean demasiados y sea incómodo teclearlos cada vez que se linka. Losficheros de texto de extensión MAP contienen información útil para el programador sobre la distribución dememoria de los segmentos.

6.4.3. - EXE2BIN.

Los ficheros EXE generados por TLINK o LINK no son copia exacta de lo que aparece en lamemoria, sino que el DOS -tras cargarlos- debe realizar una última operación de «montaje». Un programaCOM en memoria es una copia del fichero del disco, es algo más corto y más sencillo de desensamblar. Alcontrario de lo que algunos opinaron en su día, el tiempo ha demostrado que nunca llegarían a serdirectamente compatibles con los actuales entornos multitarea.

EXE2BIN permite transformar un fichero EXE en COM siempre que el módulo ocupe menos de 64Ky que esté ensamblado con ORG 100h. Si no se indicó el parámetro /t en TLINK, será necesario esteprograma (al igual que cuando se utiliza LINK). Cuando se crean programas SYS (que se diferencian de losCOM básicamente en que no tienen ORG 100h) no se puede ejecutar TLINK /t, por lo que es necesaria laayuda de EXE2BIN para convertir el programa EXE en SYS. Sintaxis:

EXE2BIN fich.exe (a veces hay que indicar EXE2BIN fich.exe fich.com)

Si el programa no contiene ORG 100h, EXE2BIN genera un fichero binario puro de extensión BIN.Si además existen referencias absolutas a segmentos, EXE2BIN preguntará el segmento en que va a correr(algunas versiones permiten indicarlo de la manera /Ssegmento): esto permite generar código para serejecutado en un segmento determinado de la memoria (como pueda ser una memoria EPROM o ROM).

6.4.4. - TLIB/LIB.

El gestor de librerías permite reunir módulos objeto en un único fichero para poder tomar de él lasrutinas que se necesiten en cada caso. En este libro no se desarrollan programas tan complejos que justifiquensu utilización. En cualquier caso, la sintaxis es la siguiente:

TLIB fichero_libreria comandos, fichero_listado

Si no se indican comandos se obtiene simplemente información del contenido de la librería en elfichero de listado (que puede ser CON para listado por pantalla). 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 +-, reemplaza el módulo existente en la librería-* alternativamente *-, extrae el módulo de la librería y lo borra de ella

Page 96: PCA, PS2 ,IBM y AT

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

Por ejemplo, para añadir el módulo QUICK.OBJ, borrar el SLOW.OBJ y reemplazar el SORT.OBJpor una nueva versión en LIBRERIA.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 delmismo (si no cabe en una línea del fichero, puede escribirse & al final antes de pasar a la siguiente).

6.4.5. TCREF/CREF.

Esta utilidad genera listados en orden alfabético de los símbolos, como ayuda a la depuración. Conel MASM la opción /c crea un fichero de referencias cruzadas de extensión CRF (respondiendoafirmativamente cuando pregunta por el mismo o indicándolo explícitamente en la línea de comandos); laopción /c de TASM lo incluye en el listado, aunque si se indica el nombre del fichero de referencias cruzadasgenera un fichero de extensión XRF. CREF y TCREF interpretan respectivamente los ficheros CRF y XRFgenerando un fichero de texto con extensión REF que contiene el listado de referencias cruzadas. Ej.:

TASM fichero,,,ficheroTCREF fichero

Las referencias cruzadas son un listado de todos los símbolos del programa, indicando los númerosde línea del mismo en que son referenciados (la línea en que son definidos se marca con #); estos númerosde línea son relativos al listado de ensamblaje del programa (y no al fichero fuente). Es útil para depurarprogramas grandes y complejos.

6.4.6. - MAKE.

Esta utilidad se apoya en unos ficheros especiales, al estilo de los BAT del DOS, de cara aautomatizar el proceso de ensamblaje. Sólo es recomendable para programas grandes, divididos en módulos,en los que MAKE chequea la fecha y hora para ensamblar sólo las partes que hayan sido modificadas.

6.5. - LA UTILIDAD DEBUG/SYMDEB.

La utilidad DEBUG incluída en los sistemas MS-DOS, es una herramienta para depuración deprogramas muy interesante que permite desensamblar los módulos y, además, ejecutar programas paso a paso,viendo las modificaciones que sufren los registros y banderas. Se trata de un programa menos complejo,cómodo y potente que depuradores de código como Turbo Debugger (de Borland) o Codeview (Microsoft),pero en algunos casos es más útil. Veremos ahora los principales comandos del DEBUG, los cuales tambiénson admitidos en su mayoría por Codeview, por lo que el tiempo invertido en aprenderlos será útil no sólopara conocer el clásico y mítico DEBUG.

Antes de empezar con ellos, conviene hacer referencia al programa SYMDEB que acompaña alMASM de Microsoft: se trata de un DEBUG mejorado, con ayuda, más rápido e inteligente (indica el tipode función del sistema cuando al tracear un programa éste llama al DOS) y, en la práctica, es 99%compatible. También admite las instrucciones adicionales del 286 y los NEC V20/V30. Su diferencia principales que al abandonarlo para volver al DOS restaura los vectores de interrupción, lo que puede no ser deseableen algunos casos muy concretos. Además, desde la versión 4.0 se admite el parámetro /S (con SYMDEB /Snomfich.ext) lo que permite conmutar entre la pantalla de depuración y la de ejecución pulsando la tecla ’\’.

Sintaxis general: DEBUG [programa.ext [parámetros] ]

Los programas pueden ser de tipo EXE o COM; en el caso de los primeros se les cargará yamontados y con los registros inicializados, listos para su ejecución. Evidentemente, los programas COMtambién se cargan con los registros inicializados y el correspondiente PSP preparado, así como con IP=100h.

Page 97: PCA, PS2 ,IBM y AT

97EL ENSAMBLADOR EN ENTORNO DOS

Los parámetros opcionales no son los de el DEBUG o SYMDEB sino los que normalmente se suministraríanal programa a depurar. También se pueden cargar otros ficheros de cualquier extensión o simplemente entraren el programa sin cargar ningún fichero. Al entrar, aparecerá el prompt particular del DEBUG: un guión (-).Entonces se pueden teclear órdenes que constarán generalmente de una sola letra. La mayoría de las mismasadmiten parámetros, que normalmente irán separados por comas. Estos parámetos pueden ser númeroshexadecimales de hasta dos o cuatro dígitos, registros y, además:

- Cadenas de caracteres: Encerradas entre comillas simples o dobles. El texto puede a su vez encerrarfragmentos entrecomillados, empleando comillas distintas a las más exteriores. Ejemplo:

"Cadena de caracteres", "Otra ’cadena’ más", ’Curso de "8086"’

Con SYMDEB debe tenerse cuidado de no colocar el nombre de un registro de segmento enmayúsculas y seguido de dos puntos, ya que no se interpretará correctamente:

"ESTO ES: ESTA CADENA SERA MAL TRADUCIDA."

La cadena ’ES:’ no será bien traducida a sus correspondientes valores ASCII. Con DEBUGeste problema no existe.

- Direcciones: Pueden expresarse con sus correspondientes valores numéricos o bien apoyándose enalgún registro de segmento, aunque el offset siempre será numérico: 1E93:AD21, CS:100, ES:19AC

El depurador SYMDEB es mucho más flexible y permite también emplear registros depropósito general en el offset. Sería válida la dirección DS:BX+AX+104.

- Rangos: Son dos direcciones separadas por una coma; o bien una dirección, la letra ’L’ y un valornumérico que indica el número de bytes a partir de la dirección.

- Listas: Son secuencias de bytes y/o cadenas separadas por comas:

AC, "Texto de ejemplo", 0D, 0A, ’$’

El DEBUG del MS-DOS 5.0 y el SYMDEB poseen una ayuda invocable con el comando ?, en laque se resumen las principales órdenes. A continuación se listan las más interesantes:

Q (Quit): permite abandonar el programa y volver al DOS.

D [<dirección> [numbytes]] (dump): visualiza el contenido de la memoria. SYMDEB permiteademás visualizarla en palabras (DW), dobles palabras (DD), coma flotante ...

A [<dirección>] (assemble): permite ensamblar a partir de CS:IP si no se indica una direcciónconcreta. Se admiten las directivas DB y DW del ensamblador. Las instrucciones que requieranindicar un registro de segmento, con DEBUG hay que ponerlas en una sola línea. Por ejemplo:

XLAT CS: ; mal ensamblado con DEBUG (no así con SYMDEB)MOV WORD PTR ES:[100],1234 ; error en DEBUG (sí vale con SYMDEB)CS: ; bien emsamblado con ambosXLATES: ; y esto tambiénMOV WORD PTR [100],1234

Los saltos inter-segmento deben especificarse como FAR (ej., CALL FAR [100]) a no serque sea evidente que lo son (ej. CALL 1234:5678).

E <dirección> [<lista>] (enter): permite consultar y modificar la memoria, byte a byte. Por

Page 98: PCA, PS2 ,IBM y AT

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

ejemplo, con E 230 1,2,3 se introducirían los bytes 1, 2 y 3 a partir de DS:230. Si no se indica<lista>, se visualizará la memoria byte a byte, pudiéndose modificar los bytes deseados, avanzar alsiguiente (barra espaciadora) o retroceder al anterior (signo -). Para acabar se pulsa RETURN.

U [<direccion> [<rango>]] (unassemble): desensambla la memoria. Como ejemplos válidos: UES:100, U E000:1940 ... si se indica rango, DEBUG desensamblará ese número de bytes y SYMDEBese número de líneas. Por defecto se emplea CS: como registro de segmento.

R [<registro>] (register): permite visualizar y modificar el valor de los registros. Por ejemplo, sise ejecuta la orden ’rip’, se solicitará un nuevo valor para IP; con RF se muestran los flags y sepermite modificar alguno:

Flag Activo Borrado

Desbordamiento OV NVDirección DN (↓) UP (↑)Interrupción EI DISigno NG (<0) PL (>0)Cero ZR (=0) NZ (!=0)Acarreo auxiliar AC NAParidad PE (par) PO (impar)Acarreo CY NC

G [=<dirección> [,<dirección>,...]] (go): ejecuta código desde CS:IP (a menos que se indique unadirección concreta). Si se trabaja sobre memoria ROM no debe indicarse la segunda dirección. Paraque el flujo del programa se detenga en la 2ª dirección o posteriores debe pasar necesariamente porella(s). Se puede indicar hasta 10 direcciones donde debe detenerse.

T [<veces>] (trace): ejecuta una instrucción del programa (a partir de CS:IP) mostrando acontinuación el estado de los registros y la siguiente instrucción. Ejecutar T10 equivaldría a ejecutar16 veces el comando T. Si la instrucción es CALL o INT, se ejecutará como tal introduciéndose enla subrutina o servidor de interrupciones correspondiente (SYMDEB no entra en los INT 21h).

P [<veces>] (proceed): similar al comando T, pero al encontrarse un CALL o INT lo ejecuta degolpe sin entrar en su interior (ojo, ¡esto último falla al tracear sobre memoria ROM!).

N <especificacion_fichero> (name): se asigna un nombre al programa que está siendo creado omodificado. Se puede indicar la trayectoria de directorios.

L [<dirección>] (load): carga el fichero de nombre indicado con el comando N. Si es ejecutablelo prepara adecuadamente para su inmediata ejecución. En BX:CX queda depositado el tamaño delfichero (BX=0 para ficheros de menos de 64 Kb). Por defecto, la dirección es CS:100h.

L <dirección> <unidad> <primer_sector> <num_sectores> (load): carga sectores de la unidad 0,1, ... (A, B, ...) a memoria. 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 más de 32 Mb.

W [<dirección>] (write): graba el contenido de una zona de memoria a disco. Si no se indica ladirección, se graba desde CS:100h hasta CS:100h+número_bytes; el número de bytes se indica enBX:CX (no es una dirección segmentada sino un valor de 32 bits). Si se trata de un EXE no sepermitirá grabarlo (para modificarlos, hay que renombrarles para cambiarles la extensión, aunque deesta manera no serán montados al cargarlos).

W <dirección> <unidad> <primer_sector> <num_sectores> (write): graba sectores de la memoriaa disco en la unidad 0, 1, ... (A, B, ...). Se trata de sectores lógicos del DOS y no los sectores físicosde la BIOS. Las versiones antiguas de SYMDEB dan errores en particiones de disco duro de más de32 Mb.

S <rango> <lista> (search): busca una cadena de bytes por la memoria. Para buscar la cadena

Page 99: PCA, PS2 ,IBM y AT

99EL ENSAMBLADOR EN ENTORNO DOS

"PEPE" terminada por cero en un área de 512 bytes desde DS:100 se haría: S 100 L 200 "PEPE",0(por defecto se busca en DS:). No se encontraría sin embargo "pepe" (en minúsculas).

F <rango> <lista> (fill): llena la zona de memoria especificada con repeticiones de la lista debytes indicada. Por ejemplo, para rellenar códigos 0AAh 100h bytes a partir de 9800h:0 se ejecutaríaF 9800:0 L 100 AA; en vez de AA se podría haber indicado una lista de bytes o cadenas decaracteres.

C <rango> <dirección> (compare): compara dos zonas de memoria mostrando las diferencias. Porejemplo, para comparar 5 bytes de DS:100 y DS:200 se hace: C 100 L 5 200.

M <rango> <dirección> (move): Más que mover, copia una zona de memoria en otra de manerainteligente (controlando los posibles solapamientos de los bloques).

I <puerto> (input): visualiza la lectura del puerto de E/S indicado.

O <puerto> <valor> (output): envia un valor a un puerto de E/S.

H <valor1> <valor2> (hexaritmetic): muestra la suma y resta de valor1 y valor2, ambos operandosde un máximo de 16 bits (si hay desbordamiento se trunca el resultado, que tampoco excede los 16bits).

También existen comandos en DEBUG para acceder a la memoria expandida: XS (obtener el estadode la memoria expandida), XA npag (localizar npag páginas), XD handle (desalojar el handle indicado) y XMpagina_logica pagina_fisica handle (mapear páginas).

Con SYMDEB pueden además colocarse, con suma facilidad, puntos de ruptura (breakpoints); conDEBUG se pueden implementar con la orden G (indicando más de una dirección hasta un máximo de 10,donde debe detenerse el programa si pasa por ellas) aunque es más incómodo. En SYMDEB se pueden definircon BP dirección, borrarse con BC num_breakpoint, habilitarse con BP num_breakpoint (necesario antes deemplearlos), deshabilitarse con BD num_breakpoint y listar los definidos con BL. Además, SYMDEB puedevisualizar datos en coma flotante de 32, 64 y 80 bits con el comando D (DS, DL y DT).

SYMDEB es realmente un depurador simbólico (SYMbolic DEBugger) que permite mostrarinformación adicional y depurar con mayor comodidad los programas que han sido ensamblados coninformación de depuración.

Una posibilidad interesante de DEBUG y SYMDEB es que admiten el redireccionamiento del sistemaoperativo. Ello permite, por ejemplo, crear ficheros ASCII con órdenes y después suministrárselas alprograma, como en el siguiente ejemplo: DEBUG < ORDENES.TXT. La última orden de este fichero deberáser Q (quit), de lo contrario no se devolvería el control al DOS ni se podría parar el programa (la entrada pordefecto -el teclado- no actúa). También es versátil la posibilidad de redireccionar la salida. Por ejemplo, trasDEBUG > SALIDA.TXT, se puede teclear un comando para desensamblar (U) y otro para salir (Q): en eldisco aparecerá el fichero con los datos del desensamblaje (se teclea a ciegas, lógicamente, porque la salidapor pantalla ha sido redireccionada al fichero). Por supuesto, también es posible redireccionar entrada y salidaa un tiempo: DEBUG < ORDENES.TXT > SALIDA.

6.6 - LAS FUNCIONES DEL DOS Y DE LA BIOS.

El código de la BIOS, almacenado en las memorias ROM del ordenador, constituye la primera capade software de los ordenadores compatibles. La BIOS accede directamente al hardware, liberando a losprogramas de usario de las tareas más complejas. Parte del código de la BIOS es actualizado durante elarranque del ordenador, con los ficheros que incluye el sistema operativo. El sistema operativo o DOS

Page 100: PCA, PS2 ,IBM y AT

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

propiamente dicho se instala después: el DOS no realiza ningún acceso directo al hardware, en su lugar seapoya en la BIOS, constituyendo una segunda capa de software. El DOS pone a disposición de los programasde usuario unas funciones muy evolucionadas para acceder a los discos y a los recursos del ordenador. Porencima del DOS se suele colocar habitualmente al COMMAND.COM, aunque realmente el COMMAND noconstituye capa alguna de software: es un simple programa de utilidad, como cualquier otro, ejecutado sobreel DOS y que además no pone ninguna función a disposición del sistema (al menos, documentada), su únicamisión es cargar otros programas.

FUNCIONES DE LA BIOS

Las funciones de la BIOS se invocan, desde los programas de usuario, ejecutando una interrupciónsoftware con un cierto valor inicial en los registros. La BIOS emplea un cierto rango de interrupciones, cadauna encargada de una tarea específica:

INT 10h: Servicios de Vídeo (texto y gráficos).INT 11h: Informe sobre la configuración del equipo.INT 12h: Informe sobre el tamaño de la memoria convencional.INT 13h: Servicios de disco (muy elementales: pistas, sectores, etc.).INT 14h: Comunicaciones en serie.INT 15h: Funciones casette (PC) y servicios especiales del sistema (AT).INT 16h: Servicios de teclado.INT 17h: Servicios de impresora.INT 18h: Llamar a la ROM del BASIC (sólo máquinas IBM).INT 19h: Reinicialización del sistema.INT 1Ah: Servicios horarios.INT 1Fh: Apunta a la tabla de los caracteres ASCII 128-255 (8x8 puntos).

La mayoría de las interrupciones se invocan solicitando una función determinada (que se indica enel registro AH al llamar) y se limitan a devolver un resultado en ciertos registros, realizando la tareasolicitada. En general, sólo resultan modificados los registros que devuelven algo, aunque BP es corrompidoen los servicios de vídeo de las máquinas más obsoletas.

FUNCIONES DEL DOS

El DOS emplea varias interrupciones, al igual que la BIOS; sin embargo, cuando se habla defunciones del DOS, todo el mundo sobreentiende que se trata de llamar a la INT 21h, la interrupción másimportante con diferencia.

INT 20h: Terminar programa (tal vez en desuso).INT 21h: Servicios del DOS.INT 22h: Control de finalización de programas.INT 23h: Tratamiento de Ctrl-C.INT 24h: Tratamiento de errores críticos.INT 25h: Lectura absoluta de disco (sectores lógicos).INT 26h: Escritura absoluta en disco (sectores lógicos).INT 27h: Terminar dejando residente el programa (en desuso).INT 28h: Idle (ejecutada cuando el ordenador está inactivo).INT 29h: Impresión rápida en pantalla (no tanto).INT 2Ah: Red local MS NET.INT 2Bh-2Dh: Uso interno del DOS.INT 2Eh: Procesos Batch.INT 2Fh: Interrupción Multiplex.INT 30h-31h: Compatibilidad CP/M-80.INT 32h: Reservada.

Page 101: PCA, PS2 ,IBM y AT

101EL ENSAMBLADOR EN ENTORNO DOS

Las funciones del DOS se invocan llamando a la INT 21h e indicando en el registro AH el númerode función a ejecutar. Sólo modifican los registros en que devuelven los resultados, devolviendo normalmenteel acarreo activo cuando se produce un error (con un código de error en el acumulador). Muchas funcionesde los lenguajes de programación frecuentemente se limitan a llamar al DOS.

Todos los valores mostrados a continuación son hexadecimales; el de la izquierda es el número defunción (lo que hay que cargar en AH antes de llamar); algunas funciones del DOS se dividen a su vez ensubfunciones, seleccionables mediante AL (segundo valor numérico, en los casos en que aparece). Lasfunciones marcadas con U> fueron históricamente indocumentadas, aunque Microsoft desclasificó casi todasellas a partir del MS-DOS 5.0 (en muchas secciones de este libro, escritas con anterioridad, se las referenciaaún como indocumentadas). Se indica también la versión del DOS a partir de la que están disponibles.

En general, se debe intentar emplear siempre las funciones que requieran la menor versión posibledel DOS; sin embargo, no es necesario buscar la compatibilidad con el DOS 1.0: esta versión no soportasubdirectorios, y el sistema de ficheros se basa en el horroroso método FCB. Los FCB ya no están soportadossiquiera en la ventana de compatibilidad DOS de OS/2, siendo recomendable ignorar su existencia y trabajarcon los handles, al estilo del UNIX, que consisten en unos números que identifican a los ficheros cuando sonabiertos. Existen 5 handles predefinidos permanentemente abiertos: 0 (entrada estándar -teclado-), 1 (salidaestándar -pantalla-), 2 (salida de error estándar -también pantalla-), 3 (entrada/salida por puerto serie) y 4(salida por impresora): la pantalla, el teclado, etc. pueden ser manejados como simples ficheros.

Las funciones precedidas de un asterisco son empleadas o mencionadas en este libro, y puedenconsultarse en el apéndice al efecto al final del mismo.

ENTRADA/SALIDA DE CARACTERESAH AL Versión Nombre original Traducción

01 -- DOS 1+ - READ CHARACTER FROM STANDARD INPUT, WITH ECHO . . . . . . LEER CARACTER DE LA ENTRADA ESTANDAR, CON IMPRESION*02 -- DOS 1+ - WRITE CHARACTER TO STANDARD OUTPUT . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN LA SALIDA ESTANDAR03 -- DOS 1+ - READ CHARACTER FROM STDAUX . . . . . . . . . . . . . . . . . . . . . . . . . LEER CARACTER DEL PUERTO SERIE04 -- DOS 1+ - WRITE CHARACTER TO STDAUX . . . . . . . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN EL PUERTO SERIE05 -- DOS 1+ - WRITE CHARACTER TO PRINTER . . . . . . . . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN LA IMPRESORA06 -- DOS 1+ - DIRECT CONSOLE OUTPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SALIDA DIRECTA A CONSOLA06 -- DOS 1+ - DIRECT CONSOLE INPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ENTRADA DIRECTA POR CONSOLA07 -- DOS 1+ - DIRECT CHARACTER INPUT, WITHOUT ECHO . . . . . . . . . . . . . . LECTURA DIRECTA DE CARACTER, SIN IMPRESION08 -- DOS 1+ - CHARACTER INPUT WITHOUT ECHO . . . . . . . . . . . . . . . . . . . . . LECTURA DE CARACTERES, SIN IMPRESION*09 -- DOS 1+ - WRITE STRING TO STANDARD OUTPUT . . . . . . . . . . . . . . . . . . . . ESCRIBIR CADENA EN LA SALIDA ESTANDAR*0A -- DOS 1+ - BUFFERED INPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ENTRADA DESDE TECLADO POR BUFFER0B -- DOS 1+ - GET STDIN STATUS . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER ESTADO DE LA ENTRADA ESTANDAR0C -- DOS 1+ - FLUSH BUFFER AND READ STANDARD INPUT . . . . . . . . . . . . . LIMPIAR BUFFER Y LEER DE LA ENTRADA ESTANDAR

GESTION DE FICHEROS

0F -- DOS 1+ - OPEN FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . APERTURA DE FICHERO EMPLEANDO FCB10 -- DOS 1+ - CLOSE FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CERRAR FICHERO EMPLEANDO FCB11 -- DOS 1+ - FIND FIRST MATCHING FILE USING FCB . . . . . . . . . . . . . . . . . . . BUSCAR PRIMER FICHERO EMPLEANDO FCB12 -- DOS 1+ - FIND NEXT MATCHING FILE USING FCB . . . . . . . . . . . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO FCB13 -- DOS 1+ - DELETE FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR FICHERO EMPLEANDO FCB16 -- DOS 1+ - CREATE OR TRUNCATE FILE USING FCB . . . . . . . . . . . . . . . . . . . . CREAR/TRUNCAR FICHERO EMPLEANDO FCB17 -- DOS 1+ - RENAME FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . RENOMBRAR FICHERO EMPLEANDO FCB23 -- DOS 1+ - GET FILE SIZE FOR FCB . . . . . . . . . . . . . . . . . . . . . . . . OBTENER TAMAÑO DE FICHERO EMPLEANDO FCB29 -- DOS 1+ - PARSE FILENAME INTO FCB . . . . . . . . . . . . . . . . . . . . EXPANDIR EL NOMBRE DEL FICHERO EMPLEANDO FCB

*3C -- DOS 2+ - "CREAT" - CREATE OR TRUNCATE FILE . . . . . . . . . . . . . . . . . . CREAR/TRUNCAR FICHERO EMPLEANDO HANDLE*3D -- DOS 2+ - "OPEN" - OPEN EXISTING FILE . . . . . . . . . . . . . . . . . . . . ABRIR FICHERO EXISTENTE EMPLEANDO HANDLE*3E -- DOS 2+ - "CLOSE" - CLOSE FILE . . . . . . . . . . . . . . . . . . . . . . . CERRAR FICHERO EXISTENTE EMPLEANDO HANDLE41 -- DOS 2+ - "UNLINK" - DELETE FILE . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR FICHERO EMPLEANDO HANDLE43 00 DOS 2+ - GET FILE ATTRIBUTES . . . . . . . . . . . . . . . . . . . . . OBTENER ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE43 01 DOS 2+ - "CHMOD" - SET FILE ATTRIBUTES . . . . . . . . . . . . . . . MODIFICAR ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE45 -- DOS 2+ - "DUP" - DUPLICATE FILE HANDLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DUPLICAR EL HANDLE46 -- DOS 2+ - "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE . . . . . . . . . . . . . . . . . . REDIRECCIONAR EL HANDLE4E -- DOS 2+ - "FINDFIRST" - FIND FIRST MATCHING FILE . . . . . . . . . . . . . . . BUSCAR PRIMER FICHERO EMPLEANDO HANDLE4F -- DOS 2+ - "FINDNEXT" - FIND NEXT MATCHING FILE . . . . . . . . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO HANDLE56 -- DOS 2+ - "RENAME" - RENAME FILE . . . . . . . . . . . . . . . . . . . . . . . . . RENOMBRAR FICHERO EMPLEANDO HANDLE57 00 DOS 2+ - GET FILE’S DATE AND TIME . . . . . . . . . . . . . . . . . OBTENER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE57 01 DOS 2+ - SET FILE’S DATE AND TIME . . . . . . . . . . . . . . . ESTABLECER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE5A -- DOS 3+ - CREATE TEMPORARY FILE . . . . . . . . . . . . . . . . . . . . . . . . CREAR FICHERO TEMPORAL EMPLEANDO HANDLE5B -- DOS 3+ - CREATE NEW FILE . . . . . . . . . . . . . . . CREAR NUEVO FICHERO SIN MACHACARLO SI EXISTIA EMPLEANDO HANDLE67 -- DOS 3.3+ - SET HANDLE COUNT . . . . . . . . . . . . . . . ESTABLECER MAXIMO NUMERO DE HANDLES PARA LA TAREA EN CURSO68 -- DOS 3.3+ - "FFLUSH" - COMMIT FILE . . . . . . . . . . . . . . . . . . . . . . . . . . VOLCAR BUFFERS INTERNOS A DISCO

OPERACIONES SOBRE FICHEROS

14 -- DOS 1+ - SEQUENTIAL READ FROM FCB FILE . . . . . . . . . . . . . . . . . . LECTURA SECUENCIAL DE FICHERO EMPLEANDO FCB15 -- DOS 1+ - SEQUENTIAL WRITE TO FCB FILE . . . . . . . . . . . . . . . . . ESCRITURA SECUENCIAL EN FICHERO EMPLEANDO FCB*1A -- DOS 1+ - SET DISK TRANSFER AREA ADDRESS . . . . . . . . . . . . . . . . . ESTABLECER EL AREA DE TRANSFERENCIA A DISCO21 -- DOS 1+ - READ RANDOM RECORD FROM FCB FILE . . . . . . . . . . . . . . . . LECTURA ALEATORIA DE REGISTRO EMPLEANDO FCB22 -- DOS 1+ - WRITE RANDOM RECORD TO FCB FILE . . . . . . . . . . . . . . . . ESCRITURA ALEATORIA DE REGISTRO EMPLEANDO FCB24 -- DOS 1+ - SET RANDOM RECORD NUMBER FOR FCB . . . . . . . . . . . . . PASAR DE E/S SECUENCIAL A ALEATORIA EMPLEANDO FCB27 -- DOS 1+ - RANDOM BLOCK READ FROM FCB FILE . . . . . . . . . . . . . . . . . . LECTURA ALEATORIA DE BLOQUE EMPLEANDO FCB28 -- DOS 1+ - RANDOM BLOCK WRITE TO FCB FILE . . . . . . . . . . . . . . . . . ESCRITURA ALEATORIA DE BLOQUE EMPLEANDO FCB*2F -- DOS 2+ - GET DISK TRANSFER AREA ADDRESS . . . . . . . . . . . OBTENER LA DIRECCION DEL AREA DE TRANSFERENCIA A DISCO*3F -- DOS 2+ - "READ" - READ FROM FILE OR DEVICE . . . . . . . . . . . . . . . . . . . . LEER DE UN FICHERO EMPLEANDO HANDLE*40 -- DOS 2+ - "WRITE" - WRITE TO FILE OR DEVICE . . . . . . . . . . . . . . . . . . ESCRIBIR EN UN FICHERO EMPLEANDO HANDLE

Page 102: PCA, PS2 ,IBM y AT

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

42 -- DOS 2+ - "LSEEK" - SET CURRENT FILE POSITION . . . . . . . . MOVER EL PUNTERO RELATIVO EN EL FICHERO EMPLEANDO HANDLE5C -- DOS 3+ - "FLOCK" - RECORD LOCKING . . . . . . . . . . . . . BLOQUEAR/DESBLOQUER UNA ZONA DEL FICHERO EMPLEANDO HANDLE

OPERACIONES CON DIRECTORIOS

39 -- DOS 2+ - "MKDIR" - CREATE SUBDIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CREAR SUBDIRECTORIO3A -- DOS 2+ - "RMDIR" - REMOVE SUBDIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR SUBDIRECTORIO3B -- DOS 2+ - "CHDIR" - SET CURRENT DIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . CAMBIAR EL DIRECTORIO ACTIVO47 -- DOS 2+ - "CWD" - GET CURRENT DIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL DIRECTORIO ACTUAL

MANEJO DE DISCO

0D -- DOS 1+ - DISK RESET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . REINICIALIZAR EL DISCO0E -- DOS 1+ - SELECT DEFAULT DRIVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER UNIDAD POR DEFECTO19 -- DOS 1+ - GET CURRENT DEFAULT DRIVE . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA UNIDAD ACTUAL POR DEFECTO1B -- DOS 1+ - GET ALLOCATION INFORMATION FOR DEFAULT DRIVE . . . . OBTENER INFORMACION DE ESPACIO EN EL DISCO POR DEFECTO1C -- DOS 1+ - GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE . . . . . . OBTENER INFORMACION DE ESPACIO EN EL DISCO INDICADO2E -- DOS 1+ - SET VERIFY FLAG . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER EL BANDERIN DE VERIFICACION*36 -- DOS 2+ - GET FREE DISK SPACE . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL ESPACIO LIBRE EN DISCO54 -- DOS 2+ - GET VERIFY FLAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL BANDERIN DE VERIFICACION

CONTROL DE PROCESOS

00 -- DOS 1+ - TERMINATE PROGRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TERMINAR PROGRAMA26 -- DOS 1+ - CREATE NEW PROGRAM SEGMENT PREFIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CREAR PSP*31 -- DOS 2+ - TERMINATE AND STAY RESIDENT . . . . . . . . . . . . . . . . . . . . . . . . . TERMINAR Y PERMANECER RESIDENTE*4B -- DOS 2+ - "EXEC" - LOAD AND/OR EXECUTE PROGRAM . . . . . . . . . . . . . . . . . . . . . CARGAR Y/O EJECUTAR PROGRAMA*4C -- DOS 2+ - "EXIT" - TERMINATE WITH RETURN CODE . . . . . . . . . . . . . . . . . TERMINAR PROGRAMA CON CODIGO DE RETORNO4D -- DOS 2+ - GET RETURN CODE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER CODIGO DE RETORNO*50 -- DOS 2+ internal - SET CURRENT PROCESS ID (SET PSP ADDRESS) . . . . . . . . . . . . ESTABLECER DIRECCION DEL PSP ACTUAL*51 -- DOS 2+ internal - GET CURRENT PROCESS ID (GET PSP ADDRESS) . . . . . . . . . . . . . OBTENER DIRECCION DEL PSP ACTUAL*62 -- DOS 3+ - GET CURRENT PSP ADDRESS . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER DIRECCION DEL PSP ACTUAL

GESTION DE MEMORIA

*48 -- DOS 2+ - ALLOCATE MEMORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ASIGNAR MEMORIA*49 -- DOS 2+ - FREE MEMORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . LIBERAR MEMORIA*4A -- DOS 2+ - RESIZE MEMORY BLOCK . . . . . . . . . . . . . . . . . . MODIFICAR EL TAMAÑO DE UN BLOQUE DE MEMORIA ASIGNADA*58 -- DOS 3+ - GET OR SET MEMORY ALLOCATION STRATEGY . . . . . . . OBTENER/ESTABLECER LA ESTRATEGIA DE ASIGNACION DE MEMORIA*58 -- DOS 5.0 - GET OR SET UMB LINK STATE . . . . . . . . . OBTENER/ESTABLECER EL ESTADO DE CONEXION DE LA MEMORIA SUPERIOR

CONTROL DE FECHA Y HORA

*2A -- DOS 1+ - GET SYSTEM DATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA FECHA DEL SISTEMA2B -- DOS 1+ - SET SYSTEM DATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER LA FECHA DEL SISTEMA*2C -- DOS 1+ - GET SYSTEM TIME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA HORA DEL SISTEMA2D -- DOS 1+ - SET SYSTEM TIME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER LA HORA DEL SISTEMA

FUNCIONES MISCELANEAS

18 -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M1D -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M1E -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M1F -- DOS 1+ - GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE . . . . . . . . . . . . . OBTENER EL DPB DE LA UNIDAD POR DEFECTO20 -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M*25 -- DOS 1+ - SET INTERRUPT VECTOR . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER VECTOR DE INTERRUPCION*30 -- DOS 2+ - GET DOS VERSION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER VERSION DEL DOS32 -- DOS 2+ - GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE . . . . . . . . . . . OBTENER EL DPB DE LA UNIDAD INDICADA33 -- DOS 2+ - EXTENDED BREAK CHECKING . . . . . . . . . . . . . . . . . . . . CONTROLAR EL NIVEL DE DETECCION DE CTRL-BREAK33 02 DOS 3.x+ internal - GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE . . INDICAR/OBTENER NIVEL DETECCION CTRL-BREAK33 05 DOS 4+ - GET BOOT DRIVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DETERMINAR UNIDAD DE ARRANQUE33 06 DOS 5.0 - GET TRUE VERSION NUMBER . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER VERSION REAL DEL DOS*34 -- DOS 2+ - GET ADDRESS OF INDOS FLAG . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA DIRECCION DE INDOS*35 -- DOS 2+ - GET INTERRUPT VECTOR . . . . . . . . . . . . . . . . . . . OBTENER LA DIRECCION DE UN VECTOR DE INTERRUPCION37 00 DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER . . . . . . . . . . . . . . . . OBTENER EL CARACTER INDICADOR DE PARAMETROS37 01 DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER . . . . . . . . . . . . . . ESTABLECER EL CARACTER INDICADOR DE PARAMETROS37 -- DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE . . . . . . . . . . CONTROLAR EL USO DEL PREFIJO \DEV\*38 -- DOS 2+ - GET COUNTRY-SPECIFIC INFORMATION . . . . . . . . . . . . . . . . . . . OBTENER INFORMACION RELATIVA AL PAIS38 -- DOS 3+ - SET COUNTRY CODE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER EL CODIGO DEL PAIS44 00 DOS 2+ - IOCTL - GET DEVICE INFORMATION . . . . . . . . . . . . . . CONTROL E/S: OBTENER INFORMACION DEL DISPOSITIVO44 01 DOS 2+ - IOCTL - SET DEVICE INFORMATION . . . . . . . . . . . . . CONTROL E/S: ESTABLECER INFORMACION DEL DISPOSITIVO44 02 DOS 2+ - IOCTL - READ FROM CHARACTER DEVICE CONTROL CHANNEL . . . . . CONTROL E/S: LEER DE CANAL CONTROL DISP. CARAC.44 03 DOS 2+ - IOCTL - WRITE TO CHARACTER DEVICE CONTROL CHANNEL . . . . CONTROL E/S: ESCRIBIR EN CANAL CONTROL DISP. CARAC.44 04 DOS 2+ - IOCTL - READ FROM BLOCK DEVICE CONTROL CHANNEL . . . . . . . CONTROL E/S: LEER DE CANAL CONTROL DISP. BLOQUE44 05 DOS 2+ - IOCTL - WRITE TO BLOCK DEVICE CONTROL CHANNEL . . . . . . CONTROL E/S: ESCRIBIR EN CANAL CONTROL DISP. BLOQUE44 06 DOS 2+ - IOCTL - GET INPUT STATUS . . . . . . . . . . . . . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA ENTRADA44 07 DOS 2+ - IOCTL - GET OUTPUT STATUS . . . . . . . . . . . . . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA SALIDA44 08 DOS 3.0+ - IOCTL - CHECK IF BLOCK DEVICE REMOVABLE . . . . . CONTROL E/S: COMPROBAR SI EL DISP. DE BLOQUE ES REMOVIBLE44 09 DOS 3.1+ - IOCTL - CHECK IF BLOCK DEVICE REMOTE . . . . . . . CONTROL E/S: COMPROBAR SI EL DISP. DE BLOQUE ES REMOTO44 0A DOS 3.1+ - IOCTL - CHECK IF HANDLE IS REMOTE . . . . . . . . . . . . . . CONTROL E/S: COMPROBAR SI UN HANDLE ES REMOTO44 0B DOS 3.1+ - IOCTL - SET SHARING RETRY COUNT . . . . . CONTROL E/S: DEFINIR NUMERO DE REINTENTOS EN MODO DE COMPARTICION44 0C DOS 3.2+ - IOCTL - GENERIC CHARACTER DEVICE REQUEST . . . . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE CARACTERES44 0D DOS 3.2+ - IOCTL - GENERIC BLOCK DEVICE REQUEST . . . . . . . . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE BLOQUE44 0E DOS 3.2+ - IOCTL - GET LOGICAL DRIVE MAP . . . . . . . . . . . . . . . . . . . OBTENER ASIGNACION DE UNIDADES LOGICAS44 0F DOS 3.2+ - IOCTL - SET LOGICAL DRIVE MAP . . . . . . . . . . . . . . . . . . . DEFINIR ASIGNACION DE UNIDADES LOGICAS*52 -- U> DOS 2+ internal - "SYSVARS" - GET LIST OF LISTS . . . . . . . . . . . OBTENER EL LISTADO DE LAS LISTAS DEL SISTEMA53 -- DOS 2+ internal - TRANSLATE BIOS PARAMETER BLOCK TO DRIVE PARAM BLOCK . . . . . . . . . . . . . . TRADUCIR BPB A DPB55 -- DOS 2+ internal - CREATE CHILD PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CREAR PSP HIJO*59 -- DOS 3+ - GET EXTENDED ERROR INFORMATION . . . . . . . . . . . . . . . . . . OBTENER INFORMACION EXTENDIDA DE ERRORES*5D 06 U> DOS 3.0+ internal - GET ADDRESS OF DOS SWAPPABLE DATA AREA . . . OBTENER DIRECCION DEL AREA INTERCAMBIABLE DEL DOS*5D 0A DOS 3.1+ - SET EXTENDED ERROR INFORMATION . . . . . . . . . . . . . . . . ESTABLECER INFORMACION EXTENDIDA DE ERRORES*5D 0B U> DOS 4.x only internal - GET DOS SWAPPABLE DATA AREAS . . . . . . . . . . . . OBTENER AREAS INTERCAMBIABLES DEL DOS60 -- DOS 3.0+ - CANONICALIZE FILENAME OR PATH . . . . . EXPANDIR NOMBRE DE FICHERO A ESPECIFICACION COMPLETA DE DIRECTORIOS61 -- DOS 3+ - UNUSED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NO USADA AUN64 -- DOS 3.2+ internal - SET DEVICE DRIVER LOOKAHEAD FLAG . . . . ESTABLECER BANDERIN DE LECTURA ADELANTADA DE DISPOSITIVO65 -- DOS 3.3+ - GET EXTENDED COUNTRY INFORMATION . . . . . . . . . . . . . . . . . OBTENER INFORMACION EXTENDIDA DEL PAIS65 23 U> DOS 4+ internal - DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONS . . . . DETERMINAR SI UNA LETRA INDICA SI O NO65 -- U> DOS 4+ internal - COUNTRY-DEPENDENT FILENAME CAPITALIZATION . . . . MAYUSCULIZACION DE NOMBRE DEPENDIENTE DEL PAIS66 01 DOS 3.3+ - GET GLOBAL CODE PAGE TABLE . . . . . . . . . . . . . . . . . . . . . . OBTENER LA PAGINA DE CODIGOS GLOBAL66 02 DOS 3.3+ - SET GLOBAL CODE PAGE TABLE . . . . . . . . . . . . . . . . . . . . ESTABLECER LA PAGINA DE CODIGOS GLOBAL69 -- U> DOS 4+ internal - GET/SET DISK SERIAL NUMBER . . . . . . . . . . OBTENER/ESTABLECER EL NUMERO DE SERIE DE UN DISCO6B -- U> DOS 5.0 - NULL FUNCTION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . FUNCION NULA6C 00 DOS 4+ - EXTENDED OPEN/CREATE . . . . . . . . . . . . . . . APERTURA/CREACION DE FICHEROS EXTENDIDA EMPLEANDO HANDLE

Page 103: PCA, PS2 ,IBM y AT

103ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

Capítulo VII: ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

7.1. - LAS INTERRUPCIONES

Son señales enviadas a la CPU para que termine la ejecución de la instrucción en curso y atienda unapetición determinada, continuando más tarde con lo que estaba haciendo.

Cada interrupción lleva asociado un número que identifica el tipo de servicio a realizar. A partir dedicho número se calcula la dirección de la rutina que lo atiende y cuando se retorna se continúa con lainstrucción siguiente a la que se estaba ejecutando cuando se produjo la interrupción. La forma de calcularla dirección de la rutina es multiplicar por cuatro el valor de la interrupción para obtener un desplazamientoy, sobre el segmento 0, con dicho desplazamiento, se leen dos palabras: la primera es el desplazamiento yla segunda el segmento de la rutina deseada. Por tanto, en el primer kilobyte de memoria física del sistema,existe espacio suficiente para los 256 vectores de interrupción disponibles.

Hay tres tipos básicos de interrupciones:

- Interrupciones internas o excepciones: Las genera la propia CPU cuando se produce una situaciónanormal o cuando llega el caso. Por desgracia, IBM se saltó olímpicamente la especificación de Intelque reserva las interrupciones 0-31 para el procesador.

INT 0: error de división, generada automáticamente cuando el cociente no cabe en elregistro o el divisor es cero. Sólo puede ser generada mediante DIV o IDIV. Hay una sutildiferencia de comportamiento ante esta interrupción según el tipo de procesador: el8088/8086 y los NEC V20 y V30 almacenan en la pila, como cabría esperar, la dirección dela instrucción que sigue a la que causó la excepción. Sin embargo, el 286 y superioresalmacenan la dirección del DIV o IDIV que causa la excepción.

INT 1: paso a paso, se produce tras cada instrucción cuando el procesador está en modotraza (utilizada en depuración de programas).

INT 2: interrupción no enmascarable, tiene prioridad absoluta y se produce incluso aunqueestén inhibidas las interrupciones (con CLI) para indicar un hecho muy urgente (fallo en laalimentación o error de paridad en la memoria).

INT 3: utilizada para poner puntos de ruptura en la depuración de programas, debido a quees una instrucción de un solo byte muy cómoda de utilizar.

INT 4: desbordamiento, se dispara cuando se ejecuta un INTO y había desbordamiento.

INT 5: rango excedido en la instrucción BOUND (sólo 286 y superiores). Ha sidoincorrectamente empleada por IBM para volcar la pantalla por impresora.

INT 6: código de operación inválido (sólo a partir del 286). Se produce al ejecutar unainstrucción indefinida, en la pila se almacena el CS:IP de la instrucción ilegal.

INT 7: dispositivo no disponible (sólo a partir del 286).

Page 104: PCA, PS2 ,IBM y AT

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

- Interrupciones hardware: Son las generadas por la circuitería del ordenador en respuesta a algúnevento. Las más importantes son:

INT 8: Se produce con una frecuencia periódica determinada por el canal 0 del chip temporizador8253/8254 (en la práctica, unas 18,2 veces por segundo). Como desde esta interrupción se invoca asu vez a INT 1Ch -porque así lo dispuso IBM-, es posible ligar un proceso a INT 1Ch para que seejecute periódicamente.

INT 9: generada al pulsar o soltar una tecla.

INT 0Ah, 0Bh, 0Ch, 0Dh, 0Eh, 0Fh: Puertos serie, impresora y controladores de disquete.

INT 70h, 71h, 72h, 73h, 74h, 75h, 76h, 77h: Generadas en los AT y máquinas superiores por elsegundo chip controlador de interrupciones.

- Interrupciones software: Producidas por el propio programa (instrucción INT) para invocar ciertassubrutinas. La BIOS y el DOS utilizan algunas interrupciones a las que se puede llamar condeterminados valores en los registros para que realicen ciertos servicios. También existe alguna queotra interrupción que se limita simplemente a apuntar a modo de puntero a una tabla de datos.

Los vectores de interrupción pueden ser desviados hacia un programa propio que, además, podríaquedar residente en memoria. Si se reprograma por completo una interrupción y ésta es de tipo hardware, hayque realizar una serie de tareas adicionales, como enviar una señal fin de interrupción hardware al chipcontrolador de interrupciones. Si se trata además de la interrupción del teclado del PC o XT, hay que enviaruna señal de reconocimiento al mismo ... en resumen: conviene documentarse debidamente antes de intentarhacer nada. 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, 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. De hecho, algunos programas de DOSfuncionan también bajo OS/2 si han sido diseñados con esta técnica. Basta con llamar al servicio 25hdel DOS (INT 21h) y decirle qué interrupción hay que desviar y a dónde:

MOV AH,25h ; servicio para cambiar vectorMOV AL,vector ; entre 0 y 255LEA DX,rutina ; DS:DX nueva rutina de gestiónINT 21h ; 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. Es además mucho más incómodo ylargo, pero muy usado por programadores despistados:

MOV BL,vector*4 ; vector a cambiar en BLMOV BH,0 ; ahora en BXMOV AX,0PUSH DS ; preservar DSMOV DS,AX ; apuntar al segmento 0000LEA DX,rutina ; CS:DX nueva rutina de gestiónCLI ; evitar posible interrupciónMOV [BX],DX ; cambiar vector (offset)MOV [BX+2],CS ; cambiar vector (segmento)STI ; permitir interrupcionesPOP DS ; restaurar DS

3) El «método correcto» es similar al «psé», consiste en cambiar el vector «de un tirón» (cambiara la vez segmento y offset con un REP MOVS) con objeto de evitar una posible interrupción noenmascarable que se pueda producir en ese momento crítico en que ya se ha cambiado el offset perotodavía no el segmento (CLI no inhibe la interrupción no enmascarable). Este sistema es todavía algomás engorroso, pero es el mejor y es el que utiliza el DOS en el método (1).

Page 105: PCA, PS2 ,IBM y AT

105ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

4) El «método incorrecto» es muy usado por los malos programadores. Es similar al «psé» sólo quesin inhibir las interrupciones mientras se cambia el vector, con el riesgo de que se produzca unainterrupción cuando se ha cambiado sólo medio vector. Los peores programadores lo emplean sobretodo para cambiar INT 8 ó INT 1Ch, que se producen con una cadencia de 18,2 veces por segundo.

7.2. - LA MEMORIA. LOS PUERTOS DE ENTRADA Y SALIDA.

Dentro del megabyte que puede direccionar un 8086, los primeros 1024 bytes están ocupados por latabla de vectores de interrupción. A continuación existen 256 bytes de datos de la BIOS y otros tantos parael BASIC y el DOS. De 600h a 9FFFFh está la memoria del usuario (casi 640 Kb). En A0000h comienzael área de expansión de memoria de pantalla (EGA y VGA). En B0000h comienzan otros 64 Kb de losadaptadores de texto MDA y gráficos (CGA). De C0000h a EFFFFh aparecen las extensiones de la ROM(añadidas por las tarjetas gráficas, discos duros, etc.) y en F0000h suele estar colocada la BIOS del sistema(a veces tan sólo 8 Kb a partir de FE000h). Los modernos sistemas operativos (DR-DOS y MS-DOS 5.0 yposteriores) permiten colocar RAM en huecos «vacíos» por encima de los 640 Kb en las máquinas 386 (yalgún 286 con cierto juego especial de chips). Esta zona de memoria sirve para cargar programas residentes.De hecho, el propio sistema operativo se sitúa (en 286 y superiores) en los primeros 64 Kb de la memoriaextendida (HMA) que pueden ser direccionados desde el DOS, dejando más memoria libre al usuario dentrode los primeros 640 Kb. Para más información, puede consultarse el apéndice I y el capítulo 8.

Los puertos de entrada y salida (E/S) permiten a la CPU comunicarse con los periféricos. Los 80x86utilizan los buses de direcciones y datos ordinarios para acceder a los periféricos, pero habilitando una líneaque distinga el acceso a los mismos de un acceso convencional a la memoria (si no existieran los puertos deentrada y salida, los periféricos deberían interceptar el acceso a la memoria y estar colocados en algún áreade la misma). Para acceder a los puertos E/S se emplean las instrucciones IN y OUT. Véase el apéndice IV.

7.3.- LA PANTALLA EN MODO TEXTO.

Cuando la pantalla está en modo de texto, si está activo un adaptador de vídeo monocromo, ocupa4 Kb a partir del segmento 0B000h. Con un adaptador de color, son 16 Kb a partir del segmento 0B800h.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 valorentre 0 y 4 para un adaptador de color. Los modos 0 y 1 son de 40 columnas y el 2 y 3 de 80. Los modos0 y 2 son de «color suprimido», aunque en muchos monitores salen también en color (y no en tonos de gris).Cada carácter en la pantalla (empezando por arriba a la izquierda) ocupa dos bytes consecutivos: en elprimero se almacena el código ASCII del carácter a visualizar y en el segundo los atributos de color.Obviamente, en un modo de 80x25 se utilizan 4000 bytes (los 96 restantes hasta los 4096 de los 4 Kb sedesprecian). En los adaptadores de color, como hay 16 Kb de memoria para texto, se pueden definir entre4 páginas de texto (80 columnas) y 8 (40 columnas). La página activa puede consultarse también llamandoa la BIOS, con objeto de conocer el segmento real donde empieza la pantalla (B800 más un cierto offset).En el 97,5% de los casos sólo se emplea la página 0, lo que no quiere decir que los buenos programas debanasumirla como la única posible. La BIOS utiliza la interrupción 10h para comunicarse con el sistemaoperativo y los programas de usuario.

El byte de atributos permite definir el color de fondo de los caracteres (0-7) con los bits 4-6, el dela tinta (0-15) con los bits 0-3 y el parpadeo con el bit 7. La función de este último bit puede ser redefinidapara indicar el brillo de los caracteres de fondo (existiendo entonces también 16 colores de fondo), aunqueen CGA es preciso para ello un acceso directo al hardware. En el adaptador monocromo, y para la tinta, elcolor 0 es el negro; el 1 es «subrayado normal», del 1 al 7 son colores «normales»; el 8 es negro, el 9 es«subrayado brillante» y del 10 al 15 son «brillantes». Para el papel todos los colores son negros menos el7 (blanco), no obstante para escribir en vídeo inverso es necesario no sólo papel 7 sino además tinta 0 (almenos, en los auténticos adaptadores monocromos). El bit 7 siempre provoca parpadeo en este adaptador. En

Page 106: PCA, PS2 ,IBM y AT

106 EL UNIVERSO DIGITAL DEL IBM PC, 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 VGAempleando otros métodos). Tabla de colores:

0 - Negro 4 - Rojo 8 - Gris 12 - Rojo claro1 - Azul 5 - Magenta 9 - Azul claro 13 - Magenta claro2 - Verde 6 - Marrón 10 - Verde claro 14 - Amarillo3 - Cian 7 - Blanco 11 - Cian claro 15 - Blanco brillante

Conviene tener cuidado con la tinta azul (1 y 9) ya que, en estos colores, los adaptadoresmonocromos subrayan -lo que puede ser un efecto indeseable-. Cuando se llama al DOS para imprimir, ésteinvoca a su vez a la BIOS, por lo que la escritura puede ser acelerada llamando directamente a este último,que además permite escribir en color. De todas maneras, lo mejor en programas de calidad es escribirdirectamente sobre la memoria de pantalla para obtener una velocidad máxima, aunque con ciertasprecauciones -para convivir mejor con entornos pseudo-multitarea y CGA’s con nieve-.

Las pantallas de 132 columnas no son estándar y varían de unas tarjetas gráficas a otras, por lo queno las trataremos. Lo que sí se puede hacer -con cualquier EGA y VGA- es llamar a la BIOS para que cargueel juego de caracteres 8x8, lo que provoca un aumento del número de líneas a 43 (EGA) o 50 (VGA), asícomo un lógico aumento de la memoria de vídeo requerida (que como siempre, empieza en 0B800h).

En las variables de la BIOS (apéndice III) los bytes 49h-66h están destinados a controlar la pantalla;su consulta puede ser interesante, como demostrará este ejemplo: el siguiente programa comprueba el tipode pantalla, para determinar su segmento, llamando a la BIOS (véase el apéndice de las funciones del DOSy de la BIOS). Si no es una pantalla de texto estándar no realiza nada; en caso contrario la recorre y conviertetodos sus caracteres a mayúsculas, sin alterar el color:

mays SEGMENTASSUME CS:mays, DS:maysORG 100h ; programa .COM ordinario

inicio:MOV AH,15 ; función para obtener modo de vídeoINT 10h ; llamar a la BIOSMOV BX,0B000h ; segmento de pantalla monocromaMOV CX,2000 ; tamaño (caracteres) de la pantallaCMP AL,7 ; ¿es realmente modo monocromo?JE datos_ok ; en efectoMOV BX,0B800h ; segmento de pantalla de colorCMP AL,3 ; ¿es modo de texto de 80 columnas?JE pant_color ; en efectoCMP AL,2 ; ¿es modo de texto de 80 columnas?JE pant_color ; en efectoMOV CX,1000 ; tamaño (caract.) pantalla 40 col.CMP AL,1 ; ¿es modo texto de 40 columnas?JBE pant_color ; así esMOV AL,1 ; pantalla gráfica o desconocida:JMP final ; fin de programa (errorlevel=1)

pant_color: MOV AX,40h ; considerar página activa<>0MOV DS,AX ; DS = 40h (variables de la BIOS)MOV AX,DS:[4Eh] ; desplazamiento de la página activa

SHR AX,1 ; desplazamiento / 2SHR AX,1 ; desplazamiento / 4SHR AX,1 ; desplazamiento / 8SHR AX,1 ; desplazamiento / 16 (párrafos)ADD BX,AX ; segmento de vídeo efectivo

datos_ok: MOV DS,BX ; DS = segmento de pantallaXOR BX,BX ; BX = 0 (primer carácter)

otra_letra: CMP BYTE PTR [BX],’a’; ¿código ASCII menor que ’a’?JB no_minuscula ; luego no puede ser minúsculaCMP BYTE PTR [BX],’z’; ¿código ASCII mayor de ’z’?JA no_minuscula ; luego no puede ser minúsculaAND BYTE PTR [BX],0DFh ; poner en mayúsculas

no_minuscula: ADD BX,2 ; apuntar siguiente carácterLOOP otra_letra ; repetir con los CX caracteres

MOV AL,0 ; fin programa (errorlevel=0)final: MOV AH,4Ch

INT 21h

mays ENDSEND inicio

7.4 - LA PANTALLA EN MODO GRÁFICO.

7.4.1. - MODOS GRÁFICOS.

Dada la inmensidad de estándares gráficos existentes para los ordenadores compatibles, quesucedieron al primer adaptador que sólo soportaba texto (MDA), y que de hecho llenan varias estanterías enlas librerías, sólo se tratará de una manera general el tema. Se considerarán los estándares más comunes, conalgunos ejemplos de programación de la pantalla gráfica CGA con la BIOS y programando la VGAdirectamente para obtener la velocidad y potencia del ensamblador. Las tarjetas gráficas tradicionalesadministran normalmente entre 16 Kb y 1 Mb de memoria de vídeo, en el segmento 0B800h lasCGA/Hércules y en 0A000h las VGA. En los modos de vídeo que precisan más de 64 Kb se recurre atécnicas especiales, tales como planos de bits para los diferentes colores, o bien dividir la pantalla enpequeños fragmentos que se seleccionan en un puerto E/S. Las tarjetas EGA y posteriores vienenacompañadas de una extensión ROM que parchea la BIOS normal del sistema para añadir soporte al nuevosistema de vídeo. A continuación se listan los principales modos gráficos disponibles en MDA, CGA, EGAy VGA, así como en las SuperVGA Paradise, Trident y Genoa. No se consideran las peculiaridades del PCJr.

Page 107: PCA, PS2 ,IBM y AT

107ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

Modo Texto Resolución Colores Segmento Tarjeta

04h 40x25 320x200 4 B800 CGA, EGA, MCGA, VGA05h 40x25 320x200 4 grises B800 CGA, EGA05h 40x25 320x200 4 B800 CGA, VGA06h 80x25 640x200 2 B800 CGA, EGA, MCGA, VGA0Dh 40x25 320x200 16 A000 EGA, VGA0Eh 80x25 640x200 16 A000 EGA, VGA0Fh 80x25 640x350 2 A000 EGA, VGA10h 80x25 640x350 4 A000 EGA con 64K10h 80x25 640x350 16 A000 EGA con 256K, VGA11h 80x30 640x480 2 A000 VGA, MCGA12h 80x30 640x480 16/256k A000 VGA13h 40x25 320x200 256/256k A000 VGA, MCGA

27h 720x512 16 Genoa29h 800x600 16 A000 Genoa2Dh 640x350 256/256k A000 Genoa2Eh 640x480 256/256k A000 Genoa2Fh 720x512 256 Genoa30h 800x600 256/256k A000 Genoa37h 1024x768 16 A000 Genoa58h 100x75 800x600 16/256k A000 Paradise VGA59h 100x75 800x600 2 A000 Paradise VGA5Bh 100x75 800x600 16/256k A000 Trident TVGA 8800, 89005Bh 640x350 256 Genoa 64005Ch 80x25 640x400 256 A000 Trident TVGA 88005Ch 640x480 256 Genoa 64005Dh 80x30 640x480 256 A000 Trident TVGA 8800 (512K)5Eh 80x25 640x400 256 Paradise VGA5Eh 800x600 256 Trident 89005Eh 800x600 256 Genoa 64005Fh 80x30 640x480 256 Paradise VGA (512K)5Fh 1024x768 16/256k A000 Trident TVGA 8800 (512K)5Fh 1024x768 16 Genoa 640061h 96x64 768x1024 16/256k A000 Trident TVGA 8800 (512K)62h 1024x768 256 Trident TVGA 89006Ah 800x600 16 Genoa 64007Ch 512x512 16 Genoa7Dh 512x512 256 Genoa

Las tarjetas gráficas son muy distintas entre sí a nivel de hardware, por la manera en que gestionanla memoria de vídeo. Las tarjetas SuperVGA complican aún más el panorama. En general, un programa quedesee aprovechar al máximo el ordenador deberá apoyarse en drivers o subprogramas específicos, uno paracada tarjeta de vídeo del mercado. Esto es así porque aunque la BIOS del sistema (o el de la tarjeta) soportauna serie de funciones estándar para trabajar con gráficos, existen bastantes problemas. En primer lugar, suineficiente diseño lo hace extremadamente lento para casi cualquier aplicación seria. Bastaría con que lasfunciones que implementa la BIOS (pintar y leer puntos de la pantalla) fueran rápidas, ¡sólo eso!, para lo quetan sólo hace falta una rutina específica para cada modo de pantalla, que la BIOS debería habilitar nada máscambiar de modo; casi todas las demás operaciones realizadas sobre la pantalla se apoyan en esas dos y ellono requeriría software adicional para mantener la compatibilidad entre tarjetas. Sin embargo, los programascomerciales no tienen más remedio que incluir sus propias rutinas rápidas para trazar puntos y líneas endrivers apropiados (y de paso añaden alguna función más compleja). Además, y por desgracia, no existe NIUNA SOLA función oficial en la BIOS que informe a los programas que se ejecutan de cosas tan elementalescomo los modos gráficos disponibles (con sus colores, resolución, etc.); esto no sólo es problemático en lastarjetas gráficas: la anarquía y ausencia de funciones de información también se repite con los discos, elteclado, ... aunque los programadores ya estamos acostumbrados a realizar la labor del detective paraaveriguar la información que los programas necesitan. Sin embargo, con los gráficos no podemos y nosvemos obligados a preguntar al usuario qué tarjeta tiene, de cuántos colores y resolución, en qué modo... ylo que es peor: la inexistencia de funciones de información se agrava con el hecho de que las VGA de losdemás fabricantes hayan asignado de cualquier manera los números de modo. De esta manera, por ejemplo,una tarjeta Paradise en el modo 5Fh tiene de 640x400 puntos con 256 colores, mientras que una Trident tiene,en ese mismo modo, 1024x768 con 16 colores. En lo único que coinciden todas las tarjetas es en los primeros

Page 108: PCA, PS2 ,IBM y AT

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

modos de pantalla, definidos inicialmente por IBM. Muchas SuperVGA tienen funciones que informan desus modos, colores y resoluciones, lo que sucede es que en esto no se han podido poner de acuerdo losfabricantes y la función de la BIOS de la VGA a la que hay que invocar para obtener información, ¡difierede unas tarjetas a otras!. Afortunadamente, existe un estándar industrial en tarjetas SuperVGA, el estándarVESA, que aunque ha llegado demasiado tarde, múltiples VGA lo soportan y a las que no, se les puedeañadir soporte con un pequeño driver residente. Hablaremos de él más tarde.

No conviene seguir adelante sin mencionar antes la tarjeta gráfica Hércules. Se trata de una tarjetaque apareció en el mercado muy poco después que la CGA de IBM, con el doble de resolución ymanteniendo la calidad MDA en modo texto. Esta tarjeta no está soportada por la BIOS (manufacturada porIBM) y los fabricantes de SuperVGA tampoco se han molestado en soportarla por software, aunque sí porhardware. Está muy extendida en las máquinas antiguas, pero hoy en día no se utiliza y su programaciónobliga a acceder a los puertos de entrada y salida de manera directa al más bajo nivel.

7.4.2.- DETECCIÓN DE LA TARJETA GRÁFICA INSTALADA.

El siguiente procedimiento es uno de tantos para evaluar la tarjeta gráfica instalada en el ordenador.Devuelve un valor en BL que es el mismo que retorna la INT 10h al llamarla con AX=1A00h (ver funcionesde la BIOS en los apéndices): 0 ó 1 para indicar que no hay gráficos; 2 si hay CGA; 3, 4 ó 5 si existe unaEGA; 6 si detecta una PGA; 7 u 8 si hay VGA o superior y 10, 11 ó 12 si existe MCGA. Retorna 255 si latarjeta es desconocida (muy raro). La rutina funciona en todos los ordenadores, con o sin tarjetas gráficasinstaladas y del tipo que sean.

tipo_tarjeta PROCPUSH DSMOV AX,1A00hINT 10h ; solicitar información VGA a la BIOSCMP AL,1Ah ; BL = tipo de tarjetaJE tarjeta_ok ; función soportada (hay VGA)MOV AX,40hMOV DS,AXMOV BL,10hMOV AH,12hINT 10h ; solicitar información EGA a la BIOSCMP BL,10hJE no_ega ; de momento, no es EGAMOV BL,1 ; supuesto MDATEST BYTE PTR DS:[87h],8 ; estado del control de vídeoJNZ tarjeta_ok ; es MDAMOV BL,4 ; supuesto EGA colorOR BH,BHJZ tarjeta_ok ; así esINC BL ; es EGA monoJMP tarjeta_ok

no_ega: MOV BL,2 ; supuesto CGACMP WORD PTR DS:[63h],3D4h ; base del CRTJE tarjeta_ok ; así esDEC BL ; es MDA

tarjeta_ok: POP DSRET

tipo_tarjeta ENDP

7.4.3. - INTRODUCCIÓN AL ESTÁNDAR GRÁFICO VGA.

La tarjeta VGA es el estándar actual en ordenadores personales, siendo el sistema de vídeo mínimoque incluye la máquina más asequible. En este apartado estudiaremos la forma básica de programar susmodos gráficos, haciendo un especial hincapié en el tema menos claramente explicado por lo general: elcolor. Se ignorarán por completo las tarjetas CGA y Hércules, aunque sí se indicará qué parte de lo expuestose puede aplicar también a la EGA. Tampoco se considerará la MCGA, un híbrido entre EGA y VGA quesolo equipa a los PS/2-30 de IBM, bastante incompatible además con la EGA y la VGA.

Page 109: PCA, PS2 ,IBM y AT

109ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

La VGA soporta todos los modos gráficos estándar de las tarjetas anteriores, resumidos en la figura7.4.3.1, si bien los correspondientes a la CGA (320x200 en 4 colores y 640x200 monocromo) son inserviblespara prácticamente cualquier aplicación gráfica actual.

Modo (hex) Resolución Colores Segmento Organización Adaptador

4 y 5 320 x 200 4 B800 entrelazado CGA

6 640 x 200 2 B800 entrelazado CGA

0Dh 320 x 200 16 A000 planos de bit EGA

0Eh 640 x 200 16 A000 planos de bit EGA

0Fh 640 x 350 2 A000 planos de bit EGA

10h 640 x 350 4 A000 planos de bit EGA

10h 640 x 350 16 A000 planos de bit EGA (128K)

11h 640 x 480 2 A000 lineal VGA/MCGA

12h 640 x 480 16 A000 planos de bit VGA

13h 320 x 200 256 A000 lineal VGA/MCGA

FIGURA 7.4.3.1: MODOS GRÁFICOS DE VIDEO

La organización dela memoria (entrelazado,planos de bit o lineal) es lamanera en que se direccionala memoria de vídeo porparte de la CPU. Porejemplo, en el modo 6, cadapixel de la pantalla estáasociado a un bit (8 pixelspor byte) a partir de ladirección B800:0000; sinembargo, cuando se recorren80 bytes en la memoria (640bits o pixels, primera línea

completa) no se pasa a la segunda línea de la pantalla sino unas cuantas más abajo, en una arquitecturarelativamente compleja debida a las limitaciones del hardware de la CGA. Esto ha sido superado en lassiguientes tarjetas, en las que las líneas están consecutivas de manera lógica en una organización lineal, sibien el límite de 64 Kb de memoria que puede direccionar en un segmento el 8086 ha obligado al truco delos planos de bit. Para establecer el modo de vídeo se puede emplear una función del lenguaje deprogramación que se trate o bien llamar directamente a la BIOS, si no se desea emplear la librería gráficadel compilador: la función 0 (AH=0) de servicios de vídeo de la BIOS (INT 10h) establece el modo de vídeosolicitado en AL. En Turbo C sería, por ejemplo:

#include <dos.h>

main()

struct REGPACK r;

r.r_ax=0x0012; /* AH = 00, AL=12h */intr (0x10, &r); /* ejecutar INT 10h */

7.4.3.1 - EL HARDWARE DE LA VGA.

El chip VGA consta de varios módulos internos, que definen conjuntos de registros direccionablesen el espacio E/S del 80x86. En la EGA eran de sólo escritura, aunque en la VGA pueden ser tanto escritoscomo leídos. Por un lado está el secuenciador, encargado de la temporización necesaria para el acceso a lamemoria de vídeo. Por otro lado tenemos el controlador de gráficos, encargado del tráfico de informaciónentre la CPU, la memoria de vídeo y el controlador de atributos; consta de 9 registros cuya programación esnecesaria para trazar puntos a gran velocidad en los modos de 16 colores. El controlador de atributosgestiona la paleta de 16 colores y el color del borde. Por último, el DAC o Digital to Analog Converter seencarga en la VGA (no dispone de él la EGA) de gestionar los 262.144 colores que se pueden visualizar enpantalla. La parte del león son los ¡768 registros! de 6 bits que almacenan la intensidad en las componentesroja, verde y azul de cada color, de los 256 que como mucho puede haber simultáneamente en la pantalla(256*3=768).

7.4.3.2 - EL COLOR.

La CGA puede generar 16 colores diferentes, utilizando un solo bit por componente de color más uncuarto que indica la intensidad. Sin embargo, la EGA emplea dos bits por cada una de las tres componentesde color, con lo que obtiene 26=64 colores diferentes. Para asociar estos 64 colores a los no más de 16 quepuede haber en un momento determinado en la pantalla, se emplean los 16 registros de paleta del controladorde atributos: En cada uno de estos registros, de 6 bits significativos, se definen los 16 colores posibles. La

Page 110: PCA, PS2 ,IBM y AT

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

BIOS de la EGA y la VGA carga los registros de paleta adecuadamente para emular los mismos colores dela CGA. Así, por ejemplo, en los modos de texto el color 0 es el negro y el 15 el blanco brillante, si biense puede alterar esta asignación. Un cambio en un registro de paleta afecta instantáneamente a todo el áreade pantalla pintado de ese color. El valor binario almacenado en los registros de paleta tiene el formatoxxrgbRGB, siendo rgb los bits asociados a las componentes roja, verde y azul de baja intensidad, y RGBsus homólogos en alta intensidad. Así, el valor 010010b se corresponde con el verde más brillante.

Modos de 16 colores en VGA.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, debido a la presencia del DAC: una matriz de 256 elementos que constan cada unode 3 registros de 6 bits. Cada uno de los registros de paleta apunta a un elemento del DAC, que es quienrealmente contiene el color; lo que sucede es que los registros del DAC son programados por la BIOS paraemular los 64 colores de la EGA. Existen dos maneras diferentes de indexar en el DAC los registros depaleta, de manera que se puede dividir el DAC en 16 bloques de 16 elementos o bien en 4 bloques de 64elementos: en un momento dado, sólo uno de los bloques (denominado página de color del DAC) está activo.Esto significa que se pueden crear 16 ó 4 subpaletas, pudiéndose activar una u otra libremente con unafunción de la BIOS de la VGA. Por defecto, la BIOS establece 4 páginas de 64 elementos en el DAC, demanera que valores en el rango 0-63 en los 16 registros de paleta referencien a posiciones distintas en el DAC(al área 0-63, al 64-127, al 128-191 ó al 192-255): por defecto, la BIOS emplea los elementos 0..63 del DACque programa para emular los 64 colores de la EGA. Sin embargo, puede resultar más interesante disponerde 16 subpaletas de 16 elementos para conseguir determinados efectos gráficos: en este caso no tiene sentidoque los registros de paleta almacenen valores fuera del rango 0-15 (de hecho, solo se consideran los 4 bitsmenos significativos de los mismos). La figura 7.4.3.2 expresa gráficamente la manera en que se genera elcolor. Se pueden definir, por ejemplo, las 16 subpaletas en tonos ascendentes de azul y, cambiando la páginao subpaleta activa a cierta velocidad se puede hacer que la imagen se encienda y apague rítmica ysuavemente. Por supuesto, también se pueden obtener efectos similares alterando directamente los registrosdel DAC, aunque es mucho más lento que conmutar entre varias paletas ya definidas. Conviene resaltar queel color del borde de la pantalla se define en la EGA y en la VGA en una especie de registro que sigue a los16 registros de paleta: en la VGA no interviene el DAC en la generación del color del borde, del que soloexisten por consiguiente 64 tonos (si bien el borde suele estar en color negro y su tamaño reducido y variablelo hace inservible para nada).

Los pixels en los modos gráficos de 16 colores pueden parpadear, si bien es una técnica pocoempleada: para ello, basta con cambiar un bit de un registro del controlador de atributos, aunque existe unafunción de la BIOS que realiza dicha tarea (llamar a la INT 10h con AX=1003h y BX=1 para activar elparpadeo -situación por defecto en los modos de texto- ó BX=0 para desactivarlo).

0..63CASO 4 X 64

64..127valor 0..63 elemento del DAC página (0..3)

128..191 seleccionable(0 por defecto)

192..255

color 0..15en pantalla (0..15) valor 0..15 elemento del DAC 16..31

32..47CASO 16 x 16 : página (0..15)

: seleccionable224..239240..255

Elementos del DAC

FIGURA 7.4.3.2: OBTENCIÓN DEL COLOR EN LOS MODOS DE 16 COLORES (VGA)

16 Registros de paleta

El truco del mono.Los monitores monocromos VGA solo admiten 64 tonos y se limitan siempre a presentar la

componente verde del DAC. Lo que sucede es que la BIOS ajusta la intensidad de la señal verde para emularla presencia de las otras dos. En concreto, suma el 30% del valor rojo, el 59% del verde y el 11% del azul

Page 111: PCA, PS2 ,IBM y AT

111ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

y el resultado lo fuerza al rango 0-63, lo cual simula aproximadamente la intensidad que percibiría el ojohumano con los colores reales. Si se accediera directamente al hardware sin ayuda de la BIOS, lo cual noes nuestro caso, este sería un aspecto a considerar. Por último, decir que en el modo de 4 colores y 350líneas, solo se emplean los registros de paleta 0, 1, 4 y 5, si bien lo normal aquí es esperar que existan 16colores (caso de la VGA, o incluso de la EGA con 128K).

Modo de 256 colores.En el modo 13h de 320x200 con 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, ya que solo interviene el DAC: el byte de memoria devídeo asociado a cada punto de la pantalla apunta directamente a un elemento del DAC. Por tanto, losregistros de paleta del controlador de atributos no se emplean en este modo, siendo más sencillo el procesode generación del color.

Cómo definir la paleta y los registros del DAC.A la hora de cambiar la paleta es conveniente emplear funciones de la BIOS o del lenguaje de

programación, ya que un acceso directo al hardware sin más precauciones puede provocar interferencias conalgunas tarjetas VGA. Conviene también emplear las funciones que cambian de una sola vez un conjunto deregistros del DAC, ya que hacerlo uno por uno es demasiado lento. Otra ventaja de emplear la BIOS es queésta hace automáticamente las conversiones necesarias para lograr la mejor visualización posible en pantallasmonocromas. En algunos casos, las paletas que define por defecto la BIOS al establecer el modo de pantallason apropiadas. Sin embargo, puede ser útil cambiarlas para lograr un degradado atractivo en los modos de16 colores y casi obligatorio en el modo de 256 colores, dada la absurda paleta propuesta por la BIOS. Paradefinir un color en el DAC, basta con un poco de imaginación: si las tres componentes están a cero, saldráel negro; si están a 63 (valor máximo) saldrá un blanco brillante; si se ponen la roja y la azul en 32 y laverde en 0, saldrá un morado de oscuridad mediana. Se puede realizar un bucle y llenar los primeros 64elementos del DAC con valores crecientes en una componente de color, poniendo a 0 las demás: de esamanera, se genera una paleta óptima para hacer degradados (escalas de intensidad) de un color puro.

FIGURA 7.4.3.3:/********************************************************************** EJEMPLO DE CAMBIO DE LA PALETA DE 16 COLORES (EGA/VGA) LLAMANDO AL ** BIOS PARA ELEGIR LOS COLORES DESEADOS, ENTRE LOS 64 POSIBLES DE LA ** EGA (POR DEFECTO EMULADOS POR EL DAC DE LA VGA). **********************************************************************/

#include <dos.h>#include <graphics.h>

void main()

struct REGPACK r;int gdrv, gmodo, coderr, i, x, color, pixel;char paleta[17];

/* ESTABLECER MODO EGA/VGA 640x350 - 16 COLORES */

detectgraph (&gdrv, &gmodo); coderr=graphresult();if (((gdrv!=EGA) && (gdrv!=VGA)) || (coderr!=grOk)) printf("\nNecesaria tarjeta EGA o VGA.\n"); exit(1);

gmodo=EGAHI; initgraph(&gdrv, &gmodo, ""); coderr=graphresult();if (coderr!=grOk) printf("Error gráfico: %s.\n", grapherrormsg(coderr)); exit(1);

/* DIBUJAR BANDAS VERTICALES DE EJEMPLO */

for (x=color=0; color<16; color++)for (pixel=0; pixel<getmaxx()/16; pixel++, x++) setcolor (color); line (x, 0, x, getmaxy());

/* DEFINIR NUEVA PALETA */

paleta[0]=0; /* __rgbRGB = 0 --> negro */paleta[1]=4; /* __000100 = 4 --> componente roja normal */paleta[2]=4*8; /* __100000 = 32 --> componente roja oscura */paleta[3]=4*8+4; /* __100100 = 36 --> ambas: rojo brillante */for (i=4; i<17; i++) paleta[i]=0; /* resto colores y borde negros */

r.r_es=FP_SEG(paleta); r.r_dx=FP_OFF(paleta);r.r_ax=0x1002; intr (0x10, &r); /* establecer paleta y borde */

getch(); closegraph();

Para establecer la paleta se puede llamar ala BIOS (INT 10h) con AX=1002h y ES:DXapuntando a un buffer de 17 bytes: uno para cadaregistro de paleta más otro final para el color delborde de la pantalla. El Turbo C permite cambiarla paleta con instrucciones de alto nivel; sinembargo, quienes no deseen aprender lasparticularidades de cada compilador, siemprepueden recurrir a la BIOS, que cambiando la paletaes bastante solvente. Echemos un vistazo alejemplo de la figura 7.4.3.3 (para ejecutar esteprograma hay que tener en cuenta que el ficheroEGAVGA.BGI del compilador ha de estar en eldirectorio de trabajo). Al principio se trazan unasbandas verticales con la función line() que seráncoloreadas con los 16 colores por defecto, aunquecambiarán instantáneamente al modificar la paleta.Al definir la paleta, los 4 primeros registros sonasignados con los 4 posibles tonos de rojo, másbien 3 (el primero es el negro absoluto): rojo, rojo

oscuro y rojo brillante. Todos los demás registros y el borde de la pantalla son puestos a 0 (negro) por lo queen la pantalla quedan visibles sólo las tres bandas verticales citadas. El cambio de la paleta es instantáneo,lo que permite hacer efectos especiales. En la VGA, recuérdese que los valores de la paleta son simplespunteros al DAC y no los colores reales. Lo que sucede es que los registros del DAC son inicializados alcambiar el modo de pantalla de tal manera que emulan los colores que se obtendría en una EGA... a menosque se cambien los valores de dichos registros.

Page 112: PCA, PS2 ,IBM y AT

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

Para ello, nada mejor que llamar de nuevo a la INT 10h con AX=1012h, indicando en BX el primerelemento del DAC a cambiar (típicamente 0) y en CX el número de elementos a modificar (a menudo los256 posibles). 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, verde y azul) aunque solo son significativos los 6bits de menor orden de cada byte. Existe también otra función bastante interesante, invocable con AX=1013hy que consta de dos subservicios: el primero se selecciona poniendo un 0 en BL, e indicando en BH si sedesean 4 páginas de 64 elementos en el DAC (BH=0) ó 16 páginas de 16 elementos (BH=1). El segundoservicio se indica llamando con BL=1, y permite seleccionar la página del DAC activa en BH (0-3 ó 0-15,según cómo esté estructurado). Obviamente, esta función no está disponible en el modo 13h de 256 colores,en el que no interviene la paleta (sólo el DAC y entero, no a trocitos). La figura 7.4.3.4 contiene un nuevo

FIGURA 7.4.3.4:/********************************************************************** EJEMPLO DE CAMBIO DE LA PALETA DE 16 COLORES Y REPROGRAMACION DEL ** DAC DE LA VGA POR EL BIOS PARA ELEGIR LOS 16 COLORES ENTRE 262.144 **********************************************************************/

#include <dos.h>#include <graphics.h>

void main()

struct REGPACK r;int gdrv, gmodo, coderr, pagina, i, x, color, pixel;char paleta[17], dac[256][3];

/* ESTABLECER MODO VGA 640x480 - 16 COLORES */

detectgraph (&gdrv, &gmodo); coderr=graphresult();if ((gdrv!=VGA) || (coderr!=grOk)) printf("\nNecesaria tarjeta VGA.\n"); exit(1);

gmodo=VGAHI; initgraph(&gdrv, &gmodo, ""); coderr=graphresult();if (coderr!=grOk) printf("Error gráfico: %s.\n", grapherrormsg(coderr)); exit(1);

/* DIBUJAR BANDAS VERTICALES DE EJEMPLO */

for (x=color=0; color<16; color++)for (pixel=0; pixel<getmaxx()/16; pixel++, x++) setcolor (color); line (x, 0, x, getmaxy());

/* SELECCIONAR 16 BLOQUES DE 16 ELEMENTOS EN EL DAC */

r.r_ax=0x1013; r.r_bx=0x0100; intr (0x10, &r);

/* PAGINA 2: LA PALETA SE APOYARA EN ELEMENTOS 32..47 DEL DAC */

pagina=2; r.r_ax=0x1013; r.r_bx=(pagina<<8) | 1; intr (0x10, &r);

/* APUNTAR REGISTROS DE PALETA A ELEMENTOS CONSECUTIVOS DEL DAC */

for (i=0; i<16; i++) paleta[i]=i;paleta[16]=0; /* color del borde */

r.r_es=FP_SEG(paleta); r.r_dx=FP_OFF(paleta);r.r_ax=0x1002; intr (0x10, &r); /* establecer paleta y borde */

/* LLENAR ELEMENTOS 32..47 DEL DAC DE AMARILLOS CRECIENTES */

for (i=32; i<48; i++) dac[i][0]=i*4; /* valores crecientes 0..60 de rojo */dac[i][1]=i*4; /* valores crecientes 0..60 de verde */dac[i][2]=0; /* sin componente azul */

r.r_bx=32; /* primer elemento del DAC */r.r_cx=16; /* número de elementos a definir */r.r_es=FP_SEG(dac[32]); r.r_dx=FP_OFF(dac[32]);r.r_ax=0x1012; intr (0x10, &r); /* programar elementos del DAC */

getch();closegraph();

programa completo de demostración, desarrolladoa partir del anterior, que requiere ya un auténticoadaptador VGA. Lo primero que se hace esseleccionar el modo de 16 páginas en el DAC,estableciendo la página 2 como activa(exclusivamente por antojo mio). Ello significa quese emplearán los elementos 32..47 del DAC (lapágina 0 apuntaría a los elementos 0..15, la 1hubieran sido los elementos 16..31 y asísucesivamente). Los registros de paleta, simplesíndices en el DAC, toman los valores 0,1,...,15(excepto el 17º byte, color del borde, puesto a 0para seleccionar el negro). A continuación, bastaprogramar los registros 32..47 del DAC con loscolores deseados, entre los 262.144 posibles. Comocada componente puede variar entre 0 y 63,elegimos 16 valores espaciados proporcionalmente(0, 4, 8,..., 60) y los asignamos a las componentesroja y verde (rojo+verde=amarillo), apareciendo enla pantalla una escala de 16 amarillos (el primero,negro absoluto) de intensidad creciente. Si bien 16colores son pocos, son suficientes para representarcon relativa precisión algunas imágenes,especialmente en las que predomina un colordeterminado (los ficheros gráficos se vennormalmente tan mal en los modos de 16 coloresdebido a que respetan la paleta de la EGA, en laVGA sería otra historia).

Por supuesto, existen más funciones que éstas, entre ellas las que permiten cambiar sólo un registrode paleta o un elemento del DAC (y no un bloque); sin embargo, son más lentas cuando se va a cambiar unconjunto de registros. En cualquier caso, el lector puede consultarlas en el fichero INTERRUP.LST si lodesea. También existen en la VGA las funciones inversas (obtener paletas y registros del DAC). El accesopor medio de la BIOS para cambiar la paleta es a menudo más cómodo que emplear funciones del lenguajede programación y garantiza en ocasiones un mayor nivel de independencia respecto a la evolución futuradel hardware (aunque si la librería gráfica llama a la BIOS...). Sin embargo, para otras aplicaciones, es mejorno usar la BIOS. Por ejemplo, el programa de la figura 7.4.3.5 accede directamente a los registros de la VGApara modificar la paleta en dos bucles, en el primero disminuyendo la luminosidad de la pantalla (hastadejarla negra) y en el segundo restaurándola de nuevo. Este efecto cinematográfico hubiera sido imposiblea través de la BIOS por razones de velocidad: el acceso directo al hardware, con precauciones (en este caso,esperar el retrazado vertical para evitar interferencias) es a veces inevitable. El programa de ejemplo funcionatambién en monitores monocromos, aunque en la práctica sólo actúe en ellos sobre la componente verde. Ellector deberá consultar bibliografía especializada para realizar este tipo de programación.

Page 113: PCA, PS2 ,IBM y AT

113ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

7.4.3.3 - DIRECCIONAMIENTO DE PIXELS.

Para pintar pixels en la pantalla y para consultar su color, existen funciones de la BIOS de uso norecomendado. La razón estriba en el mal diseño de la BIOS inicial de IBM, no mejorado tampoco por lasVGA clónicas. El problema es que las BIOS emplean 4, 5 y hasta 10 veces más tiempo del necesario paraFIGURA 7.4.3.5:/********************************************************************** EFECTO «CINEMATOGRAFICO» DE DESVANECIMIENTO Y POSTERIOR ** REAPARICION DE LA PANTALLA CON ACCESO DIRECTO AL HARDWARE VGA. **********************************************************************/

#include <dos.h>

void main()unsigned char dac[256][3];register i, j;

for (i=0; i<256; i++) /* anotar la paleta activa */disable();outportb (0x3C7, i);dac [i][0] = inportb (0x3C9); /* R */dac [i][1] = inportb (0x3C9); /* G */dac [i][2] = inportb (0x3C9); /* B */enable();

/* claridad descendente desde el64/64-avo al 0/64-avo de intensidad */

for (i=64; i>=0; i--) while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */for (j=0; j<256; j++) disable();outportb (0x3C8, j);outportb (0x3C9, dac[j][0]*i >> 6);outportb (0x3C9, dac[j][1]*i >> 6);outportb (0x3C9, dac[j][2]*i >> 6);enable();

/* claridad ascendente desde el

0/64-avo al 64/64-avo de intensidad */for (i=0; i<=64; i++) while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */for (j=0; j<256; j++) disable();outportb (0x3C8, j);outportb (0x3C9, dac[j][0]*i >> 6);outportb (0x3C9, dac[j][1]*i >> 6);outportb (0x3C9, dac[j][2]*i >> 6);enable();

trazar los puntos. La causa de este problema noreside en que empleen rutinas multipropósito paratodos los modos, ya que existen básicamente sólotres tipos de arquitectura de pantalla (modos CGA,16 colores y 256 colores). El fallo reside,simplemente, en que han sido desarrollados sinpensar en la velocidad. Por ejemplo, la BIOSemplea el algoritmo más lento posible que existepara trazar puntos en los modos de 16 colores. Lomás conveniente es utilizar los recursos dellenguaje de programación o, mejor aún, accederdirectamente a la memoria de pantalla consubrutinas en ensamblador. Este es elprocedimiento seguido por la mayoría de lasaplicaciones comerciales. Sin embargo, la BIOStiene la ventaja de que permite normalizar el accesoa la pantalla. Así, un programa puede fácilmentetrazar un punto en el modo 1024x768x256 de unaSuperVGA (y nunca mejor dicho, porque comosean muchos más de uno...). Para trazar un puntose coloca en CX la coordenada X, en DX lacoordenada Y, en AL el color, en BH la página yen AH el valor 0Ch. A continuación se llama,

como es costumbre, a la INT 10h. Para consultar el color de un punto en la pantalla, se cargan CX y DX consus coordenadas y BH con la página, haciendo AH=0Dh antes de llamar a la INT 10h, la cual devuelve elcolor del pixel en AL. La página será normalmente la 0, aunque en los modos de vídeo que soportan variaspáginas ésta se puede seleccionar con la función 5 de la INT 10h. La existencia de varias páginas de vídeose produce cuando en el segmento de 64 Kb de lamemoria de vídeo se puede almacenar más de unaimagen completa (caso por ejemplo del modo640x350x16): existen entonces varias páginas (2, 4,etc.) que se reparten el segmento a partes iguales.Se puede en estas circunstancias visualizar unapágina cualquiera mientras se trabaja en las otras,que mientras tanto permanecen ocultas a los ojosdel usuario.

Modo 13h de 256 colores.Este modo, de organización lineal, no

presenta complicación alguna: los pixels se sucedenen la memoria de vídeo de izquierda a derecha y dearriba a abajo, a partir del segmento A000. Cadapunto está asociado a un byte, cuyo valor (0-255)referencia directamente a un elemento del DAC. Enla figura 7.4.3.6 hay un nuevo listado de ejemplo,en este caso sin emplear la librería gráfica delTurbo C. El programa se limita a activar este modo

FIGURA 7.4.3.6:/********************************************************************** EJEMPLO DE USO DEL MODO DE 320x200 CON 256 COLORES ** SIN EMPLEAR LA LIBRERIA GRAFICA DEL COMPILADOR. **********************************************************************/

#include <dos.h>

void main()

struct REGPACK r;char dac[256][3], far *vram;register x, y;int i,ii;

/* ESTABLECER MODO DE PANTALLA */

r.r_ax=0x13; intr (0x10, &r); vram=MK_FP(0xA000, 0);

/* LLENAR LA PANTALLA CON LINEAS HORIZONTALES DE COLOR 0..199 */

for (y=0; y<200; y++) for (x=0; x<320; x++) *vram++=y;

/* DEFINIR PALETA EN EL DAC */

for (i=0; i<100; i++) dac[i][0]=0;dac[i][1]=0; /* definir azules */dac[i][2]=i >> 1;

for (i=100; i<200; i++) ii=200-i;dac[i][0]=ii >> 1;dac[i][1]=ii >> 2; /* definir naranjas */dac[i][2]=0;

r.r_ax=0x1012; r.r_bx=0; r.r_cx=200;r.r_es=FP_SEG(dac); r.r_dx=FP_OFF(dac); intr (0x10, &r);

getch(); r.r_ax=3; intr (0x10, &r);

de pantalla pintando las 200 líneas con los valores 0..199. A continuación define los elementos 0..199 delDAC de la siguiente manera: los primeros 100 en tonos ascendentes de azul, y los siguientes 100 elementos

Page 114: PCA, PS2 ,IBM y AT

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

en tonos descendentes de naranja, lo que divide automáticamente la pantalla en dos zonas con la estructuracitada. Conseguir el naranja no es complicado: basta sumar rojo con amarillo; como el amarillo es a su vezrojo más verde, el naranja se obtiene sumando dos cantidades de rojo por cada una de verde. Los elementos200..255 del DAC, no empleados en este ejemplo, podrían ser definidos con otros colores para dibujar algunaotra cosa.

Modos de 16 colores.Para direccionar puntos en los modos de 16 colores, en los que actúan interrelacionados los registros

de paleta y el DAC de la manera descrita con anterioridad, es necesario un acceso directo al hardware porcuestiones de velocidad. Los lectores que no vayan a emplear las funciones del lenguaje de programacióndeberán consultar bibliografía especializada en gráficos.

Y nada más.La única diferencia de la VGA respecto a la EGA, de hecho, se debe a su peculiar manera de

gestionar el color, así como a la inclusión del modo de 320x200 con 256 colores (el modo de 640x480 esidéntico en funcionamiento al de 640x350 de la EGA, solo cambia la altura de la pantalla). Existe tambiénla posibilidad de colocar la VGA en dos modos de 256 colores alternativos al 13h y basados en el mismo;en uno se alcanzan 320x240 puntos y en el otro 320x400. La bibliografía especializada en gráficos explicalos pasos a realizar para conseguir esto, factible en la totalidad de las tarjetas VGA del mercado. Sinembargo, estos modos requieren un cambio en el modo de direccionamiento de los pixels, que pasa a ser máscomplejo -aunque más potente para algunas aplicaciones-.

7.4.4. - EJEMPLO DE GRÁFICOS EMPLEANDO LA BIOS.

Este programa ejemplo accede a la pantalla empleando las funciones de la BIOS para trazar puntos(ver apéndice sobre funciones de la BIOS). Utiliza el modo CGA de 640x200 puntos, aunque se puedeconfigurar para cualquier otro modo. El programa dibuja una conocida red en las cuatro esquinas de lapantalla, trazando líneas. 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, pero es fácil deimplementar si en vez de llamar a la BIOS para pintar se emplea una rutina propia mezclada con la que trazala recta). La velocidad del algoritmo es muy elevada, sobre todo con las líneas largas, máxime teniendo encuenta que se trata posiblemente de una de sus implementaciones más optimizada (sólo usa una variable ymantiene todos los demás valores en los 7 registros de datos de la CPU, sin emplear demasiado la pila yduplicando código cuando es preciso en los puntos críticos). No entraré en explicaciones matemáticas delmétodo, del que hay pautas en su listado. Existen versiones de este método que consideran de manera especiallas líneas verticales y horizontales para pintarlas de manera más rápida, aunque yo personalmente prefierorutinas independientes para esas tareas con objeto de no ralentizar el trazado de rectas normales.

; ********************************************************************; * *; * RED.ASM - Demostración de gráfica en CGA utilizando BIOS *; * *; ********************************************************************

modo EQU 6 ; modo de vídeomax_x EQU 640max_y EQU 200max_color EQU 2

red SEGMENTASSUME CS:red, DS:red

ORG 100hinicio:

MOV AX,modoINT 10h ; modo de pantallaMOV AL,max_color-1 ; color visibleMOV BX,0 ; contador para eje YMOV BP,0 ; contador para eje X

otras_cuatro: MOV CX,0MOV DX,BXMOV SI,BPMOV DI,max_y-1CALL recta ; primera rectaMOV CX,max_x-1MOV SI,max_x-1SUB SI,BPCALL recta ; segundaMOV CX,BPMOV DX,0MOV SI,0MOV DI,max_y-1SUB DI,BXCALL recta ; terceraMOV CX,max_x-1SUB CX,BPMOV SI,max_x-1

CALL recta ; cuartaADD BX,6ADD BP,14CMP BX,max_yJB otras_cuatroMOV AH,0INT 16h ; esperar pulsación de teclaMOV AX,3INT 10h ; volver a modo textoINT 20h ; fin de programa

recta PROCPUSH AX ; de (CX,DX) a (SI,DI) color ALPUSH BXPUSH CXPUSH DXPUSH SIPUSH DIPUSH BPMOV color,ALMOV AX,SISUB AX,CX ; AX = X2-X1JNC absx2x1NEG AXXCHG CX,SIXCHG DX,DI

absx2x1: MOV BX,DI ; AX = ABS(X2-X1) = «dx»SUB BX,DXMOV BP,1 ; BP = 1 = «yincr» si Y2>Y1JNC absy2y1NEG BP ; BP = -1 = «yincr» si Y2<=Y1NEG BX

absy2y1: CMP AX,BX ; BX = ABS(Y2-Y1) = «dy»PUSHFJA noswap ; ABS(pendiente) menor de 1XCHG AX,BX

noswap: SHL BX,1 ; BX = «dy» * 2MOV SI,BXSUB SI,AX ; SI = «dy» * 2 - «dx» = «d»

Page 115: PCA, PS2 ,IBM y AT

115ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

MOV DI,BXSUB DI,AXSUB DI,AX ; DI = «dy»*2-«dx»*2 = «incr2»POPFJBE penmay1 ; pendiente mayor de 1

penmen1: PUSH AXMOV AL,colorCALL punto ; en (CX, DX) = («x», «y»)POP AXINC CX ; «x»++AND SI,SI ; (SI>0) ? -> «d» > 0 ?JS noincyADD SI,DI ; «d» > 0 : «d» = «d» + «incr2»ADD DX,BP ; «y» = «y» + «yincr»DEC AX ; «dx»--JNZ penmen1JMP fin

noincy: ADD SI,BX ; «d» < 0 : «d» = «d» + «incr1»DEC AXJNZ penmen1JMP fin

penmay1: PUSH AXMOV AL,colorCALL punto ; en (CX, DX) = («x», «y»)POP AXADD DX,BP ; «y» = «y» + «yincr»AND SI,SI ; (SI>0) ? -> «d» > 0 ?JS noincxADD SI,DI ; «d» > 0 : «d» = «d» + «incr2»INC CX ; «x»++DEC AX ; «dx»--JNZ penmay1JMP fin

noincx: ADD SI,BX ; «d» = «d» + «incr1»

DEC AX ; «dx»--JNZ penmay1

fin: POP BPPOP DIPOP SIPOP DXPOP CXPOP BXPOP AXRET

color DB 0recta ENDP

punto PROCPUSH BX ; preservar registros (salvo AX)PUSH CXPUSH DXPUSH BPPUSH SIPUSH DIMOV AH,0Ch ; trazar punto usando BIOSXOR BX,BXINT 10hPOP DIPOP SIPOP BPPOP DXPOP CXPOP BXRET

punto ENDP

red ENDSEND inicio

Quizá el lector opine que RED.ASM no es tan rápido. Y tiene razón: la culpa es de la BIOS, queconsume un alto porcentaje del tiempo de proceso. Sustituyendo la rutina «punto» por una rutina de trazadode puntos propia, como la que se lista a continuación, la velocidad puede llegar a quintuplicarse en unhipotético RED2.ASM que la invocara.

punto640x200_C PROC ; en (CX, DX) de color AL (CGA 640x200)PUSH DS ; sólo se corrompe AXPUSH BXPUSH CXPUSH DXMOV BX,0B800h ; segmento de pantalla CGAMOV DS,BXMOV AH,CL ; preservar parte baja de «cx»XCHG BX,CX ; BX = «cx»MOV CL,3SHR BX,CL ; BX = «cx» / 8SHR DX,1 ; DX = int («cy» / 2)JNC no_addADD BX,8192 ; BX = «cx» / 8 + («cy» MOD 2) * 8192

no_add: INC CL ; CL = 4SHL DX,CL ; DX = («cy» / 2) * 16ADD BX,DX ; BX = BX + («cy» / 2) * 16

SHL DX,1SHL DX,1 ; DX = («cy» / 2) * 64ADD BX,DX ; BX = BX + («cy» / 2) * 80MOV CL,AH ; recuperar parte baja de «cx»AND CL,7 ; dejar nº de bit a pintar (0..7)XOR CL,7 ; invertir orden de numeraciónMOV AH,1 ; bit a borrar de la pantalla en AHSHL AX,CL ; AH = bit a borrar, AL = bit a pintarNOT AHAND [BX],AH ; borrar punto anteriorOR [BX],AL ; ubicar nuevo punto (1/0)POP DXPOP CXPOP BXPOP DSRET

punto640x200_C ENDP

Para estudiar el funcionamiento de la pantalla CGA el lector puede hacer un programa que recorrala memoria de vídeo para comprender la manera en que está organizada, un tanto peculiar pero no demasiadocomplicada. Sin embargo, con EGA y VGA no es tan sencillo realizar operaciones sobre la pantalla debidoa la presencia de planos de bit; salvo contadas excepciones como la del siguiente apartado.

7.4.5. - EJEMPLO DE GRÁFICOS ACCEDIENDO AL HARDWARE.

El siguiente programa de ejemplo accede directamente al segmento de vídeo de la VGA (0A000h)para trazar los puntos. Dibuja un vistoso ovillo basado en circunferencias con centro ubicado en unacircunferencia base imaginaria, aprovechando los 256 colores de la VGA estándar en el modo 320x200. Comola paleta establecida por defecto es poco interesante, se define previamente una paleta con apoyo directo enel hardware (el método empleado es sencillo pero no recomendable, provoca nieve con algunas tarjetas). Seemplea el color verde, único visualizable en monitores monocromos (aunque cambiando la paleta con lasfunciones de la BIOS no hubiera sido necesario). La VGA en modo 13h asocia cada punto de pantalla a unbyte, por lo que la pantalla es una matriz de 64000 bytes en el segmento 0A000h. Recordar que la fórmulapara calcular el desplazamiento para un punto (cx,cy) es 320*cy+cx.

Si se sustituye la rutina «punto», que traza el punto, por otra que lo haga llamando a la BIOS, en unaVGA Paradise (BIOS de 14/7/88) se emplean 4 segundos y 8 centésimas en generar la imagen, mientras quetal y como está el programa lo dibuja en 40,4 centésimas (10,1 veces más rápido); todos estos datoscronometrados con precisión sobre un 386-25 sin memoria caché teniendo instalada la opción de «SHADOWROM» (la lenta ROM copiada en RAM, incluida la BIOS de la VGA, por tanto no compite con desventaja).

El algoritmo empleado para trazar la circunferencia es de J. Michener, quien se basó a su vez en otrode J. Bresseham desarrollado para plotter. La versión que incluyo genera circunferencias en pantallas de

Page 116: PCA, PS2 ,IBM y AT

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

relación de aspecto 1:1, en otras (ej., de 640 x 200) produciría elipses. No entraré en su demostraciónmatemática, que nada tiene que ver con el ensamblador; baste decir que la rutina se basa exclusivamente enla aritmética entera calculando un solo octante de la circunferencia (los demás los obtiene por simetría).

; ********************************************************************; * *; * OVILLO.ASM - Demostración de gráfica en VGA utilizando hardware *; * *; ********************************************************************

modo EQU 13h ; modo de vídeomax_x EQU 320max_y EQU 200max_color EQU 256

oviseg SEGMENTASSUME CS:oviseg, DS:oviseg

ORG 100hinicio:

MOV AX,modoINT 10hCALL paleta_verdeMOV CX,max_xSHR CX,1 ; CX = max_x / 2MOV DX,max_ySHR DX,1 ; DX = max_y / 2MOV BX,DXSHR BX,1 ; BX = ma_y / 4CALL ovillo ; en (CX, DX) de radio BXMOV AH,0INT 16h ; esperar pulsación de teclaMOV AX,3INT 10h ; volver a modo textoINT 20h ; fin de programa

paleta_verde PROCMOV CX,256 ; los 256 registrosMOV DX,3C8h

otro_reg: MOV AL,CLOUT DX,AL ; registro a programarINC DXXOR AL,ALOUT DX,AL ; componente rojaMOV AL,CLREPT max_x/320SHR AL,1ENDMOUT DX,AL ; componente verdeXOR AL,ALOUT DX,AL ; componente azulDEC DXLOOP otro_regRET

paleta_verde ENDP

ovillo PROC ; circunferencia de circunferenciasMOV BP,BX ; en (CX, DX) con radio BX y color ALMOV AL,0MOV SI,BXXOR DI,DISHL BP,1SUB BP,3NEG BP ; BP = 3 - 2 * BX

ovillo_acaba: CMP DI,SIJG ovillo_ok ; ovillo completadoADD CX,SIADD DX,DICALL circunferencia ; en (x+SI, y+DI)INC ALSUB CX,SISUB CX,SICALL circunferencia ; en (x-SI, y+DI)INC ALSUB DX,DISUB DX,DICALL circunferencia ; en (x-SI, y-DI)INC ALADD CX,SIADD CX,SICALL circunferencia ; en (x+SI, y-DI)INC ALSUB CX,SIADD DX,DIADD CX,DIADD DX,SICALL circunferencia ; en (x+DI, y+SI)INC ALSUB CX,DISUB CX,DICALL circunferencia ; en (x-DI, y+SI)INC ALSUB DX,SISUB DX,SICALL circunferencia ; en (x-DI, y-SI)INC ALADD CX,DIADD CX,DICALL circunferencia ; en (x+DI, y-SI)INC ALSUB CX,DIADD DX,SI ; CX = x, DX = yCMP BP,0JG ovillo_decxADD BP,DIADD BP,DIADD BP,DIADD BP,DIADD BP,6JMP ovillo_incy

ovillo_decx: DEC SIPUSH AXMOV AX,DISUB AX,SISHL AX,1SHL AX,1ADD BP,AXPOP AXADD BP,10

ovillo_incy: INC DIJMP ovillo_acaba

ovillo_ok: RETovillo ENDP

circunferencia PROC ; en (CX,DX) con radio BX y color ALPUSH BXPUSH CXPUSH DXPUSH SIPUSH DIMOV SI,BXXOR DI,DISHL BX,1SUB BX,3NEG BX ; BX = 3 - 2 * BX

circunf_acaba: CMP DI,SIJG circunf_ok ; circunferencia completadaADD CX,SIADD DX,DICALL punto ; en (x+SI, y+DI)SUB CX,SISUB CX,SICALL punto ; en (x-SI, y+DI)SUB DX,DISUB DX,DICALL punto ; en (x-SI, y-DI)ADD CX,SIADD CX,SICALL punto ; en (x+SI, y-DI)SUB CX,SIADD DX,DIADD CX,DIADD DX,SICALL punto ; en (x+DI, y+SI)SUB CX,DISUB CX,DICALL punto ; en (x-DI, y+SI)SUB DX,SISUB DX,SICALL punto ; en (x-DI, y-SI)ADD CX,DIADD CX,DICALL punto ; en (x+DI, y-SI)SUB CX,DIADD DX,SI ; CX = x, DX = yCMP BX,0JG circunf_decxADD BX,DIADD BX,DIADD BX,DIADD BX,DIADD BX,6JMP circunf_incy

circunf_decx: DEC SIPUSH AXMOV AX,DISUB AX,SISHL AX,1SHL AX,1ADD BX,AXPOP AXADD BX,10

circunf_incy: INC DIJMP circunf_acaba

circunf_ok: POP DIPOP SIPOP DXPOP CXPOP BXRET

circunferencia ENDP

punto PROC ; trazar punto en 320x200 con 256 col.PUSH DS ; en (CX, DX) con color ALPUSH CXPUSH DXXCHG DH,DL ; DX = «cy» * 256ADD CX,DX ; CX = «cy» * 256 + «cx»SHR DX,1SHR DX,1 ; DX = «cy» * 64ADD CX,DX ; CX = «cy» * 320 + «cx»MOV DX,0A000hMOV DS,DX ; segmento VGAXCHG BX,CX ; preservar BX en CX, BX = offsetMOV [BX],AL ; pintar el puntoXCHG BX,CX ; restaurar BXPOP DX ; restaurar demás registrosPOP CXPOP DSRET

punto ENDP

oviseg ENDSEND inicio

7.4.6. - EL ESTÁNDAR GRÁFICO VESA.

Debido a la anarquía reinante en el mundo de las tarjetas gráficas, en 1989 se reunieron un grupoimportante de fabricantes (ATI, Genoa, Intel, Paradise, etc) para intentar crear una norma común. El resultado

Page 117: PCA, PS2 ,IBM y AT

117ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

de la misma fue el estándar VESA. Este estándar define una interface software común a todas las BIOS parapermitir a los programadores adaptarse con facilidad a las diversas tarjetas sin tener en cuenta sus diferenciasde hardware.

Actualmente, las principales tarjetas soportan la norma VESA. Las más antiguas pueden tambiénsoportarla gracias a pequeños programas residentes que el usuario puede instalar opcionalmente. Paradesarrollar una aplicación profesional, es una buena norma soportar algún modo estándar de la VGA y, paraobtener más prestaciones, algún modo VESA para los usuarios que estén equipados con dicho soporte.Intentar acceder directamente al hardware o a las funciones BIOS propias de cada tarjeta del mercado porseparado, salvo para aplicaciones muy concretas, es ciertamente poco menos que imposible.

Modos gráficos.

El estándar VESA soporta multitud de modos gráficos, numerados a partir de 100h, si bien algunosde los más avanzados (con 32000 o 16 millones de colores) sólo están soportados por las versiones másrecientes de la norma. Entre 100h y 107h se definen los modos más comunes de 16 y 256 colores de todaslas SuperVGA, aunque el modo 6Ah también es VESA (800x600x16) al estar soportado por múltiplestarjetas.

Una de las grandes ventajas del estándar VESA es la enorme información que pone a disposición delprogramador. Es posible conocer todos los modos y qué características de resolución, colores y arquitecturatienen. Además, hay funciones adicionales muy útiles para guardar y recuperar el estado de la tarjeta, deespecial utilidad para programas residentes: así, estos pueden fácilmente conmutar a modo texto (con laprecaución de preservar antes los 4 primeros Kbytes de la RAM de vídeo empleados para definir loscaracteres) y volver al modo gráfico original dejando la pantalla en el estado inicial.

El programa de ejemplo.

En el apéndice donde se resumen las funciones del DOS y la BIOS aparecen también las funcionesVESA de vídeo. Estas funciones se invocan vía INT 10h, con AX tomando valores por lo general desde4F00h hasta 4F08h. Para realizar programas que utilicen la norma, el lector deberá consultar dichainformación. Sin embargo, se expone aquí un sencillo programa de demostración que recoge prácticamentetodos los pasos necesarios para trabajar con un modo VESA.

El primer paso consiste en detectar la presencia de soporte VESA en el sistema, tarea que realiza lafunción testvesa(). La función getbest256() se limita a buscar el modo de mayor resolución de 256 coloressoportado por la tarjeta gráfica de ese equipo, barriendo sistemáticamente todos los modos de pantalla desdeel "mejor" hasta el "peor". Para comprobar la existencia de un determinado modo gráfico, existe_modo()invoca también a la BIOS VESA. La función setmode() establece un modo gráfico VESA, devolviendoademás dos informaciones interesantes: la dirección de memoria de la rutina de conmutación de bancos (yaveremos para qué sirve) y el segmento de memoria de vídeo, que será normalmente 0A000h. Finalmente,getinfo() devuelve información sobre cualquier modo gráfico. En principio, los modos utilizados por esteprograma de demostración son conocidos. Sin embargo, la lista de modos de vídeo puede ser mayor enalgunas tarjetas, sobre todo en el futuro. Por tanto, un esquema alternativo podría consistir no en buscarciertos modos concretos sino en ir recorriendo todos y elegir el que cumpla ciertas características deresolución o colores, entre todos los disponibles.

De toda la información que devuelve getinfo() es particularmente interesante el número de bancosque necesita ese modo de vídeo. Hay que tener en cuenta que todos los modos de 256 colores de más de320x200 ocupan más de 64 Kb de memoria. De esta manera, por ejemplo, una imagen de 640x480 con 256colores utiliza unos 256 Kb de RAM, dividida en 4 bancos. En un momento dado, sólo uno de los 4 bancospuede estar direccionado en el segmento de memoria de vídeo. Para elegir el banco activo (más bien, el iniciode la ventana lógica sobre el total de la memoria de vídeo, aunque nuestro ejemplo es una simplificación)existe una función de la BIOS VESA o, mejor aún: podemos llamar directamente a una subrutina que realiza

Page 118: PCA, PS2 ,IBM y AT

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

rápidamente esa tarea (sin tener que utilizar interrupciones) cuya dirección nos devolvió setmode(). De estamanera, el interface VESA evita que tengamos que hacer accesos directos al hardware. La rutina setbank()se limita a cargar el registro DX con el banco necesario antes de ejecutar el CALL. De todas maneras, estamodalidad de llamada no tiene por qué estar soportada por todas las BIOS VESA (en cuyo caso devuelvenuna dirección 0000:0000 para el CALL) aunque la inmensa mayoría, por fortuna, lo soportan.

El único cometido de este programa de demostración es buscar el mejor modo de 256 colores, entrelos normales de las SuperVGA, activarlo e ir recorriendo todos los bancos que componen la memoria devídeo (excepto el último, que podría estar incompleto) para llenar la pantalla con bytes de valor 55h y 0AAh.Finalmente, antes de terminar, se imprime la resolución y cantidad de memoria consumida por ese modo.

/********************************************************************** ** ESTANDAR GRAFICO VESA: EJEMPLO DE USO DEL MEJOR MODO DE 256 ** COLORES EN CUALQUIER SUPERVGA. ** **********************************************************************/

#include <dos.h>#include <alloc.h>#include <stdio.h>#include <stdlib.h>#include <string.h>

#define M640x400x256 0x100 /* modos VESA normales de 256c */#define M640x480x256 0x101#define M800x600x256 0x103#define M1024x768x256 0x105#define M1280x1024x256 0x107

unsignedtestvesa (void), /* Detectar soporte VESA */existe_modo (unsigned), /* Comprobar si un modo es soportado */getbest256 (void); /* Obtener mejor modo de 256c */

voidsetbank (long, unsigned), /* Conmutar banco de memoria */setmode (unsigned, long *, /* Establecer modo VESA */unsigned *),

getinfo (unsigned, /* Obtener información del modo */unsigned *,unsigned *, unsigned *, unsigned *);

/* DEMOSTRACION */

void main()struct REGPACK r;longConmutaBanco; /* dirección FAR del conmutador de banco */

unsignedvideo_seg, /* dirección del segmento de vídeo */far *pantalla,i, modo, max_x, max_y, vram, bancos, banco, limite;

if (!testvesa()) printf ("\nNecesario soporte VESA para este programa.\n");exit (1);

modo = getbest256();setmode (modo, &ConmutaBanco, &video_seg);getinfo (modo, &max_x, &max_y, &vram, &bancos);

for (banco=0; banco<bancos; banco++) setbank (ConmutaBanco, banco); /* direccionar banco */pantalla=MK_FP(video_seg, 0); /* normalmente 0xA000:0 */

if (banco!=bancos-1)limite=32768; /* todo el segmento de 64 Kb */

elselimite=(vram-banco*64)*512; /* palabras último banco */

for (i=0; i<=limite; i++) *pantalla++=0x55AA; /* pintar */

setbank (ConmutaBanco, 0);printf ("Modo de %dx%dx256 con %d Kb\n\n", max_x, max_y, vram);

/* COMPROBAR QUE EXISTE SOPORTE VESA */

unsigned testvesa(void)struct REGPACK r;char far *mem;unsigned vesa;

mem = farmalloc (256L);r.r_es = FP_SEG (mem); r.r_di = FP_OFF (mem);r.r_ax = 0x4F00; intr (0x10, &r);mem[4]=0; if (strcmp (mem, "VESA")==0) vesa=1; else vesa=0;farfree (mem);return (vesa);

/* BUSCAR EL MODO DE 256 COLORES DE MAYOR RESOLUCION */

unsigned getbest256 (void)

if (existe_modo (M1280x1024x256)) return (M1280x1024x256);if (existe_modo (M1024x768x256)) return (M1024x768x256);if (existe_modo (M800x600x256)) return (M800x600x256);if (existe_modo (M640x480x256)) return (M640x480x256);if (existe_modo (M640x400x256)) return (M640x400x256);return (0);

/* COMPROBAR LA EXISTENCIA DE UN MODO GRAFICO */

unsigned existe_modo (unsigned modo)

struct REGPACK r;unsigned far *mem, far *array;

mem = farmalloc (256L);r.r_es = FP_SEG (mem); r.r_di = FP_OFF (mem);r.r_ax=0x4F00; intr (0x10, &r);array = MK_FP (mem[8], mem[7]);farfree (mem);

while ((*array!=0xFFFF) && (*array!=modo)) array++;return (*array==modo);

/* 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, long *conmutar, unsigned *videoseg)

struct REGPACK r;long far *mem;

mem = farmalloc (256L);r.r_es = FP_SEG (mem); r.r_di = FP_OFF (mem);r.r_ax = 0x4F01; r.r_cx = modo; intr (0x10, &r);*conmutar = *(mem+3);*videoseg = *(mem+2);farfree (mem);r.r_ax=0x4F02; r.r_bx=modo; intr (0x10, &r);

/* OBTENER INFORMACION SOBRE UN MODO GRAFICO VESA */

void getinfo (unsigned modo, unsigned *max_x, unsigned *max_y,unsigned *vram, unsigned *bancos)

struct REGPACK r;unsigned far *mem;

mem = farmalloc (256L);r.r_es = FP_SEG (mem); r.r_di = FP_OFF (mem);r.r_ax = 0x4F01; r.r_cx = modo; intr (0x10, &r);

*max_x = mem[9]; *max_y = mem[10];*vram = (unsigned) ( (long) mem[8] * mem[10] / 1024L);farfree (mem);*bancos = *vram / 64;if (*vram % 64) (*bancos)++;

/* CONMUTAR DE BANCO CON LA MAXIMA VELOCIDAD */

void setbank (long direccion, unsigned banco)

asm mov ax,4f02hmov dx,bancomov bx,0call dword ptr direccion

Page 119: PCA, PS2 ,IBM y AT

119ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

7.5. - EL TECLADO.

En este apartado se estudiará a fondo el funcionamiento del teclado en los ordenadores compatibles,a tres niveles: bajo, intermedio y alto. En el capítulo 12 se documenta el funcionamiento del hardware delteclado, interesante para ciertas aplicaciones concretas, aunque para la mayor parte de las labores deprogramación no es necesario llegar a tanto.

7.5.1. - BAJO NIVEL.

Funcionamiento general del teclado.

Al pulsar una tecla se genera una interrupción 9 (IRQ 1) y el código de rastreo que identifica la teclapulsada puede leerse en el puerto de E/S 60h, tanto en XT como en AT (se corresponde en los AT con elregistro de salida del 8042); si se suelta la tecla se produce otra interrupción y se genera el mismo códigode rastreo+128 (bit 7 activo). Por ejemplo, si se pulsa la ’A’ se generará una INT 9 y aparecerá en el puertodel teclado (60h) el byte 1Eh, al soltar la ’A’ se generará otra INT 9 y se podrá leer el byte 9Eh del puertodel teclado (véase la tabla del apéndice V, donde se listan los códigos de rastreo del teclado).

Bajo el sistema DOS, el teclado del AT es idéntico al del XT en los códigos de rastreo ycomportamiento, debido a la traducción que efectúa el 8042 en el primero. No obstante, el teclado del ATposee unos comandos adicionales para controlar los LEDs. En otros sistemas operativos (normalmente UNIX)el teclado del AT es programado para trabajar en modo AT y pierde la compatibilidad con el del XT (loscódigos de rastreo son distintos y al soltar una tecla se producen dos interrupciones) pero bajo DOS esto nosucede en ningún caso y la compatibilidad es casi del 100%.

Las teclas expandidas -las que han sido añadidas al teclado estándar de 83/84 teclas- tienen uncomportamiento especial, ya que pueden generar hasta 4 interrupciones consecutivas (con un intervalo de unos1,5 milisegundos, ó 3 ms en los códigos dobles que convierte en uno el 8042) con objeto de emular, aunquebastante mal, ciertas combinaciones de las teclas no expandidas; en general es bastante deficiente laemulación por hardware y el controlador del teclado (KEYB) tiene que tratarlas de manera especial en lapráctica. Así, por ejemplo, cuando está inactivo NUM LOCK y se pulsa el cursor derecho expandido, segeneran dos interrupciones consecutivas: en la primera aparece un valor 0E0h en el puerto del teclado queindica que es una tecla expandida; en la segunda interrupción aparece el valor 4Dh: el mismo que hubieraaparecido pulsando el ’6’ del teclado numérico. Sin embargo, si NUM LOCK está activo, en un tecladonormal de 83 teclas hay que pulsar el ’6’ del teclado numérico junto con shift para que el cursor avance. Estose simula en el teclado expandido por medio de 4 interrupciones: En las dos primeras puede aparecer lasecuencia 0E0h-2Ah ó bien 0E0h-36h (2Ah y 36h son los códigos de las teclas shift normales): con esto sesimula que está pulsado shift aunque ello no sea realmente cierto (las BIOS más antiguas ignoran la mayoríade los bytes mayores de 128, entre ellos el 0E0h); después aparecen otras dos interrupciones con los valores0E0h-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, el cursor avanza a la derecha; al soltar latecla aparecerá la secuencia de interrupciones 0E0h-CDh-0E0h-0AAh, o en su defecto la secuenciaequivalente 0E0h-CDh-0E0h-0B6h. En general, estos códigos shift fantasma dan problemas cuando las teclasde SHIFT adquieren otro significado diferente que el de conmutar el estado NUM LOCK, lo que sucede encasi todos los editores de texto de los modernos compiladores. Por ello, la BIOS o el KEYB tratan de maneraespecial las teclas expandidas; en los ordenadores más antiguos (con BIOS -o al menos su tecnología- anteriora Noviembre de 1985), si no se carga el KEYB, el teclado expandido funcionará mal, incluso en EstadosUnidos -aunque las teclas estén bien colocadas-. Cuando se lee un valor 0E0h en una interrupción de teclado,el KEYB o la BIOS activan el bit 1 (el que vale 2) de la posición de memoria 0040h:0096h; en la siguienteinterrupción ese bit se borra y ya se sabe que el código leído es el de una tecla expandida. El bit 0 de esamisma 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, el único- y genera un prefijo 0E1h en vez del 0E0h habitual; dehecho, esta tecla no genera códigos al ser soltada, pero al pulsarla aparece la secuencia E1-1D-45-E1-9D-C5).

Page 120: PCA, PS2 ,IBM y AT

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

El buffer del teclado.

Cuando se pulsa una tecla normal, la rutina que gestiona INT 9 deposita en un buffer dos bytes consu código ASCII y el código de rastreo, para cuando el programa principal decida explorar el teclado -lo harásiempre consultando el buffer-. Si el código ASCII depositado es cero ó 0E0h, se trata de una tecla especial(ALT-x, cursor, etc.) y el segundo byte indica cuál (son los denominados códigos secundarios). El códigoASCII 0E0h sólo es generado en los teclados expandidos por las teclas expandidas (marcadas como ’Ex’ enla tabla de códigos de rastreo del apéndice V), aunque las funciones estándar de la BIOS y del DOS queinforman del teclado lo convierten en cero para compatibilizar con teclados no expandidos. Así mismo, elcódigo ASCII 0F0h está reservado para indicar las combinaciones de ALT-tecla que no fueron consideradasinicialmente en el software de soporte de los teclados no expandidos, pero sí actualmente (de esta manera,las rutinas de la BIOS saben si deben informar de estas teclas o no según se esté empleando una funciónavanzada u obsoleta, para compatibilizar). En todo caso, las secuencias introducidas por medio de ALT-teclado_numérico llevan asociado un código de rastreo 0, por lo que el usuario puede generar los caracteresASCII 0E0h y 0F0h sin que se confundan con combinaciones especiales; además, según IBM, si el códigoASCII 0 va acompañado de un código de rastreo 3 los programas deberían interpretarlo como un auténticocódigo ASCII 0 (esta secuencia se obtiene con Ctrl-2) lo que permite recuperar ese código perdido en indicarcombinaciones especiales.

Es importante señalar que aunque el buffer (organizado como cola circular) normalmente está situadoentre 0040h:001Eh y 0040h:003Eh, ello no siempre es así; realmente el offset del inicio y el fin del bufferrespecto al segmento 0040h lo determinan las variables (tamaño palabra) situadas en 0040h:0080h y0040h:0082h en todos los ordenadores posteriores a 1981. Por ello, la inmensa mayoría de las pequeñasutilidades de las revistas y los ejemplos de los libros son, por desgracia, incorrectos: la manera correcta decolocar un valor en el buffer -para simular, por ejemplo, la pulsación de una tecla- o extraerlo del mismo escomprobando adecuadamente los desbordamientos de los punteros teniendo en cuenta las variablesmencionadas. El puntero al inicio del buffer es una variable tamaño palabra almacenada en la posición0040h:001Ah y el fin otra ubicada en 0040h:001Ch. El siguiente ejemplo introduce un carácter de códigoASCII AL y código de rastreo AH (es cómodo y válido hacer AH=0) en el buffer del teclado:

MOV BX,40h ; meter carácter AX en el buffer del tecladoMOV DS,BXCLI ; evitar conflictos con interrupcionesMOV BX,DS:[1Ch] ; puntero a la cola del bufferMOV CX,BXADD CX,2 ; apuntar CX al siguiente datoCMP CX,DS:[82h] ; más allá del fin del bufferJB no_desbMOV CX,DS:[80h] ; inicio de la cola circular

no_desb: CMP CX,DS:[1Ah] ; puntero al inicio del bufferJE fin_rutina ; ZF = 1 --> buffer llenoMOV DS:[BX],AX ; introducir carácter ASCII (AL) en el bufferMOV DS:[1Ch],CX ; actualizar puntero al final del bufferCMP SP,0 ; ZF=0 (SP siempre <> 0) --> buffer no lleno

fin_rutina: STI

El valor 0 para el código de rastreo es usado para introducir también algunos caracteres especiales,como las vocales acentuadas, etc., aunque por lo general no es demasiado importante su valor (de hecho, losprogramas suelen comprobar preferentemente el código ASCII; de lo contrario, en un teclado español y otrofrancés, ¡la tecla Z tendría distinto código!). No estaría de más en este ejemplo comprobar si las variables40h:80h y 40h:82h son distintas de cero por si el ordenador es demasiado antiguo, medida de seguridad quede hecho toma el KEYB del DR-DOS (en estas máquinas además no es conveniente ampliar el tamaño delbuffer cambiándolo de sitio, por ejemplo; lo normal es que esté entre 40h:1Eh y 40h:3Eh). En el apéndiceV se listan los códigos secundarios: son el segundo byte (el más significativo) de la palabra depositada enel buffer del teclado por la BIOS o el KEYB.

Gestión de la interrupción del teclado.

He aquí un ejemplo de una subrutina que intercepta la interrupción del teclado apoyándose en elcontrolador habitual y limitándose a detectar las teclas pulsadas, espiando lo que sucede pero sin alterar la

Page 121: PCA, PS2 ,IBM y AT

121ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

operación normal del teclado:

nueva_int9: STI ; permitir interrupción periódicaPUSH AX ; preservar registros modificadosIN AL,60h ; código de la tecla pulsadaPUSHF ; preparar la pila para IRETCALL CS:anterior_int9 ; llamar a la INT 9 original

; hacer algo con esa teclaPOP AX ; restaurar registros modificadosIRET ; volver al programa principal

Evidentemente, es necesario preservar y restaurar todos los registros modificados, como en cualquierotra interrupción hardware, dado que puede producirse en el momento más insospechado y no debe afectara la marcha del programa principal, anterior_int9 es una variable de 32 bits que contiene la dirección de lainterrupción del teclado antes de instalar la nueva rutina. Es necesario hacer PUSHF antes de llamar porquela subrutina invocada va a retornar con IRET y no con RETF. En general, el duo PUSHF/CALL es unamanera alternativa de simular una instrucción INT.

Si se implementa totalmente el control de una tecla en una rutina que gestione INT 9 -sin llamar alprincipio o al final al anterior gestor-, en los XT hay que enviar una señal de reconocimiento al tecladoponiendo a 1 y después a 0 el bit 7 del puerto de E/S 61h (en AT no es necesario, aunque tampoco resultaperjudicial hurgar en ese bit en las máquinas fabricadas hasta ahora); es importante no enviar más de unaseñal de reconocimiento, algo innecesario por otra parte, de cara a evitar anomalías importantes en el tecladode los XT. Además, tanto en XT como AT hay que enviar en este caso una señal de fin de interrupciónhardware (EOI) al 8259 (con un simple MOV AL,20h; OUT 20h,AL) al igual que cuando se gestionacualquier otra interrupción hardware. El ejemplo anterior quedaría como sigue:

nueva_int9: STIPUSH AXIN AL,60h ; código de la tecla pulsadaCMP AL,tecla ; ¿es nuestra tecla?JNE fin ; noPUSH AX ; vamos a «manchar» AXIN AL,61hOR AL,10000000bOUT 61h,ALAND AL,01111111bOUT 61h,AL ; señal de reconocimiento enviadaPOP AX ; AL = tecla pulsada

; gestionarlaMOV AL,20hOUT 20h,AL ; EOI al 8259POP AX ; AX del programa principalIRET ; volver al programa principal

fin: POP AX ; AX del programa principalJMP CS:anterior_int9 ; saltar al gestor previo de INT 9

Como se puede observar, esta rutina gestiona una tecla y las demás se las deja al KEYB o la BIOS.Sólo en el caso de que la gestione él es preciso enviar una señal de reconocimiento y un EOI al 8259. Encaso contrario, se salta al controlador previo a esta rutina con un JMP largo (segmento:offset); ahora no espreciso el PUSHF, como en el caso del CALL, por razones obvias. La instrucción STI del principio habilitalas interrupciones, siempre inhibidas al principio de una interrupción -valga la redundancia-, lo que esconveniente para permitir que se produzcan más interrupciones -por ejemplo, la del temporizador, que llevanada menos que la hora interna del ordenador-. En el ejemplo, el EOI es enviado justo antes de terminar degestionar esa tecla; ello significa que mientras se la procesa, las interrupciones hardware de menor prioridad -todas, menos el temporizador- están inhibidas por mucho que se haga STI; el programador ha de decidir puessi es preciso enviar antes o no el EOI (véase la documentación sobre el controlador de interrupciones 8259de los capítulos posteriores), aunque si la rutina es corta no habrá demasiada prisa.

Es habitual en los controladores de teclado de AT (tanto la BIOS como el KEYB del MS-DOS)deshabilitar el teclado mientras se procesa la tecla recién leída, habilitándolo de nuevo al final, por medio delos comandos 0ADh y 0AEh enviados al 8042. Sin embargo, la mayoría de las utilidades residentes no toman

Page 122: PCA, PS2 ,IBM y AT

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

estas precauciones tan sofisticadas (de hecho, el KEYB del DR-DOS tampoco). Lógicamente sólo se puedenenviar comandos al 8042 cuando el registro de entrada del mismo está vacío, lo que puede verificarsechequeando el bit 1 del registro de estado: no es conveniente realizar un bucle infinito que dejaría colgadoel ordenador de fallar el 8042, de ahí que sea recomendable un bucle que repita sólo durante un cierto tiempo;en el ejemplo se utiliza la temporización del refresco de la memoria dinámica de los AT para no emplear másde 15 ms esperando al 8042. Además las interrupciones han de estar inhibidas en el momento crítico en quedura el envío del comando, aunque cuidando de que sea durante el menor tiempo posible:

nueva_int9: STI ; breve ventana para interrupcionesPUSH AXCALL esperaMOV AL,0ADhOUT 60h,AL ; inhibir tecladoCALL esperaIN AL,60h ; ¿tecla?STI ; permitir rápidamente interrupciones... ; procesar tecla y enviar EOI al 8259CALL esperaMOV AL,0AEhOUT 60h,AL ; desinhibir tecladoPOP AXIRET ; no merece la pena hacer STI

espera: PUSH AXPUSH CXMOV CX,995 ; constante para 15 msCLI

testref: IN AL,61hAND AL,10h ; método válido solo en ATCMP AL,AHJZ testrefMOV AH,ALIN AL,64h ; registro de estado del 8042TEST AL,2 ; ¿buffer de entrada lleno?LOOPNZ testref ; así esPOP CXPOP AXRET

7.5.2. - NIVEL INTERMEDIO.

Consulta de SHIFT, CTRL, ALT, etc (marcas de teclado).

Estas teclas pueden ser pulsadas para modificar el resultado de la pulsación de otras. IBM no hadefinido combinaciones con ellas (excepto CTRL-ALT, que sirve para reinicializar el sistema si se pulsa enconjunción con DEL) por lo que los programas residentes suelen precisamente emplear combinaciones de doso más teclas de estas para activarse sin eliminar prestaciones al teclado; por defecto, si se pulsan dos o másteclas de estas la BIOS o el KEYB asignan prioridades y consideran sólo una de ellas: ALT es la tecla demayor prioridad, seguida de CTRL y de SHIFT. Por otra parte, cabe destacar el hecho de que CTRL, ALTy SHIFT (al igual que Num Lock, Caps Lock, Scroll Lock e Ins) no poseen la característica de autorepeticiónde las demás teclas debido a la gestión que realiza la BIOS o el KEYB.

- Teclado no expandido.

Llamando con AH=2 a la INT 16h (función 2 de la BIOS para el teclado), se devuelve en AL un bytecon información sobre las teclas de control (SHIFT, CTRL, etc.) que es el mismo byte almacenado en0040h:0017h (véase en el apéndice III el área de datos de la BIOS y las funciones de la BIOS para teclado).En 0040h:0018h, existe otro byte de información adicional, aunque no hay función BIOS para consultarloen los teclados no expandidos, por lo que a menudo es necesario leerlo directamente. Por lo general es mejoremplear las funciones BIOS, si existen, que consultar directamente un bit, por razones de compatibilidad.Evidentemente, todas las funciones para teclados no expandidos pueden usarse también con los expandidos.

Page 123: PCA, PS2 ,IBM y AT

123ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

- Teclado expandido.

A partir de 0040h:0096h hay otros bytes con información adicional y específica sobre el teclado delAT y los teclados expandidos: parte de esta información, así como de la de 0040:0018h, puede ser consultadaen los teclados expandidos con la función 12h de la BIOS del teclado expandido, que devuelve en AX unapalabra: en AL de nuevo el byte de 0040h:0017h y en AH otro byte mezcla de diversas posiciones dememoria con información útil (consultar funciones de la BIOS para teclado).

Los bits de 40h:96h sólo son fiables si está instalado el KEYB del MS-DOS o 99% compatible; porejemplo, el KEYB del DR-DOS 5.0/6.0 (excepto en modo KEYB US) no gestiona correctamente el bit deAltGr, aunque sí los demás bits. Antes de usar esta función conviene asegurarse de que está soportada porla BIOS o el KEYB instalado.

Lectura de teclas ordinarias.

Con la función 0 de la INT 16h (AH=0 al llamar) se lee una tecla del buffer del teclado, esperandosu pulsación si es preciso, y se devuelve en AX (AH código de rastreo y AL código ASCII); con la función1 (AH=1 al llamar a INT 16h) se devuelve también en AX el carácter del buffer pero sin sacarlo (habrá quellamar de nuevo con AH=0), aunque en este caso no se espera a que se pulse una tecla (si el buffer estabavacío se retorna con ZF=1 en el registro de estado). En los equipos con soporte para teclado expandidoexisten 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 0E0hen 0, así como la función 5 (introducir caracteres en el buffer).

Combinaciones especiales de teclas.

- BREAK: se obtiene pulsando CTRL-PAUSE en los teclados expandidos (CTRL-SCROLL LOCK en losno expandidos). El controlador del teclado introduce una palabra a cero en el buffer e invoca la interrupción1Bh. Los programas pueden interceptar esta interrupción para realizar ciertas tareas críticas antes de terminarsu ejecución (ciertas rutinas del DOS, básicamente las de impresión por pantalla, detectan BREAK y abortanel programa en curso).

- PAUSE: se obtiene con dicha tecla o bien con CTRL-NUM LOCK (teclados no expandidos); provoca queel ordenador se detenga hasta que se pulse una tecla no modificadora (ni SHIFT, ni ALT, etc.), tecla que seráignorada pero servirá para abandonar la pausa. La pausa es interna a la rutina de control del teclado.

- PTR SCR (SHIFT con el (*) del teclado numérico en teclados no expandidos): vuelca la pantalla porimpresora al ejecutar una INT 5.

- SYS REQ: al pulsarla genera una INT 15h (AX=8500h) y al soltarla otra INT 15h (AX=8501h).

- CTRL-ALT-DEL: el controlador del teclado coloca la palabra 1234h en 0040h:0072h (para evitar elchequeo de la memoria) y salta a la dirección 0FFFFh:0 reinicializando el ordenador.

- ALT-teclado_numérico: manteniendo pulsada ALT se puede teclear en el teclado numérico un valornumérico en decimal; al soltar ALT el código ASCII que representa se introducirá en el buffer. El controladordel teclado almacena en 40h:19h el número en proceso de formación: cada vez que llega un nuevo dígitomultiplica el contenido anterior por 10 y se lo suma. Al soltar ALT, se hace 40h:19h=0.

Detección de soporte para teclado expandido.

Normalmente no será necesario distinguir entre un teclado expandido o estándar, aunque en algunoscasos habrá que tener en cuenta la posible pulsación de una tecla expandida y su código 0E0h asociado. Entodo caso, el bit 4 de 0040h:0096h indica si el teclado es expandido; sin embargo es suicida fiarse de esto

Page 124: PCA, PS2 ,IBM y AT

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

y es más seguro chequear por otros medios la presencia de funciones de la BIOS para teclado expandido antesde usarlas. En teoría, las BIOS de AT del 15 de noviembre de 1985 en adelante soportan las funciones 5, 10hy 11h; los de XT a partir del 10 de enero de 1986 soportan la 10h y la 11h. Sin embargo, en la práctica todasellas normalmente están disponibles también en cualquier máquina más antigua si tiene instalado un KEYBeficiente, venga equipada o no con teclado expandido. Por ello, lo ideal es chequear la presencia de estasfunciones por otros procedimientos. Por ejemplo: llamar a la función 12h con AL=0. Por desgracia, si lafunción no está implementada no devuelve el acarreo activo para indicar el error. Pero hay un truco: si elresultado sigue siendo AX=1200h, las funciones de teclado expandido no están soportadas. Esto se debe aque al no estar implementada la función, nadie ha cambiado el valor de AX: además, en caso de estarimplementada no podría devolver 1200h porque ello significaría una contradicción entre AH y AL.

MOV AX,1200hINT 16h ; invocar función teclado expandidoCMP AX,1200hJE no_expandido ; función no soportadaJMP si_expandido ; función soportada

Posibilidades avanzadas.

La rutina de la BIOS del AT (y de los KEYB) que lee el buffer del teclado, cuando no hay teclasy tiene que esperar por las mismas ejecuta de manera regular la función 90h (AH=90h) de la interrupción 15hindicando una espera de teclado al llamar (AL=2). De esta manera, un hipotético avanzado sistema operativopodría aprovechar ese tiempo muerto para algo más útil. Así mismo, cuando un carácter acaba de serintroducido en el buffer del teclado, se ejecuta la función 91h para indicar que ya ha finalizado la entraday hay caracteres disponibles. En general, estas características no son útiles en el entorno DOS y, por otraparte, han sido deficientemente normalizadas. Por ejemplo, al acentuar incorrectamente se generan doscaracteres (además del familiar pitido): el KEYB del MS-DOS sólo ejecuta una llamada a la INT 15h conla función 91h (pese a haber introducido dos caracteres en el buffer) y el de DR-DOS hace las dos llamadas...

Lo que sí puede resultar más interesante es la función de intercepción de código del teclado: las BIOSde AT no demasiado antiguas y el programa KEYB, tras leer el código de rastreo en AL, activan el acarreoy ejecutan inmediatamente la función 4Fh de la INT 15h para permitir que alguien se de por enterado de latecla y opcionalmente aproveche para manipular AL y simular que se ha pulsado otra tecla: ese alguien puededevolver además el acarreo borrado para indicar al KEYB que no continúe procesando esa tecla y que laignore (en caso contrario se procedería a interpretarla normalmente). 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:BXy comprobar que el bit 4 de la posición direccionada por ES:[BX+5] está activo. Alternativamente, puedeverificarse la presencia del programa KEYB, lo que también permite emplear esta función en los PC/XT,aunque es más arriesgado. Para detectar la presencia del KEYB del MS-DOS en memoria basta con llamara la interrupción 2Fh con AX=0AD80h y comprobar que devuelve AL=0FFh (esta función devuelve laversión del KEYB en BX y un puntero a un área de datos en ES:DI). [DR-DOS usa AX=0AD00h].

Consideraciones finales.

Conviene señalar que los teclados de AT pueden generar interrupciones aunque no se pulsen teclas,normalmente para devolver una señal de reconocimiento cuando alguien les ha enviado algo -por ejemplo,la BIOS puede enviar un comando para cambiar los led’s-; por ello, en el momento más insospechado puedeproducirse una INT 9 con el código de rastreo 0FAh, y la secuencia de interrupciones generada por las teclasque tienen asociado un led en los AT, debido a los códigos 0FAh, no es exactamente idéntica a la de los XT,aunque se trata de un detalle poco relevante -incluso para quienes pretendan hacer algo especial con estasteclas-. También es conveniente indicar que en los AT se puede leer puerto del teclado, para averiguar laúltima tecla pulsada o soltada, en casi cualquier momento -por ejemplo, periódicamente desde la interrupcióndel temporizador-. De todas formas, esta práctica tiene efectos secundarios debidos al mal diseño del softwaredel sistema de los AT (tales como teclas shift que se enganchan, como si se quedaran pulsadas, numeritosque aparecen al pulsar los cursores expandidos, etc.). Además, en los XT sólo se obtendrá una lectura correctainmediatamente después de producirse la interrupción del teclado y antes de enviar la correspondiente señal

Page 125: PCA, PS2 ,IBM y AT

125ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

de reconocimiento al mismo -por tanto, no desde una interrupción periódica-. Todo esto desaconseja la lecturadel puerto del teclado desde cualquier otro sitio que no sea INT 9, salvo contadas excepciones.

Por último indicar que en los AT se puede modificar el estado de CAPS LOCK, NUM LOCK oSCROLL LOCK por el simple procedimiento de alterar el bit correspondiente en 40h:17h; dicho cambio severá reflejado en los led’s cuando el usuario pulse una tecla o el programa lea el teclado con cualquierfunción -en la práctica, de manera casi instantánea-. Sin embargo, para aplicar esta técnica es aconsejableverificar que se trata de un AT porque en los PC/XT el led -si existe- no se actualiza y pasa a indicar unainformación incorrecta. Realmente, en los XT, el control de los led lo lleva la propia circuitería del tecladode manera independiente al ordenador.

7.5.3. - ALTO NIVEL.

El acceso al teclado a alto nivel puede realizarse a través de las funciones 1, 6, 7, 8 y 0Ah del DOS,considerándolo como dispositivo de entrada estándar. Algunas de estas funciones, si devuelven un 0, se tratade una tecla especial y la siguiente lectura devuelve el código secundario. El DOS utiliza las funciones BIOS.

7.6. - LOS DISCOS.

7.6.1. - ESTRUCTURA FISICA.

Los discos son el principal medio de almacenamiento externo de los ordenadores compatibles. Puedenser unidades de disco flexible, removibles, o discos duros -fijos-. Constan básicamente de una superficiemagnética circular dividida en pistas concéntricas, cada una de las cuales se subdivide a su vez en ciertonúmero de sectores de tamaño fijo. Como normalmente se emplean ambas caras de la superficie, la unidadmás elemental posee en la actualidad dos cabezas de lectura/escritura, una para cada lado del disco. Los tresparámetros comunes a todos los discos son, por tanto: el número de cabezas, el de pistas y el de sectores.El término cilindro i hace referencia a la totalidad de las pistas i de todas las caras. Bajo DOS, los sectorestienen un tamaño de 512 bytes (tanto en discos duros como en disquetes) que es difícil cambiar (aunque noimposible). Los sectores se numeran a partir de 1, mientras que las pistas y las caras lo hacen desde 0. ElDOS convierte esta estructura física de tres parámetros a otra: el número de sector lógico, que se numera apartir de 0 (los sectores físicos les denominaremos a partir de ahora sectores BIOS para distinguirlos de lossectores lógicos del DOS). Para un disco de SECTPISTA sectores BIOS por pista y NUMCAB cabezas, lossectores lógicos se relacionan con la estructura física por la siguiente fórmula:

Sector lógico = (sector_BIOS - 1) + cara * SECTPISTA + cilindro * SECTPISTA * NUMCAB - X1

Es decir, el DOS recorre el disco empezando la pista 0 (la exterior, la más alejada del centro) y porla cara o cabezal 0, recorriendo todos los sectores; luego avanza una cara y recorre de nuevo todos lossectores; después pasa al siguiente cilindro... y repite de nuevo el proceso. De esta manera, varios cabezalespodrían -hipotéticamente- leer bloques de información consecutivos simultáneamente. En los disquetes, X1=0,pero en los discos duros se resta un cierto factor de compensación X1, ya que éstos pueden estar divididosen varias particiones y la que usa el DOS puede no estar al principio del mismo. En general, un disco durodividido en varias particiones de tipo DOS determina varias unidades lógicas de disco, cada una de las cualesdispone de un conjunto de sectores lógicos numerados a partir de 0 y un factor de compensación propio parala fórmula. Las siguientes fórmulas transforman sectores DOS en sus correspondientes BIOS:

Sector_BIOS = (sector MOD SECTPISTA) + 1Cara = (sector / SECTPISTA) MOD NUMCABCilindro = sector / (SECTPISTA * NUMCAB) + X2

Como la partición del DOS no suele empezar en el cilindro 0 (reservado en gran parte para la tablade 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 elcilindro efectivo; esto es así porque en la práctica las particiones suelen empezar y acabar ocupando cilindrosenteros y exactos (aunque en realidad, y dada la arquitectura de la tabla de partición, podrían empezar y

Page 126: PCA, PS2 ,IBM y AT

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

acabar no sólo en un determinado cilindro sino también en cierto sector y cara del disco, pero no esfrecuente). X1 y X2 se obtienen consultando e interpretando la tabla de particiones o el sector de arranque.

7.6.2. - CABEZA 0. PISTA 0. SECTOR 1.

El primer sector físico de todos los discos contiene información especial (el sector_BIOS 1 delcilindro 0 y cabezal 0). Tanto en disquetes como en discos duros, contiene un pequeño programa que seencarga de poner en marcha el ordenador: es el sector de arranque de los disquetes, o bien el código de latabla de particiones de los discos duros. En este último caso, ese programa realiza una tarea muy sencilla:consulta la tabla de particiones ubicada en ese mismo sector, determina cuál es la partición activa y dóndeempieza y acaba; a continuación carga el sector lógico 0 de esa partición (sector de arranque) y lo ejecuta.En los disquetes no existe este paso intermedio: el sector físico 0 del disquete, en terminos absolutos, es yael sector de arranque y no el de partición. Esto es así porque los disquetes contienen poca información y sonbaratos, no siendo preciso particionarlos para compartirlos con varios sistemas operativos. El programaubicado en el sector de arranque busca el fichero oculto del sistema IBMBIO.COM o IO.SYS, lo carga y leentrega el control. El programa contenido en este fichero cargará a su vez IBMDOS.COM o MSDOS.SYS,el cual a su vez cargará finalmente el intérprete de comandos (normalmente, COMMAND.COM).

Formato de la tabla de partición de los discos duros:

Esta tabla comienza en unoffset 1BEh del sector (al principioestá el código ejecutable); cadapartición de las 4 posibles ocupa16 bytes; al final de las cuatro estála marca 0AA55h, ubicada en eloffset 1FEh, que indica que la tablaes válida. Los 16 bytes que laforman se interpretan como indicael cuadro de la derecha:

byte 0: 0 para partición inactiva, 80h en la de arranque.byte 1: cabeza donde comienza la partición.byte 2: bits 0 al 5: sector de inicio de la partición; 6, 7: parte alta del

número de cilindro.byte 3: parte baja del número de cilindro de inicio de la partición.byte 4: tipo de partición, las más comunes son 0: No usada; 1: DOS-12 (FAT

12 bits); 4: DOS-16 (FAT 16 bits); 5: DOS Extendida; 6:BIGDOS (másde 32Mb); 7: OS/2 HPFS ó WinNT NTFS; 0Ah: OS/2 Boot Manager; 0Bh:32-bit FAT Win95 (0Ch con LBA); 0Eh y 0Fh (como 06 y 05 pero conLBA); 81h Linux; 82h Linux swap; 83h: Linux native; 0A5h: FreeBSDo BSD/386; 0F2h: partición secundaria (no estudiada en este libro).

byte 5: cabeza donde termina la partición.byte 6: bits 0 al 5: sector de fin de la partición; 6, 7: parte alta del

número de cilindro.byte 7: parte baja del número de cilindro de fin de la partición.bytes 8 al 11: Doble palabra que indica el sector relativo (en todo el

disco) en que comienza la partición, expresado en sectores.bytes 12 al 15: Doble palabra con el tamaño de esa partición en sectores.

Formato de la TABLA DE PARTICIÓN

Habitualmente, las particiones suelen empezar en el segundo cabezal del cilindro 0, con lo que todala primera pista física del disco duro está vacía. Lugar ideal para virus, algunos fabricantes han utilizado estainteresante característica para mejorar el arranque, colocando una falsa tabla de partición que muestre unmenú en pantalla y cargue después la partición de verdad, permitiendo también más de 4 particiones. Sinembargo, estas maniobras suelen reducir la compatibilidad. Existen también código de particiones sofisticadoque permite seleccionar una de las 4 particiones manteniendo pulsada una tecla en el arranque, sin tener queandar ejecutando FDISK para seleccionar la partición activa... ¡lo que se puede hacer con 400 bytes decódigo!. Realmente, la arquitectura global de las particiones de un equipo (en particular si tiene más de 4,una mezcla de sistemas operativos y/o varios discos duros), puede llegar a ser compleja: practíquese con unbuen editor de disco para aprender más (ej. el DISKEDIT de las Norton Utilities o las PC-Tools).

Las particiones extendidas llevan su propio sector de partición adicional, en el que no hay código deprograma sino, en su lugar, una lista de dispositivos. Hay dos entradas por cada dispositivo: la primera indicael tipo (1-FAT12, 4-FAT16); la segunda entrada apunta al siguiente dispositivo (caso de existir) o es 0 (nohay más dispositivos). El DOS 4.0 y posteriores eliminaron la limitación de los 32 Mb en las particiones yel software actual, ya actualizado, no da problemas con los discos de más de 32 Mb. Por ello, en discos demás de 32 ó 40 Mb lo normal es instalar DOS 4.0 ó superior.

Formato del sector de arranque:

En el sector de arranque, además del sencillo programa de puesta en marcha del sistema, hay ciertainformación útil acerca de las características del disco o partición. 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,

Page 127: PCA, PS2 ,IBM y AT

127ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

aunque conviene que dicha instrucción de salto esté al principio del sector de arranque para que algunossistemas validen dicho sector (es válido un salto corto seguido de NOP o un salto completo de 3 bytes). Apartir del cuarto (offset 3) se puede encontrar la información válida. En el sector de arranque del disqueteestá contenido el BPB (Bios Parameter Block) que analizaremos más tarde.

offset 3 (8 bytes): Identificación del sistema (ej., "IBM 3.3")offset 11 (1 palabra): Bytes por sector, ej. 512.offset 13 (1 byte): Sectores por cluster (ej. 2)offset 14 (1 palabra): Sectores reservados al principio (1 en diquettes)offset 16 (1 byte): Número de copias de la FAT (2 normalmente)offset 17 (1 palabra): Número de entradas al directorio raíz (112 en discos de 360 Kb)offset 19 (1 palabra): Número total de sectores del disco (0 en discos de más de 32 Mb)offset 21 (1 byte): Byte de tipo de disco (véase tabla más adelante)offset 22 (1 palabra): Número de sectores ocupados por cada FAToffset 24 (1 palabra): Número de sectores por pistaoffset 26 (1 palabra): Número de cabezas (2 en disquetes de doble cara)offset 28 (2 palabras): Número de sectores especiales reservados. Nota: sólo se debe considerar la primera mitad de

esta doble palabra en versiones del sistema 3.30 o anteriores (no hay problemas con DR-DOS,que en todas sus versiones, hasta la 6.0 incluida, es un DOS 3.31). El valor de este campodepende de la posición relativa que ocupe la partición dentro del disco duro (será 0 en losdisquetes), este valor ha de sumarse al del número de sector del DOS antes de traducirlo aun número de sector de la BIOS.

offset 32 (2 palabras): Número total de sectores del disco en discos de más de 32 Mb (esta información sólo debeobtenerse de aquí si la palabra ubicada en el offset 19 es cero).

offset 36 (1 byte): Número de unidad física (a partir del DOS 4.0).offset 37 (1 byte): Reservado.offset 38 (1 byte): valor 29h desde DOS 4.0 (marca de validación que indica que los bytes ubicados desde el

offset 36 al offset 61 están definidos).offset 39 (2 palabras): Número de serie del disco (a partir de DOS 4.0).offset 43 (11 bytes): Título del disco (desde DOS 4.0); por defecto se inicializa con "NO NAME ", aunque tanto

el DOS 4.0 como el 5.0 y 6.X siguen empleando además las tradicionales etiquetas de volumen.offset 54 (8 bytes): Sistema de ficheros (a partir de DOS 4.0): puede ser "FAT12 " o "FAT16 ".

Formato del SECTOR DE ARRANQUE

El byte del tipo de disco (offset 21) intenta identificar el tipo de disco, aunque no lo consigue enmuchos casos dada la ilógica utilización que se ha hecho de él. La recomendación es hacer lo que vienehaciendo el DOS desde la 3.30: no hacer caso de lo que dice este byte para identificar los discos. La únicaexcepción tal vez sea el valor 0F8h que identifica a los dispositivos no removibles:

0FEh - discos de 5¼-160 Kb (1 cara, 8 sectores/pista, 40 pistas)0FFh - discos de 5¼-320 Kb (2 caras, 8 sectores/pista, 40 pistas)0FCh - discos de 5¼-180 Kb (1 cara, 9 sectores/pista, 40 pistas)0FDh - discos de 5¼-360 Kb (2 caras, 9 sectores/pista, 40 pistas)0F9h - discos de 5¼-1,2 Mb (2 caras, 15 sectores/pista, 80 pistas)0F9h - discos de 3½-720 Kb (2 caras, 9 sectores/pista, 80 pistas)0F8h - discos duros y algunos virtuales0F0h - discos de 3½-1,44 Mb (2 caras, 18 sectores/pista, 80 pistas)0F0h - discos de 3½-2,88 Mb (2 caras, 36 sectores/pista, 80 pistas)0F0h - restantes formatos de disco

Tipos de Discos

7.6.3. - LA FAT.

Después del sector de arranque, aparecen en el disco una serie de sectores que constituyen la Tablade Localización de Ficheros (File Alocation Table o FAT). Consiste en una especie de mapa que indica quézonas del disco están libres, cuáles ocupadas, dónde están los sectores defectuosos, etc. Normalmente hay doscopias consecutivas de la FAT (véase el offset 16 del sector de arranque), ya que es el área más importantedel disco de la que dependen todos los demás datos almacenados en él. No deja de resultar extraño que ambascopias de la FAT estén físicamente consecutivas en el disco: si accidentalmente se estropeara una de ellas(por ejemplo, rayando con un bolígrafo el disco) lo más normal es que la otra también resultara dañada. Engeneral, muchos programas de chequeo de disco no se molestan en verificar si ambas FAT son idénticas(empezando por algunas versiones de CHKDSK). Por otra parte, hubiera sido mejor elección haberla colocadoen el centro del disco: dada la frecuencia de los accesos a la misma, de cara a localizar los diferentesfragmentos de los ficheros, ello mejoraría notablemente el tiempo de acceso medio. Aunque cierto es que loscachés de disco y los buffers del config.sys pueden hacer casi milagros... a costa de memoria.

Antes de seguir adelante, conviene hacer un pequeño paréntesis y explicar el concepto de cluster: uncluster es la unidad mínima de información a la que accede el DOS, desde el punto de vista lógico.Normalmente consta de varios sectores (ver offset 13 del sector de arranque): dos en un disquete de 360 Kb,

Page 128: PCA, PS2 ,IBM y AT

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

uno en un disquete de alta densidad, y entre 4 y 16 -normalmente- en un disco duro. El disco queda dividido,por tanto, en un cierto número de clusters. La FAT es realmente un mapa que contiene 12 ó 16 bits -comoveremos- por cada cluster, indicando su estado:

cluster libre: valor 0cluster defectuoso: valores 0FF7h (ó 0FFF7h).cluster no utilizable: valores 0FF5 al 0FF6h (ó 0FFF5 al 0FFF6h).último cluster del fichero: valor 0FF8 al 0FFFh (ó 0FFF8h al 0FFFFh).otro valor: puntero al siguiente cluster del fichero.

Los ficheros en disco no siempre ocupan posiciones contiguas: normalmente están más o menosfragmentados debido a que se aprovechan los huecos dejados por otros ficheros borrados, de ahí el auge delos programas que compactan los discos con objeto de acelerar el acceso a los datos. Por tanto, cada ficheroconsta de un cluster inicial indicado en la entrada del directorio -como se verá- que inicia una cadena tanlarga como la longitud del mismo (expresada en clusters), existiendo normalmente un valor 0FFFh ó 0FFFFhen el último cluster para señalar el final (del 0FF8h al 0FFEh y del 0FFF8h al 0FFFEh no se emplean).Consultando la FAT se puede determinar la ubicación de los fragmentos en que están físicamente divididoslos ficheros en los discos, así como qué zonas están aún disponibles y cuáles son defectuosas en el mismo.Los cluster se numeran a partir de 2, ya que las dos primeras entradas en la FAT están reservadas para elsistema. Los clusters hacen referencia exclusiva a la zona de datos: el área que va detrás del sector dearranque, la FAT y el directorio. Por ello, en un disquete de 360 Kb, con clusters de 1 Kb y 354 Kb librespara datos, hay 354 clusters (numerados de 2 a 355) y los 6 Kb misteriosos que faltan son el sector dearranque, las dos FAT y -como veremos después- el directorio raíz. Puede ser válida, por ejemplo, lasiguiente FAT de 12 bits habiendo un fichero A que ocupe los clusters 2, 3, 5 y 6:

Elemento de la FAT Valor Interpretación0 FFD El disco es de tipo 0FDh (despreciar restantes bits)1 FFF Entrada no utilizada2 003 El siguiente cluster del fichero A es el 33 005 El siguiente cluster del fichero A es el 54 FF7 Cluster defectuoso5 006 El siguiente cluster del fichero A es el 66 FFF Este es el último cluster del fichero A7 013 El siguiente cluster del fichero B es el 013... ...

Como se ve, el primer byte de la primera entrada a la FAT es inicializado con el mismo valor queel byte de tipo de disco del sector de arranque. Los restantes bits de las dos primeras entradas suelen estartodos a 1. Para determinar el número de clusters del disco, ha de restarse del número total de sectores la cifracorrespondiente al número de sectores reservados (normalmente 1 en los disquetes, correspondiente al sectorde arranque), los que ocupa la FAT y los empleados por el directorio raíz (que se verá más adelante); acontinuación se divide ese número de sectores de datos resultante por el número de sectores por cluster.

El hecho de emplear FAT’s de 12 bits es debido a que con menos bits (ej., un byte) sólo podría haberunos 250 clusters en el disco. En un disco de 1,2 Mb ello significaría que la unidad mínima de informaciónsería 1200/250 = 5 Kb: el fichero más pequeño (de 1 byte) ocuparía ¡5 Kb!. Empleando FAT’s de 16 bitsse podrían hacer clusters incluso de tamaño menor que el sector (menos de 512 bytes), aprovechando másel espacio del disco. Sin embargo, ello haría que la propia FAT ocupase demasiado espacio en el disco. Porello, en los disquetes se emplean FAT’s de 12 bits (1 byte y medio): para un programa en código máquinaello no ralentiza los cálculos (aunque al ser humano no se le de muy bien trabajar con medios bytes). En lapráctica, se toman palabras de 16 bits y se desprecian los 4 bits más significativos en los clusters pares y los4 menos significativos en los impares.

A continuación se listan dos rutinas que permiten acceder a una FAT de 12 bits previamente cargadaen memoria, con objeto de consultar o modificar alguna entrada. Evidentemente, después habrá que volvera grabar la FAT en disco, tantas veces como copias de la misma existan en éste. Las rutinas necesitan quela FAT esté completamente cargada en memoria, lo cual no es un requerimiento demasiado costoso, habidacuenta de que no puede ocupar más de 4085 * 1,5 = 6128 bytes.

Page 129: PCA, PS2 ,IBM y AT

129ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

; ************ Escribir un elemento en una FAT de 12 bits; Entrada: AX = posición de dicho elemento; DS:BX = FAT completamente cargada en memoria; DX = nuevo valor de dicho elemento

poke_fat PROCPUSH AX ; preservar registrosPUSH BXPUSH DXADD BX,AX ; BX = BX + clusterSHR AX,1 ; AX = cluster / 2PUSHF ; CF = 1 si imparADD BX,AX ; BX = BX + cluster * 1,5MOV AX,[BX] ; AX = palabra con dato 12 bitsPOPFJC poke_fat_impAND AX,1111000000000000b ; preservar la otra entradaJMP poke_fat_ok

poke_fat_imp: AND AX,0000000000001111b ; preservar la otra entradaPUSH CXMOV CL,4SHL DX,CL ; colocarlo: 4 bits a la izdaPOP CX

poke_fat_ok: OR AX,DX ; «mezclar»MOV [BX],AX ; nuevo valor en la FATPOP DXPOP BXPOP AXRET ; retorno sin alterar registros

poke_fat ENDP

; ************ Leer un elemento de una FAT de 12 bits; Entrada: AX = posición de dicho elemento; DS:BX = FAT completamente cargada en memoria; Salida: DX = valor de dicho elemento

peek_fat PROCPUSH AX ; preservar registrosPUSH BXADD BX,AX ; BX = BX + clusterSHR AX,1 ; AX = cluster / 2PUSHF ; CF = 0 si parADD BX,AX ; BX = BX + cluster * 1,5MOV DX,[BX]POPFJNC peek_fat_parPUSH CXMOV CL,4SHR DX,CL ; DX=DX/16: si DX=xyz0, DX=0xyzPOP CX

peek_fat_par: AND DH,00001111b ; borrar posible dígito izdoPOP BXPOP AXRET ; retornar sólo DX modificado

peek_fat ENDP

Tal vez, en futuros disquetes de elevada capacidad sea necesario pasar a una FAT de 16 bits,aparecida con el DOS 3.0, que es la usada por todos los discos duros excepto el de 10 Mb del XT originalde IBM. Con una FAT de 12 bits el nº de cluster más alto posible es 4085, que se corresponde con un discode 4084 clusters (numerados de 2 a 4085). En principio, no existe ninguna manera sencilla de averiguar eltipo de FAT de un disco, ya que el fabricante olvidó incluir un byte de identificación al efecto. Ladocumentación publicada es contradictoria en las diversas fuentes que he consultado, y en todas es pordesgracia incorrecta (unos dicen que la FAT 16 comienza a partir de 4078 clusters, otros que a partir de 4086,otros confunden el número de clusters con el número más alto de cluster...). Sin embargo, todas las versionesdel DOS comprobadas (MS-DOS 3.1, 3.3, 4.0, 5.0 y DR-DOS 5.0 y 6.0) operan con una FAT de 16 bits endiscos de 4085 clusters (inclusive) en adelante; esto es, a partir de 4086 como número de cluster más alto.Esto puede verificarse fácilmente creando discos virtuales con 4084/4085 clusters, copiando algunos ficherosy mirando la FAT con algún programa de utilidad (a simple vista se distingue si las entradas son de 12 ó 16bits). Por desgracia, salvo en MS-DOS 3.3 y en DR-DOS 6.0, los comandos CHKDSK del sistema consideranerróneamente que los discos de 4085, 4086 y 4087 clusters ¡poseen una FAT de 12 bits!, lo cual resultaademás completamente absurdo, dado que 4087 (0FF7h) es la marca de cluster defectuoso en una FAT de12 bits y ¡en ningún caso podría ser un número de cluster cualquiera!. Sin embargo, pese a este problemade CHKDSK, los discos con más de 4084 clusters han de ser diseñados con una FAT de 16 bit, ya que esmucho más grave tener problemas con el DOS que con CHKDSK. Otra solución es procurar no crear discosde ese número crítico de clusters, o confiar que el usuario no ejecute el casi olvidado CHKDSK sobre ellos.Por fortuna, los discos normales no están por ahora en la frontera crítica entre la FAT de 12 y la de 16 bits,aunque con los discos virtuales sí se pueden crear unidades con esos tamaños críticos: la casi totalidad de losdiscos virtuales del mercado tienen problemas en estos casos. En algunos discos duros se puede determinartambién el tipo de FAT consultando la tabla de particiones, aunque no es el método más conveniente. Debetener en cuenta el lector que manipular una FAT sin conocer su tipo supone destrozar la informaciónalmacenada en el disco. Sin embargo, tampoco hay que tener tanto miedo: lo que sí puede resultar peligrosoes llegar al extremo de preguntar al usuario el tipo de FAT...

Ahora puede surgir la pregunta: si la FAT mantiene una cadena que indica cómo está distribuido unfichero en el disco, ¿dónde se almacena el inicio de esa cadena, esto es, la primera entrada en la FAT delfichero?.

7.6.4.- EL DIRECTORIO RAÍZ.

Inmediatamente después de la FAT y su(s) réplica(s) de seguridad viene el directorio raíz. Detrás deéste ya vienen los clusters conteniendo la información del disco propiamente dicha. El directorio consta de32 bytes por cada fichero/subdirectorio (los subdirectorios no son más que un tipo especial de fichero). Enlos discos de 360 Kb, por ejemplo, el directorio se extiende a lo largo de 7 sectores (3584 bytes = 112entradas como máximo). El tamaño y ubicación del directorio pueden obtenerse del sector de arranque, comose vio al principio. La información almacenada en los 32 bytes es la siguiente:

Page 130: PCA, PS2 ,IBM y AT

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

offset 0 (8 bytes): Nombre del fichero bit 0: activo si el fichero es de sólo lecturaoffset 8 (3 bytes): Extensión del nombre del fichero bit 1: activo si el fichero es ocultooffset 11 (1 byte): Byte de atributos bit 2: activo si el fichero es de sistemaoffset 12 (10 bytes): Reservado (PASSWORD cifrada DR-DOS) bit 3: activo si esa entrada de directorio esoffset 22 (2 bytes): Hora*2048 + minutos*32 + segundos/2 la etiqueta de volumenoffset 24 (2 bytes): (año-1980)*512 + mes*32 + día bit 4: activo si es un subdirectoriooffset 26 (2 bytes): Primera entrada en la FAT bit 5: bit de archivo usado por BACKUP y RESTOREoffset 28 (4 bytes): Tamaño del fichero en bytes bits 6,7: no utilizados

ENTRADA DE DIRECTORIO BYTE DE ATRIBUTOS

En el byte de atributos, varios bits pueden estar activos a un tiempo. El atributo de sistema no tieneun significado en particular, es una reliquia heredada del CP/M (los ficheros ocultos del sistema lo tienenactivo). En un mismo disco sólo puede haber una entrada con el bit 3 activo; además, en este caso seinterpretan el nombre y la extensión como un único conjunto de 11 caracteres. Las entradas de tiposubdirectorio (bit 4 del byte de atributos activo) tienen un valor cero en el campo de tamaño (offset 28): eltamaño de un fichero subdirectorio está determinado por el número de entradas que ocupa en la FAT (en lapráctica, esto sucede con cualquier otro fichero, aunque si no es de directorio en el offset 28 esta informaciónse indica con precisión de bytes).

El nombre del fichero puede comenzar por 0E5h, lo que indica que el fichero que estuvo ahí ha sidoborrado. Si empieza por 2Eh (código ASCII del punto (.)) ó por 2Eh, 2Eh (dos puntos consecutivos) se tratade una entrada que referencia a un fichero subdirectorio.

7.6.5. - LOS SUBDIRECTORIOS.

Como hemos visto, un subdirectorio en principio puede ser una simple entrada del directorio raíz.El subdirectorio, físicamente, es a su vez un fichero un tanto especial: contiene datos binarios ... que son nadamás y nada menos que otras entradas de directorio para otros ficheros, de 32 bytes como siempre. Dentro decada subdirectorio hay al menos dos entradas especiales: un fichero con un nombre punto (.) que referenciaal propio subdirectorio -que así puede autolocalizarse- y otro con doble punto (..) que referencia al directoriopadre -del que cuelga- siendo posible, gracias a ello, retroceder cuanto se desee por el árbol de directoriossin necesidad de que todos los caminos partan del raíz. Si la primera entrada en la FAT del fichero (..) esun 0, quiere decir que ese subdirectorio cuelga del raíz, de lo contrario apuntará al primer cluster del ficherosubdirectorio padre.

El tamaño de un fichero subdirectorio es ilimitado -sin exceder, evidentemente, la capacidad deldisco-. Por ello, en un subdirectorio puede haber una gran cantidad de ficheros (muchos más de 112 ó 500)sin problemas. Cada fichero que se crea en un subdirectorio aumenta el tamaño del fichero subdirectorio en32 bytes. Por ello, en un disco de 360 Kb (354 Kb libres) se puede crear un subdirectorio y en él se puedenintroducir, en caso extremo, 11326 ficheros (más el (.) y el (..)) de tamaño cero que paradójicamente llenaríanel disco (recordar que cada entrada al directorio ocupa 32 bytes). Normalmente nadie suele cometer esosexcesos. Si en un subdirectorio había demasiados ficheros y se borra una buena parte de los mismos, eltamaño del fichero subdirectorio debería reducirse, pero en la práctica el DOS no se ocupa de estaspequeñeces, 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 ficherosa él, borrando el anterior para recuperar el espacio libre).

Considerando el nombre completo de un fichero, con toda la trayectoria de directorios, el procesoa seguir para localizarlo en el disco es ir recorriendo los ficheros subdirectorio de uno en uno, hasta llegaral fichero subdirectorio donde está registrado el fichero y, en la posición correspondiente, obtener su puntode entrada en la FAT.

Dicho sea de paso, tal vez sea una pena que el disco no conste de un único «fichero raíz» privilegiadode directorio, que podríamos denominar «subdirectorio raíz». Ello permitiría también un número ilimitadode entradas (en vez de 112, 224, etc.) y sería más lógico que una ristra de sectores. Sin embargo, esta peculiarcircunstancia también aparece en otros sistemas operativos, como el UNIX. Sus motivos tendrá.

Page 131: PCA, PS2 ,IBM y AT

131ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

7.6.6. - EL BPB Y DPB.

El BPB (Bios Parameter Block) es una estructura de datos que contiene información relativa a launidad de disco. El BPB es una pieza vital en los controladores de dispositivo de bloques, como veremos enun futuro capítulo, por lo que a continuación se expone su contenido (idéntico a una parte del sector 0):

offset 0 DW bytes_por_sectoroffset 2 DB sectores_por_clusteroffset 3 DW sectores_reservados_al_comienzo_del_discooffset 5 DB número_de_FATsoffset 6 DW número_de_entradas_en_el_directorio_raízoffset 8 DW número_total_de_sectores (0 con nº de sector de 32 bits)offset 10 DB byte_descriptor_de_mediooffset 11 DW numero_de_sectores_por_FAT-- A partir del DOS 3.0:offset 13 DW sectores_por_pistaoffset 15 DW número_de_cabezasoffset 17 DD número_de_sectores_ocultos-- A partir del DOS 4.0 (más bien DOS 3.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_cilindrosoffset 33 DB tipo_de_dispositivooffset 34 DW atributos_del_dispositivo

E l D O S c o n v i e r t einternamente el BPB en DPB (DriveParameter Block), una estructurasimilar con más información útil.Para obtener el DPB de una unidaddeterminada, puede utilizarse lafunción 32h del DOS, Get DriveParameter Block (indocumentada);la cadena de DPBs del DOS puederecorrerse a partir del primer DPB(obtenido con la función 52h delDOS, Get List of Lists, tambiénindocumentada).

7.6.7. - LA BIOS Y LOS DISQUETES.

Resulta interesante conocer el comportamiento de la BIOS en relación a los disquetes, ya que lasaplicaciones desarrolladas bajo DOS de una u otra manera habrán de cooperar con la BIOS por razones decompatibilidad (o al menos respetar ciertas especificaciones). El funcionamiento del disquete se controla através de funciones de la INT 13h, aunque esta interrupción por lo general acaba llamando a la INT 40h quees quien realmente gestiona el disco en las BIOS modernas de AT. Las funciones soportadas por estainterrupción son: reset del sistema de disco (reset del controlador de disquetes, envío del comando specifyy recalibramiento del cabezal), consulta del estado del disco (obtener resultado de la última operación),lectura, escritura y verificación de sectores, formateo de pistas, obtención de información del disco y lasdisqueteras, detección del cambio de disco, establecimiento del tipo de soporte para formateo... algunas deestas últimas funciones no están disponibles en las máquinas PC/XT. La BIOS se apoya en varias variablesubicadas en el segmento 40h de la memoria. Estas variables son las siguientes (para más información,consultar el apéndice al final del libro):

Byte 40h:3Eh Estado de recalibramiento del disquete. Esta variable indica varias cosas: si se ha producido una interrupción de disquete,o si es preciso recalibrar alguna disquetera debido a un reset anterior.

Byte 40h:3Fh Estado de los motores. En esta variable se indica, además del estado de los motores de las 4 posibles disqueteras (siestán encendidos o no), la última unidad que fue seleccionada y la operación en curso sobre la misma.

Byte 40h:40h Cuenta para la detención del motor. Este byte es decrementado por la interrupción periódica del temporizador; cuandollega a 0 todos los motores de las disqueteras (realmente, el único que estaba girando) son detenidos. Dejar el motorgirando unos segundos tras la última operación evita tener que esperar a que el motor acelere antes de la siguiente (siesta llega poco después).

Byte 40h:41h Estado de la última operación: se actualiza tras cada acceso al disco, indicando los errores producidos (0 = ninguno).Bytes 40h:42h A partir de esta dirección, 7 bytes almacenan el resultado de la última operación de disquete o disco duro. Se trata de

los 7 bytes que devuelve el NEC765 tras los principales comandos.Byte 40h:8Bh Control del soporte (AT). Esta variable almacena, entre otros, la última velocidad de transferencia seleccionada.Byte 40h:8Fh Información del controlador de disquete (AT). Se indica si la unidad soporta 80 cilindros (pues sí, la verdad) y si soporta

varias velocidades de transferencia.Byte 40h:90h Estado del soporte en la unidad A. Se indica la velocidad de transferencia a emplear en el disquete introducido en esta

unidad, si precisa o no saltos dobles del cabezal (caso de los disquetes de 40 cilindros en unidades de 80), y el resultadode los intentos de la BIOS (la velocidad puede ser correcta o no, según se haya logrado determinar el tipo de soporte).

Byte 40h:91h Lo mismo que el byte anterior, pero para la unidad B.Byte 40h:92h Estado del soporte en la unidad A al inicio de la operación.Byte 40h:93h Estado del soporte en la unidad B al inicio de la operación.Byte 40h:94h Número de cilindro en curso en la unidad A.Byte 40h:95h Número de cilindro en curso en la unidad B.

Además de estas variables, la BIOS utiliza también una tabla de parámetros apuntada por la INT 1Eh.Los valores para programar ciertas características del FDC según el tipo de disco pueden variar, aunque

Page 132: PCA, PS2 ,IBM y AT

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

algunos son comunes. Esta tabla determina las principales características de operación del disco. Dicha tablaestá inicialmente en la ROM, en la posición 0F000h:0EFC7h de todas las BIOS compatibles (prácticamenteel 100%), aunque el DOS suele desviarla a la RAM para poder actualizarla. El formato de la misma es:

byte 0: Se corresponde con el byte 1 del comando ’Specify’ del765, que indica el step rate (el tiempo de accesocilindro-cilindro, a menudo es 0Dh = 3 ó 6 ms) y elhead unload time (normalmente, 0Fh = 240 ó 480 ms).

byte 1: Es el byte 2 del comando ’Specify’: los bits 7..1 indicanel head load time (normalmente 01h = 2 ó 4 ms) y elbit 0 suele estar a 0 para indicar modo DMA.

byte 2: Tics de reloj (pulsos de la interrupción 8) quetranscurren tras el acceso hasta que se para el motor.

byte 3: Bytes por sector (0=128, 1=256, 2=512, 3=1024).

byte 4: Sectores por pista.byte 5: Longitud del GAP entre sectores (normalmente 2Ah en

unidades de 5¼ y 1Bh en las de 3½).byte 6: Longitud de sector (ignorado si el byte 3 no es 0).byte 7: Longitud del GAP 3 al formatear (80 en 5¼ y 3½-DD,

84 en 5¼-HD y 108 en 3½-HD).byte 8: Byte de relleno al formatear (normalmente 0F6h).byte 9: Tiempo de estabilización del cabezal en ms.byte 10: Tiempo de aceleración del motor (en unidades de 1/8 de

segundo).

El tiempo de estabilización del cabezal es el tiempo que hay que esperar tras mover el cabezal alcilindro adecuado, hasta que éste se asiente, con objeto de garantizar el éxito de las operaciones futuras; estabreve pausa es establecida en 25 milisegundos en la BIOS del PC original, aunque otras BIOS y el propioDOS suelen bajarlo a 15. Del mismo modo, el tiempo de aceleración del motor (byte 10) es el tiempo quese espera a que el motor adquiera la velocidad de rotación correcta, nada más ponerlo en marcha. Encualquier caso, es norma general intentar tres veces el acceso a disco (con resets de por medio) hastaconsiderar que un error es real. En general, pese a estos valores usuales, la flexibilidad del sistema de discoes extraordinaria y suele responder favorablemente con unos altísimos niveles de tolerancia en lastemporizaciones. Una excepción quizá la constituye el valor de GAP empleado al formatear, al ser unparámetro demasiado importante.

7.6.8. - DISQUETES FLOPTICAL 3½ DE 20 MB.

Las unidades que soportan estos disquetes, que también admiten los de 720K y 1.44M (aunque amenudo no los de 2.88M) trabajan con controladoras SCSI e incorporan una BIOS propia para dar soportea estos dispositivos. El secreto de estos disquetes está en el posicionamiento óptico del cabezal, lo quepermite elevar notablemente el número de pistas. Por ejemplo, las unidades de 20 Mb parecen estar equipadascon 753 cilindros y 27 sectores/pista. Aunque en el sector de arranque indica que posee 251 cilindros y 6cabezales, el sentido común nos permite deducir que esto no puede ser así. Lo de los 27 sectores por pistaparece indicar que la velocidad de transferencia de estos disquetes es exactamente un 50% mayor que la delos convencionales de 1.44M (750 Kbit/seg frente a 500 Kbit/seg).

El FORMAT del DOS 5.0 y posteriores puede formatear los disquetes floptical, pero lo hace a bajonivel, con lo que tarda cerca de 30-45 minutos en inicializarlos. Como ya vienen formateados de fábrica, enrealidad basta con añadirles un sector de arranque e inicializar la FAT y el directorio raíz. También se puedeverificar la superficie magnética para detectar posibles sectores defectuosos. Los programas de utilidad queacompañan estas unidades realizan todas estas tareas en unos 4 minutos. El tipo de FAT asignado puede serseleccionado por el usuario (12 ó 16 bits), así como otros parámetros técnicos (tamaño de clusters, etc.).

Las tarjetas controladoras suelen permitir un cierto grado de flexibilidad, de cara a seleccionar la letrade unidad que se desea asignar al floptical. Configurándolo como A: se puede incluso arrancar desde undisquete de éstos.

7.6.9. - EJEMPLO DE ACCESO AL DISCO A ALTO NIVEL.

Se puede acceder a varios niveles, siendo mejor el más alto por razones de compatibilidad:

1) Programando directamente el controlador de disquetes/disco duro para acceder a sectores físicos.2) Llamando a la BIOS para leer cierto sector, de cierta cara y cierto cilindro.3) Llamando al DOS para leer un sector lógico determinado en la unidad que se le indique.4) Llamando al DOS para acceder a un fichero por su nombre y ruta.

Page 133: PCA, PS2 ,IBM y AT

133ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

El método (1) es apropiado para realizar formateos especiales en sistemas de protección anticopia;el (2) es útil para acceder a otras particiones de otros sistemas operativos o a disquetes formateados por otrossistemas operativos; las opciones (3) y (4) son las más cómodas e interesantes. En general, en la medida delo posible es conveniente no bajar del nivel (3); de lo contrario se pierde la posibilidad de acceder a ciertasunidades (por ejemplo, un disco virtual no existe en absoluto para la BIOS).

A continuación se muestra un programa de ejemplo que solicita el nombre de un fichero y lovisualiza por pantalla, cargándolo por fragmentos y apoyándose en las funciones del DOS que se comentanen el apéndice que resume las funciones del sistema operativo. Paradójicamente, el acceso se realiza a altonivel pese a tratarse de un programa en ensamblador. Como se puede observar, al final del programa sedefinen dos buffers de datos de 80 y 2048 bytes. Si no se desea que estos buffers alarguen el tamaño delprograma ejecutable, pueden definirse de la siguiente manera:

fichnom EQU $buffer EQU $+80

Sin embargo, si se procede de esta última manera convendría asegurarse primero de que existen 2128bytes de memoria libres tras el código del programa, ya que de esta manera el DOS no realiza lacomprobación por nosotros (se limita a cargar cualquier programa que quepa en memoria). De todas maneras,normalmente suele haber más de 2128 bytes libres de memoria tras cargar cualquier programa... Convienehacer notar que si en lugar de DUP (0) se coloca DUP (?), el linkador de Borland (TLINK 3.0), al contrarioque el LINK de Microsoft, TAMPOCO reserva espacio efectivo para esas variables. Esto sólo sucede,lógicamente, cuando el DUP (?) está al final del programa y no hay nada más a continuación -ni más códigoni datos que no sean DUP (?)-.

; ********************************************************************; * *; * MIRA.ASM - Utilidad para visualizar ficheros de texto. *; * *; ********************************************************************

mira SEGMENTASSUME CS:mira, DS:mira

ORG 100h ; programa de tipo .COMinicio:

LEA DX,input_txt ; mensajeMOV AH,9 ; función de impresiónINT 21h ; llamar al DOSLEA DX,fichnom ; dirección para el «input»MOV BYTE PTR [fichnom],60 ; no más de 60 caracteresMOV AH,10 ; función de entrada de tecladoINT 21h ; llamar al DOSMOV BL,[fichnom+1] ; longitud efectiva tecleadaMOV BH,0 ; en BXADD BX,OFFSET fichnom ; apuntar al finalMOV BYTE PTR [BX+2],0 ; poner un cero al final

LEA DX,fichnom+2 ; offset a cadena ASCIIZ nombreMOV AL,0 ; modo de lecturaMOV AH,3Dh ; función para abrir ficheroINT 21h ; llamar al DOSJC error ; CF=1 --> errorMOV handle,AX ; código de acceso al fichero

trocito: MOV BX,handle ; código de acceso al ficheroMOV CX,2048 ; número de bytes a leerLEA DX,buffer ; dirección del bufferMOV AH,3Fh ; función para leer del ficheroINT 21h ; llamar al DOSJC error ; CF=1 --> error

MOV CX,AX ; bytes leídos realmenteJCXZ cerrar ; no hay nada que imprimirPUSH AX ; preservarlosLEA BX,buffer ; imprimir buffer ...

imprime: MOV DL,[BX] ; carácter a carácterMOV AH,2 ; ir llamando al servicio 2 delINT 21h ; DOS para imprimir en pantallaINC BX ; siguiente carácterLOOP imprime ; acabar caracteresPOP AX ; recuperar nº de bytes leídosCMP AX,2048 ; ¿leidos 2048 bytes?JE trocito ; sí, leer otro trocito más

cerrar: MOV BX,handle ; código de acceso al ficheroMOV AH,3Eh ; cerrar ficheroINT 21h ; llamar al DOSJC error ; CF = 1 --> errorINT 20h ; fin del programa

error: LEA DX,fallo_txt ; mensaje de errorMOV AH,9 ; función de impresiónINT 21h ; llamar al DOSCMP handle,0 ; ¿fichero abierto?JNE cerrar ; sí: cerrarloINT 20h ; fin del programa

; ------------ datos y variables

handle DW 0 ; handle de control del ficheroinput_txt DB 13,10,"Nombre del fichero: $"fallo_txt DB 13,10,"*** Error ***",13,10,10,"$"fichnom DB 80 DUP (0) ; buffer para leer desde el tecladobuffer DB 2048 DUP (0) ; " " " " el disco

mira ENDSEND inicio

7.6.10. - EJEMPLO DE ACCESO AL DISCO A BAJO NIVEL.

El programa de ejemplo desarrollado requiere un adaptador VGA ya que utiliza el modo de 640 por480 con 16 colores para obtener una representación gráfica de alta calidad del contenido del disco, en lugarde la tradicional y pobre representación habitual en modo texto. Además, se reprograman los registros depaleta y el DAC de la VGA para elegir colores más atractivos. El funcionamiento del programa se basa enacceder a la FAT y crear una imagen gráfica de la misma. Para ello, calcula cuantos puntos de pantalla debetrazar por cada cluster de disco (utiliza una ventana de 636x326 = 207336 puntos). Aunque este número noes entero, por razones de eficiencia se trabaja con fracciones para evitar el empleo de coma flotante. Muchasveces el ensamblador no es suficiente para asegurar la velocidad: la primera versión del programa tardaba18 segundos en dibujar un mapa en un 386-25, con una rutina escrita en su mayor parte en ensamblador. Trasmejorar el algoritmo y optimizar el código en la zona crítica donde se trazan los puntos, se redujo a menos

Page 134: PCA, PS2 ,IBM y AT

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

de 0,66 segundos el tiempo necesario (¡314000 puntos por segundo a 25 MHz!). Para leer los sectores deldisco no se utiliza la función absread() del Borland C 2.0, ya que posee una errata por la que falla conunidades de más de 32767 clusters. En su lugar, una rutina en ensamblador se encarga de llamar a lainterrupción 25h teniendo cuidado con el tipo de disco (particiones de más de 32 Mb o de menos de esacantidad). La FAT se lee en una matriz, ya que no ocupa más de 128 Kb en el peor de los casos. Se lee detres veces para evitar que en un sólo acceso a disco, vía INT 25h, se rebasen los 64 Kb permitidos si la FATocupa más de 64 Kb (el puntero al buffer apunta al inicio del segmento al ser de tipo HUGE). Acontinuación, se interpreta la FAT (según sea de 12 ó 16 bits) y se crea otra matriz de tamaño equivalenteal número de clusters del disco. Esta última matriz -que indica los clusters libres, ocupados y defectuosos-es la que se volcará en pantalla adecuadamente. El programa también imprime información general sobre eldisco, utilizando la función de impresión de la BIOS. Se imprime todo lo necesario antes de dibujar ya quepara trazar los puntos es preciso programar el adaptador de vídeo de una manera diferente a la que empleala BIOS (por razones de velocidad): después de ejecutar prepara_punto(), la BIOS no es capaz de escribir enpantalla. La inclusión de ensamblador en los programas en C se verá con detalle en un capítulo posterior.

/********************************************************************//* *//* DMAP 2.1 - Utilidad de información gráfica de discos. *//* *//* (c) Julio 1994 Ciriaco García de Celis. *//* *//* Compilar con Borland C++ en modelo large con *//* la opción «Jump optimization» desactivada. *//* *//********************************************************************/

#include <string.h>#include <dos.h>#include <dir.h>#include <conio.h>#include <alloc.h>

#define C_PACIENCIA 78 /* colores */#define C_PACIENCIAM 9#define C_NEGRO 0 /* VGA negro */#define C_CABECERA 1 /* VGA oro */#define C_TITULOS 2 /* VGA rojo */#define C_INFO 3 /* VGA naranja */#define C_LEYENDA 4 /* VGA azul claro */#define C_MARCO 5 /* VGA amarillo */#define C_OCUPADA 6 /* VGA verde oscuro */#define C_LIBRE 7 /* VGA verde claro */#define C_ERRONEA 8 /* VGA verde muy oscuro */

#define MODO 0x12 /* modo de vídeo */#define MIN_X 2#define MAX_X 637 /* ventana de dibujo de FAT */#define MIN_Y 152#define MAX_Y 477

void preservar_pantalla(), restaurar_pantalla(), init_video(),aviso_espera(), carga_fat(), escribir(), salida_error(),dec2str(), porc2str(), genera_bitfat(), analiza_fat(),informe_disco(), leyendas(), marco(),pinta_fat(), prepara_punto(), punto(), prepara_paleta();

int existe_vga(), info_disco(), leesect(), HablaSp();

int sp, unidad, tamcluster, sectfat,tsect, scr_ok=0, modo, cb, pag, cur_x, cur_y;

unsigned long numsect, inifat, tamfat;unsigned numclusters, clusters_datos, clusters_malos;unsigned char huge *boot, huge *fat, huge *bitfat, far *scrbuf;

void main(int argc, char **argv)sp=HablaSp(); /* determinar idioma del país */

cb=0;if (!strcmp(strupr(argv[argc-1]),"/I")) cb++; /* parámetro /I */

sp^=cb;if (argc>cb+1)

unidad=(*argv[1] | 0x20)-’a’;elseunidad=getdisk();

preservar_pantalla (&scrbuf,&modo,&pag,&cur_x,&cur_y,&scr_ok,&cb);if (!existe_vga()) salida_error (1);if ((boot=farmalloc(2048L))==NULL) salida_error (2);if (leesect(unidad, 1, 0L, boot)!=0) salida_error (3);if (!info_disco (boot, &numsect, &numclusters, &tamcluster,

&inifat, &sectfat, &tamfat, &tsect)) salida_error(5);if ((fat=farmalloc(tamfat))==NULL) salida_error (2);if ((bitfat=farmalloc((long)numclusters))==NULL) salida_error (2);aviso_espera();carga_fat (fat, inifat, sectfat, tsect);genera_bitfat (fat, bitfat, numclusters);analiza_fat (bitfat, numclusters, &clusters_datos, &clusters_malos);init_video();prepara_paleta();informe_disco (unidad, boot, numsect,

clusters_datos, clusters_malos);leyendas (numclusters, clusters_datos, clusters_malos);prepara_punto();marco();while (kbhit()) getch();pinta_fat (bitfat, numclusters);if (!getch()) getch();restaurar_pantalla (scrbuf,modo,pag,cur_x,cur_y,scr_ok,cb);

void preservar_pantalla(char far **scrbuf, int *modo, int *pag,int *cx, int *cy, int *scr_ok, int *colorbits)

*scr_ok=0; /* supuesto que no va a ser posible */*modo=peekb(0x40, 0x49);if (((*modo<=3)||(*modo==7))&&((*scrbuf=farmalloc(4096L))!=NULL))

*scr_ok=1;if (*modo==7)

movedata(0xb000,0,FP_SEG(*scrbuf),FP_OFF(*scrbuf),4096);else

movedata(0xb800,peek(0x40,0x4e),FP_SEG(*scrbuf),FP_OFF(*scrbuf),4096);

*pag=peekb(0x40,0x62);*cx=peekb(0x40,0x50+(*pag)*2); *cy=peekb(0x40,0x51+(*pag)*2);*colorbits=peek(0x40, 0x10) & 0x30;

void restaurar_pantalla(char far *scrbuf, int modo, int pag,int cx, int cy, int scr_ok, int colorbits)

struct REGPACK r;

poke (0x40, 0x10, peek(0x40, 0x10) & 0xFFCF | colorbits);if (scr_ok)

if (modo!=peekb(0x40,0x6c)) r.r_ax=modo; intr (0x10, &r); r.r_ax=0x500+pag; intr (0x10, &r); /* restaura página activa */if (modo==7)

movedata(FP_SEG(scrbuf),FP_OFF(scrbuf),0xb000,0,4096);else

movedata(FP_SEG(scrbuf),FP_OFF(scrbuf),0xb800,peek(0x40,0x4e),4096);

r.r_ax=0x200; r.r_bx=pag<<8; r.r_dx=cy<<8+cx; intr (0x10, &r);farfree(scrbuf);

else r.r_ax=modo; intr (0x10, &r); /* imposible reponer pantalla */

int existe_vga() /* devolver condición cierta si hay VGA */

struct REGPACK r;

r.r_ax=0x1A00; intr (0x10, &r);return ((r.r_ax & 0xFF)==0x1A);

void init_video()

struct REGPACK r;

/* forzar modo color */poke (0x40, 0x10, peek (0x40, 0x10) & 0xFFCF | 0x20);/* establecer modo 640x480x16 */r.r_ax=MODO; intr (0x10, &r);

void prepara_paleta()

struct REGPACK r;char i, paleta[17];static unsigned char dac[][3] = /* R G B */ 0, 0, 0, /* VGA negro */63, 42, 0, /* VGA oro */63, 16, 0, /* VGA rojo */63, 32, 0, /* VGA naranja */ 0, 40, 63, /* VGA azul claro */63, 63, 0, /* VGA amarillo */ 0, 48, 0, /* VGA verde oscuro */ 0, 63, 0, /* VGA verde claro */ 0, 28, 0 /* VGA verde muy oscuro */;

r.r_ax=0x1013; r.r_bx=0x0100;intr (0x10, &r); /* DAC: 16 bloques de 16 elementos */

r.r_ax=0x1013; r.r_bx=1;intr (0x10, &r); /* página 0: paleta en elementos 0..15 del DAC */

for (i=0; i<16; i++) paleta[i]=i; /* índices correctos */paleta[16]=0; /* borde negro */

r.r_es=FP_SEG(paleta); r.r_dx=FP_OFF(paleta);r.r_ax=0x1002; intr (0x10, &r); /* establecer paleta y borde */

r.r_bx=0; /* primer elemento del DAC */r.r_cx=9; /* número de elementos a definir */r.r_es=FP_SEG(dac); r.r_dx=FP_OFF(dac);r.r_ax=0x1012; intr (0x10, &r); /* programar elementos del DAC */

Page 135: PCA, PS2 ,IBM y AT

135ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

void aviso_espera()

int cx;

if (modo>1) cx=25; else cx=4;escribir (cx, 12, C_PACIENCIA," ");escribir (cx, 13, C_PACIENCIA,sp?" ANALIZANDO AREAS DEL SISTEMA ":

" PROCESSING SYSTEM AREAS ");escribir (cx+32, 13, C_PACIENCIAM, " ");escribir (cx, 14, C_PACIENCIA," ");escribir (cx+32, 14, C_PACIENCIAM, " ");escribir (cx+1,15,C_PACIENCIAM," ");

void carga_fat (unsigned char huge *fat, long inifat,int sectfat, int tsect)

int parte1, parte2, parte3;

parte1=(sectfat+2)/3;parte2=(sectfat-parte1)/2;parte3=sectfat-parte1-parte2; /* la FAT se carga de tres veces */if (parte1)if (leesect(unidad, parte1, inifat, fat)!=0) salida_error (3);

if (parte2)if (leesect(unidad, parte2, inifat+parte1,

fat + (unsigned long) parte1 * tsect)!=0) salida_error (3);if (parte3)

if (leesect(unidad, parte3, inifat+parte1+parte2, fat +(unsigned long) (parte1+parte2) * tsect)!=0) salida_error (3);

void escribir (int cx, int cy, int color, unsigned char *cadena)struct REGPACK r;unsigned char *p, pagina;unsigned char far *cursor_x;

pagina = peekb(0x40, 0x62);r.r_ax=0x200; r.r_bx = (pagina << 8); r.r_dx=0xFF00;

intr (0x10, &r); /* eliminar cursor de la pantalla */

cursor_x = MK_FP (0x40, 0x50 + (pagina <<1) );poke (0x40, 0x50 + (pagina << 1), (cy << 8) + cx);

p=cadena;while (*p) r.r_ax=0x900 | *p; r.r_bx = (pagina << 8) | color; r.r_cx=1;intr (0x10, &r);(*cursor_x)++;p++;

int info_disco (unsigned char *boot, unsigned long *numsect,unsigned *numclusters, int *tamcluster,unsigned long *inifat, int *sectfat,unsigned long *bytesfat, int *tamsect)

unsigned long nclus, nsect;*tamsect = boot[0x0B] | ((int) boot[0x0C] << 8);*numsect = boot[0x13] | ((unsigned long) boot[0x14] << 8);if (!*numsect) *numsect=(long) boot[0x20] | (long) boot[0x21]<<8

| (long) boot[0x22]<<16 | (long) boot[0x23]<<24;

*sectfat=boot[0x16] | (int) boot[0x17] << 8;*inifat=boot[0x0E] | (int) boot[0x0F] << 8;

if ((*tamsect<32) || (numsect==0) || (boot[0x0D]==0) ||(*sectfat==0))

return (0); /* retorno con error */else nsect=*numsect - (*inifat) - (*sectfat) * boot[0x10] -

(boot[0x11] | (int) boot[0x12] << 8) * 32 /*tamsect;

nclus = nsect / boot[0x0D];if (nclus>65535L) salida_error (4);*numclusters = nclus;*tamcluster = (*tamsect) * boot[0x0D];*bytesfat=(long) (*sectfat) * (*tamsect);return (1); /* retorno correcto */

void salida_error(int error)

restaurar_pantalla (scrbuf,modo,pag,cur_x,cur_y,scr_ok,cb);switch (error) case 1: printf (sp?"\n Este programa requiere adaptador VGA.\n":

"\n This program requires VGA adaptor.\n");break;

case 2: printf (sp?"\n Memoria insuficiente.\n":"\n Insufficient memory.\n");

break;case 3: printf (sp?"\n Unidad incorrecta, no preparada, HPFS o de

red.\n":"\n Incorrect, not ready, HPFS or network

drive.\n");break;

case 4: printf (sp?"\n Sólo soportados sistemas FAT12/FAT16.\n":"\n Only supported FAT12/FAT16 filesystems.\n");

break;case 5: printf (sp?"\n Sector de arranque dañado, imposible

informar.\n":"\n Boot record damaged, impossible to analyze

drive.\n");break;

exit (error);

void dec2str (char *cadena, unsigned long num, int longitud)unsigned long div;int i, coma;

switch (longitud) case 13: coma=1; div=1000000000L; break;case 6: coma=2; div=10000L; break;

for (i=0; i<longitud; i++, div/=10L)

if (i==coma) cadena[i]=’.’; coma+=4; i++;

cadena[i]=num/div+’0’; num%=div;

cadena[i]=0;while (((*cadena==’0’) || (*cadena==’.’)) && (*(cadena+1)))

*cadena++=’ ’;

void porc2str (char *cadena, int num)

cadena[0]=num/10000 | ’0’; num%=10000;cadena[1]=num/1000 | ’0’; num%=1000;cadena[2]=num/100 | ’0’; num%=100;if (sp) cadena[3]=’,’; else cadena[3]=’.’;cadena[4]=num/10 | ’0’;

if (cadena[0]==’0’) cadena[0]=’ ’;if (cadena[1]==’0’) cadena[1]=’ ’;

void genera_bitfat (unsigned char huge *fat,unsigned char huge *bitfat, unsigned numclusters)

unsigned int fat16=0, elemento, pos;unsigned i;

if (numclusters>4084) fat16++;

for (i=2; i<numclusters+2; i++)if (fat16)

elemento = fat[(long)i<<1] | (fat [((long)i<<1)|1] << 8);if (!elemento)

bitfat[i-2]=C_LIBRE; /* cluster libre */else

if (elemento == 0xFFF7)bitfat[i-2]=C_ERRONEA; /* cluster defectuoso */

elsebitfat[i-2]=C_OCUPADA; /* cluster ocupado */

else /* FAT12 */

pos = (i*3L) >> 1;if (i & 1)

elemento = (fat[pos] >> 4) | (fat[pos+1L] << 4);else

elemento = fat[pos] | ((fat[pos+1L] & 0x0F) << 8);if (!elemento)

bitfat[i-2]=C_LIBRE; /* cluster libre */else

if (elemento == 0xFF7)bitfat[i-2]=C_ERRONEA; /* cluster defectuoso */

elsebitfat[i-2]=C_OCUPADA; /* cluster ocupado */

void analiza_fat (unsigned char huge *bitfat, unsigned numclusters,unsigned *clusters_datos, unsigned *clusters_malos)

unsigned i, elemento, libres=0;

for (i=0; i<numclusters; i++)if ((elemento=bitfat[i])==C_LIBRE)

libres++;else

if (elemento == C_ERRONEA) (*clusters_malos)++;*clusters_datos=numclusters-libres-(*clusters_malos);

void informe_disco (int unidad, unsigned char *boot,unsigned long numsect,unsigned datos, unsigned malos)

char id[17], c;int tamsect, sectpista, numcaras, sectfat, sectcluster, i;

tamsect = boot[0x0B] | (int) boot[0x0C] << 8;sectpista = boot[0x18] | (int) boot[0x19] << 8;numcaras = boot[0x1A] | (int) boot[0x1B] << 8;sectfat = boot[0x16] | (int) boot[0x17] << 8;sectcluster = boot[0x0D];

escribir (0, 0, C_CABECERA, sp?" DMAP 2.1 (c) Julio 1994 CiriSOFT Informe unidad

A: ":" DMAP 2.1 (c) July 1994 CiriSOFT Drive A:

report ");

id[0]=(char) unidad + ’A’; id[1]=0;escribir (sp?68:61, 0, C_CABECERA, id);

escribir (0, 1, C_TITULOS, sp?"ID sistema: ":"System ID: ");for (i=3; i<11; i++) id[i-3]=boot[i]; id[8]=0;escribir (15, 1, C_INFO, id);escribir (0, 2, C_TITULOS, sp?"Byte de Medio: ":"Media byte: ");c=boot[0x15] >> 4 | ’0’; if (c>’9’) c+=7; id[0]=c;c=boot[0x15] & 0x0F | ’0’; if (c>’9’) c+=7; id[1]=c; id[2]=0;escribir (19, 2, C_INFO, id);escribir (0, 3, C_TITULOS, "Bytes/sector: ");dec2str (id, tamsect, 6);escribir (15, 3, C_INFO, id);escribir (0, 4, C_TITULOS, sp?"Cilindros: ":"Cylinders: ");dec2str (id, (numsect/sectpista/numcaras*256+255) >> 8, 6);escribir (15, 4, C_INFO, id);escribir (0, 5, C_TITULOS, sp?"Caras: ":"Sides: ");dec2str (id, numcaras, 6);escribir (15, 5, C_INFO, id);escribir (0, 6, C_TITULOS, sp?"Pistas: ":"Tracks: ");dec2str (id, numsect/sectpista, 6);escribir (15, 6, C_INFO, id);escribir (26, 1, C_TITULOS, sp?"Sectores/pista:":"Sectors/track: ");dec2str (id, sectpista, 6);escribir (43, 1, C_INFO, id);escribir (26, 2, C_TITULOS, sp?"Sectores/cluster:":"Sectors/cluster:

");dec2str (id, sectcluster, 6);escribir (43, 2, C_INFO, id);escribir (26, 3, C_TITULOS, sp?"Sectores/FAT: ":"Sectors/FAT: ");dec2str (id, sectfat, 6);escribir (43, 3, C_INFO, id);escribir (26, 4, C_TITULOS, sp?"Número de FATs:":"Number of FATs:");

Page 136: PCA, PS2 ,IBM y AT

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

dec2str (id, boot[0x10], 6);escribir (43, 4, C_INFO, id);escribir (26, 5, C_TITULOS, sp?"Sectores reserv.:":"Reserved

sectors:");dec2str (id, boot[0x0E] | (int) boot[0x0F] << 8, 6);escribir (43, 5, C_INFO, id);escribir (26, 6, C_TITULOS, sp?"Entradas en raiz:":"Root dir

entries:");dec2str (id, boot[0x11] | (int) boot[0x12] << 8, 6);escribir (43, 6, C_INFO, id);escribir (52, 1, C_TITULOS, sp?"Sectores: ":"Sectors:");dec2str (id, numsect, 13);escribir (67, 1, C_INFO, id);escribir (52, 2, C_TITULOS, "Clusters: ");numsect = numsect - (boot[0x0E] | (int) boot[0x0F] << 8) -

(sectfat) * boot[0x10] -(boot[0x11] | (int) boot[0x12] << 8) * 32 / tamsect;

dec2str (id, numsect/sectcluster, 13);escribir (67, 2, C_INFO, id);escribir (52, 3, C_TITULOS, "Total bytes:");dec2str (id, (long)numclusters*tamsect*sectcluster, 13);escribir (67, 3, C_INFO, id);escribir (52, 4, C_TITULOS, sp?"Bytes libres:":"Bytes free: ");dec2str (id, (((long)numsect/sectcluster-datos-malos)

*tamsect*sectcluster), 13);escribir (67, 4, C_INFO, id);escribir (52, 5, C_TITULOS, sp?"Bytes ocupados:":"Bytes used: ");dec2str (id, (long)datos*sectcluster*tamsect, 13);escribir (67, 5, C_INFO, id);escribir (52, 6, C_TITULOS, sp?"Bytes erróneos:":"Bytes damaged: ");dec2str (id, (long)malos*sectcluster*tamsect, 13);escribir (67, 6, C_INFO, id);strcpy (id, " ");for (i=0; i<5; i++)

escribir (i<<4, 7, C_CABECERA, id);

void leyendas (unsigned numclusters, unsigned datos, unsigned malos)

int porc;char *cad="100,0%)";

escribir (sp?2:4, 8, C_OCUPADA, " ");escribir (sp?5:7, 8, C_LEYENDA, sp?"Area ocupada (":"Used area (");

porc=datos*10000L/numclusters+5;porc2str (cad, porc); escribir (sp?19:18, 8, C_LEYENDA, cad);escribir (28, 8, C_LIBRE, " ");escribir (31, 8, C_LEYENDA, sp?"Area libre (":"Free area (");

porc=(numclusters-datos-malos)*10000L/numclusters+5;porc2str (cad, porc); escribir (sp?43:42, 8, C_LEYENDA, cad);escribir (52, 8, C_ERRONEA, " ");escribir (55, 8, C_LEYENDA, sp?"Area defectuosa (":"Damaged area (");

porc=malos*10000L/numclusters+5;porc2str (cad, porc); escribir (sp?72:69, 8, C_LEYENDA, cad);

void marco()int x, y;

for (y=MIN_Y; y<=MAX_Y; y++) punto (MIN_X-2, y, C_MARCO); punto (MIN_X-1, y, C_MARCO);punto (MAX_X+1, y, C_MARCO); punto (MAX_X+2, y, C_MARCO);

for (x=MIN_X-2; x<=MAX_X+2; x++) punto (x, MIN_Y-2, C_MARCO); punto (x, MIN_Y-1, C_MARCO);punto (x, MAX_Y+2, C_MARCO); punto (x, MAX_Y+1, C_MARCO);

void pinta_fat (unsigned char huge *bitfat, unsigned numclusters)

unsigned long factor;unsigned x, y,ant_pixel_l=0, ant_pixel_h=0, coord_x=2, coord_y=MIN_Y*80;

factor=(long) (MAX_X-MIN_X+1)*(MAX_Y-MIN_Y+1);factor=factor*16384L/numclusters;

asm push ax; push bx; push cx; push dx; push si; push di; push es;mov cx,numclustersles bx,bitfatmov si,bx /* SI --> posición del primer cluster */

proc_fat: asm mov al,es:[bx]

cuenta: asm inc bxcmp al,es:[bx]loope cuentamov di,bxsub di,si /* DI --> número de cluster hasta donde avanzar */push simov ax,word ptr factormul dimov si,axmov ax,dimov di,dx /* DI:SI producto parcial */mul word ptr [factor+2] /* DX:AX segundo producto parcial */add ax,diadc dx,0 /* DX:AX:SI producto */shl si,1rcl ax,1rcl dx,1shl si,1rcl ax,1rcl dx,1 /* DX:AX = DX:AX:SI / 16384 = pixel */mov si,dxmov di,axsub di,ant_pixel_lsbb si,ant_pixel_h /* SI:DI = nº de pixels a pintar */mov ant_pixel_l,axmov ant_pixel_h,dxpush bx; push cx; push ds; push bp;mov ch,es:[bx-1]mov bx,coord_xmov bp,coord_ymov dx,3CEhmov ax,0A000hmov ds,axmov al,8

mov cl,bl /* BX = cx, BP = cy*80 */and cl,7mov ah,80hshr ah,cl /* AH = bit a pintar en su sitio */push bxmov cl,3shr bx,cladd bx,bp /* BX = cy*80+cx/8 */push simov si,80out dx,ax

pinta_mas: asm mov cl,[bx] /* acceso en lectura */mov [bx],ch /* pintar punto */sub di,1jc dec_msb /* evitar salto la mayoría de las veces */

incy: asm add bx,siadd bp,sicmp bp,(MAX_Y+1)*80jb pinta_masror ah,1 /* siguiente pixel en el eje X */out dx,axpop sipop bxinc bxpush bxpush simov si,80mov bp,MIN_Y*80mov cl,3shr bx,cladd bx,bp /* BX = cy*80+cx/8 */push axmov ah,1int 16hpop axjz pinta_maspop si; pop bx; pop bp; pop ds; pop cx; pop bx; pop si;jmp fin_proc /* tecla pulsada */

dec_msb: asm pop sisub si,1push simov si,80jnc incypop sipop bxmov ax,bppop bppop dsmov coord_x,bxmov coord_y,axpop cxpop bxpop sijcxz fin_procjmp proc_fat

fin_proc: asm pop es; pop di; pop si; pop dx; pop cx; pop bx; pop ax;

void prepara_punto()

asm push ax; push dx /* preparar la VGA para punto() */mov dx,3CEhmov ax,205h /* registro de modo (5): escr. 2 lect. 0 */out dx,axmov ax,3 /* cambiar AH para hacer OR/XOR/AND */out dx,axpop dx; pop ax

void punto (int coord_x, int coord_y, int color)

asm /* rutina rápida sólo para modos de 640x???x16 */push dspush ax; push bx; push cx; push dx;mov cx,coord_xmov dx,coord_yxchg bx,cx /* BX = cx, DX = cy */mov cx,0A000hmov ds,cxmov cl,4shl dx,cl /* DX = cy * 16 */mov ax,dxshl ax,1shl ax,1 /* CX = cy * 64 */add dx,ax /* DX = cy * 80 */mov al,bldec clshr bx,cl /* CL = 3 */add bx,dx /* BX = cy * 80 + cx / 8 */and al,7mov cl,almov ah,80hshr ah,cl /* AH = bit a pintar en su sitio */mov dx,3CEh /* registro de direcciones */mov al,8out dx,axmov al,[bx] /* acceso en lectura */mov ax,colormov [bx],alpop dx; pop cx; pop bx; pop ax;pop ds

int leesect(int unidad, int nsect, unsigned long psect, void *buffer)

struct fatinfo fatdisco;static anterior_unidad=0xFFFF, tipo_disco;unsigned buffer_s, buffer_o, psectl, psecth, flags;

if (unidad!=anterior_unidad) /* ahorrar tiempo si mismo disco */getfat(unidad+1, &fatdisco);if (((unsigned)fatdisco.fi_nclus *

(unsigned long)fatdisco.fi_sclus) > 0xFFFFL)tipo_disco=1; /* unidad de más de 65535 sectores */

elsetipo_disco=0; /* unidad de menos de 65536 sectores */

Page 137: PCA, PS2 ,IBM y AT

137ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

anterior_unidad=unidad;

buffer_o=FP_OFF(buffer); buffer_s=FP_SEG(buffer);psectl=psect & 0xFFFF; psecth=psect >> 16;

if (tipo_disco) /* unidades con más de 65535 sectores */asm

push ax; push bx; push cx; push dx; push si; push di;push bp; push ds;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,unidad /* unidad */mov bx,spmov dx,ssmov ds,dx /* DS:BX = SS:SP */mov cx,0ffffh /* sectores de 32 bits */int 25h /* acceso al disco */pushfpop flags /* resultado de la operación */add sp,12 /* equilibrar pila */pop ds; pop bp;pop di; pop si; pop dx; pop cx; pop bx; pop ax

else /* unidades con menos de 65536 sectores */asm

push ax; push bx; push cx; push dx; push si; push di;push bp; push ds;

mov ax,unidad /* unidad */mov dx,psectl /* sector inicial */mov cx,nsect /* número de sectores */mov bx,buffer_o /* offset del buffer */mov ds,buffer_s /* segmento */int 25h /* acceso al disco */pushfpop flags /* resultado de la operación */add sp,2 /* equilibrar pila */pop ds; pop bp;pop di; pop si; pop dx; pop cx; pop bx; pop ax

return (flags & 1);

int HablaSp() /* devolver 1 si mensajes en castellano */

union REGS r; struct SREGS s;char info[64];int i, idioma, spl[]=54, 591, 57, 506, 56, 593, 503, 34, 63, 502,

504, 212, 52, 505, 507, 595, 51, 80, 508, 598, 58, 3, 0;

idioma=0; /* supuesto el inglés */

if (_osmajor>=3) r.x.ax=0x3800; s.ds=FP_SEG(info); r.x.dx=FP_OFF(info);intdosx (&r, &r, &s);i=0; while (spl[i++]) if (spl[i-1]==r.x.bx) idioma=1;

return (idioma);

7.7. - EL PSP.

Como se vio en el capítulo anterior, antes de que el COMMAND.COM pase el control al programaque se pretende ejecutar, se crea un bloque de 256 bytes llamado PSP (Program Segment Prefix), cuyadescripción detallada se da a continuación.

La dirección del PSP en los programas COM viene determinada por la de cualquier registro desegmento (CS=DS=ES=SS) nada más comenzar la ejecución del mismo. Sin embargo, en los programas detipo EXE sólo viene determinada por DS y ES. En cualquier caso, existe una función del DOS para obtenerla dirección del PSP, cuyo uso recomienda el fabricante del sistema en aras de una mayor compatibilidad confuturas versiones del sistema operativo. La función es la 62h y está disponible a partir del DOS 3.0.

En la siguiente información, los campos del PSP que ocupen un byte o una palabra han deinterpretarse como tal; los que ocupen 4 bytes deben interpretarse en la forma segmento:offset. En negritase resaltan los campos más importantes.

- offsets 0 al 1: palabra 20CDh, correspondiente a la instrucción INT 20h. En CP/M se podía terminar unprograma ejecutando un salto a la posición 0. En MS-DOS, un programa COM ¡también!.

- offsets 2 al 3: una palabra con la dirección de memoria (segmento) del último párrafo disponible en elsistema. Teniendo en cuenta dónde acaba la memoria y el punto en que está cargado nuestro programa, noes difícil saber la memoria que queda libre. Supuesto ES apuntando al PSP:

MOV AX,ES:[2] ; párrafo más alto disponibleMOV CX,ES ; segmento del PSPSUB AX,CX ; AX = párrafos libresMOV CX,16MUL CX ; DX:AX bytes libres

- offset 4: no utilizado.

- offsets 5 al 9: salto al despachador de funciones del DOS (en CP/M se ejecutaba un CALL 5, el MS-DOS¡también lo permite!). No es recomendable llamar al DOS de esta manera. Los PSP creados por la función4Bh en algunas versiones del DOS no tienen correctamente inicializado este campo.

- offsets 0Ah al 0Dh: contenido previo del vector de terminación (INT 22h).

- offsets 0Eh al 11h: contenido previo del vector de Ctrl-Break (INT 23h).

- offsets 12h al 15h: contenido previo del vector de manipulación de errores críticos (INT 24h).

Page 138: PCA, PS2 ,IBM y AT

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

- offsets 16h al 17h: segmento del PSP padre.

- offsets 18h al 2Bh: tabla de trabajo del sistema con los ficheros (Job File Table o JFT) : un byte por handle(a 0FFh si cerrado; los primeros son los dispositivos CON, NUL, ... y siempre están abiertos). Sólo hasta 20ficheros (si no, véase offset 32h).

- offsets 2Ch al 2Dh: desde el DOS 2.0, una palabra que apunta al segmento del espacio de entorno, dondese puede encontrar el valor de variables de entorno tan interesantes como PATH, COMSPEC,... y hasta elnombre del propio programa que se está ejecutando en ese momento y el directorio de donde se cargó (nosiempre es el actual; el programa pudo cargarse, apoyándose en el PATH, en cualquier otro directoriodiferente del directorio en curso). Véase el capítulo 8 para más información de las variables de entorno.

- offsets 2Eh al 31h: desde el DOS 2.0, valor de SS:SP en la entrada a la última INT 21h invocada.

- offsets 32h al 33h: desde el DOS 3.0, número de entradas en la JFT (por defecto, 20).

- offsets 34h al 37h: desde el DOS 3.0, puntero al JFT (por defecto, PSP:18h). Desde el DOS 3.0 puede habermás de 20 ficheros abiertos a la vez gracias a este campo, que puede ser movido de sitio. Sin embargo, essólo a partir del DOS 3.3 cuando en un PSP hijo (por ejemplo, creado con la función EXEC) se copia lainformación de más que de los 20 primeros ficheros, si hay más de 20. Se puede saber si un fichero esremoto (en la MS-net) comprobando si el byte de la JFT está comprendido entre 80h-0FEh, aunque es mejorsiempre acceder antes a las funciones del DOS.

- offsets 38h al 3Bh: desde el DOS 3.0, puntero al PSP previo (por defecto, 0FFFFh:0FFFFh en las versionesdel DOS 3.x); es utilizado por SHARE en el DOS 3.3.

- offsets 3Ch al 3Fh: no usados hasta ahora.

- offsets 40h al 41h: desde el DOS 5.0, versión del sistema a devolver cuando se invoca la función 30h.

- offsets 42h al 47h: no usados hasta ahora.

- offset 48h: desde Windows 3, el bit 0 está activo si la aplicación es no-Windows.

- offsets 49h al 4Fh: no usados hasta ahora.

- offsets 50h al 52h: código de INT 21h/RETF. No recomendado hacer CALL PSP:5Ch para llamar al DOS.

- offsets 53h al 5Bh: no usados hasta ahora.

- offsets 5Ch al 7Bh: apuntan a los dos FCB’s (File Control Blocks) usados antaño para acceder a los ficheros(uno en 5Ch y el otro en 6Ch). Es una reliquia en desuso, y además este área no se inicializa si el programaes cargado en memoria superior con el comando LOADHIGH del MS-DOS 5.0 y posteriores, por lo que noconviene usarlo ni siquiera para captar parámetros, al menos en programas residentes -susceptibles de serinstalados con LOADHIGH-. Si se utiliza el primer FCB se sobreescribe además el segundo.

- offsets 7Ch al 7Fh: no usados hasta ahora.

- offsets 80h al 0FFh: es la zona donde aparecen los parámetros suministrados al programa. El primer byteindica la longitud de los parámetros, después vienen los mismos y al final un retorno de carro (ASCII 13)que es un tanto redundante -a fin de cuentas, ya se sabe la longitud de los parámetros-. Ese retorno de carro,sin embargo, no «se cuenta» en el byte que indica la longitud. Téngase en cuenta que no son mayusculizadosautomáticamente (están tal y como los tecleó el usuario), y además los parámetros pueden estar separadospor uno o más espacios en blanco o tabuladores (ASCII 9).

Page 139: PCA, PS2 ,IBM y AT

139ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

En general, comprobar los valores que recibe el PSP cuando se carga un programa es una tarea quese realiza de manera sencilla con el programa DEBUG/SYMDEB. Para ello basta una orden tal como"DEBUG PROGRAMA.COM HOLA /T": al entrar en el DEBUG (o SYMDEB) basta con hacer «D 0» paraexaminar el PSP de PROGRAMA. Para ver los parámetros (HOLA /T en el ejemplo) se haría «D 80».

7.8. - EL PROCESO DE ARRANQUE DEL PC.

Al conectar el PC éste comienza a ejecutar código en los 16 últimos bytes de la memoria (dirección0FFFF0h en PC/XT, 0FFFFF0h en 286 y 0FFFFFFF0h en 386 y superiores). En esa posición de memoria,en la que hay ROM, existe un salto a donde realmente comienza el código de la BIOS. Este salto suele serde tipo largo (segmento:offset) con objeto de cargar en CS un valor que referencie al primer mega dememoria, donde también está direccionada la ROM (todos los microprocesadores arrancan en modo real). Elprograma de la ROM inicialmente se limita a chequear los registros de la CPU, primero el de estado y luegolos demás (en caso de fallo, se detiene el sistema). A continuación, se inicializan los principales chips(interrupciones, DMA, temporizador...); se detecta la configuración del sistema, accediendo directamente alos puertos de E/S y también consultando los switches de configuración de la placa base (PC/XT) o la CMOS(AT); se establecen los vectores de interrupción y se chequea la memoria RAM si el contenido de la dirección40h:72h es distinto de 1234h (el contenido de la memoria es aleatorio inicialmente). Por último, se entregael control sucesivamente a las posibles memorias ROM adicionales que existan (la de la VGA, el disco duroen XT, etc.) con objeto de que desvíen los vectores que necesiten. Al final del todo, se intenta acceder a laprimera unidad de disquetes: si no hay disquete, se procede igualmente con el primer disco duro (en los PCde IBM, si no hay disco duro ni disquete se ejecuta la ROM BASIC). Se carga el primer sector en ladirección 0:7C00h y se entrega el control a la misma. Ese sector cargado será el sector de arranque deldisquete o la tabla de partición del disco duro (el código que contiene se encargará de cargar el sector dearranque del propio disco duro, según la partición activa). El programa del sector de arranque busca el ficherodel sistema IO.SYS (o IBMBIO.COM en PC-DOS) y lo carga, entregándole el control (programa SYSINIT)o mostrando un mensaje de error si no lo encuentra. Las versiones más modernas del DOS no requieren queIO.SYS ó IBMBIO.COM comience en el primer cluster de datos del disco, aunque sí que se encuentre enel directorio raíz. Puede que también se cargue al principio el fichero MSDOS.SYS (o IBMDOS.COM) o bienpuede que el encargado de cargar dicho fichero sea el propio IO.SYS o IBMBIO.COM. El nombre de losficheros del sistema depende de si éste es PC-DOS (o DR-DOS) o MS-DOS. Teniendo en cuenta que el MS-DOS y el PC-DOS son prácticamente idénticos desde la versión 2.0 (PC-DOS funciona en máquinas noIBM), la existencia de las dos versiones se explica sólo por razones comerciales. El fichero IO.SYS oIBMBIO.COM en teoría debería ser entregado por el vendedor del ordenador: este fichero provee soporte alas diferencias específicas que existen en el hardware de las diferentes máquinas. Sin embargo, como todoslos PC compatibles son casi idénticos a nivel hardware (salvo algunas de las primeras máquinas queintentaron imitar al PC) en la práctica es el fabricante del DOS (Microsoft o Digital Research) quien entregadicho fichero. Ese fichero es como una capa que se interpone entre la BIOS del PC y el código del sistemaoperativo contenido en MSDOS.SYS o IBMDOS.COM. Este último fichero es el encargado de inicializar losvectores 20h-2Fh y completar las tablas de datos internas del sistema. También se interpreta el CONFIG.SYSpara instalar los controladores de dispositivo que den soporte a las características peculiares de laconfiguración del ordenador. Finalmente, se carga el intérprete de mandatos: por defecto esCOMMAND.COM aunque no hay razón para que ello tenga que ser así necesariamente (pruebe el lector aponer en CONFIG.SYS la orden SHELL C:\DOS\QBASIC.EXE; aunque si se abandona QBASIC algunasversiones modernas del DOS son aún capaces de cargar el COMMAND por sus propios medios, después delerror pertinente, en vez de bloquear el ordenador). En las versiones más recientes del DOS, el sistema puederesidir en memoria superior o en el HMA: en ese caso, el proceso de arranque se complica ya que esnecesario localizar el DOS en esa zona después de cargar los controladores de memoria.

7.9. - FORMATO DE LAS EXTENSIONES ROM.

Las memorias ROM que incorporan diversas tarjetas (de vídeo, controladoras de disco duro, de red)pueden estar ubicadas en cualquier punto del área 0C0000h-0FFFFFh. La ROM BIOS del ordenador se

Page 140: PCA, PS2 ,IBM y AT

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

encarga de ir recorriéndolas y entregándolas el control durante la inicialización, con objeto de permitirlasdesviar vectores de interrupción y ejecutar otras tareas propias de su inicialización.

La BIOS recorre este área en incrementos de 2 Kb buscando la signatura 55h, 0AAh: estos dos bytesconsecutivos tienen que aparecer al principio para considerar que ahí hay una ROM. El tercer byte, que vadetrás de éstos, indica el tamaño de esa extensión ROM en bloques de 512 bytes. Por razones de seguridad,se realiza una suma de comprobación de toda la extensión ROM y si el resultado es 0 se considera unaauténtica ROM válida. En ese caso, se entrega el control (con un CALL entre segmentos) al cuarto byte dela extensión ROM. Ahí habrá de estar ubicado el código de la extensión ROM (habitualmente un salto adonde realmente comienza). Al final del todo, el código de la extensión ROM debe devolver de nuevo elcontrol a la BIOS del sistema, por medio de un retorno lejano (RETF).

El código almacenado en estas extensiones ROM puede contener accesos directos al hardware yllamadas a la ROM BIOS del sistema. Sin embargo, conviene recordar que el DOS no ha sido cargado aúny no se pueden emplear sus funciones. La ventaja de las extensiones ROM es que aumentan las prestacionesdel sistema antes de cargar el DOS. El inconveniente es que en otros sistemas operativos (UNIX, etc.) queemplean el modo protegido, estas memorias ROM en general no son accesibles. En la actualidad, con ladisponibilidad de memoria superior bajo DOS, resulta más conveniente que las extensiones de hardwarevengan acompañadas de drivers para DOS, WINDOWS, OS/2,... que no con una ROM, mucho más difícilde actualizar. Un ejemplo de memoria ROM podría ser:

bios DB 55h, 0AAhDB 32 ; 16 Kb de ROMJMP inicio......

fin_bios ... ; la suma de todos los bytes = 0

Los primeros ordenadores de IBM incorporaban una memoria ROM con el BASIC. El COMMANDde aquellas versiones del DOS (desconozco si el actual también) era capaz de ejecutar comandos internosdefinidos en estas ROM, al igual que un CLS o un DIR, vamos. El formato era, por ejemplo:

bios_basic DB 55h, 0AAhDB 64 ; 32 Kb de ROM-BASICJMP inicioDB 5 ; longitud del siguiente comandoDB "BASIC"JMP basic ; salto al comienzo del BASICDB 6 ; longitud del siguiente comandoDB "BASICA"JMP basic ; salto al comienzo (el mismo del BASIC)DB 0 ; no más comandos

basic ......

fin_bios ... ; la suma de todos los bytes = 0

Si esto le parece una tontería al lector, es que no ha visto lo que vamos a ver ahora. Resulta quetambién se pueden almacenar programas en BASIC (el código fuente, aunque tokenizado) en las BIOS. ¡Sí,un listado en ROM!:

mortgagebas DB 55h, 0AAhDB 48 ; 24 Kb de contabilidadRETF ; nada que hacerDB 0AAh, 55h ; esto es un listado BASIC... ; aquí, el programa

fin_bios ... ; la suma de todos los bytes = 0

7.10. - FORMATO FÍSICO DE LOS FICHEROS EXE.

Los ficheros EXE poseen una estructura en el disco distinta de su imagen en memoria, al contrarioque los COM. Es conveniente conocer esta estructura para ciertas tareas, como por ejemplo la creación deantivirus -y también la de virus-, que requiere modificar un fichero ejecutable ya ensamblado o compilado.

Page 141: PCA, PS2 ,IBM y AT

141ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS

Analizaremos como ejemplo de programa EXE el del capítulo 6, que reúne las principales característicasnecesarias para nuestro estudio. Se comentarán los principales bytes que componen el fichero ejecutable enel disco (1088 en total). A continuación se lista un volcado del fichero ejecutable a estudiar. Todos los datosestán en hexadecimal (parte central) y ASCII (derecha); la columna de la izquierda es el offset del primerbyte de la línea. Donde hay puntos suspensivos, se repite la línea de arriba tantas veces como sea preciso:

0000 4D 5A 40 00 03 00 01 00-20 00 00 00 FF FF 04 00 MZ@..... .......0010 00 02 00 00 00 00 02 00-3E 00 00 00 01 00 FB 30 ........>.....00020 6A 72 00 00 00 00 00 00-00 00 00 00 00 00 00 00 jr..............0030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 05 00 ................0040 02 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................0050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................. . . . . . . . . . . . . . . . .01F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................0200 0D 0A 54 65 78 74 6F 20-61 20 69 6D 70 72 69 6D ..Texto a imprim0210 69 72 0D 0A 24 00 00 00-00 00 00 00 00 00 00 00 ir..$...........0220 1E 33 C0 50 B8 00 00 8E-D8 BA 00 00 B4 09 CD 21 [email protected]:..4.M!0230 CB 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 K...............0240 70 69 6C 61 70 69 6C 61-70 69 6C 61 70 69 6C 61 pilapilapilapila. . . . . . . . . . . . . . . . .0430 70 69 6C 61 70 69 6C 61-70 69 6C 61 70 69 6C 61 pilapilapilapila

Los ficheros EXE constan de una cabecera, seguida de los segmentos de código, datos y pila; estacabecera se carga en un buffer auxiliar y no formará parte de la imagen definitiva del programa en memoria.A continuación se explica el contenido de los bytes de la cabecera:

Offset 0 (2 bytes): Valores fijos 4Dh y 5Ah (en ASCII, ’MZ’) ó 5Ah y 4Dh (’ZM’); esta información indicaque el fichero es realmente de tipo EXE y no lleva esa extensión por antojo de nadie.

Offset 2 (2 palabras): Tamaño del fichero en el disco. La palabra más significativa (offset 4) da elnúmero total de sectores que ocupa: 3 en este caso (3 * 512 = 1536). El tercer sector no estátotalmente lleno, pero para eso está la palabra menos significativa (offset 2) que indica que el últimosector sólo tiene ocupados los primeros 40h bytes. Por tanto, el tamaño efectivo del fichero es de1024 + 64 = 1088 bytes, lo que se corresponde con la realidad.

Offset 6 (1 palabra): Número de reubicaciones a realizar. Indica cuántas veces se hace referencia a unsegmento absoluto: el montador del sistema operativo tendrá que relocalizar en memoria todas lasreferencias a segmentos absolutos según en qué dirección se cargue el programa para su ejecución.En el ejemplo sólo hay 1 (correspondiente a la instrucción MOV AX,datos).

Offset 8 (1 palabra): Tamaño de esta cabecera del fichero EXE. La cabecera que estamos analizando y queprecede al código y datos del programa será más o menos larga en función del tamaño de la tablade reubicaciones, como luego veremos. En el ejemplo son 200h (=512) bytes, el tamaño mínimo,habida cuenta que sólo hay una reubicación (de hecho, aún cabrían muchas más).

Offset 0Ah (1 palabra): Mínima cantidad de memoria requerida por el programa, en párrafos, en adición altamaño del mismo. En el ejemplo es 0 (el programa se conforma con lo que ocupa en disco).

Offset 0Ch (1 palabra): Máxima cantidad de memoria requerida (párrafos). Si es 0, el programa se cargarálo más alto posible en la memoria (opción /H del LINK de Microsoft); si es 0FFFFh, como en elejemplo, el programa se cargará lo más abajo posible en la memoria -lo más normal-.

Offset 0Eh (2 palabras): Valores para inicializar SS (offset 0Eh) y SP (offset 10h). Evidentemente, el valorpara SS está aún sin reubicar (habrá de sumársele el segmento en que se cargue el programa). En elejemplo, el SS relativo es 4 y SP = 200h (=512 bytes de tamaño de pila definido).

Offset 12h (1 palabra): Suma de comprobación: son en teoría los 16 bits de menos peso de la negación dela suma de todas las palabras del fichero. El DOS debe hacer poco caso, porque TLINK no semolesta ni en inicializarlo (El LINK de Microsoft sí). Olvidar este campo.

Page 142: PCA, PS2 ,IBM y AT

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). El valor para CS está aúnsin reubicar y habrá de sumársele el segmento definitivo en que se cargue el programa. En elejemplo, el valor relativo de CS es 2, siendo IP = 0.

Offset 18h (1 palabra): Inicio de la tabla de reubicación, expresado como offset. En el ejemplo es 3Eh, loque indica que la tabla comienza en el offset 3Eh. Cada entrada en la tabla ocupa 4 bytes. La únicaentrada de que consta este programa tiene el valor 0002:0005 = 25h, lo que indica que en el offset200h+25h (225h) hay una palabra a reubicar -se suma 200h que es el tamaño de la cabecera-. Enefecto, en el offset 225h hay una palabra a cero, a la que habrá de sumársele el segmento donde seacargado el programa. Esta palabra a cero es el operando de la instrucción MOV AX,datos (el códigode operación de MOV AX,n es 0B8h).

Offset 1Ah (1 palabra): Número de overlay (0 en el ejemplo, es un programa principal).

Offset 1Ch al 3Dh: Valores desconocidos (dependientes de la versión de LINK o TLINK).

Page 143: PCA, PS2 ,IBM y AT

143LA GESTIÓN DE MEMORIA DEL DOS

Capítulo VIII: LA GESTIÓN DE MEMORIA DEL DOS

8.1. - TIPOS DE MEMORIA EN UN PC.

Daremos un breve repaso a los tipos de memoria asociados a los ordenadores compatibles en laactualidad. Conviene también echar un vistazo al apéndice I, donde se describe de manera más esquemática,para completar la explicación.

8.1.1. - Memoria convencional.

Es la memoria RAM comprendida entre los 0 y los 640 Kb; es la memoria utilizada por el DOS paralos programas de usuario. Los 384 Kb restantes hasta completar el megabyte se reservan para otros usos,como memoria para gráficos, BIOS, etc. En muchas máquinas, un buen fragmento de esta memoria estáocupado por el sistema operativo y los programas residentes, quedando normalmente no más de 560 Kb adisposición del usuario.

8.1.2. - Memoria superior.

Este término, de reciente aparición, designa el área comprendida entre los 640 y los 1024 Kb dememoria del sistema. Entre 1989 y 1990 aparecieron programas capaces de gestionar este área paraaprovechar los huecos de la misma que no son utilizados por la BIOS ni las tarjetas gráficas. La memoriasuperior no se toma de la memoria instalada en el equipo, sino que está en ciertos chips aparte relacionadoscon la BIOS, los gráficos, etc. Por ello, un AT con 1 Mb de RAM normalmente posee 640 Kb de memoriaconvencional y 384 Kb de memoria extendida. Los segmentos A0000 y B0000 están reservados para gráficos,aunque rara vez se utilizan simultáneamente. El segmento C0000 contiene la ROM del disco duro en XT (enAT el disco duro lo gestiona la propio BIOS del sistema) y/o BIOS de tarjetas gráficas. El segmento D0000es empleado normalmente para el marco de página de la memoria expandida. El segmento E0000 suele estarlibre y el F0000 almacena la BIOS del equipo. Los modernos sistemas operativos DOS permiten (en losequipos 386 ó 386sx y superiores) colocar memoria física extendida en el espacio de direcciones de lamemoria superior; con ello es factible rellenar los huecos vacíos y aprovecharlos para cargar programasresidentes. Ciertos equipos 286 también soportan esta memoria, gracias a unos chips de apoyo, pero no esfrecuente.

8.1.3. - Memoria de vídeo.

El primer adaptador de vídeo de IBM era sólo para texto y empleaba 4 Kb. Después han idoapareciendo la CGA (16 Kb), EGA (64-256 Kb), VGA (256 Kb) y SVGA (hasta 2 Mb). Como sólo hay 128Kb reservados para gráficos en el espacio de direcciones del 8086, las tarjetas más avanzadas tienen paginadasu 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, sólo 64 Kb en A0000).

8.1.4. - Memoria expandida.

Surgió en los PC/XT como respuesta a la necesidad de romper el límite de los 640 Kb, y se trata deun sistema de paginación. Consiste en añadir chips de memoria en una tarjeta de expansión, así como unacierta circuitería que permita colocar un fragmento de esa memoria extra en lo que se denomina marco depágina de memoria expandida, que normalmente es el segmento D0000 del espacio de direcciones del 8086

Page 144: PCA, PS2 ,IBM y AT

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

(64 Kb). Este marco de página está dividido en 4 bloques de 16 Kb. Allí se pueden colocar bloques de 16Kb extraídos de esos chips adicionales por medio de comandos de E/S enviados a la tarjeta de expansión.Para que los programas no tengan que hacer accesos a los puertos y para hacer más cómodo el trabajo, surgióla especificación LIM-EMS (Lotus-Intel-Microsoft Expanded Memory System) que consiste básicamente enun driver instalable desde el config.sys que pone a disposición de los programas un amplio abanico defunciones invocables por medio de la interrupción 67h. La memoria expandida está dividida en páginaslógicas de 16 Kb que pueden ser colocadas en las normalmente 4 páginas físicas del marco de página. Losmicroprocesadores 386 (incluido obviamente el SX) permiten además convertir la memoria extendida enexpandida, gracias a sus mecanismos de gestión de memoria: en estas máquinas la memoria expandida esemulada por EMM386 o algún gestor similar.

8.1.5. - Memoria extendida.

Es la memoria ubicada por encima del primer mega en los procesadores 286 y superiores. Sólo sepuede acceder a la mayoría de esta memoria en modo protegido, por lo que su uso queda relegado aprogramas complejos o diversos drivers que la aprovechen (discos virtuales, cachés de disco duro, etc.). Haceya bastante tiempo se diseñó una especificación para que los programas que utilicen la memoria extendidapuedan convivir sin conflictos: se trata del controlador XMS. Este controlador implementa una serie defunciones normalizadas que además facilitan la utilización de la memoria extendida, optimizando lastransferencias de bloques en los 386 y superiores (utiliza automáticamente palabras de 32 bits para acelerarel acceso). La especificación XMS viene en el programa HIMEM.SYS, HIDOS.SYS y en algunas versionesdel EMM386. El controlador XMS también añade funciones normalizadas para acceder a la memoriasuperior.

8.1.6. - Memoria caché.

Desde el punto de vista del software, es memoria (convencional, expandida o extendida) empleadapor un controlador de dispositivo (driver) para almacenar las partes del disco de más frecuente uso, con objetode acelerar el acceso a la información. A nivel hardware, la memoria caché es una pequeña RAM ultrarrápidaque acompaña a los microprocesadores más avanzados; los programas no tienen que ocuparse de la misma.También incorporan memorias caché algunos controladores de disco duro, aunque se trata básicamente dememoria normal y corriente para acelerar los accesos.

8.1.7. - Memoria shadow RAM.

Los chips de ROM no han evolucionado tanto como las memorias RAM; por ello es frecuente queun 486 a 66 MHz tenga una BIOS de sólo 8 bits a 8 Mhz. A partir de los procesadores 386 (también 386sx)y superiores, existen unos mecanismos de gestión de memoria virtual que permiten colocar RAM en elespacio lógico de direcciones de la ROM. Con ello, es factible copiar la ROM en RAM y acelerarsensiblemente el rendimiento del sistema, especialmente con los programas que se apoyan en la BIOS.También los chipset de la placa base pueden añadir soporte para esta característica. La shadow RAMnormalmente son 384 Kb que reemplazan cualquier fragmento de ROM ubicado entre los 640-1024Kb deRAM durante el proceso de arranque (boot) del sistema. En ocasiones, el usuario puede optar entre 384 Kbde shadow ó 384 Kb más de memoria extendida en el programa SETUP de su ordenador.

8.1.8. - Memoria CMOS RAM.

Son 64 bytes de memoria (128 en algunas máquinas) ubicados en el chip del reloj de tiempo real dela placa base de los equipos AT y superiores. A esta memoria se accede por dos puertos de E/S y en ella sealmacena la configuración y fecha y hora del sistema, que permanecen tras apagar el ordenador (gracias alas pilas). Evidentemente no se puede ejecutar código sobre la RAM CMOS (Ni pueden esconderse virus,al contrario de lo que algunos mal informados opinan. Otra cosa es que utilicen algún byte de la CMOS paracontrolar su funcionamiento).

Page 145: PCA, PS2 ,IBM y AT

145LA GESTIÓN DE MEMORIA DEL DOS

8.1.9. - Memoria alta o HMA.

Se trata de los primeros 64 Kb de la memoria extendida (colocados entre los 1024 y los 1088 Kb).Normalmente, cuando se intentaba acceder fuera del primer megabyte (por ejemplo, con un puntero del tipoFFFF:1000 = 100FF0) un artificio de hardware lo impedía, convirtiendo esa dirección en la 0:0FF0 por elsimple procedimiento de poner a cero la línea A20 de direcciones del microprocesador en los 286 ysuperiores. Ese artificio de hardware lo protagoniza el chip controlador del teclado (8042) ya que la línea A20pasa por sus manos. Si se le insta a que conecte los dos extremos (enviando un simple comando alcontrolador del teclado) a partir de ese momento es el microprocesador quien controla la línea A20 y, portanto, en el ejemplo anterior se hubiera accedido efectivamente a la memoria extendida. Los nuevos sistemasoperativos DOS habilitan la línea A20 y, gracias a ello, están disponibles otros 64 Kb adicionales. Para serexactos, 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. Téngase en cuenta que las direcciones FFFF:0000 a la FFFF:000F estándentro del primer megabyte. En el HMA se cargan actualmente el DR-DOS 5.0/6.0 y el MS-DOS 5.0 yposteriores; evidentemente siempre que el equipo, además de ser un AT, disponga como mínimo de 64 Kbde memoria extendida. En ciertos equipos poco compatibles es difícil habilitar la línea A20, por lo que elHIMEM.SYS de Microsoft dispone de un parámetro que se puede variar probando docenas de veces hastaconseguirlo, si hay suerte (además, hay BIOS muy intervencionistas que dificultan el control de A20).

8.2. - BLOQUES DE MEMORIA.

Vamos ahora a conocer con profundidad la manera en que el sistema operativo DOS gestiona lamemoria; un tema poco tratado, ya que esta información no está oficialmente documentada por Microsoft.

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. La memoria de un PC -siempre bajo DOS- está, por tanto, divididaen grupos de párrafos. Por tanto, una palabra de 16 bits permite almacenar la dirección del párrafo decualquier posición de memoria dentro del megabyte direccionable por el 8086. Todo bloque de memoria tieneasociado un propietario, que bien puede ser el DOS o un programa residente que haya solicitado al DOSel control de dicho bloque. Cuando se ejecuta un programa, el sistema crea dos bloques para el mismo: elbloque de memoria del programa y el bloque de memoria del entorno.

8.2.1. - El bloque de memoria del programa.

Cuando se ejecuta un programa, el DOS busca el mayor bloque de memoria disponible (convencionalo superior, según sea el caso) y se lo asigna -y no el bloque más cercano a la dirección 0, como algunosafirman-. Este área recibe el nombre de bloque de programa o segmento de programa. La dirección del primerpárrafo del mismo es de suma importancia y se denomina PID (Process ID, identificador de proceso). En losprimeros 256 bytes de este área el DOS crea el PSP ya conocido -256 bytes- formado por varios campos deinformación relacionada con el programa. Tras el PSP viene el código del programa ejecutable. Para losobjetivos de este capítulo basta con conocer dos campos del PSP: el primero está en su offset 0 y son dosbytes (por tanto, los primeros dos bytes del PSP) que contienen la palabra 20CDh (ó 27CDh en algunoscasos). Esto se corresponde con el código de operación de la instrucción ensamblador INT 20h (o INT 27h);esto es así por razones históricas heredadas del CP/M. Por ello, cuando un programa finaliza, puede hacerlocon un salto al inicio del PSP (un JMP 0 en los programas COM) donde se ejecuta el INT 20h, aunquenormalmente el programador ejecuta directamente el INT 20h que es más seguro. El otro campo del PSP quenos interesa es el offset 2Ch: en él hay una palabra que indica el párrafo donde comienza el bloque deentorno asociado al programa.

8.2.2. - El bloque del entorno.

El espacio de entorno del COMMAND.COM es el bloque de entorno del COMMAND.COM (quepodemos considerar como un programa residente). Es una zona de memoria donde se almacenan las variables

Page 146: PCA, PS2 ,IBM y AT

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

de entorno definidas con el mandato SET del sistema, así como con algunos comandos como PATH,PROMPT, etc. Por ejemplo, la orden PATH C:\DOS es análoga a SET PATH=C:\DOS. Las variables deentorno pueden consultarse con SET (sin parámetros). las variables de entorno sirven para crear informaciónque puedan usar múltiples programas, aunque se usan poco en la realidad. Cuando un programa es cargado,además del bloque de memoria del programa se crea el bloque del entorno. Se trata de una vulgar copia delespacio de entorno del COMMAND.COM; de esta manera, el programa en ejecución tiene acceso a lasvariables de entorno del sistema aunque no las puede modificar (estaría modificando una mera copia). Lasvariables de entorno se almacenan en formato ASCIIZ ordinario (esto es, terminadas por un byte a cero) ytienen una sintaxis del tipo VARIABLE=SU VALOR. Tras la última de las variables hay otro byte más acero para indicar el final. Después de esto, y sólo a partir del DOS 3.0, viene una palabra que indica elnúmero de cadenas ASCIIZ especiales que vienen a continuación: normalmente 1, que contiene unainformación muy útil: la especificación completa del nombre del programa que está siendo ejecutado -incluidala unidad y ruta de directorios- lo que permite a los programas saber su propio nombre y desde qué directorioestán siendo ejecutados y, por tanto, dónde deben abrir sus ficheros (por educación no es conveniente hacerloen el directorio raíz o en el actual). En el espacio de entorno del COMMAND, este añadido del DOS 3.0 yposteriores parece no estar definido.

8.2.3. - Los bloques de control de memoria (MCB’s).

Todos los bloques de memoria (tanto programa como entorno) vienen precedidos por una cabecerade un párrafo (16 bytes) que almacena información relativa al mismo. Esta cabecera recibe el nombre técnicode MCB (Memory Control Block) y tiene la siguiente estructura:

offset 0 1 3 5 8 15

byte PID Nombre del propietariode propietario Tamaño ... (sólo en bloque de programamarca y MS-DOS ≥4.0 ó DRDOS ≥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 enmemoria, 5Ah si es el último), en el offset 1 hay una palabra que indica el PID del programa propietario delbloque, en el offset 3 otra palabra indica el tamaño (como siempre, párrafos) del bloque, sin incluir estepárrafo del MCB. Los bytes que van del 5 al 7 están reservados. Entre el 8 y el 15 se sitúa el nombre delprograma propietario, aunque esta información sólo existe en los bloques de programa y con MS-DOS 4.0ó posterior (también en DR-DOS 5.0/6.0, aunque este operativo es aparentemente un DOS 3.31). El nombreacaba con un cero si tiene menos de 8 caracteres (en DR-DOS 5.0 acaba siempre con un cero, truncándoseel 8º carácter si lo había; esta errata ha sido corregida en DR-DOS 6.0).

8.2.4. - La cadena de los bloques de memoria.

Cuando un programa finaliza su ejecución, normalmente el DOS libera su bloque de memoria y deentorno. Sin embargo, los programas residentes permanecen con el bloque de memoria y de entorno en laRAM del sistema, hasta que se les desinstale o se reinicialice el equipo. Los buenos programas residentessuelen liberar el bloque de memoria del entorno antes de terminar, con objeto de economizar una memoriaque normalmente no usan (entre otras razones porque tiene un tamaño variable e impredecible). Comomínimo existen dos programas residentes en todo momento: el núcleo (kernel) del sistema operativo y elCOMMAND.COM, aunque los usuarios suelen añadir el KEYB y, en muchos casos, el PRINT, APPEND,GRAPHICS, GRAFTABL, NLSFUNC, SHARE, etc.

Como todos los bloques de memoria están ubicados unos tras otros, y además se conoce el tamañode los mismos, es factible hacer un programita que recorra la cadena de bloques de memoria hasta que seencuentre uno cuyo byte de marca valga 5Ah (último MCB), pudiéndose identificar los programas residentescargados y la memoria que emplean. La dirección del primer MCB era al principio un secreto de Microsoft,aunque hoy casi todo el mundo sabe que las siguientes líneas:

Page 147: PCA, PS2 ,IBM y AT

147LA GESTIÓN DE MEMORIA DEL DOS

MOV AH,52hINT 21hMOV AX,ES:[BX-2]

devuelven en AX la dirección del primer MCB de la cadena, utilizando la función indocumentada52h del sistema operativo.

8.2.5. - Relación entre bloque de programa y de entorno.

El siguiente esquema aclarará la relación existente entre el bloque de programa y el de entorno. Losvalores numéricos que figuran son arbitrarios (pero correctos).

Bloque del entorno

1DB7 Marca PID Tamaño (reservados)4Dh 316F 000B

1DB8 variable 1 00 variable 2 00 variable 3 00

... (más variables terminadas en 0) ... última variable 00

00 0001 C:\UTIL\VARIOS\PROGRAMA.EXE 00

Bloque del programa

316E Marca PID Tamaño (reservados) (nombre propietario)4Dh 316Fh 1C70 P R O G R A M A

316F (offset 0) (offset 2Ch)20CDh ... 1DB8 ...

8.2.6. - Tipos de bloques de memoria.

Básicamente existen cinco tipos de bloques de memoria: bloques de programa, de entorno, delsistema, bloques de datos y bloques libres. Los dos primeros ya han sido ampliamente explicados. Losbloques del sistema se corresponden con el kernel o núcleo del sistema operativo o los dispositivosinstalables; normalmente tienen su PID como 0008. En los nuevos sistemas operativos y en las máquinasdonde la cadena de bloques de memoria puede avanzar por encima de los 640 Kb, las zonas correspondientesa RAM de vídeo y extensiones BIOS suelen tener un PID 0007 en DR-DOS (que indica área excluida) ó0008 (MS-DOS 5.0) y son consideradas como bloques de memoria ordinarios, aunque sólo sea para saltarlosde alguna manera. Los bloques libres tienen un PID 0000. El PID 0006 (sólo aparece en DR-DOS) indicaque se trata de un bloque de memoria superior XMS.

Los bloques de datos aparecen en raras ocasiones, debido al uso de las funciones del sistemaoperativo para localizar bloques de memoria. Cuando un programa se ejecuta, tiene asignada la mayor partede la memoria para sí, pero es perfectamente factible que solicite al DOS una reducción de la memoriaasignada (función 4Ah) y, con los Kb que haya liberado, puede volver a llamar al DOS para crear bloquesde memoria (función 48h) o destruirlos (con la función 49h).

A la hora de recorrer la cadena de bloques de memoria, si se sigue el siguiente orden de evaluaciónel resultado será siempre correcto: en primer lugar, si aparece un PID 0000 significa que es un bloque libre.Si el PID no apunta a un PSP (no apunta a un área que empieza por 20CDh ó 27CDh) se trata entonces deun bloque del sistema. Si el PID apunta al MCB+1, se trata de un bloque del programa (recuérdese que elMCB lo precede inmediatamente). Si el PID apunta a un PSP en cuyo offset 2Ch una palabra apunta al

Page 148: PCA, PS2 ,IBM y AT

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

MCB+1, se trata del bloque del entorno de ese PSP. Si no es ninguno de estos últimos bloques, poreliminación ha de ser un bloque de datos.

8.2.7. - Liberar el espacio de entorno en programas residentes.

Resulta triste ver como algunos sofisticados programas residentes llegan incluso a autorrelocalizarseen memoria machacando parte del PSP con objeto de economizar algunos bytes; después un alto porcentajede los mismos se olvida de liberar el espacio de entorno, que para nada utilizan y que suele ocupar inclusomás memoria que todo el PSP.

La manera de liberar el espacio de entorno antes de que un programa quede residente es la siguiente(necesario DOS 3.0 como mínimo si se obtiene la dirección del PSP utilizando la función 62h):

MOV AH,62hINT 21 ; obtener dirección del PSP en BXMOV ES,BXMOV ES,ES:[2Ch] ; dirección del espacio de entornoMOV AH,49h ; función para liberar bloqueINT 21h ; bloque destruido

Alternativamente, se puede liberar directamente el bloque de memoria del entorno poniendodirectamente un 0 en su PID, aunque es menos elegante. Si ES apunta al PSP:

MOV AX,ES:[2Ch] ; dirección del espacio de entornoDEC AX ; apuntar a su MCBMOV ES,AXMOV WORD PTR ES:[1],0 ; liberar bloque (PID=0)

8.2.8. - Peculiaridades del MS-DOS 4.0 y posteriores.

La información siguiente explica las particularidades de los bloques de memoria con MS-DOS 4.0y posteriores; no es válida para DR-DOS aunque algunos aspectos concretos puedan ser comunes. Desde elMS-DOS 3.1, el primer bloque de memoria es un segmento de datos del sistema, que contiene los driversinstalados desde el CONFIG.SYS. A partir del DOS 4.0, este bloque de memoria está dividido en subbloques,cada uno de ellos precedidos de un bloque de control de memoria con el siguiente formato:

offset 0: Byte, indica el tipo de subsegmento:"D" - controlador de dispositivo"E" - extensión de controlador de dispositivo"I" - IFS (Installable File System) driver"F" - FILES= (área de almacenamiento de estas estructuras, si FILES>5)"X" - FCBS= (área de almacenamiento de estas estructuras)"C" - BUFFERS= /X (área de buffers en memoria expandida)"B" - BUFFERS= (área de buffers)"L" - LASTDRIVE= (área de almacenamiento de las CDS)"S" - STACKS= (zona de código y datos de las pilas del sistema)"T" - INSTALL= (área transitoria de este mandato)

offset 1: Palabra, indica dónde comienza el subsegmento (normalmente a continuación)offset 3: Palabra, indica el tamaño del subsegmento (en párrafos)offset 8: 8 bytes: en los tipos "D" e "I", nombre del fichero que cargó el driver.

Por tanto, desde el DOS 4.0, una vez localizado el primer MCB, puede despreciarse y tomar el queviene inmediatamente a continuación (párrafo siguiente) para recorrer los subsegmentos conectados. En elDOS 5.0 y siguientes, los bloques propiedad del sistema tienen el nombre "SC" (System Code, código del

Page 149: PCA, PS2 ,IBM y AT

149LA GESTIÓN DE MEMORIA DEL DOS

sistema o áreas de memoria superior excluidas) o bien "SD" (System Data, con controladores de dispositivo,etc.). Desde la versión 5.0 del DOS, estos bloques "SD" contienen subbloques con las mismas característicasque los del DOS 4.0.

Adicionalmente, el DOS 5.0 introdujo los bloques denominados UMB que recorren la memoriasuperior, en las diferentes áreas en que puede estar fragmentada. Acceder a estos bloques de control dememoria es bastante complicado: el segmento donde empiezan está almacenado en el offset 1Fh de la tablade información sobre buffers de disco, cuya dirección inicial a su vez se obtiene en el puntero largo quedevuelve en ES:BX+12h la función indocumentada Get List of Lists (52h): normalmente el resultado es elsegmento 9FFFh. En general, es más sencillo ignorar la memoria superior como una entidad independientey recorrer toda la memoria sin más. Sin embargo, para poder acceder a los bloques de memoria superior éstoshan de estar ligados a los de la memoria convencional: para conectarlos, si no lo están, puede emplearse lafunción, tradicionalmente indocumentada (aunque recientemente ha dejado de serlo) Get or Set MemoryAllocation Strategy (58h) del DOS: es conveniente preservarla antes y volver a restaurar esta informacióndespués de alterarla. En cualquier caso, el formato de los bloques de control UMB es el siguiente:

offset 0: Byte con valor 5Ah para el último bloque y 4Dh en otro caso.offset 1: Palabra con el PID.offset 3: Palabra con el tamaño del bloque en párrafos.offset 8: 8 Bytes: "UMB" si es el primer bloque UMB y "SM" si es el último.

8.2.9. - Cómo recorrer los bloques de memoria.

La organización de la memoria varía según la versión del sistema operativo instalada. En líneasgenerales, todo lo comentado hasta ahora -excepto lo del apartado anterior- es válido para cualquier versióndel DOS. Sin embargo, en las máquinas que tienen memoria superior, las cosas pueden cambiar un poco enesta zona de memoria: si tienen instalado algún gestor de memoria extraño, este área puede estar desconectadapor completo de los primeros 640 Kb. Con DR-DOS el usuario puede utilizar el comando MEMMAX parahabilitar o inhibir el acceso a la memoria superior; desde el MS-DOS 5.0 existen funciones específicas delsistema para estas tareas.

El programa de ejemplo listado más abajo recorre toda la memoria sin adentrarse en lasparticularidades de ningún sistema operativo. Tan sólo se toma la molestia de intentar detectar si existememoria superior y, en ese caso, mostrar también su contenido. Este algoritmo puede no enseñar todo lo quepodría enseñar gracias a las últimas versiones del DOS, pero sí gran parte, y funciona en todas las versiones.Para comprobar si existe memoria superior utiliza una técnica muy sencilla: al alcanzar el último bloque dememoria, se comprueba si el siguiente empezaría en el segmento 9FFFh en vez del A000h como cabríaesperar en una máquina de 640Kb (sólo suelen tener memoria superior las máquinas que al menos tienen 640Kb). Si esto es así no se considera que el bloque sea el último y se prosigue con el siguiente, saltando labarrera de los 640 Kb. En este caso, obviamente, los 16 bytes que faltan para completar los 640 Kb dememoria son precisamente un MCB. Esta técnica funciona sólo a partir del MS-DOS 5.0; en DR-DOS 6.0,si la memoria superior está inhibida con MEMMAX -U, no funciona (DR-DOS 6.0 se encarga de machacarel último MCB de la memoria convencional y no deja ni rastro) aunque sí con MEMMAX +U. También seimprime el nombre de los programas, aunque en DOS 3.30 y versiones anteriores salga basura. Además, elPID de tipo 6 se interpreta como un bloque de memoria superior XMS -que se estudiará en el siguienteapartado de este mismo capítulo- bajo DR-DOS 6.0, imprimiéndose también el nombre.

La primera acción de MAPAMEM al ser ejecutado es rebajar la memoria que tiene asignada hastael mínimo necesario; por ello en el resultado figura ocupando sólo 1440 bytes y teniendo tras de sí un granbloque libre. Es conveniente que los programas rebajen al principio la memoria asignada con objeto defacilitar el trabajo bajo ciertos entornos pseudo-multitarea soportados por el DOS; de hecho, es norma comúnen el código generado por los compiladores realizar esta operación al principio. Sin embargo, no todo elmundo se preocupa de ello y, a fin de cuentas, tampoco es tan importante.

Page 150: PCA, PS2 ,IBM y AT

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

Un ejemplo de la salida que puede producir este programa es el siguiente, tomado de una máquinacon memoria superior y bajo los dos sistemas operativos más comunes (aunque en los ejemplos los espaciosde entorno han coincidido junto al bloque de programa, ello no siempre sucede así). Las diferentesocupaciones de memoria de los programas en ambos sistemas operativos se deben frecuentemente a que setrata de versiones distintas:

DR-DOS 6.0 MS-DOS 5.0

MAPAMEM 2.2 MAPAMEM 2.2- Información sobre la memoria del sistema. - Información sobre la memoria del sistema.

Tipo Ubicación Tamaño PID Propietario Tipo Ubicación Tamaño PID Propietario-------- --------- ------- ----- --------------- -------- --------- ------- ----- ---------------Sistema 0000-003F 1.024 Interrupciones Sistema 0000-003F 1.024 InterrupcionesSistema 0040-004F 256 Datos del BIOS Sistema 0040-004F 256 Datos del BIOSSistema 0050-023C 7.888 Sistema Operat. Sistema 0050-0252 8.240 Sistema Operat.Sistema 023E-02FD 3.072 0008 Sistema 0254-045F 8.384 0008Programa 02FF-031E 512 02FF COMMAND Sistema 0461-0464 64 0008Entorno 0320-033F 512 02FF COMMAND Programa 0466-050E 2.704 0466 COMMANDDatos 0341-0358 384 02FF COMMAND Libre 0510-0513 64 0000 <Nadie>Programa 035A-03EE 2.384 035A MATAGAME Entorno 0515-0544 768 0466 COMMANDEntorno 03F0-0408 400 040A KEYRESET Entorno 0546-0567 544 0569 MAPAMEMPrograma 040A-041D 320 040A KEYRESET Programa 0569-05C2 1.440 0569 MAPAMEMEntorno 041F-0437 400 0439 MAPAMEM Libre 05C4-9FFE 631.728 0000 <Nadie>Programa 0439-0492 1.440 0439 MAPAMEM Sistema A000-D800 229.392 0008Libre 0494-9FFE 636.592 0000 <Nadie> Sistema D802-E159 38.272 0008Sistema A000-DEFF 258.048 0007 Libre E15B-E17F 592 0000 <Nadie>Sistema DF01-E477 22.384 0008 Programa E181-E18D 208 E181 DOSVERSistema E479-E483 176 0008 Programa E18F-E23C 2.784 E18F NLSFUNCSistema E485-E48D 144 0008 Programa E23E-E3AF 5.920 E23E GRAPHICSSistema E48F-E591 4.144 0008 Programa E3B1-E533 6.192 E3B1 SHARESistema E593-E7DA 9.344 0008 Programa E535-E637 4.144 E535 DOSKEYSistema E7DC-E806 688 0008 Programa E639-E7E2 6.816 E639 PRINTSistema E808-E810 144 0008 Programa E7E4-E840 1.488 E7E4 RCLOCKSistema E812-E81A 144 0008 Programa E842-E862 528 E842 DISKLEDSistema E81C-E8DE 3.120 0008 Programa E864-ECF0 18.640 E864 DATAPLUSPrograma E8E0-EA51 5.920 E8E0 GRAPHICS Programa ECF2-ED59 1.664 ECF2 HBREAKPrograma EA53-EA60 224 EA53 CLICK Programa ED5B-ED7E 576 ED5B ANSIUPPrograma EA62-EA6E 208 EA62 DOSVER Programa ED80-ED8C 208 ED80 PATCHKEYPrograma EA70-EA7F 256 EA70 ALTDUP Programa ED8E-ED93 96 ED8E TDSKArea XMS EA81-EA8F 240 0006 B1M92VAC Datos ED95-F6D4 37.888 ED8E TDSKPrograma EA91-EAC0 768 EA91 VSA Libre F6D6-F6FF 672 0000 <Nadie>Area XMS EAC2-EB17 1.376 0006 RCLOCKArea XMS EB19-EB30 384 0006 DISKLEDPrograma EB32-EDB4 10.288 EB32 VWATCHArea XMS EDB6-EEEC 4.976 0006 DATAPLUSArea XMS EEEE-EF4F 1.568 0006 HBREAKLibre EF51-EFFE 2.784 0000 <Nadie>Sistema F000-F5FF 24.576 0007Sistema F601-F6FF 4.080 0008

; ********************************************************************; * *; * MAPAMEM 2.2 - Utilidad para listar los bloques de memoria. *; * *; ********************************************************************

mapamem SEGMENTASSUME CS:mapamem; DS:mapamem

ORG 100h ; programa tipo COM

mapa PROCMOV BX,tam_mapmem ; tamaño de este programaMOV AH,4Ah ; modificar memoria asignadaINT 21h ; ejecutar función del DOSLEA DX,cabecera_txtCALL printMOV AH,52h ; función "Get List of Lists"INT 21hMOV AX,ES:[BX-2] ; segmento del primer M.C.B.MOV ES,AXDEC AXCALL print16hex ; imprimir dónde acaba el DOSINC AXSUB AX,50hMOV DX,16MUL DX ; pasar párrafos a bytesMOV CL,8+16CALL print_32 ; imprimir tamaño zona del DOSLEA DX,cabx_txtCALL print

otro_mcb: MOV BX,WORD PTR ES:[1] ; P.I.D. (Process ID)MOV DL,0 ; supuesta zona libre (tipo DL)CMP BX,0JE tipo_ok ; lo es (PID = 0)MOV DL,1 ; supuesto bloque XMS de DR-DOSCMP BX,6JE tipo_ok ; lo es (PID = 6)MOV DL,2 ; supuesta zona del sistemaPUSH DSMOV DS,BXMOV AX,WORD PTR DS:[0] ; AX = [PID:0000]MOV CX,WORD PTR DS:[2Ch] ; CX = [PID:002C]POP DSCMP AX,20CDh

JE no_tipo_sys ; es un PSPCMP AX,27CDhJNE tipo_ok ; no es un PSP

no_tipo_sys: MOV DL,3 ; supuesta zona de programaMOV AX,ESINC AXCMP BX,AX ; ¿PID=MCB+1?JE tipo_ok ; lo esMOV DL,4 ; supuesta zona de entornoCMP CX,AXJE tipo_okINC DL ; por eliminación zona de datos

tipo_ok: MOV pid,BXMOV tipo,DLCALL imprime_tipo ; tipo del bloqueCALL imprime_rango ; ubicación y tamañoCALL imprime_pidCALL imprime_nombreMOV AL,13 ; retorno de carroCALL printALMOV AL,10 ; salto de líneaCALL printALMOV AX,ES ; MCB ya tratadoADD AX,ES:[3] ; tamaño del bloqueINC AX ; apuntar al siguiente MCBCMP BYTE PTR ES:[0],5Ah ; ¿es el último?MOV ES,AX ; puntero al siguiente MCBJNE otro_mcb ; no, no era el último

PUSH AXINT 12hMOV BX,64MUL BXDEC AXMOV BX,AXPOP AX

CMP AX,BX ; ¿hay RAM superior (DOS 5)?JE otro_mcb ; así esMOV AX,4C00hINT 21h ; fin del programa

mapa ENDP

imprime_tipo PROCLEA SI,tabla_tiposMOV AL,tipo

Page 151: PCA, PS2 ,IBM y AT

151LA GESTIÓN DE MEMORIA DEL DOS

XOR AH,AHSHL AX,1 ; AX = tipo * 2ADD SI,AXMOV DX,[SI] ; dirección del mensajeCALL print ; imprimirloRET

imprime_tipo ENDP

imprime_rango PROCMOV AX,ESINC AXCALL print16hex ; imprimir inicio del bloqueMOV AL,’-’CALL printAL ; imprimir guiónMOV AX,ESADD AX,ES:[3]CALL print16hex ; imprimir final del bloqueMOV AX,ES:[3]MOV DX,16MUL DX ; pasar bytes a párrafosMOV CL,8+16CALL print_32 ; imprimir tamaño del bloqueRET

imprime_rango ENDP

imprime_pid PROCMOV AL,’ ’CALL printALCALL printALMOV AX,pidCALL print16hexMOV AL,’ ’CALL printALCALL printALRET

imprime_pid ENDP

imprime_nombre PROCPUSH ESLEA DX,libre_txtCMP tipo,0 ; ¿bloque libre?JNE no_libre ; noCALL print ; imprimirloJMP nombre_ok

no_libre: CMP tipo,1JE nombre_listo ; bloque XMS: nombre de ES:8 a ES:16CMP tipo,2JE nombre_ok ; nombre del propietario desconocidoMOV BX,ES:[1] ; segmento del PSP dueño del bloqueDEC BX ; apuntar al MCBMOV ES,BX

nombre_listo: MOV BX,7 ; nombre de ES:BX+1 a ES:BX+9MOV CX,8 ; máximo tamaño del nombre

otra_letra: INC BXMOV AL,ES:[BX] ; carácter del nombreAND AL,ALJZ nombre_ok ; es cero: fin del nombreCMP AL,’ ’JAE cod_normalMOV AL,’?’ ; evitar códigos raros en DOS < 4.0

cod_normal: CALL printAL ; imprimirloLOOPNZ otra_letra ; a por otro (8 como máximo)

nombre_ok: POP ESRET

imprime_nombre ENDP

print PROC ; imprimir cadena en DS:DX conPUSH AX ; el final delimitado por un ’$’PUSH CXMOV AH,9INT 21hPOP CXPOP AXRET

print ENDP

printAL PROC ; imprimir carácter en ALPUSH AXPUSH DX ; registros usados preservadosMOV AH,2 ; función de impresión del DOSMOV DL,AL ; carácter a imprimirINT 21h ; llamar al sistemaPOP DXPOP AX ; recuperar registrosRET ; retornar

printAL ENDP

print4hex PROC ; imprimir carácter hexadecimal (AL)PUSH AX ; preservar AXADD AL,’0’ ; pasar binario a ASCIICMP AL,’9’JBE no_sup9 ; no es letraADD AL,’A’-’9’-1 ; lo es

no_sup9: CALL printAL ; imprimir dígito hexadecimalPOP AX ; restaurar AXRET

print4hex ENDP

print8hex PROC ; imprimir byte hexadecimal en ALPUSH CXPUSH AXMOV CL,4SHR AL,CL ; pasar bits 4..7 a 0..3CALL print4hex ; imprimir nibble más significativoPOP AX ; restaurar ALPUSH AX ; y preservarlo de nuevoAND AL,1111b ; dejar nibble menos significativoCALL print4hex ; e imprimirloPOP AXPOP CXRET

print8hex ENDP

print16hex PROC ; imprimir palabra hexadecimal (AX)PUSH AXMOV AL,AHCALL print8hex ; imprimir parte altaPOP AXCALL print8hex ; imprimir parte bajaRET

print16hex ENDP

; -------------------------- PRINT-32 v3.1 --------------------------;; Subrutina para imprimir nº decimal de 32 bits en DXAX formateado.;; No requiere ningún registro de segmento apuntándola; se apoya en; la rutina «print» para imprimir la cadena DS:DX delimitada por ’$’.

;; 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, empezando por la derecha, se; consideran parte fraccional, e irán precedidos; del correspondiente separador);; Salidas:; nº impreso, ningún registro modificado.;; * Ejemplo, si DXAX=9384320 y CL=010 1 1011; se imprimirá ( ’_’ representa un espacio en blanco ): __93.843,20;; Tener cuidado al especificar la plantilla para que ésta se adapte; al número a imprimir. Si se especifican, por ej., pocos dígitos en; la parte entera (=demasiados en la fraccional) no tiene sentido; imprimir el separador de millares. Si se intenta, la rutina podría; colgarse porque no valida el formato.

print_32 PROCPUSHFPUSH AX ; preservar registrosPUSH BXPUSH CXPUSH DXPUSH SIPUSH DIPUSH DSPUSH ESMOV BX,CSMOV DS,BXMOV ES,BXMOV formato_pr32,CL ; byte del formato de impresiónMOV BX,OFFSET tabla_pr32MOV CX,10

digit_pr32: PUSH CXPUSH AXPUSH DXXOR DI,DIMOV SI,1 ; DISI = 1DEC CX ; CX - 1JCXZ hecho_pr32

factor_pr32: SAL SI,1RCL DI,1 ; DISI * 2MOV DX,DIMOV AX,SISAL SI,1RCL DI,1SAL SI,1RCL DI,1 ; DISI * 8ADD SI,AXADC DI,DX ; DISI=DISI*8+DISI*2=DISI*10LOOP factor_pr32 ; DISI=DISI*(10^(CX-1))

hecho_pr32: POP DXPOP AX ; CX se recuperará más tardeMOV CL,0FFh

rep_sub_pr32: INC CLSUB AX,SISBB DX,DI ; DXAX = DXAX - DISIJNC rep_sub_pr32 ; restar factor cuanto se puedaADD AX,SI ; subsanar el desbordamiento:ADC DX,DI ; DXAX = DXAX + DISIADD CL,’0’ ; pasar binario a ASCIIMOV [BX],CLPOP CX ; CX se recupera ahoraINC BXLOOP digit_pr32 ; próximo dígito del númeroSTD ; transferencias hacia atrásDEC BX ; BX apunta al último dígitoMOV final_pr32,BX ; último dígitoMOV ent_frac_pr32,BX ; frontera parte entera/fracc.MOV CL,5MOV AL,formato_pr32SHR AL,CL ; AL = nº de decimalesAND AL,ALJZ no_frac_pr32 ; ningunoMOV CL,ALXOR CH,CHMOV SI,final_pr32MOV DI,SIINC DIREP MOVSB ; cadena arriba (hacer hueco)INC final_pr32MOV AL,fracc_pr32MOV [DI],AL ; separador de parte fraccionalMOV ent_frac_pr32,SI ; indicar nueva frontera

no_frac_pr32: MOV AL,formato_pr32TEST AL,16 ; interpretar el formatoJZ poner_pr32 ; imprimir como tal

entera_pr32: MOV CX,final_pr32 ; añadir separadores de millarSUB CX,ent_frac_pr32ADD CX,3MOV SI,final_pr32MOV DI,SIINC DIREP MOVSB ; cadena arriba (hacer hueco)MOV AL,millares_pr32MOV [DI],AL ; poner separador de millaresINC final_pr32MOV ent_frac_pr32,SI ; usar la variable como punteroSUB SI,OFFSET tabla_pr32CMP SI,3JAE entera_pr32 ; próximo separador

poner_pr32: MOV BX,final_pr32MOV BYTE PTR [BX+1],"$" ; delimitador fin de cadenaMOV BX,OFFSET tabla_pr32MOV principio_pr32,BX ; inicio de cadena

limpiar_pr32: MOV AL,[BX]CMP AL,’0’JE blanco_pr32 ; cero a la izda --> poner " "CMP AL,millares_pr32 ; separador millares a la izdaJE blanco_pr32CMP AL,fracc_pr32JNE acabar_pr32MOV BYTE PTR [BX-1],’0’ ; reponer 0 antes de la comaDEC principio_pr32

acabar_pr32: MOV AL,formato_pr32 ; imprimirAND AL,00001111bXOR AH,AHMOV DX,final_pr32SUB DX,AXINC DX ; DX = offset ’principio’AND AX,AXJNZ format_pr32 ; longitud solicitadaMOV DX,principio_pr32 ; longitud obtenida del número

Page 152: PCA, PS2 ,IBM y AT

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

format_pr32: CALL print ; imprimir cadena en DS:DXPOP ESPOP DS ; restaurar todos los registrosPOP DIPOP SIPOP DXPOP CXPOP BXPOP AXPOPFRET ; salida del procedimiento

blanco_pr32: MOV BYTE PTR [BX],’ ’ ; quitar 0 / separador millaresINC BX ; sustituyendo por espaciosINC principio_pr32CMP BX,final_pr32JB limpiar_pr32MOV DX,BX ; es el número 0.000.000.00XJMP SHORT acabar_pr32 ; imprimir

formato_pr32 DB 0DB 5 DUP (’ ’) ; área de trabajo

tabla_pr32 DT 0DW 0,0

millares_pr32 EQU ’.’ ; separador de millaresfracc_pr32 EQU ’,’ ; " parte fraccionalfinal_pr32 DW 0 ; offset último byte a imprimirprincipio_pr32 DW 0 ; " " primer " " "ent_frac_pr32 DW 0 ; offset frontera entero-fracc.print_32 ENDP

; ------------ Datos

cabecera_txt LABEL BYTEDB 13,10,"MAPAMEM 2.2"DB 13,10," - Información sobre la memoria del sistema.",13,10,10DB "Tipo Ubicación Tamaño PID Propietario",13,10DB "-------- --------- ------- ----- ---------------"DB 13,10,"Sistema 0000-003F 1.024 Interrupciones"DB 13,10,"Sistema 0040-004F 256 Datos del BIOS"DB 13,10,"Sistema 0050-$"

cabx_txt DB " Sistema Operat.",13,10,"$"

tabla_tipos DW tipo_libre, tipo_xms, tipo_sistemaDW tipo_programa, tipo_entorno, tipo_datos

tipo_libre DB "Libre $"tipo_xms DB "Area XMS $"tipo_sistema DB "Sistema $"tipo_programa DB "Programa $"tipo_entorno DB "Entorno $"tipo_datos DB "Datos $"libre_txt DB "<Nadie>$"

tipo DB 0pid DW 0tam_mapmem EQU ($-OFFSET mapamem)/16+1 ; tamaño de MAPAMEM

mapamem ENDSEND mapa

8.3. - MEMORIAS EXTENDIDA Y SUPERIOR XMS.

El controlador XMS implementa una serie de funciones para acceder de manera sencilla a la memoriaextendida. En principio, hay funciones para asignar y liberar el HMA (frecuentemente ya estará ocupado porel sistema operativo), para controlar la línea A20 (en la actualidad suele estar permanentemente habilitada),para averiguar la memoria extendida disponible, para asignar dicha memoria a los programas que la solicitan(a los que devuelve un handle de control, igual que cuando se abre un fichero), liberarla, devolver ladirección física para quien desee realizar transferencias directas y lo más interesante: para mover bloques,bien sea entre zonas de la memoria extendida o entre la memoria convencional y la extendida, de la maneramás óptima y rápida según el tipo de CPU que se trate. Digamos que la memoria extendida XMS es comoun gran banco o almacén de memoria torpe, del que podemos traer o llevar datos y nada más.

Adicionalmente, el controlador XMS añade funciones para gestionar la memoria superior. Los bloquesde memoria superior no son accesibles de manera directa por los programas, a menos que éstos seanexpresamente cargados en este área con HILOAD ó LOADHIGH. Sin embargo, los programas puedensolicitar zonas de memoria superior al controlador XMS, que además de la memoria extendida gestionatambién estas áreas. Estos bloques de memoria son gestionados de manera independiente a los de la memoriaconvencional, existiendo funciones específicas del controlador XMS para localizar y liberar los bloques. ConDR-DOS 6.0 y algunos gestores de memoria, en la memoria superior pueden residir tanto bloques de memoriaDOS gestionados por el sistema (normalmente, como consecuencia de un HILOAD para instalar programasresidentes), así como auténticos bloques de memoria XMS. Realmente, las zonas que emplea el DR-DOS noson sino bloques de este tipo de memoria.

El MS-DOS 5.0 y posteriores, sin embargo, reservan toda la memoria superior para sus propios usos-cargar programas residentes- cuando se indica DOS=UMB en el CONFIG.SYS; por lo que si algunaaplicación solicita memoria superior XMS no la encontrará. Pero se puede emplear la función 58h paraconectar la memoria superior y a continuación, con la misma función, cambiar la estrategia de asignación dememoria para que el sistema asigne memoria superior en respuesta a las funciones ordinarias de asignaciónde memoria. Después es conveniente restaurar la estrategia de asignación y el estado de la memoria superiora la situación inicial (también se puede consultar previamente con la función 58h).

La hecho de que un programa pueda solicitar memoria superior al sistema es una posibilidadinteresante: ello permite a los programas residentes auto-relocalizarse de una manera sencilla a estas zonas,anticipándose a la actuación de usuarios inexpertos que podrían olvidarse del HILOAD o el LOADHIGH.Por otra parte, se economiza algo de memoria al poder suprimirse el PSP en la copia. Con MS-DOS 5.0 yposteriores, no obstante, el programa deberá dejar algo residente en memoria convencional (si no se terminaresidente, el sistema libera los bloques asignados en memoria superior) o bien modificar el PID de los bloquesen memoria superior para que al terminar sin quedar residente el DOS no los libere.

Page 153: PCA, PS2 ,IBM y AT

153LA GESTIÓN DE MEMORIA DEL DOS

Para poder emplear los servicios del controlador XMS hay que verificar primero que está instaladoel programa HIMEM.SYS o alguno equivalente (el EMM386 del DR-DOS 6.0 integra también las funcionesdel HIMEM.SYS, así como el QEMM386). Para ello se chequea la entrada 43h en la interrupción Multiplex,comprobando si devuelve 80h en el registro AL (y no 0FFh como otros programas residentes):

MOV AX,352Fh ; obtener vector de INT 2Fh en ES:BXINT 21hMOV AX,ESCMP AX,0JE no_hay_XMS ; en DOS 2.x la INT 2Fh está indefinidaMOV AX,4300h ; chequear presencia de XMSINT 2Fh ; interrupción MultiplexCMP AL,80hJE hay_XMSJNE no_hay_XMS

Antes de llamar a la INT 2Fh se comprueba que esta interrupción está apuntando a algún sitio (conel segmento distinto de 0) ya que en algunas versiones 2.x del DOS está sin inicializar y el sistema se cuelgasi se invoca sin precauciones. Las funciones del controlador XMS no se invocan por medio de ningunainterrupción, como sucede con las del DOS o la BIOS. En su lugar, una vez detectada la presencia del mismose le debe interrogar preguntándole dónde está instalado, por medio de la subfunción 10h:

MOV AX,4310h ; preguntar dirección del controladorINT 2FhMOV XMS_seg,ES ; almacenarlaMOV XMS_off,BX

donde XMS_seg y XMS_off es una estructura del tipo:

gestor_XMS LABEL DWORDXMS_off DW 0XMS_seg DW 0

Posteriormente, cuando haya que utilizar un servicio o función del controlador XMS se colocará elnúmero del mismo en AH y se ejecutará un CALL gestor_XMS. Para utilizar las llamadas al XMS espreciso que en la pila queden al menos 256 bytes libres. En un apéndice al final del libro se listan ydocumentan todas las funciones XMS.

Si por cualquier motivo fuera necesario en un programa residente interceptar las llamadas alcontrolador XMS realizadas por los programas de aplicación, hay que decir que ello es posible. Por supuesto,no es tan sencillo como desviar un vector de interrupción: hay que modificar el código del propio controlador.Por fortuna, todos los controladores XMS suelen comenzar con una instrucción de salto larga o corta (JMPXXXX:XXXX, JMP XXXX, JMP SHORT XX) y, si ésta ocupa menos de 5 bytes, los restantes estáncubiertos de instrucciones NOP (código de operación 90h). Se pueden modificar los primeros bytes del mismopara poner un salto hacia nuestra propia rutina, que luego acabe llamando a su vez al controlador previo (elRAMDRIVE de Microsoft, por ejemplo, realiza esta complicada maniobra).

8.4.- MEMORIA EXPANDIDA EMS.

La memoria expandida, como se comentó al principio del capítulo, es una técnica de paginación parasolventar la limitación de 640 Kb de memoria de los PC. Hasta la versión 3 del controlador de memoriaexpandida, esta extensión consiste en un segmento de memoria de 64 Kb (en la dirección 0D0000h o0E0000h, a veces otras como 0C8000h, etc.) dividido en cuatro páginas adyacentes de 16 Kb. Ese segmentose denomina marco de página de la memoria expandida. Las cuatro páginas son las páginas físicasnumeradas entre 0 y 3. Cuando un programa solicita memoria expandida, se le asigna un handle de control(un número de 16 bits) que la referencia, así como cierto número de páginas lógicas asociado al mismo. Apartir de ese momento, cualquier página lógica puede ser mapeada sobre una de las cuatro páginas físicas.De este modo, es factible acceder simultáneamente a cuatro páginas lógicas entre todas las disponibles. Por

Page 154: PCA, PS2 ,IBM y AT

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

ello es posible incluso asignar la misma página lógica a más de una página física, aunque es un tanto absurdo.La principal utilidad de la memoria expandida es de cara a almacenar grandes estructuras de datos evitandoen lo posible un acceso a disco. La memoria expandida se implementa con una extensión del hardware,aunque algunos equipos 286 ya la tienen integrada en la placa base. En los 386 y superiores, la CPU puedeser colocada en modo virtual 86, una variante del modo protegido en la que la memoria expandida puede seremulada por las técnicas de memoria virtual de este microprocesador, sin necesidad de una extensiónhardware. Algunos sistemas de memoria expandida real (no emulada) pueden soportar incluso unareinicialización del PC sin perder el contenido de esa memoria.

DFFFF16 Kb 3

DC000 A2 B

D8000 C1 D

D4000 E0 F

D0000 G

MARCO DE PÁGINA DE MEMORIA EXPANDIDA PÁGINAS DE MEMORIA EXPANDIDA ASIGNABLES(PÁGINAS FÍSICAS) (PÁGINAS LÓGICAS)

En este ejemplo se ha solicitado al EMM 8 páginas (numeradas en elgráfico A-G) y cualquiera de ellas puede ser «colocada» (paginada)en cualquiera de las 4 páginas físicas, a elegir.

Para utilizar la memoria expandida hay que invocar la interrupción 67h. Para detectar la presenciadel controlador hay dos métodos. El primero consiste en buscar un dispositivo "EMMXXXX0", ya que elgestor de memoria expandida se carga desde el CONFIG.SYS y define un controlador de dispositivo decaracteres con ese nombre. Es tan sencillo como intentar abrir un fichero con ese nombre y comprobar siexiste. 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úngracioso haya creado!: para cerciorarse, hay unas funciones de control IOCTL en el DOS para asegurar quese trata de un dispositivo y no de un fichero. Sin embargo, no es recomendable este método para detectar elEMM en los programas residentes y en los controladores de dispositivo: existe otro medio más convenientepara esos casos, que también puede ser empleado de manera general en cualquier otra aplicación. Consisteen buscar la cadena "EMMXXXX0" en el offset 10 del segmento apuntado por el vector 67h (despreciandoel offset de dicho vector) ¡así de sencillo!.

Las funciones del EMM se invocan colocando en AH el número de función y ejecutando la INT 67h:a la vuelta, AH normalmente valdrá 0 para indicar que todo ha ido bien. En un apéndice al final del librose listan y documentan todas las funciones EMS. Estas funciones se numeran a partir de 40h, aunque desdela 4Fh sólo están disponibles a partir de la versión 4.0 del controlador, si bien en muchos casos no sonnecesarias. Las principales funciones (soportadas por EMS 3.2) son:

40h - Obtener el estado del controlador (ver si es operativo y la memoria EMS puede funcionar bien).41h - Obtener el segmento del marco de página (no tiene por qué se 0D000h ni 0E000h).42h - Preguntar el número de páginas libres que aún no están asignadas.43h - Asignar páginas (esta función devuelve un handle de control, igual que cuando se abre un fichero).44h - Mapear páginas (colocar una cierta página lógica 0..N en una de las físicas 0..3).45h - Liberar las páginas asignadas, para que puedan usarlas futuros programas (¡es vital!).46h - Preguntar la versión del controlador de memoria expandida.47h - Salvar el contexto del mapa de páginas (usado por los TSR para no alterar el marco de página).48h - Restaurar el contexto del mapa de páginas (usado por los TSR para no alterar el marco de página).4Dh - Obtener información de todos los handles que hay y las páginas que tienen asignadas.

Page 155: PCA, PS2 ,IBM y AT

155LA GESTIÓN DE MEMORIA DEL DOS

La memoria expandida, lejos de ser sólo un invento obsoleto para superar los 640K en los viejosordenadores, es una de las memorias más versátiles disponibles bajo DOS. Muchos programas pueden verincrementado notablemente el rendimiento si se desarrollan empleando esta memoria en lugar de la XMS.La razón es que, con la memoria extendida, hay que traerla (copiarla) a la memoria convencional, procesarlay volverla a copiar a la memoria extendida. Sin embargo, con la memoria expandida EMS, una rapidísimafunción coloca en el espacio de direcciones del 8086 la memoria que va a ser accedida: allí mismo puede serprocesada sin necesidad de movimiento físico. Esto es debido a que la conmutación páginas de memoriaexpandida se hace, dicho entre comillas, seleccionando el chip de RAM que se utiliza, sin existir movimientofísico de datos. En algunos casos, sin embargo, la EMS no aumenta el rendimiento: por ejemplo, al construirun disco virtual, habrá que transferir datos desde la memoria convencional a la XMS ó la EMS; en cualquiercaso se va a producir un movimiento físico (¿qué mas da que sea hacia la EMS que hacia la XMS?).

En los modernos sistemas operativos, la memoria expandida soportada a partir de las versiones 4.0del EMM (Expanded Memory Manager) cubre un amplio espectro del espacio de direcciones dentro delmegabyte gestionado por el MS-DOS. Aquí, las páginas no han de ser necesariamente consecutivas; son másde 4 y tampoco tienen que ser necesariamente de 16 Kb. Sin embargo, por defecto -y por razones decompatibilidad- las cuatro primeras páginas físicas están colocadas adyacentemente por encima de los 640Ky son de 16 Kb, no siendo recomendable modificar esta especificación. Por ejemplo, en el sistema 386 enque se escribieron las primeras versiones de este libro, con un EMM 4.0, las páginas físicas 0 a la 3 estabanubicadas a partir de la dirección 0C8000h; las páginas 4 a la 27h estaban ubicadas entre la dirección 10000ha la 9FFFFh, cubriendo también los primeros 640 Kb (excepto los primeros 64 Kb).

Si alguien está pensando en desviar la interrupción 67h desde un programa residente, para interceptary manipular las llamadas de los programas de aplicación a esa interrupción, ya puede ir olvidándose. La razónes que los 386 y superiores están en modo virtual 86 con los controladores EMS instalados. Esto significaque cuando un programa invoca una interrupción, como la INT 67h, la CPU -de la manera que estáprogramada- pasa inmediatamente a continuación a ejecutar una rutina en modo protegido fuera del espaciode direcciones del MS-DOS. Con algunos gestores de memoria, como el EMM386 del DR-DOS 6.0, nosucede nada: ese programa supervisor retorna a la tarea virtual y ejecuta el código ubicado en el espacio dedirecciones del MS-DOS. Sin embargo, con QEMM386, el controlador de memoria está ubicado fuera de eseespacio de direcciones, y ya no vuelve a él. Si se mira con el DEBUG a donde apunta la INT 67h en unamáquina con QEMM (por ejemplo, traceando una llamada a la interrupción), se verá que este vector apuntaal siguiente código:

INT 28hIRET

Evidentemente, ¡ese no es el controlador de memoria!. Para acceder a él hay que ejecutar unainterrupción de verdad. Supongo que a través de la especificación VCPI (Virtual Control Program Interface)que regula el acceso a los modos extendidos del 386, habrá algún medio de poder acceder al código delcontrolador EMS, o interceptar las llamadas. Sin embargo, no es tan fácil como cambiar un vector...

Page 156: PCA, PS2 ,IBM y AT
Page 157: PCA, PS2 ,IBM y AT

157SUBPROCESOS, RECUBRIMIENTOS Y FILTROS

Capítulo IX: SUBPROCESOS, RECUBRIMIENTOS Y FILTROS

9.1. - LLAMADA A SUBPROCESOS Y RECUBRIMIENTOS U OVERLAYS.

La función EXEC del DOS (4Bh) es el pilar que sustenta la ejecución de programas desde dentro deotros programas, así como la carga de subrutinas de un mismo programa desde disco (overlays). Si noexistiera la función EXEC, el proceso sería arduo: habría que reservar memoria, cargar el fichero ejecutableen memoria, relocalizarlo si es de tipo EXE, crear su PSP y demás áreas de datos (entorno, etc)... por fortuna,la función EXEC se ocupa de todo ello. Además, esta función posee una característica no documentada hastael DOS 5.0 (sí ha sido documentada desde dicha versión), que es la posibilidad de cargar un programa sinejecutarlo, lo cual puede ser interesante de cara a la creación de depuradores de código.

Para llamar a la función EXEC para cargar y ejecutar un programa se pone un 0 en AL. Hay queapuntar DS:DX a la dirección del nombre del programa (una cadena ASCIIZ, esto es, terminada por cero)que puede incluir la ruta de directorios y debe incluir la extensión. También hay que apuntar en ES:BX a unaestructura 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. A 0 si es eldel programa padre. Los programas hijos siempre accederán a una copia y no al original.offset 2: Doble palabra que apunta a los parámetros del programa a ejecutar (los que ese programaadmite, por sí solo, en la línea de comandos). Tiene el mismo formato que el contenido de PSP:80h.offset 6: Doble palabra que apunta al primer FCB a copiar en el proceso hijo.offset 10: Doble palabra que apunta al segundo FCB a copiar en el proceso hijo.offset 14: Si se carga sin ejecutar, devuelve el SS:SP inicial del subprograma.offset 18: Si se carga sin ejecutar, devuelve el CS:IP inicial del subprograma.

El subprograma cargado hereda los ficheros abiertos del programa padre. Antes de llamar a estafunción, el ordenador debe tener suficiente memoria libre. Cuando se ejecuta un programa COM ordinario,toda la memoria del sistema está asignada al mismo (el mayor bloque en realidad, lo que en la prácticasignifica toda la memoria). Por tanto, un programa COM que desee cargar otros programas debe primerorebajar la memoria que el DOS le ha asignado y quedarse sólo con la que necesita. Con los programas EXE,la cantidad de memoria que les asigna el DOS inicialmente depende del compilador y las opciones decompilación; en ensamblador suele ser también toda la memoria, por lo que es deber de éste liberar la queno necesita. Para ello, se calcula cuanta memoria necesita el programa y se llama a la función del sistemapara modificar el tamaño del bloque de memoria del propio programa (función 4Ah del DOS, pasando enES la dirección del PSP).

En los programas COM, la pila está apuntando al final del segmento (SP está próximo a 0FFFEh).Por ello, si el programa va a ocupar menos de 64 Kb, será preciso mover SP más abajo para que no se salgadel futuro bloque de memoria del programa. Si no se toma esta precaución, SP apuntará dentro del siguientebloque de memoria, que es más que probablemente el que utilizará EXEC, con lo que el ordenador deberíacolgarse a no ser que haya mucha suerte.

Tras llamar a la función EXEC, en teoría todos los registros son destruidos, según la documentaciónoficial, incluidos SS:SP. Esto significa que antes de llamar a EXEC deben apilarse los registros que no sedesee alterar y guardar en un par de variables SS y SP. Tras llamar a EXEC, inmediatamente a continuacióny antes de hacer nada se deben recargar SS y SP, para proceder después a recuperar de la pila los demás

Page 158: PCA, PS2 ,IBM y AT

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

registros. Este comportamiento de EXEC parece romper la tónica habitual de comportamiento del DOS. Sinembargo, lo cierto es que esto sólo sucedía en el DOS 2.X: aunque Microsoft no lo diga oficialmente, lasversiones posteriores del sistema sólo corrompen DX y BX al llamar a EXEC.

El siguiente programa de ejemplo, de tipo COM, realiza todas las tareas necesarias para cargar otroprograma. Como ejemplo, he decidido cargar el COMMAND.COM, aunque el programa a ejecutar podríaser cualquier otro; la ventaja de COMMAND es que crea una nueva sesión de intérprete de comandos ypermite comprobar con comodidad qué ha sucedido con la memoria.

; ********************************************************************; * *; * SHELL.ASM 1.0 - Demostración de carga de subprograma. *; * *; ********************************************************************

TAMTOT EQU 1024 ; este programa y su pila caben en 1 Kb.

shell SEGMENTASSUME CS:shell, DS:shell

ORG 100hinicio:

MOV SP,TAMTOT ; redefinir la pilaMOV AH,4AhMOV BX,TAMTOT/16INT 21h ; redimensionar bloque memoriaLEA DX,hola_txtMOV AH,9INT 21h ; mensaje de bienvenidaLEA BX,exec_infoMOV WORD PTR [BX],0MOV WORD PTR [BX+2],80h ; PSPMOV WORD PTR [BX+4],CS

MOV WORD PTR [BX+6],5Ch ; FCB 0MOV WORD PTR [BX+8],CSMOV WORD PTR [BX+0Ah],6Ch ; FCB 1MOV WORD PTR [BX+0Ch],CSLEA DX,nombreMOV AX,4B00hINT 21h ; cargar y ejecutar programaPUSH CSPOP DS ; DS = CSLEA DX,adios_txtMOV AH,9INT 21h ; mensaje de despedidaMOV AX,4C00hINT 21h ; terminar

nombre DB "C:\DOS\COMMAND.COM",0 ; programa a ejecutarexec_info DB 22 DUP (0)hola_txt DB 13,10

DB "Estás dentro de SHELL.COM ...",13,10,"$"adios_txt DB 13,10

DB "... Acabas de abandonar SHELL.COM",13,10,"$"

shell ENDSEND inicio

Al ejecutar el programa anterior, y suponiendo que el ordenador tenga el COMMAND.COM enC:\DOS (es más cómodo que andar buscando la variable de entorno COMSPEC), se puede generar una sesiónde trabajo como la que se muestra a continuación, en la que la utilidad MAPAMEM permite verificar laestructura de la memoria tras la ejecución de SHELL.COM:

C:\COMPILER\86\AREA>shell

Estás dentro de SHELL.COM ...

Microsoft(R) MS-DOS(R) Versión 5.00(C)Copyright Microsoft Corp 1981-1991.

C:\COMPILER\86\AREA>mapamem

MAPAMEM 2.2- Información sobre la memoria del sistema.

Tipo Ubicación Tamaño PID Propietario-------- --------- ------- ----- ---------------Sistema 0000-003F 1.024 InterrupcionesSistema 0040-004F 256 Datos del BIOSSistema 0050-0B59 45.216 Sistema Operat.Sistema 0B5B-0CF1 6.512 0008Programa 0CF3-0E1C 4.768 0CF3 COMMANDLibre 0E1E-0E21 64 0000 <Nadie>Entorno 0E23-0E52 768 0CF3 COMMANDEntorno 0E54-0E6D 416 0E6F SHELLPrograma 0E6F-0EAE 1.024 0E6F SHELLDatos 0EB0-0EC8 400 0ECA COMMANDPrograma 0ECA-0F72 2.704 0ECA COMMANDEntorno 0F74-0F8B 384 0ECA COMMANDEntorno 0F8D-0FA5 400 0FA7 MAPAMEMPrograma 0FA7-0FFA 1.344 0FA7 MAPAMEMLibre 0FFC-9FFE 589.872 0000 <Nadie>Sistema A000-D800 229.392 0008Sistema D802-E159 38.272 0008Libre E15B-E179 496 0000 <Nadie>Programa E17B-E187 208 E17B DOSVERPrograma E189-E5B7 17.136 E189 BUFFERSPrograma E5B9-E617 1.520 E5B9 FILESPrograma E619-E663 1.200 E619 LASTDRIVPrograma E665-E712 2.784 E665 NLSFUNCPrograma E714-E885 5.920 E714 GRAPHICSPrograma E887-EA09 6.192 E887 SHAREPrograma EA0B-EB0D 4.144 EA0B DOSKEYPrograma EB0F-ECB8 6.816 EB0F PRINTPrograma ECBA-ED17 1.504 ECBA RCLOCKPrograma ED19-ED39 528 ED19 DISKLEDPrograma ED3B-F1C7 18.640 ED3B DATAPLUSPrograma F1C9-F230 1.664 F1C9 HBREAKPrograma F232-F255 576 F232 ANSIUPPrograma F257-F25C 96 F257 TDSKDatos F25E-F65D 16.384 F257 TDSKLibre F65F-F6FF 2.576 0000 <Nadie>

C:\COMPILER\86\AREA>exit

... Acabas de abandonar SHELL.COM

C:\COMPILER\86\AREA>_

Page 159: PCA, PS2 ,IBM y AT

159SUBPROCESOS, RECUBRIMIENTOS Y FILTROS

La subfunción EXEC para cargar un programa sin ejecutarlo se selecciona con AL=1; ES:BX apuntaal bloque de parámetros que se definió para el caso normal de carga+ejecución. Esta subfunción asigna elPID, no obstante, al PSP del subprograma cargado.

La subfunción de EXEC para cargar un overlay o recubrimiento, se llama con los mismos valoresen los registros que la anterior, exceptuando AL (que ahora vale 3). Sin embargo el bloque de parámetrosapuntado por ES:BX es ahora mucho más sencillo:

Offset 0: Segmento donde cargar el overlay (la memoria ha de asignarla el programa principal).Offset 2: Factor de reubicación, si se trata de un fichero EXE (normalmente el mismo valor que elanterior, si el subprograma va a correr en el mismo segmento en que es cargado).

El overlay puede haber sido ensamblado, por ejemplo, con un desplazamiento relativo nulo (ORG0) de manera que para llamarlo hay que hacer un CALL FAR al segmento donde ha sido cargado, con unoffset 0. Claro que también se puede calcular la distancia que hay entre el segmento del programa principaly el del overlay, multiplicarlo por 16 y utilizarlo como offset en la llamada al mismo segmento del programaprincipal. Sin embargo, esto requiere que el overlay sea ensamblado con cierto offset ... a calcular. Quienesproponen este segundo método -que los hay- andaban ese día más bien despistados. En general, laprogramación con overlays es compleja, y más aún si los overlays constan de varios segmentos internos.

Para conocer si la función EXEC se ha realizado correctamente o ha fracasado, se puede utilizar lafunción 4Dh del DOS (Obtener código de retorno), que devuelve en AH: 0 (terminación normal), 1(programa abortado por Ctrl-Break), 2 (terminación por error crítico) ó 3 (terminación residente). Al llamara la función 4Dh, se borra la información que devuelve (sólo funciona la primera llamada). En AL sedevuelve el valor que retorna el programa que finaliza (valor de ERRORLEVEL).

9.2. - FILTROS.

El DOS es un sistema operativo que soporta el redireccionamiento. Las posibilidades son, sinembargo, muy limitadas. La razón es la ineficiencia del sistema en las operaciones de entrada y salida, queobliga a las aplicaciones a hacer accesos directos al hardware. Por ejemplo: con el comando interno CTTY,a través de un puerto serie es factible poner a un PC como servidor remoto de otro. Esto permite operar enla línea de comandos desde el terminal remoto ubicado a varios metros de distancia. Sin embargo, nada másejecutar un programa, el teclado del PC con el emulador de terminal dejará de funcionar y será precisoutilizar ¡el del propio servidor!: la razón es que muy pocos programas usan el DOS para leer el teclado; nodigamos para escribir en la pantalla...

Sin embargo, aún en la actualidad muchos usuarios de PC trabajan en la línea de comandos, dondesí es posible, como se ha mencionado, utilizar el DOS como un sistema con dispositivos de entrada y salidaestándar que soportan el redireccionamiento. El redireccionamiento bajo DOS es empleado sobre todo paraprocesar ficheros de texto.

Un filtro es un programa normal que lee datos de la entrada estándar (por defecto, el teclado), losprocesa de alguna manera y los deposita en la salida estándar (por defecto, la pantalla). Tanto la entrada comola salida estándar, popularmente conocidas como STDIN y STDOUT, respectivamente, así como la salidaestándar para errores (STDERR) son dispositivos permanentemente abiertos en el DOS. Tienen asociados unhandle de control, como cualquier fichero: 0 para STDIN (denominado CON), 1 para STDOUT (tambiénconocido por CON), 2 para STDERR (también CON), 3 para la salida serie (denominada AUX) y 4 para laimpresora (conocida por PRN).

Por tanto, un filtro normal debe limitarse a leer, con las funciones de manejo de ficheros ordinarias,información procedente del handle 0; tras procesarla debe escribirla en el handle 1. Si se produce un erroren el proceso, o hay una salida de log que no deba mezclarse con la salida deseada por el usuario, se puede

Page 160: PCA, PS2 ,IBM y AT

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

escribir el mensaje en el handle 2. El redireccionamiento y el sistema de ficheros por handle fue incluido apartir del DOS 2.0 (en versiones anteriores no hay siquiera subdirectorios).

Cuando se ejecuta una orden del tipo COMANDO | FILTRO, el intérprete de comandos cierra lasalida estándar y crea un fichero auxiliar (de nombre extraño); a continuación abre ese fichero para salida:como al cerrar la salida estándar se había liberado el handle 1, ese handle será asignado al nuevo fichero. Estosignifica que toda la salida de COMANDO no irá a la pantalla (CON) sino al fichero auxiliar. Cuando seacabe de ejecutar COMANDO, el intérprete de mandatos cerrará el fichero auxiliar y volverá a abrir la salidaestándar, restaurando el sistema al estado normal. Pero la cosa no queda ahí, evidentemente: a continuaciónse cierra la entrada estándar y se abre como entrada el fichero auxiliar recién creado, que pasará a ser elnuevo dispositivo de entrada por defecto. Seguidamente, se carga y ejecuta FILTRO, que tomará los datosdel fichero auxiliar en lugar del teclado. Al final, el fichero auxiliar es cerrado y borrado, abriéndose yrestaurándose la entrada por defecto normal. Si se ejecuta DIR | SORT, aparte del directorio ordenadoaparecerán dos extraños ficheros con 0 bytes (este era su tamaño cuando se ejecutó DIR): el DOS crea dosficheros auxiliares para sustituir la entrada y salida estándar, aunque en este ejemplo sólo se emplee uno deellos. Actuarán los dos si se utilizan filtros encadenados que obliguen a redireccionar simultáneamente tantola entrada como la salida a ficheros auxiliares, en una orden del tipo DIR | SORT | MORE. A partir delDOS 5.0, si está definida la variable de entorno TEMP los ficheros auxiliares se crean donde ésta indica yno en el directorio activo, por lo que a simple vista podrían no verse dichos ficheros.

Cuando se utilizan los redirectores habituales (’<’, ’>’, ’<<’ y ’>>’) suceden procesos similares, todosellos desencadenados por COMMAND.COM, con objeto de alterar la salida y entrada por defecto paratrabajar con un fichero en su lugar. Por tanto, los filtros son programas que no tienen que preocuparse de cuales la entrada o salida; su codificación es extremadamente sencilla y puede realizarse en cualquier lenguajede alto o bajo nivel. El siguiente programa en C estándar, NULL.C, es un filtro nulo que no realiza tareaalguna: se limita a enviar todo lo que recibe (por tanto, DIR es lo mismo que DIR | NULL):

#include <stdio.h>

void main()int c;

do putchar(c=getchar()); while (c!=EOF);

El siguiente filtro, algo más útil, transforma en minúsculas todo lo que pasa por él, teniendo cuidadocon los caracteres españoles (Ñ, Ü, Ç, etc.). Lee bloques de medio Kbyte de una sola vez para reducir elnúmero de llamadas al DOS y ganar velocidad. Si se ejecuta sin más (sin emplear ’|’ ni ’<’ ni ningún símbolode redireccionamiento o filtro) se limita a leer líneas del teclado y a reescribirlas en minúsculas, hasta quese acaba la entrada estándar (teclear Ctrl-Z y Return al final).

; ********************************************************************; * *; * MIN.ASM 1.0 - Filtro para poner en minúsculas ASCII Español. *; * *; ********************************************************************

segmento SEGMENTASSUME CS:segmento, DS:segmento

STDIN EQU 0STDOUT EQU 1

ORG 100hinicio:

CALL lee_entrada ; leer de STDINJCXZ fin_filtro ; en CX, bytes leídosPUSHFCALL pon_minusculasCALL escribe_salida ; escribir en STDOUTPOPFJNC inicio

fin_filtro: MOV AX,4C00h ; CF = 1 si fin de ficheroINT 21h

lee_entrada PROCLEA DX,bufferMOV CX,512MOV BX,STDINMOV AH,3FhINT 21h ; leerMOV CX,AXRET

lee_entrada ENDP

escribe_salida PROCLEA DX,bufferMOV BX,STDOUTMOV AH,40hINT 21h ; escribirRET

escribe_salida ENDP

pon_minusculas PROCPUSH CXLEA BX,buffer

procesa_car: MOV AL,[BX]CMP AL,’A’JB car_okCMP AL,128JAE car8CMP AL,’Z’JA car_okOR AL,32

car_ok: MOV [BX],ALINC BXLOOP procesa_carPOP CXRET

car8: MOV AH,’ñ’CMP AL,’Ñ’JE trad_okMOV AH,’ç’CMP AL,’Ç’JE trad_okMOV AH,’ü’CMP AL,’Ü’JE trad_okMOV AH,’é’CMP AL,’É’JE trad_okMOV AH,AL

trad_ok: MOV AL,AHJMP car_ok

pon_minusculas ENDP

buffer DB 512 DUP (?)

segmento ENDSEND inicio

Page 161: PCA, PS2 ,IBM y AT

161PROGRAMAS RESIDENTES

Capítulo X: PROGRAMAS RESIDENTES

En este capítulo vamos a abordar uno de los temas más estrechamente relacionados con laprogramación de sistemas: la creación de programas residentes. El DOS es un sistema monousuario ymonotarea, diseñado para atender sólo un proceso en un momento dado. Los programas residentes, aquellosque permanecen en memoria tras ser ejecutados, surgieron como intento de superar esta limitación. Algunosde estos programas residentes proporcionan en la práctica multitarea real (tales como colas de impresión orelojes), pero otros están muertos a menos que el usuario los active. A la hora de construir programasresidentes el ensamblador es el lenguaje más apto: es el más potente, el programador controla totalmente lamáquina sin depender de facetas ocultas del compilador y, además, es el lenguaje más sencillo para crearprogramas residentes (en inglés, TSR: Terminate and Stay Resident). Para los programas más complejospuede ser necesario, en cambio, utilizar algún lenguaje de alto nivel próximo a la máquina. Sin duda, losprogramas residentes que pretendan captar gran número de usuarios, deben cumplir dos requisitos: por unlado, ocupar poca memoria; por otro, estar disponibles rápidamente cuando son requeridos y, también, serfiables y crear pocos conflictos. Esto último es importante, ya que un programa residente puede funcionarmás o menos bien pero no del todo: si bien la máquina puede resistirse a colgarse, pueden aparecer anomalíaso conflictos con algunas aplicaciones. En particular, es muy común la circunstancia de que dos programasresidentes sean incompatibles entre sí.

10.1. - PRINCIPIOS BÁSICOS.

Un programa residente o TSR es un programa normal y corriente que, tras ser cargado, permaneceparcial o totalmente en memoria al finalizar su ejecución. Ello es posible utilizando una función específicadel sistema operativo. Los programas residentes pueden ser activados mediante una combinación de teclaso bien actuar con cierta periodicidad, asociados a la interrupción del temporizador. También puedeninterceptar funciones del DOS o de la BIOS para cambiar o modificar su funcionamiento. Al final, casisiempre resulta totalmente inevitable desviar alguna interrupción hacia una nueva rutina que la gestione, conobjeto de activar el programa residente. Como en casi todos los aspectos de la programación, existen unoscuantos principios fundamentales que conviene respetar:

1) Los programas residentes no deben alterar el funcionamiento normal del resto del ordenador. Estosignifica que deben preservar el estado de todo lo que van a modificar durante su ejecución, restaurándolodespués antes de retornar al programa principal, lo cual no se limita por supuesto a los registros de la CPU,sino que incluye también la pantalla, los discos, el estado de la memoria expandida y extendida, etc. Cuandose produce la interrupción que activa el programa residente, los registros de la CPU pueden tener un valorque hay que interpretar o bien pueden ser aleatorios. Este último es el caso de la interrupción periódica deltemporizador: el programa residente sólo puede fiarse de CS:IP, los demás registros deberán ser inicializadosantes de empezar a operar (lógicamente, habrán de ser primero preservados para ser restaurados al final).

2) No se pueden invocar libremente desde un programa residente los servicios del sistema operativo.Si el lector es la primera vez que oye esto, quizá se quede extrañado. Tal vez se pregunte qué sucedería sidesde un programa residente se llama (pongamos por ejemplo, una vez cada segundo) a la función deimpresión del DOS para sacar una ’A’ por la pantalla. Lo que puede suceder -y acabará sucediendo, si noa la primera ’A’, a la segunda o la tercera- es que el ordenador se cuelgue. Esto es debido a que el DOS esun sistema operativo no reentrante, entre otras razones porque conmuta a una pila propia al ser invocado.Por ello, si se llama a un servicio del DOS desde un programa residente, es posible que en ese momento el

Page 162: PCA, PS2 ,IBM y AT

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

DOS ya estuviese realizando otra función del programa principal y lo que vamos a conseguir es que se vuelvaloco y pierda el control cuando se acabe la tarea residente (el contenido previo de la pila ha sido destrozado).Para utilizar el DOS desde un programa residente hay que conocer cómo están organizadas las pilas delsistema operativo, así como determinar el estado del DOS para saber si se puede interrumpir en ese momentoo si hay que esperar. Utilizar el DOS es prácticamente indispensable a la hora de acceder al disco, por lo quemás adelante en este capítulo lo veremos con detenimiento. Para utilizar el DOS hay que emplear funcionesmás o menos secretas del sistema no documentadas por Microsoft, si bien esto no es peligroso: esta empresalas utiliza y las ha utilizado siempre profusamente en sus propios programas, por lo que resulta más queseguro esperar que futuras versiones del DOS sigan soportándolas.

3) La BIOS no es tampoco completamente reentrante. Por fortuna, la BIOS utiliza la pila delprograma que le llama. Por ello, para utilizar funciones de la BIOS desde un programa residente basta conasegurar que el sistema no está ya ejecutando una función BIOS incompatible (normalmente, una interrupción10h en el caso de las funciones de vídeo o la 13h en las de disco).

4) El hardware puede ser accedido sin limitaciones desde los programas residentes, si bien el nivelde uso que puede hacerse está limitado por el sentido común (puede haber problemas, por ejemplo, si unprograma residente cambia la posición del cabezal de un disquete cuando el programa principal estabaejecutando una función del DOS o la BIOS para acceder al disquete).

5) Los programas residentes tienen una causa que provoca su activación. Si cuando ya están activos,se vuelve a reproducir la causa, estamos ante un problema de reentrada que compete exclusivamente alprogramador. Por lo general, se suele denegar una demanda de activación cuando el programa residente yaestaba activo (si el programa tiene pila propia esto es además obligatorio). Pongamos por caso que se pulsaCTRL-ALT-R para mostrar un reloj residente en pantalla, ¿qué sucederá si se vuelve a pulsar CTRL-ALT-Rcon el reloj ya activado?. Para solucionar esto, existen dos caminos: uno de ellos es utilizar una variable queindique que el programa ya está activo. El otro, es utilizar para desactivar el programa la misma secuenciade teclas que para activarlo. Lógicamente, los programas que realicen algo periódicamente (pongamos porcaso 18,2 veces por segundo) basta con que se limiten a no pillarse los dedos, esto es, utilizar menos de1/18,2 segundos de tiempo de CPU para sus tareas.

10.2. - UN EJEMPLO SENCILLO.

El siguiente programa residente no realiza tarea alguna, tan sólo es una demostración de la manerageneral de proceder para crear un programa residente. En principio, el código de instalación está colocadoal final, con objeto de no dejarlo residente y economizar memoria. La rutina de instalación (MAIN) seencarga de preservar el vector de la interrupción periódica y desviarlo para que apunte a la futura rutinaresidente. También se instala una rutina de control de la interrupción 10h. Finalmente, se libera el espaciode entorno para economizar memoria y se termina residente. El procedimiento CONTROLA_INT8 puedeser modificado por el lector para que el programa realice una tarea útil cualquiera 18,2 veces por segundo:de la manera que está, 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).Esto significa que el lector podrá utilizar libremente los servicios de vídeo de la BIOS, si bien para utilizarpor ejemplo los de disquetes habría que desviar y monitorizar también INT 13h; por supuesto además queno se puede llamar al DOS en este TSR (no se puede hacer INT 21h directamente desde el código residente).Por cierto, si se fija el lector en la manera de controlar la INT 10h verá que al final se retorna al programaprincipal con IRET: los flags devueltos son los del propio programa que llamó y no los de la INT 10h real.Con la INT 10h se puede hacer esto, ya que los servicios de vídeo de la BIOS no utilizan el registro deestado para devolver ninguna condición. Sin embargo, con otras interrupciones BIOS (ej. 16h) o las del DOShabría que actuar con más cuidado para que la rutina de control no altere nada el funcionamiento normal.

Puede que el lector haya visto antes programas residentes que no toman la precaución de monitorizarla interrupción 10h o la 13h de la BIOS, y tal vez se pregunte si ello es realmente necesario. La respuesta

Page 163: PCA, PS2 ,IBM y AT

163PROGRAMAS RESIDENTES

es tajantemente que sí. Como se verá en el futuro en otro programa de ejemplo, reentrar a la BIOS sin máspuede provocar conflictos.

demores SEGMENTASSUME CS:demores, DS:demores

ORG 100hinicio:

JMP main

controla_int08 PROCPUSHFCALL CS:ant_int08 ; llamar al gestor normal de INT 8STICMP CS:in10,0JNE fin_int08 ; estamos dentro de INT 10h

;; Colocar aquí el proceso a ejecutar 18,2 veces/seg.; que puede invocar funciones de INT 10h

fin_int08:IRET

controla_int08 ENDP

controla_int10 PROCINC CS:in10 ; indicar entrada en INT 10hPUSHFCALL CS:ant_int10DEC CS:in10 ; fin de la INT 10hIRET

controla_int10 ENDP

in10 DB 0 ; mayor de 0 si hay INT 10hant_int08 LABEL DWORDant_int08_off DW ?ant_int08_seg DW ?ant_int10 LABEL DWORDant_int10_off DW ?ant_int10_seg DW ?

; Dejar residente hasta aquí.

main: PUSH ESMOV AX,3508hINT 21h ; obtener vector de INT 8MOV ant_int08_seg,ESMOV ant_int08_off,BXMOV AX,3510hINT 21h ; obtener vector de INT 10hMOV ant_int10_seg,ESMOV ant_int10_off,BXPOP ES

LEA DX,controla_int08MOV AX,2508hINT 21h ; nueva rutina de INT 8

LEA DX,controla_int10MOV AX,2510hINT 21h ; nueva rutina de INT 10h

PUSH ESMOV ES,DS:[2Ch] ; dirección del entornoMOV AH,49hINT 21h ; liberar espacio de entornoPOP ES

LEA DX,main ; fin del código residenteADD DX,15 ; redondeo a párrafoMOV CL,4SHR DX,CL ; bytes -> párrafosMOV AX,3100h ; terminar residenteINT 21h

demores ENDSEND inicio

10.3. - LOCALIZACIÓN DE UN PROGRAMA RESIDENTE.

Un programa residente que ya está instalado en memoria puede volver a ser cargado desde disco yesto hay que tenerlo en cuenta. Puede que el programa sea de éstos que se cargan una sola vez y carecen deparámetros. En ese caso, no sucederá nada porque sea creada en memoria una nueva copia del mismo: esproblema del usuario. Sin embargo, si una recarga posterior puede provocar un cuelgue del sistema o,simplemente, el programa tiene opciones y se pretende modificar los parámetros de la copia ya residente,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.

10.3.1 - MÉTODO DE LOS VECTORES DE INTERRUPCIÓN.

El método más simple es también el más simplón -inútil- y consiste en apoyarse en los vectores deinterrupción. Por ejemplo, si el programa quedó residente interceptando la interrupción 9, basta con mirar adónde apunta dicha interrupción y comprobar un grupo de bytes o alguna identificación que permitadeterminar si el programa que la gestiona es ya una copia de él mismo. El inconveniente de este método, fácilde deducir, es que si se carga más de un programa residente que emplee la INT 9, sólo el último cargado serácapaz de encontrarse a sí mismo en memoria.

10.3.2. - MÉTODO DE LA CADENA DE BLOQUES DE MEMORIA.

Otro método alternativo es rastrear la cadena de bloques de memoria del sistema operativo buscandoprogramas residentes y comprobándolos uno por uno. Este método es bastante rápido, habida cuenta de queno van a existir más de 20-50 bloques de memoria. Sin embargo, la organización de la memoria en los PCses a veces tan anárquica que este método (que debería ser el más elegante) es un poco peligroso en cuantoa la seguridad, aunque mucho menos que el anterior. Lo cierto es que puede ser difícil intentar recorrer lamemoria superior, habida cuenta del desigual tratamiento que recibe en las diversas versiones del DOS y conlos diversos controladores de memoria que pueden estar instalados.

Por cierto, la idea de rastrear toda la memoria (1 Mb), buscando desesperadamente una cadena deidentificación, no es nueva. Sin embargo es tremendamente lenta llevada a la práctica. Es incómoda (hay queconsiderar el caso de que el propio programa que busca se encuentre a sí mismo, en particular en áreas comolos buffers de transferencia con disco del DOS) y bastante salvaje.

Page 164: PCA, PS2 ,IBM y AT

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

10.3.3. - MÉTODO DE LA INTERRUPCIÓN MULTIPLEX.

Finalmente, existe la posibilidad de utilizar el mismo sistema que emplea el DOS para comprobarla presencia de sus propios programas residentes (como el KEYB, GRAPHICS, GRAFTABL, SHARE,PRINT, etc) basado en la interrupción Multiplex (2Fh). Este sistema es el más seguro, aunque un tantolaborioso. Consiste en llamar a la INT 2F con un valor en el registro AH que indica quién está llamando, yotro valor en AL para decir por qué está llamando (normalmente 0). Los valores 00-BFh en AH estánreservados para el DOS, y de C0h-FFh para las aplicaciones. A la vuelta, AL devuelve un valor 0 para indicarque el programa no está instalado pero está permitida la instalación, un valor 1 para decir que no estáinstalado ni tampoco está permitida la instalación. Si devuelve FFh, significa que el programa ya estabainstalado. Por ejemplo, el KEYB del DOS llama a INT 2Fh con AX=AD80h, donde ADh significa que quienpregunta es el KEYB -y no otro programa- para conocer si ya está instalado o no. En caso de que lo esté(AL=FFh a la vuelta), también se devuelve en ES:DI la dirección del KEYB ya residente (que es lo solicitadocon AL=80h). En el caso concreto del KEYB, si a la vuelta AL<>FFh se interpreta que el programa no estáaún residente, por lo que se procede a su instalación (en este caso, curiosamente incluso aunque AL=1).

Esta técnica cuenta con la complicación que supone decidir qué valor emplear en la interrupciónmultiplex. Es evidente que dos programas residentes no pueden utilizar el mismo. Los programas menoseficientes utilizan un valor fijo predeterminado, con lo que limitan las posibilidades del usuario. Sin embargo,para solucionarlo existen varias alternativas, que se verán más adelante.

Aviso: Aunque no es frecuente, algunas versiones 2.X del sistema no tienen inicializado el vector dela INT 2Fh. Por ello, es una buena práctica asegurarse de que esta interrupción apunta a algo antes dellamarla (por ejemplo, verificando que el segmento es distinto de cero). Por otro lado, el comando PRINTdel DOS en las versiones 2.X del sistema gestiona de tal manera la INT 2Fh que ninguna otra aplicaciónpuede emplearla. Por ello, el método de la interrupción Multiplex está más bien reservado para versiones 3.0o superiores (también la 2.X si el usuario prescinde de PRINT).

10.4. - EXPULSIÓN DE UN PROGRAMA RESIDENTE DE LA MEMORIA

Se trata de una tarea bastante sencilla en sí, aunque hay que tener en cuenta una serie de factores.En primer lugar, el programa debe restaurar todos los vectores de interrupción que había interceptado. Ellosignifica que si ha sido instalado tras él otro programa residente que modifica uno de los vectores que élinterceptaba, ya no es posible restaurarlo. Por ello, un primer requisito para permitir la desinstalación es quesea el último programa residente cargado que utiliza un vector de interrupción dado. Esto es fácil deverificar, basta con comprobar que todas las interrupciones interceptadas siguen apuntando a una copia deél. Si esta prueba es superada satisfactoriamente, puede procederse a restaurar los vectores de interrupcióny 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 DOSpara liberar el bloque de memoria.

2) Liberando directamente el bloque de memoria al colocar una palabra a cero en los bytes del MCBque identifican al propietario del bloque. Este método puede ser más seguro si está instalado ungestor de memoria expandida extraño, aunque es menos elegante y quizá menos recomendable.

Por lo general, no tiene mucho sentido que un usuario elimine un programa residente después dehaber cargado otro -aunque ello sea posible- ya que se origina un hueco en la memoria que normalmente nose utilizará para nada -el DOS asigna siempre el mayor bloque disponible al cargar cualquier aplicación-,aunque esto es realmente problema exclusivo del usuario.

Como se verá después, ciertos programas residentes sofisticados permiten ser desinstalados aún sinser los últimos instalados; sin embargo, estos programas residentes tienen que tener algo en común:

Page 165: PCA, PS2 ,IBM y AT

165PROGRAMAS RESIDENTES

comportarse de la misma manera y actuar también de una manera definida. Ello significa que si entre dosprogramas residentes que cumplen el mismo convenio el usuario instala un programa que no lo respeta, sepierden todas las posibilidades.

10.5.- GESTIÓN AVANZADA DE LA INTERRUPCIÓN MULTIPLEX.

10.5.1. - EL CONVENIO BMB COMPUSCIENCE.

Para solucionar el problema de que dos programas residentes no pueden utilizar el mismo valor deidentificación en la interrupción Multiplex, los señores de BMB Compuscience Canada pensaron un buensistema, publicado en el INTERRUP.LST de Ralf Brown, que expongo a continuación.

La idea consiste en asignar dinámicamente el valor del registro AH empleado al llamar a lainterrupción Multiplex. Para ello se empieza, por ejemplo, con AH=0C0h. Se coloca un 0 en AL para solicitarchequeo de instalación y se hace que los registros ES:DI valgan 0EBEBh:0BEBEh (porque sí), llamando acontinuación a la INT 2Fh. A la vuelta se devuelve en 0 en AL para indicar programa no instalado, un 1para señalar además que no se debe instalar, y FFh para decir que ya está instalado... ¿quién?: un programacuyo nombre de fabricante abreviado (MMMM), nombre de producto (PPPPPPPP) y versión (NNNN) estánen ES:DI de la forma "BMB MMMMPPPPPPPPvNNNN". Si se comprueba que ese programa no es elbuscado, se incrementa AH y si AH es menor o igual a 0FFh se repite el proceso. De este bucle puede salirsede dos maneras: encontrando el programa buscado (y su ubicación en memoria) o sin encontrarle, en cuyocaso también se habrá localizado algún valor de AH aún no utilizado por ninguna tarea residente (a no serque el usuario haya instalado ya 64 programas residentes con esta técnica). Lógicamente, el programaresidente debe interceptar también INT 2Fh y devolver (cuando alguien pregunta por él) un valor FFh en ALy, si además el que preguntaba llamaba con ES:DI=0EBEBh:0BEBEh entonces debe devolver en ES:DI lainformación antes mencionada. Lo de emplear 0EBEBh y 0BEBEh constituye un mecanismo similar a unpassword, para evitar que al programa que llama a INT 2Fh se le modifique ES:DI sin que lo sepa.

10.5.2. - EL CONVENIO CiriSOFT.

El convenio anterior adolece de un defecto importante: ya puestos a determinar con tanto detalle elfabricante, nombre y versión del programa, ¿por qué no colocar más información útil?. Por ejemplo, seríainteresante disponer de información sobre los contenidos previos de los vectores de interrupción que elprograma ha desviado, lo cual permitiría su desinstalación aunque no sea el último cargado, ser desinstaladopor parte de otros programas o incluso emplear ciertas técnicas de relocalización en memoria para evitar lafragmentación de la misma cuando es desinstalado. Con objeto de aumentar la eficacia, el autor de este librodesarrolló un método nuevo, extensión del expuesto en el apartado anterior, que permitiera sacar mayorpartido de la interrupción Multiplex. Al igual que el anterior, el nuevo convenio también está publicado enel INTERRUP.LST, lo que garantiza su difusión y la inversión de quienes decidan emplearlo.

El método es similar al anterior, con la diferencia de que en ES:DI está almacenado en el momentode llamar el valor 1492h:1992h. En AH se indica, como siempre, el número de entrada de la interrupciónMultiplex y en AL se coloca un 0 solicitando chequeo de instalación. Tras llamar, si AL devuelve un 1 ó un0FFh significa que esa entrada ya está empleada, si devuelve un 0 significa que está libre y que puede serutilizada. Hasta ahora, todo sucede como es costumbre en los programas que utilizan la interrupciónMultiplex. Sin embargo, por el hecho de haber llamado con ES:DI=1492h:1992h, el programa residente sabeque quien lo llama es alguien que respeta el convenio. Por ello, además de devolver un 0FFFFh en AX,modifica ES y DI para apuntar a una tabla con la siguiente información:

Offset Tamaño Descripción-16 WORD segmento donde realmente comienza el código del TSR (CS en programas

con PSP, segmento de memoria superior XMS si instalado como UMB...)-14 WORD offset donde realmente comienza el código del TSR (frecuentemente 100h

en programas *.COM y 0 en TSR’s en memoria superior).-12 WORD memoria empleada por el TSR (en párrafos). Conociendo la memoria que

emplea el TSR es posible determinar si los vectores que intercepta están

Page 166: PCA, PS2 ,IBM y AT

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

aún apuntándolo (y si es seguro el proceso de desinstalación).-10 BYTE de características

bits 0-2: 000 programa normal (con PSP)001 bloque de memoria superior XMS (se necesita función de HIMEM.SYS

para liberar la memoria al desinstalar)010 device driver (*.SYS)011 device driver en formato EXE1xx otros (reservados)

bits 3-6 reservadosbit 7 activo si tabla_extra definida y soportada

-9 BYTE número de entrada en la interrupción Multiplex (redefinible por un agenteexterno). Notar que el TSR debe usar ESTA variable en su rutina de controlde INT 2Fh.

-8 WORD offset a la tabla area_vectores (se verá después)-6 WORD offset a la tabla area_extra (ver bit 7 en offset -10)-4 4 BYTEs "*##*" (asegurar que el TSR verifica el convenio)00h ??? "AUTOR:NOMBRE_DEL_PROGRAMA:VERSION",0 (longitud variable, este área

es empleada de cara a determinar si el TSR está ya residente y suversión; 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 quepermanece residente, ya que se considera que la ubicación del programa comienza en el offset 0 relativo alsegmento definido en ES:DI-16 y, por tanto, el tamaño del programa definido en ES:DI-12 es relativotambién con offset 0 a ese segmento. Si bien se puede opinar que son demasiados campos, son sólo poco másde 16 bytes los que se añaden al programa residente. Además, muchas de las variables anteriores han de estardefinidas necesariamente: ¿por qué no juntarlas de una manera convenida?. En la tabla anterior se define unpuntero a una estructura con información sobre los vectores interceptados. No se respeta sin embargo elformato de los encabezamientos de interrupción propuesto en la BIOS del PS/2 (la intención de IBM esbuena, pero ha llegado demasiado tarde).

Formato de la tabla area_vectores:Offset Tamaño Descripción-1 BYTE número de vectores interceptados por el TSR00h BYTE número del primer vector01h DWORD puntero al primer vector antes de instalar el TSR05h BYTE número del segundo vector06h DWORD puntero al segundo vector antes de instalar el TSR. . (y así sucesivamente). Notar que el TSR debe usar ESTAS variables para

invocar las anteriores rutinas de control de esas interrupciones, ya que un. . agente externo podría actualizarlas.

En las primeras versiones de este convenio ya no existían más reglas. Sin embargo, al final comprendíla necesidad de ampliar las prestaciones. Por ello, el convenio fue ampliado con dos tablas más, opcionales,que es conveniente rellenar incluso también en aquellos TSR más sencillos que ocupan menos de 64 Kb yson totalmente reubicables (no contienen referencias absolutas a segmentos). Estas tablas permitirían a unhipotético sistema operativo mover los programas residentes para evitar la fragmentación de la memoria, tareaque mientras tanto puede realizar algún programa de utilidad. Aquellos TSR que contengan referencias ensu 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, al ser recargados en memoria desde disco(necesario para la reubicación) deben instalarse silenciosamente sin chitar, autoinhibiéndose a continuación.En general, la mayoría de los programas residentes escritos en ensamblador son relocalizables, así como loselaborados en el modelo Tiny del C, por lo que no es muy complejo realizar esta tarea. La única pega quese puede poner es que, por desgracia, ¡pocos programas usan este convenio!.

Formato de la tabla area_extra (opcional):Offset Tamaño Descripción00h WORD offset a la tabla control_externo (0 si no soportada)02h WORD reservado para futuro uso (0)

Formato de la tabla control_externo (opcional):Offset Tamaño Descripción00h 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.0Bh DWORD puntero a la última variable (todas están en el mismo bloque).

Page 167: PCA, PS2 ,IBM y AT

167PROGRAMAS RESIDENTES

La variable que activa o inhibe el TSR permite paralizarlo momentáneamente antes de realizar ciertastareas críticas, si bien no está pensada su utilización de cara a relocalizarlo en memoria o a desinstalarlo.

A continuación se listan dos rutinas que habrá de incorporar todo programa que desee emplear esteconvenio (u otras equivalentes). Las rutinas las he denominado mx_get_handle y mx_find_tsr. La primerapermite buscar un valor para la interrupción Multiplex aún no empleado por otra tarea residente, tanto si éstaes del convenio como si no. La segunda sirve para que el programa residente se busque a sí mismo en lamemoria. En esta segunda rutina se indica el tamaño de la cadena de identificación (la que contiene elnombre del fabricante, programa y versión) en CX. Si no se encuentra el programa residente en la memoria,puede repetirse la búsqueda con CX indicando sólo el tamaño del nombre del fabricante y el programa, sinincluir el de la versión: así se podría advertir al usuario que tiene instalada ya otra versión distinta.

; ------------ 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 PROCMOV AH,0C0h

mx_busca_hndl: PUSH AXMOV AL,0INT 2FhCMP AL,0FFhPOP AXJNE mx_si_huecoINC AHJNZ mx_busca_hndl

mx_no_hueco: STCRET

mx_si_hueco: CLCRET

mx_get_handle ENDP

; ------------ 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 PROCMOV AH,0C0h

mx_rep_find: PUSH AXPUSH CXPUSH SI

PUSH DSPUSH ESPUSH DIMOV AL,0PUSH CXINT 2FhPOP CXCMP AL,0FFhJNE mx_skip_hndl ; no hay TSR ahíCLDPUSH DIREP CMPSB ; comparar identificaciónPOP DIJE mx_tsr_found ; programa buscado hallado

mx_skip_hndl: POP DIPOP ESPOP DSPOP SIPOP CXPOP AXINC AHJNZ mx_rep_findSTCRET

mx_tsr_found: ADD SP,4 ; «sacar» ES y DI de la pilaPOP DSPOP SIPOP CXPOP AXCLCRET

mx_find_tsr ENDP

La rutina mx_unload desinstala un programa residente que verifique el convenio; basta con indicarel número de interrupción Multiplex que emplea el TSR. El proceso de desinstalación falla si se ha instaladodespués un TSR que no verifica el convenio y tiene alguna interrupción en común, ya que la rutina no puedeen ese caso recorrer la cadena de vectores para modificarla anulando la tarea residente. Para que un TSR seauto-desinstale basta con que suministre a esta rutina su propio número de identificación. El método empleadopor la rutina para cambiar los vectores de interrupción no es muy ortodoxo, pero simplifica el algoritmo yposee un nivel de seguridad razonable. Esta rutina da dos pasadas: el objeto de la primera es sólo asegurarque el TSR puede ser desinstalado antes de empezar a cambiar ningún vector. En la segunda, se cambian losenlaces entre los vectores y se libera la memoria, bien llamando al DOS o al controlador XMS (según quiénla haya asignado). Hay una maniobra más o menos complicada para hacer que el vector 2Fh sea el últimorestaurado, con objeto de poder seguir la cadena de interrupciones hasta el propio TSR invocando la INT 2Fh.

; ------------ Eliminar TSR del convenio si es posible. A la entrada,; en AH se indica la entrada Multiplex; a la salida, CF=1; si fue imposible y CF=0 si se pudo. Se corrompen todos; los registros salvo los de segmento. En caso de fallo; al desinstalar, AL devuelve el vector «culpable».

mx_unload PROCPUSH ESCALL mx_ul_tsrcv?JNC mx_ul_ablePOP ESRET

mx_ul_able: XOR AL,ALXCHG AH,ALMOV BP,AX ; BP=entrada Multiplex del TSRMOV CX,2

mx_ul_pasada: PUSH CX ; siguiente pasadaLEA SI,tabla_vectoresMOV CL,ES:[SI-1]MOV CH,0 ; CX = nº vectores

mx_ul_masvect: POP AXPUSH AX ; pasada en cursoDEC ALPUSH CX

mx_ul_2f: MOV AL,ES:[SI] ; vector en cursoJNZ mx_ul_pasokCMP CX,1 ; ¿último vector?JNE mx_ul_noultMOV AL,2Fh

LEA SI,tabla_vectoresmx_ul_busca2f: CMP ES:[SI],AL ; ¿INT 2Fh?

JE mx_ul_pasokADD SI,5JMP mx_ul_busca2f

mx_ul_noult: CMP AL,2Fh ; ¿restaurar INT 2Fh?JNE mx_ul_pasokADD SI,5JMP mx_ul_2f

mx_ul_pasok: PUSH ESPUSH AXMOV AH,0SHL AX,1SHL AX,1DEC AXMOV CS:mx_ul_tsroff,AXMOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectoresPOP AXPUSH AXMOV AH,35hINT 21h ; vector en ES:BXPOP AXMOV CL,4SHR BX,CLMOV DX,ESADD DX,BX ; INT xx en DX (aprox.)MOV AH,0C0h

mx_ul_masmx: CALL mx_ul_tsrcv?JNC mx_ul_tsrcv

Page 168: PCA, PS2 ,IBM y AT

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

JMP mx_ul_otromx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI

PUSH ES:[DI-12]MOV DI,ES:[DI-8] ; offset a la tabla de vectoresMOV CL,ES:[DI-1]MOV CH,0 ; número de vectores en CX

mx_ul_buscav: CMP AL,ES:[DI]JE mx_ul_usavect ; este TSR usa vector analizadoADD DI,5LOOP mx_ul_buscavADD SP,4 ; no lo usaJMP mx_ul_otro

mx_ul_usavect: POP CX ; tamaño del TSRPOP BX ; segmento del TSRCMP DX,BXJB mx_ul_otro ; la INT xx no le apuntaADD BX,CXCMP DX,BXJA mx_ul_otro ; la INT xx le apuntaPUSH AXXOR AL,ALXCHG AH,ALCMP AX,BP ; ¿es el propio TSR?POP AXJNE mx_ul_chain ; noPOP ES ; sí: ¡posible reponer vector!POP CXPOP BXPUSH BXPUSH CXPUSH ESDEC BXJNZ mx_ul_norest ; no es la segunda pasadaPOP ES ; segunda pasada...PUSH ESPUSH DSMOV BX,CS:mx_ul_tsroff ; restaurar INT’sMOV DS,CS:mx_ul_tsrsegCLIMOV CX,ES:[SI+1]MOV [BX+1],CXMOV CX,ES:[SI+3]MOV [BX+3],CXSTIPOP DS

mx_ul_norest: POP ESPOP CXADD SI,5 ; siguiente vectorDEC CXJZ mx_unloadable ; no más, ¡desinstal-ar/ado!JMP mx_ul_masvect

mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direcciónMOV CS:mx_ul_tsrseg,ES ; de la variable vectorMOV DX,ES:[DI+1]MOV CL,4SHR DX,CL

MOV CX,ES:[DI+3]ADD DX,CX ; INT xx en DX (aprox.)MOV AH,0BFh

mx_ul_otro: INC AH ; a por otro TSRJZ mx_ul_exitnok ; ¡se acabaron!JMP mx_ul_masmx

mx_ul_exitnok: ADD SP,6 ; equilibrar pilaPOP ESSTCRET ; imposible desinstalar

mx_unloadable: POP CXDEC CXJZ mx_ul_exitok ; desinstaladoJMP mx_ul_pasada ; 1ª pasada exitosa: por la 2ª

mx_ul_exitok: TEST ES:info_extra,111b ; ¿tipo de instalación?MOV ES,ES:segmento_real ; segmento real del bloqueJZ mx_ul_freeml ; cargado en RAM convencionalCMP xms_ins,1JNE mx_ul_freeml ; no hay controlador XMS (¿?)MOV DX,ESMOV AH,11hCALL gestor_XMS ; liberar memoria superiorPOP ESCLCRET

mx_ul_freeml: MOV AH,49hINT 21h ; liberar bloque de memoria ES:POP ESCLCRET

mx_ul_tsrcv?: PUSH AX ; ¿es TSR del convenio?...PUSH ESPUSH DIMOV DI,1492hMOV ES,DIMOV DI,1992hINT 2FhCMP AX,0FFFFhJNE mx_ul_ncvexitCMP WORD PTR ES:[DI-4],"#*"JNE mx_ul_ncvexitCMP WORD PTR ES:[DI-2],"*#"JNE mx_ul_ncvexitADD SP,4 ; CF=0POP AXRET

mx_ul_ncvexit: POP DI ; ...no es TSR del convenioPOP ESPOP AXSTC ; CF=1RET

mx_ul_tsroff DW 0mx_ul_tsrseg DW 0mx_unload ENDP

Los dos programas siguientes constituyen dos pequeñas utilidades de apoyo a los TSR de esteconvenio. TSRLIST lista los TSR del convenio que están instalados en el ordenador, con informacióndetallada; TSRKILL permite eliminar uno o todos los TSR que estén instalados en cualquier orden, no sólonecesariamente el último que fue cargado. Lógicamente, si entre varios programas que respetan el conveniohay uno que lo viola, TSRKILL puede no ser capaz de desinstalar un TSR del convenio. En ese caso, seinforma de qué vector ha sido el culpable. Ejemplo de salida de TSRLIST /V:

TSRLIST 1.3 (c) Febrero 1994 CiriSOFT.Listado de tareas residentes normalizadas:

Programa Ver. Dirección Tamaño Mx. ID Vectores interceptados-------- ----- --------- ------ -------- -------------------------------------RCLOCK 2.3 E8A3:0000 1424 192 08 09 10 2FKEYBFIX 1.0 E15B:0000 208 193 09 2FDISKLED 2.1 E8FD:0060 528 194 08 09 13 2FDATAPLUS 2.4 E91F:0060 18640 195 09 2FANSIUP 1.0 EDAD:0060 576 196 29 2FHBREAK 4.1 EDD2:0000 1584 197 08 09 20 21 27 2F 70SCRCAP 1.0 F23E:0100 2144 198 08 09 13 28 2F

- ID de programas residentes que incumplen convenio: 210;

La entrada multiplex 210 (0D2h) de que informa TSRLIST es utilizada por QEMM386; TSRLISTtambién informa de las entradas que están siendo utilizadas por programas que no respetan el convenio,aunque lógicamente no da más información.

/********************************************************************//* *//* TSRLIST 1.3 - Utilidad de listado de TSR’s normalizados - BC++ *//* *//********************************************************************/

#include <dos.h>#include <string.h>

void cabecera(),listar_tsr(),obtener_item();

void main (int argc, char *argv[])

int entrada, /* para rastrear entradas de INT 0x2F */

vect=0, /* a 1 si se detecta parámetro /V */primera_vez=1, /* a 0 cuando no lo sea */raro=0; /* a 1 si detectado TSR no del convenio */

char tsr_raro[64]; /* flags de TSRs que no respetan el convenio */

if ((argc>1) && (!strcmp(strupr(argv[1]),"/V"))) vect=1;

printf("\nTSRLIST 1.3 (c) Febrero 1994 CiriSOFT.\n");printf(" Listado de tareas residentes normalizadas:\n\n");

for (entrada=0xc0; entrada<=0xff; entrada++) tsr_raro[entrada-0xc0]=0;if (hay_tsr(entrada))

if (tsr_convenio (entrada)) if (primera_vez) cabecera(vect); /* encabezamiento */

Page 169: PCA, PS2 ,IBM y AT

169PROGRAMAS RESIDENTES

listar_tsr (entrada, vect); /* informar del TSR */primera_vez=0;

else tsr_raro[entrada-0xc0]=raro=1; /* TSR no del convenio */

if (raro) printf("\n- ID de programas residentes que incumplen convenio: ");for (entrada=0; entrada<64; entrada++)if (tsr_raro[entrada]) printf("%2d; ", entrada+0xc0);

if (vect) printf("\n");

if (!vect) printf("\n- Ejecute con /V para listado de vectores.\n");

int hay_tsr (int entrada) /* función booleana: 1 si hay TSR */struct REGPACK r;r.r_ax=entrada << 8;intr (0x2f, &r);return ((r.r_ax & 0xff)==0xff);

int tsr_convenio (int entrada)struct REGPACK r;

r.r_ax=entrada << 8;r.r_es=0x1492; r.r_di=0x1992;intr (0x2f, &r);return ((r.r_ax==0xFFFF) &&(peek(r.r_es,r.r_di-4)==9002) && (peek(r.r_es,r.r_di-2)==10787));

void cabecera(int vect)

printf("Programa Ver. Dirección Tamaño Mx. ID ");if (vect)

printf (" Vectores interceptados\n");elseprintf (" Autor/fabricante\n");

printf("-------- ----- --------- ------ -------- ");printf("-----------------------------------\n");

void listar_tsr (int entrada, int vect)struct REGPACK r;char cad[40];unsigned int base, cont;char huge *info;

r.r_ax=entrada << 8; r.r_es=0x1492; r.r_di=0x1992;intr (0x2f, &r); info=MK_FP(r.r_es, r.r_di);

obtener_item (1, 8, info, cad); /* elemento 1: nombre */printf("%-8s", cad);obtener_item (2, 3, info, cad); /* elemento 2: versión */printf(" %-4s %04X:%04X ",cad, peek(r.r_es, r.r_di-16), peek(r.r_es, r.r_di-14));

printf("%6u %03u ",peek(r.r_es, r.r_di-12)*16, peekb(r.r_es, r.r_di-9) & 0xff);

if (vect) /* listado de vectores */ base=peek(r.r_es, r.r_di-8);for (cont=0; cont<peekb(r.r_es, base-1); cont++) if (!(cont % 12) && cont) /* excesivos vectores: otra línea */printf ("\n ");

printf("%02X ", peekb(r.r_es, base+cont*5));

else /* imprimir autor */ obtener_item (0, 37, info, cad); /* elemento 0: autor */printf("%s", cad);

printf("\n");

void obtener_item (int posicion, int max_long,char huge *info, char *cad)

int i;

for (i=0; i<posicion; i++) while ((*info++)!=’:’);i=0; while ((*info!=’:’) && (*info)) cad[i++]=*info++;cad[i]=cad[max_long]=0; /* fin de cadena y controlar tamaño */

######################################################################

/********************************************************************//* *//* TSRKILL 1.3 - Utilidad de desinstalación de TSRs normalizados. *//* Compilar en el modelo «Large» de Borland C. *//* *//********************************************************************/

#include <dos.h>#include <string.h>#include <stdio.h>#include <stdlib.h>

struct tsr_info unsigned segmento_real;unsigned offset_real;unsigned ltsr;unsigned char info_extra;unsigned char multiplex_id;unsigned vectores_id;unsigned extension_id;unsigned long validacion;char autor_nom_ver[80];

;

int tsr_convenio(),mx_unload(),existe_xms();

void liberar_umb(),desinstalar();

void main (int argc, char **argv)

int mxid;struct tsr_info far *tsr;

printf ("\nTSRKILL 1.3\n");if ((((mxid=atoi(argv[1]))<0xc0) || (mxid>0xFF)) && (mxid!=-1))

printf (" - Indicar número Mx. ID (TSRLIST) entre 192 y 255");printf (" (-1 todos los TSR).\n");exit (1);

if (mxid==-1) for (mxid=0xc0; mxid<=0xFF; mxid++)

if (tsr_convenio(mxid, &tsr)) desinstalar (mxid);

elsedesinstalar (mxid);

void desinstalar (int mxid)

int vector, correcto;char far *nombre, *p,

cadena [80], cadaux[80];

correcto=mx_unload (mxid, &vector, &nombre);

if (correcto || (vector<0x100)) strcpy (cadaux, nombre); p=cadaux;while (*p) if ((*p++)==’:’) *(p-1)=0; p=cadaux;while (*p++); strcpy (cadena, p); /* nombre programa */strcat (cadena, " ");while (*p++); strcat (cadena, p); /* versión */strcat (cadena, " de ");strcat (cadena, cadaux); /* autor */

if (correcto)printf(" - Desinstalado el %s\n", cadena);

else if (vector==0x100)

printf (" - No hay TSR %u o no es del convenio.\n", mxid);else if (vector==0x101)

printf (" - HBREAK es «demasiado fuerte» para TSRKILL.\n");else if (vector==0x102)

printf (" - 2MGUI es «demasiado fuerte» para TSRKILL.\n");else

printf (" - El %s no se puede desinstalar: ", cadena);printf ("fallo en el vector %02X.\n", vector);

int mx_unload (int mxid, int *interrupción, char far **tsrnombre)

int mx, posible, vx, vector, i, nofincadena;unsigned intptr, iniciotsr, tablaptr[256][2], sgm, ofs;char numvect;struct tsr_info far *tsr, far *tsrx;struct REGPACK r;void interrupt (*interr)();

if (!tsr_convenio (mxid, &tsr)) *interrupción=0x100;return (0);

numvect = peekb(FP_SEG(tsr), tsr->vectores_id-1);for (i=0; i<256; i++) tablaptr[i][0]=tablaptr[i][1]=0;

for (posible=1, vx=0; posible && (vx<numvect); vx++) vector = peekb(FP_SEG(tsr), tsr->vectores_id+5*vx);intptr = FP_SEG(getvect(vector)) + (FP_OFF(getvect(vector)) >> 4);nofincadena=1; mx=0xC0;while (posible && nofincadena)

if (tsr_convenio (mx, &tsrx)) iniciotsr=tsrx->segmento_real; /* el OFFSET se desprecia */i=peekb(FP_SEG(tsrx), tsrx->vectores_id-1);while ((peekb(FP_SEG(tsrx),tsrx->vectores_id+5*(i-1))!=vector)

&& i) i--;if (i && (intptr>=iniciotsr)&&(intptr<=iniciotsr+tsrx->ltsr))

if (mx==mxid) nofincadena=0;else

tablaptr[vx][0]=FP_SEG(tsrx);tablaptr[vx][1]=tsrx->vectores_id+5*(i-1)+1;intptr=peek(tablaptr[vx][0],tablaptr[vx][1]+2) +((unsigned) peek(tablaptr[vx][0],tablaptr[vx][1]) >>4);mx=0xBF; /* compensar incremento posterior */

if (mx==0xFF) posible=0; else mx++;

*interrupción = vector;*tsrnombre = tsr->autor_nom_ver;

if (strstr(*tsrnombre, "HBREAK")!=NULL) posible=0; *interrupción=0x101;

if (strstr(*tsrnombre, "2MGUI")!=NULL) posible=0; *interrupción=0x102;

if (posible) for (i=0; i<numvect; i++)

vector = peekb(FP_SEG(tsr), tsr->vectores_id+5*i);sgm = peek(FP_SEG(tsr), tsr->vectores_id+5*i+3);ofs = peek(FP_SEG(tsr), tsr->vectores_id+5*i+1);if ((tablaptr[i][0]==0) && (tablaptr[i][1]==0))

interr=MK_FP(sgm, ofs);setvect (vector, interr);

else asm clipoke (tablaptr[i][0], tablaptr[i][1], ofs);poke (tablaptr[i][0], tablaptr[i][1]+2, sgm);

Page 170: PCA, PS2 ,IBM y AT

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

asm sti

switch (tsr->info_extra & 3) case 0: r.r_es=tsr->segmento_real; r.r_ax=0x4900;

intr (0x21, &r); break;case 1: if (existe_xms()) liberar_umb (tsr->segmento_real);

break;

return (posible);

int tsr_convenio (int entrada, struct tsr_info far **info)struct REGPACK r;

r.r_ax=entrada << 8;r.r_es=0x1492; r.r_di=0x1992;intr (0x2f, &r);*info = MK_FP(r.r_es, r.r_di-16);return ((r.r_ax==0xFFFF) &&(peek(r.r_es,r.r_di-4)==9002) && (peek(r.r_es,r.r_di-2)==10787));

int existe_xms ()

struct REGPACK r;

r.r_ax=0x4300; intr (0x2F, &r); return ((r.r_ax & 0xFF)==0x80);

void liberar_umb (unsigned segmento)

long controlador;

asm push es; push si; push di;mov ax,4310hint 2Fhmov word ptr controlador,bxmov word ptr controlador+2,esmov ah,11hmov dx,segmentocall controladorpop di; pop si; pop es;

10.5.3.- LA PROPUESTA AMIS.

La interrupción Multiplex presenta un elevado nivel de polución debido al gran número de programasque la utilizan incorrectamente. En algunos casos se soluciona el problema instalando primero los programasconflictivos y después los que trabajan bien. Lo mínimo que se puede exigir a un programa residente queutilice esta interrupción es que soporte el chequeo de instalación (la llamada con AL=0) y devuelva una señalde reconocimiento afirmativo (AL=0FFh) si está empleando esa entrada en cuestión. Sin embargo, algunosno llegan ni a eso. Por fortuna, son tan malos que casi nadie los emplea. Sin embargo, con objeto desolucionar estos casos, Ralf Brown -autor del INTERRUP.LST- ha desarrollado un método alternativo basadoen la interrupción 2Dh. Esta interrupción no ha sido empleada hasta ahora por el DOS ni por ningunaaplicación importante. La propuesta AMIS (Alternate Multiplex Interrupt Specification) implementa unsistema estandarizado de interface con los programas residentes. Habida cuenta de que las principalesempresas desarrolladoras de software de sistemas ojean el INTERRUP.LST antes de utilizar una interrupción,para evitar conflictos entre aplicaciones, es de esperar que la propia Microsoft no utilice tampoco la INT 2Dhpara sus propósitos en futuras versiones del DOS. Por tanto, no es muy arriesgado seguir este convenio. Lainformación que expongo a continuación se corresponde con la versión 3.4 de la especificación.

Los programas que emplean la INT 2Dh deben interceptarla e implementar una serie de funciones.Como luego veremos, no es necesario que soporten todas las que propone el convenio. A la hora de llamara la INT 2Dh se indicará en AH, tal como se hacía con la interrupción Multiplex, el número de entrada y enAL la función. Todo el funcionamiento se basa en invocar funciones en el programa residente. Elinconveniente de ejecutar código en la copia residente es que ocupa algo más de memoria, y la necesidad deimplementar dichas funciones. La ventaja de ejecutar código en la copia residente es que ésta puede, en dondesea procedente, restaurar el estado del sistema de manera más completa o realizar tareas específicas que seannecesarias. Por citar un ejemplo, TSRKILL no puede desinstalar las conocidas utilidades HBREAK o 2MGUI,que, en cambio, con la propuesta AMIS podrían haber soportado una función de desinstalación accesible porcualquier agente externo. Existen las siguientes funciones:

- Función 0: Chequeo de instalación. Si no hay un TSR utilizando ese número se devuelve un 0 enAL. En caso contrario se devuelve un 0FFh en AL; en CX se devuelve además el número de versión delinterface AMIS que soporta el TSR (ej. CX=340h para la v3.4); en DX:DI se entrega la dirección de lacadena de identificación, con el siguiente formato:

Offset 0 (8 bytes): Nombre del fabricante (rellenado con espacios al final).Offset 8 (8 bytes): Nombre del programa (rellenado con espacios si hace falta).Offset 16 (hasta 64 bytes): Cadena ASCIIZ (terminada en 0) con la descripción del producto;este campo puede constar simplemente de un cero si no se desea inicializarlo.

- Función 1: Obtener punto de entrada. 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 solicitaal TSR un punto de entrada alternativo para poder llamarlo de una manera más directa sin la INT 2Dh. Si

Page 171: PCA, PS2 ,IBM y AT

171PROGRAMAS RESIDENTES

devuelve un 0 en AL, significa que el TSR debe ser invocado obligatoriamente vía INT 2Dh. Si devuelveun 0FFh en AL ello implica que soporta una llamada directa, cuyo punto de entrada devuelve en DX:BX.

- Función 2: Desinstalación. A la entrada, se indica al TSR en DX:BX el punto donde deberá saltartras su autodesinstalación (si la soporta). A la vuelta, el TSR devuelve un código en AL que se interpreta:

0 - Función no implementada.1 - Fallo.2 - No es posible desinstalar ahora, el TSR lo intentará cuando pueda.3 - Es seguro desinstalar, pero el TSR no dispone de rutina al efecto. El TSR está aún habilitado ydevuelve en BX el segmento del bloque de memoria donde reside.4 - Es seguro desinstalar, pero el TSR no dispone de rutina al efecto. El TSR está inhibido ydevuelve en BX el segmento del bloque de memoria donde reside.5 - No es seguro desinstalar ahora. Intentar de nuevo más tarde.0FFh - Todo ha ido bien, TSR desinstalado: retorna con AX corrompido a la dirección DX:BX.

- Función 3: Solicitud de POP-UP. Esta función está diseñada sólo para los programas residentes quemuestran menús en pantalla al ser activados (normalmente con una combinación de teclas). El valor quedevuelve en AL se interpreta:

0 - Función no implementada, el TSR no es de tipo POP-UP.1 - No es posible el POP-UP ahora, intentar solicitud más tarde.2 - No es posible el POP-UP en este preciso instante, el TSR lo reintentará en breve.3 - El TSR ya está POP-UPado.4 - Imposible hacer POP-UP, se requiere intervención del usuario. En BX se devuelve la causagenérica del fallo: 0-Desconocido, 1-La cadena de interrupciones se solapa con memoria que debeser desalojada para el POP-UP, 2-Fallo en las operaciones de swapping necesarias para el POP-UP.Además, en CX se devuelve un código de error exclusivo de la aplicación que se trate.0FFh - El TSR fue correctamente POP-UPado y posteriormente abandonado por el usuario. A lavuelta, BX entrega un 0 para no indicar nada, un 1 para indicar que el TSR fue descargado por elusuario y los valores 2 al 0FFh están reservados para futuros usos. Los valores 100h al 0FFFFh enBX están a disposición del programa que se trate.

- Función 4: Determinar los vectores interceptados. A la entrada se indica en BL el número de lainterrupción (excepto 2Dh). A la vuelta, AL devuelve un código:

0 - Función no implementada.1 - Imposible determinar.2 - La interrupción indicada ha sido interceptada.3 - La interrupción indicada ha sido interceptada, DX:BX apunta a la rutina que la gestiona.4 - Se devuelve en DX:BX la lista de interrupciones interceptadas.0FFh - Esa interrupción no ha sido interceptada.

Esto en principio significa que el TSR puede hacer casi lo que le da la gana cuando le preguntan quéinterrupciones controla. Los valores 1 al 3 sólo están definidos por compatibilidad con versiones anterioresde la especificación (v3.3), el autor del convenio avisa que no serán quizá soportados en otras versiones. Portanto, lo más normal es que el TSR devuelva un valor 4 sin hacer caso del valor de BL (de lo contrario, elprograma que llama tendría que hacer un molesto bucle comprobando todas las interrupciones). Sería unalástima que un TSR devolviera un valor 0. 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).Offset 1 (2 bytes): Offset a la rutina de control de interrupción.

La rutina de control de interrupción respeta este formato, propuesto por IBM en las BIOS de PS/2:

Page 172: PCA, PS2 ,IBM y AT

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

Offset 0 (2 bytes): Salto corto a donde realmente empieza la rutina de control (10EBh).Offset 2 (4 bytes): Dirección previa de ese vector de interrupción.Offset 6 (2 bytes): Valor 424Bh (consejo de IBM).Offset 8 (1 byte): Banderín de EOI, 0 si es interrupción software o controlador secundario de lainterrupción hardware, 80h si es el controlador primario de la interrupción hardware (debe enviar uncomando EOI al controlador de interrupciones 8259).Offset 9 (2 bytes): Salto corto a la rutina de reset hardware (que retornará con RETF).Offset 0Bh (7 bytes): Reservados (a 0).Offset 12h: Rutina que controla la interrupción.

- Funciones 5 y siguientes: Reservadas para futuras versiones del convenio, devuelven 0 al no estarimplementadas.

Por supuesto, los programas que cumplan la propuesta AMIS deben asignar dinámicamente el númerode entrada que van a utilizar en la INT 2Dh, buscando uno libre. Para chequear su instalación han de emplearlos 16 bytes que indican el nombre del fabricante y el programa. Como dije al principio, no es preciso queun programa soporte todas estas funciones: para cumplir con la versión 3.4 de la especificación basta conimplementar las funciones 0, 2 (sin obligación de disponer de rutina de desinstalación) y la 4 (devolviendoun valor 4).

10.5.4.- COMPARACIÓN ENTRE MÉTODOS.

Cualquiera de los tres métodos expuestos es válido para lograr una correcta localización del programaresidente en memoria. El más sencillo es el primero (aunque ES:DI puede estar asignado de la manera queel lector considere oportuna, por supuesto). Sin embargo, son los dos últimos los más recomendables, por lasprestaciones que ofrecen. El más completo es la propuesta AMIS.

10.6. - MÉTODOS ESPECIALES PARA ECONOMIZAR MEMORIA.

De cara a aumentar el número potencial de usuarios de un programa residente es fundamentalconsiderar el aspecto de la ocupación de memoria. El método más sencillo es implementar el programa comofalso controlador de dispositivo (se verán en el capítulo siguiente) con objeto de evitar el PSP; sin embargo,estos programas sólo pueden ser ejecutados una vez en el momento de arranque del sistema. No obstante, conlos programas COM y EXE normales también se pueden tomar una serie de medidas para reducir laocupación de memoria: la primera y más efectiva es no dejar residente el inservible espacio de entorno, comose vio en capítulos anteriores. Otra de ellas consiste en emplear el PSP para almacenar datos; esto último sólodebe hacerse después de finalizada la ejecución del programa -después de haber entregado el control alsistema-, ya que el PSP es utilizado por el DOS al terminar la ejecución. En todo caso conviene respetar almenos los dos primeros bytes (y a ser posible también los dos situados en el offset 2Ch) con objeto de queno se vuelvan locos los programas del sistema que informan sobre el estado de la memoria(fundamentalmente el comando MEM). Si el programa utiliza pocos datos como para cubrir el PSP, cabe laposibilidad de colocar código en el mismo, para lo cual el programa puede auto-relocalizarse hacia atrás enla memoria, machacando los 171 últimos bytes del PSP que no son vitales para el sistema: en efecto, en eloffset 5Ch comienza el primer FCB; los 7 bytes anteriores corresponden al FCB extendido -circunstancia quepoco suelen poner de relieve los libros técnicos- por lo que el único área que es obligatorio respetar es lazona 00-54h: 85 bytes (incluso este área podría ser también casi totalmente ocupada, como se dijo antes, perodespués de finalizar la ejecución del programa). Por comodidad, se respetarán los primeros 96 bytes, justo6 párrafos: moviendo el programa hacia atrás un número entero de párrafos, al final resulta sencillo desviarlos vectores de interrupción decrementando su segmento en 6 unidades menos antes de desviarlos. Esta tretasólo es factible, por supuesto, en programas de un solo segmento, tipo COM. Los de tipo EXE normalmentedejarán residente todo el PSP, ya que es un segmento previo al programa (de hecho, al terminar residente hayque añadir el tamaño del PSP) y sería complicada la reubicación.

Page 173: PCA, PS2 ,IBM y AT

173PROGRAMAS RESIDENTES

Es cierto que estas técnicas, con programas que se mueven a si mismos dando vueltas por la memoria,automodificándose ... no son consideradas elegantes por los programadores conservadores, y no se puedenhacer estas salvajadas en entornos con protección de memoria (UNIX, etc.); de hecho, Niklaus Wirth sellevaría sin duda las manos a la cabeza. Sin embargo el DOS y el 8086 las permiten y pueden ser bastanteútiles, en especial para los programadores de sistemas. Además, escondiendo bien los fuentes, lo más probablees que nadie se entere de ello...

10.7. - PROGRAMAS AUTOINSTALABLES EN MEMORIA SUPERIOR.

Los TSR más eficientes deben detectar la presencia de memoria superior e instalarse automáticamenteen ella, por varios motivos. Por un lado, se mejora el rendimiento en aquellas máquinas con usuariosinexpertos que no emplean el HILOAD o el LOADHIGH del sistema. Por otro, un programa residente puedeocupar mucho más espacio en disco que lo que luego ocupará en memoria. Si se utiliza LOADHIGH oHILOAD, el sistema intenta reservar memoria para poder cargar el fichero desde disco. Esto significa quepuede haber casos en que no tenga suficiente memoria para cargar el programa, con lo que lo cargará enmemoria convencional. Sin embargo, ese TSR tal vez hubiera cabido en la memoria superior: si es el propioTSR el que se auto-relocaliza (copiándose a sí mismo) hacia la memoria superior, este problema desaparece.Tratándose de programas de un solo segmento real, como los COM, no es problema alguno realizar laoperación de copia.

Con DR-DOS y, en general, con ciertos controladores de memoria (tales como QEMM) la memoriasuperior es gestionada por la especificación de memoria extendida XMS (véase apartado 8.3). Para utilizarla memoria superior en estos sistemas hay que detectar la presencia del controlador XMS y pedirle lamemoria (también habrá que llamarle después para liberarla). Con MS-DOS 5.0 y posteriores sólo existememoria superior XMS si NO se indica DOS=UMB en el CONFIG.SYS; sin embargo, la mayoría de losusuarios suelen indicar esta orden con objeto de que el MS-DOS permita emplear LOADHIGH yDEVICEHIGH. Por desgracia, con MS-DOS, cuando el DOS gestiona la memoria superior, se la roba todaal controlador XMS. Por tanto, habrá que pedírsela al DOS. Con MS-DOS, el procedimiento general es elsiguiente: Primero, preservar el estado de la estrategia de asignación de memoria y el estado de los bloquesde memoria superior (si están o no conectados con los de la memoria convencional). A continuación, seconectan los bloques de memoria superior con los de la convencional, por si no lo estaban. Seguidamente,se modifica la estrategia de asignación de memoria, estableciendo -por ejemplo- un best fit en memoriasuperior. Finalmente, se asigna memoria utilizando la función convencional de asignación (48h). Tras estasoperaciones, habrá de ser restaurada la estrategia de asignación de memoria y el estado de los bloques dememoria superior.

Es conveniente intentar primero asignar memoria superior XMS: si falla, se puede comprobar si laversión del DOS es 5 (o superior) y aplicar el método propio que requiere este sistema. De esta manera, losTSR podrán asignar memoria superior sea cual sea el sistema operativo, controlador de memoria oconfiguración del sistema activos. Sin embargo, con el método propio del DOS 5.0 hay un inconveniente:al acabar la ejecución del código de instalación del TSR, el DOS ¡libera el bloque de memoria que se asignócon la función 48h!. Para evitar esto, hay dos métodos: uno, consiste en terminar residente (aunque seadejando sólo los primeros 96 bytes del PSP) con objeto de que el sistema respete el bloque de memoriacreado. Si no se desea este ligero derroche de memoria convencional, hay un método más contundente.Consiste en engañar al DOS y, tras asignar el bloque de memoria, modificar en su correspondiente bloquede control la información del propietario (PID), haciéndole apuntar -por ejemplo- a sí mismo. De esta manera,al acabar el programa, el DOS recorrerá la cadena de bloques de memoria y no encontrará ninguno quepertenezca al programa que finaliza... conviene también, en este caso, que los dos primeros bytes del bloquede memoria superior contengan la palabra 20CDh (ubicada al inicio de los PSP), con objeto de que algunosprogramas de diagnóstico lo confundan con un programa (no obstante, el comando MEM del DOS norequiere este detalle y lo tomaría directamente por un programa). También hay que crear el nombre delprograma en los 8 últimos bytes del MCB manipulado. Las siguientes rutinas asignan memoria superior XMS(UMB_alloc) o memoria superior DOS 5 (UPPER_alloc):

Page 174: PCA, PS2 ,IBM y AT

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

; ------------ Reservar bloque de memoria superior del nº párrafos AX,; devolviendo en AX el segmento donde está. CF=1 si no; está instalado el gestor XMS (AX=0) o hay un error (AL; devuelve el código de error del controlador XMS).

UMB_alloc PROCPUSH BXPUSH CXPUSH DXCMP xms_ins,1JNE no_umb_disp ; no hay controlador XMSMOV DX,AX ; número de párrafosMOV AH,10h ; solicitar memoria superiorCALL gestor_XMSCMP AX,1 ; ¿ha ido todo bien?MOV AX,BX ; segmento UMB/código de errorJNE XMS_fallo ; falloPOP DX ; okPOP CXPOP BXCLCRET

no_umb_disp: MOV AX,0XMS_fallo: POP DX

POP CXPOP BXSTCRET

UMB_alloc ENDP

; ------------ Reservar memoria superior, con DOS 5.0, del tamaño; solicitado (AX párrafos). Si no hay bastante CF=1,; en caso contrario devuelve el segmento en AX.

UPPER_alloc PROCPUSH AXMOV AH,30hINT 21hCMP AL,5POP AXJAE UPPER_existeSTCJMP UPPER_fin ; necesario DOS 5.0 mínimo

UPPER_existe: PUSH AX ; preservar párrafos...MOV AX,5800hINT 21hMOV alloc_strat,AX ; preservar estrategia

MOV AX,5802hINT 21hMOV umb_state,AL ; preservar estado UMBMOV AX,5803hMOV BX,1INT 21h ; conectar cadena UMB’sMOV AX,5801hMOV BX,41hINT 21h ; High Memory best fitPOP BX ; ...párrafos requeridosMOV AH,48hINT 21h ; asignar memoriaPUSHFPUSH AX ; guardado el resultadoMOV AX,5801hMOV BX,alloc_stratINT 21h ; restaurar estrategiaMOV AX,5803hMOV BL,umb_stateXOR BH,BHINT 21h ; restaurar estado cadena UMBPOP AXPOPFJC UPPER_fin ; hubo falloPUSH DSDEC AXMOV DS,AXINC AXMOV WORD PTR DS:[1],AX ; manipular PIDMOV WORD PTR DS:[16],20CDh ; simular PSPPUSH ESMOV CX,DSMOV ES,CXMOV CX,CSDEC CXMOV DS,CXMOV CX,8MOV SI,CXMOV DI,CXCLDREP MOVSB ; copiar nombre de programaPOP ESPOP DSCLC

UPPER_fin: RETUPPER_alloc ENDP

La rutina UMB_alloc requiere una variable (xms_ins) que indique si está instalado el controlador dememoria extendida, así como otra (gestor_XMS) con la dirección del mismo. La rutina UPPER_alloc necesitauna variable de palabra (alloc_strat) y otra de tipo byte (umb_state) en que apoyarse. El método expuestoconsiste en modificar el PID para evitar que el DOS desasigne la memoria al acabar la ejecución delprograma; también se coloca oportunamente la palabra 20CDh para simular un PSP y se asigna al nuevobloque de programa el mismo nombre que el del bloque de programa real. Los programas con autoinstalaciónen memoria superior deberían tener un parámetro (al estilo del /ML de los de DR-DOS) para forzar lainstalación en memoria convencional si el usuario así lo requiere.

10.8. - PROGRAMAS RESIDENTES EN MEMORIA EXTENDIDA CON DR-DOS 6.0

El auténtico empleo de memoria extendida para instalar programas residentes, aprovechando el modoprotegido en que está el ordenador con el controlador de memoria expandida instalado, no será tratado en estelibro. En particular, algún emulador de coprocesador para 386 emplea esas técnicas. Aquí nos limitaremosa un objetivo más modesto, en los primeros 64 Kb de memoria extendida accesibles desde DOS.

El DR-DOS 6.0 fue el primer sistema operativo DOS que permitía instalar programas residentes enlos primeros 64 Kb de la memoria extendida, zona comúnmente conocida por HMA. La ventaja de cargaraquí las utilidades residentes es que no ocupan memoria, dicho entre comillas (al menos, no memoriaconvencional ni superior). El inconveniente principal es que este área es bastante limitada (en la práctica, algomenos de 20 Kb libres) y la instalación un tanto compleja. Ciertos programas del sistema (COMMAND,KEYB, NLSFUNC, SHARE, TASKMAX) se pueden cargar en esta zona -algunos incluso lo hacenautomáticamente-. Otro inconveniente es la complejidad de la instalación: normalmente los programas secargarán en el segmento 0FFFEh con un offset variable y dependiente de la zona en que sean instalados. Porello, el primer requisito que han de cumplir es el de ser relocalizables: en la práctica, la rutina de instalaciónhabrá de montar el código en memoria asignando posiciones absolutas a ciertos modos de direccionamiento.

El MS-DOS 5.0 también utiliza el HMA para cargar programas residentes; sin embargo no está tannormalizado como en el caso del DR-DOS y es probable que en futuras versiones cambie el método. De unamanera torpe, Microsoft eligió a DISPLAY.SYS para ocupar parte del área que el propio DOS deja libre enel HMA tras instalarse. Este fichero es utilizado en la conmutación de páginas de códigos (factible en

Page 175: PCA, PS2 ,IBM y AT

175PROGRAMAS RESIDENTES

máquinas con EGA y VGA) para adaptar el juego de caracteres a ciertas lenguas. Hubiera sido mucho másinteligente elegir el KEYB y otros programas similares que casi todo el mundo tiene instalados.

Por consiguiente, limitaremos el estudio al caso del DR-DOS. La información que viene acontinuación fue obtenida por la labor investigadora del autor de este libro, que la envió posteriormente a RalfBrown para incluirla en el Interrupt List. Conviene hacer ahora hincapié en que esta manera de gestionar elHMA, a nivel de bloques de memoria, es propia del DR-DOS 6.0, y no de otras versiones anteriores de estesistema, aunque probablemente sí de las posteriores. Para comprobar que en una máquina está presente el DR-DOS puede verificarse la presencia de una variable de entorno del tipo «OS=DRDOS» y otra «VER=X.XX»con la versión. En todo caso, es mucho más seguro utilizar una función del sistema al efecto:

MOV AX,4452h ; función exclusiva del DR-DOSINT 21hJC no_es_drdos ; probablemente es MS-DOSCMP AX,1063hJE drdos341CMP AX,1065hJE drdos5CMP AX,1067hJE drdos6JA drdos_futuro

El DR-DOS 6.0 implementa un nuevo servicio para gestionar la carga de programas en el HMA. Conlas siguientes líneas:

MOV AX,4458hINT 21hMOV SI,ES:[BX+10h] ; variable exclusiva de DR-DOSMOV DI,ES:[BX+14h] ; otra variable de DR-DOS

se obtiene en SI el offset al primer bloque libre de memoria en el HMA (ubicado en 0FFFFh:SI), y en DIel offset al primer bloque ocupado de memoria en el HMA (en 0FFFFh:DI). Si el offset al primer bloque dememoria libre es 0, significa que el DR-DOS no está instalado en el HMA o que no está instalado elEMM386.SYS, con lo que no es posible instalar programas en el HMA. Sólo si el kernel del DR-DOS resideen el HMA se puede utilizar esta técnica, para compartir la memoria con el sistema operativo.

En el HMA los bloques de memoria forman una cadena pero mucho más simple que en los demástipos de memoria. En concreto, tienen una cabecera de sólo 5 bytes: los dos primeros apuntan al offset delsiguiente bloque de memoria (cero si éste era el último) y los dos siguientes el tamaño de este bloque.Téngase en cuenta que los bloques no han de estar necesariamente seguidos, por lo que la información deltamaño no debe emplearse para direccionar al siguiente bloque: ¡para algo están los primeros dos bytes!. Elquinto byte puede tomar un valor entre 0 y 5 para indicar el tipo de programa, por este orden: System,KEYB, NLSFUNC, SHARE, TaskMAX, COMMAND. Como se ve, no se almacena el nombre en formatoASCII sino con un código. Los programas creados por el usuario pueden utilizar cualquiera de los códigos,aunque quizá el más recomendable sea el 0 (de todas maneras, puede haber varios bloques con el mismocódigo).

Para cargar un programa residente aquí, primero se recorre la cadena de bloques libres hasta encontraruno del tamaño suficiente -si lo hay, claro está-. A continuación, se rebaja el tamaño de este bloquemodificando su cabecera. Después, se crea una cabecera para el nuevo bloque (que se sitúa al final del bloquelibre empleado, siempre tendiendo hacia direcciones altas) y se consulta la variable del DOS que indica elprimer bloque ocupado: el nuevo bloque creado habrá de apuntarle; a su vez, esta variable del DOS ha deser actualizada ya que desde ahora el primer bloque ocupado (bueno, en realidad el último) es el reciéncreado. Ha de tenerse en cuenta que si lo que sobra del bloque libre que va a ser utilizado son menos de 16bytes, se le debe desechar -porque así lo establece el sistema-, eliminándolo de la lista encadenada por elsimple procedimiento de hacer apuntar su predecesor a su sucesor. Lógicamente, si el bloque no teníapredecesor -si era el primer bloque- lo que hay que hacer es modificar la variable del DOS que indica elprimer bloque libre para que apunte a su sucesor. En general, se trata de gestionar una lista encadenada, loque más que un problema de ensamblador lo es de sentido común. No eliminar los posibles bloques libresde menos de 16 bytes es saltarse una norma del sistema operativo y podría tener consecuencias imprevisiblescon futuros programas cargados.

Page 176: PCA, PS2 ,IBM y AT

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

Una vez reservado espacio para el nuevo programa, habrá de copiarse este desde la memoriaconvencional hacia el HMA, con una simple instrucción de transferencia. Allí -o antes de realizar latransferencia- habrá de relocalizarse el código. Lo normal en los programas del sistema -y, por consiguiente,lo más recomendable- es que nuestras aplicaciones corran en la dirección 0FFFEh:XXXX y no la0FFFFh:XXXX como en principio podría suponerse, aunque quizá se trate de un detalle irrelevante. Porúltimo, se han de desviar los correspondientes vectores de interrupción a las nuevas rutinas del programaresidente. Obviamente, el programa principal instalador deberá acabar normalmente -y no residente-.

En general, la gestión del HMA es engorrosa porque el sistema realiza poco trabajo sucio,delegándoselo al programa que quiera emplear este área.

10.9. - EJEMPLO DE PROGRAMA RESIDENTE QUE UTILIZA LA BIOS.

El programa de ejemplo es un completo reloj-alarma residente. No posee intuitivas ventanas deconfiguración ni cientos de opciones, pero es sencillo y muy económico en cuanto a consumo de memoriase refiere. 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 unaalarma (/A=OFF) previamente programada -por defecto, no hay alarma definida-. Los parámetros ON y OFF,por sí solos, se emplean para controlar la aparición en pantalla o no del reloj -por defecto aparece nada másser instalado-. El parámetro /T puede tomar un valor 1 para activar la señal horaria -por defecto-, 2 paraavisar a las medias, 4 para pitar a los cuartos y 5 para avisar cada cinco minutos; si vale 0 no se haránseñales de ninguna clase. Los parámetros opcionales X e Y permiten colocarlo en la posición deseada dentrode la pantalla: si /X=72 (valor por defecto), el reloj no aparecerá realmente en esa coordenada sino lo mása la derecha posible en cada tipo de pantalla activa. Con /C se puede modificar el valor del byte de atributosempleado para colorear el reloj. /ML fuerza la instalación en memoria convencional. Por último, con /U sepuede desinstalar de la memoria, en los casos en que sea posible.

Es posible ejecutarlo cuando ya está instalado con objeto de cambiar sus parámetros o programar laalarma. Si las coordenadas elegidas están fuera de la pantalla -ej., al cambiar a un modo de menos columnaso filas- el resultado puede ser decepcionante (esto no sucede si /X=72). Si se produce un cambio de modode pantalla o una limpieza de la misma, el reloj seguirá apareciendo correctamente casi al instante -se refrescasu impresión 4 veces por segundo-.

Una vez cargado, se puede controlar la presencia o no en pantalla pulsado Ctrl-Alt-R o AltGr-R (sinnecesidad de volver a ejecutar el programa con los parámetros ON u OFF). Cuando se expulsa el reloj dela pantalla, se restaura el contenido anterior a la aparición del reloj. Por ello, si se han producido cambiosen el monitor desde que apareció el reloj, el fragmento de pantalla restaurado puede quedar feo, aunquetambién quedaría feo de todas maneras si se rellenara de espacios en blanco. De hecho, esto último es lo quesucede cuando se trabaja con pantallas gráficas.

Cuando comienza a sonar la alarma, estando o no el reloj en pantalla, se puede pulsar Ctrl-Alt-R oAltGr-R para cancelarla; de lo contrario avisará durante 15 segundos. Este es el único caso en que AltGr-Ro Ctrl-Alt-R no servirá para activar o desactivar el reloj (una posterior pulsación, sí). Después de habersonado, la alarma quedará desactivada y no volverá a actuar, ni siquiera al cabo de 24 horas.

El programa utiliza el convenio CiriSOFT para detectar su presencia en memoria, por lo que esdesinstalable incluso aunque no sea el último programa residente cargado, siempre que tras él se hayaninstalado sólo programas del convenio (o al menos otros que no utilicen las mismas interrupciones). Poseesu propia rutina de desinstalación (opción /U), con lo que no es necesario utilizar la utilidad general dedesinstalación. También está equipado con las rutinas que asignan memoria superior XMS o, en su defecto,

Page 177: PCA, PS2 ,IBM y AT

177PROGRAMAS RESIDENTES

memoria superior solicitada al DOS 5.0: por ello, aunque el fichero ejecutable ocupa casi 6 Kb, sólo hacenfalta 1,5 Kb libres de memoria superior para instalarlo en este área, lo que se realiza automáticamente entodos los entornos operativos que existen en la actualidad. Evidentemente, también se instala en memoriaconvencional y sus requerimientos mínimos son un PC/XT y (recomendable) DOS 3.0 o superior.

Se utiliza la función de impresión en pantalla de la BIOS, con lo cual el reloj se imprime tambiénen las pantallas gráficas (incluida SuperVGA). Por ello, es preciso desviar la INT 10h con objeto de detectarsu invocación y no llamarla cuando ya se está dentro de ella (el reloj funciona ligado a la interrupciónperiódica y es impredecible el estado de la máquina cuando ésta se produce). Si se anula la rutina quecontrola INT 10h, en los modos gráficos SuperVGA de elevada resolución aparecen fuertes anomalías aldeslizarse la pantalla (por ejemplo, cuando se hace DIR) e incluso cuando se imprime; sin embargo, la BIOSes dura como una roca (no se cuelga el ordenador, en cualquier caso). En los modos de pantalla normalesno habría tanta conflictividad, aunque conviene ser precavidos. La impresión del reloj se produce sólo 4 vecespor segundo para no ralentizar el ordenador; aunque se realizara 18,2 veces por segundo tampoco se notaríaun retraso perceptible. La interrupción periódica es empleada no sólo para imprimir el reloj sino también parahacer sonar la música, enviando las notas adecuadamente al temporizador a medida que se van produciendolas interrupciones. No se utiliza INT 1Ch porque la considero menos segura y fiable que INT 8; sin embargose toma la precaución de llamar justo al principio al anterior controlador de la interrupción. De la manera queestá diseñado el programa, es sencillo modificar las melodías que suenan, o crear una utilidad de músicaresidente por interrupciones para amenizar el uso del PC. Los valores para programar el temporizador, segúnla nota que se trate, se obtienen de una tabla donde están ya calculados, ya que sería difícil utilizar la comaflotante al efecto. Al leer el teclado, se tiene la precaución de comprobar si al pulsar Ctrl-Alt-R o AltGr-Rla BIOS o el KEYB han colocado un código Alt-R en el buffer. Esto suele suceder a menos que el KEYBno sea demasiado compatible (Ctrl-Alt equivale, en teoría, a Alt a secas). Si así es, ese carácter se saca delbuffer para que no lo detecte el programa principal (si se sacara sin cerciorarse de que realmente está, en casode no estar el ordenador se quedaría esperando una pulsación de tecla). El método utilizado para detectar lapulsación de AltGr en los teclados expandidos no funciona con el KEYB de DR-DOS 5.0/6.0 (excepto enmodo KEYB US), aunque esto es un fallo exclusivo de dicho controlador.

Sin duda, la parte más engorrosa del programa es la interpretación de los parámetros en la línea decomandos, tarea incómoda en ensamblador. Aún así, el programa es bastante flexible y se puede indicar, porejemplo, un parámetro /A=000020:3:48 para programar la alarma a las 20:03:48. Sin embargo, el uso delensamblador para este tipo de programas es más que recomendable: además de aumentar la fiabilidad delcódigo, el consumo de memoria es más que asequible, incluso en máquinas modestas.

;*********************************************************************;* *;* RCLOCK v2.3 (c) Septiembre 1992 CiriSOFT *;* (c) Grupo Universitario de Informática - Valladolid *;* *;* »»» Utilidad de reloj-alarma residente ««« *;* *;*********************************************************************

; ------------ Macros de propósito general

XPUSH MACRO RMIRP reg, <RM>PUSH reg

ENDMENDM

XPOP MACRO RMIRP reg, <RM>POP reg

ENDMENDM

; ------------ Programa

rclock SEGMENTASSUME CS:rclock, DS:rclock

ORG 100h

ini_residente EQU $

; ****************************************; * *; * D A T O S R E S I D E N T E S *; * *; ****************************************

inicio: JMP main

; ------------ Identificación estandarizada del programa

program_id LABEL BYTE

segmento_real DW 0 ; segmento real donde será cargadooffset_real DW 0 ; offset real " " "longitud_total DW 0 ; zona de memoria ocupada (párrafos)info_extra DB 80h ; bits 0, 1 y 2-> 000: normal, con PSP

; 001: bloque UMB XMS; 010: *.SYS; 011: *.SYS formato EXE; bit 7 a 1: «extension_id» definida

multiplex_id DB 0 ; número Multiplex de este TSRvectores_id DW tabla_vectoresextension_id DW tabla_extra

DB "*##*"autor_nom_ver DB "CiriSOFT:RCLOCK:2.3",0

DB 4 ; número de vectores de interrupción usadostabla_vectores EQU $

DB 8 ; INT 8ant_int08 LABEL DWORD ; dirección originalant_int08_off DW 0ant_int08_seg DW 0

DB 9 ; INT 9ant_int09 LABEL DWORD ; dirección originalant_int09_off DW 0ant_int09_seg DW 0

DB 10h ; INT 10hant_int10 LABEL DWORD ; dirección originalant_int10_off DW 0ant_int10_seg DW 0

DB 2Fh ; INT 2Fhant_int2F LABEL DWORD ; dirección originalant_int2F_off DW 0ant_int2F_seg DW 0

tabla_extra LABEL BYTEDW ctrl_exterior ; permitido control exteriorDW 0 ; campo reservado

ctrl_exterior LABEL BYTEreubicabilidad DB 1 ; programa 100% reubicableactivacion DW visibilidad

; ------------ Tabla de períodos de las notas;

Page 178: PCA, PS2 ,IBM y AT

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

; Datos para el período de las 89 notas, tomando como base un reloj de; 1,19318 MHz (el del 8253). Las notas están ordenadas ascendentemente; como las de un piano, aunque las de código 0 al 6 son «silenciosas».; Los datos (para notas mayores de 6) se han calculado con la fórmula:;; 1193180/(36.8*(2^(1/12))^(nota-6));;; 41 43 46 48 50 53 55 58 60 62;; . . . .;; . . 40 42 44 45 47 49 51 52 54 56 57 59 61 63 . .;;

tabla_periodos LABEL WORDDW 37,37,37,37,37,37,37,30603DW 28885,27264,25734,24290,22926,21640,20425,19279DW 18197,17175,16211,15301,14442,13632,12867,12145DW 11463,10820,10212,9639,9098,8587,8105,7650DW 7221,6816,6433,6072,5731,5410,5106,4819DW 4549,4293,4052,3825,3610,3408,3216,3036DW 2865,2705,2553,2409,2274,2146,2026,1912DW 1805,1704,1608,1518,1432,1352,1276,1204DW 1137,1073,1013,956,902,852,804,759DW 716,676,638,602,568,536,506,478DW 451,426,402,379,358,338,319,301DW 284

; ------------ Sonido

; formato de la música:; número de nota (0-88), duración (en 1/18,2 seg.);; Las primeras 7 notas son inaudibles y sirven para; hacer pausas; si al byte de duración se le suma 128,; se produce una pausa de 1/18,2 segundos antes de que; suene otra nota. El final se indica con un 255.

; fragmento del preludio 924 de Bach:

musica_alarma DB 47,2,52,2,56,3,1,1,47,2,52,2,56,3,1,1DB 47,2,52,2,54,3,1,1,51,2,54,2,59,3,1,1DB 49,2,54,2,59,3,1,1,49,2,54,2,57,3,1,1DB 49,2,52,2,56,3,1,1,52,2,56,2,61,3,1,1DB 51,2,56,2,61,3,1,1,51,2,56,2,59,3,1,1DB 51,2,54,2,57,3,1,1DB 255

; típica música de las iglesias:

musica_horas DB 61,10,57,10,59,10,52,20,1,7,52,10,59,10,61,10,57DB 20,255

; tres pitidos descendentes

musica_medias DB 47,7,54,7,56,7,52,7,255

; tres pitidos ascendentes:

musica_cuartos DB 52,7,56,7,59,10,255

; un par de dobles pitidos:

musica_5min DB 57,3+128,57,3+128,1,8,57,3+128,57,3+128,255

; ------------ Parámetros básicos del reloj

alarm_enable DB 0 ; por defecto, alarma OFFhora_alarma LABEL BYTEalarm_h DW "0 "

DB ":"alarm_m DW "00"

DB ":"alarm_s DW "00"visibilidad DB 1 ; por defecto, reloj aparecetipo_aviso DB 1 ; 1 -> señal horaria; 2 -> a las medias

; 4 -> a los cuartos; 5 -> cada 5 min.,; 0 -> sin señal

c_x DB 72 ; coordenada X para el relojc_y DB 0 ; coordenada Ycolor DB 14+4*16 ; tinta amarilla y fondo rojorefresco DB 4 ; cada 4/18,2 sg. se reimprime el reloj

; ------------ Variables de control general

in10 DW 0 ; flag contador de entradas en INT 10hcont_refresco DB 1 ; contador de INT’s 8 a «saltar»pagina DB 0 ; página de vídeo activamodo_video DB 255 ; modo de vídeo activo (valor imposible

; para provocar inicialización)operacion DB 0 ; 8/9 para preservar/restaurar la zona

; de pantalla ocupada por el relojvisible DB 1 ; 1 si el reloj está en pantallac_xx DB 0 ; coordenada X real del relojmusica_sonando DB 0 ; a 1 si música sonandopuntero_notas DW 0 ; apunta a la siguiente nota musical

; que va a sonarcontador_nota DB 0 ; INT’s 8 que le quedan por sonar a la

; nota que está en cursoturno_blanco DB 0 ; a 1 si se procesa la nota separadora

; de notasparando DB 0 ; contador para detener el sonido

; ------------ Cadenas para imprimir

hora_actual LABEL BYTEhorasH DB 0horasL DB 0

DB ":"minutosH DB 0minutosL DB 0

DB ":"segundosH DB 0segundosL DB 0

DB 0

restaurar DB 8 DUP (’ ’) ; para almacenar el contenido previoDB 8 DUP (7) ; de la pantalla (sólo modo texto)

; ***************************************; * *; * C O D I G O R E S I D E N T E *; * *; ***************************************

; ------------ Rutina de gestión de INT 2Fh

ges_int2F PROC FARSTICMP AH,CS:multiplex_idJE preguntanJMP CS:ant_int2F ; saltar al gestor de INT 2Fh

preguntan: CMP DI,1992hJNE ret_no_info ; no llama alguien del convenioMOV AX,ESCMP AX,1492hJNE ret_no_info ; no llama alguien del convenioPUSH CSPOP ES ; sí llama: darle informaciónLEA DI,autor_nom_ver

ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"IRET

ges_int2F ENDP

; ------------ Rutina de control INT 10h. No se imprimirá en pantalla; cuando se ejecute una INT 10h para no reentrar al BIOS.

ges_int10 PROC FARINC CS:in10 ; indicar entrada en INT 10hPUSHFCALL CS:ant_int10DEC CS:in10 ; fin de la INT 10hIRET

ges_int10 ENDP

; ------------ Rutina de gestión de INT 9

ges_int09 PROC FARPUSH AXIN AL,60h ; espiar código de rastreoPUSHFCALL CS:ant_int09 ; llamar al KEYBCMP AL,13h ; ¿tecla «R»?JNE fin_int09 ; noPUSH DSMOV AX,40hMOV DS,AXMOV AL,DS:[17h]XOR AL,12 ; invertir bits de Ctrl y AltTEST AL,12JZ ctrl_alt ; pulsado Ctrl-AltTEST BYTE PTR DS:[96h],8JZ fin_int09ds ; no pulsado AltGr

ctrl_alt: STIPUSH CSPOP DSMOV AH,1CMP musica_sonando,AHJNE no_sonando ; no hay sonidoDEC AHMOV parando,19 ; en 1 segundo, no más notasMOV musica_sonando,AH ; parar músicaMOV alarm_enable,AH ; desactivar alarmaCALL chiton ; silenciar altavozJMP ret_int09

no_sonando: XOR visibilidad,AH ; invertir visibilidad relojMOV cont_refresco,AH ; acelerar presencia/ausencia

ret_int09: XPUSH <BX, CX, BP>MOV AH,1INT 16h ; consultar estado del bufferJZ no_hay_alt_r ; no se colocó Alt-R en bufferMOV AH,0 ; este KEYB es más compatible:INT 16h ; sacar código Alt-R del buffer

no_hay_alt_r: XPOP <BP, CX, BX>fin_int09ds: POP DSfin_int09: POP AX

IRETges_int09 ENDP

; ------------ Rutina de gestión de INT 8

ges_int08 PROC FARPUSHFCALL CS:ant_int08 ; llamar al controlador previoSTIXPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>MOV AX,CSMOV DS,AXMOV ES,AXCALL avisos_sonoros ; darlos si es necesarioDEC cont_refresco ; contador de INTs 8 a «saltar»JNZ fin_int08 ; no han pasado las suficientesMOV AL,refrescoMOV cont_refresco,AL ; recargar cuentaCMP CS:in10,0JNE fin_int08 ; estamos dentro de INT 10hCALL obtiene_hora ; crear cadena con la horaCMP visibilidad,1 ; ¿reloj visible?JNE restaurar? ; noCMP visible,1 ; sí, ¿acaba de aparecer?JE scr_getted ; noMOV visible,1 ; en efecto: es precisoMOV operacion,8 ; entonces tomar el contenidoCALL bios_scr_proc ; previo de la pantalla

scr_getted: CALL gestiona_fondo ; detectar cambio en pantallaCALL print_reloj ; imprimir relojJMP fin_int08

restaurar?: CMP visible,1 ; reloj oculto ¿recientemente?JNE fin_int08 ; no, ya había desaparecidoMOV visible,0 ; sí:MOV operacion,9CALL bios_scr_proc ; reponer contenido de pantalla

fin_int08: XPOP <ES, DS, BP, DI, SI, DX, CX, BX, AX>IRET

ges_int08 ENDP

; ------------ Controlar la generación de señales sonoras

avisos_sonoros PROCCMP parando,0 ; ¿"callar" durante 1 segundo?JE avisos_on ; noDEC parando ; síJMP fin_avisos

avisos_on: CMP musica_sonando,1JNE no_mas_notas ; no hay sonido en cursoDEC contador_notaJNZ misma_nota ; sigue sonando todavía la notaCMP turno_blanco,0 ; ¿pausa entre notas?JE otra_nota ; noMOV turno_blanco,0 ; sí, sólo una vezMOV contador_nota,1 ; y durante una interrupciónMOV AX,0 ; período inaudibleCALL programar_8253

Page 179: PCA, PS2 ,IBM y AT

179PROGRAMAS RESIDENTES

misma_nota: JMP fin_avisosotra_nota: MOV BX,puntero_notas ; puntero a la siguiente nota

INC BXINC BXMOV puntero_notas,BX ; actualizarloMOV BX,[BX] ; siguiente notaMOV AL,BHAND AL,128 ; aislar bit más significativoROL AL,1 ; ahora el menos significativoMOV turno_blanco,AL ; bit de separación entre notasAND BH,127 ; el resto de BH es la duraciónCMP BL,255 ; ¿se acabaron las notas?JNE sonar ; no, luego tocar esta notaMOV musica_sonando,0 ; síMOV alarm_enable,0 ; desactivar alarmaCALL chiton ; acallar altavozJMP no_mas_notas

sonar: INC BHMOV contador_nota,BH ; INT’s 8 que dura esa notaXOR BH,BH ; BX = posición en la tablaSHL BX,1 ; la tabla es de palabrasMOV AX,[BX+tabla_periodos] ; período del sonidoCALL programar_8253JMP fin_avisos

no_mas_notas: CMP alarm_enable,0JE no_alarma ; alarma desactivadaLEA SI,hora_actualLEA DI,hora_alarmaMOV CX,8CLDREP CMPSB ; ¿hora actual = hora alarma?JNE no_alarma ; no es la hora de la alarmaLEA AX,musica_alarma-2 ; sí lo esJMP fin_avisando

no_alarma: MOV CL,tipo_avisoMOV SI,WORD PTR minutosHMOV DI,WORD PTR segundosHCMP SI,"00" ; ¿hora en punto?JNE media?CMP DI,"00"JNE media?LEA AX,musica_horas-2 ; hora en puntoCMP CL,1 ; ¿avisar a las horas?JAE fin_avisando ; en efecto

media?: CMP SI,"03" ; ¿30 minutos exactos?JNE cuarto?CMP DI,"00"JNE cuarto?LEA AX,musica_medias-2 ; 30 minutos exactosCMP CL,2 ; ¿avisar a las medias?JAE fin_avisando ; en efecto

cuarto?: CMP SI,"51" ; ¿15 ó 45 minutos exactos?JE cuar_quiza?CMP SI,"54"JNE cinco_min?

cuar_quiza?: CMP DI,"00"JNE cinco_min?LEA AX,musica_cuartos-2 ; 15 ó 45 minutos exactosCMP CL,4 ; ¿avisar a los cuartos?JAE fin_avisando ; en efecto

cinco_min?: CMP minutosL,’5’ ; ¿minutos múltiplos de 5?JE cinc_quiza?CMP minutosL,’0’JNE fin_avisos

cinc_quiza?: CMP DI,"00"JNE fin_avisosLEA AX,musica_5min-2 ; minutos múltiplo exacto de 5CMP CL,5 ; ¿avisar cada 5 minutos?JB fin_avisos ; pues no

fin_avisando: MOV puntero_notas,AX ; inicio de la melodíaMOV contador_nota,1 ; compensar futuro decrementoMOV musica_sonando,1 ; activar música

fin_avisos: RETavisos_sonoros ENDP

; ------------ Detener sonido por el altavoz

chiton PROCIN AL,61hAND AL,0FChJMP SHORT $+2JMP SHORT $+2OUT 61h,AL ; altavoz silenciadoRET

chiton ENDP

; ------------ Preparar la producción de sonido

programar_8253 PROCPUSH AXMOV AL,182 ; preparar canal 2OUT 43h,ALPOP AXJMP SHORT $+2JMP SHORT $+2OUT 42h,ALMOV AL,AHJMP SHORT $+2JMP SHORT $+2OUT 42h,AL ; canal #2 del 8253 programadoJMP SHORT $+2JMP SHORT $+2IN AL,61hOR AL,3JMP SHORT $+2JMP SHORT $+2OUT 61h,AL ; activar sonidoRET

programar_8253 ENDP

; ------------ Controlar posible cambio de modo de pantalla o página; de visualización activa, que afectan al fragmento de; pantalla preservado antes de imprimir el reloj.

gestiona_fondo PROCMOV AH,15INT 10h ; modo de vídeo AL y página BHCMP AL,modo_video ; ¿ha cambiado modo de vídeo?JNE clr_fondo? ; en efectoCMP BH,pagina ; ¿ha cambiado la página?JNE clr_fondo? ; así esRET ; no ha cambiado nada

clr_fondo?: MOV modo_video,AL ; actualizar nuevos parámetrosMOV pagina,BHMOV BL,c_x ; coordenada X teóricaCMP BL,72 ; ¿es la 72?JNE dejar_c_x ; no: se deja como talMOV BL,AH ; sí: ajustar posición lo más

SUB BL,8 ; a la derecha posibledejar_c_x: MOV c_xx,BL ; coordenada X real

CMP AL,3 ; ¿modo de texto de color?JBE get_fondo ; sí: preservar área pantallaCMP AL,7 ; ¿modo de texto monocromo?JE get_fondo ; sí: preservar área pantallaMOV CX,8 ; modo gráfico: no preservar,LEA BX,restaurar ; cubrir con espacios en blanco

fondo_clr_ar: MOV BYTE PTR DS:[BX],’ ’MOV BYTE PTR DS:[BX+8],7 ; y atributos blancosINC BXLOOP fondo_clr_ar ; acabar bufferRET

get_fondo: MOV operacion,8 ; preservar zona de la pantallaCALL bios_scr_procRET

gestiona_fondo ENDP

; ------------ Imprimir reloj en pantalla

print_reloj PROCMOV AH,3MOV BH,paginaINT 10h ; coordenadas del cursor en DXPUSH DX ; guardarlas para restaurarlasMOV AH,2MOV DL,c_xxMOV DH,c_y ; coordenadas del relojMOV BH,paginaINT 10h ; ubicar cursorLEA BX,hora_actual ; cadena a imprimirCALL bios_print ; imprimir relojPOP DX ; recuperar posición del cursorMOV BH,pagina ; y página activaMOV AH,2INT 10h ; restaurar posición del cursorRET

print_reloj ENDP

; ------------ Crear cadena de caracteres con la hora actual

obtiene_hora PROCPUSH DSXOR AX,AXMOV DS,AXMOV SI,DS:[46Ch]MOV DI,DS:[46Eh] ; contador de hora del BIOSPOP DSMOV AX,1080CALL mult32x16 ; DXDISI = DISI * 1080MOV AX,19663CALL divi48x15 ; DXDISI = DXDISI / 19663PUSH DIPUSH SI ; DISI = tics/18,2065 = seg.MOV AX,3600CALL divi48x15MOV AX,SI ; AX = SI = horasMOV CL,10DIV CL ; pasar a BCD no empaquetadoOR AX,"00" ; pasar BCD a ASCIICMP AL,’0’JNE no_cero_izdaMOV AL,’ ’ ; evitar cero a la izda en hora

no_cero_izda: MOV horasH,ALMOV horasL,AHMOV AX,3600MUL SI ; DXAX = horas*3600POP SIPOP DISUB SI,AXSBB DI,DX ; DISI = segundos+minutos*60MOV AX,SIMOV CL,60DIV CL ; AL = minutosPUSH AXMOV AH,0MOV CL,10DIV CL ; pasar binario a BCDOR AX,"00" ; pasar BCD a ASCIIMOV minutosH,ALMOV minutosL,AHPOP AXMOV CL,60MUL CLSUB SI,AX ; SI = segundos restantesMOV AX,SIMOV CL,10DIV CL ; pasar binario a BCDOR AX,"00" ; pasar BCD a ASCIIMOV segundosH,ALMOV segundosL,AHRET

obtiene_hora ENDP

; ------------ Imprimir en color usando BIOS; sería más rápido acceder; a la memoria de vídeo, pero así también funciona en los; modos gráficos y en cualquier tarjeta (incluído SVGA).; La cadena ASCIIZ se entrega en DS:BX.

bios_print PROCMOV AL,[BX] ; primer carácter a imprimirINC BXAND AL,ALJZ fin_print ; byte 0 -> fin de cadenaPUSH BXMOV AH,9 ; función de impresiónMOV BH,paginaMOV BL,colorMOV CX,1 ; número de caracteresINT 10hCALL cursor_derecha ; avanzar cursorPOP BXJMP bios_print ; siguiente carácter

fin_print: RETbios_print ENDP

; ------------ Avanzar cursor a la derecha

cursor_derecha PROCMOV BH,paginaMOV AH,3INT 10h ; DX = coordenadas actualesINC DL ; incrementar X (sin controlarMOV AH,2 ; posible desbordamiento)MOV BH,paginaINT 10h ; actualizar posición cursorRET

cursor_derecha ENDP

Page 180: PCA, PS2 ,IBM y AT

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

; ------------ Procesar fragmento de pantalla empleado por el reloj:; si «operacion» es 8 se copiará de la pantalla a un; buffer y si es 9 se hará la operación inversa.

bios_scr_proc PROCMOV AH,3MOV BH,paginaINT 10h ; obtener posición del cursorPUSH DX ; y preservarla para el finalMOV AH,2MOV DL,c_xxMOV DH,c_y ; coordenadas del relojMOV BH,paginaINT 10h ; mover cursorLEA SI,restaurar ; dirección del bufferMOV CX,8 ; 8 caracteres

proximo_car: PUSH CXMOV AH,operacion ; 8 ->preservar, 9 ->restaurarMOV BH,paginaMOV BL,[SI+8] ; preparar BL por si AH=9MOV AL,[SI] ; preparar AL por si AH=9MOV CX,1 ; preparar CX por si AH=9INT 10h ; leer/escribir carácterCMP operacion,8 ; ¿se trataba de leer?JNE opcont ; noMOV [SI],AL ; sí, guardar carácter leídoMOV [SI+8],AH ; y su atributo

opcont: CALL cursor_derecha ; siguiente posiciónINC SI ; próximo carácterPOP CXLOOP proximo_car ; acabar caracteresPOP DX ; recuperar coordenadasMOV BH,paginaMOV AH,2INT 10h ; y reponer posición del cursorRET

bios_scr_proc ENDP

; ------------ Rutina para multiplicar números de 32 por números de 16; bits generando resultado de 48 bits: DISI * AX = DXDISI

mult32x16 PROCPUSH AXXCHG SI,AX ; multiplicador en SIMUL SI ; AX (parte baja) * SI --> DXAXPUSH DX ; preservar resultado parcialPUSH AXMOV AX,DIMUL SI ; AX (parte alta) * SI --> DXAXPOP SI ; parte baja del resultadoPOP DI ; parte media del resultadoADD DI,AX ; acumular resultado intermedioADC DX,0 ; arrastrar posible acarreoPOP AXRET

mult32x16 ENDP

; ------------ Rutina para dividir números de 48 por números de 15; bits sin desbordamientos y con cociente de 48 bits.; DXDISI/AX --> cociente en DXDISI y resto en AX.; No se modifican otros registros. No se comprueba si; el divisor es cero o excede los 15 bits.

divi48x15 PROCPUSH BXPUSH CXXOR BX,BXMOV CX,49 ; rotar 49 veces

divi48_15_cmp: CMP AX,BXJA divi48_nosubSUB BX,AXSTC

divi48_nosub: RCL SI,1RCL DI,1RCL DX,1PUSHFCMP CX,1JE divi48_resto ; ¡no rotar el resto al final!POPFRCL BX,1PUSHF

divi48_resto: POPFLOOP divi48_15_cmpMOV AX,BXPOP CXPOP BXRET

divi48x15 ENDP

fin_residente EQU $ ; fin del área residente

bytes_resid EQU fin_residente-ini_residente

parrafos_resid EQU (bytes_resid+15)/16

; *****************************; * *; * I N S T A L A C I O N *; * *; *****************************

main PROCLEA DX,rclock_txt ; nombre del programaCALL printCALL obtener_param ; analizar posibles parámetrosJNC params_ok ; son correctosCALL print_err ; no: informar del error/ayudaJMP fin_noresid

params_ok: CALL inic_XMS ; considerar presencia de XMSCALL residente? ; ¿programa ya residente?JC no_residente ; todavía noCMP param_u,1 ; sí: ¿solicitan desinstalarlo?JE desinst ; así esCALL adaptar_param ; parámetros en copia residenteJMP fin_noresid

desinst: MOV ES,tsr_segCALL rclock_offMOV AH,ES:multiplex_idCALL mx_unload ; desinstalarlo:LEA DX,des_ok_txtJNC mens_ok ; ha sido posibleLEA DX,des_no_ok_txt ; es imposible

mens_ok: CALL printJMP fin_noresid

no_residente: CMP AX,0 ; ¿reside una versión distinta?JE instalable ; no: se admite instalación

CALL error_version ; error de versión incompatibleJMP fin_noresid

instalable: CMP param_u,1 ; no residente: ¿desinstalar?JNE instalar ; no lo pidenLEA DX,imp_desins_txt ; lo piden, ¡serán despistados!CALL printJMP fin_noresid

instalar: CALL mx_get_handle ; obtener entrada MultiplexJNC handle_okLEA DX,nocabe_txt ; no quedan entradasCALL printJMP fin_noresid

handle_ok: MOV multiplex_id,AH ; entrada multiplex para RCLOCKLEA DX,instalado_txt ; mensaje de instalaciónCALL printCALL preservar_ints ; tomar nota de vectoresCMP param_ml,0 ; ¿se indicó parámetro /ML?JNE instalar_ml ; en efectoMOV AX,parrafos_resid ; párrafos de memoria precisosCALL UMB_alloc ; pedir memoria superior XMSJNC instalar_umb ; hay la suficienteMOV AX,parrafos_residCALL UPPER_alloc ; pedir memoria superior DOS 5JC instalar_ml ; no hay la suficienteSTC ; indicar que usa memoria DOS

instalar_umb: MOV ES,AX ; segmento del bloque UMBMOV DI,0 ; ES:0 zona a donde reubicarCALL inicializa_id ; inicializar identificaciónCALL reubicar_prog ; reubicar el programa a ES:DICALL activar_ints ; interceptar vectoresJMP fin_noresid ; programa instalado «arriba»

instalar_ml: STC ; indicar que usa memoria DOSMOV DI,60h ; instalación mem. convencionalCALL inicializa_id ; inicializar identificaciónCALL reubicar_prog ; reubicar programa a ES:DICALL activar_ints ; interceptar vectoresCALL free_environ ; liberar espacio de entornoMOV DX,parrafos_resid ; tamaño zona residente, desdeADD DX,6 ; PSP:60h bytes (6 párrafos)MOV AX,3100hINT 21h ; terminar residente

fin_noresid: MOV AX,4C00hINT 21h ; terminar no residente

main ENDP

;*********************************************************;* *;* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION *;* *;*********************************************************

; ------------ Admitir posibles parámetros en la línea de comandos

obtener_param PROCMOV BX,81h ; apuntar a zona de parámetros

otro_pmt_mas: CALL saltar_esp ; saltar delimitadoresJNC otro_pmt ; quedan más parámetrosJMP fin_proc_pmt ; no más parámetros

otro_pmt: CMP AL,’/’JE pmt_barrado ; parámetro precedido por ’/’CMP AL,’?’MOV DH,128 ; código de «error» para ayudaJNE pmt_nobarradoJMP mal_proc_pmt ; «error» de solicitud de ayuda

pmt_nobarrado: OR WORD PTR [BX]," " ; pasar a minúsculasCMP WORD PTR [BX],"no" ; ¿parámetro ON?JNE pmt_off?MOV visibilidad,1MOV visible,1MOV param_onoff,1ADD BX,2JMP otro_pmt_mas

pmt_off?: CMP WORD PTR [BX],"fo" ; ¿parámetro OFx?MOV DH,0 ; código de errorJNE mal_proc_pmtOR BYTE PTR [BX+2],’ ’ ; pasar a minúsculasCMP BYTE PTR [BX+2],’f’ ; ¿parámetro OFF?JNE mal_proc_pmtMOV visibilidad,0MOV visible,0MOV param_onoff,1ADD BX,3JMP otro_pmt_mas

pmt_barrado: INC BXMOV AL,[BX] ; letra del parámetroCMP AL,13 ; ¿fin de mandatos?MOV DH,0JE mal_proc_pmt ; falta parámetroCMP AL,’?’MOV DH,128 ; código de «error» para ayudaJE mal_proc_pmtOR AL,’ ’ ; poner en minúsculasCMP AL,’h’JE mal_proc_pmtCMP AL,’a’JNE pmt_no_AJMP pmt_A ; parámetro /A=hh:mm:ss|ON|OFF

pmt_no_A: CMP AL,’u’JE pmt_UMOV SI,[BX] ; ¿parámetro de dos caracteres?OR SI," " ; mayusculizarCMP SI,"lm" ; ¿parámetro /ML?JNE no_mlMOV param_ml,1 ; en efectoADD BX,2JMP otro_pmt_mas

no_ml: PUSH AXCALL get_num ; obtener valor del parámetroPOP CX ; CL tipo de parámetroMOV DH,7 ; código de errorJC mal_proc_pmt ; parámetro incorrectoCMP CL,’t’JE pmt_TCMP CL,’x’JE pmt_XCMP CL,’y’JE pmt_YCMP CL,’c’MOV DH,2 ; código de errorJE pmt_C

mal_proc_pmt: STC ; error en parámetro(s)RET

fin_proc_pmt: CLC ; parámetros procesadosRET

pmt_U: MOV param_u,1INC BXJMP otro_pmt_mas

Page 181: PCA, PS2 ,IBM y AT

181PROGRAMAS RESIDENTES

pmt_T: MOV param_t,1MOV tipo_aviso,ALCMP AX,5MOV DH,3JA mal_proc_pmtCMP AL,3JE mal_proc_pmtJMP otro_pmt_mas

pmt_X: MOV param_x,1MOV c_x,ALCMP AX,124 ; admitir hasta 132 columnasMOV DH,4JA mal_proc_pmtJMP otro_pmt_mas

pmt_Y: MOV param_y,1MOV c_y,AL ; y hasta 60 líneasCMP AX,59MOV DH,5JA mal_proc_pmtJMP otro_pmt_mas

pmt_C: MOV param_c,1MOV color,ALCMP AX,255MOV DH,6JA mal_proc_pmtJMP otro_pmt_mas

pmt_A: PUSH BXCALL get_numJNC bien_pmt_APOP BXADD BX,2OR WORD PTR [BX]," " ; pasar a minúsculasCMP WORD PTR [BX],"no" ; ¿parámetro ON?JNE pmt_A_off?MOV alarm_enable,1MOV param_a_onoff,1ADD BX,2JMP otro_pmt_mas

pmt_A_off?: CMP WORD PTR [BX],"fo" ; ¿parámetro OFx?MOV DH,0 ; código de errorJNE mal_proc_pmOR BYTE PTR [BX+2],’ ’ ; pasar a minúsculasCMP BYTE PTR [BX+2],’f’ ; ¿parámetro OFF?JNE mal_proc_pmMOV alarm_enable,0MOV param_a_onoff,1ADD BX,3JMP otro_pmt_mas

bien_pmt_A: MOV param_a,1ADD SP,2 ; «sacar» BX de la pilaCMP AX,23JA mal_pmtAMOV CL,10DIV CL ; pasar binario a BCDADD AX,"00" ; pasar BCD a ASCIICMP AL,’0’JNE no_cero_izda2MOV AL,’ ’ ; evitar cero a la izda. hora

no_cero_izda2: MOV BYTE PTR alarm_h,ALMOV BYTE PTR alarm_h+1,AHDEC BXCALL get_numJC mal_pmtACMP AX,59JA mal_pmtAMOV CL,10DIV CL ; pasar binario a BCDADD AX,’00’ ; pasar BCD a ASCIIMOV BYTE PTR alarm_m,ALMOV BYTE PTR alarm_m+1,AHDEC BXCALL get_numJC mal_pmtACMP AX,59JA mal_pmtAMOV CL,10DIV CL ; pasar binario a BCDADD AX,’00’ ; pasar BCD a ASCIIMOV BYTE PTR alarm_s,ALMOV BYTE PTR alarm_s+1,AHMOV alarm_enable,1JMP otro_pmt_mas

mal_pmtA: MOV DH,1mal_proc_pm: JMP mal_proc_pmtobtener_param ENDP

; ------------ Saltar espacios, tabuladores, ... buscando un parámetro

saltar_esp: MOV AL,[BX]INC BXCMP AL,9 ; carácter tabuladorJE saltar_espCMP AL,32 ; espacio en blancoJE saltar_espCMP AL,0Dh ; fin de zona de parámetrosJE fin_paramDEC BX ; puntero al primer carácterCLC ; hay parámetroRET

fin_param: STC ; no hay parámetroRET

; ------------ Obtener número chequeando delimitadores /= y /:

get_num: INC BXMOV AL,[BX]INC BXCMP AL,’=’JE delimit_okCMP AL,’:’JE delimit_ok

err_sintax: STC ; sintaxis incorrectaRET

delimit_ok: MOV AL,[BX]CALL obtener_numJC err_sintaxINC BXRET

; ------------ Extraer nº de 16 bits y depositarlo en AX; al final, el; puntero (BX) apuntará al final del número y CF=1 si el; número era incorrecto.

obtener_num PROCCMP AL,0Dh ; fin zona parámetros y númeroJE fin_numCMP AL,32 ; fin númeroJE fin_num

CMP AL,9 ; fin númeroJE fin_numCMP AL,’/’ ; fin número (otro parámetro)JE fin_numCMP AL,’:’ ; fin número (otro dato)JE fin_numINC BXMOV AL,[BX]JMP obtener_num

fin_num: MOV SI,BXDEC SIXOR DX,DXMOV AX,1 ; AX = 10 elevado a la 0 = 1

otro_car: DEC BX ; próximo carácter a procesarMOV CL,[BX]CMP CL,’=’JE ok_num ; delimitador: fin de númeroCMP CL,’:’JE ok_num ; delimitador: fin de númeroCMP CL,’.’JNE no_millar ; saltar los puntos de millarCMP AX,1000JE otro_carJMP mal_num ; separador millar descolocado

no_millar: CMP CL,’0’JB mal_numCMP CL,’9’JA mal_numSUB CL,’0’ ; pasar ASCII a binarioMOV CH,0 ; CX = 0 .. 9PUSH AX ; AX = 10 elevado a la NAND AX,AXJNZ multiplicaAND CL,CLJNZ mal_num_pop ; a la izda sólo permitir ceros

multiplica: PUSH DX ; tras completar 5º dígitoMUL CXPOP DXJC mal_num_popADD DX,AX ; DX = DX + digito (CX) * 10 ^ N (AX)JC mal_num_popPOP AXCMP AX,10000JNE potencia ; AX*10 no se desbordaráMOV AX,0 ; como próximo dígito<>0 aJMP otro_car ; la izda ... pobre usuario

potencia: MOV DI,10PUSH DX ; no manchar DX al multiplicarMUL DI ; AX = AX elevado a la (N+1)POP DXJMP otro_car

mal_num_pop: POP AX ; reequilibrar pilamal_num: MOV BX,SI ; número mayor de 65535

STC ; condición de errorRET

ok_num: MOV BX,SI ; número correctoMOV AX,DX ; resultadoCLC ; condición de Ok.RET

obtener_num ENDP

; ------------ Imprimir errores en los parámetros

print_err PROCCMP DH,128 ; error: DH código de errorJNE no_ayudaLEA DX,ayuda_txtJMP pr_ret

no_ayuda: MOV AH,DHMOV AL,CL ; CL=parámetro en errores 1..6LEA DX,ini_err_txtCALL printLEA BX,tabla_err ; tabla de mensajes de errorPUSH AXMOV AL,AHSHL AL,1 ; AL = AL * 2XOR AH,AH ; AX = ALADD BX,AXMOV DX,[BX] ; dirección del textoCALL printPOP AX ; recuperar código y parámetroCMP AH,1JBE no_pr_pmt ; error 0 ó 1MOV DL,ALMOV AH,2INT 21h ; imprimir letra del parámetro

no_pr_pmt: LEA DX,fin_err_txtpr_ret: CALL print

RETprint_err ENDP

; ------------ Ya está instalada otra versión distinta del programa

error_version PROCPUSH ESLEA DX,mal_ver_txt1CALL printLES DI,tsr_dirMOV AL,’:’MOV CL,255CLDREPNE SCASBREPNE SCASBMOV DL,ES:[DI] ; número de versiónMOV AH,2INT 21hMOV DL,’.’MOV AH,2INT 21hMOV DL,ES:[DI+2] ; revisiónMOV AH,2INT 21hLEA DX,mal_ver_txt2CALL printPOP ESRET

error_version ENDP

; ------------ Considerar presencia de controlador XMS

inic_XMS PROCMOV AX,4300hINT 2Fh ; chequear presencia XMSCMP AL,80hJNE XMS_ausente ; no instaladoPUSH ESMOV AX,4310hINT 2Fh ; sí: obtener su dirección

Page 182: PCA, PS2 ,IBM y AT

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

MOV XMS_off,BX ; y preservarlaMOV XMS_seg,ESMOV xms_ins,1POP ESRET

XMS_ausente: MOV xms_ins,0RET

inic_XMS ENDP

; ------------ Comprobar si el programa ya reside en memoria. A la; salida, CF=0 si programa ya reside, con «tsr_seg» y; «tsr_off» inicializadas apuntando a la cadena de; identificación de la copia residente. Si CF=1, el; programa no reside aún (AX=0) o reside pero en otra; versión distinta (AX=1).

residente? PROCPUSH CXPUSH SIPUSH DIPUSH ESPUSH AXLEA DI,autor_nom_ver ; identificación del programaMOV SI,DIMOV AL,0MOV CL,255CLDREPNE SCASBSUB DI,SIMOV CX,DI ; tamaño autor+programa+versiónMOV AX,1492hMOV ES,AXMOV DI,1992h ; ES:DI protocolo de búsquedaCALL mx_find_tsr ; buscar si está en memoriaMOV tsr_off,DI ; anotar la dirección programaMOV tsr_seg,ES ; por si estaba instaladoPOP AXJNC resid_ok ; CF=0 -> programa ya residentePOP ESPUSH ESLEA DI,autor_nom_verMOV SI,DIMOV AL,’:’MOV CL,255REPNE SCASBREPNE SCASBSUB DI,SIMOV CX,DI ; tamaño autor+programaMOV AX,1492hMOV ES,AXMOV DI,1992h ; ES:DI protocolo de búsquedaCALL mx_find_tsr ; buscar si está en memoriaMOV tsr_off,DI ; anotar dirección del programaMOV tsr_seg,ES ; por si instalada otra versiónMOV AX,0JC resid_ok ; CF=1, AX=0 -> no residenteMOV AX,1STC ; CF=1, AX=1 -> sí: otra vers.

resid_ok: POP ESPOP DIPOP SIPOP CXRET

residente? ENDP

; ------------ Adaptar parámetros de un RCLOCK ya instalado.; Sólo se adaptan los indicados, testeando la variable; que indica si se han especificado.

adaptar_param PROCLEA DX,ya_install_txtCALL printMOV ES,tsr_segCMP param_onoff,1JNE param_a?MOV AL,visibilidad ; parámetros ON u OFF:MOV ES:visibilidad,AL ; adaptar visibilidad del reloj

param_a?: CMP param_a,1JNE param_aonoff?LEA SI,alarm_enable ; parámetro /A=hh:mm:ssMOV DI,SI ; programar nueva alarmaMOV CX,9CLDREP MOVSB

param_aonoff?: CMP param_a_onoff,1JNE param_t?MOV AL,alarm_enable ; parámetro /A=ON o /A=OFF:MOV ES:alarm_enable,AL ; actualizar estado alarma

param_t?: CMP param_t,1JNE param_x?MOV AL,tipo_aviso ; parámetro /T:MOV ES:tipo_aviso,AL ; actualizar byte

param_x?: CMP param_x,1JNE param_y?MOV AL,ES:visibilidad ; parámetro /X:MOV ES:visibilidad,0 ; eliminar reloj de pantallaCALL espera_reloj ; esperar a que se vayaMOV AH,c_xMOV ES:c_x,AH ; actualizar coordenada XMOV ES:c_xx,AHMOV ES:visibilidad,AL ; restaurar visibilidad

param_y?: CMP param_y,1JNE param_c?MOV AL,ES:visibilidad ; parámetro /Y:MOV ES:visibilidad,0 ; eliminar reloj de pantallaCALL espera_reloj ; esperar a que se vayaMOV AH,c_yMOV ES:c_y,AH ; actualizar coordenada YMOV ES:visibilidad,AL ; restaurar visibilidad

param_c?: CMP param_c,1JNE param_adaptedMOV AL,color ; parámetro /C:MOV ES:color,AL ; actualizar byte de atributos

param_adapted: RETadaptar_param ENDP

; ------------ Eliminar el RCLOCK de la pantalla

rclock_off PROCMOV ES:visibilidad,0CALL espera_reloj ; eliminarlo de la pantallaMOV ES:musica_sonando,0IN AL,61h ; parar posible sonidoAND AL,0FChJMP SHORT $+2JMP SHORT $+2OUT 61h,ALRET

rclock_off ENDP

; ------------ Esperar una INT 8 que refresque la impresión del reloj; en pantalla si ésta -la impresión- está habilitada.

espera_reloj PROCPUSH DSPUSH AXPUSH CXMOV CL,refresco ; nº tics suficientes para queMOV CH,0 ; aparezca en pantallaADD CX,2 ; redondear hacia arribaMOV AX,40hMOV DS,AXSTI

espera_tics: MOV AX,DS:[6Ch]espera_tic: CMP AX,DS:[6Ch]

JE espera_ticLOOP espera_ticsPOP CXPOP AXPOP DSRET

espera_reloj ENDP

; ------------ Preservar vectores de interrupción previos

preservar_INTs PROCPUSH ESPUSH DILEA DI,tabla_vectoresMOV CL,[DI-1]MOV CH,0 ; CX vectores interceptados

otro_vector: PUSH CXPUSH DIMOV AH,35hMOV AL,[DI]INT 21h ; obtener vector de INT xxPOP DIPOP CXMOV [DI+1],BX ; anotar donde apuntaMOV [DI+3],ESADD DI,5LOOP otro_vector ; repetir con los restantesPOP DIPOP ESRET

preservar_INTs ENDP

; ------------ Liberar espacio de entorno

free_environ PROCPUSH ESMOV ES,DS:[2Ch] ; dirección del entornoMOV AH,49hINT 21h ; liberar espacio de entornoPOP ESRET

free_environ ENDP

; ------------ Reservar bloque de memoria superior del nº párrafos AX,; devolviendo en AX el segmento donde está. CF=1 si no; está instalado el gestor XMS (AX=0) o hay un error (AL; devuelve el código de error del controlador XMS).

UMB_alloc PROCPUSH BXPUSH CXPUSH DXCMP xms_ins,1JNE no_umb_disp ; no hay controlador XMSMOV DX,AX ; número de párrafosMOV AH,10h ; solicitar memoria superiorCALL gestor_XMSCMP AX,1 ; ¿ha ido todo bien?MOV AX,BX ; segmento UMB/código de errorJNE XMS_fallo ; falloPOP DX ; okPOP CXPOP BXCLCRET

no_umb_disp: MOV AX,0XMS_fallo: POP DX

POP CXPOP BXSTCRET

UMB_alloc ENDP

; ------------ Reservar memoria superior, con DOS 5.0, del tamaño; solicitado (AX párrafos). Si no hay bastante CF=1,; en caso contrario devuelve el segmento en AX.

UPPER_alloc PROCPUSH AXMOV AH,30hINT 21hCMP AL,5POP AXJAE UPPER_existeSTCJMP UPPER_fin ; necesario DOS 5.0 mínimo

UPPER_existe: PUSH AX ; preservar párrafos...MOV AX,5800hINT 21hMOV alloc_strat,AX ; preservar estrategiaMOV AX,5802hINT 21hMOV umb_state,AL ; preservar estado UMBMOV AX,5803hMOV BX,1INT 21h ; conectar cadena UMB’sMOV AX,5801hMOV BX,41hINT 21h ; High Memory best fitPOP BX ; ...párrafos requeridosMOV AH,48hINT 21h ; asignar memoriaPUSHFPUSH AX ; guardado el resultadoMOV AX,5801hMOV BX,alloc_stratINT 21h ; restaurar estrategiaMOV AX,5803hMOV BL,umb_stateXOR BH,BHINT 21h ; restaurar estado cadena UMB

Page 183: PCA, PS2 ,IBM y AT

183PROGRAMAS RESIDENTES

POP AXPOPFJC UPPER_fin ; hubo falloPUSH DSDEC AXMOV DS,AXINC AXMOV WORD PTR DS:[1],AX ; manipular PIDMOV WORD PTR DS:[16],20CDh ; simular PSPPUSH ESMOV CX,DSMOV ES,CXMOV CX,CSDEC CXMOV DS,CXMOV CX,8MOV SI,CXMOV DI,CXCLDREP MOVSB ; copiar nombre de programaPOP ESPOP DSCLC

UPPER_fin: RETUPPER_alloc ENDP

; ------------ 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.

inicializa_id PROCPUSHFMOV segmento_real,ES ; anotar segmento del bloqueMOV offset_real,DI ; ídem con el offsetMOV longitud_total,parrafos_residMOV CL,4MOV AX,DISHR AX,CLADD longitud_total,AX ; consumirá desde offset=0MOV AL,1POPF ; CF=0: usar memoria UMB XMSJNC info_okDEC AL ; usar memoria convencional

info_ok: OR info_extra,ALRET

inicializa_id ENDP

; ------------ Reubicar programa residente a su dirección definitiva.

reubicar_prog PROCPUSH DILEA SI,ini_residenteMOV CX,bytes_residCLDADD SI,2 ; no copiar primera palabraADD DI,2 ; respetar primera palabraSUB CX,2REP MOVSBPOP DIRET

reubicar_prog ENDP

; ------------ 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 PROCPUSH CXPUSH DS ; preservar DS para el retornoMOV AX,100hSUB AX,DI ; AX = 100h-DIMOV CL,4SHR AX,CL ; AX = (100h-DI)/16MOV CX,ESSUB CX,AXMOV DS,CXLEA SI,offsets_intsMOV CX,CS:[SI] ; CX vectores a desviarADD SI,2

desvia_otro: MOV AL,CS:[SI] ; número del vector en cursoMOV DX,CS:[SI+1] ; obtener offsetMOV AH,25hINT 21h ; desviar INT xx a DS:DXADD SI,3LOOP desvia_otroPOP DSPOP CXRET

activar_INTs ENDP

; ------------ 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 PROCMOV AH,0C0h

mx_busca_hndl: PUSH AXMOV AL,0INT 2FhCMP AL,0FFhPOP AXJNE mx_si_huecoINC AHJNZ mx_busca_hndl

mx_no_hueco: STCRET

mx_si_hueco: CLCRET

mx_get_handle ENDP

; ------------ 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 PROCMOV AH,0C0h

mx_rep_find: PUSH AXPUSH CXPUSH SIPUSH DSPUSH ES

PUSH DIMOV AL,0PUSH CXINT 2FhPOP CXCMP AL,0FFhJNE mx_skip_hndl ; no hay TSR ahíCLDPUSH DIREP CMPSB ; comparar identificaciónPOP DIJE mx_tsr_found ; programa buscado hallado

mx_skip_hndl: POP DIPOP ESPOP DSPOP SIPOP CXPOP AXINC AHJNZ mx_rep_findSTCRET

mx_tsr_found: ADD SP,4 ; «sacar» ES y DI de la pilaPOP DSPOP SIPOP CXPOP AXCLCRET

mx_find_tsr ENDP

; ------------ 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 PROCPUSH ESCALL mx_ul_tsrcv?JNC mx_ul_ablePOP ESRET

mx_ul_able: XOR AL,ALXCHG AH,ALMOV BP,AX ; BP=entrada Multiplex del TSRMOV CX,2

mx_ul_pasada: PUSH CX ; siguiente pasadaLEA SI,tabla_vectoresMOV CL,ES:[SI-1]MOV CH,0 ; CX = nº vectores

mx_ul_masvect: POP AXPUSH AX ; pasada en cursoDEC ALPUSH CX

mx_ul_2f: MOV AL,ES:[SI] ; vector en cursoJNZ mx_ul_pasokCMP CX,1 ; ¿último vector?JNE mx_ul_noultMOV AL,2FhLEA SI,tabla_vectores

mx_ul_busca2f: CMP ES:[SI],AL ; ¿INT 2Fh?JE mx_ul_pasokADD SI,5JMP mx_ul_busca2f

mx_ul_noult: CMP AL,2Fh ; ¿restaurar INT 2Fh?JNE mx_ul_pasokADD SI,5JMP mx_ul_2f

mx_ul_pasok: PUSH ESPUSH AXMOV AH,0SHL AX,1SHL AX,1DEC AXMOV CS:mx_ul_tsroff,AXMOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectoresPOP AXPUSH AXMOV AH,35hINT 21h ; vector en ES:BXPOP AXMOV CL,4SHR BX,CLMOV DX,ESADD DX,BX ; INT xx en DX (aprox.)MOV AH,0C0h

mx_ul_masmx: CALL mx_ul_tsrcv?JNC mx_ul_tsrcvJMP mx_ul_otro

mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DIPUSH ES:[DI-12]MOV DI,ES:[DI-8] ; offset a la tabla de vectoresMOV CL,ES:[DI-1]MOV CH,0 ; número de vectores en CX

mx_ul_buscav: CMP AL,ES:[DI]JE mx_ul_usavect ; este TSR usa vector analizadoADD DI,5LOOP mx_ul_buscavADD SP,4 ; no lo usaJMP mx_ul_otro

mx_ul_usavect: POP CX ; tamaño del TSRPOP BX ; segmento del TSRCMP DX,BXJB mx_ul_otro ; la INT xx no le apuntaADD BX,CXCMP DX,BXJA mx_ul_otro ; la INT xx le apuntaPUSH AXXOR AL,ALXCHG AH,ALCMP AX,BP ; ¿es el propio TSR?POP AXJNE mx_ul_chain ; noPOP ES ; sí: ¡posible reponer vector!POP CXPOP BXPUSH BXPUSH CXPUSH ESDEC BXJNZ mx_ul_norest ; no es la segunda pasadaPOP ES ; segunda pasada...PUSH ESPUSH DSMOV BX,CS:mx_ul_tsroff ; restaurar INT’sMOV DS,CS:mx_ul_tsrsegCLI

Page 184: PCA, PS2 ,IBM y AT

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

MOV CX,ES:[SI+1]MOV [BX+1],CXMOV CX,ES:[SI+3]MOV [BX+3],CXSTIPOP DS

mx_ul_norest: POP ESPOP CXADD SI,5 ; siguiente vectorDEC CXJZ mx_unloadable ; no más, ¡desinstal-ar/ado!JMP mx_ul_masvect

mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direcciónMOV CS:mx_ul_tsrseg,ES ; de la variable vectorMOV DX,ES:[DI+1]MOV CL,4SHR DX,CLMOV CX,ES:[DI+3]ADD DX,CX ; INT xx en DX (aprox.)MOV AH,0BFh

mx_ul_otro: INC AH ; a por otro TSRJZ mx_ul_exitnok ; ¡se acabaron!JMP mx_ul_masmx

mx_ul_exitnok: ADD SP,6 ; equilibrar pilaPOP ESSTCRET ; imposible desinstalar

mx_unloadable: POP CXDEC CXJZ mx_ul_exitok ; desinstaladoJMP mx_ul_pasada ; 1ª pasada exitosa: por la 2ª

mx_ul_exitok: TEST ES:info_extra,111b ; ¿tipo de instalación?MOV ES,ES:segmento_real ; segmento real del bloqueJZ mx_ul_freeml ; cargado en RAM convencionalCMP xms_ins,1JNE mx_ul_freeml ; no hay controlador XMS (¿?)MOV DX,ESMOV AH,11hCALL gestor_XMS ; liberar memoria superiorPOP ESCLCRET

mx_ul_freeml: MOV AH,49hINT 21h ; liberar bloque de memoria ES:POP ESCLCRET

mx_ul_tsrcv?: PUSH AX ; ¿es TSR del convenio?...PUSH ESPUSH DIMOV DI,1492hMOV ES,DIMOV DI,1992hINT 2FhCMP AX,0FFFFhJNE mx_ul_ncvexitCMP WORD PTR ES:[DI-4],"#*"JNE mx_ul_ncvexitCMP WORD PTR ES:[DI-2],"*#"JNE mx_ul_ncvexitADD SP,4 ; CF=0POP AXRET

mx_ul_ncvexit: POP DI ; ...no es TSR del convenioPOP ESPOP AXSTC ; CF=1RET

mx_ul_tsroff DW 0mx_ul_tsrseg DW 0mx_unload ENDP

; ------------ imprimir cadena en DS:DX delimitada por un ’$’

print PROCPUSH AXMOV AH,9INT 21hPOP AXRET

print ENDP

; ***********************************************; * *; * D A T O S N O R E S I D E N T E S *; * *; ***********************************************

xms_ins DB 0 ; a 1 si presente controlador XMSgestor_XMS LABEL DWORD ; dirección del controlador XMSXMS_off DW 0XMS_seg DW 0

alloc_strat DW 0 ; estrategia asignación (DOS 5)umb_state DB 0 ; estado de bloques UMB (DOS 5)

tsr_dir LABEL DWORD ; dirección de la copia residentetsr_off DW 0tsr_seg DW 0

offsets_ints DW 4 ; número de vectores interceptadosDB 8 ; tabla de offsets de los vectoresDW ges_int08 ; de interrupción interceptados

DB 9DW ges_int09DB 10hDW ges_int10DB 2FhDW ges_int2F

param_ml DB 0 ; a 1 si se indicó /MLparam_u DB 0 ; a 1 si se indicó /Uparam_onoff DB 0 ; a 1 si se indicó ON u OFFparam_a DB 0 ; a 1 si se indicó /Aparam_a_onoff DB 0 ; a 1 si se indicó /A=ON o /A=OFFparam_t DB 0 ; a 1 si se indicó /Tparam_x DB 0 ; a 1 si se indicó /Xparam_y DB 0 ; a 1 si se indicó /Yparam_c DB 0 ; a 1 si se indicó /C

rclock_txt DB 13,10," RCLOCK v2.3$"

instalado_txt DB " instalado.",13,10,"$"

ya_install_txt DB " ya instalado.",13,10DB " - Parámetros indicados actualizados."DB 13,10,"$"

tabla_err DW err0_txt, err1_txt, err2_txt, err3_txtDW err4_txt,err5_txt, err6_txt, err7_txt

ini_err_txt DB 13,10," - Error: $"err0_txt DB "sintaxis incorrecta$"err1_txt DB "hora de alarma incorrecta$"err2_txt DB "parámetro no admitido: /$"err3_txt DB "parámetro distinto de 0, 1, 2, 4 ó 5: /$"err4_txt DB "parámetro fuera del rango 0..124: /$"err5_txt DB "parámetro fuera del rango 0..59: /$"err6_txt DB "parámetro fuera del rango 0..255: /$"err7_txt DB "necesario numéro en el parámetro /$"fin_err_txt DB 13,10

DB " Ejecute RCLOCK /? para obtener ayuda."DB 13,10,7,"$"

mal_ver_txt1 DB " - Error: ya está instalada la versión $"mal_ver_txt2 DB " de este programa.",13,10,7,"$"

des_ok_txt DB " desinstalado.",13,10,"$"

des_no_ok_txt DB 13,10," - Desinstalación imposible (se ha "DB "instalado después un programa"DB 13,10," que no respeta el convenio y tiene "DB "alguna interrupción común).",13,10,7,"$"

imp_desins_txt DB 13,10," - Programa aún no instalado: "DB "imposible desinstalarlo.",13,10,"$"

nocabe_txt DB ": Instalación imposible.",13,10DB " Ya hay 64 programas residentes con la "DB "misma técnica.",13,10,"$"

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

rclock ENDSEND inicio

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 empleardirectamente los servicios del DOS. Si se salta esta norma se pueden crear programas que funcionen bajodeterminadas circunstancias, pero nada robustos. Por ejemplo, una utilidad para volcar la pantalla a un ficheroen disco al pulsar una cierta combinación de teclas, podría funcionar correctamente si es ejecutada desde lalínea de comandos, o desde dentro de un editor de texto. Sin embargo, si es invocada mientras se ejecuta un

Page 185: PCA, PS2 ,IBM y AT

185PROGRAMAS RESIDENTES

comando DIR o mientras el programa principal está accediendo al disco o, simplemente, ejecutando cualquierfunción del DOS tal como consultar la fecha, nuestra utilidad dejaría de funcionar correctamente. Y el fallono consiste en que la pantalla no se vuelque en disco, o se vuelque mal: el problema es que el ordenador secuelga, siendo preciso reinicializarlo.

Aunque es fácil y, en ocasiones más cómodo y recomendable acceder directamente a la pantalla yal teclado, el DOS es la herramienta más potente para acceder al disco y su utilidad en este campo esprácticamente insustituíble. Para la BIOS o el hardware no existen los discos virtuales ni las unidades dedisco en red; por otra parte, el DOS constituye un soporte básico que permite a los programas ignorar laevolución futura de las unidades de almacenamiento. Por consiguiente, poder utilizar el DOS desde losprogramas residentes es algo más que interesante. Con este objetivo, la propia Microsoft tuvo que enfrentarsea las limitaciones del sistema para desarrollar el comando PRINT desde la versión 2.0; en la actualidad escasi universalmente conocido lo que hay que hacer para emplear el DOS desde un programa residente, aunqueuna gran mayoría de los libros aún no expliquen estas técnicas. Algunos de ellos, incluso muestran programasresidentes que llaman descaradamente al DOS, sin tomar precauciones de ninguna clase ¡por algo no los heincluido en la bibliografía!.

El término no reentrante que se aplica al DOS significa que no puede ser empleado simultáneamentepor dos procesos, sin embargo se trata de un código serialmente reusable como veremos. El DOS posee trespilas 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 DOSdurante 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 unafunción (y ya había conmutado a la pila interna correspondiente) volverá a inicializar el puntero de pila y enla nueva reentrada se cargará el contenido previo de la pila. Si estaba ejecutando una función 0-0Ch y sele 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 conmutaa ninguna pila interna: la 33h, 50h, 51h, 62h y 64h: con ellas sí es reentrante; con las demás (que ademásson 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é ejecutandoalguna función; esto es, cuando no está dentro de una INT 21h. Alternativamente, el programa residentepuede salvar todo el contexto del DOS, incluyendo las tres pilas internas, para restaurarlas después de haberrealizado su tarea. En este libro trataremos especialmente el primer método, tradicionalmente el más empleadoy 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) sepodría desviar esta interrupción y colocar una nueva rutina que incrementara una variable indicativa alprincipio, 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á trabajandoantes de llamarle (variable distinta de cero). Sin embargo, más que una variable habría que tener dos (unapara indicar que la pila E/S está en uso y otra para la pila de disco). Por otro lado, la rutina debería ser algomás sofisticada todavía, ya que hay funciones del DOS que no retornan (las de terminar programa: la 0, 31hy 4Ch) y esto, si no se tiene cuidado, significaría no decrementar como es debido la variable que indica quese 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 porteclado), nuestra variable diría que no es posible usar el DOS en ese momento, ya que está ya en uso, cuandoestá científicamente demostrado que en este caso sí es reentrante si se utiliza una función 0Dh o superior (enla 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 rutinapropuesta, ya que el propio sistema posee una variable interna que indica si en ese momento puede ser

Page 186: PCA, PS2 ,IBM y AT

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 paraobtener la dirección de esta variable, de un byte, que valdrá 0 en el caso de que el DOS esté libre y puedaser llamado desde un programa residente. Esa variable se incrementa automática y adecuadamente con lasllamadas a la INT 21h, y se decrementa al salir.

No hay mejor manera de aprender a construir programas residentes fiables y eficientes que espiarcómo lo hace el fabricante del sistema operativo con los suyos propios. El comando PRINT del DOS, cuandose queda residente, desvía un montón de interrupciones, entre ellas la 1Ch (equivalente a la 8) y la 28h. Lainterrupción 28h (Idle) es invocada por el DOS en las operaciones de entrada por teclado, cuando seencuentra libre de otras tareas, para permitir a los programas residentes aprovechar ese tiempo muerto deCPU. Desde dentro de una INT 28h se puede usar el DOS incluso aunque InDOS sea igual a 1. El comandoPRINT, cuando entra en acción, realiza además una serie de tareas adicionales: preserva el DTA activo (áreade 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; acontinuación establece un DTA y un PSP propios. Tras enviar los caracteres a la impresora, leyéndolos deldisco (con las funciones del DOS, por supuesto) vuelve a restaurar todo lo salvado. Pero vayamos másdespacio.

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 unpuntero en ES:BX a dicha variable. La dirección de InDOS es constante, por lo que se puede inicializar alinstalar el programa residente (no cambiará de lugar en toda la sesión de trabajo). Como luego nos será deutilidad, conviene decir aquí ahora que el Banderín de Errores Críticos del DOS está situado justo despuésde InDOS en las versiones 2.x y justo antes en la 3.0 (en la 3.1 y siguientes, la función 5D06h permiteobtener su dirección en DS:SI). Por tanto, desde los programas residentes bastará, en principio, comprobarque InDOS es igual a cero antes de llamar al DOS (y, de paso, que el Banderín de Errores Críticos estambién cero). En caso contrario, se puede inicializar una variable que indique que el programa residente tieneaún pendiente su ejecución: desde la interrupción periódica se puede comprobar si está pendiente la activacióndel programa residente y se puede verificar el estado del DOS hasta que éste esté listo para ser llamado, loque 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 (perono 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 laborpropia del programa residente.

En el PSP se almacena mucha información vital para la ejecución de los programas. Una de las áreasmás importantes es el JFT (Job File Table) que contiene información referida a los ficheros del programaque 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 deltrabajo se procederá a restaurar el PSP del programa principal. Si no se toma esta precaución, podría sucederde 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 seentere. 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 unnuevo PSP indicando en BX el segmento. Si se desea mantener la compatibilidad con el DOS 2.x, hay quetener en cuenta además un error de este sistema operativo. La errata consiste en que las funciones 50h y 51hno operan bien en el DOS 2.x a menos que el sistema use la pila de errores críticos. Por tanto, con estaversión del sistema se puede forzar el Banderín de Errores Críticos a un valor 0FFh antes de llamar a lasfunciones 50h y 51h, para volverlo a poner a cero después: así, el DOS cree que el sistema está en mediode 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 aldisco: este área está normalmente en el offset 80h del PSP (sobrescribe el campo de parámetros de la línea

Page 187: PCA, PS2 ,IBM y AT

187PROGRAMAS RESIDENTES

de comandos cuando el programa accede a disco) y ocupa 128 bytes. Basta con preservar el DTA delprograma principal, cuya dirección se obtiene en ES:BX con la función Get DTA (2Fh), y activar un nuevoDTA (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 elprograma principal comete un error y el DOS genera la correspondiente información extendida de errores (apartir de la versión 3.0). Si en ese momento se activa el programa residente, puede que realice alguna funcióndel DOS con éxito y el DOS sobrescribirá la condición de error previa. Por tanto, es deber del programaresidente preservar y restaurar la información extendida de errores antes de actuar. La función Get ExtendedError Information (59h) devuelve en AX, BX y CX la información extendida de errores. Con la funciónSet Extended Error Information (5D0Ah), en DS:DX se suministra al DOS la dirección de una tabla quecontiene 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 esconveniente monitorizar la INT 13h para evitar un acceso a disco cuando no ha finalizado el anterior (aunqueel DOS esté en posición correcta). Si se van a emplear las INT 25h/26h, convendría monitorizarlas; así comola INT 10h si se utilizan servicios de vídeo (aunque sean del DOS). Por monitorizar se entiende interceptaresa interrupción e instalar una rutina de control que incremente y decremente una variable cada vez queempieza 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 ejemploa 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 elprograma residente. Será posible activarlo si: no estaba ya activo, no hay una INT 13h en curso, InDOS=0y 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 laactivación del programa residente (por si no se pudo cuando se pulsaron las teclas); en ese caso, volverlo aintentar 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 puedeproceder a activar el programa residente.

- Como mínimo habrán de existir dos variables de control: Una que indica si el programa residenteya 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). Ambasvariables 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 losprogramas residentes que usan el DOS deben definir una pila propia, ya que la del programa interrumpidopuede no ser suficientemente grande. Por el hecho de definir una pila propia, los programas residentes queusan 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á preservarel DTA, el PSP y la información extendida de errores, así como los vectores de INT 1Bh/23h/24h. Despuésdeberá desviar las INT 1Bh e INT 23h hacia un IRET (para evitar un Ctrl-Break ó Ctrl-C) y la INT 24h, paraimplementar una gestión propia de los errores críticos. Al final, deberá restaurar todo de nuevo.

Page 188: PCA, PS2 ,IBM y AT

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 sacadola conclusión de que es conveniente también desviar la INT 21h e intentar desde la misma activar elprograma residente, tal como si se tratara de una interrupción periódica más. El motivo es que desde la INT8 ó la INT 1Ch hay que tener bastante suerte para que el DOS esté desocupado cuando se producen, ya queestas interrupciones sólo suceden 18 veces cada segundo. Esto significa que, por ejemplo, mientras seformatea un disco y se intenta activar el programa residente, puede que éste no responda hasta haberseformateado 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 lasuerte. Desviar la INT 21h e intentar activar el programa residente desde ella permite por ejemplo que ésteactú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 intentaactivar el programa residente, cada cierto número de líneas éste responde; controlando la INT 21h, respondecada dos o tres caracteres impresos. Es evidente que la INT 21h pone a nuestra disposición un método muchomá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 decomprobar 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 programaresidente. Sin embargo, este método depende de la molesta variable InDOS. Esto limita la efectividad de losprogramas residentes, que no pueden ser activados por ejemplo cuando se ejecuta un comando TYPE. Lasolución alternativa que se apuntaba al principio de este apartado consiste en salvar el contexto del DOS yrestaurarlo después, algo factible desde el DOS 3.0. Esto supone bastantes diferencias respecto al métodoestudiado hasta ahora. En lugar de chequear InDOS se debe verificar que el DOS no está en una seccióncrítica (que por fortuna es lo más normal) como luego veremos; y esto tanto desde la interrupción del tecladocomo desde la periódica o desde la INT 28h. Al comienzo del código del programa residente, se debe salvarel estado del DOS: esto significa que hay que pedir memoria al sistema (o tenerla reservada de antemano encantidad suficiente) para contener esa información. También hay que instalar las nuevas rutinas de controlde 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 deerrores: aunque se debe establecer un nuevo DTA, al restaurar el estado del DOS más tarde éste será tambiénautomáticamente restablecido. Y bien, ¿en qué consiste el estado o contexto del DOS?: se basa en un áreade datos, el SDA (Swappable Data Area), cuyo tamaño oscila entre 24 bytes y 2 Kbytes. Este área almacenael 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 SwappableData Area (5D06h), que devuelve en DS:SI un puntero al SDA, en DX el número mínimo de bytes apreservar 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 DOSSwappable Data Areas (5D0Bh), ya que este sistema no posee un único área de datos sino múltiples. Elprocedimiento 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 demomento crítico) o bien AX=8100h o AX=8200h (fin de momento crítico). Se debe interceptar la INT 2Ahe 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, sise quiere asegurar la compatibilidad con futuras versiones del sistema, habrá que reservar mucho más de 2Kb

Page 189: PCA, PS2 ,IBM y AT

189PROGRAMAS RESIDENTES

para almacenar el SDA (intentar utilizar memoria convencional puede fallar, ya que el programa principalpuede tenerla toda asignada) aunque este problema es menor en máquinas con memoria expandida oextendida. No hay que olvidar que el SDA no se puede grabar en disco (para eso hay que usar el DOS, y elDOS no se puede emplear hasta no haber salvado el SDA). También es quizá algo más complejo. Sinembargo, añade algo más de potencia a los programas residentes, ya que pueden ser activados casi encualquier momento y prácticamente en cualquier circunstancia. El autor de este libro nunca ha empleado estemétodo.

10.10.5.- METODOS MENOS ORTODOXOS.

Hay programadores que utilizan métodos muy curiosos para emplear los servicios del DOS desde losprogramas 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 funcionesordinarias del DOS: de esta manera, se utiliza la pila de errores críticos en lugar de la de disco, con lo queno hay conflictos. Esto, por supuesto, sin que el DOS estuviera antes en estado crítico (en caso de estarlo hayque esperar). El inconveniente de este método es que sólo un programa residente de este tipo puede estaractivo en un momento dado en el ordenador. Evidentemente, también hay que desviar la INT 24h paracontrolar 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 casode 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 otrodescendente que la sucede, para confirmar que ha grabado. Los ficheros que genera tienen por nombreSCRxx-nn.SCR, donde xx es la anchura de la pantalla en columnas (en hexadecimal) y nn el número defichero, entre 00 y 99. Los ficheros se crean a partir de 00 cuando se instala el programa, sobrescribiendootros existentes con anterioridad. Al almacenar en el nombre del fichero la anchura del modo de vídeo, esfá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 quede esta manera puede salvar pantallas extrañas no estándar (como 132x60, etc.) que pueden poseer ciertastarjetas. El fichero es creado en el directorio activo por defecto; si se invoca la utilidad mientras se ejecutaun DIR, el fichero podría crearse en el directorio visualizado (algunas versiones del COMMAND cambianel directorio activo momentáneamente). Como cabía esperar, el programa se autoinstala automáticamente enmemoria 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. Enese sentido, SCRCAP puede ser invocado con éxito mientras se formatea un disquete (bueno, pero tampocopara grabar precisamente sobre ese disquete). Se define una pila interna de 0,75 Kbytes, suficiente para elprograma que graba la pantalla y para dar cabida a todas las interrupciones hardware que puedan anidarsedurante el proceso (examinando la memoria con DEBUG se puede observar qué cantidad máxima de pila esconsumida tras un rato de trabajo, ya que los caracteres ’PILA’ permanecen en la zona de la misma aún noempleada). Desde la rutina de control de INT 8 e INT 9 se llama a una subrutina, proceso_tsr, que toma ladecisión de activar el programa residente si el DOS está preparado, o lo pospone en caso contrario. Desdela INT 28h se hace la comprobación más relajada de InDOS (basta con que sea no mayor de 1) y se tomatambién la decisión de activar el programa residente o seguir esperando: en el primer caso se llama aproceso_tsr con una variable (in28) que indica que ya no hay que hacer más comprobaciones. En proceso_tsrse comprueba la variable activo para evitar una reentrada al programa residente: como es un semáforo, espreciso inhibir las interrupciones con objeto de que entre su consulta y ulterior hipotética modificación nopueda ser modificado por nadie (por otro proceso lanzado por interrupciones). Al final, la rutina tarea_TSRes el auténtico programa residente. Simplemente modificando esta rutina se pueden crear programas residentesque realicen cualquier función, pudiendo llamar para ella al DOS.

Page 190: PCA, PS2 ,IBM y AT

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

SCRCAP termina residente dejando en memoria todo el PSP, a diferencia de programas anteriores.Los últimos 128 bytes del PSP se dejan residentes porque serán empleados como área de transferencia a disco(DTA). Conviene ahora hacer un pequeño apunte importante: cuando el programa es relocalizado a lamemoria superior, hay que actualizar un campo en el PSP relocalizado (rutina reubicar_prog): se trata delcampo que apunta a la JFT (offset 36h del PSP), con objeto de que apunte correctamente al nuevo segmentoen que reside el PSP. Si no se tomara esta precaución, no se accedería al disco correctamente.

Si se compara el listado de SCRCAP con el de RCLOCK, el lector comprobará que tienen comúncerca del 50% de las líneas. Sólo cambia la ayuda, algún parámetro, alguna subrutina de la instalación y, porsupuesto, el código residente. En general, las subrutinas que componen ambos programas son losuficientemente generales como para acomodar múltiples soluciones informáticas: se puede considerar queambos programas son una especie de plantillas para crear utilidades residentes. Para hacer nuevos programasresidentes que hagan otras tareas, basta con cambiar sólo la parte residente y poco más. Esto permite trabajarcon comodidad, pese a tratarse del lenguaje ensamblador, y producir múltiples programas en tiempo récord.

; ********************************************************************; * *; * SCRCAP 1.0 *; * *; * Utilidad residente de captura de pantallas de texto. *; * *; ********************************************************************

; ------------ Macros de propósito general

XPUSH MACRO RMIRP reg, <RM>PUSH reg

ENDMENDM

XPOP MACRO RMIRP reg, <RM>POP reg

ENDMENDM

; ------------ Programa

scrcap SEGMENTASSUME CS:scrcap, DS:scrcap

ORG 100h

ini_residente EQU $

inicio: JMP main

; ------------ Identificación estandarizada del programa

program_id LABEL BYTEsegmento_real DW 0 ; segmento real donde será cargadooffset_real DW 0 ; offset real " " "longitud_total DW 0 ; zona de memoria ocupada (párrafos)info_extra DB 80h ; bits 0, 1 y 2-> 000: normal, con PSP

; 001: bloque UMB XMS; 010: *.SYS; 011: *.SYS formato EXE; bit 7 a 1: «extension_id» definida

multiplex_id DB 0 ; número Multiplex de este TSRvectores_id DW tabla_vectoresextension_id DW tabla_extra

DB "*##*"autor_nom_ver DB "CiriSOFT:SCRCAP:1.0",0

DB 6 ; vectores de interrupción interceptadostabla_vectores EQU $

DB 8 ; INT 8ant_int08 LABEL DWORD ; dirección originalant_int08_off DW 0ant_int08_seg DW 0

DB 9 ; INT 9ant_int09 LABEL DWORD ; dirección originalant_int09_off DW 0ant_int09_seg DW 0

DB 13h ; INT 13hant_int13 LABEL DWORD ; dirección originalant_int13_off DW 0ant_int13_seg DW 0

DB 21h ; INT 21hant_int21 LABEL DWORD ; dirección originalant_int21_off DW 0ant_int21_seg DW 0

DB 28h ; INT 28hant_int28 LABEL DWORD ; dirección originalant_int28_off DW 0ant_int28_seg DW 0

DB 2Fh ; INT 2Fhant_int2F LABEL DWORD ; dirección originalant_int2F_off DW 0ant_int2F_seg DW 0

tabla_extra LABEL BYTEDW ctrl_exterior ; permitido control exteriorDW 0 ; campo reservado

ctrl_exterior LABEL BYTEreubicabilidad DB 1 ; programa 100% reubicableactivacion DW actact DW 1

; ------------ Variables internas

dosver DW ? ; versión del DOSega DB ON ; a ON si EGA o superioractivo DB OFFinminente DB OFFmarcas DB 8 ; Por defecto, Alt...

cod_rastreo DB 54h ; ...SysReq (PetSys)in13 DW 0in28 DW 0indos LABEL DWORDindos_off DW ?indos_seg DW ?crit_err LABEL DWORDcrit_err_off DW ?crit_err_seg DW ?ant_pila_off DW ?ant_pila_seg DW ?mainpsp DW ? ; PSP del programa principalmaindta LABEL DWORD ; DTA del programa principalmaindta_off DW ?maindta_seg DW ?errinfo LABEL DWORD ; Extended error informationerrinfo_ax DW ? ; del programa principalerrinfo_bx DW ?errinfo_cx DW ?

DW 8 DUP (0) ; DX, SI, DI, DS, ES, etc.ret_off DW ?ret_seg DW ?ret_flags DW ?

DB 192 DUP ("PILA") ; 0,75 Kb de pilapila_ini EQU $

fich_nom DB "SCRxx-00.SCR",0fich_handle DW ?

local_ints DW 3DB 1Bh ; INT 1BhDW ges_int1B ; nueva dirección

ant_int1B LABEL DWORD ; dirección originalant_int1B_off DW 0ant_int1B_seg DW 0

DB 23h ; INT 23hDW ges_int23 ; nueva dirección

ant_int23 LABEL DWORD ; dirección originalant_int23_off DW 0ant_int23_seg DW 0

DB 24h ; INT 24hDW ges_int24 ; nueva dirección

ant_int24 LABEL DWORD ; dirección originalant_int24_off DW 0ant_int24_seg DW 0

; ------------ Rutina de gestión de INT 2Fh

ges_int2F PROC FARSTICMP AH,CS:multiplex_idJE preguntanJMP CS:ant_int2F ; saltar al gestor de INT 2Fh

preguntan: CMP DI,1992hJNE ret_no_info ; no llama alguien del convenioMOV AX,ESCMP AX,1492hJNE ret_no_info ; no llama alguien del convenioPUSH CSPOP ES ; sí llama: darle informaciónLEA DI,autor_nom_ver

ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"IRET

ges_int2F ENDP

; ------------ Rutina de gestión de INT 8

ges_int08 PROCPUSHFCALL CS:ant_int08STICMP CS:inminente,ONJNE exit_08 ; no hay ejecución pendienteCALL proceso_tsr ; ejecutar TSR si es posible

exit_08: IRETges_int08 ENDP

; ------------ Rutina de gestión de INT 9

ges_int09 PROCSTIPUSH AXIN AL,60hPUSHFCALL CS:ant_int09CMP AL,CS:cod_rastreo ; ¿tecla de activación?JNE fin_09MOV AX,40hPUSH DSMOV DS,AXMOV AL,DS:[17h]POP DS

Page 191: PCA, PS2 ,IBM y AT

191PROGRAMAS RESIDENTES

AND AL,15CMP AL,CS:marcas ; ¿marcas de activación?JNE fin_09CALL proceso_tsr ; ejecutar TSR si es posible

fin_09: POP AXIRET

ges_int09 ENDP

; ------------ Rutina de gestión de INT 13h

ges_int13 PROC FAR ; gestionar INT 13hSTIPUSHFINC CS:in13 ; indicar entrada en INT 13hCALL CS:ant_int13PUSHF ; mucho cuidado con los flagsDEC CS:in13 ; salida de INT 13hPOPFRET 2 ; retornar sin tocar flags

ges_int13 ENDP

; ------------ Rutinas de gestión de INT 1Bh, 23h y 24h.

ges_int1B EQU THIS BYTE ; gestionar INTs 1Bh/23hges_int23 PROC

IRET ; ignorar Ctrl-C y Ctrl-Breakges_int23 ENDP

ges_int24 PROC ; gestionar INT 24hSTIMOV AX,3 ; función de falloCMP CS:dosver,300hJAE ret_int24XOR AX,AX ; 0 en DOS 2.x

ret_int24: IRETges_int24 ENDP

; ------------ Rutina de gestión de INT 21h

ges_int21 PROC FARPOP CS:ret_off ; offset de retornoPOP CS:ret_seg ; segmento de retornoPOP CS:ret_flags ; flags de retornoPUSH CS:ret_segPUSH CS:ret_off ; dejar sólo segmento:offsetPUSH CS:ret_flagsCALL CS:ant_int21PUSHFCMP CS:inminente,ONJNE exit_21 ; no hay ejecución pendienteCALL proceso_tsr ; ejecutar TSR si es posible

exit_21: POPFRET ; retornar sin alterar flags

ges_int21 ENDP

; ------------ Rutina de gestión de INT 28h

ges_int28 PROC ; gestionar INT 28hSTICMP CS:activo,ONJE exit_28 ; TSR ya activoCMP CS:inminente,ONJNE exit_28 ; no hay que activarloCMP CS:in13,0JA exit_28 ; INT 13h en cursoXPUSH <DS, BX>LDS BX,CS:crit_errCMP BYTE PTR [BX],0 ; ¿error crítico?XPOP <BX, DS>JNE exit_28XPUSH <DS, BX>LDS BX,CS:indosCMP BYTE PTR [BX],1 ; ¿Indos>1?XPOP <BX, DS>JA exit_28INC CS:in28 ; dentro de INT 28hCALL proceso_tsr ; ejecutar código del TSRDEC CS:in28 ; fuera de INT 28h

exit_28: JMP CS:ant_int28ges_int28 ENDP

; ------------ Rutina de control de ejecución del TSR

proceso_tsr PROC ; ejecutar TSR si se puedeCMP CS:in28,0JNE proceder ; dentro de INT 28hCMP CS:in13,0JA no_proceder ; INT 13h en cursoXPUSH <DS, BX, AX>LDS BX,CS:crit_errMOV AL,[BX]LDS BX,CS:indosOR AL,[BX] ; crit_err OR indosAND AL,ALXPOP <AX, BX, DS>JZ proceder ; se cumple que ambos a 0

no_proceder: MOV CS:inminente,ON ; esperar próxima INT 8/28hRET

proceder: CLI ; a comprobar semáforo...CMP CS:activo,ON ; ¿ya estaba activo?JE exit_proceso ; evitar reentradaMOV CS:activo,ON ; ahora sí, activoSTI ; ...semáforo comprobadoMOV CS:inminente,OFF ; ya atendida la peticiónMOV CS:ant_pila_off,SPMOV CS:ant_pila_seg,SS ; preservar pilaCLIMOV SP,CSMOV SS,SPLEA SP,pila_ini ; nueva pila habilitadaSTIXPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>XPUSH <CS, CS>XPOP <DS, ES> ; DS y ES apuntan al TSRCALL pushset_intsCALL pushset_pspCALL pushset_dtaCALL push_crit_errCALL kbuff_limpCALL tarea_TSR ; ejecutar proceso residenteCALL pop_crit_errCALL pop_dtaCALL pop_pspCALL pop_intsXPOP <ES, DS, BP, DI, SI, DX, CX, BX, AX>CLIMOV SP,CS:ant_pila_segMOV SS,SP

MOV SP,CS:ant_pila_off ; pila restauradaMOV CS:activo,OFF

exit_proceso: STIRET

proceso_tsr ENDP

; ------------ Subrutinas de apoyo

pushset_ints PROC ; interceptar INT 1Bh/23h/24hPUSH ESLEA SI,local_intsMOV CX,[SI]

phst_otro: PUSH CXMOV AL,[SI+2]MOV AH,35hINT 21hMOV [SI+5],BXMOV [SI+7],ES ; INT xx preservadaMOV DX,[SI+3]MOV AL,[SI+2]MOV AH,25hINT 21h ; INT xx desviadaADD SI,7POP CXLOOP phst_otroPOP ESRET

pushset_ints ENDP

pop_ints PROC ; restaurar vectores INT 1Bh/23h/24hPUSH DSLEA SI,local_intsMOV CX,[SI]

pop_otro: PUSH CXMOV AL,CS:[SI+2]MOV AH,25hMOV DX,CS:[SI+5]MOV DS,CS:[SI+7]INT 21h ; INT xx restauradaADD SI,7POP CXLOOP pop_otroPOP DSRET

pop_ints ENDP

pushset_psp PROC ; preservar PSP y activar el nuevoMOV AX,dosverCMP AH,2JA getpsp3PUSH DS ; en DOS 2.x ...LDS DI,crit_errMOV BYTE PTR [DI],0FFh ; forzar error críticoMOV AH,51hINT 21h ; BX = PSP activo (DOS 2.x)PUSH BXMOV AH,50hMOV BX,CS:segmento_realINT 21h ; activar nuevo PSPMOV BYTE PTR [DI],0 ; anular error críticoPOP BXPOP DSJMP psp_ok

getpsp3: MOV AH,62hINT 21h ; BX = PSP activo (DOS 3+)PUSH BXMOV AH,50hMOV BX,segmento_realINT 21h ; activar nuevo PSPPOP BX

psp_ok: MOV mainpsp,BXRET

pushset_psp ENDP

pop_psp PROC ; restaurar PSP programa principalPUSH DSMOV AX,dosverCMP AH,2JA setpsp3LDS BX,crit_err ; en DOS 2.x ...MOV BYTE PTR [BX],0FFh ; forzar error críticoPUSH BXMOV AH,50hMOV BX,CS:mainpspINT 21h ; restaurar PSPPOP BXMOV BYTE PTR [BX],0 ; anular error críticoJMP psp_poped

setpsp3: MOV AH,50h ; DOS 3+MOV BX,mainpspINT 21h ; restaurar PSP

psp_poped: POP DSRET

pop_psp ENDP

pushset_dta PROCXPUSH <DS, ES>MOV AH,2FhINT 21hMOV maindta_off,BXMOV maindta_seg,ES ; almacenar DTA activoMOV AH,1AhMOV DX,80hMOV DS,segmento_realINT 21h ; establecer nuevo DTAXPOP <ES, DS>RET

pushset_dta ENDP

pop_dta PROCPUSH DSMOV AH,1AhMOV DX,maindta_offMOV DS,maindta_segINT 21h ; restaurar DTAPOP DSRET

pop_dta ENDP

push_crit_err PROCCMP dosver,300hJB push_crit_fin ; necesario DOS 3.0+MOV AH,59hMOV BX,0INT 21hMOV errinfo_ax,AX ; preservar información deMOV errinfo_bx,BX ; errores críticosMOV errinfo_cx,CX

Page 192: PCA, PS2 ,IBM y AT

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

push_crit_fin: RETpush_crit_err ENDP

pop_crit_err PROCCMP dosver,300hJB pop_crit_fin ; necesario DOS 3.0+MOV AX,5D0AhMOV BX,0LEA DX,errinfoINT 21h ; restaurar información de

pop_crit_fin: RET ; errores críticospop_crit_err ENDP

kbuff_limp PROC ; limpiar buffer del tecladoMOV AH,1INT 16hJZ kbuff_limpioMOV AH,0INT 16hJMP kbuff_limp

kbuff_limpio: RETkbuff_limp ENDP

; ------------ Proceso residente que puede emplear el DOS

tarea_TSR PROCCALL sonidoUpCALL init_nomfichLEA DX,fich_nomMOV CX,0MOV AH,3ChINT 21h ; abrir ficheroJC tarea_errMOV fich_handle,AXCALL dscx_eq_videoMOV BX,CS:fich_handleXOR DX,DXMOV AH,40hINT 21h ; grabar pantallaJC tarea_errPUSH CSPOP DSMOV BX,fich_handleMOV AH,3EhINT 21h ; cerrar ficheroJC tarea_errCALL inc_nombre ; preparar futuro nombreCALL sonidoDownRET

tarea_err: PUSH CSPOP DSRET

tarea_TSR ENDP

; ------------ Inicializar nombre de fichero con anchura de pantalla.

init_nomfich PROCPUSH DSMOV AX,40hMOV DS,AXMOV AX,DS:[4Ah] ; anchura de pantallaPOP DSMOV AH,ALSHR AH,1SHR AH,1SHR AH,1SHR AH,1AND AL,15ADD AX,’00’ ; binario -> hexCMP AL,’9’JBE al_es_hexADD AL,’A’-’9’-1

al_es_hex: CMP AH,’9’JBE ah_es_hexADD AH,’A’-’9’-1

ah_es_hex: XCHG AH,ALMOV WORD PTR fich_nom+3,AX ; anchura de pantallaRET

init_nomfich ENDP

; ------------ Obtener segmento de vídeo y tamaño de la pantalla

dscx_eq_video PROC ; devolver CX = tamaño pantallaMOV AX,40h ; y apuntar DS a la mismaMOV DS,AXMOV AL,DS:[49h] ; modo de pantallaMOV BX,0B000h ; supuesto adaptador monocromoMOV CX,4000 ; número de bytesCMP AL,7JE video_okMOV BX,0B800h ; adaptador de colorMOV AX,DS:[4Eh] ; offset de la página activaMOV CL,4SHR AX,CL ; bytes -> párrafosADD BX,AX ; segmento de vídeo efectivoMOV AX,25 ; 25 líneasCMP CS:ega,ONJNE modo_ok ; tarjeta modestaXOR AH,AHMOV AL,DS:[84h]INC AL ; AX = líneas EGA/VGA

modo_ok: MUL WORD PTR DS:[4Ah] ; líneas*columnas = caracteresSHL AX,1 ; AX = tamaño buffer de vídeoMOV CX,AX

video_ok: MOV DS,BXRET

dscx_eq_video ENDP

; ------------ Incrementar número de fichero para siguiente vez

inc_nombre PROCLEA BX,fich_nomMOV AX,[BX+6]INC AHCMP AH,’9’JBE inc_okMOV AH,’0’INC ALCMP AL,’9’JBE inc_okMOV AL,’9’

inc_ok: MOV [BX+6],AXRET

inc_nombre ENDP

; ------------ Sonido ascendente

sonidoUp PROC

CALL espera55msCALL sonidoONMOV AX,2400MOV CX,18

sonar_arriba: CALL sonidoAXCALL espera55msSUB AX,30LOOP sonar_arribaCALL sonidoOFFRET

sonidoUp ENDP

; ------------ Sonido descendente

sonidoDown PROCCALL espera55msCALL sonidoONMOV AX,3000MOV CX,18

sonar_abajo: CALL sonidoAXCALL espera55msADD AX,30LOOP sonar_abajoCALL sonidoOFFRET

sonidoDown ENDP

; ------------ Pausa de 55 milisegundos

espera55ms PROCXPUSH <AX, DS>MOV AX,40hMOV DS,AXSTI ; por si acasoMOV AL,DS:[6Ch]

espera_tic: CMP AL,DS:[6Ch]JE espera_ticXPOP <DS, AX>RET

espera55ms ENDP

; ------------ Activar sonido

sonidoON PROCPUSH AXIN AL,61hOR AL,3JMP SHORT $+2JMP SHORT $+2OUT 61h,AL ; activar sonidoMOV AL,182JMP SHORT $+2JMP SHORT $+2OUT 43h,AL ; preparar canal 2POP AXRET

sonidoON ENDP

; ------------ Inhibir sonido

sonidoOFF PROCPUSH AXIN AL,61hAND AL,255-3JMP SHORT $+2JMP SHORT $+2OUT 61h,AL ; desactivar sonidoPOP AXRET

sonidoOFF ENDP

; ------------ Programar la nota AX en el temporizador

sonidoAX PROCPUSH AXOUT 42h,ALMOV AL,AHJMP SHORT $+2JMP SHORT $+2OUT 42h,AL ; canal 2 del 8253 programadoPOP AXRET

sonidoAX ENDP

; ------------ Fin del área residente

fin_residente EQU $

bytes_resid EQU fin_residente-ini_residente

parrafos_resid EQU (bytes_resid+15)/16

; *****************************; * *; * I N S T A L A C I O N *; * *; *****************************

main PROCLEA DX,scrcap_txt ; mensaje inicialCALL printCALL inic_general ; inicializar ciertas variablesCALL detectarEGACALL obtener_param ; analizar posibles parámetrosJNC params_ok ; son correctosCALL info_err_param ; no: informar del error/ayudaJMP fin_noresid

params_ok: CALL residente? ; ¿programa ya residente?JC no_residente ; aún noCMP param_u,1 ; ¿se solicita desinstalarlo?JE desinst ; así esCALL adaptar_param ; parámetros en copia residenteLEA DX,ya_install_txtCALL printCALL info_ya_ins ; informar de teclas activaciónJMP fin_noresid

desinst: MOV ES,tsr_segMOV AH,ES:multiplex_idCALL mx_unload ; desinstalarlo:LEA DX,des_ok_txtJNC no_pesame ; ha sido posibleLEA DX,des_no_ok_txt ; no es posible

no_pesame: CALL printJMP fin_noresid

no_residente: CMP AX,0 ; ¿reside una versión distinta?JE instalable ; no: se admite instalaciónCALL error_version ; error de versión incompatible

Page 193: PCA, PS2 ,IBM y AT

193PROGRAMAS RESIDENTES

JMP fin_noresidinstalable: CMP param_u,1 ; no residente: ¿desinstalar?

JNE instalar ; no lo pidenLEA DX,imp_desins_txt ; lo piden, ¡serán despistados!CALL printJMP fin_noresid

instalar: MOV AX,parrafos_resid ; área residenteADD AX,16 ; 256 bytes de PSP (completo)MOV memoria,AXCALL mx_get_handle ; obtener entrada MultiplexJNC handle_okLEA DX,nocabe_txt ; no quedan entradasCALL printJMP fin_noresid

handle_ok: MOV multiplex_id,AH ; entrada multiplex para SCRCAPLEA DX,instalado_txt ; mensaje de instalaciónCALL printCALL info_ya_ins ; informar teclas activaciónCALL preservar_ints ; tomar nota de vectoresCMP param_ml,0 ; ¿se indicó parámetro /ML?JNE instalar_ml ; en efectoMOV AX,memoria ; párrafos de memoria precisosCALL UMB_alloc ; pedir memoria superior XMSJNC instalar_umb ; hay la suficienteMOV AX,memoriaCALL UPPER_alloc ; pedir memoria superior DOS 5JC instalar_ml ; no hay la suficienteSTC ; indicar que usa memoria DOS

instalar_umb: MOV ES,AX ; segmento del bloque UMBMOV DI,256 ; ES:256 zona a donde reubicarCALL inicializa_id ; inicializar identificaciónCALL reubicar_prog ; reubicar el programa a ES:DICALL activar_ints ; interceptar vectoresJMP fin_noresid ; programa instalado «arriba»

instalar_ml: STCMOV DI,256 ; instalación mem. convencionalCALL inicializa_id ; inicializar identificaciónCALL reubicar_prog ; reubicar programa a ES:DICALL activar_ints ; interceptar vectoresCALL free_environ ; liberar espacio de entornoMOV DX,memoria ; tamaño zona residenteMOV AX,3100hINT 21h ; terminar residente

fin_noresid: MOV AX,4C00hINT 21h ; terminar no residente

main ENDP

; *************************************; * *; * SUBRUTINAS PARA LA INSTALACION *; * *; *************************************

; ------------ Extraer posibles parámetros de la línea de comandos

obtener_param PROCMOV BX,81h ; apuntar a zona de parámetros

otro_pmt_mas: CALL saltar_esp ; saltar delimitadoresJNC otro_pmt ; quedan más parámetrosJMP fin_proc_pmt ; no más parámetros

otro_pmt: CMP AL,’/’JE pmt_barrado ; parámetro precedido por ’/’CMP AL,’?’JE pmt_hlpJMP mal_proc_pmt

pmt_barrado: INC BXMOV AL,[BX] ; letra del parámetroCMP AL,13 ; ¿fin de mandatos?JE mal_proc_pmt ; falta parámetroCMP AL,’?’JE pmt_hlpOR AL,’ ’ ; poner en minúsculasCMP AL,’h’JE pmt_hlpCMP AL,’s’JE pmt_S ; parámetro /S=CMP AL,’t’JE pmt_T ; parámetro /T=CMP AL,’u’JE pmt_UMOV SI,[BX] ; ¿parámetro de dos caracteres?OR SI," " ; mayusculizarCMP SI,"lm" ; ¿parámetro /ML?JE pmt_ML

mal_proc_pmt: STC ; error en parámetro(s)RET

fin_proc_pmt: CLC ; parámetros procesados ok.RET

pmt_hlp: MOV param_ayuda,1JMP mal_proc_pmt ; «error» de ayuda

pmt_S: MOV param_s,1CALL get_numJC mal_proc_pmtMOV marcas,ALCMP AX,15JA fuera_rangoAND AL,ALJZ fuera_rangoJMP otro_pmt_mas

fuera_rango: MOV marcas,255JMP mal_proc_pmt

pmt_T: MOV param_t,1CALL get_numMOV cod_rastreo,ALJMP otro_pmt_mas

pmt_U: MOV param_u,1INC BXJMP otro_pmt_mas

pmt_ML: MOV param_ml,1 ; en efectoADD BX,2JMP otro_pmt_mas

obtener_param ENDP

; ------------ Saltar espacios, tabuladores, ... buscando un parámetro

saltar_esp: MOV AL,[BX]INC BXCMP AL,9 ; carácter tabuladorJE saltar_espCMP AL,32 ; espacio en blancoJE saltar_espCMP AL,0Dh ; fin de zona de parámetrosJE fin_paramDEC BX ; puntero al primer carácterCLC ; hay parámetroRET

fin_param: STC ; no hay parámetro

RET

; ------------ Obtener número chequeando delimitadores /= y /:

get_num: INC BXMOV AL,[BX]INC BXCMP AL,’=’JE delimit_okCMP AL,’:’JE delimit_ok

err_sintax: STC ; sintaxis incorrectaRET

delimit_ok: MOV AL,[BX]CALL obtener_numJC err_sintaxINC BXRET

; ------------ Extraer nº de 16 bits y depositarlo en AX; al final, el; puntero (BX) apuntará al final del número y CF=1 si el; número era incorrecto.

obtener_num PROCCMP AL,0Dh ; fin zona parámetros y númeroJE fin_numCMP AL,32 ; fin númeroJE fin_numCMP AL,9 ; fin númeroJE fin_numCMP AL,’/’ ; fin número (otro parámetro)JE fin_numCMP AL,’:’ ; fin número (otro dato)JE fin_numINC BXMOV AL,[BX]JMP obtener_num

fin_num: MOV SI,BXDEC SIXOR DX,DXMOV AX,1 ; AX = 10 elevado a la 0 = 1

otro_car: DEC BX ; próximo carácter a procesarMOV CL,[BX]CMP CL,’=’JE ok_num ; delimitador: fin de númeroCMP CL,’:’JE ok_num ; delimitador: fin de númeroCMP CL,’.’JNE no_millar ; saltar los puntos de millarCMP AX,1000JE otro_carJMP mal_num ; separador millar descolocado

no_millar: CMP CL,’0’JB mal_numCMP CL,’9’JA mal_numSUB CL,’0’ ; pasar ASCII a binarioMOV CH,0 ; CX = 0 .. 9PUSH AX ; AX = 10 elevado a la NAND AX,AXJNZ multiplicaAND CL,CLJNZ mal_num_pop ; a la izda sólo permitir ceros

multiplica: PUSH DX ; tras completar 5º dígitoMUL CXPOP DXJC mal_num_popADD DX,AX ; DX = DX + digito (CX) * 10 ^ N (AX)JC mal_num_popPOP AXCMP AX,10000JNE potencia ; AX*10 no se desbordaráMOV AX,0 ; como próximo dígito<>0 aJMP otro_car ; la izda ... pobre usuario

potencia: MOV DI,10PUSH DX ; no manchar DX al multiplicarMUL DI ; AX = AX elevado a la (N+1)POP DXJMP otro_car

mal_num_pop: POP AX ; reequilibrar pilamal_num: MOV BX,SI ; número mayor de 65535

STC ; condición de errorRET

ok_num: MOV BX,SI ; número correctoMOV AX,DX ; resultadoCLC ; condición de Ok.RET

obtener_num ENDP

; ------------ Mensajes de error / ayuda

info_err_param PROCCMP param_ayuda,1JNE otro_errorLEA DX,ayuda_txtCALL printRET

otro_error: LEA DX,err_sintax_txtCMP marcas,255JNE err_okLEA DX,err_tec_txt

err_ok: CALL printLEA DX,err_sintax_finCALL printRET

info_err_param ENDP

; ------------ Ya está instalada otra versión distinta del programa

error_version PROCPUSH ESLEA DX,mal_ver_txt1CALL printLES DI,tsr_dirMOV AL,’:’MOV CL,255CLDREPNE SCASBREPNE SCASBMOV DL,ES:[DI] ; número de versiónMOV AH,2INT 21hMOV DL,’.’MOV AH,2INT 21hMOV DL,ES:[DI+2] ; revisiónMOV AH,2INT 21h

Page 194: PCA, PS2 ,IBM y AT

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

LEA DX,mal_ver_txt2CALL printPOP ESRET

error_version ENDP

; ------------ Considerar presencia de controlador XMS

inic_XMS PROCMOV AX,4300hINT 2Fh ; chequear presencia XMSCMP AL,80hJNE XMS_ausente ; no instaladoPUSH ESMOV AX,4310hINT 2Fh ; sí: obtener su direcciónMOV XMS_off,BX ; y preservarlaMOV XMS_seg,ESMOV xms_ins,1POP ESRET

XMS_ausente: MOV xms_ins,0RET

inic_XMS ENDP

; ------------ Comprobar si el programa ya reside en memoria. A la; salida, CF=0 si programa ya reside, con «tsr_seg» y; «tsr_off» inicializadas apuntando a la cadena de; identificación de la copia residente. Si CF=1, el; programa no reside aún (AX=0) o reside pero en otra; versión distinta (AX=1).

residente? PROCPUSH CXPUSH SIPUSH DIPUSH ESPUSH AXLEA DI,autor_nom_ver ; identificación del programaMOV SI,DIMOV AL,0MOV CL,255CLDREPNE SCASBSUB DI,SIMOV CX,DI ; tamaño autor+programa+versiónMOV AX,1492hMOV ES,AXMOV DI,1992h ; ES:DI protocolo de búsquedaCALL mx_find_tsr ; buscar si está en memoriaMOV tsr_off,DI ; anotar la dirección programaMOV tsr_seg,ES ; por si estaba instaladoPOP AXJNC resid_ok ; CF=0 -> programa ya residentePOP ESPUSH ESLEA DI,autor_nom_verMOV SI,DIMOV AL,’:’MOV CL,255REPNE SCASBREPNE SCASBSUB DI,SIMOV CX,DI ; tamaño autor+programaMOV AX,1492hMOV ES,AXMOV DI,1992h ; ES:DI protocolo de búsquedaCALL mx_find_tsr ; buscar si está en memoriaMOV tsr_off,DI ; anotar dirección del programaMOV tsr_seg,ES ; por si instalada otra versiónMOV AX,0JC resid_ok ; CF=1, AX=0 -> no residenteMOV AX,1STC ; CF=1, AX=1 -> sí: otra vers.

resid_ok: POP ESPOP DIPOP SIPOP CXRET

residente? ENDP

; ------------ Inicializar ciertas variables

inic_general PROCXPUSH <ES, DS> ; **MOV AH,30hINT 21hXCHG AH,ALMOV dosver,AX ; versión del DOSCALL inic_XMS ; detectar controlador XMSMOV AH,34hINT 21hMOV indos_off,BXMOV indos_seg,ES ; dirección de InDOSINC BXCMP dosver,300hJB crit_ok ; Critical Error detrás en 2.xSUB BX,2CMP dosver,300hJE crit_ok ; Critical Error antes en 3.0MOV AX,5D06hINT 21hXPUSH <DS, SI>XPOP <BX, ES>

crit_ok: POP DS ; *MOV crit_err_off,BXMOV crit_err_seg,ES ; dirección de ese flagPOP ES ; *RET

inic_general ENDP

; ------------ Detectar EGA o tarjeta superior

detectarEGA PROCMOV BL,10hMOV AH,12hINT 10h ; pedir información EGA al BIOSCMP BL,10hMOV AL,OFFJE ega_ini ; no es EGAMOV AL,ON

ega_ini: MOV ega,ALRET

detectarEGA ENDP

; ------------ Informar de las teclas que activan SCRCAP

info_ya_ins PROC

PUSH DSCALL residente?JC tec_no_resMOV DS,tsr_seg

tec_no_res: MOV AL,marcasMOV AH,cod_rastreoPOP DSLEA DX,act_teclas_txtCALL printTEST AL,4JZ alt?LEA DX,act_ctrlCALL print_

alt?: TEST AL,8JZ shift_izq?LEA DX,act_altCALL print_

shift_izq?: TEST AL,2JZ shift_der?LEA DX,act_shift_izqCALL print_

shift_der?: TEST AL,1JZ finLEA DX,act_shift_derCALL print_

fin: CMP cod_rastreo,0JE no_mas_teclasLEA DX,act_c_txtCMP AH,54hJE act_okLEA DX,act_otra_txt

act_ok: CALL print_no_mas_teclas: LEA DX,act_fin_txt

CALL printRET

print_: CALL printPUSH AXMOV DL,’-’MOV AH,2INT 21hPOP AXRET

info_ya_ins ENDP

; ------------ Adaptar parámetros de un SCRCAP ya instalado en memoria

adaptar_param PROCPUSH ESMOV ES,tsr_segCMP param_s,1JNE s_okMOV AL,marcasMOV ES:marcas,AL

s_ok: CMP param_t,1JNE c_okMOV AL,cod_rastreoMOV ES:cod_rastreo,AL

c_ok: POP ESRET

adaptar_param ENDP

; ------------ 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.

inicializa_id PROCPUSHFMOV segmento_real,ES ; anotar segmento del bloqueMOV offset_real,DI ; ídem con el offsetMOV AX,memoriaMOV longitud_total,AXMOV AL,1POPF ; CF=0: usar memoria UMB XMSJNC info_okDEC AL ; usar memoria convencional

info_ok: OR info_extra,ALRET

inicializa_id ENDP

; ------------ Preservar vectores de interrupción previos

preservar_INTs PROCPUSH ESPUSH DILEA DI,tabla_vectoresMOV CL,[DI-1]MOV CH,0 ; CX vectores interceptados

otro_vector: PUSH CXPUSH DIMOV AH,35hMOV AL,[DI]INT 21h ; obtener vector de INT xxPOP DIPOP CXMOV [DI+1],BX ; anotar donde apuntaMOV [DI+3],ESADD DI,5LOOP otro_vector ; repetir con los restantesPOP DIPOP ESRET

preservar_INTs ENDP

; ------------ Liberar espacio de entorno

free_environ PROCPUSH ESMOV ES,DS:[2Ch] ; dirección del entornoMOV AH,49hINT 21h ; liberar espacio de entornoPOP ESRET

free_environ ENDP

; ------------ Reservar bloque de memoria superior del nº párrafos AX,; devolviendo en AX el segmento donde está. CF=1 si no; está instalado el gestor XMS (AX=0) o hay un error (AL; devuelve el código de error del controlador XMS).

UMB_alloc PROCPUSH BXPUSH CXPUSH DXCMP xms_ins,1JNE no_umb_disp ; no hay controlador XMSMOV DX,AX ; número de párrafosMOV AH,10h ; solicitar memoria superiorCALL gestor_XMS

Page 195: PCA, PS2 ,IBM y AT

195PROGRAMAS RESIDENTES

CMP AX,1 ; ¿ha ido todo bien?MOV AX,BX ; segmento UMB/código de errorJNE XMS_fallo ; falloPOP DX ; okPOP CXPOP BXCLCRET

no_umb_disp: MOV AX,0XMS_fallo: POP DX

POP CXPOP BXSTCRET

UMB_alloc ENDP

; ------------ Reservar memoria superior, con DOS 5.0, del tamaño; solicitado (AX párrafos). Si no hay bastante CF=1,; en caso contrario devuelve el segmento en AX.

UPPER_alloc PROCPUSH AXMOV AH,30hINT 21hCMP AL,5POP AXJAE UPPER_existeSTCJMP UPPER_fin ; necesario DOS 5.0 mínimo

UPPER_existe: PUSH AX ; preservar párrafos...MOV AX,5800hINT 21hMOV alloc_strat,AX ; preservar estrategiaMOV AX,5802hINT 21hMOV umb_state,AL ; preservar estado UMBMOV AX,5803hMOV BX,1INT 21h ; conectar cadena UMB’sMOV AX,5801hMOV BX,41hINT 21h ; High Memory best fitPOP BX ; ...párrafos requeridosMOV AH,48hINT 21h ; asignar memoriaPUSHFPUSH AX ; guardado el resultadoMOV AX,5801hMOV BX,alloc_stratINT 21h ; restaurar estrategiaMOV AX,5803hMOV BL,umb_stateXOR BH,BHINT 21h ; restaurar estado cadena UMBPOP AXPOPFJC UPPER_fin ; hubo falloPUSH DSDEC AXMOV DS,AXINC AXMOV WORD PTR DS:[1],AX ; manipular PIDMOV WORD PTR DS:[16],20CDh ; simular PSPPUSH ESMOV CX,DSMOV ES,CXMOV CX,CSDEC CXMOV DS,CXMOV CX,8MOV SI,CXMOV DI,CXCLDREP MOVSB ; copiar nombre de programaPOP ESPOP DSCLC

UPPER_fin: RETUPPER_alloc ENDP

; ------------ Reubicar programa residente a su dirección definitiva.; Se copia también el PSP.

reubicar_prog PROCPUSH DILEA SI,ini_residenteMOV CX,bytes_residCLDREP MOVSBXOR SI,SIXOR DI,DIMOV CX,256REP MOVSBPOP DIMOV ES:[36h],ES ; nuevo segmento de la JFTRET

reubicar_prog ENDP

; ------------ 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 PROCPUSH CXPUSH DS ; preservar DS para el retornoMOV AX,100hSUB AX,DI ; AX = 100h-DIMOV CL,4SHR AX,CL ; AX = (100h-DI)/16MOV CX,ESSUB CX,AXMOV DS,CXLEA SI,offsets_intsMOV CX,CS:[SI] ; CX vectores a desviarADD SI,2

desvia_otro: MOV AL,CS:[SI] ; número del vector en cursoMOV DX,CS:[SI+1] ; obtener offsetMOV AH,25hINT 21h ; desviar INT xx a DS:DXADD SI,3LOOP desvia_otroPOP DSPOP CXRET

activar_INTs ENDP

; ------------ 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 PROCMOV AH,0C0h

mx_busca_hndl: PUSH AXMOV AL,0INT 2FhCMP AL,0FFhPOP AXJNE mx_si_huecoINC AHJNZ mx_busca_hndl

mx_no_hueco: STCRET

mx_si_hueco: CLCRET

mx_get_handle ENDP

; ------------ 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 PROCMOV AH,0C0h

mx_rep_find: PUSH AXPUSH CXPUSH SIPUSH DSPUSH ESPUSH DIMOV AL,0PUSH CXINT 2FhPOP CXCMP AL,0FFhJNE mx_skip_hndl ; no hay TSR ahíCLDPUSH DIREP CMPSB ; comparar identificaciónPOP DIJE mx_tsr_found ; programa buscado hallado

mx_skip_hndl: POP DIPOP ESPOP DSPOP SIPOP CXPOP AXINC AHJNZ mx_rep_findSTCRET

mx_tsr_found: ADD SP,4 ; «sacar» ES y DI de la pilaPOP DSPOP SIPOP CXPOP AXCLCRET

mx_find_tsr ENDP

; ------------ 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 PROCPUSH ESCALL mx_ul_tsrcv?JNC mx_ul_ablePOP ESRET

mx_ul_able: XOR AL,ALXCHG AH,ALMOV BP,AX ; BP=entrada Multiplex del TSRMOV CX,2

mx_ul_pasada: PUSH CX ; siguiente pasadaLEA SI,tabla_vectoresMOV CL,ES:[SI-1]MOV CH,0 ; CX = nº vectores

mx_ul_masvect: POP AXPUSH AX ; pasada en cursoDEC ALPUSH CX

mx_ul_2f: MOV AL,ES:[SI] ; vector en cursoJNZ mx_ul_pasokCMP CX,1 ; ¿último vector?JNE mx_ul_noultMOV AL,2FhLEA SI,tabla_vectores

mx_ul_busca2f: CMP ES:[SI],AL ; ¿INT 2Fh?JE mx_ul_pasokADD SI,5JMP mx_ul_busca2f

mx_ul_noult: CMP AL,2Fh ; ¿restaurar INT 2Fh?JNE mx_ul_pasokADD SI,5JMP mx_ul_2f

mx_ul_pasok: PUSH ESPUSH AXMOV AH,0SHL AX,1SHL AX,1DEC AXMOV CS:mx_ul_tsroff,AXMOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectoresPOP AXPUSH AXMOV AH,35hINT 21h ; vector en ES:BXPOP AXMOV CL,4SHR BX,CLMOV DX,ESADD DX,BX ; INT xx en DX (aprox.)MOV AH,0C0h

mx_ul_masmx: CALL mx_ul_tsrcv?JNC mx_ul_tsrcvJMP mx_ul_otro

mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DIPUSH ES:[DI-12]MOV DI,ES:[DI-8] ; offset a la tabla de vectores

Page 196: PCA, PS2 ,IBM y AT

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

MOV CL,ES:[DI-1]MOV CH,0 ; número de vectores en CX

mx_ul_buscav: CMP AL,ES:[DI]JE mx_ul_usavect ; este TSR usa vector analizadoADD DI,5LOOP mx_ul_buscavADD SP,4 ; no lo usaJMP mx_ul_otro

mx_ul_usavect: POP CX ; tamaño del TSRPOP BX ; segmento del TSRCMP DX,BXJB mx_ul_otro ; la INT xx no le apuntaADD BX,CXCMP DX,BXJA mx_ul_otro ; la INT xx le apuntaPUSH AXXOR AL,ALXCHG AH,ALCMP AX,BP ; ¿es el propio TSR?POP AXJNE mx_ul_chain ; noPOP ES ; sí: ¡posible reponer vector!POP CXPOP BXPUSH BXPUSH CXPUSH ESDEC BXJNZ mx_ul_norest ; no es la segunda pasadaPOP ES ; segunda pasada...PUSH ESPUSH DSMOV BX,CS:mx_ul_tsroff ; restaurar INT’sMOV DS,CS:mx_ul_tsrsegCLIMOV CX,ES:[SI+1]MOV [BX+1],CXMOV CX,ES:[SI+3]MOV [BX+3],CXSTIPOP DS

mx_ul_norest: POP ESPOP CXADD SI,5 ; siguiente vectorDEC CXJZ mx_unloadable ; no más, ¡desinstal-ar/ado!JMP mx_ul_masvect

mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direcciónMOV CS:mx_ul_tsrseg,ES ; de la variable vectorMOV DX,ES:[DI+1]MOV CL,4SHR DX,CLMOV CX,ES:[DI+3]ADD DX,CX ; INT xx en DX (aprox.)MOV AH,0BFh

mx_ul_otro: INC AH ; a por otro TSRJZ mx_ul_exitnok ; ¡se acabaron!JMP mx_ul_masmx

mx_ul_exitnok: ADD SP,6 ; equilibrar pilaPOP ESSTCRET ; imposible desinstalar

mx_unloadable: POP CXDEC CXJZ mx_ul_exitok ; desinstaladoJMP mx_ul_pasada ; 1ª pasada exitosa: por la 2ª

mx_ul_exitok: TEST ES:info_extra,111b ; ¿tipo de instalación?MOV ES,ES:segmento_real ; segmento real del bloqueJZ mx_ul_freeml ; cargado en RAM convencionalCMP xms_ins,1JNE mx_ul_freeml ; no hay controlador XMS (¿?)MOV DX,ESMOV AH,11hCALL gestor_XMS ; liberar memoria superiorPOP ESCLCRET

mx_ul_freeml: MOV AH,49hINT 21h ; liberar bloque de memoria ES:POP ESCLCRET

mx_ul_tsrcv?: PUSH AX ; ¿es TSR del convenio?...PUSH ESPUSH DIMOV DI,1492hMOV ES,DIMOV DI,1992hINT 2FhCMP AX,0FFFFhJNE mx_ul_ncvexitCMP WORD PTR ES:[DI-4],"#*"JNE mx_ul_ncvexitCMP WORD PTR ES:[DI-2],"*#"JNE mx_ul_ncvexitADD SP,4 ; CF=0POP AXRET

mx_ul_ncvexit: POP DI ; ...no es TSR del convenioPOP ESPOP AXSTC ; CF=1RET

mx_ul_tsroff DW 0mx_ul_tsrseg DW 0mx_unload ENDP

; ------------ Imprimir cadena en DS:DX delimitada por un 0

print PROCXPUSH <AX, BX, CX, DX>MOV BX,DX

print_mas: MOV AL,[BX]AND AL,ALJZ fin_printMOV DL,ALMOV AH,2PUSH BXINT 21hPOP BXINC BXJMP print_mas

fin_print: XPOP <DX, CX, BX, AX>RET

print ENDP

; **********************************; * *; * DATOS PARA LA INSTALACION *; * *; **********************************

ON EQU 1 ; constantes booleanasOFF EQU 0

xms_ins DB 0 ; a 1 si presente controlador XMSgestor_XMS LABEL DWORD ; dirección del controlador XMSXMS_off DW 0XMS_seg DW 0

alloc_strat DW 0 ; estrategia asignación (DOS 5)umb_state DB 0 ; estado de bloques UMB (DOS 5)

tsr_dir LABEL DWORD ; dirección de la copia residentetsr_off DW 0tsr_seg DW 0

memoria DW 0 ; párrafos que ocupará SCRCAP

offsets_ints DW 6 ; número de vectores interceptadosDB 8 ; tabla de offsets de los vectoresDW ges_int08 ; de interrupción interceptadosDB 9DW ges_int09DB 13hDW ges_int13DB 21hDW ges_int21DB 28hDW ges_int28DB 2FhDW ges_int2F

param_ml DB 0 ; a 1 si se indicó parámetro /MLparam_s DB 0 ; a 1 si se indicó parámetro /Sparam_t DB 0 ; a 1 si se indicó parámetro /Tparam_u DB 0 ; a 1 si se indicó parámetro /Uparam_ayuda DB 0 ; a 1 si se indicaron parámetros /? /H ó ?

; ------------ Texto

scrcap_txt DB 13,10," SCRCAP 1.0",0

instalado_txt DB " instalado.",0

ya_install_txt DB " ya instalado.",0

act_teclas_txt DB 13,10," - Pulse ",0act_ctrl DB "Ctrl",0act_alt DB "Alt",0act_shift_der DB "ShiftDer",0act_shift_izq DB "ShiftIzq",0act_c_txt DB "SysReq",0act_otra_txt DB 8," y la tecla elegida",0act_fin_txt DB 8," para activarlo.",13,10,0

nocabe_txt DB ": Instalación imposible.",13,10DB " Ya hay 64 programas residentes con la "DB "misma técnica.",13,10,0

err_sintax_txt DB 13,10," - Parámetro(s) incorrecto(s).",0err_tec_txt DB 13,10," - Parámetro /S fuera de rango.",0err_sintax_fin DB 13,10," Ejecute SCRCAP /? para obtener "

DB "ayuda.",13,10,7,0

mal_ver_txt1 DB 13,10DB " - Error: ya está instalada la versión ",0

mal_ver_txt2 DB " de este programa.",13,10,7,0

des_ok_txt DB " desinstalado.",13,10,0

des_no_ok_txt DB 13,10," - Desinstalación imposible (se ha "DB "instalado después un programa"DB 13,10," que no respeta el convenio y tiene "DB "alguna interrupción común).",13,10,7,0

imp_desins_txt DB 13,10," - Programa aún no instalado: "DB "imposible desinstalarlo.",13,10,0

ayuda_txt LABEL BYTEDB 13,9," SCRCAP 1.0 - Utilidad de captura de pantallas de texto."DB 13,10DB " (c) 1992 CiriSOFT, (c) Grupo Universitario de Informática - "DB "Valladolid.",13,10,10DB 9," SCRCAP [/ML] [/S=marcas] [/T=codigo de rastreo] [/U] [/?|H]"DB 13,10,10DB " Una vez instalado, al pulsar Alt-SysReq (Alt-PetSis) la "DB "pantalla actual se",13,10DB " salvará en disco con nombre SCRxx-nn.SCR, donde xx es la "DB "anchura hexadecimal",13,10DB " de la misma (en columnas) y nn el número de fichero; ya que, "DB "partiendo de 00",13,10DB " tras instalar el programa, se crean sucesivamente cada vez "DB "que se invoca la",13,10DB " utilidad. Se salvan también pantallas de texto no estándar "DB "(más de 25 líneas",13,10DB " u 80 columnas); las pantallas gráficas generan ficheros "DB "inservibles. Lo que",13,10DB " se almacena en los ficheros es exactamente el contenido del "DB "buffer de vídeo;",13,10DB " la captura va precedida y sucedida de un sonido de aviso "DB "durante 1 segundo.",13,10,10DB " Por defecto se instala residente en memoria superior (si la "DB "hay) de manera",13,10DB " automática, sea cual sea la versión del sistema o el "DB "controlador de memoria",13,10DB " (incluso sin indicar DOS=UMB en el CONFIG del DOS 5.0): con "DB "/ML se fuerza la",13,10DB " instalación en memoria convencional. Consumo: 2208 bytes (2,16 "DB "Kb).",13,10,10DB " El parámetro /S permite elegir la combinación de teclas de "DB "activación (se",13,10DB " obtiene sumando: 1-shift derecho, 2-shift izdo, 4-Ctrl, "DB "8-Alt); con /T puede",13,10DB " cambiarse opcionalmente la tecla de activación. Se puede "DB "desinstalar con /U,",13,10DB " siendo a menudo posible incluso aunque no sea el último TSR "DB "instalado.",13,10,0

fin_prog EQU $

scrcap ENDSEND inicio

Page 197: PCA, PS2 ,IBM y AT

197PROGRAMAS RESIDENTES

Para visualizar las pantallas capturadas puede utilizarse la utilidad SCRVER.C, que admite comodinespara poder ver cualquier conjunto de ficheros. Con SCR2TXT.C se convierten las pantallas capturadas (de40/80/94/100/120/132 ó 160 columnas) a modo texto: se suprimen los colores, se eliminan la mayoría de loscódigos de control, se quitan los espacios en blanco al final de las líneas y se añaden retornos de carro parasepararlas. Esto último provoca, en pantallas que ocupan justo las 80 columnas, que al emplear el TYPE delDOS las líneas queden separadas por una línea extra en blanco (si tuvieran 79 columnas o si se carga desdeun editor de texto, no habrá problemas).

/********************************************************************//* *//* SCRVER 1.0 - Utilidad para visualizar pantallas 80x25 y 40x25 *//* capturadas por SCRCAP. Borland C en modo "Large". *//* *//********************************************************************/

#include <dos.h>#include <dir.h>#include <fcntl.h>#include <conio.h>#include <string.h>

void main(int argc, char **argv)int handle, ultimo;void far *buffer;struct ffblk fichero;char disco[MAXDRIVE], direct[MAXDIR],

fich[MAXFILE], ext[MAXEXT], ruta[MAXPATH];

if (argc<2) printf("\nIndique el(los) fichero(s) a visualizar.\n");

exit (1);

buffer=MK_FP((peekb(0x40,0x49)==7 ? 0xB000: 0xB800), 0);

fnsplit (argv[1], disco, direct, fich, ext);if (!*ext) strcpy (ext, ".*");fnmerge (ruta, disco, direct, fich, ext);ultimo=findfirst (ruta, &fichero, FA_ARCH|FA_HIDDEN|FA_RDONLY);if (ultimo)

printf("\nNombre de fichero incorrecto.\n"); exit(1);

while (!ultimo) fnmerge (ruta, disco, direct, fichero.ff_name, "");if (fichero.ff_name[3]==’2’)

_AX=1; __emit__(0xcd, 0x10); /* modo de 40x25 */else

_AX=3; __emit__(0xcd, 0x10); /* modo 80x25 */if ((handle=open(ruta, O_RDONLY | O_BINARY, 0)) == -1)

printf("Error al abrir fichero de entrada.\n"); exit(1); read(handle, buffer, 30000); close(handle);ultimo=(getch()==27) || findnext (&fichero);

_AX=3; __emit__(0xcd, 0x10); /* modo 80x25 */

/********************************************************************//* *//* SCR2TXT 1.0 - Utilidad para convertir pantallas capturadas por *//* SCRCAP a modo texto. Borland C en modo "Large". *//* *//********************************************************************/

#include <dos.h>#include <dir.h>#include <fcntl.h>#include <conio.h>#include <string.h>

void main(int argc, char **argv)int handler, handlew, ultimo, ancho, ih, il;struct ffblk fichero;char buffer[512], *p,

disco[MAXDRIVE], direct[MAXDIR],fich[MAXFILE], ext[MAXEXT], rutar[MAXPATH], rutaw[MAXPATH];

printf("\n");if (argc<2) printf("Indique el(los) fichero(s) a convertir.\n"); exit (1);

fnsplit (argv[1], disco, direct, fich, ext);if (!*ext) strcpy (ext, ".*");fnmerge (rutar, disco, direct, fich, ext);ultimo=findfirst (rutar, &fichero, FA_ARCH|FA_HIDDEN|FA_RDONLY);if (ultimo) printf("Nombre de fichero incorrecto.\n"); exit(1);

while (!ultimo)

fnmerge (rutar, disco, direct, fichero.ff_name, "");strcpy (rutaw, rutar); p=rutaw; while ((*p) && (*p!=’.’)) p++;*(p-5)=*(p-4)=*(p-3)=’0’; *(p+1)=*(p+3)=’T’; *(p+2)=’X’; *(p+4)=0;

ih=fichero.ff_name[3]-’0’; if (ih>9) ih-=’A’-’9’-1;il=fichero.ff_name[4]-’0’; if (il>9) il-=’A’-’9’-1;ancho=(ih<<4)+il;if ((ancho!=40) && (ancho!=80) && (ancho!=94) && (ancho!=100) &&

(ancho!=114) && (ancho!=120) && (ancho!=132) && (ancho!=160)) printf(" - Error: el fichero %s no es del tipo SCRxx-nn.SCR\n",rutar); exit(1);

if ((handler=open(rutar, O_RDONLY | O_BINARY, 0)) == -1) printf("Error al abrir fichero de entrada.\n"); exit(1);

if ((handlew=_creat(rutaw, 0)) == -1) printf("Error al abrir fichero de salida.\n"); exit(1);

printf("Procesando %s\n", rutar);

while (read(handler, buffer, ancho<<1)==ancho<<1) for (il = (ancho<<1)-2; (il>=0) && buffer[il]==’ ’; il-=2);p=buffer;for (ih=0; ih<=il; ih+=2)

if (((*p>6) && (*p<32)) || !*p) *p=’ ’; /* carácter control */write (handlew, p, 1); p+=2;

p=buffer; *p++=0x0D; *p++=0x0A; *p=0;write (handlew, buffer, 2);

close(handler); close (handlew);ultimo=findnext (&fichero);

10.12. - PROGRAMAS RESIDENTES INVOCABLES EN MODOS GRÁFICOS.

La mayoría de los programas residentes prefieren operar con pantallas de texto: ocupan menosmemoria, son totalmente estándar y más rápidas. En la práctica, la dificultad asociada al proceso de preservarel contenido de una pantalla gráfica y después restaurarla lleva a muchos programas residentes a no dejarseactivar cuando la pantalla está en modo gráfico. Sin embargo, existe una técnica sencilla que permitesimplificar este proceso, siendo operativa en todos los modos de la EGA y VGA estándar, aunque presentaalguna dificultad en ciertos modos de la VGA.

10.12.1 - CASO GENERAL.

En los modos estándar de IBM (y en general también en los no estándar) cuando se solicita a la BIOSque establezca el modo de vídeo (véanse las funciones de la BIOS en los apéndices) si el bit más significativodel modo se pone a 1, al cambiar de modo no se limpia la pantalla. Esta característica está disponible sólo

Page 198: PCA, PS2 ,IBM y AT

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

en máquinas con tarjeta EGA o VGA (tanto XT como AT). Se trata de una posibilidad muy interesante, quepermite a los programas residentes activar momentáneamente una pantalla de texto, preservar el fragmentode la misma que van a emplear y, al final, restaurarlo y volver al modo gráfico como si no hubiera sucedidonada, sin necesidad de preservar ni restaurar zonas gráficas. También habrán de preservar la posición inicialdel cursor y la página de vídeo activa inicialmente (que habrán de restaurar junto con el modo de vídeo), asícomo las paletas de la EGA y VGA, tareas éstas que puede simplificar la BIOS.

Por ejemplo: si la pantalla estaba en modo 12h (VGA 640x480 con 16 colores) se puede activar elmodo 83h (el 3 con el bit 7 activo) de texto de 80x25 y, cuando halla que restaurarla, activar el modo 92h(el 12h con el bit 7 activo). Evidentemente, después habrá que engañar de alguna manera a la BIOS para quecrea que la pantalla está en modo 12h y no 92h (sutil diferencia, ¿no?) y ello se consigue borrando el bit mássignificativo de la posición 40h:87h (la variable de la BIOS 40h:49h indica siempre el número de modo depantalla con el bit más significativo borrado: este bit se almacena separadamente en 40h:87h). Esta operaciónes segura, ya que la diferencia entre el modo 12h y el 92h es sólo a nivel de software y no de hardware. Unprograma residente elegante, además, se tomará la molestia de dejar activo el bit de 40h:87h si así lo estabaal principio, antes de restaurar el modo gráfico (poco probable, pero posible -sobre todo cuando el usuarioactiva más de un programa residente de manera simultánea-).

10.12.2 - CASO DEL MODO 13H DE LA VGA Y MODOS SUPERVGA.

Esta técnica presenta, sin embargo, 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. El problema consiste en que, al pasara modo texto, la BIOS define el juego de caracteres -que en la EGA/VGA es totalmente programable-utilizando una cierta porción de la memoria de vídeo de la tarjeta. Por desgracia, esa porción de la memoriade la tarjeta gráfica es parte de la pantalla en el modo 13h y en los modos SuperVGA. La solución no es muycomplicada, aunque sí un poco engorrosa. Ante todo, recordar que esto sólo es necesario en modos depantalla avanzados o en el 13h. Una posible solución consiste en preservar la zona que va a ser manchada(8 Kb) en un buffer, pasar a modo texto y, antes de volver al modo gráfico, redefinir el juego de caracteresde texto de tal manera que al volver a modo gráfico ya esté restaurada la zona manchada. Este orden deoperaciones no es caprichoso y lo he elegido para reducir los accesos al hardware, como se verá. El problemaprincipal radica en el hecho de que la arquitectura de la pantalla en los modos gráficos y de texto varía demanera espectacular. Por ello, no hay un algoritmo sencillo para acceder a la zona de memoria de gráficosque hay que preservar. Para no desarrollar complicadas rutinas -por si fuera poco, una para cada modográfico- es más cómodo programar el controlador de gráficos para configurar de manera cómoda la memoriade vídeo y preservar sin problemas los 8 Kb deseados. Después, no hace falta restaurar el estado de ningúncontrolador de vídeo, ya que la BIOS lo reprogramará correctamente al pasar a modo texto. Por último, yestando aún en modo texto, se redefinirá el juego de caracteres con los 8 Kb preservados. Comoinmediatamente después se vuelve al modo gráfico, el usuario no notará la basura que aparezca en la pantalladurante breves instantes y, de nuevo, la BIOS reprogramará adecuadamente el controlador de gráficos. Elsiguiente ejemplo práctico parte de la suposición de que nos encontramos en el modo 13h:

CALL def_car_on ; habilitar acceso a tabla de caracteresCALL preservar8k ; guardar 8 Kb de A000:0000 en un bufferMOV AX,83hINT 10h ; pasar a modo texto 80x25

; ... operar en modo texto ...CALL def_car_on ; habilitar acceso a tabla de caracteresCALL restaurar8k ; copiar el buffer de 8 Kb en A000:0000MOV AX,93h ; 13h + 80hINT 10h ; restaurar de nuevo el modo gráfico

Las rutinas preservar8k y restaurar8k son tan obvias que, evidentemente, no las comentaré. Sinembargo, la rutina que prepara el sistema de vídeo de tal manera que se pueda redefinir el juego de caracteresde texto, requiere conocimientos acerca de la arquitectura de las tarjetas gráficas EGA y VGA a bajo nivel.Esta información puede obtenerse en libros especializados sobre gráficos (consúltese la bibliografía) aunquea continuación expongo el listado de def_car_on; eso sí, sin entrar en detalles técnicos acerca de sufuncionamiento:

Page 199: PCA, PS2 ,IBM y AT

199PROGRAMAS RESIDENTES

def_car_on PROCMOV DX,3C4h ; puerto del secuenciadorLEA SI,car_on ; códigos a enviarleMOV CX,4CLDCLI ; precauciones

def_on_1: LODSWOUT DX,AX ; programar registroLOOP def_on_1STI ; no más precaucionesMOV DL,0CEh ; 3CEh = puerto del controlador de gráficosMOV CX,3

def_on_2: LODSWOUT DX,AX ; programarloLOOP def_on_2RET

car_on DW 100h, 402h, 704h, 300h, 204h, 5, 6 ; datosdef_car_on ENDP

10.12.3 - ALGUNOS PROBLEMAS.

En la aplicación práctica de las rutinas expuestas se han detectado algunos problemas decompatibilidad con algunas tarjetas. El más grave se produjo con una OAK SuperVGA: en algunos modosde 800 y 1024 puntos, se colgaba el ordenador al ejecutar def_car_on. La solución adoptada consistió en darun paso intermedio: antes de llamar a def_car_on se puede poner la pantalla en un modo no conflictivo y quesea gráfico para evitar que la BIOS defina el juego de caracteres (como el 13h+80h=93h); en este modo síse puede ejecutar def_car_on, antes de pasar al modo texto.

10.12.4 - CONSIDERACIONES FINALES.

El método propuesto es ciertamente sencillo, aunque se complique un poco más en algunos modosde la VGA. Tiene requerimientos (como el buffer de 8 Kb) que no están quizá al alcance de los programasresidentes menos avanzados. Los más avanzados pueden grabar los 8 Kb en disco duro, si la máquina estádotada del mismo, así como toda la memoria de pantalla CGA (unos modestos 16 Kb) en las máquinas queno están dotadas de EGA o VGA y no pueden conmutar el modo de pantalla sin borrar la misma. Lasmáquinas que no tengan disco duro aumentarán el consumo de memoria del programa residente en 8/16 Kb,aunque ¡peor sería tener que preservar hasta 1 Mb de memoria de vídeo!. El problema está en las tarjetas nocompatibles VGA: mucho cuidado al utilizar la rutina def_car_on (hay que detectar antes la presencia de unaauténtica EGA/VGA, ¡no vale la MCGA!). En MCGA no se puede aplicar def_car_on en el modo 13h,aunque afortunadamente esta tarjeta está poco extendida (sólo acompaña al PS/2-30, en sus primeros modelosun compatible XT); los más perfeccionistas siempre pueden consultar bibliografía especializada en gráficospara tratar de manera especial este adaptador de vídeo, aunque sería incluso más recomendable ocuparse antesde la Hércules. Otro premio reservado para estos perfeccionistas será la posibilidad de conmutar los modosde pantalla accediendo al hardware y sin apoyo de la BIOS, para que no borre la pantalla en las CGA.Téngase en cuenta que esta operación sería mucho más delicada en las EGA y VGA (es más difícil restaurartodos los parámetros hardware del modo gráfico activo inicialmente) en las que además habría que definirun juego de caracteres de texto. Por cierto, el estándar VESA posee también funciones para preservar yrestaurar el estado del adaptador de vídeo; el lector podría encontrar interesante documentarse acerca de ello.

10.13. - PROGRAMAS RESIDENTES EN ENTORNO WINDOWS 3.

El tema de los programas residentes de DOS funcionando bajo Windows no es demasiado importanteya que, en teoría, desde dentro de Windows no es necesario tener instalados programas residentes, al tratarsede un entorno multitarea que permite tener varios programas activos en pantalla a la vez. Sin embargo, puedeser interesante en ocasiones crear programas residentes que también operen bajo Windows, de cara a no tenerque desarrollar una versión específica no residente para este entorno.

Un problema importante de los programas residentes consiste en la dificultad para leer el teclado. Larazón es que Windows reemplaza totalmente al controlador del DOS, anulando los TSR que se activan por

Page 200: PCA, PS2 ,IBM y AT

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

teclado. En los AT se puede leer el puerto del teclado en cualquier momento (fuera de la INT 9) aunque noes recomendable porque la práctica reiterada de este método provoca anomalías en el mismo (tales comoaparición de números en los cursores, estado de Shift que se engancha, etc.) debido a las limitaciones delhardware. Un método más recomendable, aunque menos potente, consiste en comprobar las variables de laBIOS que indican el estado de mayúsculas, bloque numérico, shift, ... ya que estas variables soncorrectamente actualizadas desde dentro de Windows. El único problema es la limitación de combinacionesposibles que se pueden realizar con estas teclas, de cara a permitir la convivencia de varios programasresidentes (problema que se puede solventar permitiendo al usuario elegir las teclas de activación).

El otro problema está relacionado con la multitarea de Windows. Si se abren varios procesos DOSdesde este entorno y se activa el programa residente en más de uno de ellos, pueden aparecer problemas dereentrada (la segunda ejecución estropeará los datos de la primera). La solución más sencilla consiste en nopermitir la invocación del programa residente desde más de una tarea; sin embargo, en algunos TSR (talescomo utilidades de macros de teclado, etc.) esto supone una grave e intolerable restricción. Otra soluciónsencilla consiste en obligar al usuario a instalar el TSR en cada sesión de DOS abierta, con lo que todo elentorno de operación será local a dicha sesión. Para los casos en que no sea recomendable esto último, sepuede quemar el último y más efectivo cartucho: comunicar el TSR con el conmutador de tareas de Windowspara emplear memoria instantánea. El único inconveniente es que Windows sólo facilita memoria instantáneaen el modo extendido 386, no en el modo estándar ni -en el caso de la versión 3.0- en el real. Sin embargo,con la versión 3.1 de Windows, en el modo estándar se puede emplear el conmutador de tareas del DOS 5.0,que es el que utiliza dicho modo. No deja de ser una pena tener que utilizar un método diferente para elmodo estándar que para el extendido, aunque la recompensa para quien implemente soporte en sus TSR paralos dos métodos es que les hará compatibles también con el conmutador de tareas del MS-DOS 5.0. Se puedeinterceptar el arranque de Windows y comprobar si lo hace en modo real, en cuyo caso se puede abortar suejecución y emitir un mensaje de error para solicitar al usuario que no desinstale el TSR antes de entrar enese modo de Windows.

Cuando Windows arranca, llama a la INT 2Fh con AX=1605h: un TSR puede interceptar esta llamada(como en cualquier otra interrupción, llamando primero al controlador previo) y comprobar si el bit 0 de DXestá a cero (en ese caso se estará ejecutando en modo extendido): si se desea abortar la ejecución de Windowsbastará cargar un valor distinto de 0 en CX antes de retornar.

Si el TSR necesita áreas de datos locales a cada sesión en el modo extendido, puede indicárselo aWindows con un puntero a un área de datos denominado SWSTARTUPINFO en ES:BX. Para ello, yteniendo en cuenta que puede haber varios TSR que intercepten las llamadas a la INT 2Fh con AX=1605h,este área ha sido diseñada para almacenar una cadena de referencias entre todos ellos; por ello es precisoalmacenar primero el ES:BX inicial de la rutina en dicha estructura y cargar ES:BX apuntándola antes deretornar. El formato de SWSTARTUPINFO es el siguiente:

DW 3 ; versión de la estructuraDD ? ; puntero a la próxima estructura SWSTARTUPINFO (ES:BX inicial)DD 0 ; puntero al nombre ASCIIZ del dispositivo virtual (ó 0)DD 0 ; datos de referencia del dispositivo virtual (si tiene nombre)DD ? ; puntero a la tabla de registros de datos locales (ó 0)

El formato de la tabla de registros de datos locales, que define las estructuras de datos que seránlocales a cada sesión, es el siguiente:

DD ? ; dirección de memoria de la estructuraDW ? ; tamaño de la estructura. . .. . .DD 0 ; estructura NULLDW 0 ; (fin de lista)

En los momentos críticos en que el TSR deba evitar una conmutación de tareas, puede emplear lasfunciones BeginCriticalSection (llamar a INT 2Fh con AX=1681h) y EndCriticalSection (llamar a INT 2Fhcon AX=1682h); el TSR debe estar poco tiempo en fase crítica para no ralentizar Windows.

Page 201: PCA, PS2 ,IBM y AT

201PROGRAMAS RESIDENTES

Para detectar la presencia del conmutador de tareas del MS-DOS 5.0 se debe llamar a la INT 2Fhcon AX=4B02h: si a la vuelta AX es 0, significa que está cargado y ES:DI apunta a la rutina de servicio delmismo, 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 laCadena de Notificación) al ser desinstalados, para informar al conmutador. Una vez enganchado, el TSR serállamado por el conmutador de tareas para ser informado de todo lo interesante que suceda (de cosas talescomo la creación y destrucción de sesiones, suspensión del conmutador, etc.) por medio de la ejecución dela rutina de notificación del mismo, pudiendo el TSR permitir o no, por ejemplo, la suspensión de la sesión...el aviso de inicio de sesión es fundamental para los TSR que tienen áreas de datos temporales que inicializaral comienzo de cada sesión. El procedimiento general lo inicia el conmutador de tareas llamando a la INT2Fh con AX=4B01h: los TSR serán invocados unos tras otros (pasándose mutuamente el control). Paragestionar esto existe una estructura de datos denominada SWCALLBACKINFO (apuntada por ES:BX alllamar a INT 2Fh con AX=4B01h):

DD ? ; puntero a la estructura SWCALLBACKINFO anteriorDD ? ; puntero a la rutina de notificación del TSRDD ? ; área reservadaDD ? ; puntero a la lista de estructuras SWAPINFO

La lista de estructuras SWAPINFO tiene a su vez el siguiente formato:

DW 10 ; longitud de la estructuraDW ? ; identificador del API (1-NETBIOS, 2-802.2, 3-TCP/IP, 4-Tuberías

LanManager, 5-NetWare IPX)DW ? ; número de la mayor versión del API soportadaDW ? ; número de la menor versión del API soportadaDW ? ; nivel de soporte: 1-mínimo (el TSR impide la conmutación de la tarea

incluso tras finalizar sus funciones), 2-soporte a nivel API (el TSRimpide la conmutación de tareas si las peticiones son importantes), 3-Compatibilidad de conmutación (se permite conmutar de tarea incluso conpeticiones importantes, aunque algunas podrían fallar), 4-Sincompatibilidad (se permite siempre la conmutación).

Cuando el conmutador de tareas arranca, ejecuta una INT 2Fh con AX=4D05h para tomar nota delos bloques de datos locales a cada sesión, llamada que los TSR deberán detectar del mismo modo quecuando comprobaban la ejecución de Windows en modo extendido: la estructura de datos es además, porfortuna, la misma en ambos casos.

Las funciones que debe soportar la rutina de notificación, apuntada por la estructuraSWCALLBACKINFO, son las siguientes:

0000h inicialización del conmutadorDevuelve: AX = 0000h si permitido

= no cero si no permitir iniciar el conmutador0001h pregunta de suspensión del conmutador

BX = Identificación de sesiónDevuelve: AX = 0000h si permitir conmutación (el TSR no está en región crítica)

= 0001h si no0002h suspensión del conmutador

BX = Identificación de sesióninterrupciones inhibidas

Devuelve: AX = 0000h si permitido conmutar de sesión= 0001h si no

0003h activando conmutadorBX = Identificación de sesiónCX = banderines de estado de la sesión

bit 0: activo si primera activación de la sesiónbits 1-15: reservado (0)

interrupciones inhibidasDevuelve: AX = 0000h

0004h sesión activa del conmutadorBX = Identificación de sesiónCX = banderines de estado de la sesión

bit 0: activo si primera activación de la sesiónbits 1-15: reservado (0)

Devuelve: AX = 0000h0005h crear sesión del conmutador

BX = Identificación de sesiónDEVUELVE: AX = 0000h si permitido

= 0001h si no

Page 202: PCA, PS2 ,IBM y AT

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

0006h destruir sesiónBX = Identificación de sesiónDevuelve: AX = 0000h

0007h salida del conmutadorBX = banderines

bit 0: activo si el conmutador que llama es el único cargadobits 1-15: reservados (0)Devuelve: AX = 0000h

Page 203: PCA, PS2 ,IBM y AT

203CONTROLADORES DE DISPOSITIVOS

Capítulo XI: CONTROLADORES DE DISPOSITIVO

11.1. - INTRODUCCIÓN.

Los controladores de dispositivo (device drivers en inglés) son programas añadidos al núcleo delsistema operativo, concebidos inicialmente para gestionar periféricos y dispositivos especiales. Loscontroladores de dispositivo pueden ser de dos tipos: orientados a caracteres (tales como los dispositivosNUL, AUX, PRN, etc. del sistema) o bien orientados a bloques, constituyendo las conocidas unidades dedisco. La diferencia fundamental entre ambos tipos de controladores es que los primeros reciben o envían lainformación carácter a carácter; en cambio, los controladores de dispositivo de bloques procesan, como supropio nombre indica, bloques de cierta longitud en bytes (sectores). Los controladores de dispositivo,aparecidos con el DOS 2.0, permiten añadir nuevos componentes al ordenador sin necesidad de rediseñar elsistema operativo.

Los controladores de dispositivo han sido tradicionalmente programas binarios puros, similares a losCOM aunque ensamblados con un ORG 0, a los que se les colocaba una extensión SYS. Sin embargo, nohay razón para que ello sea así ya que un controlador de dispositivo puede estar incluido dentro de unprograma EXE, con la condición de que el código del controlador sea el primer segmento de dicho programa.El EMM386.EXE del MS-DOS 5.0 sorprendió a más de uno en su día, ya que llamaba la atención observarcómo se podía cargar con DEVICE: lo cierto es que esto es factible incluso desde el DOS 2.0 (pese a lo quepueda indicar algún libro), pero ha sido mantenido casi en secreto. Actualmente es relativamente frecuenteencontrar programas de este tipo. La ventaja de un controlador de dispositivo de tipo EXE es que puede serejecutado desde el DOS para modificar sus condiciones de operación, sin complicar su uso por parte delusuario con otro programa adicional. Además, un controlador de dispositivo EXE puede superar el límite delos 64 Kb, ya que el DOS se encarga de relocalizar las referencias absolutas a segmentos como en cualquierprograma EXE ordinario. Por cierto, el RAMDRIVE.SYS de WINDOWS 3.1 (no el de MS-DOS 5.0) y elVDISK.SYS de DR-DOS 6.0 son realmente programas EXE, aunque renombrados a SYS (aviso: norecomiendo a nadie ponerles extensión EXE y ejecutarlos después).

11.2.- ENCABEZAMIENTO Y PALABRA DE ATRIBUTOS.

Todo controlador de dispositivo de bloques comienza con una cabecera estándar, mostrada acontinuación:

CABECERA DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES

offset 0 DD 0FFFFFFFFh ; doble palabra de valor -1offset 4 DW 0 ; palabra de atributos (ejemplo arbitrario)offset 6 DW estrategia ; desplazamiento de la rutina de estrategiaoffset 8 DW interrupcion ; desplazamiento de la rutina de interrupciónoffset 10 DB 1 ; número de discos definidos: 1 por ejemplooffset 11 DB 7 DUP (0) ; 7 bytes no usados

Al principio, una doble palabra con el valor 0FFFFFFFFh (-1 en complemento a 2) será modificadaposteriormente por el DOS para enlazar el controlador de dispositivo con los demás que haya en el sistema,formando una cadena. No fue una ocurrencia muy feliz elegir precisamente ese valor inicial como obligatoriopara la copia en disco, dado que la instrucción de código de operación 0FFFFh es ilegal y bloquea la CPUsi es ejecutada. Esto significa que un controlador de dispositivo binario puro no puede ser renombrado a

Page 204: PCA, PS2 ,IBM y AT

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

COM y ejecutado también desde el DOS (habrá de ser necesariamente de tipo EXE). A continuación, trasesta doble palabra viene una palabra de atributos, cuyo bit más significativo está borrado en los dispositivosde bloques para diferenciarlos de los dispositivos de caracteres. Tras ello, aparecen los offsets a las rutinasde estrategia e interrupción, únicas de las que consta el controlador. Por último, un byte indica cuántasnuevas unidades de disco se definen y detrás hay 7 bytes reservados -más bien no utilizados-.

PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES

bit 15: borrado para indicar dispositivo de bloquesbit 14: activo si se soporta IOCTLbit 13: activo para indicar disco de formato no-IBMbit 12: reservadobit 11: en DOS 3+ activo si soportadas órdenes OPEN/CLOSE y REMOVEbit 10: reservadosbit 9: no documentado. Al parecer, el DRIVER.SYS del DOS 3.3 lo emplea para

indicar que no está permitida una E/S directa en las unidades «nuevas»bit 8: no documentado. El DRIVER.SYS del DOS 3.3 lo pone activo para las

unidades «nuevas»bit 7: en DOS 5+ activo si soportada orden 19h (CHECK GENERIC IOCTL SUPPORT)bit 6: en DOS 3.2+ activo si soportada orden 13h (GENERIC IOCTL)bits 5-2: reservadosbit 1: activo si el driver soporta direccionamientos de sector de 32 bits

(unidades de más de 65536 sectores y, por ende, más de 32 Mb).bit 0: reservado

En la palabra de atributos, el bit 15 indicaba si el dispositivo es de bloques o caracteres: en esteúltimo caso, la cabecera del controlador de dispositivo cambia ligeramente para indicar cuál es el nombre deldispositivo:

CABECERA DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES

offset 0 DD 0FFFFFFFFh ; doble palabra de valor -1offset 4 DW 8000h ; palabra de atributos (ejemplo arbitrario)offset 6 DW estrategia ; desplazamiento de la rutina de estrategiaoffset 8 DW interrupcion ; desplazamiento de la rutina de interrupciónoffset 10 DB "AUX " ; nombre del dispositivo (8 caracteres)

Aunque en el ejemplo aparece AUX, ello es un ejemplo de lo que no se debe hacer, a no ser que sealo que realmente se desea hacer (se está creando un dispositivo AUX que ya existe, con lo que se sobrescribey anula el puerto serie original). En general, además de los nombres de los dispositivos del sistema, nodeberían utilizarse los que crean ciertos programas (como el EMMXXXX0 del controlador EMS, etc.).Conviene decir aquí que muchos de los controladores de dispositivo de caracteres instalados en el ordenadorno lo son tal realmente, sino que se trata de simples programas residentes que se limitan a dar error a quienintenta acceder a ellos (pruebe el lector a ejecutar la orden COPY *.* EMMXXXX0: con el controlador dememoria expandida instalado) aunque algunos implementan ciertas funciones vía IOCTL.

La palabra de atributos del controlador de dispositivo de caracteres también cambia respecto al debloques, pero sustancialmente:

PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES

bit 15: activo para indicar dispositivo de caracteresbit 14: activo si se soporta IOCTLbit 13: en DOS 3+ activo si se soporta orden 10h (OUTPUT UNTIL BUSY)bit 12: reservadobit 11: en DOS 3+ activo si soportadas órdenes OPEN/CLOSE y REMOVE)bits 10-8: reservadosbit 7: en DOS 5+ activo si soportada orden 19h (CHECK GENERIC IOCTL SUPPORT)bit 6: en DOS 3.2+ activo si soportada orden 13h (GENERIC IOCTL)bit 5: reservadobit 4: activo si el dispositivo es «especial» y utiliza la INT 29h (llamada

por el DOS para imprimir e carácter ubicado en AL).bit 3: activo si es el dispositivo CLOCK$ (CLOCK en MS-DOS 2.X y anteriores)

Este dispositivo poco conocido es útil para consultar o establecer encualquier momento la hora del sistema con la siguiente secuencia de 6bytes: DW dias_transcurridos_desde_1980

DB minutosDB horasDB centésimas de segundoDB segundos

bit 2: activo si es el dispositivo NULbit 1: activo si es el dispositivo de salida estándarbit 1: activo si es el dispositivo de entrada estándar

Page 205: PCA, PS2 ,IBM y AT

205CONTROLADORES DE DISPOSITIVOS

11.3. - RUTINAS DE ESTRATEGIA E INTERRUPCIÓN.

Cuando el DOS va a acceder a un dispositivo (debido a una petición de un programa de usuario)ejecuta, de manera secuencial, las rutinas de estrategia e interrupción, que son de tipo FAR. Hay que recordarque el paso del MS-DOS 1.0 al 2.0 supuso una emigración de la filosofía del CP/M a la del UNIX. La razónde la existencia separada de las rutinas de estrategia e interrupción se inspira en la filosofía de diseño delUNIX y su arquitectura multitarea, aunque para el DOS hubiera sido suficiente una sola rutina. De hecho,la rutina de estrategia tiene como única misión recoger la dirección de la cabecera de petición de solicitudque el DOS envía al driver, en ES:BX. Las 3 líneas de código siguientes constituyen una rutina de estrategia,ya que son prácticamente idénticas en todos los controladores de dispositivo:

RUTINA DE ESTRATEGIA

estrategia PROC FAR ; de tipo FARMOV CS:pcab_pet_desp,BXMOV CS:pcab_pet_segm,ESRET

estrategia ENDP

pcab_peticion LABEL DWORDpcab_pet_desp DW 0pcab_pet_segm DW 0

¿Para qué sirve la cabecera de petición de solicitud?: sencillamente, es un área de datos que el DOSutiliza para comunicarse con el controlador de dispositivo. Por medio de este área se envían las órdenes ylos parámetros que el dispositivo soporta, y se recogen ciertos resultados. La rutina de interrupción deldispositivo, además de preservar todos los registros que va a alterar para restaurarlos al final, se encarga deconsultar la dirección de la cabecera de petición de solicitud que almacenó la rutina de estrategia y comprobarqué le está pidiendo el DOS. No es realmente una rutina de interrupción ya que retorna con RETF, en vezde con IRET, por lo que nunca podrá ser invocada por una interrupción hardware. Aunque según la ordena procesar el tamaño de la cabecera de petición de solicitud puede variar, los primeros 13 bytes son:

CABECERA DE PETICIÓN DE SOLICITUD (13 PRIMEROS BYTES) COMÚN A TODAS LAS ÓRDENES

offset 0 DB longitud_bloque ; longitud total de la cabeceraoffset 1 DB num_disco ; disco implicado (sólo en disp. bloques)offset 2 DB orden ; orden solicitada por el sistemaoffset 3 DW palabra_estado ; donde devolver la palabra de estadooffset 5 DD pun_dos ; apuntador usado por el DOSoffset 9 DD encadenamiento ; usado por el DOS para encadenar

En general, la rutina de interrupción suele multiplicar por dos el número de la orden (almacenada enel offset 2 de la cabecera de petición), para así acceder indexadamente a una tabla de palabras que contienelos desplazamientos a las rutinas que procesan las diversas órdenes: aunque esto no ha de ser necesariamenteasí, casi todos los controladores de dispositivo se comportan de esta manera.

11.4. - ORDENES A SOPORTAR POR EL CONTROLADOR DE DISPOSITIVO.

00h INIT01h MEDIA CHECK (dispositivos de bloque)02h BUILD BPB (dispositivos de bloque)03h IOCTL INPUT04h INPUT05h NONDESTRUCTIVE INPUT, NO WAIT (dispositivos de caracteres)06h INPUT STATUS (dispositivos de caracteres)07h INPUT FLUSH (dispositivos de caracteres)08h OUTPUT09h OUTPUT WITH VERIFY0Ah OUTPUT STATUS (dispositivos de caracteres)0Bh OUTPUT FLUSH (dispositivos de caracteres)0Ch IOCTL OUTPUT0Dh (DOS 3+) DEVICE OPEN0Eh (DOS 3+) DEVICE CLOSE0Fh (DOS 3+) REMOVABLE MEDIA (dispositivos de bloques)10h (DOS 3+) OUTPUT UNTIL BUSY (dispositivos de caracteres)11h-12h no usada13h (DOS 3.2+) GENERIC IOCTL14h-16h no usadas17h (DOS 3.2+) GET LOGICAL DEVICE18h (DOS 3.2+) SET LOGICAL DEVICE19h (DOS 5.0+) CHECK GENERIC IOCTL SUPPORT

Page 206: PCA, PS2 ,IBM y AT

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

La tabla anterior resume las órdenes que puede soportar un controlador de dispositivo; en general noserá preciso implementar todas: de hecho, incluso para un disco virtual basta con algunas de las primeras 16.Todas las órdenes devuelven una palabra de estado al sistema operativo, cuyo formato puede consultarse acontinuación. En general, las ordenes no soportadas pueden originar un error o bien ser sencillamenteignoradas (en ese sentido, crear un dispositivo NUL es tarea realmente sencilla).

FORMATO DE LA PALABRA DE ESTADO

bit 15: Activo si hay error, en ese caso los bits 0-7 indican el tipo de errorbits 14-10: Reservadosbit 9: Activo si el controlador de dispositivo no está listo. En las operaciones

de entrada está listo si hay un carácter en el buffer de entrada o si talbuffer no existe; en las de salida cuando el buffer aún no está lleno.

bit 8: Activo si el controlador de dispositivo ha acabado de ejecutar la orden.Hasta el DOS 5.0 al menos, esto es siempre así (en un hipotético sistemamultitarea, una orden podría ejecutarse en varias ráfagas de CPU).

bits 7-0: Código de error, si el bit 15 está activo:00h disco protegido contra escritura01h unidad desconocida02h unidad no preparada03h orden desconocida04h error de CRC05h longitud inválida de la cabecera de petición06h fallo en el posicionamiento del cabezal07h medio físico desconocido08h sector no encontrado09h impresora sin papel0Ah error de escritura0Bh error de lectura0Ch anomalía general0Dh reservado0Eh (CD-ROM) medio físico no disponible0Fh cambio de disco no permitido

La construcción de rutinas de gestión para las diversas órdenes que han de soportarse no es unproceso muy complicado, pese a que está envuelto en una leyenda negra. Sin embargo, puede que parte dela explicación que viene a continuación sobre dichas órdenes sea difícil de entender al lector poco iniciado.No hay que olvidar que los controladores de dispositivo respetan unas normas de comportamiento definidaspor el fabricante del DOS, y más que de intentar comprender por qué una cosa es de una maneradeterminada, de lo que se trata es de obedecer. En general, lo que no se entienda puede ser pasado por altoya que probablemente no es estrictamente necesario conocerlo. Además, casi ningún controlador necesitasoportar todas las órdenes, como se verá al final en los programas de ejemplo.

11.4.0. - Orden 0 o INIT.

CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 0 (INIT)

offset 0 13 BYTES: Ya vistos con anterioridad.offset 0Dh BYTE: A la vuelta, indicar al DOS el nº de unidades de disco

definidas (solo en dispositivos de bloque).offset 0Eh: DWORD: A la vuelta, indica el último byte residente con un

puntero largo de 32 bits. Si el dispositivo no se instalaante algún fallo, para no quedar residente basta indicarun offset 0 (el segmento es vital inicializarlo con CS).

offset 12h: DWORD: A la entrada, el DOS indica dónde comienza la línea deparámetros del CONFIG.SYS. A la salida se indica al DOSla dirección de la tabla de apuntadores a estructuras BPB(esto último sólo en los dispositivos de bloques).

offset 16h: BYTE: Desde el DOS 3.0, número de discos lógicos existenteshasta ese momento ej. 3 para A: B: y C: (solo en losdispositivos de bloque).

Esta es la primera de todas las órdenes y se ejecuta siempre una vez cuando el dispositivo es cargadoen memoria, con objeto de que éste se inicialice. Aquí sí se pueden emplear libremente las funciones del DOS(en el resto de las órdenes no: el driver es un programa residente más). En su inicialización el driver decidequé cantidad de memoria se queda residente y puede analizar la línea de comandos del CONFIG.SYS paracomprobar los parámetros del usuario. En los dispositivos de bloque se indica también al sistema el númerode unidades definidas por el controlador y la dirección de una tabla de punteros a estructuras BPB, ya que

Page 207: PCA, PS2 ,IBM y AT

207CONTROLADORES DE DISPOSITIVOS

existe una de estas estructuras para cada unidad lógica. El BPB (BIOS Parameter Block) es una estructuraque contiene información sobre las unidades; puede consultarse en el capítulo 7. Aunque el BPB ha sidoampliado en las últimas versiones del DOS, para construir discos de menos de 65536 sectores solo hace faltacompletar los primeros campos (solo hasta los relacionados con el DOS 2.0 o, como mucho, el 3.0).

Los parámetros en la línea de comandos del CONFIG.SYS son similares a los de un programaordinario, aunque como se observa en el cuadro anterior su dirección se obtiene en el puntero de 32 bitsubicado en el offset 12h de la cabecera de petición de solicitud. Por ello, si ES:BX apunta a dicha cabecera,la instrucción LES BX,ES:[BX+12h] tiene como resultado alterar el valor de ES:BX para que ahora apuntea la zona de parámetros. En ella, aparece todo lo que había después del ’=’ o el ’ ’ que seguía al DEVICE.Por ejemplo, para una línea de config.sys como la siguiente:

DEVICE \DOS\VDISK.SYS 128

el contenido de la zona de parámetros sería ’\DOS\VDISK.SYS 128’ -sin incluir las comillas,lógicamente-. Como se puede observar, el nombre y ruta del programa están separados de sus parámetros poruno o más delimitadores (espacios en blanco o tabuladores -ASCII 9-); al final se encuentra el código deretorno de carro -ASCII 13- aunque quizá en algunas versiones del DOS podría estar indicado el final de lacadena por un salto de línea -ASCII 10- en lugar del retorno de carro. Aviso: tras el nombre/ruta del fichero,las versiones más antiguas del DOS colocan un byte a cero. No se debe modificar la línea de parámetros:además de improcedente puede ser peligroso, al tratarse de un área de datos del sistema. En los dispositivosde bloque, el mismo campo donde se obtiene la dirección de los parámetros ha de ser empleado para devolveral DOS la dirección de los punteros a los BPB: el sentido común indica que primero debe leerse la direcciónde los parámetros y después puede modificarse dicho campo.

11.4.1. - Orden 1 o MEDIA CHECK.

Esta orden sólo es preciso implementarla en los dispositivos de bloques, sirve para que el sistemapregunte al controlador si se ha producido un cambio en el soporte: por ejemplo, si se ha cambiado eldisquete de la disquetera. En general, los discos fijos y virtuales suelen responder que no, ya que es seguroque nadie puede haberlos cambiado; en los disquetes suele responderse que sí (ante la duda). En caso de queel soporte haya cambiado, el DOS invalida y libera todos los buffers en memoria relacionados con el mismo.Si no ha cambiado, el DOS sacará la información de sus buffers internos evitando en lo posible un accesoal disco.

CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 1 (MEDIA CHECK)

offset 0 13 BYTES: Ya vistos con anterioridad.offset 13 BYTE: A la entrada, el DOS indica el descriptor del soporte

(solo en dispositivos de bloque)offset 14 BYTE: A la vuelta, el driver indica el resultado: 0FFh si se ha

producido un cambio, 0 si se desconoce (lo que equivaleal primer caso) y 1 si no ha habido cambio.

11.4.2. - Orden 2 o BUILD BPB.

Es ejecutada por el sistema si la respuesta a la orden MEDIA CHECK es afirmativa (cambio desoporte). El DOS necesita entonces averiguar las características del nuevo soporte, para lo que pide al driverque le suministre un BPB con información. De nuevo, esta orden solo ha de implementarse en los dispositivosde bloques. Desde el DOS 3.0 se recomienda anotar la etiqueta de volumen del disco cuando se ejecuta estaorden para detectar un posible cambio ilegal del mismo, aunque lo cierto es que este método es bastanteineficiente (discos sin etiquetar, con la misma etiqueta...); desde el DOS 4.0 se mejora este asunto con losnúmeros de serie, pero pocos drivers se molestan en comprobarlos. Las versiones más antiguas del DOS (2.x)necesitan que cambie el byte descriptor de soporte para detectar el cambio de disco. Las versiones actuales,habida cuenta del caos de bytes de identificación comunes para disquetes diferentes, no requieren que el bytedescriptor cambie para aceptar el cambio y confían en la información que suministra MEDIA CHECK.

Page 208: PCA, PS2 ,IBM y AT

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

En los discos de tipo IBM, los más comunes, el DOS intenta cooperar con el controlador dedispositivo en los cambios de disco. Por ello, se las apaña para leer el primer sector de la FAT y se lo pasaal driver, que así tiene más fácil la tarea de detectar el tipo de disco y suministrar al DOS el BPB adecuado,ya que el primer byte de la FAT contiene el tipo de disco (byte descriptor de medio). En los discos que noson de tipo IBM es el driver quien, por sus propios medios, ha de apañárselas para detectar el tipo de discointroducido en la unidad correspondiente: por ejemplo, leyendo el sector de arranque. En algunos casos puederesultar útil indicar que el disco es de tipo no IBM; por ejemplo en un controlador para un soporte físico quenecesite detectar el medio introducido para poder acceder al mismo. Por ejemplo en una disquetera: alintroducir un nuevo disco de densidad diferente al anterior, el intento por parte del DOS de leer la FAT enlos discos tipo IBM provocaría un fallo (si esto no sucede con el controlador del propio sistema para lasdisqueteras es porque la BIOS suplanta al DOS, realizando quizá algunas tareas más de las que debería tenerestrictamente encomendadas al detectar un cambio de disco).

CABECERA DE PETICIÓN DE SOLICITUD PARA LA ORDEN 2 (BUILD BPB)

offset 0 13 BYTES: Ya vistos con anterioridad.offset 13 BYTE: A la entrada, el DOS indica el descriptor del soporte.

(solo en dispositivos de bloque)offset 14 DWORD: A la entrada, el DOS apunta a un buffer que contiene el

primer sector de la FAT (cuyo 1º byte es el descriptor desoporte) si el disco es de tipo IBM; de lo contrario elbuffer está vacío y puede emplearse para otro propósito.

offset 18 DWORD: A la vuelta, el driver devuelve aquí la dirección del BPBdel nuevo disco (no la de ninguna tabla de punteros).

11.4.3. - Orden 3 o IOCTL INPUT.

Puede ser soportada tanto por los dispositivos de caracteres como por los de bloque, el sistema solola utiliza si así se le indicó en la palabra de atributos del dispositivo (bit 14). El IOCTL es un mecanismogenérico de comunicación de las aplicaciones con el controlador de dispositivo; por medio de esta función,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. Es frecuente que no esté soportada enlos dispositivos más simples. La cabecera de petición de solicitud de esta orden y de varias de las queveremos 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 13 BYTES: Ya vistos con anterioridad.offset 13 BYTE: A la entrada, el DOS indica el descriptor del soporte.

(solo en dispositivos de bloque)offset 14 DWORD: En entrada, dirección del área de transferencia a memoriaoffset 18 WORD: En entrada, número de sectores (dispositivos de bloques)

o bytes (dispositivos de caracteres) a transferir.A la salida, sectores/bytes realmente transferidos.

offset 20 WORD: Número de sector de comienzo (solo en los dispositivos debloques y de menos de 32 Mb)

offset 22 DWORD: En las órdenes 4 y 8 y desde el DOS 3.0 se devuelve alDOS un puntero a la etiqueta de volumen del disco en elcaso de un error 0Fh.

offset 26 DWORD: Número de sector de comienzo en discos de más de 32Mb(ver bit 1 de palabra de atributos). En cualquier caso,solo debe considerarse este campo si la longitud de lacabecera de petición (byte 0) es mayor de 1Ah.

11.4.4. - Orden 4 o INPUT.

Esta orden es una de las más importantes. Sirve para que el sistema lea los datos almacenados en eldispositivo. Si el dispositivo es de caracteres, los almacenará en un buffer de entrada a medida que le vanllegando del periférico y los enviará en respuesta a esta orden (si no los tiene, espera un tiempo razonablea que le lleguen antes de "fallar"). Si el dispositivo es de bloque, no se envían bytes sino sectores completos.

Page 209: PCA, PS2 ,IBM y AT

209CONTROLADORES DE DISPOSITIVOS

En los dispositivos de caracteres, lo más normal es que el DOS solicite transferir sólo 1 en cada vez, aunqueen teoría podría solicitar cualquier cantidad. En el caso de los dispositivos de bloque esta orden es ejecutadapor el DOS cuando se accede a disco vía INT 25h/26h.

11.4.5. - Orden 5 o NONDESTRUCTIVE INPUT.

Solo debe ser soportada por los dispositivos de caracteres. Es análoga a INPUT, con la diferencia deque no se avanza el puntero interno al buffer de entrada de datos tras leer el carácter. Por ello, tras utilizaresta orden será preciso emplear después la 4 para leer realmente el carácter. La principal utilidad de esto esque el sistema puede saber si el dispositivo tiene ya un nuevo carácter disponible antes de llamarle, paraevitar que éste se quede parado hasta que le llegue. El bit 9 de la palabra de estado devuelta indica, si estáactivo, que el dispositivo está ocupado (sin caracteres).

11.4.6. - Orden 6 o INPUT STATUS.

Es totalmente análoga a NONDESTRUCTIVE INPUT, con la salvedad de que ni siquiera se envíael siguiente carácter del buffer de entrada. Sólo sirve para determinar el estado del controlador, indagandosi tiene caracteres disponibles o no.

11.4.7. - Orden 7 o INPUT FLUSH.

Solo disponible en dispositivos de caracteres, vacía el buffer del dispositivo. Lo que éste suele haceres sencillamente igualar los punteros al buffer de entrada interno (el puntero al último dato recibido delperiférico y el puntero al próximo carácter a enviar al sistema cuando se lo pida).

11.4.8. - Orden 8 u OUTPUT.

Es otra de las órdenes más importantes, análoga a INPUT pero actuando al revés. Permite al sistemaenviar datos al dispositivo, bien sean caracteres o sectores completos, según el tipo de dispositivo.

11.4.9. - Orden 9 u OUTPUT VERIFY.

Es análoga a OUTPUT, con la salvedad de que el dispositivo efectúa, tras escribir, una lecturainmediata hacia un buffer auxiliar, con la correspondiente comprobación de que lo escrito es correcto alcomparar ambos buffers. Resulta totalmente absurdo implementarla en un disco virtual (el 11% de la memoriadel sistema podría estar ya destinada a detectar un fallo en cualquier byte de la misma, y además es igual deprobable el error durante la escritura que durante la verificación) por lo que en este caso debe comportarseigual que la orden anterior. En los discos físicos de verdad, sin embargo, conviene tomarla en serio.

11.4.10. - Orden 0Ah u OUTPUT STATUS.

Es similar a INPUT STATUS y, como ésta, propia de los dispositivos de caracteres. Su misión esanáloga, pero relacionada con el buffer de salida en vez del buffer de entrada.

11.4.11. - Orden 0Bh u OUTPUT FLUSH.

También exclusiva de dispositivos de caracteres, es equivalente a INPUT FLUSH, vaciándose elbuffer de salida en lugar de el de entrada.

11.4.12. - Orden 0Ch o IOCTL OUTPUT.

Es complementaria de la orden IOCTL INPUT: se pueden enviar cadenas de información a travésde la función 44h del DOS (subfunciones 3 y 5). Es útil para lograr una comunicación de ciertasinformaciones con el controlador a través de otro canal, sin tener que mezclarla con los datos que se le

Page 210: PCA, PS2 ,IBM y AT

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

envían. Algunos programas residentes, instalados como falsos controladores de dispositivo de caracteressoportan ciertos comandos vía IOCTL, evitando a las aplicaciones acceder directamente a la zona de memoriadonde está instalado el controlador para modificar sus variables.

11.4.13. - Orden 0Dh o DEVICE OPEN.

Solo implementada desde el DOS 3.0 y superior, indica que el dispositivo o un fichero almacenadoen él ha sido abierto. El controlador se limita a incrementar un contador. Esta orden y las dos siguientes nohan de estar necesariamente soportadas.

11.4.14. - Orden 0Eh o DEVICE CLOSE.

Solo implementada desde el DOS 3.0 y superior, indica que el dispositivo o un fichero almacenadoen él ha sido cerrado. El controlador se limita a decrementar un contador: si éste llega a cero, se reinicializanlos buffers internos, si los hay, para permitir por ejemplo un posible cambio de disco.

11.4.15. - Orden 0Fh o REMOVABLE MEDIA.

Solo implementada también desde el DOS 3.0 y superior, indica al sistema si el dispositivo esremovible o no, apoyándose en los resultados de las dos órdenes anteriores.

11.4.16. - Orden 10h u OUTPUT UNTIL BUSY.

Solo es admitida en dispositivos de caracteres y a partir del DOS 3.0; sirve para enviar más de uncarácter al periférico. En concreto, se envían todos los que sean posibles (de la cantidad solicitada) hasta queel periférico esté ocupado: entonces se retorna. Aquí no se considera un error no haber podido transferir todo.Esta función es útil para acelerar el proceso de salida.

11.4.17. - Otras órdenes.

Las órdenes 11h, 12h, 14h, 15h y 16h no han sido aún definidas, ni siquiera en el DOS 5.0. Laorden 13h o GENERIC IOCTL, disponible desde el DOS 3.2 permite un mecanismo más sofisticado decomunicación IOCTL. También en el DOS 3.2 han sido definidas las órdenes 17h (GET LOGICALDEVICE) y 18h (SET LOGICAL DEVICE). El DOS 5.0 añade una nueva: la 19h (CHECK GENERICIOCTL SUPPORT). Por cierto, las ordenes 80h y superiores están destinadas a la comunicación con losdispositivos CD-ROM...

11.5. - LA CADENA DE CONTROLADORES DE DISPOSITIVO INSTALADOS.

Los controladores de dispositivo forman una cadena en la memoria, una lista conectada por los 4primeros bytes de la cabecera utilizados a modo de puntero. A medida que se van instalando en memoria,quedan de tal manera que los últimos cargados apuntan a los predecesores. Al final, el sistema operativoapunta el dispositivo NUL al último dispositivo instalado, colocándose NUL al final de la cadena. Por tanto,averiguando la dirección del dispositivo NUL y siguiendo la cadena de apuntadores obtenida en los primeros4 bytes de cada uno (en la forma segmento:offset) se puede recorrer la lista de dispositivos (ya sean decaracteres o de bloque) en orden inverso al que fueron instalados en memoria. El último de ellos estaráapuntando a XXXX:FFFF. La lista de controladores de dispositivo puede pasar por la memoria convencionalo por la superior, saltando de una a la otra múltiples veces. Algunos gestores de memoria, como QEMMcuando se utiliza LOADHI.SYS (en lugar del DEVICEHIGH del DOS) colocan la cadena de dispositivos enmemoria convencional, aunque luego instalen el mismo en memoria superior. Esto quiere decir que paraacceder al código o datos internos del dispositivo conviene tomar precauciones, de cara a averiguar ladirección donde realmente reside. El programa TURBODSK que veremos más adelante utiliza la cadena decontroladores de dispositivo para buscarse a sí mismo en memoria e identificar todas las posibles unidades

Page 211: PCA, PS2 ,IBM y AT

211CONTROLADORES DE DISPOSITIVOS

que controla. Por desgracia, la manera de obtener la dirección del dispositivo NUL varía de unas versionesdel DOS a otras, aunque solo ligeramente. 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 comienzala cabecera del dispositivo NUL (el propio dispositivo, no un puntero al mismo). Ese offset es 17h para lasversiones 2.X del DOS, 28h para la 3.0X y 22h para todas las demás, habidas y por haber. La utilidadDRV.C listada más abajo recorre los dispositivos instalados, informando de ellos. Adicionalmente, exceptoen las versiones más antiguas del DOS, DRV.C accede a los bloques de control de memoria que precedena los dispositivos que están ubicados en un offset 0 respecto al segmento, con objeto de indicar el consumode memoria de los mismos y el nombre del fichero ejecutable. Con DR-DOS 5.0 no se informa correctamentedel nombre, ni tampoco del tamaño (excepto si el dispositivo está instalado en memoria superior); no hayproblemas sin embargo con DR-DOS 6.0 ni, por supuesto, con MS-DOS 4.0 ó posterior. A continuación,antes del listado del programa, se muestra un ejemplo de salida del mismo bajo MS-DOS 5.0 (por supuesto,no recomiendo a nadie instalar tantos discos virtuales).

DRV 1.0 LISTA DE DISPOSITIVOS DEL SISTEMA (c) 1992 CiriSOFTDirección Tipo Nombre Estrat. Interr. Atributo Programa Tamaño

0116:0048 Carácter NUL 0DC6 0DCC 8004E279:0000 Bloque Unidad I: 00CB 00D6 0800 RAMDRIVE 1184E22B:0000 Bloque Unidad H: 00CB 00D6 0800 RAMDRIVE 1232E1A7:0000 Bloque Unidad G: 0086 0091 0800 VDISK 2096E103:0000 Bloque Unidad F: 0086 0091 0800 VDISK 2608E0E6:0000 Bloque Unidad E: 005A 0065 0800 TDSK 448E0BE:0000 Bloque Unidad D: 005A 0065 0800 TDSK 624E013:0000 Carácter CON 0078 0083 8013 ZANSI 2720E003:0000 Carácter ALTDUP$ 00C2 00CD 8000 ALTDUP 240DFD8:0000 Carácter KEYBSP50 0012 0018 8000 KEYBSP 672DD90:0000 Carácter gmouse 0012 0021 8000 GMOUSE 9328DD85:0000 Carácter ACCESOS$ 0013 001A 8000 ACCESOS 160DD7C:0000 Carácter &FDREAD2 0012 0012 8000 FDREAD 1280316:0000 Carácter KEYBUF21 0012 0018 8000 KEYBUFF 160D803:0000 Carácter SMARTAAR 00A2 00AD C800 SMARTDRV 224000255:003F Carácter QEMM386$ 0051 007D C0000255:0000 Carácter EMMXXXX0 0051 0064 C000 QEMM386 30720070:0023 Carácter CON 06F5 0700 80130070:0035 Carácter AUX 06F5 0721 80000070:0047 Carácter PRN 06F5 0705 A0C00070:0059 Carácter CLOCK$ 06F5 0739 80080070:006B Bloque Unidades A:-C: 06F5 073E 08C20070:007B Carácter COM1 06F5 0721 80000070:008D Carácter LPT1 06F5 070C A0C00070:009F Carácter LPT2 06F5 0713 A0C00070:00B8 Carácter LPT3 06F5 071A A0C00070:00CA Carácter COM2 06F5 0727 80000070:00DC Carácter COM3 06F5 072D 80000070:00EE Carácter COM4 06F5 0733 8000

// DRV 1.0// Utilidad para listar los controladores de dispositivo instalados.

#include <dos.h>#include <stdio.h>

struct REGPACK r;unsigned long huge *siguiente;unsigned char huge *disp;int i, disco, dosver;

void main()

r.r_ax=0x3000; intr (0x21, &r); /* obtener versión del DOS */dosver=(r.r_ax << 8) | (r.r_ax >> 8);if ((dosver & 0xFF00)==0x200) i=0x17; /* DOS 2.XX */else if ((dosver>0x2FF) && (dosver<0x30A)) i=0x28; /* DOS 3.0X */else i=0x22; /* otra versión */

r.r_ax=0x5200; intr (0x21, &r); /* "Get List of Lists" */

siguiente=MK_FP(r.r_es, r.r_bx+i); disco=’A’-1;while (FP_OFF(siguiente)!=0xffff) disp = (unsigned char huge *) siguiente;if (!(disp[5] & 0x80)) disco+=disp[10]; /* contar discos */siguiente = (unsigned long huge *) *siguiente;

siguiente=MK_FP(r.r_es, r.r_bx+i);printf("\n DRV 1.0 LISTA DE DISPOSITIVOS DEL SISTEMA

(c) 1992 CiriSOFT \n");printf(" Dirección Tipo Nombre Estrat. Interr.

Atributo Programa Tamaño \n");printf("

");

while (FP_OFF(siguiente)!=0xffff) disp = (unsigned char huge *) siguiente;printf(" \n %04X:%04X ", FP_SEG(disp), FP_OFF(disp));if (disp[5] & 0x80)

printf("Carácter ");for (i=10; i<18; i++) printf("%c",disp[i]); printf(" ");

else

printf("Bloque ");if (disp[10]==1)

printf("Unidad %c: ", disco--);else

printf("Unidades %c:-%c:",disco-disp[10]+1, disco);disco-=disp[10];

printf(" %04X %04X %04X ", disp[6] | (disp[7]<<8),

disp[8] | (disp[9]<<8), disp[4] | (disp[5]<<8));

if ((!FP_OFF(disp)) && (dosver>0x31E)) for (i=-8; i<0; i++)

if (disp[i]>=’ ’) printf("%c",disp[i]); else printf(" ");printf(" %6u ",(disp[-13] | (disp [-12] << 8)) << 4);

elseprintf(" ");

siguiente = (unsigned long huge *) *siguiente;

printf(" \n "); for (i=1; i<78; i++) printf(" "); printf(" \n");

Page 212: PCA, PS2 ,IBM y AT

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

11.6. - EJEMPLO DE CONTROLADOR DE DISPOSITIVO DE CARACTERES.

El controlador propuesto de ejemplo crea un dispositivo HEX$ que imprime en pantalla y enhexadecimal todo lo que recibe. Por supuesto, el programa se instala en el CONFIG.SYS con una orden deltipo DEVICE=HEX.SYS. En principio, sería un programa mucho más simple si se limitara a imprimir loscaracteres que recibe, aunque ello no tendría utilidad alguna. De hecho, la mayor parte de la complejidad dellistado no se debe al controlador de dispositivo, sino al resto. Para empezar, las órdenes Open, Close oRemove, en un hipotético dispositivo que simplemente sacara por pantalla lo que recibe están de más.Además, la rutina que procesa los caracteres (procesa_AL) se limitaría a imprimirles; también se eliminaríantodas las demás subrutinas de apoyo. Sin embargo, el hecho de realizar un volcado hexadecimal complicabastante el asunto. El listado hexadecimal que se obtiene es similar al siguiente:

C:\WP51\TEXTOS>type prueba.bin > hex$

00000000 45 73 74 65 20 65 73 20 - 75 6E 20 66 69 63 68 65 Este es un fiche00000010 72 6F 20 64 65 20 70 72 - 75 65 62 61 73 2E 20 53 ro de pruebas. S00000020 A2 6C 6F 20 73 69 72 76 - 65 20 70 61 72 61 20 70 ólo sirve para p00000030 72 6F 62 61 72 2E 0A 0D robar...

Es preciso implementar la orden Open para detectar el inicio de la transferencia, inicializando a ceroel contador de offset relativo de la izquierda. Los caracteres se imprimen unos tras otros en hexadecimal (conun guión separador tras el octavo) y se van almacenando en un buffer hasta completar 16: entonces, seimprimen de nuevo pero en ASCII (sustituyendo por puntos los códigos de control). La orden Close sirvepara detectar el final de la operación: ante ella se escriben los espacios necesarios y se vuelcan los códigosASCII acumulados hasta el momento (entre 0 y 15) que restasen por ser imprimidos. Por emplear Open yClose este controlador de dispositivo necesita DOS 3.0 o superior.

Utilizando COPY en vez de TYPE, al enviar varios ficheros con los comodines el COMMAND sueleencadenarles en uno solo y el offset es relativo al primero enviado (esto depende de la versión del intérpretede comandos). Aunque se supone que el DOS va a enviar los caracteres de uno en uno, el dispositivo se tomala molestia de prever que esto pueda no ser así, procesando en un bucle todos los que se le indiquen. Paraimprimir se utiliza la INT 29h del DOS (fast console OUTPUT), más recomendable que llamar a un serviciodel sistema operativo (que a fin de cuentas va a parar a esta interrupción). No hay que olvidar que loscontroladores de dispositivo son también programas residentes a todos los efectos, con las mismaslimitaciones. Sin embargo, desde los programas normales no es recomendable utilizar la INT 29h, entre otrasrazones porque esos programas, además de imprimir a poca velocidad, no soportarían redireccionamiento enla salida (la INT 29h no es precisamente rápida, aunque sí algo más que llamar al DOS).

El dispositivo HEX$ sólo actúa en salida, imprimiendo en pantalla lo que recibe. Si se intenta leerdesde él devuelve una condición de error (por ejemplo, al realizar COPY HEX$ FICH.TXT). Para visualizarficheros binarios que puedan contener la marca de fin de fichero (^Z) no basta hacer TYPE o COPY a secas:en estos casos se debe emplear COPY /B FICHERO.EXT HEX$, la opción /B sirve para que la salida no sedetenga ante el ^Z. La operación de impresión en pantalla se supone siempre exitosa; por ello el dispositivono modifica la variable que indica el número de caracteres a procesar: al devolverla precisamente como estabaal principio indica que se han procesado sin problemas todos los solicitados. En la instalación se compruebala versión del DOS, para cerciorarse de la presencia de un 3.0 o superior. Este driver de ejemplo sóloconsume 464 bytes de memoria bajo MS-DOS 5.0. Tras ensamblarlo y linkarlo hay que aplicar EXE2BINpara pasarlo de EXE a SYS (TLINK /t sólo opera cuando hay un ORG 100h).

Como se puede verificar observando el listado, las únicas órdenes realmente soportadas por eldispositivo son, aparte de OPEN, CLOSE y REMOVE, las órdenes WRITE y WRITE VERIFY. Todas lasdemás, en este controlador que no depende del hardware típico de entrada/salida, son innecesarias. Como elproceso de escritura en pantalla se supone siempre con éxito, WRITE VERIFY es idéntica a WRITE, sinrealizar verificación alguna. Las órdenes no soportadas pueden ser ignoradas o bien desembocar en un error,según sea el caso.

Page 213: PCA, PS2 ,IBM y AT

213CONTROLADORES DE DISPOSITIVOS

; ********************************************************************; * *; * HEX$ 1.0 - (c) 1992 Ciriaco García de Celis. *; * *; * Controlador de dispositivo para volcado hexadecimal en salida. *; * *; ********************************************************************

; ------------ Macros de propósito general

XPUSH MACRO RM ; apilar lista de registrosIRP reg, <RM>PUSH reg

ENDMENDM

XPOP MACRO RM ; desapilar lista de registrosIRP reg, <RM>POP reg

ENDMENDM

; ************ Inicio del área residente.

HEXSEG SEGMENTASSUME CS:HEXSEG, DS:HEXSEG

DD -1 ; encadenamiento con otros driverstipo_drive DW 8800h ; 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.0+)

DW estrategia ; rutina de estrategiaDW interrupción ; rutina de interrupciónDB "HEX$ " ; nombre del dispositivo

; ------------ Variables y tablas de datos globales fijas.

pcab_peticion LABEL DWORD ; puntero a la cabecera de peticiónpcab_pet_desp DW 0pcab_pet_segm DW 0

p_rutinas LABEL WORD ; tabla de rutinas del controladorDW initDW media_checkDW build_bpbDW ioctl_inputDW readDW read_nowaitDW input_statusDW input_flushDW writeDW write_verifyDW output_statusDW output_flushDW ioctl_outputDW openDW closeDW remove

ini_buffer EQU $DB 8 DUP (0) ; buffer para contener los caracteres

med_buffer EQU $ ; recibidos de 16 en 16.DB 8 DUP (0)

fin_buffer EQU $

puntero DW ini_buffer ; puntero al buffer

dirl DW 0 ; offset relativo del carácter deldirh DW 0 ; fichero o canal en proceso

; ------------ Rutina de estrategia.

estrategia PROC FARMOV CS:pcab_pet_desp,BXMOV CS:pcab_pet_segm,ESRET

estrategia ENDP

; ------------ Rutina de interrupción.

interrupción PROC FARXPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>LDS BX,CS:pcab_peticionMOV AL,[BX+2] ; AL = ordenCBW ; AX = orden (AH = 0)CMP AL,0FhJBE orden_ok ; orden correctaMOV AX,8102hJMP exit_interr

orden_ok: SHL AX,1 ; orden = orden * 2LEA SI,p_rutinasADD SI,AXXPUSH <BX,DS>CALL CS:[SI] ; ejecutar ordenXPOP <DS,BX>

exit_interr: MOV [BX+3],AX ; devolver palabra de estadoXPOP <ES,DS,BP,DI,SI,DX,CX,BX,AX>RET

interrupción ENDP

; ------------ 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.

input_status: ; conjunto de órdenes conoutput_status: ; tratamiento idénticoinput_flush:output_flush:ioctl_output:retorno_ok: MOV AX,100h ; no hay error, ignorar orden

RET

media_check:build_bpb:read: ; sólo soportada la salidaread_nowait:ioctl_input: MOV AX,8103h ; órdenes no soportadas

RET

open PROC ; inicio de transferencia:MOV CS:puntero,OFFSET ini_buffer ; inicializa punteroMOV CS:dirl,0MOV CS:dirh,0 ; offset relativo a ceroJMP retorno_ok

open ENDP

close PROC ; fin de transferencia:MOV AX,CSMOV DS,AXLEA CX,fin_bufferMOV BX,punteroSUB CX,BX ; CX caracteres faltanMOV AX,CX ; para un párrafoADD CX,CXADD CX,AX ; CX = CX * 3CMP BX,OFFSET med_bufferJA tam_okADD CX,2 ; dos espacios de separación

tam_ok: MOV AL,’ ’JCXZ esp_escr

escr_esp: CALL print_ALLOOP escr_esp

esp_escr: MOV BX,punterolimpia_buffer: CMP BX,OFFSET fin_buffer

JAE fin_buffMOV BYTE PTR [BX],’ ’INC BXJMP limpia_buffer

fin_buff: LEA BX,ini_buffer ; acabado el buffer:MOV puntero,BXCALL imprimir_asc ; imprimirlo en ASCIIJMP retorno_ok

close ENDP

remove PROCMOV AX,300h ; indicarRET ; «controlador ocupado»

remove ENDP

write PROCwrite_verify: MOV CX,[BX+12h] ; bytes a transferir

LES DI,[BX+0Eh] ; dirección inicialMOV AX,CSMOV DS,AX ; DS: -> HEX$

otro_car: MOV AL,ES:[DI]XPUSH <CX, DI>CALL procesa_AL ; procesar carácterXPOP <DI, CX>INC DI ; otro carácterLOOP otro_carJMP retorno_ok ; siempre Ok.

write ENDP

procesa_AL PROC ; permitido corromper registrosMOV BX,punteroMOV [BX],AL ; guardar carácterINC punteroCMP BX,OFFSET ini_bufferJNE no_direcc ; no es inicio de «párrafo»CALL imprimir_desp ; imprimir desplazamiento

no_direcc: CMP BX,OFFSET med_bufferJNE no_sep ; aún no alzanzada la mitadCALL imprimir_sep

no_sep: ADD dirl,1 ; INC no afecta al acarreoADC dirh,0 ; incrementada direcciónCALL print_8hex ; imprimir byte en hexadecimalMOV AL,’ ’CALL print_AL ; espacio separadorCMP puntero,OFFSET fin_bufferJB AL_procesadoLEA BX,ini_buffer ; acabado el buffer:MOV puntero,BXCALL imprimir_asc ; imprimirlo en ASCII

AL_procesado: RETprocesa_AL ENDP

imprimir_desp PROC ; imprimir desplazamientoPUSH AXMOV AL,’ ’CALL print_ALCALL print_AL ; dos espacios al principioMOV AX,dirhXCHG AH,ALCALL print_8hex ; byte alto palabra altaXCHG AH,ALCALL print_8hex ; byte bajo palabra altaMOV AX,dirlXCHG AH,ALCALL print_8hex ; byte alto palabra bajaXCHG AH,ALCALL print_8hex ; byte bajo palabra bajaMOV AL,’ ’CALL print_ALCALL print_AL ; dos espacios separadoresPOP AXRET

imprimir_desp ENDP

imprimir_sep PROC ; imprimir guión separadorPUSH AXMOV AL,’-’CALL print_ALMOV AL,’ ’CALL print_ALPOP AXRET

imprimir_sep ENDP

imprimir_asc PROC ; imprimir en ASCII 16 bytesMOV AL,’ ’ ; a partir de DS:BXCALL print_AL ; espacio separadorMOV CX,16

asc_dump: MOV AL,[BX]CMP AL,’ ’JAE asc_okMOV AL,’.’ ; no imprimir los de control

asc_ok: CALL print_ALINC BXLOOP asc_dumpMOV AL,0DhCALL print_AL ; retorno de carroMOV AL,0AhCALL print_AL ; salto de líneaRET

imprimir_asc ENDP

print_8hex PROC ; imprimir byte hexad. en ALPUSH AXMOV AH,ALMOV CL,4SHR AL,CLCALL print_4hex

Page 214: PCA, PS2 ,IBM y AT

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

MOV AL,AHAND AL,00001111bCALL print_4hexPOP AXRET

print_8hex ENDP

print_4hex PROC ; imprimir nibble hexad. en ALPUSH AXADD AL,’0’CMP AL,’9’JBE hex_ALADD AL,’A’-’9’-1

hex_AL: CALL print_ALPOP AXRET

print_4hex ENDP

print_AL PROC ; imprimir ASCII en ALINT 29hRET

print_AL ENDP

; ************ Instalación invocada desde el CONFIG.SYS

init PROCPUSH BXMOV AH,30hINT 21h ; obtener versión del DOSPOP BXCMP AL,3JAE dos_okMOV WORD PTR [BX+0Eh],0 ; OFFSET 0: terminar

MOV [BX+10h],CS ; sin quedar residenteLEA BX,mal_dos_txtCALL printMOV AX,100hRET

dos_ok: LEA AX,retorno_okMOV CS:p_rutinas,AX ; anular rutina INITMOV WORD PTR [BX+0Eh],OFFSET initMOV [BX+10h],CS ; indicado área residenteLEA BX,instalado_txtCALL printMOV AX,100h ; instalación siempre Ok.RET

init ENDP

print PROC ; imprimir cadena en CS:BXMOV DL,CS:[BX]AND DL,DLJZ fin_printMOV AH,2PUSH BXINT 21hPOP BXINC BXJMP print

fin_print: RETprint ENDP

instalado_txt DB 13,10,"Dispositivo HEX$ instalado.",13,10,0mal_dos_txt DB 13,10,"Error: HEX$ necesita DOS 3.0 o superior."

DB 13,10,0

HEXSEG ENDSEND

11.7. - EJEMPLO DE CONTROLADOR DE DISPOSITIVO DE BLOQUES.

11.7.1. - DISCO VIRTUAL TURBODSK: CARACTERÍSTICAS.

El disco virtual propuesto no es el clásico minidisco de ejemplo, de un segmento de 64 Kb. Por elcontrario, se ha preferido crear un disco completo que pueda competir al mismo nivel que los del sistema,con objeto de recoger todas las circunstancias posibles que implica su desarrollo. Al final, este disco ha sidodotado de varias comodidades adicionales no disponibles en los discos del DOS. Por un lado, es posiblemodificar su tamaño una vez que ha sido instalado, sin necesidad de arrancar de nuevo el ordenador. Estaasignación dinámica de la memoria significa que, en la práctica, es factible tener instalado el controlador sinreservar memoria: cuando es preciso utilizar el disco, se le formatea; después de ser usado, se puededesasignar la memoria extendida, expandida o convencional que ocupaba. Esto último es más querecomendable si, por ejemplo, se va a ejecutar WINDOWS a continuación y ya no se necesita el disco virtual.

Otra ventaja es que es mucho más flexible que los discos virtuales que acompañan al sistemaoperativo, permitiendo definir con mayor libertad los parámetros e incluyendo uno nuevo (el tamaño decluster). Los usuarios avanzados nunca estuvieron contentos con los discos del sistema que abusabandemasiado del ajuste de parámetros. Aunque una elección torpe de parámetros de TURBODSK puede crearun disco prácticamente inútil, e incluso incompatible con algunas versiones del DOS, también es cierto quelos usuarios con menos conocimientos pueden dejar a éste que elija los parámetros por ellos, con excepcióndel tamaño del disco. Los usuarios más informados, en cambio, no tendrán ahora trabas.

Sin embargo, la pretensión inicial de hacer TURBODSK más rápido que los discos del sistema, dela que hereda su peculiar nombre, ha tenido que enfrentarse a la elevada eficiencia de RAMDRIVE. Lasúltimas versiones de este disco ya apuran bastante el rendimiento del sistema, por lo que superarle sólo hasido 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 dememoria. La velocidad es sin duda el factor más importante de un disco virtual, con mucho, por lo que nose deben ahorrar esfuerzos para conseguirla.

A continuación se resumen las características de TURBODSK, comparándolo con los discos virtualesdel sistema: RAMDRIVE en representación del MS-DOS 5.0 (aunque se incluye una versión más recienteque viene con WINDOWS 3.1) y el VDISK de DR-DOS 6.0. Como puede observarse, la única característicaque TURBODSK no presenta es el soporte de memoria extendida vía INT 15h de VDISK, tampocoimplementado ya en RAMDRIVE. El motivo es simplificar el programa, ya que en la actualidad es difícilencontrar máquinas con memoria extendida que no tengan instalada la especificación XMS que implementaHIMEM.SYS o algunas versiones del EMM386.

Page 215: PCA, PS2 ,IBM y AT

215CONTROLADORES DE DISPOSITIVOS

CARACTERÍSTICAS

RAMDRIVE VDISK TURBODSK(WINDOWS 3.1) (DR-DOS 6.0) v2.3

Capacidad máxima: 32 Mb 32 Mb 64 MbSoporte de memoria convencional: Sí Sí SíSoporte de memoria EMS: Sí Sí SíSoporte de memoria extendida INT 15h: No Sí NoSoporte de memoria extendida XMS: Sí No SíTamaño de sector soportado: 128-1024 128-512 32-2048Ficheros en directorio raíz: 4-1024 4-512 1-65534Asignación dinámica de la memoria: No No SíTamaño de cluster definible: No No SíMemoria convencional consumida (MS-DOS 5.0): 1184-1232 2096-2608 448-624

Para calcular la velocidad de los discos virtuales se ha utilizado el programa KBSEC.C listado másabajo. Los resultados de KBSEC pueden variar espectacularmente en función del fabricante del controladorde memoria o del sistema operativo. Este programa de test es útil para analizar el rendimiento de un discovirtual en fase de desarrollo o para que el usuario elija la memoria más rápida según la configuración de suequipo. Dicho programa bloquea todas las interrupciones excepto IRQ 0 (INT 8), la cual a su vez desvía conobjeto de aumentar la precisión del cálculo; por ello es exclusivo para la comprobación de discos virtualesy no flexibles. Debe ser ejecutado sin tener instalado ningún caché. KBSEC fuerza el buffer de transferenciaa una dirección de memoria determinada, con objeto de no depender aleatoriamente de la velocidad disparde la memoria y los controladores XMS/EMS en función del segmento que sea utilizado. La fiabilidad deKBSEC está avalada por el hecho de que siempre da exactamente el mismo resultado al ser ejecutado en lasmismas condiciones. Para hacerse una idea de la potencia de los discos virtuales, conviene tener en cuentaque un disco fijo con 19 ms de tiempo de acceso e interface IDE, en un 386-25 puede alcanzar una velocidadde transferencia de casi un megabyte, 17 veces menos que la mejor configuración de disco virtual -queademás posee un tiempo de acceso prácticamente nulo- en esa misma máquina.

Velocidad del disco bajo MS-DOS 5.0, calculada por KBSEC, con los buffers queestablece el DOS por defecto (aunque esto no influye en KBSEC) y con sólo KEYB yDOSKEY instalados. Para evaluar la memoria convencional no estaba instalado ningúncontrolador de memoria; para la memoria XMS estaba instalado sólo HIMEM.SYS y parala EMS, tanto HIMEM.SYS como EMM386.EXE a la vez (los resultados varían bastanteen función de la gestión de memoria del sistema). Datos en Kb/segundo.

VDISK RAMDRIVE TURBODSK8088-8 MHz:

- Memoria convencional: 563 573 573286-12 Mhz (sin estados de espera):

- Memoria extendida/XMS: 1980 4253 4253- Memoria convencional: 4169 4368 4368

386-25 MHz (sin caché):- Memoria extendida/XMS: 6838 17105 17095- Memoria expandida EMS: 1261 8308 14937- Memoria convencional: 7297 6525 14843

486-25 MHz sin caché externa:- Memoria extendida/XMS: 7370 10278 10278- Memoria expandida EMS: 2533 7484 9631- Memoria convencional: 8256 8454 11664

/********************************************************************** ** KBSEC 1.2 - Utility to calc with high precision the data transfer ** rate (the read data transfer read) in a ramdisk. ** ** (C) 1992-1995 Ciriaco García de Celis ** ** - Do not run this program with a cache program loaded; compile ** it in LARGE memory model with «Test stack overflow» option ** disabled. Use Borland C. This program has english messages. ** **********************************************************************/

#include <stdio.h>#include <dos.h>#include <conio.h>#include <stdlib.h>

#define MAXBUF 64512L /* 63 Kb (no sobrepasar 64 Kb en un acceso) */#define TIEMPO 110L /* 6 segundos * 18,2 ≈ 110 tics (error < 1%) */#define TM 18.2 /* cadencia de interrupciones del temporizador */#define HORA_BIOS MK_FP(0x40, 0x6c) /* variable de hora del BIOS */

unsigned long ti, vueltas, far *cbios;unsigned segmento, tamsect, far *pantalla;unsigned char far *sbuffer;static unsigned tiempo;int unidad;void interrupt (*viejaIRQ0)();

void interrupt nuevaIRQ0 () /* rutina ejecutada cada 55 ms */

tiempo++; /* incrementar nuestro contador de hora */outportb (0x20,0x20); /* EOI al controlador de interrupciones */

void prep_hw (void)

viejaIRQ0=getvect(8); /* preservar vector de int. periódica */setvect (8, nuevaIRQ0); /* instalar nueva rutina de control */outportb (0x21, 0xfe); /* inhibir todas las int. salvo timer */

void rest_hw (unsigned long tiempo_transcurrido_con_reloj_parado)

Page 216: PCA, PS2 ,IBM y AT

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

outportb (0x21, 0); /* autorizar todas las interrupciones */setvect (8, viejaIRQ0); /* restaurar vector de int. periódica */cbios=HORA_BIOS; *cbios+=tiempo_transcurrido_con_reloj_parado;

void main(int argc, char **argv)if (allocmem ((unsigned) ((MAXBUF+0x1800) >> 4), &segmento)!=-1) printf("\nInsufficient memory.\n"); exit(255);

sbuffer=MK_FP((segmento+0x100) & 0xff00 | 0x80, 0); /* 2Kb+n*4Kb */

if (argc<2) printf("\nChoose the drive to test.\n"); exit(1); unidad=(argv[1][0] | 0x20) - ’a’;if ((unidad<2) || (absread (unidad, 1, 0L, sbuffer)!=0)) printf ("\nChoose drive C or above with less than 32 Mb.\n");exit (2);

tamsect = sbuffer[11] | (sbuffer[12]<<8);ti = (long) tamsect * ((sbuffer[0x14] << 8) | sbuffer[0x13]);if ((ti < MAXBUF) || (ti > 33554431L))

printf ("\nNeeds a disk from %2.0f Kb to 32 Mb\n", MAXBUF/1024.0);exit (3);

textmode (C80); clrscr();printf ("\nComputing speed (wait %2.0f sec.)...", TIEMPO/TM);

pantalla=MK_FP((peekb(0x40,0x49)==7 ? 0xB000:0xB800), 0x140);

prep_hw(); ti=tiempo=vueltas=0;while (ti==tiempo); /* esperar pulso del reloj */ ti+=TIEMPO;

while (ti >= tiempo)if (absread (unidad, MAXBUF / tamsect, 0L, sbuffer)!=0)

rest_hw(ti-tiempo); printf ("\nError reading the disk.\n");exit(254);

else if (!(vueltas++ & 7)) *pantalla++=0xf07; /* "imprimir" */

rest_hw(TIEMPO); clrscr();

printf("\nKBSEC 1.2: Effective data transfer rate on drive %c:\%6.0f Kb/sec.\n", unidad+’A’,MAXBUF/1024.0*vueltas/(TIEMPO/TM));

11.7.2. - ENSAMBLANDO TURBODSK.

El listado fuente de TURBODSK consta de un único fichero que ha de ser ensamblado sindemasiados parámetros especiales. Este programa puede ser perfectamente ensamblado de manera indistintapor MASM 6.X (con el parámetro de compatibilidad con versiones anteriores) o por TASM, aunquepreferiblemente por el segundo. Versiones de MASM anteriores a la citada no tienen potencia suficiente,básicamente porque no permiten emplear la directiva .386 dentro de los segmentos. Con TASM convieneemplear la opción /m5 para que el ensamblador ejecute todas las pasadas necesarias para optimizar el códigoal máximo (como mínimo habría que solicitar 2, en cualquier caso, para que no emita errores).

11.7.3. - ANÁLISIS DETALLADO DEL LISTADO DE TURBODSK.

El listado completo de TURBODSK puede consultarse al final de este apartado. Se describirán pasoa paso todas las peculiaridades del programa, por lo que el listado debería ser comprensible prácticamenteal 100%. A lo largo de la explicación aparecen numerosas alusiones al comportamiento de RAMDRIVE yVDISK. Por supuesto, los detalles referidos a RAMDRIVE o VDISK se refieren exclusivamente a la versiónde los mismos que acompaña a Windows 3.1 y a DR-DOS 6.0, respectivamente, no siendo necesariamenteaplicable a otras anteriores o futuras de dichos programas. Evidentemente, la información sobre ambos noha sido obtenida escribiendo al fabricante para solicitarle el listado fuente, por lo que es un tanto difusa eincompleta, aunque sí suficiente para complementar la explicación de TURBODSK y dar una perspectiva másamplia.

LA CABECERA DE TURBODSK

El inicio de TURBODSK es el clásico de todos los controladores de dispositivo de bloques. Lapalabra de atributos es idéntica a la de VDISK o RAMDRIVE. Hay que hacer aquí una breve mención al bit13 que indica si el dispositivo es de tipo IBM o no: la verdad es que en nuestro caso daría igual elegir untipo que otro (la diferencia es que en los de tipo IBM el DOS accede a la FAT antes que al propio sector dearranque para verificar el tipo de disco). Finalmente se optó por seguir la corriente de los discos del DOS,aunque existen por ahí discos virtuales de tipo «no-IBM». En principio, hoy por hoy da lo mismo cómo estéeste bit de la palabra de atributos, tan sólo existe una sutil diferencia en la orden BUILD BPB.

A continuación vienen las variables de TURBODSK, la mayoría de las cuales son intuitivas. Sinembargo, las dos primeras son algo especiales. La primera (cs_tdsk) está destinada a almacenar el valor delregistro CS, que indica dónde reside el disco virtual. Aunque en principio puede parecer redundante, estaoperación es necesaria para lograr la compatibilidad con algunos gestores de memoria, como QEMM, quepueden cargar la cabecera del dispositivo en memoria convencional y el resto del mismo en la superior: anosotros nos interesa conocer la dirección donde reside todo el dispositivo, con objeto de acceder a él paraulteriores modificaciones de sus condiciones de operación. Cuando se utiliza el LOADHI de QEMM, eldispositivo es cargado en memoria superior, pero después QEMM se encarga de copiar la cabecera enmemoria convencional, pasando la cadena de controladores de dispositivo del DOS por dicha memoria. Comonosotros buscaremos a un posible TURBODSK residente siguiendo esa cadena, gracias a la variable cs_tdsk

Page 217: PCA, PS2 ,IBM y AT

217CONTROLADORES DE DISPOSITIVOS

podemos saber la dirección real del disco virtual. QEMM crea además unas falsas rutinas de estrategia einterrupción en memoria convencional que luego llaman a las de la memoria superior. Sin embargo, esto noes relevante para nosotros. Por fortuna, QEMM 6.0 también soporta el DEVICEHIGH del DOS, en cuyo casola totalidad del dispositivo es cargado en memoria superior; sin embargo, no está de más tomar precaucionespara los casos en que no sea así.

La segunda variable es id_tdsk y su utilidad es fundamental: sirve para certificar que el controladorde dispositivo es TURBODSK, indicando además la versión. Esta variable está ubicada en los primeros 18bytes de la cabecera, que son los que QEMM copia en memoria convencional. Si algún gestor de memoriaextraño realizara la misma maniobra de QEMM y copiase menos de 18 bytes en memoria convencional, nopasaría nada: TURBODSK sería incapaz de hallarse a sí mismo residente en la memoria superior, por lo queno habría riesgo alguno de provocar un desastre. Por fortuna, estas complicadas argucias de los controladoresde memoria tienden a desaparecer desde la aparición del DOS 5.0 que, de alguna manera, ha normalizadoel uso de la memoria superior.

Existe otra variable importante, tipo_soporte, que indica en todo momento el estado del disco. Engeneral, las variables más importantes de TURBODSK han sido agrupadas al principio y el autor delprograma se ha comprometido a no moverlas en futuras versiones. Esto significa que otros programas podrándetectar la presencia de TURBODSK e influir en sus condiciones de operación.

Más adelante hay otras variables internas al programa: por un lado, la tabla de saltos para las rutinasque controlan el dispositivo; por otro, un BPB con información válida (si no fuera correcto, el DOS se podríaestrellar al cargar el dispositivo desde el CONFIG). Este BPB será modificado cuando se defina el disco, sedefina éste desde el CONFIG o no (esto último es lo más normal y recomendable). En el BPB solo se hancompletado los campos correspondientes al DOS 2.x; la razón es que los demás no son necesarios ni siquierapara el DOS 5.0: la información adicional de las últimas versiones de los BPB es empleada por las rutinasde más bajo nivel del sistema operativo, aquellas que se relacionan con la BIOS y el hardware; sin embargo,estas nuevas variables no son relevantes para la interfaz del DOS con el controlador de dispositivo.

LAS RUTINAS QUE CONTROLAN EL DISPOSITIVO.

Veremos ahora las principales rutinas de TURBODSK. Para empezar, la rutina de estrategia deTURBODSK no merece ningún comentario, pero sí la de interrupción. Es bastante parecida a la de los discosdel sistema, pero con una diferencia: si el disco no está aún preparado y no se ha reservado memoria paraél (esto sucede con la variable tipo_soporte igual a cero) hay que rechazar todos los accesos al discodevolviendo un código de unidad no preparada, algo así como decir que no hay disquete dentro de ladisquetera virtual. En cualquier otro caso, y valiéndose de la tabla de saltos, llamamos a la subrutinaadecuada que gestiona cada orden. Estas subrutinas devuelven en AX la palabra de estado que hay quedevolver al sistema, por lo que al final se realiza esta operación. 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), seindica al DOS que se han transferido 0 sectores; de lo contrario, esta variable de la cabecera de peticiónqueda como estaba al principio, indicando que se han transferido tantos sectores como fueron solicitados.

Las órdenes READ NOWAIT, INPUT STATUS, INPUT FLUSH, OUTPUT STATUS, OUTPUTFLUSH, IOCTL OUTPUT, OPEN y CLOSE no están realmente soportadas. Sin embargo, si el DOS lasinvoca, TURBODSK se limita a terminar como si nada hubiera sucedido, devolviendo una palabra de estado100h que indica función terminada. A la orden IOCTL INPUT, en cambio, se responde con un error (ordenno soportada) ya que TURBODSK no está preparado para enviar cadenas IOCTL a nadie (una cosa es nohacer caso de las que envían, ¡pero cuando además las solicitan!); en general, el comportamiento hasta elmomento es 100% idéntico al de RAMDRIVE.

Sin embargo, la orden MEDIA CHECK es totalmente diferente de la de los discos virtuales del DOS.A la pregunta de ¿ha habido cambio de disco?, tanto VDISK como RAMDRIVE responden siempre que no.En cambio, TURBODSK puede haber sido modificado por el usuario, debido a la asignación dinámica de

Page 218: PCA, PS2 ,IBM y AT

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

memoria que soporta. En estos casos, el programa que formatea el disco virtual (el propio TURBODSKcuando el usuario define un disco) colocará la variable cambiado a un valor 0FFh. Este valor es el que sedevolverá la primera vez al DOS, indicando que se ha producido un cambio de disco. Las siguientes veces,TURBODSK no volverá a cambiar (no hasta otro formateo), motivo por el cual la variable se redefine a 1.

En el momento en que el disco es cambiado, el DOS ejecuta la orden BUILD BPB, con la que sele suministra la dirección del nuevo BPB (la misma de siempre, pero con un BPB actualizado).

La orden REMOVE se limita a devolver una condición de controlador ocupado. No estaba muy claroqué había que hacer con ella, por lo que se optó por imitar el funcionamiento de RAMDRIVE. Lo cierto esque hay órdenes que casi nunca serán empleadas, o que no tiene sentido que sean utilizadas, pero convieneconsiderarlas en todo caso.

Las últimas órdenes que implementa TURBODSK son las de lectura y escritura o escritura converificación. En estas órdenes simplemente se inicializa un flag (el registro BP) que indica si se trata de leero escribir: si BP es 0 es una escritura, si es 1 una lectura. Finalmente, se salta a la rutina Init_io que seencarga de preparar los registros para la lectura o escritura, consultando el encabezamiento de petición desolicitud para estas órdenes.

Más o menos mezclada con estas órdenes está la rutina que gestiona la interrupción 19h. Estainterrupción es necesario desviarla para mejorar la convivencia con algunos entornos multitarea basados enel modo virtual del 386. En principio, cuando una tarea virtual es cancelada (debido a un CTRL-ALT-DELo a un cuelgue de la misma) el sistema operativo debería desasignar todos los recursos ligados a ella, incluidala memoria expandida o extendida que tuviera a su disposición. Sin embargo, parece que existen entornosno muy eficientes en los que al anular una tarea no se recupera la memoria que ocupaba. Por tanto, es deberde la propia tarea, antes de morir, el devolver la memoria a los correspondientes controladores. Lainterrupción 19h se ejecuta en estos momentos críticos, por lo que TURBODSK aprovecha para liberar lamemoria EMS/XMS ocupada y, tras restaurar el vector previo de INT 19h (para mejorar la compatibilidad)continúa el flujo normal de la INT 19h. La mayoría de los discos virtuales no desvían la INT 19h; sinembargo, RAMDRIVE sí y TURBODSK no quería ser menos... aunque, en el caso de utilizar memoriaconvencional no se realiza ninguna tarea (RAMDRIVE ejecuta una misteriosa y complicada rutina).

La rutina Init_io se ejecuta inmediatamente antes de una lectura o escritura en el disco, preparandolos registros. Se controla aquí que el primer y último sector a ser accedido estén dentro del disco: en casocontrario se devuelve un error de sector no encontrado. En realidad, TURBODSK no comprueba si el primersector está en el disco, para ahorrar memoria; al contrario que la mayoría de los discos virtuales. La razónes que si el último sector está dentro del disco ¡como no lo va a estar también el primero!. También hay quetener en cuenta la histórica leyenda de los 64 Kb. En concreto, el problema reside en la dirección dondedepositar o leer los datos. Pongamos por ejemplo que un programa pretende leer del disco virtual 48 Kb dedatos en la dirección DS:A000h. En principio, el manual de referencia para programadores de Microsoft diceque el dispositivo solo está obligado a transferir cuanto pueda sin cambiar de segmento. Sin embargo, elRAMDRIVE de Microsoft no considera esta circunstancia, por lo que si un programa intenta hacer un accesoilegal de este tipo se corromperá también una parte indeseada del segmento de datos, ya que al llegar al finalde un segmento se comienza por el principio del mismo otra vez (esto no es así en el caso de emplearmemoria extendida, pero sí en la convencional y expandida). En TURBODSK se prefirió limitar latransferencia al máximo posible antes de que se desborde el segmento: hay que tener en cuenta que undesbordamiento en el segmento de datos puede llegar a afectar al de código, con todo lo que ello implica.Cierto es que un acceso incorrecto a disco es una circunstancia crítica de la que no se puede responsabilizaral mismo, pero a mi juicio es mejor no poner las cosas todavía peor.

Otro asunto es controlar el tamaño absoluto del área a transferir: en ningún caso debe rebasar los 64Kb, aunque no está muy claro si los puede alcanzar o no. RAMDRIVE opera con palabras de 16 bits,permitiendo un máximo de 8000h (exactamente 64 Kb), excepto en el caso de trabajar con memoriaextendida: al pasar el nº de palabras a bytes, unidad de medida del controlador XMS, el 8000h se convierte

Page 219: PCA, PS2 ,IBM y AT

219CONTROLADORES DE DISPOSITIVOS

en 0 (se desborda el registro de 16 bits al multiplicar por 2): con este tipo de memoria RAMDRIVE nosoporta transferencias de 64 Kb exactos (por ello, KBSEC.C emplea un buffer de 63 y no de 64 Kb). EnTURBODSK se decidió transferir 64 Kb inclusive como límite máximo, en todos los casos. En memoriaexpandida y convencional, por otro lado, existe el riesgo de que el offset del buffer sea impar y, debido altamaño del mismo, se produzca un acceso de 16 bits en la dirección 0FFFFh, ilegal en 286 y superiores. Estoprovoca un mensaje fatal del controlador de memoria, preguntando si se desea seguir adelante o reinicializarel sistema (QEMM386), o simplemente se cuelga el ordenador (con el EMM386 del MS-DOS 5.0 o enmáquinas 286). Por ejemplo, pruebe el lector a leer justo 32 Kb en un buffer que comience en 8001h conRAMDRIVE en memoria EMS: RAMDRIVE no pierde el tiempo comprobando estas circunstancias críticas,aunque VDISK parece que sí. En TURBODSK se optó también por ser tolerante a los fallos del programaque accede al disco: además de limitar el acceso máximo a 64 Kbytes, y de transferir sólo lo que se puedaantes del desbordamiento del segmento, puede que todavía se transfiera entre uno y tres bytes menos, ya quese redondea por truncamiento la cuenta de palabras que faltan para el final del segmento para evitar undireccionamiento ilegal en el offset 0FFFFh (estas circunstancias críticas deben evaluarse utilizando lasinterrupciones 25h/26h, ya que al abrir ficheros ordinarios el DOS es siempre suficientemente cauto para noponer a prueba la tolerancia a fallos de las unidades de disco).

Inmediatamente después de la rutina Init_io de TURBODSK está colocada la que gestiona el discoen memoria expandida. No existe ningún nexo de unión y ambas se ejecutan secuencialmente. Al final deInit_io hay una instrucción para borrar el acarreo. Esto es así porque la rutina que gestiona el disco puedeser accedida, además de desde Init_io, desde el gestor de la interrupción 19h. El acarreo sirve aquí paradiscernir si estamos ante una operación normal de disco o ante una inicialización del sistema. En el caso deuna operación de disco, BP indica además si es lectura o escritura. TURBODSK soporta también memoriaextendida XMS y convencional: cuando se utilizan estas memorias, la rutina correspondiente sustituye a lade memoria EMS por el simple y efectivo procedimiento de copiarla encima. Esta técnica, que horrorizaráa más de un programador, es frecuente en la programación de sistemas bajo MS-DOS. De esta manera,TURBODSK y RAMDRIVE (que también comete esta inmoralidad) economizan memoria, ya que solo quedaresidente el código necesario. El hecho de que por defecto esté colocada la rutina de memoria expandida esdebido a que es, con diferencia, la más larga de todas y así siempre queda hueco para copiar encima las otras.A la hora de terminar residente, si la máquina tiene memoria extendida y no se indica /A, no se dejaráespacio más que para las rutinas de memoria extendida y convencional, para economizar más memoria.

ANÁLISIS DE LAS RUTINAS DE GESTIÓN DE MEMORIA.

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, así como para liberarla memoria asignada en respuesta a una interrupción 19h. Retornan devolviendo en AX el resultado de laoperación, que será normalmente exitoso. En caso de fallo de algún controlador de memoria, devolverían uncódigo de error de anomalía general.

Trabajando con memoria EMS.

La rutina más compleja es la que gestiona la memoria expandida EMS. Además, un disco virtual quese 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. Este aspecto no es tenido en cuenta porningún disco virtual de dominio público con soporte de memoria EMS que yo conozca, aunque sí por los delDOS; a esto se debe que algunas aplicaciones que trabajan con memoria expandida adviertan que puedenoperar mal con ciertos discos virtuales.

En el caso de VDISK, el algoritmo es muy poco eficiente: este disco virtual realiza un bucle, con unavuelta para cada sector, donde hace todas estas tareas: preservar el contexto del mapa de páginas, calcularlas direcciones, transferir a un buffer auxiliar, recuperar el contexto del mapa de páginas y transferir delbuffer auxiliar hacia donde solicita el DOS. Ello significa que, para transferir 32 Kb en sectores de 0,5 Kb,se salva y restaura ¡64 veces! el contexto del mapa de páginas. No digamos si los sectores son más pequeños,

Page 220: PCA, PS2 ,IBM y AT

220 EL UNIVERSO DIGITAL DEL IBM PC, 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 lasdirecciones. Cierto es que salvar el contexto del mapa de páginas y volverlo a restaurar es necesario, de caraa que el disco virtual (un programa residente a todos los efectos) no afecte al programa de usuario que se estáejecutando, por si éste utiliza también memoria expandida. La pregunta es, ¿por qué no sacaron los autoresde VDISK esas operaciones fuera del bucle?, y ¿por qué utilizar un buffer auxiliar?. Lógicamente hay unarespuesta. Piense el lector qué sucederá si el buffer donde leer o escribir que suministra el programa principal,está en memoria expandida: ¡se solapa con el disco virtual!. Para solucionar este posible solapamiento,VDISK se ve obligado a realizar esas operaciones con objeto de permitir una transferencia de la memoriaexpandida a la propia memoria expandida, a través de un buffer auxiliar. Este algoritmo provoca que VDISKsea prácticamente tan lento como un buen disco duro cuando trabaja con memoria expandida y sectores de512 bytes, ¡y bastante más lento si se utilizan los sectores de 128 bytes que suele establecer por defecto!.Además, el buffer del tamaño de un sector incrementa el consumo de memoria en 512 bytes.

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.

- - - - - - - - - - - - - - - - - - - - - - - -M

Página 3 E Página 3M --O 16

Página 2 R Página 2 KbI --A

Página 1 Página 1E caso BM

Página 0 S Página 0- - - - - - - - - - - - - - - - - - - - - - - -

caso A

Resulta evidente, en el caso A, que si el buffer donde leer/escribir los datos comienza pordebajo de la dirección marcada por la flecha (o justo en esa dirección) no colisionará con lapágina 0, ya que no excede de 16 Kb de longitud. Como al convertir la dirección segmentada apárrafos se pierde precisión, TURBODSK se asegura que la dirección esté 401h párrafos (16 Kbmás 1 párrafo) por debajo del inicio de la página 0.

En el caso B, el buffer está en memoria expandida pero comienza justo detrás de la página 0y, por lo que no hay colisión con esta página. Una vez más, por razones de redondeo, TURBODSKcomprueba que el buffer comience al menos 401h párrafos por encima del inicio de la página 0.En realidad, bastaría con comprobar si dista al menos 400h bytes, ya que el redondeo alconvertir la dirección segmentada se hace truncando.

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. ¿Qué sucede si hay colisión?. Pues queno se puede emplear la página 0, que se solapa con el buffer. En ese caso, bastaría con elegirla página 2 ya que si el buffer empieza justo donde apunta la flecha del caso B, como su tamañoes de no más de 16 Kb, no puede invadir... sí, ¡sí puede invadir la página 2, aunque sólo unpárrafo! (no olvidar que si empieza por encima de la flecha no colisiona con la página 0). Portanto, tenemos que utilizar la página 3. En general, en un sistema con memoria EMS 4.0 dondelas páginas pueden ser definidas por el usuario en la dirección que desee (parámetros /Pn= delEMM386 del MS-DOS 5.0), basta con asegurarse que la página alternativa a la 0, para los casosen que hay colisión, está alejada al menos 48 Kb de la página 0 (esto es, que entre ambaspáginas hay una distancia absoluta de 32 Kb).

Se comprende ahora la necesidad de restaurar el contexto del mapa de páginas antes de pasarutilizar una nueva página para las transferencias: el hecho de necesitar una nueva página vienedeterminado porque la hasta entonces utilizada se solapa con el buffer ¡y es preciso restaurarel contenido del buffer!. Además, hay que volver a salvar el contexto de manera inmediata paraque quede salvado para otra ocasión (o para cuando se acabe el acceso al disco y haya de serrestaurado).

En principio, no se recomienda a nadie intentar comprender la rutina de TURBODSK para lamemoria EMS (Procesa_ems): dada su complejidad, es más fácil para un programador desarrollar la suyapropia que intentar entender la actual: fundamentalmente, porque los escasos 247 bytes que ocupa evidencianen qué medida el autor se ha decantado por la eficiencia en detrimento de la claridad al diseñarla. Sinembargo, las pautas que se darán pueden ser útiles. TURBODSK utiliza una técnica totalmente diferente a

Page 221: PCA, PS2 ,IBM y AT

221CONTROLADORES DE DISPOSITIVOS

la de VDISK, para evitar el buffer auxiliar. En principio, debido a que TURBODSK transfiere bloques dehasta 16 Kb en cada iteración, el bucle no dará nunca más de 5 vueltas (un bloque de disco de 64 Kb puedeestar comprendido en 5 páginas EMS). Al principio se salva una sola vez el contexto de la memoriaexpandida, antes de entrar en el bucle, volviéndose a restaurar al final del todo, también una sola vez. No serealizará esto más veces si no hay solapamientos. Por otra parte, como sólo se utiliza una página de memoriaexpandida a un tiempo, TURBODSK elige inteligentemente una que no colisione con la del buffer delprograma principal a donde enviar/recibir los datos. En el caso en que haya colisión con la página 0,TURBODSK restaura el contexto y lo vuelve a salvar, con objeto de devolver la memoria expandida a lasituación inicial y mantener la primera copia que se hizo del contexto; además, elige otra página que disteal menos 32 Kb de la página 0 (bastaría con 16 Kb, pero se hace así para evitar problemas en los redondeossi los buffers no empiezan en posiciones alineadas a párrafo). El esquema gráfico lo explica con mayorclaridad.

Tras la transferencia, si había habido colisión se vuelve de nuevo a restaurar y preservar el contexto,para volver al estado previo a la entrada en el bucle. Estas operaciones hacen que TURBODSK sealigeramente más lento cuando el buffer de lectura/escritura está en memoria expandida, pero probablementela diferencia no llegue al 1% al caso en que no hay solapamientos. El funcionamiento general consiste en irmapeando las páginas de memoria expandida una a una, considerando las tres posibilidades: al principio,puede ser necesario transferir un fragmento del final de la primera página mapeada; después, puede serpreciso transferir algunas páginas enteras y, por último, una parte inicial de la última página. Esto significaque TURBODSK sólo mapea (y una sola vez) las páginas estrictamente necesarias para la transferencia;además, no transfiere sector a sector sino el mayor número posible que pueda ser transferido de una sola vezy se evita la necesidad de hacer doble transferencia (con el consiguiente ahorro, además, del buffer de 512bytes). Este algoritmo permite que TURBODSK sea tan rápido como cabría esperar de un disco virtual,incluso al trabajar con memoria EMS. De hecho, al transferir 32 bits en los 386 y superiores, la velocidadque desarrolla en memoria EMS no se queda muy por detrás de la que consigue el controlador de memoriaXMS en estas máquinas. El inconveniente de la rutina de gestión de memoria EMS en TURBODSK es, comose dijo antes, la complejidad: está optimizada para reducir en lo posible el tamaño, por lo que puede resultarde difícil comprensión. Por ejemplo, posee una subrutina encargada de acceder al controlador de memoriaque, en caso de fallo, altera la pila para retornar directamente al programa principal y no al procedimientoque la llamó. Estas maniobras que aumentan la complejidad y dificultan posteriores modificaciones delcódigo, están bastante documentadas en el listado, por lo que no habrá más referencias a ellas. Hay quereconocer que por 30 ó 40 bytes más la rutina podría haber sido todo un ejemplo de programaciónestructurada, pero cuando se escribió TURBODSK, entre los principales objetivos estaba reducir el consumode memoria. Esta rutina es además la misma para leer que para escribir: en el caso de la escritura, se limitasimplemente a intercambiar la pareja DS:SI con la ES:DI antes y después de realizar la transferencia.

RAMDRIVE, por su parte, cuenta con un algoritmo con un rendimiento similar al de TURBODSK,pero totalmente distinto. La principal diferencia es que RAMDRIVE mapea varias páginas consecutivas, loque le permitiría en ocasiones ser levemente más rápido que TURBODSK; sin embargo, como no transfierecon 32 bits, en los 386 y superiores es notablemente más lento que TURBODSK. RAMDRIVE necesita quelas páginas de memoria expandida sean contiguas (podrían no serlo en EMS 4.0), emitiendo un error deinstalación en caso contrario; el método de TURBODSK es algo más tolerante: no necesita que seanestrictamente contiguas, basta solo con que entre las 4 primeras haya alguna que diste de la primera al menos32 Kb, la cual asigna dinámicamente.

Para terminar con el análisis de la gestión de este tipo de memoria, hablaremos algo acerca de lamanera de comunicarse con el controlador de memoria. En principio, lo más normal es cargar los registrose invocar la INT 67h, analizando el valor en AH para determinar si ha habido error. Sin embargo, se haconstatado que RAMDRIVE, ante un código de error 82h (EMM ocupado) vuelve a reintentar de maneraindefinida la operación, excepto en el caso de la función 40h (obtener el estado del gestor) utilizada en lainstalación, en la que hay sólo 32768 intentos. Este comportamiento parece estar destinado a mejorar laconvivencia con entornos multitarea, en los que en un momento dado el controlador de memoria puede estarocupado pero algo más tarde puede responder. Por tanto, también se incorporó esta técnica a TURBODSK.

Page 222: PCA, PS2 ,IBM y AT

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

Un último aspecto a considerar está relacionado con el uso de instrucciones de 32 bits en las rutinasde TURBODSK: en principio han sido cuidadosamente elegidas con el objetivo de economizar memoria. Porello, la instrucción PUSHAD (equivalente a PUSHA, pero con los registros de 32 bits) venía muy bien paraapilar de una sola vez todos los registros de propósito general. Sin embargo, la correspondiente instrucciónPOPAD no opera correctamente, por desgracia, en la mayoría de los 386, aunque el fallo fue corregido enlas últimas versiones de este procesador (los 386 de AMD también lo tienen, ¡qué curioso!). Se trata de unfallo conocido por los fabricantes de software de sistemas, pero poco divulgado, aunque tampoco es muygrave: básicamente, el problema reside en que EAX no se restaura correctamente. El fallo de esta instrucción,al parecer descubierto por Jeff Prothero está ligado a las instrucciones que vienen inmediatamente acontinuación, y está demostrado que poniendo un NOP detrás -entre otros- nunca falla. En las rutinas deTURBODSK se observa también que los registros de 32 bits empleados en la transferencia son enmascaradospara que no excedan de 0FFFFh, ya que podrían tener la parte alta distinta de 0 y ello provocaría una trágicaexcepción del controlador de memoria al intentar un acceso -por otra parte, de manera incorrecta- fuera delos segmentos de 64Kb.

Trabajando con memoria XMS.

La memoria extendida vía XMS, implementada por HIMEM.SYS y algún controlador de memoriaexpandida, es notablemente más sencilla de manejar que la expandida. En el caso de VDISK, se emplea eltradicional método de la INT 15h de la BIOS para transferir bloques en memoria extendida. Pese a ello, elVDISK de DR-DOS 6.0 es una versión moderna del legendario controlador, y puede convivirsatisfactoriamente con WINDOWS y con los programas que soportan la especificación XMS debido a quetoma las precauciones necesarias. En TURBODSK se prefirió emigrar a los servicios del controlador XMS(rutina Procesa_xms, al final del listado), al igual que RAMDRIVE, ya que casi todas las máquinas queposeen memoria extendida en la actualidad tienen instalado el controlador XMS. Las que no lo tieneninstalado, se les puede añadir fácilmente (solo requiere al menos DOS 3.0). Las ventajas del controlador XMSson múltiples. Por un lado, la velocidad es bastante elevada, ya que en los 386 y superiores utilizaautomáticamente instrucciones de transferencia de 32 bits. Por otro, es extraordinariamente sencillo elproceso: basta crear una estructura con la información del bloque a mover de la memoria convencionalhacia/desde la extendida e invocar la función 0Bh. La diferencia entre TURBODSK y RAMDRIVE es queel primero crea la estructura sobre la pila (solo son 8 palabras). La ventaja de ello es que las instruccionesPUSH consumen mucha menos memoria que las MOV; por otro lado así no hace falta reservar el buffer parala estructura. Hablando de pila: todos los programas residentes que utilizan servicios XMS suelen definir unapila interna, ya que la llamada al controlador XMS puede crear una trama de pila de hasta ¡256 bytes!. Sinembargo, RAMDRIVE no define una pila propia, y no es difícil deducir por qué: el DOS, antes de accedera los controladores de dispositivo, conmuta a una de sus pilas internas, que se supone suficientemente grandepara estos eventos. Por el mismo motivo, se decidió no incorporar una pila a TURBODSK, aunque hay discosvirtuales de dominio público que sí lo hacen. Es fácil comprobar la pila que el DOS pone a disposición delos drivers: basta hacer un pequeño programa en DEBUG que acceda al disco virtual (por ejemplo, vía INT25h) y, sabiendo dónde reside éste, poner un punto de ruptura en algún lugar del mismo con una INT 3. Alejecutar el programa en DEBUG, el control volverá al DEBUG al llegar al punto de ruptura del disco virtual,mostrando los registros. En MS-DOS 5.0, donde se hizo la prueba, todavía quedaban más de 2 Kb de pilaen el momento del acceso al disco virtual (el tamaño de la pila es el valor de SP). Finalmente, decir quedebido a que utilizan la misma memoria de la misma manera, TURBODSK y RAMDRIVE desarrollanvelocidades prácticamente idénticas al operar en memoria extendida.

Hay sin embargo un detalle curioso que comentar: RAMDRIVE instala una rutina que intercepta lasllamadas al controlador XMS. Hacer esto es realmente complicado, teniendo en cuenta que el controladorXMS no se invoca por medio de una interrupción, como los demás controladores, sino con un CALLinter-segmento. Por ello, es preciso modificar parte del código ejecutable del propio controlador de memoria.Esto es posible porque el controlador XMS siempre empieza también por una instrucción de salto lejana decinco bytes (o una corta de dos o tres, seguida de NOP’s, considerando RAMDRIVE todas estas diferentesposibilidades). RAMDRIVE intercepta la función 1 (asignar el HMA), pero comprobando también si ALvale 40h: esto significa que está intentando detectar la llamada de algún programa en concreto, ya que el

Page 223: PCA, PS2 ,IBM y AT

223CONTROLADORES DE DISPOSITIVOS

valor de AL es irrelevante para el controlador XMS. En ese caso, en lugar de continuar el flujo normal,determina la memoria extendida libre y hace unas comprobaciones, pudiendo a consecuencia de ello retornarcon un error 91h (el HMA ya está asignado). Todo parece destinado a mejorar la compatibilidad con algúnprograma, probablemente también de Microsoft, aunque ningún otro disco virtual -TURBODSK entre ellos-realiza estas extrañas maniobras. Esta forma de trabajar es lo que podríamos denominar programación a nivelde cloacas, usando código basura para tapar la suciedad de otros programas previos.

Trabajando con memoria convencional.

En memoria convencional hay pocas diferencias entre todos los discos virtuales. Como no haycontroladores de memoria por el medio, la operación del disco siempre resultará exitosa. La diferencia deTURBODSK frente a RAMDRIVE y VDISK es que en los 386 y superiores utiliza de nuevo transferenciasde 32 bits. Sin embargo, esto no es demasiado importante, ya que estas máquinas suelen tener la memoriaconvencional destinada a cosas más útiles que un disco. En los PC/XT el rendimiento de todos los discosvirtuales suele ser muy similar, excepto algún despistado de dominio público que mueve palabras de 8 bits.La rutina Procesa_con ubicada al final de TURBODSK se encarga de gestionar esta memoria.

LA SINTAXIS DE TURBODSK.

TURBODSK puede ser ejecutado desde el DOS o el CONFIG.SYS indistintamente, y además en elprimer caso de manera repetida, para cambiar las características de un disco ya definido. En cualquier caso,el programa habrá de ser instalado obligatoriamente en el CONFIG.SYS. Repasaremos la sintaxis que admiteantes de proceder a estudiar la instalación del programa:

DEVICE=TDSK.EXE [tamaño [tsect [nfich [tclus]]]] [/E] [/A|X] [/M] [/F]

Alternativamente, 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 deal menos 1024 bytes). Se puede omitir en el CONFIG si no se desea definir el disco en ese momento, y desdeel DOS si solo se quiere obtener información del disco definido. Tsect es el tamaño de sector, entre 32 y2048 bytes en potencias de dos. Sin embargo, DR-DOS no opera correctamente con sectores de menos de128 bytes, aunque sí el MS-DOS 5.0, que por otro lado no soporta sectores de más de 512 bytes (DR-DOSsí). El número de ficheros del directorio raíz viene a continuación (nfich) y ha de estar comprendido entre1 y 65534: TURBODSK lo ajusta para aprovechar totalmente los sectores empleados en el directorio. Aviso:con sectores de 32 bytes, el MS-DOS 5.0 toma el nº de entradas del directorio raíz como módulo 256. Eltamaño de cluster (sectores/cluster) es el último parámetro numérico, debiendo estar comprendido entre 1 y255. Sin embargo, el MS-DOS no soporta tamaños de cluster que no sean potencia de 2 (DR-DOS sí). Losparámetros numéricos intermedios que se desee omitir se pueden poner a cero, para que TURBODSK tomevalores por defecto.

TURBODSK sólo necesita que se indique el tamaño del disco, ajustando los demás parámetros dela manera más aconsejable. De lo expuesto anteriormente se deduce que es sencillo crear discos que nooperen correctamente, si no se tienen en cuenta las limitaciones de los diversos sistemas operativos, aunqueesto es responsabilidad del usuario y el programa no limita su libertad. Con /E se fuerza la utilización dememoria extendida, aunque es un parámetro un tanto redundante (TURBODSK utiliza por defecto estamemoria). /A y /X sirven, indistintamente, para utilizar memoria expandida.

Hasta ahora, la sintaxis de TURBODSK es idéntica a la de RAMDRIVE y VDISK, si se exceptúael parámetro adicional del tamaño de cluster. Sin embargo, TURBODSK soporta la presencia de variasunidades instaladas simultáneamente: desde el DOS puede ser preciso indicar también la letra de la unidada tratar, aunque por defecto se actúa siempre sobre la primera. También se puede indicar /C desde el DOS

Page 224: PCA, PS2 ,IBM y AT

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

para forzar el empleo de memoria convencional en máquinas con memoria expandida y/o extendida. /Mgenera una salida menos espectacular, en monocromo y redireccionable (desde el CONFIG se imprime enmonocromo por discreción y este conmutador actúa al revés, forzando una salida en color). La opción /F, nodocumentada en la ayuda del programa, permite elegir el número de FATS (1 ó 2). Lo normal es trabajar conuna FAT, pero TURBODSK soporta la definición de 2 con objeto de permitir la creación de discos idénticosa los estándar del DOS. Así, con un pequeño programa de utilidad es fácil montar ficheros imagen dedisquetes (creados con el DISKCOPY de DR-DOS 6.0, con DCOPY o con otras utilidades) en un discovirtual de tamaño suficiente. Dicho volcado debe hacerse justo tras redefinir el disco y antes de realizarningún acceso al mismo, para aprovechar el hecho de que el DOS va a ser informado de un cambio desoporte. Ejemplo de lo que puede aparecer en pantalla al definir un disco:

TURBODSK 2.3 - Unidad D: Tamaño de sector: 512Nº entradas raiz: 128

Tamaño: 512 Kbytes Sectores/cluster: 1Memoria: Extendida XMS 1012 clusters (FAT12)

EL PROCESO DE INSTALACIÓN DE TURBODSK.

Casi el 80% del listado de TURBODSK está destinado a instalar y mantener el disco virtual enmemoria. TURBODSK puede ser ejecutado desde la línea de comandos y desde el CONFIG.SYS; losprocedimientos Main e Init, respectivamente, constituyen el programa principal en ambos casos. Elfuncionamiento del programa es muy similar en los dos casos, aunque hay ciertas diferencias lógicas. Alprincipio de ambas rutinas se inicializa una variable que indica si estamos en el CONFIG o en elAUTOEXEC (más en general, en la línea de comandos). Algunas subrutinas concretas actuarán de maneradiferente según desde donde sea ejecutado el programa.

El procedimiento Init se corresponde exactamente con la orden INIT del controlador de dispositivo,realizando todas las tareas que cabría esperar de la misma: inicializar el puntero a la tabla de BPB’s (solouno, ya que cada TURBODSK instalado controla un solo disco), el número de unidades (una), así como lamemoria que ocupa el programa: al final de Init, si no se va utilizar memoria expandida se reserva espaciosólo para las rutinas de memoria convencional y extendida. Se puede definir el disco desde el CONFIG o,sin indicar capacidad o indicando un tamaño 0, instalar el driver sin reservar memoria: para definir el discose puede ejecutar TURBODSK después desde el DOS. En cualquier caso, desde el CONFIG no se permitedefinir el disco en memoria convencional, ya que si así fuera no se podría desasignar en el futuro. Tampocoes muy recomendable reservar memoria extendida o expandida, para evitar una posible fragmentación de lamisma (esto depende de la eficacia de los controladores de memoria) aunque sí se permite definir un discode estos desde el CONFIG. También es vital considerar el parámetro de tamaño de sector que el usuariopueda definir, incluso aunque no se cree el disco al indicar un tamaño 0. La razón es que el DOS asigna eltamaño de sus buffers de disco para poder soportar el sector más grande que defina algún controlador dedispositivo de bloques. El MS-DOS 5.0 no soporta sectores de más de 512 bytes, pero DR-DOS operasatisfactoriamente con sectores de uno o dos Kbytes, e incluso más. Sin embargo, no es recomendable utilizarsectores de más de 512 bytes, ya que el tamaño de los buffers aumenta y se consume más memoria. Empero,TURBODSK, gracias a los sectores de más de 512 bytes permitiría operar con discos de más de 32 Mb sinrebasar el límite máximo de 65535 sectores. Otro pequeño detalle: si la versión del DOS es anterior a la 3.0,se ajusta la palabra de atributos, para indicar que no se soportan las órdenes Open/Close/Remove, con objetode parecerse lo más posible a un controlador del DOS 2.X (RAMDRIVE también se toma esta molestia).También desde el CONFIG se desvía la INT 19h.

El procedimiento Main es muy similar al Init, la principal diferencia radica en que en el caso deutilizar memoria convencional hay que terminar residente, para que el DOS respete el bloque de memoriacreado para contener el disco. Sin embargo, se dejan residentes sólo los primeros 96 bytes del PSP. Tambiéndesde Main puede ser necesario desalojar la memoria de un disco previo, si se indica uno nuevo. Es preciso,así mismo, considerar ciertas circunstancias nuevas que no podían darse desde el CONFIG: una versión delDOS anterior a la 2.0, que el driver no haya sido instalado antes desde el CONFIG, que se indique una letra

Page 225: PCA, PS2 ,IBM y AT

225CONTROLADORES DE DISPOSITIVOS

de unidad que no se corresponda con un driver TURBODSK, que el tamaño de sector exceda el máximo quepermite la configuración del DOS, que se solicite memoria expandida y no se halla reservado espacio parala rutina que la soporta o que se intente redefinir el disco desde WINDOWS. Este último aspecto se consideróa raiz de los riesgos que conlleva. Supongamos, por ejemplo, que el usuario abre una sesión DOS desdeWINDOWS y define un disco de media mega en memoria convencional, volviendo después a WINDOWS:WINDOWS recupera toda la memoria convencional que había asignado para su propio uso, peroTURBODSK no puede darse cuenta de esta circunstancia y, si el usuario intenta grabar algo en el discovirtual, el sistema se estrellará. La memoria virtual de WINDOWS también da problemas al crear discos enmemoria expandida o extendida. Por tanto, las definiciones del disco han de hacerse antes de entrar enWINDOWS. Tampoco conviene definir el disco desde DESQVIEW, aunque si se anula de nuevo antes deabandonar DESQVIEW no habrá problemas, por lo que TURBODSK sí permite modificar el disco desde elinterior de este entorno.

Tanto Init como Main leen la línea de parámetros indicados por el usuario y ejecutan ordenadamentelos procedimientos necesarios para definir el disco, si ésto es preciso.

LAS PRINCIPALES SUBRUTINAS PARA LA INSTALACIÓN.

Veremos ahora con detalle algunas rutinas importantes ejecutadas durante la instalación del discovirtual.

La rutina Gestionar_ram, ejecutada sólo desde la línea de comandos del DOS, rebaja la memoriaasignada al TDSK.EXE en ejecución a 96 bytes. Esto se hace así para poder utilizar después las funcionesestándar del sistema para asignar memoria. Esta acrobacia provoca la creación de un bloque de control dememoria (MCB) en el offset 96 del PSP, lo cual es inocuo; también se libera el espacio de entorno por siacaso se fuera a terminar residente.

Los procedimientos Errores_Dos y Errores_config comprueban algunos errores que puedenproducirse al ejecutar el programa desde la línea de comandos del DOS o desde el CONFIG. En elprocedimiento Max_sector invocado desde Errores_Dos se comprueba si el tamaño de sector indicadoexcede el máximo que soporta el DOS, para lo que se utiliza la función 52h (Get List of Lists); si es así seindica al usuario que ese tamaño de sector debe definirse previamente desde el CONFIG.

En la rutina TestWin se comprueba si Windows está activo, para evitar en ese caso una modificacióndel disco por parte del usuario. Por desgracia, hay que chequear en dos interrupciones distintas las presenciade Windows. Antes de llamar a la INT 2Fh se comprueba que esta interrupción esté apuntando a algún sitio:en el sistema DOS 2.11 en que se probó TURBODSK esa interrupción estaba apuntando a 0000:0000 y elordenador se colgaba si no se tomaba esta precaución.

También desde el DOS, el procedimiento Reside_tdsk? busca la primera unidad TURBODSKresidente de todas las que puede haber en la memoria. Para ello crea una tabla con todos los dispositivos debloque del sistema (rutina Lista_discos) y empieza a buscar desde el final hacia atrás (se trata de encontrarla primera unidad TURBODSK y no la última). Alternativamente, si se había indicado una letra de unidad,el procedimiento Obtener_segm recorre la tabla de discos para asegurarse de que esa letra de unidad es undispositivo TURBODSK, así como para anotar la dirección donde reside.

La rutina Inic_letra, ejecutada desde el CONFIG, calcula la letra que el sistema asignará a la unidad,con objeto de informar en el futuro al usuario. Desde el DOS 3.0, el encabezamiento de petición de solicitudde la orden INIT almacena este dato. Dado que DR-DOS 6.0 no inicializa correctamente el tamaño delencabezamiento de solicitud de esta orden, es más seguro verificar la versión del DOS que comprobar si estedato está definido o no, en función de las longitudes, que sería lo normal. En el caso del DOS 2.X, no haymás remedio que crear una tabla con los dispositivos de bloque del sistema y contarlos (¿a que ya sabe porqué RAMDRIVE y VDISK no informan o informan incorrectamente de la letra de unidad al instalarse enestas versiones del DOS?).

Page 226: PCA, PS2 ,IBM y AT

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

El procedimiento Lista_discos, como dije con anterioridad, crea una tabla con todos los dispositivosde bloque del sistema. Para ello utiliza la valiosa función indocumentada 52h (Get List of Lists) del DOS.Por desgracia, la manera de acceder a la cadena de controladores de dispositivo varía según la versión delDOS, por lo que TURBODSK tiene en cuenta los tres casos posibles (DOS 2.X, 3.0 y versiones posteriores).En la tabla creada, con cuatro bytes por dispositivo: los dos primeros indican el segmento donde reside, elsegundo el número de unidades que controla y el tercero puede valer 1 ó 0 para indicar si se trata de unaunidad TURBODSK o no. El final de la tabla se delimita con un valor de segmento igual a cero. En el casode un dispositivo TURBODSK no se anota el segmento donde reside sino la variable cs_tdsk del mismo, queindica la dirección real incluso en el caso de que el dispositivo haya sido relocalizado por QEMM a lamemoria superior.

La rutina Desinstala libera la memoria que ocupa un disco residente con anterioridad, inhabilitandoel driver. En el caso de la memoria convencional hay que liberar tanto el segmento que ocupaba el discocomo el del PSP previamente residente.

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. En principio se procura utilizar la memoria que el usuarioindica. De lo contrario, por defecto se intenta emplear, en este orden, memoria extendida, expandida oconvencional. En el caso de que no haya suficiente memoria se rebaja la cantidad solicitada, generándose unmensaje de advertencia. Si no se indica el tipo de memoria, en el caso de no haber la suficiente extendida(aunque haya algo) se utiliza la expandida, pero el recurso a la memoria convencional se evita siempre. Ala memoria expandida se le asigna menos prioridad que a la extendida debido a que, en equipos 386 ysuperiores, normalmente es memoria extendida que emula por software la expandida: suele ser más rápidodejar directamente al controlador XMS la tarea de realizar las transferencias de bloques de memoria. Elprocedimiento Mem_info se apoya en tres subrutinas que calculan la cantidad disponible de cada tipo dememoria, despreciando longitudes inferiores a 8 Kb que es el tamaño mínimo del disco. La subrutinaEval_xms chequea la presencia de un controlador de memoria extendida; sin embargo, antes de llamar a INT2Fh se toma una vez más la precaución de comprobar que esta interrupción está apuntado a algo. Lasubrutina Eval_ems detecta la presencia del controlador de memoria expandida buscando un dispositivo"EMMXXXX0". El método ordinario suele ser intentar abrir ese dispositivo y después comprobar por IOCTLque no se trata de un fichero con ese nombre; sin embargo, los controladores de dispositivo invocados desdeel CONFIG.SYS no deben acceder a las funciones IOCTL, por lo que se utiliza el algoritmo alternativo decomprobar si esa cadena está en el offset 10 del vector 67h. En esta subrutina se comprueba además laversión del controlador: en la 4.0 y posterior hay que buscar, recuérdese, dos páginas de memoria expandida(una de ellas la 0) que disten entre sí 32 Kb. Finalmente, la subrutina Eval_con determina la memoriaconvencional disponible. Al principio le solicita casi 1 Mb al DOS, con objeto de que éste falle e indiquecual es la cantidad máxima de memoria disponible. Seguidamente se procede a pedir justo esa memoria, paraque el DOS devuelva el segmento en que está disponible, volviéndose a liberarla inmediatamente acontinuación. Al final, al tamaño de ese bloque de memoria se le restan 128 Kb ya que, con memoriaconvencional, hay que tener la precaución de no ocuparla toda y dejar algo libre. Además, en esos 128 Kbque se perdonan será preciso que TDSK.EXE se autoreubique antes de formatear el disco, como veremosdespués. Con MS-DOS 5.0 se puede crear un disco virtual en memoria superior, cargando TDSK.EXE conel comando LOADHIGH: sin embargo, hay que pedir sólo exactamente la cantidad de memoria superiordisponible en la máquina (o algo menos); de lo contrario el DOS asignará memoria convencional parasatisfacer la demanda: dado que normalmente hay más memoria convencional libre que superior, no serápreciso solicitar en estos casos, afortunadamente, 128 Kb de menos para lograr que sea asignada memoriasuperior (TDSK.EXE se autorelocalizará hacia la memoria convencional y permitirá emplear toda la memoriasuperior libre que quede).

El procedimiento Mem_reserva procede a la efectiva asignación de memoria al disco, en el caso deque finalmente éste se instale, y una vez que ya se había decidido el tipo de memoria a emplear. Si se utilizamemoria expandida, desde la versión 4.0 del controlador se asigna un nombre al handle con objeto de quelos programas de diagnóstico muestren una información más detallada al usuario. El afán de información nose detiene aquí: en el caso de emplear memoria extendida, TURBODSK comprueba si la creación de un

Page 227: PCA, PS2 ,IBM y AT

227CONTROLADORES DE DISPOSITIVOS

handle XMS implica la aparición de otro handle EMS, lo busca y le renombra. Esto sucede con QEMM yotros controladores de memoria que no distinguen la expandida de la extendida.

La subrutina Adaptar_param es una pieza clave dentro del programa: aquí se decide qué parte deldisco va a ocupar el directorio, la FAT, el tipo de FAT, etc. Se toman valores por defecto o, en casocontrario, los que el usuario haya indicado, considerando todas las posibilidades de error. TURBODSKpermite un elevado grado de libertad. Por ejemplo, es factible definir un directorio raíz que consuma la mitadde la capacidad del disco, clusters de hasta 31 Kbytes... evidentemente, los valores que TURBODSK asignapor defecto suelen ser bastante más operativos; pero en principio hay, como se dijo, libertad total para lasdecisiones del usuario. En el caso de versiones 2.X del sistema se establece un tamaño de cluster por defectotal que nunca sea necesaria una FAT de 16 bits (no soportada por estas versiones). El algoritmo paradeterminar el tipo de FAT del disco consiste en considerar el número de sectores libres que quedan despuésde descontar el sector de arranque y el directorio raíz. Teniendo en cuenta el tamaño de cluster en bytes yque la FAT de 12 bits añade 1,5 bytes adicionales para cada cluster, se aplica esta fórmula:

número de sectores libres * tamaño de sector+ 1

tamaño de cluster + 1,5

que devuelve el número de cluster más alto del disco (se añade uno ya que los clusters se numerandesde dos; por ejemplo, 100 clusters se numerarían entre 2 y 101 inclusive). Si el resultado es mayor o igualque 4086, la FAT no puede ser de 12 bits, por lo que se debe recalcular la fórmula sustituyendo el 1,5 por2 y definiendo una FAT de 16 bits. Hay casos críticos en que una FAT de 12 bits no alcanza, pero aldefinirla de 16 el tamaño adicional que ella misma ocupa hace que el número de cluster más alto baje de4086: en estos casos se reserva espacio para una FAT de 16 bits que luego será realmente de 12; sinembargo, se trata de una circunstancia muy puntual y poco probable. En principio, con los tamaños de clustery sector que TURBODSK asigna por defecto, la FAT será de 12 bits a menos que el disco exceda los 8 Mb.

Conviene hacer hincapié en que los discos con 4085 clusters o más (con número de cluster más alto4086 o superior) tienen una FAT de 16 bits. Por desgracia, casi todos los libros consultados (y ya es malasuerte) tienen esta información incorrecta: para unos, la FAT16 empieza a partir de 4078 clusters; para otros,a partir de 4086; otros, no distinguen entre nº de clusters y nº más alto de cluster... hay un auténtico caos yaque las fuentes de información se contradicen. Al final, lo más sencillo es crear discos virtuales con4084/4085 clusters y espiar qué hace el DOS. Es muy fácil: se graban algunos ficheros y se mira la FAT conalgún programa de utilidad (PCTOOLS, DISKEDIT). A simple vista se deduce si el DOS asigna una FATde 12 o de 16 bits. Tanto el MS-DOS 3.1 como el 3.3, 4.0 y 5.0; así como el DR-DOS 3.41, 5.0 y 6.0asignan FAT’s de 16 bits a partir de 4085 clusters inclusive. Por fortuna, todas las versiones del DOS parecencomportarse igual. Asignar el tipo de FAT correcto es vital por muchos motivos; entre otros por que si fueraexcesivamente pequeña el disco funcionaría mal. Sin embargo, los CHKDSK de casi todas las versiones delDOS (excepto el del MS-DOS 3.30 y el de DR-DOS 6.0), incluido el de MS-DOS 5.0, poseen una errata porla que suponen que los discos de 4085 a 4087 clusters tienen una FAT de 12 bits, con lo que puedenestropear el disco si el usuario ejecuta un CHKDSK/F. Esto es un fallo exclusivo de CHKDSK que deberíaser corregido en el futuro, por lo que no se ha evitado estos tamaños de disco (casi nadie ejecuta CHKDSKsobre un disco virtual, y en ese caso no va a tener tan mala suerte). Resulta curioso este fallo de CHKDSK,teniendo en cuenta que es un programa que accede a la FAT y que 4087 (0FF7h) es precisamente la marcade cluster defectuoso en una FAT de 12 bits, ¡nunca un número de cluster cualquiera!. Por ejemplo, con uncomando del tipo TDSK 527 128 0 1 /E (no vale la memoria expandida, ya que redondearía a 528 Kb), sepuede crear un disco de 4087 clusters en el que los CHKDSK de las versiones del DOS señaladas informenincorrectamente de la presencia de errores (si decide hacer pruebas, retoque el número de entradas deldirectorio para variar ligeramente el número de clusters).

Una vez definidos los parámetros básicos de la estructura del disco, el procedimiento Preparar_bpbinicializa el BPB, actualizándolo al nuevo disco; también se indica que ha habido cambio de disco. Elprocedimiento Prep_driver se encarga de copiar el BPB recién creado sobre el del driver residente enmemoria, así como de actualizar las variables de la copia residente en memoria, copiando simplemente las

Page 228: PCA, PS2 ,IBM y AT

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

del TDSK.EXE en ejecución. También se instala la rutina necesaria para gestionar el disco, según el tipo dememoria a emplear por el mismo: esta rutina se instala por partida doble, tanto en la copia residente comoen el propio código del TDSK.EXE que se ejecuta (la rutina de gestión de memoria será accedidadirectamente al formatear el disco virtual).

En el caso de emplear memoria convencional, antes de formatear el disco hay que tomarprecauciones. El motivo radica en el hecho de que el disco probablemente comience en el offset 96 del PSP.Por tanto, si se inicializa sin más el sector de arranque, la FAT y el directorio raíz (en eso consistesimplemente el formateo) el propio TDSK.EXE se autodestruirá. Para evitarlo, TDSK.EXE se copia a símismo en esos 128 Kb libres que siempre hay, incluso en el peor de los casos, pasando a ejecutarse en esenuevo destino por medio de una instrucción RETF que carga CS al retornar (procedimiento Relocalizar). Secopia todo, pila incluida (se actualiza también SS). No habrá problemas, ya que TDSK.EXE es realmente unprograma COM disfrazado de EXE, que carece de referencias absolutas a segmentos. Se toma la precauciónde relocalizar TDSK.EXE (que no ocupa más de 12 Kb) justo a la mitad de ese área de 128 Kb, para evitarsolapamientos consigo mismo en casos críticos. Se puede llegar a sobreescribir parte de la zona transitoriadel COMMAND.COM, lo cual provoca simplemente su recarga desde disco. Ciertamente, no es muyortodoxo que un programa en ejecución vaya dando paseos por la memoria del PC, pero estas cosas sepueden hacer en MS-DOS y nadie puede cuestionar la efectividad del método. Los programadores másconservadores han tenido suerte de que el adaptador de vídeo monocromo cuente con sólo 4 Kb.

ESQUEMA DE LA AUTORELOCALIZACIÓN DE TDSK.EXE (UN CASO CONCRETO)

Casi todas las cifras son arbitrarias, a modo de ejemplo práctico.

1 Mb 1 Mb

640 Kb 640 Kb

aprox. 588 Kbnueva pila de TDSK.EXE

128 KbTDSK.EXE

PSP TDSK.EXE (256 bytes)576 Kb

64 Kb libres (área deseguridad)

512 Kb. . . . . .. . . . . .. . . . . .

Futuros programaspila de TDSK.EXE

Área de almacenamientoTDSK.EXE del disco virtual

PSP TDSK.EXE (256 bytes) PSP TDSK.EXE (96 bytes)

DOS/BIOS DOS/BIOS0 Kb 0 Kb

Antes Después

En este esquema se muestra la autorelocalización de TDSK.EXE en memoria en el caso dedefinirse el disco en memoria convencional. No están reflejados los bloques de control dememoria ni otros detalles. Si la memoria está suficientemente fragmentada (por haber instaladoprogramas residentes tras definir algún disco) puede que no fuera estrictamente necesariorespetar 128 Kb al final del bloque que nos asigna el DOS ni tampoco quizá relocalizar TDSK.EXE;sin embargo, el programa no está optimizado hasta ese extremo. El hecho de relocalizar TDSKhacia la frontera de los 576 Kb en lugar de los 512 se debe a evitar problemas de colisiones encasos críticos de cantidad de memoria libre y tamaño de disco solicitado por el usuario.

Page 229: PCA, PS2 ,IBM y AT

229CONTROLADORES DE DISPOSITIVOS

El procedimiento Formatear_tdsk es extraordinariamente sencillo: se encarga de realizar lo que desdehace algún tiempo ha dado en llamarse formateo rápido. Evidentemente, en un disco virtual no es precisoverificar la memoria buscando posibles sectores defectuosos. Basta copiar un sector de arranque y poner a0 la FAT y el directorio raíz, con la excepción de los primeros 3 bytes de la FAT (4 si es de 16 bits) y los32 primeros bytes del directorio raíz, que contienen una entrada con la etiqueta de volumen. TURBODSKse toma la molestia de consultar la fecha y hora actuales para inicializar la etiqueta de volumen. Para grabarlos sectores en el disco no se puede emplear el elegante método de llamar a la INT 26h: aunque el driverresidente ya está totalmente preparado para operar, si se reserva memoria desde el CONFIG.SYS el DOS noestá aún listo para ejecutar la INT 26h ya que el driver aún no está encadenado a la lista de dispositivos; porello es preciso acceder directamente al mismo (sin embargo, una vez terminado el arranque del ordenadorno hubiera habido problema alguno).

Hablando de acceso directo al disco, otra ventaja de no utilizar INT 25h/INT 26h es que Windows 95no permite un uso directo de estas funciones. Los programas que acceden a estas interrupciones sonconsiderados inadecuados. TURBODSK puede funcionar bajo Windows 95, sin obligar al usuario areconfigurar nada, gracias entre otros motivos a que no utiliza INT 26h.

Con MS-DOS 2.11 y 3.1 hubo bastantes problemas, ya que estos sistemas no detectan muy bien elcambio de disco aunque la rutina MEDIA CHECK del controlador de dispositivo se lo indique: son versionesdel DOS muy desconfiadas que además comprueban el byte descriptor de medio. Es de suponer que cuandoel disco informa que ha habido cambio, estas versiones invalidarán los buffers asociados a él; sin embargo,si creen que se trata de un disco del mismo tipo no se molestan en actualizar el BPB. Por ello, con estasversiones, tras el formateo TURBODSK hace dos cambios de disco consecutivos, con modificación del bytedescriptor de medio entre ambos. El hecho de hacer un segundo cambio se debe al interés de restaurar el bytedescriptor de medio inicial. Además, el DOS 2.11 probado necesitaba dos cambios en cualquier caso: si no,no se tomaba en serio el cambio de disco. Entre cambio y cambio, se pregunta al sistema el espacio libre endisco para forzar un acceso al mismo.

El procedimiento renombrar_mcb cambia el nombre del bloque de memoria de TDSK.EXE: en elcaso de que el disco ocupe memoria convencional/superior, el comando MEM del sistema operativo indicaráclaramente que se trata de TDSK y además qué unidad controla. Es una tontería, pero mola.

AMPLIACIONES DE TURBODSK

Después de esta completa exposición sobre las rutinas que componen TURBODSK, espero que ellector esté suficientemente preparado para entender en conjunto el funcionamiento del programa y para crearunidades de disco por su cuenta. Una posible mejora de TURBODSK sería evitar la pérdida de datos alredefinir el disco, tratándose por ejemplo de aumentar su capacidad. Es complejo añadir esta optimización,ya que la arquitectura del nuevo disco puede cambiar demasiado (nuevo tamaño de FAT e incluso tipo dela misma). Además, el usuario iba a tener muchos problemas siempre, ya que sería muy frecuente que cuandotratase de reducir el tamaño del disco éste estuviera demasiado lleno. En general, los discos virtualesredimensionables que soportan una redefinición sin pérdida de datos, suelen permitir esto de manera limitaday bajo circunstancias concretas. Lo que sí sería más interesante es crear un disco virtual con asignación dememoria en tiempo real: cuando el usuario pretende crear un fichero, habilitar el espacio suficiente. Sinembargo, esto significa unir las complicaciones anteriores a otras nuevas, complicaciones que restaríanvelocidad al disco virtual, además de la dificultad de implementarlas que desanima al programador más audaz.Por otra parte, no está muy claro que el MS-DOS sea un sistema adecuado para soportar tal disco: al final,el proyecto podría quedar descartado en la fase de análisis (si es que alguien acepta el reto).

Page 230: PCA, PS2 ,IBM y AT

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

;;; ; ; ; ; ; ; ; ; ;;; 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);; Internet Email: [email protected]; FidoNet: 2:341/21.8;; Mensajes en alemán cortesía de Axel Christoph Frinke; Internet Email: [email protected];;

; 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:;; - 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.

; ------------ Macros de propósito general

XPUSH MACRO regmem ; apilar lista de registrosIRP rm, <regmem>PUSH rm

ENDMENDM

XPOP MACRO regmem ; desapilar lista de registrosIRP rm, <regmem>POP rm

ENDMENDM

; ------------ Estructuras de datos

cab_PETICION STRUC ; parte inicial común a todostamano DB ? ; los comandos de la cabeceraunidad DB ? ; de peticiónorden DB ?estado DW ?dos_info DB 8 DUP (?)cab_PETICION ENDS

cab_INIT_BBPB STRUC ; para comandos INIT/BUILD_BPBDB (TYPE cab_PETICION) DUP (?)

num_discos DB ? ; número de unidades definidasfin_resid_desp DW ? ; área que quedará residentefin_resid_segm DW ?bpb_cmd_desp DW ? ; línea de órdenes del CONFIGbpb_cmd_segm DW ? ; y puntero al BPBnuevo_disco DB ? ; (DOS 3+) (0-A:, 1-B:,...)cab_INIT_BBPB ENDS

cab_MEDIACHECK STRUC ; estructura para MEDIA CHECKDB (TYPE cab_PETICION) DUP (?)

media_descrip DB ? ; descriptor de mediocambio DB ? ; 1: no cambiado, 0FFh:sí, 0:?cab_MEDIACHECK ENDS

cab_READ_WRITE STRUCDB (TYPE cab_PETICION) DUP (?)DB ? ; descriptor de medio

transfer_desp DW ? ; dirección de transferenciatransfer_segm DW ?transfer_sect DW ? ; nº de sectores a transferirtransfer_sini DW ? ; primer sector a transferircab_READ_WRITE ENDS

; ************ Disco virtual: inicio del área residente.

_PRINCIPAL SEGMENTASSUME CS:_PRINCIPAL, DS:_PRINCIPAL

DD -1 ; encadenamiento con otros driverstipo_drive DW 0800h ; 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+)

DW estrategia ; rutina de estrategiaDW interrupcion ; rutina de interrupciónDB 1 ; número de unidades

; ------------ 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.

id_tdsk DB "TDS23" ; esto es TURBODSK 2.3 y no otro; controlador de dispositivo

num_ordenes DB 10h ; nº de órdenes soportadas

i_tdsk_ctrl EQU $ ; inicio del área a actualizar

tipo_soporte DB 0FFh ; 0: disco no formateado; 1: se emplea memoria XMS 2.0+; 2: " " " EMS 3.2+; 3: " " " convencional; 0FFh: aún no ejecutada INIT

cambiado DB ? ; al formatear el disco virtual se pone; a 0FFh (para indicar cambio de disco)

mem_handle DW ? ; para memoria EMS/XMS; si se utiliza; memoria convencional, apunta al; segmento donde empieza el disco

tdsk_psp DW ? ; segmento del PSP residente si se; utiliza memoria convencional

ems_pagina0 DW ? ; segmento de página EMS (si se emplea)ems_paginai DW ? ; segmento alternativoems_pagni DB ? ; nº de página física alternativa

xms_driver LABEL DWORD ; dirección del controlador XMS, en elxms_desp DW ? ; caso de emplear memoria XMS.xms_segm DW ?

cpu386 DB OFF ; a ON si 386 ó superior

f_tdsk_ctrl EQU $ ; final del área a actualizar

letra_unidad DB ? ; letra ASCII del disco (’C’, ’D’,...)

bpb_ptr DW bpb ; puntero al BPB del disco

rutina_larga DB OFF ; a ON si reservado espacio en; memoria para la larga rutina de; gestión de memoria EMS.

; ------------ Variables internas de TURBODSK; su ubicación podría; cambiar en futuras versiones del programa.

pcab_peticion LABEL DWORD ; puntero a la cabecera de peticiónpcab_pet_desp DW 0pcab_pet_segm DW 0

p_rutinas LABEL WORD ; tabla de rutinas del controladorDW initDW media_checkDW build_bpbDW ioctl_inputDW readDW read_nowaitDW input_statusDW input_flushDW writeDW write_verifyDW output_statusDW output_flushDW ioctl_outputDW open ; DOS 3.0+DW close ; DOS 3.0+DW remove ; DOS 3.0+

media EQU 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.

bpb LABEL BYTE ; Estos valores del BPB son arbitrarios:bytes_sector DW 512 ; se inicializarán si se define el discosect_cluster DB 1 ; al instalar desde el CONFIG; en casosect_reserv DW 1 ; contrario, como son correctos, el DOSnum_fats DB 1 ; no tendrá problemas para realizar susentradas_raiz DW 128 ; cálculos internos iniciales al instalarnum_sect DW 128 ; el driver. En concreto, el tamaño demedia_byte DB media ; sector influye de manera directa en elsectores_fat DW 4 ; tamaño de los buffers de disco del DOS.fin_bpb EQU $

; ------------ Rutina de estrategia del disco virtual.

estrategia PROC FARMOV CS:pcab_pet_desp,BXMOV CS:pcab_pet_segm,ESRET

estrategia ENDP

; ------------ 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

Page 231: PCA, PS2 ,IBM y AT

231CONTROLADORES DE DISPOSITIVOS

XPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>LDS BX,CS:pcab_peticionMOV AL,[BX].orden ; AL = ordenMOV AH,0 ; AX = ordenCMP AL,CS:num_ordenesJB orden_ok ; orden soportadaMOV AL,3 ; " desconocida (IOCTL INPUT)

orden_ok: CMP CS:tipo_soporte,AHJNE no_test_fmt ; tipo_soporte distinto de 0MOV AX,8102h ; disco no formateado: errorJMP exit_interr

no_test_fmt: SHL AX,1 ; orden = orden * 2MOV SI,AXXPUSH <BX,DS>XOR BP,BPMOV AX,100hCALL CS:[SI+OFFSET p_rutinas] ; ejecutar ordenXPOP <DS,BX>AND AH,AHJNS exit_interr ; no hubo error (bit 15 = 0)CMP AL,3JE exit_interr ; error de orden desconocidaMOV [BX].transfer_sect,0 ; otro: movidos 0 sectores

exit_interr: MOV [BX].estado,AXXPOP <ES,DS,BP,DI,SI,DX,CX,BX,AX>RET

interrupcion ENDP

; ------------ 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 AL,CS:cambiado ; condición de «disco cambiado»MOV CS:cambiado,AH ; de momento ya no cambiará másMOV [BX].cambio,AL

read_nowait: ; conjunto de órdenes coninput_status: ; tratamiento idénticoinput_flush:output_status:output_flush:ioctl_output:open:close:retorno_ok: RET ; no hay error, ignorar orden

build_bpb: MOV [BX].bpb_cmd_desp,OFFSET bpbMOV [BX].bpb_cmd_segm,CSJMP retorno_ok

ioctl_input: MOV AX,8103h ; orden no soportadaRET

remove: MOV AH,3 ; fin de función, indicarRET ; «controlador ocupado»

nueva_int19 PROC ; Interceptar reinicialización.286PUSHAXPUSH <DS,ES> ; Esto es una interrupciónXOR AX,AXMOV ES,AXCMP AL,CS:tipo_soporte ; ¿Disco formateado?JE no_lib ; noMOV CS:tipo_soporte,AL ; sí: anularloCALL procesa_io ; CF=1: liberar memoria EMS/XMS

no_lib: LEA SI,ant19offMOV DI,64h ; desplazamiento de INT 19hPUSH CSPOP DSCLIMOVSWMOVSWXPOP <ES,DS>POPADB 0EAh ; código de JMP FAR SEG:OFF

ant19off DW ?ant19seg DW ?

.8086nueva_int19 ENDP

read: INC BP ; indicar lectura (BP=1)write: ; escritura (BP=0)write_verify:

init_io PROC ; preparar registros E/SLES DI,DWORD PTR [BX].transfer_desp ; * direc. ES:DILDS AX,DWORD PTR [BX].transfer_sect ; nº sectores AXMOV BX,DS ; 1º sector ¡DS indefinido!

io_proc: MOV SI,CS:bytes_sectorADD AX,BXJNC io_ok? ; último sector < 65536

io_no_ok: MOV AX,8108h ; «sector no encontrado»RET

io_ok?: CMP AX,CS:num_sectJA io_no_ok ; sector final ¡fuera!SUB AX,BXMUL SI ; DX(CF):AX = tamaño bloqueRCR AX,1 ; CF:AX/2 -> AX = palabrasMOV CX,DINEG CX ; 10000h-CX: CF=1 si CX<>0CMC ; CF:CX bytes hasta fin deRCR CX,1 ; segmento = (10000h-DI)/2CMP AX,CXJAE io_cx_okMOV CX,AX ; * tamaño: CX palabras

io_cx_ok: JCXZ io_no_ok ; CX=0 si DI=0FFFFh (fatal)MOV AX,BX ; sector inicialMUL SI ; * desplazamiento en DX:AXCLC ; ¡no reinicializando!

init_io ENDP

; ------------ 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 $

; ---- 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

; 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.

procesa_ems PROCJNC no_emslibMOV DH,45h ; sistema reinicializando:CALL llama_EMM ; liberar memoria EMSRET

no_emslib: MOV SI,DX ; preservar DXMOV DH,47hCALL llama_EMM ; DH=47h -> salvar contexto EMSMOV DX,SI ; recuperar DXMOV BX,4000h ; tamaño de página (16 Kb)DIV BX ; AX = 1ª página EMS a mapearMOV SI,DX ; offset relativo en 1ª página

procesa_pag: PUSH CX ; **MOV BX,DIMOV CL,4SHR BX,CL ; bytes del offset -> párrafosMOV CX,ESADD BX,CX ; AX = segmento de datosMOV CX,CS:ems_pagina0MOV DS,CXXOR DL,DL ; intentar emplear página 0SUB BX,CXJNC rposNEG BX ; valor absoluto

rpos: CMP BX,401h ; distancia respecto página EMSJAE no_conflicto ; más de 16 Kb: no solapamientoCALL copia_contexto ; está CX apiladoMOV DS,CS:ems_paginaiMOV DL,CS:ems_pagni ; usar página alternativaOR BP,8000h ; indicar su uso

no_conflicto: POP CX ; * pila totalmente equilibradaMOV BX,AXMOV DH,44h ; DL = 0 ó 2 (página física)CALL llama_EMM ; DH = 44h -> mapear página EMSXPUSH <CX,SI> ; ++SUB SI,4000hNEG SI ; SI = 4000h - SI: «resto»SHR SI,1 ; bytes -> palabrasCMP CX,SIJB cx_ok ; no ocupada toda la páginaMOV CX,SI

cx_ok: POP SI ; + SI=desplazamiento relativoCLDPOP BX ; + palabras restantesSUB BX,CX ; descontar las que se moveránPUSH BX ; * volver a apilar el viejo CXCALL coloca_regsCMP CS:cpu386,ON ; ¿386 o superior?JNE trans_16bit.386PUSHADSHR CX,1 ; nº palabras de 32 bit a moverJCXZ transferido ; evitar desgraciaXOR EAX,EAX ; asegurar no violaciónDEC AX ; de segmento-64KAND ECX,EAX ; EAX = 0FFFFhAND ESI,EAXAND EDI,EAXREP MOVSD ; transferencia ultrarrápida

transferido: POPAD ; POPAD falla en muchos 386.8086NOP ; arreglar fallo de POPADADD CX,CXADD DI,CX ; simular cambio normal de DIADD SI,CX ; y de SIJMP fin_trans

trans_16bit: REP MOVSW ; mover palabras de 16 bitfin_trans: CALL coloca_regs

AND BP,BP ; ¿se usó página alternativa?JNS ahorra_msCALL copia_contexto ; está CX apiladoAND BP,1 ; de momento, no se usará más

ahorra_ms: POP CX ; **JCXZ fin_leer ; no quedan más palabrasINC AX ; próxima página EMSXOR SI,SI ; ahora desde inicio página EMSJMP procesa_pag

fin_leer: MOV DH,48hCALL llama_EMM ; DH=47h restaurar contexto EMSMOV AX,100h ; no hubo problemasRET

procesa_ems ENDP

; ---- ¡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 PROCXPUSH <AX,BX,CX,BP>MOV AX,DX ; función en AX

llama_denuevo: MOV DX,CS:mem_handle ; handle EMSXPUSH <AX,BX>INT 67h ; llamar al EMMMOV CL,AHXPOP <BX,AX>AND CL,CLJZ llama_ok ; además, ZF = 1CMP CL,82hJE llama_denuevo ; intentarlo hasta que funcione

llama_ok: XPOP <BP,CX,BX,AX>JNE ret_atrasRET

ret_atras: POP AX ; sacar dirección de retornoMOV AX,810Ch ; error de «anomalía general»RET ; retornar dos niveles atrás

llama_EMM ENDP

; ---- ¡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 PROCXPOP <BX,CX> ; equilibrar pila a llama_EMM

Page 232: PCA, PS2 ,IBM y AT

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

MOV DH,48hCALL llama_EMM ; restaurar contexto EMSMOV DH,47hCALL llama_EMM ; preservarlo de nuevoPUSH CXJMP BX ; más rápido que PUSH BX/RET

copia_contexto ENDP

coloca_regs PROC ; ¿invertir sentido?TEST BP,1JNZ colocadosXCHG SI,DI ; escritura: invertir sentidoXPUSH <DS,ES>XPOP <DS,ES>

colocados: RETcoloca_regs ENDP

tam_proc_ems EQU $-OFFSET procesa_ems ; tamaño de esta rutina

; <<< Fin del código residente del disco virtual >>>

; ************ Instalación (invocada desde CONFIG.SYS).

init PROCMOV CS:modo,CONFIG ; ejecutando desde CONFIGCALL obtDosVer ; obtener versión del DOSLEA AX,retorno_okMOV CS:p_rutinas,AX ; anular rutina INITINC CS:tipo_soporte ; 0: disco no formateadoMOV CS:cs_tdsk,CS ; inicializar esa variableCMP CS:dosver,300h ; ¿DOS inferior al 3.0?JAE dos_ok ; DOS 3.0+AND CS:tipo_drive,0F7FFh ; ajustar atributosMOV CS:num_ordenes,0Dh ; y número de órdenes

dos_ok: MOV SI,[BX].bpb_cmd_despMOV ES,[BX].bpb_cmd_segm ; ES:SI -> parámetrosMOV [BX].num_discos,1 ; una unidad de discoLEA AX,bpb_ptrMOV [BX].bpb_cmd_desp,AXMOV [BX].bpb_cmd_segm,CS ; inicializado puntero BPBCALL desvia_int19 ; controlar INT 19hCALL inic_letra ; obtener letra de unidadMOV BX,CSMOV DS,BX ; DS: -> _PRINCIPALMOV BX,SI ; ES:BX -> parámetrosCALL salta_nombre ; buscar inicio parámetrosCALL procesar_param ; procesar parámetrosPUSH DS ;POP ES ; ES: -> _PRINCIPALCMP param_b,ONJNE pet_ayuda?MOV AH,8 ; opción /BMOV DL,80hPUSH ESINT 13h ; ¿nº de discos duros?POP ESAND DL,3JZ pet_ayuda? ; no existe disco duroLEA AX,procesa_ioNEG AXJMP bytes_res_ok ; no quedará residente

pet_ayuda?: CMP param_h,ONJE fin_instalar ; piden ayudaCALL max_sector ; obtener mayor sectorMOV BX,param_tsectCMP BX,AX ; ¿el nuestro es mayor?JBE sect_def_ok ; noMOV bytes_sector,BX ; sí: ajustar BPB

sect_def_ok: CALL errores_configTEST lista_err,ERROR0+ERROR1JNZ fin_instalar ; algún error importanteCMP param_tdisco,0 ; ¿se define disco ahora?JE fin_instalar ; no: no hay más que hacerCALL mem_info ; evaluar memoria del PCCMP tdisco,0 ; ¿se reservará memoria?JE fin_instalar ; no: no hay más que hacerCALL mem_reserva ; reservar memoriaJC fin_instalar ; fallo al reservarlaCALL test_CPU ; detectar 386 ó superiorCALL adaptar_param ; adaptar parámetros discoCALL preparar_BPB ; BPB del nuevo discoCALL prep_driver ; preparar el driverCALL formatear_tdsk ; inic. BOOT, FAT y ROOT

fin_instalar: CALL info_disco ; informar sobre el discoCMP tipo_soporte,2JE res_largo ; se utiliza memoria EMSCMP param_a,ONJE res_largo ; se indicó /ACALL eval_xmsCALL eval_emsCMP ems_kb,0JE res_corto ; no hay memoria EMSCMP xms_kb,0JNE res_corto ; la hay, pero también XMS

res_largo: MOV AX,tam_proc_emsMOV rutina_larga,ON ; dejar sitio a rutina EMSJMP bytes_res_ok

res_corto: MOV AX,tam_proc_xms ; dejar sitio a XMS/conv.MOV BX,tam_proc_conCMP AX,BXJAE bytes_res_okXCHG AX,BX

bytes_res_ok: LDS BX,CS:pcab_peticionADD AX,OFFSET procesa_ioMOV [BX].fin_resid_desp,AX ; reservar memoria paraMOV [BX].fin_resid_segm,CS ; las rutinas a usarMOV AX,100h ; instalación siempre Ok.RET

init ENDP

; ------------ Redefinición (invocada desde el AUTOEXEC.BAT o el DOS).

main PROC FARMOV CS:modo,AUTOEXEC ; ejecutando desde el DOSCALL obtDosVer ; obtener versión del DOSCALL gestionar_ram ; gestión de memoriaMOV AX,_PRINCIPAL ; programa de un segmentoMOV DS,AX ; DS: -> _PRINCIPALMOV BX,81h ; ES:BX línea de órdenesCALL procesar_param ; procesar parámetrosCMP param_h,ONJE exit_instalar ; piden ayudaPUSH DSPOP ES ; ES: --> _PRINCIPALCALL errores_DosTEST err_grave,0FFFFhJNZ exit_instalar ; algún error grave

MOV ES,segm_tdsk ; ES: --> disco residenteCMP param_a,ONJNE cabria_emsCMP ES:rutina_larga,ONJE cabria_ems ; cabe la rutina EMSOR lista_err,ERROR2

cabria_ems: TEST lista_err,ERROR0+ERROR2 ; ¿error sintaxis ó EMS?JNZ exit_instalar ; sí: no modificar discoCMP param_tdiscof,ONJNE exit_instalar ; no indicado nuevo tamañoCMP ES:tipo_soporte,0JE cont_instalar ; no estaba formateado aúnCALL desinstala ; liberar memoria ocupada

cont_instalar: CALL mem_info ; evaluar memoria del PCCMP tdisco,0 ; ¿se reservará memoria?JE exit_instalar ; no: no hay más que hacerCALL mem_reserva ; reservar memoriaJC exit_instalar ; fallo reservando memoriaCALL test_CPU ; detectar 386 ó superiorCALL adaptar_param ; adaptar parámetros discoCALL preparar_BPB ; BPB del nuevo discoCALL relocalizar ; autoreubicación de TDSKCALL prep_driver ; preparar el driverCALL formatear_tdsk ; BOOT, FAT y ROOT

exit_instalar: CALL info_disco ; informar sobre el discoCMP tipo_soporte,3 ; ¿memoria convencional?JNE fin_no_res ; no usadaCALL renombrar_mcb ; cambiar nombre del MCBMOV DX,6 ; usada: 96 bytes de PSPMOV AX,3100hINT 21h ; terminar residente

fin_no_res: CALL set_errorlevel ; preparar ERRORLEVELMOV AH,4ChINT 21h ; final normal

main ENDP

; ------------ Inicializar la variable con la versión del DOS

obtDosVer PROCXPUSH <AX,BX,CX,DX>MOV AH,30hINT 21hXCHG AH,ALMOV CS:dosver,AXXPOP <DX,CX,BX,AX>RET

obtDosVer ENDP

; ------------ 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 PROCMOV CS:segm_psp,DS ; indicar segmento del PSPMOV AX,DS:[2] ; segmento más altoMOV CS:top_ram,AX ; indicar tope de memoriaPUSH ESMOV ES,DS:[2Ch] ; segmento del entornoMOV AH,49hINT 21h ; liberar área de entornoPOP ES ; ES: -> PSPMOV BX,6MOV AH,4Ah ; hacer creer al DOS queINT 21h ; TDSK ocupa sólo 96 bytesRET

gestionar_ram ENDP

; ------------ 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 PROCCALL busca_param ; saltar delimitadoresJC fin_param ; no hay más parámetrosCALL param_barra ; gestionar parámetro tipo "/A"JC procesar_param ; era parámetro tipo "/A"MOV param_tdisco,AX ; es numérico: tamaño del discoMOV param_tdiscof,ON ; parámetro de tamaño indicado

p_param2: CALL busca_paramJC fin_paramCALL param_barraJC p_param2MOV param_tsect,AX ; tamaño de sector

p_param3: CALL busca_paramJC fin_paramCALL param_barraJC p_param3MOV param_tdir,AX ; entradas al directorio

p_param4: CALL busca_paramJC fin_paramCALL param_barraJC p_param4MOV param_tcluster,AX ; tamaño de cluster

p_param5: CALL busca_paramJC fin_paramCALL param_barra ; últimas opciones posiblesJC p_param5

fin_param: CALL validacion ; validación de parámetrosRET

procesar_param ENDP

param_barra PROCCMP AX,"e/" ; ¿indicado /E?JNE p_exp1?MOV param_e,ONJMP p_barra_exit

p_exp1?: CMP AX,"a/" ; ¿indicado /A?JNE p_exp2?

p_exp: MOV param_a,ONJMP p_barra_exit

p_exp2?: CMP AX,"x/" ; /A y /X son equivalentesJE p_expCMP AX,"c/" ; ¿indicado /C?JNE p_ayuda?MOV param_c,ONJMP p_barra_exit

p_ayuda?: CMP AX,"h/" ; ¿indicado /H?JNE p_exit?

p_ayuda: MOV param_h,ONJMP p_barra_exit

p_exit?: CMP AX,"?/" ; /H y /? son equivalentesJE p_ayuda

Page 233: PCA, PS2 ,IBM y AT

233CONTROLADORES DE DISPOSITIVOS

CMP AX,"m/" ; ¿indicado /M?JNE param_id?MOV param_m,ONJMP p_barra_exit

param_id?: CMP AX,"i/" ; ¿indicado /I= o /I:?JNE param_fats?ADD BX,3CMP BYTE PTR ES:[BX-1],’=’JE p_id_okCMP BYTE PTR ES:[BX-1],’:’JNE param_b_mal

p_id_ok: CALL obt_num ; leer código telefónicoMOV param_i,ONMOV codigo_tfno,AXSUB BX,2JMP p_barra_exit

param_fats?: CMP AX,"f/" ; ¿indicado /F= o /F:?JNE param_b?ADD BX,3CMP BYTE PTR ES:[BX-1],’=’JE p_f_okCMP BYTE PTR ES:[BX-1],’:’JNE param_b_mal

p_f_ok: CALL obt_num ; leer número de FATsMOV param_f,AXSUB BX,2JMP p_barra_exit

param_b?: CMP AX,"b/" ; ¿indicado /B?JNE param_unidad?MOV param_b,ONJMP p_barra_exit

param_unidad?: CMP AH,’:’ ; ¿parámetro de unidad?JNE param_num?AND AL,255-32 ; poner en mayúsculasMOV param_unidad,ALJMP p_barra_exit

param_num?: CMP AL,’/’JNE param_num ; puede ser número

param_b_mal: OR lista_err,ERROR0param_num: CALL obt_num ; es parámetro numérico: leerlo

CLC ; no es parámetro barradoRET

p_barra_exit: ADD BX,2 ; saltar este parámetroSTC ; es parámetro barradoRET

param_barra ENDP

validacion PROCMOV AX,0FFFFhCMP AX,param_tdisco ; ¿números correctos?JE sintax_errCMP AX,param_tsectJE sintax_errCMP AX,param_tdirJE sintax_errCMP AX,param_tclusterJE sintax_errCMP param_tdisco,0JE valida_tsect ; no indicado tamaño (o 0)CMP param_tdisco,8JB sintax_errCMP param_tdisco,65534JA sintax_err

valida_tsect: MOV AX,param_tsectCMP AX,0JE valida_tclus ; no indicado tamaño de sectorCMP AX,32JE valida_tclusCMP AX,64JE valida_tclusCMP AX,128JE valida_tclusCMP AX,256JE valida_tclusCMP AX,512JE valida_tclusCMP AX,1024JE valida_tclusCMP AX,2048JNE sintax_err

valida_tclus: CMP param_tcluster,256JAE sintax_err ; debe estar entre 0..255CMP param_f,1JB pf_a1 ; /F=1 ó /F=2 exclusivamenteCMP param_f,2 ; si no, forzarlo y perdonarJBE fin_validarMOV param_f,2JMP fin_validar

pf_a1: MOV param_f,1JMP fin_validar

sintax_err: MOV param_tdiscof,OFF ; no definir disco ahoraXOR AX,AXMOV param_tdisco,AXMOV param_tsect,AXMOV param_tdir,AXMOV param_tcluster,AXOR lista_err,ERROR0 ; aviso de error de sintaxis

fin_validar: RETvalidacion ENDP

salta_nombre PROC ; saltar nombre del driver enMOV AL,ES:[BX] ; línea de órdenes del CONFIGINC BXCMP AL,’ ’JE fin_nombreCMP AL,9JE fin_nombreCMP AL,0DhJE fin_nombreCMP AL,0AhJE fin_nombreAND AL,ALJZ fin_nombre ; necesario para DOS 2.xJMP salta_nombre

fin_nombre: DEC BXRET

salta_nombre ENDP

busca_param PROC ; saltar delimitadoresDEC BX

p_delimit: INC BXMOV AX,ES:[BX]CMP AL,’ ’JE p_delimit ; espacio en blancoCMP AL,9JE p_delimit ; tabuladorCMP AL,13JE p_final ; CR ó LF indican el final

CMP AL,10JE p_finalOR AX," " ; poner en minúsculasCLCRET

p_final: STC ; se acabaron los parámetrosRET

busca_param ENDP

obt_num PROC ; leer número: devolver 65535XPUSH <CX,DX,SI> ; si hay errorXOR AX,AX ; número en proceso de creación

otro_digito: MOV CL,ES:[BX]CMP CL,’0’JB no_digitoCMP CL,’9’JBE digito_ok

no_digito: CMP CL,’ ’ ; posibles delimitadores...JE fin_numCMP CL,9JE fin_numCMP CL,13JE fin_numCMP CL,10JE fin_numCMP CL,’/’JE fin_numJMP num_incorr

digito_ok: XOR DX,DXMOV SI,10MUL SI ; AX = AX * 10JC num_incorrXOR CH,CHSUB CL,’0’ADD AX,CX ; AX = AX + datoJC num_incorrINC BXJMP otro_digito

num_incorr: MOV AX,65535 ; indicar valor incorrectofin_num: XPOP <SI,DX,CX>

RETobt_num ENDP

; ------------ Detectar errores que se pueden producir sólo en la; línea de comandos.

errores_Dos PROCPUSH ESCMP dosver,200h ; necesario DOS 2.x+JAE existe_tdsk?OR err_grave,ERROR0 ; error de DOS incorrectoJMP fin_err_Dos

existe_tdsk?: CALL reside_tdsk? ; ¿instalado TURBODSK?CMP segm_tdsk,0JNE busca_unidad ; ya instaladoOR err_grave,ERROR1 ; error: TURBODSK no instaladoJMP fin_err_Dos

busca_unidad: MOV ES,segm_tdsk ; ES: -> disco virtualCMP param_unidad,0JE disco_defecto ; no se indicó letra de unidadCALL obtener_segm ; segmento del TDSK indicadoJC fin_err_Dos ; fallo (no es unidad TDSK)

disco_defecto: CALL max_sector ; obtener mayor sectorMOV BX,param_tsectCMP BX,AXJBE fin_err_Dos ; tamaño de sector correctoOR lista_err,ERROR3 ; el tamaño no definible ahoraMOV param_tsect,0 ; ignorar tamaño indicado

fin_err_Dos: CALL test32MbCALL testWinPOP ESRET

errores_Dos ENDP

; ------------ Detectar errores que se pueden producir sólo desde; el CONFIG.SYS

errores_config PROCCMP param_unidad,0JE no_unidadOR lista_err,ERROR1

no_unidad: CMP param_c,ONJNE fin_err_conOR lista_err,ERROR1

fin_err_con: CALL test32MbRET

errores_config ENDP

; ------------ Preparar valor de ERRORLEVEL para el retorno.

set_errorlevel PROCMOV AL,255TEST err_grave,ERROR1 ; ¿TDSK no instalado?JNZ fin_cod_okDEC ALTEST err_grave,ERROR2 ; ¿unidad incorrecta?JNZ fin_cod_okDEC ALTEST err_grave,ERROR3 ; ¿dentro de Windows?JNZ fin_cod_okDEC ALTEST lista_err,ERROR0 ; error de sintaxisJNZ fin_cod_okCMP param_h,ON ; ayuda: handle desconocidoJE fin_cod_okMOV AL,BYTE PTR ES:mem_handle ; handle XMS/EMSCMP ES:tipo_soporte,0JNE fin_cod_okMOV AL,0 ; disco no formateado

fin_cod_ok: RETset_errorlevel ENDP

; ------------ Obtener mayor tamaño de sector definido en el sistema.

max_sector PROCXPUSH <BX,ES>MOV AH,52hINT 21h ; Get List of ListsADD BX,10hCMP CS:dosver,30AhJAE psect_okINC BX ; DOS anterior al 3.1

psect_ok: MOV AX,ES:[BX] ; mayor tamaño de sectorXPOP <ES,BX> ; definido por cualquier disp.RET

max_sector ENDP

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

Page 234: PCA, PS2 ,IBM y AT

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

; es de al menos 1024 bytes.

test32Mb PROCCMP param_tdisco,32768JBE fin32mbCMP param_tsect,1024JAE fin32mbOR lista_err,ERROR15 ; sector de menos de 1024MOV param_tdisco,32768 ; evitar fallo posterior

fin32mb: RETtest32Mb ENDP

; ------------ Desde Windows, no se permite redefinir el disco.

testWin PROCCMP param_tdiscof,ONJNE fin_testWin ; no redefinido el discoCMP dosver,300hJB fin_testWin ; no buscar Windows en DOS 2.xMOV AX,1600hINT 2FhAND AL,AL ; ¿Windows en modo extendido?JZ noWinEnhCMP AL,80h ; ¿Windows en modo extendido?JE noWinEnh

siWin: OR err_grave,ERROR3 ; estamos dentro de WindowsJMP fin_testWin

noWinEnh: MOV AX,4680hINT 2FhAND AX,AXJZ siWin ; Windows en modo real/estándar

fin_testWin: RETtestWin ENDP

; ------------ 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? PROCXPUSH <AX, SI>CALL lista_discosLEA SI,area_trabajo-4

busca_final: ADD SI,4CMP WORD PTR [SI],0JNE busca_final ; ir al final de la tabla

busca_tdsk: SUB SI,4CMP SI,OFFSET area_trabajoJB fin_busca ; no reside (segm_tdsk = 0)CMP BYTE PTR [SI+3],1JNE busca_tdskMOV AX,[SI] ; encontrada unidad TURBODSKMOV segm_tdsk,AXPUSH DSMOV DS,AXMOV AL,letra_unidad ; con esta letra de unidadPOP DSMOV letra_unidad,AL

fin_busca: XPOP <SI, AX>RET

reside_tdsk? ENDP

; ------------ 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.

obtener_segm PROCCALL lista_discosLEA SI,area_trabajo-4

busca_ultimo: ADD SI,4CMP WORD PTR [SI],0JNE busca_ultimo ; realmente, el primero

recorre_dsks: SUB SI,4CMP SI,OFFSET area_trabajoJB tdsk_no_hayCMP BYTE PTR [SI+3],1JNE recorre_dsksPUSH DSMOV DS,[SI]MOV AL,letra_unidad ; unidad del TDSK residentePOP DSCMP AL,param_unidad ; disco TDSK: ¿es el buscado?JNE recorre_dsksMOV letra_unidad,AL ; inicializar letra de unidadMOV AX,[SI]MOV segm_tdsk,AX ; inicializar segmentoMOV ES,AXCLCRET

tdsk_no_hay: OR err_grave,ERROR2 ; unidad indicada no es TDSKSTCRET

obtener_segm ENDP

; ------------ 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 PROCXPUSH <BX,DS,ES>MOV BX,CSMOV DS,BXCALL test_CPUCMP cpu286,ONJNE fin_desvia19 ; no es 286 ó superiorMOV AX,3519hINT 21h ; ES:BX anterior INT 19hMOV ant19off,BXMOV ant19seg,ESLEA DX,nueva_int19MOV AX,2519hINT 21h ; nueva rutina de control

fin_desvia19: XPOP <ES,DS,BX>RET

desvia_int19 ENDP

; ------------ 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 PROCXPUSH <AX,BX,SI,DS>MOV AL,[BX].nuevo_disco ; unidad en DOS 3.0+ADD AL,’A’PUSH CSPOP DS ; DS -> _PRINCIPALCMP dosver,300hJAE letra_okCALL lista_discos ; hallar unidad en DOS 2.xLEA SI,area_trabajoXOR AL,AL ; cuenta de discos

cuenta_discos: ADD AL,[SI+2]ADD SI,4CMP WORD PTR [SI],0JNE cuenta_discosADD AL,’A’

letra_ok: MOV letra_unidad,AL ; guardar letra de unidadXPOP <DS,SI,BX,AX>RET

inic_letra ENDP

; ------------ 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 PROCXPUSH <AX,BX,CX,DX,SI,DI,ES>MOV AH,52h ; "Get list of lists"INT 21h ; obtener puntero en ES:BXMOV CX,17h ; supuesto DOS 2.xCMP dosver,300hJB pdisp_okMOV CX,28h ; supuesto DOS 3.0xCMP dosver,30AhJB pdisp_okMOV CX,22h ; versiones del DOS superiores

pdisp_ok: ADD BX,CXLEA DI,area_trabajo-4 ; tabla de dispositivos-4

disp_otro: ADD DI,4disp_skip: LES BX,ES:[BX] ; siguiente dispositivo

CMP BX,-1JE disp_finTEST BYTE PTR ES:[BX+5],80hJNZ disp_skip ; es dispositivo de caracteresMOV CL,ES:[BX+10] ; es de bloquesMOV [DI],ES ; anotar direcciónMOV [DI+2],CLMOV BYTE PTR [DI+3],0 ; de momento, no es TDSKPUSH DILEA SI,id_tdsk ; identificación de TURBODSKMOV DI,SIMOV CX,5CLDREP CMPSB ; ¿es TURBODSK?POP DIJNE disp_otro ; es de bloques, pero no TDSKMOV AX,ES:cs_tdsk ; segmento real de TDSKMOV [DI],AX ; corregir dirección en tablaINC BYTE PTR [DI+3] ; indicar dispositivo TDSKJMP disp_otro ; buscar hasta completar tabla

disp_fin: MOV WORD PTR [DI],0 ; final de la listaXPOP <ES,DI,SI,DX,CX,BX,AX>RET

lista_discos ENDP

; ------------ Liberar la memoria ocupada por un TURBODSK residente.

desinstala PROCMOV DX,ES:mem_handleMOV AL,ES:tipo_soporteDEC ALJZ libera_ext ; liberar memoria extendidaDEC ALJZ libera_exp ; liberar memoria expandidaPUSH ESMOV ES,DXMOV AH,49h ; liberar memoria convencional:INT 21hPOP ESPUSH ESPUSHF ; condición de errorMOV ES,ES:tdsk_psp ; liberar PSP residenteMOV AH,49hINT 21hPUSHFCMP dosver,31EhJA mcb_ok ; DOS 3.31+: el MCB es correctoMOV AX,ESDEC AXMOV ES,AXMOV DI,8MOV CX,DICLDMOV AL,’ ’REP STOSB ; hasta DOS 3.30 borrar nombre

mcb_ok: POPFJNC lib_con_ok? ; liberado correctamentePOPFPOP ESSTC ; ha habido falloJMP desinstalado

lib_con_ok?: POPF ; recuperar condición de errorPOP ESJMP desinstalado

libera_ext: MOV AH,0AhCALL ES:xms_driverCMP AX,1JE desinstalado ; éxito al liberar memoria XMSSTCJMP desinstalado ; fallo

libera_exp: MOV AH,45hINT 67hCMP AH,0JE desinstaladoCMP AH,82h ; ¿EMM ocupado?JE libera_expSTC ; fallo al liberar memoria EMS

desinstalado: MOV ES:tipo_soporte,0 ; disco «no formateado»JNC desins_okOR lista_err,ERROR14 ; fallo al liberar memoria

Page 235: PCA, PS2 ,IBM y AT

235CONTROLADORES DE DISPOSITIVOS

STCdesins_ok: RETdesinstala 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 PROCMOV tdisco,0 ; ley de MurphyCALL eval_xms ; inicializar «xms_kb»CALL eval_ems ; inicializar «ems_kb»CALL eval_con ; inicializar «con_kb»MOV AX,param_tdisco ; cantidad de memoria necesariaCMP param_a,ONJNE no_ems ; no solicitan memoria EMSMOV BX,ems_kb ; solicitan memoria EMS...AND BX,BXJNZ usara_emsOR lista_err,ERROR7 ; no hay memoria EMS disponibleJMP mem_infoado

usara_ems: CMP AX,BXJBE usar_ems ; piden algo razonableMOV AX,BXOR lista_err,ERROR4 ; rebajado el tamaño

usar_ems: MOV tdisco,AXMOV tipo_soporte,2 ; indicar memoria expandidaJMP mem_infoado

no_ems: CMP param_e,ONJNE no_xms ; no solicitan memoria XMSMOV BX,xms_kb ; solicitan memoria XMS...AND BX,BXJNZ usara_xmsOR lista_err,ERROR6 ; no hay memoria XMS disponibleJMP mem_infoado

usara_xms: CMP AX,BXJBE usar_xms ; piden algo razonableMOV AX,BXOR lista_err,ERROR4 ; rebajado el tamaño

usar_xms: MOV tdisco,AXMOV tipo_soporte,1 ; indicar memoria extendidaJMP mem_infoado

no_xms: CMP param_c,ONJNE no_con ; no solicitan memoria conv.

forzar_con: MOV BX,con_kb ; solicitan memoria conv. ...AND BX,BXJNZ usara_conOR lista_err,ERROR10 ; no hay memoria conv. libreJMP mem_infoado

usara_con: CMP AX,BXJBE usar_con ; piden algo razonableMOV AX,BXOR lista_err,ERROR4 ; rebajado el tamaño

usar_con: MOV tdisco,AXMOV tipo_soporte,3 ; indicar memoria convencionalJMP mem_infoado

no_con: CMP AX,xms_kb ; no indicado tipo de memoriaJBE usar_xms ; intentar emplear memoria XMSCMP ES:rutina_larga,ONJE valdria_emsMOV BX,xms_kbCMP BX,0 ; imposible usar EMSJNE usara_xms ; queda algo de XMSJMP usar_con?

valdria_ems: MOV BX,ems_kbCMP AX,BXJA nv_emsJMP usar_ems ; emplear memoria EMS

nv_ems: MOV BX,ems_kbOR BX,xms_kbJZ usar_con? ; no hay un ápice de XMS ni EMSOR lista_err,ERROR4 ; rebajado el tamaño solicitadoMOV AX,xms_kbCMP AX,ems_kbJAE usar_xms ; hay más o igual XMS que EMSMOV AX,ems_kbJMP usar_ems ; hay algo de EMS (más que XMS)

usar_con?: CMP modo,AUTOEXECJE forzar_con ; sólo se puede usar mem. conv.OR lista_err,ERROR5 ; ho hay memoria EMS ni XMS

mem_infoado: RETmem_info ENDP

; ---- Calcular memoria extendida disponible

eval_xms PROCPUSH ESMOV AX,352FhINT 21h ; dirección de INT 2Fh en ES:BXMOV AX,ESAND AX,AXJZ xms_ok ; apunta a 0000:XXXX (DOS 2.x)MOV AX,4300hINT 2FhCMP AL,80h ; ¿hay controlador XMS?JNE xms_okMOV AX,4310h ; obtener su direcciónINT 2FhMOV xms_segm,ESMOV xms_desp,BXMOV AH,8CALL xms_driver ; preguntar memoria libreAND AX,AXJNZ xms_kb_ok ; no hubo falloCMP BL,0A0hJE xms_kb_ok ; asignada ya toda la memoriaTEST BL,80hJZ xms_kb_ok ; no hay memoria XMS disponibleOR lista_err,ERROR8 ; fallo real del controlador

xms_kb_ok: CMP AX,8 ; mayor bloque XMS disponibleJB xms_okMOV xms_kb,AX ; mínimo necesario: 8 Kb

xms_ok: POP ESRET

eval_xms ENDP

; ---- 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 PROCPUSH ESMOV AX,3567hINT 21h ; vector de INT 67h en ES:BXMOV DI,10

LEA SI,emm_idMOV CX,8CLDREP CMPSB ; ¿instalado controlador EMS?JE ems_existeJMP ems_ok

ems_existe: MOV CX,8000h ; nº de intentos prudenteemm_llama: MOV AH,40h

INT 67hAND AH,AHJZ emm_respondeCMP AH,82hLOOPE emm_llama

emm_fatal: OR lista_err,ERROR9 ; fallo del EMMJMP ems_ok

emm_responde: MOV AH,41hINT 67hAND AH,AHJZ emm_pag_okCMP AH,82hJE emm_responde ; reintentar (EMM ocupado)JMP emm_fatal

emm_pag_ok: MOV ems_pagina0,BX ; inicializar página EMSADD BX,0C00hMOV ems_paginai,BXMOV ems_pagni,3 ; página alternativa: la 3MOV AH,46hINT 67h ; obtener versión del EMMCMP AL,40hJB emm_obt_kb ; versión anterior a la 4.0MOV ems4,ON

emm_obt_pag: XPUSH <ES,DS>POP ESMOV AX,5800h ; obtener dirección de páginasLEA DI,area_trabajoINT 67hPOP ESAND AH,AHJZ emm_pags_okCMP AH,82hJE emm_obt_pagJMP emm_fatal

emm_pags_ok: XOR DX,DXCALL emm_busca_pag ; buscar página 0JC emm_fatalMOV ems_pagina0,BX

ems_busca_i: INC DX ; buscar la siguienteCMP DX,5 ; la 5ª y siguientes no valenJE emm_fatal ;CALL emm_busca_pag ;JC emm_fatal ; > > <-- pág iMOV ems_paginai,BX ;0C00h 32MOV ems_pagni,DL ; pá KbSUB BX,ems_pagina0 ; rraJNC bxpositivo ; fos >NEG BX ;

bxpositivo: CMP BX,0C00h ; > <-- pág 0JB ems_busca_i ; no distan 32 Kb: buscar otra

emm_obt_kb: MOV AH,42hINT 67hAND AH,AHJZ emm_kb_okCMP AH,82hJE emm_obt_kbJMP emm_fatal

emm_kb_ok: MOV CL,4SHL BX,CL ; páginas EMS disponiblesMOV ems_kb,BX ; Kb EMS disponibles (0,16,...)

ems_ok: POP ESRET

eval_ems ENDP

emm_busca_pag PROC ; buscar página nº DX (EMS 4.0)LEA SI,area_trabajoPUSH CX

emm_otra_pag: LODSWMOV BX,AX ; BX = segmento de la páginaLODSW ; AX = nº de la páginaCMP AX,DXJE hallada_pagLOOP emm_otra_pagSTC

hallada_pag: POP CXRET

emm_busca_pag ENDP

; ---- 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 PROCCMP modo,AUTOEXEC ; ¿se ejecuta desde el DOS?JNE conv_ok ; no, desde el configMOV AH,48hMOV BX,0FFFFh ; pedir 1 Mb al DOS (fallará)INT 21hMOV DX,BX ; tamaño del mayor bloqueMOV CL,6SHR BX,CL ; BX = Kb del mayor bloqueSUB BX,128 ; restar 128 KbJC conv_ok ; no quedan ni 128 KbCMP BX,8JB conv_ok ; no quedan siquiera 8 KbMOV con_kb,BXMOV BX,DX ; tamaño del mayor bloqueMOV AH,48hPUSH BXINT 21h ; localizarlo (AX=segmento)POP BXXPUSH <ES,AX> ; preservar ES y segmento (AX)ADD AX,BX ; añadir longitudSUB AX,1024/16*64 ; restar 64 KbMOV segm_reubicar,AX ; segmento de autoreubicaciónPOP ES ; recuperar segmento del bloqueMOV AH,49hINT 21h ; liberarloPOP ES ; recuperar ES

conv_ok: RETeval_con ENDP

; ------------ 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

Page 236: PCA, PS2 ,IBM y AT

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

; ese caso se le renombra, para mejorar la información de; los programas de diagnóstico.

mem_reserva PROCMOV AL,tipo_soporte ; tipo de memoria empleadaDEC ALJZ mem_r_xms ; 1: memoria extendida XMSDEC ALJZ mem_r_ems ; 2: memoria expandida EMSMOV CL,6MOV BX,tdisco ; 3: memoria convencionalSHL BX,CLMOV AH,48hINT 21hMOV mem_handle,AX ; segmento del disco virtualMOV BX,segm_pspMOV tdsk_psp,BX ; inicializar esta variableRET

mem_r_xms: CMP ems4,ONJNE skip_lst_hndlLEA BX,area_trabajoCALL lista_handles ; EMS 4.0+: listado de handles

skip_lst_hndl: MOV AH,9MOV DX,tdiscoCALL xms_driver ; pedir memoria XMSAND AX,AXJNZ mem_rda_xmsOR lista_err,ERROR8 ; fallo del controlador XMSSTC ; indicar error

mem_rda_xms: MOV mem_handle,DXPUSHF ; preservar condición de errorCMP ems4,ONJNE skip_ren_hndlCALL ren_handle ; en EMS 4.0+ renombrar handle

skip_ren_hndl: POPFRET

mem_r_ems: MOV BX,tdiscoADD BX,15AND BL,11110000b ; redondear para arribaMOV tdisco,BXMOV CL,4SHR BX,CL ; Kb -> nº páginas de 16 KbMOV AH,43hINT 67h ; pedir memoria EMSAND AH,AHJZ mem_rda_emsOR lista_err,ERROR9 ; fallo del controlador EMSSTC ; indicar errorRET

mem_rda_ems: MOV mem_handle,DXCMP ems4,ONJNE nhandle_okCALL nombrar_hndl ; en EMS 4.0+ nombrar handle

nhandle_ok: CLCRET

mem_reserva ENDP

ren_handle PROC ; detectar el handle EMS ligadoXPUSH <ES,DS> ; al handle XMS y renombrarloPOP ESLEA BX,area_trabajo[512]CALL lista_handles ; crear nueva lista de handlesLEA SI,area_trabajoLEA DI,area_trabajo[512]MOV CX,256CLDREP CMPSW ; comparar con vieja listaJE ren_hnld_finMOV DX,[DI-2] ; handle nuevoCALL nombrar_hndl

ren_hnld_fin: POP ESRET

ren_handle ENDP

lista_handles PROC ; crear en DS:BX una lista conMOV CX,256 ; los 256 posibles handlesXOR DX,DX ; activos indicando los usados

listar_h: MOV AX,5300hLEA DI,area_trabajo[tam_a_trabajo-8] ; zona no usadaXPUSH <BX,CX,DX>INT 67hXPOP <DX,CX,BX>CMP AH,0JE handle_usadoMOV WORD PTR [BX],0 ; error (handle no usado)JMP lista_h

handle_usado: MOV [BX],DX ; anotar número de handlelista_h: ADD BX,2

INC DXLOOP listar_hRET

lista_handles ENDP

nombrar_hndl PROC ; nombrar handle (EMS 4.0+)MOV AX,5301hLEA SI,nombre_tdskMOV BL,letra_unidadMOV [SI+5],BLINT 67h ; dar nombre al handleRET

nombrar_hndl ENDP

; ------------ Detectar 286 y 386 o superior.

test_CPU PROCPUSHFPOP AXOR AH,70h ; intentar activar bit 12, 13 ó 14PUSH AX ; del registro de estadoPOPFPUSHFPOP AXAND AH,0F0hCMP AH,0F0hJE fin_test_CPU ; es 8086 o similarMOV cpu286,ON ; es 286 o superiorAND AH,70h ; 286 pone bits 12, 13 y 14 a ceroJZ fin_test_CPU ; es 286MOV cpu386,ON ; 386 o superior

fin_test_CPU: RETtest_CPU ENDP

; ------------ Definir valores por defecto y adaptar los parámetros; indicados por el usuario a la realidad. Esta rutina; inicializa el futuro sector 0 del disco. No se permite; que el usuario indique un directorio que ocupe más de; medio disco. Para determinar el tipo de FAT se halla el; nº de sectores libres del disco (llamémoslo nsect),

; 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.

adaptar_param PROCMOV AX,tdisco ; en KbMOV BX,AX ; entradas de directorio propuestasMOV CL,1 ; sectores por cluster propuestosCMP AX,128 ; ¿disco de 128 Kb o menos?JBE prop_okMOV BX,128CMP AX,512 ; ¿disco de 512 Kb o menos?JBE prop_okMOV BX,256CMP AX,2042 ; ¿disco de casi 2 Mb o menos?JBE prop_okMOV CL,2 ; evitar FAT16CMP AX,4084 ; ¿disco de casi 4 Mb o menos?JBE prop_okMOV CL,4 ; evitar FAT16 hasta 8 MbMOV BX,384CMP AX,16384 ; ¿disco de menos de 16 Mb?JB prop_okMOV BX,512

prop_ok: CMP dosver,300hJAE prop_validoCMP AX,4084*2 ; en DOS 2.xx evitar FAT16JB prop_validoMOV CL,8CMP AX,4084*4JB prop_validoMOV CL,16CMP AX,4084*8JB prop_validoMOV CL,32

prop_valido: MOV tdir,BXMOV tcluster,CL ; inicializar valores recomendadosMOV DX,1024 ; AX = tamaño del disco en KbMUL DX ; DX:AX = bytes totales del discoMOV CX,param_tsectAND CX,CXJNZ tsect_def ; se ha definido tamaño de sector

tsect_rec: MOV CX,tsect ; tamaño por defectotsect_def: CALL divCX

JNC nsect_ok ; menos de 65536 sectores: correctoOR lista_err,ERROR11JMP tsect_rec ; asumir por defecto y recalcular

nsect_ok: MOV tsect,CXMOV numsect,AXMOV BX,AXSHR BX,1 ; BX = 1/2 del nº total de sectoresMOV CX,param_tdirAND CX,CXJNZ tdir_def ; se ha definido nº entradas

tdir_rec: MOV CX,tdir ; nº por defectotdir_def: MOV AX,tsect

XOR DX,DXMOV SI,32 ; 32 bytes = tamaño entrada direct.DIV SI ; AX nº entradas direct. por sectorXCHG AX,CXXOR DX,DX ; DX:AX = nº de entradasDIV CX ; CX = entradas en cada sectorAND DX,DX ; AX = nº sectores del ROOTJZ dir_ok?INC AX ; redondear tamaño de ROOT

dir_ok?: CMP AX,BX ; BX = 1/2 nº sectores del discoJB dir_okOR lista_err,ERROR12 ; directorio excesivoJMP tdir_rec ; directorio por defecto

dir_ok: MOV sdir,AXMUL tsectMOV CX,32CALL divCXMOV tdir,AX ; optimizar tamaño de directorioMOV AX,512XOR DX,DXDIV tsect ; 512 / tamaño de sectorMOV BL,tclusterXOR BH,BHMUL BX ; ajustar tamaño de clusterAND AL,ALJZ propclus_okMOV tcluster,AL

propclus_ok: MOV BX,param_tclusterAND BX,BXJNZ tcluster_def ; se ha definido tamaño de cluster

tcluster_rec: MOV BL,tcluster ; tamaño por defectoXOR BH,BH

tcluster_def: SHL BX,1CMP BX,numsect ; ¿cabe seguro un cluster?JB tcluster_ok

tcluster_mal: OR lista_err,ERROR13 ; tamaño de cluster incorrectoJMP tcluster_rec

tcluster_ok: SHR BX,1MOV AX,tsectMUL BX ; DX:AX = tamaño de clusterJC tcluster_malCMP AX,31*1024JA tcluster_mal ; cluster de más de 31 KbMOV tcluster,BL ; sectores por clusterMOV tamcluster,AX ; tamaño de clusterMOV CX,param_f ; considerar número de FATsMOV nfats,CLMOV SI,3MOV CX,param_fSHL SI,CLSHR SI,1CALL eval_clust ; obtener nº más alto de clusterCMP AX,4086

Page 237: PCA, PS2 ,IBM y AT

237CONTROLADORES DE DISPOSITIVOS

JAE fat16 ; el nº más alto supera 4085MOV CX,3MUL CX ; clusters * 3SHR DX,1RCR AX,1 ; clusters * 3 / 2 = clusters * 1,5JMP calc_sfat

fat16: MOV SI,4MOV CX,param_f ; considerar número de FATsSHL SI,CLSHR SI,1CALL eval_clustSHL AX,1RCL DX,1 ; clusters * 2

calc_sfat: DIV tsect ; AX = nº sectores de FAT aprox.AND DX,DXJZ fat_okINC AX ; redondeo

fat_ok: MOV sfat,AXMOV AX,numsect ; nº total de sectoresDEC AX ; descontar BOOTSUB AX,sdir ; descontar ROOTSUB AX,sfat ; descontar FATMOV CL,tclusterXOR CH,CHXOR DX,DXDIV CX ; AX = número real de clustersINC AX ; se numeran desde 2MOV ultclus,AXRET

adaptar_param ENDP

eval_clust PROC ; obtener el nº más alto de clusterMOV AX,numsectDEC AX ; restar BOOTSUB AX,sdir ; restar ROOTMUL tsect ; DX:AX = nsect * tamsectSHL AX,1RCL DX,1 ; DX:AX = nsect * tamsect * 2MOV CX,tamclusterSHL CX,1ADD CX,SI ; CX = 2 * tamcluster + SIDIV CXINC AX ; los clusters se numeran desde 2AND DX,DX ; ¿sobra un «cacho» de cluster?JZ clust_eval ; redondear: ¡es preferible queINC AX ; sobre un poco de FAT a que falte!

clust_eval: XOR DX,DX ; resultado en DX:AXRET

eval_clust ENDP

; ------------ Preparar el BPB del disco virtual según los parámetros; y forzar que el DOS lo lea indicando cambio de disco.

preparar_BPB PROCMOV AX,tsectMOV bytes_sector,AXMOV AL,tclusterMOV sect_cluster,ALMOV AX,tdirMOV entradas_raiz,AXMOV AX,numsectMOV num_sect,AXMOV AL,nfatsMOV num_fats,ALMOV AX,sfatMOV sectores_fat,AXMOV cambiado,0FFh ; ha habido «cambio» de discoRET

preparar_BPB ENDP

; ------------ Preparar el disco para operar. ES apunta al disco al; entrar. Se procederá a copiar la rutina necesaria en; función del tipo de memoria que gestiona el disco.; Después, se copiarán las variables que gestionan TDSK; sobre la copia residente, así como el nuevo BPB.

prep_driver PROCMOV AL,tipo_soporteLEA SI,procesa_xmsMOV CX,tam_proc_xmsDEC ALJZ prep_mem ; instalar rutina XMSLEA SI,procesa_emsMOV CX,tam_proc_emsDEC ALJZ prep_mem ; instalar rutina EMSLEA SI,procesa_conMOV CX,tam_proc_con ; instalar rutina memoria conv.

prep_mem: LEA DI,procesa_ioCLDXPUSH <SI,DI,CX>REP MOVSB ; instalar rutina en el discoXPOP <CX,DI,SI>XPUSH <ES,DS>POP ESREP MOVSB ; y en el propio TDSK.EXE (paraPOP ES ; usarla después al formatear)LEA CX,f_tdsk_ctrlLEA SI,i_tdsk_ctrlSUB CX,SIMOV DI,SIREP MOVSB ; actualizar variablesLEA CX,fin_bpbLEA SI,bpbSUB CX,SIMOV DI,SIREP MOVSB ; actualizar BPBRET

prep_driver ENDP

; ------------ Autorelocalización de TDSK.EXE; Es necesario si se reserva memoria convencional para el; disco virtual. El motivo es evitar que al inicializar; la BOOT, la FAT y el ROOT al inicio del disco, si éste; está justo encima de TDSK, TDSK se autodestruya. Por; ello, TDSK se autocopiará en la mitad de los 128 Kb del; mayor bloque de memoria libre, nunca utilizados por el; disco (aunque este bloque no haya sido reservado, ¡como; está libre!). Finalmente pasará a correr en ese nuevo; destino. Se copia TODO, pila incluida. La copia se hace; en «segm_reubicar» que apunta a la mitad de esos 128 Kb; con objeto de evitar solapamientos origen/destino (TDSK; ocupa sólo alrededor de 16 Kb en memoria).

relocalizar PROCCMP tipo_soporte,3JE procede_reloc ; usada memoria convencionalRET

procede_reloc: PUSH ES ; * preservar ESMOV ES,segm_reubicar ; segmento de reubicaciónXOR SI,SIXOR DI,DIMOV BX,SS ; final de TURBODSK (pila)MOV CX,DS ; inicio de _PRINCIPALSUB BX,CX ; tamaño de TDSK en párrafosMOV CL,4SHL BX,CL ; ahora en bytesADD BX,tam_pila+16 ; 16 por si acasoMOV CX,BX ; CX = bytes a relocalizarCLDREP MOVSB ; auto-copiaje arribaMOV AX,ESMOV DS,AX ; nuevo segmento de datosPOP ES ; * restaurar ESMOV BX,CSSUB AX,BX ; ES - CS --> cuantía del saltoMOV BX,SSADD BX,AXMOV SS,BX ; actualizar segmento de pilaPOP AX ; dirección de retorno cercanoPUSH DS ; segmento de «retorno»PUSH AX ; offsetRETF ; retorno cargando CS:

relocalizar ENDP

; ------------ Inicializar la BOOT, FAT y ROOT del disco virtual.; En versiones del DOS anteriores a la 3.3, el sistema; inexplicablemente hace caso omiso del cambio de disco; (¿?), por lo que hay que avisarle ¡dos veces!, con el; correspondiente doble cambio del byte descriptor de; medio, para que se tome en serio el cambio de disco.; Por fortuna desde el DOS 3.3 ya no es preciso hacer; esta extraña maniobra. Para que el DOS acceda al disco,; se le pregunta simplemente el espacio libre del mismo.

formatear_tdsk PROCPUSH ES ; *PUSH DSPOP ESLEA SI,sector_ceroLEA DI,area_trabajoMOV CX,128CLDREP MOVSB ; primeros 128 bytes del BOOTXOR AX,AXMOV CX,tam_a_trabajo-128REP STOSB ; a 0 resto del área de trabajoLEA DI,area_trabajoADD DI,tsectMOV [DI-2],0AA55h ; marca de sector válidoCALL escribe_sectAX ; escribir sector BOOT (AX=0)LEA DI,area_trabajoMOV CX,tsectREP STOSB ; borrar area de trabajoMOV AX,sfatMOV CX,param_f ; considerar número de FATsSHL AX,CLSHR AX,1ADD AX,sdir ; AX = sectores fat + dir. raiz

ini_fat: CMP AX,1JE pfatCALL escribe_sectAX ; inicializar directorio raizDEC AX ; y últimos sectores de la FATJMP ini_fat

pfat: LEA DI,area_trabajoMOV BYTE PTR [DI],mediaMOV AX,0FFFFh ; inicializar 3 bytes FAT...MOV DS:[DI+1],AXCMP ultclus,4086 ; ¿menos de 4085 clusters?JB pfat_okMOV DS:[DI+3],AL ; inicializar 4º byte FAT

pfat_ok: MOV AX,1CALL escribe_sectAX ; primer sector FAT preparadoCALL fecha_horaLEA SI,dir_raizMOV [SI+22],AX ; hora actualMOV [SI+24],DX ; fecha actualLEA DI,area_trabajoMOV CX,32REP MOVSBMOV AX,sfatMOV CX,param_f ; considerar número de FATsSHL AX,CLSHR AX,1INC AXCALL escribe_sectAX ; primer sector raiz preparadoPOP ES ; *CMP dosver,31EhJAE formateado ; DOS 3.3+NOT ES:media_byte ; cambiar descriptor de medioMOV AH,36h ; «obtener espacio libre»MOV DL,ES:letra_unidadSUB DL,’A’-1 ; unidad de disco virtualPUSH DXINT 21h ; primer acceso al discoPOP DXNOT ES:media_byte ; restaurar descriptor de medioMOV ES:cambiado,0FFh ; nuevo «cambio» de discoMOV AH,36hINT 21h ; acceder otra vez al disco

formateado: RETformatear_tdsk ENDP

; ---- Escribir el sector nº AX del disco virtual. No; se utiliza INT 26h (imposible desde el CONFIG).

escribe_sectAX PROCPUSHF ; preservar bit DFXPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>XOR BP,BP ; indicar escrituraLEA DI,area_trabajo ; ES:DI bufferMOV BX,AX ; número de sectorMOV AX,1 ; 1 sectorCALL io_proc ; acceder al disco directamenteXPOP <ES,DS,BP,DI,SI,DX,CX,BX,AX>POPFRET

escribe_sectAX ENDP

; ---- Obtener fecha y hora del sistema en DX y AX

fecha_hora PROCMOV AH,2AhINT 21h ; obtener fecha del sistemaMOV AL,32MUL DH ; AX = mes * 32

Page 238: PCA, PS2 ,IBM y AT

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

SUB CX,1980SHL CL,1 ; (año-1980)*2ADD AH,CL ; sumar (año-1980)*512MOV CL,DL ; CX = dia (CH=0)ADD AX,CXPUSH AX ; * guardar fechaMOV AH,2ChINT 21h ; obtener hora del sistemaMOV AL,32MUL CL ; AX = minutos*32MOV CL,3SHL CH,CLXOR CL,CL ; CX = hora*2048ADD AX,CXSHR DH,1 ; segundos/2ADD AL,DHADC AH,0POP DX ; * recuperar fechaRET

fecha_hora ENDP

; ------------ Cambiar el nombre al bloque de control de memoria para; mejorar la información del comando MEM del sistema si; el disco se define en memoria convencional/superior.

renombrar_mcb PROCPUSH ESMOV AL,letra_unidadMOV BYTE PTR nombre_tdsk+5,ALMOV BYTE PTR nombre_tdsk+4,’(’MOV BYTE PTR nombre_tdsk+6,’)’MOV AX,segm_pspDEC AXMOV ES,AXLEA SI,nombre_tdskMOV DI,8MOV CX,DICLDREP MOVSBPOP ESRET

renombrar_mcb ENDP

; ------------ Informar sobre el disco virtual instalado.

info_disco PROCCALL InitMultiPrintLEA DX,ayuda_txt ; ayuda en españolCMP param_h,ON ; ¿solicitud de ayuda?JNE cont_info ; noJMP info_exit

cont_info: TEST err_grave,0FFFFhJZ info_no_fatalLEA DX,err_grave_gen ; texto de encabezamientoCALL imprimir ; imprimir errores graves:LEA DX,e0TEST err_grave,ERROR0JZ otro_fallo ; no es error de DOS incorrectoCALL imprimirMOV SP,tam_pilaPUSH segm_psp ; en DOS 1.x hay que terminarXOR AX,AX ; con CS = PSPPUSH AXRETF ; ejecutar INT 20h de PSP:0

otro_fallo: LEA DX,e1TEST err_grave,ERROR1JNZ info_gLEA DX,e2TEST err_grave,ERROR2JNZ info_gLEA DX,e3

info_g: JMP info_exitinfo_no_fatal: CMP ES:tipo_soporte,0 ; error no fatal

JNE info_reporteLEA DX,info_ins ; disco no formateadoCALL imprimirCALL impr_unidadLEA DX,info_ins2CMP lista_err,0JE info_exit ; sin mensajes de advertenciaCALL imprimir ; ... o con ellosJMP info_err

info_reporte: CALL pr_info ; disco formateadoCMP lista_err,0JE info_ret ; sin mensajes de advertenciaLEA DX,cab_adv_txt ; ... o con ellosCALL imprimir ; cabecera de advertencias

info_err: MOV AX,lista_errLEA BX,tabla_mens-2 ; tabla de mensajesMOV CX,16 ; 16 posibles mensajes

busca_err: ADD BX,2SHR AX,1JC informa

mas_mens: LOOP busca_err ; no se produce ese errorJMP info_ret

informa: LEA DX,mens_cabec ; inicio común a los mensajesCALL imprimirMOV DX,[BX] ; dirección de ese mensajeCALL imprimirJMP mas_mens ; acabar con todos

info_exit: CALL imprimirinfo_ret: RETinfo_disco ENDP

pr_info PROCLEA DX,info_txtCALL imprimirCALL impr_unidadLEA DX,inf_tsectCALL imprimirMOV AX,ES:bytes_sectorXOR DX,DXMOV CL,5CALL print_32LEA DX,inf_tdirCALL imprimirMOV AX,ES:entradas_raizXOR DX,DXMOV CL,5CALL print_32LEA DX,inf_tdiscoCALL imprimirMOV AX,ES:num_sectMUL ES:bytes_sectorMOV BX,1024DIV BXMOV CL,5CALL print_32

LEA DX,inf_tclusterCALL imprimirMOV AL,ES:sect_clusterXOR AH,AHXOR DX,DXMOV CL,5CALL print_32LEA DX,inf_memCALL imprimirMOV AL,ES:tipo_soporteLEA DX,inf_mem_xmsDEC ALJZ mem_ifdo ; memoria XMSLEA DX,inf_mem_emsDEC ALJZ mem_ifdo ; memoria EMSLEA DX,inf_mem_conCMP ES:mem_handle,0A000hJB mem_ifdo ; memoria convencionalLEA DX,inf_mem_sup ; memoria superior

mem_ifdo: CALL imprimirLEA DX,inf_nclustersCALL imprimir

MOV AX,ES:entradas_raizMOV BX,32MUL BX ; bytes ocupados por directorioDIV ES:bytes_sector ; AX = sectores del directorioADD AX,ES:sect_reservADD AX,ES:sectores_fatSUB AX,ES:num_sectNEG AX ; AX = sectores libresXOR DX,DXMOV BL,ES:sect_clusterXOR BH,BHDIV BX ; AX = nº de clustersXOR DX,DXMOV CL,5CALL print_32LEA DX,inf_tfatCALL imprimirLEA DX,inf_tfat12CMP AX,4085 ; ¿FAT12?JB ifat_okLEA DX,inf_tfat16

ifat_ok: CALL imprimirLEA DX,inf_finalCALL imprimirRET

pr_info ENDP

; --- Imprimir letra de unidad en AL.

impr_unidad PROCXPUSH <AX, DX>MOV AL,letra_unidadMOV AH,0MOV WORD PTR area_trabajo,AXLEA DX,area_trabajoCALL imprimirXPOP <DX, AX>RET

impr_unidad ENDP

; --- Imprimir un nº decimal de 32 bits en DXAX formateado por CL.;; 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, empezando por la derecha,; se consideran parte fraccional, e irán precedidos; del correspondiente separador);; Salidas: nº impreso, ningún registro modificado.;; * Ejemplo, si DXAX=9384320 y CL=010 1 1011; se imprimirá ( ’_’ representa un espacio en blanco ): __93.843,20

print_32 PROCPUSH DSPUSH ESPUSH CSPUSH CSPOP DSPOP ESPUSH AX ; preservar todos los registrosPUSH BXPUSH CXPUSH DXPUSH SIPUSH DIPUSHFMOV formato_pr32,CL ; byte del formato de impresión

elegidoMOV CX,idioma_seps

separ_pr32: MOV millares_pr32,CH ; separador de millaresMOV fracc_pr32,CL ; separador parte fraccionalMOV BX,OFFSET tabla_pr32MOV CX,10

digit_pr32: PUSH CXPUSH AXPUSH DXXOR DI,DIMOV SI,1 ; DISI = 1DEC CX ; CX - 1JCXZ hecho_pr32

factor_pr32: SAL SI,1RCL DI,1 ; DISI * 2MOV DX,DIMOV AX,SISAL SI,1RCL DI,1SAL SI,1RCL DI,1 ; DISI * 8ADD SI,AXADC DI,DX ; DISI = DISI*8 + DISI*2 = DISI*10LOOP factor_pr32 ; DISI = DISI*10*10* ... (CX-1

veces)hecho_pr32: POP DX ; luego DISI = 10 elevado a (CX-1)

POP AX ; CX se recuperará más tardeMOV CL,0FFh

rep_sub_pr32: INC CLSUB AX,SISBB DX,DI ; DXAX = DXAX - DISIJNC rep_sub_pr32 ; restar el factor cuanto se puedaADD AX,SI ; subsanar el desbordamiento:

Page 239: PCA, PS2 ,IBM y AT

239CONTROLADORES DE DISPOSITIVOS

ADC DX,DI ; DXAX = DXAX + DISIADD CL,’0’ ; pasar binario a ASCIIMOV [BX],CLPOP CX ; CX se recupera ahoraINC BXLOOP digit_pr32 ; próximo dígito del númeroSTD ; transferencias (MOVS) hacia

atrásDEC BX ; BX apunta al último dígitoMOV final_pr32,BX ; último dígitoMOV ent_frac_pr32,BX ; frontera parte entera/fraccionalMOV CL,5MOV AL,formato_pr32SHR AL,CL ; AL = nº de decimalesAND AL,ALJZ no_frac_pr32 ; ningunoMOV CL,ALXOR CH,CHMOV SI,final_pr32MOV DI,SIINC DIREP MOVSB ; correr cadena arriba (hacer

hueco)INC final_pr32MOV AL,fracc_pr32MOV [DI],AL ; poner separador de parte

fraccionalMOV ent_frac_pr32,SI ; indicar nueva frontera

no_frac_pr32: MOV AL,formato_pr32TEST AL,16 ; interpretar el formato

especificadoJZ poner_pr32 ; imprimir como tal

entera_pr32: MOV CX,final_pr32 ; añadir separadores de millarSUB CX,ent_frac_pr32ADD CX,3MOV SI,final_pr32MOV DI,SIINC DIREP MOVSB ; correr cadena arriba (hacer

hueco)MOV AL,millares_pr32MOV [DI],AL ; poner separador de millaresINC final_pr32MOV ent_frac_pr32,SI ; usar esta variable como punteroSUB SI,OFFSET tabla_pr32CMP SI,3JAE entera_pr32 ; próximo separador

poner_pr32: MOV BX,final_pr32MOV BYTE PTR [BX+1],0 ; delimitador de fin de cadenaMOV BX,OFFSET tabla_pr32MOV principio_pr32,BX ; inicio de cadena

limpiar_pr32: MOV AL,[BX]CMP AL,’0’JE blanco_pr32 ; cero a la izda --> poner " "CMP AL,millares_pr32 ; separador millares a la izdaJE blanco_pr32CMP AL,fracc_pr32JNE acabar_pr32MOV BYTE PTR [BX-1],’0’ ; reponer 0 antes de la comaDEC principio_pr32

acabar_pr32: MOV AL,formato_pr32 ; imprimirAND AL,00001111bXOR AH,AHMOV DX,final_pr32SUB DX,AXINC DX ; DX = offset ’principio’AND AX,AXJNZ format_pr32 ; longitud especificada por el

usuarioMOV DX,principio_pr32 ; longitud obtenida del número

format_pr32: CALL imprimirPOPF ; restaurar todos los registrosPOP DIPOP SIPOP DXPOP CXPOP BXPOP AXPOP ESPOP DSRET ; salida del procedimiento

blanco_pr32: MOV BYTE PTR [BX],’ ’ ; sustituir 0 ó separador demillares

INC BX ; a la izda. por espacio en blancoINC principio_pr32CMP BX,final_pr32JB limpiar_pr32MOV DX,BX ; es el número 0.000.000.00XJMP SHORT acabar_pr32 ; imprimir

formato_pr32 DB 0DB 5 DUP (’ ’) ; espacios en blanco para cubrir

la; mayor plantilla que pueda ser

espe-; cificada en el formato

tabla_pr32 DT 0 ; reservar 14 bytes (nº más ., másASCIIZ)

DW 0,0 ; aquí se solapa un buffer de 32bytesmillares_pr32 DB ’.’ ; separador de millaresfracc_pr32 DB ’,’ ; " parte fraccionalfinal_pr32 DW 0 ; offset al último byte a imprimirprincipio_pr32 DW 0 ; " " primer " " "ent_frac_pr32 DW 0 ; offset a la fronteraentero-fracc.

DT 0 ; $ - tabla_pr32 = 32 bytes usadospor

; INT 21h al principio de print_32print_32 ENDP

; ------------ Dividir DX:AX / CX sin desbordamientos (cociente: AX,; resto: DX). Si el cociente excede los 16 bits, CF = 1; y todos los registros intactos.

divCX PROCXPUSH <BX,SI,CX,AX,DX>MOV SI,32XOR BX,BX

divmas: SHL AX,1RCL DX,1RCL BX,1CMP BX,CXJB dividido ; "no cabe"SUB BX,CXINC AL ; 1 al cociente

dividido: DEC SIJNZ divmasAND DX,DX

JZ div_okXPOP <DX,AX> ; errorSTCJMP div_fin

div_ok: MOV DX,BX ; resto en DX y cociente en AXADD SP,4 ; «sacar» sin sacar DX y AXCLC

div_fin: XPOP <CX,SI,BX> ; recuperar CX, SI y BXRET

divCX ENDP

; ------------ Impresión en color o monocroma (esta última; redireccionable). Desde el CONFIG.SYS se imprime en; monocromo para no llamar la atención, a menos que; indiquen /M, al contrario que desde el DOS.

imprimir PROCPUSH AXMOV AL,param_mCMP modo,CONFIG ; ¿en CONFIG.SYS?JNE m_ok ; noXOR AL,ON ; sí: /M opera al revés

m_ok: MOV pr_mono,ALCALL printPOP AXRET

imprimir ENDP

; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.; Si acaba en 0, se imprime como tal; en caso contrario,; se supone que el mensaje es multilingüe y los diversos; idiomas (1, 2, ... N) separan sus cadenas por sucesivos; códigos 255. El carácter de control 127 realiza una; pausa hasta que se pulsa una tecla.

print PROCXPUSH <AX, BX, CX, DX, SI, DI, ES>CMP idioma,0JNE pr_decidirPUSH DX ; *MOV AH,30hINT 21hXCHG AH,ALMOV CX,AX ; CX = versión del DOSCMP param_i,ONMOV AX,codigo_tfnoMOV BX,1234hJNE pr_busca_cod ; parámetro /I=cod no indicadoMOV BX,AXMOV AL,0FFhCMP BX,255JAE pr_cod ; código mayor o igual de 255MOV AL,BL ; código menor de 255

pr_cod: CMP CX,200hJAE pr_cod_tfno ; DOS >= 2.X

pr_busca_cod: CMP CX,200hMOV AX,1 ; inglés para DOS < 2.XJB pr_habla_axMOV AL,0

pr_cod_tfno: LEA DX,area_trabajoMOV AH,38hXPUSH <BX, CX>INT 21h ; obtener información del paisXPOP <CX, AX>JC pr_habla_ax ; fallo en la funciónCMP CX,20BhJE pr_habla_ax ; DOS 2.11: AX cód. telefónicoCMP CX,300hMOV AX,1JB pr_habla_ax ; 2.x excepto 2.11: mala suerteMOV AX,BXLEA BX,area_trabajoMOV CH,[BX+7] ; separador de millaresMOV CL,[BX+9] ; separador de decimalesMOV idioma_seps,CX

pr_habla_ax: LEA BX,info_paises-2MOV CX,1 ; supuesto idioma 1

pr_busca_idi: ADD BX,2MOV DX,[BX]CMP AX,DXJE pr_habla_eseAND DX,DXJNZ pr_busca_idiINC CX ; será otro idiomaCMP [BX+2],DXJNE pr_busca_idi ; no es fin de la tabla

pr_habla_ese: MOV idioma,CLPOP DX ; *

pr_decidir: MOV CL,idiomaMOV CH,0 ; nº de idioma a usar (1..N)MOV BX,DX

pr_busca_msg: MOV DX,BXDEC BX

pr_busca_ter: INC BXCMP BYTE PTR [BX],0JE pr_usar_ese ; acaba en 0: no buscar másCMP BYTE PTR [BX],255JNE pr_busca_terINC BXLOOP pr_busca_msg ; acaba en 255 pero no es ese

pr_usar_ese: MOV BX,DXDEC BX

pr_cad_lon: INC BXCMP BYTE PTR [BX],0JE prlong_okCMP BYTE PTR [BX],127 ; carácter de pausaJE prpausaCMP BYTE PTR [BX],255JNE pr_cad_lon ; calcular longitudJMP prlong_ok

prpausa: PUSH BXMOV CX,BXSUB CX,DXCALL pr_cad ; imprimir hasta el código 127

pr_limpbuf: MOV AH,1INT 16hJZ pr_notecMOV AH,0INT 16h ; limpiar buffer del tecladoJMP pr_limpbuf

pr_notec: MOV AH,0INT 16h ; esperar teclaPOP BXINC BXMOV DX,BXCMP AL,27 ; ¿tecla ESC?STC

Page 240: PCA, PS2 ,IBM y AT

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

JE pr_retJMP pr_cad_lon ; imprimir el resto

prlong_ok: MOV CX,BXSUB CX,DXCALL pr_cad ; terminar impresiónCLC

pr_ret: XPOP <ES, DI, SI, DX, CX, BX, AX> ; CF=1 si se pulsó ESCRET

pr_cad: ; MOV AH,40h; MOV BX,1; INT 21h ; imprimir con el DOSMOV SI,DXLEA DI,area_trabajoPUSH DSPOP ES ; por si acasoCLDREP MOVSBMOV [DI],CL ; ASCIIZLEA DX,area_trabajoCALL MultiPrint ; imprimir en colorRET

print ENDP

; ------------ Impresión en pantalla, en color o monocromo, usando el; BIOS o el DOS respectivamente. Antes deberá ejecutarse; InitMultiPrint para inicializar. Al hacer scroll se; intenta respetar el posible color global de fondo.; Con «pr_mono» en ON se solicita imprimir en monocromo.;; - El texto a imprimir es apuntado por DS:DX.; - Códigos de control soportados:;; 0 -> final de cadena; 1 -> el siguiente carácter indica el color (BIOS); 2 -> el siguiente carácter indica el nº de veces que; se imprimirá el que viene detrás; 3 -> avanzar cursor a la derecha; 10 -> retorno de carro y salto de línea estilo UNIX

MultiPrint PROCXPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>PUSH DSPOP ESPUSH CSPOP DSLEA AX,pr_AL_dosCMP pr_mono,ONJE pr_rut_okLEA AX,pr_AL_bios

pr_rut_ok: MOV pr_rut,AX ; instalar rutina de impresiónMOV BX,DX

pr_otro: MOV AL,ES:[BX]PUSH BXCMP AL,’ ’JAE pr_ASCII ; no es un código de controlAND AL,ALJZ pr_exit ; código de control 0: finalCMP AL,1JE pr_setcolor ; código de control 1: colorCMP AL,2JE pr_setveces ; código de control 2: repetir

pr_ASCII: CALL pr_rutPOP BXINC BXJMP pr_otro

pr_setcolor: MOV AL,ES:[BX+1]MOV pr_color,AL ; actualizar colorPOP BXADD BX,2JMP pr_otro

pr_setveces: MOV AL,ES:[BX+1]MOV pr_veces,AL ; actualizar repeticionesPOP BXADD BX,2JMP pr_otro

pr_exit: XPOP <BX,ES,DS,BP,DI,SI,DX,CX,BX,AX>RET

MultiPrint ENDP

pr_AL_bios PROC ; imprimir en color usando BIOSPUSH AXMOV AH,3MOV BH,pr_paginaINT 10h ; DX = coordenadas del cursorPOP AXCMP AL,3JE pr_derecha ; código de control 3: avanzarCMP AL,10JE pr_crlf ; código de control 10: CR & LFMOV AH,9MOV BH,pr_paginaMOV BL,pr_colorMOV CL,pr_vecesXOR CH,CHPUSH DXINT 10h ; imprimir carácterPOP DX

pr_derecha: ADD DL,pr_vecesMOV pr_veces,1CMP DL,pr_maxXJBE pr_av

pr_crlf: XOR DL,DL ; volver al inicio de líneaINC DH ; salto a la siguienteCMP DH,pr_maxYJBE pr_avDEC DHPUSH DX ; es preciso hacer scrollMOV AX,601hMOV BH,pr_colorb ; color por defectoXOR CX,CXMOV DL,pr_maxXMOV DH,pr_maxYINT 10h ; hacer scroll usando BIOSPOP DX

pr_av: MOV BH,pr_paginaMOV AH,2INT 10h ; posicionar cursorRET ; retorno del procedimiento

pr_AL_bios ENDP

pr_AL_dos PROC ; imprimir usando DOSCMP AL,3JNE pr_no_derMOV AL,’ ’ ; código de control 3: avanzar

pr_no_der: CMP AL,10JNE pr_dosMOV AL,13 ; código de control 10: CR & LFCALL pr_dos ; llamada "recursiva"

MOV AL,10pr_dos: MOV CL,pr_veces

XOR CH,CHMOV pr_veces,1MOV DL,AL

pr_chr: XPUSH <DX,CX>MOV AH,2INT 21h ; imprimir carácterXPOP <CX,DX>LOOP pr_chrRET

pr_AL_dos ENDP

InitMultiPrint PROCXPUSH <AX,BX,CX,DX,BP,DS,ES>PUSH CSPOP DSMOV pr_veces,1MOV pr_color,15 ; valores por defectoMOV pr_mono,OFF

pr_i_80?: MOV AH,0FhINT 10hCMP AH,80 ; ¿80 ó más columnas?JAE pr_i_video_ok ; así esMOV AX,3INT 10h ; forzar modo de 80 columnasJMP pr_i_80?

pr_i_video_ok: MOV pr_maxX,AH ; inicializar máxima coord. XMOV pr_pagina,BH ; inicializar página activaMOV AX,40hMOV ES,AX ; ES: -> variables del BIOSMOV AL,ES:[84h] ; variable de nº líneas - 1CMP AL,24 ; ¿el BIOS define la variable?JB pr_i_maxy_ok ; noMOV pr_maxY,AL ; inicializar máxima coord. Y

pr_i_maxy_ok: MOV AH,8 ; (BH = página)INT 10h ; obtener color por defectoMOV pr_colorb,AHXPOP <ES,DS,BP,DX,CX,BX,AX>RET

InitMultiPrint ENDP

pr_pagina DB 0 ; página de visualización activapr_veces DB 1 ; veces que se imprime cada carácterpr_color DB 15 ; color BIOS para imprimirpr_colorb DB ? ; color por defecto en pantallapr_maxX DB 80 ; máxima coordenada X en pantallapr_maxY DB 24 ; máxima coordenada Y en pantallapr_mono DB OFF ; a ON si imprimir en monocromopr_rut DW ? ; apunta a pr_AL_bios / pr_AL_dos

; ------------ Rutina de gestión de memoria XMS. Se copiará sobre; la de memoria EMS si se utiliza memoria XMS.; En esta rutina se emplea la pila para pasar los; parámetros al controlador XMS.

procesa_xms PROCMOV DS,CS:mem_handleJNC no_xmslib.286 ; rutina ejecutada desde 286+PUSHA ; sistema reinicializando:MOV AH,0DhCALL llama_XMS ; desbloquear EMB (prudente)MOV AH,0AhCALL llama_XMS ; liberar EMBPOPA.8086RET

no_xmslib: DEC BP ; leer/escribir en el discoJNZ xms_escribePUSH ESPUSH DI ; segmento:offset destinoPUSH BP ; handle destino (BP=0)

xms_escribe: PUSH DXPUSH AX ; desplazamiento DX:AXPUSH DS ; handle fuente/destinoJZ xms_generalINC BP ; hacer BP = 0PUSH ESPUSH DI ; segmento:offset fuentePUSH BP ; handle fuente (BP=0)

xms_general: SHL CX,1 ; palabras -> bytesRCL BP,1 ; BP era 0PUSH BP ; tamaño bloque (parte alta)PUSH CX ; tamaño bloque (parte baja)MOV SI,SPPUSH SSPOP DS ; DS:SI apuntando a la pilaMOV AH,0Bh ; función para mover EMBCALL llama_XMS ; mover EMB (DS no importa)ADD SP,16 ; equilibrar pilaCMP AL,1 ; ¿falló el controlador?JE xms_proc_okMOV AX,0C81h ; anomalía general

xms_proc_ok: XCHG AH,AL ; colocar resultadoRET

procesa_xms ENDP

llama_XMS PROCMOV DX,DS ; handle en DS (si utilizado)CALL CS:xms_driver ; ejecutar función XMSRET

llama_XMS ENDP

tam_proc_xms EQU $-OFFSET procesa_xms ; tamaño de esta rutina

; ------------ Rutina de gestión de memoria convencional. Se copiará; sobre la de memoria EMS si se utiliza memoria conv.

procesa_con PROCJC con_exit ; sistema inicializándoseMOV BX,16 ; bytes por párrafoDIV BX ; AX = segmento, DX = offsetADD AX,CS:mem_handle ; segmento de inicio datosMOV DS,AXMOV SI,DX ; DS:SI inicio de datosDEC BP ; y ES:DI destino del bufferJZ con_general ; es lecturaXCHG SI,DI ; escritura: intercambiarXPUSH <DS,ES>XPOP <DS,ES>

con_general: CLDCMP CS:cpu386,ONJE con_tr32bitREP MOVSWJMP con_tr_fin

con_tr32bit: SHR CX,1 ; nº palabras de 32 bit a moverJCXZ con_trdo ; evitar desgracia

Page 241: PCA, PS2 ,IBM y AT

241CONTROLADORES DE DISPOSITIVOS

.386PUSHADXOR EAX,EAX ; asegurar no violaciónDEC AX ; de segmento-64KAND ECX,EAX ; EAX = 0FFFFhAND ESI,EAXAND EDI,EAXREP MOVSD ; transferencia ultrarrápida

con_trdo: POPAD ; POPAD falla en muchos 386NOP ; arreglar fallo de POPAD.8086

con_tr_fin: MOV AX,100h ; todo fue bien, por supuestocon_exit: RETprocesa_con ENDP

tam_proc_con EQU $-OFFSET procesa_con ; tamaño de esta rutina

; ************ Datos no residentes para la instalación

ON EQU 1 ; constantes booleanasOFF EQU 0

CONFIG EQU 1 ; TURBODSK ejecutado desde el CONFIGAUTOEXEC EQU 2 ; TURBODSK se ejecuta desde el DOS

emm_id DB "EMMXXXX0" ; identificación del controlador EMS

nombre_tdsk DB "TDSK U: " ; para nombrar handle EMS y el MCB

modo DB ? ; CONFIG/AUTOEXECdosver DW ? ; versión del DOStop_ram DW 0 ; segmento más alto de la RAMsegm_psp DW 0 ; segmento del PSPsegm_tdsk DW 0 ; segmento donde reside TURBODSKsegm_reubicar DW 0 ; segmento donde reubicar TURBODSKems4 DB OFF ; a ON si EMS versión 4.0+cpu286 DB OFF ; a ON si 286 ó superioridioma DB 0 ; selecciona el número de idioma (1..N)idioma_seps DW ",." ; separadores de millares/decimales

param_unidad DB 0 ; letra de unidad (si indicada)param_tdiscof DB OFF ; a ON si se define tamaño de discoparam_tdisco DW 0 ; tamaño de disco (si se define)param_tsect DW 0 ; tamaño de sector (si se define)param_tdir DW 0 ; número de entradas (si se define)param_tcluster DW 0 ; tamaño de cluster (si se define)param_a DB OFF ; a ON si indicado parámetro /A o /Xparam_e DB OFF ; a ON si indicado parámetro /Eparam_b DB OFF ; a ON si indicado parámetro /Bparam_c DB OFF ; a ON si indicado parámetro /Cparam_h DB OFF ; a ON si indicado parámetro /? o /Hparam_m DB OFF ; a ON si indicado parámetro /Mparam_i DB OFF ; Y ON si indicado parámetro /Iparam_f DW 1 ; nº de FATs (1-2): parámetro /F=

codigo_tfno DW ? ; valor de /I= si se indicatdisco DW ? ; tamaño de disco (Kb)ultclus DW ? ; número más alto de clustertamcluster DW ? ; tamaño de cluster (bytes)sdir DW ? ; sectores para directorio raizxms_kb DW 0 ; Kb de memoria XMS libresems_kb DW 0 ; Kb de memoria EMS librescon_kb DW 0 ; Kb de memoria convencional libres

sector_cero LABEL BYTEJMP SHORT botarNOPDB "TDSK 2.3" ; identificación del sistema

tsect DW 512 ; tamaño de sector por defectotcluster DB ? ; sectores por cluster

DW 1 ; sectores reservadosnfats DB ? ; número de FAT’stdir DW ? ; número de entradas al dir. raiznumsect DW ? ; nº sectores del disco (<=32Mb)

DB media ; descriptor de mediosfat DW ? ; sectores por FAT

DW 1, 1 ; sectores por pista / cabezasDD 0 ; sectores ocultosDD 0 ; nº total de sectores (si > 32Mb)DB 7 DUP (0) ; 7 bytes reservados

botar: DB 0EAh ; código de JMP FAR...DW 0,0FFFFh ; ...FFFF:0000 (programa BOOT)DB "(C)1992 CiriSOFT"; resto de primeros 64 bytesDB ". Grupo Universi"DB "tario de Informá"DB "tica (GUI) - Val"DB "ladolid (España)"; resto de primeros 128 bytes

dir_raiz DB "TURBODSK "; Directorio raiz: primera entradaDB 8 ; etiqueta de volúmenDB 10 DUP (0) ; reservadoDW ? ; hora (inicializado al formatear)DW ? ; fechaDW 0,0,0 ; últimos bytes (hasta 32)

; ------------ Areas de datos para información del disco virtual

; --- Código telefónico de países de habla; hispana (mucha o poca).

info_paises DW 54 ; ArgentinaDW 591 ; BoliviaDW 57 ; ColombiaDW 506 ; Costa RicaDW 56 ; ChileDW 593 ; EcuadorDW 503 ; El SalvadorDW 34 ; EspañaDW 63 ; FilipinasDW 502 ; GuatemalaDW 504 ; HondurasDW 212 ; MarruecosDW 52 ; MéxicoDW 505 ; NicaraguaDW 507 ; PanamáDW 595 ; ParaguayDW 51 ; PerúDW 80 ; Puerto RicoDW 508 ; República DominicanaDW 598 ; UruguayDW 58 ; VenezuelaDW 3 ; LatinoaméricaDW 0 ; fin de la información

; --- Código telefónico de países de habla alemana.

DW 41 ; Switzerland

DW 43 ; AustriaDW 49 ; GermanyDW 0 ; fin de la información

DW 0 ; no más idiomas

; ------------ Mensaje de no formateado

info_ins DB 10,1,10,"TURBODSK 2.3 - Unidad ",255DB 10,1,10,"TURBODSK 2.3 - Laufwerk ",255DB 10,1,10,"TURBODSK 2.3 - Drive ",0

info_ins2 DB ": sin formatear.",10,1,14,255DB ": nicht formatiert.",10,1,14,255DB ": unformatted.",10,1,14,0

; ------------ Cuadro de información

colA EQU 11+1*16 ; color del recuadro y los mensajescolB EQU 15+1*16 ; color de los parámetros de operación del discocolC EQU 15+0*16 ; color de lo que rodea a la ventanacolD EQU 10+1*16 ; color de «TURBODSK»

info_txt DB 10,2,12,3,1,colA," ",2,27," ",2,25," ",1,colCDB 10,2,12,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colADB " - Unidad ",1,colBDB 255

DB 10,2,10,3,1,colA," ",2,28," ",2,28," ",1,colCDB 10,2,10,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colADB " - Laufwerk ",1,colBDB 255

DB 10,2,12,3,1,colA," ",2,26," ",2,25," ",1,colCDB 10,2,12,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colADB " - Drive ",1,colBDB 0

inf_tsect DB ":",1,colA," Tamaño de sector:",1,colB," ",255DB ":",1,colA," Sektorgröße:",2,8," ",1,colB," ",255DB ":",1,colA," Sector size:",2,5," ",1,colB," ",0

inf_tdir DB " ",1,colA," ",1,colC,10,2,12,3DB 1,colA," ",2,27," Nº entradas raiz:",1,colB," "DB 255

DB " ",1,colA," ",1,colC,10,2,10,3DB 1,colA," ",2,28," Verzeichniseinträge:",1,colB,

" "DB 255

DB " ",1,colA," ",1,colC,10,2,12,3DB 1,colA," ",2,26," Root entries:",2,4," ",1,colB,"

"DB 0

inf_tdisco DB " ",1,colA," ",1,colC,10DB 2,12,3,1,colA," Tamaño: ",1,colB," "DB 255

DB " ",1,colA," ",1,colC,10DB 2,10,3,1,colA," Größe:",2,10," ",1,colB," "DB 255

DB " ",1,colA," ",1,colC,10DB 2,12,3,1,colA," Size:",2,4," ",1,colB," "DB 0

inf_tcluster DB " Kbytes ",1,colA," Sectores/cluster:",1,colB,""

DB 255DB " KB ",1,colA," Sektoren/Cluster:",2,3,"

",1,colB," "DB 255DB " Kbytes ",1,colA," Sectors/cluster: ",1,colB,"

"DB 0

inf_mem DB " ",1,colA," ",1,colC,10DB 2,12,3,1,colA," Memoria: ",1,colBDB 255

DB " ",1,colA," ",1,colC,10DB 2,10,3,1,colA," Speicher: ",1,colBDB 255

DB " ",1,colA," ",1,colC,10DB 2,12,3,1,colA," Memory: ",1,colBDB 0

inf_nclusters DB " ",1,colA," ",1,colB," ",255DB " ",1,colA," ",1,colB," ",255DB 1,colA," ",1,colB," ",0

inf_tfat DB 1,colA," clusters (",1,colB,"FAT",255DB 1,colA," Cluster (",1,colB,"FAT",255DB 1,colA," clusters (",1,colB,"FAT",0

inf_tfat12 DB "12",0inf_tfat16 DB "16",0

inf_final DB 1,colA,") ",1,colA," ",1,colC,10,2,12,3DB 1,colA," ",2,27," ",2,25," ",1,colC,10DB 255

DB 1,colA,")",2,5," ",1,colA," ",1,colC,10DB 2,10,3,1,colA," ",2,28," ",2,28," ",1,colC,10DB 255

DB 1,colA,") ",1,colA," ",1,colC,10,2,12,3DB 1,colA," ",2,26," ",2,25," ",1,colC,10DB 0

inf_mem_xms DB "Extendida (XMS)",255DB "Erweitert (XMS)",255DB "Extended (XMS) ",0

inf_mem_ems DB "Expandida (EMS)",255DB "Expansion (EMS)",255DB "Expanded (EMS) ",0

inf_mem_sup DB " Superior (UMB) ",255DB "Oberer Sp. (UMB)",255DB " Upper (UMB) ",0

inf_mem_con DB " Convencional ",255DB " Konventionell",255DB " Conventional ",0

Page 242: PCA, PS2 ,IBM y AT

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

; ------------ Errores «leves»

ERROR0 EQU 1ERROR1 EQU 2ERROR2 EQU 4ERROR3 EQU 8 ; TURBODSK es muy flexible y se instalaERROR4 EQU 16 ; casi de cualquier forma, aunque aERROR5 EQU 32 ; veces no se reserve memoria y seaERROR6 EQU 64 ; necesario volver a ejecutarlo despuésERROR7 EQU 128 ; desde el DOS para «formatearlo».ERROR8 EQU 256ERROR9 EQU 512ERROR10 EQU 1024ERROR11 EQU 2048ERROR12 EQU 4096ERROR13 EQU 8192ERROR14 EQU 16384ERROR15 EQU 32768

lista_err DW 0 ; palabra que indica los mensajes a imprimir

mens_cabec DB 2,8,3,0

tabla_mens DW m0,m1,m2,m3,m4,m5,m6,m7DW m8,m9,m10,m11,m12,m13,m14,m15

cab_adv_txt DB 10,2,8,3,1,12DB "Advertencias y/o errores de TURBODSK:",2,27,"

",10,1,10DB 255

DB 10,2,8,3,1,12DB "Warnungen und Fehlermeldungen von TURBODSK:",2,27,"

",10,1,10DB 255

DB 10,2,8,3,1,12DB "Warnings and errors of TURBODSK:",2,32," ",10,1,10DB 0

m0 DB "- Error de sintaxis o parámetro fuera de rango.No se define el",10,2,8,3

DB " disco virtual ahora o no se modifica el queestaba definido. ",10

DB 255

DB "- Syntaxfehler oder ungültiger Parameter. DieRAM-Disk ist zur ",10,2,8,3

DB " Zeit nicht definiert bzw. wurde nichtmodifiziert.",10

DB 255

DB "- Syntax error and/or parameter out of range. TheRamdisk is not",10,2,8,3

DB " defined now or the previous one is notmodified.",2,14," ",10

DB 0

m1 DB "- El parámetro /C o la letra de unidad sólo han deemplearse",2,4," ",10,2,8,3

DB " desde la línea de comandos o el AUTOEXEC (lesignoraré).",2,6," ",10

DB 255

DB "- Parameter /C und Laufwerksbuchstaben können nurbei Aufrufen ",2,4," ",10,2,8,3

DB " von TURBODSK in der AUTOEXEC verwendet werden.",2,6," ",10DB 255

DB "- The /C parameter and the driver letter only canbe used when ",10,2,8,3

DB " executing TURBODSK in command line or AUTOEXEC(now, ignored).",10

DB 0

m2 DB "- Para poder emplear memoria expandida hay queincluir la opción",10,2,8,3

DB " /A en CONFIG.SYS, con objeto de dejar espaciopara las rutinas",10,2,8,3

DB " de control EMS: la memoria ocupada crecerá de 432a 608 bytes.",10

DB 255

DB "- Zur Verwendung von EMS müssen Sie Option /A inCONFIG.SYS ",10,2,8,3

DB " setzen, um Speicher für die EMS-Unterstützung zureservieren. ",10,2,8,3

DB " Dadurch erhöht sich der Speicherbedarf von 432auf 608 Bytes. ",10

DB 255

DB "- In order to use expanded memory you must includethe /A option",10,2,8,3

DB " in CONFIG.SYS, needed to reserve too space forthe EMS support",10,2,8,3

DB " routines: the memory used will increase from 432to 608 bytes.",10

DB 0

m3 DB "- El tamaño de sector es mayor que el definido encualquier otro",10,2,8,3

DB " controlador de dispositivo: indíquese ese tamañoen CONFIG.SYS",10,2,8,3

DB " para que el DOS ajuste sus buffers (¡más consumode memoria!).",10

DB 255

DB "- Die Sektorengröße ist größer als in allen anderenTreibern; ",10,2,8,3

DB " Sie müssen die Sektorgröße in CONFIG.SYSfestlegen, da DOS die",10,2,8,3

DB " Puffergröße anpassen muß (höhererSpeicherverbrauch) ",10

DB 255

DB "- Sector size is greater than any other definedby any device",10,2,8,3

DB " driver loaded: you must indicate the sector sizein CONFIG.SYS",10,2,8,3

DB " because DOS need adjust buffers length (morememory spent!). ",10

DB 0

m4 DB "- La cantidad de memoria solicitada no existe, seha rebajado. ",10

DB 255

DB "- Die gewünschte Speichergröße existiert nicht undwurde reduziert.",10

DB 255

DB "- The amount of memory requested does not exist:size reduced. ",10

DB 0

m5 DB "- No hay memoria XMS/EMS disponible: no la reservo;ejecute TDSK",10,2,8,3

DB " de nuevo desde el DOS para utilizar memoriaconvencional.",2,5," ",10

DB 255

DB "- Kein XMS/EMS verfügbar: Führen Sie TDSK nochmalsvon der ",10,2,8,3

DB " Kommandozeile aus und benutzen Siekonventionellen Speicher. ",2,5," ",10

DB 255

DB "- There is not XMS/EMS memory available: executeTDSK again from",10,2,8,3

DB " DOS command line or AUTOEXEC and use conventionalmemory.",2,5," ",10

DB 0

m6 DB "- No existe memoria XMS: pruebe a indicar EMS ensu lugar (/A) ",10

DB 255

DB "- Kein XMS verfügbar: Versuchen Sie, EMS zuverwenden (/A). ",10

DB 255

DB "- There is not XMS memory available: try to requestEMS (/A). ",10

DB 0

m7 DB "- No existe memoria EMS: pruebe a indicar XMS ensu lugar (/E) ",10

DB 255

DB "- Kein EMS verfügbar: Versuchen Sie, XMS zuverwenden (/E). ",10

DB 255

DB "- There is not EMS memory available: try to requestXMS (/E). ",10

DB 0

m8 DB "- Fallo del controlador XMS: imposible usar memoriaextendida. ",10

DB 255

DB "- Fehler des XMS-Managers: Verwendung von XMSunmöglich. ",10

DB 255

DB "- XMS controller failure: imposible to use extendedmemory.",2,5," ",10

DB 0

m9 DB "- Fallo del controlador EMS: imposible usar memoriaexpandida. ",10

DB 255

DB "- Fehler des EMS-Managers: Verwendung von EMSunmöglich. ",10

DB 255

DB "- EMS controller failure: imposible to use expandedmemory.",2,5," ",10

DB 0

m10 DB "- No existe suficiente memoria convencional paraTURBODSK.",2,6," ",10

DB 255

DB "- Nicht genügend konventioneller Speicher fürTURBODSK verfügbar.",2,6," ",10

DB 255

DB "- There is not sufficient conventional memory forTURBODSK.",2,5," ",10

DB 0

m11 DB "- Tamaño de sector incorrecto: lo establezco pordefecto.",2,7," ",10

DB 255

DB "- Ungültige Sektorengröße angegeben, Vorgabewertwird verwendet.",2,7," ",10

DB 255

DB "- Incorrect sector size indicated: default valuesassumed.",2,6," ",10

DB 0

m12 DB "- Número de entradas incorrecto: lo establezco pordefecto.",2,5," ",10

DB 255

DB "- Ungültige Anz. von Verzeichnisanträgen,Vorgabewert wird verwendet.",2,5," ",10

DB 255

DB "- Incorrect number of root entries: default valueassumed.",2,6," ",10

DB 0

m13 DB "- Tamaño de cluster incorrecto: lo establezco pordefecto.",2,6," ",10

DB 255

DB "- Ungültige Clustergröße angegeben, Vorgabewertwird verwendet.",2,6," ",10

DB 255

DB "- Incorrect cluster size indicated: default valueassumed.",2,6," ",10

DB 0

m14 DB "- FATAL: fallo al liberar la memoria que ocupabael disco.",2,6," ",10

DB 255

DB "- ACHTUNG: Freigabe des belegten Speichersgescheitert.",2,6," ",10

Page 243: PCA, PS2 ,IBM y AT

243CONTROLADORES DE DISPOSITIVOS

DB 255

DB "- FATAL: imposible to free memory alocated byTURBODSK.",2,9," ",10

DB 0

m15 DB "- Para discos de más de 32 Mb, hace falta un tamañode sector de",10,2,8,3

DB " al menos 1024 bytes.",2,42," ",10DB 255

DB "- Laufwerke mit mehr als 32 MB erfordern eineSektorgröße",10,2,8,3

DB " von mindestens 1024 Bytes.",2,42," ",10DB 255

DB "- In drives over 32 Mb, sector size must be atleast 1024 bytes.",10

DB 0

; ------------ Errores «graves» (se imprime sólo el más importante)

err_grave DW 0 ; tipo de error grave a imprimir

err_grave_gen DB 10,1,10,"TURBODSK 2.3",10,1,12,0

e0 DB " - Este disco virtual requiere DOS 2.0 osuperior.",10,255

DB " - Diese RAM-Disk erfordert mindestens DOS2.0.",10,255

DB " - This Ram Disk needs at least DOS 2.0 orabove.",10,0

e1 DB " - Instale primero TURBODSK desde CONFIG.SYS (conDEVICE).",10

DB " - Puede solicitar ayuda con TDSK /?",10DB 255

DB " - Sie müssen zuerst TURBODSK von der CONFIG.SYSaus installieren",10

DB " (mit DEVICE). Hilfe erhalten Sie durch Eingabevon TDSK /?",10

DB 255

DB " - You must install first TURBODSK from CONFIG.SYS(using DEVICE).",10

DB " - Help is available with TDSK /?",10DB 0

e2 DB " - La unidad indicada no es un dispositivo TURBODSK2.3",10,255

DB " - Angegebener Laufwerksbuchstabe bezeichnet keinenTreiber von TURBODSK.", 10,255

DB " - Drive letter indicated does not is a TURBODSK 2.3device.",10,0

e3 DB " - No pueden modificarse las características deoperación de",10

DB " TURBODSK dentro de WINDOWS. Configúrelo conanterioridad.",10

DB 255

DB " - TURBODSK kann nicht innerhalb einerWINDOWS-Sitzung modifiziert werden.",10

DB " Sie müssen die Einstellungen vorherdurchführen.",10

DB 255

DB " - Operational characteristics of disk can not bealtered inside",10,2,4

DB " a WINDOWS session. You must configure TURBODSKbefore.",10

DB 0

; ------------ Ayuda

colorA EQU 15+4*16+128 ; color de «TURBODSK»colorAm EQU 14+1*16 ; color del marco de fondo de «TURBODSK»colorB EQU 13+1*16 ; color de la fechacolorC EQU 10+1*16 ; color de sintaxis y parámetroscolorD EQU 15+1*16 ; color principal del textocolorDm EQU 11+1*16 ; color del marco de fondocolorDmx EQU 11+0*16 ; color de la esquina del marcocolorE EQU 11+1*16 ; color del nombre del autorcolorF EQU 14+1*16 ; color para llamar la atencióncolorG EQU 12+1*16 ; color para la dirección de mailcolorH EQU 9+1*16 ; color para mensaje de dominio público

ayuda_txt LABEL BYTEDB 10,3,1,colorDm," ",1,colorA," TURBODSK 2.3

",1,colorAm," "DB 1,colorB,2,51," 12/12/95 ",1,colorDmx," ",10DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorEDB " (C) 1995 Ciriaco García de Celis. ",1,colorGDB "(Mail: [email protected]).",1,colorDm," ",10DB 3,1,colorE," (C) Grupo Universitario de Informática.

"DB "Apartado 6062, Valladolid (España).

",1,colorDm," ",10DB 3,1,colorH,2,18," ","* * * Programa de Dominio

Público * * *"DB 2,18," ",1,colorDm," ",10DB 3,1,colorD," Bienvenido al disco virtual

",1,colorF,"más rápido"DB 1,colorD,", con soporte de memoria EMS, XMS y

",1,colorDm," ",10DB 3,1,colorD," convencional; redimensionable, fácil

de usar. En DOS "DB "5 ocupa 432-608 bytes. ",1,colorDm," ",10DB 3,1,colorC,2,77," ",1,colorDm," ",10DB 3,1,colorC," DEVICE=TDSK.EXE [tamaño [tsector "DB "[nfich [scluster]]]] [/E] [/A|X] [/C] [/M]

",1,colorDm," ",10DB 3,1,colorC,2,77," ",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," El tamaño

debe de estar en "DB "el rango 8 - 65534 Kb; son válidos sectores de

",1,colorDmDB " ",10DB 3,1,colorD," 32 a 2048 bytes (en potencias de dos,

aunque algún "DB "sistema sólo los soporta ",1,colorDm," ",10DB 3,1,colorD," de 128 a 512). El número de ficheros

del directorio "DB "raiz debe estar entre 1 ",1,colorDm," ",10DB 3,1,colorD," y 65534 y el de sectores por cluster

entre 1 y 255 ("DB "en algún sistema han de ",1,colorDm," ",10

DB 3,1,colorD," ser potencia de dos). Según el tamañose ajustará "

DB "lo demás automáticamente. ",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," Se puede

indicar ",1,colorCDB "/E",1,colorD," para emplear memoria extendida XMS,

y ",1,colorCDB "/A",1,colorD," o ",1,colorC,"/X",1,colorD," para

la ",1,colorDmDB " ",10DB 3,1,colorD," expandida EMS; aunque por defecto,

TURBODSK utilizará"DB " automáticamente estas ",1,colorDm," ",10DB 3,1,colorD," memorias si puede. Con la opción

",1,colorC,"/C"DB 1,colorD," se pide el uso de memoria convencional.

",1,colorDmDB " ",10DB 3,1,colorD,32,1,colorF," ",1,colorD," Tras ser

instalado, se puede"DB " ejecutar desde el DOS para cambiar el tamaño

",1,colorDmDB " ",10DB 3,1,colorD," del disco (perdiéndose los datos

almacenados): con "DB "un tamaño 0 se anula el ",1,colorDm," ",10DB 3,1,colorD," disco por completo, liberándose la

memoria. "DB "Utilizando memoria convencional ",1,colorDm," ",10DB 3,1,colorD," es ",1,colorF,"MUY",1,colorD,"

conveniente anular el "DB "disco previo antes de modificar su tamaño. Con

",1,colorDmDB " ",10DB 3,1,colorD," más de un disco presente se pueden

distinguir "DB "indicando la letra de unidad. ",1,colorDm," ",10DB 3,1,1*16," ",1,colorDm,2,76," ",10

DB 255

DB 10,3,1,colorDm," ",1,colorA," TURBODSK 2.3",1,colorAm," "

DB 1,colorB,2,52," 12/12/95 ",1,colorDmx," ",10DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorEDB " (C) 1995 Ciriaco García de Celis. ",1,colorGDB "(Mail: [email protected]). ",1,colorDm," ",10DB 3,1,colorE," (C) Grupo Universitario de Informática.

"DB "Apartado 6062, Valladolid (Spanien).

",1,colorDm," ",10DB 3,1,colorC,2,78," ",1,colorDm," ",10DB 3,1,colorD," Willkommen bei der

",1,colorF,"schnelleren"DB 1,colorD," RAM-Disk, die auch EMS-, XMS- und

konven- ",1,colorDm," ",10DB 3,1,colorD," tionellen Speicher unterstützt;

größenverstellbar,"DB " einfache Bedienung wie ",1,colorDm," ",10DB 3,1,colorD," bei DOS-RAM-Disks, erfordert maximal

608 Bytes. "DB 1,colorH," Das Programm ist Freeware!.

",1,colorDm," ",10DB 3,1,colorC,2,78," ",1,colorDm," ",10DB 3,1,colorC," DEVICE=TDSK.EXE [Größe [Sekt. [Dateien

[Cluster]]]]"DB " [/E] [/A|X] [/C] [/M] ",1,colorDm," ",10DB 3,1,colorC,2,78," ",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," Zulässig für

Größe: 8-65534 KB;"DB " zulässig für Sektoren: 32-2048 Bytes (2er-

",1,colorDm," ",10DB 3,1,colorD," Potenz), obwohl einige DOS-Versionen

nur 128,"DB " 256 und 512 unterstützen. ",1,colorDm," ",10DB 3,1,colorD, " Zulässige Anzahl der

Verzeichniseinträge: "DB "1-65534, Sektoren/Cluster: 1-255

",1,colorDm," ",10DB 3,1,colorD," (einige Systeme erforden

2er-Potenzen)."DB " Nur die Größenangabe ist notwendig.

",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," Bei

",1,colorC, "/E",1,colorDDB " wird XMS, bei ",1,colorC, "/A",1,colorD," oder

",1,colorC,"/X",1,colorDDB " wird EMS, und bei ",1,colorC, "/C",1,colorD,"

wird konventioneller ",1,colorDmDB " ",10,3,1,colorD, " Speicher benutzt. Normalerweise

versucht TURBODSK,"DB " XMS oder EMS zu benutzen. ",1,colorDm," ",10DB 3,1,colorD,32,1,colorF," ",1,colorD," Nach der

Installation in"DB " CONFIG.SYS sollte TURBODSK später nochmal ausge-

",1,colorDm," ",10DB 3,1,colorD," führt werden, um die Größe zu ändern

(den"DB " Speicherverbrauch); dadurch wird

",1,colorDm," ",10DB 3,1,colorD," der Inhalt der RAM-Disk gelöscht.

Durch Größe 0 wird"DB " die RAM-Disk komplett ",1,colorDm," ",10DB 3,1,colorD," gelöscht, bei Verwendung von

konventionellem Speicher"DB " kann eine Annulierung ",1,colorDm," ",10DB 3,1,colorF," VOR",1,colorD," der Größenveränderung

sinnvoll sein. Wenn mehrere"DB " TURBODSK’s installiert ",1,colorDm," ",10DB 3,1,colorD," sind, können diese durch ihren

Laufwerksbuchstaben"DB " angesteuert werden. ",2,6," ",1,colorDm," ",10DB 3,1,1*16," ",1,colorDm,2,77," ",10

DB 255

DB 10,3,1,colorDm," ",1,colorA," TURBODSK 2.3",1,colorAm," "

DB 1,colorB,2,51," 12/12/95 ",1,colorDmx," ",10DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorEDB " (C) 1995 Ciriaco Garcia de Celis. ",1,colorGDB "(Mail: [email protected]).",1,colorDm," ",10DB 3,1,colorE," (C) Grupo Universitario de

Informática. "DB "Apartado 6062, Valladolid (Spain).

",1,colorDm," ",10DB 3,1,colorC,2,77," ",1,colorDm," ",10DB 3,1,colorD," Welcome to the

Page 244: PCA, PS2 ,IBM y AT

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

",1,colorF,"faster",1,colorDDB " RAM disk!, which includes support of both EMS,

XMS "DB 1,colorDm," ",10DB 3,1,colorD," and conventional memory. Full

resizeable, easy to "DB "use like DOS RAM disks, ",1,colorDm," ",10DB 3,1,colorD," in DOS 5.0 it takes only about 432-608

bytes. "DB 1,colorH,"This program is freeware!.",2,4,"

",1,colorDm," ",10DB 3,1,colorC,2,77," ",1,colorDm," ",10DB 3,1,colorC," DEVICE=TDSK.EXE [size [s_sector [files

[s_cluster]]]]"DB " [/E] [/A|X] [/C] [/M] ",1,colorDm," ",10DB 3,1,colorC,2,77," ",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," Size must be

in the range "DB "8 - 65534 Kb; are valid sectors from 32 to 2048

",1,colorDmDB " ",10DB 3,1,colorD," bytes (in power of 2), though some DOS

versions only "DB "support 128, 256 & 512 ",1,colorDm," ",10DB 3,1,colorD," bytes. Files of root may be 1 to 65534

and sectors "DB "by cluster can vary from ",1,colorDm," ",10DB 3,1,colorD," 1 to 255 (some systems need a power of

2). Only the "DB "size is necessary.",2,6," ",1,colorDm," ",10DB 3,1,colorD," ",1,colorF," ",1,colorC,"

/E",1,colorD," force the "DB "use of XMS memory, ",1,colorC,"/A",1,colorD," and

",1,colorCDB "/X",1,colorD," indicates the use of EMS memory

",1,colorDmDB " ",10DB 3,1,colorD," and ",1,colorC,"/C",1,colorD," the

conventional. By "DB "default, TURBODSK try to use XMS or EMS memory.

",1,colorDm

DB " ",10DB 3,1,colorD," ",1,colorF," ",1,colorD," After been

installed in "DB "CONFIG.SYS, TURBODSK must be executed in AUTOEXEC

",1,colorDmDB " ",10DB 3,1,colorD," or command line in order to vary the

disk size (the "DB "amount of memory used); ",1,colorDm," ",10DB 3,1,colorD," this operation erase the disk contents.

A size 0 "DB "can be used to complitely ",1,colorDm," ",10DB 3,1,colorD," anulation of the disk freezen the

memory: when using "DB "conventional memory it ",1,colorDm," ",10DB 3,1,colorD," is useful to annulate the disk

",1,colorF,"BEFORE"DB 1,colorD," resizing. When more than one TURBODSK

",1,colorDmDB " ",10DB 3,1,colorD," is installed, they can be identified

using in "DB "adition the drive letter.",2,5," ",1,colorDm," ",10DB 3,1,1*16," ",1,colorDm,2,76," ",10

DB 0

tam_a_trabajo EQU 4096 ; tamaño del mayor sectorsoportado

; por TURBODSK o del mayor texto; a imprimir

area_trabajo EQU $DB tam_a_trabajo DUP (?)

_PRINCIPAL ENDS

tam_pila EQU 2048 ; 2 Kb de pila son suficientes

_PILA SEGMENT STACK ’STACK’DB tam_pila DUP (?)

_PILA ENDS

END main

11.8. - LOS CONTROLADORES DE DISPOSITIVO Y EL DOS.

Una vez instalado el controlador de dispositivo, puede ser necesario para los programas del usuariointeraccionar con él. Para ello se ha definido oficialmente un mecanismo de comunicación: el control IOCTL.En principio, un controlador de dispositivo puede ser hallado recorriendo la cadena de controladores dedispositivo para localizarlo y acceder directamente a su código y datos. Sin embargo, en los controladoresmás evolucionados, el método IOCTL es el más recomendable.

El control IOCTL (que permite separar el flujo de datos con el dispositivo de la información decontrol) se ejerce por medio de la función 44h del DOS, siendo posible lo siguiente:

- Averiguar los atributos de un controlador de dispositivo, a partir del nombre. Esto permite, entreotras cosas, distinguir entre un dispositivo real y un fichero con el mismo nombre. Seguro que el lector haconstruido alguna vez un programa que abre un fichero de salida de datos con el nombre que indica elusuario: hay usuarios muy pillines que en lugar del clásico PEPE.TXT prefieren indicar, por ejemplo, CON,estropeando la bonita pantalla que tanto trabajo había costado pintar. Una solución consiste, antes de abrirel fichero de salida, en asegurarse de que es realmente un fichero.

- Leer del controlador o enviarle una tira de caracteres de control. Esto sólo es posible si elcontrolador soporta IOCTL. Por ejemplo, un driver encargado de gestionar un puerto serie especial podríaadmitir cadenas del tipo "9600,n,8,1" para fijar la velocidad de transmisión, paridad, etc. El trabajo querequiere codificar la rutina IOCTL OUTPUT, encargada de recibir estos datos, puede en muchos casosmerecer la pena.

- Averiguar el estado del controlador: saber si tiene caracteres disponibles, o si ya ha transmitido elúltimo enviado. Esta característica, entre otras, es implementada por la orden IOCTL INPUT del controlador.

Para obtener información detallada acerca de la función 44h del DOS hay que consultar, lógicamente,la bibliografía al respecto (recomendable el INTERRUP.LST).

Page 245: PCA, PS2 ,IBM y AT

245EL HARDWARE DE APOYO AL MICROPROCESADOR

Capítulo XII: EL HARDWARE DE APOYO AL MICROPROCESADOR

En este capítulo se mostrará detenidamente el funcionamiento de todos los chips importantes que llevael ordenador en la placa base y alguno de los colocados en las tarjetas de expansión.

Nota: Por limitaciones técnicas, al describir los circuitos integrados las señales que son activasa nivel bajo no tendrán la tradicional barra negadora encima; en su lugar apareceránprecedidas del signo menos: -CS, -WR, -MEMR, ...

En algunos casos, acceder directamente a los chips no es necesario: en general, es mejor dejar eltrabajo al DOS, o en su defecto a la BIOS. Sin embargo, hay casos en que es estrictamente necesario hacerlo:por ejemplo, para programar temporizaciones, hacer sonidos, comunicaciones serie por interrupciones, accesoa discos de formato no estándar, etc. Algunas veces bastará con la información que aparece en el apartadodonde se describe la relación del chip con los PC; sin embargo, a menudo será necesario consultar lainformación técnica del apartado ubicado inmediatamente antes, para lo que bastan unos conocimientosrazonables de los sistemas digitales. Los ordenadores modernos normalmente no llevan los integradosexplicados en este capítulo; sin embargo, poseen circuitos equivalentes que los emulan por completo.

12.1. - LAS CONEXIONES DEL 8088.

Resulta interesante tener una idea global de las conexiones del 8086 con el exterior de cara a entendermejor la manera en que interacciona con el resto de los elementos del ordenador. Se ha elegido el 8088 porser el primer procesador que tuvo el PC; a efectos de entender el resto del capítulo es suficiente con el 8088.

El 8088 puede trabajar en dos modos: mínimo (pequeñas aplicaciones) y máximo (sistemasmultiprocesador). Los requerimientos de conexión con el exterior cambian en función del modo que se decidaemplear, aunque una parte de las señales es común en ambos.

GND 1 40 Vcc

A14 2 39 A15

A13 3 38 A16/S3

A12 4 37 A17/S4

A11 5 36 A18/S5

A10 6 35 A19/S6

A9 7 34 -SS0

A8 8 33 MN/-MX

AD7 9 32 -RD

AD6 10 31 HOLD (-RQ/-GT0)

AD5 11 30 HLDA (-RQ/-GT1)

AD4 12 29 -WR (-LOCK)

AD3 13 28 IO/-M (S2)

AD2 14 27 DT/-R (-S1)

AD1 15 26 -DEN (-S0)

AD0 16 25 ALE

NMI 17 24 -INTA

INTR 18 23 -TEST

CLK 19 22 READY

GND 20 21 RESET’8088

LÍNEAS COMUNES AL MODO MÁXIMO Y MÍNIMO DEL 8088.

AD7..0: Address Data Bus. Son líneas multiplexadas, que pueden actuarcomo bus de datos o de direcciones, evidentemente en tiemposdistintos.

A15..8: Address Bus. En todo momento almacenan la parte media del busde direcciones.

A19..16/S6..3: Address/Status. Parte alta del bus de direcciones, multiplexada:cuando no salen direcciones, la línea S5 indica el estado delbanderín de interrupciones; las líneas S4:S3 informan del registrode segmento empleado para realizar el acceso a memoria: 00-ES,01-SS, 10-CS, 11-DS; S6 no se usa.

-RD: Read. Indica una lectura de memoria o de un dispositivo deentrada/salida.

READY: Ready. Línea de entrada que indica el final de la operación dememoria o E/S.

INTR: Interrupt Request. Línea de petición de interrupcionesenmascarables; el 8088 la observa periódicamente.

-TEST: Test. En respuesta a la instrucción máquina WAIT (¡no TEST!), el8088 se para a comprobar esta línea hasta que se ponga a 0.

NMI: Non-maskable Interrupt. Línea de petición de la interrupción detipo 2, que no puede ser enmascarada.

RESET: Provoca una inicialización interna que culmina saltando a FFFF:0.MN/-MX: Esta línea indica si se trata de un sistema mínimo o máximo.

Page 246: PCA, PS2 ,IBM y AT

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

LÍNEAS EXCLUSIVAS DEL MODO MÍNIMO DEL 8088.

IO/-M: Status Line. Indica si se trata de un acceso a memoria o a un puerto de entrada/salida. No es válida todo el tiempo (solo a ratos).-wr: Write. Indica una escritura en memoria o en un dispositivo de entrada/salida (según el estado de IO/-M).-INTA: Interrupt Acknowledge. Es la señal de reconocimiento de interrupción (solicitada a través de INTR o NMI).ALE: Address Latch Enable. Indica al exterior que las líneas de dirección contienen una dirección válida, con objeto de que la

circuitería externa la almacene en una pequeña memoria (latch). Señal necesaria sólo por culpa de la multiplexación.DT/-R: Data Transmit/Receive. Señal necesaria para emplear un transceiver 8286/8287 en el bus, con objeto de controlar el flujo de

datos a través del mismo (si se recibe/transmite).-DEN: Data Enable. Necesario también para emplear el transceiver: sirve como entrada de habilitación para el mismo.HOLD: Hold. Línea de entrada para solicitar al 8088 que se desconecte de los buses. Empleada por los controladores de DMA.HLDA: Hold Acknowledge. Línea complementaria de HOLD: el 8088 envía una señal de reconocimiento cuando se desconecta del bus.-SS0: Status Line. Línea de apoyo que, junto con IO/-M y DT/-R, permite determinar con precisión el estado del bus:

IO/-M DT/-R -SS0 Estado del bus

1 0 0 Reconocimiento de interrupción1 0 1 Lectura de puerto E/S1 1 0 Escritura en puerto E/S1 1 1 Estado Halt0 0 0 Acceso a código0 0 1 Lectura de memoria0 1 0 Escritura en memoria0 1 1 Inactivo

LÍNEAS EXCLUSIVAS DEL MODO MÁXIMO DEL 8088.

-S0/-S1/-S2: Status. Estas líneas indican el estado del bus:

-S2 -S1 -S0 Estado del bus

0 0 0 Reconocimiento de interrupción0 0 1 Lectura de puerto E/S0 1 0 Escritura en puerto E/S0 1 1 Estado Halt1 0 0 Acceso a código1 0 1 Lectura de memoria1 1 0 Escritura en memoria1 1 1 Inactivo

-RQ/-GT0..1: Request/Grant. Estas patillas bidireccionales permiten a los demás procesadores conectados al bus forzar al 8088 a que libereel bus al final del ciclo en curso.

-LOCK: Lock. Línea que sirve al 8088 para prohibir el acceso al bus a otros procesadores (se activa tras la instrucción máquina LOCKy dura mientras se ejecuta la siguiente instrucción -la que sigue a LOCK, que es realmente un prefijo-). También se activaautomáticamente en los momentos críticos de un ciclo de interrupción.

QS1/QS0: Queue Status. Permite determinar el estado de la cola de instrucciones del 8088.

DIFERENCIAS IMPORTANTES CON EL 8086.

El 8086 cambia el patillaje sensiblemente, aunque la mayoría de las señales son similares. En lugar de 8 líneas de datos y direccionesmultiplexadas (AD0..7) el 8086 posee 16, ya que el bus de datos es de 16 bits. Existe una línea especialmente importante en el 8086, -BHE/S7 (BusHigh Enables/Status), que normalmente indica si se accede a la parte alta del bus de datos o no (operaciones 8/16 bits). El 8086 posee una cola deinstrucciones de 6 bytes, en lugar de 4.

FORMATO DE LAS INSTRUCCIONES DEL 8086.

Resulta absurdo estudiar la composición binaria de las instrucciones máquina de ningún procesador;en los casos en que sea necesario se pueden ver los códigos con alguna utilidad de depuración. Sin embargo,a título de curiosidad, se expone a continuación el formato general de las instrucciones (aunque hay algunasexcepciones y casos especiales).

Código de Operación D W MOD REG REG/MEM byte/palabra despl. byte/palabra inmed.

El código de operación ocupa 6 bits; el bit D indica si es el operando fuente (=0) el que está en el campo registro (REG)o si lo es el operando destino (=1): la razón es que el 8086 sólo admite un operando a memoria, como mucho (o el fuente, o eldestino, no los dos a la vez). El bit W indica el tamaño de la operación (byte/palabra). MOD indica el modo de direccionamiento:00-sin desplazamiento (no existe campo de desplazamiento), 01-desplazamiento de 8 bits, 10-desplazamiento de 16 bits y 11-registro(tanto fuente como destino están en registro). El campo REG indica el registro involucrado en la instrucción, que puede ser de 8 ó16 bits (según indique W): 0-AX/AL, 1-CX/CL, 2-DX/DL, 3-BX/BL, 4-SP/AH, 5-BP/CH, 6-SI/DH, 7-DI/BH; en el caso de registrosde segmento sólo son significativos los dos bits de menor peso: 00-ES, 01-CS, 10-SS, 11-DS. El campo R/M, en el caso de modoregistro (MOD=11) se codifica igual que el campo REG; en caso contrario se indica la forma en que se direcciona la memoria: 0:[BX+SI+desp], 1: [BX+DI+desp], 2: [BP+SI+desp], 3: [BP+DI+desp], 4: [SI+desp], 5: [DI+desp], 6: [BP+desp], 7: [BX+desp].

Page 247: PCA, PS2 ,IBM y AT

247EL HARDWARE DE APOYO AL MICROPROCESADOR

12.2. - EL INTERFAZ DE PERIFÉRICOS 8255.

El PPI 8255 es un dispositivo de E/S general, programable, capaz de controlar 24 líneas condiferentes configuraciones (entrada/salida) y en hasta 3 modos de operación.

12.2.1 - DESCRIPCIÓN DEL INTEGRADO.

Conexiones del 8255 con el exterior:

PA3 1 40 PA4

PA2 2 39 PA5

PA1 3 38 PA6

PA0 4 37 PA7

-RD 5 36 -WR

-CS 6 35 RESET

GND 7 34 D0

A1 8 33 D1

A0 9 32 D2

PC7 10 31 D3

PC6 11 30 D4

PC5 12 29 D5

PC4 13 28 D6

PC0 14 27 D7

PC1 15 26 Vcc

PC2 16 25 PB7

PC3 17 24 PB6

PB0 18 23 PB5

PB1 19 22 PB4

PB2 20 21 PB3’8255

D0..D7: Bus de datos bidireccional de 3 estados.RESET: Esta señal borra el registro de control y todos los puertos (A, B y

C) son colocados en modo entrada.-RD: Utilizada por la CPU para leer información de estado o datos

procedentes del 8255.-WR: Utilizada por la CPU para enviar palabras de control o datos al

8255.A0..A1: Líneas de dirección: permiten seleccionar uno de los tres puertos

o el registro de control.PA0..PA7: Puerto A: puerto de entrada/salida de 8 bits.PB0..PB7: Puerto B: puerto de entrada/salida de 8 bits.PC0..PC7: Puerto C: puerto de entrada/salida de 8 bits.

DESCRIPCIÓN FUNCIONAL

Las dos líneas de direcciones definen cuatro puertos de E/S enel ordenador: los tres primeros permiten acceder a los puertos A, B yC; el cuarto sirve para leer o escribir la palabra de control. El 8255está dividido en dos grupos internos: el grupo A, formado por elpuerto A y los 4 bits más significativos del puerto C; y el grupo B,constituido por el puerto B junto a los 4 bits menos significativos delpuerto C. El puerto C está especialmente diseñado para ser dividido endos mitades y servir de apoyo a los puertos A y B en algunos sistemas.

PROGRAMACIÓN DEL 8255

El 8255 soporta 3 modos de operación: el modo 0 (entrada y salida básica), el modo 1 (entrada ysalida con señales de control) y el modo 2 (bus bidireccional de comunicaciones). Tras un Reset, los 3puertos quedan configurados en modo entrada, con las 24 líneas puestas a "1" gracias a la circuitería interna.Esta configuración por defecto puede no obstante ser alterada con facilidad. El modo para el puerto A y Bse puede seleccionar por separado; el puerto C está dividido en dos mitades relacionadas con el puerto A yel B. Todos los registros de salida son reseteados ante un cambio de modo, incluyendo los biestables deestado. Las configuraciones de modos son muy flexibles y se acomodan a casi todas las necesidades posibles.Los tres puertos pueden ser accedidos en cualquier momento a través de la dirección E/S que les corresponde,como se vio en el apartado anterior. La palabra de control a enviar a la 4ª dirección es:

1 D6 D5 D4 D3 D2 D1 D0GRUPO A: GRUPO B:-------- --------

Puerto C (parte baja)Modo 1 - Entrada, 0 - Salida00 - 0, 01 - 1, 1X - 2 Puerto BPuerto A 1 - Entrada, 0 - Salida1 - Entrada, 0 - Salida ModoPuerto C (Parte alta) 0 ó 11 - Entrada, 0 - Salida

Si el bit más significativo de la palabra de control está borrado, es tratada entonces como un comandoespecial que permite activar o inhibir selectivamente los bits del puerto C:

Page 248: PCA, PS2 ,IBM y AT

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

0 D6 D5 D4 D3 D2 D1 D0

Nuevo valor de ese bit

No importa su valor Bit del puerto C a cambiar (0..7)

Esto es particularmente útil para los modos 1 y 2, donde las interrupciones generadas por las líneasdel puerto C pueden ser activadas o inhibidas simplemente poniendo a 1 ó 0, respectivamente, el flip-flopinterno INTE correspondiente a la interrupción que se trate. Todos son puestos a cero tras establecer el modo.

MODOS DE OPERACIÓN DEL 8255MODO 0: Esta configuración implementa simples funciones de entrada/salida para cada bit de los 2 puertos de 8 bits y los 2 puertos de 4 bits;

los datos son leídos y escritos sin más, sin ningún tipo de control adicional. Los puertos pueden ser configurados de entrada (sinlatch) o salida (los datos permanecen memorizados en un latch).

MODO 1: Este modo es el strobed input/output (entrada/salida a través de un protocolo de señales). Existen dos grupos (A y B) formados porlos puertos A y B más el puerto C, que es repartido a la mitad entre ambos grupos para gestionar las señales de control. Tanto sise configura de entrada como de salida, los datos permanecen en un latch. Con este modo es factible conectar dos 8255 entre sí pararealizar transferencias de datos en paralelo a una velocidad considerable, con posibilidad de generar interrupciones a la CPU en elmomento en que los datos son recibidos o hay que enviar uno nuevo (consúltese documentación técnica).

MODO 2: En este modo se constituye un bus bidireccional de 8 bits, por el que los datos pueden ir en un sentido o en otro, siendo el flujoregulado de nuevo por señales de control a través del puerto C. Este modo sólo puede operar en el Grupo A. Tanto las entradas comosalidas son almacenadas en latch.

NOTA: Existen varias combinaciones posibles de estos modos, en las que las líneas del puerto C que no son empleadas como señales decontrol pueden actuar como entradas o salidas normales, quedando las líneas de control fuera del área de influencia de los comandosque afectan a las restantes.

12.2.2 - EL 8255 EN EL PC.

El 8255 es exclusivo de los PC/XT; ha sido eliminado de la placa base de los AT y PS/2, en los queciertos registros realizan algunas funciones que en los PC/XT realiza el 8255; por ello, en estas máquinas NOse puede programar el 8255 (ha sido eliminado y no existe nada equivalente). El 8255 de los PC/XT estáconectado a la dirección base E/S 60h; por ello, los puertos A, B y C se acceden, respectivamente, a travésde los puertos de E/S 60h, 61h y 62h; la palabra de control se envía por el puerto 63h: la BIOS del PC y XTprograma el 8255 con una palabra de control 10011001b, que configura todos los puertos en el modo 0, conel A y C de entrada y el B de salida. El 8255 es empleado, básicamente, para almacenar los datos que llegandel teclado (puerto A), para leer la configuración del ordenador en los conmutadores de la placa base (puertoC) y para controlar el altavoz y la velocidad en los XT-Turbo (puerto B).

12.2.3 - UN MÉTODO PARA AVERIGUAR LA CONFIGURACIÓN DEL PC/XT.

Aviso: los PC tienen un byte de identificación 0FFh; los XT 0FEh (este byte está en la posición de memoria 0FFFF:0Eh); por otrolado, parte de esta información es accesible también por medio de la variable BIOS ubicada en 40h:10h, método mucho más recomendable.

Puerto A (60h): tiene una doble función: cuando el bit 7 del puerto B está a 1, el puerto A recibe el código de rastreo de la teclapulsada, que luego puede ser leído desde la interrupción del teclado. Si el bit 7 del puerto B está a 0, entonces el puerto A devuelve informaciónsobre la configuración del sistema en los PC (no en los XT): en el bit 0 (a 1 si hay disqueteras), bits 2..3 (número de bloques de 16 kb dememoria ¡que obsoleto e inútil!), bits 4..5 (tipo de pantalla: 11 MDA, 10 Color 80x25, 01 Color 40x25) y bits 6..7 (número de unidades de disco,si el bit 0=1).

Puerto B (61h): bit 0 (PC/XT: conectado a la línea GATE del contador 2 del 8253), bit 1 (PC/XT: conectado al altavoz), bit 2 (sóloPC: selecciona el contenido del puerto C), bit 3 (en XT: selecciona contenido del puerto C; en PC: a 0 para activar el motor del casete), bit 4(PC/XT: a 0 para activar la RAM), bit 5 (PC/XT: a 0 para activar señales de error en el slot de expansión), bit 6 (PC/XT: a 1 activa la señalde reloj del teclado), bit 7 (en PC: empleado para seleccionar la función del puerto A; tanto en PC como en XT sirve además para enviar unaseñal de reconocimiento al teclado).

Puerto C (62h):Si el bit 2 del puerto B (PC) o el bit 3 del puerto B (XT) están a 1:- En los PC: los bits 0..3: mitad inferior del 2º banco de conmutadores de la placa base (RAM en slots de expansión); bit 4 (entrada de casete).- En los XT: bit 1 (activo si coprocesador instalado), bits 2..3 (bancos de RAM en placa base).- En PC/XT: bit 5 (OUT del contador 2 del 8253), bit 6 (a 1 si comprobar errores en slots de expansión), bit 7 (1 si comprobar error de paridad).Si el bit 2 del puerto B (PC) o el bit 3 del puerto B (XT) están a 1:- En los PC: bits 0..3 parte alta del segundo banco de conmutadores de configuración (no usada).- En los XT: bits 0..1 tipo de pantalla (11 MDA, 10 color 80x25, 01 color 40x25), bits 2..3 (nº de disqueteras menos 1).- En PC/XT: los bits 4..7 están igual que en el caso anterior (no dependen del bit 2 ó 3 del puerto B).

Page 249: PCA, PS2 ,IBM y AT

249EL HARDWARE DE APOYO AL MICROPROCESADOR

12.3. - EL TEMPORIZADOR 8253 U 8254.

El 8253/4 es un chip temporizador que puede ser empleado como reloj de tiempo real, contador desucesos, generador de ritmo programable, generador de onda cuadrada, etc. En este capítulo, la informaciónvertida estará relacionada con el 8254 que equipa a los AT, algo más potente que el 8253 de los PC/XT; sinembargo, las pocas diferencias serán comentadas cuando llegue el caso.

12.3.1 - DESCRIPCIÓN DEL INTEGRADO.

Este circuito integrado posee 3 contadores totalmente independientes, que pueden ser programadosde 6 formas diferentes.

D7 1 24 Vcc

D6 2 23 -WR

D5 3 22 -RD

D4 4 21 -CS

D3 5 20 A1

D2 6 19 A0

D1 7 18 CLK 2

D0 8 17 OUT 2

CLK 0 9 16 GATE 2

OUT 0 10 15 CLK 1

GATE 0 11 14 GATE 1

GND 12 13 OUT 1’8254

D7..D0: BUS de datos bidireccional de 3 estados.CLK 0: CLOCK 0, entrada de reloj al contador 0.OUT 0: Salida del contador 0.GATE 0: Puerta de entrada al contador 0.CLK 1: CLOCK 1, entrada de reloj al contador 1.OUT 1: Salida del contador 1.GATE 1: Puerta de entrada al contador 1.CLK 2: CLOCK 2, entrada de reloj al contador 2.OUT 2: Salida del contador 2.GATE 2: Puerta de entrada al contador 2.A0..A1: Líneas de dirección para seleccionar uno de los tres

contadores o el registro de la palabra de control.-CS: Habilita la comunicación con la CPU.-WR: Permite al 8254 aceptar datos de la CPU.-RD: Permite al 8254 enviar datos a la CPU.

DESCRIPCIÓN FUNCIONAL

El diagrama funcional del 8254, con la estructura interna de las diversas partes que lo componen, semuestra a la izquierda. A la derecha, diagrama de los bloques internos de un contador:

BUFFER CLK 0 REGISTRO DE LATCH DEDEL BUS CONTADOR 0 GATE 0 LA PALABRA ESTADODE DATOS OUT 0 DE CONTROL

CR CRD0..D7 M L

REGISTRO-RD DE ESTADO-WR LÓGICA CLK 1

DE LECTURA CONTADOR 1 GATE 1A0 Y ESCRITURA OUT 1 LÓGICAA1 DE CE

CONTROL-CS

REGISTRO DE CLK 2LA PALABRA CONTADOR 2 GATE 2DE CONTROL OUT 2 CLK n OL OL

M LGATE n

OUT n

El buffer del bus de datos, de 8 bits y tres estados, comunica el 8254 con la CPU. La lógica delectura y escritura acepta entradas del bus y genera señales de control para las partes funcionales del 8254.Las líneas A0..A2 seleccionan uno de los tres contadores o el registro de la palabra de control, para poderleerlos o escribirlos. El registro de la palabra de control es seleccionado cuando A0=A1=1, este registrosólo puede ser escrito (se puede obtener información de estado, como se verá más adelante, con el comandoread-back del 8254, no disponible en el 8253). Los contadores 1, 2 y 3 son idénticos en su funcionamiento,por lo que sólo se describirá uno; son totalmente independientes y cada uno de ellos puede ser programadoen una modalidad diferente. Si se observa el esquema de un contador, a la derecha, se verá el registro de la

Page 250: PCA, PS2 ,IBM y AT

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

palabra de control: aunque no es parte del contador propiamente dicho, afecta a su modo de funcionamiento.El registro de estado, cuando es transferido al correspondiente latch, contiene el valor en curso del registrode la palabra de control y alguna información adicional (como se verá después en el comando read-back).El contador propiamente dicho está representado en la figura por CE (Counting Element) y es un contadordescendente síncrono de 16 bits que puede ser inicializado. OLM y OLL son dos latch de 8 bits (OL significaOutput Latch; los subíndices M y L están relacionados con el más y el menos significativo byte,respectivamente); ambos son referenciados normalmente como un conjunto denominado OL a secas. Estoslatches siguen normalmente la cuenta descendente de CE, pero la CPU puede enviar un comando paracongelarlos y poder leerlos; tras la lectura continuarán siguiendo a CE. La lógica de control del contador seencarga de que un sólo latch esté activo a un tiempo, ya que el bus interno del 8254 es de 8 bits. CE nopuede ser nunca leído directamente (lo que se lee es OL). De manera análoga, existen un par de registrosCRM y CRL (CR significa Count Register) que almacenan la cuenta del contador y se la transmitenconvenientemente a CE. Los valores de cuenta se escriben siempre sobre CR (y no directamente sobre CE).La lógica de control gestiona la conexión con el exterior a través de las líneas CLK, GATE y OUT.

DESCRIPCIÓN OPERACIONAL

Tras el encendido del ordenador, el 8254 está en un estado indefinido; con un modo, valor de cuentay estado de salida aleatorios. Es entonces cuando hay que programar los contadores que se vayan a emplear;el resto, no importa dejarlos de cualquier manera.

Programación del 8254.

Para programar un contador del 8254 hay que enviar primero una palabra de control y, después, unvalor de cuenta inicial. Los contadores se seleccionan con las líneas A0 y A1; el valor A0=A1=1 seleccionala escritura de la palabra de control (en la que se identifica el contador implicado). Por tanto, el 8254 ocupanormalmente 4 direcciones de E/S consecutivas ligadas a los contadores 0, 1, 2 y al registro de la palabra decontrol. Para enviar la cuenta inicial se utiliza simplemente el puerto E/S ligado al contador que se trate. Elformato de la palabra de control es:

D7 D6 D5 D4 D3 D2 D1 D0

SC1 SC0 RW1 RW0 M2 M1 M0 BCD

Contador:0 Binario 16 bits

Elegir contador: 1 BCD de 4 décadas0 0 Contador 0 Operación: Modo:0 1 Contador 1 0 0 Comando de enclavamiento 0 0 0 Modo 01 0 Contador 2 0 1 Leer/escribir byte bajo 0 0 1 Modo 11 1 Comando Read Back 1 0 Leer/escribir byte alto X 1 0 Modo 2

1 1 Leer/escribir byte bajo X 1 1 Modo 3y después el alto 1 0 0 Modo 4

1 0 1 Modo 5

Operaciones de escritura.

El 8254 es muy flexible a la hora de ser programado. Basta con tener en cuenta dos cosas: por unlado, escribir siempre primero la palabra de control, antes de enviar la cuenta inicial al contador. Por otro,dicha cuenta inicial debe seguir exactamente el formato seleccionado en la palabra de control (enviar sólobyte bajo, enviar sólo byte alto, o bien enviar ambos consecutivamente). Teniendo en cuenta que cadacontador tiene su propio puerto y que la palabra de control indica el contador al que está asociada, no hayque seguir un orden especial a la hora de programar los contadores. Esto significa que, por ejemplo, se puedeenviar la palabra de control de cada contador seguida de su cuenta inicial, o bien enviar todas las palabrasde control para los 3 contadores y después las 3 cuentas iniciales; también es válida cualquier combinaciónintermedia de estas secuencias (por ejemplo: enviar la palabra de control para el contador 0, después lapalabra de control para el contador 1, después la parte baja de la cuenta para el contador 0, luego la partebaja de la cuenta para el contador 1, la parte alta de la cuenta para el contador 0, etc...).

Page 251: PCA, PS2 ,IBM y AT

251EL HARDWARE DE APOYO AL MICROPROCESADOR

Un nuevo valor de cuenta inicial puede ser almacenado en un contador en cualquier momento, sin que elloafecte al modo en que ha sido programado (el resultado de esta operación dependerá del modo, como se verámás adelante). Si se programa el contador para leer/escribir la cuenta como dos bytes consecutivos (bajo yalto), el sentido común indica que entre ambos envíos/recepciones no conviene transferir el control a unasubrutina que utilice ese mismo contador para evitar un resultado incorrecto.

Operaciones de lectura.

Existen tres posibles métodos para leer el valor de un contador en el 8254. El primero es el comandoRead-Back, sólo disponible en el 8254 (y no en el 8253), como luego veremos. El segundo consiste en leersimplemente el contador accediendo a su puerto correspondiente: este método requiere inhibir la entrada CLKal contador (por ejemplo, a través de la línea GATE o utilizando circuitería exterior de apoyo) con objeto deevitar leer la cuenta en medio de un proceso de actualización de la misma, lo que daría un resultadoincorrecto. El tercer método consiste en el comando de enclavamiento.

Comando de enclavamiento (Counter Latch Command).

Este comando se envía cual si de una palabra de control se tratara (A1=A0=1): para diferenciarlo deellas los bits 5 y 4 están a cero. En los bits 7 y 6 se indica el contador afectado. Los demás bits deben estara cero para compatibilizar con futuras versiones del chip. Cuando se envía el comando, el OL del contadorseleccionado queda congelado hasta que la CPU lo lee, momento en el que se descongela y pasa de nuevoa seguir a CE. Esto permite leer los contadores al vuelo sin afectar la cuenta en curso. Se pueden enviarvarios de estos comandos a los diversos contadores, cuyos OL’s quedarán enclavados hasta ser leídos. Si seenvían varios comandos de enclavamiento al mismo contador, separados por un cierto intervalo de tiempo,sólo se considerará el primero (por tanto, la cuenta leída corresponderá al valor del contador cuando fueenclavado por vez primera).

Por supuesto, el contador debe ser leído utilizando el formato que se definió al enviar la palabra decontrol; aunque en el caso de leer 16 bits, las dos operaciones no han de ser necesariamente consecutivas (sepueden insertar en el medio otras acciones relacionadas con otros contadores).

Otra característica interesante (¿disponible tal vez sólo en el 8254?) consiste en la posibilidad demezclar lecturas y escrituras del mismo contador. Por ejemplo, si ha sido programado para cuentas de 16 bits,es válido hacer lo siguiente: 1) leer el byte menos significativo, 2) escribir el nuevo byte menos significativo,3) leer el byte más significativo, 4) escribir el nuevo byte más significativo.

Comando Read-Back.

Sólo está disponible en el 8254, no en el 8253. Este comando permite leer el valor actual de lacuenta, así como averiguar también el modo programado para un contador y el estado actual de la patillaOUT, además de verificar el banderín de cuenta nula (Null Count) de los contadores que se indiquen. Elformato del comando Read-Back es el siguiente:

D7 D6 D5 D4 D3 D2 D1 D0

1 1 -COUNT -STATUS CNT 2 CNT 1 CNT 0 0

0 Si enclavar la cuentade los contadores a 1 los contadores seleccionadosseleccionados

0 Si enclavar el byte de estado del contador seleccionado

El comando Read-Back permite enclavar la cuenta en varios OL’s de múltiples contadores de unasola vez, sin requerir múltiples comandos de enclavamiento, poniendo el bit 5 a cero. Todo funciona a partirde aquí como cabría esperar (los contadores permanecen enclavados hasta ser leídos, los que no son leídos

Page 252: PCA, PS2 ,IBM y AT

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

permanecen enclavados, si el comando se reitera sólo actúa la primera vez reteniendo la primera cuenta...).También es posible enviar información de estado al latch OL, enclavándola para que puede ser leída concomodidad por el puerto que corresponda a ese contador. La palabra de estado tiene el siguiente formato:

D7 D6 D5 D4 D3 D2 D1 D0

OUTPUT NULL RW1 RW0 M2 M1 M0 BCDCOUNT

Contador:valor de la patilla OUT 0 Binario 16 bits

modo activo 1 BCD 4 décadas1 "Null Count"0 Cuenta disponible para ser leída

En D0..D5 se devuelve justo la misma información que se envió en la última palabra de control; enel bit D7 se entrega el estado actual de la patilla OUT del 8254, lo que permite monitorizar por software lassalidas del temporizador economizando hardware en ciertas aplicaciones. El bit NULL COUNT (D6) indicacuándo la última cuenta escrita en CR ha sido transferida a CE: el momento exacto depende del modo defuncionamiento del contador. Desde que se programa un nuevo valor de cuenta, pasa un cierto tiempo hastaque éste valor pasa de CR a CE: leer el contador antes de que se haya producido dicha transferencia implicaleer un valor no relacionado con la nueva cuenta. Por ello, según las aplicaciones, puede llegar a ser necesarioesperar a que NULL COUNT alcance el valor 0 antes de leer. El funcionamiento es el siguiente:

Operación ConsecuenciasA - Escribir al registro de la palabra de control (1) NULL COUNT = 1B - Escribir al registro contador (CR) (2) NULL COUNT = 1C - Nueva cuenta cargada en CE (CR - CE) NULL COUNT = 0

Notas: (1) Sólo el contador especificado por la palabra de control tiene su NULL COUNT a 1; losdemás contadores, lógicamente, no ven afectado su correspondiente bit NULL COUNT.(2) Si el contador es programado para cuentas de 16 bits, NULL COUNT pasa a valer 1inmediatamente después de enviar el segundo byte.

Si se enclava varias veces seguidas la palabra de estado, todas serán ignoradas menos la primera, porlo que el estado leído será el correspondiente al contador en el momento en que se enclavó por vez primerala palabra de estado.

Se pueden enclavar simultáneamente la cuenta y la palabra de estado (en un comando Read-Back conD5=D4=0), lo que equivale a enviar dos Read-Back consecutivos. En este caso, y con independencia de quiénde los dos hubiera sido enclavado primero, la primera lectura realizada devolverá la palabra de estado y lasegunda la cuenta enclavada (que automáticamente quedará de nuevo desenclavada).

MODOS DE OPERACIÓN DEL 8254

MODO 0: Interrupt On Terminal Count (Interrupción al final de la cuenta).Es empleado típicamente para contar sucesos. Tras escribir la palabra de control, OUT está

inicialmente en estado bajo, y permanecerá así hasta que el contador alcance el cero: entonces se pone a 1y no volverá a bajar hasta que se escriba una nueva cuenta o una nueva palabra de control. La entrada GATEpuesta a 0 permite inhibir la cuenta, sin afectar a OUT. El contador sigue evolucionando tras llegar a cero(0FFFFh, 0FFFEh, ...) por lo que lecturas posteriores del mismo devuelven valores pseudoaleatorios.

Tras escribir la cuenta inicial y la palabra de control en el contador, la cuenta inicial será cargada enel próximo pulso del reloj conectado (CLK), pulso que no decrementa el contador: para una cuenta inicialN, OUT permanecerá a 0 durante N+1 pulsos del reloj tras escribir la cuenta inicial.

Si se escribe una nueva cuenta en el contador, será cargada en el próximo pulso del reloj y elcontador comenzará a decrementarse; si se envía una cuenta de dos bytes, el primer byte enviado inhibe lacuenta y OUT es puesto a cero inmediatamente (sin esperar a CLK): tras escribir el segundo byte, la cuentaserá cargada en el siguiente pulso del reloj. Esto permite sincronizar la secuencia de conteo por software.

Page 253: PCA, PS2 ,IBM y AT

253EL HARDWARE DE APOYO AL MICROPROCESADOR

Si se escribe una nueva cuenta mientras GATE=0, ésta será cargada en cualquier caso en el siguientepulso del reloj: cuando GATE suba, OUT se pondrá en alto tras N pulsos del reloj (y no N+1 en este caso).

CLK(N=5)

-WR

GATE

OUT5 4 3 2 1 0

MODO 1: Hardware Retriggerable One-Shot (Monoestable programable).OUT será inicialmente alta y bajará en el pulso de reloj que sigue al flanco de subida de GATE,

permaneciendo en bajo hasta que el contador alcance el cero. Entonces, OUT sube y permanece activo hastael pulso del reloj que siga al próximo flanco de subida de GATE.

Tras escribir la palabra de control y la cuenta inicial, el contador está preparado. Un flanco de subidade GATE provoca la carga del contador (CR CE) y que OUT baje en el próximo pulso del reloj,comenzando el pulso One-Shot de N ciclos de reloj de duración; el contador vuelve a ser recargado si seproduce un nuevo flanco de subida de GATE, de ahí que OUT permanezca en bajo durante N pulsos de relojtras la última vez que suceda esto. El pulso One-Shot puede repetirse sin necesidad de recargar el contadorcon el mismo valor. GATE no influye directamente en OUT.

Si se escribe una nueva cuenta durante un pulso One-Shot, el One-Shot en curso no resulta afectado,a menos, lógicamente, que se produzca un nuevo flanco de subida de GATE: en ese caso, el contador seríarecargado con el nuevo valor.

CLK(N=4)

-WR

GATE

OUT4 3 2 4 3 2 1 0

MODO 2: Rate Generator (Generador de ritmo).En este modo, el contador funciona como un divisor por N. Es empleado típicamente para las

interrupciones de los relojes de tiempo real.OUT estará inicialmente en alto. Cuando el contador se decremente hasta el valor 1, OUT pasará a

estado bajo durante un pulso del reloj; tras ello, volverá a subir y el contador se recargará con la cuentainicial, repitiéndose el proceso. Este modo es, por tanto, periódico, y la misma secuencia se repiteindefinidamente. Para una cuenta inicial N, la secuencia se repite cada N ciclos de reloj (CLK).

Si GATE=0 la cuenta descendiente se detiene: si GATE es bajado durante un pulso de salida, OUTsube inmediatamente. Un flanco de subida en GATE provoca una recarga del contador con el valor de cuentainicial en el siguiente pulso del reloj (después, como cabría esperar, OUT bajará tras los N pulsos del relojcorrespondientes): GATE puede ser utilizado para sincronizar el contador.

Tras escribir la palabra de control y la cuenta inicial, el contador será cargado en el próximo pulsodel reloj: OUT bajará N pulsos de reloj después, lo que permite también una sincronización por software.

Escribir un nuevo valor de cuenta durante el funcionamiento del contador no afecta a la actualsecuencia de cuenta; si se recibe un flanco de subida de GATE antes del final del período el contador serecargará con ese nuevo valor de cuenta inicial tras el próximo pulso del reloj y volverá a comenzar, en casocontrario se recargará con el nuevo valor tras finalizar con normalidad el ciclo en curso.

CLK(N=4) (N=3)

-WR

OUT4 3 2 1 0(4) 3 2 1 0(3) 2 1 0

Page 254: PCA, PS2 ,IBM y AT

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

MODO 3: Square Wave Mode (Generador de onda cuadrada).Este modo es empleado normalmente para la generación de una señal de onda cuadrada. Este modo

es similar al 2, con la diferencia de que la salida OUT conmuta al transcurrir la mitad de la cuenta:inicialmente está en alto, pero al pasar la mitad de la cuenta pasa a estado bajo hasta que la cuenta finaliza.Este modo es también periódico: la onda resultante para una cuenta inicial N tiene un período de N ciclos.

Si GATE=0 la cuenta descendiente se detiene: si GATE es bajado durante un pulso de salida, OUTsube inmediatamente sin esperar ningún CLK. Un flanco de subida en GATE provoca una recarga delcontador con el valor de cuenta inicial en el siguiente pulso del reloj: GATE puede ser utilizado parasincronizar el contador.

Tras escribir la palabra de control y la cuenta inicial, el contador será cargado en el próximo pulsodel reloj: también puede ser sincronizado por software.

Escribir un nuevo valor de cuenta durante el funcionamiento del contador no afecta a la actualsecuencia de cuenta; si se recibe un flanco de subida de GATE antes del final del medio-período el contadorse recargará con ese nuevo valor de cuenta inicial tras el próximo pulso del reloj y volverá a comenzar, encaso contrario se recargará con el nuevo valor tras finalizar con normalidad el medio-ciclo en curso.

Para valores de cuenta impares, la duración a nivel alto de OUT será un período de reloj mayor quela duración a nivel bajo.

CLK4 3 2 1 4 3 2 1 4 3 2 1 4 3

OUT (N=4)5 4 3 2 1 5 4 3 2 1 5 4 3 2

OUT (N=5)

MODO 4: Software Triggered Mode (Pulso Strobe iniciado por software).OUT está en alto al principio; cuando la cuenta inicial expira, OUT baja durante un pulso de reloj

y luego vuelve a subir. El proceso se inicia cuando se escribe la cuenta inicial.GATE=0 inhibe el contador y GATE=1 lo habilita; GATE no influye en OUT. Tras escribir la palabra

de control y la cuenta inicial, el contador será cargado en el próximo pulso del reloj: como ese pulso nodecrementa el contador, para una cuanta inicial N, OUT no bajará hasta N+1 pulsos de CLK. Si se escribeuna nueva cuenta durante el proceso, se cargará en el próximo pulso CLK y continuará el proceso de cuentacon la nueva cuenta escrita; si la cuenta es de 2 bytes, al escribir el primero no se altera el funcionamientodel contador hasta que se envíe el segundo.

CLK(N=4)

-WR

GATE 4 4 3 2 1 0

OUT

MODO 5: Hardware Triggered Strobe (Pulso Strobe iniciado por hardware).OUT estará en alto al principio: con el flanco de subida de la señal GATE, el contador comienza a

decrementar la cuenta. Cuando llega a cero, OUT baja durante un pulso CLK y luego vuelve a subir.Después de escribir la palabra de control y la cuenta inicial, el contador no será cargado hasta el

pulso de reloj posterior al flanco de subida de GATE. Este pulso CLK no decrementa el contador: por ello,ante una cuenta inicial N, OUT no bajará hasta que pasen N+1 pulsos de reloj. GATE no afecta a OUT.

Si una nueva cuenta inicial es escrita durante el proceso, la actual secuencia del contador no seráalterada; si se produce un flanco de subida en GATE antes de que la nueva cuenta sea escrita pero despuésde que expire la cuenta actual, el contador será cargado con la nueva cuenta en el próximo pulso del reloj.

CLK

GATE 4 3 4 3 2 1 0

OUT

Page 255: PCA, PS2 ,IBM y AT

255EL HARDWARE DE APOYO AL MICROPROCESADOR

Operación de GATE Rango de las cuentas

MODO Bajo o Bajando Subiendo Alto Mínima Máxima

0 Desactiva la cuenta -- Activa la cuenta 1 0

1) Inicia la cuenta1 -- 2) Resetea OUT tras -- 1 0

el siguiente CLK

1) Desactiva cuenta 1) Carga contador2 2) Pone OUT en alto 2) Inicia la cuenta Activa la cuenta 2 0

inmediatamente

1) Desactiva cuenta 1) Carga contador3 2) Pone OUT en alto 2) Inicia la cuenta Activa la cuenta 2 0

inmediatamente

4 Desactiva la cuenta -- Activa la cuenta 1 0

5 -- Inicia la cuenta -- 1 0

12.3.2 - EL 8254 EN EL ORDENADOR.

Todos los AT y PS/2 llevan instalado un 8254 o algo equivalente; los PC/XT van equipados con un8253, algo menos versátil; los PS/2 más avanzados tienen un temporizador con un cuarto contador ligado ala interrupción no enmascarable, si bien no lo consideraremos aquí. Todos los contadores van conectados aun reloj que oscila a una frecuencia de 1.193.180 ciclos por segundo (casi 1,2 Mhz). La dirección base enel espacio de E/S del ordenador elegida por IBM cuando diseñó el PC es la 40h. Por tanto, los tres contadoresson accedidos, respectivamente, a través de los puertos 40h, 41h y 42h; la palabra de control se envía alpuerto 43h.

La señal GATE de los contadores 0 y 1 está siempre a 1; en el contador 2 es seleccionable el nivelde la línea GATE a través de bit 0 del puerto E/S 61h. La BIOS programa por defecto el contador 0 en elmodo 3 (generador de onda cuadrada) y el contador 1 en el modo 2 (generador de ritmo); el usuarionormalmente programa el contador 2 en el modo 2 ó 3.

La salida del contador 0 está conectada a IRQ 0 (ligado a la INT 8, que a su vez invoca a INT 1Ch);este contador está programado por defecto con el valor cero (equivalente a 65536), por lo que la cadenciade los pulsos es de 1.193.180/65.536 = 18,2 veces por segundo, valor que determina la precisión del relojdel sistema, ciertamente demasiado baja. Se puede modificar el valor de recarga de este contador en unprograma, llamando a la vieja INT 8 cada 1/18,2 segundos para no alterar el funcionamiento normal delordenador, si bien no es conveniente instalar programas residentes que cambien permanentemente estaespecificación: los programas del usuario esperan encontrarse el temporizador a la habitual y poco útilfrecuencia de 18,2 interrupciones/segundo.

La salida del contador 1 controla el refresco de memoria en todas las máquinas, su valor normal parael divisor es 18; aumentándolo se puede acelerar el funcionamiento del ordenador, con el riesgo -eso sí- deun fallo en la memoria, detectado por los chips de paridad -si los hay-, que provoca generalmente el bloqueodel equipo. De todas maneras, en los PC/XT se puede aumentar entre 19 y 1000 sin demasiados riesgos,acelerándose en ocasiones hasta casi un 10% la velocidad de proceso del equipo. En los AT la ganancia develocidad es mucho menor y además este es un punto demasiado sensible que conviene no tocar para nocorrer riesgos, aunque se podría bajar hasta un valor 2-17 para ralentizar el sistema. Sin embargo, no esconveniente alterar esta especificación porque, como se verá más adelante, hay un método para realizarretardos (empleado por la BIOS y algunas aplicaciones) que se vería afectado.

El contador 2 puede estar conectado al altavoz del ordenador para producir sonido; alternativamentepuede emplearse para temporizar. Es el único contador que queda realmente libre para el usuario, lo que sueledar quebraderos de cabeza a la hora de producir sonido.

Page 256: PCA, PS2 ,IBM y AT

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

12.3.3 - TEMPORIZACIÓN.

Los contadores 0 y 1, especialmente este último, ya están ocupados por el sistema; en la práctica elúnico disponible es el 2. Este contador ha sido conectado con el doble propósito de temporizar y de generarsonido. Para emplearlo en las temporizaciones, es preciso habilitar la puerta GATE activando el bit 0 delpuerto 61h; también hay que asegurarse de que la salida del contador no está conectada al altavoz (a menosque se desee música mientras se cronometra) poniendo a 0 el bit 1 del mismo puerto (61h):

IN AL,61hAND AL,11111101b ; borrar bit 1 (conexión contador 2 con el altavoz)OR AL,00000001b ; activar bit 0 (línea GATE del contador 2)JMP SHORT $+2 ; estado de espera para E/SOUT 61h,AL

El siguiente programa de ejemplo, CRONOS.ASM, incluye dos subrutinas para hacer retardos de altaprecisión. La primera de ellas, inic_retardo, hay que llamarla al principio para que programe el contador 2del temporizador; la rutina retardo se encarga de hacer el retardo que se indique en AX (en unidades de1/1193180 segundos).

; ********************************************************************; * *; * CRONOS.ASM - Subrutinas para hacer retardos de precisión. *; * *; * INIT_RETARDO: llamarla al principio del todo. *; * RETARDO: Entregar en AX el nº de 1193180-avos de *; * segundo que dura el retardo (máximo 65400). *; * *; ********************************************************************

programa SEGMENTASSUME CS:programa, DS:programa

ORG 100hinicio:

CALL inic_retardoMOV CX,20 ; 20 retardosMOV AX,59659 ; de 50 milisegundos

retard: CALL retardoLOOP retardINT 20h

inic_retardo PROCPUSH AXIN AL,61hAND AL,11111101bOR AL,1JMP SHORT $+2OUT 61h,ALMOV AL,10110100b ; contador 2, modo 2, binarioJMP SHORT $+2OUT 43h,ALPOP AXRET

inic_retardo ENDP

retardo PROCPUSH AXPUSH BXCLIOUT 42h,AL ; parte baja de la cuentaMOV AL,AHJMP SHORT $+2OUT 42h,AL ; parte altaJMP SHORT $+2IN AL,61hXOR AL,1 ; bajar GATEJMP SHORT $+2OUT 61h,ALXOR AL,1 ; subir GATEJMP SHORT $+2OUT 61h,ALSTIJMP SHORT $+2MOV BX,0FFFFh

retardando: MOV AL,10000000bOUT 43h,AL ; enclavamientoJMP SHORT $+2IN AL,42h ; leer contadorMOV AH,ALJMP SHORT $+2IN AL,42hXCHG AH,AL ; AX = valor del contadorCMP AX,BXMOV BX,AXJBE retardandoPOP BXPOP AXRET

retardo ENDP

programa ENDSEND inicio

El procedimiento inic_retardo programa el contador 2 en el modo 2, con datos en binario y dejándololisto para enviar/recibir secuencias de 2 bytes para la cuenta (primero el byte menos significativo y luego elalto). Las instrucciones JMP SHORT $+2 colocadas oportunamente (para saltar a la siguiente línea) evitanque las máquinas AT más antiguas fallen en dos operaciones de E/S consecutivas demasiado rápidas. Elprocedimiento retardo envía el nuevo valor de cuenta. A continuación baja y vuelve a subir la señal GATE,con objeto de provocar un flanco de subida en esta línea, lo cual provoca que el contador se cargue con elvalor recién enviado de manera inmediata (de lo contrario, no se recargaría hasta acabar la cuenta anterior).Finalmente, entramos en un bucle donde se enclava continuamente la cuenta y se espera hasta que acabe. Lomás intuitivo sería comprobar si la cuenta es cero, pero esto es realmente difícil ya que cambia nada menosque ¡más de 1 millón de veces por segundo!. Por tanto, nos limitamos a comprobar si tras dos lecturasconsecutivas la segunda es mayor que la primera ...¡no puede ser!... sí, si puede ser, si tras llegar a 0 elcontador se ha recargado. De esta manera, el mayor valor admitido en AX al llamar es 65535, aunque noconviene que sea superior a 65400, para permitir que las recargas puedan ser detectadas en la máquina máslenta (un XT a 4.77 y en 135/1193180 segundos dispone de unos 540 ciclos, en los que holgadamente cubreeste bucle).

A la hora de emplear las rutinas anteriores hay que tener en cuenta dos consideraciones. Por un lado,están diseñadas para hacer pequeños retardos: llamándolas repetidamente, el bucle que hay que hacer (y lasinterrupciones que se producen durante el proceso) provoca que retarden más de la cuenta. Por ejemplo, en

Page 257: PCA, PS2 ,IBM y AT

257EL HARDWARE DE APOYO AL MICROPROCESADOR

el programa principal, poniendo 1200 en CX en lugar de 20, el retardo debería ser de 60 segundos; sinembargo, comparando este dato con el contador de hora de la BIOS (en una versión ligeramente modificadadel programa) resulta ser de casi 60,2 segundos. La segunda consideración está relacionada con lasinterrupciones: de la manera que está el listado, se puede producir una interrupción en la que algún programaresidente utilice el contador 2 del temporizador, alterando el funcionamiento de las rutinas de retardo (porejemplo, una utilidad de click en el teclado) o incluso provocando un fallo en la misma (si a ésta no le datiempo a comprobar que ya es la hora): este es un aspecto a tener en cuenta en un caso serio. Se puede, porejemplo, inhibir todas las interrupciones (o enmascar sólo las más molestas), aunque anular la interrupcióndel temporizador, la más peligrosa, provocaría un retraso de la hora del ordenador.

Para hacer retardos o temporizaciones de más de 50 milisegundos, es más conveniente emplear elcontador de hora de la BIOS (variable de 32 bits en 0040h:006Ch) que la INT 8 se encarga de incrementar18,2 veces cada segundo y de volver a ponerlo a cero cada 24 horas. No es conveniente mirar el valor delcontador de hora de la BIOS, sumarle una cantidad y esperar a que alcance dicha cantidad fija: la experienciademuestra que eso produce a veces cuelgues del ordenador, no solo debido a que suele fallar cuando son las23:59:59 sino también porque cuando se alcanza el valor esperado, por cualquier motivo (tal como unalargamiento excepcional de la rutina que controla INT 8 ó INT 1Ch debido a algún programa residente)puede que el programa principal no llegue a tiempo para comprobar que ya es la hora... y haya que esperarotras 24 horas a probar suerte. Lo ideal es contar las veces que cambia el contador de hora de la BIOS.

Por último, como ejemplo ameno, el siguiente fragmento de programa hace que la hora del ordenadorvaya diez veces más rápida -poco recomendable, aunque muy divertido- programando el contador 0 con unvalor de cuenta 6553 (frente al 0=65536 habitual), de la siguiente manera:

MOV AL,00110110b ; contador 0, operación 11b, datos binariosOUT 43h,ALMOV BX,6553 ; valor de cuentaMOV AL,BLJMP SHORT $+2OUT 40h,AL ; enviar byte bajoMOV AL,BHJMP SHORT $+2OUT 40h,AL ; enviar byte alto

Un método genial para hacer retardos y controlar timeouts en AT.

Aunque ausente en todos los manuales de referencia técnica y en todos los libros relacionados conla programación de PC, existe un método muy fácil y eficiente para temporizar disponible en todos losordenadores AT. Pese a no estar documentado, un programa muy usual como es el KEYB del MS-DOS (apartir de la versión 5.0 del sistema) lo utiliza en todos los AT, sin importar el modelo. Por ello, cabe suponerque seguramente los futuros equipos mantendrán la compatibilidad en este aspecto. Sucede que la salida delcontador 1 del 8254, encargada del refresco de la memoria, controla de alguna manera desconocida (tal veza través de un flip-flop) la generación de una onda cuadrada de unos 33 KHz que puede leerse a través delbit 4 del puerto 61h (no se trata de la salida OUT del contador 1: éste está programado en modo 2 y nogenera precisamente una onda cuadrada). El contador 1 es programado por la BIOS en todos los PC con unacuenta 18, conmutando el nivel de la salida cada segundo 1193180/18 = 66287,77 veces. Para hacer undeterminado retardo basta con contar las veces que el bit cambia de nivel: la función en ensambladorretardo_asm() del programa de ejemplo lo ilustra. Este método es especialmente interesante en los programasresidentes que precisen retardos de precisión, para sonido u otras tareas, tales como limitar la duraciónmáxima de una comprobación en un bit de estado a unos milisegundos o microsegundos (control detimeouts); la principal ventaja es que no se modifica en absoluto la configuración de ningún chip que puedaestar empleando el programa principal, empezando por el 8254. Además, no requiere preparación previaalguna. Para los más curiosos, decir que el bit 5 del puerto 61h es la salida OUT del contador 2 del 8254 (lalínea OUT del contador 2 del 8253 de los PC/XT también puede consultarse a través del bit 5, pero delpuerto 62h).

El único inconveniente del método es la alta frecuencia con que cambia el bit: esta misma rutinaescrita en C podría no ser suficientemente ágil para detectar todas las transiciones en las máquinas AT más

Page 258: PCA, PS2 ,IBM y AT

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

lentas a 6 MHz. A partir de 8 MHz sí puede ser factible, como evidencian las pruebas realizadas, aunque hayque extremar las precauciones para que el código compilado sea lo bastante rápido: utilizar las dos variablesregistro que realmente soportan los compiladores y huir de la aritmética de 32 bits, como puede observarseen la función retardo_c() del programa de ejemplo. Una mala codificación o compilador podrían hacerinservible el método incluso en una máquina a 16 ó 20 MHz. Para no tener problemas, es mejor emplear laversión en ensamblador, escrita en un C no mucho menos estándar. La macro MICRO() ayuda a seleccionarcon más comodidad el retardo, indicándolo en µs, aunque implica una operación en coma flotante que porsí sola añade unos 100 µs de retardo adicionales en un 386-25 sin coprocesador y con las librerías de Borland.

Anécdota: Para los más curiosos, decir que los programadores de Microsoft emplean este método en el KEYB en dos ocasiones: paralimitar a un tiempo razonable la espera hasta que el registro de entrada del 8042 se llene (15 ms) y, en otra ligera variante, paracontrolar la duración del pitido de error. Los aficionados al ensamblador pueden comprobarlo personalmente aplicando el comandoU del DEBUG sobre el KEYB para desensamblar a partir de los offsets 0E39 y 0D60, respectivamente: en el primer caso, la subrutinasólo es ejecutada en AT; en el segundo, veréis como el KEYB se asegura de que el equipo es un AT comprobando el valor de BPantes de saltar a 0D70 (ejecuta un bucle vacío en las demás máquinas). Esta nueva técnica ha permitido eliminar respecto a anterioresversiones del programa algunos test sobre tipos de ordenadores, cuya finalidad más común era ajustar las constantes de retardo. Sonválidos tanto el KEYB del MS-DOS 5.0 castellano como el del MS-DOS 6.0 en inglés o castellano indistintamente (¡las direccionesindicadas coinciden!). También en las BIOS modernas suele haber ejemplos de esta técnica, aunque las direcciones ya no coinciden...

/********************************************************************//* *//* Programa de demostración del método de retardo basado en la *//* monitorización de los ciclos de refresco de memoria del AT. *//* *//********************************************************************/

#include <dos.h>

#define MICRO(microseg) ((long)(microseg/15.08573727))

void retardo_asm(), retardo_c();

void main()

/* cuatro formas de hacer un mismo retardo de precisión */

retardo_asm (66267L); /* un segundo */retardo_asm (MICRO(1000000L)); /* otro segundo (¡más claro!) */retardo_c (66267L); /* ahora en C */retardo_c (MICRO(1000000L)); /* la otra alternativa */

void retardo_asm (long cuenta) /* método ensamblador recomendado */

asm push axasm push cxasm push dxasm mov cx,word ptr cuenta /* DX:CX = cuenta */asm mov dx,word ptr [cuenta+2]

asm jcxz fin_l /* posible cuenta baja nula */esp_ref: asm in al,61h

asm and al,10h /* aislar bit 5 */asm cmp al,ahasm je esp_ref /* esperar cambio de nivel */asm mov ah,alasm loop esp_ref /* completar cuenta baja */

fin_l: asm and dx,dxasm jz fin_ret /* posible cuenta alta nula */asm dec dxasm jmp esp_ref /* completar cuenta alta */

fin_ret: asm pop dxasm pop cxasm pop ax

void retardo_c (long cuenta) /* método en C no recomendado */

register a, b;unsigned cuenta_h, cuenta_l;

cuenta_h=cuenta >> 16; cuenta_l=cuenta & 0xFFFF;

dodo

while (a==(b=inportb(0x61) & 0x10));a=b;

while (cuenta_l--);while (cuenta_h--);

12.3.4 - SÍNTESIS DE SONIDO.

La producción de sonido es uno de los puntos más débiles de los ordenadores compatibles, que sólosuperan por muy escaso margen a alguno de los micros legendarios de los 80, si bien las tarjetas de sonidohan solventado el problema. Pero aquí nos conformaremos con describir la programación del altavoz. Entodos los PCs existen dos métodos diferentes para generar sonido, con la utilización del 8254 o sin él, queveremos por separado.

Control directo del altavoz.

El altavoz del ordenador está ligado en todas las máquinas al bit 1 del puerto E/S 61h. Si se hacecambiar este bit (manteniéndolo durante cierto tiempo alto y durante cierto tiempo bajo, repitiendo el procesoa gran velocidad) se puede generar una onda cuadrada de sonido. Cuanto más deprisa se realice el proceso,mayor será la frecuencia del sonido. Por fortuna, la baja calidad del altavoz del PC redondea la onda cuadraday produce un sonido algo más musical de forma involuntaria. No existe, en cualquier caso, control sobre elvolumen, que dada la calidad del altavoz también está en función de la frecuencia. Este método de producciónde sonido tiene varios inconvenientes. Por un lado, la frecuencia con que se hace vibrar al bit que lo produce,si no se tiene mucho cuidado, está a menudo más o menos ligada a la capacidad de proceso del ordenador:esto significa que el sonido es más grave en máquinas lentas y más agudo en las rápidas. Esto esparticularmente grave y evidente cuando las temporizaciones se hacen con bucles de retardo con registros de

Page 259: PCA, PS2 ,IBM y AT

259EL HARDWARE DE APOYO AL MICROPROCESADOR

la CPU: la frecuencia del sonido está totalmente a merced de la velocidad de la máquina en que se produce.Es por ello que el pitido de error que produce el teclado es a menudo distinto de unos ordenadores a otros,aunque tengan el mismo KEYB instalado. Otro gran inconveniente de este método es que las interrupciones,fundamentalmente la del temporizador, producen fuertes interferencias sobre el sonido. Por ello, es normaltenerlas inhibidas, con el consiguiente retraso de la hora. Por último, un tercer gran inconveniente es que laCPU está completamente dedicada a la producción de sonido, sin poder realizar otras tareas mientras tanto.

Antes de comenzar a producir el sonido con este método hay que bajar la línea GATE del 8254, yaque cuando está en alto y se activa también el bit 1 del puerto E/S 61h, el temporizador es el encargado deproducir el sonido (este es el segundo método, como veremos). Por tanto, es preciso poner primero a ceroel bit 0 del mismo puerto (61h):

CLI ; evitar posible INT 8, entre otrasIN AL,61hAND AL,11111110bJMP SHORT $+2 ; estado de espera para E/SOUT 61h,AL ; bajar GATE del contador 2 del 8254MOV CX,100h ; 256 vueltas

otro_ciclo: PUSH CXIN AL,61hXOR AL,2 ; invertir bit 1JMP SHORT $+2OUT 61h,ALMOV CX,300 ; constante de retardo

retardo: LOOP retardoPOP CXLOOP otro_cicloSTI

Control del altavoz por el temporizador.

El otro método posible consiste en emplear el contador 2 del temporizador conectado al altavoz; así,enviando el período del sonido (1.193.180/frecuencia_en_Hz) a dicho contador (programado en modo 3), éstese encarga de generar el sonido. Esto permite obtener sonidos idénticos en todos los ordenadores. Existe elpequeño problema de que la duración del sonido ha de ser múltiplo de 1/18,2 segundos si se desea utilizarel reloj del sistema para determinarla (un bucle de retardo sería, una vez más, dependiente de la máquina)ya que el contador 2 está ahora ocupado en la producción de sonido y no se puede usar para temporizar (almenos, no sin hacer malabarismos). Alternativamente, se podría evaluar la velocidad de la CPU para ajustarlas constantes de retardo o aumentar la velocidad de la interrupción periódica.

Para emplear este sistema, primero se prepara el contador 2 para temporizar (poniendo a 1 el bit 0del puerto 61h) y luego se conecta su salida al altavoz (poniendo a 1 el bit 1 del puerto 61h). Al final,conviene borrar ambos bits de nuevo. Ahora no es preciso inhibir las interrupciones para garantizar la calidaddel sonido:

MOV AL,10110110b ; contador 2, modo 3, operación 11b, datos binariosOUT 43h,AL ; programar contador 2MOV AX,2711 ; 1.193.180 / 440 Hz (nota LA) = 2711JMP SHORT $+2OUT 42h,ALMOV AL,AHJMP SHORT $+2OUT 42h,AL ; frecuencia programadaJMP SHORT $+2IN AL,61hOR AL,00000011bJMP SHORT $+2OUT 61h,AL ; altavoz sonandoMOV CX,0

demora: LOOP demora ; esperar un cierto tiempo por el peor métodoIN AL,61hAND AL,11111100bJMP SHORT $+2OUT 61h,AL ; altavoz callado

Las frecuencias en Hz de las distintas notas musicales están oficialmente definidas y los músicossuelen tenerlas en cuenta a la hora de afinar los instrumentos. La escala cromática temperada, adoptada porla American Standards Asociation en 1936, establece el LA4 como nota de referencia en 440 Hz. En general,una vez conocidas las frecuencias de las notas de una octava, las de la octava siguiente o anterior se obtienen

Page 260: PCA, PS2 ,IBM y AT

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

multiplicando y dividiendo por dos, respectivamente. La fórmula de abajo permite obtener las frecuencias delas notas asignándolas un número (a partir de 6 y hasta 88; el LA de 440 Hz es la nota 49) con una precisiónrazonable, máxime teniendo en cuenta que van a ir a parar al altavoz del PC. Tal curiosa relación se verificadebido a que la respuesta del oído humano es logarítmica, lo que ha permitido reducir a simples matemáticasel viejo saber milenario de los músicos.

41 43 46 48 50 53 55 58 60 62

... ...

... 40 42 44 45 47 49 51 52 54 56 57 59 61 63 ...

Page 261: PCA, PS2 ,IBM y AT

261EL HARDWARE DE APOYO AL MICROPROCESADOR

12.4. - EL CONTROLADOR DE INTERRUPCIONES 8259.

12.4.1. - COMO Y POR QUE DE LAS INTERRUPCIONES.

Los ordenadores se comunican con el exterior por medio de los dispositivos de entrada y salida. Estosdispositivos son normalmente lentos en comparación con la elevada velocidad de la unidad central. Unejemplo típico puede ser el teclado: entre las pulsaciones de cada tecla hay un espacio de tiempo impredecibley dependiente del usuario. Una manera simple de gestionar los dispositivos de E/S consiste en comprobarcontinuamente si alguno de ellos tiene un dato disponible o lo está solicitando. Sin embargo, esto supone unaimportante pérdida de tiempo para el microprocesador, que mientras tanto podría estar haciendo otras cosas.En una máquina multitarea y/o multiusuario, resulta más interesante que los periféricos puedan interrumpiral microprocesador para solicitarle una operación de entrada o salida en el momento necesario, estando laCPU liberada de la misión de comprobar cuándo llega ese momento. Cuando se produce la interrupción, elmicroprocesador ejecuta la correspondiente rutina de servicio y después continúa con su tarea normal. Loscompatibles PC poseen un hardware orientado por completo a la multitarea (otra cosa es que el 8086 y elDOS no la aprovechen) y la entrada/salida se gestiona casi por completo mediante interrupciones en todaslas máquinas. Por ejemplo, en las operaciones de disco, cuando acaba la transferencia de datos se produceuna interrupción de aviso y una rutina de la BIOS activa una variable que lo indica, en el segmento dememoria 40h. Las propias funciones de la BIOS para acceder al disco se limitan a chequear continuamenteesa variable hasta que cambie, lo que significa un evidente desaprovechamiento de las posibilidades que lagestión por interrupciones pone a nuestra disposición.

Las interrupciones añaden cierta complejidad al diseño del hardware: en principio, es necesariojerarquizarlas de alguna manera para decidir cuál se atiende en el caso de que se produzcan dossimultáneamente. También es importante el control de prioridad para el caso de que se produzca unainterrupción mientras se está procesando otra: sólo se la atenderá si es de mayor prioridad. En este capítulosólo consideraremos las interrupciones hardware, no las de software ni las excepciones del procesador.

12.4.2. - DESCRIPCIÓN DEL INTEGRADO 8259.

Este circuito integrado está especialmente diseñado para controlar las interrupciones en sistemasbasados en el 8080/8085 y en el 8086. Puede controlar hasta 8 interrupciones vectorizadas. Además, a un8259 se le pueden conectar en cascada un máximo de 8 chips 8259 adicionales, lo que permite gestionarsistemas con hasta 64 interrupciones, como veremos.

-CS 1 28 Vcc

-WR 2 27 A0

-RD 3 26 -INTA

D7 4 25 IR7

D6 5 24 IR6

D5 6 23 IR5

D4 7 22 IR4

D3 8 21 IR3

D2 9 20 IR2

D1 10 19 IR1

D0 11 18 IR0

CAS 0 12 17 INT

CAS 1 13 16 -SP/-EN

GND 14 15 CAS 2’8259

El significado e interpretaciónde las señales se muestra a la derecha:

-CS: Habilita la comunicación con la CPU.-WR: Permite al 8259 aceptar comandos de la CPU.-RD: Permite al 8259 dejar la información en el bus de datos.D7..D0: Bus de datos bidireccional, por el que se transmite la

información de control/estado y el número de vector deinterrupción.

CAS0..CAS2: Líneas de cascada, actúan como salida en el 8259 maestro ycomo entrada en los 8259 esclavos, en un sistema con varios8259 interconectados, constituyendo un bus local.

-SP/-EN: Pin de doble función: en el buffered mode del 8259 actuarácomo -EN, para habilitar los buffers del bus; en el modonormal indicará si el 8259 es maestro o esclavo (-SP).

INT: Conectado a la patilla INT de la CPU para producir lainterrupción cuando llegue el momento.

IR0..IR7: Líneas asíncronas de petición de interrupción. Una petición deinterrupción se ejecuta manteniendo IR en alto hasta que serecibe el reconocimiento (modo por flancos) o simplementeponiendo en alto la línea IR (modo por niveles).

-INTA: Línea de reconocimiento de interrupción, por medio de estalínea se fuerza al 8259 a depositar en el bus la informacióndel vector de interrupción. INTA es independiente de -CS.

A0: En conjunción con -CS, -WR y -RD es empleada para enviarlas palabras de comando al 8259 y para solicitar informaciónal mismo. Suele ir conectada a la línea A0 de la CPU.

Page 262: PCA, PS2 ,IBM y AT

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

DESCRIPCIÓN FUNCIONAL

El diagrama funcional del 8259, con la estructura interna de las diversas partes que lo componen, esel siguiente:

INTINTA

BUFFER DEL LÓGICABUS DE DATOS DE CONTROL

D0..D7

-RD LÓGICA DE IR0-WR LECTURA Y I.S.R. LÓGICA I.R.R. IR1A0 ESCRITURA DE IR2

(In GESTIÓN (Interrupt IR3-CS Service DE Request IR4

Register) PRIORIDAD Register) IR5IR6IR7

CAS 0 BUFFER DECAS 1 CASCADA YCAS 2 COMPARADOR IMR

(Interrupt Mask Register)-SP/-EN

bus interno

Los principales registros internos del 8259 son el IRR (Interrupt Request Register) y el ISR (InService Register). El IRR almacena todas las peticiones de interrupción pendientes; el ISR almacena todaslas interrupciones que están siendo atendidas en un momento dado. La lógica de gestión de prioridaddetermina qué interrupción, de las solicitadas en el IRR, debe ser atendida primero: cuando lleguen las señalesINTA dicha interrupción será la primera procesada y su bit correspondiente se activará en el ISR. El bufferdel bus de datos conecta el 8259 con el bus de datos de la placa principal del ordenador: su diseño en 3estados permite desconectarlo cuando sea necesario; a través de este bus circulan las palabras de control yla información de estado. La lógica de lectura y escritura acepta los comandos que envía la CPU: aquí hayregistros para almacenar las palabras de inicialización y operación que envía el procesador; también sirve paratransferir el estado del 8259 hacia el bus de datos. El buffer de cascada/comparador almacena y comparalas identificaciones de todos los 8259 que posea el sistema: el 8259 maestro envía la identificación del 8259esclavo en las líneas CAS, los 8259 esclavos la leen y el implicado en la operación coloca en el bus de datosla dirección (vector) de la rutina que atenderá la interrupción en los 2 próximos (o el próximo) ciclos INTA.

FUNCIONAMIENTO DEL 8259

El funcionamiento del 8259 varía ligeramente en función del sistema en que esté instalado, según seaeste un 8086 o un 8080/8085. Veremos primero el caso del 8086:

1) Una o más líneas IR son activadas por los periféricos, lo que pone a 1 el correspondiente bit del IRR.2) El 8259 evalúa la prioridad de estas interrupciones y solicita la interrupción a la CPU (línea INT) si

es necesario.3) Cuando la CPU reconoce la interrupción, envía la señal -INTA.4) Nada más recibida la señal -INTA de la CPU, el 8259 activa el bit correspondiente a la interrupción

de mayor prioridad (la que va a ser procesada) en el ISR y lo borra en el IRR. En este ciclo, el 8259aún no controla el bus de datos.

5) Cuando la CPU envía un segundo ciclo -INTA, el 8259 deposita en el bus de datos un valor de 8 bitsque indica el número de vector de interrupción del 8086, para que la CPU lo pueda leer.

6) En el modo AEOI del 8259, el bit de la interrupción en el ISR es borrado nada más acabar elsegundo pulso -INTA; en caso contrario, ese bit permanece activo hasta que la CPU envíe elcomando EOI al final de la rutina que trata la interrupción (caso más normal).

Page 263: PCA, PS2 ,IBM y AT

263EL HARDWARE DE APOYO AL MICROPROCESADOR

En el caso de sistemas basados en el 8080/8085, el funcionamiento es idéntico hasta el punto (3),pero a continuación sucede lo siguiente:

4) Nada más recibida la señal -INTA de la CPU, el 8259 activa el bit correspondiente a la interrupciónde mayor prioridad (la que va a ser procesada) en el ISR y lo borra en el IRR. En este ciclo, el 8259deposita en el bus de datos el valor 11001101b, correspondiente al código de operación de lainstrucción CALL del 8080/85.

5) Esta instrucción CALL provoca que la CPU envíe dos pulsos -INTA.6) El 8259 utiliza estos dos pulsos -INTA para depositar en el bus de datos, sucesivamente, la parte baja

y alta de la dirección de memoria del ordenador de la rutina de servicio de la interrupción (16 bits).7) Esto completa la instrucción CALL de 3 bytes. En el modo AEOI del 8259, el bit de la interrupción

en el ISR es borrado nada más acabar el tercer pulso -INTA; en caso contrario, ese bit permaneceactivo hasta que la CPU envíe el comando EOI al final de la rutina que trata la interrupción.

Si en el paso (4), con ambos tipos de microprocesador, no está presente la petición de interrupción(por ejemplo, porque ha sido excesivamente corta) el 8259 envía una interrupción de nivel 7 (si hubiera un8259 conectado en IR7, las líneas CAS permanecerían inactivas y la dirección de la rutina de servicio deinterrupción sería suministrada por el 8259 maestro).

PROGRAMACIÓN DEL 8259

El 8259 acepta dos tipos de comandos generados por la CPU: los ICW (Inicialization CommandWord) que inicializan el 8259, y los OCW (Operation Command Word) que permiten programar lamodalidad de funcionamiento. Antes de que los 8259 de un sistema comiencen a trabajar deben recibir unasecuencia de ICW que los inicialice. Los ICW y OCW constan de secuencias de 2 a 4 comandos consecutivosque el 8259 espera recibir secuencialmente, unos tras otros, a través del bus de datos, según sea necesario(el propio 8259 se encarga de contarlos midiendo los pulsos de la línea -WR). Los OCW pueden ser enviadosen cualquier momento, una vez realizada la inicialización.

La comunicación con el 8259 emplea las líneas -WR y -RW, así como A0. El hecho de que existauna sola línea de direcciones implica que el 8259 sólo ocupa dos direcciones de puerto de E/S en el espaciode entrada y salida del ordenador.

ICWS (Inicialization Command Words).

ICW1: Cuando un comando es enviado con A0=0 y D4=1, el 8259 lo interpreta como la primera palabrade la inicialización (ICW1) e inicia dicha secuencia de inicialización, lo que implica lo siguiente:

- Se resetea el circuito sensible a los niveles, lo que quiere decir que hasta nueva orden las líneas IRserán sensibles por flancos de transición bajo-alto.- Se limpia el IMR.- A la línea IR7 se le asigna un nivel de prioridad 7.- Se desactiva el Special Mask Mode. Se queda listo para devolver IRR en la próxima lectura OCW3.- Si IC4 (bit D0) es 0, todas las funciones seleccionadas en ICW4 serán puestas a 0 (non bufferedmode, no AEOI, sistema 8080/85) e ICW4 no será necesaria.

A0 D7 D6 D5 D4 D3 D2 D1 D0

0 A7 A6 A5 1 LTIM ADI SNGL IC4

"Call Address a 0 si ICW4dirección del vector de interrupción, interval": innecesaria

líneas A7..A5 (sólo 8080/85) 1 - 4 bytes0 - 8 bytes

1 - IR por niveles 1 modo single0 - IR por flancos 0 en cascada

Page 264: PCA, PS2 ,IBM y AT

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

Notas: Si SNGL es 1 significa que el 8259 es único en el sistema y no será enviada ICW3. Si IC4es 0, tampoco será enviada ICW4. En el 8080/85, las diversas interrupciones generanCALL’s a 8 direcciones adyacentes separadas 4 u 8 bytes (según indique ADI): paracomponer la dirección, el 8259 inserta A0..A4 (o A0..A5) convenientemente, según lainterrupción que se trate. En el 8086, A7..A5 y ADI son ignoradas.

ICW2: Se envía con A0=1, para diferenciarlo de ICW0 (hacer OUT a la siguiente dirección de puerto).

A0 D7 D6 D5 D4 D3 D2 D1 D0

A15 A14 A13 A12 A111 A10 A9 A8

ó T7 ó T6 ó T5 ó T4 ó T3

Notas: En el 8080/85, A15..A8 completan la dirección de la rutina de servicio; en el 8086, T7..T3determinan los cinco bits más significativos del número de vector de interrupción a invocar(los 3 bajos los suministra el 8259 según la interrupción que se trate).

ICW3: Se envía sólo en el caso de que haya más de un 8259 en el sistema (bit SNGL de ICW1 a cero), encaso contrario en su lugar se enviaría ICW4 (si procede).

Formato de ICW3 a enviar a un 8259 maestro:

A0 D7 D6 D5 D4 D3 D2 D1 D0

1 S7 S6 S5 S4 S3 S2 S1 S0

0 - La línea IR correspondiente no tiene conectado un 8259 esclavo1 - La línea IR correspondiente va conectada a un 8259 esclavo

Formato de ICW3 a enviar a un 8259 esclavo para que memorice de qué línea IR del maestro cuelga:

A0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 0 0 0 ID2 ID1 ID0

ID (identificación) del esclavo (0..7)

ICW4: Se envía sólo si IC4=1 en ICW1, con objeto de colocar el 8259 en un modo de operación distintodel establecido por defecto (que equivale a poner a cero todos los bits de ICW4).

A0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 0 SFNM BUF M/S AEOI µPM

1 - modo 80861 Special Fully Nested Mode 0 - " 8080/850 Not Special Fully Nested Mode

0 X non buffered mode1 0 buffered mode esclavo1 1 buffered mode maestro

1 - Auto EOI0 - EOI normal

Notas: El Special Fully Nested Mode, el buffered mode y la modalidad AEOI serán explicadas mástarde. Nótese que con el 8086 es obligatorio enviar ICW4 para seleccionar esta CPU.

Page 265: PCA, PS2 ,IBM y AT

265EL HARDWARE DE APOYO AL MICROPROCESADOR

OCWS (Operation Command Words).

Una vez inicializado, el 8259 está listo para procesar las interrupciones que se produzcan. Sinembargo, durante su funcionamiento normal está capacitado para recibir comandos de control por parte dela CPU.

OCW1:A0 D7 D6 D5 D4 D3 D2 D1 D0

1 M7 M6 M5 M4 M3 M2 M1 M0

Este comando activa y borra bits en el IMR (Interrupt Mask Register). Los bits M0..M7 deOCW1 se corresponden con sus correspondientes bits del IMR. Un bit a 1 significa interrupciónenmascarada (inhibida) y a 0, interrupción habilitada.

OCW2:A0 D7 D6 D5 D4 D3 D2 D1 D0

0 R SL EOI 0 0 L2 L1 L0

Nivel de IR sobre el que actuar

0 0 1 EOI no específico Fin de interrupción0 1 1 (*) EOI específico1 0 1 Rotar en comando EOI no específico1 0 0 Activar rotación en modo AEOI Rotación automática0 0 0 Desactivar rotación en modo AEOI1 1 1 (#) EOI específico asignando prioridad Rotación específica1 1 0 (#) Comando para asignar prioridad0 1 0 No operación

(*) Usados L0..L2(#) en L0..L2 se indica la línea IR que recibirá la

menor prioridad (en IR+1 queda la mayor).

OCW3:A0 D7 D6 D5 D4 D3 D2 D1 D0

0 0 ESMM SMM 0 1 P RR RIS

Modo de máscara especial: Comando de lectura de registro:------------------------- -------------------------------

0 X - No actuar 0 X - No actuar1 0 - Inhibir Special Mask Mode 1 0 - Leer IRR en próximo pulso -RD1 1 - Activar Special Mask Mode 1 1 - Leer ISR en próximo pulso -RD

1 - Comando POLL0 - No es comando POLL

TRABAJANDO CON EL 8259

En las ICW y, sobre todo, en las OCW, se han introducido un aluvión de elementos nuevos que seránexplicados a continuación.

Fully Nested Mode.Por defecto, el 8259 opera en esta modalidad (modo de anidamiento completo), a menos que se le

programe de otra manera. En este modo las interrupciones quedan ordenadas, por prioridades, de 0 (máxima)a 7 (mínima). Cuando se produce un reconocimiento de interrupción por parte de la CPU, el 8259 evalúa cuáles la interrupción pendiente de mayor prioridad, coloca su número de vector en el bus y activa su bit

Page 266: PCA, PS2 ,IBM y AT

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

correspondiente en el ISR. Este bit permanece activo hasta que el 8259 recibe el comando EOI (situación másnormal); sin embargo, en el modo AEOI, ese bit se bajaría inmediatamente después del último -INTA.Mientras el bit del ISR esté activo, todas las interrupciones de igual o menor prioridad que lleguenpermanecen inhibidas; sin embargo, las de mayor prioridad podrán interrumpir. En el caso del 8086, cuandocomienza el tratamiento de la interrupción, un bit del registro de estado de la CPU mantiene inhibidas todaslas interrupciones: lo normal es que el programa de control comience con STI para permitir que el 8086 envíenuevas señales INTA al 8259, así el 8259 podrá enviar las interrupciones de mayor prioridad que le lleguen.Tras la secuencia de inicialización, las interrupciones quedan ordenadas de mayor (IR0) a menor prioridad(IR7), aunque este orden puede modificarse en la modalidad de prioridad rotatoria o con el comando deasignación de prioridad. Nótese que cuando se utiliza el modo AEOI o el Special Mask Mode no se respetael modo Fully Nested Mode (debido a que una interrupción de menor prioridad podría interrumpir a unarutina que gestiona otra de mayor prioridad).

Special Fully Nested Mode.Se emplea en sistemas que tienen varios 8259 conectados. Sólo el 8259 maestro es programado en

este modo, lo que implica las siguientes diferencias respecto al Fully Nested Mode normal:- Cuando se atiende una interrupción de un 8259 esclavo, si viene otra de mayor prioridad de ese

mismo 8259 esclavo, se provoca una interrupción al maestro (normalmente, el 8259 esclavo estaríaenmascarado mientras se procesa una de sus interrupciones).

- Cuando acaba la rutina de servicio de interrupción, hay que enviar un EOI no-específico al 8259esclavo; además hay que leer a continuación su ISR y comprobar si es cero: en ese caso, hay que enviarademás otro EOI al 8259 maestro (si no es cero significa que aún hay interrupciones en proceso en el 8259esclavo).

Modos de EOI.El EOI (End Of Interrupt) sirve para bajar el bit del ISR que representa la interrupción que está

siendo procesada. El EOI puede producirse automáticamente (AEOI) al final de la última señal INTA queenvía la CPU al 8259 para una interrupción dada (tercer ciclo INTA en el 8080/85 y segundo en el 8086);sin embargo, la mayoría de los sistemas requieren una gestión de prioridades en las interrupciones, lo quesignifica que es más conveniente que EOI lo envíe el propio procesador al 8259, a través de OCW2, cuandoacabe la rutina de gestión de interrupción, para evitar que mientras se gestiona esa interrupción se produzcanotras de igual o menor prioridad. En un sistema con varios 8259, el EOI debe ser enviado no sólo al 8259esclavo implicado sino también al maestro. Hay dos modalidades de EOI: la específica y la no-específica.En el EOI no específico, el 8259 limpia el bit más significativo que esté activo en el ISR, que se supone quees el correspondiente a la última interrupción producida (la de mayor prioridad y que está siendo procesada).Esto es suficiente para un sistema donde se respeta el Fully Nested Mode. En el caso en que no fuera así, el8259 es incapaz de determinar cuál fue el último nivel de interrupción procesado, por lo que la rutina quegestiona la interrupción debe enviar un EOI específico al 8259 indicándole qué bit hay que borrar en el ISR.

Rotación de prioridades.Hay sistemas en que varios periféricos tienen el mismo nivel de prioridad, en los que no interesa

mantener un orden de prioridades en las líneas IR. En condiciones normales, nada más atender unainterrupción de un periférico, podría venir otra que también se atendería, mientras los demás periféricos secruzarían de brazos. La solución consiste en asignar el menor nivel de prioridad a la interrupción reciénatendida para permitir que las demás pendientes se procesen también. Para ello se envía un EOI que rote lasprioridades: si, por ejemplo, se había procesado una IR3, IR3 pasará al menor nivel de prioridad e IR4 almayor, quedando las prioridades ordenadas (de mayor a menor): IR4, IR5, IR6, IR7, IR0, IR1, IR2, IR3.Existe también una rotación específica de prioridades, a través de OCW2, que puede realizarse en uncomando EOI o independientemente del mismo (comando para asignar prioridad).

Special Mask Mode.Hay ocasiones en las que mientras se ejecuta una rutina de servicio de interrupción es necesario

permitir que se produzcan ciertas interrupciones de menor prioridad en algunos momentos, o prohibirlo enotros, sin ser quizá interesante enviar el EOI antes de tiempo. Esto implica alterar la estructura normal de

Page 267: PCA, PS2 ,IBM y AT

267EL HARDWARE DE APOYO AL MICROPROCESADOR

prioridades. La manera de realizar esto es activando el Special Mask Mode a través de OCW3 durante larutina de servicio de interrupción (es más que conveniente inhibirlo de nuevo al final). Una vez activado estemodo, el IMR indica qué interrupciones están permitidas (bit a 0) y cuáles inhibidas (bit a 1). Por ello, sueleser conveniente activar el bit del IMR correspondiente a la IR en servicio (para evitar que se produzca denuevo cuando aún no ha sido procesada). Al final hay que enviar un EOI específico, ya que este modo detrabajo altera el Fully Nested Mode habitual.

Comando POLL.En esta modalidad poco habitual, habilitada a través de OCW3, no se emplea la salida INT del 8259

o bien el microprocesador trabaja con las interrupciones inhibidas. El servicio a los periféricos es realizadopor software utilizando el comando POLL. Una vez enviado el comando POLL, el 8259 interpreta la próximalectura que se realice como un reconocimiento de interrupción, actualizando el ISR y consultando el nivelde prioridad. Durante esa lectura, la CPU obtiene en el bus de datos la palabra POLL que indica (en el bit7) si hay alguna interrupción pendiente y, en ese caso, cuál es la de mayor prioridad (bits 0-2).

Lectura de información del 8259.El IMR puede ser leído a través de OCW0; para leer el contenido del IRR y el ISR hay que emplear

OCW3. Para estos dos últimos registros hay que enviar una OCW3 que elija el IRR o el ISR; a continuaciónse puede leer el bus de datos (A0=0) sin necesidad de enviar más OCW3 (el 8259 es capaz de recordar sitiene que leer el IRR o el ISR). Esto último no es así, evidentemente, en el caso de utilizar el comando POLL(tras enviarlo, la próxima lectura se interpreta como un INTA). Tras inicializarse, el 8259 queda preparadopor defecto para devolver IRR a la primera lectura.

Buffered Mode.Al emplear el 8259 en grandes sistemas, donde se requieren buffers en los buses de datos, si se va

a emplear el modo cascada existe el problema de la habilitación de los buffers. Cuando se programa el modobuffer, la patilla -SP/-EN del 8259 actúa automáticamente como señal de habilitación del los buffers cada vezque se deposita algo en el bus de datos. Si se programa de esta manera el 8259 (bit BUF de ICW4) serápreciso distinguir por software si se trata de un 8259 maestro o esclavo (bit M/S de ICW4).

12.4.3. - EL 8259 DENTRO DEL ORDENADOR.

Los PC/XT vienen equipados con un 8259 conectado a la dirección base E/S 20h; este controladorde interrupciones es accedido, por tanto, por los puertos 20h (A0=0) y 21h (A0=1). En los AT y máquinassuperiores, adicionalmente, existe un segundo 8259 conectado en cascada a la línea IR2 del primero. Estesegundo controlador es accedido a través de los puertos 0A0h y 0A1h. La BIOS del ordenador, al arrancarla máquina, coloca la base de interrupciones del primer controlador en 8, lo que significa que las respectivasIR0..IR7 están ligadas a los vectores de interrupción 8..15; el segundo 8259 de los AT genera lasinterrupciones comprendidas entre 70h y 77h. La asignación de líneas IR para los diversos periféricos delordenador es la siguiente (por orden de prioridad):

IRQ 0 Temporizador (INT 08h)IRQ 1 Teclado (INT 09h)IRQ 2 En los PC/XT: canal E/S (INT 0Ah)

IRQ 8 Reloj de tiempo real (INT 70h)IRQ 9 Simulación de IRQ2 (INT 71h)IRQ 10 Reservado (INT 72h)IRQ 11 Reservado (INT 73h)IRQ 12 Reservado (INT 74h) Sólo AT y PS/2IRQ 13 Coprocesador aritmético (INT 75h)IRQ 14 Controlador de disco duro (INT 76h)IRQ 15 Reservado (INT 77h)

IRQ 3 COM2 (INT 0Bh)IRQ 4 COM1 (INT 0Ch)IRQ 5 Disco duro PC/XT (LPT2 en el AT) (INT 0Dh)IRQ 6 Controlador de disquetes (INT 0Eh)IRQ 7 LPT1 (INT 0Fh)

En los AT, la línea IR2 del 8259 maestro es empleada para colgar de ella el segundo 8259 esclavo.Como la línea IR2 está en el slot de expansión de 8 bits, por razones de compatibilidad los AT tienenconectado en su lugar la IR9 que simula la IR2 original. Cuando se produce una IR9 debido a un periférico

Page 268: PCA, PS2 ,IBM y AT

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

de XT que pretendía generar una IR2, el AT ejecuta una rutina de servicio en INT 71h que salta simplementea la INT 0Ah (tras enviar un EOI al 8259 esclavo).

La colocación de IRQ0-IRQ7 en el rango INT 8-INT 15 fue bastante torpe por parte de IBM, alsaltarse la especificación de Intel que reserva las primeras 32 interrupciones para el procesador. En modoprotegido, algunas de esas excepciones es estrictamente necesario controlarlas. Por ello, los sistemasoperativos que trabajan en modo extendido y ciertos extensores del DOS (como las versiones 3.x deWINDOWS) se ven obligados a mover de sitio estas interrupciones. En concreto, WINDOWS 3.x las colocaen INT 50h-INT 57h (por software, las máquinas virtuales 8086 emulan las correspondientes INT 8-INT 15).Además, en el modo protegido del 286/386 (o el virtual-86 del 386) la tradicional tabla de vectores deinterrupción es sustituida por otra de descriptores, aunque el funcionamiento global es similar.

La interrupción no enmascarable del 80x86 no está controlada por el 8259: es generada por lacircuitería que controla la memoria si se detecta un error de paridad. La interrupción no enmascarable puedeser enmascarada en los ordenadores compatibles gracias a la circuitería de apoyo al procesador, aunque noes frecuente; en los AT el bit 7 del puerto 70h controla su habilitación (si es cero, la NMI está habilitada)sin embargo también se podría inhibir el control de paridad directamente (activando los bits 2 y 3 de ladirección E/S 61h, respetando el resto de los bits de ese puerto por medio de una lectura previa). En losPC/XT, es el puerto 0A0h el que controla la habilitación de la NMI, también con el bit 7 (con la diferenciade que debe estar a cero para inhibirla).

Durante la inicialización del ordenador, la BIOS envía sucesivamente al 8259 las palabras ICW1 aICW4 de la siguiente manera (listado extraído directamente de la BIOS):

; Inicialización del 8259 maestro (XT/AT)MOV AL,10001b ; funcionamiento por flancos, cascada, ICW4 necesariaOUT 20h,AL ; enviar ICW1JMP SHORT $+2 ; estado de espera para E/SMOV AL,8 ; base de interrupciones en INT 8OUT 21h,AL ; enviar ICW2JMP SHORT $+2MOV AL,4 ; hay un esclavo en IR2 (S2=1) ¡poner 0 en PC/XT!OUT 21h,AL ; enviar ICW3JMP SHORT $+2MOV AL,1 ; modo 8086, EOI normal, not buffered modeOUT 21h,AL ; enviar ICW4: completada la inicialización del 8259-1JMP SHORT $+2MOV AL,255OUT 21h,AL ; enmascarar todas las interrupciones

JMP SHORT $+2 ; Inicialización del 8259 esclavo (sólo AT)MOV AL,10001b ; funcionamiento por flancos, cascada, ICW4 necesariaOUT 0A0h,AL ; enviar ICW1JMP SHORT $+2MOV AL,70h ; base de interrupciones en INT 70hOUT 0A1h,AL ; enviar ICW2JMP SHORT $+2MOV AL,2 ; es el esclavo conectado a IR2OUT 0A1h,AL ; enviar ICW3JMP SHORT $+2MOV AL,1 ; modo 8086, EOI normal, not buffered modeOUT 0A1h,AL ; enviar ICW4: completada la inicialización del 8259-2JMP SHORT $+2MOV AL,255OUT 0A1h,AL ; enmascarar todas las interrupciones

Como se puede observar, la rutina de arriba enmascara todas las interrupciones a través del IMR. Elobjetivo de esta medida es evitar que se produzcan interrupciones antes de desviar los correspondientesvectores, pudiendo incluso mientras tanto estar habilitadas las interrupciones con STI.

Cuando se produce una interrupción de la CPU (bien por software o por hardware), el indicador deinterrupciones del registro de estado del 8086 se activa para inhibir otra posible interrupción mientras seprocesa esa (la instrucción IRET recuperará los flags del programa principal devolviendo las interrupcionesa su estado previo). Lo normal suele ser que las rutinas que gestionan una interrupción comiencen por un STIcon objeto de permitir la generación de otras interrupciones; las interrupciones sólo deben estar inhibidas enbrevísimos momentos críticos. Sin embargo, cuando se procesa una interrupción hardware, el registro deinterrupciones activas (ISR) indica qué interrupción en concreto está siendo procesada; si en ese momentollega otra interrupción hardware de menor o igual prioridad le será denegada la petición, si es de mayorprioridad le será concedida (si la rutina comenzaba por STI). Cuando acaba de procesarse la interrupciónhardware, la instrucción IRET no le dice nada al 8259, por lo que el programador debe preocuparse de borrarel ISR antes de acabar. Si, por ejemplo, se gestiona la interrupción del temporizador sin limpiar al final el

Page 269: PCA, PS2 ,IBM y AT

269EL HARDWARE DE APOYO AL MICROPROCESADOR

ISR, a partir de ese momento quedarán bloqueados el teclado, los discos ... Conviene aquí señalar que unarutina puede apoyarse en una interrupción hardware sin necesidad de reprogramarla por completo. Ejemplo:

STIPUSH AXIN AL,puerto_tecladoCALL anterior_int9;; procesar tecla;POP AXIRET

Al producirse la INT 9 se lee el código de rastreo de la tecla y luego sellama a la rutina que gestionaba con anterioridad a ésta la INT 9: ella seencargará de limpiar el ISR que, por tanto, no es tarea de nuestra rutina. Sihubiera que limpiar el ISR, bastaría con un EOI no específico (OCW2: enviarun valor 20h al puerto 20h para el 8259-1 y al puerto 0A0h para el 8259-2; enlas IRQ8-IRQ15 hay que enviar el EOI a ambos controladores de interrupción).

Aviso: Aunque el funcionamiento del 8259 es suficientemente lógico como para pasar casi inadvertido, hay veces en quehay que tenerlo en cuenta. Por ejemplo, al utilizar el servicio 86h de la INT 15h del AT (con objeto de hacer retardos) desdeuna interrupción hardware comprendida entre IRQ 0 e IRQ 7, conviene limpiar el ISR antes de llamar: no basta con hacerloal final de la rutina. La causa es que la BIOS utiliza las interrupciones asociadas al reloj de tiempo real para hacer el retardo,y en algunas máquinas es poco precavido y no limpia el ISR al principio, lo que deja totalmente bloqueado el ordenador.

12.4.4. - EJEMPLO: CAMBIO DE LA BASE DE LAS INTERRUPCIONES.

La siguiente utilidad reprograma el 8259 maestro para desviar las INT 8-INT 15 a los nuevos vectoresINT 50h-INT 57h (que invocan a los originales, para que el sistema siga funcionando con normalidad). Estanueva ubicación no ha sido elegida por capricho, y es la misma que emplea WINDOWS 3.x. La razón es queel 386 trabaja normalmente en modo virtual-86 bajo MS-DOS 5.0; cuando se produce una interrupción seejecuta una rutina en modo protegido. El EMM386 del MS-DOS 5.0 no está preparado para soportar lasIRQ0-IRQ7 en otra localización que no sea la tradicional INT 8-INT 15 ó en su defecto INT 50h-INT 57h(por compatibilidad con WINDOWS). Con el QEMM386 o, simplemente, sin controlador de memoriaexpandida instalado, no habría problemas y se podría elegir otro lugar distinto. Por cierto: si se entra y sesale de WINDOWS, la nueva localización establecida, ya sea en 50h o en otro sitio, deja de estar vigente:esto significa que WINDOWS reprograma la interrupción base al volver al DOS. Personalmente hecomprobado que aunque IRQDEMO fuera más elegante (empleando funciones de la especificación VCPI),nuestro querido WINDOWS no lo sería: ¡para qué molestarse!. Sin embargo, IRQDEMO sí se toma lamolestia de comprobar si la máquina es un XT o un AT para enviar correctamente la ICW3 del 8259.

; ********************************************************************; * IRQDEMO.ASM - Utilidad residente de demostración, que desvía *; * las interrupciones hardware INT 8-INT 15 hacia *; * los vectores INT 50h a INT 57h. *; ********************************************************************

irqdemo SEGMENTASSUME CS:irqdemo, DS:irqdemo

ORG 100hinicio:

JMP main

; ------------ Area residente

irq0: INT 8 ; simular IRQ’s normales (seIRET ; podría aprovechar también

irq1: INT 9 ; para hacer algo más útil).IRET

irq2: INT 10IRET

irq3: INT 11IRET

irq4: INT 12IRET

irq5: INT 13IRET

irq6: INT 14IRET

irq7: INT 15IRET

tam_resid EQU ($-OFFSET inicio+256+15)/16

; ------------ Código de instalación

main PROCLEA BX,tabla_intsMOV AL,50h ; nueva base para IRQ’s 0-7

otra_int: PUSH AXPUSH BXMOV AH,25hMOV DX,[BX]INT 21h ; desviar INT 50h-57hPOP BXADD BX,2POP AXINC ALCMP AL,58hJB otra_intCALL es_AT?MOV BL,4MUL BLMOV BL,AL ; BL = 4 en AT y 0 en PC/XTCALL inic_8259LEA DX,texto_txt

MOV AH,9INT 21h ; mensaje de instalaciónMOV ES,ES:[2Ch]MOV AH,49hINT 21h ; liberar entornoMOV AH,31hMOV DX,tam_residINT 21h ; terminar residente

main ENDP

; ------------ Subrutinas de apoyo a la instalación.

inic_8259 PROC ; Inicialización 8259 maestroMOV AL,0FFhOUT 21h,AL ; enmascarar todas las IRQJMP SHORT $+2MOV AL,10001b ; flancos, maestro, sí ICW4OUT 20h,AL ; enviar ICW1JMP SHORT $+2 ; estado de espera E/SMOV AL,50h ; base interrupciones INT 50hOUT 21h,AL ; enviar ICW2JMP SHORT $+2MOV AL,BL ; 4 en AT y 0 en PC/XTOUT 21h,AL ; enviar ICW3JMP SHORT $+2MOV AL,1 ; modo 8086, EOI normalOUT 21h,AL ; enviar ICW4JMP SHORT $+2MOV AL,0OUT 21h,AL ; permitir todas las IRQRET

inic_8259 ENDP

es_AT? PROC ; comprobar si es XT ó ATPUSHFPOP AXAND AX,0FFFhPUSH AXPOPFPUSHFPOP AXAND AX,0F000hCMP AX,0F000hMOV AX,1 ; indicar ATJNE es_ATDEC AX ; indicar PC/XT

es_AT: RETes_AT? ENDP

tabla_ints DW irq0, irq1, irq2, irq3, irq4, irq5, irq6, irq7texto_txt DB 13,10,"Las interrupciones 8-15 son ahora 50-57h."

DB 13,10,"$"

irqdemo ENDSEND inicio

Page 270: PCA, PS2 ,IBM y AT

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

12.5 - EL CHIP DMA 8237.

12.5.1 - EL ACCESO DIRECTO A MEMORIA.

El acceso directo a memoria es una técnica de diseño del hardware que permite a los periféricosconectados a un sistema realizar transferencias sobre la memoria sin la intervención del procesador. De estamanera, las lentas operaciones de entrada y salida de bloques de datos, se pueden realizar en la sombramientras la CPU se dedica a otras tareas más útiles. Como la memoria del ordenador sólo puede ser accedidaa un tiempo por una fuente, en el momento en que el DMA realiza las transferencias el microprocesador sedesconecta de los buses, cediéndole el control. El funcionamiento del controlador de DMA se basa en unosregistros que indican la dirección de memoria a ser accedida y cuántas posiciones de memoria quedan aúnpor transferir. La transferencia de datos entre los periféricos y la memoria por DMA no suele efectuarse degolpe, sino más bien poco a poco, robándole algunos ciclos a la CPU. Los controladores de DMA suelendisponer de varias líneas de petición de DMA, pudiendo atender las necesidades de varios periféricos quesoliciten una transferencia, quienes deben haber sido diseñados expresamente para soportar el DMA.

12.5.2 - DESCRIPCIÓN DEL INTEGRADO 8237.

El 8237 es un controlador de DMA de 4 canales programables en 3 modos diferentes, con posibilidadde ser conectado en cascada con otros de su misma especie. Además de las funciones tradicionales, el 8237soporta también transferencias memoria-memoria, incluyendo la posibilidad de rellenar un área de la memoriacon cierto dato. La arquitectura es de 16 bits, tanto para direcciones como datos, por lo que estáespecialmente diseñado para sistemas basados en el Z80 y 8085; aunque puede operar también conprocesadores más avanzados, como la serie 80x86, pero sin alcanzar a aprovechar todas sus posibilidades.

-IOR 1 40 A7

-IOW 2 39 A6

-MEMR 3 38 A5

-MEMW 4 37 A4

NC 5 36 -EOP

READY 6 35 A3

HLDA 7 34 A2

ADSTB 8 33 A1

AEN 9 32 A0

HRQ 10 31 Vcc

-CS 11 30 DB0

CLK 12 29 DB1

RESET 13 28 DB2

DACK2 14 27 DB3

DACK3 15 26 DB4

DREQ3 16 25 DACK0

DREQ2 17 24 DACK1

DREQ1 18 23 DB5

DREQ0 19 22 DB6

GND 20 21 DB7’8237

CLK: Señal de reloj básica.-CS: Línea de habilitación del chip.RESET: Esta señal provoca la limpieza de los registros de comando, estado, solicitud y los temporales;

borra el banderín last/first y el contador de registro de modo; el registro de máscara se asignapara ignorar las solicitudes. El 8237 queda en Ciclo Inactivo.

READY: Señal que puede ser empleada para extender los pulsos de lectura y escritura en memoria del8237 para trabajar con memorias lentas.

HLDA: Hold Acknowledge, línea por la que la CPU indica que ha liberado los buses.DREQ0..3: DMA Request; son 4 líneas asíncronas de petición de DMA. En el modo de prioridad fija,

DREQ0 tiene la máxima y DREQ3 la mínima. Los periféricos solicitan el servicio de DMAen estas líneas y esperan a bajarlas hasta el correspondiente DACK. La polaridad de DREQes programable. Las líneas no usadas deben ser enmascaradas.

DB0..DB7: BUS de datos bidireccional y triestado. Durante los ciclos de DMA, los 8 bits mássignificativos de la dirección son colocados en el bus de datos con objeto de ser almacenadosen un latch exterior controlado por ADSTB. En las operaciones memoria-memoria, el bus dedatos recibe y envía los bytes a transferir.

-IOR: I/O Read. Línea bidireccional de 3 estados. En el ciclo inactivo es una entrada empleada porla CPU para leer los registros de control; en el ciclo activo actúa como línea de salida paraque el 8237 controle la lectura de datos de los periféricos.

-IOW: I/O Write. Línea bidireccional de 3 estados. En el ciclo inactivo es una entrada empleada porla CPU para escribir los registros del 8237; en el ciclo activo actúa como línea de salida paraque el 8237 controle la escritura de datos en los periféricos.

-EOP: End Of Process. Línea bidireccional que informa de la finalización del servicio DMA. El 8237permite que un ente exterior fuerce el final de un servicio bajando esta línea. El propio 8237genera un pulso en ella cuando se alcanza un TC (Terminal Count, fin de cuenta) en algúncanal, salvo en el modo memoria-memoria del canal 0 (en ese caso, la señal se produce alalcanzarse el TC del canal 1). Esta patilla está conectada en el interior del chip a un transistoren colector abierto, por lo que requiere una resistencia externa. Cuando llega una señal -EOP,el 8237 finaliza el servicio aunque en el modo de autoinicialización los registros base volverána ser escritos en los registros en curso del canal implicado. El canal resulta enmascarado salvoen el caso del modo de autoinicialización.

A0..A3: Líneas bidireccionales triestado de direcciones. En el ciclo inactivo son entradas empleadas para direccionar los registros internos a leer o escribir. Enel ciclo activo, son salidas y proveen los 4 bits menos significativos de la dirección.

A4..A7: Líneas triestado de salida de direcciones. Proveen los 4 bits altos de la dirección durante el ciclo activo.HRQ: Hold Request. Línea de salida para solicitar los buses a la CPU, en el caso en que haya que realizar una transferencia. En los sistemas en que el 8237

controla totalmente el bus, esta patilla puede ir directamente conectada a HLDA.DACK0..3: DMA Acknowledge. Avisa a los periféricos de que ha sido atendida su petición. El nivel de operación de esta línea es programable. RESET las baja.AEN: Address Enable. Habilita el latch de 8 bits que guarda la parte alta de la dirección. Sirve también para inhibir el acceso al bus por parte de otras fuentes.ADSTB: Address Strobe. Línea que controla el almacenamiento de la parte alta de la dirección, cuando está en el bus de datos, en el latch externo.-MEMR: Memory Read. Salida triestado empleada para acceder a la memoria durante la lectura o las transferencias memoria-memoria.-MEMW: Memory Write. Salida triestado empleada para acceder a la memoria durante la escritura o las transferencias memoria-memoria.

Page 271: PCA, PS2 ,IBM y AT

271EL HARDWARE DE APOYO AL MICROPROCESADOR

DESCRIPCIÓN FUNCIONAL

Los modos de operación del 8237 están diseñados para soportar transferencias de una sola palabrade datos y flujos de datos discontinuos entre la memoria y los periféricos. El controlador de DMA esrealmente un circuito secuencial generador de señales de control y direcciones que permite la transferenciadirecta de los datos sin necesidad de registros temporales intermedios, lo que incrementa drásticamente la tasade transferencia de datos y libera la CPU para otras tareas. Las operaciones memoria-memoria precisan deun registro temporal intermedio, por lo que son al menos dos veces más lentas que las de E/S, aunque enalgunos casos aún más veloces que la propia CPU (no es el caso de los ordenadores compatibles).

El 8237 consta internamente de varios bloques: un bloque de control de tiempos que genera lasseñales de tiempo internas y las señales de control externas; un bloque de gestión de prioridades, que resuelvelos conflictos de prioridadcuando varios canales de DMAson accedidos a la vez; tambiénposee un elevado número deregistros para gestionar elfuncionamiento. Los registrosinternos del 8237 estánresumidos en la figura de laderecha.

Tipo de registro Tamaño Nº registros

Registro base de dirección 16 bits 4Registro base contador de palabras 16 bits 4Registro de dirección en curso 16 bits 4Registro contador de palabras en curso 16 bits 4Registro temporal de dirección 16 bits 1Registro temporal contador de palabras 16 bits 1Registro de estado 8 bits 1Registro de comandos 8 bits 1Registro temporal 8 bits 1Registro de modo 6 bits 4Registro de máscara 4 bits 1Registro de petición 4 bits 1

OPERACIÓN DEL DMA

En un sistema, los buses del 8237 están conectados en paralelo al bus general del ordenador, siendonecesario un latch externo para almacenar la parte alta de la dirección de memoria. Cuando está inactivo, el8237 está desconectado de los buses; cuando se produce una petición de DMA pasa a controlar los buses ya generar las señales necesarias para realizar las transferencias. La operación que realiza el 8237 esconsecuencia de la programación realizada previamente en los registros de comando, modo, base de direccióny contador de palabras a transferir.

Para comprender mejor el funcionamiento del 8237 es conveniente considerar los estados generadospor cada ciclo. El DMA opera básicamente en dos ciclos: el activo y el inactivo (o idle). Tras serprogramado, el DMA permanece normalmente inactivo hasta que se produce la solicitud de DMA en algúncanal o vía software. Cuando ésta llega, si ese canal no estaba enmascarado (es decir, inhibido) el 8237solicita los buses a la CPU y se pasa al ciclo activo. El ciclo activo se compone de varios estados internos,en función de la manera en que sea programado el chip.

El 8237 puede asumir 7 diferentes estados, cada uno de ellos compuesto de un ciclo de relojcompleto. El estado 1 (S1) es el estado inactivo o idle. En él se entra cuando no hay pendiente una peticiónde DMA válida, al final de la secuencia de transferencia, o tras un reset o un Master Clear (que se verá másadelante). En S1 el DMA está inactivo pero puede ser programado por el microprocesador del sistema. Elestado 0 (S0) es el primer estado de servicio DMA. El 8237 ha solicitado los buses a la CPU a través de lalínea HRQ pero la CPU aún no ha respondido a través de HLDA. En esta situación, el 8237 puede aúntodavía ser programado. Una vez que la CPU responde, la labor del 8237 puede comenzar: los estados S2,S3 y S4 se suceden entonces para realizar el servicio. Si se necesitara más tiempo, está prevista la posibilidadde insertar estados de espera entre S2 ó S3 y S4 a través de la patilla READY.

Téngase en cuenta que los datos son pasados directamente de la memoria hacia/desde los periféricos,por lo tanto no cruzan a través del DMA (las líneas -IOR y -MEMW, o -IOW y -MEMR, son activadas almismo tiempo). El caso de las operaciones memoria-memoria es especial, ya que para cada palabra a moverhay que realizar la operación de lectura (en unos estados denominados S11, S12, S13 y S14) y después lade escritura (estados S21, S22, S23, S24).

Page 272: PCA, PS2 ,IBM y AT

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

Ciclo Inactivo.Este es el estado en el que el 8237 espera pacientemente a que aparezca alguna solicitud de DMA,

comprobando las líneas DREQ en los flancos de bajada de las señales de reloj: en esto consisten los estadosS1. En esta situación, el 8237 puede ser programado por la CPU. Para ello, las líneas A0..A3 seleccionan elregistro interno y -IOR e -IOW indican si se trata de leer o escribir. Como algunos de los registros internosson de 16 bits, existe un flip-flop interno que conmuta en cada operación de escritura sobre ellos, para queel 8237 sepa si está recibiendo el byte alto o el bajo (este flip-flop es puesto a cero en un Reset o en uncomando Master Clear, existiendo también comandos especiales para controlarlo). Algunas combinacionesde A0..A3 y las líneas -IOR e -IOW, en lugar de acceder a los registros, constituyen comandos especiales.

Ciclo Activo.Cuando el 8237 está en el ciclo inactivo y se produce una petición por software o un canal no

enmascarado solicita servicio DMA, se pasa al estado activo y se opera en uno de estos 4 modos:

Single Transfer Mode (Modo de transferencia única):

El dispositivo es programado para realizar una única transferencia. El registro contador de palabrases decrementado y el de direcciones se incrementa/decrementa según ha sido programado. Cuando el registrocontador de palabras se desborda (pasa de 0 a 0FFFFh) se activa el bit Terminal Count (fin de cuenta) enel registro de estado y la patilla -EOP genera un pulso. Si el canal estaba programado para autoinicializarseesto es lo que realiza; en caso contrario, se activa automáticamente el bit de máscara para inhibir hasta nuevaorden ese canal.

DREQ debe permanecer activo hasta que DACK responda. Sin embargo, si DREQ permanece activohasta que acaba el proceso de transferencia, la línea HRQ baja y se ceden momentáneamente los buses alsistema. Después, vuelve a subir, y cuando se recibe el HLDA de la CPU se pueden realizar mástransferencias de este tipo. En la serie 8080 y 80x86, esto asegura al menos un ciclo para la CPU entre lassucesivas transferencias del DMA.

Block Transfer Mode (Modo de transferencia de bloque).

Se diferencia del anterior en que en lugar de transferir una sola palabra se mueven todas lasnecesarias hasta que el registro contador de palabras se desborda. Lógicamente, también se acaba el procesosi alguien actúa sobre la patilla -EOP. DREQ sólo es preciso activarlo hasta que DACK responde.

Demand Transfer Mode (Modo de transferencia por demanda).

Se diferencia del anterior en que la transferencia se realiza sólo mientras DREQ permanece activo.Esto significa que se pueden transferir datos hasta agotar las posibilidades del dispositivo; cuando eldispositivo tenga más datos listos puede volver a activar DREQ para continuar donde lo dejó. Esta modalidadpermite dejar ciclos a la CPU cuando no es realmente necesario que el DMA opere. Además, en los períodosde inactividad, los valores de dirección en curso y contador de palabras son almacenados en el Registro dedirecciones en curso y en el Registro contador de palabras en curso correspondientes al canal implicado;mientras tanto, otros canales de mayor prioridad pueden ser atendidos por el 8237.

Conexión en cascada de varios 8237.

Esta conexión es empleada para conectar más de un 8237 en el sistema. La línea HRQ de los 8237hijo es conectada a la DREQ del 8237 padre; la HLDA lo es a la DACK. Esto permite que las peticiones enlos diversos 8237 se propaguen de uno a otro a través de la escala de prioridades del 8237 del que cuelgan.La estructura de prioridades es por tanto preservada. Teniendo en cuenta que el canal del 8237 padre esempleado sólo para priorizar el 8237 adicional que cuelga (hijo), no puede emitir direcciones ni señales decontrol por sí mismo: esto podría causar conflictos con las salidas del canal activo en el 8237 hijo. Por tanto,el 8237 padre se limita en el canal del que cuelga el 8237 hijo a controlar DREQ, DACK y HRQ, dejando

Page 273: PCA, PS2 ,IBM y AT

273EL HARDWARE DE APOYO AL MICROPROCESADOR

inhibidas las demás señales. El -EOP externo será ignorado por el 8237 padre, pero sí tendrá efecto en el8237 hijo correspondiente.

Cuando de un 8237 cuelga otro, estamos ante un sistema DMA de dos niveles. Si del DMA hijocuelga a su vez otro, sería un sistema DMA de tres niveles, como el mostrado a continuación:

C.P.U.’8237

DREQ HRQDACK HLDA

HRQHLDA DREQ HRQ

DACK HLDA’8237

’8237

DREQ HRQDACK HLDA

’8237

Primer nivel Segundo Nivel Tercer Nivel

Al programar los 8237 en cascada, se debe empezar por el primer nivel. Tras un Reset, las salidasDACK son programadas por defecto para ser activas a nivel bajo y son colocadas en alto. Si están conectadasdirectamente a HLDA, el segundo nivel de 8237 no puede ser programado hasta que la polaridad de DACKno se cambie para que sea activa a nivel alto. Los bits de máscara de canales del 8237 padre funcionan comocabría esperar, permitiendo inhibir 8237’s de niveles inferiores.

Modos de transferencia.

Cada uno de los 3 modos de transferencia puede realizar 3 tipos distintos de transferencias: lectura,escritura y verificación. La lectura pasa datos de la memoria al dispositivo E/S (activando -IOW y -MEMR);la escritura mueve datos desde los dispositivos E/S a la memoria (activando -IOR y -MEMW). Lastransferencias de tipo verificación son pseudotransferencias: el funcionamiento es similar a la lectura oescritura pero sin tocar las líneas de control de la memoria ni de los periféricos; durante el modo deverificación se ignora la línea READY; este modo no es permitido en las operaciones memoria-memoria.

Autoinicialización.

Cualquier canal puede ser programado para incluir esta característica. En el momento de programarel chip, los registros base de dirección y base contador de palabras son cargados a la vez y con el mismovalor que los registros de dirección en curso y contador de palabras en curso. Los registros base permaneceninalterados en todo momento, por lo que al final del servicio sirven, en este modo de trabajo, para recargarde nuevo los registros en curso. Esto sucede justo tras la señal -EOP, quedando el 8237 listo para repetir denuevo la misma transferencia (cuando se solicite a través de la línea DREQ o por software). En estamodalidad, los bits de máscara están a 0.

Page 274: PCA, PS2 ,IBM y AT

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

Memoria-Memoria.

En este tipo de transferencia se emplean siempre los canales 0 y 1. La transferencia comienzaactivando la línea DREQ del canal 0, bien por hardware o por software. El 8237 solicita entonces un serviciode DMA ordinario, con el que lee el byte de la memoria a través de 4 estados y empleando el Block TransferMode visto con anterioridad. El registro de dirección en curso del canal 0, que indica la dirección origen enla memoria, es incrementado/decrementado (según haya sido programado) y el dato es almacenado en elregistro temporal del 8237. En otros 4 estados más, el dato es pasado del 8237 de nuevo a la memoria,usando la dirección del registro de dirección en curso del canal 1, que indica la dirección destino en memoria,el cual es también incrementado/decrementado según proceda. Además, se decrementa el registro contadorde palabras en curso del canal 1: si al decrementar se desborda (pasa de 0 a 0FFFFh) se activa el bit TC delregistro de estado (Terminal Count, fin de cuenta) y se genera un pulso -EOP, finalizando el proceso. En elcaso de que el valor del registro contador de palabras del canal 0 pase de 0 a 0FFFFh, sin embargo, no seactúa sobre TC ni sobre EOP (no finaliza el proceso) aunque este canal se autoinicializa si así estabaprogramado.

Si se desea una autoinicialización total en este tipo de transferencias, los registros contadores depalabras del canal 0 y 1 han de ser programados con el mismo valor inicial; de lo contrario, sólo uno de losdos canales se autoinicializará (el que primero desborde su registro contador de palabras).

El canal 0 puede ser también programado para retener siempre la misma dirección durante todas lastransferencias, lo que permite copiar un mismo byte en todo un bloque de la memoria.

El 8237 puede responder a señales -EOP externas durante este tipo de transferencias, pero sólo cedeel control de los buses después de completar la transferencia de la palabra que tenga entre manos. Loscircuitos para comparar datos en búsquedas de bloques pueden emplear -EOP para terminar la operación trasencontrar lo que buscan. Las operaciones memoria-memoria se pueden detectar por hardware como unacombinación de AEN activo sin que al mismo tiempo se produzcan salidas DACK.

Prioridad.

El 8237 tiene dos maneras de codificar la prioridad, seleccionables por software. La primera es laprioridad fija, basada en el número del canal (0-máxima, 3-mínima). Una vez que un canal es atendido, losdemás esperan hasta que acabe. La segunda modalidad es la prioridad rotatoria: el último canal servido pasaa tener la menor prioridad y el que le sigue la máxima. La rotación de prioridades se produce cada vez quese devuelven los buses a la CPU. Esta última modalidad de prioridad asegura que un canal sea atendido almenos después de haber atendido los otros 3, evitando que un solo canal monopolice el uso del DMA. Conindependencia del tipo de prioridad programada, ésta es evaluada cada vez que el 8237 recibe un HLDA.

Compresión de tiempo.

De cara a mejorar el rendimiento en los sistemas más potentes, el 8237 puede ser programado paracomprimir el tiempo de transferencia a dos ciclos de reloj. En cualquier caso, esta posibilidad no estádisponible en las transferencias memoria-memoria.

Generación de direcciones.

Para reducir el número de pines, el 8237 tiene multiplexada la parte alta del bus de direcciones. Enel estado S1, los 8 bits más significativos de la dirección son depositados en un latch externo a través del busde datos. La línea AEN indica a la circuitería externa que debe habilitar el latch como parte alta del bus dedirecciones cuando llega el momento (la parte baja la suministra directamente el 8237). En el Block TransferMode y en el Demand Transfer Mode, que implican múltiples transferencias, el 8237 es suficientementeinteligente como para generar estados S1 sólo cuando hay acarreo en la parte baja del bus de direcciones (1de cada 256 veces) evitando acceder al latch externo cuando no es necesario modificarlo y ahorrando tiempo.

Page 275: PCA, PS2 ,IBM y AT

275EL HARDWARE DE APOYO AL MICROPROCESADOR

PROGRAMACIÓN DEL 8237

El 8237 puede ser programado cuando HLDA está inactivo, siendo responsabilidad del programadorque esto sea así (es decir, programarlo antes de que comience a operar). En cualquier caso, puede existir elriesgo de que mientras se programa un canal, se produzca una petición de DMA en el mismo antes de acabarla programación, y probablemente en un punto crítico (cuando, por ejemplo, se acababa de enviar la mitadde un valor de 16 bits). Para evitar este riesgo, antes de comenzar a programar un canal puede ser necesarioenmascararlo, desinhibiéndolo después.

Registros internos del 8237.

Current Address Register (Registro de dirección en curso).

Cada canal tiene un registro de dirección en curso que almacena la dirección de memoria empleadadurante las transferencias del DMA. Su contenido es incrementado/decrementado después de cadatransferencia. Este registro es inicializado por la CPU enviando dos bytes consecutivos; en modoautoinicialización, su contenido inicial se restaura cuando ésta se produce.

Current Word Register (Registro contador de palabras en curso).

Cada canal tiene un registro contador de palabras en curso, que determina el número de bytes atransferir en la operación menos uno (para un valor inicial 100, por ejemplo, se transmiten 101 bytes). Trascada transferencia se decrementa: cuando pasa de 0 a 0FFFFh se genera el TC (Terminal Count) y el procesofinaliza. Este registro es inicializado por la CPU enviando dos bytes consecutivos; en modo autoinicialización,su contenido inicial se restaura cuando ésta se produce; de lo contrario continúa con un valor 0FFFFh.

Base Address & Base Word Count Registers (Registros base de dirección y base contador de palabras).

Cada canal tiene también un registro base de dirección y otro base contador de palabras. Estosregistros almacenan el valor inicial de los registros de dirección en curso y contador de palabras en curso,ya que ambos tipos de registros se cargan simultáneamente durante la programación. El valor almacenadoen estos registros se emplea en la autoinicialización, para recargar los registros en curso.

Canal Registro(s) DirecciónA3 A2 A1 A0

Base de dirección y de dirección en curso Escribir 0 0 0 00 De dirección en curso Leer 0 0 0 0

Base contador de palabras y contador de palabras en curso Escribir 0 0 0 1Contador de palabras en curso Leer 0 0 0 1

Base de dirección y de dirección en curso Escribir 0 0 1 01 De dirección en curso Leer 0 0 1 0

Base contador de palabras y contador de palabras en curso Escribir 0 0 1 1Contador de palabras en curso Leer 0 0 1 1

Base de dirección y de dirección en curso Escribir 0 1 0 02 De dirección en curso Leer 0 1 0 0

Base contador de palabras y contador de palabras en curso Escribir 0 1 0 1Contador de palabras en curso Leer 0 1 0 1

Base de dirección y de dirección en curso Escribir 0 1 1 03 De dirección en curso Leer 0 1 1 0

Base contador de palabras y contador de palabras en curso Escribir 0 1 1 1Contador de palabras en curso Leer 0 1 1 1

Direcciones E/S de los registros de direcciones y contadores

Command Register (Registro de comandos).

Es un registro de 8 bits que controla el funcionamiento del 8237. Se borra tras un Reset o uncomando Master Clear:

Page 276: PCA, PS2 ,IBM y AT

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

7 6 5 4 3 2 1 0

0 DACK sensible 0 no es memoria-memoriaa nivel bajo 1 modo memoria-memoria

1 DACK sensiblea nivel alto 0 no fijar dirección en canal 0

1 fijar dirección canal 0X si bit 0 = 0

0 controlador habilitado0 DREQ sensible en alto 1 controlador inhibido1 DREQ sensible en bajo

0 compresión de tiempo inhibida1 compresión de tiempo activadaX si bit 0 = 1

0 prioridad fija1 prioridad rotatoria

0 escritura posterior activa1 escritura extendida activaX si bit 3 = 1

Mode Register (Registro de modo).

Cada canal tiene un registro de modo asociado, de 6 bits. Cuando se escribe el registro de modo, seenvía un byte al 8237 que selecciona (en los bits 0 y 1) el canal cuyo registro de modo se desea escribir, yel resto de los bits cargan el registro de modo. Cuando se lee, dichos bits estarán a 1 (para leer un registrode modo hay que utilizar antes el comando Clear Mode Register Counter, como se verá en la sección decomandos).

7 6 5 4 3 2 1 0

0 sin autoinicialización 00 seleccionar canal 01 con autoinicialización 01 seleccionar canal 1

10 seleccionar canal 20 modo incremento de direcciones 11 seleccionar canal 31 modo decremento de direcciones

00 transferencia de verificación00 Demand Transfer Mode 01 transferencia de escritura01 Single Transfer Mode 10 transferencia de lectura10 Block Transfer Mode 11 ilegal11 Conexión en cascada XX si bits 6 y 7 ambos activos

Request Register (Registro de petición de DMA).

El 8237 puede responder a peticiones de DMA tanto por hardware (línea DREQ) como por software.En este registro posee un bit para cada canal de DMA. Las peticiones por software no se pueden enmascarar,aunque están sujetas a la lógica de evaluación de prioridades. Cada bit de este registro es activado o borradoselectivamente por software. Todo el registro es borrado ante un Reset. Para modificar sus bits, se debe enviarel comando Write Request register. Si se lee el registro, los bits 0 al 3 muestran el estado de las peticionesen los canales 0 al 3 (los demás bits están a 1). Las peticiones de DMA por software pueden serloindistintamente en el modo single o en el block. Para operaciones memoria-memoria, hay que hacer unapetición de DMA por software en el canal 0.

Mask Register (Registro de máscara de DMA).

Cada canal tiene asociado un bit de máscara que puede ser activado para inhibir las solicitudes deDMA a través de la línea DREQ. Este bit es automáticamente activado cada vez que se produce un -EOP (alfinal de la transferencia) a menos que el canal esté en modo autoinicialización. Cada bit de máscara puedeser modificado por separado, o todos a la vez, con el comando apropiado. Todo el registro es puesto a 1 a

Page 277: PCA, PS2 ,IBM y AT

277EL HARDWARE DE APOYO AL MICROPROCESADOR

través del comando Master Clear o debido a un Reset, lo que inhibe las solicitudes de DMA por hardwarehasta que se envía un comando para limpiar el registro de máscara (o se borran los bits que se desee en elmismo). Existen tres órdenes para actuar sobre el registro de máscara; la primera es a través del comandoClear Mask Register, que borra todos los bits de máscara; la segunda es por medio del comando Write SingleMask Bit, modificando un solo bit; la tercera forma consiste en los comandos Read y Write All Mask Bits,con los que se pueden consultar y alterar todos los bits de máscara a la vez.

Status Register (Registro de estado).

Contiene información de estado lista para ser leída por la CPU. Los bits 0 al 3 indican si losrespectivos canales han alcanzado un TC (Terminal Count) o se les ha aplicado una señal -EOP externa. Estosbits se borran ante un Reset, un comando Master Clear o, simplemente, al leer el propio registro de estado.Los bits 4 al 7 indican qué canales están solicitando servicio, con independencia de que estén enmascaradoso no. De esta manera, enmascarando todos los canales y leyendo el registro de estado, por software se puededecidir qué canales conviene desenmascarar, pudiendo el sistema operativo aplicar la gestión de prioridadesque desee llegado el caso. Estos bits (4 al 7) son actualizados cuando el reloj está en alto; un Reset o uncomando Master Clear los borran.

7 6 5 4 3 2 1 0

canal 3 canal 2 canal 1 canal 0 canal 3 canal 2 canal 1 canal 0

a 1 si hay una petición de DMA a 1 si se ha alcanzado el TC

Temporary Register (Registro temporal).

Se emplea para contener los bytes que se transfieren en las operaciones memoria-memoria. Trascompletar el proceso de transferencia, la CPU puede averiguar la última palabra transferida leyendo esteregistro, a no ser que el registro haya sido borrado por un Reset o un comando Master Clear.

Comandos del 8237.

A continuación se citan algunos comandos especiales que pueden ser ejecutados leyendo o escribiendosobre el 8237. A diferencia de cuando hay que acceder a los registros de direcciones y contadores, aquí elbit A3 está activo. Por tanto, de los 16 puertos de E/S que ocupa el 8237 en cualquier sistema, los 8 últimosestán relacionados con los comandos y los registros especiales. En el siguiente cuadro se recogen todos, ydespués se explican los más confusos.

Comando Modo de Direcciónu operación acceso A3 A2 A1 A0

Read Status Register (leer registro de estado) Leer 1 0 0 0Write Command Register (escribir registro de comandos) Escribir 1 0 0 0Read Request Register (leer registro de petición de DMA) Leer 1 0 0 1Write Request Register (escribir registro de petición de DMA) Escribir 1 0 0 1Read Command Register (leer registro de comandos) Leer 1 0 1 0Write Single Mask Bit (escribir un solo bit de máscara de DMA) Escribir 1 0 1 0Read Mode Register (leer registro de modo) Leer 1 0 1 1Write Mode Register (escribir registro de modo) Escribir 1 0 1 1Set Byte Pointer F/F (activar flip-flop primero/último) Leer 1 1 0 0Clear Byte Pointer F/F (borrar flip-flop primero/último) Escribir 1 1 0 0Read Temporary Register (leer registro temporal) Leer 1 1 0 1Master Clear (inicialización principal) Escribir 1 1 0 1Clear Mode Register Counter (limpiar contador de registro de modo) Leer 1 1 1 0Clear Mask Register (borrar registro de máscara de DMA) Escribir 1 1 1 0Read All Mask Bits (leer todos los bits de máscara de DMA) Leer 1 1 1 1Write All Mask Bits (escribir todos los bits de máscara de DMA) Escribir 1 1 1 1

Direcciones E/S de los comandos

Page 278: PCA, PS2 ,IBM y AT

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

Clear first/last flip-flop (borrar flip-flop primero/último).

Dado que los valores de 16 bits se envían de dos veces, existe un flip-flop interno que permite al8237 conocer si lo que le llega es la primera mitad del dato o la segunda. Por precaución, se puede borrarprimero para asegurar que el primer byte enviado se interprete como el menos significativo y, el segundo,como el más significativo.

Set first/last flip-flop (activar flip-flop primero/último).

Dado que los valores de 16 bits se envían de dos veces, existe un flip-flop interno que permite al8237 conocer si lo que le llega es la primera mitad del dato o la segunda. Por precaución, se puede activarprimero para asegurar que el primer byte enviado se interprete como el más significativo y, el segundo, comoel menos significativo.

Master Clear (inicialización principal).

Este comando tiene el mismo efecto que un Reset hardware. Los registros de comando, estado,petición de DMA, temporales y los flip-flops internos (first/last y mode register counter) son puestos a cero,siendo el registro de máscaras rellenado con bits a 1 (inhibir canales). El 8237 entra en estado inactivo.

Read/Write Request Register (leer/escribir registro de petición de DMA).

El comando Write es empleado para escribir al registro de petición de DMA y provocar una peticiónde DMA por software; también se puede utilizar Read para consultar su estado: los bits 0 al 3 muestranentonces el estado de las peticiones en los canales 0 al 3 (los demás bits están a 1). El formato para escribires el siguiente:

7 6 5 4 3 2 1 0

00 seleccionar canal 0No importa su valor al escribir 01 seleccionar canal 1

Bits 4..7 a 1 al leer 10 seleccionar canal 211 seleccionar canal 3

0 borrar bit de petición1 activar bit de petición

Clear Mask Register (borrar registro de máscara de DMA).

Este comando limpia los bits de máscara de los 4 canales, habilitándoles para recibir peticiones deDMA por hardware.

Write Single Mask bit (escribir un sólo bit de máscara de DMA).Con este comando se puede seleccionar el bit de máscara que se desea modificar (activándolo o

borrándolo).

7 6 5 4 3 2 1 0

00 seleccionar canal 0No importa su valor al escribir 01 seleccionar canal 1

10 seleccionar canal 211 seleccionar canal 3

0 borrar bit de máscara1 activar bit de máscara

Page 279: PCA, PS2 ,IBM y AT

279EL HARDWARE DE APOYO AL MICROPROCESADOR

Read/Write All Mask bits (leer/escribir todos los bits de máscara de DMA).Este comando permite consultar o establecer el estado de todos los bits de máscara de DMA a la vez,

en los 4 canales.

7 6 5 4 3 2 1 0

canal 3 canal 2 canal 1 canal 0No importa al escribirTodos a 1 al leer

0 limpiar su bit de máscara1 activar su bit de máscara

Clear Mode Register Counter (limpiar contador de registro de modo).Cuando se escribe el registro de modo, se envía un byte al 8237 que selecciona (en los bits 0 y 1)

el canal cuyo registro de modo se desea escribir, y el resto de los bits cargan el registro de modo. Sinembargo, al leer, ¿cómo seleccionar el canal cuyo registro de modo se desea leer?. La solución consiste enhacer n lecturas consecutivas, en las que el 8237 devuelve unos seguidos de otros los 4 registros de modo,gracias a un contador interno de 2 bits que le dice qué tiene que devolver a continuación. Con este comandose borra dicho contador, de manera que a la siguiente lectura el 8237 devuelva el registro de modo del canal0 (habrá que seguir leyendo más hasta obtener el registro de modo del canal deseado). Cuando se lee elregistro de modo de cualquier canal, los bits 0 y 1 del byte devuelto aparecen siempre activos.

12.5.3 - EL 8237 EN EL ORDENADOR.

Todos los ordenadores compatibles vienen equipados con un 8237 accesible a partir de la direcciónE/S base 0. Es por tanto el chip del ordenador donde resulta más fácil traducir las direcciones E/S de lastablas técnicas del fabricante a la dirección del espacio de E/S del PC.

Los AT y PS/2 poseen un 8237 adicional, accesible a partir de la dirección E/S 0C0h. Los puertosestán direccionados en intervalos de 2, al repetirse en dos direcciones adyacentes (esto permite en los IBMy otros muchos hacer un OUT de 16 bits en lugar de dos consecutivos de 8, pero no todas las máquinas losoportan). En los AT, este 2º controlador de DMA actúa como maestro y está encargado de las operacionesde 16 bits; su canal 0 es empleado para colgar de él otro 8259 que realiza las operaciones de 8 bits, porcompatibilidad con el PC. Por ello, los AT poseen 7 canales de DMA, frente a los 4 de los PC/XT.

La siguiente tabla resume todos los puertos de entrada y salida a emplear para acceder a amboscontroladores de DMA (el de 16 bits, recuérdese, sólo disponible en AT):

Comando o registro Modo de acceso 8 bits 16 bits

Registro dirección canal 0 lectura y escritura 00 C0Registro de cuenta canal 0 lectura y escritura 01 C2Registro dirección canal 1 lectura y escritura 02 C4Registro de cuenta canal 1 lectura y escritura 03 C6Registro dirección canal 2 lectura y escritura 04 C8Registro de cuenta canal 2 lectura y escritura 05 CARegistro dirección canal 3 lectura y escritura 06 CCRegistro de cuenta canal 3 lectura y escritura 07 CEStatus Register lectura 08 D0Command Register escritura 08 D0Request Register lectura y escritura 09 D2Command Register lectura 0A D4Single Mask Bit escritura 0A D4Mode Register lectura y escritura 0B D6Set Byte Pointer F/F lectura 0C D8Clear Byte Pointer F/F escritura 0C D8Temporary Register lectura 0D DAMaster Clear escritura 0D DAClear Mode Register Counter lectura 0E DCClear Mask Register escritura 0E DCRead/Write All Mask bits lectura y escritura 0F DE

Direcciones E/S de los controladores de DMA

Page 280: PCA, PS2 ,IBM y AT

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

Los PC/XT utilizan el canal 0 de su 8237 para el refresco de la memoria, el 2 para los disquetes yel 3 para el disco duro. El único canal que queda libre es el 1.

Sin embargo, en los AT el panorama cambia bastante. El 8237 encargado de las transferencias de 8bits (esclavo) que cuelga del que controla las transferencias de 16 bits (maestro) define los canales 0 al 3,de los cuáles sólo el canal 2 está ocupado en las operaciones de disquetes, al igual que los PC/XT. El 8237encargado de las operaciones de 16 bits define los canales 5, 6 y 7 (el 4 está ocupado en colgar de él el otro8237), estando todos ellos libres. La razón es que en los AT la memoria no se refresca por el DMA y el discoduro por lo general se accede directamente, también sin DMA. Por tanto, en estas máquinas quedan nadamenos que 6 canales de DMA libres (el 0, 1 y 3 del DMA de 8 bits y el 5, 6 y 7 del DMA de 16 bits).

Seguramente, el lector se habrá dado cuenta de que los registros de direcciones del DMA son de 16bits, mientras que la serie 80x86 puede direccionar entre 1 Mb y 4 Gbde memoria. Si tiene algo de sentido común, se le habrá ocurrido lapregunta: ¿Cómo es posible entonces que el DMA acceda a lamemoria del ordenador, con direcciones de 20 a 32 bits?. La solucióntécnica adoptada por los diseñadores del PC consistió en añadir unosregistros externos, ubicados fuera del 8237, que se encargan desuministrar los bits de direcciones que faltan: son los denominadosregistros de página de DMA, habiendo uno por cada canal.

Canal Puerto E/S delDMA registro de página

0 87h (sólo AT)1 83h2 81h3 82h5 8Bh (sólo AT)6 89h (sólo AT)7 8Ah (sólo AT)

En los PC/XT, los registros de página de DMA poseen sólo 4 bits significativos y generan la partealta de la dirección de memoria. En los AT, son significativos los 8 bits completos del registro de página deDMA en el 8237 que controla las operaciones de 8 bits y 7 en el que gestiona las operaciones de 16 bits. Elsiguiente esquema muestra cómo se generan las direcciones de memoria:

Registro de página Registro de direcciones del 8237

PC/XT A19 A18 A17 A16 A15 A14 A13 A12 A11 A16 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0

D3 D2 D1 D0

Registro de página Registro de direcciones del 8237

AT (DMA 8) A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A12 A11 A16 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0

D7 D6 D5 D4 D3 D2 D1 D0

Registro de página Registro de direcciones del 8237

AT (DMA 16) A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A12 A11 A16 A9 A8 A7 A6 A5 A4 A3 A2 A1 0

D7 D6 D5 D4 D3 D2 D1siempre a cero

Los restantes bits del espacio de direcciones (líneas A24 a A31 del 386) no se pueden emplear, deahí que algunas implementaciones de Unix tuvieran problemas para soportar más de 16 Mb de memoria.

En general, desde el punto de vista del DMA, se puede imaginar la memoria como 16 bloques de64 Kb (caso del PC/XT), como 256 bloques de 64 Kb (en accesos de 8 bits en el AT) o bien como 128bloques de 128 Kb (en accesos de 16 bits también en el AT). En el DMA que trabaja con 16 bits, setransfieren sólo palabras (65536 palabras = 128 Kb) y siempre en direcciones pares, de ahí que A0=0.

Nota: Con los controladores de memoria expandida actuales (EMM386), los diseñadoreshan sido suficientemente cautos como para colocar los primeros 640 Kb de la memoriavirtual justo en los primeros 640 Kb de memoria física del ordenador. La memoria depantalla y la de la tarjeta VGA también están en su sitio. Por tanto, bajo las últimas versionesdel DOS es factible (y probablemente lo seguirá siendo) programar directamente el DMApara realizar transferencias sobre la memoria normal. Sin embargo, sobre la memoria superior

Page 281: PCA, PS2 ,IBM y AT

281EL HARDWARE DE APOYO AL MICROPROCESADOR

tampoco hay problemas. Aunque la dirección virtual ya no coincide con la física, cuando seejecuta una instrucción OUT sobre un registro de página, el controlador de memoria detectala circunstancia, ya que al parecer está protegido el acceso a esos puertos. A continuación,averigua qué instrucción ha provocado la excepción y modifica convenientemente el valorcon el que se pretendía hacer OUT para adecuarlo a la dirección de memoria física y permitirque siga funcionando. Esto explica por qué una instrucción de E/S sobre uno de estos puertospuede tardar nada menos que ¡1000 ciclos! en un 386.

La BIOS del AT inicializa los 8237 con un valor 0 en el Command Register. Casi todos los canalesson establecidos por defecto (y así permanecen cuando no se usan) en el modo single, transferencia deverificación, autoinicialización inhibida y modo incremento. Por ello, en el 8237 esclavo se escribe el valor40h en el registro de modo del canal 0, el 41h en el canal 1, el 42h en el canal 2 y el 43h en el canal 3. Enel 8237 maestro, el registro de modo del canal 4 (canal 0 de este chip) se programa con 0C0h, que equivaleal modo cascada; los demás canales se programan como en el otro 8237. El siguiente listado ha sido extraídodirectamente de la BIOS del AT:

SUB AL,AL ; DACK sensible en bajo, DREQ sensible en altoOUT 8,0 ; Escritura posterior, prioridad fija, sin compresión,

; controlador habilitado, sin fijar dirección en canal 0,; memoria-memoria deshabilitado

OUT 0D0h,AL ; lo mismo con el segundo controladorMOV AL,40h ; establecer modo para el canal 0OUT 0Bh,ALMOV AL,0C0h ; modo cascada en el canal 4OUT 0D6h,ALJMP SHORT $+2MOV AL,41h ; establecer modo para el canal 1OUT 0Bh,ALOUT 0D6h,AL ; y para el 5JMP SHORT $+2MOV AL,42h ; establecer modo para el canal 2OUT 0Bh,ALOUT 0D6h,AL ; y para el 6JMP SHORT $+2MOV AL,43h ; establecer modo para el canal 3OUT 0Bh,ALOUT 0D6h,AL ; y para el 7

La BIOS del PC/XT inicializa el canal 0 del DMA para el refresco de la memoria. El refresco de lasmemorias dinámicas consiste en ir leyéndolas con suficiente rapidez como para que no se borre su contenido;en realidad, dada su organización en filas y columnas, se puede refrescar a la vez un gran número de bytesleyendo uno sólo. Para una memoria de 1 Mb, basta con acceder a cualesquiera 1024 posiciones de memoriaconsecutivas, cada menos de 4 milisegundos, para garantizar la fiabilidad del sistema. Para ello, el canal 0del DMA es colocado en modo single, en modo incremento de direcciones, con autoinicialización y en modotransferencia de lectura (enviando el valor 58h al registro de modo). A continuación, dicho canal esdesenmascarado, comenzando el refresco de la memoria. La razón es que la salida del contador 1 deltemporizador 8253 está conectada a la línea de petición del canal 0 del DMA, por lo que periódicamente el8237 sustrae el control de los buses al 8086 para continuar el refresco por la dirección de memoria en quese llegara (el contador 1 del 8253 está programado con una cuenta 18, igual que en los AT: aunque éstosúltimos no refrescan la memoria por DMA utilizan una base de tiempos compatible). El registro de páginadel canal 0 no existe en los PC/XT; sin embargo, debido al diseño de la placa, es el registro de página delcanal 3 el que actúa. En cualquier caso, es indiferente la dirección de memoria base empleada para refrescar.Los restantes canales DMA, así como el Command Register, son programados del mismo modo que suscolegas en el AT.

12.5.4 - RALENTIZAR UN EQUIPO AT CON EL DMA.

La posibilidad de emplear el DMA para realizar transferencias memoria-memoria en los ordenadorescompatibles, a través de los canales 0 y 1, es poco atractiva. En los PC/XT es factible, como demuestranalgunas rutinas de dominio público, aunque ello suponga anular momentáneamente el refresco de la memoriadinámica (la propia transferencia de bytes la refresca); sin embargo es más complicado y el movimiento serealizaría dentro de un único segmento de 64 Kb. En los AT no existen todas estas complicaciones, pero aquí

Page 282: PCA, PS2 ,IBM y AT

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

no es recomendable por dos motivos: por un lado, el registro interno del 8237 encargado de almacenar el bytea transferir es de 8 bits (es decir, nada de emplear un canal de DMA de 16 bits, que sería mucho más rápido)y, por otro lado, el más modesto 286 es bastante más rápido que el DMA (por algo el disco duro del AT selee sin DMA). No digamos un 386 u otra máquina superior.

Cierto célebre libro de soluciones para programadores de compatibles afirma en la página 328 quelos AT emplean el DMA automáticamente en las instrucciones MOVS para mejorar el rendimiento. Fueradel ámbito de la ciencia-ficción, aquí propondremos otro uso no más común pero, en cambio, factible:ralentizar el funcionamiento de los ordenadores AT. La auténtica utilidad del DMA, conviene recordarlo, estáligada al acceso a los disquetes, aunque de ello hay ejemplos en el apartado donde se trata la programacióndel NEC765.

El truco, cuya idea original hay que atribuir a Jesús Arias, consiste en programar un canal en modoautoinicialización, para que se ponga a trabajar continuamente. Programándolo en modo single, le va robandociclos a la CPU de manera continua. En teoría, en el modo block se debería quedar bloqueado el ordenador,aunque las máquinas en donde lo he probado esto no sucede. En los PC/XT no conseguí un resultado exitoso,además de que no tiene mucho sentido hacerlos más lentos. Sin embargo, en los AT es bastante sencillo elproceso y funciona en todas las máquinas en que se probó. A la hora de elegir un canal, se puede optar porel 0, 1, 3, 5, 6 ó 7. Casi todos son válidos, pero el 0 y 1 no son recomendables: son los canales de másprioridad y, si se utilizan para ralentizar el ordenador, las disqueteras dejan de funcionar (utilizan el canal 2).Este es otro de los motivos por los que no es conveniente hacer esto en los PC/XT (su único canal disponiblees el 1). Por tanto, la elección queda relegada al canal 3 (de 8 bits) o al 5, 6 ó 7 (de 16 bits). De esta manera,los disquetes pueden continuar funcionando, ya que su canal de DMA toma el control cuando es necesariodebido a su mayor prioridad.

Resulta interesante observar cómo ralentiza más emplear un canal de 8 bits que uno de 16: en elsistema 386-25 donde lo probé, el famoso test de velocidad de LANDMARK estima la velocidadhabitualmente en 27,8 MHz. Poniendo en marcha el canal 7, de 16 bits, la velocidad cae nada menos que a7,3 MHz; utilizando el 3 (de 8 bits) baja a 6,3 MHz. Combinando ambos canales a la vez, el descenso es aúnmayor, hasta los 4,3 MHz.

Las tradicionales utilidades de dominio público para ralentizar los AT suelen emplear la interrupcióndel temporizador, parando por completo el ordenador durante algunos instantes y dejándole a toda velocidadel resto del tiempo. La ventaja de ralentizar por DMA es que el ordenador baja la velocidad de una manerauniforme y no va a saltitos. Por otro lado, ralentiza también los juegos que controlan por su propia cuentala interrupción del temporizador. Además, casi ningún programa comercial se ocupa de programar los canalesdel DMA, ni el propio BIOS toca los que no le incumben; por ello, una vez activado, es seguro que el efectodurará cuanto desee el usuario. Por último, el método es aún más elegante porque ni siquiera se trata de unprograma residente: ¡consume 0 bytes!.

Combinando el método de ralentización por DMA con un aumento de los ciclos de refresco de lamemoria (a través del canal 1 del 8254) se puede bajar todavía aún más la velocidad, de manera tambiénuniforme. En concreto, en la máquina citada anteriormente, si se programa el canal 1 del 8254 con un valorde cuenta 2 la velocidad cae a 1,4 MHz, según el test de Landmark: los ciclos de refresco de memoriacastigan mucho a la CPU cuando la restan pocos MHz...

El inconveniente de ralentizar demasiado, combinando los dos métodos citados, es que el tecladocomienza a fallar en mayor o menor medida (se enganchan las teclas de Shift y Ctrl, siendo preciso pulsarlasde vez en cuando para desengancharlas; aparecen números en los cursores expandidos...). En el siguienteprogramita de demostración, existen dos niveles de freno seleccionables. Utiliza el peor método paracomprobar si el ordenador es un AT, a través del byte de identificación de la ROM (es 0FCh en un grannúmero de ATs y 0F8h en los PS/2-80), aunque es sin duda una de las maneras más rápidas de hacerlo. Lasfunciones dmako() se encargan de poner K.O. el canal correspondiente, activando el DMA. Las recíprocasdmaok() devuelven el canal asociado a la normalidad, inhibiendo el DMA.

Page 283: PCA, PS2 ,IBM y AT

283EL HARDWARE DE APOYO AL MICROPROCESADOR

#include <dos.h>#include <string.h>

voiddmacnt(), dmako3(), dmako7(), dmaok3(), dmaok7();

void main(int argc, char **argv)unsigned nivel;

printf ("\nDMAKO 1.1 + AT-Ralentizador por DMA (c) 1992 CiriSOFT");

if ((peekb(0xF000,0xFFFE)!=-4) && (peekb(0xF000,0xFFFE)!=-8)) printf("\n Este programa necesita máquina AT o superior\n");exit (1);

if ((argc<2) || ((nivel=atoi(argv[1]))>3)) printf("\n ");printf("Indicar nivel de freno (1, 2 ó 3) ó 0 para acelerar.\n");exit (2);

dmacnt();if (nivel==1) dmaok3(); dmaok7(); dmako7();printf ("\n Ralentización moderada activa.\n");

else if (nivel==2) dmaok3(); dmaok7(); dmako3();printf ("\n Ralentización elevada activa.\n");

else if (nivel==3) dmako3(); dmako7();printf ("\n Ralentización máxima activa.\n");

else dmaok3(); dmaok7();printf ("\n Ralentización desactivada.\n");

void dmacnt()

outportb(0x07, 0xFF); /* cuenta del canal 3 a 0xFFFF */outportb(0x07, 0xFF);outportb(0xCE, 0xFF); /* cuenta del canal 7 a 0xFFFF */outportb(0xCE, 0xFF);

void dmako3 (void)

outportb (0x0B, 0x5B); /* canal 3: autoinic., read */outportb (0x0A, 3); /* desenmascarar */

void dmaok3 (void)

outportb (0x0A, 7); /* enmascarar */outportb (0x0B, 0x43); /* canal 3: modo normal */

void dmako7 (void)

outportb (0xD6, 0x5B); /* canal 7: autoinic., read */outportb (0xD4, 3); /* desenmascarar */

void dmaok7 (void)

outportb (0xD4, 7); /* enmascarar */outportb (0xD6, 0x43); /* canal 7: modo normal */

27,8

Velocidad estimadatras la ejecuciónde DMAKO.C en unAT 386-25. Datoscalculados con eltest de LANDMARK 7,3

6,34,3

DMAKO 0 DMAKO 1 DMAKO 2 DMAKO 3

12.5.5 - ACERCA DE LAS PAGINAS DE DMA.

Al emplear el DMA conviene tener cuidado con evitar un desbordamiento en el offset 0FFFFh dela página de 64K empleada (DMA 8 bits). Esto se verá con más detalle en el apartado dedicado al controladorde disquetes. Hay que tener en cuenta que una dirección segmentada aparentemente inocente puede estarcruzando una frontera de DMA. Por ejemplo, 512 bytes contenidos a partir de 3FF2:0000 (que llegan hasta3FF2:01FF) ocupan las direcciones físicas 3FF20 a la 4011F, estando contenidos en las páginas 3 y 4.

Un intento de acceso DMA al límitede una página no produce error alguno, peroel resultado es la corrupción indeseada dezonas de memoria no previstas, ya que alllegar al final del segmento se vuelve denuevo a pasar por el principio del mismo.

Tratándose del DMA de 16 bits, elproblema estaría en rebasar una frontera de128 Kb. Realmente, estos problemas no sedeben al propio DMA en sí y no suelenpresentarse en los sistemas que emplean elDMA. Lo que sucede es que los IBM PC,AT, etc. utilizan un DMA con direccionesde 16 bits concebido para máquinas con64K de memoria...

Page 284: PCA, PS2 ,IBM y AT

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

12.6 - EL CONTROLADOR DE DISQUETES NEC 765

12.6.1 - LA TECNOLOGÍA DE GRABACIÓN EN DISCO

Simple y Doble densidad: MF y MFM.

La superficie magnética de un disco está dividida en pistas concéntricas, en cualquiera de las cualesel cabezal de lectura/escritura puede ser posicionado con ayuda de un motor paso a paso. Los únicos datosque se almacenan en el disco son bits, como se verá. El cabezal de la unidad de disco es, en esencia, unabobina en la que se verifican dos leyes fundamentales de la física electrónica: por un lado, una corrientealterna en dicha bobina provoca un campo magnético que varía al mismo ritmo que la corriente (lo quepermite magnetizar la superficie del disco para grabar los datos); por otro lado, aplicando un campomagnético variable de manera constante a la bobina se genera una tensión constante en la misma (lo quepermite leer los datos previamente registrados sobre esa superficie magnética, dejando el cabezal deslizarsesobre la misma).

A simple vista, por tanto, se podría intuir que registrar datos en un disco es una tarea sencilla: sepodrían representar los bits (a 1 ó 0) según la presencia/ausencia de magnetización en cada punto de lasuperficie. Sin embargo, la electrónica y mecánicas de precisión necesarias para este tipo de grabación seescapan aún de las posibilidades tecnológicas actuales. La solución adoptada consiste en registrar, junto a losbits de datos, una frecuencia de reloj de referencia que permita localizar los bits sin problemas: entre dosregistros magnéticos de referencia en el disco (marcados con ’*’), puede existir o no otro registro (que es loque implica que el dato sea un 1 ó un 0):

* * * * * * * *

1 1 0 1 0 0 0 1

Esto es lo que se denomina grabación en simple densidad (MF). Al final, la superficie magnéticase puede considerar como un conjunto de pequeños imanes magnetizados en un sentido u otro: cuando serecorra el disco con el cabezal en modo lectura, la variación magnética inducirá una corriente cuyainterpretación permitirá recuperar los datos grabados.

La electrónica de este sistema trabaja con dos tiempos básicos diferentes: el que transcurre entre dosimpulsos del reloj de referencia (bits a 0) y el que separa un impulso del reloj de referencia de los bit a 1.Un impulso de referencia suele durar unos 500 nanosegundos y la distancia entre estos impulsos es de 8microsegundos. Por ello, para un byte de datos son necesarios 64 microsegundos: como la disquetera da 300vueltas por minuto, emplea 200 milisegundos en cada vuelta; esto significa que en cada pista podríaalmacenar teóricamente 200000/64 = 3125 bytes. En un disco convencional de 80 cilindros y dos caras (160pistas), esto supone 500000 bytes; sin embargo, estos discos suelen almacenar 1.000.000 (doble densidad)y hasta 2.000.000 de bytes (alta densidad) antes de ser formateados (típicamente 720 Kb y 1,44 Mb tras elformateo). ¿Cómo se las apañan para doblar o cuadruplicar los discos actuales esta capacidad?. La respuestaconsiste en emplear los formatos de doble y alta densidad, respectivamente.

La técnica de grabación en doble densidad (MFM) consiste en prescindir de los impulsos dereferencia en la medida de lo posible. El método se basa en no emplearlos para registrar bits a 1, o bien bitsa 0 aislados: tan solo se usarán para registrar secuencias de varios bits consecutivos a 0 (de lo contrario, unasecuencia de bits a 0, sin impulsos de referencia, implicaría una pérdida de sincronización). Aquí existenahora tres tiempos diferentes: el intervalo elemental es el lapsus de tiempo entre dos bits a 1; un intervalode doble duración que éste representa la secuencia de bits 1-0-1; por último, un tercer lapso de tiempocorrespondiente a 1,5 intervalos de tiempo elementales es empleado para crear los impulsos de referencia

Page 285: PCA, PS2 ,IBM y AT

285EL HARDWARE DE APOYO AL MICROPROCESADOR

(marcados con ’*’) o abandonar su generación. Aunque en el gráfico no queda quizá muy claro, este métodopermite grabar el doble de datos en un mismo intervalo de tiempo que el método de simple densidad:

* *

1 1 0 1 0 0 0 1

Las unidades de alta densidad y las (ya difuntas) de extra alta densidad se basan en una mayordepuración de la electrónica de control, que permite reducir los tiempos de los diversos intervalos.

El formateo del disco: Ejemplo con el NEC 765.

La división del disco en pistas no es suficiente, ya que la cantidad de datos que almacenan esdemasiado elevada (unos 9 Kb por cada cilindro y cara en los discos de alta densidad actuales). Por tanto,se comprende la necesidad de subdividir cada pista en unidades lógicas menores (sectores) de un tamañorazonable, que puedan ser accedidas por separado. En esto consiste el proceso de formateo, en el que el discoqueda estructurado como se describirá a continuación. Se ha tomado como referencia el proceso de formateoque realiza el FDC (Floppy Disk Controller) 765 de NEC en MFM (en MF varía ligeramente).

El disco posee una perforación de índice (el pequeño agujerito de la superficie) que es comprobadapor un sensor óptico, lo que permite detectar el inicio de la información grabada en cada pista. Nada máscomenzar la pista, hay 80 bytes con el valor 4Eh (ver esquema de la página siguiente): es lo que se denominael GAP 4A (GAP significa algo así como hueco o espacio). La razón de existencia de este pequeño área sedebe a la necesidad de sincronizar las distintas unidades de disco, ya que no todos los sensores ópticos actúande manera totalmente idéntica. Tras el GAP 4Ah se escriben 12 bytes a 0 en un área denominada SYNC. Lamisión de estos bytes a cero es crear un área de marcas de sincronismo para que el controlador de disco sesincronice con el reloj de referencia. Tras el campo SYNC viene un área especial de tres bytes denominadaIndex Address Mark o IAM (marca de dirección índice), que existe sólo al principio de la pista. Tras ellaaparece un byte 0FCh y, detrás, un GAP 1, en esta ocasión de 50 bytes con el valor 4Eh: su misión es dartiempo a que el FDC procese la marca de dirección índice, que será decodificada e interpretada por hardware.Después, a continuación vienen ya los sectores de datos del disco, que tienen todos el mismo formato.

Los sectores comienzan por 12 bytes de SYNC (a 0), a los que sigue la ID Address Mark o ID-AM(marca de dirección de identificación), también de 3 bytes. Detrás, un byte 0FEh. Tras todo esto, apareceel campo de ID: son 4 bytes que contienen la siguiente información: número de cilindro, cara del disco,número de sector y tamaño de sector (en la forma (LOG2 bytes_por_sector)-7). Esto permite identificar a cadasector por separado. Por razones de seguridad, se realiza una comprobación CRC (especie de suma deseguridad) de 16 bits entre la ID-AM y los 4 bytes del campo ID, cuyo resultado se almacena en los dosbytes inmediatamente siguientes, con objeto de detectar futuros fallos en la integridad de la información. Paradar tiempo al FDC a que se prepare para leer los datos que se vienen encima, hay después un nuevo GAP2 de 22 bytes con el valor 4Eh. Entre otras razones, este área le sirve al FDC, en las operaciones de escritura,para abandonar la lectura y prepararse para la inminente escritura (tarea que siempre lleva algo de tiempo).Detrás vienen otros 12 bytes SYNC. Tras él otros 3 bytes: constituyen la DATA Address Mark o DATA-AM(similar a la ID-AM o a la IAM) y, finalmente, un byte 0FBh. ¡Ahora sí!, tras ello vienen los datos delsector: puede tener una longitud de 128, 256, 512, 1024, 2048 ó 4096 bytes (según haya sido definido) quenada más ser formateado es inicializado con un valor seleccionable por el usuario. Por supuesto, a este áreade datos se le aplica también un algoritmo CRC (junto con los bytes de la DATA AM y el byte 0FBh) y los2 bytes que se obtienen se graban a continuación. Finalmente, aparece el GAP 3, formado por cierto númerode bytes 4Eh seleccionable por el usuario al formatear (típicamente entre 54 y 116). Este último GAP tieneuna función muy importante: al escribir un sector en el disco, es difícil que la velocidad de la unidad seatotalmente idéntica a la de la unidad que formateó el disco: si es menor, no sucede nada (el sector ocuparía

Page 286: PCA, PS2 ,IBM y AT

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

un pelo menos de disco) pero si es mayor, el GAP 3 evita que se invada el siguiente sector. Cuando seescriben datos, el GAP 3 es mucho menor que cuando se formatea (del orden de la mitad de tamaño), paraasegurar que no se invadirá la zona del siguiente sector si la unidad es algo más rápida de lo previsto. Lossectores se suceden unos tras otros hasta completar la pista. Después, el resto del espacio hasta que aparezcade nuevo la perforación de índice se rellena con el GAP 4B final. Todo esto, en MFM (en MF, por ejemplo,los bytes añadidos entre sectores por el 765 -excluyendo el GAP 3- no son 62 en total sino 31).

GAP 4A SYNC IAM I-FC GAP 1

Principio de pista 80 bytes 4E 12 bytes 00 3 bytes byte FC 50 bytes 4E ...

SYNC ID-AM I-FE ID CRC GAP 2 SYNC

... 12 bytes 00 3 bytes byte FE 4 bytes 2 bytes 22 bytes 4E 12 bytes 00 ...

Cilindro, cara, nº sector, tamañoSECTOR

DATA-AM I-FB DATOS DEL SECTOR CRC GAP 3

... 3 bytes byte FB 128, 256, 512,..., 4096 bytes 2 bytes 54-116 bytes 4E ...

GAP 4B

... otro sector ... 100-400 bytes 4E Fin de pista

12.6.2 - DESCRIPCIÓN DEL FDC (Floppy Disk Controller) 765.

Este controlador de disquetes es un chip muy evolucionado que realiza tareas de un nivelrelativamente alto. Fabricado inicialmente por NEC, también lo comercializan Rockwell (R 6765) e Intel(i8272). Sus principales características son: tamaño de sector programable (128, 256, 512, 1024, 2048 ó 4096bytes), posibilidad de programar todos los datos de las unidades, capacidad para controlar 4 disqueteras,transferencia con o sin DMA, generación de interrupciones; es compatible con múltiples microprocesadores(Z80, 8086,...) y trabaja con un reloj sencillo de una sola fase (4 u 8 Mhz). Soporta densidades MF (simpledensidad) y MFM (doble densidad) en unidades estándar de 3, 3½, 5¼ y 8 pulgadas.

RESET 1 40 Vcc

-RD 2 39 -RW/SEEK

-WR 3 38 LCT/DIR

-CS 4 37 FR/STP

A0 5 36 HDL

DB0 6 35 RDY

DB1 7 34 WP/TS

DB2 8 33 FLT/TRK0

DB3 9 32 PS0

DB4 10 31 PS1

DB5 11 30 WR DATA

DB6 12 29 DS0 ó US0

DB7 13 28 DS1 ó US1

DRQ 14 27 HDSEL

-DACK 15 26 MFM

TC 16 25 WE

IDX 17 24 VCO

INT 18 23 RD DATA

CLK 19 22 DWIN

GND 20 21 WR CLK’765

SEÑALES DEL 765

Interface con la CPU.RESET: Reset. Línea de reinicialización al estado por defecto.-CS: Chip Selection. Línea de selección del integrado.-RD: Read. Patilla por la que la CPU lee datos del FDC.-WR: Write. Patilla por la que la CPU escribe datos en el FDC.A0: Address. Esta línea de dirección define dos direcciones de E/S para

comunicar con la CPU. Suele ir conectada al A0 de la CPU.DB0..7: Data Bus. 8 líneas de datos bidireccionales.INT: Interrupt. Salida de petición de interrupción a la CPU del FDC, por

cada byte transferido.

Señales para el modo DMA.DRQ: DMA Request. Solicitud de DMA al controlador de DMA.-DACK: DMA Acknowledge. Señal de reconocimiento de solicitud concedida.TC: Terminal Count. Línea que indica el final de la cuenta de transferencia

en modo DMA; cuando no se emplea el DMA sirve también paraacabar la transferencia en sistemas controlados por interrupciones.

Señales para el interface con la disquetera.DS0-1: Drive Select 0-1. También conocidas como US0-1 (Unit Select).

Selecciona una de las cuatro disqueteras conectadas.HDSEL: Head Select. Selecciona el cabezal en unidades de doble cara.HDL: Head Load. Empleado para provocar el contacto físico del cabezal

sobre el disquete o levantarlo.IDX: Index. Entrada del sensor óptico que detecta el inicio de la pista gracias a la perforación de índice del disquete.RDY: Ready. Señal enviada por la disquetera indicando que el disco gira a velocidad adecuada (el FDC espera a que se cumpla RDY).WE: Write Enable. Salida que habilita la escritura de datos en el disquete.

Page 287: PCA, PS2 ,IBM y AT

287EL HARDWARE DE APOYO AL MICROPROCESADOR

-RW/SEEK: Read Write/Seek. Algunas de las líneas que comunican el FDC con la disquetera tienen doble función (para ahorrarpatillas en el chip): esta señal permite elegir la función de las 4 siguientes patillas.

FR/STP: Fit Reset/Step. La función FR permite borrar el error de flip-flop de algunas unidades. La función STP, mucho másutilizada, mueve un paso (un cilindro) la cabeza de lectura/escritura (en la dirección que indica LCT/DIR).

FLT/TRK0: Fault/Track0. La señal FLT es generada por algunas disqueteras en caso de error, pudiendo borrarsea través de la patilla anterior (FR/STP). La salida TRK0 indica cuándo el cabezal alcanza el cilindro0, gracias a un sensor óptico o mecánico, tras el comando de programación Seek o el derecalibración.

LCT/DIR: Low Current/Direction. La señal LCT es necesaria para limitar la corriente de escritura al acceder a los cilindros másinternos, por razones físicas. DIR indica en modo Seek el sentido del movimiento del cabezal.

WP/TS: Write protect/Two Side. La señal WP indica si el disco está protegido contra escritura y es comprobada en lasoperaciones de lectura/escritura; la señal TS se comprueba en las operaciones Seek y sólo es necesaria en unidades dedos cabezales.

WR DATA: Write Data. Línea de entrada en serie de los datos de escritura (para escribir sector, para formatear,...).PS0-1: Pre Shift 0-1 (Precompensation). En el formato MFM, el FDC indica a la circuitería electrónica adecuada cómo debe

ser escrito el flujo de datos: para la precompensación caben tres estados posibles (Early, Normal y late).RD DATA: Read Data. Entrada al FDC de datos en serie (bits) procedentes de la disquetera y leídos del disquete.DW: Data Window. Señal obtenida en un separador de datos a partir de los datos leídos.VCO: VCO Syn. Esta señal es precisa en el separador de datos PLL para el control del VCO.MFM: MFM Mode. Indica al FDC si se trabaja en simple o doble densidad.

Alimentación y señales de reloj.Vcc: Entrada de +5v, el chip no suele consumir más de 150 mA.GND: Masa.CLK: Entrada de reloj: 4 u 8 MHz habitualmente.WR CLK: Entrada de reloj para controlar la transferencia: determina la velocidad de transferencia de datos con la disquetera.

PROGRAMACIÓN DEL ’765

La única línea de direcciones del integrado (A0) define dos únicos puertos de E/S: el primero es elregistro principal de estado que sólo puede ser leído. A través del segundo puerto, de lectura/escritura, seaccede al registro de datos, a través del cual se programa el FDC, se envían y reciben los datos y seobtienen los resultados.

Con el FDC se trabaja en tres fases diferenciadas: la fase de comando u orden es empleada paraenviar al FDC información sobre lo que tiene que hacer, lo que puede implicar enviar hasta 9 bytes enalgunos comandos. A continuación viene la fase de ejecución. Finalmente, la fase de resultados puedeobligar a leer del FDC hasta siete informaciones de estado diferentes (hasta que no se leen, el FDC no admitemás órdenes). Este es el esquema general, si bien algunas órdenes carecen de fase de resultados, otras notienen fase de ejecución...

El FDC dispone de 5 registros de estado internos. El principal puede ser accedido directamente comose vio (A0=0) en cualquier momento. Los otros 4 registros (ST0, ST1, ST2 y ST3) sólo son accesibles enalgunas órdenes y durante la fase de resultados.

1) COMANDO LEER DATOS.

Para que el FDC lea los datos del disco hay que enviarle 9 bytes de información en la fase deórdenes. Este activa la señal Head Load y espera el tiempo de Head Load programado. El FDC comienza aleer los ID’s (identificadores) de los sectores hasta encontrar el sector buscado, con lo que pasa a la fase deejecución, o hasta encontrar por segunda vez la perforación de índice del disco (en ese caso se pasa a la fasede resultados para dar el error). En la fase de ejecución, los datos son leídos del disco y enviados alprocesador o al DMA, a razón de un byte cada 8, 16, 26.67 ó 32 microsegundos (según la densidadempleada: a 1000, 500, 300 y 250 Kbit/seg respectivamente). Tras acabar la transferencia del último byte delúltimo sector hay que dar un impulso en la patilla TC (Terminal Count) del 765 para evitar que siga leyendolos sectores que van detrás en el proceso denominado multi-sector-read (se leen más sectores hasta llegar alfinal de la pista). En este comando, al igual que en alguno más, se puede igualar el último sector de la pistaal primero a ser accedido, pudiéndose prescindir en ese caso de la señal TC al acceder a un solo sector. Detodas maneras, al emplear el DMA, la transferencia finalizará realmente cuando el registro contador del DMA

Page 288: PCA, PS2 ,IBM y AT

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

alcanza el valor 0, al encargarse el propio controlador de DMA de activar la señal TC, pudiéndose leer portanto el número de sectores deseado. Personalmente he comprobado que el último número de sector en lapista es más bien el último sector al que se desea acceder. Este comando produce 7 bytes en la fase deresultados, que deben ser leídos obligatoriamente para que el FDC pueda admitir más órdenes.

FORMATO DEL COMANDO LEER DATOS:

Byte 0 MT MF SK 0 0 1 1 0

Skip-bit: a 1 si saltar sectores borrados

a 0 si MF, a 1 si MFM

Multitrack bit: a 1 si la función multi-sector debecontinuar en la segunda cara (unidades de 2 cabezales)

Byte 1 X X X X X HD US1 US0

Cabezal (0 ó 1) Unidad (0-3)

Byte 2 Número de cilindroByte 3 Número de cabezaByte 4 Número de sectorByte 5 Tamaño de sector: (LOG2 nºbytes)-7Byte 6 Ultimo número de sector en la pistaByte 7 Tamaño del GAP 3Byte 8 Longitud de datos (si tamaño de sector = 0)

RESULTADO (OBLIGATORIO LEERLO):

Byte 0 Registro de estado 0Byte 1 Registro de estado 1Byte 2 Registro de estado 2Byte 3 Número de cilindroByte 4 Número de cabezaByte 5 Número de sectorByte 6 Tamaño de sector

2) COMANDO ESCRIBIR DATOS.

Este comando es totalmente análogo al de lectura, pero actuando en escritura sobre el disco. Lasecuencia de bytes a enviar y recibir es idéntica: sólo cambian algunos bits del primer byte de comando.

Byte 0 MT MF 0 0 0 1 0 1

Bytes 1 al 8 y fase de resultados: Igual que el comando LEER DATOS.

3) COMANDO LEER DATOS BORRADOS.

Por sector borrado se entiende aquel cuyo DATA-AM está borrado (por haber sido grabado dichosector con el comando Escribir Datos Borrados): estos sectores son ignorados en las operaciones normalesde lectura y escritura, aunque esta orden también permite leerlos. Por supuesto, esto no tiene relación algunacon la recuperación de ficheros borrados en la unidad y la utilidad de este comando es bastante cuestionable.

Byte 0 MT MF SK 0 1 1 0 0

Bytes 1 al 8 y fase de resultados: Igual que el comando LEER DATOS.

4) COMANDO ESCRIBIR DATOS BORRADOS.

Este comando graba sectores con el DATA-AM borrado, con objeto de que sólo puedan ser leídoscon el comando Leer Datos Borrados. La secuencia de bytes a enviar/recibir es idéntica al comando Leer

Page 289: PCA, PS2 ,IBM y AT

289EL HARDWARE DE APOYO AL MICROPROCESADOR

Datos: sólo cambian algunos bits del primer byte.

Byte 0 MT MF 0 0 1 0 0 1

Bytes 1 al 8 y fase de resultados: Igual que el comando LEER DATOS.

5) COMANDO LEER PISTA.

Este comando es similar a Leer Datos, se diferencia en que se leen todos los sectores de la pista (siel último número de sector se indica correctamente) empezando cuando se detecta el paso de la perforaciónde índice (si el sector inicial indicado no es realmente el primer sector de la pista, se producirá error). Aúnen caso de error de CRC en el campo de ID o en el de datos, se continúa leyendo la pista.

Byte 0 0 MF SK 0 0 0 1 0

Bytes 1 al 8 y fase de resultados: Igual que el comando LEER DATOS.

6) COMANDO FORMATEAR PISTA.

Este comando de 6 bytes realiza de manera automática y sin dar trabajo al programador todas lastareas necesarias para inicializar una pista del disquete. Tras enviar el comando, habrá que pasar al FDC 4bytes por cada sector que haya en la pista a formatear: en ellos, para cada sector se indica el número desector deseado, lo que permite numerar los sectores de manera no consecutiva. El factor de Interleave 1:Nde un disco equivale al número N de vueltas que hay que dar para acceder una vez a toda la pista (dependede que los sectores estén numerados consecutivamente o no); elegir un interleave óptimo es decisivo paramejorar el rendimiento (si la unidad gira lo bastante rápida como para que no de tiempo a acceder a dossectores físicamente consecutivos, el interleave debería ser mayor de 1:1; de lo contrario sería necesaria unavuelta completa del disco cada vez que se accede a dos sectores de número consecutivo, que resulta serademás lo más frecuente). El formateo comienza cuando el sensor correspondiente detecta el inicio de la pista(por la perforación de índice), por ello todas las pistas quedan con los sectores colocados exactamente en lamisma posición física: así, el sector N en una cara del disco coincide en su posición con el de la otra y conel del cilindro adyacente (si se numeran todas las pistas igual, claro).

Byte 0 0 MF 0 0 1 1 0 1

Byte 1 Idéntico al byte 1 del comando LEER DATOS

Byte 2 Tamaño de sector: (LOG2 nºbytes)-7Byte 3 Sectores por pistaByte 4 Tamaño del GAP 3Byte 5 Byte de relleno al formatear

RESULTADO (OBLIGATORIO LEERLO): El mismo que en el comando LEER DATOS.

Una vez enviado el comando, para cada sector de la pista habrá que pasar al FDC:

1º Byte Número de cilindro2º Byte Número de cabeza3º Byte Número de sector4º Byte Tamaño de sector: (LOG2 nºbytes)-7

7) COMANDO LEER ID.

Este comando permite leer del disquete el siguiente ID que aparezca. El ID asociado a cada sectorson los 4 bytes asignados durante el formateo, y consiste en información relativa al número de cilindro,número de cabeza, número de sector y tamaño del mismo. Estos números suelen coincidir con los valoresfísicos reales relacionados con la posición que ocupa el sector en el disco, si bien se pueden falsear en

Page 290: PCA, PS2 ,IBM y AT

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

técnicas de protección de datos, aunque los copiones más ordinarios esquivan sin problemas estas trampastan simples. Este comando consta de sólo 2 bytes; en la fase de resultado devuelve la misma información queel comando Leer Datos (precisamente, la información solicitada).

Byte 0 0 MF 0 0 1 0 1 0

Byte 1 y fase de resultados: igual que el comando LEER DATOS

8), 9) y 10) COMANDOS PARA VERIFICAR (SCAN).

El comando verificar (SCAN) permite al FDC comparar los datos almacenados en el disquete conun byte enviado por el procesador. Hay 3 comandos Scan de verificación, que indican el modo decomparación por cada byte cotejado: igual, menor o igual, mayor o igual. El comando finaliza cuando secumple el criterio de comparación elegido en todo el sector dado, cuando se comprueba el último sector dela pista o bien cuando se activa la patilla TC. La secuencia de bytes a enviar (9 en total) y a recibir es casiidéntica al comando Leer Datos:

Byte 0 MT MF SK 1 0 1

Modo:00 - IGUAL 10 - MENOR O IGUAL 11 - MAYOR O IGUAL

Bytes 1 al 8 y fase de resultados: Igual que el comando LEER DATOS.

Nota: Tras este comando, hay que enviar al FDC el byte que usará para la comparación.

11) COMANDO DE RECALIBRADO.

Este comando mueve el cabezal al cilindro 0 del disco. El FDC comienza a generar impulsos (pormedio de la línea ST) para mover el motor paso a paso hasta que se le informe que ya se ha alcanzado elcilindro 0 (a través de la patilla TRK0 del 765); en cualquier caso, el comando finaliza tras enviar un máximode 77 impulsos a la unidad (de ahí que pueda ser preciso repetirlo en las actuales unidades de 80 cilindros,que siguen comportándose así por compatibilidad). Este comando carece de fase de resultados (puedeevaluarse el resultado por medio del registro de estado) y consta de sólo 2 bytes.

Byte 0 0 0 0 0 0 1 1 1

Byte 1 Idéntico al byte 1 del comando LEER DATOS

12) COMANDO DE POSICIONAMIENTO DEL CABEZAL (SEEK).

El 765 posee 4 registros internos que memorizan la posición del cabezal (sobre qué cilindro se halla)en las 4 unidades de disco soportadas; tras el comando de recalibrado son puestos a 0. Cuando se envía estecomando al FDC, para colocar el cabezal sobre un cierto cilindro, éste comprueba si ya se encuentra sobreel mismo: en caso contrario, genera las señales de control necesarias para instruir a la disquetera. Estecomando no posee fase de resultados: para comprobar el éxito de la operación hay que emplear la orden LeerEstado de Interrupciones obligatoriamente (de lo contrario, el FDC no aceptará más órdenes de lectura oescritura). En cualquier caso, si la siguiente operación es de escritura, tras este comando hay que hacer unabreve pausa (15 ms vale) porque si el cabezal no ha dejado de vibrar acarrearía una escritura incorrecta (sedetectaría gracias al CRC en una lectura posterior, pero ¡casi nadie verifica tras escribir!: mejor asegurar queno hay error). Si la siguiente operación es de lectura, no es necesaria dicha pausa ya que en caso de fallar,sería reintentada y no tendría mayor consecuencia. Si se trata de seleccionar el otro cabezal en el mismocilindro, después de haber posicionado el otro, tampoco es necesaria pausa alguna. Abusar de las pausaspodría acarrear una ralentización del acceso, al no hallarse en ocasiones el sector buscado hasta la siguientevuelta del disco. 3 bytes:

Byte 0 0 0 0 0 1 1 1 1

Page 291: PCA, PS2 ,IBM y AT

291EL HARDWARE DE APOYO AL MICROPROCESADOR

Byte 1 Idéntico al byte 1 del comando LEER DATOS

Byte 2 Número de cilindro

13) COMANDO LEER ESTADO DE INTERRUPCIONES (REGISTRO DE ESTADO 0).

El 765 genera interrupciones al final de un comando Seek/Recalibrado o debido a un cambio en laseñal RDY (Ready) de alguna unidad; en modo NO-DMA las genera además al inicio de la fase de resultadosy durante la fase de ejecución. Las dos últimas causas pueden ser reconocidas con facilidad por elmicroprocesador, pero con las primeras es preciso emplear este comando para conocer la causa con exactitud,gracias a los bits del registro ST0. Esta orden se compone de un solo byte, devolviendo otros 2 en la fasede resultado:

Byte 0 0 0 0 0 1 0 0 0

RESULTADO (OBLIGATORIO LEERLO):

Byte 0 Registro de estado 0Byte 1 Nº cilindro en que quedó el cabezal (SEEK)

14) COMANDO LEER ESTADO DE UNIDAD (REGISTRO DE ESTADO 3).

Esta orden permite obtener el contenido del registro de estado ST3 de la unidad deseada, siendo ésteel único medio de conseguirlo. Consta de sólo dos bytes, obteniéndose un solo byte de resultado:

Byte 0 0 0 0 0 0 1 0 0

Byte 1 Idéntico al byte 1 del comando LEER DATOS

RESULTADO (OBLIGATORIO LEERLO):

Byte 0 Registro de estado 3

15) COMANDO SPECIFY (ESTABLECER DATOS DE LA UNIDAD).

Aunque descrito en último lugar, este comando debería ser el primero ejecutado antes de comenzarlas operaciones de disco. Sirve para indicar si se va a trabajar con DMA o no, así como los tres tiemposbásicos que regirán la operación del chip. Estos tiempos están en función de la velocidad de reloj empleada,dependiente de la densidad de disco seleccionada. El comando emplea 3 bytes y carece de fase de resultados.

Step Rate Time: Tiempo comprendido entre dos impulsos consecutivos en la señal que mueve el motor paso a paso del cabezal (lo quedetermina el tiempo de acceso cilindro-cilindro). Depende de las características físicas de la unidad. El valor para los bits SR se calculacon la fórmula (16-SR)*2 en unidades DD y con (16-SR) en unidades HD (tiempos expresados en milisegundos).Head Load Time: Tiempo de demora tras activar la señal Head Load, sólo relevante por lo general en unidades de 8" (en las demás suelecargarse el cabezal nada más activarse la señal Motor On). El tiempo ’Head Load’ (bits HL) se calcula con la fórmula (HL+1)*4 enunidades DD y (HL+1)*2 en las unidades HD. La unidad de medida es el milisegundo.Head Unload Time: Tiempo esperado, tras el último acceso al disco, hasta que la señal Head Load vuelva a ser inactiva (sólo suele serrealmente significativo, una vez más, en las unidades de 8"). Las viejas unidades de 8" normalmente estaban girando continuamente (paraevitar sus lentas aceleraciones y frenados por la inercia) y levantar o bajar el cabezal era un medio de protección de la superficiemagnética. El tiempo ’Head Unload’ (bits HU) se calcula con la fórmula HU*32 en unidades DD y con HU*16 en unidades HD. Launidad de medida es el milisegundo.

Byte 0 0 0 0 0 0 0 1 1

Byte 1 SR3 SR2 SR1 SR0 HU3 HU2 HU1 HU0

Byte 2 HL6 HL5 HL4 HL3 HL2 HL1 HL0

0 - Modo DMA / 1 - NO DMA

Page 292: PCA, PS2 ,IBM y AT

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

LOS REGISTROS DE ESTADO DEL 765.

Como se comentó, el 765 dispone de 5 registros de estado: el registro principal de estado, que puedeser accedido en cualquier momento; los registros ST0, ST1 y ST2 que se obtienen como resultado de diversasórdenes; y el registro ST3. Los registros ST1 y ST2 no se pueden leer directamente (sólo se obtienen comoresultado de algunas órdenes), pero ST0 y ST3 pueden ser leídos con un comando al efecto.

El Registro Principal de Estado.

En este registro se representan en todo momento los datos más importantes sobre el estado del FDC.Sirve también para regular la comunicación entre el microprocesador y el FDC. Significado de sus bits:

Bit 7 (RQM): Request For Master (listo para E/S). Cuando este bit está a 1, el FDC está listo para recibir o enviarbytes a través del registro de datos; en caso contrario no es posible la transferencia.

Bit 6 (DIO): Data Input/Output (entrada/salida de datos). Cuando este bit está a 1, significa que el FDC tiene unbyte preparado para el procesador. Cuando está a 0, quiere decir que está esperando un byte delprocesador. Este bit no es válido hasta que RQM=1.

Bit 5 (NDM): Non DMA Mode (Modo no-DMA). En modo no DMA estará a 1 si empezó la fase de ejecución; pasaa valer 0 cuando dicha fase finaliza.

bit 4 (CB): FDC Busy (FDC ocupado). Cuando está a 1, el FDC está elaborando una orden de lectura o escrituray, por tanto, no puede procesar más comandos. Este bit se pone a 1 nada más recibir el primer bytede un comando, y baja cuando es leído el último byte de resultados.

Bits 0..3 (DB): FDD0..3 Busy (unidad ocupada). Cada bit está asociado a una unidad (de la A:-D:). Cuando se iniciaun comando Seek o un recalibrado en alguna unidad, su bit se activa: mientras alguno de estos bitsesté a 1, no se podrán enviar órdenes de lectura o escritura al FDC, pero sí más comandos Seek o derecalibrado de las demás unidades. Estos bits no se ponen a 0 por sí solos: se borran enviando elcomando Leer Estado de Interrupciones (si había finalizado ya el comando Seek o el recalibramiento).

El Registro de Estado 0 (ST0).

Este registro se denomina también registro de estado de interrupciones, ya que en modo no DMApermite identificar la causa de las interrupciones.

Bits 7, 6: Interrupt Code (código de interrupción). Con la notación Bit7-Bit6 se tiene: 00 - Normal Terminationó NT: comando finalizado con éxito. 01 - Abnormal Termination ó AT: terminación brusca (comandoiniciado pero no terminado): puede deberse a un error real o puede que no, ya que algunos sistemasno emplean la señal TC y es necesario programar en ellos el último sector de la pista como el últimosector a acceder. 10 - Invalid Command Issue (IC): comando inválido (comando que no puedeempezar al ser ilegal; puede producirse también si se ejecuta el comando Leer estado deInterrupciones sin haber ninguna en ese momento). 11 - Terminación anormal (esta señal se produceante una variación de la línea RDY (Ready) durante el comando, que empieza pero no finaliza -porejemplo, si se retira el disquete de la unidad en medio de una operación-).

Bit 5 (SE): Seek End (Fin de Seek). Este bit se pone a 1 cuando acaba la operación Seek.Bit 4 (EC): Equipment Check (comprobación de equipo). Este bit se pone a 1 si la unidad informa de un error;

también puede ponerse a 1 si, tras un recalibrado, no aparece aún la señal TRK0 que indica que seha alcanzado el cilindro 0. Esto puede suceder si el cabezal está sobre un cilindro superior al 77, yaque el obsoleto FDC (y las más modernas controladoras de disco, por compatibilidad) sólo lo muevenun máximo de 77 cilindros antes de considerar que el intento ha fallado (repítase el recalibrado).

Bit 3 (NR): Not Ready (no preparado). Se activa cuando la unidad informa de esta condición; también cuando seintenta acceder al segundo cabezal en unidades que solo tienen uno.

Bit 2 (HD): Head Address (dirección de cabezal). Indica el cabezal activo en el momento de la interrupción.Bits 1, 0 (US): Unit Select (Unidad activa): unidad activa durante la interrupción (0-A y 1-B; en PS/2 01-A y 10-B).

El Registro de Estado 1 (ST1).

Este registro informa, durante la fase de resultados, sobre el desarrollo de la fase de ejecución de losdiversos comandos.

Page 293: PCA, PS2 ,IBM y AT

293EL HARDWARE DE APOYO AL MICROPROCESADOR

Bit 7 (EN): End of Cylinder. Este bit se pone a 1 si se intenta acceder a un sector tras alcanzar el fin de pistaprogramado.

Bit 6: No utilizado (a 0).Bit 5 (DE): Data Error (error de datos). Se pone a 1 si al leer los datos y calcular su CRC (o al calcular el CRC

de los campos de ID), éste no coincide con el CRC almacenado en el disco junto a dichos datos ó IDscuando fueron grabados.

Bit 4 (OR): Overrun (excedido el tiempo de transferencia). Los datos transitan entre el microprocesador y el FDCa una velocidad mínima determinada (8, 16, 26.67 ó 32 microsegundos). Si al leer datos del FDC elprocesador no es suficientemente rápido, puede llegar un dato sobrescribiendo el anterior cuando aúnno había sido leído, lo que provoca que este bit se ponga a 1 para señalar el error.

Bit 3: No utilizado (a 0).Bit 2 (ND): No Data (no hay datos). Se pone a 1 durante la lectura o scan si el FDC no puede hallar el sector

indicado. Se pone también a 1 con el comando leer ID si el FDC no puede leer sin errores el campoID (si falla el CRC). Por último, también se pone a 1 si en el comando leer pista el sector inicial noes encontrado.

Bit 1 (NW): Not Writable (escritura no permitida). Se pone a 1 al ejecutar algún comando que implique modificarel contenido del disco, si este está protegido contra escritura.

Bit 0 (MA): Missing Address Mark (Address Mark perdida). Se pone a 1 cuando en la lectura el FDC no halla,al cabo de una vuelta completa del disco, la ID de sector. La ausencia de Data Address Mark (y laausencia también de una Data Address Mark borrada) pone a 1 este bit (junto al bit MD del registrode estado 2).

El Registro de Estado 2 (ST2).

Bit 7: No utilizado (a 0).Bit 6 (CM): Control mark (marca de control). Se pone a 1 si el FDC halla una Data Address Mark borrada durante

una lectura o comando de scan.Bit 5 (DD): Data Error in Data Field (error en campo de datos). Se pone a 1 si hay error de CRC, pero sólo en

el CRC correspondiente al campo de datos.Bit 4 (WC): Wrong Cylinder (cilindro erróneo). Al formatear la pista, se graba para cada sector información

relativa al número de cilindro, número de cabeza, número de sector y tamaño del mismo. Si al leerdespués dicha pista hay contradicción entre el nº de cilindro solicitado y el nº de cilindro que fueregistrado al formatear (debido normalmente a un posicionamiento del cabezal en un cilindro erróneo),este bit se pone a 1.

Bit 3 (SH): Scan Equal Hit (resultado de scan igual). Tras un comando de scan con la condición de igual, este bitse pone a 1 para indicar que la comparación resultó correcta en todos los bytes.

Bit 2 (SN): Scan Not Satisfied (scan no satisfecho). Si tras un comando de scan cualquiera no se halla ningúnsector en la pista que corresponda con las especificaciones, este bit se pone a 1.

Bit 1 (BC): Bad Cylinder (cilindro defectuoso). Este bit es similar al WC, con la diferencia de que se pone a 1si el número de cilindro leído es 0FFh y no coincide con el de la orden.

Bit 0 (MD): Missing Address Mark in Data Field (falta marca de direcciones en campo de datos). Se pone a 1 sien la lectura de datos no aparece una Data Address Mark (ni siquiera borrada).

El Registro de Estado 3 (ST3).

Este registro de estado sólo puede ser consultado por medio de la orden Leer estado de unidad. Seobtiene la siguiente información:

Bit 7 (FT): Fault (fallo). Este bit se corresponde con la línea Fault de algunas unidades.Bit 6 (WP): Write protected (protección contra escritura). Si este bit está a 1, significa que el disco introducido en

la unidad está protegido contra escritura.Bit 5 (RDY): Ready (preparado). Este bit se corresponde con la línea RDY (Ready) de la unidad. Si está a 1, la

unidad está preparada.Bit 4 (T0): Track 0 (cilindro 0). Este bit se corresponde con la línea TRK0 de la unidad. Si está a 1, el cabezal

de la unidad y cara elegidas se encuentra en ese momento en el cilindro 0.Bit 3 (TS): Two Side (dos caras). Si este bit está a 1, la unidad de disco posee dos cabezales.Bit 2 (HD): Head Address (dirección del cabezal). Este bit se corresponde con la línea Head Select del FDC.Bits 1, 0 (US): Unit Select (unidad seleccionada). Estos bits se corresponden con el estado de dichas líneas del FDC.

Page 294: PCA, PS2 ,IBM y AT

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

12.6.3 - EL 765 DENTRO DEL ORDENADOR.

El controlador de disquetes es accedido a través de dos puertos de E/S, en la dirección 3F4h (registrode estado) y en la 3F5h (datos). Adicionalmente, existe un registro denominado Registro de Salida Digital,en la dirección E/S 3F2h, que controla los motores de las unidades y permite reinicializar el sistema de discoy seleccionar la modalidad de operación (con o sin DMA). Los valores de bits establecidos para el registrode salida digital son los siguientes (los PS/2 sólo soportan dos disqueteras y el bit 1 está reservado):

7 6 5 4 3 2 1 0

A 1 si activar motor de D: 0 0 - seleccionar A:A 1 si activar motor de C: 0 1 - seleccionar B:

A 1 si activar motor de B: 1 0 - seleccionar C:A 1 si activar motor de A: 1 1 - seleccionar D:

A 1 si interrupciones y DMA activos (reservado en PS/2) A 0 si reinicializar el FDC

Tras poner a 0 el bit que reinicializa el FDC hay que devolverlo a 1 y (con o sin las interrupcioneshabilitadas en el bit 3) esperar la interrupción de disquete que vendrá (IRQ6 INT 0Eh) ejecutando despuésel comando leer estado de interrupciones; también hay que recalibrar, ya que el registro interno del FDC queindica el cilindro actual es puesto a 0. En las máquinas 486 en particular, es necesario hacer una leve pausatras bajar este bit, ya que devolviéndolo inmediatamente a 1 sucede que en ocasiones el 765 no se entera delcambio ¡y no se resetea! (algunos microsegundos bastan). Efectuar un reset es conveniente tras un error dedisco. En las máquinas AT o con controladoras de alta densidad existe otro registro más al que se accede enlectura, el Registro de Entrada Digital (3F7h). Su bit más significativo indica si ha habido cambio de discoen la última unidad seleccionada a través del registro de salida digital; los restantes bits se emplean paragestionar el disco duro. Una vez detectada la condición de cambio de disco, hay que bajar este bit paradetectar futuros nuevos cambios por el procedimiento, un tanto extraño y quizá absurdo de llevar el cabezalal cilindro 1 y después al 0. Para leer la línea de cambio de disco el motor debe estar encendido (se puedeencender, leer la línea y volver a apagarlo después tan deprisa que el usuario no note siquiera parpadear elled de la disquetera). Si no se puede bajar este bit será debido a que no hay disquete introducido. Tambiéna través del puerto 3F7h, pero actuando como salida, se accede al Registro de Control del Disquete, quepermite seleccionar la velocidad de transferencia de la unidad en sus dos bits menos significativos:

00 - 500.000 bits/segundo (disquetes de alta densidad de 1.2M y 1.44M)01 - 300.000 bits/segundo (disquetes de 360K en unidades de 1.2M)10 - 250.000 bits/segundo (disquetes de 3½ - 720K).11 - 1.000.000 bits/segundo (disquetes de 3½ - 2.88M).

Seleccionar la velocidad correcta en los AT es un requisito totalmente indispensable para lograr enviary recibir datos del disco. Las unidades de alta densidad de 1.2M siempre trabajan con 80 cilindros, lo quesucede es que pueden leer discos de doble densidad saltando los cilindros de dos en dos. Esto significa quepara leer el cilindro 15 de un disco de 360K, será necesario mover el cabezal al cilindro 30 (y programar el765 para leer el 15, por supuesto, ya que ha sido formateado con ese número). La BIOS automatiza este tipode operaciones, pero cuando se accede directamente al disco no queda más remedio que considerarlas. Enlos discos de 3½ nunca es necesario esto, ya que tienen siempre 80 cilindros. En la terminología anglosajona,la velocidad de transferencia se denomina data transfer rate y el movimiento doble del cabezal en los discosde doble densidad recibe el nombre de double stepping. Los PS/2 poseen en 3F0h y en 3F1h dos registrosde estado adicionales que no es preciso considerar.

Un consejo útil para los programadores en ensamblador es que realicen siempre una pequeña pausade algunos microsegundos (40-60) entre bytes sucesivos de un comando enviado al 765. La razón para ellono está muy clara, pero las BIOS AMI de 486 hacen esto y sus motivos tendrán. Accediendo desde unlenguaje de alto nivel o en procesadores 386 o inferiores esto probablemente no es necesario.

12.6.4 - DENSIDADES DE DISCO Y FORMATOS ESTÁNDAR.

Las unidades de 5¼ de doble densidad giran a 300 r.p.m. (revoluciones por minuto); esto significaque dan una vuelta cada 200 milisegundos. La velocidad de transferencia empleada es de 250 Kbit/segundo.

Page 295: PCA, PS2 ,IBM y AT

295EL HARDWARE DE APOYO AL MICROPROCESADOR

Echando cuentas, en 200 ms se pueden registrar unos 250000*0,2 = 50000 bits de datos = 6250 bytes porpista. Los disquetes de 360K poseen 9 sectores de 512 bytes; por cada sector hacen falta además 62 bytesque añade el NEC765 (ver al final del apartado 12.6.1) y otros 80 de GAP 3 que estima oportuno IBM: entotal, 654 bytes. Así, en la pista no caben 10 sectores pero sí los 9 citados. Como hay 40 cilindros en estosdisquetes (y dos caras) en total caben 9*40*2 = 720 sectores (que equivalen a 360 Kb). Por supuesto,estrechando algo el GAP 3 al formatear sí se pueden introducir 10 sectores, maniobra bastante fiable querealizan ciertos formateadores avanzados. Sin embargo, IBM fue excesivamente conservadora al principio,ya que sólo formateaba 8 sectores por pista; luego se dio cuenta y rectificó. Eran los viejos discos de 320Kb, totalmente obsoletos aunque soportados aún por el FORMAT del DOS. También han existido antañoformatos de 180 e incluso 160 Kb, basados en unidades de una sola cabeza. Las unidades de 5¼ de altadensidad giran a 360 r.p.m.; esto supone 166,66 ms por cada vuelta del disco. El aumento de velocidad sedecidió por motivos de fiabilidad. A nadie se le escapa que si el disco girara más lento y se le enviaran losdatos a la misma velocidad, cabrían más datos... pero todo tiene un límite (lo contrario sería un chollo). Lapretensión de IBM de elevar excesivamente -para la tecnología del momento- la velocidad de transferencia(de 250 a 500 Kbit/seg) obligó a tomar la medida de acelerar la unidad. Aquí, con los disquetes de dobledensidad de 5¼ se emplea la tasa de 300 Kbit/segundo: la mayor velocidad de rotación del disco escompensada exactamente por la proporcionalmente mayor velocidad de transferencia, resultando posible deesta manera leer los discos creados en unidades de doble densidad: 300000*0,16666 = 50000 bits de datos,¡exactamente igual que en las unidades de doble densidad!. Por supuesto, estas unidades giran siempre a 360r.p.m. y no es posible alterar la velocidad para leer los viejos formatos, como indican otras publicaciones ¡loque cambia es la tasa de transferencia!. Las controladoras de alta densidad pueden, por lo tanto, emplearvelocidades de 300, 500 y (aunque no usada en 5¼) 250 Kbit/seg. Con disquetes de alta densidad de 5¼y a 500 Kbit/seg caben 500000*0,16666 = 83333 bits por pista (10416 bytes). El GAP 3 que emplea elFORMAT del DOS es de 84 bytes: cada sector ocupa 512+62+84 = 658 bytes, con lo que caben 15. Esto,unido a los 80 cilindros del disco permite almacenar 1200 Kb en el mismo (en estas unidades se accede alos discos de 360K saltando los cilindros de dos en dos).

Las más modernas unidades de 3½ permitieron mantener la velocidad de 500 Kbit/seg con lavelocidad de rotación clásica de 300 r.p.m., sin problemas de fiabilidad, lo que eleva aún más la capacidad.Con ello, los disquetes de alta densidad de 3½ almacenan 500000*0,2 = 100000 bits de datos (12500 bytes)en cada pista. El FORMAT del DOS emplea un amplio GAP 3 de 108 bytes; cada sector ocupa por lo tanto512+62+108 = 682 bytes, con lo que caben 18 por pista en estas condiciones, lo que genera los conocidosdiscos de 1440 Kb. Antes de las unidades de alta aparecieron las de doble densidad de 3½: estas empleanuna velocidad de 250 Kbit/segundo, con lo que sólo admiten 6250 bytes por pista (los mismos que undisquete de doble densidad de 5¼) y 720 Kb por disco (también emplean un GAP 3 de 80 bytes). Concontroladoras de alta densidad se puede seleccionar con estos disquetes la velocidad de 300 Kbit/segundo,lo que permite formatear discos de 3½ y doble densidad con cerca de 1 Mb, sin problemas de fiabilidad. Sinembargo, el FORMAT del DOS y las rutinas de la BIOS sólo soportan en estos discos la velocidad de 250Kbit/segundo al ser la única que los PC/XT normalmente admiten. Por supuesto, el usuario siempre puedeperforar el disco para convertirlo en uno de alta densidad: la calidad de la superficie magnética en los discosde 360K es suficientemente baja para que den errores en las últimas pistas (las más próximas al centro y conmenor longitud de circunferencia) al formatearles en alta densidad; sin embargo, en 3½ los fabricantes no sehan complicado la vida y es probable que a veces se puedan formatear los discos de doble densidad comode alta sin problemas, algo que pese a todo no es quizá recomendable. Las unidades de 3½ detectan el tipo

FORMATOS DE DISCO ESTÁNDAR 5¼ Doble Densidad 5¼ Alta Densidad 3½ Doble Densidad 3½ Alta Densidad 3½ Extra Alta D.

Velocidad de rotación (R.P.M.) 300/360(*) 360 300 300 300Velocidad de transferencia (bits/seg.) 250000/300000(**) 500.000 250.000 500.000 1.000.000Esquema de codificación de información MFM MFM MFM MFM MFMBytes brutos por pista 6.250 10.416 6.250 12.500 25.000Tamaño de sector en bytes [1] 512 512 512 512 512GAP 3 al formatear con FORMAT [2] 80 84 80 108 80Bytes que usa el 765 entre sectores [3] 62 62 62 62 62Bytes ocupados por sector ([1]+[2]+[3]) 654 658 654 682 654Sectores por pista 9 15 9 18 36Bytes que usa el 765 en inicio de pista 146 146 146 146 146Bytes aproximados que restan en GAP 4B 218 400 218 78 1310Cilindros 40 80 80 80 80Caras o cabezales 2 2 2 2 2Sectores en el disco 720 2400 1440 2880 5760Kbytes por disco 360 1200 720 1440 2880

(*) 300 en unidades de doble densidad y 360 en las de alta densidad(**) 250.000 en unidades de doble densidad y 300.000 en las de alta densidad

Page 296: PCA, PS2 ,IBM y AT

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

de disco y las perforaciones del mismo sólo sirven para que la disquetera sepa qué velocidad de transferenciaemplear (sin embargo, en 5¼ no hay perforaciones y la unidad es capaz de detectar la velocidad apropiada).

Finalmente, los disquetes de extraalta densidad de 3½ trabajan con 1 Mbit/segundo de velocidadde transferencia, con 25000 bytes por pista y 36 sectores: el doble de datos que en alta densidad, pero a unprecio mucho más del doble, lo que les ha convertido en un lujo y un fracaso comercial. Existen unidadesde 3½ perfeccionadas por medios ópticos que almacenan 20 megabytes por disco, y que también admitendisquetes de 720K y 1.44M (y a menudo, no los de 2.88M). El secreto de estos discos ópticos (flopticals)es la precisión en el posicionamiento del cabezal, lo que permite almacenar cientos de cilindros en lugar delas 80 habituales. También hay unidades ZIP que admiten disquetes (aproximadamente de 3½) con capacidadde 100 Mb ó 1 Gb, pero menos convencionales (están sectorizadas por hardware).

Los discos normales están formateados con sectores de 512 bytes en todos los casos. Estos sectoresson numerados a partir de 1 (y no a partir de 0) en el momento del formateo, y así habrán de ser accedidosen el futuro. En una sola vuelta del disco es factible escribir o leer todos los sectores de una pista si se hacede una vez con el comando apropiado, ya que accediendo de sector en sector podría no dar tiempo a accederal siguiente sector cuando el anterior acaba de pasar por delante del cabezal, lo que además obligaría a daruna vuelta al disco por cada sector, con un desplome en picado del rendimiento. Lo mismo puede sucedersi los sectores están excesivamente próximos debido al empleo de un formato no estándar de más capacidad:normalmente, los GAP 3 que separan los sectores son bastante amplios como para dar tiempo al 765, en lasoperaciones de escritura, a conmutar entre la escritura de los últimos bytes del sector (junto al CRC que vadetrás) y la lectura de los ID del sector siguiente; en caso contrario la operación de escritura de múltiplessectores terminaría con error (sector no encontrado), a no ser que fueran escritos de uno en uno, con laconsiguiente ralentización del acceso. Experimentalmente se puede afirmar que el GAP 3 en alta densidadno debería ser inferior a 32, ni tampoco inferior a 40 en doble densidad, lo que parece indicar que la unidadnecesita que los sectores estén separados al menos entre 0.5 y 1 ms, respectivamente; aunque estas cifras sepueden rebajar incluso casi a la mitad, esos valores son los mínimos recomendados. En caso de tener queinfringir esta regla, la solución sería emplear un interleave distinto del 1:1 habitual: en otras palabras, lossectores pueden ser numerados de manera no consecutiva. Por ejemplo, con 9 sectores, se les puede colocaren la pista, sucesivamente, con los números 1, 6, 2, 7, 3, 8, 4 ,9, 5. Así, entre dos sectores de númeroconsecutivo hay otro, y se gana tiempo para poder pillarlo; este ejemplo en concreto corresponde a uninterleave 1:2, ya que hay que dar dos vueltas al disco para poder acceder una vez a toda la pista. Hay casosen que al juntar mucho los sectores e intentar escribir una pista no se produce el error: esto puede ocurrirsobre todo con sectores de más de 512 bytes, ya que cuando el cabezal acaba de acceder a un sector y va apor el siguiente (que acaba de pasar de largo), no encuentra los ID del que va detrás hasta pasado un buenrato; de ahí a volver a encontrarse con el sector buscado puede transcurrir bastante menos de una vuelta deldisco y finalmente lo encontraría sin devolver error. Naturalmente, esto sigue sin ser interesante, una vez más,por razones de velocidad. Finalmente señalar que el GAP mínimo para operaciones de lectura multisector esmucho menor que para las operaciones de escritura (bastaría con un GAP de 1 ó 2 bytes), ya que la unidadno pierde tiempo en conmutar entre la escritura del sector y la lectura de IDs del siguiente.

Un pequeño detalle más: conviene recordar que al formatear una pista, la controladora espera al pasode la marca de índice -el pequeño agujerito del disquete- lo que provoca que si todas las pistas se numeranpor igual, en ambas caras del disco están colocados físicamente en la misma posición los mismos númerosde sector, gracias a esta sincronización, conservando la estructura a lo largo de unos radios imaginarios.Digamos que si el disco es una tarta, al cortar las porciones cada comensal se lleva todos los cilindros delmismo y único sector N que le ha tocado. En la operación habitual del disco, cuando se acaba de acceder auna pista, lo más probable es que haya que continuar en la siguiente (bien en el otro cabezal o en el cilindroadyacente). Esta conmutación de cabezal hace perder cierto tiempo: cuando se acaba de acceder a una pista,el cabezal está al final de la misma y, por consiguiente, muy cerca también del principio (a nadie se le escapaque las pistas son circulares); si se conmuta de cabezal y el disco ya ha girado lo suficiente como para pasarpor delante del primer sector de la nueva pista, habrá que volver a dar una vuelta entera. Esto puede sucedersi el GAP que hay al final de la pista no es lo suficientemente grande. Y, por desgracia, de hecho sucede contodos los formatos de disco del DOS. Al pasar de una pista a la adyacente, en operaciones de escritura, se

Page 297: PCA, PS2 ,IBM y AT

297EL HARDWARE DE APOYO AL MICROPROCESADOR

pierden unos 18 milisegundos (3 del desplazamiento del cabezal y 15 de espera hasta que éste deje de vibrar)lo que equivale a 1125 bytes en un disco de alta densidad de 3½: ¡unos dos sectores!. Por eso, cuando seacaba con el sector 18 de una pista y se pasa a la siguiente, el cabezal está sobre algún punto del sector 2ó el 3 y el primer sector que se encuentra es el 3 ó el 4, teniendo que esperar a que pasen otros 15 ó 16 parallegar al 1. La solución a este problema pasa por numerar los sectores, de una pista a otra, deslizando lanumeración (técnica conocida como skew o sector sliding):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Pista N16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Pista N+113 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 Pista N+2

En el esquema se han trazado sólo tres pistas, pero las siguientes tendrían un tratamiento análogo.Realmente, al conmutar de un cabezal a otro en el mismo cilindro no hace falta deslizar tanto la numeración,ya que es una operación más ágil y con menos retardos. En el ejemplo, experimentalmente se puededeterminar que en vez de 3 bastaría con desplazar 2 sectores la numeración. En los discos de 5¼ de altadensidad se pueden recomendar los mismos desplazamientos de numeración. Sin embargo, en los de 5¼ ydoble densidad bastaría con desplazar un sector el orden al conmutar de cabezal (y los mismos 3 al cambiarde cilindro). En los de doble densidad de 3½ conviene desplazar un sector la numeración al conmutar decabezal y 2 al cambiar de cilindro. Por supuesto, estos valores son los más convenientes en general, si bienalgún ordenador en concreto podría operar mejor con otra numeración similar a ésta aunque no idéntica. Encualquier caso, numerar todos los sectores de las pistas por igual, que es lo que hacen todas las versiones delFORMAT del DOS (al menos hasta la versión 6.0 del sistema), resulta extremadamente ineficiente y puedereducir a la mitad la velocidad de los disquetes. Algunos buenos formateadores (como FDFORMAT con susopciones /X e /Y) suelen tener en cuenta estos factores. Por supuesto, esta numeración de los sectores noimplica la más mínima pérdida de compatibilidad en los disquetes estándar: lo que sucede es que loscreadores del DOS no se han preocupado demasiado hasta ahora de optimizar el rendimiento.

12.6.5 - ACCESO A DISCO CON DMA.

Los disquetes son gestionados por la BIOS en todas las máquinas empleando el DMA, por medio delcanal 2 del 8237. Sin embargo, como veremos en un apartado posterior, es factible realizar las operacionesdirectamente, sin ayuda del DMA. Al emplear el modo DMA, se produce una interrupción IRQ6 (INT 0Eh)para avisar del término de la operación de disco realizada. Al emplear el DMA conviene tener cuidado conevitar un desbordamiento en el offset 0FFFFh de la página empleada. Por ejemplo, intentar leer o grabar unsector normal de 512 bytes entre las direcciones de memoria 3FF2:0000 y la 3FF2:01FF (direccionesabsolutas 3FF20 a la 4011F) resultará fallido al estar implicadas las páginas de DMA 3 y 4, cuando sólopuede estarlo una de las dos. En la práctica, será necesario reservar memoria por importe del doble deltamaño del (o los) sector(es) a ser accedido(s) y hacer cálculos para establecer una dirección de transferenciaque coincida dentro de una sola página de DMA. No tener en cuenta este factor es jugar a la lotería con losdiscos. La BIOS del sistema se encarga de comprobar por software si el buffer facilitado cruza una fronterade DMA antes de realizar las operaciones de E/S, retornando con el error correspondiente en caso afirmativo.Por hardware es imposible detectar esta circunstancia al no producirse errores, pero sí falla la operación: secorrompen zonas de memoria no previstas yel resultado probable es disfunción y/ocuelgue del sistema (a no ser que hayamucha suerte). Sin embargo, cuando el DOSse carga en memoria al principio delarranque, modifica la INT 13h de la BIOSpara que esta interrupción nunca devuelvaun error debido a este motivo (en cambio, laINT 40h, que es quien realmente controlalos disquetes en la inmensa mayoría de losordenadores AT y que es invocada desdeINT 13h, sí puede devolver errores defrontera de DMA).

765DEBUG 3.1 - UTILIDAD PARA ANALISIS AVANZADO A BAJO NIVEL DE DISQUETES.Programación directa del controlador NEC765 y el DMA 8237.Funcionamiento probado bajo sistemas PC XT, AT, 386 y 486.Soporte para disquetes de 360K, 720K, 1.2M, 1.44M y 2.88M.

(C) 1992, 1993, 1994 - Ciriaco García de Celis.

F2 - Seleccionar unidad/densidad y resetear.F3 - Recalibrar cabezal (necesario tras F2).

F4 - Cambiar de cabezal.F5 - Posicionar cabezal.F6 - Leer ID’s.F7 - Leer sector.F8 - Escribir sector.F9 - Formatear pista.

F10 - Conmutar MF/MFM.ESC - Salir

Unidad A: 500 Kbit/seg en MFM - Cilindro 0 y Cabezal 0

Elige una opción: _

Figura 12.6.5.1 PANTALLA PRINCIPAL DEL PROGRAMA

Page 298: PCA, PS2 ,IBM y AT

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

El siguiente programa de ejemplo ha sido realizado íntegramente en Borland C (compilable tambiénsin errores en Turbo C 2.0) y permite practicar al lector con la operación a bajo nivel del disco. Se puedenleer y escribir sectores (con tamaños normales o no), formatear pistas, leer los ID de una pista, y todas lasoperaciones auxiliares necesarias (seleccionar unidad, velocidad de transferencia, recalibrar, seleccionarcabezal, posicionar cabezal, elegir MF/MFM). La opción de leer ID’s es especialmente útil para analizardiscos con protecciones anticopia; se trata además de una tarea inevitable que ha de realizar necesariamentecualquier copión, como paso previo a la duplicación del disquete. En esta opción se utiliza una interesanterutina de temporización de alta precisión, empleando el 8254, para poder medir con exactitud losmilisegundos de disco que ocupa cada sector en la pista y poder hacerse una idea de cómo está organizaday aprovechada. El formateo también es especialmente versátil, ya que permite editar, sin lujos pero coneficacia, los bytes de los sectorespropuestos por defecto -los más razonablespor otra parte- antes de enviarlos alcontrolador. Este programa es un útil bancode pruebas para medir la fiabilidad detécnicas de formateo especial, para idear yprobar métodos de protección anticopia y,en general, para aprender sobre elfuncionamiento a bajo nivel de los discos.El dato de la velocidad de transferencia noes relevante por lo general en los PC/XT.La selección incorrecta de una sola opciónpuede provocar que el programa falle,aunque al cabo de unos segundos serecupera el control. Las dos primerasopciones del menú no son obligatorias;pero conviene seleccionarlas al principio y,en general, cada vez que se cambie dedisco. Una línea inferior informapermanentemente de los principalesparámetros activos, si bien no convienecreer ciegamente en ella. Por ejemplo, si seha intentado posicionar el cabezal en elcilindro 120 de un disco formateado, yluego se le vuelve a posicionar en el 70, enesa línea aparecerá el valor 70 aunque alleer los ID podríamos descubrir que estárealmente sobre el cilindro 31, ya que esaunidad no soporta más de 82 cilindros(numerados de 0 a 81) y no pudo pasar del81 cuando se le ordenó ir al 120. En esteejemplo particular, lo más aconsejabledespués sería recalibrar, ya que el programacree que está sobre el cilindro 70 y lasopciones de leer y escribir sector fallarán;ya que no preguntan el número de cilindroy emplean el que se supone activo al enviarel comando al controlador.

Sector a leer: 1

Tamaño de sector:0 -> 1-128 bytes1 -> 256 bytes2 -> 512 bytes3 -> 1024 bytes4 -> 2048 bytes5 -> 4096 bytes

Elige: 2

Resultado de la operación:

[ST0=0x01] [ST1=0x00] [ST2=0x00][Cilindro 1] [Cabezal 0] [Sector 1] [Tamaño 2]

Pulsa una tecla para ver el sector [ESC=salir].

0000: EB 3C 90 4D 53 44 4F 53 - 35 2E 30 00 02 01 01 00 δ<ÉMSDOS5.0.....0010: 02 E0 00 40 0B F0 09 00 - 12 00 02 00 00 00 00 00 .α.@.≡..........0020: 00 00 F8 04 00 00 29 EC - 1D 64 3C 4E 4F 20 4E 41 ..°...)∞.d<NO NA0030: 4D 45 20 20 20 20 46 41 - 54 31 36 20 20 20 FA 33 ME FAT16 30040: C0 8E D0 BC 00 7C 16 07 - BB 78 00 36 C5 37 1E 56 Ä .|.. x.6 7.V0050: 16 53 BF 3E 7C B9 0B 00 - FC F3 A4 06 1F C6 45 FE .S >| .. ≤ñ.. E0060: 0F 8B 0E 18 7C 88 4D F9 - 89 47 02 C7 07 3E 7C FB .ï..|êM ëG. .>|√0070: CD 13 72 79 33 C0 39 06 - 13 7C 74 08 8B 0E 13 7C .ry3 9..|t.ï..|0080: 89 0E 20 7C A0 10 7C F7 - 26 16 7C 03 06 1C 7C 13 ë. |á.|≈&.|...|.0090: 16 1E 7C 03 06 0E 7C 83 - D2 00 A3 50 7C 89 16 52 ..|...|â .úP|ë.R00A0: 7C A3 49 7C 89 16 4B 7C - B8 20 00 F7 26 11 7C 8B |úI|ë.K| .≈&.|ï00B0: 1E 0B 7C 03 C3 48 F7 F3 - 01 06 49 7C 83 16 4B 7C ..|. H≈≤..I|â.K|00C0: 00 BB 00 05 8B 16 52 7C - A1 50 7C E8 92 00 72 1D . ..ï.R|íP|ΦÆ.r.00D0: B0 01 E8 AC 00 72 16 8B - FB B9 0B 00 BE E3 7D F3 .Φ¼.r.ï√ .. π≤00E0: A6 75 0A 8D 7F 20 B9 0B - 00 F3 A6 74 18 BE 9E 7D ªu.ì ..≤ªt. 00F0: E8 5F 00 33 C0 CD 16 5E - 1F 8F 04 8F 44 02 CD 19 Φ_.3 .^.Å.ÅD. .

Bytes 0000-0255 del sector (1/2)Utiliza los cursores [ESC=salir]

0100: 58 58 58 EB E8 8B 47 1A - 48 48 8A 1E 0D 7C 32 FF XXXδΦïG.HHè..|20110: F7 E3 03 06 49 7C 13 16 - 4B 7C BB 00 07 B9 03 00 ≈π..I|..K| .. ..0120: 50 52 51 E8 3A 00 72 D8 - B0 01 E8 54 00 59 5A 58 PRQΦ:.r .ΦT.YZX0130: 72 BB 05 01 00 83 D2 00 - 03 1E 0B 7C E2 E2 8A 2E r ...â ....|ΓΓè.0140: 15 7C 8A 16 24 7C 8B 1E - 49 7C A1 4B 7C EA 00 00 .|è.$|ï.I|íK|Ω..0150: 70 00 AC 0A C0 74 29 B4 - 0E BB 07 00 CD 10 EB F2 p.¼. t) . .. .δ≥0160: 3B 16 18 7C 73 19 F7 36 - 18 7C FE C2 88 16 4F 7C ;..|s.≈6.| ê.O|0170: 33 D2 F7 36 1A 7C 88 16 - 25 7C A3 4D 7C F8 C3 F9 3 ≈6.|ê.%|úM|°0180: C3 B4 02 8B 16 4D 7C B1 - 06 D2 E6 0A 36 4F 7C 8B .ï.M| . µ.6O|ï0190: CA 86 E9 8A 16 24 7C 8A - 36 25 7C CD 13 C3 0D 0A åΘè.$|è6%| . ..01A0: 45 72 72 6F 72 2C 20 64 - 65 20 64 69 73 63 6F 20 Error, de disco01B0: 64 65 20 73 69 73 74 65 - 6D 61 0D 0A 52 65 65 6D de sistema..Reem01C0: 70 6C 61 63 65 20 79 20 - 70 72 65 73 69 6F 6E 65 place y presione01D0: 20 63 75 61 6C 71 75 69 - 65 72 20 74 65 63 6C 61 cualquier tecla01E0: 0D 0A 00 49 4F 20 20 20 - 20 20 20 53 59 53 4D 53 ...IO SYSMS01F0: 44 4F 53 20 20 20 53 59 - 53 00 00 00 00 00 55 AA DOS SYS.....U¬

Bytes 0256-0511 del sector (2/2)Utiliza los cursores [ESC=salir]

Figura 12.6.5.2 LECTURA DE UN SECTOR

Al principio del programa se asignan valores por defecto a las variables, se establece la velocidad detransferencia en 500 Kbit/seg y se reserva memoria para almacenar un sector. Como se vio anteriormente,hay que asegurar que el buffer no cruza una frontera de DMA, por lo que en la práctica se reserva el doblede la memoria necesaria y se asigna el puntero de tal manera que esto no suceda en ningún caso. El programaconsta de un menú desde el que se accede a las diversas opciones que desembocan finalmente en funciones

Page 299: PCA, PS2 ,IBM y AT

299EL HARDWARE DE APOYO AL MICROPROCESADOR

independientes. La función seleccionar() permite elegir la unidad activa, reseteándola y enviando el comandospecify al FDC.

La función recalibrar() envía este comando al FDC y lo repite si falla, por si estaba sobre un cilindrosuperior al 77; en esta función y en las restantes, para detectar el fin de la operación se espera la llegada dela interrupción de disco correspondiente (IRQ 6, ligada a INT 0Eh). La BIOS se encarga en esta interrupciónde activar el bit más significativo de la posición 40h:3Eh. La función esperar_int() espera la llegada de lainterrupción comprobando dicho bit durante un par de segundos antes de considerar que la operación hafallado, devolviendo después dicho bit a 0. Realmente, aunque haya un error la interrupción debe llegar y elcomando ha de finalizar. Sin embargo, el FDC es a veces demasiado flexible: por ejemplo, si la portezuelade la unidad (en 5¼) está abierta y hay un disco introducido, se puede quedar esperando indefinidamente.Además, en general, en la programación a bajo nivel es conveniente no hacer nunca bucles infinitos paraesperar a que suceda algo. Tras el comando de recalibrado hay que ejecutar el de lectura de estado deinterrupciones, cuyo resultado es además impreso en pantalla durante 1,5 segundos para dar tiempo a leerlosin tener que pulsar teclas (es muy poca información y se puede leer en menos de un segundo...).

La función posicionar() lleva el cabezal sobre el cilindro solicitado. Si se está trabajando con unavelocidad de 300 Kbit/seg, correspondiente normalmente a un disco de 5¼ y doble densidad (360K), sepregunta al usuario si la unidad es de 80 cilindros (1.2M) y se le pide que confirme que el disco es de 360K.En ese caso, el número de cilindro será multiplicado por dos al enviar el comando seek al FDC, ya que esun disco formateado con 40 pistas. Al final se ejecuta nuevamente el comando de lectura de estado deinterrupciones, imprimiendo el resultado y haciendo una pausa para que de tiempo a leerlo, aunque si seomitiera este paso y la siguiente operación fuera de escritura al menos habría que esperar 15 milisegundospara dar tiempo al cabezal a asentarse y dejar de vibrar. Realmente, en este programa ni eso haría falta, ya

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 10.77] 10.77 9 512 ( 2) 0 0 0x00 0x00 0x00[ 21.53] 10.76 10 512 ( 2) 0 0 0x00 0x00 0x00[ 32.31] 10.78 11 512 ( 2) 0 0 0x00 0x00 0x00[ 43.07] 10.76 12 512 ( 2) 0 0 0x00 0x00 0x00[ 53.85] 10.78 13 512 ( 2) 0 0 0x00 0x00 0x00[ 64.63] 10.78 14 512 ( 2) 0 0 0x00 0x00 0x00[ 75.52] 10.89 15 512 ( 2) 0 0 0x00 0x00 0x00[ 86.30] 10.77 16 512 ( 2) 0 0 0x00 0x00 0x00[ 97.07] 10.77 17 512 ( 2) 0 0 0x00 0x00 0x00[ 111.31] 14.24 18 512 ( 2) 0 0 0x00 0x00 0x00[ 122.07] 10.76 1 512 ( 2) 0 0 0x00 0x00 0x00[ 132.85] 10.78 2 512 ( 2) 0 0 0x00 0x00 0x00[ 143.61] 10.76 3 512 ( 2) 0 0 0x00 0x00 0x00[ 154.38] 10.77 4 512 ( 2) 0 0 0x00 0x00 0x00[ 165.15] 10.77 5 512 ( 2) 0 0 0x00 0x00 0x00[ 175.93] 10.78 6 512 ( 2) 0 0 0x00 0x00 0x00[ 186.69] 10.77 7 512 ( 2) 0 0 0x00 0x00 0x00[ 197.46] 10.77 8 512 ( 2) 0 0 0x00 0x00 0x00[ 208.24] 10.78 9 512 ( 2) 0 0 0x00 0x00 0x00[ 219.00] 10.76 10 512 ( 2) 0 0 0x00 0x00 0x00[ 229.78] 10.79 11 512 ( 2) 0 0 0x00 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 399.32] 399.32 12 512 ( 2) 0 0 0x40 0x01 0x00[ 798.94] 399.62 12 512 ( 2) 0 0 0x40 0x01 0x00[ 1198.43] 399.50 12 512 ( 2) 0 0 0x40 0x01 0x00[ 1598.09] 399.66 12 512 ( 2) 0 0 0x40 0x04 0x00[ 1997.53] 399.44 12 512 ( 2) 0 0 0x40 0x01 0x00[ 2396.95] 399.41 12 512 ( 2) 0 0 0x40 0x01 0x00[ 2796.40] 399.45 12 512 ( 2) 0 0 0x40 0x01 0x00[ 3196.00] 399.61 12 512 ( 2) 0 0 0x40 0x01 0x00[ 3595.62] 399.61 12 512 ( 2) 0 0 0x40 0x04 0x00[ 3995.22] 399.61 12 512 ( 2) 0 0 0x40 0x01 0x00[ 4394.62] 399.40 12 512 ( 2) 0 0 0x40 0x04 0x00[ 4794.18] 399.56 12 512 ( 2) 0 0 0x40 0x04 0x00[ 5193.60] 399.42 12 512 ( 2) 0 0 0x40 0x04 0x00[ 5593.10] 399.50 12 512 ( 2) 0 0 0x40 0x01 0x00[ 5992.69] 399.59 12 512 ( 2) 0 0 0x40 0x01 0x00[ 6392.16] 399.47 12 512 ( 2) 0 0 0x40 0x01 0x00[ 6791.64] 399.48 12 512 ( 2) 0 0 0x40 0x04 0x00[ 7191.33] 399.70 12 512 ( 2) 0 0 0x40 0x01 0x00[ 7590.84] 399.50 12 512 ( 2) 0 0 0x40 0x01 0x00[ 7990.23] 399.40 12 512 ( 2) 0 0 0x40 0x01 0x00[ 8389.74] 399.51 12 512 ( 2) 0 0 0x40 0x01 0x00

Una tecla para leer más ID’s [ESC=salir].

Figura 12.6.5.3 LECTURAS CORRECTA E INCORRECTA DE ID’s

que no hay humano tan rápido que enmenos de 15 ms después de haber escogidola opción de posicionar cabezal puedaelegir la de escribir sector en el menúprincipal. Pero en otros programas, dondese posicione repetidamente el cabezal y seacceda al disco en escritura repetitivamente,conviene no olvidar hacer la pausa. Bueno,si se olvida, no sucede nada: sólo se podríaproducir algún error al escribir que no sedetectaría hasta una posterior lectura. Lomalo es que estos errores son esporádicosy resulta muy difícil localizar su origen.

Las funciones leer_sector() yescribir_sector() son muy parecidas. Laprincipal diferencia es que la primeramuestra el sector leído (ver figura 12.6.5.2)y la segunda tiene que preguntar el bytecon que rellenará el sector escrito, ya queno permite editarlo. Antes de leer el sectorse rellena el buffer en memoria con lasignatura 5AA5h. Tras la lectura, el sectores mostrado -incluso si se produjo error-aunque si el usuario observa que contieneprecisamente 5AA5h podrá deducir que el

error iba muy en serio. Hay casos en que con error y todo puede ser interesante ver el sector, como luegoveremos. La lectura y escritura de los sectores se realiza por DMA, el cual es programado porprepara_dma().

Page 300: PCA, PS2 ,IBM y AT

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

La función leer_id() envía 22 veces dicho comando al FDC, para leer los ID (los 4 bytes con quese formateó cada sector) y la información de estado (registros ST0..ST2). Probablemente no habrá más de21 sectores en una pista, por lo que será posible echar un vistazo detallado a la misma. El primer sector enaparecer no es el 1 ni el de número másbajo: sencillamente, el primero en pasar porel cabezal al ejecutar el comando; como launidad estaba girando con antelación y elusuario elige la opción cuando quiere, elprimer sector visualizado será cualquiersector de la pista aleatoriamente. Si hubieramás de 21 sectores en la pista, sevisualizarían sólo los 21 primeros en pasardelante del cabezal. Resulta interesantesaber cuánto tiempo transcurre entre elpaso de un sector y otro, lo que permiteconocer su tamaño real (interesante endiscos con protección anticopia) y tambiénensayar nuevos formatos de disco. Porejemplo, si se formatean más sectores delos que caben en una pista, el comando deformatear termina siempre con éxito, peroalguno de los últimos sectores habrámachacado a los primeros, y la manera mássencilla de verlo es examinando los ID aver si están todos. De hecho, entre elúltimo sector de la pista y el primerodebería existir una mayor separación queentre otros dos sectores cualquiera, debidoa los GAP ubicados al final de la pista y alprincipio de la misma (que conviene noreducir demasiado). Para medir el tiempo,se programa el 8254 (u 8253 en losPC/XT) con una cuenta 0xFFFF. A partirde ese momento, se espera que llegue lainterrupción de disco y se comprueba si elcontador se ha decrementado hasta 0 y seha vuelto a recargar con 0xFFFF: en esecaso, la variable cnth se incrementa paraindicar que han pasado 65535/1193180segundos más; si llegara a valer más de 8se abortaría el proceso al considerar que lainterrupción tarda demasiado en llegar (másde 0,4 segundos en los que el disco máslento ya ha dado dos vueltas). Tras el finalde cada comando de lectura de ID, serecarga inmediatamente la cuenta inicial (elvalor 0xFFFF) en el contador 2, por elprocedimiento de bajar y subir la líneaGATE del mismo, con objeto de queempiece a contar el tiempo para el próximosector desde ya mismo. Se lee lainformación que devuelve el FDC pero no

Tamaño de sector:0 -> 128 bytes1 -> 256 bytes2 -> 512 bytes3 -> 1024 bytes4 -> 2048 bytes5 -> 4096 bytes

Elige: 0

Número de sectores: 25

Valor para el GAP 3: 50

Byte para inicializar sectores: 65

Puntualizaciones sobre el formateo:

He establecido por defecto una tabla con los cuatrobytes que hay que enviar al controlador, por cada unode los sectores de la pista, que están numerados:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2021 22 23 24 25

Puedes elegir lo siguiente:

1 - Introducir tú los 4 bytes de un sector.2 - Modificar un cierto byte en todos los sectores.ESC - Dejar las cosas como están ahora.

Elige opción.

Sector a alterar: 6Nº Cilindro (anterior=0): 0Nº cabezal (anterior=0): 0Nº sector (anterior=6): 6Tamaño sector (anterior=0): 1¿De acuerdo (S/N)?

Resultado de la operación:

[ST0=0x01] [ST1=0x00] [ST2=0x00][Cilindro 65] [Cabezal 1] [Sector 0] [Tamaño 0]

Formateo correcto. Pulsa una tecla.

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 6.25] 6.25 19 128 ( 0) 0 0 0x01 0x00 0x00[ 12.52] 6.26 20 128 ( 0) 0 0 0x01 0x00 0x00[ 18.77] 6.26 21 128 ( 0) 0 0 0x01 0x00 0x00[ 25.03] 6.26 22 128 ( 0) 0 0 0x01 0x00 0x00[ 31.30] 6.27 23 128 ( 0) 0 0 0x01 0x00 0x00[ 37.56] 6.26 24 128 ( 0) 0 0 0x01 0x00 0x00[ 50.42] 12.86 25 128 ( 0) 0 0 0x01 0x00 0x00[ 56.68] 6.26 1 128 ( 0) 0 0 0x01 0x00 0x00[ 62.93] 6.25 2 128 ( 0) 0 0 0x01 0x00 0x00[ 69.19] 6.26 3 128 ( 0) 0 0 0x01 0x00 0x00[ 75.46] 6.27 4 128 ( 0) 0 0 0x01 0x00 0x00[ 81.72] 6.26 5 128 ( 0) 0 0 0x01 0x00 0x00[ 87.98] 6.26 6 256 ( 1) 0 0 0x01 0x00 0x00[ 94.25] 6.27 7 128 ( 0) 0 0 0x01 0x00 0x00[ 100.51] 6.26 8 128 ( 0) 0 0 0x01 0x00 0x00[ 106.77] 6.26 9 128 ( 0) 0 0 0x01 0x00 0x00[ 113.03] 6.26 10 128 ( 0) 0 0 0x01 0x00 0x00[ 119.28] 6.26 11 128 ( 0) 0 0 0x01 0x00 0x00[ 125.55] 6.26 12 128 ( 0) 0 0 0x01 0x00 0x00[ 131.81] 6.26 13 128 ( 0) 0 0 0x01 0x00 0x00[ 138.07] 6.26 14 128 ( 0) 0 0 0x01 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

Figura 12.6.5.4 FORMATEO DE UNA PISTA

se imprime por problemas de velocidad, sino que se almacena en una matriz. La variable cnth y el últimovalor de cuenta leído del 8254 permiten determinar con precisión milimétrica el tiempo que ha pasado desde

Page 301: PCA, PS2 ,IBM y AT

301EL HARDWARE DE APOYO AL MICROPROCESADOR

el envío del comando de lectura de ID’s hasta la obtención del resultado. El primer dato de tiempo leído esincorrecto por doble motivo: por un lado, el cabezal podía estar en medio de un sector cuando se envió elcomando y el tiempo medido no sería la longitud del sector anterior sino de medio sector anterior; por otrolado, la cuenta es recargada (cambio de la línea GATE) al final de cada comando en lugar de al principio,por razones de precisión. Por ello, se imprimirán los resultados de las 21 últimas muestras, descartando laprimera. En la figura 12.6.5.3 hay dos ejemplos de lectura de ID, de la primera pista de un disquete de 1.44Mcreado por el FORMAT del DOS. En el primero el resultado es correcto; en el segundo, la velocidadseleccionada era incorrecta (no los 500 Kbit/seg necesarios) y el FDC no ha podido encontrar los sectores,teniendo además que dar dos vueltas al disco (200 ms en cada una de ellas). Si no hubiera disquete o laportezuela estuviera abierta, al cabo de un minuto y medio aparecería una pantalla con datos de tiempo N.D.(no determinado) y todos los demás bytes con ?? para indicar el error. Resulta increíble la precisión mediade la medida: 399,5 ms frente a los 400 reales: una desviación media de ¡0,5 milisegundos!, si bien estodependerá del ordenador: cuanto más rápido, más exacta resulta la medida.

La función formatear_pista() pregunta los parámetros básicos (número de sectores, tamaño, GAPy byte de inicialización) y genera una tabla con los 4 bytes que hay que enviar al FDC por cada sector. Sinembargo, permite al usuario editar rudimentariamente dicha tabla con la función editar_tabla_fmt(), parapermitir a éste ensayar trucos, ya que los valores propuestos por defecto son por lo general los másconvenientes. En esos 4 bytes que hay por cada sector se almacenan el número de cilindro, el de cabezal, elnúmero de sector y el tamaño. En la función de edición se permite cambiar los bytes de un sólo sector, ocambiar uno de los 4 bytes en todos los sectores. Estos 4 bytes identifican cada sector y son comparados conlos que se envían en el futuro comando delectura o escritura de sector, debiendocoincidir plenamente para que el FDCencuentre el sector. El número de cilindroy el de cabezal suelen coincidir -y así sonpropuestos por defecto- con el cilindro y elcabezal en que esté dicho sector; cambiaresto puede ser interesante en técnicas deprotección de información, ya que el sectordesaparece pero realmente sigue estandoahí: la diferencia es que a la hora de leerlohay que indicar al FDC no el cilindro realsobre el que está posicionado el cabezalsino el número de cilindro y cabezal que seprogramaron al formatear el sector, quepueden ser cualquier otro. Este programa,a la hora de leer los sectores no pregunta elnúmero de cilindro ni cabezal -para ahorrartiempo- por lo que no permite verificar estapropiedad, pero con una pequeña y sencillamodificación el lector podría comprobarlopor sí mismo. Lo que sí puede resultar másinteresante es cambiar el número de sectorpropuesto por defecto o, mejor aún: sutamaño. Al formatear la pista, el tamaño de

Sector a leer: 6

Tamaño de sector:0 -> 1-128 bytes1 -> 256 bytes2 -> 512 bytes3 -> 1024 bytes4 -> 2048 bytes5 -> 4096 bytes

Elige: 1

Resultado de la operación:

[ST0=0x41] [ST1=0x20] [ST2=0x20][Cilindro 0] [Cabezal 0] [Sector 6] [Tamaño 1]

Error de lectura (el sector puede estar mal leído).Nota: el buffer de lectura contenía el patrón 5AA5.

Pulsa una tecla para ver el sector [ESC=salir].

0000: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0010: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0020: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0030: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0040: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0050: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0060: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0070: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA0080: 6B 70 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E kpNNNNNNNNNNNNNN0090: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN00A0: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN00B0: 4E 4E 4E 4E 00 00 00 00 - 00 00 00 00 00 00 00 00 NNNN............00C0: A1 A1 A1 FE 00 00 07 00 - 40 8B 4E 4E 4E 4E 4E 4E ííí ....@ïNNNNNN00D0: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN00E0: 00 00 00 00 00 00 00 00 - 00 00 00 00 A1 A1 A1 FB ............ííí√00F0: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

Bytes 0000-0255 del sector (1/1)Utiliza los cursores [ESC=salir]

Figura 12.6.5.5 LECTURA DEL SECTOR DE TAMAÑO TRUCADO

los sectores es asignado al enviar el comando de formateo al FDC: todos los sectores tendrán dicho tamaño,con independencia del tamaño particular que se asigne al enviar los 4 bytes específicos. En otras palabras,si se programa un tamaño 2 (de 512 bytes) en el comando de formateo, todos los sectores serán de 512 bytes,aunque alguno esté definido como de 1024, de 256 bytes,... en el 4º byte de información enviado por cadasector al FDC. Por tanto, ¿Para que sirve este byte?: una vez más, para posibilitar la lectura. Si un sector estáprogramado con tamaño 3 (1024 bytes) habrá de ser leído indicando tamaño 3. Si era de 512 bytes, lo quesucede es que además del sector se leen, ni más ni menos, los GAPs que van detrás, los ID’s e incluso partedel siguiente sector; por supuesto que se produce un lógico error de CRC al leer, pero los datos leídos son

Page 302: PCA, PS2 ,IBM y AT

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

correctos. La figura 12.6.5.4 constituye un ejemplo de formateo: en un disquete de 360K se colocan 25sectores de 128 bytes con un GAP 3 de 50 bytes, rellenándolos al formatear con el byte 65 (41h, códigoASCII de la A). Teniendo en cuenta los 62 bytes que el FDC añade entre sectores en MFM,(128+62+50)*25=6000, por debajo del límite de 6250 en este tipo de disquetes. Los 4 bytes del sector 6resultan modificados para asignarle un tamaño 1 (256 bytes), aunque el sector es realmente de 128 bytes. Laposterior lectura de ID’s demuestra cómo ha quedado la pista, si bien sólo se pueden ver en una pantalla losID de 21 sectores. En la figura 12.6.5.5 se intenta leer dicho sector y, pese al error de CRC, resulta evidenteque es bien leído (junto con todo lo que va detrás). La última línea del volcado hexadecimal es el inicio delsiguiente sector de la pista. El lector puede verificar que el esquema del final del apartado 12.6.1 es rigurosay milimétricamente cierto: todos los GAPs, ID y bytes introducidos por el FDC entre sectores aparecenclaramente reflejados en la figura. Por supuesto, una posterior escritura del sector 6 pisaría el 7. De ahí que,anécdotas a parte, no suele resultar muy útil generalmente hacer este tipo de maniobras... ¿o tal vez si?.

La función mostrar_resultados() es invocada desde las anteriores, con objeto de leer los 7 bytes quedevuelve el FDC al término de los principales comandos e imprimirles en pantalla. La funciónmostrar_sector() enseña en pantalla el volcado hexadecimal del buffer donde se leen los sectores, en páginasde 256 bytes, teniendo en cuenta el tamaño de los mismos y permitiendo cierta movilidad.

La función motor_on() arranca el motor de la unidad si aún no estaba en marcha, ajustando al valormáximo la variable que indica cuándo se detendrá, con objeto de evitarlo en lo posible. Al menos estarágirando durante 14 segundos en el peor de los casos. La función motor_off() ajusta dicha variable para queel motor se pare en unos 3 segundos. La función outfdc() envía bytes al FDC pero sin esperar más de 440ms en caso de que éste, por cualquier error, no esté dispuesto a recibirlos. Su recíproca infdc() lee un bytedel FDC considerando un fracaso la operación si éste no responde en menos de 440 ms (en estos casosdevuelve un valor negativo para que la función que llama advierta el error). La función esperar_int() ya fuecomentada anteriormente. Por último, la función prepara_dma() programa el 8237 para transferir el númerode bytes indicado, en el modo apropiado (lectura/escritura) y en la dirección del buffer empleado.

/********************************************************************** ** 765DEBUG 3.1 - Programa de análisis avanzado a bajo nivel de ** los disquetes, programando el 765 y el 8237. ** ** Compilar en Turbo C 2.0 o Borland C (modelo Large). ** **********************************************************************/

#include <dos.h>#include <alloc.h>#include <conio.h>

#define SMAX 32768L /* mayor sector soportado por el programa */

#define SELECT 1#define RECALIBRAR 2#define SEEK 3#define LEERIDS 4#define LEER 5#define ESCRIBIR 6#define FORMATEAR 7#define SALIR 8

#define FDCDATA 0x3F5 /* registro de datos del 765 */#define FDCSTATUS 0x3F4 /* registro principal de estado del 765 */#define ODIGITAL 0x3F2 /* registro de salida digital */#define CONTROL 0x3F7 /* registro de control del disquete */

int menu(), infdc();void reservar_memoria(), seleccionar(), adios(), recalibrar(),

posicionar(), leer_sector(), escribir_sector(),formatear_pista(), editar_tabla_fmt(), leer_id(),mostrar_resultados(), mostrar_sector(), motor_on(),motor_off(), outfdc(), esperar_int(), prepara_dma();

void main()

unsigned char far *buffer; /* buffer para sector de hasta 4 Kb */int unidad=0, vunidad=0, mf_mfm=1, cabezal=0, cilindro=0;

outportb (CONTROL, vunidad); /* velocidad por defecto */reservar_memoria (&buffer);

for (;;)switch (menu (unidad, vunidad, &mf_mfm, cilindro, &cabezal)) case SELECT: seleccionar (&unidad, &vunidad); break;case RECALIBRAR: recalibrar (unidad,&cabezal,&cilindro); break;case SEEK: posicionar (unidad, cabezal, vunidad,

&cilindro); break;case LEER: leer_sector (unidad, mf_mfm, cabezal,

cilindro, buffer); break;case ESCRIBIR: escribir_sector (unidad, mf_mfm, cabezal,

cilindro, buffer); break;case FORMATEAR: formatear_pista (unidad, mf_mfm, cabezal,

cilindro, buffer); break;case LEERIDS: leer_id (unidad, mf_mfm, cabezal); break;

case SALIR: adios(); break;

void reservar_memoria (unsigned char far **buffer)

unsigned long dir;

if ((*buffer=farmalloc(SMAX<<1))==NULL) printf("\nMemoria insuficiente\n");exit(1);

dir = ((unsigned long) FP_SEG(*buffer) <<4) + FP_OFF(*buffer);if ( (dir>>16) != ( (dir+SMAX) >> 16) )

*buffer+=SMAX; /* evitar buffer entre dos páginas de DMA */

int menu (unidad, vunidad, mf_mfm, cilindro, cabezal)int unidad, vunidad, *mf_mfm, cilindro, *cabezal;

int opc, opcion;

clrscr();puts("765DEBUG 3.1 - UTILIDAD PARA ANALISIS AVANZADO A BAJO NIVEL DE

DISQUETES.");puts(" Programación directa del controlador NEC765 y

el DMA 8237.");puts(" Funcionamiento probado bajo sistemas PC XT, AT,

386 y 486.");puts(" Soporte para disquetes de 360K, 720K, 1.2M,

1.44M y 2.88M.");puts("");puts(" (C) 1992, 1993, 1994 - Ciriaco García

de Celis.");puts("");puts("");puts(" F2 - Seleccionar unidad/densidad y

resetear.");puts(" F3 - Recalibrar cabezal (necesario tras

F2).");puts("");puts(" F4 - Cambiar de cabezal.");puts(" F5 - Posicionar cabezal.");puts(" F6 - Leer ID’s.");puts(" F7 - Leer sector.");puts(" F8 - Escribir sector.");puts(" F9 - Formatear pista.");puts(" F10 - Conmutar MF/MFM.");puts(" ESC - Salir");gotoxy(7,25);cputs("Elige una opción:");

while (kbhit()) getch(); opcion=0;do

gotoxy(18, 22);printf("Unidad %c: %4d Kbit/seg en %s - Cilindro %2d y Cabezal %d",

unidad+’A’, !vunidad?500:vunidad==1?300:vunidad==2?250:1000,

Page 303: PCA, PS2 ,IBM y AT

303EL HARDWARE DE APOYO AL MICROPROCESADOR

!*mf_mfm?"MF ":"MFM", cilindro, *cabezal);gotoxy (25, 25);opc=getch(); if (!opc) opc=getch();switch (opc) case 60: opcion=SELECT; break;case 61: opcion=RECALIBRAR; break;case 62: *cabezal^=1; break;case 63: opcion=SEEK; break;case 64: opcion=LEERIDS; break;case 65: opcion=LEER; break;case 66: opcion=ESCRIBIR; break;case 67: opcion=FORMATEAR; break;case 68: *mf_mfm^=1; break;case 27: opcion=SALIR; break; /* ESC */case 0x2D: opcion=SALIR; break; /* ALT-X */default: opcion=0; break;

while (!opcion);

return (opcion);

void seleccionar (int *unidad, int *vunidad)clrscr();printf("\n\n\n\n\n\n\n\n\t\t\t Unidad (A, B,...): ");do *unidad=(getch() | 0x20)-’a’; while ((*unidad>3) || (*unidad<0));printf("%c\n\n\n", *unidad+’A’);

printf("\tDensidades:\t 360K en unidad 360K: 250 Kbit/seg -> 2\n");printf("\t\t\t 360K en unidad 1.2M: 300 Kbit/seg -> 1\n");printf("\t\t\t 1.2M: 500 Kbit/seg -> 0\n");printf("\t\t\t 720K: 250 Kbit/seg -> 2\n");printf("\t\t\t 1.44M: 500 Kbit/seg -> 0\n");printf("\t\t\t 2.88M: 1000 Kbit/seg -> 3\n");

printf("\n\t\tElige densidad: ");

do *vunidad=getch()-’0’; while ((*vunidad<0) || (*vunidad>3));

outportb (CONTROL, *vunidad);

/**** Modo DMA, arrancar motor y reset ****/

outportb (ODIGITAL, 1<<(*unidad+4) | *unidad | 8); /* reset */delay (1);outportb (ODIGITAL, 1<<(*unidad+4) | *unidad | 8+4); /* fin reset */

esperar_int(); /* esperar interrupción */

outfdc (8); /* comando ’leer estado de interrupciones’ */(void) infdc(); /* leer y desechar resultado */(void) infdc();

/**** Enviar comando ’Specify’ ****/

outfdc (3); /* comando */if (*vunidad==3)

outfdc (0xAF); /* tiempo de acceso pista-pista y head unload */else if (!*vunidad)outfdc (0xBF);

elseoutfdc (0xDF);

outfdc (2); /* head load time = 1; modo DMA */

void recalibrar (int unidad, int *cabezal, int *cilindro)int recal, res, pis;

clrscr();printf("\n\n\n\n\n\n\n\n\n\n\n\t\t\t\tRecalibrando...");

*cilindro=0;

motor_on (unidad); /* asegurar que el motor está en marcha */

/**** Recalibrar hasta dos veces si es preciso ****/

for (recal=0; recal<2; recal++)

outfdc (7); /* comando de recalibrado */outfdc (*cabezal << 2 | unidad); /* byte 1 de dicho comando */

esperar_int(); /* esperar interrupción */

outfdc (8); /* comando ’leer estado de interrupciones’ */

res=infdc(); /* leer resultado */pis=infdc();

printf("\n\n\t\t\t ST0=0x%02X - Pista=%d", res, pis);

if (!((res ^ 32) & (0xF0))) break; /* resultado correcto */

motor_off(); delay (1500);

void posicionar (unidad, cabezal, vunidad, cilindro)int *cilindro;int r;

clrscr();printf("\n\n\n\n\n\n\n\n\n\n\n\t\t\t Cilindro (0..N): ");scanf("%d", cilindro);

if ((vunidad==1) && cilindro) printf("\n\t\t¿Es disco 5¼-360K en unidad 1.2M-HD? (S/N): ");r=((getch() | 0x20)==’s’)+1;printf("%c\n", r==1?’N’:’S’);

elser=1;

motor_on (unidad); /* asegurar que el motor está en marcha */

/**** Desplazar cabezal hasta la pista ****/

outfdc (0xF); /* comando ’Seek’ */outfdc (cabezal << 2 | unidad); /* byte 1 de dicho comando */outfdc (*cilindro*r);

esperar_int(); /* esperar interrupción */

outfdc (8); /* comando ’leer estado de interrupciones’ */

printf("\n\t\t\t ST0=0x%02X", infdc());printf(" Pista=%d", infdc());

motor_off(); delay (1500);

void leer_sector (unidad, densidad, cabezal, cilindro, buffer)unsigned char far *buffer;

int sector, tsector, t128;long r;

clrscr();printf("Sector a leer: "); scanf("%d", &sector);printf("\n\nTamaño de sector:\n");printf(" 0 -> 1-128 bytes\n");printf(" 1 -> 256 bytes\n");printf(" 2 -> 512 bytes\n");printf(" 3 -> 1024 bytes\n");printf(" 4 -> 2048 bytes\n");printf(" 5 -> 4096 bytes\n");printf("\n Elige: ");do tsector=getch()-’0’; while ((tsector<0) || (tsector>8));printf("%d\n", tsector);if (tsector==0)

printf("\n Concreta el tamaño (1-128): ");scanf("%d", &t128);

for (r=0; r<SMAX; r+=2) buffer[r]=0x5A; buffer[r+1]=0xA5; /* "borrar" el buffer */

motor_on (unidad);

prepara_dma (0x46, 128 << tsector, buffer);

outfdc (0x06 | densidad << 6); /* comando para leer */outfdc (cabezal << 2 | unidad); /* byte 1 de dicho comando */outfdc (cilindro);outfdc (cabezal);outfdc (sector);outfdc (tsector);outfdc (sector);outfdc (1); /* GAP para leer: poco importante */outfdc (t128); /* tamaño si tsector=0 */

esperar_int(); /* esperar interrupción */

mostrar_resultados (&r);

motor_off();

if (r & 0xC0) printf("Error de lectura (el sector puede estar mal leído).\n");printf("Nota: el buffer de lectura contenía el patrón 5AA5.\n");

printf(" Pulsa una tecla para ver el sector [ESC=salir].");if (getch()!=27) mostrar_sector (buffer, tsector, t128);

void escribir_sector (unidad, densidad, cabezal, cilindro, buffer)unsigned char far *buffer;

int r, sector, tsector, t128, gap, pokete;long i;

clrscr();printf("Sector a escribir: "); scanf("%d", &sector);printf("\n\nTamaño de sector:\n");printf(" 0 -> 1-128 bytes\n");printf(" 1 -> 256 bytes\n");printf(" 2 -> 512 bytes\n");printf(" 3 -> 1024 bytes\n");printf(" 4 -> 2048 bytes\n");printf(" 5 -> 4096 bytes\n");printf("\n Elige: ");do tsector=getch()-’0’; while ((tsector<0) || (tsector>8));printf("%d\n", tsector);if (tsector==0)

printf("\n Concreta el tamaño (1-128): ");scanf("%d", &t128);

printf("\nValor para el GAP (1/2 de el de formateo): ");scanf("%d", &gap);printf("\nByte para inicializar sector: "); scanf("%d", &pokete);

for (i=0; i<SMAX; i++) buffer[i]=pokete; /* llenar sector */

motor_on (unidad);

prepara_dma (0x4A, 128 << tsector, buffer);

outfdc (0x05 | densidad << 6); /* comando para escribir */outfdc (cabezal << 2 | unidad); /* byte 1 de dicho comando */outfdc (cilindro);outfdc (cabezal);outfdc (sector);outfdc (tsector);outfdc (sector);outfdc (gap);outfdc (t128); /* tamaño si tsector=0 */

esperar_int(); /* esperar interrupción */

mostrar_resultados (&r);

motor_off();

if ((r & 0xC0)!=0) printf ("Error de escritura. Pulsa una tecla.");getch();

else printf ("Escritura correcta. Pulsa una tecla.");getch();

void formatear_pista (unidad, densidad, cabezal, cilindro, buffer)unsigned char far *buffer;

Page 304: PCA, PS2 ,IBM y AT

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

int r, tsector, sectores, gap, pokete, i;

clrscr();printf("\n\nTamaño de sector:\n");printf(" 0 -> 128 bytes\n");printf(" 1 -> 256 bytes\n");printf(" 2 -> 512 bytes\n");printf(" 3 -> 1024 bytes\n");printf(" 4 -> 2048 bytes\n");printf(" 5 -> 4096 bytes\n");printf("\n Elige: ");do tsector=getch()-’0’; while ((tsector<0) || (tsector>8));printf("%d\n", tsector);printf("\nNúmero de sectores: "); scanf("%d", &sectores);printf("\nValor para el GAP 3: "); scanf("%d", &gap);printf("\nByte para inicializar sectores: "); scanf("%d", &pokete);

for (i=0; i<sectores; i++) /* tabla propuesta para formatear */buffer[i*4]=cilindro;buffer[i*4+1]=cabezal;buffer[i*4+2]=i+1;buffer[i*4+3]=tsector;

editar_tabla_fmt (buffer, sectores); /* permitir su alteración */

motor_on (unidad);

prepara_dma(0x4A, sectores<<2, buffer);

outfdc (0x0D | densidad <<6); /* comando para formatear */outfdc (cabezal << 2 | unidad); /* byte 1 de dicho comando */outfdc (tsector);outfdc (sectores);outfdc (gap);outfdc (pokete); /* byte de relleno */

esperar_int(); /* esperar interrupción */

mostrar_resultados (&r);

motor_off();

if ((r & 0xC0)!=0) printf ("Error al formatear. Pulsa una tecla.");getch();

else printf ("Formateo correcto. Pulsa una tecla.");getch();

void editar_tabla_fmt (unsigned char far *buffer, int numsect)

int i, opcion, sector, dato;

do clrscr();printf("Puntualizaciones sobre el formateo:\n\n");printf(" He establecido por defecto una tabla con los cuatro\n");printf("bytes que hay que enviar al controlador, por cada uno\n");printf("de los sectores de la pista, que están numerados:\n\n");for (i=0; i<numsect; i++) printf ("%4d", buffer[i*4+2]);printf("\n\n Puedes elegir lo siguiente: \n\n");printf(" 1 - Introducir tú los 4 bytes de un sector.\n");printf(" 2 - Modificar un cierto byte en todos los sectores.\n");printf("ESC - Dejar las cosas como están ahora.\n");printf("\n Elige opción.");

do opcion=getch(); if (!opcion) opcion=getch()<<8;

while (((opcion<’1’) || (opcion>’3’)) && (opcion!=27));

if (opcion==’1’) do printf("\n\nSector a alterar: "); scanf ("%d", &sector);for (i=0; i<numsect; i++) if (buffer[i*4+2]==sector) break;if (buffer[i*4+2]!=sector)

printf("Ese sector no existe. No discutamos ");else printf("Nº Cilindro (anterior=%d): ", buffer[i*4]);scanf ("%d", &dato); buffer[i*4]=(char) dato;printf("Nº cabezal (anterior=%d): ", buffer[i*4+1]);scanf ("%d", &dato); buffer[i*4+1]=(char) dato;printf("Nº sector (anterior=%d): ", buffer[i*4+2]);scanf ("%d", &dato); buffer[i*4+2]=(char) dato;printf("Tamaño sector (anterior=%d): ", buffer[i*4+3]);scanf ("%d", &dato); buffer[i*4+3]=(char) dato;

printf("¿De acuerdo (S/N)?"); while ((getch() | 0x20)!=’s’);

else if (opcion==’2’)

do printf("\n\nCaracterística a cambiar: \n");printf(" (0) Nº Cilindro, (1) Nº cabezal,");printf(" (2) Nº sector, (3) Tamaño de sector: ");opcion=getch();

while ((opcion<’0’) || (opcion>’3’));printf("\n Nuevo valor para todos los sectores: ");scanf ("%d", &dato);for (i=0; i<numsect; i++) buffer[i*4+opcion-’0’]=(char) dato;

while (opcion!=27);clrscr();

void leer_id (unidad, densidad, cabezal)unsigned long tmp[22], acu;int nec[22][7];unsigned i, j, lectura, antlectura, cnth;

do clrscr();printf("\n\n\n\n\n\n\n\n\n\n\n\t\t\t\tLeyendo ID’s...");

motor_on (unidad); /* asegurar que el motor está en marcha */

outportb (0x61, inportb(0x61) & 0xFD | 1); /* inhibir sonido */outportb (0x43, 0xB4); /* contador 2 */outportb (0x42, 0xFF); outportb (0x42, 0xFF); /* cuenta 0xFFFF */

for (i=0; i<22; i++)

outfdc (0x0A | densidad << 6); /* comando ’Leer ID’ */outfdc (cabezal << 2 | unidad); /* byte 1 del comando */

lectura=0xFFFF; cnth=0; /* cuenta inicial */

do /* esperar interrupción */antlectura=lectura;outportb (0x43, 0x80); /* enclavamiento */lectura=inportb(0x42); /* parte baja de la cuenta */lectura|=inportb(0x42) << 8; /* parte alta de la cuenta */if (lectura>antlectura) if (cnth++>8) break; /* timeout */

while (!(peekb(0x40, 0x3E) & 0x80));

pokeb (0x40, 0x3E, peekb (0x40, 0x3E) & 0x7F); /* reset int. */

outportb (0x61, inportb(0x61) & 0xFE); /* bajar GATE */outportb (0x61, inportb(0x61) | 1); /* subir GATE */

if (kbhit()) if (getch()==27) goto fin_ids; /* tecla ESC */

for (j=0; j<7; j++) nec[i][j]=infdc();

if (cnth<9)tmp[i]=cnth*65535L + (65535-lectura);

else tmp[i]=0L; /* error */nec[i][0]=-1; /* no informar */pokeb (0x40, 0x40, 0xFF); /* asegurar motor en marcha */ /* porque probablemente se está perdiendo mucho tiempo */

outportb (0x61, inportb(0x61) & 0xFC);

clrscr();printf("\r Longitud (ms) ");printf(" Sector Tamaño Cilindro Cabeza ST0 ST1 ST2 \n");printf(" - ");printf(" \n");acu=0;for (j=0; j<21; j++) /* rechazar primera muestra */

if (tmp[j+1] && tmp[j]) acu+=tmp[j+1];printf(" [%8.2f]%7.2f ", acu/1193.18, tmp[j+1]/1193.18);

elseprintf(" N.D. ");

if (nec[j][0]>=0) printf(" %3d ", nec[j][5]);printf("%5d (%3d)", nec[j][6]<9?128<<nec[j][6]:0, nec[j][6]);printf(" %4d %4d 0x%02X 0x%02X 0x%02X\n", nec[j][3],

nec[j][4], nec[j][0], nec[j][1], nec[j][2]);else

printf(" ?? ?? ??");printf(" ?? ?? ?? ??\n");

printf("\n\t\t Una tecla para leer más ID’s [ESC=salir].");

while (getch()!=27);

fin_ids: motor_off();

void adios()

outportb (CONTROL, peekb(0x40, 0x8B) >> 6); /* velocidad normal */clrscr(); printf("Fin de 765DEBUG\n");exit (0);

void mostrar_resultados (int *res)

printf("\nResultado de la operación:\n\n");*res=infdc();if (*res>=0)

printf(" [ST0=0x%02X] ", *res);printf("[ST1=0x%02X] ", infdc());printf("[ST2=0x%02X]\n", infdc());printf(" [Cilindro %d] ", infdc());printf("[Cabezal %d] ", infdc());printf("[Sector %d] ", infdc());printf("[Tamaño %d]\n\n", infdc());

else printf(" [ST0=??] ¡El FDC no responde!\n\n");

void mostrar_sector (unsigned char far *buffer, int tamano, int tt)

unsigned char far *p;int vv, i, j, k, tecla;

vv = (1 << tamano) >> 1; if (!vv) vv++;if (tamano) tt=256;

i=0;do

p=&buffer[i*256];clrscr(); printf("\n\n\n");for (j=0; j<tt; j+=16)

printf(" %04X: ", p-buffer);for (k=0; k<8; k++) printf("%02X ", *p++);printf("- ");for (k=8; k<16; k++) printf("%02X ", *p++); p-=16;printf(" ");for (k=0; k<16; k++)

if (*p<’ ’) printf("."); else printf("%c", *p);p++;

printf("\n");

printf("\n\t\t Bytes %04d-%04d del sector (%d/%d)\n",i*tt, (i+1)*tt-1, i+1, vv);

printf("\t\t Utiliza los cursores [ESC=salir]");do

tecla=getch();while (tecla && (tecla!=27) && (tecla!=32) && (tecla!=13));if ((tecla==32) || (tecla==13))

i++; if (i>=vv) i=0;

if (!tecla) tecla=getch();if (tecla==0x48) i--; /* cursor arriba */if (tecla==0x50) i++; /* cursor abajo */

Page 305: PCA, PS2 ,IBM y AT

305EL HARDWARE DE APOYO AL MICROPROCESADOR

if (tecla==0x47) i=0; /* Inicio */if (tecla==0x4f) i=vv-1; /* Fin */if (tecla==0x49) i-=2; /* Re Pág */if (tecla==0x51) i+=2; /* Av pág */if (i<0) i=0; if (i>=vv) i=vv-1;

while (tecla!=27);

void motor_on (unidad)int i;

/**** Evitar que la BIOS pare el motor (al menos en 14") ****/

pokeb(0x40,0x40,0xFF);

/**** Si no lo está, ponerlo en marcha y esperar 1 segundo ****/

if (((i=peekb(0x40, 0x3F)) & (1 << unidad))==0) outportb (ODIGITAL, 1<<(unidad+4) | 4+8 | unidad);pokeb (0x40, 0x3F, i | (1 << unidad));delay (1000);pokeb(0x40,0x40,0xFF);

void motor_off()

pokeb(0x40,0x40,55); /* la BIOS lo detendrá en 55/18.2 segundos */

void outfdc (unsigned char dato) /* enviar byte al FDC */ /* no esperando más de 440 ms */

int t, i=0, rd;

do i++; t=peekb(0x40, 0x6C);while ((t==peekb(0x40, 0x6C)) && ((rd=inportb(FDCSTATUS)>>7)==0));

while ((i<8) && !rd);

if (rd) outportb (FDCDATA, dato);

int infdc (void) /* leer byte del FDC */ /* no esperando más de 440 ms */int t, i=0, rd;

do i++; t=peekb(0x40, 0x6C);while ((t==peekb(0x40, 0x6C)) && ((rd=inportb(FDCSTATUS)>>7)==0));

while ((i<8) && !rd);

if (rd) return (inportb (FDCDATA)); else return (-1); /* fallo */

void esperar_int (void) /* Esperar interrupción no más de 2 seg. */

int t, i=0;

do i++; t=peekb(0x40, 0x6C);while ((t==peekb(0x40, 0x6C)) && (!(peekb(0x40, 0x3E) & 0x80)));

while ((i<37) && (!(peekb(0x40, 0x3E) & 0x80)));

pokeb (0x40, 0x3E, peekb (0x40, 0x3E) & 0x7F);

void prepara_dma (rmodo, bytes, buffer)unsigned rmodo, bytes;unsigned char far *buffer;

unsigned long dir;unsigned dmapag, dmaoff;

dir = ((unsigned long) FP_SEG(buffer) <<4) + FP_OFF(buffer);dmapag = dir >> 16; dmaoff = dir & 0xFFFF;

outportb (0x81, dmapag); /* registro de página del canal 2 */outportb (0xB, rmodo); /* programar registro de modo */outportb (0xC, 0); /* clear first/last flip-flop */outportb (4,dmaoff & 0xFF); /* dirección base (parte baja) */outportb (4,dmaoff >> 8); /* dirección base (parte alta) */outportb (5,(bytes-1) % 256); /* nº de bytes menos 1 (parte baja) */outportb (5,(bytes-1) / 256); /* nº de bytes menos 1 (parte alta) */outportb (0xA, 2); /* habilitar canal 2 */

12.6.6 - LECTURA Y ESCRITURA DE SECTORES DE DISCO SIN DMA.

Si bien lo normal es emplear el DMA para realizar los accesos a disco, ello no es estrictamentenecesario (excepto en los auténticos PS/2): generalmente también se puede acceder enviando directamentelos bytes al FDC, aunque sería más útil emplear el DMA (la CPU no tendría tiempos muertos de espera paramover los bytes). Realmente, bajo DOS da lo mismo acceder con el DMA que sin el, ya que aún cuando seemplea el DMA ¡la pobre CPU se queda esperando a que llegue la interrupción que indica el final de laoperación!. La única ventaja real de utilizar el DMA, que motivó su uso por parte de los programadores deIBM, es que el contador de hora de la BIOS sigue avanzando (y el reloj no se atrasa), mientras que sin elDMA se pararía al tener que inhibir las interrupciones en el momento crítico de la transferencia del sector,con objeto de no perder datos. En otros sistemas operativos multitarea, el DMA permite a la CPU continuartrabajando (perdiendo sólo los ciclos estrictamente necesarios para la transferencia) a la par que es realizadala operación de disco: aunque el rendimiento global del sistema se degrada durante la operación, al menosno se detienen todos los procesos.

El siguiente programa de ejemplo, realizado íntegramente en ensamblador, permite leer y escribirsectores de disco aislados en el formato MFM habitual. Soporta las unidades A: y B:, así como discos ydisqueteras de todos los formatos y densidades -incluidos los no estándar-. Se preguntan todos y cada unode los parámetros necesarios, dando algunas pautas para ayudar. Es importante responder correctamente,aunque el control de errores suele recuperar los fallos, sin dejar bloqueado el ordenador, en un plazo detiempo razonable. Esta utilidad se basa en un menú principal donde se tiene acceso a las diversas opciones,que desembocan en las rutinas de bajo nivel que controlan el disco. No describiremos las rutinas encargadasde tomar datos del teclado ni tampoco las de impresión en pantalla, bastante obvias. Sin embargo, daremosun ligero repaso a las subrutinas encargadas de controlar el disco.

El procedimiento init_drv enciende el motor de la disquetera y resetea el FDC a través de lasubrutina reset_drv, esperando después a que el motor alcance un régimen de rotación adecuado. Enreset_drv se selecciona además el modo NO DMA en el registro de salida digital, se espera por lainterrupción que indica el fin del reset y se envía el comando specify al FDC; también se establece lavelocidad de transferencia apropiada para el tipo de disquete a ser accedido. El procedimiento recalibrarejecuta dicho comando del FDC hasta un máximo de dos veces en caso de fallo, entre otros motivos paraprevenir que el cabezal estuviera inicialmente en una pista superior a la 77. Tanto en este procedimiento comoen el seek_drv se detecta el inicio de la fase de resultados esperando la pertinente interrupción de disco (en

Page 306: PCA, PS2 ,IBM y AT

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

la rutina espera_int). Debido a que las interrupciones no llegan cuando está activo el modo NO DMA en elregistro de salida digital, por algún oscuro motivo que desconozco, es preciso establecer momentáneamenteel modo DMA a través del bit 3 de dicho registro (rutina habilita_int) y volverlo a desactivar una vez quellega la interrupción; realmente, aún seleccionando esta modalidad, el DMA no será empleado ya que no seutiliza en los comandos de recalibración ni en el de posicionamiento del cabezal. En esta última rutina setiene en cuenta el caso especial que supone un disquete de 40 pistas en una unidad de 80, multiplicándoseentonces por 2 el número de cilindro antes de enviarlo al FDC.

La rutina sector_io es la encargada de leer y escribir los sectores de disco. Tras enviar el comandoal FDC, se espera que éste encuentre el sector y seguidamente se pasa a leer/escribir el mismo directamente,aunque en lugar de emplear las rutinas E/S habituales (fdc_read y fdc_write) se realiza el proceso de maneradirecta para acelerarlo. Más que para acelerarlo, para que no nos pille: la velocidad es aquí crítica (el procesose realiza con las interrupciones apagadas) ya que cada 16-32 microsegundos hay que transferir un byte entrela CPU y el FDC y dormirse en los laureles supondría un error irrecuperable. Si se está escribiendo un sectory se produce un fallo, es fácil detectarlo (el FDC deja de recibir datos e intenta enviar los bytes de la fasede resultados) pero en la lectura de sectores serían leídos dichos resultados confundidos como datos delsector, aunque al terminar el comando (y bajar el bit CB del registro de estado) se detectaría afortunadamenteel final de la operación y se podría suponer que los últimos 7 bytes leídos no eran del sector sino la fase deresultados. En general, si el usuario ha indicado bien todos los parámetros y el disquete no está defectuoso,no habrá problemas. Estas rutinas de lectura de sectores no están diseñadas de manera tolerante a fallos, yaque realizan saltos condicionales comprobando los bits del registro de estado, que en caso de quedarsecongelados y no cambiar supondrían un cuelgue del sistema. Sin embargo, añadir controles de timeoutalargaría los tiempos de ejecución y podría provocar, si no se tiene cuidado, que los PC/XT más lentos nofueran bastante potentes para acceder al disco con la suficiente rapidez. Además, la mejor técnica paracontrolar los timeout es, indiscutiblemente, la monitorización de los ciclos de refresco de la memoriadinámica de los AT (ese bit del puerto 61h que cambia 66287 veces por segundo): en los PC/XT sería máscomplicado...

Por último, las rutinas fdc_read y fdc_write se encargan de la comunicación CPU-FDC en ambossentidos, aunque aquí sí se han establecido unos rudimentarios controles de timeout, de esos que tardan mástiempo en recuperar el control en las máquinas más lentas. De ahí que estas subrutinas no sean empleadasdesde sector_io, por razones de velocidad.

Acceder a disco sin DMA es más incómodo y problemático que hacerlo a través del DMA, y noofrece absolutamente ninguna ventaja adicional, a no ser que el 8237 esté averiado en el ordenador. De hecho,yo personalmente dejé de utilizar durante algún tiempo el DMA en los accesos de disco (me hice uncontrolador especial que además me ayudó a subir nota en una asignatura), creyendo que los errores en latransferencia de datos en mis disqueteras se debían a este integrado. Sin embargo, finalmente averigué quela causa estaba en los SIPPs de memoria un tanto flojos (por fortuna, resulta que un amigo mío sí teníaestropeado el DMA de verdad en las operaciones de escritura, y ese driver le vino muy bien para poderescribir en sus disquetes). Anécdotas aparte, este programa es meramente educativo y no un modelo a seguir.

; ********************************************************************; * *; * 765NODMA.ASM 2.0 - Programa de demostración de acceso a *; * bajo nivel al disquete sin emplear DMA. *; * *; ********************************************************************

; ************ Macros de propósito general.

XPUSH MACRO regmem ; apilar lista de registrosIRP rm, <regmem>PUSH rm

ENDMENDM

XPOP MACRO regmem ; desapilar lista de registrosIRP rm, <regmem>POP rm

ENDMENDM

; ************ Programa principal.

fdc_test SEGMENTASSUME CS:fdc_test, DS:fdc_test

ORG 100h

main PROC

CALL menu ; opcionesDEC ALJZ leer ; opción de leer sectorDEC ALJZ escribir ; opción de escribirloLEA DX,adios_txtCALL print ; opción de salir:MOV AX,40hMOV DS,AXMOV AL,DS:[8Bh] ; velocidad previa al programaMOV CL,6SHR AL,CL ; pasarla a bits 0..1MOV DX,3F7hOUT DX,AL ; restaurar velocidad previaINT 20h

leer: LEA DX,cls_txtCALL print ; borrar pantallaLEA DX,lectura_txtCALL print ; mensaje inicialLEA DX,aviso_txtCALL printCALL pide_sector ; pedir pista, cabeza, ...MOV orden,F_READCALL init_drvCALL recalibrarJC falloCALL seek_drvJC falloLEA DI,buffer

Page 307: PCA, PS2 ,IBM y AT

307EL HARDWARE DE APOYO AL MICROPROCESADOR

CALL sector_io ; cargar dicho sectorJC falloCALL imprime_sector ; mostrar su contenidoJMP main

escribir: LEA DX,cls_txtCALL print ; limpiar pantallaLEA DX,escritura_txtCALL print ; mensaje inicialLEA DX,aviso_txtCALL printCALL pide_sector ; pedir pista, cabeza, ...CALL pide_relleno ; pedir byte de rellenoMOV orden,F_WRITECALL init_drvCALL recalibrarJC falloCALL seek_drvJC falloLEA DI,bufferCALL sector_io ; grabar dicho sectorJC falloJMP main

fallo: LEA DX,fallo_txt ; mensaje de errorCALL printCALL getchJMP main

main ENDP

; ************ Subrutinas de apoyo

menu PROCLEA DX,cls_txtCALL printLEA DX,opciones_txt ; texto del menúCALL print

espera_opc: CALL getchCMP AL,’1’JE opc_ok ; elegida opción 1CMP AL,’2’JE opc_ok ; elegida opción 2CMP AL,27JE opc3_ok ; ESC (opción ’3’)CMP AX,2D00hJNE espera_opc ; no es ALT-X

opc3_ok: MOV AL,’3’opc_ok: SUB AL,’0’

RETmenu ENDP

; ------------ Solicitar información del sector a ser accedido.

pide_sector PROCLEA DX,unidad_txtCALL input_AL ; pedir unidadMOV unidad,ALLEA DX,vunidad_txtCALL input_AL ; seleccionar velocidadMOV vunidad,ALLEA DX,tdisco_txtCALL input_AL ; problema de 40/80 pistasMOV tunidad,ALLEA DX,tamano_txtCALL input_AL ; preguntar tamaño sectorMOV tsector,ALLEA DX,gap_rw_txtCALL input_AL ; preguntar tamaño sectorMOV gap,ALLEA DX,pista_txtCALL input_AL ; pedir pistaMOV cilindro,ALLEA DX,cabeza_txtCALL input_AL ; pedir cabezaMOV cabezal,ALLEA DX,sector_txtCALL input_AL ; pedir sectorMOV sector_ini,ALMOV sector_fin,ALMOV CL,tsectorMOV CH,0INC CX ; CX: 1-128 bytes, 2-256, ...MOV AX,64

computab: SHL AX,1LOOP computabMOV bsector,AX ; bytes/sectorMOV AL,cabezalSHL AL,1SHL AL,1OR AL,unidadMOV byte1,AL ; byte 1 común a muchas órdenesRET

pide_sector ENDP

; ------------ Imprimir sector en hex/ASCII en bloques de 256 bytes.

imprime_sector PROCLEA BX,bufferMOV AX,bsectorMOV CL,AHMOV CH,0 ; CX secciones de 256 bytesAND CX,CXJNZ otra_mitadINC CX ; al menos imprimir una vez

otra_mitad: PUSH CXLEA DX,cls_txtCALL printMOV CX,16 ; 16 líneas

otra_linea: PUSH CXMOV CX,16 ; de 16 caracteres

pr_hexa: MOV AL,’ ’CALL printALMOV AL,[BX]INC BXCALL print8hexLOOP pr_hexaMOV AL,’ ’CALL printALCALL printALSUB BX,16MOV CX,16

pr_ascii: MOV AL,[BX]INC BXCMP AL,’ ’JAE ascii_okMOV AL,’.’

ascii_ok: CALL printALLOOP pr_asciiMOV AL,13CALL printAL

MOV AL,10CALL printALPOP CXLOOP otra_lineaLEA DX,ptecla_txtCALL printCALL getchPOP CXLOOP otra_mitadRET

imprime_sector ENDP

; ------------ Pedir byte para llenar el sector a grabar.

pide_relleno PROCLEA DX,relleno_txtCALL input_ALLEA DI,bufferMOV CX,bsector ; tamaño de sector en bytesCLDREP STOSBRET

pide_relleno ENDP

; ------------ Imprimir cadena en DS:DX terminada en un ’$’.

print PROCPUSH AXMOV AH,9 ; función de impresiónINT 21h ; llamar al sistemaPOP AXRET

print ENDP

; ------------ Imprimir carácter en AL

printAL PROCPUSH AXPUSH DX ; registros usados preservadosMOV AH,2 ; función de impresión del DOSMOV DL,AL ; carácter a imprimirINT 21h ; llamar al sistemaPOP DXPOP AX ; recuperar registrosRET ; retornar

printAL ENDP

; ------------ Imprimir carácter hexadecimal (AL).

print4hex PROCPUSH AX ; preservar AXADD AL,’0’ ; pasar binario a ASCIICMP AL,’9’JBE no_sup9 ; no es letraADD AL,’A’-’9’-1 ; lo es

no_sup9: CALL printAL ; imprimir dígito hexadecimalPOP AX ; restaurar AXRET

print4hex ENDP

; ------------ Imprimir byte hexadecimal en AL.

print8hex PROCPUSH CXPUSH AXMOV CL,4SHR AL,CL ; pasar bits 4..7 a 0..3CALL print4hex ; imprimir nibble más

significativoPOP AX ; restaurar ALPUSH AX ; y preservarlo de nuevoAND AL,1111b ; dejar nibble menos significativoCALL print4hex ; e imprimirloPOP AXPOP CXRET

print8hex ENDP

; ------------ Esperar pulsación de tecla y devolverla en AX.

getch PROCMOV AH,1 ; esperar carácter (algunosINT 16h ; KEYB de XT se cuelganJZ getch ; al usar directamente elMOV AH,0 ; servicio 0).INT 16hRET

getch ENDP

; ------------ Leer nº decimal de hasta 3 dígitos y devolverlo en AL.

input_AL PROCPUSH BXPUSH CXPUSH DXPUSH AX

pedir_dato: CALL printMOV AH,0Ah ; función de entrada (teclado)LEA DX,bufferMOV BX,DXMOV WORD PTR [BX],4 ; (inicializar dos variables)INT 21h ; llamar al sistemaMOV CL,[BX+1]XOR CH,CH ; número de caracteres pulsadosPOP AXPOP DXPUSH DXPUSH AXJCXZ pedir_dato ; se pulsó RETURN: reiterarXOR DX,DX

gen_num: MOV AX,10MUL DXMOV DX,AXMOV AL,[BX+2]SUB AL,’0’INC BXXOR AH,AHADD DX,AXLOOP gen_num ; conversión ASCII -> binarioPOP AXMOV AL,DL ; resultadoPOP DXPOP CXPOP BXRET

input_AL ENDP

; ------------ Encender motor y esperar a que tome cierta velocidad.

Page 308: PCA, PS2 ,IBM y AT

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

init_drv PROCPUSH CXCALL reset_drvMOV CX,18CALL retardo ; esperar aceleración discoPOP CXRET

init_drv ENDP

; ------------ Establecer modalidad de operación del controlador; y asegurar que el motor está en marcha.

reset_drv PROCXPUSH <DS, AX, BX, CX, DX>PUSH DSMOV BX,40h ; engañar al BIOS paraMOV DS,BX ; que no pare el motor alMOV BYTE PTR DS:[BX],255 ; menos durante 14 seg.POP DSMOV DX,3F2h ; registro de salida digitalMOV CL,unidadADD CL,4MOV AL,1SHL AL,CL ; colocar bit del motorOR AL,unidad ; seleccionar unidad; NO DMAOUT DX,AL ; resetOR AL,00000100bJMP SHORT $+2OUT DX,AL ; fin del resetCALL espera_intMOV AL,3CALL fdc_write ; Comando ’Specify’:MOV AL,0DFhCALL fdc_writeMOV AL,3 ; modo NO DMACALL fdc_write ; head load y modoPUSH DSMOV BX,40hMOV DS,BXMOV CL,CS:unidadMOV AL,1SHL AL,CLAND BYTE PTR DS:[BX-1],11110000bOR DS:[BX-1],AL ; indicar motor ONPOP DSMOV DX,3F7hMOV AL,vunidad ; velocidad de transferenciaOUT DX,ALXPOP <DX, CX, BX, AX, DS>RET

reset_drv ENDP

; ------------ Recalibrar la unidad (si hay error se intenta otra vez; para el caso de que deba moverse más de 77 pistas).

recalibrar PROCXPUSH <AX, CX>MOV CX,2 ; dos veces como mucho

recalibra: CALL habilita_intMOV AL,7CALL fdc_write ; comando de ’recalibrado’JZ fallo_recalMOV AL,byte1CALL fdc_write ; enviar HD, US1, US0JZ fallo_recalCALL espera_int ; esperar interrupciónJZ fallo_recalMOV AL,8CALL fdc_write ; comando ’leer estado int...’JZ fallo_recalCALL fdc_read ; leer registro de estado 0JZ fallo_recalMOV AH,ALCALL fdc_read ; leer cilindro actualXOR AH,00100000b ; bajar bit de ’seek end’TEST AH,11110000b ; comprobar resultado y ST0JNZ fallo_recal ; sin ’seek end’ o sin TRK0XPOP <CX, AX>CLC ; Ok.RET

fallo_recal: LOOP recalibra ; reintentar comandoXPOP <CX, AX>STC ; condición de falloRET

recalibrar ENDP

; ------------ Llevar el cabezal a la pista indicada.

seek_drv PROCXPUSH <AX, CX>CLICALL habilita_int ; usar interrupcionesMOV AL,0FhCALL fdc_write ; comando ’seek’JZ fallo_seekMOV AL,byte1CALL fdc_write ; enviar HD, US1, US0MOV AL,cilindroCMP tunidad,0JE pista_ok ; es unidad de doble densidadCMP vunidad,1 ; es de alta:JNE pista_ok ; no es disco 5¼-360SHL AL,1 ; cilindro=cilindro*2

pista_ok: CALL fdc_write ; enviar cilindroCALL espera_int ; esperar interrupciónCLIMOV AL,8CALL fdc_write ; comando ’leer estado int...’JZ fallo_seekCALL fdc_read ; leer registro de estado 0CALL fdc_read ; leer cilindro actualSTIMOV CX,1CALL retardo ; esperar asentamiento cabezalXPOP <CX, AX>CLC ; retornar con éxitoRET

fallo_seek: STIXPOP <CX, AX>STC ; retornar indicando falloRET

seek_drv ENDP

; ------------ Habilitar interrupción disquete (y modo DMA).

habilita_int PROCXPUSH <AX, CX, DX>

MOV CL,unidadADD CL,4MOV AL,1SHL AL,CL ; colocar bit del motorOR AL,unidad ; seleccionar unidadOR AL,00000100b ; no hacer resetMOV DX,3F2hOUT DX,ALOR AL,00001000b ; modo DMAJMP SHORT $+2OUT DX,ALXPOP <DX, CX, AX>RET

habilita_int ENDP

; ------------ Esperar interrupción de disquete y volver de nuevo al; modo NO DMA (lo que inhibe interrupción disquete).

espera_int PROCSTIXPUSH <AX, CX>XPUSH <DS, 40h>POP DSMOV AH,0FFh

esperar_int: CMP AL,DS:[6Ch]JE mira_intMOV AL,DS:[6Ch]INC AHCMP AH,37 ; no esperar más de 2 segundosJA fin_espera ; timeout

mira_int: TEST BYTE PTR DS:[3Eh],80hJZ esperar_int

fin_espera: AND BYTE PTR DS:[3Eh],127 ; resetear flagPOP DS ; para futura interrupciónMOV CL,unidadADD CL,4MOV AL,1SHL AL,CL ; colocar bit del motorOR AL,unidad ; seleccionar unidadOR AL,00000100b ; no hacer reset y no DMAMOV DX,3F2hOUT DX,ALXPOP <CX, AX>RET

espera_int ENDP

; ------------ Cargar o escribir CX sector(es) del disco en ES:DI,; actualizando la dirección en ES:DI pero sin alterar; ningún otro registro. Si hay error se devuelve CF=1 y; no se modifica ES:DI. En el momento crítico en que se; leen/escriben los sectores, no se llama a las; subrutinas habituales por razones de velocidad, lo; que implica duplicar código y alargar el programa.

sector_io PROCXPUSH <AX, BX, CX, DX, DI>MOV AL,ordenCLICALL fdc_write ; comando leer/escribir del 765JNZ io_procJMP sector_io_ko

io_proc: MOV AL,byte1CALL fdc_write ; enviar HD, US1, US0MOV AL,cilindroCALL fdc_write ; enviar cilindroMOV AL,cabezalCALL fdc_write ; enviar cabezalMOV AL,sector_iniCALL fdc_write ; enviar nº sectorMOV AL,tsectorCALL fdc_write ; longitud sectorMOV AL,sector_finCALL fdc_write ; último sectorMOV AL,gapCALL fdc_write ; GAP de lectura/escrituraMOV AL,128CALL fdc_write ; tamaño sector si longitud=0CLDMOV AL,sector_finSUB AL,sector_iniINC ALXOR AH,AH ; AX = nº de sectoresMUL bsectorMOV CX,AX ; bytes a leer/escribirMOV DX,3F4h ; registro de estado del FDC

espera_exec: IN AL,DXTEST AL,80h ; ¿alcanzada fase ejecución?JZ espera_execCMP orden,F_WRITEJE fdc_wr_sect

fdc_rd_sect: IN AL,DXTEST AL,80h ; ¿listo para E/S?JZ fdc_rd_sectTEST AL,16JZ sector_io_ko ; fallo en lecturaINC DX ; apuntar al registro de datosIN AL,DX ; leer byte del sectorDEC DXSTOSB ; ES:[DI++] <-- ALLOOP fdc_rd_sect ; repetir hasta fin sector(es)JMP sect_io_fin

fdc_wr_sect: IN AL,DXTEST AL,80h ; ¿listo para E/S?JZ fdc_wr_sectTEST AL,64JNZ sector_io_ko ; fallo en escrituraMOV AL,ES:[DI]INC DX ; apuntar al registro de datosOUT DX,AL ; escribir byte del sectorDEC DXINC DILOOP fdc_wr_sect ; hasta acabar sector(es)

sect_io_fin: MOV CX,7sect_io_rx: CALL fdc_read ; leyendo resultados del éxito

LOOP sect_io_rxSTI ; ...fin de la fase críticaPOP CX ; «sacar» DI sin cambiarloCLC ; indicar éxitoJMP sector_io_fin

sector_io_ko: MOV DX,3F4h ; leer resultados del fallokill_info: IN AL,DX

TEST AL,80h ; ¿listo para E/S?JZ kill_infoTEST AL,64JZ info_killed ; el 765 no devuelve datosINC DX ; apuntar al registro de datosIN AL,DX ; leer byte de resultadosDEC DX

Page 309: PCA, PS2 ,IBM y AT

309EL HARDWARE DE APOYO AL MICROPROCESADOR

JMP kill_infoinfo_killed: STI

POP DI ; anular cambio de DISTC ; indicar fallo

sector_io_fin: XPOP <DX, CX, BX, AX>RET

sector_io ENDP

; ------------ Recibir byte del FDC en AL. A la vuelta, ZF = 1 si; la operación fracasó (el FDC no estaba listo).

fdc_read PROCPUSH CXPUSH DXMOV DX,3F4h ; registro de estado del FDCXOR CX,CX ; evitar cuelgue total si falla

espera_rd: IN AL,DX ; leer registro de estadoTEST AL,80h ; ¿bit 7 inactivo?LOOPZ espera_rd ; así es: el FDC está ocupadoINC DX ; apuntar al registro de datosIN AL,DX ; leer byte del FDCAND CX,CX ; ZF = 1 si fallo al leerPOP DXPOP CXRET

fdc_read ENDP

; ------------ Enviar byte AL al FDC. A la vuelta, ZF = 1 si; la operación fracasó (el FDC no estaba listo).

fdc_write PROCPUSH AXPUSH CXPUSH DXMOV DX,3F4h ; registro de estado del FDCXCHG AH,AL ; preservar AL en AHXOR CX,CX ; evitar cuelgue total si falla

espera_wr: IN AL,DX ; leer registro de estadoTEST AL,80h ; ¿bit 7 inactivo?LOOPZ espera_wr ; así es: el FDC está ocupadoXCHG AH,AL ; recuperar el dato de ALINC DX ; apuntar al registro de datosOUT DX,AL ; enviar byte al FDCAND CX,CX ; ZF = 1 si fallo al escribirPOP DXPOP CXPOP AXRET

fdc_write ENDP

; ------------ Esperar CX 1/18,2 avos de segundo.

retardo PROCPUSH DSPUSH AXPUSH CXMOV AX,40hMOV DS,AXSTI

espera_tics: MOV AX,DS:[6Ch] ; esperar que el contadorespera_tic: CMP AX,DS:[6Ch] ; de hora del BIOS...

JE espera_ticLOOP espera_tics ; ... cambie lo suficientePOP CXPOP AXPOP DSRET

retardo ENDP

; ************ Mensajes

cls_txt DB 10,10,10,10,10,10,10,10,10,10,10,10DB 10,10,10,10,10,10,10,10,10,10,10,10,13,"$"

opciones_txt DB "765NODMA.ASM 2.0 - Acceso a disquete sin DMA."DB 13,10," (c) 1991 Jesús Arias Alvarez."DB 13,10," (c) 1992, 1993 Ciriaco García de Celis."DB 13,10,10,9,"1.- Leer sector"DB 13,10,9,"2.- Escribir sector"DB 13,10,10,9," ESC-Salir"DB 13,10,10," Elige una opción: $"

lectura_txt DB 13,10,"Lectura de sector.$"

escritura_txt DB 13,10,"Escritura de sector.$"

aviso_txt DB 13,10,"--------------------",13,10DB "Aviso: No se validan las entradas.",10,"$"

adios_txt DB 13," Hasta luego. ",13,10,"$"

ptecla_txt DB 13,10,"- Estás viendo 256 bytes del sector."DB 13,10,"- Pulsa una tecla para continuar.$"

fallo_txt DB 13,10,10,"¡Fallo al acceder al disco!",7,"$"

unidad_txt DB 13,10,"Unidad (0-A, 1-B): $"vunidad_txt DB 13,10,"Velocidad: "

DB 13,10," (0) 500 Kbaudios (5¼ HD y 3½ HD)"DB 13,10," (1) 300 Kbaudios (5¼ DD)"DB 13,10," (2) 250 Kbaudios (3½ DD)"DB 13,10," Elige: $"

tdisco_txt DB 13,10,"Disquete 40 pistas en unidad de 80: "DB "(1) sí, (0) no: $"

tamano_txt DB 13,10,"Tamaño de sector (2->512 bytes): $"gap_rw_txt DB 13,10,"Tamaño del GAP (41-DD, 27-HD): $"pista_txt DB 13,10,"Pista: $"cabeza_txt DB 13,10,"Cabezal: $"sector_txt DB 13,10,"Sector: $"relleno_txt DB 13,10,"Byte para inicializar sector: $"

; ************ Datos

F_READ EQU 01100110b ; orden de lectura del FDCF_WRITE EQU 01000101b ; orden de escritura del FDC

orden DB ? ; orden a procesarunidad DB ?vunidad DB ? ; velocidad de transferenciatunidad DB ? ; control de salto de pistacilindro DB ? ; pista del disco a usarcabezal DB ? ; cabezasector_ini DB ? ; sector inicialsector_fin DB ? ; sector finaltsector DB ? ; tamaño de sector (logaritmo)bsector DW ? ; tamaño de sector (bytes)gap DB ? ; GAP para lectura/escriturabyte1 DB ? ; bits HD, US1, US0relleno DB ? ; byte de relleno (al escribir)

buffer EQU $ ; para leer/escribir sector

fdc_test ENDSEND main

12.6.7 - PROGRAMACION AVANZADA DEL CONTROLADOR DE DISQUETES: 2M 3.0

Hasta ahora hemos descrito todo lo necesario para poder programar la controladora de disquetes.Ahora aplicaremos dicha información a un caso práctico real, con un programa. Ciertas aplicacionescomerciales de backup ya emplean formatos de disco de más capacidad para almacenar los datos, además demanera comprimida. Sin embargo, estos disquetes no pueden ser empleados directamente por el DOS. Porel contrario, la utilidad que desarrollaremos, 2M, es un programa residente que permite gestionar disquetescon sectores de más de 512 bytes e, incluso, con sectores de distinto tamaño en las pistas. Este últimoformato obtendrá algo más de capacidad, pero menos velocidad y fiabilidad. En 3½", los disquetes máscomunes de 1.44M (1440K) se podrán formatear a 1804K y 1886K, respectivamente. Los de 720K alcanzaránlos 984/1066K. En 5¼" los de 1.2M pasan a 1476/1558K y los de 360K a 820/902K. Los formatos de1886K, 1066K y 1558K no pueden ser reproducidos por la versión de enero de 1992 del poderoso copiónCOPYWRITE; el de 902K sí es duplicado en algunos ordenadores, aunque a veces algunas pistas quedan mal.Esto no es problema para el usuario normal, que podrá hacer DISKCOPY (si 2M está instalado en memoria)hacia un disco destino ya formateado. Para formatear estos nuevos disquetes se empleará un pequeñoprograma escrito en C (2MF.C) que se limitará a llamar a las funciones de INT 13h reforzadas por 2M; dichoprograma será descrito más adelante.

Los programas que formatean los discos a mayor capacidad de la normal suelen limitarse a reducirel GAP 3 al formatear, colocando gracias a ello más sectores en las pistas. Sin embargo, la utilidad propuestaaquí rompe con el tamaño estándar de 512 bytes: al colocar sectores de mayor tamaño, existen menos sectoresy también menos GAP de separación. El inconveniente de este método es que difícilmente sectores de 1024,2048 ó más bytes pueden encajar aprovechando óptimamente la capacidad de la pista. Por ello se han

Page 310: PCA, PS2 ,IBM y AT

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

adoptado dos soluciones diferentes que han originado 8 nuevos formatos de disco (2 por cada tipo de mediomagnético):

Empleo de sectores de 1 Kb. Pese a ser más grandes, se pueden colocar más o menos bien en los 4tipos de disco (360-1.2-720-1.44) aprovechando más la capacidad de la pista, ya que al haber menossectores también se derrocha menos espacio en GAPs sin necesidad de reducirlos excesivamente ni,por tanto, degradar la fiabilidad de los discos. Esta solución, si se tiene cuidado de optimizar elformateo de las pistas (con la numeración adecuada de los sectores en las mismas) permite obtenerdisquetes de mayor capacidad de la normal, tan fiables como los estándar del DOS y sensiblementemás rápidos que los creados por el FORMAT debido a dos motivos: en estos formatos el disco dasólo las vueltas necesarias para acceder a los datos y, además, se leen más datos en dichas vueltas.

La otra solución alternativa consiste en emplear sectores aún de mayor tamaño, hasta 2 Kb (mayoresno permitirían una ventaja significativa) y rellenar el hueco restante de la pista, donde no cabe otrosector de 2 Kb, con sectores menores. Esto implica colocar sectores de distinto tamaño en las pistas,lo cual escapa en teoría de las posibilidades del controlador de disquetes, si se repasa ladocumentación de las páginas anteriores. Sin embargo, sólo en teoría, ya que existen programas

Parámetros /X e /Yde FDFORMAT para unformateo correcto.

/X /Y

5¼-DD 1 35¼-HD 2 33½-DD 1 23½-HD 2 3

comerciales con protección anticopia que realizan esta tarea. La técnicaque veremos permite realizar esto, pese a lo cual estos formatos dedisco no son recomendados: son poco seguros en cuanto a portabilidad-disquetes creados en una máquina podrían tener problemas para serreconocidos en otro ordenador o incluso ser destruidos al escribir- yaumentan poco la capacidad respecto a la 1ª solución; pese a todo hansido calibrados de tal manera que se puede afirmar que en unelevadísimo porcentaje de veces el funcionamiento y la portabilidadserán satisfactorios.

A lo largo de este apartado se hará alguna referencia al popular programa de formateo FDFORMATcreado por Christoph H. Hochstätter; esta utilidad permite formatear disquetes normales desplazando lossectores de manera óptima (opciones /X e /Y) y también añadir más sectores (estrechando el GAP 3). Parasuperar las limitaciones de flexibilidad de la BIOS es preciso tener residente un pequeño programa de sólo128 bytes de cara a soportar los formatos extendidos. Este programa, bastante superior al FORMAT en todoslos aspectos, con el que además es compatible, está muy extendido en las principales BBS (su código fuenteen Turbo Pascal viene incluido) y aborda desde otro punto de vista la ampliación de la capacidad normal delos disquetes, respetando los sectores de 512 bytes.No hay que olvidar que este programa permitecrear, además de algunos formatos extendidos,disquetes totalmente estándar de 360K, 1.2M, 720Ky 1.44M que, por supuesto, no necesitan soporteresidente y son mucho más rápidos que los creadospor el FORMAT del DOS. Mientras el FORMATdel sistema operativo no corrija la numeraciónincorrecta de sectores, que lleva practicando desde1981, y a la espera de que David Astruga saque lapróxima versión de su programa de copia yformateo (a finales del 94 o comienzos del 95); porel momento, FDFORMAT y sus parámetros /X e/Y constituyen la única solución para los usuariosmás entendidos (aquellos que usan 4DOS en vez deCOMMAND.COM, QEMM en lugar de EMM386,etc): emplear el FORMAT actual no es deconservadores sino de no informados. 2M(abreviatura de 2 megas, aunque no se alcanza esacapacidad por disco) es un programa residente que

[1867/1867] B:\>dir

Volume in drive B is unlabeled Serial number is 2FE6:7632File not found "B:\*.*"

0 bytes in 0 file(s)1.912.320 bytes free

[1867/1867] B:\>chkdskNúmero de serie de volumen es 2FE6-7632

1912320 bytes de espacio total en disco1912320 bytes disponibles en disco

512 bytes en cada unidad de asignación3735 total de unidades de asignación en el disco3735 unidades de asignación disponibles en disco

655360 bytes de memoria total649760 bytes libres

[1867/1867] B:\>testdiskTD-Test Disco, Edición Estandar 4.50, (C) Copr 1984-88, Peter NortonTraducción Castellano, Copyright (C) 1989 ANAYA Multimedia, S.A.

Verificar DISCO, ARCHIVO, o AMBOSPulse D, F, o A ... D

Puede pulsar BREAK (Ctrl-C) durante laverificación para interrumpir Test Disco

Test leyendo el disco B:, zonas del sistema y de datosLa zona del sistema consta de boot, FAT, y directorio

Zona del sistema sin errores

La zona de datos consta de clusters numerados 2 - 3.736Zona de datos sin errores

[1867/1867] B:\>_

EJEMPLO DE ACCESO A DISQUETE 2M DE 1.44 FORMATEADO A CASI 1.90

Page 311: PCA, PS2 ,IBM y AT

311EL HARDWARE DE APOYO AL MICROPROCESADOR

da soporte a los nuevos formatos de disco. Una vez instalado 2M en memoria, los nuevos disquetes seránreconocidos sin problemas: se podrá hacer DIR, COPY, CHKDSK,... e incluso DISKCOPY hacia un discodestino ya formateado. El código residente de 2M funciona también bajo WINDOWS 3.X; sin embargo,en OS/2 2.1 hay problemas, aunque se pueden arreglar, como veremos luego, usando el DOS de Microsoft(y no el que viene con el propio OS/2) desde un disquete o, mejor aún, creando una imagen en disco durode ese disquete. De esta última manera, el usuario ni siquiera nota al diferencia entre estas ventanas de DOSy las normales. Tal vez alguien escriba algún día el driver oportuno para facilitar la operación en estesistema... de momento, 2M está diseñado sólo para los sistemas más extendidos. En WINDOWS NT, dondeno ha sido probado, probablemente existirán problemas y limitaciones mayores de las que se producen bajoOS/2. Al momento de escribirse estas líneas, el autor de 2M tiene constancia de que hay intentos de portarloal sistema operativo Linux por parte de Alain Knaff y David Niemi, si bien desconoce el grado de avanceen esta materia.

2M añade un nuevo servicio a la INT 13h para poder formatear los nuevos disquetes. No es probableque gracias a ello la próxima versión de PC-TOOLS soporte los nuevos formatos, pero añadir rutinas deformateo apenas alargaba el código residente (sólo 0.75 Kb más hasta alcanzar los 5 Kb) y se trataba de lasolución más elegante. Para formatear los nuevos disquetes se ha creado un programa en C de alto nivel, quesencillamente invoca la INT 13h sin verse obligado a realizar ni un solo acceso directo al hardware, pese aque el código residente de 2M accede siempre a disco a través del controlador de disquetes, sin una sola

Ensamblador Comentario Offset

JMP SHORT BootP ; 2 bytes 0NOP ; 1 byte 2DB "2M-STV08" ; ID sistema 3DW 512 ; bytes/sector 11DB 1 ; sectores por cluster 13DW 1 ; sectores reservados al principio 14DB 2 ; nº copias de la FAT 16DW 224 ; entradas al directorio raíz 17DW 3608 ; nº total de sectores del disco 19DB 0F0h ; byte descriptor de medio 21DW 11 ; sectores ocupados por la FAT 22DW 22 ; sectores por pista 24DW 2 ; nº de cabezales 26DD 0 ; sectores especiales reservados 28DD 0 ; nº sectores (unidad 32 bit) 32DB 0 ; unidad física 36DB 0 ; reservado 37DB 29h ; disco con número de serie 38DD 8BC1AD20h ; número de serie provisional 39DB "NO NAME " ; título del disco 43DB "FAT12 " ; tipo de FAT 54DB Flags ; bit 0 = 1 si FechaF/HoraF definido 62DB ? ; checksum de la información vital 63DB 7 ; versión formato (>=7 si BOOT virtual) 64DB 0 ; a 1 si escribir al formatear 65DB 0 ; velocidad transferencia pista 0 66DB 0 ; velocidad transf. demás pistas 67DW BootP ; offset al programa de arranque 68DW Infp0 ; T1: información para pista 0 70DW InfpX ; T2: información demás pistas 72DW InfTm ; T3: tabla tamaños demás pistas 74DW FechaF ; Fecha de formateo (2M 3.0+) 76DW HoraF ; Hora de formateo (2M 3.0+) 78

Infp0 DB 19, 70 ; nº sectores / GAP de formateoDB 1,2,3,4,5,6,7,8 ; sectores ordenados (20..22 no existen)DB 9,10,11,12,13,14DB 15,16,17,18,19

InfpX DB 11, 40 ; nº sectores / GAP de formateoDB 3 ; tamañoDB 1, 2 ; desplazamiento numeración

InfTm DB 3,3,3,3,3,3 ; tamaño sector 1, 2, 3,...DB 3,3,3,3,3

BootP ... ; programa del sector de arranque

SECTOR DE ARRANQUE DE UN DISQUETE 2M DE 3½ A 1.80M

llamada al DOS/BIOS en ningún momento.

La capacidad obtenida por 2Msupera la conseguida por los programascomerciales de backup en los formatosespeciales para almacenar sólo datos. Con laayuda de un compresor de datos de dominiopúblico líder (PKZIP, ARJ, etc) tambiénsuperior en rendimiento a los programas debackup, se puede conseguir el método debackups que, indiscutiblemente, másaprovecha los disquetes, con una aplastantediferencia -y además el más barato-. Sinembargo, el usuario debería tener cuidadocon el tipo de datos que almacena en estosdiscos, ya que no son tan portables como losestándar y sería problemático migrarlosdespués a otros entornos.

Existen versiones de 2M tanto parasistemas AT como para PC/XT, con el únicorequisito de que la controladora y lasunidades sean de alta densidad.

12.6.7.1 - FORMATO DE LA PRIMERA PISTA.

La primera pista (cilindro y cabezal 0) de los nuevos disquetes tiene el formato normal de sectoresde 512 bytes, conteniéndolos en cantidad también más o menos normal. Uno de los motivos es permitir quela FAT, zona del disco en la que a menudo cambia un sólo sector (y no varios consecutivos) tenga un accesomás ágil. En algunos formatos de disco, parte del directorio raíz también cabe en esta pista; en cualquier caso,esto no es demasiado importante porque sólo se accede al directorio raíz una vez por cada fichero.

Debido al empleo en la primera pista de sectores físicos de 512 bytes, no se pueden emular todos lossectores virtuales. En 3½-HD por ejemplo, los nuevos formatos de disco contarán aparentemente con 22-23sectores por pista. Realmente serán muchos menos y de más de 512 bytes, pero se engañará al DOS para

Page 312: PCA, PS2 ,IBM y AT

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

hacerle creer que son la cantidad citada de sectores de 512 bytes, de cara a mantener la compatibilidad. Encualquier caso, esta cifra es muy superior a los 18 sectores habituales en este tipo de disco. Como la primerapista contiene sectores reales de 512 bytes, no se pueden meter tantos (no caben más de 21 y eso juntandoexcesivamente los sectores, como hace FDFORMAT en el formato 1.72M).

Para arreglar este problema, el código residente de 2M se extralimita en sus funciones y, suponiendoque los discos se emplean bajo DOS, ignora las escrituras sobre la segunda copia de la FAT (que estaríasobre alguno de los sectores que no existen en la primera pista) devolviendo la primera copia de la FAT aquien quiera leer la segunda. Así se consigue además una pequeña velocidad extra, ya que la escritura sobrela segunda copia de la FAT que realiza el DOS al crear ficheros resulta ignorada. Realmente, es un pocoinnecesaria la presencia de 2 FAT en un disquete, máxime teniendo en cuenta que su adyacencia físicapropicia que en caso de daño se estropeen las dos (¿cuántas veces el lector ha tenido que echar mano de lasegunda copia de la FAT para recuperar susdatos?). El MS-DOS, incluso en la versión6.0 no respeta sus propias especificaciones yasume que los disquetes tienen 2 copias de laFAT: aunque se indique sólo una en el sectorde arranque, hará caso omiso. Esta es, por unlado, una buena manera de darle el corte demangas; por otro, un medio ideal parasimular más sectores en la primera pistafísica.

El sector de arranque de los nuevosdisquetes es en principio similar al decualquier otro disco, pero contiene másinformación adicional para describir elformato físico de disco que se trate y asípoder gestionarlo luego. De esta manera, sesistematiza el soporte de los nuevos formatosy se simplifica el programa residente. Detrásde los primeros 62 bytes, donde va lainformación colocada por el FORMATnormal del DOS (incluyendo las últimasmodas, como campos para etiqueta de disco,número de serie, etc.) existen unos camposcon información adicional, que describiremosmás adelante. Detras de este área está el

Ensamblador Comentario Offset

JMP SHORT BootP ; 2 bytes 0NOP ; 1 byte 2DB "2M-STV04" ; ID sistema 3DW 512 ; bytes/sector 11DB 1 ; sectores por cluster 13DW 1 ; sectores reservados al principio 14DB 2 ; nº copias de la FAT 16DW 224 ; entradas al directorio raíz 17DW 3772 ; nº total de sectores del disco 19DB 0F0h ; byte descriptor de medio 21DW 11 ; sectores ocupados por la FAT 22DW 23 ; sectores por pista 24DW 2 ; nº de cabezales 26DD 0 ; sectores especiales reservados 28DD 0 ; nº sectores (unidad 32 bit) 32DB 0 ; unidad física 36DB 0 ; reservado 37DB 29h ; disco con número de serie 38DD 4B368A0Eh ; número de serie (aleatorio) 39DB "NO NAME " ; título del disco 43DB "FAT12 " ; tipo de FAT 54DB Flags ; bit 0 = 1 si FechaF/HoraF definido 62DB ? ; checksum de la información vital 63DB 7 ; versión formato (>=7 si BOOT virtual) 64DB 1 ; a 1 si escribir al formatear 65DB 0 ; velocidad transferencia pista 0 66DB 0 ; velocidad transf. demás pistas 67DW BootP ; offset al programa de arranque 68DW Infp0 ; T1: información para pista 0 70DW InfpX ; T2: información demás pistas 72DW InfTm ; T3: tabla tamaños demás pistas 74DW FechaF ; Fecha de formateo (2M 3.0+) 76DW HoraF ; Hora de formateo (2M 3.0+) 78

Infp0 DB 19, 70 ; nº sectores / GAP de formateoDB 1,2,3,4,5,6,7,8 ; sectores ordenados (20..23 no existen)DB 9,10,11,12,13,14DB 15,16,17,18,19

InfpX DB 64, 3 ; nº sectores / GAP de formateoDB 7 ; nº sectores a renumerarDB 128+1, 4, 4 ; tabla de renumeración formateo:DB 128+12, 1, 4 ; nº sector, nuevo número, tamañoDB 128+23, 5, 4DB 128+34, 2, 4DB 128+45, 6, 3DB 128+51, 3, 4DB 128+62, 7, 2

InfTm DB 4,4,4,4,4,3,2 ; tamaño sector 1, 2, 3,...BootP:... ; programa del sector de arranque

SECTOR DE ARRANQUE DE UN DISQUETE 2M DE 3½ A 1.88M

programa de arranque del disquete, que en sus primeras versiones se limitaba a imprimir en pantalla unmensaje diciendo que el disco no es de arranque; actualmente arranca desde el disco duro si éste existe y,desde 2M 2.0, carga el código SuperBOOT almacenado en el disco si es de alta densidad. Los discos 2M dealta densidad utilizan 5 sectores libres de la segunda copia de la FAT (ubicados en la primera pista) paraalmacenar gran parte del código residente de 2M (todo, excepto las rutinas de formateo). De esta manera,desde 2M 2.0 es posible botar de un disco 2M de alta densidad, que puede crearse con un SYS ordinario.De hecho, el primer sector de la segunda copia de la FAT emula al auténtico sector de arranque, y los 5restantes almacenan el código residente de 2M. Así, cuando 2M está instalado, el comando SYS y cualquieraplicación que acceda al sector de arranque estará accediendo realmente a un falso sector de arranque queestá físicamente colocado en la FAT2. Y podrá modificarlo sin riesgo alguno para 2M, ya que el auténticosector de arranque permanece inmutable; las versiones anteriores de 2M necesitaban proteger este sectorrestringiendo de alguna manera su acceso (para evitar que un simple SYS lo modificara y borrara lainformación vital que contiene). La denominación SuperBOOT para el código de 2M almacenado en laprimera pista de los discos se debe exclusivamente a cuestiones de marketing. Debido a que se necesita untamaño mínimo de FAT, modificar el tamaño de cluster en el sector de arranque no es conveniente, aunqueestá permitido y puede generar discos que no funcionen. Sin embargo, la utilidad estándar de formateo no

Page 313: PCA, PS2 ,IBM y AT

313EL HARDWARE DE APOYO AL MICROPROCESADOR

deja cambiar el tamaño de cluster (por otra parte de sólo 512 bytes) y no hay muchos programas conocidosque alteren estos parámetros de los disquetes ya formateados.

Cuando el sistema arranca de un disco 2M de alta densidad, el código SuperBOOT rebaja la memorialibre en 5 Kbytes (normalmente, de 640K a 635K) ubicándose al final de la memoria convencional y seinstala en la INT 13h. Después, se carga el sector de arranque vía INT 13h (que en adelante será el falsosector de arranque emulado, al que pudo acceder el SYS) y se ejecuta, procediéndose al arranque normal delsistema, ya que la nueva BIOS soporta discos 2M... este sector de arranque ubicado en la FAT2 esdenominado sector de arranque virtual en la documentación de 2M. Como puede observar el lector, dejarla primera pista con sectores de 512 bytes y emular la segunda copia de la FAT sobre la primera fue una ideaprimitiva que luego ha permitido muchas aplicaciones interesantes.

Naturalmente, está previsto un mecanismo para poder acceder a los sectores físicos sin emulaciones:esto es útil además para permitir al programa de formateo grabar el código SuperBOOT y acceder al sectorde arranque físico, ya que los programas normales no tienen motivos especiales para necesitar un acceso adichas áreas. Cuando 2M está instalado, cualquier acceso al cabezal 128 ó 129 en lugar del 0 ó el 1 permiteacceder al disco sin realizar ningún tipo de emulación; si bien esto sólo funciona con discos 2M (con un discoestándar en la unidad, aunque 2M esté instalado, el acceso a estos cabezales devuelve un error).

En adelante nos referiremos al sector de arranque físico, no al virtual (que puede ser distinto si eldisco es de sistema o ha sido alterado por alguna utilidad). El primer campo propio de 2M en el sector dearranque es una variable con flags, empleada sólo desde 2M 3.0 para indicar si se almacena la fecha y horade formateo en el sector de arranque (bit 0 = 1 en caso afirmativo). Detrás hay un checksum o suma decomprobación de la zona vital del sector de arranque. El algoritmo empleado ha variado en las sucesivasversiones del programa. Desde la versión 6 del formateador (byte ubicado justo después del checksum) lazona total afectada por el checksum va desde el offset 64 hasta justo antes del programa de arranque deldisco. Las versiones anteriores de 2M realizaban un checksum distinto, por lo que los discos formateados porellas no están sujetos a la comprobación de checksum para evitar problemas. La suma total de este área (ennúmero de 8 bits) debe dar un resultado 0. Por tanto, se permite modificar el programa de arranque e inclusolos campos del principio.Cualquier otro cambio nopermitido hará que 2M falleen la comprobación delchecksum la primera vez queel disco es introducido en launidad; en este caso INT 13h

GAPs y /X e /Y probados en 2MF /F

5¼-DD 5¼-HD 3½-DD 3½-HD

GAP mínimo de lectura soportado en las pruebas 1 2 1 2GAP mínimo de escritura soportado en las pruebas 13 26 20 28GAP máximo de escritura soportado en las pruebas 197 76 187 49GAP 3 de formateo adoptado finalmente 100 50 100 40Valor óptimo obtenido experimentalmente para /X 1 1 1 1Valor óptimo obtenido experimentalmente para /Y 1 2 1 2

2MF ES EL FORMATEADOR PARA 2M. CON /F SE CREAN DISCOS NORMALES Y /M INDICA MÁXIMA CAPACIDAD.

devuelve un Seek Error poco habitual para señalizar la circunstancia. Sin embargo, un cambio en el campoID (bytes 3 al 10) podría acarrear que 2M no reconociera el disco como suyo. Quizá el lector opine quehubiera sido mejor ser más tolerantes, pero yo opino que no: si el sector de arranque está corrompido, elcódigo residente de 2M, que no valida nada de dicho sector, podría estrellarse si se fía de la información delmismo. Así nadie podrá decir: «se me cuelga al hacer DIR A:», como mucho: «me dice Seek Error y no medeja acceder al disco». En realidad, es difícil que se produzcan estos errores porque nadie que intente alterarel sector de arranque físico lo podrá conseguir con 2M en memoria, sin saber como hacerlo o sin accederdirectamente a la controladora.

Tras el checksum hay un byte que indica la versión del formateador, de cara a permitir que futurasversiones de 2M sepan con qué formato de disco se enfrentan para respetar los viejos formatos (en caso deque surjan otros nuevos). El siguiente byte indica si es necesaria una escritura tras el formateo: en losformatos de más capacidad, trasformatear la pista hay que escribirla para evitar que una lectura posteriorproduzca errores de CRC, como luego veremos y explicaremos. En los formatos normales este byte estaráa 0, y a 1 en los de más capacidad.

Los siguientes 2 bytes indican la velocidad de transferencia a emplear en la primera pista (cilindroy cabezal 0) y en las demás; el dato no está, por supuesto, en Kbit/seg sino que se trata del valor que hay

Page 314: PCA, PS2 ,IBM y AT

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

que enviar al registro de salida digital. En los disquetes de 3½-DD se utilizará la velocidad de 250 Kbit/segen la primera pista y 300 Kbit/seg en las demás. El motivo es que las primeras versiones de 2M delegabanparte del trabajo de reconocer la densidad de disco a la BIOS, la cual sólo soporta 250 Kbit/seg en estasunidades. Actualmente no sería necesario, ya que 2M detecta la densidad de los discos (y de hecho, sustituyea la BIOS original en esta tarea), pero se ha mantenido por compatibilidad con los primeros formatos de discode 2M. Tras estos campos hay unos punteros a diversas áreas interesantes: el primero apunta al programa dearranque y será empleado por dicho programa para conocer con comodidad su propia ubicación; después hayun puntero a una tabla con información sobre la estructura de la primera pista del disco, otro puntero apuntaa una tabla con información de las demás pistas y, finalmente, un último puntero referencia una tabla detamaños de los sectores de las pistas (excepto la primera). Los últimos campos sólo se emplean desde 2M3.0 y almacenan la fecha y hora de formateo.

La primera tabla contiene un byte que indica el número real de sectores de la primera pista, seguidode otro byte con el valor de GAP 3 empleado al formatear. Después vienen los números de sectores, uno trasotro, lo que permite elegir líbremente el interleave. Las últimas versiones de 2M acceden de manera eficientea la primera pista (y a todas las demás) soportando perfectamente un interleave 1:1, si bien los primerosdisquetes 2M fueron formateados con un factor 1:2. En los formatos de 1.80/1.88M la FAT ocupa 11sectores, y otro el sector de arranque físico. Los sectores que van del 1 al 12 están, por lo tanto,necesariamente ocupados; pero del 13 al 19 hay sitio para 7 sectores que pueden contener el BOOT virtual(1 sector) y el código SuperBOOT (5 sectores). El sector restante se debe a que en discos de 1.88M con 84pistas la FAT1 ocuparía un sector más.

Capacidad bruta real antes de Bytes netos obtenidos por los principales formateadoresformatear (con 82 pistas y encontroladora de alta densidad) FORMAT (40/80p) (*) FDFORMAT (82p) (**) 2MF 3.0 /F (82p) 2MF 3.0 /M (82p)

5¼-DD 1.025.000 bytes (0,98 Mb) 368.640 (360K) 839.680 (820K) 839.680 (820K) 923.648 (902K)5¼-HD 1.708.224 bytes (1,63 Mb) 1.228.800 (1200K) 1.511.424 (1476K) 1.511.424 (1476K) 1.595.392 (1558K)3½-DD 1.230.000 bytes (1,17 Mb) 737.280 (720K) 839.680 (820K) 1.007.616 (984K) 1.091.584 (1066K)3½-HD 2.050.000 bytes (1,96 Mb) 1.474.560 (1440K) 1.763.328 (1722K) 1.847.296 (1804K) 1.931.264 (1886K)

(*) También FDFORMAT cuando se emplean los formatos estándar del DOS.

(**) Formatos de máxima capacidad soportados (820-1.48-1.72).

La segunda tabla contiene información de las demás pistas del disco. El contenido y el formato deesta tabla varía según el tipo de disco: los formatos normales (como el caso de 1.80M) poseen 5 bytes: elprimero indica el número de sectores de la pista, el siguiente el GAP 3 al formatear, otro byte indica eltamaño de sector empleado (siempre 3, esto es, 1024 bytes) y los dos últimos bytes son equivalentes a losparámetros /X e /Y de FDFORMAT para desplazar de manera óptima la numeración de los sectores en laspistas consecutivas. Estos valores de /X e /Y son sensiblemente menores que los de FDFORMAT, pero nohay que olvidar que aquí los sectores son dos veces más grandes. En los formatos de disco de máximacapacidad (como en 1.88M) esta tabla cambia radicalmente de estructura: el primer byte sigue siendo elnúmero de sectores, pero ahora son sectores de 128 bytes. Esto se debe a que en estos formatos, las pistasson preformateadas (en una primera pasada) con sectores de 128 bytes. El siguiente byte es el GAP 3, quecomo se puede observar es muy pequeño (de 3 a 5 bytes). Finalmente, viene el número de sectores arenumerar. La razón es que, durante el formateo, se asignan números a partir de 129 a la mayoría de lossectores; sin embargo, algunos de ellos no se llevan el que les correspondería sino que siguen otra numeraciónmás baja a partir de 1. En estos sectores, además, al ser enviada su información al FDC durante el formateo,se indicará un tamaño distinto de 128 (512, 1024 ó 2048). Así, por ejemplo, en 1.88M la pista quedaformateada con nada menos que 64 sectores de 128 bytes numerados desde 129, habiendo sin embargoalgunos de ellos con números más bajos (1, 2,..., 7) y definidos con mayor tamaño. Al ser escritos dichossectores (segunda fase del formateo) se machacarán los sectores de 128 bytes que les siguen y quedarán sóloellos en la pista. Esto permite colocar sectores de distinto tamaño en la pista. El GAP 3 definitivo será mayor(13 bytes en el peor de los casos). Ahora comprenderá el lector por qué había que escribir la pista, despuésdel formateo, en estos formatos de disco... Por último, señalar que en esta tabla se elige un factor deinterleave adecuado, que si se echa un vistazo resulta ser de 1:2, ya que los sectores están demasiadopróximos para numerarlos consecutivamente (por razones de velocidad, si bien al ser accedidos uno a unola controladora no tendría problemas para encontrarlos). En el caso del formato 1.88M, por ej., quedannumerados: 4,1,5,2,6,3,7.

Page 315: PCA, PS2 ,IBM y AT

315EL HARDWARE DE APOYO AL MICROPROCESADOR

La última tabla es la única que realmente emplea 2M para acceder a todas las pistas, con excepciónde la primera. Se trata de una lista ordenada de los tamaños de los sectores. En los formatos de disconormales es una lista de treses, ya que todos los sectores son iguales y de 1024 bytes. En los formatos demáxima capacidad, como 1.88M, se puede comprobar que la lista es más variada. Las otras dos tablas vistascon anterioridad sólo son empleadas durante el formateo del disco.

12.6.7.2 - PUNTUALIZACIONES SOBRE EL FORMATO DE MAXIMA CAPACIDAD.

El formateo de disquetes 2M se realiza con un programa que veremos más adelante, 2MF.EXE, quepermite elegir entre formatos normales (2MF sin parámetros o con la opción /F) y formatos de máximacapacidad (2MF /M). Como se vio en la descripción del sector de arranque, el formato de máxima capacidadlogra introducir sectores de distinto tamaño en la misma pista. Seguramente la descripción dada en el apartadoanterior no ha quedado muy clara, por lo que ahora puntualizaremos un poco más.

Uno de los principales objetivos al realizar 2M fue conseguir un nivel de compatibilidad losuficientemente alto, incluso en los formatos menos seguros como el que se describirá a continuación, almenos en comparación con los ya estudiados de sectores de 1 Kb. Hay disqueteras de 1.44M que soportanel formateo de 3 sectores de 4096 bytes en una pista, lo que permitiría obtener 1968K (en 82 cilindros,soportados por prácticamente todas las unidades). Sin embargo, hay muchos ordenadores en que esto no esposible, por tanto esta solución fue descartada. En los casos en que es posible, lo es además a costa derebasar con creces los mínimos niveles de seguridad (machacando no sólo el GAP ubicado al final de la pista,sino también el del principio e incluso el IAM; resulta increíble que algunas controladoras de disquetecontinúen reconociendo los sectores). Además, se trataría de una solución exclusiva para disquetes de 1.44M.

El truco explicado con anterioridad consiste en formatear los discos con sectores muy pequeños de128 bytes, pero definiéndoles con tamaños de 512, 1024 y 2048 bytes al enviar la información de cada sectoral controlador, de cara a agruparles posteriormente para obtener sectores de mayor tamaño. Echando cuentas,con un GAP 3 provisional de sólo 3 bytes (podríamos denominarlo GAP virtual) cada sector ocupa 128+62+3= 193 bytes. Agrupando 11 de estos sectores se obtienen 193*11=2123 bytes, suficientes para contener unsector de 2048 bytes, los 60 bytes añadidos al principio del primer sector de 128 bytes por el FDC, los 2bytes añadidos al final del último sector por el FDC y otros 13 bytes de GAP 3. Agrupando 6 sectores seobtienen 1158 bytes, suficientes para contener un sector de 1024 bytes con un GAP 3 de 72 bytes.Finalmente, agrupando 3 se consiguen 579 bytes, en los que cabe un último sector de 512 bytes con un GAP3 de 5 bytes. Así, en un disquete estándar de 1.44M, con 12500 bytes por pista, donde caben bastanteholgadamente 64 sectores de 128 bytes de las características mencionadas, se pueden colocar 5 grupos de 11,1 de 6 y otro de 3. En total: 11,5 Kb en cada pista (1886 en todo el disco, a 82 cilindros). Una vezformateada la pista, es conveniente escribir todos los sectores (la primera lectura daría error de CRC en casocontrario), de paso se asegura de esta manera, en una posterior lectura, que la escritura no ha provocado queningún sector pise a otro, asegurando la fiabilidad del método. Una vez que el disco ha sido formateado, laverificación realizada durante el formateo garantiza que es seguro; la separación o GAP 3 medio menor esde 13 bytes y puede considerarse bastante razonable (el sector de 512 bytes con un GAP 3 de sólo 5 escolocado siempre al final de la pista); en los disquetes de doble densidad es además superior, al emplearseun GAP 3 virtual en la primera fase de 4 ó 5 bytes en vez de 3.

El formateo es relativamente lento, ya que requiere tres fases: formateo, escritura y lectura paraverificar; cada una de ellas, dada la proximidad de los sectores, requiere de dos vueltas del disco (los sectoresestarán numerados alternamente con un razonable interleave 1:2); en total, 6 vueltas en un disco de 1.44Mpor cada pista, lo que equivale a 1,2 segundos por pista y 3:17 minutos en el conjunto del disquete (2 carasy 82 cilindros). Este es el precio que hay que pagar para obtener 1.912.320 bytes libres netos (los queaparecen al hacer un DIR) frente a los 1.457.664 conseguidos por el FORMAT del DOS.

Un último detalle a tener en cuenta es que, en este tipo de formato, al escribir el cabezal 1 delcilindro 0, el código de 2M se saltará el acceso al primer sector de la pista (al estar la FAT2 en él, por reglageneral, y debido a las emulaciones). Por tanto, en este caso, es necesario escribir en el cabezal 129 para

Page 316: PCA, PS2 ,IBM y AT

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

asegurar que realmente se escribe la pista y el disco queda correctamente inicializado. Por comodidad, sepuede escribir en el cabezal 128/129 de todas las pistas (salvo la primera, que no tiene realmente tantossectores como las demás y que además tampoco es necesario escribir tras el formateo).

12.6.7.3 - DESCRIPCION DE FUNCIONAMIENTO DEL SOPORTE RESIDENTE (2M).

2M es un programa residente ordinario que desvía la INT 13h/40h. En las máquinas AT con discoduro de tipo IDE (los más extendidos actualmente) o con una controladora de disco duro ordinaria de AT,la BIOS desvía a INT 40h los servicios de disquete, siendo invocada esta interrupción desde la INT 13h paraatender las funciones de disquete. Sin embargo, si el ordenador no tiene disco duro o incorpora unacontroladora de disco duro de XT, es la INT 13h quien podría controlar los disquetes. La versión 1.0 de 2Mdesviaba la INT 40h en lugar de la INT 13h, por el motivo que ahora analizaremos (ayuda en la cuestión delDMA); sin embargo, ésto hacia que el programa no funcionara en algunas máquinas AT sin disco duro o concontroladora de XT. Por ello, en la versión 1.1 se volvió a trabajar con INT 13h. Pero desde 2M 2.0+, aunqueahora más por razones de seguridad que de comodidad, se utiliza una técnica mixta: si el ordenador empleala INT 40h, 2M se instala desde esta interrupción; en caso contrario, lo hace desde INT 13h (actuándo desdeINT 40h el programa toma el control de los discos antes que otros TSR instalados después). Y volvamossobre la cuestión del DMA, que motivó el uso de INT 40h en 2M 1.0. Como el lector recordará, a la horade transferir con la disquetera hay que tener cuidado con las fronteras de DMA. Sin embargo, resultaría muyengorroso tener que tener esto en cuenta en los programas de alto nivel. El propio DOS considera que es unauténtico fastidio tener que comprobar esto cada vez que se accede al disco. Por ello, cuando el sistemaoperativo se carga en el ordenador desvía la INT 13h y la modifica para arreglar de un plumazo losproblemas con el DMA: a partir de ese momento, la INT 13h es realmente controlada por el DOS, aunquese trate de una interrupción BIOS. Las nuevas rutinas de la INT 13h colocadas por el DOS se limitan a llamara la vieja INT 13h (nadie ha hablado aún de INT 40h) y, cuando se produce un error de frontera de DMA,la operación de disco que lo había provocado es segmentada probablemente en tres fases: los sectores queestaban antes de la frontera, los que quedan por detrás y el que cae justo en medio; este sector esprobablemente transferido a través de un buffer intermedio del sistema.

Porcentaje de disco aprovechado (perdido) tras el formateo

FORMAT FDFORMAT 1.8 2MF 3.0 /F 2MF 3.0 /M

5¼-DD 35,96% (64,04%) 81,92% (18,08%) 81,92% (18,08%) 90,11% ( 9,89%)5¼-HD 71,93% (28,07%) 88,48% (11,52%) 88,48% (11,52%) 93,39% ( 6,61%)3½-DD 59,94% (40,06%) 68,27% (31,73%) 81,92% (18,08%) 88,75% (11,25%)3½-HD 71,93% (28,07%) 86,02% (13,98%) 90,11% ( 9,89%) 94,21% ( 5,79%)

Media 59,94% (40,06%) 81,17% (18,83%) 85,60% (14,40%) 91,62% ( 8,38%)

Si 2M se instala colgando de INT 13h, al introducir un disquete de tipo 2M (cuyo controlevidentemente corre a cargo de 2M) todas las llamadas del DOS a la INT 13h serían llamadas a 2M, que hasido instalado después de que el DOS arregle la INT 13h. Por tanto, 2M debe en ese caso ocuparse de laengorrosa gestión de errores de DMA, ya que el DOS no espera nunca este tipo de error de una llamada ala INT 13h. En la práctica, 2M a partir de la versión 1.1, en las operaciones que afectan a varios sectores dedisco consecutivos, se ve obligado a detectar con antelación el futuro cruce de una frontera de DMA: en casode que se vaya a producir, el sector problemático es transferido a través del buffer intermedio del programa.La versión 1.0 de 2M desviaba INT 40h en vez de INT 13h y se limitaba a devolver la condición de errorcuando se iba a producir, para que el propio DOS en INT 13h llamara de nuevo con más cuidado.

2M podría haber sido creado como controlador de dispositivo que definiera nuevas letras de unidadpara soportar los nuevos disquetes; sin embargo resulta más intuitivo para el usuario continuar empleandolas unidades A: y B: habituales. Esto se consigue, como hemos visto, modificando la INT 13h de la BIOS,lo que además permite el funcionamiento de ciertas utilidades de bajo nivel en los nuevos disquetes;realmente, en el mundo del PC no hay casi programas de utilidad a bajo nivel con el disco. Salvo loscopiones, la mayoría de los llamados programas de bajo nivel en materia de disquetes se limitan a llamar a

Page 317: PCA, PS2 ,IBM y AT

317EL HARDWARE DE APOYO AL MICROPROCESADOR

la BIOS. La técnica de ampliar la funcionalidad de la INT 13h de la BIOS es, por tanto, la más eficiente.

El listado que comentaremos es sólo la parte importante del programa. Desde 2M 3.0 ya no haylistados con partes repetidas: un único fichero 2M.ASM produce 2M.COM (sistemas AT) y 2MX.COM (enPC/XT) por medio del ensamblaje condicional. Para ello se apoya en 2MKERNEL.INC, núcleo principal contodo el código de acceso a la controladora para soportar los discos 2M, y también empleado para generar2M.SYS (versión driver para AT) y 2MFBOOT.BIN (con código SuperBOOT para el formateador). Tambiénse utiliza 2MUTIL.INC para englobar ciertas rutinas de utilidad comunes a más programas de la aplicación.Aquí nos limitaremos a comentar 2MKERNEL.INC, ya que lo restante no está relacionado con lacontroladora de discos.

2M puede controlar las unidades de disco A: y B: si son de alta densidad (de lo contrario se limitaa invocar a la INT 13h original). Por ello, además de un juego de variables globales, hay una estructura quedefine las variables propias de una unidad que se emplea para crear dos áreas de datos particulares, una para

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 19.58] 19.58 10 1024 ( 3) 0 1 0x04 0x00 0x00[ 37.44] 17.86 11 1024 ( 3) 0 1 0x04 0x00 0x00[ 55.31] 17.87 1 1024 ( 3) 0 1 0x04 0x00 0x00[ 73.18] 17.87 2 1024 ( 3) 0 1 0x04 0x00 0x00[ 91.05] 17.87 3 1024 ( 3) 0 1 0x04 0x00 0x00[ 108.91] 17.86 4 1024 ( 3) 0 1 0x04 0x00 0x00[ 126.79] 17.87 5 1024 ( 3) 0 1 0x04 0x00 0x00[ 144.65] 17.86 6 1024 ( 3) 0 1 0x04 0x00 0x00[ 162.52] 17.87 7 1024 ( 3) 0 1 0x04 0x00 0x00[ 180.39] 17.87 8 1024 ( 3) 0 1 0x04 0x00 0x00[ 198.26] 17.87 9 1024 ( 3) 0 1 0x04 0x00 0x00[ 217.85] 19.59 10 1024 ( 3) 0 1 0x04 0x00 0x00[ 235.71] 17.86 11 1024 ( 3) 0 1 0x04 0x00 0x00[ 253.71] 18.00 1 1024 ( 3) 0 1 0x04 0x00 0x00[ 271.57] 17.86 2 1024 ( 3) 0 1 0x04 0x00 0x00[ 289.44] 17.87 3 1024 ( 3) 0 1 0x04 0x00 0x00[ 307.43] 17.99 4 1024 ( 3) 0 1 0x04 0x00 0x00[ 325.43] 17.99 5 1024 ( 3) 0 1 0x04 0x00 0x00[ 343.42] 17.99 6 1024 ( 3) 0 1 0x04 0x00 0x00[ 361.28] 17.87 7 1024 ( 3) 0 1 0x04 0x00 0x00[ 379.16] 17.87 8 1024 ( 3) 0 1 0x04 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 33.95] 33.95 3 2048 ( 4) 0 1 0x04 0x00 0x00[ 45.32] 11.37 7 512 ( 2) 0 1 0x04 0x00 0x00[ 79.14] 33.82 4 2048 ( 4) 0 1 0x04 0x00 0x00[ 112.94] 33.80 1 2048 ( 4) 0 1 0x04 0x00 0x00[ 146.76] 33.82 5 2048 ( 4) 0 1 0x04 0x00 0x00[ 180.58] 33.82 2 2048 ( 4) 0 1 0x04 0x00 0x00[ 198.97] 18.39 6 1024 ( 3) 0 1 0x04 0x00 0x00[ 232.78] 33.82 3 2048 ( 4) 0 1 0x04 0x00 0x00[ 244.16] 11.37 7 512 ( 2) 0 1 0x04 0x00 0x00[ 277.97] 33.81 4 2048 ( 4) 0 1 0x04 0x00 0x00[ 311.78] 33.81 1 2048 ( 4) 0 1 0x04 0x00 0x00[ 345.60] 33.81 5 2048 ( 4) 0 1 0x04 0x00 0x00[ 379.42] 33.82 2 2048 ( 4) 0 1 0x04 0x00 0x00[ 397.80] 18.38 6 1024 ( 3) 0 1 0x04 0x00 0x00[ 431.62] 33.82 3 2048 ( 4) 0 1 0x04 0x00 0x00[ 443.00] 11.38 7 512 ( 2) 0 1 0x04 0x00 0x00[ 476.95] 33.95 4 2048 ( 4) 0 1 0x04 0x00 0x00[ 510.75] 33.81 1 2048 ( 4) 0 1 0x04 0x00 0x00[ 544.57] 33.82 5 2048 ( 4) 0 1 0x04 0x00 0x00[ 578.40] 33.83 2 2048 ( 4) 0 1 0x04 0x00 0x00[ 596.79] 18.38 6 1024 ( 3) 0 1 0x04 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

LECTURA DE ID’s EN 3½-HD (FORMATO NORMAL Y DE MAXIMA CAPACIDAD)

cada disquetera. A lo largo de la mayoríadel código residente, el registro SI estaráapuntando a esa zona de variables localesde la disquetera que se trate. Al principiodel programa está la rutina que controla lainterrupción 2Fh, empleada para gestionarla autodetección en memoria del programaresidente y permitir su posible futuradesinstalación.

La rutina que controla la INT 13hó INT 40h es más importante. Su laborconsiste en pasar el control de lasfunciones 2 (lectura), 3 (escritura), 4(verificación) y 5 (formateo) a 2M (si eldisquete introducido es de este tipo) o a lainterrupción original (si el disqueteintroducido no es de tipo 2M). Existe unavariable por cada unidad que indica en todomomento si el disquete introducido es detipo 2M (control2m_flag=ON) o no. Otrocometido consiste en detectar los cambiosde disco, para actualizar dicha variable enconsecuencia. Ante el primer cambio dedisco detectado se retorna con un error 6(porque así lo hace la BIOS original).

En el caso de la función de formateo (no implementada en el código SuperBOOT por falta deespacio), se mira si quien la invoca solicita un formateo normal o si se trata de una petición de formateo dedisquete 2M. Esto es debido a que 2M aumenta la funcionalidad de la función 5 original de la BIOS parasoportar los nuevos disquetes. En la función de la BIOS, se indica en AL el número de sectores de la pista,en CH la pista, en DH el cabezal, en DL la unidad y en ES:BX se apunta a un buffer con información paraformatear. Cuando está 2M residente y se invoca la función 5 con el registro SI=324Dh (SI="2M") y conAL=7Fh, se le indica a 2M que no llame a la función de formateo original de la BIOS y que formatee él lapista en la unidad y cabezal indicados. En este caso AL es ignorado, ya que en ES:BX lo que se le pasa ala BIOS (es decir, a 2M) no es la dirección de tabla alguna sino el sector de arranque del futuro disquete,que contiene toda la información necesaria sobre la estructura del disco para poder clonarlo. No hay que creartablas ni emplear otras funciones BIOS para seleccionar densidad ni nada por el estilo. Tampoco hay queconsiderar la complejidad de los formatos 2M (en los que difiere la primera pista de las restantes): de todose ocupa el código residente del propio 2M. La rutina format_2m invocada desde ges_int13 se encarga del

Page 318: PCA, PS2 ,IBM y AT

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

formateo. Primero se llama a la INT 13h original (previa a 2M) para solicitar un formateo en el cabezal 2,inexistente, con objeto de que retorne rápidamente ante el error. Así, se avisa a todos los demás programasresidentes de que el disco va a ser formateado: el propio DOS invalida los buffers asociados al viejo disquete;si 2M no tomara esta medida, al hacer DIR sobre el disco recién formateado aparecería aún, falsamente, sucontenido previo. A continuación realiza las siguientes tareas: toma nota de los parámetros del futuro disco,pone en marcha el motor, lleva el cabezal a la pista, crea la tabla con información para el formateo, formateala pista y retorna con el código de error o éxito correspondiente. En los formatos de máxima capacidad,recuérdese que había que escribir la pista tras el formateo, para evitar que la primera lectura diera error y paracompletar realmente el proceso. Sin embargo, el código residente de 2M no escribe nada tras el formateo.Esto permite en este caso a los programas de copia de disquetes poder ir escribiendo el disco destino a la vezque formatean; lo contrario sería una pérdida de tiempo con una escritura muerta. En el caso de programasque sólo formateen, tendrán además que escribir; esto implica que esos programas deben estar diseñados paraformatear disquetes 2M (nadie ha dicho que el FORMAT del DOS pudiera hacerlo por sí solo).

El procedimiento detecta_cambiodetermina si se ha producido un cambio dedisco. En caso de que se haya producido (ola primera vez absoluta que se ejecuta larutina tras haber instalado 2M en memoria)se intenta leer el sector de arranque delmismo para determinar la densidad delmismo y averiguar si es de tipo 2M.Primero se intenta bajar la línea de cambiode disco: si no fuera posible, es que launidad está sin disquete introducido. Elacceso se intenta tres veces, con todas lasdensidades posibles (500, 300, 250 Kbit/segy finalmente 1 Mbps). Si no se pudiera leerel sector de arranque, podría deberse a quees un disco sin formatear, o tratarse de otromedio físico, por lo que se le devuelve elcontrol a la INT 13h original hasta unfuturo nuevo cambio de disco. Esto mismopuede suceder si se consigue leer el sectorde arranque y la rutina set_info compruebaque el disco es estándar del DOS. Cuandono hay disco en la unidad y se falla albajar la línea de cambio, se delega elcontrol a la BIOS pero si ésta logra bajarla

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 31.72] 31.72 2 1024 ( 3) 0 1 0x05 0x00 0x00[ 63.27] 31.55 3 1024 ( 3) 0 1 0x05 0x00 0x00[ 103.25] 39.98 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 134.76] 31.51 5 1024 ( 3) 0 1 0x05 0x00 0x00[ 166.35] 31.59 1 1024 ( 3) 0 1 0x05 0x00 0x00[ 197.98] 31.63 2 1024 ( 3) 0 1 0x05 0x00 0x00[ 229.53] 31.55 3 1024 ( 3) 0 1 0x05 0x00 0x00[ 269.51] 39.98 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 301.01] 31.50 5 1024 ( 3) 0 1 0x05 0x00 0x00[ 332.61] 31.60 1 1024 ( 3) 0 1 0x05 0x00 0x00[ 364.24] 31.63 2 1024 ( 3) 0 1 0x05 0x00 0x00[ 395.79] 31.55 3 1024 ( 3) 0 1 0x05 0x00 0x00[ 435.77] 39.98 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 467.27] 31.50 5 1024 ( 3) 0 1 0x05 0x00 0x00[ 498.86] 31.59 1 1024 ( 3) 0 1 0x05 0x00 0x00[ 530.59] 31.72 2 1024 ( 3) 0 1 0x05 0x00 0x00[ 562.13] 31.54 3 1024 ( 3) 0 1 0x05 0x00 0x00[ 602.12] 39.99 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 633.62] 31.50 5 1024 ( 3) 0 1 0x05 0x00 0x00[ 665.22] 31.60 1 1024 ( 3) 0 1 0x05 0x00 0x00[ 696.85] 31.63 2 1024 ( 3) 0 1 0x05 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

Longitud (ms) Sector Tamaño Cilindro Cabeza ST0 ST1 ST2-

[ 56.44] 56.44 3 2048 ( 4) 0 1 0x05 0x00 0x00[ 112.90] 56.46 1 2048 ( 4) 0 1 0x05 0x00 0x00[ 143.63] 30.73 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 158.92] 15.29 2 512 ( 2) 0 1 0x05 0x00 0x00[ 165.85] 6.93 0 128 ( 0) 0 1 0x05 0x00 0x00[ 222.30] 56.45 3 2048 ( 4) 0 1 0x05 0x00 0x00[ 278.75] 56.45 1 2048 ( 4) 0 1 0x05 0x00 0x00[ 309.49] 30.73 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 324.78] 15.29 2 512 ( 2) 0 1 0x05 0x00 0x00[ 331.70] 6.92 0 128 ( 0) 0 1 0x05 0x00 0x00[ 388.16] 56.46 3 2048 ( 4) 0 1 0x05 0x00 0x00[ 444.61] 56.45 1 2048 ( 4) 0 1 0x05 0x00 0x00[ 475.34] 30.73 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 490.63] 15.29 2 512 ( 2) 0 1 0x05 0x00 0x00[ 497.55] 6.92 0 128 ( 0) 0 1 0x05 0x00 0x00[ 554.01] 56.45 3 2048 ( 4) 0 1 0x05 0x00 0x00[ 610.46] 56.45 1 2048 ( 4) 0 1 0x05 0x00 0x00[ 641.19] 30.73 4 1024 ( 3) 0 1 0x05 0x00 0x00[ 656.48] 15.29 2 512 ( 2) 0 1 0x05 0x00 0x00[ 663.41] 6.93 0 128 ( 0) 0 1 0x05 0x00 0x00[ 719.86] 56.45 3 2048 ( 4) 0 1 0x05 0x00 0x00

Una tecla para leer más ID’s [ESC=salir].

LECTURA DE ID’s EN 5¼-DD (FORMATO NORMAL Y DE MAXIMA CAPACIDAD)

(¿controladora no compatible?) se le vuelve a robar el control al siguiente acceso. Esta artimaña permitió aversiones antiguas de 2M funcionar en máquinas 486 (cuando no se tomaba la precaución de hacer un retardoal resetear la controladora y ésta quedaba en ocasiones atontada, hasta que la BIOS del sistema la reseteababien). En caso de ser un disco 2M se anotan las características del mismo, teniendo en cuenta que lo queacabamos de leer es precisamente su sector de arranque... Como 2M es el encargado de detectar la densidaddel disco, es necesario que ajuste las variables de la BIOS indicando dicha densidad, ya que ella será laencargada de controlar los disquetes normales. En realidad, la densidad sólo se ajusta en el primer acceso aldisco, existiendo dos variables en el área de datos de la BIOS, en el segmento 40h, que indican la densidada emplear en cada disquetera: si dichas variables no están correctamente inicializadas, al conmutar de unaunidad a otra la BIOS no seleccionaría la velocidad correcta y se produciría un error. Como al introducir undisco nuevo en la unidad lo primero que hace el DOS es consultar su sector de arranque, las primerasversiones de 2M dejaban la tarea de detectar la densidad del disco a la propia BIOS (espiando las lecturasdel sector de arranque que ésta realizaba para determinar el tipo de disco y decidir si robar el control o no).Sin embargo, ciertas BIOS de prestigiosa marca italiana (yo sólo conozco una) hacían cosas muy raras paradeterminar la densidad de los discos (como ir leyendo varias pistas consecutivas) y tropezaban con los

Page 319: PCA, PS2 ,IBM y AT

319EL HARDWARE DE APOYO AL MICROPROCESADOR

disquetes 2M. Esto es un botón de muestra de lo que pasa cuando los fabricantes europeos modifican mallas BIOS de los taiwaneses, para no copiarlas del todo. De ahí que la versión definitiva del programareemplace en esta tarea a la BIOS. Sin embargo, en caso de que 2M no pueda determinar la densidad de launidad sique delegando el control a la BIOS: el motivo es mantener la compatibilidad con otros soportesextraños. Este es también el motivo por el que 2M no sustituye totalmente el código BIOS de INT 13h, quehubiera dado menos problemas a la hora de programar (aunque el programa resultante ocuparía también algomás de memoria).

COPY DE 21 FICHEROS Y 1.457.664 BYTES

Formato 1.44 1.44 1.64 1.72 1.80 1.88

Formateador FORMAT FDFORMAT FDFORMAT FDFORMAT 2MF 2MF

Tiempo escritura 1:17.27 1:06.72 1:00.74 1:27.05 1:15.30 1:23.93

Tiempo lectura 0:59.82 0:48.50 0:44.11 1:05.69 0:43.78 0:54.16

Espacio libre 0 0 203,776 287,744 370,688 454,656

Escritura (Kb/s) 18.42 21.34 23.44 16.35 18.90 16.96

Lectura (Kb/s) 23.80 29.35 32.27 21.67 32.51 26.28

Promedio (Kb/s) 21.11 25.35 27.86 19.01 25.71 21.62Indice relativo 100.00 120.09 131.98 90.05 121.79 102.42

Notas:Ficheros: 2 de 256K, 3 de 128K, 4 de 64K, 5 de 32K, 6 de 16K y 1 de 15.5K.Prueba bajo DOS 6.2 y con solo 2M y FDREAD instalados.La prueba de escritura consistía en COPY C:\TEST\*.* B: y la de lectura consistía en COPY /B *.* NULAl leer del disco duro se perdieron 5.5 segundos que han sido ya descontados; el disco ya estaba girando.Con FDFORMAT se emplearon siempre los parámetros /X:2 e /Y:3 para lograr la mayor velocidad posible.

La rutina calc_chk es quien realmente realiza el checksum del sector de arranque, comprobandoademás si el disco es de tipo 2M. La rutina set_err, invocada al final del formateo y desde la rutina queaccede directamente a los sectores de disco, analiza el código de error devuelto por el controlador dedisquetes y lo convierte a la notación de errores de la BIOS. Set_bios_err copia el resultado del acceso adisco a las variables propias de la BIOS por razones de compatibilidad con el software de disco de bajo nivel.

En el procedimiento control_2m se realiza la gestión a alto nivel del acceso a disco: es aquí dondese emula la existencia de la segunda copia de la FAT apoyándose en la primera, así como el sector dearranque virtual ubicado en el primer sector físico de la FAT2. Como 2M 2.0 apareció cuando ya estababastante extendida la versión anterior, se hizo necesario (y lo sigue siendo en 2M 3.0) continuar soportandolos discos antiguos. En ellos, se sigue leyendo el sector de arranque físico en lugar del virtual, que no existe,y se permite su escritura si es correcto (si no se intentan tocar partes sensibles del mismo). Así mismo setiene en cuenta el acceso al cabezal 128 ó 129 para acceder en ese caso al 0 ó al 1 sin emulaciones. Lascoordenadas de la BIOS, en la forma cilindro-cabezal-sector son traducidas momentáneamente a las del DOSpara simplificar el proceso. También se comprueba si el checksum (o suma de comprobación) del sector dearranque, realizado con anterioridad en set_info, es correcto. Es difícil que no lo sea, porque el código de2M no deja a cualquiera escribir sobre el sector de arranque físico. Pero si no lo fuera, se devuelve un seekerror al programa que llama a la INT 13h, habiéndose elegido este código porque no había otro másdescriptivo en la lista de errores de disco de la BIOS. Si al ejecutar un comando DIR sobre un disquete 2Maparecen errores de seek ya sabrá el lector por qué...

El procedimiento ejecuta_io es llamado repetidamente desde control_2m para realizar la lectura oescritura de un conjunto de sectores. Este procedimiento es el más complicado de todo el programa, pero latarea que realiza es relativamente sencilla. Primero, vuelve a pasar las coordenadas del disco del formato DOSal formato físico propio de la BIOS. Hay que tener en cuenta que un sector físico en un disquete 2M puedeser de 512 bytes, pero también de 1024 ó 2048. Por tanto, introducimos aquí el concepto de sección parahacer referencia a 512 bytes, que a fin de cuentas es la unidad de medida a emplear.

Page 320: PCA, PS2 ,IBM y AT

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

En el caso de los formatos de mayor capacidad (2MF /M) se accede de sector en sector físico, ya quelas operaciones de lectura/escritura de varios sectores en bloque sólo tienen sentido cuando éstos están losuficientemente separados pero sin pasarse. En nuestro caso están excesivamente separados, ya que lanumeración es discontinua (interleave 1:2) y entre dos sectores de número consecutivo hay otro; por tanto,no se ganaría rendimiento en un acceso multisector; por otro lado, algunos formatos de disco tienen unnúmero par de sectores en las pistas y dos de ellos tienen que tener forzosamente el número consecutivo, conlo que fallaría el acceso multisector debido a la excesiva proximidad en este caso; además, no está muy clarosi se podrán acceder de esta manera sectores que no sean del mismo tamaño (no me molesté en probarlo).La lectura es la operación más sencilla: se extrae del disco el sector físico donde está incluida la sección quetoca leer y después se copia a la dirección de memoria definitiva. No se puede leer el sector directamente enel buffer requerido por el programa que invoca la INT 13h, ya que éste podría requerir sólo 512 bytes (o unmúltiplo impar de esta cifra) y los sectores físicos podrían exceder este tamaño, afectando a zonas nopermitidas de la memoria ubicadas tras el buffer. Por tanto se utiliza un buffer intermedio (definido con untamaño de 2 Kb para acomodar el mayor sector posible). El movimiento de la sección a su ubicacióndefinitiva no es una tarea muy costosa, ya que en un ordenador medio se ejecuta unas cien veces más rápido

MAPAMEM 2.1- Información sobre la memoria del sistema.

Tipo Ubicación Tamaño PID Propietario-------- --------- ------- ----- ---------------Sistema 0000-003F 1.024 InterrupcionesSistema 0040-004F 256 Datos del BIOSSistema 0050-0105 2.912 Sistema Operat.Sistema 0107-0143 976 0008Sistema 0145-0144 0 0008Sistema 0146-0149 64 0008Programa 014B-015A 256 014B 4DOSEntorno 015C-0174 400 0176 MAPAMEMPrograma 0176-01C9 1.344 0176 MAPAMEMLibre 01CB-9FFE 648.000 0000 <Nadie>Sistema A000-D3B4 211.792 0008Sistema D3B6-D3C2 208 D3B6Sistema D3C4-D50D 5.280 D3C4Sistema D50F-E437 62.096 0008Sistema E439-E49C 1.600 E439Sistema E49E-E4AD 256 E49ESistema E4AF-E4CE 512 E4AFSistema E4D0-E55E 2.288 E4D0Sistema E560-E568 144 E560Datos E56A-E631 3.200 014B 4DOSEntorno E633-E672 1.024 014B 4DOSLibre E674-E68C 400 0000 <Nadie>Programa E68E-E810 6.192 E68E SHAREPrograma E812-E97A 5.776 E812 PRINTEntorno E97C-E996 432 E998 VIDRAMPrograma E998-EA04 1.744 E998 VIDRAMEntorno EA06-EA1F 416 EA21 UNIVESAPrograma EA21-EBF1 7.440 EA21 UNIVESAPrograma EBF3-EC1D 688 EBF3 KEYBSPPrograma EC1F-EC77 1.424 EC1F RCLOCKPrograma EC79-EDBB 5.168 EC79 2MPrograma EDBD-EDD8 448 EDBD DISKLEDLibre EDDA-EDF3 416 0000 <Nadie>Programa EDF5-F281 18.640 EDF5 DATAPLUSPrograma F283-F34D 3.248 F283 HBREAKPrograma F34F-F354 96 F34F TDSK(D)Datos F356-FB55 32.768 F34F TDSK(D)Libre FB57-FFA5 17.648 0000 <Nadie>

MEMORIA OCUPADA POR 2M

que lo que ha tardado la lectura desde el disco. Este procesode lectura se repite tantas veces como secciones haya quetransferir. En todo momento, unas variables indican qué sectorfísico (y de qué cilindro, cabezal y unidad) está en el buffer.De este modo, por ejemplo, cuando se lee un sector de 2 Kbpara transferir su primera sección, se traen a la memoria 4secciones de golpe y ya no serán necesarios más accesos adisco si hubiera que transferir también las 3 restantes, porqueel sector en que están ya se encuentra en el buffer. La escrituraes algo más compleja, y hay que distinguir dos casos: por unlado, cuando hay que volcar a disco un número de seccionesconsecutivas suficientes para completar un sector físico; porotro, cuando hay que escribir una o varias secciones que nocompletan un sector físico. En el primer caso, se escribe sinmás; en el segundo caso es necesario leer el sector al buffer,modificar sólo la(s) seccion(es) afectada(s) y escribirlo en eldisco. Este último caso supone una fuerte degradación de lavelocidad, ya que tras leer un sector del disco habrá que volvera escribirlo, hecho que no ocurrirá hasta la siguiente vueltadel mismo. Por fortuna, cuando se hace un COPY el DOSenvía grandes bloques, lo que en la mayoría de los casos (noen todos) provoca escrituras de pistas completas, tarea en laque no se pierde un ápice de rendimiento. No obstante, esta

arquitectura de los disquetes 2M provoca que sean notablemente más lentos escribiendo que leyendo.

En los formatos normales (2MF /F) todos los sectores de la pista son del mismo tamaño, lo quetambién sucede en la primera pista de los formatos de más capacidad. Están suficientemente separados ynumerados consecutivamente. Por tanto, una acceso multisector es posible y más que interesante. Aquí nosólo no se emplea el buffer intermedio sino que además no se puede, porque el acceso multisector puedesuperar los 2 Kb de capacidad del buffer. La transferencia se hace directamente sobre la dirección deseadapor el programa que invoca la INT 13h. Sólo hay un par de excepciones: cuando la primera sección atransferir es la segunda mitad de un sector (recordemos que son de 1 Kb) y cuando la última sección es laprimera mitad de un sector. En ambos casos se emplea el buffer intermedio por el mismo motivo de siempre:evitar la alteración de zonas de memoria que vayan detrás del buffer suministrado por el programa que llamaa la INT 13h. Sobre la escritura se podrían hacer las mismas consideraciones que hacíamos con los formatosde máxima capacidad. En la operación de acceso multisector hay que considerar también el posible cruce delbuffer suministrado por el programa principal con una frontera de DMA: la rutina acceso_multi se encarga,llegado el momento, de transferir el sector crítico a través del buffer intermedio, segmentando la operaciónen tres fases (los sectores anteriores, el sector que cruza la frontera y los restantes). No controlar los

Page 321: PCA, PS2 ,IBM y AT

321EL HARDWARE DE APOYO AL MICROPROCESADOR

problemas con el DMA provoca que el ordenador se cuelgue al hacer COPY de un fichero mediano (o quelo copie mal en cualquier caso). Obviamente, el buffer intermedio se inicializa para que nunca cruce unafrontera de DMA. El único caso en que acceso_multi no necesita tomar precauciones con el DMA es en elcódigo SuperBOOT: aunque se instale desde la INT 13h, lo hace antes de la carga del sistema operativo (queserá el encargado de arreglar los problemas con el DMA).

Por tanto, en ejecuta_io es donde se toman todas las complicadas decisiones sobre cómo y dóndecargar/grabar de disco. He de agradecer aquí a Edgar Swank su colaboración en detectar y corregir erroresen esta compleja rutina, proponiéndome además las modificaciones en el listado: antes de 2M 2.0, los discos2M no soportaban realmente la escritura con verificación (VERIFY ON a nivel DOS). La variable sector_finestá a 0 para indicar el acceso a un solo sector (sector_ini) o es distinta de cero para indicar el último sectorinvolucrado en el caso de accesos multisector (junto a sector_ini). Dentro de este procedimiento, la subrutinaacceso_secc se encarga de la transferencia de una sola sección.

El procedimiento trans_secc realiza las transferencias entre el buffer interno y la direcciónsuministrada por el programa que llama a la INT 13h. La rutina leido? comprueba si el próximo sector físicoa ser accedido esta ya en el buffer, para evitar una segunda lectura innecesaria.

El procedimiento acceso_sector se encarga de hacer ciertas tareas como determinar la longitud delsector a ser leído (para poder programar luego correctamente el FDC), llevar el cabezal a la pista adecuada,cargar los registros convenientemente según haya que emplear el buffer intermedio o no, llamar a la rutinaque accede realmente al disco y tomar nota de qué sector ha sido recién leído (para evitar futuras lecturasinnecesarias).

En num_secciones se calcula elnúmero de secciones o bloques de 512bytes del sector físico en curso, apoyándoseen la información del sector de arranquedel disquete que fue anotada cuando se lereconoció por vez primera.

La rutina motor_ok arranca elmotor de la unidad si aún no estaba enmarcha. En caso de estar parado, o dellevar poco tiempo encendido a causa deuna reciente lectura de la línea de cambio

[14464/109040] C:\>dir b:

Volume in drive B is unlabeled Serial number is EA82:3F1BFile not found "B:\*.*"

0 bytes in 0 file(s)1.912.320 bytes free

[14464/109040] C:\>diskcopy b: b:

Inserte el disquete de ORIGEN en la unidad B:

Presione cualquier tecla para continuar . . .

Copiando 82 pistas23 sectores por pista, 2 cara(s)

Inserte el disquete de DESTINO en la unidad B:

Presione cualquier tecla para continuar . . .

LA COMPATIBILIDAD DE 2M ES PRACTICAMENTE DEL 100%

de disco (el contador de tiempo que resta para su detención es aún muy alto) se hace la pausa pertinente paraque alcance el régimen de rotación adecuado. Esta rutina es invocada en varias ocasiones; entre otras, desdeejecuta_io.

En reset_drv se inicializa el FDC enviándole el comando Specify; la situacion de reset es mantenidadurante unos microsegundos, pausa que también realizan las BIOS modernas, ya que en algunas versionesde 2M anteriores a la 1.3 se comprobó que no lograban resetear la controladora en algunas máquinas 486 (enestos casos no se detectaba el tipo del nuevo disco introducido en la disquetera y, al delegar el control a laBIOS, ésta generaba errores de sector no encontrado y anomalía general con los disquetes 2M).

La rutina seek_drv posiciona el cabezal seleccionado sobre el cilindro adecuado: si ya estaba sobreél (por haber accedido con anterioridad a la otra cara del disco) no es necesario esperar a que el cabezal dejede vibrar; en caso de que haya que hacer esta pausa se establecen 1 ms para el caso de la lectura (no es muypeligroso que se produzca un error, ya que la operación se reintentaría) y 15 ms para la escritura, asegurandoen este último caso el éxito de la operación, ya que escribir con el cabezal no asentado podría dañar lainformación del disco. El disco está formateado (salvo en los los formatos de máxima capacidad, que son unmundo aparte) con ciertos deslizamientos en la numeración de los sectores al conmutar de cilindro y cabezal(opciones /X e /Y del formateador) de tal manera que el acceso en escritura es factible en una sola vuelta del

Page 322: PCA, PS2 ,IBM y AT

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

disco para todas las pistas a las que se acceda consecutivamente. Rebajar a 1 ms en el caso de la lectura tienepor objeto asegurar esto mucho más todavía. Así, algún ordenador muy extraño que pinchara en los índicesde rendimiento a la hora de escribir probablemente no lo haría, al menos, al leer. Como un posicionamientodel cabezal precede siempre a las operaciones de lectura o escritura (seek_drv), se selecciona aquí lavelocidad de transferencia a emplear, acorde con la densidad de la pista a ser accedida (set_rate). En casode que la unidad precisara recalibración (debido a algún reset anterior) se llama desde aquí al procedimientorecalibrar.

El procedimiento sector_io es quien finalmente se encarga de hacer la lectura o escritura del sectoro sectores necesarios, programando el FDC. Se calcula el tamaño en bytes del bloque a transferir, seprograma el DMA por medio de las rutinas calc_dir_DMA y prepara_DMA y se envía el comandoadecuado al FDC (lectura/escritura). Al final, se anotan los resultados. La subrutina calc_dir_DMA traducela dirección segmentada al formato necesario para programar el DMA; en el código SuperBOOT tiene quedevolver además un posible error de cruce de frontera de DMA, ya que el código de 2M no evita las llamadasilegales en este caso.

En genera_info se construye la tabla de información a enviar al DMA para formatear la pistasolicitada en la función de formateo de 2M. Esta información se obtiene a partir del sector de arranque delfuturo disco, suministrado por el programa que intenta formatear. Conociendo cómo esta estructurado dichosector, la arquitectura de los disquetes 2M y qué necesita el comando del FDC para formatear se puedeentender cómo funciona la rutina, por lo que no nos detendremos en analizarla. Es formatea_pista elprocedimiento que formatea la pista a partir de la tabla creada por la rutina anterior.

La subrutina espera_int espera durante no más de 2 segundos la llegada de una interrupción dedisquete que señalice el final de una operación con el FDC. Conviene no esperar indefinidamente porque sila unidad no está preparada podría tardar muchísimo en devolver la interrupción. Así, se detecta en un tiemporazonable la circunstancia y posteriormente se reseteará la controladora (ante el error) para arreglar elproblema de la interrupción pendiente (y del FDC que no respondía). Fdc_read y fdc_write se encargan derecibir y enviar bytes al FDC, típicamente órdenes y resultados. Ambas rutinas también tienen control timeout,en este caso de 2 milisegundos; al principio de las mismas se realiza una brevísima pausa al igual que hacenlas BIOS AMI de 486 (que para algo servirá). Finalmente, las subrutinas fdc_respiro y retardo efectúan unapausa de 60 µs y AX milisegundos, respectivamente, apoyándose repetitivamente en la macro pmicro, quepierde unos 15,09 microsegundos muestreando los ciclos de refresco de memoria del AT. Pmicro no es unasubrutina (salvo en el caso del código SuperBOOT, por razones de espacio) porque el CALL y RETasociados podrían ralentizar la monitorización de los ciclos de refresco de manera excesiva en los ordenadoresmás lentos, deparando un retardo efectivo superior.

Finalmente, initcode será invocada sólo desde el sector de arranque físico durante el arranque desdedisquete, con objeto de inicializar ciertas variables y activar el código SuperBOOT. Una precauciónimportante es que, ensamblando para obtener código SuperBOOT, éste tiene que ocupar exactamente 2560bytes (5 sectores). Ciertamente, entra muy justo... pero cabe, con alguna que otra artimaña (excluir rutinasde formateo, utilizar subrutinas en vez de macros, simplificar la gestión de las fronteras de DMA, etc) aunquelos 5 sectores que ocupa impiden ubicarlo en discos de doble densidad. Pero, ¿quién va a querer hacer botableun disco 2M de doble densidad, cuando uno estándar de alta tiene más capacidad?.

;;;;;;;;; 2MKERNEL - (C) Ciriaco García de Celis.;; NUCLEO RESIDENTE DE 2M UTILIZADO POR SUS PRINCIPALES EJECUTABLES;; Los siguientes símbolos se utilizan; para el ensamblaje condicional:;; XT -> Indica que el código ejecutable es para PC/XT y no posee; instrucciones de 286 ni utiliza recursos hardware de AT.;; SUPERBOOT -> Indica que el código ejecutable se ensambla para; ocupar 2560 bytes exactamente (para autoarranque).;;

; ------------ Códigos de modos y órdenes del DMA y del FDC.

F_READ EQU 46h ; modo DMA para lecturaF_WRITE EQU 4Ah ; modo DMA para escrituraF_VERIFY EQU 42h ; modo DMA para verificaciónF_FORMAT EQU 01001101b ; orden de formateo del FDC

; ------------ Estructura de datos con información para cada unidad.

info_drv STRUCmaxs EQU 13 ; máximo 13 sectores físicos/pistatipo_drv DB ? ; tipo de la disquetera (0 = no hay)control2m_flag DB OFF ; a ON si 2M controla la unidadcambio DB ON ; a ON indica cambio de soporteversion_fmt DB ? ; versión del formato de disco 2Mmulti_io DB ? ; a 0 si posible acceso multi-sectorchk DB ? ; a 0 si checksum del sector 0 Okvunidad EQU THIS WORDvunidad0 DB ? ; velocidad pista 0vunidadx DB ? ; velocidad demás pistasgap DB ? ; GAP entre sectores (leer/escribir)sectpista DB ? ; sectores lógicos por pista

Page 323: PCA, PS2 ,IBM y AT

323EL HARDWARE DE APOYO AL MICROPROCESADOR

tabla_tsect DB maxs DUP (?) ; tamaños de sectores 1, 2, ..., Ntam_fat DB ? ; sectores/FAT en la unidad

ENDS

; ------------ Variables del programa.

info_ptr DW info_A ; punteros a datos de las unidadesDW info_B

IFDEF SUPERBOOTDB "30" ; Versión 2MFBOOT 3.0ENDIF

id_sistema DB "2M-STV" ; identificación de disco 2MIFDEF XT

tbase DW ? ; base de tiempos para retardosENDIF

unidad DB ? ; unidad física de disco en cursonumsect DW ? ; sectores a transferirsectini DW ? ; primer sector DOS a transferircilindro DB ? ; cilindro del disco a accedercabezal DB ? ; cabezal a emplearsector DB ? ; número de sector físicosector_ini DB ? ; número de sector físico inicialsector_fin DB ? ; número de sector físico finalseccion DB ? ; parte del sector físico en cursosecciones DB ? ; sectores lógicos a transferirtsector DB ? ; LOG2 (tamaño de sector) - 7buffer DW buffer_io ; puntero al buffer intermediobuf_unidad DB ? ; unidad del sector en el bufferbuf_cilcab DW ? ; cilindro/cabezal de sector bufferbuf_sector DB ? ; número de sector en el bufferstatus DB ? ; resultado de los accesos a discofdc_result DB 7 DUP (?) ; bytes de resultados del FDCorden DB ? ; operación F_READ/F_WRITE/F_VERIFYtab_ordenes DB F_READ

DB F_WRITEDB F_VERIFY ; órdenes 2, 3 y 4

; --- Interpretación BIOS de los bits de ST1

lista_errs DB 4 ; ’sector not found’DB 0DB 10h ; ’bad CRC’DB 8 ; ’DMA overrun’DB 0DB 4 ; ’sector not found’DB 3 ; ’write-protect error’DB 2 ; ’address mark not found’DB 20h ; en otro caso: ’bad NEC’

info_A info_drv <> ; datos de A:info_B info_drv <> ; datos de B:

; ***************************************; * *; * C O D I G O R E S I D E N T E *; * *; ***************************************

; ------------ Rutina de gestión de INT 2Fh.

IFNDEF SUPERBOOT ; Código SuperBOOT no soporta INT 2Fh

ges_int2F PROC FARSTICMP AH,CS:multiplex_idJE preguntanJMP CS:ant_int2F ; saltar al gestor de INT 2Fh

preguntan: CMP DI,1992hJNE ret_no_info ; no llama alguien del convenioMOV AX,ESCMP AX,1492hJNE ret_no_info ; no llama alguien del convenioPUSH CSPOP ES ; sí llama: darle informaciónLEA DI,autor_nom_ver

ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"IRET

ges_int2F ENDP

ENDIF

; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h; original o a una nueva rutina de control para la; lectura (AH=2), escritura (AH=3) y verificación (AH=4); según el tipo de disco introducido. Ante una función de; formateo (AH=5) se entrega el control a la INT 13h; original. Se detecta un posible cambio de disco y se; retorna en ese caso con el correspondiente error. En el; código SuperBOOT no hay soporte para formatear.

IFNDEF SUPERBOOT

ges_int13 PROC FARSTICLDPUSHFCMP DL,2JAE ges13bios ; no es disquetera A: ó B:PUSH SICALL set_SI_drvCMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?JE ges_2mCMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?

ges_2m: POP SIJC ges13bios ; no es unidad de alta densidadCMP AH,2JB ges13bios ; no Read/Write/Verify/FormatCMP AH,5JA ges13bios ; no Read/Write/Verify/FormatCALL detecta_cambio ; ¿cambio de disco?JNC sin_cambioPOPFSTC ; hubo cambio:MOV AX,600hRET 2 ; retornar con error

sin_cambio: CMP AH,5JNE dilucida ; no es orden de formateoCALL leer_lin_cambJNZ format_bios ; no hay disquete en la unidadCMP AL,7FhJNE format_bios ; no es orden formateo de 2MCMP SI,"2M"JE format_2m ; es orden de formateo de 2M

format_bios: CLCCALL set_flag_STV ; CF = 0 -> indicar no 2M

dilucida: PUSH SICALL set_SI_drv ; SI -> variables de la unidadCMP CS:[SI].control2m_flag,OFF

POP SIJE ges13bios ; la unidad la controla la BIOSPOPFCALL control2m ; la controla 2MRET 2

ges13bios: POPFJMP CS:ant_int13 ; saltar al gestor de INT 13h

; --- Función de formateo implementada por 2M. En los; disquetes creados con /M todas las pistas salvo; la 0 deberían ser formateadas invocando INT 13h; de manera directa (con CALL) para evitar que se; ejecute cierto código de WINDOWS en el modo; protegido que provoca errores al formatear. Antes; de formatear la primera pista física del disco se; invoca la función de formateo de la INT 13h; original (con un error para provocar un rápido; retorno) con objeto de informar al DOS y a todos; los TSR previos del cambio de soporte.; El intento de formateo en la pista/cabezal 0 con; CL=255 sirve para simular un cambio de disco.

format_2m: POPFPUSH DS ; *XPUSHA ; **PUSH CSPOP DSMOV unidad,DLCALL set_SI_drvMOV cilindro,CHMOV cabezal,DHOR CH,DHJNZ format_trx ; no es cilindro 0 y cabezal 0INC CLJNZ no_f_chgMOV [SI].cambio,ON ; simular cambio de discoJMP fmt_exit

no_f_chg: XPUSHAMOV AL,1 ; formatear (AH=5)MOV CH,0MOV DH,2 ; en cabezal 2 (incorrecto)PUSHFCALL ant_int13 ; avisar al DOS del nuevo discoCLD ; mantener DF=0STCCALL reset_drv ; asegurar aceleración motorXPOPACALL set_info ; características nuevo soporte

format_trx: CALL genera_info ; tabla de información formateoCALL motor_ok ; asegurar que está en marchaCALL seek_drvCALL formatea_pistaPUSHFCLCCALL motor_off_cnt ; cuenta normal detención motorPOPFCALL set_errCALL set_bios_err ; no altera flags

fmt_exit: XPOPA ; **MOV AH,statusPOP DS ; *RET 2

ges_int13 ENDP

ELSE ; El código SuperBOOT no formatea

ges_int13 PROC FARSTICLDPUSHFPUSH SICMP DL,2JAE ges13bios ; no es disquetera A: ó B:CALL set_SI_drvCMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?JE ges_2mCMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?

ges_2m: JC ges13bios ; no es unidad de alta densidadCMP AH,2JB ges13bios ; no Read/Write/Verify/FormatCMP AH,5JA ges13bios ; no Read/Write/Verify/FormatJNE no_formatCALL set_flag_STV ; CF = 0 -> "disco no 2M"JMP ges13bios

no_format: CALL detecta_cambio ; ¿cambio de disco?JNC dilucidaPOP SIPOPFSTC ; hubo cambio:MOV AX,600hRET 2 ; retornar con error

dilucida: CMP CS:[SI].control2m_flag,OFFJE ges13bios ; la unidad la controla la BIOSPOP SIPOPFCALL control2m ; la controla 2MRET 2

ges13bios: POP SIPOPFJMP CS:ant_int13 ; saltar al gestor de INT 13h

ges_int13 ENDP

ENDIF

; ------------ A la entrada en DL se indica la unidad y a la salida se; devuelve SI apuntando sus variables sin alterar flags.

set_SI_drv PROCPUSHFPUSH BXMOV BL,DLMOV BH,0SHL BX,1MOV SI,CS:[BX+OFFSET info_ptr]POP BXPOPFRET

set_SI_drv ENDP

; ------------ Si CF=1, indicar disquete 2M presente. A la; entrada, DL indica la unidad de disco.

set_flag_STV PROCXPUSHACALL set_SI_drvMOV AL,ON ; indicar 2MJC tipo_stv_ok

Page 324: PCA, PS2 ,IBM y AT

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

MOV AL,OFF ; indicar no 2Mtipo_stv_ok: MOV CS:[SI].control2m_flag,AL

XPOPARET

set_flag_STV ENDP

; ------------ Devolver ZF=1 si cilindro y cabezal 0.

pista0? PROCPUSH AXMOV AL,cabezalOR AL,cilindroPOP AXRET

pista0? ENDP

; ------------ Devolver ZF=1 si la línea de cambio de disco está; inactiva. A la entrada, DL contiene la unidad. El; motor es puesto en marcha y, si no lo estaba ya, la; variable que indica lo que resta para detenerlo; es llevada a su valor normal, por lo que el disco no; tardará mucho en detenerse (incluso sin quizá haber; acelerado aún). En la práctica, invocando esta rutina; desde INT 13h nunca será necesario arrancar el motor; ya que el DOS ejecuta antes la función equivalente,; la 16h, que lo pone en marcha. Es simplemente una; medida de seguridad contra las BIOS «de marca».

leer_lin_camb PROCXPUSHA ; *PUSH DSDDSMOV AL,1MOV CL,DLSHL AL,CL ; bit de motor en 0..3TEST DS:[3Fh],ALJNZ rodando ; el motor ya está girandoCLCCALL motor_off_cnt ; cuenta normal detención motor

rodando: MOV AH,DLXSHL AH,4OR AH,AL ; AH = byte BIOSXSHL AL,4OR AL,00001100b ; modo DMA, no hacer resetOR AL,DL ; AL para reg. salida digitalMOV DX,3F2hCLIMOV DS:[3Fh],AH ; actualizar variable BIOSOUT DX,AL ; arrancado motor en la unidadADD DX,5DELAYIN AL,DX ; leer línea de cambio de discoSTITEST AL,80h ; ZF=0 -> cambio de discoPOP DSXPOPA ; *RET

leer_lin_camb ENDP

; ------------ Determinar si ha habido cambio de disco y, en ese caso,; si el nuevo disquete es de tipo 2M o no. El cambio de; disco se detecta leyendo la línea de cambio de disco o; chequeando la variable que indica si ha habido cambio; o no (esta variable está a ON tras instalar 2M para; forzar la detección del tipo de disco introducido; se; pone en ON también si no se logra bajar la línea de; cambio de disco por si fuera un soporte raro y la BIOS; sí lo lograra -forzando así una detección posterior-).

detecta_cambio PROCXPUSHA ; *CALL set_SI_drv ; SI -> variables de la unidadCMP CS:[SI].cambio,ON ; ¿cambio de soporte?MOV CS:[SI].cambio,OFFJE hubo_cambioCALL leer_lin_camb ; leer línea de cambio de discoJNZ hubo_cambioXPOPACLC ; no hay cambio de discoRET

hubo_cambio: CLCCALL set_flag_STV ; CF = 0 -> supuesto no 2MXPUSH <DS, ES> ; **MOV BX,90hADD BL,DLDDSAND BYTE PTR [BX],255-16 ; densidad no determinadaXPUSH <CS, CS>XPOP <DS, ES>MOV unidad,DLSTC ; asegurar motor en marchaCALL reset_drvMOV cilindro,1MOV cabezal,0CALL seek_drv ; bajar línea cambio de discoDEC cilindroCALL seek_drvCLCCALL motor_off_cnt ; cuenta normal detención motorCALL leer_lin_camb ; ¿bajada línea cambio disco?JZ disco_dentro ; se pudo: hay disco dentroMOV [SI].cambio,ON ; futura detección tipo discoCLC ; NO indicar cambio de disco...JMP fin_detecta ; ...para pasar control a BIOS

disco_dentro: PUSH DSDDSMOV BYTE PTR DS:[41h],6 ; error ’media changed’POP DS

IFNDEF SUPERBOOTCMP AH,5 ; ¿función de formateo?JE fin_detecta_c ; no perder el tiempo

ENDIFMOV buf_unidad,-1 ; invalidar bufferMOV [SI].gap,20 ; GAP provisionalMOV CX,3 ; 3 intentos

intenta_io0: PUSH CXCMP CX,2 ; CF=1 la 3ª vez (a 0 si CX<>1)CALL reset_drvMOV [SI].vunidad0,0 ; empezar con 500 Kbit/seg.

intenta_io: MOV AL,0MOV cilindro,ALMOV cabezal,ALMOV sector,1 ; sector de arranqueMOV seccion,ALMOV secciones,1MOV orden,F_READMOV DI,bufferCALL direct_acceso

JNE otra_densidad ; es otra densidad de discoPOP CXMOV BX,bufferCALL set_info ; características nuevo soporteCLCJMP fin_detecta_c ; indicar cambio de disco

otra_densidad: MOV AL,[SI].vunidad0INC AX ; próxima velocidadCMP AL,3JA otro_intentoMOV [SI].vunidad0,ALJMP intenta_io ; probar otra velocidad

otro_intento: MOV [SI].vunidad0,0POP CXLOOP intenta_io0 ; reintento

fin_detecta_c: STC ; indicar cambio de discofin_detecta: XPOP <ES, DS> ; **

XPOPA ; *RET

detecta_cambio ENDP

; ------------ Anotar la información del disquete si es de tipo 2M.; A la entrada, DS:SI apunta a las variables de la unidad; y ES:BX al sector de arranque del disco. Se actualiza; también la variable BIOS de tipo de densidad (la BIOS; no se da cuenta del cambio de disco y conviene ayudar).

set_info PROCXPUSHACALL calc_chkJC set_info_exit ; no es disco 2MMOV [SI].chk,AL ; anotar checksumMOV [SI].version_fmt,CL ; y versión del formatoMOV DL,unidadSTCCALL set_flag_STV ; CF = 1 -> indicar disco 2MMOV AL,ES:[BX+22] ; tamaño de FATMOV [SI].tam_fat,ALMOV CL,ES:[BX+65] ; CL a 0 si acceso multi-sectorMOV [SI].multi_io,CLMOV AX,ES:[BX+66]MOV [SI].vunidad,AX ; velocidad pista 0 / demásMOV AL,ES:[BX+24]MOV [SI].sectpista,AL ; sectores/pistaMOV DI,ES:[BX+72]MOV AL,ES:[BX+DI+1] ; GAP de formateoMOV AH,ALAND CL,CL ; CL a 0 si acceso multi-sectorJZ gap_rw_ok ; GAP R/W para /FADD AH,190MOV AL,11MUL AH ; AX = (190+GAP)*11SUB AX,2048+62

gap_rw_ok: SHR AL,1 ; GAP R/W para /MMOV [SI].gap,ALMOV CX,maxsMOV DI,ES:[BX+74]ADD DI,BXLEA BX,[SI].tabla_tsect

genera_ts: MOV AL,ES:[DI]MOV [BX],ALINC BXINC DILOOP genera_ts ; información estructura pistas

set_info_exit: MOV AL,[SI].vunidad0IFDEF XTMOV CL,6SHL AL,CLELSEXSHL AL,6 ; velocidad en bits 7:6ENDIFOR AL,00010111b ; establecido otro medio físicoCMP [SI].tipo_drv,2JA modo_ok ; es unidad de 3½AND AL,11111000bOR AL,00000101b ; 1.2 en 1.2TEST AL,01000000bJZ modo_okXOR AL,00100001b ; 360 en 1.2 y seek * 2

modo_ok: PUSH DSMOV BX,90hADD BL,unidadDDSAND BYTE PTR DS:[BX],8 ; respetar bit de 2.88MOR DS:[BX],AL ; actualizar variable BIOSPOP DS ; con el tipo de densidadXPOPARET

set_info ENDP

; ------------ Calcular el checksum de la zona vital del sector de; arranque. A la entrada, ES:BX -> sector de arranque.; A la salida, CF=1 si el disco no es 2M; de otro modo; checksum en AL y versión del formato de disco en CL.

calc_chk PROCXPUSH <SI, DI>LEA DI,[BX+3] ; DI=BX+3LEA SI,id_sistemaMOV CX,6REP CMPSB ; comparar identificaciónSTCJNE chk_ret ; el disco no es 2MXOR AX,AXMOV CL,ES:[BX+64] ; versión del formateadorCMP CL,6JB chk_ok ; no usaba este checksumMOV DI,ES:[BX+68]

chk_sum: DEC DIADD AL,ES:[BX+DI]CMP DI,63JA chk_sum

chk_ok: CLCchk_ret: XPOP <DI, SI>

RETcalc_chk ENDP

; ------------ Determinar el tipo de error producido en el acceso.

set_err PROCXPUSHAJNC err_ret ; no hay errorCMP status,0 ; ¿’status’ ya asignado?JNE err_retc ; no cambiarlo si es asíMOV AL,BYTE PTR fdc_result+1AND AL,10110111b ; aislar condiciones de testLEA BX,lista_errsMOV CX,9

Page 325: PCA, PS2 ,IBM y AT

325EL HARDWARE DE APOYO AL MICROPROCESADOR

busca_err: MOV AH,[BX] ; código de error BIOSSHL AL,1JC err_ok ; es ese errorINC BXLOOP busca_err ; buscar otro error

err_ok: OR status,AHerr_retc: STC ; condición de errorerr_ret: XPOPA

RETset_err ENDP

; ------------ Actualizar variables de error de la BIOS.

set_bios_err PROCPUSHF ; *XPUSHA ; **PUSH ES ; ***DESMOV DI,41h ; bytes de resultados del 765LEA SI,status ; variable BIOS de status y 7MOV CX,4 ; bytes: 4 palabrasREP MOVSWPOP ES ; ***XPOPA ; **POPF ; *RET

set_bios_err ENDP

; ------------ Realizar lecturas, escrituras y verificaciones: rutina; que sustituye el código de la BIOS para poder soportar; los formatos 2M. La operación puede quedar dividida en; tres fases: el fragmento anterior a la FAT2, la zona; correspondiente a la FAT2 (se ignora la escritura y se; simula su lectura leyendo la FAT1) y un último bloque; ubicado tras la FAT2. El sector de arranque es emulado; empleando el primer sector físico de la FAT2 (aunque en; los discos de versión de formato anterior a la 7 se usa; el sector de arranque verdadero -permitiendo escribirlo; sólo si es válido-). En cualquier caso, si el número de; cabezal tiene el bit 7 activo, se sobreentiende que el; programa que llama soporta disquetes 2M y no se emula; la FAT2 ni el sector de arranque, para permitirle; acceder al código SuperBOOT. Las coordenadas de la BIOS; se traducen a las unidades del DOS por mayor comodidad.

control2m PROCPUSH DS ; *XPUSHA ; **PUSH CSPOP DSMOV unidad,DLCALL set_SI_drv ; SI -> variables de la unidadCMP [SI].chk,0JE chk_valido ; checksum correcto en sector 0MOV status,40h ; devolver ’Seek Error’ al DOSJMP exit_2m_ctrl

chk_valido: PUSH AX ; ***MOV AH,0MOV numsect,AX ; nº sectoresMOV AL,CH ; cilindroSHL AL,1MOV DL,DHAND DH,01111111bADD AL,DH ; cabezal físicoMUL [SI].sectpistaADD AL,CL ; sectorADC AH,0DEC AX ; AX = nº sector DOSMOV sectini,AX ; 0FFFFh si sector 0 (error)MOV DI,BX ; ES:DI -> direcciónPOP BX ; ***MOV BL,BHMOV BH,0MOV CL,[BX+OFFSET tab_ordenes-2]MOV orden,CLSHL DL,1JC acceso_final ; cabezal >= 128: no emularAND AX,AX ; ¿comienza en sector 0?JNZ io_emula ; noCMP [SI].version_fmt,7JB boot_real ; no soportado BOOT virtualMOV AL,[SI].tam_fat ; AH = 0INC AXMOV CX,1 ; sector BOOT emulado enCALL ejecuta_io ; el primer sector FAT2JNE fin_ctrl

boot_fin_op: DEC numsectINC sectiniMOV AX,sectiniJMP io_emula

boot_real: CMP orden,F_WRITEJNE io_emulaMOV BX,DI ; BOOT de 2M 1.3 y anterioresCALL calc_chkJC si_skip ; no es de tipo 2MAND AL,ALJZ io_emula ; lo es y con checksum correcto

si_skip: ADD DI,512JMP boot_fin_op ; impedir estropicio de BOOT

io_emula: MOV CL,[SI].tam_fatMOV CH,0 ; CX = primer sector FAT2 - 1CMP AX,CXJA en_fat2? ; ¿la operación afecta a FAT2?CALL calc_iop ; calcular sectores antes FAT2CALL ejecuta_io ; CX sectores desde AXJNE fin_ctrl ; errorCMP numsect,0JE fin_ctrl ; fin de la transferencia

en_fat2?: MOV AX,sectiniMOV CL,[SI].tam_fatMOV CH,0SHL CX,1 ; CX = último sector FAT2CMP AX,CXJA acceso_final ; la operación es tras la FAT2CALL calc_iop ; sectores hasta fin de FAT2CMP orden,F_WRITEJNE emula_fat1

IFDEF XTXCHG CH,CLSHL CH,1

ELSEXSHL CX,9 ; CX = CX * 512

ENDIFADD DI,CX ; ES:DI actualizadoJMP acceso_final

emula_fat1: MOV DL,[SI].tam_fatMOV DH,0SUB AX,DX ; leer de FAT1 y no de la FAT2

CALL ejecuta_io ; CX sectores desde AXJNE fin_ctrl ; error

acceso_final: CMP numsect,0JE fin_ctrl ; fin de la transferenciaMOV AX,sectiniMOV CX,numsectCALL ejecuta_io

fin_ctrl: CLCCALL motor_off_cnt ; cuenta normal detención motorCALL set_bios_err ; actualizar variables BIOS

exit_2m_ctrl: XPOPA ; **MOV AH,statusPOP DS ; *AND AH,AHJZ st_ok ; resultado correcto (CF=0)STC ; errorMOV AL,0 ; 0 sectores movidos

st_ok: RETcalc_iop: SUB CX,AX

INC CX ; CX sectoresCMP CX,numsectJBE nsect_okMOV CX,numsect ; sólo quedan CX

nsect_ok: SUB numsect,CXADD sectini,CXRET

control2m ENDP

; ------------ A la entrada, AX indica el sector inicial (coordenadas; del DOS) y CX el número de sectores a procesar.; * Definiciones: «Sector físico» es un sector del disco; de 512, 1024 ó 2048 bytes (números de sector del 1 al N; en la pista). Este sector físico está dividido en; «secciones» de 512 bytes, constando por tanto de 1, 2 ó; 4 secciones. «Sector virtual» es el número de sector; del programa que llama a INT 13h, comprendido entre 1 y; M. Esta estructura de N sectores por pista de distintos; tamaños, se verifica en todo el disco con excepción del; cabezal y cilindro 0 (con un formato más convencional; de sectores de 512 bytes numerados de 1 a J, aunque no; existen algunos de los intermedios que corresponden a; la segunda copia de la FAT).; * Primero se convierte el sector virtual (1..M) en su; correspondiente físico (1..J en la pista 0 y 1..N en; las demás), deduciendo qué porción de 512 bytes (o; sección) es afectada. Un sector virtual (512 bytes); simulado suele ser parte de un sector físico de 2048; bytes en muchos casos. Si dicho sector físico ya había; sido leído al buffer en anteriores accesos, se extrae; la sección necesaria. Si no, se carga del disco y se; extrae dicho fragmento. El número de sectores virtuales; que se solicitan (=secciones) permite realizar un bucle; hasta completar la transferencia; el interleave 1:2 de; los sectores físicos en /M permite acceder sector a; sector sin pérdida de rendimiento. En el caso de la; escritura, se estudia primero si hay varios sectores; virtuales consecutivos que escribir, completando entre; todos un sector físico: en ese caso, se prepara el; mismo y se escribe sin más. En caso de que haya que; modificar sólo una única sección de un sector físico,; salvo si éste es de 512 bytes, no hay más remedio que; cargarlo al buffer (realizar una prelectura),; actualizar la sección correspondiente y volverlo a; escribir.; * En el formato /F se realiza una operación multisector; si es posible y sin emplear el buffer intermedio (si; bien podría ser preciso emplearlo con la primera y; última sección); en los dos formatos de disco se hace; la operación multisector en la primera pista. Las; operaciones multisector puede que sea preciso; dividirlas en tres fases: los sectores antes de una; frontera de DMA, el que la cruza (que es transferido; a través del buffer intermedio) y los que están detrás.

ejecuta_io PROCMOV BX,AX ; AX = sector DOS inicialCMP AH,0FFhJE no_cabe ; (acceso a sector BIOS 0)MOV secciones,CL ; CX sectores (CL realmente)DIV [SI].sectpistaINC AH ; numerado desde 1...MOV sector,AH ; ...el resto es el sectorSHR AL,1MOV cilindro,AL ; cilindroRCL AL,1AND AL,1MOV cabezal,AL ; cabezalMOV AL,sectorADD AL,seccionesJC no_cabe ; sector+secciones > 255DEC AX ; DEC AX = DEC ALCMP AL,[SI].sectpistaJBE si_cabe

no_cabe: MOV status,4 ; ’sector no encontrado’JMP fin_io

si_cabe: MOV AL,AH ; sector en ALCBW ; sección 0 (AH = 0)CALL pista0?JZ s_xx ; sector físico en pista/cara 0LEA BX,[SI].tabla_tsect-1DEC AX ; AH = 0

resta_secc: INC BXINC AHMOV CL,[BX]SUB CL,2MOV CH,1SHL CH,CLSUB AL,CHJNC resta_secc ; en las demás pistasADD AL,CHXCHG AH,AL

s_xx: MOV sector,AL ; sector lógico convertido aMOV seccion,AH ; sector y sección físicas

direct_acceso: CALL motor_ok ; asegurar que está en marchaMOV AH,0MOV sector_fin,AH ; no acceder a más de 1 sectorCALL pista0? ; (al menos de momento)JNZ decide_multi ; no es pista 0MOV AL,seccionesMOV secciones,AH ; las que restan (AH = 0)JMP multi_proc

decide_multi: CMP [SI].multi_io,AH ; AH = 0JNE io_pasos ; acceso sector a sectorCMP seccion,AHJE multi_accCALL acceso_secc ; no acceso a inicio sectorJC fin_io

multi_acc: CMP secciones,AH ; AH = 0

Page 326: PCA, PS2 ,IBM y AT

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

JE fin_ioCALL num_seccionesMOV CL,ALMOV AL,secciones ; AH = 0DIV CLAND AL,ALJZ io_pasos ; no quedan sectores enterosMOV secciones,AH ; las que restan

multi_proc: CALL acceso_multi ; de AL sectoresJC fin_io

io_pasos: CMP secciones,0JE fin_io ; no restan secciones finalesCALL acceso_seccJNC io_pasos

fin_io: CMP status,0 ; ZF = 1 -> operación correctaRET

acceso_secc: PUSH AXCMP orden,F_WRITE ; acabar transferencia sectorJE escrituraCMP orden,F_VERIFYJE verificacionCALL leido? ; realizar lectura...JNC ya_leido ; sector ya en el buffer

hay_que_leer: CALL acceso_sector ; efectuar E/SJC acc_ret ; ha habido fallo

ya_leido: CALL trans_secc ; buffer -> memoriaJMP acc_ret

escritura: CMP seccion,0JNE prelectura ; sólo parte del sector cambiaCALL num_seccionesCMP secciones,ALJAE escribir ; Todo el sector físico cambia

prelectura: CALL leido? ; Leer el sector físico paraJNC escribir ; cambiar sólo una parte de élMOV orden,F_READ ; de momento leer...CALL acceso_sector ; efectuar E/SMOV orden,F_WRITE ; ... restaurar orden originalJC acc_ret ; ha habido fallo

escribir: CALL trans_secc ; memoria -> bufferCALL acceso_sector ; volcar buffer al discoJMP acc_ret

verificacion: PUSH BXMOV BL,seccionCALL num_secciones

dec_sec_veri: DEC seccionesJZ verificaINC BXCMP BL,ALJB dec_sec_veri

verifica: POP BXCALL acceso_sector ; leer para forzar verificación

acc_ret: PUSHFINC sector ; preparado para otro sectorMOV seccion,0 ; desde su primera secciónPOPFPOP AXRET

IFDEF SUPERBOOT ; SuperBOOT: válido el cruce del DMA

acceso_multi: PUSH AX ; AL = sectores a transferirAND AL,ALJZ acc_mult_finMOV AH,sectorMOV sector_ini,AHADD AL,AHDEC AXMOV sector_fin,ALINC ALCALL acceso_sector ; sectores no problemáticosMOV sector,AL

acc_mult_fin: POP AXRET

ELSE ; No es SuperBOOT: Evitar cruce frontera DMA

acceso_multi: PUSH AX ; AL = sectores a transferirMOV BX,ES ; desde ’sector’ teniendoXSHL BX,4 ; cuidado con el DMAADD BX,DINEG BX ; BX = bytes hasta frontera DMACALL num_seccionesMOV CH,AL ; AL secciones de 512 bytesMOV CL,0SHL CX,1 ; CX = bytes por sectorXCHG AX,BX ; BL = secciones por sectorXOR DX,DXDIV CXMOV CL,AL ; CL = sectores que cabenPOP AX ; AL = sectores a transferirCMP AL,CLJA acc_mult2 ; no hay problemas con el DMA

acc_mult1: MOV CL,ALacc_mult2: AND CL,CL

JZ acc_mult3 ; primer sector problemáticoMOV AH,sectorMOV sector_ini,AHADD AH,CLDEC AHMOV sector_fin,AHINC AHSUB AL,CLCALL acceso_sector ; sectores no problemáticosMOV sector,AHJC acc_mult_fin

acc_mult3: AND AL,AL ; ahora el sector problemáticoJZ acc_mult_finADD secciones,BL ; compensar futuro decrementoCALL acceso_secc ; a través del buffer auxiliarJC acc_mult_finDEC ALJMP acc_mult1 ; sectores que restan

acc_mult_fin: RET

ENDIF

ejecuta_io ENDP

; ------------ Mover secciones desde el buffer hacia la memoria (con; orden F_READ) después de la lectura o de la memoria al; buffer (orden F_WRITE) antes de la escritura. En la; verificación (orden F_VERIFY) no se mueve nada porque; esta subrutina no es invocada.

trans_secc PROCXPUSH <AX, BX, CX, SI> ; *MOV BL,seccion ; desde esta sección

CALL num_secciones ; nº secciones del sectorotra_secci: PUSH BX

IFDEF XTMOV BH,BLSHL BH,1MOV BL,0ELSEXSHL BX,9 ; sección * 512

ENDIFADD BX,buffer ; direcciónMOV SI,BXMOV CX,256 ; tamaño sección (palabras)CALL swap_reg ; ¿intercambiar origen-destino?REP MOVSW ; copiar 512 bytesCALL swap_reg ; ¿intercambiar origen-destino?POP BXDEC secciones ; una menosJZ fin_seccINC BX ; otra sección del sectorCMP BL,AL ; ¿sector agotado?JB otra_secci ; aún no

fin_secc: XPOP <SI, CX, BX, AX> ; *RET

swap_reg: CMP CS:orden,F_WRITEJE intercCLCRET

interc: XCHG SI,DI ; en escritura, invertir elXPUSH <ES, DS> ; sentido de la operaciónXPOP <ES, DS>RET

trans_secc ENDP

; ------------ Comprobar si el sector ya está en el buffer.

leido? PROCPUSH AXMOV AL,buf_unidadCMP AL,unidadJNE no_leido ; es en otra unidadMOV AL,cilindroMOV AH,cabezalCMP AX,buf_cilcabJNE no_leido ; es en otro cilindro/cabezalMOV AL,buf_sectorCMP AL,sectorJNE no_leido ; es otro sectorPOP AXRET ; está en el buffer

no_leido: STCPOP AXRET ; sector no leído

leido? ENDP

; ------------ Leer o escribir sector(es). Se selecciona el tamaño de; sector correcto antes de llamar a sector_io. En esta; rutina se actualiza la variable «status» en función de; los posibles errores de acceso. Si sector_fin es; distinto de 0 se accede a los sectores indicados, si es; 0 se accede sólo al sector «sector» a través del buffer; intermedio y al final se anota el sector cargado ó; escrito para evitar futuras lecturas innecesarias, a; modo de mini-caché que dispara la velocidad de acceso a; sectores lógicos consecutivos.

acceso_sector PROCXPUSH <AX, BX>CALL seek_drv ; posicionar el cabezalJNC en_pistaCMP status,0 ; ¿error ya determinado?JNE acc_fin_errOR status,40h ; no: pues ’seek error’

acc_fin_err: STCJMP acceso_fin

en_pista: CALL pista0?MOV AL,2JZ tam_acc_ok ; sectores 512 en cil./cab. 0LEA BX,[SI].tabla_tsectADD BL,sectorADC BH,0MOV AL,[BX-1]

tam_acc_ok: MOV tsector,ALCMP sector_fin,0 ; ¿usar buffer intermedio?JE acceso_bufferCALL sector_ioMOV sector_fin,0 ; no acceder a más de 1 sectorPUSHF ; **1JMP acceso_rep ; en el futuro (por defecto)

acceso_buffer: XPUSH <ES, DI>PUSH CSPOP ESMOV DI,buffer ; acceso con buffer auxiliarMOV AL,sector ; mismo sector inicial/finalMOV sector_ini,ALMOV sector_fin,ALCALL sector_ioMOV sector_fin,0XPOP <DI, ES>PUSHF ; **2MOV AL,-1 ; invalidar contenido bufferJC acceso_anota ; si hay errorCMP orden,F_VERIFYJE acceso_rep ; nada leído físicamenteMOV AL,unidad

acceso_anota: MOV buf_unidad,ALMOV AL,cilindroMOV AH,cabezalMOV buf_cilcab,AXMOV AL,sectorMOV buf_sector,AL ; anotado el sector en buffer

acceso_rep: POPF ; ** mucho cuidado con la pilaCALL set_err ; ajustar variable «status»

acceso_fin: XPOP <BX, AX>RET

acceso_sector ENDP

; ------------ Devolver el número de secciones del sector en curso.

num_secciones PROCCALL pista0?MOV AL,1JZ num_secc_ok ; sectores 512 en cil./cab. 0XPUSH <BX, CX>LEA BX,[SI].tabla_tsectADD BL,sectorADC BH,0MOV CL,[BX-1]SUB CL,2

Page 327: PCA, PS2 ,IBM y AT

327EL HARDWARE DE APOYO AL MICROPROCESADOR

MOV AL,1SHL AL,CLXPOP <CX, BX>

num_secc_ok: RET ; resultado en ALnum_secciones ENDP

; ------------ Asegurar que el motor está en marcha.

motor_ok PROCXPUSHA ; *PUSH DS ; **MOV BX,40hPUSH BXPOP DSMOV CH,255-18 ; CH = 255 - 1 segundoCLIMOV CL,CS:unidadMOV AL,1SHL AL,CLTEST [BX-1],AL ; ¿motor en marcha?JZ arrancarlo ; arrancarloCMP [BX],CH ; Si encendido y acelerado...JBE ok_motor ; ...seguir

arrancarlo: MOV AH,CLMOV CL,4SHL AH,CL ; unidad << 4OR AL,AHMOV [BX-1],AL ; nuevo estado motoresMOV BYTE PTR [BX],255 ; asegurar que no se pareMOV DX,3F2h ; registro de salida digitalADD CL,CS:unidadMOV AL,1SHL AL,CL ; colocar bit del motorOR AL,CS:unidad ; seleccionar unidadOR AL,00001100b ; modo DMA, no hacer resetOUT DX,AL ; poner en marcha el motorSTIMOV AX,90FDhCLCINT 15h ; permitir multitareaJC ok_motor ; timeoutMOV AX,1000 ; 1 segundo aceleraciónCALL retardo ; esperar aceleración disco

ok_motor: MOV [BX],CH ; cuenta máxima detención motorSTI ; sin forzar futura aceleraciónPOP DS ; **XPOPA ; *RET

motor_ok ENDP

; ------------ Establecer modalidad de operación del controlador; y poner el motor en marcha. Si CF=1 se le da tiempo; además a la unidad para que acelere.

reset_drv PROCXPUSHACALL motor_off_cnt ; cuenta detención motorMOV CL,unidadMOV AL,CLXSHL AL,4 ; unidad seleccionadaMOV AH,1 ; bit de motorSHL AH,CL ; colocar dicho bitOR AL,AHPUSH DS ; *DDSCLIMOV DS:[3Fh],ALAND BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrarPOP DS ; *XSHL AL,4 ; bits motor en nibble altoOR AL,CL ; seleccionar unidadOR AL,00001000b ; interrupciones+DMA y resetMOV DX,3F2h ; registro de salida digitalOUT DX,AL ; señal de resetIFDEF XTMOV CX,50

respiro: LOOP respiroELSECALL fdc_respiro ; tiempo reconocer reset en 486

ENDIFOR AL,00000100bOUT DX,AL ; fin de señal de resetCALL espera_int ; rehabilitará interrupcionesMOV AL,8CALL fdc_write ; comando ’leer estado int...’CALL fdc_readCALL fdc_readCALL envia_specify ; comando ’specify’ adecuadoXPOPARET

reset_drv ENDP

; ------------ Enviar comando specify a la controladora. El step-rate; se selecciona según la densidad, para evitar un sonido; extraño al posicionar o recalibrar el cabezal.

envia_specify PROCPUSH AXPUSH DSDDSMOV AH,DS:[8Bh]POP DSMOV AL,3 ; comando ’specify’CALL fdc_writeMOV AL,0BFh ; step rate para 500 kbpsAND AH,11000000bJZ spec1_okMOV AL,0AFh ; step rate para 1 MbpsCMP AH,11000000bJE spec1_okMOV AL,0DFh ; step rate para 250/300 Kbps

spec1_ok: CALL fdc_writeMOV AL,2CALL fdc_write ; head load y modo DMAPOP AXRET

envia_specify ENDP

; ------------ Recargar cuenta para la detención del motor. Si CF=1 al; entrar, se establece la mayor cuenta posible; en caso; contrario, se pone el valor normal de la tabla base.

motor_off_cnt PROCXPUSHAPUSH DSMOV AL,0FFh ; valor máximoJC motor_off_ok

IFDEF XT

XOR BX,BXMOV DS,BXELSEPUSH 0POP DSENDIFLDS BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1EhMOV AL,[BX+2] ; byte 2 tabla base disco

motor_off_ok: DDSMOV BYTE PTR DS:[40h],AL ; cuenta parada motorPOP DSXPOPARET

motor_off_cnt ENDP

; ------------ Llevar el cabezal a la pista indicada, recalibrando si; hubo un reset (se invocó la función 0 de la INT 13h o; se ejecutó reset_drv) antes de esta operación. Primero; se selecciona la velocidad de transferencia y se borra; el resultado de cualquier operación anterior, para que; todo quede listo para el próximo acceso a disco.

seek_drv PROCXPUSHACALL set_rate ; velocidad / borrar resultadosCALL envia_specify ; comando ’specify’ adecuadoMOV AH,1MOV CL,unidadSHL AH,CL ; AH = 1 (A:) ó 2 (B:)PUSH DSDDSTEST AH,DS:[3Eh]POP DSJNZ do_seek ; la unidad ya fue recalibradaCALL recalibrarJC fallo_seek ; fallo al recalibrar

do_seek: MOV BX,94hADD BL,unidadMOV AL,cilindroPUSH DS ; *DDSOR DS:[3Eh],AH ; unidad ya recalibradaMOV AH,DS:[41h] ; código de error previoCMP AL,[BX]MOV [BX],ALPOP DS ; *JNE hacer_seek ; seek necesarioCMP AH,40h ; ¿error de seek previo?JNE seek_ok ; no, evitar seek innecesario

hacer_seek: MOV AL,0FhCALL fdc_write ; comando ’seek’JC fallo_seekMOV AL,cabezalXSHL AL,2OR AL,unidadCALL fdc_write ; enviar HD, US1, US0MOV AL,cilindroCALL fdc_write ; enviar cilindroCALL espera_int ; esperar interrupciónJC fallo_seekMOV AL,8CALL fdc_write ; comando ’leer estado int...’JC fallo_seekCALL fdc_read ; leer registro de estado 0JC fallo_seekMOV AH,ALCALL fdc_read ; leer cilindro actualTEST AH,11000000b ; comprobar ST0JNZ fallo_seekMOV AL,15 ; estabilización para escrituraCMP orden,F_WRITEJE rseek_okMOV AL,1 ; estabilización para lectura

rseek_ok: CBW ; AH = 0CALL retardo ; esperar asentamiento cabezal

seek_ok: XPOPACLC ; retornar con éxitoRET

fallo_seek: XPOPASTC ; retornar indicando falloRET

seek_drv ENDP

; ------------ Establecer velocidad de transferencia correcta si aún; no ha sido seleccionada y borrar el resultado de otra; operación previa.

set_rate PROCXPUSHACALL pista0?MOV AX,[SI].vunidad ; velocidad pista 0 / demásJZ vel_okMOV AL,AH

vel_ok: PUSH DS ; *DDSMOV AH,DS:[8Bh]IFDEF XTMOV CL,6SHR AH,CLELSESHR AH,6 ; aislar bits de velocidadENDIFCMP AL,AHJE vel_set ; velocidad ya seleccionadaMOV DX,3F7hOUT DX,AL ; seleccionarlaXSHL AL,6AND BYTE PTR DS:[8Bh],00111111bOR DS:[8Bh],AL

vel_set: POP DS ; *LEA DI,statusMOV CX,8

borra_status: MOV [DI],CH ; borrar información de estadoINC DILOOP borra_statusXPOPARET

set_rate ENDP

; ------------ Recalibrar la unidad (si hay error se intenta otra vez; para el caso de que deba moverse más de 77 pistas).

recalibrar PROCXPUSHAMOV BX,94hADD BL,unidadPUSH DS ; *DDS

Page 328: PCA, PS2 ,IBM y AT

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

MOV [BX],BH ; pista actual = 0POP DS ; *MOV CX,2 ; dos veces como mucho

recalibra: MOV AL,7CALL fdc_write ; comando de ’recalibrado’JC fallo_recalMOV AL,cabezalXSHL AL,2OR AL,unidadCALL fdc_write ; enviar HD, US1, US0JC fallo_recalCALL espera_int ; esperar interrupciónJC fallo_recalMOV AL,8CALL fdc_write ; comando ’leer estado int...’JC fallo_recalCALL fdc_read ; leer registro de estado 0JC fallo_recalMOV AH,ALCALL fdc_read ; leer cilindro actualXOR AH,00100000b ; bajar bit de ’seek end’TEST AH,11110000b ; comprobar resultado y ST0JNZ fallo_recal ; sin ’seek end’ o TRK0MOV AX,1 ; pausa de 1 msCALL retardoJMP recal_ret

fallo_recal: LOOP recalibra ; reintentar comandoSTC ; condición de fallo

recal_ret: XPOPARET

recalibrar ENDP

; ------------ Cargar o escribir sector(es) del disco en ES:DI,; actualizando la dirección en ES:DI pero sin alterar; ningún otro registro. Si hay error se devuelve CF=1 y; no se modifica ES:DI. A partir de fdc_result se dejan; los 7 bytes que devuelve el FDC al final del acceso.; En caso de verificación (F_VERIFY) se programa el DMA; para que no realice transferencia física (convenio de; las BIOS con fecha 15/11/85 y posterior).

sector_io PROCXPUSH <AX, BX, CX, DX>MOV CL,tsectorMOV CH,0STCRCL CH,CLMOV CL,0 ; nº de bytes por sectorMOV AL,sector_finSUB AL,sector_iniINC AXCBW ; AX sectores (AH = 0)MUL CXMOV DX,AX ; bytes totalesMOV CX,AXDEC CX ; bytes totales - 1MOV AX,ESCALL calc_dir_DMA ; AX:DI -> base BX y página AH

IFDEF SUPERBOOTJC sector_io_ko ; chequear cruce frontera DMA

ENDIFMOV AL,orden ; modo DMA necesarioCALL prepara_DMACMP AL,F_WRITEMOV AL,11000101b ; comando de escritura del FDCJE orden_io_okMOV AL,11100110b ; comando leer (verif.) del FDC

orden_io_ok: CALL fdc_write ; comando leer/escribir del FDCJC sector_io_koMOV AL,cabezalXSHL AL,2OR AL,unidadCALL fdc_write ; byte 1 de la ordenMOV AL,cilindroCALL fdc_write ; enviar cilindroMOV AL,cabezalCALL fdc_write ; enviar cabezalMOV AL,sector_iniCALL fdc_write ; enviar nº sectorMOV AL,tsectorCALL fdc_write ; longitud sectorMOV AL,sector_finCALL fdc_write ; último sectorMOV AL,[SI].gapCALL fdc_write ; GAP de lectura/escrituraMOV AL,128CALL fdc_write ; tamaño sector si longitud=0CALL espera_intPUSHF ; *LEA BX,fdc_resultMOV CX,7

sect_io_res: CALL fdc_read ; leyendo resultadosMOV [BX],ALINC BXLOOP sect_io_resPOPF ; *JC sector_io_koTEST fdc_result,11000000bJNZ sector_io_koADD DI,DX ; actualizar direcciónCLC ; OkJMP sector_io_fin

sector_io_ko: STC ; indicar fallosector_io_fin: XPOP <DX, CX, BX, AX>

RETsector_io ENDP

; ------------ Devolver en AH la página de DMA y en BX la base. A la; entrada, AX:DI -> dirección de memoria y CX = bytes-1.; Se supone que el buffer no cruza una frontera de DMA,; aunque el código SuperBOOT devuelve error en ese caso.

calc_dir_DMA PROCPUSH DXMOV BX,16MUL BXADD AX,DIADC DX,0 ; DX:AX = dirección 20 bitsMOV BX,AX ; base en BXMOV AH,DL ; página

IFDEF SUPERBOOTMOV DX,CX ; comprobar cruce en SuperBOOTADD DX,BXJNC dir_DMA_okMOV status,9 ; error de frontera de DMA

ENDIFdir_DMA_ok: POP DX

RET

calc_dir_DMA ENDP

; ------------ Crear tabla con información para formatear. En ES:BX; está el futuro sector de arranque del disquete.

IFNDEF SUPERBOOT ; en SuperBOOT no se formatea

genera_info PROCXPUSHAMOV buf_unidad,-1 ; invalidar contenido bufferMOV SI,bufferMOV DI,BXCALL pista0?JNZ no_cilcab0 ; no es cilindro/cabezal 0ADD DI,ES:[BX+70] ; DI -> datos pista 0MOV CL,ES:[DI]MOV CH,0 ; CX sectores en pista 0INC DIMOV AL,ES:[DI] ; GAP para pista 0MOV AH,0 ; byte de rellenoINC DIMOV BYTE PTR [SI],2 ; tamaño de sectorMOV BYTE PTR [SI+1],CL ; número de sectoresMOV [SI+2],AX ; GAP / byte de relleno

genera_0: ADD SI,4MOV AL,cilindroMOV AH,cabezalMOV [SI],AX ; datos para cada sectorMOV AL,ES:[DI]MOV AH,2 ; LOG2 (tamaño)-7INC DIMOV [SI+2],AX ; nº de sector / tamañoLOOP genera_0XPOPARET

no_cilcab0: ADD DI,ES:[BX+72]CMP BYTE PTR ES:[BX+65],1JE info_stvMOV DL,ES:[DI+2] ; tamaño /FMOV DH,ES:[DI] ; nº sectoresMOV [SI],DXXCHG DH,DL ; tamaño en DHMOV CL,DLMOV CH,0 ; CX sectoresMOV AL,ES:[DI+1] ; GAP para formatearMOV AH,0 ; byte relleno /FMOV [SI+2],AX ; GAP / byte de rellenoPUSH DXMOV AX,ES:[DI+3]PUSH AXADD AL,AHMUL cilindroMOV DX,AXPOP AXMUL cabezalADD AX,DXXOR DX,DXMOV BL,ES:[DI]MOV BH,0DIV BX ; DL = móduloSUB DL,ES:[DI]NEG DLMOV AL,DLPOP DX ; restaurar tamaño en DHMOV DL,AL ; primer sector de la pista - 1MOV BL,ES:[DI] ; nº sectores en la pista

genera_pn: ADD SI,4INC DXCMP DL,BLJBE ns_okMOV DL,1 ; empezar desde el 1

ns_ok: MOV AL,cilindroMOV AH,cabezalMOV [SI],AX ; datos para cada sectorMOV [SI+2],DX ; nº sector / LOG2 (tamaño)-7LOOP genera_pnXPOPARET

info_stv: MOV CH,ES:[DI] ; nº sectoresMOV CL,0 ; CL:CH sectoresMOV [SI],CX ; tamaño (CL=0) y númeroXCHG CH,CL ; CX sectoresMOV AL,ES:[DI+1] ; GAP para formatearMOV AH,4Eh ; byte de relleno /MMOV [SI+2],AX ; GAP / byte de rellenoMOV DL,128

genera_otro: ADD SI,4INC DXMOV AL,cilindroMOV AH,cabezalMOV [SI],AX ; datos para cada sectorXPUSH <CX, DI> ; *MOV CL,ES:[DI+2] ; CH está a 0

busca_num: ADD DI,3CMP DL,ES:[DI]MOV AX,ES:[DI+1] ; número de sector / tamañoJE hallado ; es sector a cambiar númeroLOOP busca_numMOV AL,DL ; no cambiar númeroMOV AH,0 ; e indicar tamaño 128

hallado: XPOP <DI, CX> ; *MOV [SI+2],AX ; nº sector / LOG2 (tamaño)-7LOOP genera_otroXPOPARET

genera_info ENDP

; ------------ Formatear una pista.

formatea_pista PROCXPUSHAMOV BX,bufferMOV DI,BXMOV CL,[BX+1]MOV CH,0 ; CX sectoresXSHL CX,2DEC CX ; nº de bytes - 1MOV AX,DSCALL calc_dir_DMA ; AX:DI -> base BX y página AHMOV AL,4Ah ; modo DMA para escribirADD BX,4 ; saltar primeros 4 bytesCALL prepara_DMAMOV BX,bufferMOV AL,F_FORMATCALL fdc_writeJC fallo_fmtMOV AL,cabezalXSHL AL,2

Page 329: PCA, PS2 ,IBM y AT

329EL HARDWARE DE APOYO AL MICROPROCESADOR

OR AL,unidadCALL fdc_write ; byte 1 de la ordenJC fallo_fmtMOV CX,4

format_cmd: MOV AL,[BX]CALL fdc_writeINC BXLOOP format_cmdCALL espera_int

fallo_fmt: PUSHFLEA BX,fdc_resultMOV CX,7

format_res: CALL fdc_read ; leyendo resultadosMOV [BX],ALINC BXLOOP format_resPOPFJC fallo_formatTEST fdc_result,11000000bJZ format_ret

fallo_format: STC ; falloformat_ret: XPOPA

RETformatea_pista ENDP

ENDIF

; ------------ Esperar interrupción de disquete durante casi 2; segundos antes de considerar que ha sido un fracaso.

IFNDEF XT

espera_int PROCSTIXPUSHAPUSH DSDDSMOV AX,9001hCLCINT 15h ; permitir multitareaMOV DX,0280hMOV BX,3EhJC timeout_int

esp_int_1s: XOR CX,CXesp_int: TEST [BX],DL ; ¿llegó la interrupción?

JNZ fin_esperaPMICROLOOP esp_int ; esperar durante casi 1 seg.DEC DHJNZ esp_int_1s

timeout_int: OR CS:status,DL ; timeoutSTC

fin_espera: PUSHFAND BYTE PTR [BX],7Fh ; para la próxima vezPOPFPOP DSXPOPARET

espera_int ENDP

ELSE ; Si es XT...

espera_int PROCSTIXPUSHAXPUSH <DS, 40h>POP DSMOV AH,0FFh

esperar_int: CMP AL,DS:[6Ch]JE mira_intMOV AL,DS:[6Ch]INC AHCMP AH,37 ; ¿más de 2 segundos?JB mira_int

timeout_int: OR CS:status,80h ; timeoutSTCJMP fin_espera

mira_int: TEST BYTE PTR DS:[3Eh],80hJZ esperar_intAND BYTE PTR DS:[3Eh],7Fh ; CF=0

fin_espera: POP DSXPOPARET

espera_int ENDP

ENDIF

; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de; base, AH = registro de página y CX = nº bytes - 1.

prepara_DMA PROCPUSH AXCLIOUT 0Bh,AL ; registro de modo del DMAMOV AL,0DELAYOUT 0Ch,AL ; clear first/last flip-flopMOV AL,BLDELAYOUT 4,ALMOV AL,BHDELAYOUT 4,AL ; enviada dirección baseDELAYMOV AL,AHOUT 81h,AL ; registro de página del DMAMOV AL,CLDELAYOUT 5,ALMOV AL,CHDELAYOUT 5,AL ; enviada cuenta de bytesSTIMOV AL,2DELAYOUT 0Ah,AL ; habilitar canal 2 de DMAPOP AXRET

prepara_DMA ENDP

; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si; la operación fracasó (el FDC no estaba listo) y; se indica la condición de timeout en «status».

IFNDEF XT

fdc_read PROCXPUSH <CX, DX, AX>

CALL fdc_respiro ; no abrasar el FDCMOV DX,3F4h ; registro de estado del FDCMOV CX,133 ; constante para 0,002 segundos

espera_rd: DELAYIN AL,DXAND AL,11000000bCMP AL,11000000b ; ¿dato listo?JE fdc_rd_okDELAYIN AL,61hAND AL,10hCMP AL,AHJE espera_rd ; reintentarlo durante 15,09 µsMOV AH,ALLOOP espera_rdXPOP <AX, DX, CX>OR status,80h ; timeoutMOV AL,0STC ; falloRET

fdc_rd_ok: POP AXINC DX ; apuntar al registro de datosDELAYIN AL,DX ; leer byte del FDCXPOP <DX, CX>CLC ; OkRET

fdc_read ENDP

ELSE

fdc_read PROCXPUSH <CX, DX>MOV DX,3F4h ; registro de estado del FDCXOR CX,CX ; evitar cuelgue total si falla

espera_rd: IN AL,DX ; leer registro de estadoTEST AL,80h ; ¿bit 7 inactivo?LOOPZ espera_rd ; así es: el FDC está ocupadoJCXZ fdc_rd_nokINC DX ; apuntar al registro de datosIN AL,DX ; leer byte del FDCCLCXPOP <DX, CX>RET

fdc_rd_nok: OR status,80h ; timeoutSTCXPOP <DX, CX>RET

fdc_read ENDP

ENDIF

; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si; la operación fracasó (el FDC no estaba listo) y; se indica la condición de timeout en «status».

IFNDEF XT

fdc_write PROCXPUSH <CX, DX, AX>CALL fdc_respiro ; no abrasar el FDCMOV DX,3F4h ; registro de estado del FDCMOV CX,133 ; constante para 0,002 segundos

espera_wr: DELAYIN AL,DXTEST AL,80h ; ¿listo para E/S?JNZ fdc_wr_okDELAYIN AL,61hAND AL,10hCMP AL,AHJE espera_wr ; reintentarlo durante 15,09 µsMOV AH,ALLOOP espera_wrXPOP <AX, DX, CX>OR status,80h ; timeoutSTC ; falloRET

fdc_wr_ok: INC DX ; apuntar al registro de datosPOP AXDELAYOUT DX,AL ; enviar byte al FDCXPOP <DX, CX>CLC ; OkRET

fdc_write ENDP

ELSE

fdc_write PROCXPUSH <AX, CX, DX>MOV DX,3F4h ; registro de estado del FDCXCHG AH,AL ; preservar AL en AHXOR CX,CX ; evitar cuelgue total si falla

espera_wr: IN AL,DX ; leer registro de estadoTEST AL,80h ; ¿bit 7 inactivo?LOOPZ espera_wr ; así es: el FDC está ocupadoJCXZ fdc_wr_nokXCHG AH,AL ; recuperar el dato de ALINC DX ; apuntar al registro de datosOUT DX,AL ; enviar byte al FDCXPOP <DX, CX, AX>CLCRET

fdc_wr_nok: OR status,80h ; timeoutXPOP <DX, CX, AX>STCRET

fdc_write ENDP

ENDIF

; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.

IFNDEF XT

fdc_respiro PROCXPUSH <AX, CX>MOV CX,4

fdc_ret: PMICROLOOP fdc_retXPOP <CX, AX>RET

fdc_respiro ENDP

ENDIF

; ------------ Esperar exactamente AX milisegundos.

Page 330: PCA, PS2 ,IBM y AT

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

IFNDEF XT

retardo PROCPUSHFXPUSHAMOV DX,16970 ; 16970 = 1193180/18*256/1000MUL DXMOV CL,AH ; dividir DX:AX entre 256 yMOV CH,DL ; dejar el resultado en DX:CXMOV DL,DHMOV DH,0 ; DX:CX 15,09 µs-avos

retardando: PMICROLOOP retardandoAND DX,DXJZ retardadoDEC DXJMP retardando

retardado: XPOPAPOPFRET

retardo ENDP

ELSE

retardo PROCPUSHFXPUSH <AX, BX, CX, DX>

retarda_mas: CMP AX,54 ; como máximo 54 ms cada vezJBE retarda_finPUSH AXMOV AX,54CALL rt_axPOP AXSUB AX,54JMP retarda_mas

retarda_fin: CALL rt_axXPOP <DX, CX, BX, AX>POPFRET

rt_ax: MOV DX,1000 ; retardo de hasta 54 msMUL DXMUL CS:tbaseMOV CX,54925DIV CX ; AX = contador iteracionesMOV CX,AXEVEN ; forzar alineamiento

retarda: DEC CXJMP SHORT $+2JNZ retarda

RETretardo ENDP

ENDIF

IFDEF SUPERBOOT

; ------------ Esta subrutina sustituye a la macro PMICRO en el; código SuperBOOT por razones de espacio.

pmicro_iter: DELAY ; retardo de aprox. 15,09 µsIN AL,61h ; (exactamente 18/1193180 sg.)AND AL,10h ; La rutina se puede ejecutarCMP AL,AH ; repetitivamente (se apoya enJE pmicro_iter ; AX) para hacer retardos aMOV AH,AL ; través de la temporizaciónRET ; del refresco de la memoria

; ------------ Código invocado durante el SuperBOOT desde 2MFBOTHD.ASM; A la entrada: CS=ES, SS=0 y AX = tipo unidades.

initcode: PUSH DSPUSH SSPOP DSMOV ES:[info_A.tipo_drv],AL ; anotar tipo de A:MOV ES:[info_B.tipo_drv],AH ; anotar tipo de B:LEA DI,ant_int13MOV SI,13h*4 ; vector de INT 13hCLDCLIMOVSWMOVSW ; anotada dirección INT 13hMOV WORD PTR [SI-4],OFFSET ges_int13MOV [SI-2],ESSTI ; desviada INT 13hPOP DSRETF ; volver a 2MFBOTHD

DB 4 DUP(0) ; esto ocupa 2560 bytes exactos

ant_int13 LABEL DWORD ; vector de la INT 13h previaant_int13_off DW initcodeant_int13_seg DW 0AA55h ; significa "2MFBOOT correcto"

ENDIF

; --- Ubicación del sector de hasta 2048 bytes.

EVENbuffer_io EQU $tbuffer EQU 2048

12.6.7.4 - DESCRIPCION DEL PROGRAMA DE FORMATEO (2MF) PARA 2M.

El formateo de los disquetes 2M puede realizarse desde un lenguaje de alto nivel por medio de lasfunciones de la BIOS implementadas por 2M cuando está residente. El siguiente programa de ejemplodemuestra lo sencilla que es esta tarea. El único problema importante que se presentó durante su desarrollofueron los conflictos que generaba WINDOWS al intentar formatear un disco en el formato de máximacapacidad (opción /M): por algún motivo, era imposible crear este tipo de pistas al producirse un extraño erroren la función de formatear. Este problema ya se había presentado en versiones anteriores de 2M, que tambiénformateaban los discos. La solución adoptada es, sencillamente, invocar la INT 13h mediante un CALL a ladirección del vector de interrupción. De este modo no se ejecuta el código WINDOWS responsable de laincompatibilidad, que entraba en marcha al llamar a la INT 13h en modo protegido. Tenga en cuenta el lectorque una inocente instrucción INT es mucho más que eso bajo WINDOWS o con un controlador de memoriainstalado. Este fragmento de código de 2MF ha sido codificado en ensamblador, entre otros motivos porqueantes de llamar con CALL a una interrupción hay que apilar los flags y eso resulta difícil en C. Durante lasrestantes fases del formateo (lectura para verificar y la escritura previa en los formatos de máxima capacidad)se utilizan las funciones estándar de la BIOS vía INT 13h. Aunque WINDOWS no estorbara, tampocohubiera sido posible llamar con la función de formateo BIOS del compilador, ya que los parámetros cambianligeramente, si bien se podría haber hecho con código C.

El programa admite varios parámetros para controlar el formateo. Por defecto realiza el formateonormal, más fiable (o indicando la opción /F). Para seleccionar el formateo de máxima capacidad hay queindicar /M. Desde 2MF 3.0, el programa es capaz de detectar la densidad en discos de 3½ vírgenes (con laexcepción de las unidades que permiten formatear en alta densidad los discos de doble) y lo intenta en losde 5¼ (sólo funciona si ya tenían algún tipo de formato previo). En cualquier caso, siempre se puede indicarla opción /HD, /DD ó /ED para seleccionar la densidad necesaria y evitar la pequeña pérdida de tiempo endetectarla.

El número de pistas, por defecto 82, puede elegirse con /T, ya que muchas unidades soportan 84pistas o más; de todas maneras, 2MF 3.0 no permite formatear más pistas de las que admita la unidad, al

Page 331: PCA, PS2 ,IBM y AT

331EL HARDWARE DE APOYO AL MICROPROCESADOR

contrario que las versiones anteriores. Los ficheros permitidos en el directorio raíz se indican con /R. Elparámetro /S evita la producción de sonido. Con /N se evita la verificación, /K y /J eliminan la pausa inicialy final, respectivamente; /Z anula el parpadeo del led mientras se cambia el disco y /L y /V permiten poneretiquetas de volumen (serializadas en el último caso) al disco destino.

Finalmente, hay varios parámetros no documentados oficialmente que no deberían ser alterados, salvoquizá en algún ordenador muy concreto y por parte de usuarios muy especializados, que permiten elegir losfactores de desplazamiento en la numeración de los sectores al conmutar de cabezal (/X) y de cilindro (/Y)en el formato normal (/F); en el formato de máxima capacidad (/M) no tienen efecto. El parámetro /G permiteindicar el GAP o separación de sectores en todas las pistas -salvo la primera- en el formato /F; en el formato/M este valor de GAP se refiere al GAP empleado en la primera pasada del formateo (con sectores de 128bytes). Con /D0 se formatea en 3½-DD con 820/902K (en lugar de 984/1066K), algo necesario en lascontroladoras de algunos portátiles que no soportan la densidad de 300 Kbps (propia exclusivamente de lasunidades de 5¼); si bien no es preciso emplearlo ya que por defecto el programa formatea de esta maneraen esas unidades al autodetectar la densidad del disco destino. /D1 formatea 1148K en lugar de 1066K, peroel disco resultante es poco seguro y extremadamente lento. Por último, la opción /W hace que se marquensólo los clusters defectuosos y no la pista completa.

TIEMPO EMPLEADO EN EL FORMATEO

FORMAT FDFORMAT (*) FDFORMAT (**) 2MF 3.0 /F 2MF 3.0 /M

5¼-DD 0:37 0:42 1:28 1:26 2:375¼-HD 1:13 1:24 1:52 1:29 2:383½-DD 1:24 1:38 1:46 1:39 2:513½-HD 1:34 1:42 2:17 1:47 3:22

(*) Usando el formato estándar del DOS (360-720-1.2-1.44) y los parámetros /X e /Y adecuados.(**) Formatos de máxima capacidad soportados (820-1.48-1.72) y los parámetros /X e /Y adecuados.

La parte más compleja del programa es la función CrearSector0(), que como su propio nombreindica se encarga de crear el sector de arranque del futuro disquete. En un programa de copia de discos estafunción no sería necesaria, ya que al leer el disquete origen tendríamos ya el sector de arranque del futurodisquete destino y, por tanto, podríamos formatearle directamente (recordar que la función de formateo dediscos 2M sólo necesita como parámetro el sector de arranque del futuro disco). Sin embargo, aquí nos vemosobligados a crear dicho sector, lo cual es una tarea un tanto engorrosa, teniendo en cuenta la variedad deformatos. Una tabla más o menos complicada, de 5 dimensiones, contiene toda la información necesaria parala tarea. Además, el código ejecutable del sector de arranque resultaba difícil incluirlo dentro del listado Cy finalmente se optó por crear un fichero proyecto e incluir en él 2MF.C y 2MFKIT.ASM (este último integralos sectores de arranque para alta y doble densidad -con y sin soporte SuperBOOT, respectivamente- así comoel código SuperBOOT y las rutinas de utilidad).

Durante el proceso de formateo, en FormatearDisco() se está pendiente de una posible pulsación dela tecla ESC. Se controlan los posibles errores fatales, tales como unidad protegida de escritura o nopreparada, que suponen el fin del proceso de formateo, pero se toleran los demás errores -si no afectan a lasáreas del sistema del disco- marcando los clusters afectados como defectuosos si al tercer intento de formateosiguen fallando. Al final del formateo, en InformeDisco() se imprimen las características del nuevo disquetepero sin emplear funciones del DOS. Realmente, el DOS ya se ha dado cuenta del cambio de disco einformaría correctamente, pero de esta manera se asegura a ultranza que la información es correcta.

La función TipoDrive() devuelve el tipo de la disquetera que se le indique consultando estainformación a través de la BIOS. La función InicializaDisco() escribe, al final del formateo, el sector dearranque físico, el virtual, el código SuperBOOT (si el disco es de alta densidad) y la FAT; de esta últimasólo la primera copia, ya que 2M emulará la segunda.

Las funciones de sonido crean efectos especiales bastante atractivos gracias al empleo de retardos demedio milisegundo con la función PicoRetardo(); este retardo es idéntico en todas las máquinas, con total

Page 332: PCA, PS2 ,IBM y AT

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

independencia de la velocidad de la CPU, y permite que el sonido suene igual en todas. En los PC/XT nose realiza retardo alguno y, curiosamente, el sonido suena igual que en los AT (en máquinas de 8 MHz).

La función EsperarCambioDisco() espera a que se retire el disquete de la unidad y se introduzcauno nuevo, o bien a que se pulse una tecla (considerando el caso de ESC, para abortar el formateo). Estopermite formatear varios disquetes introduciéndolos unos tras otros en la unidad sin necesidad de pulsarteclas. En WINDOWS se puede abrir una ventana para formatear disquetes 2M y, dejándola en la sombra,cada vez que se oiga el sonido de fin de formateo, sin abandonar lo que se tenga en ese momento entremanos, se puede sacar el disco e introducir otro para que el proceso continúe automáticamente sin tener queactivar la ventana para pulsar una tecla. El sonido de final de formateo permite distinguir entre un formateocorrecto y otro con errores (se considera correcto aunque haya sectores defectuosos, lo de errores va por lode disco protegido contra escritura, etc.). Las rutinas de bajo nivel que acceden a la controladora de discoen 2MF lo hacen, exclusivamente, para conseguir el efecto intermitente en el led de la unidad mientras secambia de disco (y para reducir el ruido que emite la función de detección de nuevo disco de la BIOS).

Para fomentar que los usuarios envíen la postal al autor, el programa tiene un contador de discosformateados añadido cuando formatea el primer disco por el método de alargar el tamaño del fichero EXE.Al cabo de 100 discos, imprime un mensaje recordando al usuario su deber. Naturalmente, si 2MF se ejecutadesde una unidad protegida contra escritura, no será posible actualizar el contador...

Finalmente, la función HablaSp() comprueba el país en que se ejecuta el programa para inicializaruna variable global que indique si los mensajes han de ser imprimidos en castellano o en inglés.

/* \

2MF.C 3.0 - UTILIDAD DE FORMATEO DE DISQUETES 2M

(C) 1994 Ciriaco García de Celis.

- Para cualquier Turbo C o Borland C en modelo de memoria LARGE.- Este programa se compila abriendo un proyecto e introduciendoen él 2MF.C y 2MFKIT.OBJ

- Importante: no activar ciertas optimizaciones que no lo estánpor defecto (como la de alineamiento a palabra o la de salto).

- NOTA: Las funciones de bajo nivel que acceden directamente ala controladora de disquetes no son indispensables, tansólo se emplean para producir menos ruido al detectarla introducción de un nuevo disquete en la unidad.

Este programa detecta además la presencia de una posibleutilidad de intercambio de unidades A:-B: llamada FDSWAPpara que en caso de estar activado dicho intercambio seaposible acceder a la unidad física correspondiente.

\ */

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <dos.h>#include <bios.h>#include <time.h>#include <alloc.h>#include <conio.h>#include <io.h>#include <fcntl.h>

#define CARDWARE 100 /* nº discos formateados antes del aviso */#define MAXSECT 46 /* máximo número de sectores por pista */#define MAXFAT 6128 /* mayor FAT de 12 bits posible */#define BOOT2M 80 /* bytes principales del Boot */#define FD_DATA 0x3F5 /* registro de datos del 765 */#define FD_STATUS 0x3F4 /* registro principal de estado del 765 */#define FD_DOR 0x3F2 /* registro de salida digital */#define FD_DIR 0x3F7 /* registro de entrada digital (RD) */#define FD_DCR 0x3F7 /* registro de control del disquete(WR) */

typedef struct /* sector arranque disquetes 2M */unsigned char Salto[3], IdSis[8];short BytesSect;char SectCluster;short SectReserv;char NumFats;short FichRaiz, NumSect;char MediaId;short SectFat, SectPista, Caras;long Especiales, Sect32;char Unidad, Reservado, Flag;long NumSerie;char Titulo[11], TipoFat[8];char Flags;char CheckSum;

char VersionFmt, FlagWr, VelPista0, VelPistaX;short OffsetJmp, OffsetPista0, OffsetPistaX, OffsetListaTam;short FechaF;short HoraF;char Resto[512-BOOT2M]; /* depende del tamaño de lo anterior */ Boot;

typedef struct /* entrada de directorio */char Etiqueta[11];char Tipo;char Reservado[10];int Hora;int Fecha;char Resto[6]; Root;

typedef struct /* parámetros en línea de comandos */int Unidad, HD, ED, TipoFmt,

NoVerify, MarcaPoco,Pistas, FichRaiz, Silencioso, NoPausa, NoTecla, X, Y, G,Tipoetiq, NoFlash;

char Volumen[12]; Parametros;

int HablaSp (void),Hay2m (void),Hay2mBoot (void),FdswapOn (void),TipoDrive (int),EsperarCambioDisco (int, int),infdc (void),ValeDensidad (Boot *, Parametros *),FormatearDisco (Boot *,unsigned char far *,unsigned char far *,

Parametros *, long *, int *),MarcaFat (int, int, Boot *, int, int, unsigned char far *,

unsigned char far *, long *),InicializaDisco (int, Boot *, unsigned char far *,

unsigned char far *);void Ayuda (void),

ProcesarParametros (int, char **, Parametros *),DetectaMedio (Parametros *, Boot *),CrearSector0 (Boot *, Parametros),DiagnosticoError (int),InformeDisco (Boot *, Parametros *, long, int),IncrementarEtiqueta (Parametros *),SonidoSube (void),SonidoBaja (void),SonidoError (void),SonidoOn (void),SonidoOff (void),Sonido (int),posicionar (int, int),outfdc (unsigned char),EsperarInt (void),CardWare (char *, int);

extern BootHDPrg, BootHDPrgLong, BootDDPrg, BootDDPrgLong,Boot2mCode, Boot2mLong,biosdsk (int, int, int, int, int, int, void far *);void interrupt NuevaInt24 (void);

extern void PicoRetardo (void), interrupt (*ViejaInt24) (void);

int sp; /* 1-español 0-inglés */

unsigned long far *cbios=MK_FP(0x40, 0x6C); /* reloj del sistema */unsigned char far *irq6=MK_FP(0x40, 0x3E); /* flag BIOS de IRQ6 */

Page 333: PCA, PS2 ,IBM y AT

333EL HARDWARE DE APOYO AL MICROPROCESADOR

void main (int argc, char **argv)

Boot sector0;Parametros cmd;int salir, result, sg, detectar;long bytes_err, dir;unsigned char far *buffer; /* para contener toda una pista */unsigned char far *fat; /* para contener toda la FAT */int disquetes=0; /* nº discos formateados */void interrupt(*ViejaInt24) (void);

sp=HablaSp(); /* determinar idioma del país */

ProcesarParametros (argc, argv, &cmd);

if (!Hay2m())if (!Hay2mBoot()) if (sp)

printf(" 2M ó 2MX 3.0 no está instalado, imposibleformatear.\n");

elseprintf(" 2M or 2MX 3.0 is not installed, impossible to

format.\n");exit(128);

else if (sp)

printf(" Modo SuperBOOT: instale 2M para dar formato.\n");elseprintf(" SuperBOOT mode: needed to install 2M to

format.\n");exit(127);

if (((fat=farmalloc( (unsigned long) MAXFAT))==NULL) ||((buffer=farmalloc( (unsigned long) MAXSECT<<10))==NULL)) if (sp) printf(" Memoria insuficiente.\n");else printf(" Insufficient memory.\n");

exit(126);

/* Definir el buffer para que no cruce una frontera de DMA */

dir = ((unsigned long) FP_SEG(buffer) <<4) + FP_OFF(buffer);if ((dir >> 16) != ((dir + ((unsigned long) MAXSECT << 9)) >> 16))

buffer+=(unsigned long) MAXSECT << 9;

if (!cmd.NoPausa) if (sp)

printf(" Pulsa una tecla para formatear en");elseprintf(" Press any key to format on");

printf(" %c:", cmd.Unidad+’A’);salir=getch()==27;

elsesalir=0;

/* si no se indica densidad detectarla */

detectar = (cmd.HD==-1);

/* formateo de múltiples disquetes */

while (!salir) if (detectar) DetectaMedio (&cmd, &sector0);CrearSector0 (&sector0, cmd);if (!cmd.Silencioso) SonidoSube();switch (result=FormatearDisco (&sector0, fat, buffer, &cmd,

&bytes_err, &sg)) case 0: InformeDisco (&sector0, &cmd, bytes_err, sg);

if (!cmd.Silencioso) SonidoBaja();if (cmd.Tipoetiq==2) IncrementarEtiqueta (&cmd);disquetes++;break;

case 1: DiagnosticoError (result);break;

default: DiagnosticoError (result);if (!cmd.Silencioso) SonidoError(); break;

if (cmd.NoTecla)

salir=1;else if (sp)

printf("\n Introduce otro disquete para formatear en");elseprintf("\n Please insert another disk to format in");

printf(" %c:", cmd.Unidad+’A’);

if (!EsperarCambioDisco(cmd.Unidad, cmd.NoFlash)) salir=1;

printf("\r \r");

ViejaInt24=getvect(0x24);setvect (0x24, NuevaInt24); /* evitar error crítico */CardWare (argv[0], disquetes); /* intentar actualizar 2MF.EXE */setvect (0x24, ViejaInt24);

void ProcesarParametros (int argc, char **argv, Parametros *cmd)int pm, error=0, hlp=0, id=1;

cmd->Unidad=cmd->TipoFmt=cmd->ED=cmd->NoVerify=cmd->MarcaPoco=0;cmd->HD=-1; cmd->Pistas=82;cmd->FichRaiz=cmd->Silencioso=cmd->NoFlash=cmd->NoPausa=\

cmd->NoTecla=cmd->Tipoetiq=0;cmd->X=cmd->Y=cmd->G=-1;

for (pm=1; pm<argc; pm++)if (strstr(&argv[pm][1], "/")!=NULL) error=-1; /* parámetros unidos

*/

if (!error) for (pm=1; pm<argc; pm++) if ((strstr(argv[pm],"/L")!=NULL) || (strstr(argv[pm],"/l")!=NULL))

strncpy (cmd->Volumen, &argv[pm][3], 11);cmd->Volumen[11]=0;while (strlen(cmd->Volumen)<11) strcat(cmd->Volumen, " ");cmd->Tipoetiq=1;continue;e l s e i f ( ( s t r s t r ( a r g v [ p m ] , " / V " ) ! = N U L L ) | |

(strstr(argv[pm],"/v")!=NULL))

strncpy (cmd->Volumen, &argv[pm][3], 11);cmd->Volumen[11]=0;while (strlen(cmd->Volumen)<11) strcat(cmd->Volumen, " ");cmd->Tipoetiq=2;continue;

strupr (argv[pm]);if (strstr(argv[pm],"/?")!=NULL) hlp++;else if ((strstr(argv[pm],"/H")!=NULL) && (strlen(argv[pm])==2))

hlp++;else if ((strstr(argv[pm],"A:")!=NULL) ||

(strstr(argv[pm],"B:")!=NULL)) cmd->Unidad=*argv[pm]-’A’;else if (strstr(argv[pm],"/HD")!=NULL) cmd->HD=1;else if (strstr(argv[pm],"/DD")!=NULL) cmd->HD=0;else if (strstr(argv[pm],"/D0")!=NULL) cmd->HD=2;else if (strstr(argv[pm],"/D1")!=NULL) cmd->HD=3;else if (strstr(argv[pm],"/F")!=NULL) cmd->TipoFmt=0;else if (strstr(argv[pm],"/M")!=NULL) cmd->TipoFmt=1;else if (strstr(argv[pm],"/ED")!=NULL) cmd->ED=1;else if (strstr(argv[pm],"/N")!=NULL) cmd->NoVerify=1;else if (strstr(argv[pm],"/W")!=NULL) cmd->MarcaPoco=1;else if (strstr(argv[pm],"/T")!=NULL)

cmd->Pistas = atoi (&argv[pm][3]);else if (strstr(argv[pm],"/R")!=NULL)

cmd->FichRaiz = atoi (&argv[pm][3]);else if (strstr(argv[pm],"/S")!=NULL) cmd->Silencioso=1; id++;

else if (strstr(argv[pm],"/K")!=NULL) cmd->NoPausa=1;else if (strstr(argv[pm],"/J")!=NULL) cmd->NoTecla=1;else if (strstr(argv[pm],"/Z")!=NULL) cmd->NoFlash=1;else if (strstr(argv[pm],"/X")!=NULL) cmd->X=atoi(&argv[pm][3]);else if (strstr(argv[pm],"/Y")!=NULL) cmd->Y=atoi(&argv[pm][3]);else if (strstr(argv[pm],"/G")!=NULL) cmd->G=atoi(&argv[pm][3]);else if (strstr(argv[pm],"/I")!=NULL) sp^=1; id++; else error=1;

if (cmd->ED && (cmd->HD!=1)) cmd->HD=1; /* /DD ó /Dx + /E = /E */

if ((argc<=1) || (argc==id)) hlp++;

if (hlp) Ayuda();

if (sp)printf("\n2MF 3.0 - Utilidad de formateo de disquetes 2M

(ESC Salir)\n");else

printf("\n2MF 3.0 - Format utility program for 2M diskettes(ESC Aborts)\n");

if (error==1) if (sp)

printf(" Error de sintaxis. Ejecute 2MF /?.\n");else

printf(" Incorrect parameter(s). Execute 2MF /?.\n");exit (2);

if (error==-1) if (sp)

printf(" Error: Los parámetros deben separarse porespacios.\n");

elseprintf(" Error: Parameters must be separated by blank

spaces.\n");exit (2);

if (TipoDrive(cmd->Unidad)==0) if (sp)

printf(" La unidad física indicada no existe.\n");else

printf(" Physical drive indicated does not exist.\n");exit (2);

if ((TipoDrive(cmd->Unidad)!=2) && (TipoDrive(cmd->Unidad)<4)) if (sp)

printf(" La unidad indicada no es de alta densidad.\n");else

printf(" Drive indicated it is not high density one.\n");exit (2);

if ((TipoDrive(cmd->Unidad)<5) && (cmd->ED==1)) if (sp)

printf(" Necesaria unidad de 2.88M para formato ED.\n");else

printf(" Needs a 2.88M drive to perform ED format.\n");exit (2);

if ((cmd->Pistas<80) || (cmd->Pistas>86)) if (sp)

printf(" Error: Número de pistas incorrecto.\n");else

printf(" Error: Incorrect number of tracks.\n");exit (2);

if (cmd->FichRaiz && ((cmd->FichRaiz<1) || (cmd->FichRaiz>240))) if (sp)

printf(" Error: Nº de ficheros en directorio raiz erróneo.\n");else

printf(" Error: Bad number of files in root directory.\n");exit (2);

void Ayuda()

if (sp) printf("\n\n"

" 2MF 3.0 - UTILIDAD ESTANDAR DE FORMATEO DE DISQUETESPARA 2M\n"

" (C) 1994 Ciriaco García de Celis - Grupo Universitario deInformática\n"

" C/Renedo, 2, 4-C; 47005 Valladolid (España) - [email protected] 2:341/21.8\n\n"

" 2MF U: [/HD|DD|ED] [/F|M] [/N] [/L|V=etiq] [/S] [/Z] [/R=nn][/T=nn] [/K] [/J]\n\n"

" Este programa formatea disquetes a una mayor capacidad y/ovelocidad de la\n"

" normal. Para que estos nuevos disquetes funcionen debe estarinstalado 2M en\n"

" memoria. Alternativamente, si son de alta densidad se puedendejar dentro de\n"

" la unidad A: y reinicializar el ordenador, que botará pesea todo del disco\n"

" duro y podrá acceder a los disquetes 2M sin problemas enlectura/escritura.\n\n"

" /HD Formateo en alta densidad (por defecto si 2MF no detectala densidad).\n"

Page 334: PCA, PS2 ,IBM y AT

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

" /DD Fuerza el formateo en doble densidad (aunque 2MF quizála detecte).\n"

" /ED Formatear disquetes de 3½-ED (3608K por defecto o 3772Kindicando /M).\n"

" /F Disquetes rápidos y seguros -por defecto- (5¼:820-1476K,3½:984-1804K).\n"

" /M Formatear disquetes a la máxima capacidad (5¼:902-1558K,3½:1066-1886K).\n"

" /N No verificar el disquete destino (peligroso en modo/M).\n"

" /L Poner etiqueta de volúmen al disco destino (minúsculaspermitidas).\n"

" /V Etiqueta incremental en series de discos (si termina ennúmero).\n"

" /S Funcionamiento silencioso /Z Evitar parpadeo deled de disco.\n"

" /R Elegir nº ficheros raíz (1-240) /T Cambiar número depistas (80-86).\n"

" /K No realizar pausa inicial /J No realizar pausafinal.\n");

else printf("\n\n"" 2MF 3.0 - STANDARD FORMAT UTILITY FOR 2M

DISKETTES\n"" (C) 1994 Ciriaco García de Celis - Grupo Universitario de

Informática\n"" C/Renedo, 2, 4-C; 47005 Valladolid (Spain) - [email protected]

- 2:341/21.8\n\n"" 2MF U: [/HD|DD|ED] [/F|M] [/N] [/L|V=label] [/S][/Z] [/R=nn]

[/T=nn] [/K][/J]\n\n"" This program formats diskettes at a higher capacity and/or

speed than the\n"" normal ones. 2M must be installed on memory to provide

support for the new\n"" diskettes. Also, high-density diskettes can be left into A:

drive and then\n"" computer can be rebooted: really it will boot from hard disk

and after this\n"" moment 2M diskettes will be supported in the standard

read-write operation.\n\n"" /HD High density format (by default if 2MF can’t detect

diskette density).\n"" /DD Request a double-density format (but 2MF perhaps can

detect DD disk).\n"" /ED Formats 3.5-ED diskettes at 3608K (or 3772K if /M option

enabled).\n"" /F Fast and secure diskettes -by default- (5¼:820-1476K,

3½:984-1804K).\n"" /M Formats diskettes up to maximum capacity (5¼:902-1558K,

3½:1066-1886K).\n"" /N Do not verify target diskette (dangerous in /M mode).\n"" /L Sets diskette volume label (case sensitive).\n"" /V Automatic sequencing of labels (if specified one is

number terminated).\n"" /S Tells 2MF not to make sound effects /Z Turn disk led

«flashing» off.\n"" /R Sets root entries number (1-240) /T Sets number of

tracks (80-86).\n"" /K No initial pause before formatting /J No end pause

after formatting.\n");

exit (1);

int Hay2m() /* devolver 1 si 2M está instalado */int entrada, instalado=0;union REGS r; struct SREGS s;

for (entrada=0xc0; (entrada<=0xff) && (!instalado); entrada++) r.x.ax=entrada << 8; s.es=0x1492; r.x.di=0x1992;int86x (0x2f, &r, &r, &s);if (r.x.ax==0xFFFF)if ((peek(s.es,r.x.di-4)==9002) && (peek(s.es,r.x.di-2)==10787))if (strstr (MK_FP(s.es, r.x.di),"2M:3.0")) instalado=1;if (strstr (MK_FP(s.es, r.x.di),"2MX:3.0")) instalado=1;

return (instalado);

int Hay2mBoot() /* devolver 1 si 2M instalado en modo SuperBOOT */return (strstr(MK_FP(((unsigned) peek(0x40, 0x13) * 64), 4),

"2M-STV")!=NULL);

int FdswapOn() /* devolver 1 si FDSWAP 1.1+ está instalado y activo */int entrada, instalado=0;union REGS r; struct SREGS s;

for (entrada=0xc0; (entrada<=0xff) && (!instalado); entrada++) r.x.ax=entrada << 8; s.es=0x1492; r.x.di=0x1992;int86x (0x2f, &r, &r, &s);if (r.x.ax==0xFFFF)if ((peek(s.es,r.x.di-4)==9002) && (peek(s.es,r.x.di-2)==10787))if (strstr (MK_FP(s.es, r.x.di),":FDSWAP:")) instalado=1;

return ((instalado) && (peekb(s.es, peek(s.es,r.x.di-6)-1)==1));

void CrearSector0 (Boot *s0, Parametros cmd)unsigned tipo, tabla, i, j, k, m, t, s, tam, ini, fin, inc;char id[8]="2M-STV00", ch, sum, far *p;struct time h;struct date f;static unsigned char infofis [2][3][2][4][20] =10,176,7,0,1,1, 9,80,1,1, /* 5¼-DD /F */

5,100,3,1,1 ,11,176,7,1,1,1, 9,80,1,1, /* /M */32,4,5,3,1,4,2,0, 4,2,4,3,0 ,

18,224,7,0,0,0, 16,60,1,1, /* 5¼-HD /F */9,50,3,1,2 ,

19,224,7,1,0,0, 17,25,1,2, /* /M */53,3,6,4,1,5,2,6,3, 4,4,2,4,4,3 ,

0,0,0,0,0,0, 0,0,0,0, /* no usado */0,0,0,0,0, ,

14,192,7,1,2,1, 9,80,1,1, /* 3½-DD /D1 */38,2,4,3,1,4,2, 4,3,4,4 ,

12,192,7,0,2,1, 9,80,1,1, /* 3½-DD /F */6,100,3,1,1 ,

13,192,7,1,2,1, 9,80,1,1, /* /M */

38,5,6,3,1,4,2,0,0, 4,2,4,4,0,0 ,22,224,7,0,0,0, 19,70,1,1, /* 3½-HD /F */11,40,3,1,2 ,23,224,7,1,0,0, 19,70,1,1, /* /M */64,3,7,4,1,5,2,6,3,7, 4,4,4,4,4,3,2 ,

44,240,7,0,3,3, 36,108,1,1, /* 3½-ED /F */11,126,4,1,2 ,46,240,7,1,3,3, 36,108,1,1, /* /M */127,5,12,1,7,2,8,3,9,4,10,5,11,6,12,4,4,4,4,4,4,4,4,4,4,4,3 ;

/* Significado de la tabla /F:SectLogPistaX, fichraiz, verFmt, flagWr, velpista0, velpistaX,sectpista0, GAP3pista0, primsectpista0, interleavepista0,SectFisPistaX, GAP3pistaX, tamsectpistaX, /X, /YSignificado de la tabla /M:SectLogPistaX, fichraiz, verFmt, flagWr, velpista0, velpistaX,sectpista0, GAP3pista0, primsectpista0, interleavepista0,Sectpreformat, GAP3pistaX, SectFisPistaX, sects numerados...,tamaños de sectores por orden...

*/

if ((cmd.HD==2) && (TipoDrive(cmd.Unidad)>=4)) cmd.HD=0; tabla=0; tipo=0;infofis[0][0][cmd.TipoFmt][0][4]=2; /* 3½-DD a 250 Kbps */infofis[0][0][cmd.TipoFmt][0][5]=2;

else if ((cmd.HD==3) && (TipoDrive(cmd.Unidad)>=4)) cmd.HD=tipo=0;cmd.TipoFmt=1; tabla=2; /* 3½-DD con 1148K */

else if (cmd.HD>1) cmd.HD=0;tabla=cmd.HD+cmd.ED; /* seleccionar tabla de datos */if (TipoDrive(cmd.Unidad)<3)

tipo=0; /* 5¼ */else

tipo=1; /* 3½ */

ch=1+cmd.HD;if (TipoDrive(cmd.Unidad)>2) ch+=2; if (!cmd.TipoFmt) ch+=4;if (cmd.ED) ch=10-cmd.TipoFmt;id[6]=(ch/10)+’0’; id[7]=(ch % 10)+’0’; strncpy (s0->IdSis, id, 8);

s0->BytesSect=512;s0->SectCluster = s0->SectReserv = 1; s0->NumFats=2;if (cmd.ED) s0->SectCluster=2;

if (!cmd.FichRaiz)s0->FichRaiz=infofis[tipo][tabla][cmd.TipoFmt][0][1];

elseif (cmd.FichRaiz % 16)

s0->FichRaiz=((cmd.FichRaiz >> 4) + 1) << 4;else

s0->FichRaiz=cmd.FichRaiz;

if (ch==6)s0->MediaId=0xF0; /* compatible SCANDISK */

elses0->MediaId=0xFA; /* compatible SCANDISK */

s0->SectPista=infofis[tipo][tabla][cmd.TipoFmt][0][0];s0->Caras=2;s0->NumSect=cmd.Pistas*s0->Caras*s0->SectPista;

j = 3 * (s0->NumSect - (s0->FichRaiz>>4) - 1);k = 6 + 1024 * s0->SectCluster;s0->SectFat = j/k; if (j % k) s0->SectFat++;

s0->Unidad = s0->Reservado = 0; s0->Especiales = s0->Sect32 = 0L;s0->Flag=0x29; randomize();for (i=0; i<4; i++)

s0->NumSerie = (s0->NumSerie<<8) | (unsigned char) random(32767);

if (cmd.Tipoetiq)strncpy (s0->Titulo, cmd.Volumen, 11);

elsestrncpy (s0->Titulo, "NO NAME ", 11);

strncpy (s0->TipoFat, "FAT12 ", 8);

s0->VersionFmt=infofis[tipo][tabla][cmd.TipoFmt][0][2];s0->FlagWr=infofis[tipo][tabla][cmd.TipoFmt][0][3];s0->VelPista0=infofis[tipo][tabla][cmd.TipoFmt][0][4];s0->VelPistaX=infofis[tipo][tabla][cmd.TipoFmt][0][5];

s0->Flags=1; /* Fecha y hora de formateo almacenada */gettime (&h); getdate (&f);s0->FechaF=((f.da_year-1980)<<9) | (f.da_mon<<5) | f.da_day;s0->HoraF=(h.ti_hour<<11) | (h.ti_min<<5) | (h.ti_sec>>1);

tam=BOOT2M; /* lo que precede a la primera tabla */s0->OffsetPista0=tam;s0->Resto[0]=infofis[tipo][tabla][cmd.TipoFmt][1][0];s0->Resto[1]=infofis[tipo][tabla][cmd.TipoFmt][1][1];ch=infofis[tipo][tabla][cmd.TipoFmt][1][2];inc=infofis[tipo][tabla][cmd.TipoFmt][1][3];ini=tam+2; fin=ini+s0->Resto[0]; k=0;for (i=j=0; j<s0->Resto[0]; j++)

s0->Salto[ini+i]=ch++; if (ch>s0->Resto[0]) ch=1;i+=inc; if (ini+i>=fin) i=++k;

ini=fin; s0->OffsetPistaX=ini;if (!s0->FlagWr)

k=infofis[tipo][tabla][cmd.TipoFmt][2][0]; j=5;for (i=0; i<j; i++)

s0->Salto[ini+i]=infofis[tipo][tabla][cmd.TipoFmt][2][i];if (cmd.X!=-1) s0->Salto[ini+3]=cmd.X;if (cmd.Y!=-1) s0->Salto[ini+4]=cmd.Y;

else k=infofis[tipo][tabla][cmd.TipoFmt][2][2]; j=(k+1)*3;for (i=0; i<3; i++)

s0->Salto[ini+i]=infofis[tipo][tabla][cmd.TipoFmt][2][i];m=129;for (i=3; i<=k*3; i+=3)

s0->Salto[ini+i]=m;s=infofis[tipo][tabla][cmd.TipoFmt][2][i/3+2];s0->Salto[ini+i+1]=s;t=infofis[tipo][tabla][cmd.TipoFmt][3][s-1];switch (t)

case 0: m+=1; break; case 1: m+=2; break;case 2: m+=3; break; case 3: m+=6; break;case 4: m+=11; break; case 5: m+=22; break;

s0->Salto[ini+i+2]=t;

Page 335: PCA, PS2 ,IBM y AT

335EL HARDWARE DE APOYO AL MICROPROCESADOR

if (cmd.G!=-1) s0->Salto[ini+1]=cmd.G;fin=ini+j;

ini=fin; s0->OffsetListaTam=ini;if (!s0->FlagWr)

for (i=0; i<k; i++)s0->Salto[ini+i]=infofis[tipo][tabla][cmd.TipoFmt][2][2];

elsefor (i=0; i<k; i++)s0->Salto[ini+i]=infofis[tipo][tabla][cmd.TipoFmt][3][i];

fin=ini+k;

ini=fin; s0->OffsetJmp=ini;s0->Salto[0]=0xE9;s0->Salto[1]=(ini-3) % 256; s0->Salto[2]=(ini-3) >> 8;

if (cmd.HD == 0) p=(char far *) &BootDDPrg; k=BootDDPrgLong;

else p=(char far *) &BootHDPrg; k=BootHDPrgLong;

for (i=0; (i<k) && (ini+i<509); i++) s0->Salto[ini+i]=*p++;fin=ini+i;

for (i=fin; i<510; i++) s0->Salto[i]=0;if (fin<497) strncpy (&s0->Salto[496], "Made in Spain", 13);s0->Salto[509]=0; s0->Salto[510]=0x55; s0->Salto[511]=0xAA;

for (sum=0, j=64; j<ini; j++) sum+=s0->Salto[j]; /* checksum */s0->CheckSum=-sum;

void DetectaMedio (Parametros *cmd, Boot *sector0)int sg;

/* simular cambio de disco para inicialización plena de 2M */

biosdsk (5, cmd->Unidad, 0, 0, 0xFF, 0x7F, NULL);

/* hacer reset */

biosdsk (0, cmd->Unidad, 0, 0, 0, 0, NULL);

for (sg=0; sg<2; sg++) if (sp)

printf("\r Determinando densidad del disquete...");

elseprintf("\r Detecting diskette media density... ");

printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");

if (TipoDrive(cmd->Unidad)==2) /* en 5¼ intento pacífico */ cmd->HD=1; sg=2;sg=biosdsk (0, cmd->Unidad, 0, 0, 0, 0, NULL);sg=biosdsk (2, cmd->Unidad, 0, 0, 1, 1, (void *) sector0);if (sg==6) /* cambio de disco */sg=biosdsk (2, cmd->Unidad, 0, 0, 1, 1, (void *) sector0);

if (sg) break;if ((peekb (0x40, 0x8B) >> 6)!=0) cmd->HD=0; break;

cmd->ED=0; cmd->HD=1;if ((sg=ValeDensidad (sector0, cmd))==0) break; /* vale HD */if (kbhit()) if (getch()==27) break;if ((sg==3) || (sg==6) || (sg==128)) break; /* error */cmd->HD=0;if (!ValeDensidad (sector0, cmd)) break; /* vale DD */cmd->HD=1; if (kbhit()) if (getch()==27) break;cmd->HD=2;if (!ValeDensidad (sector0, cmd)) break; /* vale D0 */cmd->HD=1; if (kbhit()) if (getch()==27) break;cmd->ED=1;if (!ValeDensidad (sector0, cmd)) break; /* vale ED */cmd->HD=1; cmd->ED=0; if (kbhit()) if (getch()==27) break;

int ValeDensidad (Boot *sector0, Parametros *cmd)

CrearSector0 (sector0, *cmd);biosdsk (0, cmd->Unidad, 0, 0, 0, 0, NULL);biosdsk (5, cmd->Unidad, 0, 0, 0, 0x7F,

(unsigned char far *) sector0);return (biosdsk (2, cmd->Unidad, 0, 0,

cmd->HD==1?15:cmd->ED==1?36:9, 1,(unsigned char far *) sector0));

int FormatearDisco (sector0, fat, buffer, cmd, bytes_def, segundos)Boot *sector0;unsigned char far *fat;unsigned char far *buffer;Parametros *cmd;long *bytes_def;int *segundos;unsigned long dir, tiempo, rest, tini, hist[86], i, fase, fases;int cilindros, cilindro, cabezal, intento, error=1, spista, t;

if (cmd->G!=-1)if (sp)

printf("\r AVISO: ¡Valor de GAP alterado con opción /G!\n");elseprintf("\r WARNING: GAP value modified with /G switch!\n");

if (cmd->HD>1)if (sp)

printf("\r AVISO: ¡Parámetro indocumentado /D%d activo!\n",cmd->HD-2);

elseprintf("\r WARNING: Undocumented /D%d switch activated!\n",

cmd->HD-2);

if (cmd->MarcaPoco)if (sp)

printf("\r AVISO: ¡Parámetro indocumentado /W activo!\n");elseprintf("\r WARNING: Undocumented /W switch activated!\n");

if ((cmd->X!=-1) || (cmd->Y!=-1))if (sp)

printf("\r AVISO: ¡Parámetro indocumentado /X ó /Y activo!\n");

elseprintf("\r WARNING: Undocumented /X or /Y switch activated!\n");

if (sp)printf("\r Formateo de disquete ");

elseprintf("\r Formatting ");

switch (TipoDrive (cmd->Unidad)) case 2: printf("%s", cmd->HD==1?"5¼-1.2M":"5¼-360K"); break;case 4: printf("%s", cmd->HD==1?"3½-1.44M":"3½-720K"); break;default: if (cmd->ED) printf("3½-2.88M");

else printf("%s", cmd->HD==1?"3½-1.44M":"3½-720K");

if (sp)printf(" en %c: con %dK \n",

cmd->Unidad+’A’, sector0->NumSect>>1);else

printf(" diskette on %c: with %dK \n",cmd->Unidad+’A’, sector0->NumSect>>1);

for (i=0; i<MAXFAT; i++) fat[i]=0; /* poner a 0 la futura FAT */fat[0]=sector0->MediaId; fat[1]=fat[2]=0xFF;

for (i=0; i < ((unsigned long) MAXSECT <<9); i++) buffer[i]=0;

cilindros=sector0->NumSect/(sector0->SectPista*sector0->Caras);spista=sector0->SectPista; *bytes_def=0L;

fases=1L*cilindros*sector0->Caras*(1+(1-cmd->NoVerify)+sector0->FlagWr);fase=0L;

tini=*cbios;for (cilindro=0; cilindro < cilindros ; cilindro++)

for (cabezal=0; cabezal<sector0->Caras; cabezal++) for (intento=0; intento<3; intento++)

if (sp)printf("\r Cilindro %2d - Cara %d [F-] %3lu%%",

cilindro, cabezal, fase*100/fases);else

printf("\r Cylinder %2d - Side %d [F-] %3lu%%",cilindro, cabezal, fase*100/fases);

if (error) biosdsk (0, cmd->Unidad, 0, 0, 0, 0, NULL);t=0; while (bioskey(1)) t=bioskey(0);if ((t & 0xFF)==0x1B) error=1; goto AbortFormat;

else if ((t==0x1000) && (cilindro>1)) goto FinFormat;error=biosdsk (5, cmd->Unidad, cabezal,

cilindro, 0, 0x7F, (unsigned char far *) sector0);if (sector0->FlagWr==1) if (!error && (cilindro | cabezal))

printf ("\b\b\b\b\b\b\b\b\bI-] %3lu%%",(fase+1)*100/fases);error=biosdsk (3, cmd->Unidad, cabezal | 0x80,

cilindro, 1, spista, buffer);

if (!error&&(!cmd->NoVerify||(cmd->NoVerify && cilindro<2))) printf ("\b\b\b\b\b\b\b\b\b-V] %3lu%%",

(fase+1+sector0->FlagWr)*100/fases);error=biosdsk (2, cmd->Unidad, cabezal,

cilindro, 1, spista, buffer);

if (!error) break;

if (error)if ((error==128) || (error==3) || (error==6))

goto AbortFormat; /* error fatal */else

if (!MarcaFat(cmd->Unidad, cmd->MarcaPoco, sector0,cilindro, cabezal, fat, buffer, bytes_def))

goto AbortFormat; /* error en áreas del sistema */fase+=(1+(1-cmd->NoVerify)+sector0->FlagWr);

hist[cilindro]=*cbios;tiempo=(*cbios-tini)*10/182;printf(" [%2lu:%02lu ]", tiempo/60, tiempo % 60);if (cilindro>5)

rest=(*cbios-hist[cilindro-5])*(cilindros-cilindro)*10/910;printf("\b+%2lu:%02lu =%2lu:%02lu ]", rest/60, rest % 60,

(tiempo+rest)/60, (tiempo+rest) % 60);

if (!error && (cilindro>79)) /* verificar siempre aquí */ error=biosdsk (2, cmd->Unidad, 0, cilindro-1, 1, spista, buffer);if (error) /* no soportadas tantas pistas */

cilindros=cilindro; cilindro-=2;biosdsk (0, cmd->Unidad, 0, 0, 0, 0, NULL);

if (cmd->Pistas!=cilindros) /* no soportadas tantas pistas */t=cmd->Pistas;cmd->Pistas=cilindros; /* nº pistas correcto */CrearSector0 (sector0, *cmd); /* sector de arranque final */cmd->Pistas=t; /* restaurar parámetro */

FinFormat: error=InicializaDisco(cmd->Unidad, sector0, fat, buffer);

AbortFormat: printf("\r"); for (i=0; i<79; i++) printf(" ");

*segundos=(*cbios-tini)*10/182;

return (error);

void InformeDisco (s0, cmd, bd, tiempo)Boot *s0;Parametros *cmd;long bd;int tiempo;

unsigned long st, ua, bt;int cilindros;char label[12];

st = s0->NumSect - s0->NumFats * s0->SectFat- s0->SectReserv - (s0->FichRaiz>>4);

ua = st / (unsigned long) s0->SectCluster; bt = st*512L;

cilindros=s0->NumSect/(s0->SectPista*s0->Caras);

strncpy (label, s0->Titulo, 11); label[11]=0;

if (sp) printf ("\r Tiempo transcurrido formateando %2d:%02d\n",

tiempo/60, tiempo % 60);printf (" Volúmen con número de serie %04X-%04X",

(int) (s0->NumSerie >> 16), (int) s0->NumSerie);

Page 336: PCA, PS2 ,IBM y AT

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

if (strstr(label, "NO NAME ")==NULL)printf (" y etiqueta %11s\n", label);

elseprintf("\n");

printf ("%9d ficheros permitidos en el raíz.\n",s0->FichRaiz);

printf ("%9d unidades de asignación.\n", ua);printf ("%9d bytes por unidad de asignación.\n",

s0->SectCluster*512);printf ("%9lu bytes totales en el disco.\n", bt);printf ("%9lu bytes en sectores defectuosos.\n", bd);printf ("%9lu bytes disponibles en el disco.\n", bt-bd);if (cilindros!=cmd->Pistas)printf(" Aviso: formateado con %dK (esta unidad sólo"

" soporta %d pistas).\n", s0->NumSect>>1, cilindros);

else printf ("\r Time elapsed in the process %2d:%02d\n",tiempo/60, tiempo % 60);

printf (" Volume serial number is %04X-%04X",(int) (s0->NumSerie >> 16), (int) s0->NumSerie);

if (strstr(label, "NO NAME ")==NULL)printf (" labeled %11s\n", label);

elseprintf("\n");

printf ("%9d file capacity of root directory.\n",s0->FichRaiz);

printf ("%9d total clusters on disk.\n", ua);printf ("%9d bytes per cluster.\n",s0->SectCluster*512);

printf ("%9lu total bytes on disk.\n", bt);printf ("%9lu bytes on bad sectors.\n", bd);printf ("%9lu bytes available on disk.\n", bt-bd);if (cilindros!=cmd->Pistas)printf(" Note: formatted with %dK (this drive supports"

" only %d tracks).\n", s0->NumSect>>1, cilindros);

void IncrementarEtiqueta (Parametros *cmd)

int j=10;

while ((cmd->Volumen[j]==’ ’) && j) j--;

while (j)if ((cmd->Volumen[j] >= ’0’) && (cmd->Volumen[j] <= ’8’))

cmd->Volumen[j]++;break;

else if (cmd->Volumen[j] == ’9’) cmd->Volumen[j]=’0’;j--;

else break;

void DiagnosticoError (int codigo)if (sp)

switch (codigo) case 1: printf("\r Formateo interrumpido por el usuario.");

break;case 2: printf("\r La densidad seleccionada es incorrecta.");

break;case 3: printf("\r Disquete protegido contra escritura.");

break;case 6:case 128: printf("\r Unidad no preparada (¿puerta abierta?).");

break;default: printf("\r Anomalía general: ¿densidad incorrecta?.");

break;

else switch (codigo) case 1: printf("\r Format aborted by user.");

break;case 2: printf("\r Selected density is incorrect.");

break;case 3: printf("\r Diskette is write-protected.");

break;case 6:case 128: printf("\r Drive not ready (door open?).");

break;default: printf("\r General failure: incorrect density?.");

break;

printf(" \n");

int MarcaFat (unidad, modosuave, sector0, cil, cab, fat, buffer,bytes_mal)Boot *sector0;int unidad, modosuave, cil, cab;unsigned char far *fat;unsigned char far *buffer;long *bytes_mal;unsigned malclus, i, ini, tamsys;

tamsys = sector0->NumFats*sector0->SectFat+(sector0->FichRaiz>>4)+1;

for (i=1; i<=sector0->SectPista; i++) ini=(cil*sector0->Caras+cab)*sector0->SectPista+i-1;if (modosuave)

malclus=biosdsk (2, unidad, cab, cil, i, 1, buffer);elsemalclus=1; /* por defecto marcar la pista entera */

if (malclus) if (ini<tamsys) break; /* error en áreas del sistema */*bytes_mal+=sector0->SectCluster*512L;ini-=tamsys; ini=ini/sector0->SectCluster+2;if (ini % 2) /* posición impar */

fat [ini*3/2] = fat [ini*3/2] & 0x0F | 0x70;fat [ini*3/2+1] = 0xFF;

else /* posición par */fat [ini*3/2] = 0xF7;fat [ini*3/2+1] = fat [ini*3/2+1] & 0xF0 | 0x0F;

ini=0x7FFF;

return (ini>=tamsys);

int TipoDrive (int unidad)

union REGS r;

r.h.ah=8; r.h.dl=unidad;int86 (0x13, &r, &r);

return ((unsigned char) r.h.bl);

InicializaDisco (unidad, sector0, fat1, buffer)int unidad;Boot *sector0;unsigned char far *fat1;unsigned char far *buffer;

unsigned char far *p;int sectpista0=sector0->Salto[sector0->OffsetPista0],

spraiz=sector0->SectFat*2+1,error;

Root raiz;struct time h;struct date f;

memset (buffer, 0, (unsigned long) MAXSECT << 9);memset (&raiz, 0, sizeof (raiz));

if (strstr(sector0->Titulo, "NO NAME ")==NULL) strncpy (raiz.Etiqueta, sector0->Titulo, 11);raiz.Tipo=8;gettime (&h); getdate (&f);raiz.Fecha=((f.da_year-1980)<<9) | (f.da_mon<<5) | f.da_day;raiz.Hora=(h.ti_hour<<11) | (h.ti_min<<5) | (h.ti_sec>>1);

p=buffer;memcpy (p, sector0, 512); /* BOOT físico */

p+=512;memcpy (p, fat1, sector0->SectFat*512); /* FAT1 (la 2 emulada) */

p+=sector0->SectFat<<9;memcpy (p, sector0, 512); /* BOOT virtual */

if (sector0->SectPista>=15) /* HD */ p+=512;memcpy (p, &Boot2mCode, Boot2mLong); /* código SuperBOOT */

p=buffer+(spraiz<<9);memcpy (p, &raiz, sizeof(raiz)); /* 1ª entrada ROOT */

biosdsk (0, unidad, 0, 0, 0, 0, NULL);error=biosdsk(3, unidad, 0x80, 0, 1, sectpista0, buffer);if (!error)

error=biosdsk(3, unidad, spraiz/sector0->SectPista, 0,(spraiz % sector0->SectPista) + 1, 1, &buffer[spraiz*512]);

return (error);

void SonidoSube()

int frec=50;

SonidoOn();while (frec<5000)

Sonido (frec); PicoRetardo(); Sonido (frec+1000); PicoRetardo();frec+=10;

SonidoOff();

void SonidoBaja()

int frec=6000;

SonidoOn();while (frec>1050)

Sonido (frec); PicoRetardo(); Sonido (frec-1000); PicoRetardo();frec-=10;

SonidoOff();

void SonidoError()

int frec1=50, frec2=6000;

SonidoOn();while (frec1<5000)

Sonido (frec1); PicoRetardo(); Sonido (frec1+1000); PicoRetardo();Sonido (frec2); PicoRetardo(); Sonido (frec2-1000); PicoRetardo();frec1+=10; frec2-=10;

SonidoOff();

void SonidoOn()

disable(); outportb (0x61, inportb (0x61) | 3); enable();outportb (0x43, 182); /* preparar canal 2 */

void SonidoOff()

disable(); outportb (0x61, inportb (0x61) & 0xFC); enable();

void Sonido (int frecuencia)

unsigned periodo;

periodo=1193180L/frecuencia;outportb (0x42, periodo & 0xFF); outportb (0x42, periodo >> 8);

int EsperarCambioDisco (int disquetera, int flash)

int i, unidad;long hora, iter;

Page 337: PCA, PS2 ,IBM y AT

337EL HARDWARE DE APOYO AL MICROPROCESADOR

unidad=disquetera;if (FdswapOn()) unidad^=1; /* unidades intercambiadas por FDSWAP */

while (kbhit()) (void) getch(); /* limpiar buffer teclado */

pokeb(0x40,0x3F, peekb(0x40, 0x3F) & 0xF0); /* "motores apagados" */

do /* esperar que retiren el disquete */hora=*cbios+5;while (*cbios<hora);outportb (FD_DOR, (1<<(unidad+4)) | unidad | 4+8); /* encender */i=inportb (FD_DIR); /* leer línea de cambio */outportb (FD_DOR, unidad | 4+8); /* apagar motor */i = (i >> 7) | kbhit();

while (!i);

if (flash) /* intento de bajar la línea de cambio */iter=2000000000L;

elseiter=8L;

while (i && !kbhit()) /* y parpadeo del LED */hora=*cbios+6;pokeb (0x40, 0x40, 0xFF); /* para BIOS pelmas no estándar */outportb (FD_DOR,(1<<(unidad+4)) | unidad | 4+8); /* encender */pokeb(0x40,0x3F, peekb(0x40, 0x3F) | (1<<unidad));posicionar (unidad, 1);while ((*cbios<hora) && !kbhit());posicionar (unidad, 0);i = inportb (FD_DIR) >> 7; /* leer línea de cambio */if (i && !iter) outportb (FD_DOR, unidad | 4+8); /* apagar motor */pokeb(0x40,0x3F, peekb(0x40, 0x3F) & 0xF0);hora+=12;while ((*cbios<hora) && !kbhit());

if (iter) iter--;

/* simular cambio de disco para anular efecto de bajada de línea */

biosdsk (5, disquetera, 0, 0, 0xFF, 0x7F, NULL); /* función de 2M */

/* 3 segundos para detención del motor */

pokeb (0x40, 0x40, 54);return (kbhit()?(getch() & 0xFF)!=0x1B:1);

void posicionar (int unidad, int cilindro) /* mover cabezal */outfdc (0xF); /* comando ’Seek’ */outfdc (unidad); /* byte 1 de dicho comando */outfdc (cilindro);

EsperarInt(); /* esperar interrupción */

outfdc (8); /* comando ’leer estado de interrupciones’ */

(void) infdc(); (void) infdc();

void outfdc (unsigned char dato) /* enviar byte al FDC */ /* no esperando más de 440 ms */

int i=0, rd;long t;

do i++; t=*cbios;while ((t==*cbios) && ((rd=inportb(FD_STATUS)>>7)==0));

while ((i<8) && !rd);

if (rd) outportb (FD_DATA, dato);

int infdc() /* leer byte del FDC */ /* no esperando más de 440 ms */

int i=0, rd;long t;

do i++; t=*cbios;while ((t==*cbios) && ((rd=inportb(FD_STATUS)>>7)==0));

while ((i<8) && !rd);

if (rd) return (inportb (FD_DATA)); else return (-1); /* fallo */

void EsperarInt() /* Esperar interrupción no más de 2 seg. */int i=0;long t;

do i++; t=*cbios;while ((t==*cbios) && !(*irq6 & 0x80));

while ((i<37) && !(*irq6 & 0x80));

*irq6=*irq6 & 0x7F;

void CardWare (char *nfich, int discos)int fich, aviso=0, lcad, valor=CARDWARE;struct ftime fechahora;unsigned char contador,

chk[10],cmp[]="Cnt",num[]="000";

lcad=strlen(cmp)+1;if ((fich=open(nfich, O_BINARY | O_RDWR))==-1) return;if (getftime (fich, &fechahora)==-1) close(fich); return; if (lseek (fich, -lcad, SEEK_END)==-1) close(fich); return; if (read (fich, chk, lcad)==-1) close(fich); return; chk[lcad-1]=0;if (strcmp(chk, cmp)) /* contador no inicializado */ write (fich, cmp, lcad);if (discos) discos--;

if (lseek (fich, -1L, SEEK_END)==-1) close(fich); return; if (read (fich, &contador, 1)==-1) close(fich); return; contador+=discos;

if (contador >= CARDWARE) contador-=CARDWARE;if (contador > (CARDWARE>>1)) /* posible fallo extraño */contador=CARDWARE>>1;

aviso++; /* avisar (si se puede actualizar 2MF.EXE) */

if (lseek (fich, -1L, SEEK_END)==-1) close(fich); return; if (write (fich, &contador, 1)==-1) close(fich); return; flushall();setftime (fich, &fechahora);close (fich);

num[0]=valor / 100 +’0’; valor%=100;num[1]=valor / 10 +’0’; valor%=10;num[2]=valor+’0’;

if (aviso)if (sp)

clrscr();textcolor (LIGHTCYAN + BLINK); textbackground (BLUE);gotoxy (27, 5);cputs(" ¡¡AVISO MUY IMPORTANTE!! ");textcolor (LIGHTRED); textbackground (BLACK);gotoxy (15,7); cputs ("Esta copia de 2MF ya ha formateado ");

textcolor (YELLOW); cputs (num); textcolor(LIGHTRED);

cputs (" disquetes.");gotoxy (15,8); cputs ("Recuerda que 2M es un programa ");

textcolor (LIGHTGREEN); cputs ("CardWare");textcolor (LIGHTRED);

cputs (". Si aún");gotoxy (15,9); cputs ("no has enviado tu ");

textcolor (LIGHTMAGENTA); cputs ("tarjetapostal"); textcolor (LIGHTRED);

cputs (" al autor, no");gotoxy (15,10); cputs ("deberías continuar utilizando estos

discos.");gotoxy (15,12); cputs ("Si ya la has enviado, estoy ");

textcolor (LIGHTCYAN); cputs ("muy contento");textcolor (LIGHTRED);

cputs (" contigo");gotoxy (15,13); cputs ("y dentro de otros "); cputs (num);

cputs(" volveré a felicitarte.");gotoxy (15,15); textcolor (LIGHTGREEN); cputs ("¡Suerte!");textcolor (WHITE);gotoxy (1,16);

else clrscr();textcolor (LIGHTCYAN + BLINK); textbackground (BLUE);gotoxy (27, 5);cputs(" ¡¡VERY IMPORTANT NOTICE!! ");textcolor (LIGHTRED); textbackground (BLACK);gotoxy (15,7); cputs ("This 2MF program has already formatted

");textcolor (YELLOW); cputs (num); textcolor

(LIGHTRED);cputs (" disks.");

gotoxy (15,8); cputs ("Remember that 2M is a ");textcolor (LIGHTGREEN); cputs ("CardWare");

textcolor (LIGHTRED);cputs (" program. If you");

gotoxy (15,9); cputs ("haven’t send still your ");textcolor (LIGHTMAGENTA); cputs ("postcard");

textcolor (LIGHTRED);cputs (" to the author,");

gotoxy (15,10); cputs ("you musn’t continue on using thisdiskettes.");

gotoxy (15,12); cputs ("If you have send it yet, I’m ");textcolor (LIGHTCYAN); cputs ("very happy");

textcolor (LIGHTRED);cputs (" with you");

gotoxy (15,13); cputs ("and within next "); cputs (num); cputs("ones I will thank you again.");

gotoxy (15,15); textcolor (LIGHTGREEN); cputs ("Good luck!");textcolor (WHITE);gotoxy (1,16);

int HablaSp() /* devolver 1 si mensajes en castellano */

union REGS r; struct SREGS s;char info[64];int i, idioma, spl[]=54, 591, 57, 506, 56, 593, 503, 34, 63, 502,

504, 212, 52, 505, 507, 595, 51, 80, 508, 598, 58, 3, 0;

idioma=0; /* supuesto el inglés */

if (_osmajor>=3) r.x.ax=0x3800; s.ds=FP_SEG(info); r.x.dx=FP_OFF(info);intdosx (&r, &r, &s);i=0; while (spl[i++]) if (spl[i-1]==r.x.bx) idioma=1;

return (idioma);

###################################################################

;;;;;;;;; FICHERO CON CODIGO ENSAMBLADOR LINKABLE CON 2MF.C;; Código de 2M que será almacenado en los sectores de; los disquetes, sectores de arranque de los mismos y; algunas funciones ASM de utilidad.;; Proceso:;; TASM 2MFKIT /m5 /mx;; El fichero 2MFBOOT.DB que se carga con INCLUDE debe obtenerse; previamente a partir de 2MFBOOT.ASM con ayuda de 2MFBMAKE.BAS;;

_DATA SEGMENT WORD PUBLIC ’DATA’ASSUME CS:_DATA, DS:_DATA

Page 338: PCA, PS2 ,IBM y AT

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

PUBLIC _Boot2mCode, _Boot2mLongPUBLIC _biosdsk, _PicoRetardo, _NuevaInt24PUBLIC _BootHDPrg, _BootHDPrgLongPUBLIC _BootDDPrg, _BootDDPrgLong

; ------------ Código 2M para grabar en los 5 sectores ocultos de los; disquetes de alta densidad al formatear.

_Boot2mCode: INCLUDE 2MFBOOT.DB_Boot2mLong DW $-OFFSET _Boot2mCode

; ------------ Sectores de arranque de los disquetes 2M.

_BootHDPrg: INCLUDE 2MBOOTHD.INC_BootHDPrgLong DW $-OFFSET _BootHDPrg

_BootDDPrg: INCLUDE 2MBOOTDD.INC_BootDDPrgLong DW $-OFFSET _BootDDPrg

; ------------ Rutina de acceso a disco vía BIOS. No se utiliza la; función biosdisk() del compilador porque en algunas; versiones del mismo hace tonterías que no debe. Así,; además, se puede llamar a INT 13h con CALL (bueno,; con RETF) para que dentro de WINDOWS 2MF /M no de; problemas; además, la función de formateo de 2M; requiere SI="2M" al llamar.

_biosdsk PROC FARPUSH BPMOV BP,SPPUSH ESPUSH SIPUSH DIPUSHF ; estructura para futuro IRETPUSH CSLEA AX,bdsk_retPUSH AXXOR AX,AXMOV ES,AXPUSH ES:[13h*4+2] ; INT 13h -> pilaPUSH ES:[13h*4]MOV AH,[BP+6]MOV DL,[BP+8]MOV DH,[BP+10]MOV CH,[BP+12]MOV CL,[BP+14]MOV AL,[BP+16]

LES BX,DWORD PTR [BP+18]MOV SI,"2M"RETF ; ejecutar INT 13h

bdsk_ret: POP DIPOP SIPOP ESPOP BPMOV AL,AH ; resultadoMOV AH,0RET

_biosdsk ENDP

; ------------ Pequeño retardo de medio milisegundo.

_PicoRetardo PROC FARPUSH AXPUSHFPOP AXOR AH,70hPUSH AXPOPFPUSHFPOP AXAND AH,0F0hCMP AH,0F0h ; ¿es PC/XT?JE xtMOV CX,33 ; 18÷1193180*33*1000 = 0.5 ms

wrf: IN AL,61hAND AL,10hCMP AL,AHJE wrf ; esperar pulso refresco memoriaMOV AH,ALLOOP wrf

xt: POP AXRET

_PicoRetardo ENDP

; ------------ Nuevo gestor de errores críticos.

_NuevaInt24 PROCMOV AL,3 ; error en la función DOS invocada.IRET

_NuevaInt24 ENDP

_DATA ENDS

END

12.6.7.5 - UN PROGRAMA PARA MEDIR EL RENDIMIENTO DE LOS DISQUETES.

En las páginas donde se describía el funcionamiento de 2M aparecía una tabla con los tiemposcronometrados de un COPY de múltiples ficheros, desde y hacia un disquete en los formatos de disco máscomunes. Sin embargo, resulta interesante conocer la velocidad real del sistema de disco cuando éste esutilizado óptimamente: acceso a múltiples pistas completas y consecutivas en el disco. Los buenos programasde copia de discos, que leen de un golpe todas las pistas consecutivas que pueden antes de guardarlas en unfichero auxiliar (o que las almacenan en EMS ó XMS), dependerán de la velocidad que sea capaz de dar elformato de disco empleado, ya que las disqueteras giran a una velocidad fija en todos los ordenadores. Sipierden tiempo entre pista y pista (tal vez por escribirlas en el fichero auxiliar una por una) la velocidadobtenida podría dividirse por dos, al intentar pillar el primer sector de la siguiente pista justo cuando acabade pasar de largo por delante del cabezal.

Velocidad máxima teórica sin Velocidad real en Kb/seg estimada por 2M-FDTR (nivel BIOS).considerar tiempos de accesopista-pista ni el porcentaje FORMAT FDFORMAT (**) FDFORMAT (***) 2MF 3.0 /F 2MF 3.0 /Mde superficie magnética quese aprovecha en cada pista. Lect. Escr. Lect. Escr. Lect. Escr. Lect. Escr. Lect. Escr.

5¼-DD 36,62 Kb/seg (300 Kbit/seg) 18.16 18.16 22.11 22.12 25.00 25.00 25.04 25.00 16.49 16.495¼-HD 61,03 Kb/seg (500 Kbit/seg) 30.13 30.13 39.73 39.73 25.26 25.23 46.33 46.33 28.50 28.473½-DD 30,52 Kb/seg (250 Kbit/seg)* 15.05 15.05 19.32 19.32 21.78 21.75 25.72 25.76 16.25 16.253½-HD 61,03 Kb/seg (500 Kbit/seg) 30.14 30.14 39.58 39.53 24.79 24.79 48.49 48.50 28.74 28.77

(*) 2M emplea 300 Kbit/seg (no es compatible con controladoras de doble densidad de PC/XT).(**) Usando el formato estándar del DOS (360-720-1.2-1.44) y los parámetros /X e /Y adecuados.

(***) Formatos de máxima capacidad soportados (820-1.48-1.72) y los parámetros /X e /Y adecuados.

Con objeto de uniformizar los índices, el siguiente programa de ejemplo realiza la lectura y escrituracompleta de un disco (en este último caso, si no contenía datos, ya que se estropearían) llamando a la BIOS.La primera versión del programa empleaba el DOS (funciones absread() y abswrite() del C) y obteníaexactamente los mismos índices, aunque problemas de fiabilidad aconsejaron utilizar funciones de la BIOS,con lo que el programa ya no puede, por ejemplo, analizar el rendimiento de un disco duro (debido a laincomodidad que supone buscar el sector de arranque a través de la tabla de particiones). Se recorren enlectura y escritura todos los cilindros del disco, a partir del 1 y llegando hasta el último que exista. El motivode saltar el cilindro 0 es doble: por un lado, saltar las áreas del sistema (de cara a no escribir sobre el sectorde arranque, por ejemplo, ya que por simplicidad se escribe basura y no lo que se ha leído al principio); porotro lado, los tiempos de este cilindro pueden ser diferentes de los obtenidos en los demás cilindros, bien

Page 339: PCA, PS2 ,IBM y AT

339EL HARDWARE DE APOYO AL MICROPROCESADOR

debido a la interferencia del sistema o los programas de caché o, simplemente, porque tiene un formato físicomuy especial (como es el caso de los disquetes 2M). En el caso de los disquetes 2M, de esta forma no setiene en cuenta el tiempo extra que se pierde en este primer cilindro debido a la extraña maniobra que suponesimular la existencia de la segunda copia de la FAT (que implica volver momentáneamente al primer cabezaldespués de haber pasado al segundo).

El programa, 2M-FDTR (2M Floppy Data Transfer Rate), utiliza el contador de hora de la BIOSunido al temporizador 8254 para cronometrar. Antes de comenzar el test y arrancar el cronómetro se lee unode los últimos sectores del cilindro 1 para asegurar que el cabezal está ya sobre el mismo y a punto de pillarel primer sector. El buffer donde se realizará la lectura/escritura es asignado de tal manera que no cruce unafrontera de DMA (para que INT 13h no tenga que segmentar en varias fases la operación, lo que disminuiríala velocidad). El acceso a INT 13h se realiza de manera directa, ya que la versión 3.1 del compilador hacealguna oscura maniobra con biosdisk y al final termina perdiendo demasiado tiempo (lo suficiente como paraque en alguna máquina el disco aparente ser más lento de lo que realmente es). Con Borland C 2.0 no hayproblemas, pero...

NOTA: Los resultados de 2M-FDTR contradicen los que facilitan muchos afamados programas comerciales de test, sencillamente porque dichosprogramas no miden correctamente (y de hecho dan en cada ordenador, e incluso en la misma máquina entre ejecuciones consecutivas,resultados diferentes y contradictorios). Si estuviera instalado un programa de caché, los resultados podrían verse alterados por lo que serecomienda no instalarlos para la prueba. De todas maneras, con un disquete recién introducido no hay programa alguno de caché que puedadisminuir el tiempo de lectura del mismo (quizá sí la escritura). Insisto en que los resultados de 2M-FDTR son reales y cualquier programade aplicación que acceda a disco a medio o bajo nivel, como el propio 2M-FDTR, puede lograrlos si utiliza correctamente las funcionesde acceso a sectores del DOS o de la BIOS.

/********************************************************************** ** 2M-FDTR 2.2 - Cálculo de la tasa de transferencia de disquetes. ** (C) 1994 Ciriaco García de Celis. ** ** Para Borland C++ 2.0 ó superior en modelo de memoria large. ** **********************************************************************/

#define SMAX 23*512L /* máximo soportado: 63 sectores por pista */#define RD 2#define WR 3

#include <stdio.h>#include <stdlib.h>#include <conio.h>#include <dos.h>#include <alloc.h>#include <math.h>#include <string.h>

int evalua_io (int, unsigned char far *, int, int, int, int),biosdsk (int, int, int, int, int, int, unsigned char far *),HablaSp (void);

void ayuda (void);unsigned long tiempo (void);

int sp; /* 1-español 0-inglés */

void main (int argc, char **argv)unsigned char sector0[512], far *buf;unsigned long dir;int unidad, cilindros, sectores, cabezales;struct dfree dsk;

sp=HablaSp(); /* determinar idioma del país */

if ((!strcmp(strupr(argv[1]),"/I")) || (!strcmp(strupr(argv[2]),"/I")))sp^=1; /* parámetro /I */

printf("\n 2M Floppy Data Transfer Rate 2.2\n");

unidad=(*argv[1] | 0x20)-’a’;

if ((argc<2) || ((unidad!=0) && (unidad!=1))) ayuda();

getdfree (unidad+1, &dsk);

if (dsk.df_sclus==65535) if (sp)

printf(" Error de acceso a la unidad.\n");elseprintf(" Error on drive access.\n");

exit (3);

if ((long) dsk.df_total*dsk.df_sclus>65535L) if (sp)

printf(" Unidades de más de 32M no soportadas.\n");elseprintf(" Drive above 32M can not be tested.\n");

exit (1);

if ((buf=farmalloc (SMAX << 1))==NULL) if (sp)

printf(" ¡Memoria insuficiente!.\n");elseprintf(" Insufficient memory!.\n");

exit (2);

dir = ((unsigned long) FP_SEG(buf) <<4) + FP_OFF(buf);

if ((dir>>16)!=((dir+SMAX)>>16)) buf+=SMAX; /* por el DMA */

if (biosdsk (2, unidad, 0, 0, 1, 1, sector0)) printf(" Fatal ????.\n");exit (4);

sectores=sector0[24]+256*sector0[25]; cabezales=sector0[26];cilindros=(sector0[19]+256*sector0[20])/sectores/cabezales;

if (sectores>63) if (sp)

printf(" ¡No soportados más de 63 sectores por pista!.\n");elseprintf(" Not supported more than 63 sectors per track!.\n");

exit (3);

if (sp) printf(" Determinando tasa de transferencia BIOS a disco.\n");printf(" + Rendimiento en lectura:\n");

else printf(" Computing BIOS floppy data transfer rate.\n");printf(" + Read performance:\n");

if (evalua_io (RD, buf, unidad, cilindros, sectores, cabezales)) if (dsk.df_avail < dsk.df_total)

if (sp)printf(" + Disquete no vacío -> test de escritura

omitido.\n");else

printf(" + Diskette not empty -> write test skipped.\n");exit (4);

if (sp)printf(" + Rendimiento en escritura:\n");

elseprintf(" + Write performance:\n");

evalua_io (WR, buf, unidad, cilindros, sectores, cabezales);

void ayuda()

printf(" (C) 1994 Ciriaco García de Celis.\n");if (sp)

printf(" Indica la unidad A: o B: para medir suvelocidad.\n");

printf(" - El test se realiza accediendo a través de lasfunciones BIOS.\n");

printf(" - El buffer E/S no cruza nunca una frontera de DMAde 64K.\n");

printf(" - El acceso afecta siempre a pistas completas.\n");printf(" - El software residente puede alterar el

resultado.\n");printf(" - El test de escritura no se realiza si el disquete

contiene datos.\n");

else printf(" Choose drive A: or B: to test it absolute

speed.\n");printf(" - Test is performed always through BIOS

functions.\n");printf(" - The I/O buffer never cross a 64K DMA

frontier.\n");printf(" - Access is done always using the whole track.\n");printf(" - The TSR software may alter results.\n");printf(" - Write test is not performed if diskette contains

data.\n");

exit (255);

Page 340: PCA, PS2 ,IBM y AT

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

unsigned long tiempo()

unsigned long tm;

asm climov al,6out 43h,al /* enclavamiento contador 0 */in al,40hmov ah,alin al,40hxchg ah,alneg ax /* ax = valor del contador 0 del 8254 */push dsmov bx,40hmov ds,bxmov bx,ds:[6ch] /* bx = contador hora BIOS */stipop dsmov word ptr tm,axmov word ptr tm+2,bx

return (tm);

int biosdsk (cmd, drive, head, track, sector, nsects, buffer)int cmd, drive, head, track, sector, nsects;unsigned char far *buffer;union REGS r; struct SREGS s;

r.h.ah=cmd; r.h.dl=drive; r.h.dh=head; r.h.ch=track; r.h.cl=sector;r.h.al=nsects; s.es=FP_SEG(buffer); r.x.bx=FP_OFF(buffer);

int86x (0x13, &r, &r, &s);return (r.h.ah);

int evalua_io (operacion, buffer, unidad, cilindros, nsect, cabezales)int operacion, unidad, cilindros, nsect, cabezales;unsigned char far *buffer;int cilindro, cabezal, fin_io=0, res;unsigned long tini, tfin;float bseg;

/* Leer parte del cilindro 1 para colocar el cabezal al inicio. *//* Se leen dos sectores alejados para esquivar la caché de 2M y *//* forzar un auténtico posicionamiento en este cilindro */

outportb (0x43, 0x36); /* asegurar que cnt0 usa byte bajo-alto */

outportb (0x40, 0); outportb (0x40, 0);

biosdsk (2, unidad, 0, 1, 1, 1, buffer); /* anular caché 2M */biosdsk (2, unidad, 0, 1, nsect-2, 1, buffer); /* sincronizar */

tini=tiempo(); res=0;for (cilindro=1; cilindro<cilindros; cilindro++)

for (cabezal=0; cabezal<cabezales; cabezal++) if (kbhit()) if (getch()==27) goto aborta_io;if (res)

if (sp)printf("\r ¡Fallo en el acceso a disco!.\n");

elseprintf("\r Failure on disk access!.\n");

goto aborta_io;

if (sp)printf("\r\r Cilindro %2d - Cara %d", cilindro, cabezal);

elseprintf("\r\r Cylinder %2d - Side %d", cilindro, cabezal);

res=biosdsk (operacion, unidad, cabezal, cilindro, 1, nsect, buffer);

tfin=tiempo(); fin_io=1;

bseg=(512L*nsect*(cilindros-1)*cabezales)/((tfin-tini)/1193180.0);if (sp)

printf("\r %7.2f segundos =%7.2f Kb/seg [%7.0f bits/seg]\n",(tfin-tini)/1193180.0, bseg/1024.0, bseg*8);

elseprintf("\r %7.2f seconds =%7.2f Kb/sec [%7.0f bits/sec]\n",

(tfin-tini)/1193180.0, bseg/1024.0, bseg*8);aborta_io:printf("\r \r");return (fin_io);

int HablaSp() /* devolver 1 si mensajes en castellano */

union REGS r; struct SREGS s;char info[64];int i, idioma, spl[]=54, 591, 57, 506, 56, 593, 503, 34, 63, 502,

504, 212, 52, 505, 507, 595, 51, 80, 508, 598, 58, 3, 0;

idioma=0; /* supuesto el inglés */

if (_osmajor>=3) r.x.ax=0x3800; s.ds=FP_SEG(info); r.x.dx=FP_OFF(info);intdosx (&r, &r, &s);i=0; while (spl[i++]) if (spl[i-1]==r.x.bx) idioma=1;

return (idioma);

12.6.7.6 - LA VERSION PARA PC/XT DE 2M: 2MX [Listado no incluido en este libro].

Aunque 2M fue inicialmente concebido para máquinas AT, a partir de la versión 1.2 ha estadoacompañado de una versión para PC/XT. El único requisito es que el ordenador esté equipado con unacontroladora y unidades de alta densidad. Algunas máquinas modernas de tipo subnotebook, que caben enla palma de la mano, vienen preparadas para conectar una de estas disqueteras externas. Otros PC/XT dereciente fabricación traen ya controladoras de alta densidad y BIOS que las soportan, aunque luego el tacañofabricante haya colocado una unidad de doble densidad que el usuario puede sustituir. Finalmente, a aquellasmáquinas más antiguas que no pertenecen a ninguna de estas dos categorías, se les puede sustituir lacontroladora y unidades de doble densidad por otras de alta, que en el futuro el usuario podrá colocar en sumáquina AT cuando se la compre; se trata por tanto de una inversión rentable. Si bien resulta difícil encontraractualmente en el mercado controladoras de alta densidad para PC/XT, el usuario puede optar por poner unade AT. Yo, por ejemplo, para probar 2MX me vi obligado a pinchar una controladora de 16 bits en un slotde 8 bits. La tarjeta era una IDE multi-io; sin embargo, la parte alta del bus (que no se puede pinchar al serde 8 bits el slot) sólo se utiliza para acceder al disco duro bus AT, pudiendo ser inhibida con el jumper demarras (si bien ni esto resultó necesario). La parte correspondiente al control de disquetes, y probablementelos puertos serie/paralelo, era totalmente funcional, ya que sólo opera con la mitad baja del bus.

El principal problema radica en que la BIOS de los PC/XT en el 99% de los casos no está preparadapara soportar alta densidad. Al hacer DIR sobre un disquete de alta densidad nada más encender el ordenador,lo más probable es que funcione, ya que ésta es la densidad por defecto normalmente. Sin embargo, con losdiscos de doble densidad (donde tiene que seleccionar 250 ó 300 Kbit/seg) es imposible sacar el DIR. Encualquier caso, sacar un DIR es una cosa y otra muy diferente conseguir que el disco funcione. Como laBIOS informa siempre que todo es de doble densidad, el muy patoso del DOS modifica la tabla base deldisco para indicar como 9 el último número de sector en la pista (¿quién le mandará tocar las variables dela BIOS?) por lo que ni los discos de alta densidad funcionan a nivel de COPY (el directorio sí apareceporque coincide en los primeros sectores de las pistas). La solución en este tipo de máquinas pasa por instalaruna BIOS más moderna... pero sin tener que regrabar la eprom. Basta con cargar 2M-XBIOS.EXE, unprograma residente que emula la BIOS AMI de AT en los XT. De hecho, 2MX solicita al usuario la

Page 341: PCA, PS2 ,IBM y AT

341EL HARDWARE DE APOYO AL MICROPROCESADOR

instalación de este driver cuando advierte que no puede detectar el tipo de las unidades.

En ese sentido, la combinación 2M-XBIOS + 2MX permite a cualquier máquina PC/XT obsoletaequipada con una barata controladora de disquetes de AT trabajar con discos de cualquier densidad ycualquier formato (estándar/2M). Los problemas de versiones anteriores de 2MX han sido eliminados graciasa la extensión BIOS en que se apoya. De hecho, 2MX es en sus últimas versiones prácticamente idéntico a2M, sólo cambia en algunos aspectos puntuales relacionados con la diferente arquitectura de los XT respectoa los AT.

12.6.7.7 - LA OPCION BIOS DE 2M: 2M-ABIOS Y 2M-XBIOS [Listados no incluídos en este libro].

Algunos ordenadores poseen una BIOS antigua o con un diseño propio poco compatible en el controlde disco. En estas máquinas, 2M y otros programas de acceso a bajo nivel pueden fallar. En dichos casos,se puede instalar esta utilidad antes que 2M, y en general que cualquier otro software que acceda alsubsistema de disco. La versión 2M-ABIOS es para AT y 2M-XBIOS para PC/XT.

Estos programas actualizan el soporte de disco flexible al nivel de las BIOS AMI de 1993. Si conellos instalados 2M no opera de manera totalmente correcta (aunque en general 2M depende realmente muypoco de la BIOS, pero ya conozco algún caso al respecto) y en la máquina no está instalado algún otrosoftware de disco incompatible con 2M, entonces el ordenador no es 100% compatible hardware con elestándar; esto es particularmente cierto si ni siquiera se reconocen los discos estándar del DOS.

Esta utilidad también sirve para añadir soporte de 1.44M a máquinas con BIOS antigua, algunas deellas incluso AT. En estos casos, el usuario debe ignorar la información sobre el tipo de la unidad que puedareportar dicha BIOS al arrancar. El programa se carga desde el CONFIG.SYS con una sintaxis sencilla:

DEVICE=2M-ABIOS.EXE [A:tipo] [B:tipo] [/C] [/13]

El consumo de memoria es de unos 3.4-4.2 Kb de RAM, y contiene una emulación al 100% deleficaz código de control de disco de las BIOS AMI, relevando así por completo de esta tarea a la BIOS delsistema. De ahí que haya sido diseñado en este formato, para forzar al usuario a instalarlo antes de los demásprogramas de disco, a los que anularía por completo (ya que nunca más vuelve a llamar a la interrupción dedisco anterior). En AT generalmente no hará falta indicar el tipo de las unidades (0:no hay, 1:360K, 2:1.2M,3:720K, 4:1.44M, 5:2.88M) pero en PC/XT casi siempre será necesario. La opción /C evita en los equiposAT ajustar la CMOS, por si la máquina en cuestión tiene un algoritmo no estándar para calcular el checksumde la misma y aparece un "Incorrect CMOS checksum" al arrancar (muy poco probable). Así mismo, si enalgún momento el usuario dudara acerca de si 2M-ABIOS está controlando realmente las unidades, puedeutilizar la opción /13 para asegurarlo, si bien esta opción es poco recomendable cuando no es estrictamentenecesaria (se desvía también INT 13h además de INT 40h, incluso aunque detecte el soporte de esta última).

El listado comentado de estos programas (realmente uno solo, con ensamblaje condicional en 2M 3.0)se omite porque ya hay demasiadas rutinas de acceso a disco a bajo nivel en este libro.

12.6.7.8 - LA UTILIDAD 2MDOS [Listado no incluído en este libro].

Debido a la ineficiencia de FORMAT a la hora de crear discos rápidos y teniendo en cuenta lalimitación de DISKCOPY en el sentido de no poder formatear discos destino en formato 2M, se comprendela necesidad de un sustituto de FORMAT y DISKCOPY. Sin embargo, todos los programas al respectoexistentes en la actualidad, a mi juicio, son un perfecto desastre. La mayoría no son rápidos incluso condiscos optimizados, por una cuestión elemental: no colocar los buffers de transferencia de manera que nocrucen las fronteras de DMA (para evitar que el DOS tenga que hacer accesos redundantes para salvarlas).La mayoría, de hecho, no generan discos optimizados con la clásica técnica de Sector Sliding (que enabsoluto implica reducción de compatibilidad o fiabilidad; más bien al contrario: es como se debe formatearcorrectamente un disco y como de hecho se hace con los discos duros). Otros son poco flexibles y no

Page 342: PCA, PS2 ,IBM y AT

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

soportan discos 2M (¡hasta DISKCOPY los supera en esto!) o tienen absurdas rutinas que encuentran virusen sectores de arranque poco oficiales, o necesitan VGA y ratón (aparte de ser lentos), o no son fiables...

La solución adoptada ha sido crear un programa residente que haga trabajar a todos los demás (conla excepción de los que también acceden directamente a la controladora de disco) de la manera adecuada. Setrata de crear una utilidad para que FORMAT o cualquier otro programa que llame a la BIOS formatee discosoptimizados (aún sin saberlo) y que amplíe los formatos de disco oficiales de la BIOS para que DISKCOPY(y el DUPDISK de las Norton y programas de similar flexibilidad) sean capaces, durante el proceso de copia,de formatear el disco destino 2M si es preciso.

Con 2MDOS instalado los discos se formatean automáticamente de manera óptima y DISKCOPYsoporta el formateo de discos 2M. Incluso FORMAT puede crear discos 2M (indicando pistas y sectores) sibien el de MS-DOS (no DR-DOS) tiene problemas con los de alta densidad y necesita un parámetro opcional(de todas maneras, 2MF sigue siendo más eficiente). Además 2MDOS da soporte por defecto a disquetes noestándar, creados por la utilidad FDFORMAT y permite a FORMAT poder crear disquetes FDREAD. Elprograma consume 5,7 Kb en equipos sin memoria extendida o 2,5 Kb con ella (sólo 1,7 Kb si no está activoel soporte para hacer DISKCOPY hacia un disco 2M sin formato; esto es, con sólo las opciones deoptimización de formateo y soporte FDREAD activas).

Por si esto fuera poco, 2MDOS incorpora una nueva técnica para acelerar aún más los discos estándarde 1.2M y 1.44M, que recibe el nombre de DiskBoost por razones de marketing. El truco consiste en evitarla necesidad de Sector Sliding, para de esta manera alcanzar, por ejemplo, una tasa de transferencia de datosde 45 Kb/seg en 1.44M (frente a los 39 Kb/seg del Sector Sliding o los 30 Kb/seg del FORMAT habitual).El truco consiste en añadir un sector adicional en el cabezal 1 y dos en el cabezal 0, que no se usan, algoque no reduce sensiblemente el nivel de seguridad del disco (sería el equivalente en seguridad a un disco de1.64M, por ejemplo). Los sectores adicionales, no usados, son colocados al principio de la pista. De estamanera, cuando la controladora acaba de acceder a una pista completa en el cabezal 0 (y está al inicio justode la pista tras completar una vuelta) se conmuta al cabezal 1 para acceder a la pista siguiente. Recordemosque en el cabezal 1 había un sector no utilizado al principio: este sector pasará por delante del cabezalmientras se conmuta, pero no transcurrirá demasiado tiempo como para que no se pueda pillar el primersector de la pista que viene inmediatamente a continuación. Cuando se acabe de leer la pista en el cabezal1 (y se está de nuevo al inicio justo de la pista tras completar la vuelta) se conmuta al cabezal 0 pero delsiguiente cilindro, algo que lleva más tiempo que antes... pero para eso ya habíamos dejado dos sectores noutilizados al inicio del cabezal 0. Por tanto, también da tiempo a pillar el primer sector.

Con la técnica DiskBoost es factible leer o escribir un disco completo de 1.44M en poco más de 31segundos, al emplear sólo una vuelta por cada pista. La diferencia de velocidad, contra todo pronóstico, esaún más espectacular en las operaciones COPY o XCOPY normales. Los discos de 1.2M y 1.44M creadospor FORMAT con 2MDOS instalado son un 50% más rápidos en el uso normal.

Sin embargo, 2MDOS no es la solución definitiva. Aunque es útil para que cada cual utilice susprogramas de copia/formateo favoritos de manera óptima, lo ideal sería un programa de copia/formateorealmente eficiente. Con dicho programa, 2MDOS no sería necesario...

El listado de 2MDOS tampoco se incluye en estas páginas. 2MDOS también incorpora el códigoSuperBOOT a los discos 2M de alta densidad que se formatean bajo su control, aunque su tarea es ampliarla funcionalidad de algunas interrupciones de la BIOS y no realiza accesos directos al hardware.

12.6.7.9 - COMO SUPERAR LOS 2.000.000 DE BYTES EN 3½: 2MGUI [Listado no incluído en el libro].

En cierta ocasión un programa llamado 1968 llegó a mis manos. Se trataba de una utilidad paraformatear discos de 1.44M a esa capacidad. Sin embargo, no funcionaba en mi unidad, ni tampoco en la demis máquinas de uso habitual. En alguna de ellas lograba formatear (a base de reintentos ante los errores)todo el disco, pero por desgracia la primera pista quedaba mal. Nunca logré crear un disco de estos, aunque

Page 343: PCA, PS2 ,IBM y AT

343EL HARDWARE DE APOYO AL MICROPROCESADOR

se que si lo hubiera conseguido, ese disco -como bien decía el autor en la documentación- sí podría ser leídoen las demás unidades.

El método de este programa consistía en introducir 3 sectores de 4 Kb en cada pista. El problemaes que eso requiere (4096+62)*3 = 12474 bytes, sin contar los GAP entre sectores, y la mayoría de lasunidades giran algo más deprisa de lo normal (y por tanto, se alejan del límite teórico de 12500 bytes porpista). Por otro lado, 26 bytes son incluso pocos para respetar las marcas de inicio de pista y demás. Al final,el tercer sector suele acabar pisando al primero.

Después de algún tiempo, han aparecido más formateadores que soportan (o dicen soportar) esteformato, alguno incluso en nuestro país. Sin embargo, todos tienen el mismo problema: no hay unidades quesoporten a esos programas. Por tanto, todo parecía indicar que el límite de capacidad se quedaría para siempreen los 1.72M del FDFORMAT ó los 1.88M de 2M, únicos formatos soportados por todas las unidades yordenadores (eso sí, compatibles). Pues no. Cierto día, Jesús Arias tuvo una idea genial y me la contó. A raízde esa idea, y tras superar numerosas y difíciles trabas técnicas, finalmente ha sido posible el milagro: lograrutilizar toda la capacidad disponible en la pista del disco, como si estuviera sin formatear.

El programa que realiza esto, 2MGUI (abreviatura de 2M-Guinness), es ya una realidad. Durante sudesarrollo se han puesto de relieve circunstancias curiosas. Por ejemplo, una determinada unidad admite12440 bytes por pista al grabar información aleatoria, pero si se escribe toda la pista con bits a 0 ó a 1 sólocaben 12405 bytes. ¿Por qué?: la respuesta sigue siendo un misterio. Las rutinas residentes de 2MGUIaprovechan las terminaciones normales de error de la controladora (disco protegido contra escritura, sectorno encontrado, etc.) para la detección de errores, aunque graban adicionalmente, en cada pista de datos, unchecksum de la información almacenada junto al número de pista y cabezal reales, para realizar el controlde errores cuando la controladora no puede devolver condiciones de error (debido a una serie de factorestécnicos). De esta manera, la información se graba y recupera con la seguridad de que es correcta -en casocontrario, se detectaría el fallo-.

Realizando pruebas, la capacidad admitida por diversas unidades se mostró directamente relacionadacon la velocidad de rotación de la misma. Por ejemplo, una unidad de 3½-HD que gire cada 199,9 ms admite12405 bytes, mientras que otra que lo hace cada 199,1 ms sólo admite 12348 bytes. Ambas son casosrealmente extremos, ya que la inmensa mayoría se encuentra entre estas dos. Aún así, la capacidad finalmenteadoptada por 2MGUI serán 12329 bytes. El objetivo es permitir que los discos puedan ser intercambiadosentre unidades. En lectura nunca hay problemas, ya que la peor unidad puede leer los datos de la mejor (laque más lentamente gire) porque la señal de reloj la obtiene de los propios datos registrados en disco. Sinembargo, al escribir, la señal de reloj la extrae de su base de tiempos propia (casi igual en todos losordenadores) y al girar más deprisa se le acaba la pista antes y sobreescribe el principio. Por tanto, los discosque apuren demasiado la capacidad de una buena unidad serán estropeados al ser escritos (no leídos) en otraunidad peor.

Doble Alta Extraalta

Récord absoluto previo a 2M 820.0 Kb 1394.0 Kb --Capacidad máxima 2M (2MF /M) 902.0 Kb 1558.0 Kb -- 5.25Capacidad mínima de 2MGUI 979.0 Kb 1642.4 Kb -- (5¼)Capacidad límite teórica (82p) 1001.0 Kb 1668.2 Kb --

Récord absoluto previo a 2M 984.0 Kb 1722.0 Kb 2880.0 KbCapacidad máxima 2M (2MF /M) 1066.0 Kb 1886.0 Kb 3772.0 Kb* 3.5Capacidad mínima de 2MGUI 1178.3 Kb 1974.5 Kb 3949.0 Kb* (3½)Capacidad límite teórica (82p) 1201.2 Kb 2002.0 Kb 4003.9 Kb

(*) No probado. En esta lista están recogidos sólo los formatos soportadospor prácticamente todas las unidades y en casi todos los ordenadores.

Hay también otro pequeño problema técnico: si la capacidad de la pista es múltiplo del tamaño desector lógico empleado (aunque ese sector sea de 128 bytes en lugar de 512) se derrocha espacio al redondear

Page 344: PCA, PS2 ,IBM y AT

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

hacia abajo. La tentación era fuerte: permitir que un sector lógico pueda estar entre dos pistas. De estamanera, la capacidad total de un disco no puede ser múltiplo entero del número de pistas y cabezales.Solución: crear un controlador de dispositivo que trate al disco como un array de sectores (un dispositivo conun sector por pista, un cabezal, y muchísimas pistas, igual que un disco virtual). Así, por ejemplo, los discosde 3½-HD con 12329 bytes por pista tienen en total (con las 82 pistas habituales) 2.021.956 bytes (queequivalen a 15.796 sectores de 128 bytes, totalizando 2.021.888 bytes con un desperdicio de sólo 68).Utilizando una sola FAT, un número razonable de entradas al directorio y clusters de 2048 bytes (que en laspruebas han demostrado generar discos notablemente más rápidos que los de 512 bytes) el espacio disponiblepara el usuario (visible con DIR) alcanza los 2.015.232 bytes netos (1968K). Se trata de nuevo de 1968K...pero esta vez no son brutos, sino netos, y además en todas las unidades (y no en casi ninguna).

En escritura, estos discos son 2 ó 3 veces más lentos que en lectura, aproximadamente. En lecturason sin embargo algo más rápidos que los discos estándar optimizados. La lentitud escribiendo es obvia:imaginemos que hay que escribir un sector ubicado entre dos pistas: primero habra que leer una pista,modificar algunos bytes finales y volverla a escribir, luego leer la siguiente para cambiar unos bytes alprincipio y escribirla de nuevo...¡todo eso para cambiar un sector lógico de 128 bytes!. Sin embargo, tampocoes para tanto, ya que por lo general elDOS envía bloques grandes a losdispositivos y esto supone la escrituradirecta e inmediata de las pistascompletas... que además utilizan latécnica de Sector Sliding (la posicióninicial del sector-pista está desplazadasegún la ubicación en el disco). Dehecho, cacheando las áreas del sistema,la velocidad de escritura seriaprobablemente muy superior, al agilizarel cuello de botella que supone elacceso a la FAT. Sin embargo, elconsumo de memoria del programa(unos 17 Kb) ya es respetable sin caché,y no se llega tampoco al extremo delviejo 1968 de reservar 240 Kb de XMS.

El programa (un único ficheroEXE que se carga en el CONFIG.SYSy luego se puede ejecutar desde la líneade comandos para formatear) estotalmente flexible tanto a nivel lógico(posibilidad de reprogramar el tamañode cluster, el número de entradas aldirectorio y el número de FATs) comofísico (posibilidad de elegir número depistas, densidades, Sector Sliding X e Y(expresado además en grados angulares)e incluso un parámetro nada menos quepara indicar los bytes por pista (por si elusuario tiene una unidad que admitemás). Dispone también de una opciónpara medir con precisión la velocidad derotación de la unidad y para calcularqué capacidad máxima soporta. Laflexibilidad de un disco virtual... peroen un disquete; el número de formatos

C:\AUXI>2mgui

2MGUI instalado en memoria.- Nueva unidad E: 1.2M (unidad física A:)- Nueva unidad F: 1.44M (unidad física B:)Ejecute 2MGUI /? si desea obtener ayuda.

C:\AUXI>dir e:

Volume in drive E is unlabeledFile not found "E:\*.*"

0 bytes in 0 file(s)997.376 bytes free

C:\AUXI>chkdsk e:

997.376 bytes total disk space997.376 bytes available on disk

2.048 bytes in each allocation unit487 total allocation units on disk487 available allocation units on disk

655.360 total bytes memory649.776 bytes free

C:\AUXI>dir f:

Volume in drive F is unlabeledFile not found "F:\*.*"

0 bytes in 0 file(s)2.015.232 bytes free

C:\AUXI>chkdsk f:

2.015.232 bytes total disk space2.015.232 bytes available on disk

2.048 bytes in each allocation unit984 total allocation units on disk984 available allocation units on disk

655.360 total bytes memory649.776 bytes free

C:\AUXI>_

EJEMPLOS DE ACCESO A UN DISCO DE 360KY OTRO DE 1.44M FORMATEADOS CON 2MGUI

Page 345: PCA, PS2 ,IBM y AT

345EL HARDWARE DE APOYO AL MICROPROCESADOR

es prácticamente infinito, según la voluntad del usuario. Una de las opciones es formatear las 28 pistas másexternas en alta densidad y las 54 restantes en doble, en un disco de 360K, obteniéndose 1.2M bastante másfiables de lo que se podría esperar.

Con QEMM, si se instala el driver en memoria superior hay que indicar DMA=13 (unidades 1.44M)ó DMA=25 (unidades 2.88M) en las opciones del controlador de memoria, ya que el buffer para accesodirecto a memoria que establece por defecto es de sólo 12 Kbytes (EMM386 establece 32 Kb).

Las nuevas letras de unidad 2MGUI también soportan discos estándar e incluso 2M (teniendoinstalado también 2M). De hecho, estas nuevas unidades posibilitan el empleo de discos 2M en OS/2.

12.6.7.10 - USO DE 2M 3.0 EN OS/2 2.1

Veamos qué consideraciones hay que tener en cuenta para utilizar disquetes 2M en OS/2. Paraempezar, es necesario arrancar el DOS desde un disquete o desde un fichero imagen de disco, ya que en lasventanas DOS ordinarias 2M no puede controlar los accesos a disco. Curiosamente, sí se puede formatear enestas ventanas, pero no trabajar con el disco: lo que sucede es que el sistema de ficheros de la emulaciónDOS que incorpora OS/2 está gestionado al parecer sin llamadas a la BIOS, precisamente las que intercepta2M, que por tanto no se da cuenta de los accesos a disco. Una vez arrancado desde un fichero imagen con,por ejemplo, MS-DOS 6 (creado con el VMDISK del OS/2) 2M funcionaría perfectamente. Pero lo másprobable es que el usuario tenga instalada la utilidad FSFILTER.SYS para poder acceder a las particionesHPFS y, sobre todo, para poder escribir sobre las particiones FAT ordinarias, que serían de sólo lectura encaso contrario. Y aquí vuelven los problemas: al instalar este driver que altera la INT 21h, 2M deja de nuevode funcionar.

La solución más rápida consiste en crear un driver que implemente 2 nuevas unidades lógicas (comola D: y la E: por ejemplo) que utilicen la BIOS para acceder a disco: en estas nuevas unidades ya no habráproblemas para trabajar con los disquetes 2M. Este driver sería un programa enteramente DOS, que sinembargo no se puede instalar en las ventanas DOS normales de OS/2, ya que en ellas están prohibidos losdispositivos de bloque. Por tanto, su utilización queda restringida a las ventanas de DOS que incorporen unaauténtica versión de este sistema (obtenidas con VMDISK sobre un disquete de arranque, a menos que elusuario desee arrancarlas desde disquete cada vez que vaya a emplearlas).

Pese a la solución de dicho driver (en nuestro caso 2MGUI), existe algún problema relativamenteimportante que comentar. El más interesante consiste en que OS/2 comprueba periódicamente si ha habidoun cambio de disco en alguna unidad, accediendo a la misma en ese caso para comprobar su contenido -conindependencia de que el usuario esté haciendo otra cosa en ese momento, como jugar a los marcianitosmientras espera los resultados de un programa de cálculo-. Si no hay disco introducido no sucede nada, perosi lo hay y es de tipo 2M, OS/2 se queda intentando leerlo de manera obsesiva hasta el punto de colapsar laventana DOS, que queda literalmente colgada (aunque no el resto de las ventanas ni el sistema en conjunto).La solución, si se estaba trabajando en esta ventana, es retirar el disquete de la unidad y esperar un segundoo dos. Ah, y no volver a introducirlo hasta que no se vaya a utilizar, para evitar nuevas molestias. Porfortuna, OS/2 suele tener cuidado de no fisgar por las disqueteras cuando están siendo usadas. La soluciónideal sería un driver que integrara en OS/2 el soporte de estos disquetes, pero eso requiere saber construircontroladores para OS/2.

Las primeras versiones de 2M venían acompañadas de un driver DOS que realizaba la tarea descrita;sin embargo, desde 2M 1.3+ fue sustituido incorrectamente por una recomendación al usuario acerca de lainstalación de DRIVER.SYS, programa que no llama a la BIOS (sino al propio DOS; por tanto, con efectosnulos). Por consiguiente, con 2M 3.0+ aparece de nuevo soporte oficial para este sistema.

Page 346: PCA, PS2 ,IBM y AT

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

12.7. - EL DISCO DURO DEL AT (IDE, MFM, BUS LOCAL).

La información aquí vertida se aplica al tradicional controlador de disco duro ST506, que ha equipadoa los discos duros MFM/RLL de los AT, con el que es compatible en líneas generales tanto el interface delos ESDI como el de los IDE (ISA, PCI o Bus Local). Sin embargo, los discos SCSI no son compatibles conla información que aquí se expone, ni tampoco la controladora de los PC/XT.

12.7.1 - EL INTERFACE.

El disco duro se conecta a la controladora a través de dos cables: uno con las señales de control yotro con las de datos. El de señales de control consta de 34 conectores, y el de datos de 20.

Nombre señal Pin señal Pin masa

- HEAD SELECT 3 2 1- HEAD SELECT 2 4 3- WRITE GATE 6 5- SEEK COMPLETE 8 7- TRACK 000 10 9 Nombre señal Pin señal- WRITE FAULT 12 11- HEAD SELECT 0 14 13 -Unidad seleccionada 1

RESERVADO 16 15 +MFM Escribir datos 13- HEAD SELECT 1 18 17 -MFM Escribir datos 14- INDEX 20 19 +MFM Leer datos 17- READY 22 21 -MFM Leer datos 18- STEP 24 23 Masa 2, 4, 6, 8, 11, 12, 15, 16, 19- DRIVE SELECT 1 26 25- DRIVE SELECT 2 28 27 SEÑALES PARA TRANSFERENCIA DE DATOS- DRIVE SELECT 3 30 29- DRIVE SELECT 4 32 31- DIRECTION IN 34 33

SEÑALES DE CONTROL

Significado de las señales de control (entrada):-Write Gate: Un nivel activo permite a los datos ser escritos en disco. Inactivo implica una lectura y permite mover los

cabezales con el pulso de Step.-Head Select: Estas señales codifican un número de 4 bits para referenciar a un cabezal.-Direction In: Esta señal define la dirección en que se mueven los cabezales con la señal Step. Un valor inactivo indica

dirección Out y los pulsos Step alejan los cabezales del centro del disco; un valor activo se interpreta como Iny dichos pulsos acercan los cabezales al centro.

-Step: Esta señal provoca un movimiento de los cabezales en la dirección que indica la anterior.-Drive Select: Líneas de selección de unidad. Las unidades poseen jumpers para dejarse seleccionar con ciertas combinaciones.Significado de las señales de control (salida):-Seek Complete: Informan del final del posicionamiento de los cabezales. Si esta señal no indica que está terminada dicha

operación, no se puede leer ni escribir.-Track 000: Indica que los cabezales están sobre la pista 0.-Write Fault: Señala una condición que está provocando una operación no correcta del disco.-Index: La unidad indica aquí al exterior el comienzo de una pista (a cada revolución).-Ready: Señal necesaria junto con Seek Complete para poder leer/escribir/posicionar cabezales.

12.7.2 - PROGRAMACIÓN DE LA CONTROLADORA.

El disco duro trabajacon sectores de 512 bytes;soporta corrección de erroresECC, operaciones multisectorrebasando fronteras de pista ycilindro y funciones deautodiagnóstico. El límite decapacidad está en 1024 cilindrosy 16 cabezales. Los registros deoperación son los mostrados en

Dirección E/S hex. Significado

Primaria Secund. Lectura Escritura

1F0 170 Data registers Data register1F1 171 Error register Write precomp1F2 172 Sector count Sector count1F3 173 Sector number Sector number1F4 174 Cylinder low Cylinder low1F5 175 Cylinder high Cylinder high1F6 176 Drive/Head Drive/Head1F7 177 Status register Command register

la figura, estando la controladora ubicada normalmente en la localización E/S primaria.

Page 347: PCA, PS2 ,IBM y AT

347EL HARDWARE DE APOYO AL MICROPROCESADOR

Significado de los registros:

Data Register: Permite acceder al buffer donde está almacenado el sector para leer y escribir en elmodo PIO (esto es, sin DMA). No debería ser accedido a menos que haya unaoperación de lectura o escritura en curso. Implementa una dirección de 16 bits dentrodel buffer de la controladora que contiene al sector para las operaciones de lecturay escritura normales. Para una lectura/escritura largas 4 bytes ECC son transferidospor byte con al menos 2 microsegundos entre transferencias (la línea DRQ debe estaractiva antes de transferir los bytes ECC).

Error Register: De sólo lectura, contiene información sobre el comando previo. El dato es válidosólo cuando el bit de error en el registro de estado está activo.

Tras conectar el disco duro a la corriente o tras enviar el comando apropiado, seencuentra en modo diagnóstico: en esos casos, el registro debe ser comprobado digalo que diga el bit del registro de estado (con el significado en estos casos de 01-Nohay error, 02-Fallo del controlador, 03-Error en el buffer del sector, 04-Error en eldispositivo ECC, 05-Error en el procesador de control).

Cuando no está en modo diagnóstico, caso más común, significado de sus bits:bit 0: Data Address Mark (DAM) no encontrada en los 16 bytes del campo ID.bit 1: Error TR 000. Se activa si tras un comando Restore, la señal Track 000 no

se activa después de 1023 pulsos de retroceso.bit 2: Comando abortado. En estos casos, se debe mirar los registros de Status y

Error para determinar con precisión la causa (que estar en Write Fault, SeekComplete, Drive ready -- o comando inválido en otro caso).

bit 3: No usado.bit 4: ID no encontrada. La marca ID que identifica al cilindro, cabezal y sector

no ha sido encontrada. Si están activos los reintentos, el controlador loreintenta 16 veces antes de dar error, en caso contrario sólo explora la pistacomo mucho 2 veces antes de dar el error.

bit 5: No usado.bit 6: Error ECC. Indica si se ha producido un error ECC incorregible durante una

lectura.bit 7: Bad Block detected. Indica que se ha encontrado un sector marcado como

defectuoso en la ID; no se intentarán en él ni lecturas ni escrituras.WritePrecompensation: El valor almacenado es el cilindro de comienzo para la escritura precompensada

dividido por 4.Sector Count: Indica el número de sectores a transferir durante la lectura, escritura, verificación o

formateo. En las operaciones multisector, este registro se decrementa y el SectorNumber se incrementa; al formatear, antes de enviar cada comando de formateo debecargarse aquí el número de sectores en la pista. Se soportan operaciones multisectorque crucen fronteras de pista y cilindro. Las características de la unidad debenestablecerse con el comando Set Parameters antes de una transferencia multisector.Este registro debe cargarse con el número de sectores antes de cualquier comandorelacionado con datos. Un valor 0 representa 256 sectores.

Sector Number: Número de sector para la lectura, escritura y verificación. El sector inicial se cargaaquí en las operaciones multisector.

Cylinder Number: Número de cilindro para los comandos de lectura, escritura, verificación yposicionamiento de cabezales. Entre el registro que almacena la parte baja y el de laparte alta (low y high respectivamente) se guarda un número entre 0 y 1023.

Drive/Head: Bits 7 y 5 puestos a 1, el 6 puesto a 0. El bit 4 indica la unidad seleccionada (0 elprimer disco duro y 1 el segundo) y los bits 0-3 el número de cabezal delectura/escritura deseado. Para acceder a las cabezas 8-15, es necesario ademásactivar el bit 3 del puerto 3F6h. Importante: este registro debe cargarse con elnúmero máximo de cabezales antes de enviar un comando Set Parameters.

Page 348: PCA, PS2 ,IBM y AT

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

Status register: Se actualiza tras ejecutar los comandos. El programa debe mirar este registro paraconocer el resultado. Si el bit busy (7) está activo, los demás bits no son válidos.Una lectura de este registro borra la petición de interrupción IRQ 14. Si write-fault(bit 5) o error (bit 0) están activos, o si seek-complete (bit 4) o drive-ready (bit 6)están inactivos, la operación multisector es abortada. Significado de los bits:bit 7: Busy. Un 1 indica que el controlador está ejecutando un comando; por tanto,

este bit debe ser examinado antes de leer cualquier registro.bit 6: Drive-ready. Un 0 indica que la lectura, escritura y seek están inhibidas; para

poder ejecutarlas debe estar a 1 junto con el bit seek-complete (4).bit 5: Write-fault. Un 1 indica funcionamiento incorrecto de la unidad; la lectura,

escritura o seek están inhibidos.bit 4: Seek-complete. Un 1 indica que los cabezales han terminado el seek.bit 3: Data-request. Este bit indica que el buffer del sector necesita ser atendido en

un comando de lectura o escritura: si este bit o el busy (7) están activos, hayun comando en ejecución. Hasta recibir algún comando, este bit está a 0.

bit 2: Corrected-data. Un 1 indica que los datos leídos del disco fueron corregidosde error ECC con éxito. Errores suaves no abortan la operación multisector.

bit 1: Index. Este bit se pone a 1 tras cada revolución del disco.bit 0: Error. Un 1 indica que el comando previo terminó en error, y uno o más bits

del Error register están activos. El próximo comando enviado al controladorborra este bit. Si este bit se activa, la operación multisector es abortada.

Command Register: Acepta 8 diferentes comandos. Los comandos se programan cargando primero losdemás registros necesarios y escribiendo después el comando en éste mientras elregistro de estado devuelve una condición de no busy. Un comando no legal provocaun error de comando abortado. La solicitud de interrupción IRQ 14 se borra alescribir un comando. Los comandos soportados son:

Comando bit 7 6 5 4 3 2 1 0

Restore 0 0 0 1 R3 R2 R1 R0Seek 0 1 1 1 R3 R2 R1 R0Read sector 0 0 1 0 0 0 L TWrite sector 0 0 1 1 0 0 L TFormat track 0 1 0 1 0 0 0 0Read verify 0 1 0 0 0 0 0 TDiagnose 1 0 0 1 0 0 0 0Set Parameters 1 0 0 1 0 0 0 1

R3 R2 R1 R0 Stepping rate

0 0 0 0 35 µs0 0 0 1 0.5 ms0 0 1 0 1.0 ms0 0 1 1 1.5 ms0 1 0 0 2.0 ms0 1 0 1 2.5 ms0 1 1 0 3.0 ms0 1 1 1 3.5 ms1 0 0 0 4.0 ms1 0 0 1 4.5 ms1 0 1 0 5.0 ms1 0 1 1 5.5 ms1 1 0 0 6.0 ms1 1 0 1 6.5 ms1 1 1 0 7.0 ms1 1 1 1 7.5 ms

Bit 0 1

L Modo de datos Sólo datos Datos y 4 bytes ECCT Modo de reintentos Reintentos habilitados Reintentos inhibidos

Nota: Después de un reset o un comando Diagnose, el step rate queda en 7.5 ms.Por otro lado, el sistema verifica la operación ECC leyendo y escribiendoestos bytes: cuando los reintentos están deshabilitados, los reintentos de ECCe ID están limitados a menos de dos vueltas completas del disco.

Page 349: PCA, PS2 ,IBM y AT

349EL HARDWARE DE APOYO AL MICROPROCESADOR

Explicación de los comandos.

Restore: Envía los cabezales a la pista 0 (hasta que la señal Track 000 es activa). Si Track 000 no seactiva tras 1023 pulsos de step activa el bit de error en el registro de estado y deja el errorTR 000 en el registro error. El step rate es establecido por el propio comando.

Seek: Mueve los cabezales al cilindro indicado. Está soportado un seek simultáneo en dos unidades.Al final del comando se produce una interrupción.

Read sector: Cierto número de sectores (1-256) pueden ser leídos del disco duro con o sin el campo ECCañadido, en el modo PIO (entrada-salida programada, sin DMA). Si los cabezales no estánsobre la pista necesaria, el controlador envía pulsos step para posicionarlo, utilizando el steprate del último seek o restore. Los errores de datos de hasta 5 bits son corregidosautomáticamente en los comandos de lectura corta. Si un error no corregible tiene lugar, secontinúa leyendo el sector donde apareció pero ya no se leen más sectores en el caso de losaccesos multisector. Se produce una interrupción por cada sector cuando está preparado paraser transferido, pero no al final del comando.

Write sector: Cierto número de sectores (1-256) pueden ser escritos a disco duro con o sin el campo ECCañadido, en el modo PIO (entrada-salida programada, sin DMA). Realiza los seeks que seanecesario hacer. Las interrupciones suceden cada vez que es transferido un sector al buffer(salvo el primero) y al final del comando. El primer sector debería ser escrito en el bufferinmediatamente después de que el comando ha sido enviado y "Data-request" es activo.

Format track: Se formatea la pista indicada según la tabla de interleave que se transfiere. Hay 2 bytes porcada sector: 0, Nº sector. Así se puede elegir la numeración deseada. Hay que enviar 512bytes con independencia de que sean menos en la tabla (por ej. 34 bytes para 17 sectores).El sector count debe cargarse con el nº de sectores por pista antes de cada comando de estos.Se genera una interrupción al final del comando de formateo. Los sectores defectuosos semarcan sustituyendo el 0 que les precede por 80. Cuando se conmuta entre dos unidades,antes de formatear hay que hacer un restore.

Read Verify: Similar al comando read sector con la diferencia de que no se envían datos al ordenador; deesta manera simplemente se verifica la integridad de los mismos. Una única interrupción segenera al completarse el comando o en caso de error.

Diagnose: El adaptador ejecuta su auto-test y devuelve el resultado en el error register. Se produce unainterrupción cuando completa el comando.

Set Parameters: Establece los parámetros de la unidad: máximo número de cabezales y sectores/pista. Elregistro drive/head indica qué unidad es afectada. Hay que actualizar los registros sectorcount y drive/head antes de enviar este comando. Estos parámetros serán empleados paracruzar los cilindros en las operaciones multisector. Se genera una interrupción cuando secompleta el comando. Este comando debe ser enviado antes de intentar alguna operaciónmultisector. Se soportan dos discos duros, con diferentes características cada uno, definidaspor este comando.

Registro del controlador de disco duro (3F6h) y Registro de entrada digital (3F7h).

Además de informar de la línea de cambio de disco en los disquetes, los bits 0-5 del registro deentrada digital (3F7h) están relacionados con el disco duro.

3F6h - bits 7-4:Reservados 3F7h - bit 7: Línea de cambio de disquetes.bit 3: 0 - Reduce Write Current) bit 6: Write gate

1 - Head 3 select enable) bit 5: Head select 3 / Reduced Write Currentbit 2: 1 - Disk reset enable bit 4: Head select 2

0 - Disk reset disable bit 3: Head select 1bit 1: 0 - Disk initialization enable bit 2: Head select 0

1 - Disk initialization disable bit 1: Drive select 1bit 0: Reservado bit 0: Drive select 0

12.7.3 - EJEMPLO PRACTICO DE PROGRAMACIÓN.

En los AT la interrupción de disco duro es la IRQ 14 (INT 76h). La BIOS, en caso de producirseesta interrupción, almacena un valor 0FFh en 40h:8Eh con el gestor que tiene por defecto. Las transferencias

Page 350: PCA, PS2 ,IBM y AT

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

con el disco duro tienen lugar sin DMA por regla general. Esto se comprende mejor teniendo en cuenta quela controladora tiene un buffer interno con capacidad para algún sector y, por tanto, cuando hay quetransferirlo, no hay que esperar a que venga del disco mientras este gira lentamente (como en el caso de losdisquetes): una transferencia con el DMA ordinario aquí sería más lenta que a través de la CPU.

Parte de la documentación vista con anterioridad es sólo oficial. Por ejemplo, los discos IDE suelenvenir formateados de fábrica a bajo nivel e ignoran el comando de formateo: estas unidades son bastanteinteligentes y llevan su propia gestión de sectores defectuosos (reemplazándolos por otros que tienen librespara simular que todo está correcto) así como de interleaves (generalmente 1:1, valores peores se deben acontroladoras obsoletas que no tenían un buffer con capacidad para una pista) y skews óptimos.

El programa de ejemplo es el embrión de una utilidad para acceder directamente a la controladorade disco duro. La función operahd() solo implementa realmente el comando de leer sector. En el ejemplo,se lee el primer sector absoluto del disco, donde suele ser fácil reconocer la tabla de particiones. En nuestrocaso, en lectura, justo antes de enviar el comando se borra el flag de interrupción. Una vez enviado, cuandollegue la interrupción se procede a leer el sector. Para ello se utiliza la rápida instrucción REP INSW del 286y procesadores superiores, para E/S repetitiva, que lo trae a gran velocidad desde la memoria de lacontroladora a la del sistema.

Un acceso directo a bajo nivel puede tener mucho interés para ciertas aplicaciones. Por ejemplo, unantivirus puede asegurarse de que ha reparado la tabla de particiones (o cualquier otra zona del disco) sintemor a que en su llamada a INT 13h el virus residente le haya estropeado el trabajo (aunque si el virustrabaja en modo protegido y controla el acceso a los puertos E/S del disco duro...).

HDIRECT.C/********************************************************************** ** ACCESO A DISCO DURO ESTANDAR AT (IDE, MFM, BUS LOCAL, ETC) ** PROGRAMANDO DIRECTAMENTE LA CONTROLADORA ** ** - Compilar en modelo Large. ** - Este programa sólo implementa la función de leer sector. ** - No soportadas controladoras de XT, SCSI u otras. ** **********************************************************************/

#include <dos.h>#include <alloc.h>#include <conio.h>#include <stdio.h>#include <stdlib.h>

#define HD_RESTORE 0x10 /* comandos del controlador */#define HD_SEEK 0x70#define HD_READ 0x20#define HD_WRITE 0x30#define HD_FORMAT 0x50#define HD_READVERIFY 0x40#define HD_DIAGNOSE 0x90#define HD_SETPARAM 0x91

#define HDR_MAIN 0x3F6#define HDR_DATA 0x1F0 /* registros del controlador */#define HDR_ERROR 0x1F1#define HDR_WRITEP 0x1F1#define HDR_SECNT 0x1F2#define HDR_SEC 0x1F3#define HDR_LCYL 0x1F4#define HDR_HCYL 0x1F5#define HDR_DRVHD 0x1F6#define HDR_STATUS 0x1F7#define HDR_CMD 0x1F7

#define HD_ECC 2#define HD_NORETRY 1

#define HD_BUSY 0x80#define HD_DATA_REQ 8

int operahd (int unidad, int cabeza, int cilindro, int sector,int operacion, char huge *direccion, int numsect)

int i;

if (operacion==HD_SETPARAM)

else if (operacion==HD_DIAGNOSE)

else if (operacion==HD_FORMAT)

else if ((operacion & 0xFE) == HD_READVERIFY)

else if ((operacion & 0xFC) == HD_READ) outportb (HDR_SECNT, numsect); /* nº sectores */outportb (HDR_SEC, sector); /* primer sector */outportb (HDR_LCYL, cilindro & 0xFF); /* nº cilindro 0..7 */outportb (HDR_HCYL, cilindro >> 8); /* nº cilindro 8..9 */outportb (HDR_DRVHD, unidad << 4 | cabeza | 0xC0);outportb (HDR_MAIN, cabeza & 8);

pokeb (0x40, 0x8e, 0); /* flag de interrupción a 0 */

outportb (HDR_CMD, operacion); /* comando */

while (!peekb(0x40, 0x8e)); /* esperar interrupción 76h *//* (convendría poner un timeout) */

/* por eficiencia, el siguiente código está en ensamblador */

asm push es /* máxima lectura soportada: casi 64 Kb */push cxpush dxpush dimov cx,numsectxchg ch,cl /* CX = numsect * 256 = nº palabras */les di,direccioncldmov dx,HDR_DATAdb 0F3h, 6Dh /* instrucción 286+ ’rep insw’ */pop dipop dxpop cxpop es

else if ((operacion & 0xFC) == HD_WRITE)

else if ((operacion & 0xF0) == HD_SEEK)

else if ((operacion & 0xF0) == HD_RESTORE)

void main()

/* el puntero huge comienza en XXXX:0004 */

unsigned char huge *buffer, huge *p;unsigned i, j, k;

if ((buffer=farmalloc(0xFFFC))==NULL) printf("\nMemoria insuficiente.\n");exit(1);

/* leer sector de tabla de partición */

operahd (0, 0, 0, 1, HD_READ | HD_NORETRY, buffer, 1);

/* imprimir sector de 512 bytes */

p=buffer;for (i=0; i<2; i++)

clrscr();for (j=0; j<256; j+=16)

for (k=0; k<16; k++) printf("%02X ", *p++); p-=16;printf(" ");for (k=0; k<16; k++)

if (*p<’ ’) printf("."); else printf("%c", *p);p++;

printf("\n");

printf("\n- Estás viendo 256 bytes del sector.\n");printf("- Pulsa una tecla para continuar.");getch();

Page 351: PCA, PS2 ,IBM y AT

351EL HARDWARE DE APOYO AL MICROPROCESADOR

12.8. - EL CONTROLADOR DEL TECLADO: 8042.

En este apartado se estudiará a fondo el funcionamiento a bajo nivel del teclado en los ordenadorescompatibles, si bien es poco frecuente que sea necesario acceder al mismo de esta manera.

12.8.1 - EL 8042.

Un microordenador llamado teclado.

El teclado se conecta al ordenador por medio de un cable que contiene 4 hilos hábiles: dos queconducen la corriente, uno para datos y otro para reloj. El teclado es en realidad un pequeño microordenador;de hecho muchos teclados llevan en su interior el chip 8049 de Intel (el microprocesador esclavo del viejoQL de Sinclair) que consta de unos 2 Kb de memoria ROM y 128 bytes de RAM (las 8 primeras posicionesson empleadas como registros). Este procesador se encarga de detectar la pulsación de las teclas, generandounos bytes que las identifican y enviándolos a continuación por el cable a través de un protocolo decomunicación en serie que en el AT consta de 11 bits por cada dato (1 de inicio, 8 de datos, 1 de paridady otro de stop) y 9 en los XT (entre otras razones, porque no se controla la paridad). Los teclados de AT yde XT generan códigos diferentes para las mismas teclas. Además, al soltar una tecla, los teclados de XTgeneran el mismo código que al pulsarla pero con el bit 7 activo; sin embargo, en AT se generan dos códigosque se envían consecutivamente (0F0h y después el mismo código que al pulsarla). El teclado se encarga derepetir los códigos de una tecla cuando ésta lleva cierto tiempo pulsada, en el conocido mecanismo autorepeatde la mayoría de los teclados. Muchos teclados tienen debajo un interruptor que permite seleccionar su modode funcionamiento (XT o AT).

El teclado en los PC y XT.

Los datos, cuando llegan al ordenador, reciben un tratamiento diferente en función de si el ordenadores un XT o un AT, mucho más sencillo en el primero. En los XT se van colocando los bits que llegan enun simple registro de desplazamiento conectado al puerto 60h; al completarse los 8 se produce unainterrupción de tipo IRQ 1 (INT 9), la segunda de mayor prioridad después de la del temporizador. Noobstante, el teclado es capaz de memorizar hasta 8 pulsaciones cuando la CPU no tiene tiempo para atenderle.Después de leer el código de la tecla, el programa que la gestione habrá de enviar una señal dereconocimiento a la circuitería del ordenador para permitir que continúe la recepción de datos.

El controlador del teclado del AT: el 8042.

En los AT hay un circuito integrado encargado de interpretar los datos procedentes del teclado y,después de traducirles adecuadamente para compatibilizar con los XT si así ha sido programado, enviarlesa la CPU: el 8042 de Intel. También sirve de intermediario a las transmisiones de datos de la CPU al teclado,que en el AT es un periférico bidireccional que puede recibir comandos para configurar los LEDs, entre otrastareas. Cuando el 8042 recibe un byte entero del teclado, inhibe la comunicación hasta que la CPU lo acepta.Si el dato se recibe con error de paridad, automáticamente el 8042 lo solicita de nuevo al teclado enviandoun comando de reenvío al mismo y un byte 0FFh a la CPU indicando esta circunstancia, activando tambiénel bit 7 del registro de estado del 8042. Además, chequea que no pasen más de 2 milisegundos durante larecepción: si se excede este límite se envía también un 0FFh a la CPU y se activa el bit 6 en el registro deestado. Cuando la CPU envía algo al teclado, el 8042 inserta el bit de paridad automáticamente. Si el tecladono empieza la comunicación en menos de 15 milisegundos o tarda en recibir el dato más de 2 milisegundos,se envía un 0FEh a la CPU y se activa el bit 5 en el registro de estado. Además, el teclado ha de respondera todas las transmisiones con un byte de reconocimiento, si en esta operación hay un error de paridad seactivarán los bits 5 y 7 en el registro de estado; si tarda más de 25 milisegundos en responder también seenvía el byte 0FEh a la CPU y se activan los bits 5 y 6 del registro de estado.

La comunicación teclado-CPU puede ser inhibida por hardware por medio de la llave que incorporala unidad central, aunque la comunicación CPU-teclado sigue habilitada. El 8042 se apoya en tres registros

Page 352: PCA, PS2 ,IBM y AT

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

básicos: uno de estado, uno de salida y otro de entrada. El registro de estado, del que ya se ha explicado partede su funcionalidad, se encuentra en el puerto de E/S 64h y puede ser leído en cualquier momento. Elsignificado de sus bits se explica en el cuadro 1.

El registro de salida está ubicado en el puerto 60h y es de sólo lectura; el 8042 lo usa para enviarlos códigos de las teclas a la CPU y los bytes de datos de los comandos que los soliciten. Debería ser leídosólo cuando el bit 0 del registro de estado está activo.

El registro de entrada del 8042 es de sólo escritura y puede ser accedido por los puertos 60h y 64hsegún que lo que se quieran enviar sean datos o comandos al 8042, respectivamente; los datos seránreenviados por el 8042 hacia el teclado a menos que el propio 8042 esté esperando un dato de la CPU aconsecuencia de un comando previo enviado por ésta. Los datos deben ser escritos en este registro sólocuando el bit 1 del registro de estado esté inactivo. En el cuadro 2 se listan los comandos que admite el 8042(enviados al puerto 64h). Debe darse cuenta el lector de la particularidad de que los registros de salida yentrada son accedidos por el mismo puerto (60h), siendo la lectura y escritura las que seleccionan el accesoa uno u otro respectivamente.

BIT SIGNIFICADO

Registro de salida lleno. Un 1 indica que el 8042 ha colocado un dato en el registro de0 salida y la CPU aún no lo ha leído. Este bit se pone a 0 cuando la CPU lee el puerto 60h.

1 Registro de entrada lleno. Un 1 significa que ha sido colocado un dato en el registro deentrada y el 8042 aún no lo ha leído.

2 Banderín del sistema: asignado con un comando del 8042. 0 al arrancar.

Comando/dato. Se pone a 1 o a 0 al enviar algo al puerto 60h o al 64h respectivamente: de3 esta manera, el 8042 sabe si lo que se le envía son órdenes o datos (órdenes= 1). Ambos

puertos conectan con el registro de entrada.

4 Bit de inhibición. Este bit se actualiza siempre que se coloca un dato en el registro desalida, un 0 indica teclado inhibido.

5 Transmisión fuera de tiempo. Indica que la transmisión de un dato hacia el teclado no hasido respondida en los márgenes de tiempo adecuados.

6 Recepción fuera de tiempo. Indica si el teclado ha enviado un dato y sigue enviando másdespués del tiempo esperado.

7 Error de paridad. Indica la paridad del dato recibido: 0 la correcta.

CUADRO 1: REGISTRO DE ESTADO

12.8.2 - EL TECLADO DEL AT

Como se dijo en el apartado anterior, el teclado del AT es bidireccional y admite comandos por partedel ordenador. Estudiaremos ahora cuáles son esos comandos. En primer lugar, tras el arranque del ordenadory al recibir la alimentación el teclado, éste realiza un autotest denominado BAT (Basic Assurance Test) dondechequea su ROM, RAM y enciende y apaga todos los LED. Esta operación emplea entre 600 y 900milisegundos; al acabar el BAT y cuando sea posible establecer la comunicación con el ordenador (líneas dereloj y datos en alto) envía un byte 0AAh si todo ha ido bien y un 0FCh si ha habido fallos; inicializandodespués los parámetros de autorepetición de las teclas.

El teclado tiene un buffer interno con capacidad para 17 bytes (unas 8 teclas) con objeto de almacenarlas últimas teclas pulsadas cuando no puede enviarlas al 8042. Cuando este buffer se llena, su última posición(17ª) se rellena con 0 y se ignoran las siguientes pulsaciones.

12.8.3 - COMUNICACIÓN CPU TECLADO

Los comandos al teclado pueden ser enviados en cualquier momento al puerto 60h: a menos que el8042 esté esperando por un byte de datos en el registro de entrada, como consecuencia de un comandoprevio, redireccionará todo lo que se le envíe por el puerto 60h hacia el teclado. El teclado responderá enmenos de 20 milisegundos, devolviendo una señal de reconocimiento por medio de un byte 0FAh. Los

Page 353: PCA, PS2 ,IBM y AT

353EL HARDWARE DE APOYO AL MICROPROCESADOR

principales comandos (diferenciados de los datos por tener el bit 7 activo) son:

- Reset (0FFh): Al recibirlo envía una señal de reconocimiento y se asegura de que la CPU se de por enteradaponiendo en alto las líneas de reloj y datos un mínimo de 500 microsegundos; el teclado permanece inhibidohasta que la CPU acepta la señal de reconocimiento o envía otro comando que sobreescribe y anula éste.Llegados a este punto, el teclado ejecuta de nuevo el BAT, estableciendo valores por defecto para laautorepetición y limpiando su registro de salida.

- Reenvío (0FEh): El sistema puede enviar este comando al teclado cuando detecta un fallo en la recepcióndesde el teclado. Este comando sólo puede ser enviado después de una transmisión del teclado y antes dehabilitar la comunicación para la siguiente recepción. El teclado responde enviando de nuevo el dato anterior(si ya era un 0FEh, el último dato que envió que no fuera 0FEh).

COMANDO SIGNIFICADO

20h Leer el byte de comando del 8042 (ver cuadro 3). Esta orden envía al registro de salida (enel puerto 60h) dicho byte para que sea leído.

60h Escribir el byte de comando del 8042. El siguiente byte que se envíe al registro de entrada(puerto 60h) será el byte de comando del 8042.

AAh Autotest. El 8042 realiza un diagnóstico interno y coloca un 55h en el registro de salidasi todo va bien.

Test del interface. El controlador chequea las líneas de reloj y datos devolviendo: 0 si noABh hay errores; 1: el reloj está demasiado en bajo, 2: está demasiado en alto; 3: la línea de

datos está demasiado en bajo y 4: la línea de datos está demasiado en alto.

ACh Volcado de diagnóstico. Envía al registro de salida, sucesivamente, 16 bytes de la RAM del8042, el estado de los registros de entrada y salida y la palabra de estado del controlador.

ADh Inhibir teclado. Esto activa el bit 4 del byte de comando del 8042.

AEh Habilitar teclado. Esto baja el bit 4 del byte de comando del 8042.

Leer el puerto de entrada (véase cuadro 4). Esto obliga al 8042 a leer el puerto de entradaC0h y colocar lo que lee en el registro de salida; sólo ha de emplearse este comando cuando el

registro de salida está vacío.

D0h Leer el puerto de salida. El 8042 lee el puerto de salida y lo coloca en el registro de sa-lida; sólo debe emplearse este comando si dicho registro está vacío.

D1h Escribir el puerto de salida (ver cuadro 5). El siguiente byte que se envíe al registro deentrada (puerto 60h) se colocará en el puerto de salida.

E0h Leer entradas de testeo. El 8042 coloca en el registro de salida los bits de reloj (bit 0)y datos (bit 1) para permitir la comunicación directa con el teclado.

Los bits 0 al 3 de este comando (la parte baja de este mismo comando) se relacionan con losFxh bits 0 al 3 del puerto de salida del 8042; un 0 indica bit pulsado durante 6 microsegundos

(apróx.) y un 1 que el bit no resulta modificado; ¡cuidado con el reset!.

CUADRO 2: COMANDOS DEL 8042

- Establecer valores por defecto (0F6h): Devuelve la autorepetición a los valores habituales, limpia su registrode salida y continúa rastreando las teclas si no estaba inhibido; es una especie de reset en caliente.

- Establecer valores por defecto y parar (0F5h): Similar al comando anterior, pero dejando de rastrear lasteclas y permaneciendo inhibido hasta recibir más instrucciones.

- Habilitar (0F4): Reanuda el funcionamiento interrumpido por el comando anterior o algún otro.

- Establecer ratio y retardo de autorepetición (0F3h): Tras este comando debe enviarse otro inmediatamentea continuación, que se interpretará como dato, estableciendo los valores de autorepetición. De este segundobyte, el bit 7 estará siempre a cero; el valor de los bits 5 y 6, sumándole una unidad, indica el tiempo queha de pasar desde que se pulsa una tecla hasta que comience a autorepetirse, en unidades de 0,25 segundos(±20%). Los bits 2, 1 y 0 forman un número A; los bits 4 y 3 forman otro número B; por medio de lasiguiente fórmula se obtiene la tasa o ratio de autorepetición en «teclas por segundo»:

Page 354: PCA, PS2 ,IBM y AT

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

1

(8 + A) * ( 2 ^ B) * 0.00417

Una vez recibido este comando, el teclado envía la acostumbrada señal de reconocimiento, deja de rastrearlas teclas y espera por el parámetro de autorepetición, respondiendo al mismo con otra señal dereconocimiento y volviendo a rastrear las teclas. Si en lugar de recibir el parámetro recibe otro comando (bit7 activo) dejará inalterados los valores de autorepetición y procesará dicho comando, aunque ¡cuidado!:permanecerá inhibido hasta que se le habilite con el comando 0F4h. Por defecto, el sistema establece una tasade 10 caracteres por segundo y 0,5 segundos de espera (parámetro 4Ch).

BIT SIGNIFICADO

0 Activar la interrupción del registro de salida lleno: un 1 indica que el 8042 genereuna IRQ1 (INT 9) tras colocar un dato en el registro de salida (esto es lo normal).

1 Reservado (escribir 0).

2 Banderín del sistema. Este bit define el bit 2 del registro de estado.

3 Ignorar inhibición: con 1 se ignorará la función de inhibir el teclado.

4 Deshabilitar el teclado: un 1 baja la línea de reloj inhibiendo la comunicación del8042 con el teclado.

5 Modo IBM PC. Con 1 no se traducen los códigos del teclado ni se controla la paridad.

IBM PC compatibilidad. Un 1 selecciona la conversión de los códigos del teclado para6 emular los del PC y XT, traduciendo los códigos de rastreo y generando un único byte

al soltar las teclas. Puesto a 1 por la BIOS antes de cargar el DOS (compatibilidad).

7 Reservado (escribir 0).

CUADRO 3: BYTE DE COMANDO DEL 8042

BIT SIGNIFICADO BIT SIGNIFICADO

0-3 Indefinidos 0 Reset del sistema (como Ctrl-Alt-Del).

4 RAM del sistema. A 1 si insta- Línea A20: 0 fuerza la línea A20 de la CPU a 0, con lo quelada la extensión de 256 Kb. 1 se prohíbe acceder a la memoria por encima de 1 Mb lo cual

emula el direccionamiento de los PC/XT; un 1 deja que A205 A 0 si presente el puente (o la controle la CPU aunque hay PC’s en que esto no basta.

«jumper») del fabricante.2-3 Indefinidos.

Tipo de pantalla. 0 si la pan-6 talla principal es de color y 4 Registro de salida lleno.

1 si es monocroma.5 Registro de entrada vacío.

0: el teclado ha sido bloquea-7 do con la llave externa de la 6 Línea de reloj (comunicación directa con el teclado).

unidad central.7 Línea de datos (comunicación directa con el teclado).

CUADRO 4: BYTE RECIBIDO POR ELPUERTO DE ENTRADA CUADRO 5: BYTE A ENVIAR AL PUERTO DE SALIDA

- No operación (0F7h a 0FDh y 0EFh al 0F2h): Son códigos reservados; el teclado al recibirlos envía la señalde reconocimiento de siempre y no realiza ninguna acción.

- Eco (0EEh): Si el teclado recibe este comando, lo reenvía a continuación. Es una ayuda al diagnóstico.

- Encender/apagar los LED (0EDh). Tras este comando se ha de enviar otro byte de datos, cuyos bits 0, 1y 2 están ligados al estado de los LED de Scroll Lock, Num Lock y Caps Lock, respectivamente; los demásestán reservados. Al recibir el comando envía la correspondiente señal de reconocimiento y deja de rastrearlas teclas, esperando por el dato. Si en vez de un dato recibe otro comando, dejará intactos los LED,procesará dicho comando y continuará rastreando las teclas (sin quedar inhibido en esta ocasión). El siguienteejemplo muestra cómo establecer los LED configurados en AH:

CLIMOV AL,0EDhOUT 60h,AL ; enviar comandoXOR CX,CX

Page 355: PCA, PS2 ,IBM y AT

355EL HARDWARE DE APOYO AL MICROPROCESADOR

espera: JMP SHORT $+2 ; insertar estados de espera para AT obsoletoJMP SHORT $+2IN AL,64hTEST AL,2LOOPNZ espera ; esperar que reciba comandoMOV AL,AHOUT 60h,AL ; establecer los LEDSTI

En general, este será el procedimiento a seguir para cualquier comando que requiera parámetros: hayque esperar el momento adecuado para enviarlos; el LOOPNZ evita que la CPU se quede colgada si porcualquier motivo fallara el teclado o el 8042. Como se ve, se establecen los 3 LED a la vez, aunque si sólose desea cambiar uno habrá que consultar el estado actual de los otros en las variables de la BIOS. Noobstante, este cambio es sólo puntual ya que al pulsar las teclas que actúan sobre los LED, la BIOS o elKEYB los reajustarán anulando el cambio, siendo necesario reprogramar parcialmente la interrupción delteclado si se desea evitarlo.

12.8.4 - COMUNICACIÓN TECLADO CPU

Más bien cabría llamarla la comunicación teclado 8042: aunque muchos de estos códigos acabensiendo interpretados por la CPU, algunos se los queda el 8042 que siempre es el primero en enterarse. Acontinuación se listan los valores que el teclado puede enviar a la CPU o al 8042 en un momento dado.

- Reenvío (0FEh): El teclado puede enviar este comando a la CPU para solicitar el reenvío cuando detectaun fallo en la recepción (normalmente de paridad) o una entrada incorrecta.

- Reconocimiento ó ACK (0FAh): El teclado devuelte este valor cada vez que la CPU le envía algo, paraindicar que lo ha recibido (excepto en el caso de los comandos Eco y Reenvío de la CPU).

- Desbordamiento (0): Cuando la CPU intenta leer el teclado directamente sin haber códigos en el buffer delteclado (el buffer interno del propio teclado, se entiende) accederá a la posición 17ª del mismo, encontrándoseeste valor.

- Fallo en el diagnóstico (0FDh): El teclado periódicamente se autochequea y envía este código si detectaalgún fallo. Si el fallo sucede durante el BAT, dejará de rastrear las teclas en espera de un comando de laCPU; en cualquier otro momento continuará rastreando las teclas.

- Código de tecla soltada ó break code (0F0h): El teclado envía este código a la CPU para indicar que elsiguiente código que enviará a continuación corresponderá a una tecla soltada. Bajo MS-DOS este código lointercepta el 8042 y se lo oculta a la CPU, con objeto de emular el código de tecla soltada de los PC/XT.

- BAT completado (0AAh): Después de realizar el BAT el teclado envía un 0AAh para indicar que ha salidobien, o un 0FCh (u otro valor) si ha habido fallos.

- Respuesta al eco (0EEh): El teclado envía este valor a la CPU si ésta se lo ha enviado a él.

la comunicación directa CPU teclado.

Debido a la presencia del 8042, normalmente no será preciso que la CPU se comunique directamentecon el teclado a través de las líneas de reloj y datos. No obstante, este capítulo está explicado en el manualde referencia técnico del IBM AT, al menos en la edición de 1984; por tanto, aquellos aficionados que esténpensando construirse su propio ordenador y acoplarle un teclado ordinario de PC podrían consultar ese libro.Por cierto, en los PC y XT no es preciso tampoco realizar esta tarea, ya que el teclado con el conmutadorde selección de la parte inferior en modo XT no es realmente bidireccional (de hecho, lleva un controlautónomo de los LED) por lo que no tiene sentido intentar enviar nada. Y a la hora de recibir, hay métodosmucho más cómodos...

Page 356: PCA, PS2 ,IBM y AT

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

12.9. - EL PUERTO SERIE: UART 8250.

La transmisión de datos en serie es una de las más comunes para aquellas aplicaciones en las que lavelocidad no es demasiado importante, o no es posible conseguirla (por ejemplo, vía red telefónica). Parasimplificar el proceso de enviar los bits uno por uno han surgido circuitos integrados que realizan la función,teniendo en cuenta todos los tiempos necesarios para lograr una correcta comunicación y aliviando a la CPUde esta pesada tarea. El circuito que estudiaremos es el 8250 de National, fabricado también por Intel, aunquelas diferencias respecto al 16550 serán brevemente señaladas. Esta última UART es más reciente y muchomás potente -aunque solo sea por unos pequeños detalles- y cada vez está más extendida, en particular enlas actuales placas base.

La línea que transmite los datos en serie está inicialmente en estado alto. Al comenzar latransferencia, se envía un bit a 0 ó bit de inicio. Tras él irán los 8 bits de datos a transmitir (en ocasionesson 7, 6 ó 5): estos bits están espaciados con un intervalo temporal fijo y preciso, ligado a la velocidad detransmisión que se esté empleando. Tras ellos podría venir o no un bit de paridad generado automáticamentepor la UART. Al final, aparecerá un bit (a veces un bit y medio ó dos bits) a 1, que son los bits de paradao bits de stop. Lo de medio bit significa que la señal correspondiente en el tiempo a un bit dura la mitad;realmente, en comunicaciones se utiliza el término baudio para hacer referencia a las velocidades, ynormalmente un baudio equivale a un bit. La presencia de bits de inicio y parada permite sincronizar laestación emisora con la receptora, haciendo que los relojes de ambas vayan a la par. A la hora de transmitirlos bytes de datos unos tras otros, existe flexibilidad en los tiempos, de ahí que este tipo de comunicacionesse consideren asíncronas. La transmisión de los 8 bits de datos de un byte realmente es síncrona, pero lascomunicaciones en serie siempre han sido consideradas asíncronas.

Para una transmisión en serie básica bastan tres hilos. Sin embargo, el software que controla el puertoserie a través de la interfaz RS-232-C podría requerir más señales de control para establecer la comunicación,al igual que para controlar un modem telefónico pueden hacer falta más líneas (de control, no telefónicas...).Bromas aparte, sobre comunicaciones en serie existe todo un mundo; acerca de este tema se han escritomuchos libros completos. Lógicamente, aquí no vamos a dar ningún curso de comunicaciones en serie. Sinembargo, los menos introducidos en la materia no deben temer: ¿qué mejor manera de aprender sobre lascomunicaciones en serie que examinar cómo funciona un chip que las soporta?. Desde luego, también sepodría partir desde el punto de vista contrario, pero como entendido en sistemas digitales, el lector puede quetenga menos problemas con este interesante enfoque.

12.9.1. - DESCRIPCIÓN DEL INTEGRADO.

D0 1 40 Vcc

D1 2 39 -RI

D2 3 38 -DCD

D3 4 37 -DSR

D4 5 36 -CTS

D5 6 35 MR

D6 7 34 -OUT1

D7 8 33 -DTR

RCLK 9 32 -RTS

SIN 10 31 -OUT2

SOUT 11 30 INTRPT

CS0 12 29 NC

CS1 13 28 A0

-CS2 14 27 A1

-BAUDOUT 15 26 A2

XTAL1 16 25 -ADS

XTAL2 17 24 CSOUT

-DOSTR 18 23 DDIS

DOSTR 19 22 DISTR

GND 20 21 -DISTR’8250

El ACE 8250 (Asynchronous Communication Element) integra enun solo chip una UART (Universal Asynchronous Receiver/Transmitter)y un BRG (Baud Rate Generator). Soporta velocidades de hasta 625000baudios con relojes de hasta 10 MHz. El BRG incorporado divide lafrecuencia base para conseguir las velocidades estándar de la RS-232-C.

SIGNIFICADO DE LAS LÍNEAS DEL 8250

DISTR: Data In Strobe. Línea de entrada que indica al 8250 que deje los datos en el bus (D0..D7),los datos dejados dependen del registro seleccionado con A0..A2. Son necesarias CS0..CS2para habilitar DISTR. En vez de DISTR se puede usar -DISTR, pero sólo una de las dos.

DOSTR: Data Out Strobe. Idéntico a DISTR pero en salida.D0..D7: Data Bits 0..7: Bus triestado bidireccional de 8 líneas para transmitir datos, información

de control y de estado entre la CPU y el 8250. El primer bit enviado/recibido es D0.A0..A2: Register Select. Líneas de entrada que indican el registro del 8250 usado en la operación.XTALx: Crystal/Clock: Conexiones para el cristal del cuarzo del BRG. XTAL1 puede actuar como

entrada de reloj externa, en cuyo caso XTAL2 debería quedar abierto.SOUT: Serial Data Output: Salida de datos en serie del 8250. Una marca es un ’1’ y un espacio

es un ’0’. SOUT está en marca cuando el transmisor está inhibido, MR está a 1, el registrode transmisión está vacío o en el modo lazo (LOOP) del 8250. No es afectado por -CTS.

-CTS: Clear To Send: Línea de entrada. El estado lógico de esta señal puede consultarse en elbit CTS del Modem Status Register (MSR) -como el bit CTS es el bit 4 del MSR se

Page 357: PCA, PS2 ,IBM y AT

357EL HARDWARE DE APOYO AL MICROPROCESADOR

referencia MSR(4)-. Un cambio en el estado de -CTS desde la última lectura del MSR provoca que se active DCTS (bit MSR(0)). Cuando -CTS está activo (a 0) el modem indica que el dato en SOUT puede ser transmitido. -CTS no afecta al modo lazo (LOOP) del 8250.

-DSR: Data Set Ready: Línea de entrada. El estado lógico de esta señal puede consultarse en MSR(5). DDSR (bit MSR(1)) indica si -DSR hacambiado desde la última lectura del MSR. Cuando -DSR está activo el modem indica que está listo para intercambiar datos con el 8250;ello depende del estado del DCE (Data Communications Equipment) local y no implica que haya comunicación con la estación remota.

-DTR: Data Terminal Ready. Línea de salida que puede activarse (poner a 0) escribiendo un 1 en MCR(0), y desactivarse escribiendo un 0 endicho bit o ante la activación del pin MR. Con -DTR activo se indica al DCE que el 8250 puede recibir datos. En algunas circunstancias,esta señal se usa como LED de ’power on’. Si está inactivo, el DCE desconecta el modem del circuito de telecomunicaciones.

-RTS: Request To Send. Línea de salida que habilita el modem. Se activa (poner a 0) escribiendo un 1 en MCR(1). Esta señal se pone en altoen respuesta a MR. -RTS indica al DCE que el 8250 tiene un dato listo para transmitir. En la modalidad half-duplex, esta señal se utilizapara controlar la dirección de la línea.

-BAUDOUT: Esta línea de salida contiene una señal de reloj 16 veces mayor que la frecuencia usada para transmitir. Equivale a la frecuencia de entradaen el oscilador dividida por el BRG. La estación receptora podría emplear esta señal conectándola a RCLK (para compartir el mismo reloj).

-OUTx: Estas dos salidas de propósito general se pueden activar (poner a 0) escribiendo un 1 en MCR(2) y MCR(3). Son desactivadas por la señalMR. En el modo lazo (LOOP o bucle), están también inactivas.

-RI: Ring Indicator. Esta línea de entrada indica si el modem ha detectado que llaman por la línea y puede consultarse en MSR(6). El bit TERI(MSR(2)) indica si esta línea ha cambiado desde la última lectura del MSR. Si las interrupciones están habilitadas (IER(3) activo) estapatilla provoca una interrupción al activarse. -RI permanece activo durante el mismo intervalo de tiempo que la zona activa del ciclo dellamada e inactivo en los intervalos de la zona inactiva (o cuando el DCE no detecta la llamada). El circuito no se corta por culpa de -DTR.

-DCD: Data Carrier Detect. Línea de entrada que indica si el modem ha detectado portadora. Se puede consultar su estado lógico en MSR(7). Elbit MSR(3) indica si esta línea ha cambiado desde la última lectura del MSR. Esta línea no tiene efecto sobre el receptor. Si lasinterrupciones están permitidas, una interrupción será generada ante el cambio de esta línea.

MR: Master Reset. Esta línea de entrada lleva el 8250 a un estado inactivo interrumpiendo su posible actividad. El MCR y las salidas ligadasal mismo son borradas. El LSR es borrado en todos sus bits salvo THRE y TEMT (que son activados). El 8250 permanece en este estadohasta volver a ser programado.

INTRPT: Interrupt Request. Línea de salida que se activa cuando se produce una interrupción de alguno de estos tipos y está permitida: Recepciónde banderín de error, dato recibido disponible, registro de retención de transmisión vacío, y estado del modem. Esta línea se desactiva conel apropiado servicio de la interrupción o ante MR.

SIN: Serial Data Input. Es la línea de entrada de datos desde el modem. En el modo lazo (LOOP o bucle) están inhibidas las entradas en SIN.CS0..2: Chip Select. Estas entradas actúan como líneas de habilitación para las señales de escritura (DOSTR, -DOSTR) y lectura (DISTR, -DISTR).CSOUT: Chip Select Out. Esta línea de salida se activa cuando el chip ha sido seleccionado con CS0..2. No comenzará transferencia de datos alguna

hasta que CSOUT se active.DDIS: Driver Disable. Esta salida está inactiva cuando la CPU lee datos del 8250. Una salida activa puede emplearse para inhibir un transceiver

externo cuando la CPU está leyendo datos.-ADS: Address Strobe. Cuando esta línea de entrada está activa se enclavan las líneas A0..A2 y CS0..2; esto puede ser necesario si los pines de

selección de registro no son estables durante la duración de la operación de lectura o escritura (modo multiplexado). Si esto no es preciso,esta señal se puede mantener inactiva (modo no-multiplexado).

RCLK: Esta línea se corresponde con la entrada de reloj para la sección receptora, equivalente a 16 veces la frecuencia empleada en la transmisióny puede proceder del BAUDOUT de la estación remota o de un reloj externo.

REGISTROS DEL 8250

El 8250 dispone de 11 registros (uno más el 16550) pero sólo 3 líneas de dirección paraseleccionarlos. Lo que permita distinguir unos de otros será, aparte de las líneas de direcciones, el sentidodel acceso (en lectura o escritura) y el valor de un bit de uno de los registros: el bit DLAB del registro LCR,que es el bit 7 de dicho registro. La notación para hacer referencia a un bit de un registro se escribe REG(i);en este ejemplo, el bit DLAB sería LCR(7). Realmente, DLAB se emplea sólo puntualmente para poderacceder y programar los registros que almacenan el divisor de velocidad; el resto del tiempo, DLAB estaráa 0 para acceder a otros registros más importantes.

A2 A1 A0 DLAB MODO NOMBRE SIGNIFICADO

0 0 0 0 R RBR Receiver Buffer Register (Registro buffer de recepción)

0 0 0 1 R/W DLL Divisor Latch LSB (Divisor de velocidad, parte baja)

0 0 0 0 W THR Transmitter Holding Register (Registro de retención de transmisión)

0 0 1 0 R/W IER Interrupt Enable Register (Registro de habilitación de interrupciones)

0 0 1 1 R/W DLM Divisor latch MSB (Divisor de velocidad, parte alta)

0 1 0 X R IIR Interrupt Identification Register (Registro de identificación de interrupciones)

0 1 0 X W FCR FIFO Control Register (Registro de control FIFO) - SOLO 16550 -

0 1 1 X R/W LCR Line Control Register (Registro de control de línea) ¡¡EL BIT 7 ES DLAB!!

1 0 0 X R/W MCR Modem Control Register (Registro de control del modem)

1 0 1 X R/W LSR Line Status Register (Registro de estado de la línea)

1 1 0 X R/W MSR Modem Status Register (Registro de estado del modem)

1 1 1 X R/W SCR Scratch Register (Registro residual)

Page 358: PCA, PS2 ,IBM y AT

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

1) LCR (Line Control Register). Controla el formato del carácter de datos.

Break StickDLAB Control Parity EPS PEN STB WLS1 WLS07 6 5 4 3 2 1 0

0 0 0 Sin paridad Word Length Select0 0 1 Paridad impar 0 0 Datos de 5 bits0 1 1 Paridad par 0 1 Datos de 6 bits1 0 1 Marca (’1’) 1 0 Datos de 7 bits1 1 1 Espacio (’0’) 1 1 Datos de 8 bits

Los bits WLS seleccionan el tamaño del dato empleado. STB indica el número de bits de stop, quepueden ser 1 (STB=0) ó 2 (STB=1), al trabajar con datos de 5 bits STB=1 implica 1.5 bits de stop. PEN(Parity Enable) permite habilitar o no la generación de bit de paridad, EPS (Even Parity Select) seleccionaparidad par si está a 1 (o impar en caso contrario). Stick Parity permite forzar el bit de paridad a un estadoconocido según el valor de EPS. Cuando Break Control es puesto a 1, la salida SOUT se pone en estadoespacio (a 0), sólo afecta a SOUT y no a la lógica de transmisión. Esto permite a la CPU alertar a un terminaldel sistema sin transmitir caracteres erróneos o extraños si se siguen estas fases: 1) cargar un carácter 0 enrespuesta a THRE, 2) activar Break Control en respuesta al próximo THRE, 3) esperar a que el transmisoresté inactivo (TEMT=1) y bajar Break Control. Durante el Break, el transmisor puede usarse como un precisotemporizador de carácter.

El bit DLAB (Divisor Latch Access Bit) puesto a 1 permite acceder a los Latches divisores DLL yDLM del BRG en lectura y escritura. Para acceder al RBR, THR y al IER debe ser puesto a 0.

2) LSR (Line Status Register). Este suele ser el primer registro consultado tras una interrupción.

0 TEMT THRE BI FE PE OE DR7 6 5 4 3 2 1 0

Data ReadyTransmitter Transmitter Overrun ErrorEmpty Holding Parity Error

Register Framing ErrorEmpty Break Interrupt

DR está activo cuando hay un carácter listo en el RBR y es puesto a 0 cuando se lee el RBR. Losbits 1 al 4 de este registro (OE, PE, FE y BI) son puestos a 0 al consultarlos -cuando se lee el LSR- y alactivarse pueden generar una interrupción de prioridad 1 si ésta interrupción está habilitada. OE se activa paraindicar que el dato en el RBR no ha sido leído por la CPU y acaba de llegar otro que lo ha sobreescrito. PEindica si hay un error de paridad. FE indica si el carácter recibido no tiene los bit de stop correctos. BI seactiva cuando la entrada de datos es mantenida en espacio (a 0) durante un tiempo superior al de transmisiónde un carácter (bit de inicio + bits de datos + bit de paridad + bit de parada).

THRE indica que el 8250 puede aceptar un nuevo carácter para la transmisión: este bit se activacuando el THR queda libre y se desactiva escribiendo un nuevo carácter en el THR. Se puede producir, siestá habilitada; la interrupción THRE (prioridad 3); INTRPT se borra leyendo el IIR. El 8250 emplea unregistro interno para ir desplazando los bit y mandarles en serie (el Transmitter Shift Register), dicho registrose carga desde el THR. Cuando ambos registros (THR y el Transmitter Shift) están vacíos, TEMT se activa;volverá a desactivarse cuando se deje otro dato en el THR hasta que el último bit salga por SOUT.

3) MCR (Modem Control Register). Controla el interface con el modem.

0 0 0 LOOP OUT2 OUT1 RTS DTR7 6 5 4 3 2 1 0

Data Terminal ReadyRequest To Send

Page 359: PCA, PS2 ,IBM y AT

359EL HARDWARE DE APOYO AL MICROPROCESADOR

Las líneas de salida -DTR, -RTS, -OUT1 y -OUT2 están directamente controladas por estos bits;como se activan a nivel bajo, son puestas a 0 escribiendo un 1 en estos bits y viceversa. Estas líneas sirvenpara establecer diversos protocolos de comunicaciones.

El bit LOOP introduce el 8250 en un modo lazo (o bucle) de autodiagnóstico. Con LOOP activo,SOUT pasa a estado de marca (a 1) y la entrada SIN es desconectada. Los registros de desplazamientoempleados en la transmisión y la recepción son conectados entre sí. Las cuatro entradas de control del modem(-CTS, -DSR, DC y -RI) son desconectadas y en su lugar son internamente conectadas las cuatro salidas decontrol del modem (-DTR, -RTS, -OUT1 y -OUT2) cuyos pines son puestos en estado inactivo (alto). En estamodalidad de operación (modo lazo o bucle), los datos transmitidos son inmediatamente recibidos, lo quepermite comprobar el correcto funcionamiento del integrado. Las interrupciones son completamente operativasen este modo, pero la fuente de estas interrupciones son ahora los 4 bits bajos del MCR en lugar de las cuatroentradas de control. Estas interrupciones están aún controladas por el IER.

4) MSR (Modem Status Register).

DCD RI DSR CTS DDCD TERI DDSR DCTS7 6 5 4 3 2 1 0

Delta Trailing Delta DeltaData Data Clear Data Edge Data ClearCarrier Ring Set To Carrier of Ring Set ToDetect Indicator Ready Send Detect Indicator Ready Send

Además de la información de estado del modem, los 4 bits bajos (DDCD, TERI, DDSR, DCTS)indican si la línea correspondiente, en los 4 bits superiores, ha cambiado de estado desde la última lecturadel MSR; en el caso de TERI sólo indica transiciones bajo- alto en -RI (y no las de sentido contrario). Lalínea CTS del modem indica si está listo para recibir datos del 8250 a través de SOUT (en el modo lazo estebit equivale al bit RTS del MCR). La línea DSR del modem indica que está listo para dar datos al 8250 (enel modo lazo -o LOOP- equivale al bit DTR del MCR). RI y DCD indican el estado de ambas líneas (en elmodo lazo se corresponden con OUT1 y OUT2 respectivamente). Al leer el MSR, se borran los 4 bitsinferiores (que en una lectura posterior estarían a 0) pero no los bits de estado (los 4 más significativos).

Los bits de estado (DCD, RI, DSR y CTS) reflejan siempre la situación de los pines físicosrespectivos (estado del modem). Si DDCD, TERI, DDSR ó DCTS están a 1 y se produce un cambio deestado durante la lectura, dicho cambio no será reflejado en el MSR; pero si están a 0 el cambio seráreflejado después de la lectura. Tanto en el LSR como en el MSR, la asignación de bits de estado estáinhibida durante la lectura del registro: si se produce un cambio de estado durante la lectura, el bitcorrespondiente será activado después de la misma; pero si el bit ya estaba activado y la misma condiciónse produce, el bit será borrado tras la lectura en lugar de volver a ser activado.

5) y 6) BRSR (Baud Rate Select Register). Son los registros DLL (parte baja) y DLM (parte alta).

Estos dos registros de 8 bits constituyen un valor de 16 bits que será el divisor que se aplicará a lafrecuencia base para seleccionar la velocidad a emplear. Dicha frecuencia base (por ejemplo, 1.8432 MHz)será dividida por 16 veces el valor almacenado aquí. Por ejemplo, para obtener 2400 baudios:

1843200= 48 - DLL=48, DLM=0

16 * 2400

7) RBR (Receiver Buffer Register).

El circuito receptor del 8250 es programable para 5, 6, 7 u 8 bits de datos. En el caso de emplearmenos de 8, los bits superiores de este registro quedan a 0. Los datos entran en serie por SIN (comenzandopor el bit D0) en un registro de desplazamiento gobernado por el reloj de RCLK, sincronizado con el bit de

Page 360: PCA, PS2 ,IBM y AT

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

inicio. Cuando un carácter completa el registro de desplazamiento de recepción, sus bits son volcados al RBRy el bit DR del LSR es activado para indicar a la CPU que puede leer el RBR. El diseño del 8250 permitela recepción continua de datos sin pérdidas: el RBR almacena siempre el último carácter recibido dandotiempo suficiente a la CPU para leerlo mientras simultáneamente está cargando el registro de desplazamientocon el siguiente; si la CPU tarda demasiado un nuevo dato podría aparecer en el RBR antes de haber leídoel anterior (condición de overrun, bit OE del LSR).

8) THR (Transmitter Holding Register).

El registro de retención de transmisión almacena el siguiente carácter que va a ser transmitido en seriemientras el registro de desplazamiento de transmisión está enviando el carácter actual. Cuando el registro dedesplazamiento se vacíe, será cargado desde el THR para transmitir el nuevo carácter. Al quedar vacío THR,el bit THRE del LSR se activa. Cuando estén vacíos tanto el THR como el registro de desplazamiento detransmisión, el bit TEMT del LSR se activa.

9) SCR (Scratchpad Register).

Este registro no es empleado por el 8250, y de hecho no existía en las primeras versiones delintegrado. Puede ser empleado por el programador como una celdilla de memoria.

10) IIR (Interrupt Identification Register).

Existen 4 niveles de prioridad en las interrupciones generables por el 8250, por este orden:

1) Estado de la línea de recepción.2) Dato recibido disponible.3) Registro de retención de transmisión vacío.4) Estado del modem.

La información que indica que hay una interrupción pendiente y el tipo de la misma es almacenadaen el IIR. El IIR indica la interrupción de mayor prioridad pendiente. No serán reconocidas otrasinterrupciones hasta que la CPU envíe la señal de reconocimiento apropiada. En el registro IIR, el bit 0 indicasi hay una interrupción pendiente (bit 0=0) o si no la hay (bit 0=1), esto permite tratar las interrupciones enmodo polled consultando este bit. Los bits 1 y 2 indican el tipo de interrupción. Los restantes están a 0 enel 8250, pero el 16550 utiliza alguno más.

DCD RI DSR CTS DDCD7 6 5 4 3 2 1 0

1 - Interrupción pendiente1 1 - Colas FIFO activadas en 16550 X X - Identificación de la Interrupción0 0 - Colas FIFO no activadas A 1 en el 16550 si pendiente la interrupción TIMEOUT

IDENTIFICACIÓN DE LA INTERRUPCIÓN ACTIVACIÓN / RECONOCIMIENTO (RESET) DE LA INTERRUPCIÓN

Bit 2 Bit 1 Bit 0 Prioridad Flag Fuente Reconocimiento

X X 1 Ninguno Ninguna

1 1 0 Primera Línea de estado OE, PE, Leer LSRdel receptor FE ó BI

1 0 0 Segunda Recibido dato Recibido dato Leer RBRdisponible disponible

0 1 0 Tercera THRE THRE Leer IIR si es la fuente deinterrupción, o escribir THR

0 0 0 Cuarta Estado del modem -CTS, -DSR Leer MSR-RI, -DCD

Page 361: PCA, PS2 ,IBM y AT

361EL HARDWARE DE APOYO AL MICROPROCESADOR

11) IER (Interrupt Enable Register).

Este registro de escritura se utiliza para seleccionar qué interrupciones activan INTRPT y, porconsiguiente, van a ser solicitadas a la CPU. Deshabilitar el sistema de interrupciones inhibe el IIR ydesactiva la salida INTRPT.

0 0 0 0 IER(3) IER(2) IER(1) IER(0)7 6 5 4 3 2 1 0

IER0: A 1 si habilitar interrupción de dato disponible.IER1: A 1 si habilitar interrupción de registro de retención de transmisión vacío.IER2: A 1 si habilitar interrupción de error de recepción (bits 1 al 4 del LSR).IER3: A 1 si habilitar interrupción ante el cambio del MSR (Registro de estado del modem).

El 16550 genera también una interrupción de TIMEOUT (prioridad 1) si hay datos en la cola FIFOy no son leídos dentro del tiempo que dura la recepción de 4 bytes o si no se reciben datos durante el tiempoque tomaría recibir 4 bytes.

12) FCR (FIFO Control Register). Sólo disponible en el 16550, no en el 8250.

7 6 5 4 3 2 1 0

1 - Habilita elTamaño cola A 1 si cambiar los borrado de colas

0 0 - 1 byte pines RXRDY y TXRDY FIFO XMIT y RCVR.0 1 - 4 bytes del modo 0 al modo 11 0 - 8 bytes 1 - Borrar cola RCVR1 1 - 14 bytes 1 - Borrar cola XMIT

El bit 0 debe estar a 1 para escribir los bits 1 ó 2. Cuando el bit 1 ó el 2 son activados, la colaafectada es borrada y el bit es devuelto a 0. Los registros de desplazamiento de la transmisión y la recepción,en cada caso, no resultan afectados.

LA TRANSMISIÓN Y LA RECEPCIÓN EN EL 8250

La sección de transmisión del 8250 consiste en el Registro de Retención de transmisión (THR), elRegistro de Desplazamiento de la Transmisión (TSR) y en la lógica de control asociada. Dos bits en el LSRindican si está vacío el THR (bit THRE) o el TSR (bit TEMT). El carácter de 5-8 bits a ser transmitido esescrito en el THR; la CPU debería realizar esta operación sólo si THRE está activo: este bit es activadocuando el carácter es copiado del THR al TSR durante la transmisión del bit de inicio.

Cuando el transmisor está inactivo, tanto THRE como TEMT están activos. El primer carácter escritoprovoca que THRE baje; tras completarse la transferencia vuelve a subir aunque TEMT permanecerá bajomientras dure la transferencia en serie del carácter a través de TSR. Si un segundo carácter es escrito en THR,THRE vuelve a bajar y permanecerá bajo hasta que el TSR termine la transmisión, porque no es posiblevolcar el contenido de THR en TSR hasta que este último no acabe con el carácter que estaba transmitiendo.Cuando el último carácter ha sido transmitido fuera del TSR, TEMT vuelve a activarse y THRE también lohará tras un cierto tiempo (el que tarda en escribirse THR en TSR).

En la recepción, los datos en serie asíncronos entran por la patilla SIN. El estado inactivo de la línease considera el ’1’ lógico. Un circuito de detección de bit de inicio está continuamente buscando unatransición alto bajo que interrumpa el estado inactivo. Cuando la detecta, se resetea un contador internoy cuenta 7½ pulsos de reloj (tener en cuenta que la frecuencia base es dividida por 16), posicionándose enel centro del bit de inicio. El bit de inicio se considera válido si SIN continúa aún bajo en ese momento. Lavalidación del bit de inicio evita que un ruido espúreo en la línea sea confundido con un nuevo carácter.

Page 362: PCA, PS2 ,IBM y AT

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

El LCR tiene toda la información necesaria para la recepción: tamaño del carácter (5-8 bits), númerode bits de stop, si hay paridad o no... la información de estado que se genere será depositada en el LSR.Cuando un carácter es transmitido desde el Registro de Desplazamiento de la Recepción (RSR) al RegistroBuffer de Recepción (RBR), el bit DR del LSR se activa. La CPU lee entonces el RBR, lo que hace bajarde nuevo DR. Si el carácter no es leído antes de que el siguiente carácter que se está formando pase del RSRal RBR, el bit OE (overrun) del LSR se activa. También se puede activar PE en el LSR si hay un error deparidad. Finalmente, la circuitería que chequea la validez del bit de stop podría activar el bit FE del LSR encaso de error.

El centro del bit de inicio se define como 7½ pulsos de reloj; si los datos que entran por SINconstituyen una onda cuadrada simétrica, el centro de las celdas que contienen los bits se desviará a lo sumoun ±3.125% del centro real, lo que deja un margen de error del 46.875%; el bit de inicio puede comenzar,como mucho, 1 ciclo de reloj (de los 16) antes de ser detectado.

EL B.R.G. (BAUD RATE GENERATOR)

El BRG genera las señales de reloj para el funcionamiento de la UART, permitiendo los ratios detransferencia del estándar ANSI/CCITT. Se puede conectar un cristal a XTAL1 y XTAL2 ó una señal de reloja XTAL1. La salida -BAUDOUT puede excitar la línea XTAL1 de otro 8250.

La velocidad es determinada por los registros DLL y DLM almacenando un valor divisor de lafrecuencia del reloj conectado al 8250. El resultado debe ser 16 veces mayor que la frecuencia en baudiosdeseada, ya que el 8250 utiliza 16 pulsos de reloj para cada bit. El siguiente cuadro resume los valores quehay que asignar al divisor para lograr las frecuencias más usuales con los cristales más comunes.

Cristal de 1.8432 MHz Cristal de 2.4576 MHz Cristal de 3.072 MHz

Baudios Divisor usado % error Divisor usado % error Divisor usado % errorfinales para 16xReloj si lo hay para 16xReloj si lo hay para 16xReloj si lo hay

50 2304 3072 384075 1536 2048 2560110 1047 0.026 1396 0.026 1745 0.026134.5 857 0.058 1142 0.0007 1428 0.034150 768 1024 1280300 384 512 640600 192 256 3201200 96 128 1601800 64 85 0.392 107 0.3152000 58 0.69 77 0.260 962400 48 64 803600 32 43 0.775 53 0.6284800 24 32 407200 16 21 1.587 27 1.239600 12 16 2019200 6 8 1038400 3 4 556000 2 2.86 - -

RESET DEL 8250

Tras dar corriente al 8250 hay que tenerlo unos 500 ns con MR alto para resetearlo. Un nivel altoen MR provoca:

1) Se inicializan los contadores internos de transmisión y recepción.2) Se limpia el LSR salvo en sus bits TEMT y THRE (que son puestos a 1).

MCR, todas las líneas discretas, elementos de memoria y demás son puestos a 0.DLL y DLM, RBR y THR no son afectados.

Tras el reset (MR llevado a estado bajo) el 8250 permanece en estado inactivo hasta ser programado.Un reset hardware activa THRE y TEMT: cuando las interrupciones sean habilitadas, THRE provocará una.

Page 363: PCA, PS2 ,IBM y AT

363EL HARDWARE DE APOYO AL MICROPROCESADOR

Por software se puede forzar al 8250 a retornar a un estado totalmente conocido. Dicho reset consisteen escribir el LCR, DLL y DLM, así como MCR. LSR y RBR deberían ser leídos antes de habilitar lasinterrupciones para borrar cualquier información residual (datos o estado) de las operaciones anteriores.

REGISTRO / SEÑAL CONTROL DEL RESET EFECTO DEL RESET EN EL 8250

IER MR Todos los bits a 0 (4..7 ya lo estaban)IIR MR Bit 0 a 1, Bits 1 y 2 a 0, demás siempre a 0LCR MR Todos los bits a 0MCR MR Todos los bits a 0LSR MR Todos los bits a 0, salvo el 5 y el 6 (a 1)MSR MR Bits 0..3 a 0, bits 4..7 señal de entradaSOUT MR En altoINTRPT (RCVR error) Leer LSR / MR En bajoINTRPT (RCVR dato listo) Leer RBR / MR En bajoINTRPT (THRE) Leer IIR / Escribir THR / MR En bajoINTRPT (Cambios en el estado del modem) Leer MSR / MR En bajo-OUT2 MR En alto-RTS MR En alto-DTR MR En alto-OUT1 MR En alto

PROGRAMACIÓN DEL 8250

El 8250 se programa a través de los registros de control LCR, IER, DLL, DLM y MCR. Aunque losregistros de control pueden ser escritos en cualquier orden, IER debe ser escrito al final porque controla lahabilitación de las interrupciones. Una vez que el 8250 ha sido programado, los registros pueden seractualizados en cualquier momento en que el 8250 no se encuentre enviando o recibiendo datos.

12.9.2. - EL 8250 EN EL ORDENADOR.

Los ordenadores compatibles pueden tener conectados, de manera normal, hasta 4 puertos serie,nombrados COM1-COM4. En el área de datos de la BIOS (segmento 40h) y justo al principio de la misma,hay 4 palabras con la dirección de memoria base de los puertos serie. A esta dirección de memoria base habráque sumar el desplazamiento relativo del número de registro a ser accedido.

El principal problema reside en que sólo están previstas 2 interrupciones para los puertos serie. Elloimplica que generalmente sólo 2 de los puertos podrán emplear interrupciones a un tiempo, debido a laarquitectura del bus ISA. Generalmente COM1 y COM3 compartirán la IRQ4 (INT 0Ch) y COM2/COM4la IRQ3 (INT 0Bh). Estas asignaciones pueden ser cambiadas por el usuario actuando sobre los switches deconfiguración de las tarjetas (que en ocasiones permiten incluso elegir la IRQ5). Por tanto, no está de mástener cuidado en los programas y permitir un cierto grado de configuración en estas cuestiones.

OFFSET DLAB MODO NOMBRE SIGNIFICADO

0 0 R RBR Receiver Buffer Register (Registro buffer de recepción)

0 1 R/W DLL Divisor Latch LSB (Divisor de velocidad, parte baja)

0 0 W THR Transmitter Holding Register (Registro de retención de transmisión)

1 0 R/W IER Interrupt Enable Register (Registro de habilitación de interrupciones)

1 1 R/W DLM Divisor latch MSB (Divisor de velocidad, parte alta)

2 X R IIR Interrupt Identification Register (Registro de identificación de interrupciones)

2 X W FCR FIFO Control Register (Registro de control FIFO) - SOLO 16550 -

3 X R/W LCR Line Control Register (Registro de control de línea) ¡¡EL BIT 7 ES DLAB!!

4 X R/W MCR Modem Control Register (Registro de control del modem)

5 X R/W LSR Line Status Register (Registro de estado de la línea)

6 X R/W MSR Modem Status Register (Registro de estado del modem)

7 X R/W SCR Scratch Register (Registro residual)

El cuadro superior muestra los desplazamientos (offsets) que hay que sumar a la dirección E/S basedel puerto serie para acceder a sus registros. COM1 suele estar en 3F8h, COM2 en 2F8h, COM3 en 3E8hy COM4 en 2E8h. Sin embargo, es mejor acceder a las variables de la BIOS para obtener la dirección.

Page 364: PCA, PS2 ,IBM y AT

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

La INT 14h de la BIOS se encarga de controlar el puerto serie. El trabajo del DOS a través de losdispositivos COM1: (conocido también como AUX:) al COM4: se realiza también apoyándose en estainterrupción. El comando MODE del sistema permite inicializar el puerto serie a alto nivel. Sin embargo,tanto el DOS como la BIOS no permiten exceder los 9600 baudios, velocidad excesivamente baja para latransmisión de datos entre dos ordenadores cercanos o el trabajo con un modem.

El cristal que gobierna el 8250 oscila a 1.8432 MHz. Nosotros debemos considerar esta frecuenciadividida por 16 de cara a calcular el valor para el divisor. Por tanto, la velocidad máxima que puede alcanzar

Baudios Divisor a emplear en el PCmás

comunes Divisor DLM DLL

50 2304 9 0110 1047 4 23150 768 3 0300 384 1 1281200 96 0 962400 48 0 484800 24 0 249600 12 0 1214400 8 0 819200 6 0 628800 4 0 438400 3 0 357600 2 0 2115200 1 0 1

el puerto serie de los PC es de 1843200/16 = 115200 baudios.Con datos de 8 bit se pueden empaquetar los bytes en 10baudios (1 bit de inicio, 8 de datos, 1 de stop), lo que permitealcanzar 11520 bytes/seg (11.25 Kb/seg). Para distancias depocos metros (no decenas ni centenas) no habrá problemas,incluso para distancias algo mayores si los cables se diseñan concuidado. La programación del puerto serie en el PC a nivel dehardware es necesaria a menudo por dos razones de muchopeso: poder utilizar interrupciones y emplear velocidadessuperiores a 9600 baudios. Por supuesto, en estas transferenciaslos paquetes deberían llevar algún control de errores, aunque noprecisamente basado en la paridad.

Nota: El bit OUT2 del MCR controla en los PC la salida de la línea INTRPT. Esto significa que si dicho bit, por defectoinicializado a 0, es puesto a 1, las interrupciones del puerto serie quedan inhibidas. El bit OUT1, por el contrario,debe estar a 1 por motivos no muy claros. También se podría inhibir la INTRPT a través del 8259, por lo que estedato no es muy importante, con la excepción de evitar que una involuntaria e incorrecta asignación de OUT1 y OUT2inhiba las interrupciones. La ventaja de inhibir las interrupciones en el 8250 radica en la posibilidad de utilizarplenamente todas sus funciones incluso en el modo de no interrupciones: el olvido del diseñador de incluir estacaracterística obligó a IBM a utilizar para este fin OUT2. Realmente, el 8250 está concebido para ser utilizado pormedio de interrupciones, y hay quien duda incluso de la veracidad de la afirmación del fabricante acerca del doublebuffering (buffers duplicados) que son muy aconsejables al trabajar sin interrupciones.

12.9.3. - EJEMPLO: AUTODIAGNÓSTICO DEL 8250.

El siguiente programa de ejemplo coloca el 8250 en modo lazo (LOOP) y seguidamente comienzaa transmitir datos de 8 bits (desde 0 hasta 255) comprobando que le llegan los mismos datos que envía y sinque se produzcan errores. Se permite elegir el puerto deseado así como la velocidad de transmisión.

/********************************************************************** ** 8250T.C 1.0 - UTILIDAD DE AUTODIAGNOSTICO DEL 8250 EN TURBO C ** ** (c) 1993 Ciriaco García de Celis. ** **********************************************************************/

#include <dos.h>#include <conio.h>

#define LCR (base+3) /* registro de control de línea */#define IER (base+1) /* registro de activación de interrupciones */#define DLL (base+0) /* parte baja del divisor */#define DLM (base+1) /* parte alta del divisor */#define MCR (base+4) /* registro de control del modem */#define LSR (base+5) /* registro de estado de línea */#define RBR (base+0) /* registro buffer de recepción */#define THR (base+0) /* registro de retención de transmisión */

#define DR 1 /* bit dato disponible del LSR */#define OE 2 /* bit de error de overrun del LSR */#define PE 4 /* bit de error de paridad del LSR */#define FE 8 /* bit de error en bits de stop del LSR */#define BI 0x10 /* bit de error de break en el LSR */#define THRE 0x20 /* bit de THR vacío */

void error()printf ("\r ¡¡Fallo del puerto serie!!\n");exit (2);

void main()unsigned com, base, divisor, dato, entrada, lsr;

printf("\n8250 Test v1.0 - (c) 1993 Ciriaco García de Celis.\n");

printf("- Elige COM (1, 2, ...): "); scanf ("%d", &com);base=peek(0x40, (com-1)*2);

if (base==0) printf("\n ¡El COM elegido no existe para la BIOS!.\n");exit (1);

printf("- Elige divisor (1-65535): ");scanf ("%d", &divisor); if (!divisor) divisor=1;

printf("\nComprobando 8250 en %03Xh a %lu baudios.\nEspera...",base, 1843200L/divisor/16);

outportb (LCR, 0x83); /* DLAB=1, 8 bits, 1 stop, sin paridad */outportb (IER, 0);outportb (DLL, divisor % 256);outportb (DLM, divisor >> 8);outportb (MCR, 8+16); /* modo LOOP */outportb (LCR, 0x03); /* DLAB=0, 8 bits, 1 stop, sin paridad */

for (dato=0; (dato<0x100) && !kbhit(); dato++)

do /* esperar por THR vacío */lsr=inportb(LSR);if (lsr & (OE|PE|FE|BI)) error();

while (!(lsr & THRE));

outportb (THR, dato); /* enviar carácter */

do /* esperar por RBR lleno */lsr=inportb(LSR);if (lsr & (OE|PE|FE|BI)) error();

while (!(lsr & DR));

entrada=inportb (RBR); /* recibir carácter */

if (dato!=entrada) error();printf ("\rEnviado y recibido byte %d",dato);

if (!kbhit())printf("\rAutodiagnóstico del 8250 en COM%d superado.\n", com);

else getch(); printf("\rTecla pulsada - prueba abortada.\n");

Page 365: PCA, PS2 ,IBM y AT

365EL HARDWARE DE APOYO AL MICROPROCESADOR

12.10. - EL PUERTO DE LA IMPRESORA.

La impresora se controla desde el DOS referenciándola como dispositivo LPT1 (PRN) ó LPT2. LaBIOS utiliza la INT 17h para los servicios de impresora. En ambos casos, el funcionamiento es realmentetrivial y la dificultad estriba en el modelo de impresora que se trate (IBM, Epson, HP-III, PostScript, etc.)de cara al lenguaje que soporta. Eso no lo trataremos aquí, ya que todas las impresoras vienen acompañadasde un manual técnico de programación (o en su defecto se puede adquirir opcionalmente). Lo que veremosa continuación son los registros a bajo nivel del puerto paralelo, así como pistas para una utilización algo másallá de la impresora: la comunicación entre ordenadores.

12.10.1. - LOS REGISTROS DEL PUERTO PARALELO.

La dirección base del puerto paralelo en los ordenadores compatibles depende del tipo de adaptadorque incorporen. Las primeras máquinas traían un puerto paralelo en el adaptador de vídeo monocromo, cuyadirección base es 3BCh. Sin embargo, otros adaptadores utilizan la dirección base 378h para LPT1 y 278hpara LPT2. Por fortuna, la BIOS tiene en el área de datos una tabla con las direcciones base de los 4 posiblespuertos paralelos. Dicha tabla comienza en 40h:8 y consta de 1 palabra por puerto (a 0 si ese puerto noexiste). La asignación que realizan diversas BIOS puede ser un tanto discutible, pero si el usuario no ve salirlos datos por la impresora que desea, siempre puede cambiar los cables o configurar su programa...

Los registros de que consta el puerto paralelo son 3: el primero es el registro de datos, de 8 bits,ubicado en la dirección base (3BCh, 378h, 278h, etc.). Este registro es de sólo escritura, para enviar loscaracteres a la impresora. El siguiente registro, de sólo lectura, es el registro de estado, inmediatamente acontinuación del anterior (3BDh, 379h, 279h). Finalmente, tras ellos hay un registro de sólo escritura, elregistro de control (en 3BEh, 37Ah, 27Ah). Aunque en los tres casos he indicado la dirección, hay que teneren cuenta que lo correcto es consultar la variable de la BIOS y tomarla como punto de partida.

Los registros de estado y control están asociados a unas líneas físicas del puerto paralelo estándar,y poseen un significado concreto que resumimos a continuación. En el valor pin se hace referencia al pin delpuerto paralelo del ordenador y al correspondiente en la impresora (ordenador/impresora). Las líneas o pinesque no aparecen aquí son las de datos (líneas 2 a la 9, conectadas también con las líneas 2 a la 9 del ladode la impresora; las restantes están a masa).

Registro de estado:- Bits 0-2: no utilizados.- Bit 3: pin 15/32 (-ERROR). A 0 si hay un error gordo (a revisar los cables).- Bit 4: pin 13/13 (SLCT). A 1 si la impresora está ON LINE.- Bit 5: pin 12/12 (PE). A 1 si la impresora no tiene papel (PAPER ERROR).- Bit 6: pin 10/10 (-ACK). A 0 si la impresora confirma la recepción del carácter.- Bit 7: pin 11/11 (-BUSY). A 0 si la impresora está ocupada.

Registro de control:- Bit 0: pin 1/1 (-STROBE). A 0 si hay un carácter en el registro de datos.- Bit 1: pin 14/14 (-AUTO FEED). A 1 si la impresora debe saltar línea tras cada código 13 (CR).- Bit 2: pin 16/31 (-INIT). A 0 para resetear la impresora.- Bit 3: pin 17/36 (SLCT IN). A 1 para seleccionar la impresora (0 para OFF-LINE).- Bit 4: no conectado al puerto de impresora. A 1 activa la interrupción de la impresora.- Bits 5-7: no utilizados.

La posibilidad de emplear interrupciones es realmente interesante: cuando la señal -ACK se pone anivel 0 (esto es, se activa) viene una IRQ7 ó una IRQ5 (según cómo esté configurada la tarjeta). De todosmodos, habrá que mandar primero un carácter por el método tradicional para iniciar la transmisión. La BIOS,sin embargo, no utiliza la interrupción de la impresora.

12.10.2. - ENVÍO DE CARACTERES.

Ante todo dejar claro que cuando digamos 0 ó 1 nos referimos al valor del bit en el registro del PC,olvidando ya cuestiones como el nivel al que son activas las señales, para evitar lios: los nombres de las

Page 366: PCA, PS2 ,IBM y AT

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

señales les tomaremos como referencia, sin considerar su polaridad. Para enviar un carácter, primero se lecoloca en el registro de datos. A continuación se pone a 0 en el registro de control el bit de STROBE. Estebit debe estar muy poco tiempo activo, para evitar que la impresora lea dos veces el mismo carácter (delorden de un microsegundo). Como la impresora no tiene una capacidad de aguante ilimitada, se puededefender poniendo el bit de BUSY en el registro de estado a 0 para poder leer con tranquilidad el STROBEque le llega. Cuando lo haya leído, pondrá un 0 en ACK para indicar que ya ha recibido el carácter.

Este es el esquema básico del envío de caracteres. Sin embargo, hay que tener en cuenta que laimpresora puede devolver ciertas condiciones de error, tanto leves (falta de papel) como más graves, comoel caso de ERROR. También el ordenador puede provocar ciertos efectos en la impresora, a través del registrode control, como vimos anteriormente. Quizá el más curioso es el del AUTO FEED: ya se podían haberpuesto de acuerdo el primer día, resulta triste que además de perder horas configurando impresoras yprogramas, hasta el propio puerto pueda meter las narices en el control del salto de línea...

12.10.3. - CABLE NULL-MODEM PARA CONECTAR DOS ORDENADORES.

Anteriormente hemos visto una descripción de patillas del puerto paralelo suficiente para quecualquiera se pueda construir su propio cable centronics. De todas formas, estos cables afortunadamente sevenden ya construidos por un precio poco aceptable. Los que no se venden, aunque sí acompañan a ciertasaplicaciones software e incluso hardware (como disqueteras externas vía puerto de impresora) permiten unacomunicación bidireccional. El truco consiste en utilizar las líneas del registro de estado para recibir datos,aunque esto limita la transferencia a 5 bits (realmente 4, más otro para el protocolo de transferencia).

Se toman dos conectores centronic 25-pin machos. Se unen los pins de la siguiente forma:

2 153 134 125 106 1110 511 612 413 315 218 18

El motivo de emplear esta asignación y no otra se debe a que es la ya utilizada por ciertasaplicaciones comerciales, como LAPLINK. Es por razones de compatibilidad, para que no pase como conlos saltos de línea. La línea común (18) es masa, aunque valdría cualquier patilla entre la 18 y la 25; si seemplea un cable de 10 hilos más malla, esta última es la más adecuada para hacer de masa.

Con este cable, para enviar datos se utilizan las líneas D0 a D4 del registro de datos y para recibirloslas 5 líneas útiles del registro de estado. Como D0-D1-D2-D3-D4 están conectados en este mismo orden aERROR-SLCT-PE-ACK-BUSY, lo ideal es utilizar D0-D3 para transmitir datos y ERROR-SLCT-PE-ACKpara recibirlos. Las señales BUSY y D4 sirven para establecer el protocolo de transmisión. La transferenciapuede ser bidireccional y además de forma simultánea. En realidad, cuando se mande un dato y el ordenadorremoto indique con BUSY que ya lo tiene (a través de su línea D4), de paso nos puede haber reenviado eldato en D0-D3 para que veamos si es correcto: un control de errores bastante fiable y rápido. Sin embargo,se podría aprovechar quizá para enviar otro medio byte en sentido contrario en el caso de que las dosmáquinas se estén pasando información simultáneamente la una a la otra; el control de errores ya se haríade otra manera, a nivel de bloques con checksum, etc. Conviene aprovechar y mandar otros 4 bits de datoscada vez que se envía un reconocimiento (al informar al receptor de que ya se ha recibido su señal de "datorecibido"), lo que permite transferir un byte completo en cada ciclo del protocolo de transferencia. Ah, nohay que olvidar la polaridad de las líneas: al poner un 0 en D4 aparece un 1 en el -BUSY del otro extremo...

Si el cable no rebasa los 3 metros o poco más la transmisión será fiable, y además bastante rápida:4 bits en paralelo, a la velocidad que pueda alcanzar la CPU del ordenador más lento. No emplear elensamblador sería un acto imperdonable.

Page 367: PCA, PS2 ,IBM y AT

367EL HARDWARE DE APOYO AL MICROPROCESADOR

12.11. - EL RATÓN.

El ratón se controla normalmente a través de llamadas a la INT 33h. Existen toda suerte de funcionespara controlar su posición, el estado de los botones, el puntero que se visualiza... todas ellas son bastanteintuitivas y aptas para un programador en lenguajes de alto nivel. Aquí estudiaremos, sin embargo, elfuncionamiento a bajo nivel del ratón. En concreto, del ratón de Microsoft, el más extendido y con el queson compatibles casi todos los demás (aunque sea accionando el correspondiente conmutador).

La mayoría de los ratones se conectan vía puerto serie a 1200 baudios, 7 bits y sin paridad. Paradetectar la presencia del ratón, hay que poner la línea DTR del puerto serie a 1. Al cabo de un rato, el ratóndevuelve el código ASCII de la letra M (¿será por lo de Mouse o por Microsoft?). Los controladores deMicrosoft son un poco estrictos en esta comprobación, y si el ratón no responde en unos márgenes de tiempomuy concretos consideran que no existe, de ahí que en ocasiones haya que emplear otro controlador un pocomás flexible.

Llegados a este punto, el funcionamiento se establece a partir de interrupciones de puerto serie. Setrasmiten 3 bytes cada vez que hay un envío: en ellos se indica cuánto se ha movido el ratón en los ejes Xe Y desde la última vez, así como el estado de los botones. La unidad de medida, cómo no, son los Mickeys,que según la resolución del aparato serán 1/200 ó 1/400 pulgadas.

Los desplazamientos se toman en complemento a dos; como hay 8 bits por cada eje, el movimientopuede oscilar en el rango +128 a -127. Hay además un bit por cada botón. De los 7 bits recibidos en cadainterrupción, el más significativo (bit 6) está a 1 en el primer envío y a 0 en los restantes, con objeto deevitar malas interpretaciones de la secuencia si se pierde alguna interrupción por cualquier motivo. El formatoempleado para codificar la información es el siguiente:

1 L R Y7 Y6 X7 X6 0 X5 X4 X3 X2 X1 X0 0 Y5 Y4 Y3 Y2 Y1 Y0

El otro gran estándar de ratón, el Mouse Systems, permite trabajar hasta con tres botones. Estosratones envían (cuando están en modo Mouse) 5 bytes por cada evento. En el primero hay información sobreel estado de los botones; los 4 siguientes parecen contener el desplazamiento relativo en los ejes X e Y. Elfuncionamiento es, por tanto, similar, y al parecer quizá todavía con 7 bits. Curiosamente, al conmutar elselector de modo (Microsoft-Mouse) aparece una secuencia de bytes un tanto especial, distinta según elsentido de la conmutación, para ayudar al controlador de ratón a detectar el paso al nuevo protocolo conobjeto de poder adaptarse al mismo.

Page 368: PCA, PS2 ,IBM y AT

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

12.12. - EL RELOJ DE TIEMPO REAL DEL AT: MOTOROLA MC146818.

12.12.1. - DESCRIPCIÓN DEL INTEGRADO.

El MC146818 incorpora un completo reloj con alarma, calendario, interrupción periódicaprogramable, generador de onda cuadrada y 64 bytes libres de RAM estática de bajo consumo. Los primeros10 bytes de esta RAM son empleados para gestionar la fecha y la hora y los 4 siguientes son registros (A,B, C y D); los 50 restantes quedan a disposición del usuario.

NC 1 24 Vcc

OSC 1 2 23 SQW

OSC 2 3 22 PS

AD 0 4 21 CKOUT

AD 1 5 20 CKFS

AD 2 6 19 -IRQ

AD 3 7 18 -RESET

AD 4 8 17 DS

AD 5 9 16 NC

AD 6 10 15 R/-W

AD 7 11 14 AS

GND 12 13 -CE’146818

La línea OSC1 (de entrada) puede conectarse a señalescuadradas de 4.194304 Mhz, 1.048576 Mhz y 32768 Hz. Lafrecuencia de esta base de tiempos, como se verá, ha deindicarse en el registro A (bits DV0 a DV2). El chip provee unaútil salida de reloj en CKOUT dependiente del nivel de laentrada CKFS, según la siguiente tabla:

Señal en OSC1 Nivel de CKFS Señal en CKOUT

4,194304 Mhz 1 4,194304 Mhz4,194304 Mhz 0 1,048576 Mhz1,048576 Mhz 1 1,048576 Mhz1,048576 Mhz 0 262,144 KHz32,768 Khz 1 32,768 Khz32,768 Khz 0 8,192 Khz

La salida SQW genera una onda cuadrada, cuyafrecuencia es programable (útil para alarmas). La línea -IRQ se

encarga de solicitar las interrupciones periódicas si están habilitadas. La línea de entrada -RESET reinicializael integrado asignando valores por defecto a ciertos bits de los registros By C, aunque no afecta a la fecha/hora ni a la memoria. La entrada PS debemantenerse a nivel bajo cuando se alimenta el chip hasta que la tensión seestabilice, poniéndose después en alto; esta entrada está asociada al bitVRT del registro D que indica si el integrado está en condiciones deoperar. El bus bidireccional de direcciones y datos está multiplexado(líneas AD0..AD7): en los flancos de bajada de la entrada de validación dedirecciones (línea AS) contiene direcciones, y datos en los flancos desubida de la entrada de validación de datos (línea DS). La línea -R/-Windica si la operación es de entrada o salida; -CE permite habilitar el chipo desconectarlo de los buses.

El cuadro de la derecha refleja la estructura de la memoria delMC146818. Los primeros 14 bytes son empleados para la fecha y hora.

00 Segundos

01 Segundos Alarma

02 Minutos

03 Minutos alarma

04 Horas

05 Horas alarma

06 Dia de la semana

07 Dia del mes

08 Mes

09 Año

0A Registro A

0B Registro B

0C Registro C

0D Registro D

0E..3F 50 bytes libres

REGISTROS DEL MC146818

REGISTRO A (lectura/escritura, excepto UIP).Este registro sirve para indicar al integrado qué tipo de reloj lo gobierna, así como elegir la frecuencia

de la interrupción periódica programable y la de la salida SQW. También contiene un bit que indica si hayuna actualización del reloj en curso, lo que sucede una vez cada segundo, ya que en ese preciso instante nose pueden leer los registros con objeto de evitar lecturas incorrectas.

D7 D6 D5 D4 D3 D2 D1 D0

UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0

0 0 0 Reloj de 4,194304 MHz0 0 1 Reloj de 1,048576 MHz Tipo de reloj conectado0 1 0 Reloj de 32768 Hz

Page 369: PCA, PS2 ,IBM y AT

369EL HARDWARE DE APOYO AL MICROPROCESADOR

El bit UIP (Update In Progress), de sólo lectura, se pone a 1 mientras se actualizan los primeros 14bytes de la memoria y poco tiempo antes de que comience dicha actualización. Antes de acceder a estosbytes, hay que esperar a que el bit UIP se ponga a cero (si no lo estaba ya): con el bit UIP a 0, es seguroque en un intervalo de al menos 244 microsegundos no se va a producir ninguna actualización, por lo quehay tiempo suficiente para acceder (sin prisas, pero tampoco con pausas). La actualización dura 248microsegundos (1984 con relojes de 32768 Hz).

Los bits RS0..RS3, de selección de velocidad, definen la frecuencia de la onda cuadrada generadaen SQW y/o la de la interrupción periódica, como indica esta tabla:

Reloj 1,048576 ó 4,194304 Mhz Reloj de 32768 Hz

RS3 RS2 RS1 RS0 Velocidad INT Frecuencia SQW Velocidad INT Frecuencia SQW

0 0 0 0 (no actúa) (nula) (no actúa) (nula)

0 0 0 1 30,517 µs 32768 Hz 3,90625 ms 256 Hz

0 0 1 0 61,035 µs 16384 Hz 7,81250 ms 128 Hz

0 0 1 1 122,070 µs 8192 Hz 122,070 µs 8192 Hz

0 1 0 0 244,141 µs 4096 Hz 244,141 µs 4096 Hz

0 1 0 1 488,281 µs 2048 Hz 488,281 µs 2048 Hz

0 1 1 0 976,562 µs 1024 Hz 976,562 µs 1024 Hz

0 1 1 1 1,953125 ms 512 Hz 1,953125 ms 512 Hz

1 0 0 0 3,90625 ms 256 Hz 3,90625 ms 256 Hz

1 0 0 1 7,8125 ms 128 Hz 7,8125 ms 128 Hz

1 0 1 0 15,625 ms 64 Hz 15,625 ms 64 Hz

1 0 1 1 31,25 ms 32 Hz 31,25 ms 32 Hz

1 1 0 0 62,5 ms 16 Hz 62,5 ms 16 Hz

1 1 0 1 125 ms 8 Hz 125 ms 8 Hz

1 1 1 0 250 ms 4 Hz 250 ms 4 Hz

1 1 1 1 500 ms 2 Hz 500 ms 2 Hz

REGISTRO B (lectura/escritura).En este registro hay bits útiles, entre otros, para controlar la inicialización de la fecha y hora, para

habilitar o inhibir las diversas interrupciones y para establecer ciertas características de operación.

D7 D6 D5 D4 D3 D2 D1 D0

SET PIE AIE UIE SQWE DM 24/12 DSE

El bit SET puede ser establecido a 1, con lo que cualquier ciclo de actualización de los primeros 14bytes de la RAM resulta abortado: de este modo, es factible proceder a inicializar la fecha y la hora sin elriesgo de que se produzca en medio una actualización. Este bit no se ve afectado por la señal -RESET.

El bit PIE (Periodic Interrupt Enable) sirve para permitir la interrupción periódica cuando es puestoa 1; tras una señal -RESET es puesto a 0. El bit AIE (Alarm Interrupt Enable) ha de estar a 1 para habilitarla interrupción de alarma; también es puesto a cero tras un -RESET. El bit UIE (Update Interrupt Enable)sirve para habilitar o inhibir la interrupción de fin de actualización, que se produciría tras cada actualizacióndel reloj; la señal -RESET baja el bit UIE. Por último, el bit SQWE (Square Wave Enable) permite habilitaro inhibir la señal de onda cuadrada de la salida SQW; también es borrado ante una señal -RESET.

El bit DM (Data Mode) permite seleccionar datos en binario (1) o BCD (0) en los bytes de fecha yhora; la señal -RESET no afecta a este bit. El bit 24/12 sirve para elegir entre el modo 12 horas del reloj (bita 0) o el de 24 (bit a 1): en el modo de 12 horas, el bit más significativo del byte de la hora estará activopara indicar "PM". Si bit DSE está activo, el último domingo de abril la hora pasa de 1:59:59 AM a 3:00:00AM; en el último domingo de octubre pasa de 1:59:59 AM a 1:00:00 AM (sólo la primera vez, claro) paraajustarse al cambio de hora oficial; este bit no es afectado por -RESET.

Page 370: PCA, PS2 ,IBM y AT

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

REGISTRO C (sólo lectura).Este registro contiene bits que informan de las interrupciones que se producen. Permite identificar

al ordenador qué o cuáles interrupción(es) se ha(n) producido.

D7 D6 D5 D4 D3 D2 D1 D0

IRQF PF AF UF 0 0 0 0

El bit IRQF (Interrupt ReQuest Flag) se activa cuando el bit PF y el PIE (registro B) están activos,o bien cuando el bit AF y el AIE (registro B) están activos, o bien cuando UF y el bit UIE (registro B) estánactivos. Es decir, IRQF se pone en alto cuando es necesario que se produzca una interrupción: la línea -IRQse encarga de pedirla entonces. Por su parte: PF (Periodic Flag), AF (Alarm Flag) y UF (Update Flag)indican si es necesario que se produzca la interrupción correspondiente. Todos los bits de este registro sonborrados ante una señal -RESET, pero también ante una lectura por software del registro C.

REGISTRO D (sólo lectura).Este registro contiene sólo el bit VRT (Valid RAM and Time). Este bit está a cero cuando la patilla

PS está a cero (PS se eleva a 1 cuando la tensión de alimentación es correcta). Por software, el bit VRTpuede ser puesto a 1 mediante una simple lectura del registro D (si la patilla PS=1), con objeto de indicarque la fecha y hora establecidas son correctas; si fallara la alimentación, al caer la tensión en la patilla PSeste bit pasaría de nuevo a cero. VRT no es afectado por -RESET.

D7 D6 D5 D4 D3 D2 D1 D0

VRT 0 0 0 0 0 0 0

FUNCIONAMIENTO DE LA ALARMA

La interrupción de alarma se produce todos los días cuando llega la hora en que ha sido programaday el bit que permite esta interrupción está habilitado. Existe un método alternativo para programar la alarma,basado en los códigos indiferentes almacenables en los bytes de la alarma. Un código indiferente es cualquiervalor comprendido entre 0C0h y 0FFh. Si la hora de alarma es un código indiferente, la alarma se producirácada hora. Si la hora y minuto de alarma son códigos indiferentes, ésta se producirá cada minuto. Si tantola hora como el minuto y segundo de la alarma son códigos indiferentes, la alarma se producirá cada segundo.

12.12.2. - EL MC146818 DENTRO DEL ORDENADOR.

El MC146818 es por lo general exclusivo de los AT y PS/2. En muchos ordenadores, laimplementación física se realiza con circuitos totalmente compatibles que incluyen 128 bytes de RAM enlugar de 64. En la RAM que sobra por encima de los primeros 14 bytes se almacenan parámetros de laconfiguración del sistema, modificables con el programa SETUP durante el arranque.

Por defecto, la BIOS inicializa el chip para trabajar con un reloj de 32768 Hz y a un ritmo de 1024interrupciones periódicas por segundo (cuando están habilitadas), al escribir el valor 26h en el registro A. Dela misma manera, el registro B se carga con 2 (modo 24 horas, datos en BCD y sin horario verano/invierno).

El MC146818 está diseñado para ser conectado a un bus multiplexado, por lo que la circuitería deapoyo de los AT se encarga de gestionar la comunicación con el microprocesador, estableciendo dos puertosde entrada/salida en las direcciones 70h y 71h. Para leer o escribir cualquier registro de la RAM CMOS, bastacon enviar al puerto 70h el número de registro y, a continuación, leer o escribir del puerto 71h. Entre losaccesos a ambos puertos debe mediar un tiempo mínimo; de lo contrario la operación fallará. En particular,las últimas versiones de los compiladores de Borland no permiten acceder al reloj de tiempo real en lamayoría de las máquinas a través de las funciones outportb() e inportb(). La razón es que esas funciones estánen una librería y es preciso llamarlas con paso de parámetros a través de la pila, lo que ralentizaexcesivamente el proceso. Desde el lenguaje ensamblador, nunca hay problemas, aunque como es costumbre

Page 371: PCA, PS2 ,IBM y AT

371EL HARDWARE DE APOYO AL MICROPROCESADOR

es conveniente insertar algún estado de espera (JMP SHORT $+2) entre dos operaciones E/S consecutivas,precaución necesaria en los ordenadores más antiguos.

A nivel de interrupciones, la salida -IRQ del MC146818 está conectada a IRQ8 (INT 70h) a travésdel segundo controlador de interrupciones (véase la documentación del mismo).

Desde la interrupción 1Ah, la BIOS implementa una serie de servicios para acceder al reloj de tiemporeal, incluyendo la posibilidad de programar la alarma (que invoque una INT 4Ah cuando llegue la hora).Las funciones de retardo de la INT 15h se apoyan también en el reloj de tiempo real.

Conviene tener presente que es de vital importancia acceder a los primeros 14 bytes de la CMOS sólosi el bit UIP del registro A (bit 7) está a cero. También es necesario poner a 1 el bit SET del registro B (bit7) antes de modificar dichos bytes, devolviéndolo a 1 después. No respetar este principio puede provocar lalectura de fechas u horas incorrectas o una errónea asignación de valores. Para los demás bytes de la CMOSno es necesario tomar esta precaución.

12.12.3. - UN MÉTODO PARA AVERIGUAR LA CONFIGURACIÓN DEL AT Y PS/2.

Como se dijo antes, los AT y superiores almacenan en los 50 ó 114 últimos bytes de RAM libres dela CMOS información relativa a la configuración del sistema. Los bytes más importantes y comunes a todaslas máquinas se muestran a continuación.

Byte 0Eh: Diagnostics Status Byte. El bit 7 indica (si vale 1) que el MC146818 tiene un déficit de corriente eléctrica.El bit 6 indica (si es 1) que el chechsum o suma de comprobación de la CMOS ha fallado. El bit 5 indica(si vale 1) que la configuración del sistema es incorrecta (no hay al menos una disquetera presente o el modode vídeo de la configuración no coincide con el detectado en el hardware). El bit 4 es puesto a 1 si el tamañode la memoria detectado no coincide con el indicado en la configuración. El bit 3 activo indica que eladaptador o el disco fijo C: falló en la inicialización, siendo imposible botar desde él. El bit 2 activo indicaque la hora del reloj es incorrecta. Los bits 1 y 0 están reservados.

Byte 0Fh: Shutdown Status Byte. Los bits de este byte son asignados durante la inicialización del sistema por partede la BIOS, informando de su desarrollo (véase listado de la BIOS).

Byte 10h: Diskette Drive Type Byte. Los bits 7..4 indican el tipo de la disquetera A y los bits 3..0 el tipo de ladisquetera B. Los valores posibles son 0 (no existe esa disquetera), 1 (5¼-360K), 2 (5¼-1.2M), 3 (3½-720K),4 (3½-1.44M) y 5 (3½-2.88M en BIOS AMI) ó 6 (3½-2.88M en BIOS IBM).

Byte 11h: Reservado.Byte 12h: Fixed Disk Type Byte. Los bits 7..4 indican el tipo del primer disco fijo y los bits 3..0 el tipo del segundo.

Existe una tabla definida por IBM cuando lanzó el AT con 14 tipos de disco; ninguno que se vende hoy endia está en la tabla, por lo que es frecuente que estos campos estén inicializados con el valor 1111b (ó 0 sino hay disco duro instalado) para indicar simplemente la presencia de disco duro.

Byte 13h: Reservado.Byte 14h: Equipment Byte. Los bits 7 y 6 indican el número de disquetes instalados; los bits 5 y 4 el tipo de adaptador

de vídeo primario (00: EGA/VGA, 01: CGA-80, 10: CGA-40, 11: MDA); los bits 3 y 2 no se emplean. Elbit 1 indica si hay coprocesador aritmético y el bit 0 está activo para confirmar que hay disqueteras.

Byte 15h-16h: Low and High Base Memory Bytes. El 15h es el bajo y el 16h el alto. Entre ambos forman una palabra de16 bits que indica la cantidad de memoria convencional (típicamente 640 Kb).

Byte 17h-18h: Low and High Memory Expansion Bytes. El 17h es el bajo y el 18h el alto. Entre ambos forman unapalabra de 16 bits que indica la cantidad de memoria extendida, en Kbytes.

Byte 19h: Número del primer disco duro. Número de identificación que la BIOS asigna al primer disco duro instalado.Byte 1Ah-2Dh: Reservados.Byte 2Eh-2Fh: Checksum. El 2Eh es el alto y el 2Fh el bajo. Entre ambos forman una palabra de 16 bytes que constituye

el checksum o suma de comprobación de los bytes 10h-20h.Byte 30h-31h: Low and High Memory Expansion Bytes. Habitualmente es el mismo valor que el almacenado en los bytes

17h y 18h; esta variable refleja sólo la memoria extendida ubicada por encima del primer megabyte quedetecta la BIOS en el momento de arrancar.

Byte 32h: Date Century Byte. Valor BCD del siglo actual-1. Para 1992, por ejemplo, es 19h.Byte 33h: Information Flag. El bit 7 indica si está instalada la vieja opción de ampliación de 128 Kb (hasta los 640

Kb) del IBM AT original: hoy en día suele estar siempre activo. El bit 6 es empleado por el programa SETUPpara eliminar el mensaje inicial al usuario tras el primer SETUP. Los demás bits están reservados.

Byte 34h-3Fh: Reservados.

Page 372: PCA, PS2 ,IBM y AT