Está en la página 1de 372

EL UNIVERSO

DIGITAL
DEL IBM PC, AT Y PS/2
Edicin 4.0 (4 edicin)
Versin impresa del original electrnico ubicado en:
http://www.gui.uva.es/udigital
Limitacin de garanta:
Pese a que todos los programas e ideas incluidas en el libro han
sido probados, el autor y el editor no se responsabilizan de los daos que
su funcionamiento pueda ocasionar bajo ninguna circunstancia ni estn
obligados a corregir el contenido del libro.
Marcas registradas:
IBM PCjr, PC, XT, AT, PS/2, OS/2 y Microchannel son marcas
registradas de International Business Machines.
MS-DOS, WINDOWS, Microsoft C y Microsoft Macro Assembler son
marcas registradas de Microsoft Corporation.
DR-DOS es marca registrada de Digital Research Inc.
QEMM y Desqview son marcas registradas de Qarterdeck Corporation.
UNIX es marca registrada de AT&T Bell Laboratories.
Intel es marca registrada de Intel Corporation.
Motorola es marca registrada de Motorola Inc.
Turbo Assembler, Turbo C, Turbo Debugger y Borland C++ son marcas
registradas de Borland International Inc.
EL UNIVERSO DIGITAL
DEL IBM PC, AT Y PS/2
Ciriaco Garca de Celis
Edicin 4.0
Ediciones Grupo Universitario de Informtica (Valladolid)
EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2 - v4.0
Ciriaco Garca de Celis.
Grupo Universitario de Informtica, 1992-1997.
Publ i ca:
Asoci aci n Grupo Uni versi tari o de i nformti ca, 1992-1997.
Apartado de correos 6062, Val l adol i d.
I nternet: http://www.gui .uva.es
Autor:
Ci ri aco Garc a de Cel i s (http://www.gui .uva.es/~ci ri )
Regi stro de propi edad I ntel ectual n 1121; Madri d, 1993.
Versi n el ectrni ca en I nternet:
http://www.gui .uva.es/udi gi tal
I mpri mi , durante l a etapa i mpresa:
Servi ci o de Reprograf a de l a Uni versi dad de Val l adol i d.
Casa del Estudi ante, avda. Real de Burgos s/n.
[Actual mente no se edi ta i mpreso; abstnganse de contactar con el l os].
Ti rada, durante l a etapa i mpresa:
Ms de 1200 ejempl ares.
Li cenci a de uso y di stri buci n:
Ver pgi na 11.
5 NDICE
NDICE
PRLOGO DE LA EDICIN 4.0 ELECTRNICA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
PRLOGO DE LA TERCERA EDICIN (1994) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1 - INTRODUCCIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.1 - Nmeros binarios, octales y hexadecimales . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2 - Cambio de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.3 - Estructura elemental de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4 - Operaciones aritmticas sencillas en binario . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.5 - Complemento a dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.6 - Agrupaciones de bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.7 - Representacin de datos en memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.8 - Operaciones lgicas en binario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2 - ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES . . . . . . . . . . . . . . . . 25
2.1 - Arquitectura Von Neuman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.2 - El microprocesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3 - Breve historia del ordenador personal y el DOS . . . . . . . . . . . . . . . . . . . . . . . 27
3 - MICROPROCESADORES 8086/88, 286, 386, 486 y Pentium . . . . . . . . . . . . . . . . . . . 31
3.1 - Caractersticas generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2 - Registros del 8086 y del 286 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.3 - Registros del 386 y procesadores superiores . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.4 - Modos de direccionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5 - La pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.6 - Un programa de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4 - JUEGO DE INSTRUCCIONES 80x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.1 - Descripcin completa de las instrucciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.1.1 - De carga de registros y direcciones . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.1.2 - De manipulacin del registro de estado . . . . . . . . . . . . . . . . . . . . . . 43
4.1.3 - De manejo de la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.1.4 - De transferencia de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.1.5 - De entrada/salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.1.6 - Aritmticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Suma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Resta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Multiplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Divisin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Conversiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.1.7 - Manipulacin de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.1.8 - Operaciones lgicas a nivel de bit . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.1.9 - De control del procesador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.1.10 - De rotacin y desplazamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.2 - Resumen alfabtico de las instrucciones y banderines. ndice. . . . . . . . . . . . . 63
4.3 - Instrucciones especficas del 286, 386 y 486 en modo real . . . . . . . . . . . . . . . 64
4.3.1 - Diferencias en el comportamiento global respecto al 8086 . . . . . . . . . 64
4.3.2 - Instrucciones especficas del 286 . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.3 - Instrucciones propias del 386 y 486 . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.4 - Deteccin de un sistema AT o superior . . . . . . . . . . . . . . . . . . . . . . 68
4.3.5 - Evaluacin exacta del microprocesador instalado . . . . . . . . . . . . . . . 68
4.3.6 - Modo plano (flat) del 386 y superiores . . . . . . . . . . . . . . . . . . . . . . . 70
6 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
5 - EL LENGUAJE ENSAMBLADOR DEL 80x86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1 - Sintaxis de una lnea en ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.2 - Constantes y operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.2.1 - Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.2.2 - Operadores aritmticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.2.3 - Operadores lgicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.2.4 - Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.2.5 - Operadores de retorno de valores . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.2.6 - Operadores de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.3 - Principales directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.3.1 - De definicin de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.3.2 - De definicin de smbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.3.3 - De control del ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.3.4 - De definicin de segmentos y procedimientos . . . . . . . . . . . . . . . . . 76
5.3.5 - De referencias externas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.3.6 - De definicin de bloques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.3.7 - Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.3.8 - De listado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.4 - Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.4.1 - Definicin y borrado de las macros . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.4.2 - Ejemplo de una macro sencilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.4.3 - Parmetros formales y parmetros actuales . . . . . . . . . . . . . . . . . . . 82
5.4.4 - Etiquetas dentro de macros. Variables locales. . . . . . . . . . . . . . . . . . 83
5.4.5 - Operadores de macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.4.6 - Directivas tiles para macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.4.7 - Macros avanzadas con nmero variable de parmetros . . . . . . . . . . 87
5.5 - Programacin modular y paso de parmetros . . . . . . . . . . . . . . . . . . . . . . . . 88
6 - EL ENSAMBLADOR EN ENTORNO DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.1 - Tipos de programas ejecutables bajo DOS . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.2 - Ejemplo de programa de tipo COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.3 - Ejemplo de programa de tipo EXE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.4 - Proceso de ensamblaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.5 - La utilidad DEBUG/SYMDEB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.6 - Las funciones del DOS y de la BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7 - ARQUITECTURA DEL PC, AT y PS/2 BAJO DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
7.1 - Las interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
7.2 - La memoria. Los puertos de entrada y salida. . . . . . . . . . . . . . . . . . . . . . . . . 105
7.3 - La pantalla en modo texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.4 - La pantalla en modo grfico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.4.1 - Modos grficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.4.2 - Deteccin de la tarjeta grfica instalada . . . . . . . . . . . . . . . . . . . . . . 108
7.4.3 - Introduccin al estndar grfico VGA . . . . . . . . . . . . . . . . . . . . . . . . 108
7.4.4 - Ejemplo de grficos empleando la BIOS . . . . . . . . . . . . . . . . . . . . . 114
7.4.5 - Ejemplo de grficos a nivel hardware . . . . . . . . . . . . . . . . . . . . . . . . 115
7.4.6 - El estndar grfico VESA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
7.5 - El teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
7.5.1 - Bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
7.5.2 - Nivel intermedio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.5.3 - Alto nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.6 - Los discos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.6.1 - Estructura fsica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.6.2 - Cabeza 0. Pista 0. Sector 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
7.6.3 - La FAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7.6.4 - El directorio raz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7 NDICE
7.6.5 - Los subdirectorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.6.6 - El BPB y el DPB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7.6.7 - La BIOS y los disquetes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7.6.8 - Disquetes floptical 3 de 20 Mb . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.6.9 - Ejemplo de acceso al disco a alto nivel . . . . . . . . . . . . . . . . . . . . . . 132
7.6.10 - Ejemplo de acceso al disco a bajo nivel . . . . . . . . . . . . . . . . . . . . . 133
7.7 - El PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
7.8 - El proceso de arranque del PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.9 - Formato de las extensiones ROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.10 - Formato fsico de los ficheros EXE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
8 - LA GESTIN DE MEMORIA DEL DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.1 - Tipos de memoria en un PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.2 - Bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.2.1 - El bloque de memoria del programa . . . . . . . . . . . . . . . . . . . . . . . . 145
8.2.2 - El bloque del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.2.3 - Los bloques de control de memoria (MCBs) . . . . . . . . . . . . . . . . . . 146
8.2.4 - La cadena de los bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . 146
8.2.5 - Relacin entre bloque de programa y de entorno . . . . . . . . . . . . . . . 147
8.2.6 - Tipos de bloques de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
8.2.7 - Liberar el espacio de entorno en programas residentes . . . . . . . . . . . 148
8.2.8 - Peculiaridades del MS-DOS 4.0 y 5.0 . . . . . . . . . . . . . . . . . . . . . . . 148
8.2.9 - Cmo recorrer los bloques de memoria. Ejemplo. . . . . . . . . . . . . . . . 149
8.3 - Memorias extendida y superior XMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.4 - Memoria expandida EMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
9 - SUBPROCESOS, RECUBRIMIENTOS Y FILTROS . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.1 - Llamada a subprocesos y recubrimientos u overlays . . . . . . . . . . . . . . . . . . . 157
9.2 - Construccin de filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
10 - PROGRAMAS RESIDENTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
10.1 - Principios bsicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
10.2 - Un ejemplo sencillo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
10.3 - Localizacin de un programa residente . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
10.3.1 - Mtodo de los vectores de interrupcin . . . . . . . . . . . . . . . . . . . . . 163
10.3.2 - Mtodo de la cadena de bloque de memoria . . . . . . . . . . . . . . . . . 163
10.3.3 - Mtodo de la interrupcin Multiplex . . . . . . . . . . . . . . . . . . . . . . . . 164
10.4 - Expulsin de un programa residente de la memoria . . . . . . . . . . . . . . . . . . . 164
10.5 - Gestin avanzada de la interrupcin Multiplex . . . . . . . . . . . . . . . . . . . . . . . 165
10.5.1 - El convenio BMB Compuscience . . . . . . . . . . . . . . . . . . . . . . . . . . 165
10.5.2 - El convenio CiriSOFT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
10.5.3 - La propuesta AMIS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
10.5.4 - Comparacin entre mtodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
10.6 - Mtodos especiales para economizar memoria . . . . . . . . . . . . . . . . . . . . . . 172
10.7 - Programas autoinstalables en memoria superior . . . . . . . . . . . . . . . . . . . . . . 173
10.8 - Programas residentes en memoria extendida con DR-DOS 6.0 . . . . . . . . . . . 174
10.9 - Ejemplo de programa residente que utiliza la BIOS . . . . . . . . . . . . . . . . . . . 176
10.10 - Uso sin lmites de servicios del DOS en programas residentes . . . . . . . . . . 184
10.10.1 - Una primera aproximacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
10.10.2 - Pasos a realizar para usar el DOS . . . . . . . . . . . . . . . . . . . . . . . . 186
10.10.3 - Resumiendo, no es tan difcil! . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
10.10.4 - Un mtodo alternativo: el SDA . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
10.10.5 - Mtodos menos ortodoxos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.11 - Ejemplo de programa residente que utiliza el DOS . . . . . . . . . . . . . . . . . . . 189
10.12 - Programas residentes invocables en modos grficos . . . . . . . . . . . . . . . . . 197
10.13 - Programas residentes en entorno WINDOWS 3 . . . . . . . . . . . . . . . . . . . . . 199
8 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
11 - CONTROLADORES DE DISPOSITIVO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
11.1 - Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
11.2 - Encabezamiento y palabra de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
11.3 - Rutinas de estrategia e interrupcin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
11.4 - Ordenes a soportar por el controlador de dispositivo . . . . . . . . . . . . . . . . . . 205
11.5 - La cadena de controladores de dispositivo instalados . . . . . . . . . . . . . . . . . . 210
11.6 - Ejemplo de controlador de dispositivo de caracteres . . . . . . . . . . . . . . . . . . . 212
11.7 - Ejemplo de controlador de dispositivo de bloques . . . . . . . . . . . . . . . . . . . . . 214
11.7.1 - Disco virtual TURBODSK: Caractersticas . . . . . . . . . . . . . . . . . . . 214
11.7.2 - Ensamblando TURBODSK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
11.7.3 - Anlisis detallado del listado de TURBODSK . . . . . . . . . . . . . . . . . 216
11.8 - Los controladores de dispositivo y el DOS . . . . . . . . . . . . . . . . . . . . . . . . . . 244
12 - EL HARDWARE DE APOYO AL MICROPROCESADOR . . . . . . . . . . . . . . . . . . . . . 245
12.1 - La arquitectura del ordenador compatible . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
12.2 - El interfaz de perifricos 8255 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
12.2.1 - Descripcin del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
12.2.2 - El 8255 en el PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
12.2.3 - Un mtodo para averiguar la configuracin del PC/XT . . . . . . . . . . . 248
12.3 - El temporizador 8253 u 8254 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
12.3.1 - Descripcin del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
12.3.2 - El 8254 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
12.3.3 - Temporizacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
12.3.4 - Sntesis de sonido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
12.4 - El controlador de interrupciones 8259 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
12.4.1 - Cmo y por qu de las interrupciones . . . . . . . . . . . . . . . . . . . . . . 261
12.4.2 - Descripcin del integrado 8259 . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
12.4.3 - El 8259 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
12.4.4 - Ejemplo: cambio de la base de las interrupciones . . . . . . . . . . . . . . 269
12.5 - El chip DMA 8237 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
12.5.1 - El acceso directo a memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
12.5.2 - Descripcin del integrado 8237 . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
12.5.3 - El 8237 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
12.5.4 - Ralentizar un equipo AT con el DMA . . . . . . . . . . . . . . . . . . . . . . . 281
12.5.5 - Acerca de las pginas de DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
12.6 - El controlador de disquetes NEC 765 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.6.1 - La tecnologa de grabacin en disco . . . . . . . . . . . . . . . . . . . . . . . 284
12.6.2 - Descripcin del FDC (Floppy Disk Controller) 765 . . . . . . . . . . . . . . 286
12.6.3 - El 765 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
12.6.4 - Densidades de disco y formatos estndar . . . . . . . . . . . . . . . . . . . 294
12.6.5 - Acceso a disco con DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
12.6.6 - Lectura y escritura de sectores de disco sin DMA . . . . . . . . . . . . . . 305
12.6.7 - Programacin avanzada del controlador de disquetes: 2M 3.0 . . . . . 309
12.6.7.1 - Formato de la primera pista . . . . . . . . . . . . . . . . . . . . . . . 311
12.6.7.2 - Puntualizaciones sobre el formato de mxima capacidad . . 315
12.6.7.3 - Descripcin de funcionamiento del soporte residente . . . . . 316
12.6.7.4 - Descripcin del programa de formateo (2MF) para 2M . . . . 330
12.6.7.5 - Un programa para medir el rendimiento de los disquetes . . 338
12.6.7.6 - La versin para PC/XT de 2M: 2MX . . . . . . . . . . . . . . . . . 340
12.6.7.7 - La opcin BIOS de 2M: 2M-ABIOS y 2M-XBIOS . . . . . . . . 341
12.6.7.8 - La utilidad 2MDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
12.6.7.9 - Cmo superar los 2.000.000 de bytes en 3: 2MGUI . . . . 342
12.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) . . . . . . . . . . . . . . . . . . . . . . . . 346
12.7.1 - El interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
9 NDICE
12.7.2 - Programacin de la controladora . . . . . . . . . . . . . . . . . . . . . . . . . . 346
12.7.3 - Ejemplo prctico de programacin . . . . . . . . . . . . . . . . . . . . . . . . . 349
12.8 - El controlador del teclado: 8042 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
12.8.1 - El 8042 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
12.8.2 - El teclado del AT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
12.8.3 - Comunicacin CPU teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
12.8.4 - Comunicacin teclado CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
12.9 - El puerto serie: UART 8250 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
12.9.1 - Descripcin del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
12.9.2 - El 8250 en el ordenador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
12.9.3 - Ejemplo: autodiagnstico del 8250 . . . . . . . . . . . . . . . . . . . . . . . . . 364
12.10 - El puerto de la impresora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
12.10.1 - Los registros del puerto paralelo . . . . . . . . . . . . . . . . . . . . . . . . . 365
12.10.2 - Envo de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
12.10.3 - Cable NULL-MODEM para conectar dos ordenadores . . . . . . . . . . 366
12.11 - El ratn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
12.12 - El reloj de tiempo real del AT: Motorola MC146818 . . . . . . . . . . . . . . . . . . 368
12.12.1 - Descripcin del integrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
12.12.2 - El MC146818 dentro del ordenador . . . . . . . . . . . . . . . . . . . . . . . 370
12.12.3 - Un mtodo para averiguar la configuracin del AT y PS/2 . . . . . . . 371
13 - EL ENSAMBLADOR Y EL LENGUAJE C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.1 - Uso del Turbo C y Borland C a bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.1.1 - Acceso a los puertos de E/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.1.2 - Acceso a la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.1.3 - Control de interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
13.1.4 - Llamada a interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
13.1.5 - Cambio de vectores de interrupcin . . . . . . . . . . . . . . . . . . . . . . . . 374
13.1.6 - Programas residentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
13.1.7 - Variables globales predefinidas interesantes . . . . . . . . . . . . . . . . . . 375
13.1.8 - Insercin de cdigo en lnea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
13.1.9 - Las palabras clave interrupt y asm . . . . . . . . . . . . . . . . . . . . . . . . . 375
13.2 - Interfaz C (Borland/Microsoft) - Ensamblador . . . . . . . . . . . . . . . . . . . . . . . . 376
13.2.1 - Modelos de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
13.2.2 - Integracin de mdulos en ensamblador . . . . . . . . . . . . . . . . . . . . 376
APNDICES:
I Mapa de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
II Tabla de interrupciones del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
III Tabla de variables de la BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
IV Puertos de E/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
V Cdigos de rastreo del teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
VI Tamaos y tiempos de ejecucin de las instrucciones . . . . . . . . . . . . . . . . . 393
VII Seales del slot de expansin ISA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
VIII Funciones del sistema, la BIOS y el DOS aludidas en este libro . . . . . . . . . 401
IX Especificaciones XMS y EMS: Todas sus funciones . . . . . . . . . . . . . . . . . . 423
X Juego de caracteres ASCII extendido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
XI Bibliografa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
11 PRLOGO DE LA EDICIN 4.0 ELECTRNICA
PRLOGO
DE LA EDICIN 4.0 ELECTRNICA*
(*) http://www.gui.uva.es/udigital
Nota: Pudiendo haber discrepancias entre sucesivas ediciones de estas normas, la
versin de referencia vlida e inapelable ser la ubicada en todo momento en la
red, en la direccin electrnica arriba indicada o cualquier otra que pudiera
sucederla.
Licencia de uso y distribucin para particulares.
La edicin 4.0 (4 edicin) de El Universo Digital del IBM PC, AT y PS/2 es un libro
electrnico/impreso de dominio pblico; de libre uso, difusin, copia y distribucin entre
particulares, en cualquier soporte. Quienes decidan utilizarlo debern registrarse por va
electrnica una sola vez, por razones de tica (http://www.gui.uva.es/udigital). Tambin es
posible hacerlo enviando una carta o postal ordinaria (mejor en un sobre) al autor, con
cualquier texto, a la siguiente direccin:
Ciriaco Garca de Celis
Apartado 6105
47080 Valladolid
Espaa
Indicando claramente que el motivo es registrar el Universo Digital. Los que hayan
comprado la versin impresa en persona no necesitan registrarse, aunque lo recibira con
agrado, incluso si ha pasado bastante tiempo (pero si lo compraron por correo no deben
registrarse: conservo su pedido). Me gustara conocer en alguna medida la difusin de la obra,
en especial a partir de este momento, lo que hasta ahora me resultaba algo ms sencillo. Por
supuesto, los datos o direcciones indicadas por los usuarios nunca sern divulgados por m.
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 la
excepcin de que se recomienda un nico registro electrnico o una sola carta o postal en
representacin de todos los posibles usuarios de la entidad.
Licencia de distribucin para empresas, asociaciones y organizaciones.
Editando revistas (no libros) la distribucin est permitida en cualquier formato digital
(HTML, PostScript, WordPerfect, texto, o cualesquiera otros) tanto en fragmentos como toda
la obra completa. Siendo el formato una revista impresa slo se permiten fragmentos que no
totalicen ms del 75% de la obra en los sucesivos nmeros publicados. Es necesario citar la
procedencia. La distribucin por empresas que cobren una cierta cantidad por el soporte es
libre. Mi nica sugerencia es que la empresa me enve una copia del soporte (CD, etc.) en que
se publique, por cortesa.
Tratndose de empresas editoriales u otras cualesquiera que planeen incluirlo, entero
o por fragmentos, en el soporte impreso, electrnico u online de algn libro que vayan a
publicar, deberan contactar primero conmigo para negociar una nueva versin (que en todo
caso no implicara la desaparicin de sta en su estatus actual).
Modificaciones.
La realizacin de cambios (aadidos, eliminacin de contenidos o reemplazamiento de
los mismos) es competencia exclusiva del autor, que centraliza la generacin de nuevas
versiones actualizadas. Quien realizara alguna modificacin sin consentimiento habra de
destinar la obra resultante para uso personal e intransferible.
Orgenes de El Universo Digital.
El Universo Digital no naci tras una decisin premeditada. Su objetivo inicial fue dotar
de un manual de apoyo al Curso de Lenguaje Ensamblador, que ofrece todos los aos la
asociacin Grupo Universitario de Informtica de la Universidad de Valladolid, en el marco
de unos Cursos de Introduccin a la Informtica -para los alumnos y personal en general de
la Universidad- que abarcan un espectro mucho ms amplio que el de la programacin de los
ordenadores.
La primera versin ocupaba 116 pginas, cuando su denominacin era an la de Curso
de Ensamblador. Sin embargo, en una poca en la que era difcil encontrar informacin, y buena
bibliografa especializada, el autor sigui recopilando material interesante y aadindolo al
curso. Una buena parte de dicho material y del aadido despus ha sido adems de cosecha
propia. La primera edicin de El Universo Digital, editada no mucho tiempo despus del
manual del curso, rebas ligeramente las 300 pginas. Posteriormente se incrementara an
algo ms, hasta las 420 de la 3 edicin que ha mantenido durante la mayor parte del tiempo.
13 PRLOGO DE LA EDICIN 4.0 ELECTRNICA
El DOS en la actualidad.
Actualmente, y desde hace algn tiempo, la programacin en DOS ya no es importante,
y mucho menos al nivel que desarrolla este libro, y ello pese a que incluso Windows 95 corre
an en alguna parte sobre DOS, comportamiento que ir reducindose hasta la eliminacin
en prximas versiones.
El futuro de la programacin, sin embargo, no es slo para los programadores de alto
nivel. En alguna manera, los propios usuarios pueden y podrn cada vez en mayor medida
hacer sus propios programas incluso sin darse cuenta. Sin embargo, siempre hay alguien que
tiene que construir los sistemas operativos, y sobre todo, los controladores para dar soporte
a los dispositivos en los diversos sistemas operativos. Por no mencionar las aplicaciones
especializadas, desde mquinas industriales al microprocesador de las sondas espaciales (que,
evidentemente, no corre bajo Windows). Es para los programadores de sistemas, y para
aquellos que necesitan o quieren saber cmo funciona el PC por dentro, como ejemplo prctico
de arquitectura interna de un ordenador, para los que va destinado este libro. Que podrn
practicar en un entorno cmodo para este tipo de programacin, como es el DOS (que deja
todo el control de la mquina a cada tarea). Aunque algunos contenidos muy relacionados con
el DOS siguen presentes en esta obra, el lector habr de tener en cuenta si es pertinente
profundizar en ellos o no, en la poca que vivimos.
Mis contactos con editoriales.
Mi objetivo inicial no fue publicarlo, aunque hace dos o tres aos s me lo plante un
poco en serio.
Las ventajas de una edicin oficial sera su no engorrosa distribucin (uno de los
motivos por los que siempre ha costado poco es porque nuestra Asociacin y el propio autor
ha puesto su mano de obra gratis), as como su mayor difusin. Puesto en contacto con cuatro
prestigiosas editoriales; las que han respondido han valorado muy positivamente la obra, sin
embargo la han rechazado aduciendo otros motivos (sobrecarga del programa editorial,
solapamiento en contenidos con obras publicadas o en fase de publicacin, o simplemente falta
de inters comercial). Una de ellas an no ha respondido.
Los inconvenientes de su publicacin por una editorial seran el importante aumento
de precio, y mi renuncia a los derechos de distribucin (en particular, nuestra Asociacin
tendra que comprar en la librera los ejemplares para nuestros cursos).
Sin embargo, la ventaja de la publicacin para facilitar la difusin popular es obvia, mxime
si lo hace una editorial importante (si no, no aparecera en todas las estanteras, la publicidad la
haran los lectores lentamente, como ya se vena haciendo, y la distribucin sera incluso ms
limitada pese al recurso a los baratos servicios de reprografa por parte de los usuarios).
El Universo Digital en Internet.
Mi decisin final ya la haba acariciado con anterioridad. Algo haba que hacer, pues
la distribucin gratuita del libro llevaba mucho tiempo.
Uno de los motivos que han terminado empujndome a esta decisin, ha sido la
considerable cantidad de pedidos que hemos recibido desde pases de hispanoamrica. Se trata
14 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
de ciudadanos que conocen el ndice del libro a travs del Web y lo piden, sobre todo desde
Mxico. Sin embargo, slo en la primera ocasin lo he enviado (a Per); los motivos son,
desgraciadamente, la prctica imposibilidad de comerciar a pequea escala con esos pases (no
existe el envo contrarreembolso, por ejemplo); las enormes demoras del envo por superficie
(el coste del envo areo supera el del propio libro) y las complicadas gestiones de pago e
injustas comisiones bancarias (aunque las pague el usuario final); finalmente habra que aadir
incluso mi temor inconsciente a un aumento incontrolado de la demanda, cuando ya haba
demasiado trabajo que hacer para atender la de origen nacional (en mi memoria estaba lo que
ocurri cuando empezaron a aparecer mensajes y comenzaron a recibirse pedidos por
FidoNET). Pido desde aqu disculpas a todos los que lo han solicitado desde fuera de Espaa,
mayores adems si no he contestado el E-Mail por no haber tomado an una decisin al
respecto.
El Universo Digital de dominio pblico en formato electrnico, podr ser accedido
desde cualquier lugar del mundo, y en cualquier CD de los kioscos.
El inconveniente es que no todos tienen igual acceso a estas redes y medios, aunque
ese inconveniente disminuir exponencialmente con el tiempo (con el mismo exponente con
que crezca la red).
Fin de la distribucin impresa.
Naturalmente, una vez que he renunciado a mis derechos sobre el libro, donndolo al
dominio pblico, ya no estoy obligado a venderlo impreso (medida tomada nicamente para
mantener el copyright). Realmente, no tenemos tiempo ni medios para atender la demanda
actual: aunque es una medida dura de imponer, lamento renunciar a realizar ms envos de
ejemplares impresos. Renuncio con ello a facilitar su difusin a los lectores menos introducidos
en las redes telemticas, pero beneficio a otros muchos, que adems podrn seguir usando la
versin manuscrita utilizando una impresora.
Por otro lado, haber facturado slo aproximadamente el coste de impresin y
distribucin, me permiten tomar esa decisin sin temer el enfado de quienes lo haban
comprado. El coste de impresin de los ltimos nmeros en la reprografa oficial de la
Universidad (rechazamos opciones ms baratas de menor calidad), encuadernacin y disquete
era de 1900 pts. El libro (realmente, apuntes tcnicos fotocopiados) se venda a 2100 pts ms gastos
de envo. Ese margen de beneficios era ms bien de maniobra, ya que por ejemplo, en los
ejemplares que no llegaban a su destino, el coste del envo y la devolucin lo pagbamos
nosotros. Cada envo llevaba una media de 20 minutos de tiempo total de mano de obra,
contabilizando la preparacin de los libros (transporte fsico, disquete, gestin del pedido...),
y la mayora eran de una sola unidad (pese a que se penalizaba su envo con 100 pts
adicionales). El precio de los ms de 1200 Universos Digitales vendidos ha tenido un
crecimiento nominal cero en los cinco aos de difusin impresa.
Obtencin de ejemplares impresos.
Aunque en general no se harn ms envos, la nica excepcin corresponder a los pedidos
realizados desde bibliotecas (universitarias o no universitarias), que tal vez no tengan la impresora
adecuada o tiempo para reproducirlo, lo que perjudicara a un amplio conjunto potencial de
usuarios. No se harn envos a otras organizaciones, ni a libreras o a particulares. Subrayamos
que El Universo Digital impreso tiene el carcter legal de apuntes tcnicos impresos y no de libro.
15 PRLOGO DE LA EDICIN 4.0 ELECTRNICA
Los pedidos de ejemplares impresos sern admitidos slo desde Espaa. Habrn de
realizarse exclusivamente por carta impresa, que deber estar compulsada por el sello y en su
caso papel oficial de la biblioteca que hace el pedido, adems de debidamente firmada por
quien corresponda. Es conveniente que figure el telfono de la biblioteca o en su defecto de
la conserjera del centro. Adems del nombre completo, direccin y NIF. Nos reservamos el
derecho de rechazar aquellos pedidos que no cumplan alguno de estos requisitos, o los de
sospechosa procedencia. La direccin es: Grupo Universitario de Informtica. Apartado 6062.
47080 Valladolid. El precio por ejemplar ser el que figure en la factura que realizar el propio
servicio de reprografa (unas 2000 pts/unidad); sumando al final el coste exacto del envo y
los disquetes.
Agradecimientos.
Agradezco desde aqu al servicio de Reprografa de la Universidad, ubicado en la Casa
del Estudiante, el esmero puesto durante tanto tiempo en la reproduccin y encuadernacin
de cada nmero durante la etapa impresa. Cualquier pequeo problema de calidad se ha
debido siempre a los fallos inevitables que en ocasiones presenta toda mquina, por buena que
sea.
Mis agradecimientos tambin a las diversas instituciones de la Universidad de
Valladolid, que han recibido en ocasin la presin de la demanda a travs de incorrectas
llamadas telefnicas solicitando el libro, no siendo ellos los encargados de su distribucin;
tambin al Grupo Universitario de Informtica, por su colaboracin a todos los niveles.
No puedo decir lo mismo de los funcionarios de Correos: aunque algunos son amables,
en general, el funcionamiento de esa institucin es el que caba esperar de un monopolio no
sometido a la libre competencia en envos postales ordinarios (y que, por tanto, no tiene la
obligacin de tratar bien a sus clientes, porque tambin volvern maana). El trato que reciben
los clientes no se diferencia mucho del de los paquetes, y estos son muy expresivos en
ocasiones al llegar al destino. Por otro lado, la cantidad de papeles que hay que rellenar en
cada envo, 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 slo
Argentaria sea an la nica entidad financiera con el privilegio de gestionar las denominadas
Cuentas Corrientes Postales. Adems de que el servicio de correos es caro en la realidad (esto
es, cuando se incluye lo que pagamos en impuestos para cubrir las prdidas de la compaa)
se mantiene el viejo vicio de indexar las tarifas anuales (aumento del 8% en 1997, cuando hay
un 2% de inflacin nacional).
Sin embargo, he de reconocer que la fiabilidad de Correos (entendida en cuanto a
paquetes que llegan a su destino o en su defecto vuelven por motivo de direccin incorrecta)
es prxima al 100%: los envos no suelen perderse, al menos los de los reembolsos. En
puntualidad, aunque hay extremos de gran aleatoriedad (desde paquetes que llegan en tres
das a un pueblo perdido en la otra punta del pas, a los que tardan quince en ir de Valladolid
a Madrid) el tiempo promedio podra aproximarse, aunque por debajo, a lo que afirma la
empresa.
Ciriaco Garca de Celis
Valladolid, Noviembre de 1997
17 PRLOGO DE LA TERCERA EDICIN (1994)
PRLOGO
DE LA TERCERA EDICIN (1994)
Ha pasado un ao desde la publicacin de la primera edicin de esta obra. Desde
entonces, ha continuado la expansin de los interfaces grficos de usuario y los sistemas
operativos avanzados para PC. Sin embargo, pese a que la programacin contina
alejndose cada vez ms del bajo nivel de las mquinas, los programadores de sistemas en
el entorno del PC siguen existiendo y son muchos ms que los que trabajan para las
empresas punteras en el desarrollo de los sistemas operativos. Los ordenadores compatibles
poseen numerosas aplicaciones en el campo industrial, para las que es conveniente un
conocimiento elevado del funcionamiento interno del ordenador en general y del MS-DOS
en particular. Para aquellas personas que necesitan comprender el funcionamiento de un
ordenador, las mquinas compatibles constituyen una interesante oportunidad y punto de
partida. Este libro pretende cubrir una importante laguna en la bibliografa disponible
actualmente sobre la programacin a nivel de sistemas de los ordenadores compatibles.
Respecto a la primera edicin, se han incrementado los contenidos en una
proporcin equivalente al 20% de lo que ya exista, corrigindose adems algunos errores.
Aunque el libro comience con una introduccin a la aritmtica binaria que pueda indicar
todo lo contrario, se presupone que el lector tiene unos mnimos conocimientos de
informtica, al menos un dominio bsico del sistema operativo MS-DOS, siendo ms que
recomendable conocer algn lenguaje de programacin. Seguidamente se explica el lenguaje
ensamblador de la serie 80x86 de Intel separando claramente las instrucciones de los
diversos procesadores, aunque dejando de lado algunas instrucciones del 286 y 386 que se
salen del entorno MS-DOS. Tambin se describe la sintaxis del lenguaje ensamblador; sin
embargo, aunque este ltimo aspecto est extensamente documentado, los lectores que no
conozcan el lenguaje ensamblador de ningn microprocesador habrn de trabajar
considerablemente leyendo multitud de listados hasta adquirir la soltura necesaria y, sobre
todo, creando los suyos propios. Aunque sera conveniente describir el lenguaje C, ntimo
aliado del ensamblador en la programacin de sistemas, ello se deja por razones de espacio
para otras publicaciones.
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 organizacin interna de la memoria (actualizada
hasta el MS-DOS 6.0 y el DR-DOS 6.0), los discos y el teclado. El apartado de los grficos
se repasa slo superficialmente, ya que por s solo necesitara de un buen libro ms grueso
que este. Se dan pistas sobre la manera de conmutar los modos de vdeo sin alterar el
contenido de la pantalla, aspecto que resulta de especial inters para los programas
residentes.
Las memorias extendida XMS y expandida EMS son descritas con cierto
detenimiento, dada su presencia en todos los ordenadores modernos y su importancia.
Existen apndices que describen todas las funciones del DOS, de la BIOS y del
sistema usadas en las rutinas y programas desarrollados, as como la totalidad de las
funciones XMS y EMS. Sin embargo, no estn ni muchsimo menos todas las interrupciones
necesarias, por lo que se insta al lector a conseguir el impresionante fichero de dominio
pblico INTERRUPT.LST, complemento ideal de este libro (ver bibliografa).
Los programas residentes reciben un tratamiento especialmente profundo: desde los
mtodos ms eficientes para que detecten su propia presencia en memoria, a las tcnicas
ms avanzadas para economizar memoria, pasando por el uso de funciones del DOS de
manera concurrente al programa principal, as como tcnicas de empleo de memoria
extendida y superior para conseguir programas que usen 0 Kb dentro de los primeros 640
Kb de la mquina y todo ello sin olvidar la convivencia con los actuales entornos
operativos, como Windows, y la posibilidad de ser activados desde pantallas grficas.
Este libro tambin trata los controladores de dispositivo o device drivers, desde los
dos posibles enfoques de su uso: bien sea la creacin de controladores de dispositivo de
caracteres, bien la de nuevas unidades de disco aadidas a las del sistema; en ambos casos
se incluyen ejemplos reales de controladores completos y comprobados, en particular el
ejemplo de disco virtual: un completo ejemplo de controlador redimensionable que soporta
memoria convencional, XMS y EMS.
Existe un captulo muy prximo al hardware en el que se describen a fondo y sin
omisiones todos los chips del ordenador, para permitir al programador de sistemas un
control completo del equipo. Para asimilar este captulo hace falta cierta formacin previa
en los sistemas digitales; sin embargo, los ejemplos que siguen a la informacin tcnica
aclaran las explicaciones previas y pueden ser aprovechados de manera inmediata incluso
sin entender todo lo anterior. Los chips de apoyo al microprocesador son descritos de
manera total: primero, no relacionados con el PC sino como tales circuitos; despus
integrndolos en el ordenador y documentando profusamente su uso, con ejemplos
probados. Se consideran el interfaz de perifricos 8255 (til para averiguar la configuracin
de los PC/XT), el temporizador 8253/8254 (para temporizacin y sntesis de sonido), el
controlador de interrupciones 8259, el controlador de DMA 8237 (para acceso a disco), el
controlador de disquetes 765 (acceso directo a los sectores), la controladora de disco duro
de los AT (IDE, MFM Bus Local); el controlador del teclado del AT (8042); el UART 8250
(empleado en las comunicaciones serie) y el reloj de tiempo real MC146818 (configuracin
de AT y programacin de alarmas y temporizaciones). Los ejemplos en este captulo
experimentan una importante potenciacin respecto a la edicin anterior; en particular, en
lo relacionado con el controlador de disquetes se puede considerar que la informacin
vertida es prcticamente casi toda la existente, existiendo pautas suficientes para que el
19 PRLOGO DE LA TERCERA EDICIN (1994)
lector cree sus propios programas copiones, protecciones de disco, formatos de alta
capacidad, etc.
Existen tambin captulos que describen el funcionamiento y programacin de la
impresora; sin entrar en aspectos particulares relativos a los modelos de las diversas
marcas, s se suministra informacin comn a todas. Tambin se comenta en un captulo
el funcionamiento al ms bajo nivel del ratn, aspecto que habitualmente no suele ser
considerado.
Dada la importancia del lenguaje C en la programacin en general y en la
programacin de sistemas en particular, tanto en la actualidad como durante los prximos
aos, se incluye un captulo que describe la manera de comunicar el ensamblador con el
lenguaje C, con objeto de superar las limitaciones de este lenguaje en los puntos crticos de
la programacin de sistemas. Este captulo requiere un dominio elemental del lenguaje C
por parte del lector, aunque probablemente slo sea til para aquellos que lo conocen ms
o menos.
Resumiendo, el libro pretende reunir en una sola obra la mayora de la informacin
necesaria para el programador de sistemas, exponiendo toda la informacin y no slo lo
imprescindible, sin olvidos ni omisiones; tambin se pretende explicar las tcnicas ms
avanzadas de creacin de programas residentes. Este afn de informacin completa es el
responsable del ttulo del libro.
Todos los listados de ejemplo se suponen de dominio pblico y las rutinas pueden
ser incluidas por los lectores libremente en sus propios programas, aunque en el caso de
los programas completos debe citarse la procedencia y dejar bien claro en las versiones
modificadas quin las ha alterado. En todo caso, pese a que todas las rutinas y programas
han sido probados debidamente en un 8088, un 286, un 386 o un 486 -bajo varios sistemas
operativos y con diferentes configuraciones del hardware- el autor del libro no se
responsabiliza de su correcto funcionamiento en todas las circunstancias.
21 INTRODUCCIN
Captulo I: INTRODUCCIN
1.1. - NUMEROS BINARIOS, OCTALES Y HEXADECIMALES.
El sistema de numeracin utilizado habitualmente es la base 10; es decir, consta de 10 dgitos (0-9)
que podemos colocar en grupos, ordenados de izquierda a derecha y de mayor a menor.
Cada posicin tiene un valor o peso de 10
n
donde n representa el lugar contado por la derecha:
1357 = 1 x 10
3
+ 3 x 10
2
+ 5 x 10
1
+ 7 x 10
0
Explcitamente, se indica la base de numeracin como 1357
10
.
En un ordenador el sistema de numeracin es binario -en base 2, utilizando el 0 y el 1- hecho
propiciado por ser precisamente dos los estados estables en los dispositivos digitales que componen una
computadora.
Anlogamente a la base 10, cada posicin tiene un valor de 2
n
donde n es la posicin contando desde
la derecha y empezando por 0:
101
2
= 1 x 2
2
+ 0 x 2
1
+ 1 x 2
0
Adems, por su importancia y utilidad, es necesario conocer otros sistemas de numeracin como
pueden ser el octal (base 8) y el hexadecimal (base 16). En este ltimo tenemos, adems de los nmeros del
0 al 9, letras -normalmente en maysculas- de la A a la F.
Llegar a un nmero en estos sistemas desde base 2 es realmente sencillo si agrupamos las cifras
binarias de 3 en 3 (octal) o de 4 en 4 (hexadecimal):
Base 2 a base 8: 101 011
2
= 53
8
Base 2 a base 16: 0010 1011
2
= 2B
16
A la inversa, basta convertir cada dgito octal o hexadecimal en binario:
Base 8 a base 2: 24
8
= 010 100
2
Base 16 a base 2: 24
16
= 0010 0100
2
De ahora en adelante, se utilizarn una serie de sufijos para determinar el sistema de numeracin
empleado:
Sufijo Base Ejemplos
b 2 01101010b
o,q 8 175o
d 10 789d
h 16 6A5h
En caso de que no aparezca el sufijo, el nmero se considera decimal; es decir, en base 10.
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 prcticamente directas, existe un sistema
general para realizar el cambio de una base a otra. El paso de cualquier base a base 10 lo vimos antes:
6A5h = 6 x 16
2
+ 10 x 16
1
+ 5 x 16
0
Inversamente, si queremos pasar de base 10 a cualquier otra habr que realizar sucesivas divisiones
por la base y tomar los restos:
1234 16
114 77 16 1234d = 4D2h
2
13 4
donde 4 es el ltimo cociente (menor que la base) y los restantes dgitos 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 electrnicos que pueden adoptar
nicamente dos estados, que representamos matemticamente por 0 y 1. Cualquiera de estas unidades de
informacin se denomina BIT, contraccin de binary digit en ingls.
1.3.2. - BYTE.
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 nmero de posiciones que almacenan bytes. La cantidad de memoria
de 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 los
exponentes de las potencias de 2 que reflejan el valor de cada posicin. Un byte nos permite, por tanto,
representar 256 estados (de 0 a 255) segn la combinacin 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 byte
se llaman nibble superior (el compuesto por los bits 4 a 7) e inferior (el compuesto por los bits 0 a 3). El
nibble tiene gran utilidad debido a que cada uno almacena un dgito hexadecimal:
Binario Hex. Decimal Binario Hex. Decimal
0000 0 0 1000 8 8
0001 1 1 1001 9 9
0010 2 2 1010 A 10
0011 3 3 1011 B 11
0100 4 4 1100 C 12
0101 5 5 1101 D 13
0110 6 6 1110 E 14
0111 7 7 1111 F 15
23 INTRODUCCIN
1.4. - OPERACIONES ARITMTICAS SENCILLAS EN BINARIO.
Para sumar nmeros, tanto en base 2 como hexadecimal, se sigue el mismo proceso que en base 10:
Podemos observar que la suma se desa-
1010 1010b rrolla de la forma tradicional; es decir:
+ 0011 1100b sumamos normalmente, salvo en el caso de
1 + 1 = 10
2
, 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 nmero el que necesitamos sumarlo para obtener
00h, por ejemplo:
FFh Como en un byte solo tenemos dos nibbles, es
+ 01h decir, dos dgitos hexadecimales, el resultado es
0 (observar cmo el 1 ms significativo subrayado
100h es ignorado). Luego FFh=-1. Normalmente, el bit 7
se considera como de signo y, si est activo (a 1)
el nmero es negativo.
Por esta razn, el nmero 80h, cuyo complemento a dos es l mismo, se considera negativo (-128)
y el nmero 00h, positivo. En general, para hallar el complemento a dos de un nmero cualquiera basta con
calcular primero su complemento a uno, que consiste en cambiar los unos por ceros y los ceros por unos
en su notacin binaria; a continuacin se le suma una unidad para calcular el complemento a dos. Con una
calculadora, la operacin es ms sencilla: el complemento a dos de un nmero A de n bits es 2
n
-A.
Otro factor a considerar es cuando se pasa de operar con un nmero de cierto tamao (ej., 8 bits) a
otro mayor (pongamos de 16 bits). Si el nmero es positivo, la parte que se aade por la izquierda son bits
a 0. Sin embargo, si era negativo (bit ms significativo activo) la parte que se aade por la izquierda son bits
a 1. Este fenmeno, en cuya demostracin matemtica no entraremos, se puede resumir en que el bit ms
significativo se copia en todos los aadidos: es lo que se denomina la extensin del signo: los dos siguientes
nmeros son realmente el mismo nmero (el -3
10
): 1101
2
(4 bits) y 11111101
2
(8 bits).
1.6. - AGRUPACIONES DE BYTES.
Tipo Definicin
Palabra 2 bytes contiguos
Doble palabra 2 palabras contiguas (4 bytes)
Cudruple palabra 4 palabras contiguas (8 bytes)
Prrafo 16 bytes
Pgina 256 bytes, 16 Kb, etc.
Segmento 64 Kbytes
1.7. - REPRESENTACIN DE LOS DATOS EN MEMORIA.
1.7.1. - NUMEROS BINARIOS: mximo nmero representable:
Tipo Sin signo
1 byte 255
2 bytes 65.535
4 bytes 4.294.967.295
8 bytes 18.446.744.073.709.551.615
24 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Tipo Positivo Negativo
1 byte 127 -128
2 bytes 32.767 -32.768
4 bytes 2.147.483.647 -2.147.483.648
8 bytes 9.223.372.036.854.775.807 -9.223.372.036.854.775.808
Los nmeros binarios de ms de un byte se almacenan en la memoria en los procesadores de Intel
en orden inverso: 01234567h se almacenara: 67h, 45h, 23h, 01h.
1.7.2. - NUMEROS BINARIOS CODIFICADOS EN DECIMAL (BCD).
Consiste en emplear cuatro bits para codificar los dgitos del 0 al 9 (desperdiciando las seis
combinaciones que van de la 1010 a la 1111). La ventaja es la simplicidad de conversin a/de base 10, que
resulta inmediata. Los nmeros BCD pueden almacenarse desempaquetados, en cuyo caso cada byte contiene
un dgito BCD (Binary-Coded Decimal); o empaquetados, almacenando dos dgitos por byte (para construir
los nmeros que van del 00 al 99). La notacin BCD ocupa cuatro bits -un nibble- por cifra, de forma que
en el formato desempaquetado el nibble superior siempre es 0.
1.7.3. - NUMEROS EN PUNTO FLOTANTE.
Son grupos de bytes en los que una parte se emplea para guardar las cifras del nmero (mantisa) y
otra para indicar la posicin del punto flotante (exponente), de modo equivalente a la notacin cientfica. Esto
permite trabajar con nmeros de muy elevado tamao -segn el exponente- y con una mayor o menor
precisin en funcin de los bits empleados para codificar la mantisa.
1.7.4. - CDIGO ASCII.
El cdigo A.S.C.I.I. (American Standard Code for Information Interchange) es un convenio adoptado
para asignar a cada carcter un valor numrico; su origen est en los comienzos de la Informtica tomando
como muestra algunos cdigos de la transmisin de informacin de radioteletipo. Se trata de un cdigo de
7 bits con capacidad para 128 smbolos que incluyen todos los caracteres alfanumricos del ingls, con
smbolos de puntuacin y algunos caracteres de control de la transmisin.
Con posterioridad, con la aparicin de los microordenadores y la gran expansin entre ellos de los
IBM-PC y compatibles, la ampliacin del cdigo ASCII realizada por esta marca a 8 bits, con capacidad para
128 smbolos adicionales, experimenta un considerable auge, siendo en la actualidad muy utilizada y
recibiendo la denominacin oficial de pgina de cdigos 437 (EEUU). Se puede consultar al final de este
libro. Es habitualmente la nica pgina soportada por las BIOS de los PC. Para ciertas nacionalidades se han
diseado otras pginas especficas que requieren de un software externo. En las lenguas del estado espaol
y en las de la mayora de los dems pases de la UE, esta tabla cubre todas las necesidades del idioma.
1.8. - OPERACIONES LGICAS 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 0
1 0 0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
25 ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES
Captulo II: ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES
El ensamblador es un lenguaje de programacin que, por la traduccin directa de los mnemnicos
a instrucciones maquina, permite realizar aplicaciones rpidas, solucionando situaciones en las que los tiempos
de ejecucin constituye el factor principal para que el proceso discurra con la suficiente fluidez. Esta
situacin, que indudablemente s influye sobre la eleccin del lenguaje de programacin a utilizar en el
desarrollo de una determinada rutina, y dada la aparicin de nuevos compiladores de lenguajes de alto nivel
que optimizan el cdigo generado a niveles muy prximos a los que un buen programador es capaz de
realizar en ensamblador, no es la nica razn para su utilizacin.
Es sobradamente conocido que los actuales sistemas operativos son programados en su mayor parte
en lenguajes de alto nivel, especialmente C, pero siempre hay una parte en la que el ensamblador se hace casi
insustituible bajo DOS y es la programacin de los drivers para los controladores de dispositivos, relacionados
con las tareas de ms bajo nivel de una mquina, fundamentalmente las operaciones de entrada/salida en las
que es preciso actuar directamente sobre los dems chips que acompaan al microprocesador. Por ello y
porque las instrucciones del lenguaje ensamblador estn ntimamente ligadas a la mquina, vamos a realizar
primero un somero repaso a la arquitectura interna de un microordenador.
2.1. - ARQUITECTURA VON NEWMAN.
Centrndonos en los ordenadores sobre los que vamos a trabajar desarrollar a grandes rasgos la
arquitectura Von Newman que, si bien no es la primera en aparecer, s que lo hizo prcticamente desde el
comienzo de los ordenadores y se sigue desarrollando actualmente. Claro es que est siendo desplazada por
otra que permiten una mayor velocidad de proceso, la RISC.
En los primeros tiempos de los ordenadores, con sistemas de numeracin decimal, una electrnica
sumamente complicada muy susceptible a fallos y un sistema de programacin cableado o mediante fichas,
Von Newman propuso dos conceptos bsicos que revolucionaran la incipiente informtica:
a) La utilizacin del sistema de numeracin binario. Simplificaba enormemente los problemas
que la implementacin electrnica de las operaciones y funciones lgicas planteaban, a la vez
proporcionaba una mayor inmunidad a los fallos (electrnica digital).
b) Almacenamiento de la secuencia de instrucciones de que consta el programa en una memoria
interna, fcilmente accesible, junto con los datos que referencia. De este forma la velocidad de
proceso experimenta un considerable incremento; recordemos que anteriormente una instruccin o
un dato estaban codificados en una ficha en el mejor de los casos.
Tomando como modelo las mquinas que aparecieron incorporando las anteriores caractersticas, el
ordenador se puede considerar compuesto por las siguientes partes:
- La Unidad Central de Proceso, U.C.P., ms conocida por sus siglas en ingls (CPU).
- La Memoria Interna, MI.
- Unidad de Entrada y Salida, E/S.
- Memoria masiva Externa, ME.
26 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Realicemos a continuacin una descripcin de lo que se entiende por cada una de estas partes y cmo
estn relacionadas entre si:
- La Unidad Central de Proceso (CPU) viene a ser el cerebro del ordenador y tiene por misin
efectuar las operaciones aritmtico-lgicas y controlar las transferencias de informacin a realizar.
- La Memoria Interna (MI) contiene el conjunto de instrucciones que ejecuta la CPU en el transcurso
de un programa. Es tambin donde se almacenan temporalmente las variables del mismo, todos los
datos 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 comunicacin de
la mquina con el exterior, proporcionando al operador una forma de introducir al ordenador tanto
los programas como los datos y obtener los resultados.
Como es de suponer, estas tres partes principales de que consta el ordenador deben estar ntimamente
conectadas; aparece en este momento el concepto de bus: el bus es un conjunto de lneas que enlazan los
distintos componentes del ordenador, por ellas se realiza la transferencia de datos entre todos sus elementos.
Se distinguen tres tipos de bus:
- De control: forman parte de l las lneas que seleccionan desde dnde y hacia dnde va dirigida
la informacin, tambin las que marcan la secuencia de los pasos a seguir para dicha transferencia.
- De datos: por l, de forma bidireccional, fluyen los datos entre las distintas partes del ordenador.
- De direcciones: como vimos, la memoria est dividida en pequeas unidades de almacenamiento
que contienen las instrucciones del programa y los datos. El bus de direcciones consta de un conjunto
de lneas que permite seleccionar de qu posicin de la memoria se quiere leer su contenido. Tambin
direcciona los puertos de E/S.
La forma de operar del ordenador en su conjunto es direccionar una posicin de la memoria en busca
de una instruccin mediante el bus de direcciones, llevar la instruccin 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 la
instruccin se decodifica, interpretando qu operandos necesita: si son de memoria, es necesario llevarles a
la CPU; una vez que la operacin es realizada, si es preciso se devuelve el resultado a la memoria.
2.2. - EL MICROPROCESADOR.
Un salto importante en la evolucin de los ordenadores lo introdujo el microprocesador: se trata de
una unidad central de proceso contenida totalmente en un circuito integrado. Comenzaba as la gran carrera
en busca de lo ms rpido, ms pequeo; rpidamente el mundo del ordenador empez a ser accesible a
pequeas empresas e incluso a nivel domstico: es el boom de los microordenadores personales. Aunque
cuando entremos en la descripcin de los microprocesadores objeto de nuestro estudio lo ampliaremos, har
un pequeo comentario de las partes del microprocesador:
- Unidad aritmtico-lgica: Es donde se efectan las operaciones aritmticas (suma, resta, y a veces
producto y divisin) y lgicas (and, or, not, etc.).
- Decodificador de instrucciones: All se interpretan las instrucciones que van llegando y que
componen el programa.
- Bloque de registros: Los registros son celdas de memoria en donde queda almacenado un dato
temporalmente. Existe un registro especial llamado de indicadores, estado o flags, que refleja el
estado operativo del microprocesador.
- Bloque de control de buses internos y externos: supervisa todo el proceso de transferencias de
informacin dentro del microprocesador y fuera de l.
27 ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES
2.3. - BREVE HISTORIA DEL ORDENADOR PERSONAL Y EL DOS.
La trepidante evolucin del mundo informtico podra provocar que algn recin llegado a este libro
no sepa exactamente qu diferencia a un ordenador "AT" del viejo "XT" inicial de IBM. Algunos trminos
manejados en este libro podran ser desconocidos para los lectores ms jvenes. Por ello, haremos una
pequea introduccin sobre la evolucin de los ordenadores personales, abarcando toda la historia (ya que
no es muy larga).
La premonicin.
En 1973, el centro de investigacin de Xerox en Palo Alto desarroll un equipo informtico con el
aspecto externo de un PC personal actual. Adems de pantalla y teclado, dispona de un artefacto similar al
ratn; en general, este aparato (denominado Alto) introdujo, mucho antes de que otros los reinventaran,
algunos de los conceptos universalmente aceptados hoy en da. Sin embargo, la tecnologa del momento no
permiti alcanzar todas las intenciones. Alguna innovacin, como la pantalla vertical, de formato similar a
una hoja de papel (que desearan algunos actuales internautas para los navegadores) an no ha sido adoptada:
nuestros PCs siguen pareciendo televisores con teclas, y los procesadores de textos no muestran legiblemente
una 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 paso
hacia el logro de un PC personal, al reducir drsticamente la circuitera adicional necesaria. Sucesores de este
procesador fueron el 8008 y el 8080, de 8 bits. Ed Roberts construy en 1975 el Altair 8800 basndose en
el 8080; aunque esta mquina no tena teclado ni pantalla (slo interruptores y luces), era una arquitectura
abierta (conocida por todo el mundo) y cuyas tarjetas se conectaban a la placa principal a travs de 100
terminales, que ms tarde terminaran convirtindose en el bus estndar S-100 de la industria.
El Apple-I apareci en 1976, basado en el microprocesador de 8 bits 6502, en aquel entonces un
recin aparecido aunque casi 10 veces ms barato que el 8080 de Intel. Fue sucedido en 1977 por el
Apple-II. No olvidemos los rudimentos de la poca: el Apple-II tena un lmite mximo de 48 Kbytes de
memoria. En el mismo ao, Commodore sac su PET con 8 Kbytes. Se utilizaban cintas de casete como
almacenamiento, aunque comenzaron a aparecer las unidades de disquete de 5. Durante finales de los 70
aparecieron muchos otros ordenadores, fruto de la explosin inicial del microprocesador.
Los micros de los 80.
En 1980, Sir Clive Sinclair lanz el ZX-80, seguido muy poco despus del ZX-81. Estaban basados
en un microprocesador sucesor del 8085 de Intel: el Z80 (desarrollado por la empresa Zilog, creada por un
ex-ingeniero de Intel). Commodore irrumpi con sus VIC-20 y, posteriormente, el Commodore 64, basados
an en el 6502 y, este ltimo, con mejores posibilidades grficas y unos 64 Kb de memoria. Su competidor
fue el ZX-Spectrum de Sinclair, tambin basado en el Z80, con un chip propio para gestin de grficos y
otras tareas, la ULA, que permiti rebajar su coste y multiplic su difusin por europa, y en particular por
Espaa. Sin embargo, todos los ordenadores domsticos de la poca, como se dieron en llamar, estaban
basados en procesadores de 8 bits y tenan el lmite de 64 Kb de memoria. Los intentos de rebasar este lmite
manteniendo an esos chips por parte de la plataforma MSX (supuesto estndar mundial con la misma suerte
que ha corrido el Esperanto) o los CPC de Amstrad, de poco sirvieron.
El IBM PC.
Y es que IBM tambin fabric su propio ordenador personal con vocacin profesional: el 12 de
agosto de 1981 present el IBM PC. Estaba basado en el microprocesador 8088, de 16 bits, cuyas
instrucciones sern las que usemos en este libro, ya que todos los procesadores posteriores son bsicamente
(en MS-DOS) versiones mucho ms rpidas del mismo. El equipamiento de serie consista en 16 Kbytes de
28 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
memoria ampliables a 64 en la placa base (y a 256 aadiendo tarjetas); el almacenamiento externo se haca
en cintas de casete, aunque pronto aparecieron las unidades de disco de 5 pulgadas y simple cara
(160/180 Kb por disco) o doble cara (320/360 Kb). En 1983 apareci el IBM PC-XT, que traa como
novedad un disco duro de 10 Mbytes. Un ao ms tarde aparecera el IBM PC-AT, introduciendo el
microprocesador 286, as como ranuras de expansin de 16 bits (el bus ISA de 16 bits) en contraposicin con
las de 8 bits del PC y el XT (bus ISA de 8 bits), adems incorporaba un disco duro de 20 Mbytes y disquetes
de 5 pero con 1.2 Mbytes.
En general, todos los equipos con procesador 286 o superior pueden catalogarse dentro de la categora
AT; el trmino XT hace referencia al 8088/8086 y similares. Finalmente, por PC (a secas) se entiende
cualquiera de ambos; aunque si se hace distincin entre un PC y un AT en la misma frase, por PC se
sobreentiende un XT, menos potente. El trmino PC ya digo, no obstante, es hoy en da mucho ms general,
referenciando habitualmente a cualquier ordenador personal.
Alrededor del PC se estaba construyendo un imperio de software ms importante que el propio
hardware: estamos hablando del sistema operativo PC-DOS. Cuando aparecieron mquinas compatibles con
el PC de IBM, tenan 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 requera la BIOS del
ordenador original, cuyo copyright era de IBM). Incluso, el desarrollo de los microprocesadores posteriores
ha estado totalmente condicionado por el MS-DOS. [Por cierto, la jugada del PC-DOS/MS-DOS se repetira
en alguna manera pocos aos despus con el OS/2-Windows].
A partir de 1986, IBM fue paulatinamente dejando de tener la batuta del mercado del PC. La razn
es que la propia IBM tena que respetar la compatibilidad con lo anterior, y en ese terreno no tena ms
facilidades para innovar que la competencia. El primer problema vino con la aparicin de los procesadores
386: los dems fabricantes se adelantaron a IBM y lanzaron mquinas con ranuras de expansin an de 16
bits, que no permitan obtener todo el rendimiento. IBM desarroll demasiado tarde, en 1987, la arquitectura
Microchannel, con bus de 32 bits pero cerrada e incompatible con tarjetas anteriores (aunque se desarrollaron
nuevas tarjetas, eran caras) y la incluy en su gama de ordenadores PS/2 (alguno de cuyos modelos era an
realmente ISA). La insolente respuesta de la competencia fue la arquitectura EISA, tambin de 32 bits pero
compatible con la ISA anterior.
Otro ejemplo: si IBM gobern los estndares grficos hasta la VGA, a partir de ah sucedi un
fenmeno similar y los dems fabricantes se adelantaron a finales de los 80 con mejores tarjetas y ms
baratas; sin embargo, se perdi la ventaja de la normalizacin (no hay dos tarjetas superiores a la VGA que
funcionen igual).
EISA tambin era caro, as que los fabricantes orientales, cruzada ya la barrera de los aos 90,
desarrollaron con la norma VESA las placas con bus local (VESA Local Bus); bsicamente es una
prolongacin de las patillas de la CPU a las ranuras de expansin, lo que permite tarjetas rpidas de 32 bits
pero muy conflictivas entre s. Esta arquitectura de bus se populariz mucho con los procesadores 486. Sin
embargo, al final el estndar que se ha impuesto ha sido el propuesto por el propio fabricante de las CPU:
Intel, con su bus PCI, que con el Pentium se ha convertido finalmente en el nico estndar de bus de 32 bits.
Estas mquinas an admiten no obstante las viejas tarjetas ISA, suficientes para algunas aplicaciones de baja
velocidad (modems,... etc).
La evolucin del MS-DOS.
Una manera sencilla de comprender la evolucin de los PC es observar la evolucin de las sucesivas
versiones del DOS y los sistemas que le han sucedido.
En 1979, Seatle Computer necesitaba apoyar de alguna manera a sus incipientes placas basadas en
el 8086. Como Digital Research estaba tardando demasiado en convertir el CP/M-80 a CP/M-86, desarroll
su propio sistema: el QDOS 0.1, que fue presentado en 1980. Antes de finales de ao apareci QDOS 0.3.
29 ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES
Bill Gates, dueo de Microsoft, de momento slo posea una versin de lenguaje BASIC para 8086
no orientada a ningn sistema operativo particular, que le gust a algn directivo de IBM. Bill Gates ya haba
hecho la primera demostracin mundial de BASIC corriendo en un 8086 en las placas de Seatle Computer
(en julio de 1979) y haba firmado un contrato de distribucin no exclusiva para el QDOS 0.3 a finales de
1980. En abril de 1981 aparecieron las primeras versiones de CP/M-86 de Digital, a la vez que QDOS se
renombraba a 86-DOS 1.0 aunque en principio pareca tener menos futuro que el CP/M. En Julio, sin
embargo, Microsoft adquira todos los derechos del 86-DOS.
Digital Research no ocupa actualmente el lugar de Microsoft porque en 1981 era una compaa
demasiado importante como para cerrar un acuerdo con IBM sin imponer sus condiciones para cederle los
derechos del sistema operativo CP/M. As que IBM opt por Bill Gates, que acababa de adquirir un sistema
operativo, el 86-DOS, que pas a denominarse PC-DOS 1.0. Las versiones de PC-DOS no dependientes de
la ROM BIOS de IBM se denominaran MS-DOS, trmino que ha terminado siendo ms popular.
A continuacin se expone la evolucin hasta la versin 5.0; las versiones siguientes no aaden
ninguna caracterstica interna nueva destacable (aunque a nivel de interfaz con el usuario y utilidades
incluidas haya ms cambios). El MS-DOS 7.0 sobre el que corre Windows 95 s tiene bastantes retoques
internos, pero no es frecuente su uso aislado o independiente de Windows 95. Aunque PC-DOS y MS-DOS
siembre han caminado paralelos, hay una nica excepcin: la versin 7.0 (no confundir MS-DOS 7.0 con
PC-DOS 7.0: este ltimo es, realmente, el equivalente al MS-DOS 5.0 6.2).
Agosto de 1981. Presentacin del MS-DOS 1.0 original.
Marzo de 1982. MS-DOS 1.25, aadiendo soporte para disquetes de doble cara. Las funciones del
DOS (en INT 21h) slo llegaban hasta la 1Fh (la 30h no estaba implementada!).
Marzo de 1983. MS-DOS 2.0 introducido con el XT: reescritura del ncleo en C; mejoras en el
sistema de ficheros (FAT, subdirectorios,...); separacin de los controladores de
dispositivo del sistema.
Mayo de 1983. MS-DOS 2.01: soporte de juegos de caracteres internacionales.
Octubre de 1983. MS-DOS 2.11: eliminacin de errores.
Agosto de 1984. MS-DOS 3.0: Aade soporte para disquetes de 1.2M y discos duros de 20 Mb. No
sera necesaria una nueva versin del DOS para cada nuevo formato de disco si el
controlador integrado para A:, B: y C: lo hubieran hecho flexible algn da.
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 particiones
secundarias en los discos duros. Soporte internacional: pginas de cdigos.
Julio de 1988. MS-DOS 4.0: Soporte para discos duros de ms de 32 Mb (cambio radical interno
que forz la reescritura de muchos programas de utilidad) hasta 2 Gb. Controlador
de 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 dcada ms tarde (con DR-DOS), obliga a
Microsoft a incluir ayuda online y a ocuparse un poco ms de los usuarios.
30 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Digital Research trabaj arduamente para lograr una compatibilidad total con MS-DOS, y finalmente
consigui lanzar al mercado su sistema DR-DOS. Las versiones 5.0 y 6.0 de este sistema, as como el
Novell DOS 7.0 (cuando cedi los derechos a Novell) se pueden considerar prcticamente 100% compatibles.
El efecto del DR-DOS fue positivo, al forzar a Microsoft a mejorar la interaccin del sistema operativo con
los usuarios (documentacin en lnea, programas de utilidad, ciertos detalles...); por poner un ejemplo, hasta
el MS-DOS 6.2 ha sido necesario intercambiar tres veces el disquete origen y el destino durante la copia de
un disquete normal de 1.44M. En cierto modo, la prepotencia de Microsoft con el MS-DOS a principios de
los noventa era similar a la de Digital Research a principios de los 80 con el CP/M.
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 integracin de los diversos Windows en uno slo, que
esperemos que algn da sea suficientemente abierto para que le surjan competidores. Si en el futuro hubiera
un slo sistema operativo soportado por Microsoft, no vamos por buen camino.
En ese caso, sera de agradecer que algn juez les obligara a publicar una especificacin completa
de las funciones y protocolos del sistema, con objeto de que algn organismo de normalizacin internacional
las recogiera sin ambigedades para permitir la libre competencia de otros fabricantes. El DOS y el Windows
actuales no son ningn invento maravilloso de Microsoft. Por poner un ejemplo, el MS-DOS 1.0 careca de
funcin para identificar la versin del sistema. Exactamente lo mismo le ha sucedido a las primeras versiones
de Windows (hay varios chequeos distintos para detectarlas, segn el modo de funcionamiento y la versin):
el MS-DOS no lo escribi inicialmente Microsoft, pero Windows s, y salta a la vista que sus programadores,
para cometer semejante despiste, se sentaron delante del teclado antes de hacer un anlisis de la aplicacin
a desarrollar, igual que lo hubiera hecho alguien que hubiera aprendido a programar con unos fascculos
comprados en el kiosco. Con tanto analista en el paro...
No olvidemos que el DOS y Windows son el fruto de toda la sociedad utilizando el mismo tipo de
ordenadores y necesitando la compatibilidad con lo anterior a cualquier precio. La prueba evidente son los
procesadores de Intel, construidos desde hace tiempo para dar servicio al sistema operativo del PC. Somos
prisioneros, usuarios obligados de Microsoft. Naturalmente, no tengo nada contra Microsoft, pero opino que
el poder adquirido durante una dcada, gracias a la exclusiva de los derechos sobre un sistema operativo sin
ayuda en la lnea de comandos, o de un Windows cerrado ntimamente ligado al DOS (de quien slo
Microsoft tiene el cdigo fuente) no legitima a ninguna empresa a tener tanto poder. No lo olvidemos: el
MS-DOS ha dado un vuelco hacia la amigabilidad con el usuario cuando Digital Research ha aparecido con
el DR-DOS. Del mismo modo que Windows seguir lento o colgndose mientras Unix no tenga ms
aplicaciones comerciales.
Si hay alguien que puede competir con Windows es Unix. Y en Unix no dependemos de ningn
fabricante concreto, ni de hardware ni de software. Probablemente, la insuficiente normalizacin actual la
corregira pronto el propio mercado. Tiene usted Linux instalado en casa y lo utiliza al menos para
conectarse a Internet por Infova, o quiz le gustara hacerlo algn da?. O por el contrario es de los que
piensan que Bill Gates es un genio?. Si se queda con la segunda opcin, es que ve mucho la tele, aunque
evidentemente tiene razn: y cuantos ms como usted, ms genio que ser... ;-)
31 MICROPROCESADORES 8086/88, 286, 386 Y 486
Captulo III: Microprocesadores 8086/88, 286, 386, 486 y Pentium.
3.1. - CARACTERSTICAS 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; una
capacidad de direccionamiento de 20 bits (hasta 1 Mb) y comparten el mismo juego de instrucciones.
La filosofa de diseo de la familia del 8086 se basa en la compatibilidad y la creacin de sistemas
informticos integrados, por lo que disponen de diversos coprocesadores como el 8089 de E/S y el 8087,
coprocesador matemtico de coma flotante. De acuerdo a esta filosofa y para permitir la compatibilidad con
los anteriores sistemas de 8 bits, el 8088 se dise con un bus de datos de 8 bits, lo cual le hace ms lento
que su hermano el 8086, pues ste es capaz de cargar una palabra ubicada en una direccin par en un solo
ciclo 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), por
lo que las mquinas construidas entorno a estos microprocesadores no suelen emplear la entrada/salida por
mapa de memoria, como veremos.
Entre esas instrucciones, las ms rpidas se ejecutan en 2 ciclos tericos de reloj y unos 9 reales (se
trata del movimiento de datos entre registros internos) y las ms lentas en 206 (divisin entera con signo del
acumulador por una palabra extrada de la memoria). Las frecuencias internas de reloj tpicas son 4.77 MHz
en la versin 8086; 8 MHz en la versin 8086-2 y 10 MHz en la 8086-1. Recurdese que un MHz son un
milln de ciclos de reloj, por lo que un PC estndar a 4,77 MHz puede ejecutar de 20.000 a unos 0,5
millones de instrucciones por segundo, segn la complejidad de las mismas (un 486 a 50 MHz, incluso sin
memoria cach externa es capaz de ejecutar entre 1,8 y 30 millones de estas instrucciones por segundo).
El microprocesador Intel 80286 se caracteriza por poseer dos modos de funcionamiento
completamente diferenciados: el modo real en el que se encuentra nada ms ser conectado a la corriente y
el 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 simultnea, con la ayuda
del sistema operativo para conmutar automticamente de uno a otro optimizando el uso de la CPU, ya que
mientras un proceso est esperando a que un perifrico complete una operacin, se puede atender otro proceso
diferente. La memoria virtual permite al ordenador usar ms memoria de la que realmente tiene, almacenando
parte de ella en disco: de esta manera, los programas creen tener a su disposicin ms memoria de la que
realmente existe; cuando acceden a una parte de la memoria lgica que no existe fsicamente, se produce una
interrupcin 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 juego
de instrucciones; slo el proceso supervisor -normalmente el sistema operativo- est capacitado para realizar
ciertas tareas. Esto es as para evitar que los programas de usuario puedan campar a sus anchas y entrar en
conflictos unos con otros, en materia de recursos como memoria o perifricos. Adems, de esta manera,
aunque un error software provoque el cuelgue de un proceso, los dems pueden seguir funcionando
32 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
normalmente, y el sistema operativo podra abortar el proceso colgado. Por desgracia, con el DOS el 286 no
est en modo protegido y el cuelgue de un solo proceso -bien el programa principal o una rutina operada por
interrupciones- significa la cada inmediata de todo el sistema.
El 8086 no posee ningn mecanismo para apoyar la multitarea ni la memoria virtual desde el
procesador, por lo que es difcil disear un sistema multitarea para el mismo y casi imposible conseguir que
sea realmente operativo. Obviamente, el 286 en modo protegido pierde absolutamente toda la compatibilidad
con los procesadores anteriores. Por ello, en este libro slo trataremos el modo real, nico disponible bajo
DOS, aunque veremos alguna instruccin extra que tambin se puede emplear en modo real.
Las caractersticas generales del 286 son: tiene un bus de datos de 16 bits, un bus de direcciones de
24 bits (16 Mb); posee 25 instrucciones ms que el 8086 y admite 8 modos de direccionamiento. En modo
virtual permite direccionar hasta 1 Gigabyte. Las frecuencias de trabajo tpicas son de 12 y 16 MHz, aunque
existen versiones a 20 y 25 MHz. Aqu, la instruccin ms lenta es la misma que en el caso del 8086, solo
que emplea 29 ciclos de reloj en lugar de 206. Un 286 de categora media (16 MHz) podra ejecutar ms de
medio milln de instrucciones de estas en un segundo, casi 15 veces ms que un 8086 medio a 8 MHz. Sin
embargo, transfiriendo datos entre registros la diferencia de un procesador a otro se reduce notablemente,
aunque el 286 es ms rpido y no slo gracias a los MHz adicionales.
Versiones mejoradas de los Intel 8086 y 8088 se encuentran tambin en los procesadores NEC-V30
y NEC-V20 respectivamente. Ambos son compatibles Hardware y Software, con la ventaja de que el
procesado de instrucciones est optimizado, llegando a superar casi en tres veces la velocidad de los
originales en algunas instrucciones aritmticas. Tambin poseen una cola de prebsqueda mayor (cuando el
microprocesador est ejecutando una instruccin, si no hace uso de los buses externos, carga en una cola
FIFO de unos pocos bytes las posiciones posteriores a la que est procesando, de esta forma una vez que
concluye la instruccin en curso ya tiene internamente la que le sigue). Adems, los NEC V20 y V30
disponen de las mismas instrucciones adicionales del 286 en modo real, al igual que el 80186 y el 80188.
Por su parte, el 386 dispone de una arquitectura de registros de 32 bits, con un bus de direcciones
tambin de 32 bits (direcciona hasta 4 Gigabytes = 4096 Mb) y ms modos posibles de funcionamiento: el
modo real (compatible 8086), el modo protegido (relativamente compatible con el del 286), un modo
protegido propio que permite -por fin!- romper la barrera de los tradicionales segmentos y el modo virtual
86, en el que puede emular el funcionamiento simultneo de varios 8086. Una vez ms, todos los modos
son incompatibles entre s y requieren de un sistema operativo especfico: si se puede perdonar al fabricante
la prdida de compatibilidad del modo avanzados del 286 frente al 8086, debido a la lgica evolucin
tecnolgica, no se puede decir lo mismo del 386 respecto al 286: no hubiera sido necesario aadir un nuevo
modo protegido si hubiera sido mejor construido el del 286 apenas un par de aos atrs. Normalmente, los
386 suelen operar en modo real (debido al DOS) por lo que no se aprovechan las posibilidades multitarea
ni de gestin de memoria. Por otra parte, aunque se pueden emplear los registros de 32 bits en modo real,
ello no suele hacerse -para mantener la compatibilidad con procesadores anteriores- con lo que de entrada
se est tirando a la basura un 50% de la capacidad de proceso del chip, aunque por fortuna estos procesadores
suelen trabajar a frecuencias de 16/20 MHz (obsoletas) y normalmente de 33 y hasta 40 MHz.
El 386sx es una variante del 386 a nivel de hardware, aunque es compatible en software.
Bsicamente, es un 386 con un bus de datos de slo 16 bits -ms lento, al tener que dar dos pasadas para un
dato de 32 bits-. De hecho, podra haber sido diseado perfectamente para mantener una compatibilidad
hardware con el 286, aunque el fabricante lo evit probablemente por razones comerciales.
El 486 se diferencia del 386 en la integracin en un solo chip del coprocesador 387. Tambin se ha
mejorado la velocidad de operacin: la versin de 25 MHz dobla en trminos reales a un 386 a 25 MHz
equipado con el mismo tamao de memoria cach. La versin 486sx no se diferencia en el tamao del bus,
tambin de 32 bits, sino en la ausencia del 387 (que puede ser aadido externamente). Tambin existen
versiones de 486 con buses de 16 bits, el primer fabricante de estos chips, denominados 486SLC, ha sido
Cyrix. Una tendencia iniciada por el 486 fue la de duplicar la velocidad del reloj interno (pongamos por caso
33 MICROPROCESADORES 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 agiliza
la ejecucin de las instrucciones ms largas: bajo DOS, el rendimiento general del sistema se puede
considerar prcticamente el doble. Son los chips DX2 (tambin hay una variante a 50 MHz: 25 x 2). La
culminacin de esta tecnologa viene de la mano de los DX4 a 75/100 MHz (25/33 x 3).
El Pentium, ltimo procesador de Intel en el momento de escribirse estas lneas, se diferencia
respecto al 486 en el bus de datos (ahora de 64 bits, lo que agiliza los accesos a memoria) y en un
elevadsimo nivel de optimizacin y segmentacin que le permite, empleando compiladores optimizados,
simultanear en muchos casos la ejecucin de dos instrucciones consecutivas. Posee dos cachs internas, tiene
capacidad para predecir el destino de los saltos y la unidad de coma flotante experimenta elevadas mejoras.
Sin embargo, bajo DOS, un Pentium bsico slo es unas 2 veces ms rpido que un 486 a la misma
frecuencia de reloj. Comenz en 60/90 MHz hasta los 166/200/233 MHz de las ltimas versiones (Pentium
Pro y MMX), que junto a diversos clones de otros fabricantes, mejoran an ms el rendimiento. Todos los
equipos Pentium emplean las tcnicas DX, ya que las placas base tpicas corren a 60 MHz. Para hacerse una
idea, por unas 200000 pts de 1997 un equipo Pentium MMX a 233 MHz es cerca de 2000 veces ms rpido
en aritmtica entera que el IBM PC original de inicios de la dcada de los 80; en coma flotante la diferencia
aumenta incluso algunos rdenes ms de magnitud. Y a una fraccin del coste (un milln de pts de aquel
entonces que equivale a unos 2,5 millones de hoy en da). Aunque no hay que olvidar la revolucin del resto
de los componentes: 100 veces ms memoria (central y de vdeo), 200 veces ms grande el disco duro... y
que un disco duro moderno transfiere datos 10 veces ms deprisa que la memoria de aquel IBM PC original.
Por desgracia, el software no ha mejorado el rendimiento, ni remotamente, en esa proporcin: es la factura
pasada por las tcnicas de programacin cada vez a un nivel ms alto (aunque nadie discute sus ventajas).
Una caracterstica de los microprocesadores a partir del 386 es la disponibilidad de memorias cach
de alta velocidad de acceso -muy pocos nanosegundos- que almacenan una pequea porcin de la memoria
principal. Cuando la CPU accede a una posicin de memoria, cierta circuitera de control se encarga de ir
depositando el contenido de esa posicin y el de las posiciones inmediatamente consecutivas en la memoria
cach. Cuando sea necesario acceder a la instruccin siguiente del programa, sta ya se encuentra en la cach
y el acceso es muy rpido. Lo ideal sera que toda la memoria del equipo fuera cach, pero esto no es todava
posible actualmente. Una cach de tamao razonable puede doblar la velocidad efectiva de proceso de la
CPU. El 8088 careca de memoria cach, pero s estaba equipado con una unidad de lectura adelantada de
instrucciones con una cola de prebsqueda de 4 bytes: de esta manera, se agilizaba ya un tanto la velocidad
de proceso al poder ejecutar una instruccin 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 ms, pero no se suele emplear
bajo DOS). La misin de estos registros es almacenar las posiciones de memoria que van a experimentar
repetidas manipulaciones, ya que los accesos a memoria son mucho ms lentos que los accesos a los registros.
Adems, hay ciertas operaciones que slo se pueden realizar sobre los registros. No todos los registros sirven
para almacenar datos, algunos estn especializados en apuntar a las direcciones de memoria. La mecnica
bsica de funcionamiento de un programa consiste en cargar los registros con datos de la memoria o de un
puerto de E/S, procesar los datos y devolver el resultado a la memoria o a otro puerto de E/S. Obviamente,
si un dato slo va a experimentar un cambio, es preferible realizar la operacin directamente sobre la
memoria, si ello es posible. A continuacin se describen los registros del 8086.
AX SP CS IP
BX BP DS flags
CX SI SS Registro
puntero de
DX DI ES instrucciones
y flags
Registros Registros Registros
de punteros de de
datos pila e ndices segmento
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 registros
separados de 8 bits (byte superior e inferior) cambiando la X por H o L segn queramos referirnos
a la parte alta o baja respectivamente. Por ejemplo, AX se descompone en AH (parte alta) y AL
(parte baja). Evidentemente, cualquier cambio sobre AH o AL altera AX!: valga como ejemplo que
al incrementar AH se le estn aadiendo 256 unidades a AX.
AX = Acumulador.
Es el registro principal, es utilizado en las instrucciones de multiplicacin y divisin y en
algunas instrucciones aritmticas especializadas, as como en ciertas operaciones de carcter especfico
como entrada, salida y traduccin. Obsrvese que el 8086 es suficientemente potente para realizar las
operaciones lgicas, la suma y la resta sobre cualquier registro de datos, no necesariamente el
acumulador.
BX = Base.
Se usa como registro base para referenciar direcciones de memoria con direccionamiento
indirecto, manteniendo la direccin de la base o comienzo de tablas o matrices. De esta manera, no
es preciso indicar una posicin de memoria fija, sino la nmero BX (as, haciendo avanzar de unidad
en unidad a BX, por ejemplo, se puede ir accediendo a un gran bloque de memoria en un bucle).
CX = Contador.
Se utiliza comnmente como contador en bucles y operaciones repetitivas de manejo de
cadenas. En las instrucciones de desplazamiento y rotacin se utiliza como contador de 8 bits.
DX = Datos.
Usado en conjuncin con AX en las operaciones de multiplicacin y divisin que involucran
o generan datos de 32 bits. En las de entrada y salida se emplea para especificar la direccin del
puerto E/S.
- Registros de segmento:
Definen reas de 64 Kb dentro del espacio de direcciones de 1 Mb del 8086. Estas reas
pueden solaparse total o parcialmente. No es posible acceder a una posicin de memoria no definida
por algn segmento: si es preciso, habr de moverse alguno.
CS = Registro de segmento de cdigo (code segment).
Contiene la direccin del segmento con las instrucciones del programa. Los programas de ms
de 64 Kb requieren cambiar CS peridicamente.
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 ampliacin para zona de datos. Es extraordinariamente til actuando en
conjuncin con DS: con ambos se puede definir dos zonas de 64 Kb, tan alejadas como se desee en
el espacio de direcciones, entre las que se pueden intercambiar datos.
35 MICROPROCESADORES 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 parmetros 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, tambin
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 instruccin (instruction pointer).
Marca el desplazamiento de la instruccin en curso dentro del segmento de cdigo. Es
automticamente modificado con la lectura de una instruccin.
- Registro de estado o de indicadores (flags).
Es un registro de 16 bits de los cuales 9 son utilizados para indicar diversas situaciones
durante la ejecucin de un programa. Los bits 0, 2, 4, 6, 7 y 11 son indicadores de condicin, que
reflejan los resultados de operaciones del programa; los bits del 8 al 10 son indicadores de control
y el resto no se utilizan. Estos indicadores pueden ser comprobados por las instrucciones de salto
condicional, lo que permite variar el flujo secuencial del programa segn el resultado de las
operaciones.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
CF (Carry Flag) Indicador de acarreo. Su valor ms habitual es lo que nos llevamos en una
suma o resta.
OF (Overflow Flag) Indicador de desbordamiento. Indica que el resultado de una operacin no
cabe en el tamao del operando destino.
ZF (Zero Flag) Indicador de resultado 0 o comparacin igual.
SF (Sign Flag) Indicador de resultado o comparacin negativa.
PF (Parity Flag) Indicador de paridad. Se activa tras algunas operaciones aritmtico-lgicas
para indicar que el nmero de bits a uno resultante es par.
AF (Auxiliary Flag) Para ajuste en operaciones BCD.
DF (Direction Flag) Indicador de direccin. Manipulando bloques de memoria, indica el sentido
de avance (ascendente/descendente).
IF (Interrupt Flag) Indicador de interrupciones: puesto a 1 estn permitidas.
TF (Trap Flag) Indicador de atrape (ejecucin paso a paso).
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 ms registros de los que vamos a ver ahora. Sin embargo, bajo el
sistema operativo DOS slo se suelen emplear los que veremos, que constituyen bsicamente una extensin a 32 bits
de los registros originales del 8086.
AX SP CS IP
EAX
BX BP DS flags
EBX EBP
CX SI ES FS
ECX ESI
DX DI SS GS
EDX EDI
Se ampla el tamao de los registros
de datos (que pueden ser accedidos en
fragmentos de 8, 16 32 bits) y se aaden
dos nuevos registros de segmento
multipropsito (FS y GS). Algunos de los
registros aqu mostrados son realmente de 32
bits (como EIP en vez de IP), pero bajo
sistema operativo DOS no pueden ser
empleados de manera directa, por lo que no
les consideraremos.
3.4. - MODOS DE DIRECCIONAMIENTO.
Son los distintos modos de acceder a los datos en memoria por parte del procesador. Antes de ver
los modos de direccionamiento, echaremos un vistazo a la sintaxis general de las instrucciones, ya que
pondremos alguna en los ejemplos:
INSTRUCCIN DESTINO, FUENTE
Donde destino indica dnde se deja el resultado de la operacin en la que pueden participar (segn
casos) FUENTE e incluso el propio DESTINO. Hay instrucciones, sin embargo, que slo tienen un operando,
como la siguiente, e incluso ninguno:
INSTRUCCIN DESTINO
Como ejemplos, aunque no hemos visto an las instrucciones utilizaremos un par de ellas: la de copia
o movimiento de datos (MOV) y la de suma (ADD).
3.4.1. - ORGANIZACIN DE DIRECCIONES: SEGMENTACIN.
Como ya sabemos, los microprocesadores 8086 y compatibles poseen registros de un tamao mximo
de 16 bits que direccionaran hasta 64K; en cambio, la direccin se compone de 20 bits con capacidad para
1Mb, hay por tanto que recurrir a algn artificio para direccionar toda la memoria. Dicho artificio consiste
en la segmentacin: se trata de dividir la memoria en grupos de 64K. Cada grupo se asocia con un registro
de segmento; el desplazamiento (offset) dentro de ese segmento lo proporciona otro registro de 16 bits. La
direccin absoluta se calcula multiplicando por 16 el valor del registro de segmento y sumando el offset,
obtenindose una direccin efectiva de 20 bits. Esto equivale a concebir el mecanismo de generacin de la
direccin absoluta, como si se tratase de que los registros de segmento tuvieran 4 bits a 0 (imaginarios) a la
derecha antes de sumarles el desplazamiento:
direccin = segmento * 16 + offset
En la prctica, una direccin se indica con la notacin SEGMENTO:OFFSET; adems, una misma
direccin puede expresarse de ms de una manera: por ejemplo, 3D00h:0300h es equivalente a 3D30:0000h.
Es importante resaltar que no se puede acceder a ms de 64 Kb en un segmento de datos. Por ello, en los
procesadores 386 y superiores no se deben emplear registros de 32 bit para generar direcciones (bajo DOS),
aunque para los clculos pueden ser interesantes (no obstante, s sera posible configurar estos procesadores
para poder direccionar ms memoria bajo DOS con los registros de 32 bits, aunque no resulta por lo general
prctico).
37 MICROPROCESADORES 8086/88, 286, 386 Y 486
3.4.2. - MODOS DE DIRECCIONAMIENTO.
- Direccionamiento inmediato: El operando es una constante situada detrs del cdigo de la
instruccin. Sin embargo, como registro destino no se puede indicar uno de segmento (habr que utilizar uno
de datos como paso intermedio).
ADD AX,0fffh
El nmero hexadecimal 0fffh es la constante numrica que en el direccionamiento inmediato
se le sumar al registro AX. Al trabajar con ensambladores, se pueden definir smbolos constantes
(ojo, no variables) y es ms intuitivo:
dato EQU 0fffh ; smbolo constante
MOV AX,dato
Si se referencia a la direccin de memoria de una variable de la siguiente forma, tambin se
trata de un caso de direccionamiento inmediato:
dato DW 0fffh ; ahora es una variable
MOV AX,OFFSET dato ; AX = direccin de memoria de dato
Porque hay que tener en cuenta que cuando traduzcamos a nmeros el smbolo podra quedar:
17F3:0A11 DW FFF
MOV AX,0A11
- Direccionamiento de registro: Los operandos, necesariamente de igual tamao, estn contenidos en
los registros indicados en la instruccin:
MOV DX,AX
MOV AH,AL
- Direccionamiento directo o absoluto: El operando est situado en la direccin indicada en la
instruccin, relativa al segmento que se trate:
MOV AX,[57D1h]
MOV AX,ES:[429Ch]
Esta sintaxis (quitando la h de hexadecimal) sera la que admite el programa DEBUG (realmente
habra que poner, en el segundo caso, ES: en una lnea y el MOV en otra). Al trabajar con ensambladores,
las variables en memoria se pueden referenciar con etiquetas simblicas:
MOV AX,dato
MOV AX,ES:dato
dato DW 1234h ; variable del programa
En el primer ejemplo se transfiere a AX el valor contenido en la direccin apuntada
por la etiqueta dato sobre el segmento de datos (DS) que se asume por defecto; en el
segundo ejemplo se indica de forma explcita el segmento tratndose del segmento ES. La
direccin efectiva se calcula de la forma ya vista con anterioridad: Registro de
segmento * 16 + desplazamiento_de_dato (este desplazamiento depende de la posicin al
ensamblar el programa).
38 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
- Direccionamiento indirecto: El operando se encuentra en una direccin sealada por un registro de
segmento*16 ms un registro base (BX/BP) o ndice (SI/DI). (Nota: BP acta 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 direccin determinada por la suma
de un registro de segmento*16, un registro de ndice, SI o DI y un desplazamiento de 8 16 bits. Ejemplos:
MOV AX,[DI+DESP] MOV AX,desp[DI]
ADD [SI+DESP],BX ADD desp[SI],BX
- Indirecto con base e ndice o indexado a base: El operando se encuentra en una direccin
especificada por la suma de un registro de segmento*16, uno de base, uno de ndice y opcionalmente un
desplazamiento de 8 16 bits:
MOV AX,ES:[BX+DI+DESP] MOV AX,ES:desp[BX][DI]
MOV CS:[BX+SI+DESP],CX MOV CS:desp[BX][SI],CX
Combinaciones de registros de segmento y desplazamiento.
Como se ve en los modos de direccionamiento, hay casos en los que se indica explcitamente el
registro de segmento a usar para acceder a los datos. Existen unos segmentos asociados por defecto a los
registros de desplazamiento (IP, SP, BP, BX, DI, SI); slo es necesario declarar el segmento cuando no
coincide con el asignado por defecto. En ese caso, el ensamblador genera un byte adicional (a modo de
prefijo) para indicar cul es el segmento referenciado. La siguiente tabla relaciona las posibles combinaciones
de los registros de segmento y los de desplazamiento:
CS SS DS ES
IP S No No No
SP No S No No
BP con prefijo por defecto con prefijo con prefijo
BX con prefijo con prefijo por defecto con prefijo
SI con prefijo con prefijo por defecto con prefijo
DI con prefijo con prefijo por defecto con prefijo(1)
(1) Tambin por defecto en el manejo de cadenas.
Los 386 y superiores admiten otros modos de direccionamiento ms sofisticados, que se vern en el
prximo captulo, despus de conocer todas las instrucciones del 8086. Por ahora, con todos estos modos se
puede considerar que hay ms que suficiente. De hecho, algunos se utilizan en muy contadas ocasiones.
3.5. - LA PILA.
La pila es un bloque de memoria de estructura LIFO (Last Input First Output: ltimo en entrar,
primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de pila). Las
posiciones individuales dentro de la pila se calculan sumando al contenido del segmento de pila SS un
desplazamiento contenido en el registro puntero de pila SP. Todos los datos que se almacenan en la pila son
de longitud palabra, y cada vez que se introduce algo en ella por medio de las instrucciones de manejo de
pila (PUSH y POP), el puntero se decrementa en dos; es decir, la pila avanza hacia direcciones decrecientes.
39 MICROPROCESADORES 8086/88, 286, 386 Y 486
El registro BP suele utilizarse normalmente para apuntar a una cierta posicin de la pila y acceder
indexadamente a sus elementos -generalmente en el caso de variables- sin necesidad de desapilarlos para
consultarlos.
La pila es utilizada frecuentemente al principio de una subrutina para preservar los registros que no
se desean modificar; al final de la subrutina basta con recuperarlos en orden inverso al que fueron
depositados. En estas operaciones conviene tener cuidado, ya que la pila en los 8086 es comn al procesador
y al usuario, por lo que se almacenan en ella tambin las direcciones de retorno de las subrutinas. Esta ltima
es, de hecho, la ms importante de sus funciones. La estructura de pila permite que unas subrutinas llamen
a otras que a su vez pueden llamar a otras y as sucesivamente: en la pila se almacenan las direcciones de
retorno, que sern las de la siguiente instruccin que provoc la llamada a la subrutina. As, al retornar de
la subrutina se extrae de la pila la direccin a donde volver. Los compiladores de los lenguajes de alto nivel
la emplean tambin para pasar los parmetros de los procedimientos y para generar en ella las variables
automticas -variables locales que existen durante la ejecucin del subprograma y se destruyen
inmediatamente despus-. Por ello, una norma bsica es que se debe desapilar siempre todo lo apilado para
evitar una prdida de control inmediata del ordenador.
Ejemplo de operacin 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
Situacin inicial despus de PUSH AX despus de POP BX
AX = 1234h AX = 1234h AX = 1234h
BX = 9D33h BX = 9D33h BX = 1234h
3.6. - UN PROGRAMA DE EJEMPLO.
Aunque las instrucciones del procesador no sern vistas hasta el prximo captulo, con objeto de
ayudar a la imaginacin del lector elaboraremos un primer programa de ejemplo en lenguaje ensamblador.
La utilidad de este programa es dejar patente que lo nico que entiende el 8086 son nmeros, aunque
nosotros nos referiremos a ellos con unos smbolos que faciliten entenderlos. Tambin es interesante este
ejemplo para afianzar el concepto de registro de segmento.
En este programa slo vamos a emplear las instrucciones MOV, ya conocida, y alguna otra ms como
la instruccin INC (incrementar), DEC (disminuir una unidad) y JNZ (saltar si el resultado no es cero).
Suponemos que el programa est ubicado a partir de la direccin de memoria 14D3:7A10 (arbitrariamente
elegida) y que lo que pretendemos hacer con l es limpiar la pantalla. Como el ordenador es un PC con
monitor en color, la pantalla de texto comienza en B800:0000 (no es ms que una zona de memoria). Por
cada carcter que hay en dicha pantalla, comenzando arriba a la izquierda, a partir de la direccin B800:0000
tenemos dos bytes: el primero, con el cdigo ASCII del carcter y el segundo con el color. Lo que vamos
a hacer es rellenar los 2000 caracteres (80 columnas x 25 lneas) con espacios en blanco (cdigo ASCII 32,
20h en hexadecimal), sin modificar el color que hubiera antes. Esto es, se trata de poner el valor 32 en la
direccin B800:0000, la B800:0002, la B800:0004... y as sucesivamente.
El programa quedara en memoria de esta manera: La primera columna indica la direccin de
memoria donde est el programa que se ejecuta (CS=14D3h e IP=7A10h al principio). La segunda columna
constituye el cdigo mquina que interpreta el 8086. Algunas instrucciones ocupan un byte de memoria, otras
dos tres (las hay de ms). La tercera columna contiene el nombre de las instrucciones, algo mucho ms
legible para los humanos que los nmeros:
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 pantalla
14D3:7A16 8E D8 MOV DS,AX ; apuntar segmento de datos a la misma
14D3:7A18 BB 00 00 MOV BX,0 ; apuntar al primer carcter ASCII de la pantalla
14D3:7A1B C6 07 20 MOV BYTE PTR [BX],32 ; se pone BYTE PTR para indicar que 32 es de 8 bits
14D3:7A1E 43 INC BX ; BX=BX+1 - apuntar al byte de color
14D3:7A1F 43 INC BX ; BX=BX+1 - apuntar al siguiente carcter ASCII
14D3:7A20 49 DEC CX ; CX=CX-1 - queda un carcter menos
14D3:7A21 75 F8 JNZ -8 ; si CX no es 0, saltar 8 bytes atrs (a 14D3:7A1B)
Como se puede ver, la segunda instruccin (bytes de cdigo mquina 0B8h, 0 y 0B8h colocados en
posiciones consecutivas) est colocada a partir del desplazamiento 7A13h, ya que la anterior que ocupaba 3
bytes comenzaba en 7A10h. En el ejemplo cargamos el valor 0B800h en DS apoyndonos en AX como
intermediario. El motivo es que los registros de segmento no admiten el direccionamiento inmediato. A
medida que se van haciendo programas, el ensamblador da mensajes de error cuando se encuentra con estos
fallos y permite ir aprendiendo con facilidad las normas, que tampoco son demasiadas. La instruccin MOV
BYTE PTR [BX],32 equivale a decir: poner en la direccin de memoria apuntada por BX (DS:[BX] para
ser ms exactos) el byte de valor 32. El valor 0F8h del cdigo mquina de la ltima instruccin es el
complemento a dos (nmero negativo) del valor 8.
Normalmente, casi nunca habr que ensamblar a mano consultando unas tablas, como hemos hecho
en este ejemplo. Sin embargo, la mejor manera de aprender ensamblador es no olvidando la estrecha relacin
de cada lnea de programa con la CPU y la memoria.
41 JUEGO DE INSTRUCCIONES 80x86
Captulo IV: JUEGO DE INSTRUCCIONES 80x86
4.1. - DESCRIPCIN COMPLETA DE LAS INSTRUCCIONES.
Nota: en el efecto de las instrucciones sobre el registro de estado se utilizar la siguiente
notacin:
- bit no modificado
? desconocido o indefinido
x modificado segn el resultado de la operacin
1 puesto siempre a 1
0 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 posicin de memoria
direccionada de las formas ya vistas, con la nica condicin de que origen y destino tengan
la misma dimensin. Existen ciertas limitaciones, como que los registros de segmento no
admiten el direccionamiento inmediato: es incorrecto MOV DS,4000h; pero no lo es por
ejemplo MOV DS,AX o MOV DS,VARIABLE. No es posible, as mismo, utilizar CS como
destino (es incorrecto hacer MOV CS,AX aunque pueda admitirlo algn ensamblador). Al
hacer MOV hacia un registro de segmento, las interrupciones quedan inhibidas hasta despus
de ejecutarse la siguiente instruccin (8086/88 de 1983 y procesadores posteriores).
Ejemplos: mov ds,ax
mov bx,es:[si]
mov si,offset dato
En el ltimo ejemplo, no se coloca en SI el valor de la variable dato sino su
direccin de memoria o desplazamiento respecto al segmento de datos. En otras palabras, SI
es un puntero a dato pero no es dato. En el prximo captulo se ver cmo se declaran
las variables.
XCHG (intercambiar)
Sintaxis: XCHG destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
42 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Intercambia el contenido de los operandos origen y destino. No pueden utilizarse
registros de segmentos como operandos.
Ejemplo: xchg bl,ch
xchg mem_pal,bx
XLAT (traduccin)
Sintaxis: XLAT tabla
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Se utiliza para traducir un byte del registro AL a un byte tomado de la tabla de
traduccin. Los datos se toman desde una direccin 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 lado
de xlat es slo una redundancia opcional.
Ejemplo: mov bx,offset tabla
mov al,4
xlat
LEA (carga direccin efectiva)
Sintaxis: LEA destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Transfiere el desplazamiento del operando fuente al operando destino. Otras
instrucciones pueden a continuacin utilizar el registro como desplazamiento para acceder a
los datos que constituyen el objetivo. El operando destino no puede ser un registro de
segmento. En general, esta instruccin es equivalente a MOV destino,OFFSET fuente y
de hecho los buenos ensambladores (TASM) la codifican como MOV para economizar un
byte de memoria. Sin embargo, LEA es en algunos casos ms potente que MOV al permitir
indicar 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
ms el registro SI. Esa sola instruccin es equivalente a estas dos:
mov dx,offset datos
add 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 (direccin completa de memoria compuesta por
43 JUEGO DE INSTRUCCIONES 80x86
segmento y desplazamiento), al destino indicado y a DS. A partir de la direccin indicada
por el operando origen, el procesador toma 4 bytes de la memoria: con los dos primeros
forma una palabra que deposita en destino y, con los otros dos, otra en DS.
Ejemplo: punt dd 12345678h
lds si,punt
Como resultado de esta instruccin, en DS:SI se hace referencia a la posicin de
memoria 1234h:5678h; dd sirve para definir una variable larga de 4 bytes (denominada
punt en el ejemplo) y ser explicado en el captulo siguiente.
LES (carga un puntero utilizando ES)
Sintaxis: LES destino, origen
Esta instruccin es anloga 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 dems 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, PF
y CF respectivamente.
4.1.2. - INSTRUCCIONES DE MANIPULACIN 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 direccin)
Sintaxis: CLD
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 direccin DF, por lo que los registros SI y/o DI se
autoincrementan en las operaciones de cadenas, sin afectar al resto de los indicadores. Es
NECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce con
seguridad el valor de DF. Vase STD.
CLI (baja indicador de interrupcin)
Sintaxis: CLI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 - - - - - -
Borra el indicador de activacin de interrupciones IF, lo que desactiva las
interrupciones enmascarables. Es muy conveniente hacer esto antes de modificar la pareja
SS:SP en los 8086/88 anteriores a 1983 (vase comentario en la instruccin MOV), o antes
de cambiar un vector de interrupcin sin el apoyo del DOS. Generalmente las interrupciones
slo se inhiben por breves instantes en momentos crticos. Vase tambin 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 ningn otro indicador.
STD (pone a uno el indicador de direccin)
Sintaxis: STD
Indicadores: OF DF IF TF SF ZF AF PF CF
- 1 - - - - - - -
Pone a 1 el indicador de direccin DF, por lo que los registros SI y/o DI se
autodecrementan en las operaciones de cadenas, sin afectar al resto de los indicadores. Es
NECESARIO colocarlo antes de las instrucciones de manejo de cadenas si no se conoce con
seguridad el estado de DF. Vase tambin CLD.
45 JUEGO DE INSTRUCCIONES 80x86
STI (pone a uno el indicador de interrupcin)
Sintaxis: STI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 1 - - - - - -
Pone a 1 la bandera de desactivacin de interrupciones IF y activa las interrupciones
enmascarables. Una interrupcin pendiente no es reconocida, sin embargo, hasta despus de
ejecutar la instruccin que sigue a STI. Vase tambin 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 por
SP) al operando destino que a de ser tipo palabra, e incrementa en dos el registro SP. La
instruccin POP CS, poco til, no funciona correctamente en los 286 y superiores.
Ejemplos: pop ax
pop pepe
PUSH (introduce en la pila)
Sintaxis: PUSH origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Decrementa el puntero de pila (SP) en 2 y luego transfiere la palabra especificada
en el operando origen a la cima de la pila. El registro CS aqu s se puede especificar como
origen, 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 CF
x x x x x x x x x
Traslada al registro de los indicadores la palabra almacenada en la cima de la pila;
a continuacin el puntero de pila SP se incrementa en dos.
PUSHF (introduce los indicadores en la pila)
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 de
los 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 la
pila la direccin de la instruccin siguiente, para poder volver a ella una vez ejecutado el
procedimiento. El procedimiento puede estar en el mismo segmento (tipo NEAR) o en otro
segmento (tipo FAR). A su vez la llamada puede ser directa a una etiqueta (especificando el
tipo de llamada NEAR -por defecto- o FAR) o indirecta, indicando la direccin donde se
encuentra el puntero. Segn la llamada sea cercana o lejana, se almacena en la pila una
direccin de retorno de 16 bits o dos palabras de 16 bits indicando en este ltimo caso tanto
el offset (IP) como el segmento (CS) a donde volver.
Ejemplos: call proc1
dir dd 0f000e987h
call dword ptr dir
En el segundo ejemplo, la variable dir almacena la direccin a donde saltar. De esta
ltima manera -conociendo su direccin- puede llamarse tambin a un vector de interrupcin,
guardando previamente los flags en la pila (PUSHF), porque la rutina de interrupcin
retornar (con IRET en vez de con RETF) sacndolos.
JMP (salto)
Sintaxis: JMP direccin o JMP SHORT direccin
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Transfiere el control incondicionalmente a la direccin indicada en el operando. La
bifurcacin puede ser tambin directa o indirecta como anteriormente vimos, pero adems
puede 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 buenos
ensambladores (como TASM) cuando dan dos pasadas colocan all donde es posible un salto
corto, 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 sigue
siendo insuficiente, se puede indicar con far que es largo (salto a otro segmento).
47 JUEGO DE INSTRUCCIONES 80x86
Ejemplos: jmp etiqueta
jmp 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 direccin de la siguiente
direccin. Se extraer el registro de segmento y el desplazamiento en un procedimiento de
tipo FAR (dos palabras) y solo el desplazamiento en un procedimiento NEAR (una palabra).
si esta instruccin es colocada dentro de un bloque PROC-ENDP (como se ver en el
siguiente captulo) el ensamblador sabe el tipo de retorno que debe hacer, segn el
procedimiento sea NEAR o FAR. En cualquier caso, se puede forzar que el retorno sea de
tipo FAR con la instruccin RETF. Valor, si es indicado permite sumar una cantidad
valor en bytes a SP antes de retornar, lo que es frecuente en el cdigo generado por los
compiladores para retornar de una funcin con parmetros. Tambin se puede retornar de una
interrupcin 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 la
instruccin que se encuentra en la posicin IP+desplazamiento (desplazamiento comprendido
entre -128 y +127) si se cumple la condicin. Algunas condiciones se pueden denotar de
varias maneras. Todos los saltos son cortos y si no alcanza hay que aparselas como sea.
En negrita se realzan las condiciones ms empleadas. Donde interviene SF se consideran con
signo los operandos implicados en la ltima comparacin u operacin aritmetico-lgica, y se
indican en la tabla como (-128 a +127 -32768 a +32767); en los dems casos, indicados
como +, 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.
Gestin de bucle
LOOP (bucle)
Sintaxis: LOOP desplazamiento
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
48 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Decrementa el registro contador CX; si CX es cero, ejecuta la siguiente instruccin,
en caso contrario transfiere el control a la direccin resultante de sumar a IP +
desplazamiento. El desplazamiento debe estar comprendido entre -128 y +127. Ejemplo:
mov cx,10
bucle: .......
.......
loop bucle
Con las mismas caractersticas que la instruccin anterior:
LOOPE/LOOPZ Bucle si igual, si cero. Z=1 y CX<>0
LOOPNE/LOOPNZ Bucle si no igual, si no cero. Z=0 y CX<>0
Interrupciones
INT (interrupcin)
Sintaxis: INT n (0 <= n <= 255)
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 0 - - - - -
Inicializa un procedimiento de interrupcin de un tipo indicado en la instruccin. En
la pila se introduce al llamar a una interrupcin la direccin de retorno formada por los
registros CS e IP y el estado de los indicadores. INT 3 es un caso especial de INT, al
ensamblarla el ensamblador genera un slo byte en vez de los dos habituales; esta
interrupcin se utiliza para poner puntos de ruptura en los programas. Vase tambin IRET
y el apartado 1 del captulo VII.
Ejemplo: int 21h
INTO (interrupcin por desbordamiento)
Sintaxis: INTO
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 0 - - - - -
Genera una interrupcin de tipo 4 (INT 4) si existe desbordamiento (OF=1). De lo
contrario se contina con la instruccin siguiente.
IRET (retorno de interrupcin)
Sintaxis: IRET
Indicadores: OF DF IF TF SF ZF AF PF CF
x x x x x x x x x
Devuelve el control a la direccin de retorno salvada en la pila por una interrupcin
previa y restaura los indicadores que tambin se introdujeron en la pila. En total, se sacan
las 3 palabras que fueron colocadas en la pila cuando se produjo la interrupcin. Vase
tambin INT.
49 JUEGO 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, dependiendo
de la longitud byte o palabra respectivamente. El puerto puede especificarse mediante una
constante (0 a 255) o a travs del valor contenido en DX (0 a 65535).
Ejemplo: in ax,0fh
in 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 puerto
puede especificarse con un valor fijo entre 0 y 255 a travs del valor contenido en el
registro DX (de 0 a 65535).
Ejemplo: out 12h,ax
out dx,al
4.1.6. - INSTRUCCIONES ARITMTICAS.
* * * 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 nmero BCD no empaquetado. Si los
cuatro bits menos significativos de AL son mayores que 9 si el indicador AF est a 1, se
suma 6 a AL, 1 a AH, AF se pone a 1, CF se iguala a AF y AL pone sus cuatro bits ms
significativos a 0.
Ejemplo: add al,bl
aaa
En el ejemplo, tras la suma de dos nmeros BCD no empaquetados colocados en AL
y BL, el resultado (por medio de AAA) sigue siendo un nmero BCD no empaquetado.
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 CF
x - - - x x x x x
Suma los operandos origen, destino y el valor del indicador de acarreo (0 1) y el
resultado lo almacena en el operando destino. Se utiliza normalmente para sumar nmeros
grandes, de ms 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 CF
x - - - x x x x x
Suma los operandos origen y destino almacenando el resultado en el operando
destino. Se activa el acarreo si se desborda el registro destino durante la suma.
Ejemplos: add ax,bx
add 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 bits
menos significativos de AL son un nmero mayor que 9, el indicador AF se pone a 1 y se
suma 6 a AL. De igual forma, si los cuatro bits ms significativos de AL tras la operacin
anterior son un nmero mayor que 9, el indicador CF se pone a 1 y se suma 60h a AL.
Ejemplo: add al,cl
daa
En el ejemplo anterior, si AL y CL contenan dos nmeros BCD empaquetados, DAA
hace que el resultado de la suma (en AL) siga siendo tambin un BCD empaquetado.
INC (incrementar)
Sintaxis: INC destino
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x -
Incrementa el operando destino. El operando destino puede ser byte o palabra.
51 JUEGO DE INSTRUCCIONES 80x86
Obsrvese que esta instruccin no modifica el bit de acarreo (CF) y no es posible detectar
un desbordamiento por este procedimiento (utilcese ZF).
Ejemplos: inc al
inc 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 sustraccin de dos operandos BCD no empaquetados para
que siga siendo un nmero BCD no empaquetado. Si el nibble inferior de AL tiene un valor
mayor que 9, de AL se resta 6, se decrementa AH, AF se pone a 1 y CF se iguala a AF. El
resultado se guarda en AL con los bits de 4 a 7 puestos a 0.
Ejemplo: sub al,bl
aas
En el ejemplo, tras la resta de dos nmeros BCD no empaquetados colocados en AL
y BL, el resultado (por medio de AAS) sigue siendo un nmero BCD no empaquetado.
CMP (comparacin)
Sintaxis: CMP destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Resta origen de destino sin retornar ningn resultado. Los operandos quedan
inalterados, paro los indicadores pueden ser consultados mediante instrucciones de
bifurcacin condicional. Los operandos pueden ser de tipo byte o palabra pero ambos de la
misma dimensin.
Ejemplo: cmp bx, mem_pal
cmp ch,cl
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 nmeros BCD empaquetados,
convirtindolo tambin en un valor BCD empaquetado. Si el nibble inferior tiene un valor
mayor que 9 o AF es 1, a AL se le resta 6, AF se pone a 1. Si el nibble mas significativo
es mayor que 9 CF est a 1, entonces se resta 60h a AL y se activa despus CF.
52 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Ejemplo: sub al,bl
das
En el ejemplo anterior, si AL y BL contenan dos nmeros BCD empaquetados, DAS
hace que el resultado de la resta (en AL) siga siendo tambin un BCD empaquetado.
DEC (decrementar)
Sintaxis: DEC destino
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x -
Resta una unidad del operando destino. El operando puede ser byte o palabra.
Obsrvese que esta instruccin no modifica el bit de acarreo (CF) y no es posible detectar
un desbordamiento por este procedimiento (utilcese ZF).
Ejemplo: dec ax
dec mem_byte
NEG (negacin)
Sintaxis: NEG destino
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Calcula el valor negativo en complemento a dos del operando y devuelve el resultado
en el mismo operando.
Ejemplo: neg al
SBB (resta con acarreo)
Sintaxis: SBB destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Resta el operando origen del operando destino y el resultado lo almacena en el
operando destino. Si est a 1 el indicador de acarreo adems resta una unidad ms. Los
operandos pueden ser de tipo byte o palabra. Se utiliza normalmente para restar nmeros
grandes, de ms de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo)
de la resta anterior.
Ejemplo: sbb ax,ax
sbb ch,dh
SUB (resta)
Sintaxis: SUB destino, origen
53 JUEGO DE INSTRUCCIONES 80x86
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Resta el operando destino al operando origen, colocando el resultado en el operando
destino. Los operandos pueden tener o no signo, siendo necesario que sean del mismo tipo,
byte o palabra.
Ejemplos: sub al,bl
sub dx,dx
* * * M U L T I P L I C A C I O N * * *
AAM (ajuste ASCII para la multiplicacin)
Sintaxis: AAM
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
Corrige el resultado en AX del producto de dos nmeros BCD no empaquetados,
convirtindolo en un valor BCD tambin no empaquetado. En AH sita el cociente de AL/10
quedando en AL el resto de dicha operacin.
Ejemplo: mul bl
aam
En el ejemplo, tras el producto de dos nmeros BCD no empaquetados colocados en
AL y BL, el resultado (por medio de AAA) sigue siendo, en AX, un nmero BCD no
empaquetado.
IMUL (multiplicacin 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 CF
x - - - ? ? ? ? x
Multiplica un operando origen con signo de longitud byte o palabra por AL o AX
respectivamente. Si origen es un byte el resultado se guarda en AH (byte ms 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 ms significativas son distintas de cero,
independientemente del signo, CF y OF son activados.
Ejemplo: imul bx
imul ch
MUL (multiplicacin sin signo)
Sintaxis: MUL origen (origen no puede ser operando inmediato)
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - ? ? ? ? x
Multiplica el contenido sin signo del acumulador por el operando origen. Si el
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 el
contenido de AH es distinto de 0 activa los indicadores CF y OF. Cuando el operando origen
es de longitud palabra el acumulador es AX quedando el resultado sobre DX y AX, si el
valor de DX es distinto de cero los indicadores CF y OF se activan.
Ejemplo: mul byte ptr ds:[di]
mul dx
mul cl
* * * D I V I S I O N * * *
AAD (ajuste ASCII para la divisin)
Sintaxis: AAD
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
Convierte dos nmeros BCD no empaquetados contenidos en AH y AL en un
dividendo de un byte que queda almacenado en AL. Tras la operacin AH queda a cero. Esta
instruccin es necesaria ANTES de la operacin de dividir, al contrario que AAM.
Ejemplo: aad
div bl
En el ejemplo, tras convertir los dos nmeros BCD no empaquetados (en AX) en un
dividendo vlido, la instruccin de dividir genera un resultado correcto.
DIV (divisin 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 nmero contenido en el acumulador y su extensin
(AH, AL si el operando es de tipo byte o DX, AX si el operando es palabra) entre el
operando fuente. El cociente se guarda en AL o AX y el resto en AH o DX segn el
operando sea byte o palabra respectivamente. DX o AH deben ser cero antes de la operacin.
Cuando el cociente es mayor que el resultado mximo que puede almacenar, cociente y resto
quedan indefinidos producindose una interrupcin 0. En caso de que las partes ms
significativas del cociente tengan un valor distinto de cero se activan los indicadores CF y
OF.
Ejemplo: div bl
div mem_pal
IDIV (divisin entera)
Sintaxis: IDIV origen (origen no puede ser operando inmediato)
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - ? ? ? ? ?
55 JUEGO DE INSTRUCCIONES 80x86
Divide, considerando el signo, un nmero contenido en el acumulador y su extensin
entre el operando fuente. El cociente se almacena en AL o AX segn el operando sea byte
o palabra y de igual manera el resto en AH o DX. DX o AH deben ser cero antes de la
operacin. Cuando el cociente es positivo y superior al valor mximo que puede almacenarse
(7fh 7fffh), o cuando el cociente es negativo e inferior al valor mnimo que puede
almacenarse (81h u 8001h) entonces cociente y resto quedan indefinidos, generndose una
interrupcin 0, lo que tambin sucede si el divisor es 0.
Ejemplo: idiv bl
idiv bx
* * * C O N V E R S I O N E S * * *
CBW (conversin 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, expande
el signo de AL a AX como paso previo a una operacin de 16 bits.
CWD (conversin 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 ms
significativo de AH en todo DX.
4.1.7. - INSTRUCCIONES DE MANIPULACIN DE CADENAS.
CMPS/CMPSB/CMPSW (compara cadenas)
Sintaxis: CMPS cadena_destino, cadena_origen
CMPSB (bytes)
CMPSW (palabras)
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Compara dos cadenas restando al origen el destino. Ninguno de los operandos se
alteran, pero los indicadores resultan afectados. La cadena origen se direcciona con registro
SI sobre el segmento de datos DS y la cadena destino se direcciona con el registro DI sobre
el segmento extra ES. Los registros DI y SI se autoincrementan o autodecrementan segn el
valor del indicador DF (vanse CLD y STD) en una o dos unidades, dependiendo de si se
trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos
redundantes que slo indican el tipo del dato (byte o palabra) a comparar, es ms cmodo
colocar CMPSB o CMPSW para indicar bytes/palabras. Si se indica un registro de segmento,
ste sustituir en la cadena origen al DS ordinario. Ejemplo:
56 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
lea si,origen
lea di,destino
cmpsb
LODS/LODSB/LODSW (cargar cadena)
Sintaxis: LODS cadena_origen
LODSB (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 el
segmento de datos (DS) con el registro SI. Tras la transferencia, SI se incrementa o
decrementa segn el indicador DF (vanse CLD y STD) en una o dos unidades, segn se
estn manejando bytes o palabras. Cadena_origen es un operando redundante que slo
indica el tipo del dato (byte o palabra) a cargar, es ms cmodo colocar LODSB o LODSW
para indicar bytes/palabras.
Ejemplo: cld
lea si,origen
lodsb
MOVS/MOVSB/MOVSW (mover cadena)
Sintaxis: MOVS cadena_destino, cadena_origen
MOVSB (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 la
cadena destino direccionada por ES:DI, incrementando o decrementando a continuacin los
registros SI y DI segn el valor de DF (vanse CLD y STD) en una o dos unidades,
dependiendo de si se trabaja con bytes o con palabras. Cadena origen y cadena destino
son dos operandos redundantes que slo indican el tipo del dato (byte o palabra) a comparar,
es ms cmodo colocar MOVSB o MOVSW para indicar bytes/palabras. Si se indica un
registro de segmento, ste sustituir en la cadena origen al DS ordinario.
Ejemplo: lea si,origen
lea di,destino
movsw
SCAS/SCASB/SCASW (explorar cadena)
Sintaxis: SCAS cadena_destino
SCASB (bytes)
SCASW (palabras)
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
57 JUEGO DE INSTRUCCIONES 80x86
Resta de AX o AL una cadena destino direccionada por el registro DI sobre el
segmento extra. Ninguno de los valores es alterado pero los indicadores se ven afectados. DI
se incrementa o decrementa segn el valor de DF (vanse CLD y STD) en una o dos
unidades -segn se est trabajando con bytes o palabras- para apuntar al siguiente elemento
de la cadena. Cadena_destino es un operando redundante que slo indica el tipo del dato
(byte o palabra), es ms cmodo colocar SCASB o SCASW para indicar bytes/palabras.
Ejemplo: lea di,destino
mov al,50
scasb
STOS/STOSB/STOSW (almacena cadena)
Sintaxis: STOS cadena_destino
STOSB (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 por
el registro DI sobre el segmento extra. Tras la operacin, DI se incrementa o decrementa
segn el indicador DF (vanse CLD y STD) para apuntar al siguiente elemento de la cadena.
Cadena_destino es un operando redundante que slo indica el tipo del dato (byte o palabra)
a cargar, es ms cmodo colocar STOSB o STOSW para indicar bytes/palabras.
Ejemplo: lea di,destino
mov ax,1991
stosw
REP/REPE/REPZ/REPNE/REPNZ (repetir)
REP repetir operacin de cadena
REPE/REPZ repetir operacin de cadena si igual/si cero
REPNE/REPNZ repetir operacin de cadena si no igual (si no 0)
Estas instrucciones se pueden colocar como prefijo de otra instruccin de manejo de
cadenas, con objeto de que la misma se repita un nmero determinado de veces
incondicionalmente o hasta que se verifique alguna condicin. El nmero de veces se indica
en CX. Por sentido comn slo deben utilizarse las siguientes combinaciones:
Prefijo Funcin Instrucciones
----------- ------------------------------- ----------------
REP Repetir CX veces MOVS, STOS
REPE/REPZ Repetir CX veces mientras ZF=1 CMPS, SCAS
REPNE/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,tabla
MOV CX,200
MOV AL,69
CLD
REPNE SCASB
JE encontrado
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,datos
MOV AX,0
MOV CX,2500
CLD
REP 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 pantalla
MOV DS,CX ; en DS
LEA DI,buffer ; destino en ES:DI
MOV SI,0 ; copiar desde DS:0
MOV CX,2000 ; 2000 palabras
CLD ; hacia adelante
REP MOVSW ; copiar CX palabras
4.1.8. - INSTRUCCIONES DE OPERACIONES LGICAS A NIVEL DE BIT.
AND (y lgico)
Sintaxis: AND destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operacin de Y lgico entre el operando origen y destino quedando el
resultado en el destino. Son vlidos operandos byte o palabra, pero ambos del mismo tipo.
Ejemplos: and ax,bx
and bl,byte ptr es:[si+10h]
NOT (no lgico)
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 lgico)
Sintaxis: OR destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operacin O lgico a nivel de bits entre los dos operandos,
almacenndose despus el resultado en el operando destino.
Ejemplo: or ax,bx
59 JUEGO DE INSTRUCCIONES 80x86
TEST (comparacin lgica)
Sintaxis: TEST destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operacin Y lgica entre los dos operandos pero sin almacenar el
resultado. Los indicadores son afectados con la operacin.
Ejemplo: test al,bh
XOR (O exclusivo)
Sintaxis: XOR destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Operacin OR exclusivo a nivel de bits entre los operandos origen y destino
almacenndose el resultado en este ltimo.
Ejemplo: xor di,ax
4.1.9. - INSTRUCCIONES DE CONTROL DEL PROCESADOR.
NOP (operacin nula)
Sintaxis: NOP
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Realiza una operacin nula, es decir, el microprocesador decodifica la instruccin y
pasa a la siguiente. Realmente se trata de la instruccin XCHG AX,AX.
ESC (salida a un coprocesador)
Sintaxis: ESC cdigo_operacin, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Se utiliza en combinacin con procesadores externos, tales como los coprocesadores
de coma flotante o de E/S, y abre al dispositivo externo el acceso a las direcciones y
operandos requeridos. Al mnemnico ESC le siguen los cdigos de operacin apropiados
para el coprocesador as como la instruccin y la direccin del operando necesario.
Ejemplo: esc 21,ax
HLT (parada hasta interrupcin o reset)
Sintaxis: HLT
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 interrupcin.
Como en los PC se producen normalmente 18,2 interrupciones de tipo 8 por segundo (del
temporizador) algunos programadores utilizan HLT para hacer pausas y bucles de retardo.
Sin embargo, el mtodo 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 instruccin que se utiliza en aplicaciones de recursos compartidos para
asegurar que no accede simultneamente a la memoria ms de un procesador. Cuando una
instruccin va precedida por LOCK, el procesador bloquea inmediatamente el bus,
introduciendo una seal 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 seal en la patilla TEST.
Ocurre, por ejemplo, cuando el copro ha terminado una operacin e indica su finalizacin.
Suele preceder a ESC para sincronizar las acciones del procesador y coprocesador.
4.1.10. - INSTRUCCIONES DE ROTACIN Y DESPLAZAMIENTO.
RCL (rotacin a la izquierda con acarreo)
Sintaxis: RCL destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - - - - - x
Rotar a la izquierda los bits del operando destino junto con el indicador de acarreo
CF el nmero de bits especificado en el segundo operando. Si el nmero de bits a desplazar
es 1, se puede especificar directamente, en caso contrario el valor debe cargarse en CL y
especificar CL como segundo operando. No es conveniente que CL sea mayor de 7, en bytes;
15, en palabras.
CF alto bajo RCL RCL
Ejemplos: rcl ax,1
rcl al,cl
rcl di,1
61 JUEGO DE INSTRUCCIONES 80x86
RCR (rotacin a la derecha con acarreo)
Sintaxis: RCR destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - - - - - x
Rotar a la derecha los bits del operando destino junto con el indicador de acarreo CF
el nmero de bits especificado en el segundo operando. Si el nmero de bits es 1 se puede
especificar directamente; en caso contrario su valor debe cargarse en CL y especificar CL
como segundo operando:
alto bajo CF RCR RCR
Ejemplos: rcr bx,cl
rcr bx,1
ROL (rotacin a la izquierda)
Sintaxis: ROL destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - - - - - x
Rota a la izquierda los bits del operando destino el nmero de bits especificado en
el segundo operando, que puede ser 1 CL previamente cargado con el valor del nmero de
veces.
CF alto bajo ROL ROL
Ejemplos: rol dx,cl
rol ah,1
ROR (rotacin a la derecha)
Sintaxis: ROR destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - - - - - x
Rota a la derecha los bits del operando destino el nmero de bits especificado en el
segundo operando. Si el nmero de bits es 1 se puede poner directamente, en caso contrario
debe ponerse a travs de CL.
alto bajo CF ROR ROR
Ejemplos: ror cl,1
ror ax,cl
62 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
SAL/SHL (desplazamiento aritmtico a la izquierda)
Sintaxis: SAL/SHL destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x ? x x
Desplaza a la izquierda los bits del operando el nmero de bits especificado en el
segundo operando que debe ser CL si es mayor que 1 los bits desplazados.
CF alto bajo 0 SAL/SHL SAL/SHL
Ejemplos: shl dx,1
sal bx,cl
SAR (desplazamiento aritmtico a la derecha)
Sintaxis: SAR destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x ? x x
Desplaza a la derecha los bits del operando destino el nmero de bits especificado
en el segundo operando. Los bits de la izquierda se rellenan con el bit de signo del primer
operando. Si el nmero de bits a desplazar es 1 se puede especificar directamente, si es
mayor se especifica a travs de CL.
alto bajo CF SAR SAR
Ejemplos: sar ax,cl
sar bp,1
SHR (desplazamiento lgico a la derecha)
Sintaxis: SHR destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x ? x x
Desplaza a la derecha los bits del operando destino el nmero de los bits
especificados en el segundo operando. Los bits de la izquierda se llena con cero. Si el
nmero de bits a desplazar es 1 se puede especificar directamente en el caso en que no
ocurra se pone el valor en CL:
0 alto bajo CF SHR SHR
Ejemplos: shr ax,cl
shr cl,1
63 JUEGO DE INSTRUCCIONES 80x86
4.2. - RESUMEN ALFABTICO DE LAS INSTRUCCIONES Y BANDERINES. NDICE.
Nota: en el efecto de las instrucciones sobre el registro de estado se utilizar la siguiente
notacin:
- bit no modificado
? desconocido o indefinido
x modificado segn el resultado de la operacin
1 puesto siempre a 1
0 puesto siempre a 0
Instruccin Sintaxis Efecto sobre los flags pg.
OF DF IF TF SF ZF AF PF CF
AAA AAA ? - - - ? ? x ? x 49
AAD AAD ? - - - x x ? x ? 54
AAM AAM ? - - - x x ? x ? 53
AAS AAS ? - - - ? ? x ? x 51
ADC dst,fnt ADC dst,fnt x - - - x x x x x 50
ADD dst,fnt ADD dst,fnt x - - - x x x x x 50
AND dst,fnt AND dst,fnt 0 - - - x x ? x 0 58
CALL dsp CALL dsp - - - - - - - - - 46
CBW CBW - - - - - - - - - 55
CLC CLC - - - - - - - - 0 43
CLD CLD - 0 - - - - - - - 43
CLI CLI - - 0 - - - - - - 44
CMC CMC - - - - - - - - x 44
CMP dst,fnt CMP dst,fnt x - - - x x x x x 51
CMPS/CMPSB
CMPSW cdst,cfnt CMPS cdst,cfnt x - - - x x x x x 55
CWD CWD - - - - - - - - - 55
DAA DAA ? - - - x x x x x 50
DAS DAS - - - - x x x x x 51
DEC dst DEC dst x - - - x x x x - 52
DIV fnt DIV dst ? - - - ? ? ? ? ? 54
ESC opcode,fnt ESC opcode,fnt - - - - - - - - - 59
HLT HLT - - - - - - - - - 59
IDIV fnt IDIV fnt ? - - - ? ? ? ? ? 54
IMUL fnt IMUL fnt x - - - ? ? ? ? x 53
IN acum,port IN acum,port - - - - - - - - - 49
INC dst INC dst x - - - x x x x - 50
INT interrup INT interrup - - 0 0 - - - - - 48
INTO INTO - - 0 0 - - - - - 48
IRET IRET x x x x x x x x x 48
Jcc (JA, JBE...) Jcc dsp - - - - - - - - - 47
JMP JMP dsp - - - - - - - - - 46
JCXZ dsp JCXZ dsp - - - - - - - - - 47
LAHF LAHF - - - - - - - - - 43
LDS dst,fnt LDS dst,fnt - - - - - - - - - 42
LEA dst,fnt LEA dst,fnt - - - - - - - - - 42
LES dst,fnt LES dst,fnt - - - - - - - - - 43
LOCK LOCK - - - - - - - - - 60
LODS/LODSB/
LODSW cfnt LODS mem - - - - - - - - - 56
LOOP LOOP dsp - - - - - - - - - 47
LOOPcc (LOOPE...) LOOPcc dsp - - - - - - - - - 48
MOV dst,fnt MOV dst,fnt - - - - - - - - - 41
MOVS/MOVSB/
MOVSW cdst,cfnt MOVS cdst,cfnt - - - - - - - - - 56
MUL fnt MUL fnt x - - - ? ? ? ? x 53
NEG dst NEG fnt x - - - x x x x x 52
NOP NOP - - - - - - - - - 59
NOT dst NOT dst - - - - - - - - - 58
OR dst,fnt OR dst,fnt 0 - - - x x ? x 0 58
OUT port,acum OUT port,acum - - - - - - - - - 49
POP dst POP dst - - - - - - - - - 45
POPF POPF x x x x x x x x x 45
PUSH dst PUSH dst - - - - - - - - - 45
PUSHF PUSHF - - - - - - - - - 45
64 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Instruccin Sintaxis Efecto sobre los flags pg.
OF DF IF TF SF ZF AF PF CF
RCL dst,cnt RCL dst,cnt x - - - - - - - x 60
RCR dst,cnt RCR dst,cnt x - - - - - - - x 61
REP/REPE/REPZ/
REPNE/REPNZ REP - - - - - - - - - 57
RET [val] RET [val] - - - - - - - - - 47
RETF [val] RETF [val] - - - - - - - - - 47
ROL dst,cnt ROL dst,cnt x - - - - - - - x 61
ROR dst,cnt ROR dst,cnt x - - - - - - - x 61
SAHF SAHF - - - - x x x x x 43
SAL/SHL dst,cnt SAL dst,cnt x - - - x x ? x x 62
SAR dst,cnt SAR dst,cnt x - - - x x ? x x 62
SBB dst,fnt SBB dst,fnt x - - - x x x x x 52
SCAS/SCASB/
SCASW cdst SCAS cdst x - - - x x x x x 56
SHR dst,cnt SHR dst,cnt x - - - x x ? x x 62
STC STC - - - - - - - - 1 44
STD STD - 1 - - - - - - - 44
STI STI - - 1 - - - - - - 45
STOS/STOSB/
STOSW cdst STOS cdst - - - - - - - - - 57
SUB dst,fnt SUB dst,fnt x - - - x x x x x 52
TEST dst,fnt TEST dst,fnt 0 - - - x x ? x 0 59
WAIT WAIT - - - - - - - - - 60
XCHG dst,fnt XCHG dst,fnt - - - - - - - - - 41
XLAT tfnt XLAT tfnt - - - - - - - - - 42
XOR 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 divisin:
Las excepciones INT 0, debidas a una divisin por cero o a un cociente excesivamente
grande, provocan que en la pila se almacene el valor de CS:IP para la siguiente instruccin en el
8086. En el 286 y superiores se almacena el CS:IP de la propia instruccin que causa la excepcin.
- Cdigos de operacin indefinidos.
En el 286 y superiores se produce una excepcin 6 (INT 6) o, si es una instruccin 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 despus del PUSH (dos unidades menos).
- Desplazamientos y rotaciones.
El valor de desplazamiento en las operaciones de manipulacin de bits del 8086 es una
constante de 8 bits (indicada en CL); en el 286 y superiores se toma mdulo 32 (slo se consideran
los 5 bits menos significativos).
- Prefijos redundantes.
Las instrucciones tienen una longitud ilimitada en el 8086; en el 286 y superiores no pueden
exceder de 15 bytes. Por tanto, los prefijos redundantes pueden producir excepciones de cdigo de
operacin no vlido.
- Accesos al lmite 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
65 JUEGO DE INSTRUCCIONES 80x86
produce una excepcin de violacin de lmites. En el 386 y superiores se produce tambin en accesos
de 32 bits en las posiciones 0FFFDh a la 0FFFFh. Esto se cumple tanto para accesos a datos en
memoria como a instrucciones del programa en esos puntos crticos.
- LOCK.
Esta instruccin no est limitada de ninguna manera en el 8086 y en el 286. En el 386 y
superiores su uso est restringido a determinadas instrucciones.
- Ejecucin paso a paso.
La prioridad de la excepcin paso a paso en el 286 y superiores es ms alta que la de una
interrupcin 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 adems de un
registro de flags de 32 bits.
- Interrupcin NMI.
Desde el 286 y superiores, una NMI no puede interrumpir una rutina de tratamiento NMI.
- 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 excepcin de error de coprocesador, en el 8086 se almacena un CS:IP que
no incluye prefijos -si los haba-, al contrario que en el 286 y superiores.
- Lmite del primer megabyte.
En el 8086 la memoria es circular; al final del primer megabyte se vuelve a comenzar por
las posiciones ms 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 continuacin se describen las instrucciones adicionales que incorporan los 286 en modo real, que
tambin pueden ser consideradas cuando trabajamos con los microprocesadores compatibles V20 y V30, as
como con los procesadores superiores al 286. Las instrucciones del modo protegido se dirigen especialmente
a la multiprogramacin y el tiempo compartido, siendo especficas de la conmutacin de procesos y
tratamiento de la memoria virtual y no pueden emplearse directamente bajo DOS.
BOUND r16, mem16: Comprueba si el registro de 16 bits indicado como primer operando est
dentro de los lmites de una matriz. Los lmites de la matriz los definen dos palabras consecutivas
en la memoria apuntadas por mem16. Si est fuera de los lmites, se produce una interrupcin 5 en
la que el IP apilado queda apuntando a la instruccin 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 vlido hacer PUSH 40h.
IMUL puede multiplicar cualquier registro de 16 bits por una constante inmediata, devolviendo un
resultado palabra (CF=1 si no cabe en 16 bits); por ejemplo, es vlido IMUL CX,25. Tambin se
admiten tres operandos: IMUL r1, r2, imm. En este caso, se multiplica r2 por el valor inmediato
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, SI
y DI -o los saca en orden inverso-. Ideal en el manejo de interrupciones y muy usada en las BIOS
de 286 y 386.
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 rotacin distinta de 1.
4.3.3. - INSTRUCCIONES PROPIAS DEL 386 Y 486.
Adems de todas las posibilidades adicionales del 286, el 386 y el 486 permiten utilizar cualquier
registro de 32 bits de propsito general en todos los modos de funcionamiento, incluido el modo real,
tales como EAX, EBX, ECX, EDX, ESI, EDI, EBP. Sin embargo no deben intentarse
direccionamientos por encima de los 64K. En otras palabras, se pueden utilizar para acelerar las
operaciones pero no para acceder a ms memoria. Por ejemplo, si EBX > 0FFFFh, la instruccin
MOV AX,[EBX] tendra un resultado impredecible. Adems, estos procesadores cuentan con dos
segmentos ms: adems de DS, ES, CS y SS se pueden emplear tambin FS y GS. Aviso: parece ser
que en algunos 386 fallan ocasionalmente las instrucciones de multiplicar de 32 bits.
Nota: No es del todo cierto que el 386 y el 486 no permitan acceder a ms de 64 Kb en
modo real: en la seccin 4.3.6 hay un ejemplo de ello.
Los modos de direccionamiento aumentan notablemente su flexibilidad en el 386 y superiores. Con
los registros de 16 bits slo estn disponibles los modos tradicionales. En cambio, con los de 32 se
puede utilizar en el direccionamiento indirecto cualquier registro: es vlida, por ejemplo, una
instruccin del tipo MOV AX,[ECX] o MOV EDX,[EAX]. Los desplazamientos en el
direccionamiento indexado con registros de 32 bits pueden ser de 8 y tambin de 32 bits. Cuando dos
registros deben sumarse para calcular la direccin efectiva, el segundo puede estar multiplicado por
2, 4 u 8; por ejemplo, es vlida la instruccin MOV AL,[EDX+EAX*8]. Por supuesto, bajo DOS hay
que asegurarse siempre que el resultado de todas las operaciones que determinan la direccin efectiva
no excede de 0FFFFh (0FFFEh si se accede a palabras y 0FFFCh en accesos a dobles palabras en
memoria).
BOUND r32, mem32: Se admiten ahora operandos de 32 bits.
BSF/BSR: Exploracin de bits hacia adelante y atrs, 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 ms
significativo (BSR) del segundo operando: si no aparece ningn bit activo (a 1) el indicador ZF se
activa; en caso contrario se almacena en el primer operando la posicin relativa de ese bit:
MOV AX,8
BSF BX,AX
JZ ax_es_0 ; no se saltar, adems BX = 3
BT/BTC/BTR/BTS: Operaciones sobre bits: comprobacin, comprobacin y complementacin,
comprobacin y puesta a 0, comprobacin y puesta a 1. Sintaxis (ejemplo sobre BT):
67 JUEGO DE INSTRUCCIONES 80x86
BT reg, reg BT reg, imm8
Donde reg puede ser de 16 32 bits, el operando inmediato es necesariamente de 8. Estas
instrucciones copian el nmero de bit del primer operando que indique el segundo operando (entre
0 y 31) en el acarreo. A continuacin no le hacen nada a ese bit (BT), lo complementan (BTC), lo
borran (BTR) o lo activan (BTS). Ejemplo:
MOV AX,16
BTC 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. Se
puede emplear bajo DOS siempre que ESI y EDI (utilizando REP tambin ECX) no excedan de
0FFFFh.
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 puede
emplear bajo DOS siempre que ESI y EDI (utilizando REP tambin ECX) no excedan de 0FFFFh.
Jcc: Los saltos condicionales ahora pueden ser de 32 bits!. Mucho cuidado con la directiva .386
en los programas en que se desee mantener la compatibilidad con procesadores anteriores. JECXZ
se utiliza en vez de JCXZ (mismo cdigo de operacin).
LODSD: Similar a LODSW pero empleando ESI, EDI y ECX y cargando datos de 32 bits en
EAX. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP tambin ECX) no
excedan de 0FFFFh.
LSS, LFS, LGS: similar a LDS o LES pero con esos registros de segmento.
MOV CRx,reg / MOV DRx,reg y los recprocos: acceso a registros de control y depuracin.
MOVSD: Similar a MOVSW pero empleando ESI, EDI, ECX y moviendo datos de 32 bits. Se
puede emplear bajo DOS para acelerar las transferencias siempre que ESI y EDI (utilizando REP
tambin ECX) no excedan de 0FFFFh. Operando sobre la memoria de vdeo slo se obtiene ventaja
si la tarjeta es realmente de 32 bits.
MOVSX / MOVZX: carga con extensin de signo o cero. Toma el segundo operando, le extiende
adecuadamente el signo (o le pone a cero la parte alta) hasta que sea tan grande como el primer
operando y luego lo carga en el primer operando. Si el primer operando es de 16 bits, el segundo
slo puede ser de 8; si el primero es de 32 bits el segundo puede ser de 8 16. El primer operando
debe ser un registro, el segundo puede ser un registro u operando en memoria (nunca inmediato):
MOV EAX,0FFFFFFFFh
MOV AX,7FFFh ; resultado: EAX = 0FFFF7FFFh
MOVSX EAX,AX ; resultado: EAX = 000007FFFh
OUTSD: Similar a OUTSW pero empleando ESI, EDI, ECX y enviando datos de 32 bits. Se puede
emplear bajo DOS siempre que ESI y EDI (usando REP tambin ECX) no rebasen 0FFFFh.
Prefijos FS: y GS: en los accesos a memoria, referenciando a esos segmentos.
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 instruccin
POPAD falla en la mayora de los 386, incluidos los de AMD. Para solventar el fallo (que consiste
en que EAX no se restaura correctamente) basta colocar un NOP inmediatamente detrs de POPAD.
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 puede
emplear bajo DOS siempre que ESI y EDI (usando REP tambin ECX) no rebasen 0FFFFh.
SETcc reg8 mem8: Si se cumple la condicin cc, se pone a 1 el byte de memoria o registro de
8 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 precisin a la izquierda/derecha. La sintaxis es (ejemplo
sobre SHLD):
SHLD regmem16, reg16, imm8 SHLD regmem16, reg16, CL
SHLD regmem32, reg32, imm8 SHLD regmem32, reg32, CL
Donde regmem es un registro u operando en memoria, indistintamente, del tamao indicado.
En el caso de SHLD, se desplaza el primer operando a la izquierda tanto como indique el tercer
operando (contador). Una vez desplazado, los bits menos significativos se rellenan con los ms
significativos del segundo operando, que no resulta alterado. SHRD es anlogo pero al revs.
MOV AX,1234h
MOV BX,5678h
SHLD AX,BX,4 ; resultado: AX=2345h, BX=5678h
STOSD: Similar a STOSW pero empleando ESI, EDI, ECX y almacenando EAX. Se puede
emplear bajo DOS siempre que ESI y EDI (utilizando REP tambin ECX) no excedan de 0FFFFh.
4.3.4. - DETECCIN DE UN SISTEMA AT O SUPERIOR.
Hay casos en los que es necesario determinar si una mquina es AT o superior: no ya de cara a
emplear instrucciones propias del 286 en modo real (tambin 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 slo equipan mquinas AT o superiores. Es importante por tanto determinar
la presencia de un AT, de cara a evitar ciertas instrucciones que podran bloquear un PC o XT. No se debe
en estos casos comprobar los bytes de la ROM que identifican el equipo: a veces no son correctos y, adems,
la evolucin futura que tengan es impredecible. Lo ideal es verificar directamente si est instalado un 286
o superior.
PUSHF
POP AX ; AX = flags
AND AH,0Fh ; borrar nibble ms significativo
PUSH AX
POPF ; intentar poner a 0 los 4 bits ms significativos de los flags
PUSHF
POP AX
AND AH,0F0h ; seguirn valiendo 1 excepto en un 80286 o superior
CMP AH,0F0h
JE no_es_AT
JMP si_es_AT ; es 286 o superior
4.3.5. - EVALUACIN EXACTA DEL MICROPROCESADOR INSTALADO.
Sobra decir que las instrucciones avanzadas deben ser utilizadas con la previa comprobacin del tipo
de procesador, aunque slo sea para decir al usuario que se compre una mquina ms potente antes de abortar
la ejecucin del programa. Para averiguar el procesador de un ordenador puede emplearse el siguiente
programa de utilidad, basado en el procedimiento procesador? que devuelve en AX un cdigo numrico
entro 0 y 8 distinguiendo entre los 9 procesadores ms difciles de identificar de los ordenadores compatibles.
69 JUEGO 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 instruccin
CPUID 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 instruccin. De este modo, resulta trivial detectar el Pentium
o cualquier procesador posterior que aparezca. Esta instruccin est documentada, por ejemplo en alguno de
los ficheros que acompaan al Interrupt List. Para los propsitos de este libro no es preciso en general
detectar ms all del 386.
Es normal que el lector recin iniciado en el ensamblador no entienda absolutamente nada de este
programa, ya que hasta los siguientes captulos no ser explicada la sintaxis del lenguaje. En ese caso, puede
saltarse este ejemplo y continuar en el captulo siguiente, mxime si no tiene previsto trabajar con otras
instrucciones que no sean las del 8086. Por ltimo, recordar que las instrucciones especficas del 286 en modo
real tambin estn 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 Informtica - Valladolid *
; * *
; * Este programa determina el tipo de microprocesador del equipo *
; * y devuelve un cdigo ERRORLEVEL indicndolo: *
; * *
; * 0-8088, 1-8086, 2-NEC V20, 3-NEC V30, *
; * 4-80188, 5-80186, 6-286, 7-386, 8-486 *
; * *
; * Aviso: Utilizar TASM 2.0 o compatible exclusivamente. *
; * *
; ********************************************************************
cpu SEGMENT
ASSUME CS:cpu, DS:cpu
.386
ORG 100h
inicio:
LEA DX,texto_ini ; texto de saludo
MOV AH,9
INT 21h ; imprimirlo
CALL procesador? ; tipo de procesador en AX
PUSH AX ; guardarlo para el final
LEA BX,cpus_indice-2 ; tabla de nombres-2
MOV CX,0FFFFh ; nmero de iteracin-1
otro_proc: INC CX
ADD BX,2
MOV DX,[BX] ; nombre del primer procesador
CALL print
CMP CX,AX ; procesador del equipo?
JNE no_es_este
LEA DX,apuntador_txt ; s lo es: indicarlo
CALL print
no_es_este: LEA DX,separador_txt
CALL print
CMP CX,7 ; nmero de CPUs tratadas-1
JBE otro_proc
LEA DX,texto_fin ; ltimos caracteres
CALL print
MOV AH,4Ch ; retornar cdigo errorlevel AL
INT 21h ; fin de programa
procesador? PROC ; devolver el tipo de microprocesador en AX
PUSHF
PUSH DS
PUSH ES
PUSH CX
PUSH DX
PUSH DI
PUSH SI
MOV AX,CS
MOV DS,AX ; durante la rutina se guardar
MOV ES,AX ; el tipo de procesador en DL:
MOV DL,6 ; supuesto un 286 (DL=6) ...
PUSHF
POP AX ; AX = flags
AND AX,0FFFh ; borrar nibble ms significativo
PUSH AX
POPF ; intentar poner a 0 los 4 bits ms
PUSHF ; significativos de los flags
POP AX
AND AX,0F000h ; seguirn valiendo 1 excepto en
CMP AX,0F000h ; un 80286 o superior
JE ni286ni_super
PUSHF ; es 286 o superior
POP AX
OR AX,7000h ; intentar activar bit 12, 13 14
PUSH AX
POPF
PUSHF
POP AX
AND AX,7000h ; 286 pone bits 12, 13 y 14 a cero
JZ cpu_hallada ; es un 286 (DL=6)
INC DL ; es un 386 (DL=7) ... de momento
PUSH DX
CLI ; momento crtico
MOV EDX,ESP ; preservar ESP en EDX
AND ESP,0FFFFh ; borrar parte alta de ESP
AND ESP,0FFFCh ; forzar ESP a mltiplo de 4
PUSHFD ; guardar flags en pila (32 bits)
POP EAX ; recuperar flags en EAX
MOV ECX,EAX
XOR EAX,40000h ; conmutar bit 18
PUSH EAX
POPFD ; intentar cambiar este bit
PUSHFD
POP EAX ; ECX conserva el bit inicial
XOR EAX,ECX ; bit 18 de EAX a 1 si cambi
SHR EAX,12h ; mover bit 18 a bit 0
AND EAX,1 ; dejar slo ese bit
PUSH ECX
POPFD ; restaurar bit 18 de los flags
MOV ESP,EDX ; restaurar ESP
STI ; permitir interrupciones de nuevo
POP DX ; recuperar tipo de CPU en DL
CMP AX,0
JE 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,0FFFFh
MOV CL,33
SHL 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,0FFFFh
STI
DB 0F3h,26h,0ACh ; opcode de REPZ LODSB ES:
JCXZ tipo_bus_proc ; ... lo es, calcular bus (V20/V30)
XOR DL,DL ; ya slo puede ser un 8088/8086
tipo_bus_proc: STD ; transferencias hacia arriba
LEA DI,tipo_bus_dest
MOV AL,BYTE PTR DS:tipo_bus_byte ; opcode de STI
MOV CX,3
CLI
REP STOSB ; transferir tres bytes
CLD
NOP ; el INC CX (1 byte) ser machacado
NOP ; con STOSB pero an se ejecutar
NOP ; en un 8086/80186/V30 (y no en un
INC 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 bits
INC DL ; resulta que es de 16
cpu_hallada: MOV AL,DL
XOR AH,AH
POP SI
POP DI
POP DX
POP CX
POP ES
POP DS
POPF
RET ; AX = CPU: 0/1-8088/86, 2/3-NEC V20/V30
procesador? ENDP ; 4/5-80188/186, 6-286, 7-386, 8-486
print PROC
PUSH AX
PUSH BX
PUSH CX
MOV AH,9
INT 21h
POP CX
POP BX
POP AX
RET
print ENDP
cpus_indice DW i88,i86,v20,v30,i188,i186,i286,i386,i486
i88 DB "Intel 8088 $"
i86 DB "Intel 8086 $"
v20 DB " NEC V20 $"
v30 DB " NEC V30 $"
i188 DB "Intel 80188$"
i186 DB "Intel 80186$"
i286 DB "Intel 80286$"
i386 DB "Intel 80386$"
i486 DB "Intel 80486$"
apuntador_txt DB " < $"
texto_ini LABEL BYTE
DB 13,10,"CPU Test v2.2 "
DB "(c) Septiembre 1992 Ciriaco Garca 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 ENDS
END inicio
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 lmite de 64 Kb en los
segmentos en modo real. El problema es que al encender el ordenador, el 386 tiene definidos por defecto
dichos lmites de 64 Kb. Sin embargo, se puede pasar un momento a modo protegido, ampliar el lmite y
volver a modo real. Entonces se consigue el llamado modo flat o plano. No solo es factible de este modo
saltar la restriccin de 64 Kb, sino que adems se puede acceder directamente, desde el modo real, a toda la
memoria por encima del primer megabyte.
El problema es que pasar a modo protegido no es sencillo cuando la mquina ya est en modo
protegido emulando al modo real (el conocido como modo virtual 86). Por tanto, el siguiente programa de
ejemplo no funciona si est cargado un controlador de memoria expandida (EMM386, QEMM) o dentro de
Windows 3.x. Arrancando sin controlador de memoria (excepto HIMEM) no habr problema alguno. El
programa de ejemplo se limita a llenar la pantalla de texto (empleando ahora la direccin absoluta 0B8000h
a travs de EBX) de letras A.
Otra restriccin de este programa de ejemplo es que no activa la lnea A20 de direcciones; dicho de
otro modo, el bit 21 (de los 32 bits de la direccin de memoria) suele estar forzado a 0 por defecto al
arrancar. Para acceder a la memoria de vdeo esto no es problema, pero por encima del primer megabyte
podra haber problemas segn a qu direccin se pretenda acceder. De todos modos, sera relativamente
sencillo habilitar la lnea A20 directamente o a travs de una funcin del controlador XMS.
Naturalmente, se sale de los objetivos de este libro describir el modo protegido o explicar los pasos
que realiza esta rutina de demostracin. Consltese al efecto la bibliografa recomendada del apndice.
;
; Rutina para activar el modo flat del 386 y superiores (acceso
; a 4 Gb en modo real).
;
; TASM flat386 /m5
; TLINK flat386 /t /32
;
.386p ; slo para 386 o superior
segmento SEGMENT USE16
ASSUME CS:segmento, DS:segmento
ORG 100h
prueba:
CALL flat386 ; activar modo flat
XOR AX,AX
MOV DS,AX
MOV EBX,0B8000h ; direccin de vdeo absoluta
MOV CX,2000
llena_pant: MOV BYTE PTR [EBX],A
INC EBX
MOV BYTE PTR [EBX],15
INC EBX
LOOP llena_pant
INT 20h ; fin de programa
; ------------ Esta rutina pasa momentneamente a modo protegido de
; manera directa (necesita la CPU en modo real). No se
; activa la lnea A20 (necesario hacerlo directamente
; o a travs de algn servicio XMS antes de acceder a
; las reas de memoria extendida afectadas).
flat386 PROC
PUSH DS
PUSH ES
PUSH EAX
PUSH BX
PUSH CX
MOV CX,SS
XOR EAX,EAX
MOV AX,CS
SHL EAX,4 ; direccin lineal de segmento CS
ADD EAX,OFFSET gdt ; desplazamiento de GDT
MOV CS:[gd2],EAX ; guardar direccin lineal de GDT
CLI
LGDT CS:[gdtr] ; cargar tabla global de descriptores
MOV EAX,CR0
OR AL,1 ; bit de modo protegido
MOV CR0,EAX ; pasar a modo protegido
JMP SHORT $+2 ; borrar cola de prebsqueda
MOV BX,gcodl ; ndice de descriptor en BX
MOV DS,BX ; cargar registro de segmento DS
MOV ES,BX ; ES
MOV SS,BX ; SS
MOV FS,BX ; FS
MOV GS,BX ; GS
AND AL,11111110b
MOV CR0,EAX ; volver a modo real
JMP SHORT $+2 ; borrar cola de prebsqueda
MOV SS,CX
STI
POP CX
POP BX
POP EAX
POP ES
POP DS
RET
gdtr LABEL QWORD ; datos para cargar en GDTR
gd1 DW gdtl-1
gd2 DD ?
gdt DB 0,0,0,0,0,0,0,0 ; GDT
gcod DB 0ffh,0ffh,0,0,0,9fh,0cfh,0
gcodl EQU $-OFFSET gdt
gdat DB 0ffh,0ffh,0,0,0,93h,0cfh,0
gdtl EQU $-OFFSET gdt
flat386 ENDP
segmento ENDS
END prueba
71 EL LENGUAJE ENSAMBLADOR DEL 80x86
Captulo V: EL LENGUAJE ENSAMBLADOR DEL 80x86
Hasta ahora hemos visto los mnemnicos de las instrucciones que pasadas a su correspondiente cdigo
binario ya puede entender el microprocesador. Si bien se realiza un gran avance al introducir los mnemnicos
respecto a programar directamente en lenguaje maquina -es decir, con nmeros en binario o hexadecimal-
an resultara tedioso tener que realizar los clculos de los desplazamientos en los saltos a otras partes del
programa en las transferencias de control, reservar espacio de memoria dentro de un programa para almacenar
datos, etc... Para facilitar estas operaciones se utilizan las directivas que indican al ensamblador qu debe
hacer con las instrucciones y los datos.
Los programas de ejemplo de este libro y la sintaxis de ensamblador tratada son las del MASM de
Microsoft y el ensamblador de IBM. No obstante, todos los programas han sido desarrollados con el Turbo
Assembler 2.0 de Borland (TASM), compatible con el clsico MASM 5.0 de Microsoft pero ms potente y
al mismo tiempo mucho ms rpido y flexible. TASM genera adems un cdigo ms reducido y optimizado.
Por otra parte, MASM 5.0 no permite cambiar (aunque s la 6.0) dentro de un segmento el modo del
procesador: esto conlleva el riesgo de ejecutar indeseadamente instrucciones de 32 bits al no poder acotar
exactamente las lneas donde se desea emplearlas, algo vital para mantener la compatibilidad con procesadores
anteriores. Tambin es propenso a generar errores de fase y otros similares al tratar con listados un poco
grandes. Respecto a MASM 6.0, el autor de este libro encontr que en ocasiones calcula incorrectamente el
valor de algunos smbolos y etiquetas, aunque es probable que la versin 6.1 (aparecida sospechosa e
inusualmente muy poco tiempo despus) haya corregido dichos fallos, intolerables en un ensamblador. Por
otro lado, las posibilidades adicionales de TASM no han sido empleadas por lo general. Muchos programas
han sido ensamblados una vez con MASM, para asegurar que ste puede ensamblarlos.
Conviene decir aqu que este captulo es especialmente arduo para aquellos que no conocen el
lenguaje ensamblador de ninguna mquina. La razn es que la informacin est organizada a modo de
referencia, por lo que con frecuencia se utilizan unos elementos -para explicar otros- que an no han sido
definidos. Ello por otra parte resulta inevitable tambin en algunos libros ms bsicos, debido a la
complejidad de la sintaxis del lenguaje ensamblador ideada por el fabricante (que no la del microprocesador).
Por ello, es un buen consejo actuar a dos pasadas, al igual que el propio ensamblador en ocasiones: leer todo
una vez primero -aunque no se entienda del todo- y volverlo a leer despus ms despacio.
5.1. - SINTAXIS DE UNA LNEA EN ENSAMBLADOR.
Un programa fuente en ensamblador contiene dos tipos de sentencias: las instrucciones y las
directivas. Las instrucciones se aplican en tiempo de ejecucin, pero las directivas slo son utilizadas durante
el ensamblaje. El formato de una sentencia de instruccin es el siguiente:
[etiqueta] nombre_instruccin [operandos] [comentario]
Los corchetes, como es normal al explicar instrucciones en informtica, indican que lo especificado
entre ellos es opcional, dependiendo de la situacin que se trate.
Campo de etiqueta. Es el nombre simblico de la primera posicin de una instruccin,
puntero o dato. Consta de hasta 31 caracteres que pueden ser las letras de la A a la Z, los nmeros
del 0 al 9 y algunos caracteres especiales como @, _, . y $. Reglas:
72 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
- Si se utiliza el punto . ste debe colocarse como primer carcter de la etiqueta.
- El primer carcter no puede ser un dgito.
- 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 (:); esto
es, se considera cercana: quiere esto decir que cuando realizamos una llamada sobre dicha etiqueta
el ensamblador considera que est dentro del mismo segmento de cdigo (llamadas intrasegmento)
y el procesador slo carga el puntero de instrucciones IP. Tngase en cuenta que hablamos de
instrucciones; las etiquetas empleadas antes de las directivas, como las directivas de definicin de
datos por ejemplo, no llevan los dos puntos y sin embargo son cercanas.
Las etiquetas son de tipo FAR si el campo de etiqueta no termina con los dos puntos: en
estas etiquetas la instruccin a la que apunta no se encuentra en el mismo segmento de cdigo sino
en otro. Cuando es referenciada en una transferencia de control se carga el puntero de instrucciones
IP y el segmento de cdigo CS (llamadas intersegmento).
Campo de nombre. Contiene el mnemnico de las instrucciones vistas en el captulo
anterior, o bien una directiva de las que veremos ms adelante.
Campo de operandos. Indica cuales son los datos implicados en la operacin. Puede haber
0, 1 2; en el caso de que sean dos al 1 se le llama destino y al 2 -separado por una coma- fuente.
mov ax, es:[di] ax destino
es:[di] origen
Campo de comentarios. Cuando en una lnea hay un punto y coma (;) todo lo que sigue en
la lnea es un comentario que realiza aclaraciones sobre lo que se est haciendo en ese programa,
resulta de gran utilidad de cara a realizar futuras modificaciones al mismo.
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); tambin las hay de cadena (ej. pepe, "juan") e incluso con comillas dentro de comillas de distinto tipo
(como hola,"amigo"). En las hexadecimales, si el primer dgito no es numrico hay que poner un 0. Slo
se puede poner el signo (-) en las decimales (en las dems, calclese el complemento a dos). Por defecto, las
numricas estn en base 10 si no se indica lo contrario con una directiva (poco recomendable como se ver).
5.2.2. - OPERADORES ARITMTICOS.
Pueden emplearse libremente (+), (-), (*) y (/) -en este ltimo caso la divisin es siempre entera-. Es
vlida, por ejemplo, la siguiente lnea en ensamblador (que se apoya en la directiva DW, que se ver ms
adelante, para reservar memoria para una palabra de 16 bits):
dato DW 12*(numero+65)/7
Tambin se admiten los operadores MOD (resto de la divisin) y SHL/SHR (desplazar a la
izquierda/derecha cierto nmero de bits). Obviamente, el ensamblador no codifica las instrucciones de
desplazamiento (al aplicarse sobre datos constantes el resultado se calcula en tiempo de ensamblaje):
dato DW (12 SHR 2) + 5
73 EL LENGUAJE ENSAMBLADOR DEL 80x86
5.2.3. - OPERADORES LGICOS.
Pueden ser el AND, OR, XOR y NOT. Realizan las operaciones lgicas en las expresiones. Ej.:
MOV BL,(255 AND 128) XOR 128 ; BL = 0
5.2.4. - OPERADORES RELACIONALES.
Devuelven condiciones de cierto (0FFFFh 0FFh) o falso (0) evaluando una expresin. Pueden ser:
EQ (igual), NE (no igual), LT (menor que), GT (mayor que), LE (menor o igual que), GE (mayor o igual
que). Ejemplo:
dato EQU 100 ; dato vale 100
MOV 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, slo se puede emplear
en 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) de
segmentos en que est definida y no respecto al segmento concreto en que est definida:
MOV AX,OFFSET nombre_grupo:variable
tambin es vlido:
MOV AX,OFFSET DS:variable
Operador .TYPE: devuelve el modo de la expresin indicada en un byte. El bit 0 indica modo
relativo al cdigo y el 1 modo relativo a datos, si ambos bits estn inactivos significa modo
absoluto. El bit 5 indica si la expresin es local (0 si est definida externamente o indefinida); el bit
7 indica si la expresin contiene una referencia externa. El TASM utiliza tambin el bit 3 para indicar
algo que desconozco. Este operador es til sobre todo en las macros para determinar el tipo de los
parmetros:
info .TYPE variable
Operador TYPE: devuelve el tamao (bytes) de la variable indicada. No vlido en variables DUP:
kilos DW 76
MOV AX,TYPE kilos ; AX = 2
Tratndose de etiquetas -en lugar de variables- indica si es lejana o FAR (0FFFEh) o cercana
o NEAR (0FFFFh).
Operadores SIZE y LENGTH: devuelven el tamao (en bytes) o el n de elementos,
respectivamente, de la variable indicada (definida obligatoriamente con DUP):
matriz DW 100 DUP (12345)
MOV AX,SIZE matriz ; AX = 200
MOV BX,LENGTH matriz ; BX = 100
Operadores MASK y WIDTH: informan de los campos de un registro de bits (vase RECORD).
5.2.6. - OPERADORES DE ATRIBUTOS.
Operador PTR: redefine el atributo de tipo (BYTE, WORD, DWORD, QWORD, TBYTE) o el de
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 de
la siguiente manera:
tabla DW 10 DUP (0) ; 10 palabras a 0
Para colocar en AL el primer byte de la misma, la instruccin MOV AL,tabla es incorrecta,
ya que tabla (una cadena 10 palabras) no cabe en el registro AL. Lo que desea el programador debe
indicrselo en este caso explcitamente al ensamblador de la siguiente manera:
MOV AL,BYTE PTR tabla
Trabajando con varios segmentos, PTR puede redefinir una etiqueta NEAR de uno de ellos
para convertirla en FAR desde el otro, con objeto de poder llamarla.
Operadores CS:, DS:, ES: y SS: el ensamblador genera un prefijo de un byte que indica al
microprocesador el segmento que debe emplear para acceder a los datos en memoria. Por defecto,
se supone DS para los registros BX, DI o SI (o sin registros de base o ndice) y SS para SP y BP.
Si al acceder a un dato ste no se encuentra en el segmento por defecto, el ensamblador aadir el
byte adicional de manera automtica. Sin embargo, el programador puede forzar tambin esta
circunstancia:
MOV AL,ES:variable
En el ejemplo, variable se supone ubicada en el segmento extra. Cuando se referencia una
direccin 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 lnea dar un
error 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 instrucciones
MOV operan por defecto sobre DS (como casi todas), pero ha sido necesario indicar DS para que
el ensamblador nos entienda. Sin embargo, en el siguiente ejemplo no es necesario, ya que midato
est 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 explcitamente el segmento: al
acceder a una variable el ensamblador mira en qu segmento est declarada (vase la directiva
SEGMENT) y segn como estn asignados los ASSUME, pondr o no el prefijo adecuado segn sea
conveniente. Es responsabilidad exclusiva del programador inicializar los registros de segmento al
principio de los procedimientos para que el ASSUME no se quede en tinta mojada... s se emplean
con bastante frecuencia, sin embargo, los prefijos CS en las rutinas que gestionan interrupciones (ya
que CS es el nico registro de segmento que apunta en principio a las mismas, hasta que se cargue
DS u otro).
Operador SHORT: indica que la etiqueta referenciada, de tipo NEAR, puede alcanzarse con un
salto corto (-128 a +127 posiciones) desde la actual situacin del contador de programa. El
ensamblador TASM, si se solicitan dos pasadas, coloca automticamente instrucciones SHORT all
donde es posible, para economizar memoria (el MASM no).
Operador $: indica la posicin del contador de posiciones (Location Counter) utilizado por el
ensamblador dentro del segmento para llevar la cuenta de por dnde se llega ensamblando. Muy til:
frase DB "simptico"
longitud EQU $-OFFSET frase
75 EL 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 expresin:
dato EQU 1025
MOV AL,LOW dato ; AL = 1
MOV AH,HIGH dato ; AH = 4
5.3. - PRINCIPALES DIRECTIVAS.
La sintaxis de una sentencia directiva es muy similar a la de una sentencia de instruccin:
[nombre] nombre_directiva [operandos] [comentario]
Slo es obligatorio el campo nombre_directiva; los campos han de estar separados por al menos
un espacio en blanco. La sintaxis de nombre es anloga a la de la etiqueta de las lneas de instrucciones,
aunque nunca se pone el sufijo :. El campo de comentario cumple tambin las mismas normas. A
continuacin se explican las directivas empleadas en los programas ejemplo de este libro y alguna ms,
aunque falta alguna que otra y las explicadas no lo estn en todos los casos con profundidad.
5.3.1. - DIRECTIVAS DE DEFINICIN DE DATOS.
DB (definir byte), DW (definir palabra), DD (definir doble palabra), DQ (definir cudruple
palabra), DT (definir 10 bytes): sirven para declarar las variables, asignndolas un valor inicial:
anno DW 1991
mes DB 12
numerazo DD 12345678h
texto DB "Hola",13,10
Se pueden definir nmeros reales de simple precisin (4 bytes) con DD, de doble precisin
(8 bytes) con DQ y reales temporales (10 bytes) con DT; todos ellos con el formato empleado por
el coprocesador. Para que el ensamblador interprete el nmero como real ha de llevar el punto
decimal:
temperatura DD 29.72
espanoles91 DQ 38.9E6
Con el operando DUP pueden definirse estructuras repetitivas. Por ejemplo, para asignar 100
bytes a cero y 25 palabras de contenido indefinido (no importa lo que el ensamblador asigne):
ceros DB 100 DUP (0)
basura DW 25 DUP (?)
Se admiten tambin los anidamientos. El siguiente ejemplo crea una tabla de bytes donde se
repite 50 veces la secuencia 1,2,3,7,7:
tabla DB 50 DUP (1, 2, 3, 2 DUP (7))
5.3.2. - DIRECTIVAS DE DEFINICIN DE SMBOLOS.
EQU (EQUivalence): Asigna el valor de una expresin a un nombre simblico fijo:
olimpiadas EQU 1992
Donde olimpiadas ya no podr cambiar de valor en todo el programa. Se trata de un operador
muy flexible. Es vlido hacer:
edad EQU [BX+DI+8]
MOV AX,edad
76 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
= (signo =): asigna el valor de la expresin a un nombre simblico variable: Anlogo al anterior
pero con posibilidad de cambiar en el futuro. Muy usada en macros (sobre todo con REPT).
num = 19
num = pepe + 1
dato = [BX+3]
dato = ES:[BP+1]
5.3.3. - DIRECTIVAS DE CONTROL DEL ENSAMBLADOR.
ORG (ORiGin): pone el contador de posiciones del ensamblador, que indica el offset donde se
deposita la instruccin o dato, donde se indique. En los programas COM (que se cargan en memoria
con un OFFSET 100h) es necesario colocar al principio un ORG 100h, y un ORG 0 en los
controladores de dispositivo (aunque si se omite se asume de hecho un ORG 0).
END [expresin]: indica el final del fichero fuente. Si se incluye, expresin indica el punto donde
arranca el programa. Puede omitirse en los programas EXE si stos constan de un slo mdulo. En
los COM es preciso indicarla y, adems, la expresin -realmente una etiqueta- debe estar
inmediatamente despus del ORG 100h.
.286, .386 Y .8087 obligan al ensamblador a reconocer instrucciones especficas del 286, el 386
y del 8087. Tambin debe ponerse el . inicial. Con .8086 se fuerza a que de nuevo slo se
reconozcan instrucciones del 8086 (modo por defecto). La directiva .386 puede ser colocada dentro
de un segmento (entre las directivas SEGMENT/ENDS) con el ensamblador TASM, lo que permite
emplear instrucciones de 386 con segmentos de 16 bits; alternativamente se puede ubicar fuera de
los segmentos (obligatorio en MASM) y definir stos explcitamente como de 16 bits con USE16.
EVEN: fuerza el contador de posiciones a una posicin par, intercalando un byte con la instruccin
NOP si es preciso. En buses de 16 ms bits (8086 y superiores, no en 8088) es dos veces ms
rpido el acceso a palabras en posicin par:
EVEN
dato_rapido DW 0
.RADIX n: cambia la base de numeracin por defecto. Bastante desaconsejable dada la notacin
elegida para indicar las bases por parte de IBM/Microsoft (si se cambia la base por defecto a 16, los
nmeros no pueden acabar en d ya que se confundiran con el sufijo de decimal!: lo ideal sera
emplear un prefijo y no un sufijo, que a menudo obliga adems a iniciar los nmeros por 0 para
distinguirlos de las etiquetas).
5.3.4. - DIRECTIVAS DE DEFINICIN DE SEGMENTOS Y PROCEDIMIENTOS.
SEGMENT-ENDS: SEGMENT indica el comienzo de un segmento (cdigo, datos, pila, etc.) y
ENDS su final. El programa ms simple, de tipo COM, necesita la declaracin de un segmento
(comn para datos, cdigo y pila). Junto a SEGMENT puede aparecer, opcionalmente, el tipo de
alineamiento, la combinacin, el uso y la clase:
nombre SEGMENT [alineamiento] [combinacin] [uso] [clase]
. . . .
nombre ENDS
Se pueden definir unos segmentos dentro de otros (el ensamblador los ubicar unos tras
otros). El alineamiento puede ser BYTE (ninguno), WORD (el segmento comienza en posicin par),
DWORD (comienza en posicin mltiplo de 4), PARA (comienza en una direccin mltiplo de 16,
opcin por defecto) y PAGE (comienza en direccin mltiplo de 256). La combinacin puede ser:
77 EL LENGUAJE ENSAMBLADOR DEL 80x86
- (No indicada): los segmentos se colocan unos tras otros fsicamente, pero son
lgicamente independientes: cada uno tiene su propia base y sus propios offsets relativos.
- PUBLIC: usado especialmente cuando se trabaja con segmentos definidos en varios
ficheros que se ensamblan por separado o se compilan con otros lenguajes, por ello debe
declararse un nombre entre comillas simples -clase- para ayudar al linkador. Todos los
segmentos PUBLIC de igual nombre y clase tienen una base comn y son colocados
adyacentemente unos tras otros, siendo el offset relativo al primer segmento cargado.
- COMMON: similar, aunque ahora los segmentos de igual nombre y clase se
solapan. Por ello, las variables declaradas han de serlo en el mismo orden y tamao.
- AT: asocia un segmento a una posicin de memoria fija, no para ensamblar sino
para declarar variables (inicializadas siempre con ?) de cara a acceder con comodidad a
zonas de ROM, vectores de interrupcin, etc. Ejemplo:
vars_bios SEGMENT AT 40h
p_serie0 DW ?
vars_bios ENDS
De esta manera, la direccin del primer puerto serie puede obtenerse de esta
manera (por ejemplo):
MOV AX,variables_bios ; segmento
MOV ES,AX ; inicializar ES
MOV AX,ES:p_serie0
- STACK: segmento de pila, debe existir uno en los programas de tipo EXE; adems
el Linkador de Borland (TLINK 4.0) exige obligatoriamente que la clase de ste sea tambin
STACK, con el LINK de Microsoft no siempre es necesario indicar la clase del segmento
de pila. Similar, por lo dems, a PUBLIC.
- MEMORY: segmento que el linkador ubicar al final de todos los dems, lo que
permitira saber dnde acaba el programa. Si se definen varios segmentos de este tipo el
ensamblador acepta el primero y trata a los dems como COMMON. Tngase en cuenta que
el linkador no soporta esta caracterstica, por lo que emplear MEMORY es equivalente a
todos los efectos a utilizar COMMON. Olvdate de MEMORY.
El uso indica si el segmento es de 16 bits o de 32; al emplear la directiva .386 se asumen
por defecto segmentos de 32 bits por lo que es necesario declarar USE16 para conseguir que los
segmentos sean interpretados como de 16 bits por el linkador, lo que permite emplear algunas
instrucciones del 386 en el modo real del microprocesador y bajo el sistema operativo DOS.
Por ltimo, clase es un nombre opcional que emplear el linkador para encadenar los
mdulos, siendo conveniente nombrar la clase del segmento de pila con STACK.
ASSUME (Suponer): Indica al ensamblador el registro de segmento que se va a utilizar para
direccionar cada segmento dentro del mdulo. Esta instruccin va normalmente inmediatamente
despus del SEGMENT. El programa ms sencillo necesita que se suponga CS como mnimo para
el segmento de cdigo, de lo contrario el ensamblador empezar a protestar un montn al no saber
que registro de segmento asociar al cdigo generado. Tambin conviene hacer un assume del registro
de segmento DS hacia el segmento de datos, incluso en el caso de que ste sea el mismo que el de
cdigo: si no, el ensamblador colocar un byte de prefijo adicional en todos los accesos a memoria
para forzar que stos sean sobre CS. Se puede indicar ASSUME NOTHING para cancelar un
ASSUME anterior. Tambin 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.
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 procedimiento
lejano y las instrucciones RET en su interior se ensamblan como RETF (los CALL hacia l sern,
adems, de 32 bits). Observar que la etiqueta nunca termina con dos puntos.
5.3.5. - DIRECTIVAS DE REFERENCIAS EXTERNAS.
PUBLIC: permite hacer visibles al exterior (otros ficheros objeto resultantes de otros listados en
ensamblador u otro lenguaje) los smbolos -variables y procedimientos- indicados. Necesario para
programacin modular e interfaces con lenguajes de alto nivel. Por ejemplo:
PUBLIC proc1, var_x
proc1 PROC FAR
proc1 ENDP
var_x DW 0
Declara la variable var_x y el procedimiento proc1 como accesibles desde el exterior por
medio de la directiva EXTRN.
EXTRN: Permite acceder a smbolos definidos en otro fichero objeto (resultante de otro ensamblaje
o de una compilacin de un lenguaje de alto nivel); es necesario tambin indicar el tipo del dato o
procedimiento (BYTE, WORD o DWORD; NEAR o FAR; se emplea adems ABS para las
constantes numricas):
EXTRN proc1:FAR, var_x:WORD
En el ejemplo se accede a los smbolos externos proc1 y var_x (ver ejemplos de PUBLIC)
y a continuacin sera posible hacer un CALL proc1 o un MOV CX,var_x. Si la directiva EXTRN
se coloca dentro de un segmento, se supone el smbolo dentro del mismo. Si el smbolo est en otro
segmento, debe colocarse EXTRN fuera de todos los segmentos indicando explcitamente el prefijo
del registro de segmento (o bien hacer el ASSUME apropiado) al referenciarlo. Evidentemente, al
final, al linkar habr que enlazar este mdulo con el que define los elementos externos.
INCLUDE nombre_fichero: Aade 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 con
un editor de texto. Ahorra trabajo en fragmentos de cdigo que se repiten en varios programas (como
quiz una librera de macros). No se recomiendan INCLUDEs anidados.
5.3.6. - DIRECTIVAS DE DEFINICIN DE BLOQUES.
NAME nombre_modulo_objeto: indica el nombre del mdulo objeto. Si no se incluye NAME, se
tomar de la directiva TITLE o, en su defecto, del nombre del propio fichero fuente.
GROUP segmento1, segmento2,... permite agrupar dos o ms segmentos lgicos en uno slo de
no ms 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
79 EL LENGUAJE ENSAMBLADOR DEL 80x86
datos SEGMENT
dato DW 1234
datos ENDS
pila SEGMENT STACK STACK
DB 128 DUP (?)
pila ENDS
Cuando se accede a un dato definido en algn segmento de un grupo y se emplea el operador
OFFSET es preciso indicar el nombre del grupo como prefijo, de lo contrario el ensamblador no
generar 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 varios
segmentos. En todo caso, tngase en cuenta an en ese caso que no pueden emplearse todas las
caractersticas de la programacin con segmentos (por ejemplo, no se puede utilizar la directiva SEG
ni debe existir segmento de pila).
LABEL: Permite referenciar un smbolo con otro nombre, siendo factible redefinir el tipo. La
sintaxis es: nombre LABEL tipo (tipo = BYTE, WORD, DWORD, NEAR o FAR). Ejemplo:
palabra LABEL WORD
byte_bajo DB 0
byte_alto DB 0
En el ejemplo, con MOV AX,palabra se acceder a ambos bytes a la vez (el empleo de MOV
AX,byte_bajo dara error: no se puede cargar un slo byte en un registro de 16 bits y el ensamblador
no supone que realmente pretendamos tomar dos bytes consecutivos de la memoria).
STRUC - ENDS: permite definir registros al estilo de los lenguajes de alto nivel, para acceder de
una manera ms elegante a los campos de una informacin con cierta estructura. Estos campos
pueden componerse de cualquiera de los tipos de datos simples (DB, DW, DD, DQ, DT) y pueden
ser modificables o no en funcin de si son simples o mltiples, respectivamente:
alumno STRUC
mote DB 0123456789 ; modificable
edadaltura DB 20,175 ; no modificable
peso DB 0 ; modificable
otros DB 10 DUP(0) ; no modificable
telefono DD ? ; modificable
alumno ENDS
La anterior definicin de estructura no lleva implcita la reserva de memoria necesaria, la cual
ha 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, crendose la estructura felipe que ocupa 27 bytes. Las
cadenas de caracteres son rellenadas con espacios en blanco al final si no alcanzan el tamao mximo
de la declaracin. El TASM es ms flexible y permite definir tambin el primer elemento de los
campos mltiples sin dar error. Tras crear la estructura, es posible acceder a sus elementos utilizando
un (.) para separar el nombre del campo:
MOV AX,OFFSET felipe.telefono
LEA BX,felipe
MOV CL,[BX].peso ; equivale a [BX+12]
RECORD: similar a STRUC pero operando con campos de bits. Permite definir una estructura
determinada de byte o palabra para operar con comodidad. Sintaxis:
80 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
nombre RECORD nombre_de_campo:tamao[=valor],...
Donde nombre permitir referenciar la estructura en el futuro, nombre_de_campo identifica
los distintos campos, a los que se asigna un tamao (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 campos
que ocupan los 7 bits menos significativos del byte: el campo A ocupa los bits 6 y 5, el B los bits
1 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 C
toman los valores indicados). Ejemplos de operaciones soportadas:
MOV AL, A ; AL = 5 (desplazamiento del bit
; menos significativo de A)
MOV AL, MASK A ; AL = 01100000b (mscara de A)
MOV AL, WIDTH A ; AL = 2 (anchura de A)
5.3.7. - DIRECTIVAS CONDICIONALES.
Se emplean para que el ensamblador evale unas condiciones y, segn ellas, ensamble o no
ciertas zonas de cdigo. Es frecuente, por ejemplo, de cara a generar cdigo para varios ordenadores:
pueden existir ciertos smbolos definidos que indiquen en un momento dado si hay que ensamblar
ciertas zonas del listado o no de manera condicional, segn la mquina. En los fragmentos en
ensamblador del cdigo que generan los compiladores tambin aparecen con frecuencia (para actuar
de manera diferente, por ejemplo, segn el modelo de memoria). Es interesante tambin la posibilidad
de definir un smbolo que indique que el programa est en fase de pruebas y ensamblar cdigo
adicional en ese caso con objeto de depurarlo. Sintaxis:
IFxxx [smbolo/exp./arg.] ; xxx es la condicin
...
ELSE ; el ELSE es opcional
...
ENDIF
IF expresion (expresin distinta de cero)
IFE expresin (expresin igual a cero)
IF1 (pasada 1 del ensamblador)
IF2 (pasada 2 del ensamblador)
IFDEF smbolo (smbolo definido o declarado como externo)
IFNDEF smbolo (smbolo ni definido ni declarado como externo)
IFB <argumento> (argumento en blanco en macros -incluir < y >-)
IFNB <argumento> (lo contrario, tambin es obligado poner < y >)
IFIDN <arg1>, <arg2> (arg1 idntico 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 lneas por
pgina (modificable entre 10 y 255) y 80 columnas (seleccionable de 60 a 132). PAGE salta de
pgina e incrementa su nmero. PAGE + indica captulo nuevo (y se incrementa el nmero).
TITLE ttulo: indica el ttulo que aparece en la 1 lnea de cada pgina (mximo 60 caracteres).
81 EL LENGUAJE ENSAMBLADOR DEL 80x86
SUBTTL subttulo: dem con el subttulo (mx. 60 caracteres).
.LALL: Listar las macros y sus expansiones.
.SALL: No listar las macros ni sus expansiones.
.XALL: Listar slo las macros que generan cdigo objeto.
.XCREF: Suprimir listado de referencias cruzadas (listado alfabtico de smbolos junto al n de
lnea en que son definidos y referenciados, de cara a facilitar la depuracin).
.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 ocupar
varias lneas, el delimitador (primer carcter no blanco ni tabulador que sigue al COMMENT) indica
el inicio e indicar ms 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 llegar
a ese punto del listado, excepto cuando el listado es por pantalla y no en fichero.
.LFCOND: Listar los bloques de cdigo asociados a una condicin falsa (IF).
.SFCOND: suprimir dicho listado.
.TFCOND: Invertir el modo vigente de listado de los bloques asociados a una condicin falsa.
5.4. - MACROS.
Cuando un conjunto de instrucciones en ensamblador aparecen frecuentemente repetidas a lo largo
de un listado, es conveniente agruparlas bajo un nombre simblico que las sustituir en aquellos puntos donde
aparezcan. Esta es la misin de las macros; por el hecho de soportarlas el ensamblador eleva su categora a
la 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 instrucciones
aparece una sola vez en todo el programa y luego se invoca con CALL. Sin embargo, cada vez que se
referencia a una macro, el cdigo que sta representa se expande en el programa definitivo, duplicndose
tantas veces como se use la macro. Por ello, aquellas tareas que puedan ser realizadas con subrutinas siempre
ser ms conveniente realizarlas con las mismas, con objeto de economizar memoria. Es cierto que las macros
son algo ms rpidas que las subrutinas (se ahorra un CALL y un RET) pero la diferencia es tan mnima que
en la prctica es despreciable en el 99,99% de los casos. Por ello, es absurdo e irracional realizar ciertas
tareas con macros que pueden ser desarrolladas mucho ms eficientemente con subrutinas: es una pena que
en muchos manuales de ensamblador an se hable de macros para realizar operaciones sobre cadenas de
caracteres, que generaran programas gigantescos con menos de un 1% de velocidad adicional.
5.4.1. - DEFINICIN Y BORRADO DE LAS MACROS.
La macro se define por medio de la directiva MACRO. Es necesario definir la macro antes de
utilizarla. Una macro puede llamar a otra. Con frecuencia, las macros se colocan juntas en un fichero
independiente y luego se mezclan en el programa principal con la directiva INCLUDE:
82 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
IF1
INCLUDE fichero.ext
ENDIF
La sentencia IF1 asegura que el ensamblador lea el fichero fuente de las macros slo en la primera
pasada, para acelerar el ensamblaje y evitar que aparezcan en el listado (generado en la segunda fase).
Conviene hacer hincapi en que la definicin de la macro no consume memoria, por lo que en la prctica es
indiferente declarar cientos que ninguna macro:
nombre_simblico MACRO [parmetros]
...
... ; instrucciones de la macro
ENDM
El nombre simblico es el que permitir en adelante hacer referencia a la macro, y se construye casi
con las mismas reglas que los nombres de las variables y dems smbolos. La macro puede contener
parmetros de manera opcional. A continuacin vienen las instrucciones que engloba y, finalmente, la
directiva ENDM seala el final de la macro. No se debe repetir el nombre simblico junto a la directiva
ENDM, ello provocara un error un tanto curioso y extrao por parte del ensamblador (algo as como Fin
del 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 dems smbolos, el nombre de una macro puede
coincidir con el de una instruccin mquina o una directiva del ensamblador: a partir de ese momento, la
instruccin o directiva machacada pierde su significado original. El ensamblador dar adems un aviso de
advertencia si se emplea una instruccin o directiva como nombre de macro, aunque tolerar la operacin.
Normalmente se las asignar nombres normales, como a las variables. Sin embargo, si alguna vez se
redefiniera una instruccin mquina o directiva, para restaurar el significado original del smbolo, la macro
puede ser borrada -o simplemente porque ya no va a ser usada a partir de cierto punto del listado, y as ya
no consumir espacio en las tablas de macros que mantiene en memoria el ensamblador al ensamblar-. No
es necesario borrar las macros antes de redefinirlas. Para borrarlas, la sintaxis es la siguiente:
PURGE nombre_simblico[,nombre_simblico,...]
5.4.2. - EJEMPLO DE UNA MACRO SENCILLA.
Desde el 286 existe una instruccin muy cmoda que introduce en la pila 8 registros, y otra que los
saca (PUSHA y POPA). Quien est acostumbrado a emplearlas, puede crear unas macros que simulen estas
instrucciones en los 8086:
SUPERPUSH MACRO
PUSH AX
PUSH CX
PUSH DX
PUSH BX
PUSH SP
PUSH BP
PUSH SI
PUSH DI
ENDM
La creacin de SUPERPOP es anloga, sacando los registros en orden inverso. El orden elegido no
es por capricho y se corresponde con el de la instruccin PUSHA original, para compatibilizar. A partir de
la definicin de esta macro, tenemos a nuestra disposicin una nueva instruccin mquina (SUPERPUSH)
que puede ser usada con libertad dentro de los programas.
5.4.3. - PARMETROS FORMALES Y PARMETROS ACTUALES.
Para quien no haya tenido relacin previa con algn lenguaje estructurado de alto nivel, har un breve
comentario acerca de lo que son los parmetros formales y actuales en una macro, similar aqu a los
procedimientos de los lenguajes de alto nivel.
83 EL LENGUAJE ENSAMBLADOR DEL 80x86
Cuando se llama a una macro se le pueden pasar opcionalmente un cierto nmero de parmetros de
cierto tipo. Estos parmetros se denominan parmetros actuales. En la definicin de la macro, dichos
parmetros aparecen asociados a ciertos nombres arbitrarios, cuya nica misin es permitir distinguir unos
parmetros de otros e indicar en qu orden son entregados: son los parmetros formales. Cuando el
ensamblador expanda la macro al ensamblar, los parmetros formales sern sustituidos por sus
correspondientes parmetros actuales. Considerar el siguiente ejemplo:
SUMAR MACRO a,b,total
PUSH AX
MOV AX,a
ADD AX,b
MOV total,AX
POP AX
ENDM
....
SUMAR positivos, negativos, total
En el ejemplo, a, b y total son los parmetros formales y positivos, negativos y total
son los parmetros actuales. Tanto a como b pueden ser variables, etiquetas, etc. en otro punto del
programa; sin embargo, dentro de la macro, se comportan de manera independiente. El parmetro formal
total ha coincidido en el ejemplo y por casualidad con su correspondiente actual. El cdigo que genera el
ensamblador al expandir la macro ser el siguiente:
PUSH AX
MOV AX,positivos
ADD AX,negativos
MOV total,AX
POP AX
Las instrucciones PUSH y POP sirven para no alterar el valor de AX y conseguir que la macro se
comporte como una caja negra; no es necesario que esto sea as pero es una buena costumbre de
programacin para evitar que los programas hagan cosas raras. En general, las macros de este tipo no
deberan alterar los registros y, si los cambian, hay que tener muy claro cules.
Si se indican ms parmetros de los que una macro necesita, se ignorarn los restantes. En cambio,
si faltan, el MASM asumir que son nulos (0) y dar un mensaje de advertencia, el TASM es algo ms rgido
y podra dar un error. En general, se trata de situaciones atpicas que deben ser evitadas.
Tambin puede darse el caso de que no sea posible expandir la macro. En el ejemplo, no hubiera sido
posible ejecutar SUMAR AX,BX,DL porque DL es de 8 bits y la instruccin MOV DL,AX sera ilegal.
5.4.4. - ETIQUETAS DENTRO DE MACROS. VARIABLES LOCALES.
Son necesarias normalmente para los saltos condicionales que contengan las macros ms complejas.
Si se pone una etiqueta a donde saltar, la macro slo podra ser empleada una vez en todo el programa para
evitar que dicha etiqueta aparezca duplicada. La solucin est en emplear la directiva LOCAL que ha de ir
colocada justo despus de la directiva MACRO:
MINIMO MACRO dato1, dato2, resultado
LOCAL ya_esta
MOV AX,dato1
CMP AX,dato2 ; es dato1 el menor?
JB ya_esta ; s
MOV AX,dato2 ; no, es dato2
ya_esta: MOV resultado,AX
ENDM
En el ejemplo, al invocar la macro dos veces el ensamblador no generar la etiqueta ya_esta sino
las etiquetas ??0000, ??0001, ... y as sucesivamente. La directiva LOCAL no slo es til para los saltos
condicionales en las macros, tambin permite declarar variables internas a los mismos. Se puede indicar un
nmero casi indefinido de etiquetas con la directiva LOCAL, separndolas por comas.
84 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
5.4.5. - OPERADORES DE MACROS.
Operador ;;
Indica que lo que viene a continuacin es un comentario que no debe aparecer al expansionar
la macro. Cuando al ensamblar se genera un listado del programa, las macros suelen aparecer
expandidas en los puntos en que se invocan; sin embargo slo aparecern los comentarios normales
que comiencen por (;). Los comentarios relacionados con el funcionamiento interno de la macro
deberan ir con (;;), los relativos al uso y sintaxis de la misma con (;). Esto es adems conveniente
porque durante el ensamblaje son mantenidos en memoria los comentarios de macros (no los del resto
del programa) que comienzan por (;), y no conviene desperdiciar memoria...
Operador &
Utilizado para concatenar texto o smbolos. Es necesario para lograr que el ensamblador
sustituya un parmetro dentro de una cadena de caracteres o como parte de un smbolo:
SALUDO MACRO c
MOV AL,"&c"
etiqueta&c: CALL imprimir
ENDM
Al ejecutar SALUDO A se producir la siguiente expansin:
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 vern ms adelante)
existe un problema adicional al intentar crear etiquetas, ya que el ensamblador se come un & al hacer
la primera sustitucin, generando la misma etiqueta a menos que se duplique el operador &:
MEMORIA MACRO x
IRP i, <1, 2>
x&i DB i
ENDM
ENDM
Si se invoca MEMORIA ET se produce el error de "etiqueta ETi repetida", que se puede
salvar aadiendo tantos & como niveles de anidamiento halla en las estructuras repetitivas
empleadas, como se ejemplifica a continuacin:
MEMORIA MACRO x
IRP i, <1, 2>
x&&i DB i
ENDM
ENDM
Lo que con MEMORIA ET generar correctamente las lneas:
ET1 DB 1
ET2 DB 2
Operador ! o <>
Empleado para indicar que el carcter que viene a continuacin debe ser interpretado
literalmente y no como un smbolo. Por ello, !; es equivalente a <;>.
Operador %
Convierte la expresin que le sigue -generalmente un smbolo- a un nmero; la expresin
debe ser una constante (no relocalizable). Slo se emplea en los argumentos de macros. Dada la
macro siguiente:
85 EL 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 smbolos:
SIM1 EQU 120
SIM2 EQU 500
Invocando la macro con las siguientes condiciones:
PSUM < SIM1 + SIM2 = >, (SIM1+SIM2)
Se produce la siguiente expansin:
%OUT * SIM1 + SIM2 = (SIM1+SIM2) *
Sin embargo, invocando la macro de la siguiente manera (con %):
PSUM < SIM1 + SIM2 = >, %(SIM1+SIM2)
Se produce la expansin deseada:
%OUT * SIM1 + SIM2 = 620 *
5.4.6. - DIRECTIVAS TILES PARA MACROS.
Estas directivas pueden ser empleadas tambin sin las macros, aumentando la comodidad de la
programacin, aunque abundan especialmente dentro de las macros.
REPT veces ... ENDM (Repeat)
Permite repetir cierto nmero de veces una secuencia de instrucciones. El bloque de
instrucciones se delimita con ENDM (no confundirlo con el final de una macro). Por ejemplo:
REPT 2
OUT DX,AL
ENDM
Esta secuencia se transformar, al ensamblar, en lo siguiente:
OUT DX,AL
OUT DX,AL
Empleando smbolos definidos con (=) y apoyndose adems en las macros se puede llegar
a crear pseudo-instrucciones muy potentes:
SUCESION MACRO n
num = 0
REPT n
DB num
num = num + 1
ENDM ; fin de REPT
ENDM ; fin de macro
La sentencia SUCESION 3 provocar la siguiente expansin:
DB 0
DB 1
DB 2
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 instruccin FOR de los lenguajes de alto nivel. Los ngulos (<)
y (>) son obligatorios. El smbolo de control va tomando sucesivamente los valores (no
necesariamente numricos) arg1, arg2, ... y recorre en cada pasada todo el bloque de instrucciones
hasta alcanzar el ENDM (no confundirlo con fin de macro) sustituyendo simbolo_control por esos
valores en todos los lugares en que aparece:
IRP i, <1,2,3>
DB 0, i, i*i
ENDM
Al expansionarse, este conjunto de instrucciones se convierte en lo siguiente:
DB 0, 1, 1
DB 0, 2, 4
DB 0, 3, 9
Nota: Todo lo encerrado entre los ngulos se considera un nico parmetro. Un (;) dentro de los
ngulos no se interpreta como el inicio de un comentario sino como un elemento ms. Por
otra parte, al emplear macros anidadas, deben indicarse tantos smbolos angulares < y >
consecutivos como niveles de anidamiento existan.
Lgicamente, dentro de una macro tambin resulta bastante til la estructura IRP:
TETRAOUT MACRO p1, p2, p3, p4, valor
PUSH AX
PUSH DX
MOV AL,valor
IRP cn, <p1, p2, p3, p4>
MOV DX, cn
OUT DX, AL
ENDM ; fin de IRP
POP DX
POP AX
ENDM ; fin de macro
Al ejecutar TETRAOUT 318h, 1C9h, 2D1h, 1A4h, 17 se obtendr:
PUSH AX
PUSH DX
MOV AL, 17
MOV DX, 318h
OUT DX, AL
MOV DX, 1C9h
OUT DX, AL
MOV DX, 2D1h
OUT DX, AL
MOV DX, 1A4h
OUT DX,AL
POP DX
POP AX
Cuando se pasan listas como parmetros hay que encerrarlas entre < y > al llamar, para
no confundirlas con elementos independientes. Por ejemplo, supuesta la macro INCD:
INCD MACRO lista, p
IRP i, <lista>
INC i
ENDM ; fin de IRP
DEC p
ENDM ; fin de macro
Se comprende la necesidad de utilizar los ngulos:
87 EL LENGUAJE ENSAMBLADOR DEL 80x86
INCD AX, BX, CX, DX se expandir:
INC AX
DEC BX ; CX y DX se ignoran (4 parmetros)
INCD <AX, BX, CX>, DX se expandir:
INC AX
INC BX
INC CX
DEC DX ; (2 parmetros)
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 8
DB 1
DB 3
Ejemplo de utilizacin dentro de una macro (en combinacin con el operador &):
INICIALIZA MACRO a, b, c, d
IRPC iter, <&a&b&c&d>
DB iter
ENDM ; fin de IRPC
ENDM ; fin de macro
Al ejecutar INICIALIZA 7, 1, 4, 0 se produce la siguiente expansin:
DB 7
DB 1
DB 4
DB 0
EXITM
Sirve para abortar la ejecucin de un bloque MACRO, REPT, IRP IRPC. Normalmente se
utiliza apoyndose en una directiva condicional (IF...ELSE...ENDIF). Al salir del bloque, se pasa al
nivel inmediatamente superior (que puede ser otro bloque de estos). Como ejemplo, la siguiente
macro reserva n bytes de memoria a cero hasta un mximo de 100, colocando un byte 255 al final
del bloque reservado:
MALLOC MACRO n
maximo=100
REPT n
IF maximo EQ 0 ; ya van 100?
EXITM ; abandonar REPT
ENDIF
maximo = maximo - 1
DB 0 ; reservar byte
ENDM
DB 255 ; byte de fin de bloque
ENDM
5.4.7. - MACROS AVANZADAS CON NUMERO VARIABLE DE PARMETROS.
Como se vio al estudiar la directiva IF, existe la posibilidad de chequear condicionalmente la
presencia de un parmetro por medio de IFNB, o su ausencia con IFB. Uniendo esto a la potencia de IRP
es posible crear macros extraordinariamente verstiles. Como ejemplo, valga la siguiente macro, destinada
88 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
a introducir en la pila un nmero variable de parmetros (hasta 10): es especialmente til en los programas
que gestionan interrupciones:
XPUSH MACRO R1,R2,R3,R4,R5,R6,R7,R8,R9,R10
IRP reg, <R1,R2,R3,R4,R5,R6,R7,R8,R9,R10>
IFNB <reg>
PUSH reg
ENDIF
ENDM ; fin de IRP
ENDM ; fin de XPUSH
Por ejemplo, la instruccin:
XPUSH AX,BX,DS,ES,VAR1
Se expandir en:
PUSH AX
PUSH AX
PUSH DS
PUSH ES
PUSH VAR1
El ejemplo anterior es ilustrativo del mecanismo de comprobacin de presencia de parmetros. Sin
embargo, este ejemplo puede ser optimizado notablemente empleando una lista como nico parmetro:
XPUSH MACRO lista
IRP i, <lista>
PUSH i
ENDM
ENDM
XPOP MACRO lista
IRP i, <lista>
POP i
ENDM
ENDM
La ventaja es el nmero indefinido de parmetros soportados (no slo 10). Un ejemplo de uso puede
ser el siguiente:
XPUSH <AX, BX, CX>
XPOP <CX, BX, AX>
Que al expandirse queda:
PUSH AX
PUSH BX
PUSH CX
POP CX
POP BX
POP AX
5.5. - PROGRAMACIN MODULAR Y PASO DE PARMETROS.
Aunque lo que viene a continuacin no es indispensable para programar en ensamblador, s es
conveniente leerlo en 2 3 minutos para observar ciertas reglas muy sencillas que ayudarn a hacer
programas seguros y eficientes. Sin embargo, personalmente considero que cada uno es muy libre de hacer
lo que desee; por otra parte, en muchos casos no se pueden cumplir los principios de la programacin
elegante -especialmente en ensamblador- por lo que detesto aquellos profesionales de la informtica que se
entrometen con la manera de programar de sus colegas o alumnos, obligndolos a hacer las cosas a su gusto.
La programacin modular consiste en dividir los problemas ms complejos en mdulos separados con
unas ciertas interdependencias, lo que reduce el tiempo de programacin y aumenta la fiabilidad del cdigo.
Se pueden implementar en ensamblador con las directivas PROC y ENDP que, aunque no generan cdigo
son bastante tiles para dejar bien claro dnde empieza y acaba un mdulo. Reglas para la buena
programacin:
89 EL LENGUAJE ENSAMBLADOR DEL 80x86
- Dividir los problemas en mdulos pequeos relacionados slo por un conjunto de
parmetros de entrada y salida.
- Una sola entrada y salida en cada mdulo: un mdulo slo debe llamar al inicio de otro
(con CALL) y ste debe retornar al final con un nico RET, no debiendo existir ms puntos de salida
y no siendo recomendable alterar la direccin de retorno.
- Excepto en los puntos en que la velocidad o la memoria son crticas (la experiencia
demuestra que son menos del 1%) debe codificarse el programa con claridad, si es preciso perdiendo
eficiencia. Ese 1% documentarlo profusamente como se hara para que lo lea otra persona.
- Los mdulos han de ser cajas negras y no deben modificar el entorno exterior. Esto
significa que no deben actuar sobre variables globales ni modificar los registros (excepto aquellos
registros y variables en que devuelven los resultados, lo que debe documentarse claramente al
principio del mdulo). Tampoco deben depender de ejecuciones anteriores, salvo excepciones en que
la propia claridad del programa obligue a lo contrario (por ejemplo, los generadores de nmeros
aleatorios pueden depender de la llamada anterior).
Para el paso de parmetros entre mdulos existen varios mtodos que se exponen a continuacin. Los
parmetros pueden pasarse adems de dos maneras: directamente por valor, o bien indirectamente por
referencia o direccin. En el primer caso se enva el valor del parmetro y en el segundo la direccin inicial
de memoria a partir de la que est almacenado. El tipo de los parmetros habr de estar debidamente
documentado al principio de los mdulos.
- Paso de parmetros en los registros: Los mdulos utilizan ciertos registros muy concretos para
comunicarse. Todos los dems registros han de permanecer inalterados, por lo cual, si son empleados
internamente, han de ser preservados al principio del mdulo y restaurados al final. Este es el mtodo
empleado por el DOS y la BIOS en la mayora de las ocasiones para comunicarse con quien los llama. Los
registros sern preservados preferiblemente en la pila (con PUSH) y recuperados de la misma (con POP en
orden inverso); de esta manera, los mdulos son reentrantes y pueden ser llamados de manera mltiple
soportando, entre otras caractersticas, la recursividad (sin embargo, se requerir tambin que las variables
locales se generen sobre la pila).
- Paso de parmetros a travs de un rea comn: se utiliza una zona de memoria para la
comunicacin. Este tipo de mdulos no son reentrantes y hasta que no acaben de procesar una llamada no
se les debe llamar de nuevo en medio de la faena.
- Paso de parmetros por la pila. En este mtodo, los parmetros son apilados antes de llamar al
mdulo que los va a recoger. Este debe conocer el nmero y tamao de los mismos, para equilibrar el puntero
de pila al final antes de retornar (mtodo de los compiladores de lenguaje Pascal) o en caso contrario el
programa que llama deber encargarse de esta operacin (lenguaje C). La ventaja del paso de parmetros por
la pila es el prcticamente ilimitado nmero de parmetros admitido, de cmodo acceso, y que los mdulos
siguen siendo reentrantes. Un ejemplo puede ser el siguiente:
dato LABEL DWORD
datoL DW ?
datoH DW ?
PUSH datoL ; apilar parmetros
PUSH datoH
CALL moduloA ; llamada
ADD SP,4 ; equilibrar pila
moduloA PROC NEAR
PUSH BP
MOV BP,SP
MOV DX,[BP+4] ; parte alta del dato
MOV AX,[BP+6] ; parte baja del dato
POP BP
RET
moduloA ENDP
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 es
colocada en la pila empezando por la parte menos significativa. A continuacin se llama a MODULOA, el
cual comienza por preservar BP (lo usar posteriormente) para respetar la norma de caja negra. Se carga BP
con SP debido a que el 8086 no permite el direccionamiento indexado sobre SP. Como la instruccin CALL
se dirige a una direccin cercana (NEAR), en la pila se almacena slo el registro IP. Por tanto, en [BP+0]
est el BP del programa que llama, en [BP+2] el registro IP del programa que llama y en [BP+4] y [BP+6]
la variable enviada, que es el caso ms complejo (variables de 32 bits). Dicha variable es cargada en DX:AX
antes de proceder a usarla (tambin deberan apilarse AX y DX para conservar la estructura de caja negra).
Al final, se retorna con RET y el programa principal equilibra la pila aumentando SP en 4 unidades para
compensar el apilamiento previo de dos palabras antes de llamar. Si MODULOA fuera un procedimiento
lejano (FAR) la variable estara en [BP+6] y [BP+8], debido a que al llamar al mdulo se habra guardado
tambin en la pila el CS del programa que llama. El lenguaje Pascal hubiera retornado con RET 4, haciendo
innecesario que el programa que llama equilibre la pila. Sin embargo, el mtodo del lenguaje C expuesto es
ms eficiente porque no requiere que el mdulo llamado conozca el nmero de parmetros que se le envan:
ste puede ser variable (de hecho, el C apila los parmetros antes de llamar en orden inverso, empezando por
el ltimo: de esta manera se accede correctamente a los primeros N parmetros que se necesiten).
91 EL ENSAMBLADOR EN ENTORNO DOS
Captulo 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 un
bloque de 256 bytes llamado PSP (Program Segment Prefix), cuya descripcin detallada se ver en el
prximo captulo. En l aparecen datos tales como la direccin de retorno al dos cuando finalice el programa,
la direccin de retorno en caso de Ctrl-Break y en caso de errores crticos. Adems de la cantidad de memoria
disponible y los posibles parmetros suministrados del programa. Cuando el programa toma el control, DS
y ES apuntan al PSP. Tipos de programas:
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 direccin ms 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 = tamao de la pila definida.
Si el programa es COM podemos terminarlo con la interrupcin 20h (INT 20h), o simplemente con
un RET si la pila no est desequilibrada (apunta a un INT 20h que hay en la posicin 0 del PSP); otra
manera de acabar es por medio de la funcin 4Ch del sistema (disponible desde el DOS 2.0) que acaba
cualquier programa sin problemas y sin ningn tipo de requerimientos adicionales, tanto COM como EXE.
Los programas de tipo COM se cargan en memoria tal y como estn en disco, entregndoseles el
control. Los de tipo EXE, que pueden llegar a manejar mltiples segmentos de cdigo de hasta 64 Kb, se
almacenan en disco semiensamblados. En realidad, al ser cargados en memoria, el DOS tiene que realizar
la ltima fase de montaje, calculando las direcciones de memoria absolutas. Por ello, estos programas tienen
un formato especial en disco, generado por los ensambladores y compiladores, y su imagen en memoria no
se corresponde realmente con lo que est grabado en el disco, aunque esto al usuario no le importe. Por ello,
no se extrae el lector de haber visto alguna vez ficheros EXE de ms de 640 Kb: evidentemente, no se
cargan enteros en memoria aunque lo parezca. Los programas COM no hacen referencias a datos o
direcciones separados ms de 64 Kb, por lo que todos los saltos y desplazamientos son relativos a los
registros de segmento (no se cambia CS ni DS) con lo que no es necesaria la fase de montaje. No obstante,
un programa COM puede hacer lo que le de la gana con los registros de segmento y acceder a ms de 64
Kb de memoria, por cuenta y riesgo del programador. En general, la programacin en ensamblador est hoy
en da relegada a pequeos programas residentes, controladores de dispositivos o rutinas de apoyo a
programas hechos en otros lenguajes, por lo que no es estrictamente necesario trabajar con programas EXE
realizados en ensamblador. Salvo excepciones, la mayora de los programas desarrollados en este libro sern
de tipo COM ya que los EXE ocuparan algo ms, aunque el ensamblador da algo ms de comodidad al
programador en los mismos.
6.2. - EJEMPLO DE PROGRAMA DE TIPO COM.
El siguiente ejemplo escribe una cadena en pantalla llamando a uno de los servicios estndar de
impresin del DOS (funcin 9 de INT 21h):
92 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
cr EQU 13 ; constante de retorno de carro
lf EQU 10 ; constante de salto de lnea
programa SEGMENT ; segmento comn a CS, DS, ES, SS.
ASSUME CS:programa, DS:programa
ORG 100h ; programa de tipo COM
inicio: LEA DX,texto ; direccin de texto a imprimir
MOV AH,9 ; funcin de impresin
INT 21h ; llamar al DOS
INT 20h ; volver al sistema operativo
texto DB cr,lf,"Grupo Universitario de Informtica.",cr,lf,"$"
programa ENDS ; fin del segmento
END inicio ; fin del programa y punto de inicio
Programa tipo COM
Olvidndonos de los comentarios que comienzan por ;, en las primeras lineas las directivas EQU
definen dos constantes para el preprocesador del compilador: cr=13 y lf=10. El programa, de tipo COM,
consta de un nico segmento. La directiva ASSUME indica que, por defecto, las instrucciones mquina se
ensamblarn para el registro CS en este segmento (lo ms lgico, por otra parte); tambin conviene asumir
el registro DS, de lo contrario, si hubiera que acceder a una variable, el ensamblador aadira el prefijo del
segmento CS a la instruccin al no estar seguro de que DS apunta a los datos, consumiendo ms memoria.
Se pueden aadir los dems registros de segmento en el ASSUME, aunque es redundante. El ORG 100h es
obligatorio en programas COM, ya que estos programas sern cargados en memoria en la posicin CS:100h.
Al final, la direccin del texto a imprimir se coloca en DS:DX (CS=DS=ES=SS en un programa COM recin
ejecutado) y se llama al DOS. El carcter $ delimita la cadena a imprimir, lo cual es una herencia del CP/M
(sera ms interesante que fuera el 0 el delimitador) por razones histricas. Se acaba el programa con INT
20h. El punto de arranque es indicado con la directiva END, aunque en realidad en los programas COM el
punto indicado (en el ejemplo, inicio) debe estar forzosamente al principio del programa. Obsrvese que
no se genera cdigo hasta llegar a la lnea inicio:, todo lo anterior son directivas.
6.3. - EJEMPLO DE PROGRAMA DE TIPO EXE.
Los programas EXE (listado en la pgina siguiente) requieren algo ms de elaboracin. En primer
lugar, es necesario definir una pila y reservar espacio para la misma. Al contrario que los programas COM
(cuya pila se sita al final del segmento compartido tambin con el cdigo y los datos) esta caracterstica
obliga a definir un tamao prudente en funcin de las necesidades del programa. Tngase en cuenta que en
la pila se almacenan las direcciones de retorno de las subrutinas y al llamar a una funcin de la BIOS la pila
es usada con intensidad. En general, con medio kilobyte basta para programas tan sencillos como el del
ejemplo, e incluso para otros mucho ms complejos. El lmite mximo est en 64 Kb. El segmento de pila
se nombra siempre STACK y con el TLINK de Borland es necesario indicar tambin la clase STACK.
Como se ve, son definidos por separado el segmento de cdigo, pila y datos, lo que tambin ayuda
a estructurar ms el programa. El segmento de cdigo se define como procedimiento FAR, entre otras razones
para que el ensamblador ensamble el RET del final (con el que se vuelve al DOS) como un RETF. La
directiva ASSUME asocia cada registro de segmento con su correspondiente segmento. Como puede
observarse al principio del programa, es necesario preparar a mano la direccin de retorno al sistema. El
PUSH DS del principio coloca el segmento del PSP en la pila; el XOR AX,AX coloca un cero en AX (esta
instruccin gasta un byte menos que MOV AX,0) y el PUSH AX mete ese 0 en la pila. Con ello, al volver
93 EL ENSAMBLADOR EN ENTORNO DOS
al DOS con RET (RETF en realidad) el control pasar a DS:0, esto es, a la primera instruccin del PSP (INT
20h). Aunque pueda parecer un tanto lioso, es un juego de nios y estas tres instrucciones consecutivas
(PUSH DS / XOR AX,AX / PUSH AX) son la manera de empezar de cientos de programas EXE, que
despus acaban con RET. En general, a partir del DOS 2.0 es ms aconsejable terminar el programa con la
funcin 4Ch del DOS, que no requiere que CS apunte al PSP ni precisa de preparacin alguna en la pila y
adems permite retornar un cdigo de ERRORLEVEL en AL: en los programas futuros esto se har con
bastante frecuencia.
Tambin debe observarse cmo se inicializa DS, ya que en los programas EXE por defecto no apunta
a los datos. Ahora puede preguntarse el lector, por curiosidad, qu valdr datos?: datos tiene un valor
relativo asignado por el ensamblador; cuando el programa sea cargado en memoria, en el proceso de montaje
y en funcin de cul sea la primera posicin de memoria libre, se le asignar un valor determinado por el
montador del sistema operativo.
cr EQU 13
lf EQU 10
; Segmento de datos
datos SEGMENT
texto DB cr,lf,"Texto a imprimir",cr,lf,"$"
datos ENDS
; Segmento de pila
pila SEGMENT STACK STACK ; poner STACK es obligatorio
DB 128 dup (pila) ; reservados 512 bytes
pila ENDS
; Segmento de cdigo
codigo SEGMENT
ejemplo PROC FAR
ASSUME CS:codigo, DS:datos, SS:pila
; poner direccin de retorno al DOS en la pila:
PUSH DS ; segmento del PSP
XOR AX,AX ; AX = 0
PUSH AX ; desplazamiento 0 al PSP
; direccionar segmento de datos con DS
MOV AX,datos ; AX = direccin del segmento de datos
MOV DS,AX ; inicializar DS
; escribir texto
LEA DX,texto ; DS:DX = direccin del texto
MOV AH,9
INT 21h
; volver al DOS
RET ; en realidad, RETF (PROC FAR)
ejemplo ENDP
codigo ENDS ; fin del cdigo
END ejemplo ; punto de arranque del programa
Programa EXE
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 cdigo objeto, es decir, lenguaje mquina en
el que slo faltan las referencias a rutinas externas. Permite la obtencin de listados de cdigo y de
referencias cruzadas (smbolos, etiquetas, variables). En general, bastar con hacer TASM nombre_programa
(se supone la extensin .ASM por defecto). El fichero final tiene extensin OBJ. En general, la sintaxis del
TASM y MASM es ms o menos equivalente: en el primero se obtiene ayuda con /H y en el segundo con
/HELP. Con TASM, cuando se va a obtener la versin definitiva del programa, o si ste es corto -o el
ordenador rpido- merece la pena utilizar el parmetro /m3, con objeto de que de dos/tres pasadas y optimize
ms el cdigo. Por su lado, MASM presenta estadsticas adicionales si se indica /v y se puede cambiar con
/Btamao el n de Kb de memoria que destina al fichero fuente, entre 1 y 63. La sintaxis es (tanto para
TASM como MASM):
TASM fichero_fuente, fichero_listado, fichero_referencias_cruzadas
Se puede omitir el fichero de listado y el de referencias cruzadas. Cuando se emplea MASM 6.X,
para ensamblar los listados de este libro hay que indicar la opcin /Zm para mantener la compatibilidad con
las versiones anteriores del ensamblador, siendo adems obligatorio indicar la extensin; como se genera
directamente el fichero EXE hay que indicar /c si se desea evitar esto (si no se quiere que linke). La sintaxis
quedara:
ML /Zm fihero_fuente.asm
A continuacin se listan los parmetros comunes a TASM 2.0 (y posterior) y MASM 4.0/5.0 (NO la 6.X):
/a y /s Seleccionan un orden alfabtico o secuencial de los segmentos.
/c Genera un listado de referencias cruzadas en un fichero de extensin CRF listo para ser procesado por CREF (MASM)
aadiendo adems nmeros de lnea al listado, o bien incluye el listado de referencias cruzadas directamente dentro del
listado del programa (caso de TASM). Las referencias cruzadas son un listado de todos los smbolos del programa,
indicando los nmeros de lnea del mismo en que son definidos y referenciados.
/D De la manera /Dsmbolo[=valor] permite crear el smbolo indicado, cuya presencia puede comprobarse en el programa con
una directiva IF (es til para definir externamente un smbolo que indique que el programa est en fase de depuracin, de
cara a ensamblar cierto cdigo adicional). Aunque /d (en minsculas) es un obsoleto parmetro de MASM para obtener
un listado de la primera pasada del ensamblador, MASM 4.0 es capaz de darse cuenta de que se pretende definir un smbolo
con /d a menos que se indique solo /d.
/e Emula las instrucciones de punto flotante del 80x87, apoyndose en una librera 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 preservacin del sentido de maysculas y minsculas en los smbolos: /ml hace que se
consideres diferentes maysculas de minsculas en todos los smbolos, /mx slo con los smbolos globales y /mu hace que
se mayusculicen todos los smbolos globales. Al ensamblar mdulos para usar desde lenguaje C hay que indicar por lo
menos /mx. En MASM 6.X se emplea /Cx en lugar de /mx, /Cp en lugar de /ml y /Cu en vez de /mu.
/n Suprime las tablas de smbolos en el listado.
/p Verifica que el cdigo 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 slo las serias y /w2 slo consejos.
/X Lista las condiciones falsas (ensamblaje condicional).
/z Visualiza la lnea del error y no slo el nmero de la misma.
/Zi Genera informacin simblica para los depuradores de cdigo.
/Zd Incluye slo la informacin del nmero de lnea.
6.4.2. - TLINK/LINK.
El montador o linkador permite combinar varios mdulos objeto, realizando las conexiones entre ellos
y, finalmente, los convierte en mdulo ejecutable de tipo EXE (empleando el ML de MASM 6.X se obtiene
95 EL ENSAMBLADOR EN ENTORNO DOS
directamente el fichero EXE ya que invoca automticamente al linkador). El linkador permite el uso de
libreras de funciones y rutinas. TLINK, a diferencia de LINK, permite generar un fichero de tipo COM
directamente de un OBJ si se indica el parmetro /t, lo que agiliza an ms el proceso. Puede obtenerse ayuda
ejecutndolo sin parmetros. Los parmetros de TLINK son sensibles a maysculas y minsculas, por lo que
/T no es lo mismo que /t. Con LINK se obtiene ayuda indicando /HELP. Aunque los parmetros de uno y
otro son bastante distintos, la sintaxis genrica de ambos es:
TLINK fich_obj(s), fich_exe, fich_map, fich_libreria, fich_def
Los ficheros no necesarios se pueden omitir (o indicar NUL): para linkar el fichero prog1.obj y el
prog2.obj con la librera math.lib generando PROG1.EXE basta con ejecutar TLINK prog1+prog2,,,math.
Alternativamente se puede indicar TLINK @fichero para que tome los parmetros del fichero de texto
FICHERO, en el caso de que estos sean demasiados y sea incmodo teclearlos cada vez que se linka. Los
ficheros de texto de extensin MAP contienen informacin til para el programador sobre la distribucin de
memoria de los segmentos.
6.4.3. - EXE2BIN.
Los ficheros EXE generados por TLINK o LINK no son copia exacta de lo que aparece en la
memoria, sino que el DOS -tras cargarlos- debe realizar una ltima operacin de montaje. Un programa
COM en memoria es una copia del fichero del disco, es algo ms corto y ms sencillo de desensamblar. Al
contrario de lo que algunos opinaron en su da, el tiempo ha demostrado que nunca llegaran a ser
directamente compatibles con los actuales entornos multitarea.
EXE2BIN permite transformar un fichero EXE en COM siempre que el mdulo ocupe menos de 64K
y que est ensamblado con ORG 100h. Si no se indic el parmetro /t en TLINK, ser necesario este
programa (al igual que cuando se utiliza LINK). Cuando se crean programas SYS (que se diferencian de los
COM bsicamente en que no tienen ORG 100h) no se puede ejecutar TLINK /t, por lo que es necesaria la
ayuda de EXE2BIN para convertir el programa EXE en SYS. Sintaxis:
EXE2BIN fich.exe (a veces hay que indicar EXE2BIN fich.exe fich.com)
Si el programa no contiene ORG 100h, EXE2BIN genera un fichero binario puro de extensin BIN.
Si adems existen referencias absolutas a segmentos, EXE2BIN preguntar el segmento en que va a correr
(algunas versiones permiten indicarlo de la manera /Ssegmento): esto permite generar cdigo para ser
ejecutado en un segmento determinado de la memoria (como pueda ser una memoria EPROM o ROM).
6.4.4. - TLIB/LIB.
El gestor de libreras permite reunir mdulos objeto en un nico fichero para poder tomar de l las
rutinas que se necesiten en cada caso. En este libro no se desarrollan programas tan complejos que justifiquen
su utilizacin. En cualquier caso, la sintaxis es la siguiente:
TLIB fichero_libreria comandos, fichero_listado
Si no se indican comandos se obtiene simplemente informacin del contenido de la librera en el
fichero de listado (que puede ser CON para listado por pantalla). Los comandos son de la forma
<simbolo>nombre_de_mdulo y pueden ser los siguientes:
+ aade el mdulo objeto indicado a la librera
- borra el mdulo indicado de la librera
* saca el mdulo de la librera sin borrarlo (extrae fichero OBJ)
-+ alternativamente +-, reemplaza el mdulo existente en la librera
-* alternativamente *-, extrae el mdulo de la librera y lo borra de ella
96 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Por ejemplo, para aadir el mdulo QUICK.OBJ, borrar el SLOW.OBJ y reemplazar el SORT.OBJ
por una nueva versin en LIBRERIA.LIB se ejecutara:
TLIB libreria +quick-slow-+sort
Si la lista es muy larga se puede incluir en un fichero y ejecutar TLIB @fichero para que la lea del
mismo (si no cabe en una lnea del fichero, puede escribirse & al final antes de pasar a la siguiente).
6.4.5. TCREF/CREF.
Esta utilidad genera listados en orden alfabtico de los smbolos, como ayuda a la depuracin. Con
el MASM la opcin /c crea un fichero de referencias cruzadas de extensin CRF (respondiendo
afirmativamente cuando pregunta por el mismo o indicndolo explcitamente en la lnea de comandos); la
opcin /c de TASM lo incluye en el listado, aunque si se indica el nombre del fichero de referencias cruzadas
genera un fichero de extensin XRF. CREF y TCREF interpretan respectivamente los ficheros CRF y XRF
generando un fichero de texto con extensin REF que contiene el listado de referencias cruzadas. Ej.:
TASM fichero,,,fichero
TCREF fichero
Las referencias cruzadas son un listado de todos los smbolos del programa, indicando los nmeros
de lnea del mismo en que son referenciados (la lnea en que son definidos se marca con #); estos nmeros
de lnea son relativos al listado de ensamblaje del programa (y no al fichero fuente). Es til para depurar
programas grandes y complejos.
6.4.6. - MAKE.
Esta utilidad se apoya en unos ficheros especiales, al estilo de los BAT del DOS, de cara a
automatizar el proceso de ensamblaje. Slo es recomendable para programas grandes, divididos en mdulos,
en los que MAKE chequea la fecha y hora para ensamblar slo las partes que hayan sido modificadas.
6.5. - LA UTILIDAD DEBUG/SYMDEB.
La utilidad DEBUG includa en los sistemas MS-DOS, es una herramienta para depuracin de
programas muy interesante que permite desensamblar los mdulos y, adems, ejecutar programas paso a paso,
viendo las modificaciones que sufren los registros y banderas. Se trata de un programa menos complejo,
cmodo y potente que depuradores de cdigo como Turbo Debugger (de Borland) o Codeview (Microsoft),
pero en algunos casos es ms til. Veremos ahora los principales comandos del DEBUG, los cuales tambin
son admitidos en su mayora por Codeview, por lo que el tiempo invertido en aprenderlos ser til no slo
para conocer el clsico y mtico DEBUG.
Antes de empezar con ellos, conviene hacer referencia al programa SYMDEB que acompaa al
MASM de Microsoft: se trata de un DEBUG mejorado, con ayuda, ms rpido e inteligente (indica el tipo
de funcin del sistema cuando al tracear un programa ste llama al DOS) y, en la prctica, es 99%
compatible. Tambin admite las instrucciones adicionales del 286 y los NEC V20/V30. Su diferencia principal
es que al abandonarlo para volver al DOS restaura los vectores de interrupcin, lo que puede no ser deseable
en algunos casos muy concretos. Adems, desde la versin 4.0 se admite el parmetro /S (con SYMDEB /S
nomfich.ext) lo que permite conmutar entre la pantalla de depuracin y la de ejecucin pulsando la tecla \.
Sintaxis general: DEBUG [programa.ext [parmetros] ]
Los programas pueden ser de tipo EXE o COM; en el caso de los primeros se les cargar ya
montados y con los registros inicializados, listos para su ejecucin. Evidentemente, los programas COM
tambin se cargan con los registros inicializados y el correspondiente PSP preparado, as como con IP=100h.
97 EL ENSAMBLADOR EN ENTORNO DOS
Los parmetros opcionales no son los de el DEBUG o SYMDEB sino los que normalmente se suministraran
al programa a depurar. Tambin se pueden cargar otros ficheros de cualquier extensin o simplemente entrar
en el programa sin cargar ningn fichero. Al entrar, aparecer el prompt particular del DEBUG: un guin (-).
Entonces se pueden teclear rdenes que constarn generalmente de una sola letra. La mayora de las mismas
admiten parmetros, que normalmente irn separados por comas. Estos parmetos pueden ser nmeros
hexadecimales de hasta dos o cuatro dgitos, registros y, adems:
- Cadenas de caracteres: Encerradas entre comillas simples o dobles. El texto puede a su vez encerrar
fragmentos entrecomillados, empleando comillas distintas a las ms exteriores. Ejemplo:
"Cadena de caracteres", "Otra cadena ms", Curso de "8086"
Con SYMDEB debe tenerse cuidado de no colocar el nombre de un registro de segmento en
maysculas 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 DEBUG
este problema no existe.
- Direcciones: Pueden expresarse con sus correspondientes valores numricos o bien apoyndose en
algn registro de segmento, aunque el offset siempre ser numrico: 1E93:AD21, CS:100, ES:19AC
El depurador SYMDEB es mucho ms flexible y permite tambin emplear registros de
propsito general en el offset. Sera vlida la direccin DS:BX+AX+104.
- Rangos: Son dos direcciones separadas por una coma; o bien una direccin, la letra L y un valor
numrico que indica el nmero de bytes a partir de la direccin.
- 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 la
que se resumen las principales rdenes. A continuacin se listan las ms interesantes:
Q (Quit): permite abandonar el programa y volver al DOS.
D [<direccin> [numbytes]] (dump): visualiza el contenido de la memoria. SYMDEB permite
adems visualizarla en palabras (DW), dobles palabras (DD), coma flotante ...
A [<direccin>] (assemble): permite ensamblar a partir de CS:IP si no se indica una direccin
concreta. Se admiten las directivas DB y DW del ensamblador. Las instrucciones que requieran
indicar un registro de segmento, con DEBUG hay que ponerlas en una sola lnea. 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 ambos
XLAT
ES: ; y esto tambin
MOV WORD PTR [100],1234
Los saltos inter-segmento deben especificarse como FAR (ej., CALL FAR [100]) a no ser
que sea evidente que lo son (ej. CALL 1234:5678).
E <direccin> [<lista>] (enter): permite consultar y modificar la memoria, byte a byte. Por
98 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
ejemplo, con E 230 1,2,3 se introduciran los bytes 1, 2 y 3 a partir de DS:230. Si no se indica
<lista>, se visualizar la memoria byte a byte, pudindose modificar los bytes deseados, avanzar al
siguiente (barra espaciadora) o retroceder al anterior (signo -). Para acabar se pulsa RETURN.
U [<direccion> [<rango>]] (unassemble): desensambla la memoria. Como ejemplos vlidos: U
ES:100, U E000:1940 ... si se indica rango, DEBUG desensamblar ese nmero de bytes y SYMDEB
ese nmero de lneas. Por defecto se emplea CS: como registro de segmento.
R [<registro>] (register): permite visualizar y modificar el valor de los registros. Por ejemplo, si
se ejecuta la orden rip, se solicitar un nuevo valor para IP; con RF se muestran los flags y se
permite modificar alguno:
Flag Activo Borrado
Desbordamiento OV NV
Direccin DN () UP ()
Interrupcin EI DI
Signo NG (<0) PL (>0)
Cero ZR (=0) NZ (!=0)
Acarreo auxiliar AC NA
Paridad PE (par) PO (impar)
Acarreo CY NC
G [=<direccin> [,<direccin>,...]] (go): ejecuta cdigo desde CS:IP (a menos que se indique una
direccin concreta). Si se trabaja sobre memoria ROM no debe indicarse la segunda direccin. Para
que el flujo del programa se detenga en la 2 direccin o posteriores debe pasar necesariamente por
ella(s). Se puede indicar hasta 10 direcciones donde debe detenerse.
T [<veces>] (trace): ejecuta una instruccin del programa (a partir de CS:IP) mostrando a
continuacin el estado de los registros y la siguiente instruccin. Ejecutar T10 equivaldra a ejecutar
16 veces el comando T. Si la instruccin es CALL o INT, se ejecutar como tal introducindose en
la subrutina o servidor de interrupciones correspondiente (SYMDEB no entra en los INT 21h).
P [<veces>] (proceed): similar al comando T, pero al encontrarse un CALL o INT lo ejecuta de
golpe 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 o
modificado. Se puede indicar la trayectoria de directorios.
L [<direccin>] (load): carga el fichero de nombre indicado con el comando N. Si es ejecutable
lo prepara adecuadamente para su inmediata ejecucin. En BX:CX queda depositado el tamao del
fichero (BX=0 para ficheros de menos de 64 Kb). Por defecto, la direccin es CS:100h.
L <direccin> <unidad> <primer_sector> <num_sectores> (load): carga sectores de la unidad 0,
1, ... (A, B, ...) a memoria. Se trata de sectores lgicos del DOS y no los sectores fsicos de la BIOS.
Las versiones antiguas de SYMDEB dan errores en particiones de ms de 32 Mb.
W [<direccin>] (write): graba el contenido de una zona de memoria a disco. Si no se indica la
direccin, se graba desde CS:100h hasta CS:100h+nmero_bytes; el nmero de bytes se indica en
BX:CX (no es una direccin segmentada sino un valor de 32 bits). Si se trata de un EXE no se
permitir grabarlo (para modificarlos, hay que renombrarles para cambiarles la extensin, aunque de
esta manera no sern montados al cargarlos).
W <direccin> <unidad> <primer_sector> <num_sectores> (write): graba sectores de la memoria
a disco en la unidad 0, 1, ... (A, B, ...). Se trata de sectores lgicos del DOS y no los sectores fsicos
de la BIOS. Las versiones antiguas de SYMDEB dan errores en particiones de disco duro de ms de
32 Mb.
S <rango> <lista> (search): busca una cadena de bytes por la memoria. Para buscar la cadena
99 EL ENSAMBLADOR EN ENTORNO DOS
"PEPE" terminada por cero en un rea de 512 bytes desde DS:100 se hara: S 100 L 200 "PEPE",0
(por defecto se busca en DS:). No se encontrara sin embargo "pepe" (en minsculas).
F <rango> <lista> (fill): llena la zona de memoria especificada con repeticiones de la lista de
bytes indicada. Por ejemplo, para rellenar cdigos 0AAh 100h bytes a partir de 9800h:0 se ejecutara
F 9800:0 L 100 AA; en vez de AA se podra haber indicado una lista de bytes o cadenas de
caracteres.
C <rango> <direccin> (compare): compara dos zonas de memoria mostrando las diferencias. Por
ejemplo, para comparar 5 bytes de DS:100 y DS:200 se hace: C 100 L 5 200.
M <rango> <direccin> (move): Ms que mover, copia una zona de memoria en otra de manera
inteligente (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 operandos
de un mximo de 16 bits (si hay desbordamiento se trunca el resultado, que tampoco excede los 16
bits).
Tambin existen comandos en DEBUG para acceder a la memoria expandida: XS (obtener el estado
de la memoria expandida), XA npag (localizar npag pginas), XD handle (desalojar el handle indicado) y XM
pagina_logica pagina_fisica handle (mapear pginas).
Con SYMDEB pueden adems colocarse, con suma facilidad, puntos de ruptura (breakpoints); con
DEBUG se pueden implementar con la orden G (indicando ms de una direccin hasta un mximo de 10,
donde debe detenerse el programa si pasa por ellas) aunque es ms incmodo. En SYMDEB se pueden definir
con BP direccin, borrarse con BC num_breakpoint, habilitarse con BP num_breakpoint (necesario antes de
emplearlos), deshabilitarse con BD num_breakpoint y listar los definidos con BL. Adems, SYMDEB puede
visualizar datos en coma flotante de 32, 64 y 80 bits con el comando D (DS, DL y DT).
SYMDEB es realmente un depurador simblico (SYMbolic DEBugger) que permite mostrar
informacin adicional y depurar con mayor comodidad los programas que han sido ensamblados con
informacin de depuracin.
Una posibilidad interesante de DEBUG y SYMDEB es que admiten el redireccionamiento del sistema
operativo. Ello permite, por ejemplo, crear ficheros ASCII con rdenes y despus suministrrselas al
programa, como en el siguiente ejemplo: DEBUG < ORDENES.TXT. La ltima orden de este fichero deber
ser Q (quit), de lo contrario no se devolvera el control al DOS ni se podra parar el programa (la entrada por
defecto -el teclado- no acta). Tambin es verstil la posibilidad de redireccionar la salida. Por ejemplo, tras
DEBUG > SALIDA.TXT, se puede teclear un comando para desensamblar (U) y otro para salir (Q): en el
disco aparecer el fichero con los datos del desensamblaje (se teclea a ciegas, lgicamente, porque la salida
por pantalla ha sido redireccionada al fichero). Por supuesto, tambin es posible redireccionar entrada y salida
a un tiempo: DEBUG < ORDENES.TXT > SALIDA.
6.6 - LAS FUNCIONES DEL DOS Y DE LA BIOS.
El cdigo de la BIOS, almacenado en las memorias ROM del ordenador, constituye la primera capa
de software de los ordenadores compatibles. La BIOS accede directamente al hardware, liberando a los
programas de usario de las tareas ms complejas. Parte del cdigo de la BIOS es actualizado durante el
arranque del ordenador, con los ficheros que incluye el sistema operativo. El sistema operativo o DOS
100 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
propiamente dicho se instala despus: el DOS no realiza ningn acceso directo al hardware, en su lugar se
apoya en la BIOS, constituyendo una segunda capa de software. El DOS pone a disposicin de los programas
de usuario unas funciones muy evolucionadas para acceder a los discos y a los recursos del ordenador. Por
encima del DOS se suele colocar habitualmente al COMMAND.COM, aunque realmente el COMMAND no
constituye capa alguna de software: es un simple programa de utilidad, como cualquier otro, ejecutado sobre
el DOS y que adems no pone ninguna funcin a disposicin del sistema (al menos, documentada), su nica
misin es cargar otros programas.
FUNCIONES DE LA BIOS
Las funciones de la BIOS se invocan, desde los programas de usuario, ejecutando una interrupcin
software con un cierto valor inicial en los registros. La BIOS emplea un cierto rango de interrupciones, cada
una encargada de una tarea especfica:
INT 10h: Servicios de Vdeo (texto y grficos).
INT 11h: Informe sobre la configuracin del equipo.
INT 12h: Informe sobre el tamao de la memoria convencional.
INT 13h: Servicios de disco (muy elementales: pistas, sectores, etc.).
INT 14h: Comunicaciones en serie.
INT 15h: Funciones casette (PC) y servicios especiales del sistema (AT).
INT 16h: Servicios de teclado.
INT 17h: Servicios de impresora.
INT 18h: Llamar a la ROM del BASIC (slo mquinas IBM).
INT 19h: Reinicializacin del sistema.
INT 1Ah: Servicios horarios.
INT 1Fh: Apunta a la tabla de los caracteres ASCII 128-255 (8x8 puntos).
La mayora de las interrupciones se invocan solicitando una funcin determinada (que se indica en
el registro AH al llamar) y se limitan a devolver un resultado en ciertos registros, realizando la tarea
solicitada. En general, slo resultan modificados los registros que devuelven algo, aunque BP es corrompido
en los servicios de vdeo de las mquinas ms obsoletas.
FUNCIONES DEL DOS
El DOS emplea varias interrupciones, al igual que la BIOS; sin embargo, cuando se habla de
funciones del DOS, todo el mundo sobreentiende que se trata de llamar a la INT 21h, la interrupcin ms
importante con diferencia.
INT 20h: Terminar programa (tal vez en desuso).
INT 21h: Servicios del DOS.
INT 22h: Control de finalizacin de programas.
INT 23h: Tratamiento de Ctrl-C.
INT 24h: Tratamiento de errores crticos.
INT 25h: Lectura absoluta de disco (sectores lgicos).
INT 26h: Escritura absoluta en disco (sectores lgicos).
INT 27h: Terminar dejando residente el programa (en desuso).
INT 28h: Idle (ejecutada cuando el ordenador est inactivo).
INT 29h: Impresin rpida en pantalla (no tanto).
INT 2Ah: Red local MS NET.
INT 2Bh-2Dh: Uso interno del DOS.
INT 2Eh: Procesos Batch.
INT 2Fh: Interrupcin Multiplex.
INT 30h-31h: Compatibilidad CP/M-80.
INT 32h: Reservada.
101 EL ENSAMBLADOR EN ENTORNO DOS
Las funciones del DOS se invocan llamando a la INT 21h e indicando en el registro AH el nmero
de funcin a ejecutar. Slo modifican los registros en que devuelven los resultados, devolviendo normalmente
el acarreo activo cuando se produce un error (con un cdigo de error en el acumulador). Muchas funciones
de los lenguajes de programacin frecuentemente se limitan a llamar al DOS.
Todos los valores mostrados a continuacin son hexadecimales; el de la izquierda es el nmero de
funcin (lo que hay que cargar en AH antes de llamar); algunas funciones del DOS se dividen a su vez en
subfunciones, seleccionables mediante AL (segundo valor numrico, en los casos en que aparece). Las
funciones marcadas con U> fueron histricamente indocumentadas, aunque Microsoft desclasific casi todas
ellas a partir del MS-DOS 5.0 (en muchas secciones de este libro, escritas con anterioridad, se las referencia
an como indocumentadas). Se indica tambin la versin del DOS a partir de la que estn disponibles.
En general, se debe intentar emplear siempre las funciones que requieran la menor versin posible
del DOS; sin embargo, no es necesario buscar la compatibilidad con el DOS 1.0: esta versin no soporta
subdirectorios, y el sistema de ficheros se basa en el horroroso mtodo FCB. Los FCB ya no estn soportados
siquiera en la ventana de compatibilidad DOS de OS/2, siendo recomendable ignorar su existencia y trabajar
con los handles, al estilo del UNIX, que consisten en unos nmeros que identifican a los ficheros cuando son
abiertos. Existen 5 handles predefinidos permanentemente abiertos: 0 (entrada estndar -teclado-), 1 (salida
estndar -pantalla-), 2 (salida de error estndar -tambin pantalla-), 3 (entrada/salida por puerto serie) y 4
(salida por impresora): la pantalla, el teclado, etc. pueden ser manejados como simples ficheros.
Las funciones precedidas de un asterisco son empleadas o mencionadas en este libro, y pueden
consultarse en el apndice al efecto al final del mismo.
ENTRADA/SALIDA DE CARACTERES
AH AL Versin Nombre original Traduccin
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 ESTANDAR
03 -- DOS 1+ - READ CHARACTER FROM STDAUX . . . . . . . . . . . . . . . . . . . . . . . . . LEER CARACTER DEL PUERTO SERIE
04 -- DOS 1+ - WRITE CHARACTER TO STDAUX . . . . . . . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN EL PUERTO SERIE
05 -- DOS 1+ - WRITE CHARACTER TO PRINTER . . . . . . . . . . . . . . . . . . . . . . . . ESCRIBIR CARACTER EN LA IMPRESORA
06 -- DOS 1+ - DIRECT CONSOLE OUTPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SALIDA DIRECTA A CONSOLA
06 -- DOS 1+ - DIRECT CONSOLE INPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ENTRADA DIRECTA POR CONSOLA
07 -- DOS 1+ - DIRECT CHARACTER INPUT, WITHOUT ECHO . . . . . . . . . . . . . . LECTURA DIRECTA DE CARACTER, SIN IMPRESION
08 -- 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 BUFFER
0B -- DOS 1+ - GET STDIN STATUS . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER ESTADO DE LA ENTRADA ESTANDAR
0C -- 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 FCB
10 -- DOS 1+ - CLOSE FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CERRAR FICHERO EMPLEANDO FCB
11 -- DOS 1+ - FIND FIRST MATCHING FILE USING FCB . . . . . . . . . . . . . . . . . . . BUSCAR PRIMER FICHERO EMPLEANDO FCB
12 -- DOS 1+ - FIND NEXT MATCHING FILE USING FCB . . . . . . . . . . . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO FCB
13 -- DOS 1+ - DELETE FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR FICHERO EMPLEANDO FCB
16 -- DOS 1+ - CREATE OR TRUNCATE FILE USING FCB . . . . . . . . . . . . . . . . . . . . CREAR/TRUNCAR FICHERO EMPLEANDO FCB
17 -- DOS 1+ - RENAME FILE USING FCB . . . . . . . . . . . . . . . . . . . . . . . . . . . . RENOMBRAR FICHERO EMPLEANDO FCB
23 -- DOS 1+ - GET FILE SIZE FOR FCB . . . . . . . . . . . . . . . . . . . . . . . . OBTENER TAMAO DE FICHERO EMPLEANDO FCB
29 -- 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 HANDLE
41 -- DOS 2+ - "UNLINK" - DELETE FILE . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR FICHERO EMPLEANDO HANDLE
43 00 DOS 2+ - GET FILE ATTRIBUTES . . . . . . . . . . . . . . . . . . . . . OBTENER ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE
43 01 DOS 2+ - "CHMOD" - SET FILE ATTRIBUTES . . . . . . . . . . . . . . . MODIFICAR ATRIBUTOS DEL FICHERO EMPLEANDO HANDLE
45 -- DOS 2+ - "DUP" - DUPLICATE FILE HANDLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DUPLICAR EL HANDLE
46 -- DOS 2+ - "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE . . . . . . . . . . . . . . . . . . REDIRECCIONAR EL HANDLE
4E -- DOS 2+ - "FINDFIRST" - FIND FIRST MATCHING FILE . . . . . . . . . . . . . . . BUSCAR PRIMER FICHERO EMPLEANDO HANDLE
4F -- DOS 2+ - "FINDNEXT" - FIND NEXT MATCHING FILE . . . . . . . . . . . . . . . . BUSCAR PROXIMO FICHERO EMPLEANDO HANDLE
56 -- DOS 2+ - "RENAME" - RENAME FILE . . . . . . . . . . . . . . . . . . . . . . . . . RENOMBRAR FICHERO EMPLEANDO HANDLE
57 00 DOS 2+ - GET FILES DATE AND TIME . . . . . . . . . . . . . . . . . OBTENER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE
57 01 DOS 2+ - SET FILES DATE AND TIME . . . . . . . . . . . . . . . ESTABLECER FECHA Y HORA DEL FICHERO EMPLEANDO HANDLE
5A -- DOS 3+ - CREATE TEMPORARY FILE . . . . . . . . . . . . . . . . . . . . . . . . CREAR FICHERO TEMPORAL EMPLEANDO HANDLE
5B -- DOS 3+ - CREATE NEW FILE . . . . . . . . . . . . . . . CREAR NUEVO FICHERO SIN MACHACARLO SI EXISTIA EMPLEANDO HANDLE
67 -- DOS 3.3+ - SET HANDLE COUNT . . . . . . . . . . . . . . . ESTABLECER MAXIMO NUMERO DE HANDLES PARA LA TAREA EN CURSO
68 -- 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 FCB
15 -- 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 DISCO
21 -- DOS 1+ - READ RANDOM RECORD FROM FCB FILE . . . . . . . . . . . . . . . . LECTURA ALEATORIA DE REGISTRO EMPLEANDO FCB
22 -- DOS 1+ - WRITE RANDOM RECORD TO FCB FILE . . . . . . . . . . . . . . . . ESCRITURA ALEATORIA DE REGISTRO EMPLEANDO FCB
24 -- DOS 1+ - SET RANDOM RECORD NUMBER FOR FCB . . . . . . . . . . . . . PASAR DE E/S SECUENCIAL A ALEATORIA EMPLEANDO FCB
27 -- DOS 1+ - RANDOM BLOCK READ FROM FCB FILE . . . . . . . . . . . . . . . . . . LECTURA ALEATORIA DE BLOQUE EMPLEANDO FCB
28 -- 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
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 HANDLE
5C -- DOS 3+ - "FLOCK" - RECORD LOCKING . . . . . . . . . . . . . BLOQUEAR/DESBLOQUER UNA ZONA DEL FICHERO EMPLEANDO HANDLE
OPERACIONES CON DIRECTORIOS
39 -- DOS 2+ - "MKDIR" - CREATE SUBDIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CREAR SUBDIRECTORIO
3A -- DOS 2+ - "RMDIR" - REMOVE SUBDIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . BORRAR SUBDIRECTORIO
3B -- DOS 2+ - "CHDIR" - SET CURRENT DIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . CAMBIAR EL DIRECTORIO ACTIVO
47 -- DOS 2+ - "CWD" - GET CURRENT DIRECTORY . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL DIRECTORIO ACTUAL
MANEJO DE DISCO
0D -- DOS 1+ - DISK RESET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . REINICIALIZAR EL DISCO
0E -- DOS 1+ - SELECT DEFAULT DRIVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER UNIDAD POR DEFECTO
19 -- DOS 1+ - GET CURRENT DEFAULT DRIVE . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA UNIDAD ACTUAL POR DEFECTO
1B -- DOS 1+ - GET ALLOCATION INFORMATION FOR DEFAULT DRIVE . . . . OBTENER INFORMACION DE ESPACIO EN EL DISCO POR DEFECTO
1C -- DOS 1+ - GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE . . . . . . OBTENER INFORMACION DE ESPACIO EN EL DISCO INDICADO
2E -- DOS 1+ - SET VERIFY FLAG . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER EL BANDERIN DE VERIFICACION
*36 -- DOS 2+ - GET FREE DISK SPACE . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL ESPACIO LIBRE EN DISCO
54 -- DOS 2+ - GET VERIFY FLAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER EL BANDERIN DE VERIFICACION
CONTROL DE PROCESOS
00 -- DOS 1+ - TERMINATE PROGRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TERMINAR PROGRAMA
26 -- DOS 1+ - CREATE NEW PROGRAM SEGMENT PREFIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CREAR PSP
*31 -- DOS 2+ - TERMINATE AND STAY RESIDENT . . . . . . . . . . . . . . . . . . . . . . . . . TERMINAR Y PERMANECER RESIDENTE
*4B -- DOS 2+ - "EXEC" - LOAD AND/OR EXECUTE PROGRAM . . . . . . . . . . . . . . . . . . . . . CARGAR Y/O EJECUTAR PROGRAMA
*4C -- DOS 2+ - "EXIT" - TERMINATE WITH RETURN CODE . . . . . . . . . . . . . . . . . TERMINAR PROGRAMA CON CODIGO DE RETORNO
4D -- DOS 2+ - GET RETURN CODE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER CODIGO DE RETORNO
*50 -- DOS 2+ internal - SET CURRENT PROCESS ID (SET PSP ADDRESS) . . . . . . . . . . . . ESTABLECER DIRECCION DEL PSP ACTUAL
*51 -- DOS 2+ internal - GET CURRENT PROCESS ID (GET PSP ADDRESS) . . . . . . . . . . . . . OBTENER DIRECCION DEL PSP ACTUAL
*62 -- DOS 3+ - GET CURRENT PSP ADDRESS . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER DIRECCION DEL PSP ACTUAL
GESTION DE MEMORIA
*48 -- DOS 2+ - ALLOCATE MEMORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ASIGNAR MEMORIA
*49 -- DOS 2+ - FREE MEMORY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . LIBERAR MEMORIA
*4A -- DOS 2+ - RESIZE MEMORY BLOCK . . . . . . . . . . . . . . . . . . MODIFICAR EL TAMAO 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 SISTEMA
2B -- DOS 1+ - SET SYSTEM DATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER LA FECHA DEL SISTEMA
*2C -- DOS 1+ - GET SYSTEM TIME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBTENER LA HORA DEL SISTEMA
2D -- 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/M
1D -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M
1E -- DOS 1+ - NULL FUNCTION FOR CP/M COMPATIBILITY . . . . . . . . . . . . . . . . . FUNCION NULA PARA COMPATIBILIDAD CP/M
1F -- DOS 1+ - GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE . . . . . . . . . . . . . OBTENER EL DPB DE LA UNIDAD POR DEFECTO
20 -- 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 DOS
32 -- DOS 2+ - GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE . . . . . . . . . . . OBTENER EL DPB DE LA UNIDAD INDICADA
33 -- DOS 2+ - EXTENDED BREAK CHECKING . . . . . . . . . . . . . . . . . . . . CONTROLAR EL NIVEL DE DETECCION DE CTRL-BREAK
33 02 DOS 3.x+ internal - GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE . . INDICAR/OBTENER NIVEL DETECCION CTRL-BREAK
33 05 DOS 4+ - GET BOOT DRIVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DETERMINAR UNIDAD DE ARRANQUE
33 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 INTERRUPCION
37 00 DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER . . . . . . . . . . . . . . . . OBTENER EL CARACTER INDICADOR DE PARAMETROS
37 01 DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER . . . . . . . . . . . . . . ESTABLECER EL CARACTER INDICADOR DE PARAMETROS
37 -- 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 PAIS
38 -- DOS 3+ - SET COUNTRY CODE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ESTABLECER EL CODIGO DEL PAIS
44 00 DOS 2+ - IOCTL - GET DEVICE INFORMATION . . . . . . . . . . . . . . CONTROL E/S: OBTENER INFORMACION DEL DISPOSITIVO
44 01 DOS 2+ - IOCTL - SET DEVICE INFORMATION . . . . . . . . . . . . . CONTROL E/S: ESTABLECER INFORMACION DEL DISPOSITIVO
44 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. BLOQUE
44 05 DOS 2+ - IOCTL - WRITE TO BLOCK DEVICE CONTROL CHANNEL . . . . . . CONTROL E/S: ESCRIBIR EN CANAL CONTROL DISP. BLOQUE
44 06 DOS 2+ - IOCTL - GET INPUT STATUS . . . . . . . . . . . . . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA ENTRADA
44 07 DOS 2+ - IOCTL - GET OUTPUT STATUS . . . . . . . . . . . . . . . . . . . . . CONTROL E/S: OBTENER ESTADO DE LA SALIDA
44 08 DOS 3.0+ - IOCTL - CHECK IF BLOCK DEVICE REMOVABLE . . . . . CONTROL E/S: COMPROBAR SI EL DISP. DE BLOQUE ES REMOVIBLE
44 09 DOS 3.1+ - IOCTL - CHECK IF BLOCK DEVICE REMOTE . . . . . . . CONTROL E/S: COMPROBAR SI EL DISP. DE BLOQUE ES REMOTO
44 0A DOS 3.1+ - IOCTL - CHECK IF HANDLE IS REMOTE . . . . . . . . . . . . . . CONTROL E/S: COMPROBAR SI UN HANDLE ES REMOTO
44 0B DOS 3.1+ - IOCTL - SET SHARING RETRY COUNT . . . . . CONTROL E/S: DEFINIR NUMERO DE REINTENTOS EN MODO DE COMPARTICION
44 0C DOS 3.2+ - IOCTL - GENERIC CHARACTER DEVICE REQUEST . . . . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE CARACTERES
44 0D DOS 3.2+ - IOCTL - GENERIC BLOCK DEVICE REQUEST . . . . . . . . . . . CONTROL E/S GENERAL PARA DISPOSITIVOS DE BLOQUE
44 0E DOS 3.2+ - IOCTL - GET LOGICAL DRIVE MAP . . . . . . . . . . . . . . . . . . . OBTENER ASIGNACION DE UNIDADES LOGICAS
44 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 SISTEMA
53 -- DOS 2+ internal - TRANSLATE BIOS PARAMETER BLOCK TO DRIVE PARAM BLOCK . . . . . . . . . . . . . . TRADUCIR BPB A DPB
55 -- 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 DOS
60 -- DOS 3.0+ - CANONICALIZE FILENAME OR PATH . . . . . EXPANDIR NOMBRE DE FICHERO A ESPECIFICACION COMPLETA DE DIRECTORIOS
61 -- DOS 3+ - UNUSED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NO USADA AUN
64 -- DOS 3.2+ internal - SET DEVICE DRIVER LOOKAHEAD FLAG . . . . ESTABLECER BANDERIN DE LECTURA ADELANTADA DE DISPOSITIVO
65 -- DOS 3.3+ - GET EXTENDED COUNTRY INFORMATION . . . . . . . . . . . . . . . . . OBTENER INFORMACION EXTENDIDA DEL PAIS
65 23 U> DOS 4+ internal - DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONS . . . . DETERMINAR SI UNA LETRA INDICA SI O NO
65 -- U> DOS 4+ internal - COUNTRY-DEPENDENT FILENAME CAPITALIZATION . . . . MAYUSCULIZACION DE NOMBRE DEPENDIENTE DEL PAIS
66 01 DOS 3.3+ - GET GLOBAL CODE PAGE TABLE . . . . . . . . . . . . . . . . . . . . . . OBTENER LA PAGINA DE CODIGOS GLOBAL
66 02 DOS 3.3+ - SET GLOBAL CODE PAGE TABLE . . . . . . . . . . . . . . . . . . . . ESTABLECER LA PAGINA DE CODIGOS GLOBAL
69 -- U> DOS 4+ internal - GET/SET DISK SERIAL NUMBER . . . . . . . . . . OBTENER/ESTABLECER EL NUMERO DE SERIE DE UN DISCO
6B -- U> DOS 5.0 - NULL FUNCTION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . FUNCION NULA
6C 00 DOS 4+ - EXTENDED OPEN/CREATE . . . . . . . . . . . . . . . APERTURA/CREACION DE FICHEROS EXTENDIDA EMPLEANDO HANDLE
103 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
Captulo VII: ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
7.1. - LAS INTERRUPCIONES
Son seales enviadas a la CPU para que termine la ejecucin de la instruccin en curso y atienda una
peticin determinada, continuando ms tarde con lo que estaba haciendo.
Cada interrupcin lleva asociado un nmero que identifica el tipo de servicio a realizar. A partir de
dicho nmero se calcula la direccin de la rutina que lo atiende y cuando se retorna se contina con la
instruccin siguiente a la que se estaba ejecutando cuando se produjo la interrupcin. La forma de calcular
la direccin de la rutina es multiplicar por cuatro el valor de la interrupcin para obtener un desplazamiento
y, sobre el segmento 0, con dicho desplazamiento, se leen dos palabras: la primera es el desplazamiento y
la segunda el segmento de la rutina deseada. Por tanto, en el primer kilobyte de memoria fsica del sistema,
existe espacio suficiente para los 256 vectores de interrupcin disponibles.
Hay tres tipos bsicos de interrupciones:
- Interrupciones internas o excepciones: Las genera la propia CPU cuando se produce una situacin
anormal o cuando llega el caso. Por desgracia, IBM se salt olmpicamente la especificacin de Intel
que reserva las interrupciones 0-31 para el procesador.
INT 0: error de divisin, generada automticamente cuando el cociente no cabe en el
registro o el divisor es cero. Slo puede ser generada mediante DIV o IDIV. Hay una sutil
diferencia de comportamiento ante esta interrupcin segn el tipo de procesador: el
8088/8086 y los NEC V20 y V30 almacenan en la pila, como cabra esperar, la direccin de
la instruccin que sigue a la que caus la excepcin. Sin embargo, el 286 y superiores
almacenan la direccin del DIV o IDIV que causa la excepcin.
INT 1: paso a paso, se produce tras cada instruccin cuando el procesador est en modo
traza (utilizada en depuracin de programas).
INT 2: interrupcin no enmascarable, tiene prioridad absoluta y se produce incluso aunque
estn inhibidas las interrupciones (con CLI) para indicar un hecho muy urgente (fallo en la
alimentacin o error de paridad en la memoria).
INT 3: utilizada para poner puntos de ruptura en la depuracin de programas, debido a que
es una instruccin de un solo byte muy cmoda de utilizar.
INT 4: desbordamiento, se dispara cuando se ejecuta un INTO y haba desbordamiento.
INT 5: rango excedido en la instruccin BOUND (slo 286 y superiores). Ha sido
incorrectamente empleada por IBM para volcar la pantalla por impresora.
INT 6: cdigo de operacin invlido (slo a partir del 286). Se produce al ejecutar una
instruccin indefinida, en la pila se almacena el CS:IP de la instruccin ilegal.
INT 7: dispositivo no disponible (slo a partir del 286).
104 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
- Interrupciones hardware: Son las generadas por la circuitera del ordenador en respuesta a algn
evento. Las ms importantes son:
INT 8: Se produce con una frecuencia peridica determinada por el canal 0 del chip temporizador
8253/8254 (en la prctica, unas 18,2 veces por segundo). Como desde esta interrupcin se invoca a
su vez a INT 1Ch -porque as lo dispuso IBM-, es posible ligar un proceso a INT 1Ch para que se
ejecute peridicamente.
INT 9: generada al pulsar o soltar una tecla.
INT 0Ah, 0Bh, 0Ch, 0Dh, 0Eh, 0Fh: Puertos serie, impresora y controladores de disquete.
INT 70h, 71h, 72h, 73h, 74h, 75h, 76h, 77h: Generadas en los AT y mquinas superiores por el
segundo chip controlador de interrupciones.
- Interrupciones software: Producidas por el propio programa (instruccin INT) para invocar ciertas
subrutinas. La BIOS y el DOS utilizan algunas interrupciones a las que se puede llamar con
determinados valores en los registros para que realicen ciertos servicios. Tambin existe alguna que
otra interrupcin que se limita simplemente a apuntar a modo de puntero a una tabla de datos.
Los vectores de interrupcin pueden ser desviados hacia un programa propio que, adems, podra
quedar residente en memoria. Si se reprograma por completo una interrupcin y sta es de tipo hardware, hay
que realizar una serie de tareas adicionales, como enviar una seal fin de interrupcin hardware al chip
controlador de interrupciones. Si se trata adems de la interrupcin del teclado del PC o XT, hay que enviar
una seal de reconocimiento al mismo ... en resumen: conviene documentarse debidamente antes de intentar
hacer nada. Todos estos problemas se evitan si la nueva rutina que controla la interrupcin llama al principio
(o al final) al anterior gestor de la misma, que es lo ms normal, como se ver ms adelante.
Para cambiar un vector de interrupcin existen cuatro mtodos:
1) El elegante: es adems el ms cmodo y compatible. De hecho, algunos programas de DOS
funcionan tambin bajo OS/2 si han sido diseados con esta tcnica. Basta con llamar al servicio 25h
del DOS (INT 21h) y decirle qu interrupcin hay que desviar y a dnde:
MOV AH,25h ; servicio para cambiar vector
MOV AL,vector ; entre 0 y 255
LEA DX,rutina ; DS:DX nueva rutina de gestin
INT 21h ; llamar al DOS
2) El ps: es menos seguro y compatible (ningn programa que emplea esta tcnica corre en OS/2)
y consiste en hacer casi lo que hace el DOS pero sin llamarle. Es adems mucho ms incmodo y
largo, pero muy usado por programadores despistados:
MOV BL,vector*4 ; vector a cambiar en BL
MOV BH,0 ; ahora en BX
MOV AX,0
PUSH DS ; preservar DS
MOV DS,AX ; apuntar al segmento 0000
LEA DX,rutina ; CS:DX nueva rutina de gestin
CLI ; evitar posible interrupcin
MOV [BX],DX ; cambiar vector (offset)
MOV [BX+2],CS ; cambiar vector (segmento)
STI ; permitir interrupciones
POP DS ; restaurar DS
3) El mtodo correcto es similar al ps, consiste en cambiar el vector de un tirn (cambiar
a la vez segmento y offset con un REP MOVS) con objeto de evitar una posible interrupcin no
enmascarable que se pueda producir en ese momento crtico en que ya se ha cambiado el offset pero
todava no el segmento (CLI no inhibe la interrupcin no enmascarable). Este sistema es todava algo
ms engorroso, pero es el mejor y es el que utiliza el DOS en el mtodo (1).
105 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
4) El mtodo incorrecto es muy usado por los malos programadores. Es similar al ps slo que
sin inhibir las interrupciones mientras se cambia el vector, con el riesgo de que se produzca una
interrupcin cuando se ha cambiado slo medio vector. Los peores programadores lo emplean sobre
todo 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 estn ocupados por la
tabla de vectores de interrupcin. A continuacin existen 256 bytes de datos de la BIOS y otros tantos para
el BASIC y el DOS. De 600h a 9FFFFh est la memoria del usuario (casi 640 Kb). En A0000h comienza
el rea de expansin de memoria de pantalla (EGA y VGA). En B0000h comienzan otros 64 Kb de los
adaptadores de texto MDA y grficos (CGA). De C0000h a EFFFFh aparecen las extensiones de la ROM
(aadidas por las tarjetas grficas, discos duros, etc.) y en F0000h suele estar colocada la BIOS del sistema
(a veces tan slo 8 Kb a partir de FE000h). Los modernos sistemas operativos (DR-DOS y MS-DOS 5.0 y
posteriores) permiten colocar RAM en huecos vacos por encima de los 640 Kb en las mquinas 386 (y
algn 286 con cierto juego especial de chips). Esta zona de memoria sirve para cargar programas residentes.
De hecho, el propio sistema operativo se sita (en 286 y superiores) en los primeros 64 Kb de la memoria
extendida (HMA) que pueden ser direccionados desde el DOS, dejando ms memoria libre al usuario dentro
de los primeros 640 Kb. Para ms informacin, puede consultarse el apndice I y el captulo 8.
Los puertos de entrada y salida (E/S) permiten a la CPU comunicarse con los perifricos. Los 80x86
utilizan los buses de direcciones y datos ordinarios para acceder a los perifricos, pero habilitando una lnea
que distinga el acceso a los mismos de un acceso convencional a la memoria (si no existieran los puertos de
entrada y salida, los perifricos deberan interceptar el acceso a la memoria y estar colocados en algn rea
de la misma). Para acceder a los puertos E/S se emplean las instrucciones IN y OUT. Vase el apndice IV.
7.3.- LA PANTALLA EN MODO TEXTO.
Cuando la pantalla est en modo de texto, si est activo un adaptador de vdeo monocromo, ocupa
4 Kb a partir del segmento 0B000h. Con un adaptador de color, son 16 Kb a partir del segmento 0B800h.
Un mtodo para averiguar el tipo de adaptador de vdeo es consultar a la BIOS el modo de vdeo activo: ser
7 para un adaptador monocromo (tanto MDA como la EGA y VGA si el usuario las configura as) y un valor
entre 0 y 4 para un adaptador de color. Los modos 0 y 1 son de 40 columnas y el 2 y 3 de 80. Los modos
0 y 2 son de color suprimido, aunque en muchos monitores salen tambin en color (y no en tonos de gris).
Cada carcter en la pantalla (empezando por arriba a la izquierda) ocupa dos bytes consecutivos: en el
primero se almacena el cdigo ASCII del carcter a visualizar y en el segundo los atributos de color.
Obviamente, en un modo de 80x25 se utilizan 4000 bytes (los 96 restantes hasta los 4096 de los 4 Kb se
desprecian). En los adaptadores de color, como hay 16 Kb de memoria para texto, se pueden definir entre
4 pginas de texto (80 columnas) y 8 (40 columnas). La pgina activa puede consultarse tambin llamando
a la BIOS, con objeto de conocer el segmento real donde empieza la pantalla (B800 ms un cierto offset).
En el 97,5% de los casos slo se emplea la pgina 0, lo que no quiere decir que los buenos programas deban
asumirla como la nica posible. La BIOS utiliza la interrupcin 10h para comunicarse con el sistema
operativo y los programas de usuario.
El byte de atributos permite definir el color de fondo de los caracteres (0-7) con los bits 4-6, el de
la tinta (0-15) con los bits 0-3 y el parpadeo con el bit 7. La funcin de este ltimo bit puede ser redefinida
para indicar el brillo de los caracteres de fondo (existiendo entonces tambin 16 colores de fondo), aunque
en CGA es preciso para ello un acceso directo al hardware. En el adaptador monocromo, y para la tinta, el
color 0 es el negro; el 1 es subrayado normal, del 1 al 7 son colores normales; el 8 es negro, el 9 es
subrayado brillante y del 10 al 15 son brillantes. Para el papel todos los colores son negros menos el
7 (blanco), no obstante para escribir en vdeo inverso es necesario no slo papel 7 sino adems tinta 0 (al
menos, en los autnticos adaptadores monocromos). El bit 7 siempre provoca parpadeo en este adaptador. En
106 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
el adaptador de color no se pueden subrayar caracteres con los cdigos de color (aunque s en la EGA y VGA
empleando otros mtodos). Tabla de colores:
0 - Negro 4 - Rojo 8 - Gris 12 - Rojo claro
1 - Azul 5 - Magenta 9 - Azul claro 13 - Magenta claro
2 - Verde 6 - Marrn 10 - Verde claro 14 - Amarillo
3 - Cian 7 - Blanco 11 - Cian claro 15 - Blanco brillante
Conviene tener cuidado con la tinta azul (1 y 9) ya que, en estos colores, los adaptadores
monocromos subrayan -lo que puede ser un efecto indeseable-. Cuando se llama al DOS para imprimir, ste
invoca a su vez a la BIOS, por lo que la escritura puede ser acelerada llamando directamente a este ltimo,
que adems permite escribir en color. De todas maneras, lo mejor en programas de calidad es escribir
directamente sobre la memoria de pantalla para obtener una velocidad mxima, aunque con ciertas
precauciones -para convivir mejor con entornos pseudo-multitarea y CGAs con nieve-.
Las pantallas de 132 columnas no son estndar y varan de unas tarjetas grficas a otras, por lo que
no las trataremos. Lo que s se puede hacer -con cualquier EGA y VGA- es llamar a la BIOS para que cargue
el juego de caracteres 8x8, lo que provoca un aumento del nmero de lneas a 43 (EGA) o 50 (VGA), as
como un lgico aumento de la memoria de vdeo requerida (que como siempre, empieza en 0B800h).
En las variables de la BIOS (apndice III) los bytes 49h-66h estn destinados a controlar la pantalla;
su consulta puede ser interesante, como demostrar este ejemplo: el siguiente programa comprueba el tipo
de pantalla, para determinar su segmento, llamando a la BIOS (vase el apndice de las funciones del DOS
y de la BIOS). Si no es una pantalla de texto estndar no realiza nada; en caso contrario la recorre y convierte
todos sus caracteres a maysculas, sin alterar el color:
mays SEGMENT
ASSUME CS:mays, DS:mays
ORG 100h ; programa .COM ordinario
inicio:
MOV AH,15 ; funcin para obtener modo de vdeo
INT 10h ; llamar a la BIOS
MOV BX,0B000h ; segmento de pantalla monocroma
MOV CX,2000 ; tamao (caracteres) de la pantalla
CMP AL,7 ; es realmente modo monocromo?
JE datos_ok ; en efecto
MOV BX,0B800h ; segmento de pantalla de color
CMP AL,3 ; es modo de texto de 80 columnas?
JE pant_color ; en efecto
CMP AL,2 ; es modo de texto de 80 columnas?
JE pant_color ; en efecto
MOV CX,1000 ; tamao (caract.) pantalla 40 col.
CMP AL,1 ; es modo texto de 40 columnas?
JBE pant_color ; as es
MOV AL,1 ; pantalla grfica o desconocida:
JMP final ; fin de programa (errorlevel=1)
pant_color: MOV AX,40h ; considerar pgina activa<>0
MOV DS,AX ; DS = 40h (variables de la BIOS)
MOV AX,DS:[4Eh] ; desplazamiento de la pgina activa
SHR AX,1 ; desplazamiento / 2
SHR AX,1 ; desplazamiento / 4
SHR AX,1 ; desplazamiento / 8
SHR AX,1 ; desplazamiento / 16 (prrafos)
ADD BX,AX ; segmento de vdeo efectivo
datos_ok: MOV DS,BX ; DS = segmento de pantalla
XOR BX,BX ; BX = 0 (primer carcter)
otra_letra: CMP BYTE PTR [BX],a; cdigo ASCII menor que a?
JB no_minuscula ; luego no puede ser minscula
CMP BYTE PTR [BX],z; cdigo ASCII mayor de z?
JA no_minuscula ; luego no puede ser minscula
AND BYTE PTR [BX],0DFh ; poner en maysculas
no_minuscula: ADD BX,2 ; apuntar siguiente carcter
LOOP otra_letra ; repetir con los CX caracteres
MOV AL,0 ; fin programa (errorlevel=0)
final: MOV AH,4Ch
INT 21h
mays ENDS
END inicio
7.4 - LA PANTALLA EN MODO GRFICO.
7.4.1. - MODOS GRFICOS.
Dada la inmensidad de estndares grficos existentes para los ordenadores compatibles, que
sucedieron al primer adaptador que slo soportaba texto (MDA), y que de hecho llenan varias estanteras en
las libreras, slo se tratar de una manera general el tema. Se considerarn los estndares ms comunes, con
algunos ejemplos de programacin de la pantalla grfica CGA con la BIOS y programando la VGA
directamente para obtener la velocidad y potencia del ensamblador. Las tarjetas grficas tradicionales
administran normalmente entre 16 Kb y 1 Mb de memoria de vdeo, en el segmento 0B800h las
CGA/Hrcules y en 0A000h las VGA. En los modos de vdeo que precisan ms de 64 Kb se recurre a
tcnicas especiales, tales como planos de bits para los diferentes colores, o bien dividir la pantalla en
pequeos fragmentos que se seleccionan en un puerto E/S. Las tarjetas EGA y posteriores vienen
acompaadas de una extensin ROM que parchea la BIOS normal del sistema para aadir soporte al nuevo
sistema de vdeo. A continuacin se listan los principales modos grficos disponibles en MDA, CGA, EGA
y VGA, as como en las SuperVGA Paradise, Trident y Genoa. No se consideran las peculiaridades del PCJr.
107 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
Modo Texto Resolucin Colores Segmento Tarjeta
04h 40x25 320x200 4 B800 CGA, EGA, MCGA, VGA
05h 40x25 320x200 4 grises B800 CGA, EGA
05h 40x25 320x200 4 B800 CGA, VGA
06h 80x25 640x200 2 B800 CGA, EGA, MCGA, VGA
0Dh 40x25 320x200 16 A000 EGA, VGA
0Eh 80x25 640x200 16 A000 EGA, VGA
0Fh 80x25 640x350 2 A000 EGA, VGA
10h 80x25 640x350 4 A000 EGA con 64K
10h 80x25 640x350 16 A000 EGA con 256K, VGA
11h 80x30 640x480 2 A000 VGA, MCGA
12h 80x30 640x480 16/256k A000 VGA
13h 40x25 320x200 256/256k A000 VGA, MCGA
27h 720x512 16 Genoa
29h 800x600 16 A000 Genoa
2Dh 640x350 256/256k A000 Genoa
2Eh 640x480 256/256k A000 Genoa
2Fh 720x512 256 Genoa
30h 800x600 256/256k A000 Genoa
37h 1024x768 16 A000 Genoa
58h 100x75 800x600 16/256k A000 Paradise VGA
59h 100x75 800x600 2 A000 Paradise VGA
5Bh 100x75 800x600 16/256k A000 Trident TVGA 8800, 8900
5Bh 640x350 256 Genoa 6400
5Ch 80x25 640x400 256 A000 Trident TVGA 8800
5Ch 640x480 256 Genoa 6400
5Dh 80x30 640x480 256 A000 Trident TVGA 8800 (512K)
5Eh 80x25 640x400 256 Paradise VGA
5Eh 800x600 256 Trident 8900
5Eh 800x600 256 Genoa 6400
5Fh 80x30 640x480 256 Paradise VGA (512K)
5Fh 1024x768 16/256k A000 Trident TVGA 8800 (512K)
5Fh 1024x768 16 Genoa 6400
61h 96x64 768x1024 16/256k A000 Trident TVGA 8800 (512K)
62h 1024x768 256 Trident TVGA 8900
6Ah 800x600 16 Genoa 6400
7Ch 512x512 16 Genoa
7Dh 512x512 256 Genoa
Las tarjetas grficas son muy distintas entre s a nivel de hardware, por la manera en que gestionan
la memoria de vdeo. Las tarjetas SuperVGA complican an ms el panorama. En general, un programa que
desee aprovechar al mximo el ordenador deber apoyarse en drivers o subprogramas especficos, uno para
cada tarjeta de vdeo del mercado. Esto es as porque aunque la BIOS del sistema (o el de la tarjeta) soporta
una serie de funciones estndar para trabajar con grficos, existen bastantes problemas. En primer lugar, su
ineficiente diseo lo hace extremadamente lento para casi cualquier aplicacin seria. Bastara con que las
funciones que implementa la BIOS (pintar y leer puntos de la pantalla) fueran rpidas, slo eso!, para lo que
tan slo hace falta una rutina especfica para cada modo de pantalla, que la BIOS debera habilitar nada ms
cambiar de modo; casi todas las dems operaciones realizadas sobre la pantalla se apoyan en esas dos y ello
no requerira software adicional para mantener la compatibilidad entre tarjetas. Sin embargo, los programas
comerciales no tienen ms remedio que incluir sus propias rutinas rpidas para trazar puntos y lneas en
drivers apropiados (y de paso aaden alguna funcin ms compleja). Adems, y por desgracia, no existe NI
UNA SOLA funcin oficial en la BIOS que informe a los programas que se ejecutan de cosas tan elementales
como los modos grficos disponibles (con sus colores, resolucin, etc.); esto no slo es problemtico en las
tarjetas grficas: la anarqua y ausencia de funciones de informacin tambin se repite con los discos, el
teclado, ... aunque los programadores ya estamos acostumbrados a realizar la labor del detective para
averiguar la informacin que los programas necesitan. Sin embargo, con los grficos no podemos y nos
vemos obligados a preguntar al usuario qu tarjeta tiene, de cuntos colores y resolucin, en qu modo... y
lo que es peor: la inexistencia de funciones de informacin se agrava con el hecho de que las VGA de los
dems fabricantes hayan asignado de cualquier manera los nmeros de modo. De esta manera, por ejemplo,
una tarjeta Paradise en el modo 5Fh tiene de 640x400 puntos con 256 colores, mientras que una Trident tiene,
en ese mismo modo, 1024x768 con 16 colores. En lo nico que coinciden todas las tarjetas es en los primeros
108 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
modos de pantalla, definidos inicialmente por IBM. Muchas SuperVGA tienen funciones que informan de
sus modos, colores y resoluciones, lo que sucede es que en esto no se han podido poner de acuerdo los
fabricantes y la funcin de la BIOS de la VGA a la que hay que invocar para obtener informacin, difiere
de unas tarjetas a otras!. Afortunadamente, existe un estndar industrial en tarjetas SuperVGA, el estndar
VESA, que aunque ha llegado demasiado tarde, mltiples VGA lo soportan y a las que no, se les puede
aadir soporte con un pequeo driver residente. Hablaremos de l ms tarde.
No conviene seguir adelante sin mencionar antes la tarjeta grfica Hrcules. Se trata de una tarjeta
que apareci en el mercado muy poco despus que la CGA de IBM, con el doble de resolucin y
manteniendo la calidad MDA en modo texto. Esta tarjeta no est soportada por la BIOS (manufacturada por
IBM) y los fabricantes de SuperVGA tampoco se han molestado en soportarla por software, aunque s por
hardware. Est muy extendida en las mquinas antiguas, pero hoy en da no se utiliza y su programacin
obliga a acceder a los puertos de entrada y salida de manera directa al ms bajo nivel.
7.4.2.- DETECCIN DE LA TARJETA GRFICA INSTALADA.
El siguiente procedimiento es uno de tantos para evaluar la tarjeta grfica instalada en el ordenador.
Devuelve un valor en BL que es el mismo que retorna la INT 10h al llamarla con AX=1A00h (ver funciones
de la BIOS en los apndices): 0 1 para indicar que no hay grficos; 2 si hay CGA; 3, 4 5 si existe una
EGA; 6 si detecta una PGA; 7 u 8 si hay VGA o superior y 10, 11 12 si existe MCGA. Retorna 255 si la
tarjeta es desconocida (muy raro). La rutina funciona en todos los ordenadores, con o sin tarjetas grficas
instaladas y del tipo que sean.
tipo_tarjeta PROC
PUSH DS
MOV AX,1A00h
INT 10h ; solicitar informacin VGA a la BIOS
CMP AL,1Ah ; BL = tipo de tarjeta
JE tarjeta_ok ; funcin soportada (hay VGA)
MOV AX,40h
MOV DS,AX
MOV BL,10h
MOV AH,12h
INT 10h ; solicitar informacin EGA a la BIOS
CMP BL,10h
JE no_ega ; de momento, no es EGA
MOV BL,1 ; supuesto MDA
TEST BYTE PTR DS:[87h],8 ; estado del control de vdeo
JNZ tarjeta_ok ; es MDA
MOV BL,4 ; supuesto EGA color
OR BH,BH
JZ tarjeta_ok ; as es
INC BL ; es EGA mono
JMP tarjeta_ok
no_ega: MOV BL,2 ; supuesto CGA
CMP WORD PTR DS:[63h],3D4h ; base del CRT
JE tarjeta_ok ; as es
DEC BL ; es MDA
tarjeta_ok: POP DS
RET
tipo_tarjeta ENDP
7.4.3. - INTRODUCCIN AL ESTNDAR GRFICO VGA.
La tarjeta VGA es el estndar actual en ordenadores personales, siendo el sistema de vdeo mnimo
que incluye la mquina ms asequible. En este apartado estudiaremos la forma bsica de programar sus
modos grficos, haciendo un especial hincapi en el tema menos claramente explicado por lo general: el
color. Se ignorarn por completo las tarjetas CGA y Hrcules, aunque s se indicar qu parte de lo expuesto
se puede aplicar tambin a la EGA. Tampoco se considerar la MCGA, un hbrido entre EGA y VGA que
solo equipa a los PS/2-30 de IBM, bastante incompatible adems con la EGA y la VGA.
109 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
La VGA soporta todos los modos grficos estndar de las tarjetas anteriores, resumidos en la figura
7.4.3.1, si bien los correspondientes a la CGA (320x200 en 4 colores y 640x200 monocromo) son inservibles
para prcticamente cualquier aplicacin grfica actual.
Modo (hex) Resolucin Colores Segmento Organizacin 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 GRFICOS DE VIDEO
La organizacin de
la memoria (entrelazado,
planos de bit o lineal) es la
manera en que se direcciona
la memoria de vdeo por
parte de la CPU. Por
ejemplo, en el modo 6, cada
pixel de la pantalla est
asociado a un bit (8 pixels
por byte) a partir de la
direccin B800:0000; sin
embargo, cuando se recorren
80 bytes en la memoria (640
bits o pixels, primera lnea
completa) no se pasa a la segunda lnea de la pantalla sino unas cuantas ms abajo, en una arquitectura
relativamente compleja debida a las limitaciones del hardware de la CGA. Esto ha sido superado en las
siguientes tarjetas, en las que las lneas estn consecutivas de manera lgica en una organizacin lineal, si
bien el lmite de 64 Kb de memoria que puede direccionar en un segmento el 8086 ha obligado al truco de
los planos de bit. Para establecer el modo de vdeo se puede emplear una funcin del lenguaje de
programacin que se trate o bien llamar directamente a la BIOS, si no se desea emplear la librera grfica
del compilador: la funcin 0 (AH=0) de servicios de vdeo de la BIOS (INT 10h) establece el modo de vdeo
solicitado en AL. En Turbo C sera, 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 mdulos internos, que definen conjuntos de registros direccionables
en el espacio E/S del 80x86. En la EGA eran de slo escritura, aunque en la VGA pueden ser tanto escritos
como ledos. Por un lado est el secuenciador, encargado de la temporizacin necesaria para el acceso a la
memoria de vdeo. Por otro lado tenemos el controlador de grficos, encargado del trfico de informacin
entre la CPU, la memoria de vdeo y el controlador de atributos; consta de 9 registros cuya programacin es
necesaria para trazar puntos a gran velocidad en los modos de 16 colores. El controlador de atributos
gestiona la paleta de 16 colores y el color del borde. Por ltimo, el DAC o Digital to Analog Converter se
encarga en la VGA (no dispone de l la EGA) de gestionar los 262.144 colores que se pueden visualizar en
pantalla. La parte del len son los 768 registros! de 6 bits que almacenan la intensidad en las componentes
roja, verde y azul de cada color, de los 256 que como mucho puede haber simultneamente 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 ms un
cuarto que indica la intensidad. Sin embargo, la EGA emplea dos bits por cada una de las tres componentes
de color, con lo que obtiene 2
6
=64 colores diferentes. Para asociar estos 64 colores a los no ms de 16 que
puede haber en un momento determinado en la pantalla, se emplean los 16 registros de paleta del controlador
de atributos: En cada uno de estos registros, de 6 bits significativos, se definen los 16 colores posibles. La
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 de
la CGA. As, por ejemplo, en los modos de texto el color 0 es el negro y el 15 el blanco brillante, si bien
se puede alterar esta asignacin. Un cambio en un registro de paleta afecta instantneamente a todo el rea
de pantalla pintado de ese color. El valor binario almacenado en los registros de paleta tiene el formato
xxrgbRGB, siendo rgb los bits asociados a las componentes roja, verde y azul de baja intensidad, y RGB
sus homlogos en alta intensidad. As, el valor 010010b se corresponde con el verde ms brillante.
Modos de 16 colores en VGA.
En la VGA el tema del color en los modos de pantalla de 16 colores (tanto grficos como de texto)
se complica algo ms, debido a la presencia del DAC: una matriz de 256 elementos que constan cada uno
de 3 registros de 6 bits. Cada uno de los registros de paleta apunta a un elemento del DAC, que es quien
realmente contiene el color; lo que sucede es que los registros del DAC son programados por la BIOS para
emular los 64 colores de la EGA. Existen dos maneras diferentes de indexar en el DAC los registros de
paleta, de manera que se puede dividir el DAC en 16 bloques de 16 elementos o bien en 4 bloques de 64
elementos: en un momento dado, slo uno de los bloques (denominado pgina de color del DAC) est activo.
Esto significa que se pueden crear 16 4 subpaletas, pudindose activar una u otra libremente con una
funcin de la BIOS de la VGA. Por defecto, la BIOS establece 4 pginas de 64 elementos en el DAC, de
manera que valores en el rango 0-63 en los 16 registros de paleta referencien a posiciones distintas en el DAC
(al rea 0-63, al 64-127, al 128-191 al 192-255): por defecto, la BIOS emplea los elementos 0..63 del DAC
que programa para emular los 64 colores de la EGA. Sin embargo, puede resultar ms interesante disponer
de 16 subpaletas de 16 elementos para conseguir determinados efectos grficos: en este caso no tiene sentido
que los registros de paleta almacenen valores fuera del rango 0-15 (de hecho, solo se consideran los 4 bits
menos significativos de los mismos). La figura 7.4.3.2 expresa grficamente la manera en que se genera el
color. Se pueden definir, por ejemplo, las 16 subpaletas en tonos ascendentes de azul y, cambiando la pgina
o subpaleta activa a cierta velocidad se puede hacer que la imagen se encienda y apague rtmica y
suavemente. Por supuesto, tambin se pueden obtener efectos similares alterando directamente los registros
del DAC, aunque es mucho ms lento que conmutar entre varias paletas ya definidas. Conviene resaltar que
el color del borde de la pantalla se define en la EGA y en la VGA en una especie de registro que sigue a los
16 registros de paleta: en la VGA no interviene el DAC en la generacin del color del borde, del que solo
existen por consiguiente 64 tonos (si bien el borde suele estar en color negro y su tamao reducido y variable
lo hace inservible para nada).
Los pixels en los modos grficos de 16 colores pueden parpadear, si bien es una tcnica poco
empleada: para ello, basta con cambiar un bit de un registro del controlador de atributos, aunque existe una
funcin de la BIOS que realiza dicha tarea (llamar a la INT 10h con AX=1003h y BX=1 para activar el
parpadeo -situacin por defecto en los modos de texto- BX=0 para desactivarlo).
0..63
CASO 4 X 64
64..127
valor 0..63 elemento del DAC pgina (0..3)
128..191 seleccionable
(0 por defecto)
192..255
color 0..15
en pantalla (0..15) valor 0..15 elemento del DAC 16..31
32..47
CASO 16 x 16 : pgina (0..15)
: seleccionable
224..239
240..255
Elementos del DAC
FIGURA 7.4.3.2: OBTENCIN 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 seal verde para emular
la presencia de las otras dos. En concreto, suma el 30% del valor rojo, el 59% del verde y el 11% del azul
111 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
y el resultado lo fuerza al rango 0-63, lo cual simula aproximadamente la intensidad que percibira el ojo
humano con los colores reales. Si se accediera directamente al hardware sin ayuda de la BIOS, lo cual no
es nuestro caso, este sera un aspecto a considerar. Por ltimo, decir que en el modo de 4 colores y 350
lneas, solo se emplean los registros de paleta 0, 1, 4 y 5, si bien lo normal aqu es esperar que existan 16
colores (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 generacin del color se aparta de lo estudiado hasta
ahora para los dems modos grficos y los de texto, ya que solo interviene el DAC: el byte de memoria de
vdeo asociado a cada punto de la pantalla apunta directamente a un elemento del DAC. Por tanto, los
registros de paleta del controlador de atributos no se emplean en este modo, siendo ms sencillo el proceso
de generacin del color.
Cmo 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
programacin, ya que un acceso directo al hardware sin ms precauciones puede provocar interferencias con
algunas tarjetas VGA. Conviene tambin emplear las funciones que cambian de una sola vez un conjunto de
registros del DAC, ya que hacerlo uno por uno es demasiado lento. Otra ventaja de emplear la BIOS es que
sta hace automticamente las conversiones necesarias para lograr la mejor visualizacin posible en pantallas
monocromas. En algunos casos, las paletas que define por defecto la BIOS al establecer el modo de pantalla
son apropiadas. Sin embargo, puede ser til cambiarlas para lograr un degradado atractivo en los modos de
16 colores y casi obligatorio en el modo de 256 colores, dada la absurda paleta propuesta por la BIOS. Para
definir un color en el DAC, basta con un poco de imaginacin: si las tres componentes estn a cero, saldr
el negro; si estn a 63 (valor mximo) saldr un blanco brillante; si se ponen la roja y la azul en 32 y la
verde en 0, saldr un morado de oscuridad mediana. Se puede realizar un bucle y llenar los primeros 64
elementos del DAC con valores crecientes en una componente de color, poniendo a 0 las dems: de esa
manera, 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 grfico: %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 a
la BIOS (INT 10h) con AX=1002h y ES:DX
apuntando a un buffer de 17 bytes: uno para cada
registro de paleta ms otro final para el color del
borde de la pantalla. El Turbo C permite cambiar
la paleta con instrucciones de alto nivel; sin
embargo, quienes no deseen aprender las
particularidades de cada compilador, siempre
pueden recurrir a la BIOS, que cambiando la paleta
es bastante solvente. Echemos un vistazo al
ejemplo de la figura 7.4.3.3 (para ejecutar este
programa hay que tener en cuenta que el fichero
EGAVGA.BGI del compilador ha de estar en el
directorio de trabajo). Al principio se trazan unas
bandas verticales con la funcin line() que sern
coloreadas con los 16 colores por defecto, aunque
cambiarn instantneamente al modificar la paleta.
Al definir la paleta, los 4 primeros registros son
asignados con los 4 posibles tonos de rojo, ms
bien 3 (el primero es el negro absoluto): rojo, rojo
oscuro y rojo brillante. Todos los dems registros y el borde de la pantalla son puestos a 0 (negro) por lo que
en la pantalla quedan visibles slo las tres bandas verticales citadas. El cambio de la paleta es instantneo,
lo que permite hacer efectos especiales. En la VGA, recurdese que los valores de la paleta son simples
punteros al DAC y no los colores reales. Lo que sucede es que los registros del DAC son inicializados al
cambiar el modo de pantalla de tal manera que emulan los colores que se obtendra en una EGA... a menos
que se cambien los valores de dichos registros.
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 primer
elemento del DAC a cambiar (tpicamente 0) y en CX el nmero de elementos a modificar (a menudo los
256 posibles). Tambin se pasa en ES:DX la direccin de la tabla de 768 bytes que contiene la informacin:
3 bytes consecutivos para cada elemento del DAC (rojo, verde y azul) aunque solo son significativos los 6
bits de menor orden de cada byte. Existe tambin otra funcin bastante interesante, invocable con AX=1013h
y que consta de dos subservicios: el primero se selecciona poniendo un 0 en BL, e indicando en BH si se
desean 4 pginas de 64 elementos en el DAC (BH=0) 16 pginas de 16 elementos (BH=1). El segundo
servicio se indica llamando con BL=1, y permite seleccionar la pgina del DAC activa en BH (0-3 0-15,
segn cmo est estructurado). Obviamente, esta funcin no est disponible en el modo 13h de 256 colores,
en el que no interviene la paleta (slo 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 grfico: %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; /* nmero 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 demostracin, desarrollado
a partir del anterior, que requiere ya un autntico
adaptador VGA. Lo primero que se hace es
seleccionar el modo de 16 pginas en el DAC,
estableciendo la pgina 2 como activa
(exclusivamente por antojo mio). Ello significa que
se emplearn los elementos 32..47 del DAC (la
pgina 0 apuntara a los elementos 0..15, la 1
hubieran sido los elementos 16..31 y as
sucesivamente). Los registros de paleta, simples
ndices en el DAC, toman los valores 0,1,...,15
(excepto el 17 byte, color del borde, puesto a 0
para seleccionar el negro). A continuacin, basta
programar los registros 32..47 del DAC con los
colores deseados, entre los 262.144 posibles. Como
cada componente puede variar entre 0 y 63,
elegimos 16 valores espaciados proporcionalmente
(0, 4, 8,..., 60) y los asignamos a las componentes
roja y verde (rojo+verde=amarillo), apareciendo en
la pantalla una escala de 16 amarillos (el primero,
negro absoluto) de intensidad creciente. Si bien 16
colores son pocos, son suficientes para representar
con relativa precisin algunas imgenes,
especialmente en las que predomina un color
determinado (los ficheros grficos se ven
normalmente tan mal en los modos de 16 colores
debido a que respetan la paleta de la EGA, en la
VGA sera otra historia).
Por supuesto, existen ms funciones que stas, entre ellas las que permiten cambiar slo un registro
de paleta o un elemento del DAC (y no un bloque); sin embargo, son ms lentas cuando se va a cambiar un
conjunto de registros. En cualquier caso, el lector puede consultarlas en el fichero INTERRUP.LST si lo
desea. Tambin existen en la VGA las funciones inversas (obtener paletas y registros del DAC). El acceso
por medio de la BIOS para cambiar la paleta es a menudo ms cmodo que emplear funciones del lenguaje
de programacin y garantiza en ocasiones un mayor nivel de independencia respecto a la evolucin futura
del hardware (aunque si la librera grfica llama a la BIOS...). Sin embargo, para otras aplicaciones, es mejor
no usar la BIOS. Por ejemplo, el programa de la figura 7.4.3.5 accede directamente a los registros de la VGA
para modificar la paleta en dos bucles, en el primero disminuyendo la luminosidad de la pantalla (hasta
dejarla negra) y en el segundo restaurndola de nuevo. Este efecto cinematogrfico hubiera sido imposible
a travs de la BIOS por razones de velocidad: el acceso directo al hardware, con precauciones (en este caso,
esperar el retrazado vertical para evitar interferencias) es a veces inevitable. El programa de ejemplo funciona
tambin en monitores monocromos, aunque en la prctica slo acte en ellos sobre la componente verde. El
lector deber consultar bibliografa especializada para realizar este tipo de programacin.
113 ARQUITECTURA 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 no
recomendado. La razn estriba en el mal diseo de la BIOS inicial de IBM, no mejorado tampoco por las
VGA clnicas. El problema es que las BIOS emplean 4, 5 y hasta 10 veces ms tiempo del necesario para
FIGURA 7.4.3.5:
/*********************************************************************
* EFECTO CINEMATOGRAFICO DE DESVANECIMIENTO Y POSTERIOR *
* REAPARICION DE LA PANTALLA CON ACCESO DIRECTO AL HARDWARE VGA. *
*********************************************************************/
#include <dos.h>
void main()
{
unsigned char dac[256][3];
register i, j;
for (i=0; i<256; i++) { /* anotar la paleta activa */
disable();
outportb (0x3C7, i);
dac [i][0] = inportb (0x3C9); /* R */
dac [i][1] = inportb (0x3C9); /* G */
dac [i][2] = inportb (0x3C9); /* B */
enable();
}
/* claridad descendente desde el
64/64-avo al 0/64-avo de intensidad */
for (i=64; i>=0; i--) {
while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */
while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */
for (j=0; j<256; j++) {
disable();
outportb (0x3C8, j);
outportb (0x3C9, dac[j][0]*i >> 6);
outportb (0x3C9, dac[j][1]*i >> 6);
outportb (0x3C9, dac[j][2]*i >> 6);
enable();
}
}
/* claridad ascendente desde el
0/64-avo al 64/64-avo de intensidad */
for (i=0; i<=64; i++) {
while (!((inportb(0x3DA) & 8)==8)); /* esperar retrazo vertical */
while (!((inportb(0x3DA) & 8)==0)); /* esperar su fin */
for (j=0; j<256; j++) {
disable();
outportb (0x3C8, j);
outportb (0x3C9, dac[j][0]*i >> 6);
outportb (0x3C9, dac[j][1]*i >> 6);
outportb (0x3C9, dac[j][2]*i >> 6);
enable();
}
}
}
trazar los puntos. La causa de este problema no
reside en que empleen rutinas multipropsito para
todos los modos, ya que existen bsicamente slo
tres tipos de arquitectura de pantalla (modos CGA,
16 colores y 256 colores). El fallo reside,
simplemente, en que han sido desarrollados sin
pensar en la velocidad. Por ejemplo, la BIOS
emplea el algoritmo ms lento posible que existe
para trazar puntos en los modos de 16 colores. Lo
ms conveniente es utilizar los recursos del
lenguaje de programacin o, mejor an, acceder
directamente a la memoria de pantalla con
subrutinas en ensamblador. Este es el
procedimiento seguido por la mayora de las
aplicaciones comerciales. Sin embargo, la BIOS
tiene la ventaja de que permite normalizar el acceso
a la pantalla. As, un programa puede fcilmente
trazar un punto en el modo 1024x768x256 de una
SuperVGA (y nunca mejor dicho, porque como
sean muchos ms de uno...). Para trazar un punto
se coloca en CX la coordenada X, en DX la
coordenada Y, en AL el color, en BH la pgina y
en AH el valor 0Ch. A continuacin se llama,
como es costumbre, a la INT 10h. Para consultar el color de un punto en la pantalla, se cargan CX y DX con
sus coordenadas y BH con la pgina, haciendo AH=0Dh antes de llamar a la INT 10h, la cual devuelve el
color del pixel en AL. La pgina ser normalmente la 0, aunque en los modos de vdeo que soportan varias
pginas sta se puede seleccionar con la funcin 5 de la INT 10h. La existencia de varias pginas de vdeo
se produce cuando en el segmento de 64 Kb de la
memoria de vdeo se puede almacenar ms de una
imagen completa (caso por ejemplo del modo
640x350x16): existen entonces varias pginas (2, 4,
etc.) que se reparten el segmento a partes iguales.
Se puede en estas circunstancias visualizar una
pgina cualquiera mientras se trabaja en las otras,
que mientras tanto permanecen ocultas a los ojos
del usuario.
Modo 13h de 256 colores.
Este modo, de organizacin lineal, no
presenta complicacin alguna: los pixels se suceden
en la memoria de vdeo de izquierda a derecha y de
arriba a abajo, a partir del segmento A000. Cada
punto est asociado a un byte, cuyo valor (0-255)
referencia directamente a un elemento del DAC. En
la figura 7.4.3.6 hay un nuevo listado de ejemplo,
en este caso sin emplear la librera grfica del
Turbo 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 lneas con los valores 0..199. A continuacin define los elementos 0..199 del
DAC de la siguiente manera: los primeros 100 en tonos ascendentes de azul, y los siguientes 100 elementos
114 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
en tonos descendentes de naranja, lo que divide automticamente la pantalla en dos zonas con la estructura
citada. Conseguir el naranja no es complicado: basta sumar rojo con amarillo; como el amarillo es a su vez
rojo ms verde, el naranja se obtiene sumando dos cantidades de rojo por cada una de verde. Los elementos
200..255 del DAC, no empleados en este ejemplo, podran ser definidos con otros colores para dibujar alguna
otra cosa.
Modos de 16 colores.
Para direccionar puntos en los modos de 16 colores, en los que actan interrelacionados los registros
de paleta y el DAC de la manera descrita con anterioridad, es necesario un acceso directo al hardware por
cuestiones de velocidad. Los lectores que no vayan a emplear las funciones del lenguaje de programacin
debern consultar bibliografa especializada en grficos.
Y nada ms.
La nica diferencia de la VGA respecto a la EGA, de hecho, se debe a su peculiar manera de
gestionar el color, as como a la inclusin del modo de 320x200 con 256 colores (el modo de 640x480 es
idntico en funcionamiento al de 640x350 de la EGA, solo cambia la altura de la pantalla). Existe tambin
la posibilidad de colocar la VGA en dos modos de 256 colores alternativos al 13h y basados en el mismo;
en uno se alcanzan 320x240 puntos y en el otro 320x400. La bibliografa especializada en grficos explica
los pasos a realizar para conseguir esto, factible en la totalidad de las tarjetas VGA del mercado. Sin
embargo, estos modos requieren un cambio en el modo de direccionamiento de los pixels, que pasa a ser ms
complejo -aunque ms potente para algunas aplicaciones-.
7.4.4. - EJEMPLO DE GRFICOS EMPLEANDO LA BIOS.
Este programa ejemplo accede a la pantalla empleando las funciones de la BIOS para trazar puntos
(ver apndice sobre funciones de la BIOS). Utiliza el modo CGA de 640x200 puntos, aunque se puede
configurar para cualquier otro modo. El programa dibuja una conocida red en las cuatro esquinas de la
pantalla, trazando lneas. El algoritmo empleado es el de Bresseham con clculo incremental de puntos
(aunque al estar separada la rutina que traza el punto esta caracterstica no se aprovecha, pero es fcil de
implementar si en vez de llamar a la BIOS para pintar se emplea una rutina propia mezclada con la que traza
la recta). La velocidad del algoritmo es muy elevada, sobre todo con las lneas largas, mxime teniendo en
cuenta que se trata posiblemente de una de sus implementaciones ms optimizada (slo usa una variable y
mantiene todos los dems valores en los 7 registros de datos de la CPU, sin emplear demasiado la pila y
duplicando cdigo cuando es preciso en los puntos crticos). No entrar en explicaciones matemticas del
mtodo, del que hay pautas en su listado. Existen versiones de este mtodo que consideran de manera especial
las lneas verticales y horizontales para pintarlas de manera ms rpida, aunque yo personalmente prefiero
rutinas independientes para esas tareas con objeto de no ralentizar el trazado de rectas normales.
; ********************************************************************
; * *
; * RED.ASM - Demostracin de grfica en CGA utilizando BIOS *
; * *
; ********************************************************************
modo EQU 6 ; modo de vdeo
max_x EQU 640
max_y EQU 200
max_color EQU 2
red SEGMENT
ASSUME CS:red, DS:red
ORG 100h
inicio:
MOV AX,modo
INT 10h ; modo de pantalla
MOV AL,max_color-1 ; color visible
MOV BX,0 ; contador para eje Y
MOV BP,0 ; contador para eje X
otras_cuatro: MOV CX,0
MOV DX,BX
MOV SI,BP
MOV DI,max_y-1
CALL recta ; primera recta
MOV CX,max_x-1
MOV SI,max_x-1
SUB SI,BP
CALL recta ; segunda
MOV CX,BP
MOV DX,0
MOV SI,0
MOV DI,max_y-1
SUB DI,BX
CALL recta ; tercera
MOV CX,max_x-1
SUB CX,BP
MOV SI,max_x-1
CALL recta ; cuarta
ADD BX,6
ADD BP,14
CMP BX,max_y
JB otras_cuatro
MOV AH,0
INT 16h ; esperar pulsacin de tecla
MOV AX,3
INT 10h ; volver a modo texto
INT 20h ; fin de programa
recta PROC
PUSH AX ; de (CX,DX) a (SI,DI) color AL
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
MOV color,AL
MOV AX,SI
SUB AX,CX ; AX = X2-X1
JNC absx2x1
NEG AX
XCHG CX,SI
XCHG DX,DI
absx2x1: MOV BX,DI ; AX = ABS(X2-X1) = dx
SUB BX,DX
MOV BP,1 ; BP = 1 = yincr si Y2>Y1
JNC absy2y1
NEG BP ; BP = -1 = yincr si Y2<=Y1
NEG BX
absy2y1: CMP AX,BX ; BX = ABS(Y2-Y1) = dy
PUSHF
JA noswap ; ABS(pendiente) menor de 1
XCHG AX,BX
noswap: SHL BX,1 ; BX = dy * 2
MOV SI,BX
SUB SI,AX ; SI = dy * 2 - dx = d
115 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
MOV DI,BX
SUB DI,AX
SUB DI,AX ; DI = dy*2-dx*2 = incr2
POPF
JBE penmay1 ; pendiente mayor de 1
penmen1: PUSH AX
MOV AL,color
CALL punto ; en (CX, DX) = (x, y)
POP AX
INC CX ; x++
AND SI,SI ; (SI>0) ? -> d > 0 ?
JS noincy
ADD SI,DI ; d > 0 : d = d + incr2
ADD DX,BP ; y = y + yincr
DEC AX ; dx--
JNZ penmen1
JMP fin
noincy: ADD SI,BX ; d < 0 : d = d + incr1
DEC AX
JNZ penmen1
JMP fin
penmay1: PUSH AX
MOV AL,color
CALL punto ; en (CX, DX) = (x, y)
POP AX
ADD DX,BP ; y = y + yincr
AND SI,SI ; (SI>0) ? -> d > 0 ?
JS noincx
ADD SI,DI ; d > 0 : d = d + incr2
INC CX ; x++
DEC AX ; dx--
JNZ penmay1
JMP fin
noincx: ADD SI,BX ; d = d + incr1
DEC AX ; dx--
JNZ penmay1
fin: POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
RET
color DB 0
recta ENDP
punto PROC
PUSH BX ; preservar registros (salvo AX)
PUSH CX
PUSH DX
PUSH BP
PUSH SI
PUSH DI
MOV AH,0Ch ; trazar punto usando BIOS
XOR BX,BX
INT 10h
POP DI
POP SI
POP BP
POP DX
POP CX
POP BX
RET
punto ENDP
red ENDS
END inicio
Quiz el lector opine que RED.ASM no es tan rpido. Y tiene razn: la culpa es de la BIOS, que
consume un alto porcentaje del tiempo de proceso. Sustituyendo la rutina punto por una rutina de trazado
de puntos propia, como la que se lista a continuacin, la velocidad puede llegar a quintuplicarse en un
hipottico RED2.ASM que la invocara.
punto640x200_C PROC ; en (CX, DX) de color AL (CGA 640x200)
PUSH DS ; slo se corrompe AX
PUSH BX
PUSH CX
PUSH DX
MOV BX,0B800h ; segmento de pantalla CGA
MOV DS,BX
MOV AH,CL ; preservar parte baja de cx
XCHG BX,CX ; BX = cx
MOV CL,3
SHR BX,CL ; BX = cx / 8
SHR DX,1 ; DX = int (cy / 2)
JNC no_add
ADD BX,8192 ; BX = cx / 8 + (cy MOD 2) * 8192
no_add: INC CL ; CL = 4
SHL DX,CL ; DX = (cy / 2) * 16
ADD BX,DX ; BX = BX + (cy / 2) * 16
SHL DX,1
SHL DX,1 ; DX = (cy / 2) * 64
ADD BX,DX ; BX = BX + (cy / 2) * 80
MOV CL,AH ; recuperar parte baja de cx
AND CL,7 ; dejar n de bit a pintar (0..7)
XOR CL,7 ; invertir orden de numeracin
MOV AH,1 ; bit a borrar de la pantalla en AH
SHL AX,CL ; AH = bit a borrar, AL = bit a pintar
NOT AH
AND [BX],AH ; borrar punto anterior
OR [BX],AL ; ubicar nuevo punto (1/0)
POP DX
POP CX
POP BX
POP DS
RET
punto640x200_C ENDP
Para estudiar el funcionamiento de la pantalla CGA el lector puede hacer un programa que recorra
la memoria de vdeo para comprender la manera en que est organizada, un tanto peculiar pero no demasiado
complicada. Sin embargo, con EGA y VGA no es tan sencillo realizar operaciones sobre la pantalla debido
a la presencia de planos de bit; salvo contadas excepciones como la del siguiente apartado.
7.4.5. - EJEMPLO DE GRFICOS ACCEDIENDO AL HARDWARE.
El siguiente programa de ejemplo accede directamente al segmento de vdeo de la VGA (0A000h)
para trazar los puntos. Dibuja un vistoso ovillo basado en circunferencias con centro ubicado en una
circunferencia base imaginaria, aprovechando los 256 colores de la VGA estndar en el modo 320x200. Como
la paleta establecida por defecto es poco interesante, se define previamente una paleta con apoyo directo en
el hardware (el mtodo empleado es sencillo pero no recomendable, provoca nieve con algunas tarjetas). Se
emplea el color verde, nico visualizable en monitores monocromos (aunque cambiando la paleta con las
funciones de la BIOS no hubiera sido necesario). La VGA en modo 13h asocia cada punto de pantalla a un
byte, por lo que la pantalla es una matriz de 64000 bytes en el segmento 0A000h. Recordar que la frmula
para 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 una
VGA Paradise (BIOS de 14/7/88) se emplean 4 segundos y 8 centsimas en generar la imagen, mientras que
tal y como est el programa lo dibuja en 40,4 centsimas (10,1 veces ms rpido); todos estos datos
cronometrados con precisin sobre un 386-25 sin memoria cach teniendo instalada la opcin de SHADOW
ROM (la lenta ROM copiada en RAM, incluida la BIOS de la VGA, por tanto no compite con desventaja).
El algoritmo empleado para trazar la circunferencia es de J. Michener, quien se bas a su vez en otro
de J. Bresseham desarrollado para plotter. La versin que incluyo genera circunferencias en pantallas de
116 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
relacin de aspecto 1:1, en otras (ej., de 640 x 200) producira elipses. No entrar en su demostracin
matemtica, que nada tiene que ver con el ensamblador; baste decir que la rutina se basa exclusivamente en
la aritmtica entera calculando un solo octante de la circunferencia (los dems los obtiene por simetra).
; ********************************************************************
; * *
; * OVILLO.ASM - Demostracin de grfica en VGA utilizando hardware *
; * *
; ********************************************************************
modo EQU 13h ; modo de vdeo
max_x EQU 320
max_y EQU 200
max_color EQU 256
oviseg SEGMENT
ASSUME CS:oviseg, DS:oviseg
ORG 100h
inicio:
MOV AX,modo
INT 10h
CALL paleta_verde
MOV CX,max_x
SHR CX,1 ; CX = max_x / 2
MOV DX,max_y
SHR DX,1 ; DX = max_y / 2
MOV BX,DX
SHR BX,1 ; BX = ma_y / 4
CALL ovillo ; en (CX, DX) de radio BX
MOV AH,0
INT 16h ; esperar pulsacin de tecla
MOV AX,3
INT 10h ; volver a modo texto
INT 20h ; fin de programa
paleta_verde PROC
MOV CX,256 ; los 256 registros
MOV DX,3C8h
otro_reg: MOV AL,CL
OUT DX,AL ; registro a programar
INC DX
XOR AL,AL
OUT DX,AL ; componente roja
MOV AL,CL
REPT max_x/320
SHR AL,1
ENDM
OUT DX,AL ; componente verde
XOR AL,AL
OUT DX,AL ; componente azul
DEC DX
LOOP otro_reg
RET
paleta_verde ENDP
ovillo PROC ; circunferencia de circunferencias
MOV BP,BX ; en (CX, DX) con radio BX y color AL
MOV AL,0
MOV SI,BX
XOR DI,DI
SHL BP,1
SUB BP,3
NEG BP ; BP = 3 - 2 * BX
ovillo_acaba: CMP DI,SI
JG ovillo_ok ; ovillo completado
ADD CX,SI
ADD DX,DI
CALL circunferencia ; en (x+SI, y+DI)
INC AL
SUB CX,SI
SUB CX,SI
CALL circunferencia ; en (x-SI, y+DI)
INC AL
SUB DX,DI
SUB DX,DI
CALL circunferencia ; en (x-SI, y-DI)
INC AL
ADD CX,SI
ADD CX,SI
CALL circunferencia ; en (x+SI, y-DI)
INC AL
SUB CX,SI
ADD DX,DI
ADD CX,DI
ADD DX,SI
CALL circunferencia ; en (x+DI, y+SI)
INC AL
SUB CX,DI
SUB CX,DI
CALL circunferencia ; en (x-DI, y+SI)
INC AL
SUB DX,SI
SUB DX,SI
CALL circunferencia ; en (x-DI, y-SI)
INC AL
ADD CX,DI
ADD CX,DI
CALL circunferencia ; en (x+DI, y-SI)
INC AL
SUB CX,DI
ADD DX,SI ; CX = x, DX = y
CMP BP,0
JG ovillo_decx
ADD BP,DI
ADD BP,DI
ADD BP,DI
ADD BP,DI
ADD BP,6
JMP ovillo_incy
ovillo_decx: DEC SI
PUSH AX
MOV AX,DI
SUB AX,SI
SHL AX,1
SHL AX,1
ADD BP,AX
POP AX
ADD BP,10
ovillo_incy: INC DI
JMP ovillo_acaba
ovillo_ok: RET
ovillo ENDP
circunferencia PROC ; en (CX,DX) con radio BX y color AL
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV SI,BX
XOR DI,DI
SHL BX,1
SUB BX,3
NEG BX ; BX = 3 - 2 * BX
circunf_acaba: CMP DI,SI
JG circunf_ok ; circunferencia completada
ADD CX,SI
ADD DX,DI
CALL punto ; en (x+SI, y+DI)
SUB CX,SI
SUB CX,SI
CALL punto ; en (x-SI, y+DI)
SUB DX,DI
SUB DX,DI
CALL punto ; en (x-SI, y-DI)
ADD CX,SI
ADD CX,SI
CALL punto ; en (x+SI, y-DI)
SUB CX,SI
ADD DX,DI
ADD CX,DI
ADD DX,SI
CALL punto ; en (x+DI, y+SI)
SUB CX,DI
SUB CX,DI
CALL punto ; en (x-DI, y+SI)
SUB DX,SI
SUB DX,SI
CALL punto ; en (x-DI, y-SI)
ADD CX,DI
ADD CX,DI
CALL punto ; en (x+DI, y-SI)
SUB CX,DI
ADD DX,SI ; CX = x, DX = y
CMP BX,0
JG circunf_decx
ADD BX,DI
ADD BX,DI
ADD BX,DI
ADD BX,DI
ADD BX,6
JMP circunf_incy
circunf_decx: DEC SI
PUSH AX
MOV AX,DI
SUB AX,SI
SHL AX,1
SHL AX,1
ADD BX,AX
POP AX
ADD BX,10
circunf_incy: INC DI
JMP circunf_acaba
circunf_ok: POP DI
POP SI
POP DX
POP CX
POP BX
RET
circunferencia ENDP
punto PROC ; trazar punto en 320x200 con 256 col.
PUSH DS ; en (CX, DX) con color AL
PUSH CX
PUSH DX
XCHG DH,DL ; DX = cy * 256
ADD CX,DX ; CX = cy * 256 + cx
SHR DX,1
SHR DX,1 ; DX = cy * 64
ADD CX,DX ; CX = cy * 320 + cx
MOV DX,0A000h
MOV DS,DX ; segmento VGA
XCHG BX,CX ; preservar BX en CX, BX = offset
MOV [BX],AL ; pintar el punto
XCHG BX,CX ; restaurar BX
POP DX ; restaurar dems registros
POP CX
POP DS
RET
punto ENDP
oviseg ENDS
END inicio
7.4.6. - EL ESTNDAR GRFICO VESA.
Debido a la anarqua reinante en el mundo de las tarjetas grficas, en 1989 se reunieron un grupo
importante de fabricantes (ATI, Genoa, Intel, Paradise, etc) para intentar crear una norma comn. El resultado
117 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
de la misma fue el estndar VESA. Este estndar define una interface software comn a todas las BIOS para
permitir a los programadores adaptarse con facilidad a las diversas tarjetas sin tener en cuenta sus diferencias
de hardware.
Actualmente, las principales tarjetas soportan la norma VESA. Las ms antiguas pueden tambin
soportarla gracias a pequeos programas residentes que el usuario puede instalar opcionalmente. Para
desarrollar una aplicacin profesional, es una buena norma soportar algn modo estndar de la VGA y, para
obtener ms prestaciones, algn modo VESA para los usuarios que estn equipados con dicho soporte.
Intentar acceder directamente al hardware o a las funciones BIOS propias de cada tarjeta del mercado por
separado, salvo para aplicaciones muy concretas, es ciertamente poco menos que imposible.
Modos grficos.
El estndar VESA soporta multitud de modos grficos, numerados a partir de 100h, si bien algunos
de los ms avanzados (con 32000 o 16 millones de colores) slo estn soportados por las versiones ms
recientes de la norma. Entre 100h y 107h se definen los modos ms comunes de 16 y 256 colores de todas
las SuperVGA, aunque el modo 6Ah tambin es VESA (800x600x16) al estar soportado por mltiples
tarjetas.
Una de las grandes ventajas del estndar VESA es la enorme informacin que pone a disposicin del
programador. Es posible conocer todos los modos y qu caractersticas de resolucin, colores y arquitectura
tienen. Adems, hay funciones adicionales muy tiles para guardar y recuperar el estado de la tarjeta, de
especial utilidad para programas residentes: as, estos pueden fcilmente conmutar a modo texto (con la
precaucin de preservar antes los 4 primeros Kbytes de la RAM de vdeo empleados para definir los
caracteres) y volver al modo grfico original dejando la pantalla en el estado inicial.
El programa de ejemplo.
En el apndice donde se resumen las funciones del DOS y la BIOS aparecen tambin las funciones
VESA de vdeo. Estas funciones se invocan va INT 10h, con AX tomando valores por lo general desde
4F00h hasta 4F08h. Para realizar programas que utilicen la norma, el lector deber consultar dicha
informacin. Sin embargo, se expone aqu un sencillo programa de demostracin que recoge prcticamente
todos los pasos necesarios para trabajar con un modo VESA.
El primer paso consiste en detectar la presencia de soporte VESA en el sistema, tarea que realiza la
funcin testvesa(). La funcin getbest256() se limita a buscar el modo de mayor resolucin de 256 colores
soportado por la tarjeta grfica de ese equipo, barriendo sistemticamente todos los modos de pantalla desde
el "mejor" hasta el "peor". Para comprobar la existencia de un determinado modo grfico, existe_modo()
invoca tambin a la BIOS VESA. La funcin setmode() establece un modo grfico VESA, devolviendo
adems dos informaciones interesantes: la direccin de memoria de la rutina de conmutacin de bancos (ya
veremos para qu sirve) y el segmento de memoria de vdeo, que ser normalmente 0A000h. Finalmente,
getinfo() devuelve informacin sobre cualquier modo grfico. En principio, los modos utilizados por este
programa de demostracin son conocidos. Sin embargo, la lista de modos de vdeo puede ser mayor en
algunas tarjetas, sobre todo en el futuro. Por tanto, un esquema alternativo podra consistir no en buscar
ciertos modos concretos sino en ir recorriendo todos y elegir el que cumpla ciertas caractersticas de
resolucin o colores, entre todos los disponibles.
De toda la informacin que devuelve getinfo() es particularmente interesante el nmero de bancos
que necesita ese modo de vdeo. Hay que tener en cuenta que todos los modos de 256 colores de ms de
320x200 ocupan ms de 64 Kb de memoria. De esta manera, por ejemplo, una imagen de 640x480 con 256
colores utiliza unos 256 Kb de RAM, dividida en 4 bancos. En un momento dado, slo uno de los 4 bancos
puede estar direccionado en el segmento de memoria de vdeo. Para elegir el banco activo (ms bien, el inicio
de la ventana lgica sobre el total de la memoria de vdeo, aunque nuestro ejemplo es una simplificacin)
existe una funcin de la BIOS VESA o, mejor an: podemos llamar directamente a una subrutina que realiza
118 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
rpidamente esa tarea (sin tener que utilizar interrupciones) cuya direccin nos devolvi setmode(). De esta
manera, 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, esta
modalidad de llamada no tiene por qu estar soportada por todas las BIOS VESA (en cuyo caso devuelven
una direccin 0000:0000 para el CALL) aunque la inmensa mayora, por fortuna, lo soportan.
El nico cometido de este programa de demostracin es buscar el mejor modo de 256 colores, entre
los normales de las SuperVGA, activarlo e ir recorriendo todos los bancos que componen la memoria de
vdeo (excepto el ltimo, que podra estar incompleto) para llenar la pantalla con bytes de valor 55h y 0AAh.
Finalmente, antes de terminar, se imprime la resolucin 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
unsigned
testvesa (void), /* Detectar soporte VESA */
existe_modo (unsigned), /* Comprobar si un modo es soportado */
getbest256 (void); /* Obtener mejor modo de 256c */
void
setbank (long, unsigned), /* Conmutar banco de memoria */
setmode (unsigned, long *, /* Establecer modo VESA */
unsigned *),
getinfo (unsigned, /* Obtener informacin del modo */
unsigned *,
unsigned *, unsigned *, unsigned *);
/* DEMOSTRACION */
void main()
{
struct REGPACK r;
long
ConmutaBanco; /* direccin FAR del conmutador de banco */
unsigned
video_seg, /* direccin del segmento de vdeo */
far *pantalla,
i, modo, max_x, max_y, vram, bancos, banco, limite;
if (!testvesa()) {
printf ("\nNecesario soporte VESA para este programa.\n");
exit (1);
}
modo = getbest256();
setmode (modo, &ConmutaBanco, &video_seg);
getinfo (modo, &max_x, &max_y, &vram, &bancos);
for (banco=0; banco<bancos; banco++) {
setbank (ConmutaBanco, banco); /* direccionar banco */
pantalla=MK_FP(video_seg, 0); /* normalmente 0xA000:0 */
if (banco!=bancos-1)
limite=32768; /* todo el segmento de 64 Kb */
else
limite=(vram-banco*64)*512; /* palabras ltimo banco */
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,4f02h
mov dx,banco
mov bx,0
call dword ptr direccion
}
}
119 ARQUITECTURA 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 captulo 12 se documenta el funcionamiento del hardware del
teclado, interesante para ciertas aplicaciones concretas, aunque para la mayor parte de las labores de
programacin no es necesario llegar a tanto.
7.5.1. - BAJO NIVEL.
Funcionamiento general del teclado.
Al pulsar una tecla se genera una interrupcin 9 (IRQ 1) y el cdigo de rastreo que identifica la tecla
pulsada puede leerse en el puerto de E/S 60h, tanto en XT como en AT (se corresponde en los AT con el
registro de salida del 8042); si se suelta la tecla se produce otra interrupcin y se genera el mismo cdigo
de rastreo+128 (bit 7 activo). Por ejemplo, si se pulsa la A se generar una INT 9 y aparecer en el puerto
del teclado (60h) el byte 1Eh, al soltar la A se generar otra INT 9 y se podr leer el byte 9Eh del puerto
del teclado (vase la tabla del apndice V, donde se listan los cdigos de rastreo del teclado).
Bajo el sistema DOS, el teclado del AT es idntico al del XT en los cdigos de rastreo y
comportamiento, debido a la traduccin que efecta el 8042 en el primero. No obstante, el teclado del AT
posee unos comandos adicionales para controlar los LEDs. En otros sistemas operativos (normalmente UNIX)
el teclado del AT es programado para trabajar en modo AT y pierde la compatibilidad con el del XT (los
cdigos de rastreo son distintos y al soltar una tecla se producen dos interrupciones) pero bajo DOS esto no
sucede en ningn caso y la compatibilidad es casi del 100%.
Las teclas expandidas -las que han sido aadidas al teclado estndar de 83/84 teclas- tienen un
comportamiento especial, ya que pueden generar hasta 4 interrupciones consecutivas (con un intervalo de unos
1,5 milisegundos, 3 ms en los cdigos dobles que convierte en uno el 8042) con objeto de emular, aunque
bastante mal, ciertas combinaciones de las teclas no expandidas; en general es bastante deficiente la
emulacin por hardware y el controlador del teclado (KEYB) tiene que tratarlas de manera especial en la
prctica. As, por ejemplo, cuando est inactivo NUM LOCK y se pulsa el cursor derecho expandido, se
generan dos interrupciones consecutivas: en la primera aparece un valor 0E0h en el puerto del teclado que
indica que es una tecla expandida; en la segunda interrupcin aparece el valor 4Dh: el mismo que hubiera
aparecido pulsando el 6 del teclado numrico. Sin embargo, si NUM LOCK est activo, en un teclado
normal de 83 teclas hay que pulsar el 6 del teclado numrico junto con shift para que el cursor avance. Esto
se simula en el teclado expandido por medio de 4 interrupciones: En las dos primeras puede aparecer la
secuencia 0E0h-2Ah bien 0E0h-36h (2Ah y 36h son los cdigos de las teclas shift normales): con esto se
simula que est pulsado shift aunque ello no sea realmente cierto (las BIOS ms antiguas ignoran la mayora
de los bytes mayores de 128, entre ellos el 0E0h); despus aparecen otras dos interrupciones con los valores
0E0h-4Dh (con objeto de simular que se pulsa el 6 del teclado numrico): como el estado NUM LOCK est
activo y en teora se ha pulsado shift y el 6 del teclado numrico, el cursor avanza a la derecha; al soltar la
tecla aparecer la secuencia de interrupciones 0E0h-CDh-0E0h-0AAh, o en su defecto la secuencia
equivalente 0E0h-CDh-0E0h-0B6h. En general, estos cdigos shift fantasma dan problemas cuando las teclas
de SHIFT adquieren otro significado diferente que el de conmutar el estado NUM LOCK, lo que sucede en
casi todos los editores de texto de los modernos compiladores. Por ello, la BIOS o el KEYB tratan de manera
especial las teclas expandidas; en los ordenadores ms antiguos (con BIOS -o al menos su tecnologa- anterior
a Noviembre de 1985), si no se carga el KEYB, el teclado expandido funcionar mal, incluso en Estados
Unidos -aunque las teclas estn bien colocadas-. Cuando se lee un valor 0E0h en una interrupcin de teclado,
el KEYB o la BIOS activan el bit 1 (el que vale 2) de la posicin de memoria 0040h:0096h; en la siguiente
interrupcin ese bit se borra y ya se sabe que el cdigo ledo es el de una tecla expandida. El bit 0 de esa
misma posicin de memoria indica si se ley un byte 0E1h en lugar de 0E0h (la tecla expandida pause o
pausa es un caso especial -por fortuna, el nico- y genera un prefijo 0E1h en vez del 0E0h habitual; de
hecho, esta tecla no genera cdigos al ser soltada, pero al pulsarla aparece la secuencia E1-1D-45-E1-9D-C5).
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 con
su cdigo ASCII y el cdigo de rastreo, para cuando el programa principal decida explorar el teclado -lo har
siempre consultando el buffer-. Si el cdigo ASCII depositado es cero 0E0h, se trata de una tecla especial
(ALT-x, cursor, etc.) y el segundo byte indica cul (son los denominados cdigos secundarios). El cdigo
ASCII 0E0h slo es generado en los teclados expandidos por las teclas expandidas (marcadas como Ex en
la tabla de cdigos de rastreo del apndice V), aunque las funciones estndar de la BIOS y del DOS que
informan del teclado lo convierten en cero para compatibilizar con teclados no expandidos. As mismo, el
cdigo ASCII 0F0h est reservado para indicar las combinaciones de ALT-tecla que no fueron consideradas
inicialmente en el software de soporte de los teclados no expandidos, pero s actualmente (de esta manera,
las rutinas de la BIOS saben si deben informar de estas teclas o no segn se est empleando una funcin
avanzada u obsoleta, para compatibilizar). En todo caso, las secuencias introducidas por medio de ALT-
teclado_numrico llevan asociado un cdigo de rastreo 0, por lo que el usuario puede generar los caracteres
ASCII 0E0h y 0F0h sin que se confundan con combinaciones especiales; adems, segn IBM, si el cdigo
ASCII 0 va acompaado de un cdigo de rastreo 3 los programas deberan interpretarlo como un autntico
cdigo ASCII 0 (esta secuencia se obtiene con Ctrl-2) lo que permite recuperar ese cdigo perdido en indicar
combinaciones especiales.
Es importante sealar que aunque el buffer (organizado como cola circular) normalmente est situado
entre 0040h:001Eh y 0040h:003Eh, ello no siempre es as; realmente el offset del inicio y el fin del buffer
respecto al segmento 0040h lo determinan las variables (tamao palabra) situadas en 0040h:0080h y
0040h:0082h en todos los ordenadores posteriores a 1981. Por ello, la inmensa mayora de las pequeas
utilidades de las revistas y los ejemplos de los libros son, por desgracia, incorrectos: la manera correcta de
colocar un valor en el buffer -para simular, por ejemplo, la pulsacin de una tecla- o extraerlo del mismo es
comprobando adecuadamente los desbordamientos de los punteros teniendo en cuenta las variables
mencionadas. El puntero al inicio del buffer es una variable tamao palabra almacenada en la posicin
0040h:001Ah y el fin otra ubicada en 0040h:001Ch. El siguiente ejemplo introduce un carcter de cdigo
ASCII AL y cdigo de rastreo AH (es cmodo y vlido hacer AH=0) en el buffer del teclado:
MOV BX,40h ; meter carcter AX en el buffer del teclado
MOV DS,BX
CLI ; evitar conflictos con interrupciones
MOV BX,DS:[1Ch] ; puntero a la cola del buffer
MOV CX,BX
ADD CX,2 ; apuntar CX al siguiente dato
CMP CX,DS:[82h] ; ms all del fin del buffer
JB no_desb
MOV CX,DS:[80h] ; inicio de la cola circular
no_desb: CMP CX,DS:[1Ah] ; puntero al inicio del buffer
JE fin_rutina ; ZF = 1 --> buffer lleno
MOV DS:[BX],AX ; introducir carcter ASCII (AL) en el buffer
MOV DS:[1Ch],CX ; actualizar puntero al final del buffer
CMP SP,0 ; ZF=0 (SP siempre <> 0) --> buffer no lleno
fin_rutina: STI
El valor 0 para el cdigo de rastreo es usado para introducir tambin algunos caracteres especiales,
como las vocales acentuadas, etc., aunque por lo general no es demasiado importante su valor (de hecho, los
programas suelen comprobar preferentemente el cdigo ASCII; de lo contrario, en un teclado espaol y otro
francs, la tecla Z tendra distinto cdigo!). No estara de ms en este ejemplo comprobar si las variables
40h:80h y 40h:82h son distintas de cero por si el ordenador es demasiado antiguo, medida de seguridad que
de hecho toma el KEYB del DR-DOS (en estas mquinas adems no es conveniente ampliar el tamao del
buffer cambindolo de sitio, por ejemplo; lo normal es que est entre 40h:1Eh y 40h:3Eh). En el apndice
V se listan los cdigos secundarios: son el segundo byte (el ms significativo) de la palabra depositada en
el buffer del teclado por la BIOS o el KEYB.
Gestin de la interrupcin del teclado.
He aqu un ejemplo de una subrutina que intercepta la interrupcin del teclado apoyndose en el
controlador habitual y limitndose a detectar las teclas pulsadas, espiando lo que sucede pero sin alterar la
121 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
operacin normal del teclado:
nueva_int9: STI ; permitir interrupcin peridica
PUSH AX ; preservar registros modificados
IN AL,60h ; cdigo de la tecla pulsada
PUSHF ; preparar la pila para IRET
CALL CS:anterior_int9 ; llamar a la INT 9 original
; hacer algo con esa tecla
POP AX ; restaurar registros modificados
IRET ; volver al programa principal
Evidentemente, es necesario preservar y restaurar todos los registros modificados, como en cualquier
otra interrupcin hardware, dado que puede producirse en el momento ms insospechado y no debe afectar
a la marcha del programa principal, anterior_int9 es una variable de 32 bits que contiene la direccin de la
interrupcin del teclado antes de instalar la nueva rutina. Es necesario hacer PUSHF antes de llamar porque
la subrutina invocada va a retornar con IRET y no con RETF. En general, el duo PUSHF/CALL es una
manera alternativa de simular una instruccin INT.
Si se implementa totalmente el control de una tecla en una rutina que gestione INT 9 -sin llamar al
principio o al final al anterior gestor-, en los XT hay que enviar una seal de reconocimiento al teclado
poniendo a 1 y despus a 0 el bit 7 del puerto de E/S 61h (en AT no es necesario, aunque tampoco resulta
perjudicial hurgar en ese bit en las mquinas fabricadas hasta ahora); es importante no enviar ms de una
seal de reconocimiento, algo innecesario por otra parte, de cara a evitar anomalas importantes en el teclado
de los XT. Adems, tanto en XT como AT hay que enviar en este caso una seal de fin de interrupcin
hardware (EOI) al 8259 (con un simple MOV AL,20h; OUT 20h,AL) al igual que cuando se gestiona
cualquier otra interrupcin hardware. El ejemplo anterior quedara como sigue:
nueva_int9: STI
PUSH AX
IN AL,60h ; cdigo de la tecla pulsada
CMP AL,tecla ; es nuestra tecla?
JNE fin ; no
PUSH AX ; vamos a manchar AX
IN AL,61h
OR AL,10000000b
OUT 61h,AL
AND AL,01111111b
OUT 61h,AL ; seal de reconocimiento enviada
POP AX ; AL = tecla pulsada
; gestionarla
MOV AL,20h
OUT 20h,AL ; EOI al 8259
POP AX ; AX del programa principal
IRET ; volver al programa principal
fin: POP AX ; AX del programa principal
JMP CS:anterior_int9 ; saltar al gestor previo de INT 9
Como se puede observar, esta rutina gestiona una tecla y las dems se las deja al KEYB o la BIOS.
Slo en el caso de que la gestione l es preciso enviar una seal de reconocimiento y un EOI al 8259. En
caso contrario, se salta al controlador previo a esta rutina con un JMP largo (segmento:offset); ahora no es
preciso el PUSHF, como en el caso del CALL, por razones obvias. La instruccin STI del principio habilita
las interrupciones, siempre inhibidas al principio de una interrupcin -valga la redundancia-, lo que es
conveniente para permitir que se produzcan ms interrupciones -por ejemplo, la del temporizador, que lleva
nada menos que la hora interna del ordenador-. En el ejemplo, el EOI es enviado justo antes de terminar de
gestionar esa tecla; ello significa que mientras se la procesa, las interrupciones hardware de menor prioridad -
todas, menos el temporizador- estn inhibidas por mucho que se haga STI; el programador ha de decidir pues
si es preciso enviar antes o no el EOI (vase la documentacin sobre el controlador de interrupciones 8259
de los captulos posteriores), aunque si la rutina es corta no habr demasiada prisa.
Es habitual en los controladores de teclado de AT (tanto la BIOS como el KEYB del MS-DOS)
deshabilitar el teclado mientras se procesa la tecla recin leda, habilitndolo de nuevo al final, por medio de
los comandos 0ADh y 0AEh enviados al 8042. Sin embargo, la mayora de las utilidades residentes no toman
122 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
estas precauciones tan sofisticadas (de hecho, el KEYB del DR-DOS tampoco). Lgicamente slo se pueden
enviar comandos al 8042 cuando el registro de entrada del mismo est vaco, lo que puede verificarse
chequeando el bit 1 del registro de estado: no es conveniente realizar un bucle infinito que dejara colgado
el ordenador de fallar el 8042, de ah que sea recomendable un bucle que repita slo durante un cierto tiempo;
en el ejemplo se utiliza la temporizacin del refresco de la memoria dinmica de los AT para no emplear ms
de 15 ms esperando al 8042. Adems las interrupciones han de estar inhibidas en el momento crtico en que
dura el envo del comando, aunque cuidando de que sea durante el menor tiempo posible:
nueva_int9: STI ; breve ventana para interrupciones
PUSH AX
CALL espera
MOV AL,0ADh
OUT 60h,AL ; inhibir teclado
CALL espera
IN AL,60h ; tecla?
STI ; permitir rpidamente interrupciones
... ; procesar tecla y enviar EOI al 8259
CALL espera
MOV AL,0AEh
OUT 60h,AL ; desinhibir teclado
POP AX
IRET ; no merece la pena hacer STI
espera: PUSH AX
PUSH CX
MOV CX,995 ; constante para 15 ms
CLI
testref: IN AL,61h
AND AL,10h ; mtodo vlido solo en AT
CMP AL,AH
JZ testref
MOV AH,AL
IN AL,64h ; registro de estado del 8042
TEST AL,2 ; buffer de entrada lleno?
LOOPNZ testref ; as es
POP CX
POP AX
RET
7.5.2. - NIVEL INTERMEDIO.
Consulta de SHIFT, CTRL, ALT, etc (marcas de teclado).
Estas teclas pueden ser pulsadas para modificar el resultado de la pulsacin de otras. IBM no ha
definido combinaciones con ellas (excepto CTRL-ALT, que sirve para reinicializar el sistema si se pulsa en
conjuncin con DEL) por lo que los programas residentes suelen precisamente emplear combinaciones de dos
o ms teclas de estas para activarse sin eliminar prestaciones al teclado; por defecto, si se pulsan dos o ms
teclas de estas la BIOS o el KEYB asignan prioridades y consideran slo una de ellas: ALT es la tecla de
mayor prioridad, seguida de CTRL y de SHIFT. Por otra parte, cabe destacar el hecho de que CTRL, ALT
y SHIFT (al igual que Num Lock, Caps Lock, Scroll Lock e Ins) no poseen la caracterstica de autorepeticin
de las dems teclas debido a la gestin que realiza la BIOS o el KEYB.
- Teclado no expandido.
Llamando con AH=2 a la INT 16h (funcin 2 de la BIOS para el teclado), se devuelve en AL un byte
con informacin sobre las teclas de control (SHIFT, CTRL, etc.) que es el mismo byte almacenado en
0040h:0017h (vase en el apndice III el rea de datos de la BIOS y las funciones de la BIOS para teclado).
En 0040h:0018h, existe otro byte de informacin adicional, aunque no hay funcin BIOS para consultarlo
en los teclados no expandidos, por lo que a menudo es necesario leerlo directamente. Por lo general es mejor
emplear las funciones BIOS, si existen, que consultar directamente un bit, por razones de compatibilidad.
Evidentemente, todas las funciones para teclados no expandidos pueden usarse tambin con los expandidos.
123 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
- Teclado expandido.
A partir de 0040h:0096h hay otros bytes con informacin adicional y especfica sobre el teclado del
AT y los teclados expandidos: parte de esta informacin, as como de la de 0040:0018h, puede ser consultada
en los teclados expandidos con la funcin 12h de la BIOS del teclado expandido, que devuelve en AX una
palabra: en AL de nuevo el byte de 0040h:0017h y en AH otro byte mezcla de diversas posiciones de
memoria con informacin til (consultar funciones de la BIOS para teclado).
Los bits de 40h:96h slo son fiables si est instalado el KEYB del MS-DOS o 99% compatible; por
ejemplo, el KEYB del DR-DOS 5.0/6.0 (excepto en modo KEYB US) no gestiona correctamente el bit de
AltGr, aunque s los dems bits. Antes de usar esta funcin conviene asegurarse de que est soportada por
la BIOS o el KEYB instalado.
Lectura de teclas ordinarias.
Con la funcin 0 de la INT 16h (AH=0 al llamar) se lee una tecla del buffer del teclado, esperando
su pulsacin si es preciso, y se devuelve en AX (AH cdigo de rastreo y AL cdigo ASCII); con la funcin
1 (AH=1 al llamar a INT 16h) se devuelve tambin en AX el carcter del buffer pero sin sacarlo (habr que
llamar de nuevo con AH=0), aunque en este caso no se espera a que se pulse una tecla (si el buffer estaba
vaco se retorna con ZF=1 en el registro de estado). En los equipos con soporte para teclado expandido
existen adems las funciones 10h y 11h (correspondientes a la 0 y 1) que permiten detectar alguna tecla ms
(como F11 y F12) y diferenciar entre las expandidas y las que no lo son al no convertir los cdigos 0E0h
en 0, as como la funcin 5 (introducir caracteres en el buffer).
Combinaciones especiales de teclas.
- BREAK: se obtiene pulsando CTRL-PAUSE en los teclados expandidos (CTRL-SCROLL LOCK en los
no expandidos). El controlador del teclado introduce una palabra a cero en el buffer e invoca la interrupcin
1Bh. Los programas pueden interceptar esta interrupcin para realizar ciertas tareas crticas antes de terminar
su ejecucin (ciertas rutinas del DOS, bsicamente las de impresin por pantalla, detectan BREAK y abortan
el programa en curso).
- PAUSE: se obtiene con dicha tecla o bien con CTRL-NUM LOCK (teclados no expandidos); provoca que
el ordenador se detenga hasta que se pulse una tecla no modificadora (ni SHIFT, ni ALT, etc.), tecla que ser
ignorada pero servir para abandonar la pausa. La pausa es interna a la rutina de control del teclado.
- PTR SCR (SHIFT con el (*) del teclado numrico en teclados no expandidos): vuelca la pantalla por
impresora al ejecutar una INT 5.
- SYS REQ: al pulsarla genera una INT 15h (AX=8500h) y al soltarla otra INT 15h (AX=8501h).
- CTRL-ALT-DEL: el controlador del teclado coloca la palabra 1234h en 0040h:0072h (para evitar el
chequeo de la memoria) y salta a la direccin 0FFFFh:0 reinicializando el ordenador.
- ALT-teclado_numrico: manteniendo pulsada ALT se puede teclear en el teclado numrico un valor
numrico en decimal; al soltar ALT el cdigo ASCII que representa se introducir en el buffer. El controlador
del teclado almacena en 40h:19h el nmero en proceso de formacin: cada vez que llega un nuevo dgito
multiplica el contenido anterior por 10 y se lo suma. Al soltar ALT, se hace 40h:19h=0.
Deteccin de soporte para teclado expandido.
Normalmente no ser necesario distinguir entre un teclado expandido o estndar, aunque en algunos
casos habr que tener en cuenta la posible pulsacin de una tecla expandida y su cdigo 0E0h asociado. En
todo caso, el bit 4 de 0040h:0096h indica si el teclado es expandido; sin embargo es suicida fiarse de esto
124 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
y es ms seguro chequear por otros medios la presencia de funciones de la BIOS para teclado expandido antes
de usarlas. En teora, las BIOS de AT del 15 de noviembre de 1985 en adelante soportan las funciones 5, 10h
y 11h; los de XT a partir del 10 de enero de 1986 soportan la 10h y la 11h. Sin embargo, en la prctica todas
ellas normalmente estn disponibles tambin en cualquier mquina ms antigua si tiene instalado un KEYB
eficiente, venga equipada o no con teclado expandido. Por ello, lo ideal es chequear la presencia de estas
funciones por otros procedimientos. Por ejemplo: llamar a la funcin 12h con AL=0. Por desgracia, si la
funcin no est implementada no devuelve el acarreo activo para indicar el error. Pero hay un truco: si el
resultado sigue siendo AX=1200h, las funciones de teclado expandido no estn soportadas. Esto se debe a
que al no estar implementada la funcin, nadie ha cambiado el valor de AX: adems, en caso de estar
implementada no podra devolver 1200h porque ello significara una contradiccin entre AH y AL.
MOV AX,1200h
INT 16h ; invocar funcin teclado expandido
CMP AX,1200h
JE no_expandido ; funcin no soportada
JMP si_expandido ; funcin soportada
Posibilidades avanzadas.
La rutina de la BIOS del AT (y de los KEYB) que lee el buffer del teclado, cuando no hay teclas
y tiene que esperar por las mismas ejecuta de manera regular la funcin 90h (AH=90h) de la interrupcin 15h
indicando una espera de teclado al llamar (AL=2). De esta manera, un hipottico avanzado sistema operativo
podra aprovechar ese tiempo muerto para algo ms til. As mismo, cuando un carcter acaba de ser
introducido en el buffer del teclado, se ejecuta la funcin 91h para indicar que ya ha finalizado la entrada
y hay caracteres disponibles. En general, estas caractersticas no son tiles en el entorno DOS y, por otra
parte, han sido deficientemente normalizadas. Por ejemplo, al acentuar incorrectamente se generan dos
caracteres (adems del familiar pitido): el KEYB del MS-DOS slo ejecuta una llamada a la INT 15h con
la funcin 91h (pese a haber introducido dos caracteres en el buffer) y el de DR-DOS hace las dos llamadas...
Lo que s puede resultar ms interesante es la funcin de intercepcin de cdigo del teclado: las BIOS
de AT no demasiado antiguas y el programa KEYB, tras leer el cdigo de rastreo en AL, activan el acarreo
y ejecutan inmediatamente la funcin 4Fh de la INT 15h para permitir que alguien se de por enterado de la
tecla y opcionalmente aproveche para manipular AL y simular que se ha pulsado otra tecla: ese alguien puede
devolver adems el acarreo borrado para indicar al KEYB que no contine procesando esa tecla y que la
ignore (en caso contrario se procedera a interpretarla normalmente). Para verificar si esta funcin est
disponible en la BIOS basta con ejecutar la funcin 0C0h de la INT 15h que devuelve un puntero en ES:BX
y comprobar que el bit 4 de la posicin direccionada por ES:[BX+5] est activo. Alternativamente, puede
verificarse la presencia del programa KEYB, lo que tambin permite emplear esta funcin en los PC/XT,
aunque es ms arriesgado. Para detectar la presencia del KEYB del MS-DOS en memoria basta con llamar
a la interrupcin 2Fh con AX=0AD80h y comprobar que devuelve AL=0FFh (esta funcin devuelve la
versin del KEYB en BX y un puntero a un rea de datos en ES:DI). [DR-DOS usa AX=0AD00h].
Consideraciones finales.
Conviene sealar que los teclados de AT pueden generar interrupciones aunque no se pulsen teclas,
normalmente para devolver una seal de reconocimiento cuando alguien les ha enviado algo -por ejemplo,
la BIOS puede enviar un comando para cambiar los leds-; por ello, en el momento ms insospechado puede
producirse una INT 9 con el cdigo de rastreo 0FAh, y la secuencia de interrupciones generada por las teclas
que tienen asociado un led en los AT, debido a los cdigos 0FAh, no es exactamente idntica a la de los XT,
aunque se trata de un detalle poco relevante -incluso para quienes pretendan hacer algo especial con estas
teclas-. Tambin es conveniente indicar que en los AT se puede leer puerto del teclado, para averiguar la
ltima tecla pulsada o soltada, en casi cualquier momento -por ejemplo, peridicamente desde la interrupcin
del temporizador-. De todas formas, esta prctica tiene efectos secundarios debidos al mal diseo del software
del sistema de los AT (tales como teclas shift que se enganchan, como si se quedaran pulsadas, numeritos
que aparecen al pulsar los cursores expandidos, etc.). Adems, en los XT slo se obtendr una lectura correcta
inmediatamente despus de producirse la interrupcin del teclado y antes de enviar la correspondiente seal
125 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
de reconocimiento al mismo -por tanto, no desde una interrupcin peridica-. Todo esto desaconseja la lectura
del puerto del teclado desde cualquier otro sitio que no sea INT 9, salvo contadas excepciones.
Por ltimo indicar que en los AT se puede modificar el estado de CAPS LOCK, NUM LOCK o
SCROLL LOCK por el simple procedimiento de alterar el bit correspondiente en 40h:17h; dicho cambio se
ver reflejado en los leds cuando el usuario pulse una tecla o el programa lea el teclado con cualquier
funcin -en la prctica, de manera casi instantnea-. Sin embargo, para aplicar esta tcnica es aconsejable
verificar que se trata de un AT porque en los PC/XT el led -si existe- no se actualiza y pasa a indicar una
informacin incorrecta. Realmente, en los XT, el control de los led lo lleva la propia circuitera del teclado
de manera independiente al ordenador.
7.5.3. - ALTO NIVEL.
El acceso al teclado a alto nivel puede realizarse a travs de las funciones 1, 6, 7, 8 y 0Ah del DOS,
considerndolo como dispositivo de entrada estndar. Algunas de estas funciones, si devuelven un 0, se trata
de una tecla especial y la siguiente lectura devuelve el cdigo 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. Pueden
ser unidades de disco flexible, removibles, o discos duros -fijos-. Constan bsicamente de una superficie
magntica circular dividida en pistas concntricas, cada una de las cuales se subdivide a su vez en cierto
nmero de sectores de tamao fijo. Como normalmente se emplean ambas caras de la superficie, la unidad
ms elemental posee en la actualidad dos cabezas de lectura/escritura, una para cada lado del disco. Los tres
parmetros comunes a todos los discos son, por tanto: el nmero de cabezas, el de pistas y el de sectores.
El trmino cilindro i hace referencia a la totalidad de las pistas i de todas las caras. Bajo DOS, los sectores
tienen un tamao de 512 bytes (tanto en discos duros como en disquetes) que es difcil cambiar (aunque no
imposible). Los sectores se numeran a partir de 1, mientras que las pistas y las caras lo hacen desde 0. El
DOS convierte esta estructura fsica de tres parmetros a otra: el nmero de sector lgico, que se numera a
partir de 0 (los sectores fsicos les denominaremos a partir de ahora sectores BIOS para distinguirlos de los
sectores lgicos del DOS). Para un disco de SECTPISTA sectores BIOS por pista y NUMCAB cabezas, los
sectores lgicos se relacionan con la estructura fsica por la siguiente frmula:
Sector lgico = (sector_BIOS - 1) + cara * SECTPISTA + cilindro * SECTPISTA * NUMCAB - X1
Es decir, el DOS recorre el disco empezando la pista 0 (la exterior, la ms alejada del centro) y por
la cara o cabezal 0, recorriendo todos los sectores; luego avanza una cara y recorre de nuevo todos los
sectores; despus pasa al siguiente cilindro... y repite de nuevo el proceso. De esta manera, varios cabezales
podran -hipotticamente- leer bloques de informacin consecutivos simultneamente. En los disquetes, X1=0,
pero en los discos duros se resta un cierto factor de compensacin X1, ya que stos pueden estar divididos
en varias particiones y la que usa el DOS puede no estar al principio del mismo. En general, un disco duro
dividido en varias particiones de tipo DOS determina varias unidades lgicas de disco, cada una de las cuales
dispone de un conjunto de sectores lgicos numerados a partir de 0 y un factor de compensacin propio para
la frmula. Las siguientes frmulas transforman sectores DOS en sus correspondientes BIOS:
Sector_BIOS = (sector MOD SECTPISTA) + 1
Cara = (sector / SECTPISTA) MOD NUMCAB
Cilindro = sector / (SECTPISTA * NUMCAB) + X2
Como la particin del DOS no suele empezar en el cilindro 0 (reservado en gran parte para la tabla
de particiones) sino ms bien en el 1 en otro posterior (cuando hay ms particiones antes que la del DOS)
ser necesario aadir un cierto valor adicional de compensacin X2 a la ltima frmula para calcular el
cilindro efectivo; esto es as porque en la prctica las particiones suelen empezar y acabar ocupando cilindros
enteros y exactos (aunque en realidad, y dada la arquitectura de la tabla de particin, podran empezar y
126 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
acabar no slo en un determinado cilindro sino tambin en cierto sector y cara del disco, pero no es
frecuente). X1 y X2 se obtienen consultando e interpretando la tabla de particiones o el sector de arranque.
7.6.2. - CABEZA 0. PISTA 0. SECTOR 1.
El primer sector fsico de todos los discos contiene informacin especial (el sector_BIOS 1 del
cilindro 0 y cabezal 0). Tanto en disquetes como en discos duros, contiene un pequeo programa que se
encarga de poner en marcha el ordenador: es el sector de arranque de los disquetes, o bien el cdigo de la
tabla 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 cul es la particin activa y dnde
empieza y acaba; a continuacin carga el sector lgico 0 de esa particin (sector de arranque) y lo ejecuta.
En los disquetes no existe este paso intermedio: el sector fsico 0 del disquete, en terminos absolutos, es ya
el sector de arranque y no el de particin. Esto es as porque los disquetes contienen poca informacin y son
baratos, no siendo preciso particionarlos para compartirlos con varios sistemas operativos. El programa
ubicado en el sector de arranque busca el fichero oculto del sistema IBMBIO.COM o IO.SYS, lo carga y le
entrega 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 intrprete de comandos (normalmente, COMMAND.COM).
Formato de la tabla de particin de los discos duros:
Esta tabla comienza en un
offset 1BEh del sector (al principio
est el cdigo ejecutable); cada
particin de las 4 posibles ocupa
16 bytes; al final de las cuatro est
la marca 0AA55h, ubicada en el
offset 1FEh, que indica que la tabla
es vlida. Los 16 bytes que la
forman se interpretan como indica
el cuadro de la derecha:
byte 0: 0 para particin inactiva, 80h en la de arranque.
byte 1: cabeza donde comienza la particin.
byte 2: bits 0 al 5: sector de inicio de la particin; 6, 7: parte alta del
nmero de cilindro.
byte 3: parte baja del nmero de cilindro de inicio de la particin.
byte 4: tipo de particin, las ms comunes son 0: No usada; 1: DOS-12 (FAT
12 bits); 4: DOS-16 (FAT 16 bits); 5: DOS Extendida; 6:BIGDOS (ms
de 32Mb); 7: OS/2 HPFS WinNT NTFS; 0Ah: OS/2 Boot Manager; 0Bh:
32-bit FAT Win95 (0Ch con LBA); 0Eh y 0Fh (como 06 y 05 pero con
LBA); 81h Linux; 82h Linux swap; 83h: Linux native; 0A5h: FreeBSD
o BSD/386; 0F2h: particin secundaria (no estudiada en este libro).
byte 5: cabeza donde termina la particin.
byte 6: bits 0 al 5: sector de fin de la particin; 6, 7: parte alta del
nmero de cilindro.
byte 7: parte baja del nmero de cilindro de fin de la particin.
bytes 8 al 11: Doble palabra que indica el sector relativo (en todo el
disco) en que comienza la particin, expresado en sectores.
bytes 12 al 15: Doble palabra con el tamao de esa particin en sectores.
Formato de la TABLA DE PARTICIN
Habitualmente, las particiones suelen empezar en el segundo cabezal del cilindro 0, con lo que toda
la primera pista fsica del disco duro est vaca. Lugar ideal para virus, algunos fabricantes han utilizado esta
interesante caracterstica para mejorar el arranque, colocando una falsa tabla de particin que muestre un
men en pantalla y cargue despus la particin de verdad, permitiendo tambin ms de 4 particiones. Sin
embargo, estas maniobras suelen reducir la compatibilidad. Existen tambin cdigo de particiones sofisticado
que permite seleccionar una de las 4 particiones manteniendo pulsada una tecla en el arranque, sin tener que
andar ejecutando FDISK para seleccionar la particin activa... lo que se puede hacer con 400 bytes de
cdigo!. Realmente, la arquitectura global de las particiones de un equipo (en particular si tiene ms de 4,
una mezcla de sistemas operativos y/o varios discos duros), puede llegar a ser compleja: practquese con un
buen editor de disco para aprender ms (ej. el DISKEDIT de las Norton Utilities o las PC-Tools).
Las particiones extendidas llevan su propio sector de particin adicional, en el que no hay cdigo de
programa sino, en su lugar, una lista de dispositivos. Hay dos entradas por cada dispositivo: la primera indica
el tipo (1-FAT12, 4-FAT16); la segunda entrada apunta al siguiente dispositivo (caso de existir) o es 0 (no
hay ms dispositivos). El DOS 4.0 y posteriores eliminaron la limitacin de los 32 Mb en las particiones y
el software actual, ya actualizado, no da problemas con los discos de ms de 32 Mb. Por ello, en discos de
ms de 32 40 Mb lo normal es instalar DOS 4.0 superior.
Formato del sector de arranque:
En el sector de arranque, adems del sencillo programa de puesta en marcha del sistema, hay cierta
informacin til acerca de las caractersticas del disco o particin. Los primeros 3 bytes no son significativos:
contienen el cdigo de operacin de una instruccin JMP que salta a donde realmente comienza el cdigo,
127 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
aunque conviene que dicha instruccin de salto est al principio del sector de arranque para que algunos
sistemas validen dicho sector (es vlido un salto corto seguido de NOP o un salto completo de 3 bytes). A
partir del cuarto (offset 3) se puede encontrar la informacin vlida. En el sector de arranque del disquete
est contenido el BPB (Bios Parameter Block) que analizaremos ms tarde.
offset 3 (8 bytes): Identificacin del sistema (ej., "IBM 3.3")
offset 11 (1 palabra): Bytes por sector, ej. 512.
offset 13 (1 byte): Sectores por cluster (ej. 2)
offset 14 (1 palabra): Sectores reservados al principio (1 en diquettes)
offset 16 (1 byte): Nmero de copias de la FAT (2 normalmente)
offset 17 (1 palabra): Nmero de entradas al directorio raz (112 en discos de 360 Kb)
offset 19 (1 palabra): Nmero total de sectores del disco (0 en discos de ms de 32 Mb)
offset 21 (1 byte): Byte de tipo de disco (vase tabla ms adelante)
offset 22 (1 palabra): Nmero de sectores ocupados por cada FAT
offset 24 (1 palabra): Nmero de sectores por pista
offset 26 (1 palabra): Nmero de cabezas (2 en disquetes de doble cara)
offset 28 (2 palabras): Nmero de sectores especiales reservados. Nota: slo se debe considerar la primera mitad de
esta doble palabra en versiones del sistema 3.30 o anteriores (no hay problemas con DR-DOS,
que en todas sus versiones, hasta la 6.0 incluida, es un DOS 3.31). El valor de este campo
depende de la posicin relativa que ocupe la particin dentro del disco duro (ser 0 en los
disquetes), este valor ha de sumarse al del nmero de sector del DOS antes de traducirlo a
un nmero de sector de la BIOS.
offset 32 (2 palabras): Nmero total de sectores del disco en discos de ms de 32 Mb (esta informacin slo debe
obtenerse de aqu si la palabra ubicada en el offset 19 es cero).
offset 36 (1 byte): Nmero de unidad fsica (a partir del DOS 4.0).
offset 37 (1 byte): Reservado.
offset 38 (1 byte): valor 29h desde DOS 4.0 (marca de validacin que indica que los bytes ubicados desde el
offset 36 al offset 61 estn definidos).
offset 39 (2 palabras): Nmero de serie del disco (a partir de DOS 4.0).
offset 43 (11 bytes): Ttulo del disco (desde DOS 4.0); por defecto se inicializa con "NO NAME ", aunque tanto
el DOS 4.0 como el 5.0 y 6.X siguen empleando adems las tradicionales etiquetas de volumen.
offset 54 (8 bytes): Sistema de ficheros (a partir de DOS 4.0): puede ser "FAT12 " o "FAT16 ".
Formato del SECTOR DE ARRANQUE
El byte del tipo de disco (offset 21) intenta identificar el tipo de disco, aunque no lo consigue en
muchos casos dada la ilgica utilizacin que se ha hecho de l. La recomendacin es hacer lo que viene
haciendo el DOS desde la 3.30: no hacer caso de lo que dice este byte para identificar los discos. La nica
excepcin tal vez sea el valor 0F8h que identifica a los dispositivos no removibles:
0FEh - discos de 5-160 Kb (1 cara, 8 sectores/pista, 40 pistas)
0FFh - discos de 5-320 Kb (2 caras, 8 sectores/pista, 40 pistas)
0FCh - discos de 5-180 Kb (1 cara, 9 sectores/pista, 40 pistas)
0FDh - discos de 5-360 Kb (2 caras, 9 sectores/pista, 40 pistas)
0F9h - discos de 5-1,2 Mb (2 caras, 15 sectores/pista, 80 pistas)
0F9h - discos de 3-720 Kb (2 caras, 9 sectores/pista, 80 pistas)
0F8h - discos duros y algunos virtuales
0F0h - discos de 3-1,44 Mb (2 caras, 18 sectores/pista, 80 pistas)
0F0h - discos de 3-2,88 Mb (2 caras, 36 sectores/pista, 80 pistas)
0F0h - restantes formatos de disco
Tipos de Discos
7.6.3. - LA FAT.
Despus del sector de arranque, aparecen en el disco una serie de sectores que constituyen la Tabla
de Localizacin de Ficheros (File Alocation Table o FAT). Consiste en una especie de mapa que indica qu
zonas del disco estn libres, cules ocupadas, dnde estn los sectores defectuosos, etc. Normalmente hay dos
copias consecutivas de la FAT (vase el offset 16 del sector de arranque), ya que es el rea ms importante
del disco de la que dependen todos los dems datos almacenados en l. No deja de resultar extrao que ambas
copias de la FAT estn fsicamente consecutivas en el disco: si accidentalmente se estropeara una de ellas
(por ejemplo, rayando con un bolgrafo el disco) lo ms normal es que la otra tambin resultara daada. En
general, muchos programas de chequeo de disco no se molestan en verificar si ambas FAT son idnticas
(empezando por algunas versiones de CHKDSK). Por otra parte, hubiera sido mejor eleccin haberla colocado
en el centro del disco: dada la frecuencia de los accesos a la misma, de cara a localizar los diferentes
fragmentos de los ficheros, ello mejorara notablemente el tiempo de acceso medio. Aunque cierto es que los
cachs de disco y los buffers del config.sys pueden hacer casi milagros... a costa de memoria.
Antes de seguir adelante, conviene hacer un pequeo parntesis y explicar el concepto de cluster: un
cluster es la unidad mnima de informacin a la que accede el DOS, desde el punto de vista lgico.
Normalmente consta de varios sectores (ver offset 13 del sector de arranque): dos en un disquete de 360 Kb,
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 nmero de clusters. La FAT es realmente un mapa que contiene 12 16 bits -como
veremos- por cada cluster, indicando su estado:
cluster libre: valor 0
cluster 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 estn ms o menos
fragmentados debido a que se aprovechan los huecos dejados por otros ficheros borrados, de ah el auge de
los programas que compactan los discos con objeto de acelerar el acceso a los datos. Por tanto, cada fichero
consta de un cluster inicial indicado en la entrada del directorio -como se ver- que inicia una cadena tan
larga como la longitud del mismo (expresada en clusters), existiendo normalmente un valor 0FFFh 0FFFFh
en el ltimo cluster para sealar el final (del 0FF8h al 0FFEh y del 0FFF8h al 0FFFEh no se emplean).
Consultando la FAT se puede determinar la ubicacin de los fragmentos en que estn fsicamente divididos
los ficheros en los discos, as como qu zonas estn an disponibles y cules son defectuosas en el mismo.
Los cluster se numeran a partir de 2, ya que las dos primeras entradas en la FAT estn reservadas para el
sistema. Los clusters hacen referencia exclusiva a la zona de datos: el rea que va detrs del sector de
arranque, la FAT y el directorio. Por ello, en un disquete de 360 Kb, con clusters de 1 Kb y 354 Kb libres
para datos, hay 354 clusters (numerados de 2 a 355) y los 6 Kb misteriosos que faltan son el sector de
arranque, las dos FAT y -como veremos despus- el directorio raz. Puede ser vlida, por ejemplo, la
siguiente FAT de 12 bits habiendo un fichero A que ocupe los clusters 2, 3, 5 y 6:
Elemento de la FAT Valor Interpretacin
0 FFD El disco es de tipo 0FDh (despreciar restantes bits)
1 FFF Entrada no utilizada
2 003 El siguiente cluster del fichero A es el 3
3 005 El siguiente cluster del fichero A es el 5
4 FF7 Cluster defectuoso
5 006 El siguiente cluster del fichero A es el 6
6 FFF Este es el ltimo cluster del fichero A
7 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 que
el byte de tipo de disco del sector de arranque. Los restantes bits de las dos primeras entradas suelen estar
todos a 1. Para determinar el nmero de clusters del disco, ha de restarse del nmero total de sectores la cifra
correspondiente al nmero de sectores reservados (normalmente 1 en los disquetes, correspondiente al sector
de arranque), los que ocupa la FAT y los empleados por el directorio raz (que se ver ms adelante); a
continuacin se divide ese nmero de sectores de datos resultante por el nmero de sectores por cluster.
El hecho de emplear FATs de 12 bits es debido a que con menos bits (ej., un byte) slo podra haber
unos 250 clusters en el disco. En un disco de 1,2 Mb ello significara que la unidad mnima de informacin
sera 1200/250 = 5 Kb: el fichero ms pequeo (de 1 byte) ocupara 5 Kb!. Empleando FATs de 16 bits
se podran hacer clusters incluso de tamao menor que el sector (menos de 512 bytes), aprovechando ms
el espacio del disco. Sin embargo, ello hara que la propia FAT ocupase demasiado espacio en el disco. Por
ello, en los disquetes se emplean FATs de 12 bits (1 byte y medio): para un programa en cdigo mquina
ello no ralentiza los clculos (aunque al ser humano no se le de muy bien trabajar con medios bytes). En la
prctica, se toman palabras de 16 bits y se desprecian los 4 bits ms significativos en los clusters pares y los
4 menos significativos en los impares.
A continuacin se listan dos rutinas que permiten acceder a una FAT de 12 bits previamente cargada
en memoria, con objeto de consultar o modificar alguna entrada. Evidentemente, despus habr que volver
a grabar la FAT en disco, tantas veces como copias de la misma existan en ste. Las rutinas necesitan que
la FAT est completamente cargada en memoria, lo cual no es un requerimiento demasiado costoso, habida
cuenta de que no puede ocupar ms de 4085 * 1,5 = 6128 bytes.
129 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
; ************ Escribir un elemento en una FAT de 12 bits
; Entrada: AX = posicin de dicho elemento
; DS:BX = FAT completamente cargada en memoria
; DX = nuevo valor de dicho elemento
poke_fat PROC
PUSH AX ; preservar registros
PUSH BX
PUSH DX
ADD BX,AX ; BX = BX + cluster
SHR AX,1 ; AX = cluster / 2
PUSHF ; CF = 1 si impar
ADD BX,AX ; BX = BX + cluster * 1,5
MOV AX,[BX] ; AX = palabra con dato 12 bits
POPF
JC poke_fat_imp
AND AX,1111000000000000b ; preservar la otra entrada
JMP poke_fat_ok
poke_fat_imp: AND AX,0000000000001111b ; preservar la otra entrada
PUSH CX
MOV CL,4
SHL DX,CL ; colocarlo: 4 bits a la izda
POP CX
poke_fat_ok: OR AX,DX ; mezclar
MOV [BX],AX ; nuevo valor en la FAT
POP DX
POP BX
POP AX
RET ; retorno sin alterar registros
poke_fat ENDP
; ************ Leer un elemento de una FAT de 12 bits
; Entrada: AX = posicin de dicho elemento
; DS:BX = FAT completamente cargada en memoria
; Salida: DX = valor de dicho elemento
peek_fat PROC
PUSH AX ; preservar registros
PUSH BX
ADD BX,AX ; BX = BX + cluster
SHR AX,1 ; AX = cluster / 2
PUSHF ; CF = 0 si par
ADD BX,AX ; BX = BX + cluster * 1,5
MOV DX,[BX]
POPF
JNC peek_fat_par
PUSH CX
MOV CL,4
SHR DX,CL ; DX=DX/16: si DX=xyz0, DX=0xyz
POP CX
peek_fat_par: AND DH,00001111b ; borrar posible dgito izdo
POP BX
POP AX
RET ; retornar slo DX modificado
peek_fat ENDP
Tal vez, en futuros disquetes de elevada capacidad sea necesario pasar a una FAT de 16 bits,
aparecida con el DOS 3.0, que es la usada por todos los discos duros excepto el de 10 Mb del XT original
de IBM. Con una FAT de 12 bits el n de cluster ms alto posible es 4085, que se corresponde con un disco
de 4084 clusters (numerados de 2 a 4085). En principio, no existe ninguna manera sencilla de averiguar el
tipo de FAT de un disco, ya que el fabricante olvid incluir un byte de identificacin al efecto. La
documentacin publicada es contradictoria en las diversas fuentes que he consultado, y en todas es por
desgracia incorrecta (unos dicen que la FAT 16 comienza a partir de 4078 clusters, otros que a partir de 4086,
otros confunden el nmero de clusters con el nmero ms alto de cluster...). Sin embargo, todas las versiones
del DOS comprobadas (MS-DOS 3.1, 3.3, 4.0, 5.0 y DR-DOS 5.0 y 6.0) operan con una FAT de 16 bits en
discos de 4085 clusters (inclusive) en adelante; esto es, a partir de 4086 como nmero de cluster ms alto.
Esto puede verificarse fcilmente creando discos virtuales con 4084/4085 clusters, copiando algunos ficheros
y mirando la FAT con algn programa de utilidad (a simple vista se distingue si las entradas son de 12 16
bits). Por desgracia, salvo en MS-DOS 3.3 y en DR-DOS 6.0, los comandos CHKDSK del sistema consideran
errneamente que los discos de 4085, 4086 y 4087 clusters poseen una FAT de 12 bits!, lo cual resulta
adems completamente absurdo, dado que 4087 (0FF7h) es la marca de cluster defectuoso en una FAT de
12 bits y en ningn caso podra ser un nmero de cluster cualquiera!. Sin embargo, pese a este problema
de CHKDSK, los discos con ms de 4084 clusters han de ser diseados con una FAT de 16 bit, ya que es
mucho ms grave tener problemas con el DOS que con CHKDSK. Otra solucin es procurar no crear discos
de ese nmero crtico de clusters, o confiar que el usuario no ejecute el casi olvidado CHKDSK sobre ellos.
Por fortuna, los discos normales no estn por ahora en la frontera crtica entre la FAT de 12 y la de 16 bits,
aunque con los discos virtuales s se pueden crear unidades con esos tamaos crticos: la casi totalidad de los
discos virtuales del mercado tienen problemas en estos casos. En algunos discos duros se puede determinar
tambin el tipo de FAT consultando la tabla de particiones, aunque no es el mtodo ms conveniente. Debe
tener en cuenta el lector que manipular una FAT sin conocer su tipo supone destrozar la informacin
almacenada en el disco. Sin embargo, tampoco hay que tener tanto miedo: lo que s puede resultar peligroso
es llegar al extremo de preguntar al usuario el tipo de FAT...
Ahora puede surgir la pregunta: si la FAT mantiene una cadena que indica cmo est distribuido un
fichero en el disco, dnde se almacena el inicio de esa cadena, esto es, la primera entrada en la FAT del
fichero?.
7.6.4.- EL DIRECTORIO RAZ.
Inmediatamente despus de la FAT y su(s) rplica(s) de seguridad viene el directorio raz. Detrs de
ste ya vienen los clusters conteniendo la informacin del disco propiamente dicha. El directorio consta de
32 bytes por cada fichero/subdirectorio (los subdirectorios no son ms que un tipo especial de fichero). En
los discos de 360 Kb, por ejemplo, el directorio se extiende a lo largo de 7 sectores (3584 bytes = 112
entradas como mximo). El tamao y ubicacin del directorio pueden obtenerse del sector de arranque, como
se vio al principio. La informacin almacenada en los 32 bytes es la siguiente:
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 slo lectura
offset 8 (3 bytes): Extensin del nombre del fichero bit 1: activo si el fichero es oculto
offset 11 (1 byte): Byte de atributos bit 2: activo si el fichero es de sistema
offset 12 (10 bytes): Reservado (PASSWORD cifrada DR-DOS) bit 3: activo si esa entrada de directorio es
offset 22 (2 bytes): Hora*2048 + minutos*32 + segundos/2 la etiqueta de volumen
offset 24 (2 bytes): (ao-1980)*512 + mes*32 + da bit 4: activo si es un subdirectorio
offset 26 (2 bytes): Primera entrada en la FAT bit 5: bit de archivo usado por BACKUP y RESTORE
offset 28 (4 bytes): Tamao 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 tiene
un significado en particular, es una reliquia heredada del CP/M (los ficheros ocultos del sistema lo tienen
activo). En un mismo disco slo puede haber una entrada con el bit 3 activo; adems, en este caso se
interpretan el nombre y la extensin como un nico conjunto de 11 caracteres. Las entradas de tipo
subdirectorio (bit 4 del byte de atributos activo) tienen un valor cero en el campo de tamao (offset 28): el
tamao de un fichero subdirectorio est determinado por el nmero de entradas que ocupa en la FAT (en la
prctica, esto sucede con cualquier otro fichero, aunque si no es de directorio en el offset 28 esta informacin
se indica con precisin de bytes).
El nombre del fichero puede comenzar por 0E5h, lo que indica que el fichero que estuvo ah ha sido
borrado. Si empieza por 2Eh (cdigo ASCII del punto (.)) por 2Eh, 2Eh (dos puntos consecutivos) se trata
de una entrada que referencia a un fichero subdirectorio.
7.6.5. - LOS SUBDIRECTORIOS.
Como hemos visto, un subdirectorio en principio puede ser una simple entrada del directorio raz.
El subdirectorio, fsicamente, es a su vez un fichero un tanto especial: contiene datos binarios ... que son nada
ms y nada menos que otras entradas de directorio para otros ficheros, de 32 bytes como siempre. Dentro de
cada subdirectorio hay al menos dos entradas especiales: un fichero con un nombre punto (.) que referencia
al propio subdirectorio -que as puede autolocalizarse- y otro con doble punto (..) que referencia al directorio
padre -del que cuelga- siendo posible, gracias a ello, retroceder cuanto se desee por el rbol de directorios
sin necesidad de que todos los caminos partan del raz. Si la primera entrada en la FAT del fichero (..) es
un 0, quiere decir que ese subdirectorio cuelga del raz, de lo contrario apuntar al primer cluster del fichero
subdirectorio padre.
El tamao de un fichero subdirectorio es ilimitado -sin exceder, evidentemente, la capacidad del
disco-. Por ello, en un subdirectorio puede haber una gran cantidad de ficheros (muchos ms de 112 500)
sin problemas. Cada fichero que se crea en un subdirectorio aumenta el tamao del fichero subdirectorio en
32 bytes. Por ello, en un disco de 360 Kb (354 Kb libres) se puede crear un subdirectorio y en l se pueden
introducir, en caso extremo, 11326 ficheros (ms el (.) y el (..)) de tamao cero que paradjicamente llenaran
el disco (recordar que cada entrada al directorio ocupa 32 bytes). Normalmente nadie suele cometer esos
excesos. Si en un subdirectorio haba demasiados ficheros y se borra una buena parte de los mismos, el
tamao del fichero subdirectorio debera reducirse, pero en la prctica el DOS no se ocupa de estas
pequeeces, habida cuenta de que los ficheros subdirectorio son unos pequeos islotes en el gran ocano disco
(los usuarios ms tacaos siempre pueden optar por crear un nuevo subdirectorio y mover todos los ficheros
a l, borrando el anterior para recuperar el espacio libre).
Considerando el nombre completo de un fichero, con toda la trayectoria de directorios, el proceso
a seguir para localizarlo en el disco es ir recorriendo los ficheros subdirectorio de uno en uno, hasta llegar
al fichero subdirectorio donde est registrado el fichero y, en la posicin correspondiente, obtener su punto
de entrada en la FAT.
Dicho sea de paso, tal vez sea una pena que el disco no conste de un nico fichero raz privilegiado
de directorio, que podramos denominar subdirectorio raz. Ello permitira tambin un nmero ilimitado
de entradas (en vez de 112, 224, etc.) y sera ms lgico que una ristra de sectores. Sin embargo, esta peculiar
circunstancia tambin aparece en otros sistemas operativos, como el UNIX. Sus motivos tendr.
131 ARQUITECTURA 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 informacin relativa a la
unidad de disco. El BPB es una pieza vital en los controladores de dispositivo de bloques, como veremos en
un futuro captulo, por lo que a continuacin se expone su contenido (idntico a una parte del sector 0):
offset 0 DW bytes_por_sector
offset 2 DB sectores_por_cluster
offset 3 DW sectores_reservados_al_comienzo_del_disco
offset 5 DB nmero_de_FATs
offset 6 DW nmero_de_entradas_en_el_directorio_raz
offset 8 DW nmero_total_de_sectores (0 con n de sector de 32 bits)
offset 10 DB byte_descriptor_de_medio
offset 11 DW numero_de_sectores_por_FAT
-- A partir del DOS 3.0:
offset 13 DW sectores_por_pista
offset 15 DW nmero_de_cabezas
offset 17 DD nmero_de_sectores_ocultos
-- A partir del DOS 4.0 (ms bien DOS 3.31)
offset 21 DD nmero_de_sectores (unidades con direccionamiento de
sector de 32 bits)
offset 25 DB 6 DUP (?) (6 bytes no documentados)
offset 31 DW nmero_de_cilindros
offset 33 DB tipo_de_dispositivo
offset 34 DW atributos_del_dispositivo
El DOS c o n v i e r t e
internamente el BPB en DPB (Drive
Parameter Block), una estructura
similar con ms informacin til.
Para obtener el DPB de una unidad
determinada, puede utilizarse la
funcin 32h del DOS, Get Drive
Parameter Block (indocumentada);
la cadena de DPBs del DOS puede
recorrerse a partir del primer DPB
(obtenido con la funcin 52h del
DOS, Get List of Lists, tambin
indocumentada).
7.6.7. - LA BIOS Y LOS DISQUETES.
Resulta interesante conocer el comportamiento de la BIOS en relacin a los disquetes, ya que las
aplicaciones desarrolladas bajo DOS de una u otra manera habrn de cooperar con la BIOS por razones de
compatibilidad (o al menos respetar ciertas especificaciones). El funcionamiento del disquete se controla a
travs de funciones de la INT 13h, aunque esta interrupcin por lo general acaba llamando a la INT 40h que
es quien realmente gestiona el disco en las BIOS modernas de AT. Las funciones soportadas por esta
interrupcin son: reset del sistema de disco (reset del controlador de disquetes, envo del comando specify
y recalibramiento del cabezal), consulta del estado del disco (obtener resultado de la ltima operacin),
lectura, escritura y verificacin de sectores, formateo de pistas, obtencin de informacin del disco y las
disqueteras, deteccin del cambio de disco, establecimiento del tipo de soporte para formateo... algunas de
estas ltimas funciones no estn disponibles en las mquinas PC/XT. La BIOS se apoya en varias variables
ubicadas en el segmento 40h de la memoria. Estas variables son las siguientes (para ms informacin,
consultar el apndice al final del libro):
Byte 40h:3Eh Estado de recalibramiento del disquete. Esta variable indica varias cosas: si se ha producido una interrupcin 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, adems del estado de los motores de las 4 posibles disqueteras (si
estn encendidos o no), la ltima unidad que fue seleccionada y la operacin en curso sobre la misma.
Byte 40h:40h Cuenta para la detencin del motor. Este byte es decrementado por la interrupcin peridica del temporizador; cuando
llega a 0 todos los motores de las disqueteras (realmente, el nico que estaba girando) son detenidos. Dejar el motor
girando unos segundos tras la ltima operacin evita tener que esperar a que el motor acelere antes de la siguiente (si
esta llega poco despus).
Byte 40h:41h Estado de la ltima operacin: se actualiza tras cada acceso al disco, indicando los errores producidos (0 = ninguno).
Bytes 40h:42h A partir de esta direccin, 7 bytes almacenan el resultado de la ltima operacin 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 Informacin 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 resultado
de los intentos de la BIOS (la velocidad puede ser correcta o no, segn 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 operacin.
Byte 40h:93h Estado del soporte en la unidad B al inicio de la operacin.
Byte 40h:94h Nmero de cilindro en curso en la unidad A.
Byte 40h:95h Nmero de cilindro en curso en la unidad B.
Adems de estas variables, la BIOS utiliza tambin una tabla de parmetros apuntada por la INT 1Eh.
Los valores para programar ciertas caractersticas del FDC segn el tipo de disco pueden variar, aunque
132 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
algunos son comunes. Esta tabla determina las principales caractersticas de operacin del disco. Dicha tabla
est inicialmente en la ROM, en la posicin 0F000h:0EFC7h de todas las BIOS compatibles (prcticamente
el 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 del
765, que indica el step rate (el tiempo de acceso
cilindro-cilindro, a menudo es 0Dh = 3 6 ms) y el
head unload time (normalmente, 0Fh = 240 480 ms).
byte 1: Es el byte 2 del comando Specify: los bits 7..1 indican
el head load time (normalmente 01h = 2 4 ms) y el
bit 0 suele estar a 0 para indicar modo DMA.
byte 2: Tics de reloj (pulsos de la interrupcin 8) que
transcurren 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 estabilizacin del cabezal en ms.
byte 10: Tiempo de aceleracin del motor (en unidades de 1/8 de
segundo).
El tiempo de estabilizacin del cabezal es el tiempo que hay que esperar tras mover el cabezal al
cilindro adecuado, hasta que ste se asiente, con objeto de garantizar el xito de las operaciones futuras; esta
breve pausa es establecida en 25 milisegundos en la BIOS del PC original, aunque otras BIOS y el propio
DOS suelen bajarlo a 15. Del mismo modo, el tiempo de aceleracin del motor (byte 10) es el tiempo que
se espera a que el motor adquiera la velocidad de rotacin correcta, nada ms ponerlo en marcha. En
cualquier caso, es norma general intentar tres veces el acceso a disco (con resets de por medio) hasta
considerar que un error es real. En general, pese a estos valores usuales, la flexibilidad del sistema de disco
es extraordinaria y suele responder favorablemente con unos altsimos niveles de tolerancia en las
temporizaciones. Una excepcin quiz la constituye el valor de GAP empleado al formatear, al ser un
parmetro demasiado importante.
7.6.8. - DISQUETES FLOPTICAL 3 DE 20 MB.
Las unidades que soportan estos disquetes, que tambin admiten los de 720K y 1.44M (aunque a
menudo no los de 2.88M) trabajan con controladoras SCSI e incorporan una BIOS propia para dar soporte
a estos dispositivos. El secreto de estos disquetes est en el posicionamiento ptico del cabezal, lo que
permite elevar notablemente el nmero de pistas. Por ejemplo, las unidades de 20 Mb parecen estar equipadas
con 753 cilindros y 27 sectores/pista. Aunque en el sector de arranque indica que posee 251 cilindros y 6
cabezales, el sentido comn nos permite deducir que esto no puede ser as. Lo de los 27 sectores por pista
parece indicar que la velocidad de transferencia de estos disquetes es exactamente un 50% mayor que la de
los convencionales de 1.44M (750 Kbit/seg frente a 500 Kbit/seg).
El FORMAT del DOS 5.0 y posteriores puede formatear los disquetes floptical, pero lo hace a bajo
nivel, con lo que tarda cerca de 30-45 minutos en inicializarlos. Como ya vienen formateados de fbrica, en
realidad basta con aadirles un sector de arranque e inicializar la FAT y el directorio raz. Tambin se puede
verificar la superficie magntica para detectar posibles sectores defectuosos. Los programas de utilidad que
acompaan estas unidades realizan todas estas tareas en unos 4 minutos. El tipo de FAT asignado puede ser
seleccionado por el usuario (12 16 bits), as como otros parmetros tcnicos (tamao de clusters, etc.).
Las tarjetas controladoras suelen permitir un cierto grado de flexibilidad, de cara a seleccionar la letra
de unidad que se desea asignar al floptical. Configurndolo como A: se puede incluso arrancar desde un
disquete de stos.
7.6.9. - EJEMPLO DE ACCESO AL DISCO A ALTO NIVEL.
Se puede acceder a varios niveles, siendo mejor el ms alto por razones de compatibilidad:
1) Programando directamente el controlador de disquetes/disco duro para acceder a sectores fsicos.
2) Llamando a la BIOS para leer cierto sector, de cierta cara y cierto cilindro.
3) Llamando al DOS para leer un sector lgico determinado en la unidad que se le indique.
4) Llamando al DOS para acceder a un fichero por su nombre y ruta.
133 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
El mtodo (1) es apropiado para realizar formateos especiales en sistemas de proteccin anticopia;
el (2) es til para acceder a otras particiones de otros sistemas operativos o a disquetes formateados por otros
sistemas operativos; las opciones (3) y (4) son las ms cmodas e interesantes. En general, en la medida de
lo posible es conveniente no bajar del nivel (3); de lo contrario se pierde la posibilidad de acceder a ciertas
unidades (por ejemplo, un disco virtual no existe en absoluto para la BIOS).
A continuacin se muestra un programa de ejemplo que solicita el nombre de un fichero y lo
visualiza por pantalla, cargndolo por fragmentos y apoyndose en las funciones del DOS que se comentan
en el apndice que resume las funciones del sistema operativo. Paradjicamente, el acceso se realiza a alto
nivel pese a tratarse de un programa en ensamblador. Como se puede observar, al final del programa se
definen dos buffers de datos de 80 y 2048 bytes. Si no se desea que estos buffers alarguen el tamao del
programa ejecutable, pueden definirse de la siguiente manera:
fichnom EQU $
buffer EQU $+80
Sin embargo, si se procede de esta ltima manera convendra asegurarse primero de que existen 2128
bytes de memoria libres tras el cdigo del programa, ya que de esta manera el DOS no realiza la
comprobacin por nosotros (se limita a cargar cualquier programa que quepa en memoria). De todas maneras,
normalmente suele haber ms de 2128 bytes libres de memoria tras cargar cualquier programa... Conviene
hacer notar que si en lugar de DUP (0) se coloca DUP (?), el linkador de Borland (TLINK 3.0), al contrario
que el LINK de Microsoft, TAMPOCO reserva espacio efectivo para esas variables. Esto slo sucede,
lgicamente, cuando el DUP (?) est al final del programa y no hay nada ms a continuacin -ni ms cdigo
ni datos que no sean DUP (?)-.
; ********************************************************************
; * *
; * MIRA.ASM - Utilidad para visualizar ficheros de texto. *
; * *
; ********************************************************************
mira SEGMENT
ASSUME CS:mira, DS:mira
ORG 100h ; programa de tipo .COM
inicio:
LEA DX,input_txt ; mensaje
MOV AH,9 ; funcin de impresin
INT 21h ; llamar al DOS
LEA DX,fichnom ; direccin para el input
MOV BYTE PTR [fichnom],60 ; no ms de 60 caracteres
MOV AH,10 ; funcin de entrada de teclado
INT 21h ; llamar al DOS
MOV BL,[fichnom+1] ; longitud efectiva tecleada
MOV BH,0 ; en BX
ADD BX,OFFSET fichnom ; apuntar al final
MOV BYTE PTR [BX+2],0 ; poner un cero al final
LEA DX,fichnom+2 ; offset a cadena ASCIIZ nombre
MOV AL,0 ; modo de lectura
MOV AH,3Dh ; funcin para abrir fichero
INT 21h ; llamar al DOS
JC error ; CF=1 --> error
MOV handle,AX ; cdigo de acceso al fichero
trocito: MOV BX,handle ; cdigo de acceso al fichero
MOV CX,2048 ; nmero de bytes a leer
LEA DX,buffer ; direccin del buffer
MOV AH,3Fh ; funcin para leer del fichero
INT 21h ; llamar al DOS
JC error ; CF=1 --> error
MOV CX,AX ; bytes ledos realmente
JCXZ cerrar ; no hay nada que imprimir
PUSH AX ; preservarlos
LEA BX,buffer ; imprimir buffer ...
imprime: MOV DL,[BX] ; carcter a carcter
MOV AH,2 ; ir llamando al servicio 2 del
INT 21h ; DOS para imprimir en pantalla
INC BX ; siguiente carcter
LOOP imprime ; acabar caracteres
POP AX ; recuperar n de bytes ledos
CMP AX,2048 ; leidos 2048 bytes?
JE trocito ; s, leer otro trocito ms
cerrar: MOV BX,handle ; cdigo de acceso al fichero
MOV AH,3Eh ; cerrar fichero
INT 21h ; llamar al DOS
JC error ; CF = 1 --> error
INT 20h ; fin del programa
error: LEA DX,fallo_txt ; mensaje de error
MOV AH,9 ; funcin de impresin
INT 21h ; llamar al DOS
CMP handle,0 ; fichero abierto?
JNE cerrar ; s: cerrarlo
INT 20h ; fin del programa
; ------------ datos y variables
handle DW 0 ; handle de control del fichero
input_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 teclado
buffer DB 2048 DUP (0) ; " " " " el disco
mira ENDS
END 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 por
480 con 16 colores para obtener una representacin grfica de alta calidad del contenido del disco, en lugar
de la tradicional y pobre representacin habitual en modo texto. Adems, se reprograman los registros de
paleta y el DAC de la VGA para elegir colores ms atractivos. El funcionamiento del programa se basa en
acceder a la FAT y crear una imagen grfica de la misma. Para ello, calcula cuantos puntos de pantalla debe
trazar por cada cluster de disco (utiliza una ventana de 636x326 = 207336 puntos). Aunque este nmero no
es entero, por razones de eficiencia se trabaja con fracciones para evitar el empleo de coma flotante. Muchas
veces el ensamblador no es suficiente para asegurar la velocidad: la primera versin del programa tardaba
18 segundos en dibujar un mapa en un 386-25, con una rutina escrita en su mayor parte en ensamblador. Tras
mejorar el algoritmo y optimizar el cdigo en la zona crtica donde se trazan los puntos, se redujo a menos
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 del
disco no se utiliza la funcin absread() del Borland C 2.0, ya que posee una errata por la que falla con
unidades de ms de 32767 clusters. En su lugar, una rutina en ensamblador se encarga de llamar a la
interrupcin 25h teniendo cuidado con el tipo de disco (particiones de ms de 32 Mb o de menos de esa
cantidad). La FAT se lee en una matriz, ya que no ocupa ms de 128 Kb en el peor de los casos. Se lee de
tres veces para evitar que en un slo acceso a disco, va INT 25h, se rebasen los 64 Kb permitidos si la FAT
ocupa ms de 64 Kb (el puntero al buffer apunta al inicio del segmento al ser de tipo HUGE). A
continuacin, se interpreta la FAT (segn sea de 12 16 bits) y se crea otra matriz de tamao equivalente
al nmero 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 tambin imprime informacin general sobre el
disco, utilizando la funcin de impresin de la BIOS. Se imprime todo lo necesario antes de dibujar ya que
para trazar los puntos es preciso programar el adaptador de vdeo de una manera diferente a la que emplea
la BIOS (por razones de velocidad): despus de ejecutar prepara_punto(), la BIOS no es capaz de escribir en
pantalla. La inclusin de ensamblador en los programas en C se ver con detalle en un captulo posterior.
/********************************************************************/
/* */
/* DMAP 2.1 - Utilidad de informacin grfica de discos. */
/* */
/* (c) Julio 1994 Ciriaco Garca de Celis. */
/* */
/* Compilar con Borland C++ en modelo large con */
/* la opcin 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 vdeo */
#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 pas */
cb=0;
if (!strcmp(strupr(argv[argc-1]),"/I")) cb++; /* parmetro /I */
sp^=cb;
if (argc>cb+1)
unidad=(*argv[1] | 0x20)-a;
else
unidad=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 pgina 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 condicin 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); /* pgina 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; /* nmero 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 */
}
135 ARQUITECTURA 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 Slo soportados sistemas FAT12/FAT16.\n":
"\n Only supported FAT12/FAT16 filesystems.\n");
break;
case 5: printf (sp?"\n Sector de arranque daado, 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 */
else
bitfat[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 */
else
bitfat[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?"Nmero de FATs:":"Number of FATs:");
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 errneos:":"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,numclusters
les bx,bitfat
mov si,bx } /* SI --> posicin del primer cluster */
proc_fat: asm {
mov al,es:[bx] }
cuenta: asm {
inc bx
cmp al,es:[bx]
loope cuenta
mov di,bx
sub di,si /* DI --> nmero de cluster hasta donde avanzar */
push si
mov ax,word ptr factor
mul di
mov si,ax
mov ax,di
mov di,dx /* DI:SI producto parcial */
mul word ptr [factor+2] /* DX:AX segundo producto parcial */
add ax,di
adc dx,0 /* DX:AX:SI producto */
shl si,1
rcl ax,1
rcl dx,1
shl si,1
rcl ax,1
rcl dx,1 /* DX:AX = DX:AX:SI / 16384 = pixel */
mov si,dx
mov di,ax
sub di,ant_pixel_l
sbb si,ant_pixel_h /* SI:DI = n de pixels a pintar */
mov ant_pixel_l,ax
mov ant_pixel_h,dx
push bx; push cx; push ds; push bp;
mov ch,es:[bx-1]
mov bx,coord_x
mov bp,coord_y
mov dx,3CEh
mov ax,0A000h
mov ds,ax
mov al,8
mov cl,bl /* BX = cx, BP = cy*80 */
and cl,7
mov ah,80h
shr ah,cl /* AH = bit a pintar en su sitio */
push bx
mov cl,3
shr bx,cl
add bx,bp /* BX = cy*80+cx/8 */
push si
mov si,80
out dx,ax }
pinta_mas: asm {
mov cl,[bx] /* acceso en lectura */
mov [bx],ch /* pintar punto */
sub di,1
jc dec_msb } /* evitar salto la mayora de las veces */
incy: asm {
add bx,si
add bp,si
cmp bp,(MAX_Y+1)*80
jb pinta_mas
ror ah,1 /* siguiente pixel en el eje X */
out dx,ax
pop si
pop bx
inc bx
push bx
push si
mov si,80
mov bp,MIN_Y*80
mov cl,3
shr bx,cl
add bx,bp /* BX = cy*80+cx/8 */
push ax
mov ah,1
int 16h
pop ax
jz pinta_mas
pop si; pop bx; pop bp; pop ds; pop cx; pop bx; pop si;
jmp fin_proc } /* tecla pulsada */
dec_msb: asm {
pop si
sub si,1
push si
mov si,80
jnc incy
pop si
pop bx
mov ax,bp
pop bp
pop ds
mov coord_x,bx
mov coord_y,ax
pop cx
pop bx
pop si
jcxz fin_proc
jmp 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,3CEh
mov ax,205h /* registro de modo (5): escr. 2 lect. 0 */
out dx,ax
mov ax,3 /* cambiar AH para hacer OR/XOR/AND */
out dx,ax
pop dx; pop ax
}
}
void punto (int coord_x, int coord_y, int color)
{
asm { /* rutina rpida slo para modos de 640x???x16 */
push ds
push ax; push bx; push cx; push dx;
mov cx,coord_x
mov dx,coord_y
xchg bx,cx /* BX = cx, DX = cy */
mov cx,0A000h
mov ds,cx
mov cl,4
shl dx,cl /* DX = cy * 16 */
mov ax,dx
shl ax,1
shl ax,1 /* CX = cy * 64 */
add dx,ax /* DX = cy * 80 */
mov al,bl
dec cl
shr bx,cl /* CL = 3 */
add bx,dx /* BX = cy * 80 + cx / 8 */
and al,7
mov cl,al
mov ah,80h
shr ah,cl /* AH = bit a pintar en su sitio */
mov dx,3CEh /* registro de direcciones */
mov al,8
out dx,ax
mov al,[bx] /* acceso en lectura */
mov ax,color
mov [bx],al
pop 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 ms de 65535 sectores */
else
tipo_disco=0; /* unidad de menos de 65536 sectores */
137 ARQUITECTURA 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 ms 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 /* nmero de sectores */
push psecth /* sector inicial (parte alta) */
push psectl /* (parte baja) */
mov ax,unidad /* unidad */
mov bx,sp
mov dx,ss
mov ds,dx /* DS:BX = SS:SP */
mov cx,0ffffh /* sectores de 32 bits */
int 25h /* acceso al disco */
pushf
pop flags /* resultado de la operacin */
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 /* nmero de sectores */
mov bx,buffer_o /* offset del buffer */
mov ds,buffer_s /* segmento */
int 25h /* acceso al disco */
pushf
pop flags /* resultado de la operacin */
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 ingls */
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 captulo anterior, antes de que el COMMAND.COM pase el control al programa
que se pretende ejecutar, se crea un bloque de 256 bytes llamado PSP (Program Segment Prefix), cuya
descripcin detallada se da a continuacin.
La direccin del PSP en los programas COM viene determinada por la de cualquier registro de
segmento (CS=DS=ES=SS) nada ms comenzar la ejecucin del mismo. Sin embargo, en los programas de
tipo EXE slo viene determinada por DS y ES. En cualquier caso, existe una funcin del DOS para obtener
la direccin del PSP, cuyo uso recomienda el fabricante del sistema en aras de una mayor compatibilidad con
futuras versiones del sistema operativo. La funcin es la 62h y est disponible a partir del DOS 3.0.
En la siguiente informacin, los campos del PSP que ocupen un byte o una palabra han de
interpretarse como tal; los que ocupen 4 bytes deben interpretarse en la forma segmento:offset. En negrita
se resaltan los campos ms importantes.
- offsets 0 al 1: palabra 20CDh, correspondiente a la instruccin INT 20h. En CP/M se poda terminar un
programa ejecutando un salto a la posicin 0. En MS-DOS, un programa COM tambin!.
- offsets 2 al 3: una palabra con la direccin de memoria (segmento) del ltimo prrafo disponible en el
sistema. Teniendo en cuenta dnde acaba la memoria y el punto en que est cargado nuestro programa, no
es difcil saber la memoria que queda libre. Supuesto ES apuntando al PSP:
MOV AX,ES:[2] ; prrafo ms alto disponible
MOV CX,ES ; segmento del PSP
SUB AX,CX ; AX = prrafos libres
MOV CX,16
MUL CX ; DX:AX bytes libres
- offset 4: no utilizado.
- offsets 5 al 9: salto al despachador de funciones del DOS (en CP/M se ejecutaba un CALL 5, el MS-DOS
tambin lo permite!). No es recomendable llamar al DOS de esta manera. Los PSP creados por la funcin
4Bh en algunas versiones del DOS no tienen correctamente inicializado este campo.
- offsets 0Ah al 0Dh: contenido previo del vector de terminacin (INT 22h).
- offsets 0Eh al 11h: contenido previo del vector de Ctrl-Break (INT 23h).
- offsets 12h al 15h: contenido previo del vector de manipulacin de errores crticos (INT 24h).
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 estn abiertos). Slo hasta 20
ficheros (si no, vase offset 32h).
- offsets 2Ch al 2Dh: desde el DOS 2.0, una palabra que apunta al segmento del espacio de entorno, donde
se puede encontrar el valor de variables de entorno tan interesantes como PATH, COMSPEC,... y hasta el
nombre del propio programa que se est ejecutando en ese momento y el directorio de donde se carg (no
siempre es el actual; el programa pudo cargarse, apoyndose en el PATH, en cualquier otro directorio
diferente del directorio en curso). Vase el captulo 8 para ms informacin de las variables de entorno.
- offsets 2Eh al 31h: desde el DOS 2.0, valor de SS:SP en la entrada a la ltima INT 21h invocada.
- offsets 32h al 33h: desde el DOS 3.0, nmero de entradas en la JFT (por defecto, 20).
- offsets 34h al 37h: desde el DOS 3.0, puntero al JFT (por defecto, PSP:18h). Desde el DOS 3.0 puede haber
ms de 20 ficheros abiertos a la vez gracias a este campo, que puede ser movido de sitio. Sin embargo, es
slo a partir del DOS 3.3 cuando en un PSP hijo (por ejemplo, creado con la funcin EXEC) se copia la
informacin de ms que de los 20 primeros ficheros, si hay ms de 20. Se puede saber si un fichero es
remoto (en la MS-net) comprobando si el byte de la JFT est comprendido entre 80h-0FEh, aunque es mejor
siempre acceder antes a las funciones del DOS.
- offsets 38h al 3Bh: desde el DOS 3.0, puntero al PSP previo (por defecto, 0FFFFh:0FFFFh en las versiones
del DOS 3.x); es utilizado por SHARE en el DOS 3.3.
- offsets 3Ch al 3Fh: no usados hasta ahora.
- offsets 40h al 41h: desde el DOS 5.0, versin del sistema a devolver cuando se invoca la funcin 30h.
- offsets 42h al 47h: no usados hasta ahora.
- offset 48h: desde Windows 3, el bit 0 est activo si la aplicacin es no-Windows.
- offsets 49h al 4Fh: no usados hasta ahora.
- offsets 50h al 52h: cdigo 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 FCBs (File Control Blocks) usados antao para acceder a los ficheros
(uno en 5Ch y el otro en 6Ch). Es una reliquia en desuso, y adems este rea no se inicializa si el programa
es cargado en memoria superior con el comando LOADHIGH del MS-DOS 5.0 y posteriores, por lo que no
conviene usarlo ni siquiera para captar parmetros, al menos en programas residentes -susceptibles de ser
instalados con LOADHIGH-. Si se utiliza el primer FCB se sobreescribe adems el segundo.
- offsets 7Ch al 7Fh: no usados hasta ahora.
- offsets 80h al 0FFh: es la zona donde aparecen los parmetros suministrados al programa. El primer byte
indica la longitud de los parmetros, despus vienen los mismos y al final un retorno de carro (ASCII 13)
que es un tanto redundante -a fin de cuentas, ya se sabe la longitud de los parmetros-. Ese retorno de carro,
sin embargo, no se cuenta en el byte que indica la longitud. Tngase en cuenta que no son mayusculizados
automticamente (estn tal y como los tecle el usuario), y adems los parmetros pueden estar separados
por uno o ms espacios en blanco o tabuladores (ASCII 9).
139 ARQUITECTURA 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 que
se realiza de manera sencilla con el programa DEBUG/SYMDEB. Para ello basta una orden tal como
"DEBUG PROGRAMA.COM HOLA /T": al entrar en el DEBUG (o SYMDEB) basta con hacer D 0 para
examinar el PSP de PROGRAMA. Para ver los parmetros (HOLA /T en el ejemplo) se hara D 80.
7.8. - EL PROCESO DE ARRANQUE DEL PC.
Al conectar el PC ste comienza a ejecutar cdigo en los 16 ltimos bytes de la memoria (direccin
0FFFF0h en PC/XT, 0FFFFF0h en 286 y 0FFFFFFF0h en 386 y superiores). En esa posicin de memoria,
en la que hay ROM, existe un salto a donde realmente comienza el cdigo de la BIOS. Este salto suele ser
de tipo largo (segmento:offset) con objeto de cargar en CS un valor que referencie al primer mega de
memoria, donde tambin est direccionada la ROM (todos los microprocesadores arrancan en modo real). El
programa de la ROM inicialmente se limita a chequear los registros de la CPU, primero el de estado y luego
los dems (en caso de fallo, se detiene el sistema). A continuacin, se inicializan los principales chips
(interrupciones, DMA, temporizador...); se detecta la configuracin del sistema, accediendo directamente a
los puertos de E/S y tambin consultando los switches de configuracin de la placa base (PC/XT) o la CMOS
(AT); se establecen los vectores de interrupcin y se chequea la memoria RAM si el contenido de la direccin
40h:72h es distinto de 1234h (el contenido de la memoria es aleatorio inicialmente). Por ltimo, se entrega
el control sucesivamente a las posibles memorias ROM adicionales que existan (la de la VGA, el disco duro
en XT, etc.) con objeto de que desven los vectores que necesiten. Al final del todo, se intenta acceder a la
primera unidad de disquetes: si no hay disquete, se procede igualmente con el primer disco duro (en los PC
de IBM, si no hay disco duro ni disquete se ejecuta la ROM BASIC). Se carga el primer sector en la
direccin 0:7C00h y se entrega el control a la misma. Ese sector cargado ser el sector de arranque del
disquete o la tabla de particin del disco duro (el cdigo que contiene se encargar de cargar el sector de
arranque del propio disco duro, segn la particin activa). El programa del sector de arranque busca el fichero
del sistema IO.SYS (o IBMBIO.COM en PC-DOS) y lo carga, entregndole el control (programa SYSINIT)
o mostrando un mensaje de error si no lo encuentra. Las versiones ms modernas del DOS no requieren que
IO.SYS IBMBIO.COM comience en el primer cluster de datos del disco, aunque s que se encuentre en
el directorio raz. Puede que tambin se cargue al principio el fichero MSDOS.SYS (o IBMDOS.COM) o bien
puede que el encargado de cargar dicho fichero sea el propio IO.SYS o IBMBIO.COM. El nombre de los
ficheros 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 prcticamente idnticos desde la versin 2.0 (PC-DOS funciona en mquinas no
IBM), la existencia de las dos versiones se explica slo por razones comerciales. El fichero IO.SYS o
IBMBIO.COM en teora debera ser entregado por el vendedor del ordenador: este fichero provee soporte a
las diferencias especficas que existen en el hardware de las diferentes mquinas. Sin embargo, como todos
los PC compatibles son casi idnticos a nivel hardware (salvo algunas de las primeras mquinas que
intentaron imitar al PC) en la prctica es el fabricante del DOS (Microsoft o Digital Research) quien entrega
dicho fichero. Ese fichero es como una capa que se interpone entre la BIOS del PC y el cdigo del sistema
operativo contenido en MSDOS.SYS o IBMDOS.COM. Este ltimo fichero es el encargado de inicializar los
vectores 20h-2Fh y completar las tablas de datos internas del sistema. Tambin se interpreta el CONFIG.SYS
para instalar los controladores de dispositivo que den soporte a las caractersticas peculiares de la
configuracin del ordenador. Finalmente, se carga el intrprete de mandatos: por defecto es
COMMAND.COM aunque no hay razn para que ello tenga que ser as necesariamente (pruebe el lector a
poner en CONFIG.SYS la orden SHELL C:\DOS\QBASIC.EXE; aunque si se abandona QBASIC algunas
versiones modernas del DOS son an capaces de cargar el COMMAND por sus propios medios, despus del
error pertinente, en vez de bloquear el ordenador). En las versiones ms recientes del DOS, el sistema puede
residir en memoria superior o en el HMA: en ese caso, el proceso de arranque se complica ya que es
necesario localizar el DOS en esa zona despus de cargar los controladores de memoria.
7.9. - FORMATO DE LAS EXTENSIONES ROM.
Las memorias ROM que incorporan diversas tarjetas (de vdeo, controladoras de disco duro, de red)
pueden estar ubicadas en cualquier punto del rea 0C0000h-0FFFFFh. La ROM BIOS del ordenador se
140 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
encarga de ir recorrindolas y entregndolas el control durante la inicializacin, con objeto de permitirlas
desviar vectores de interrupcin y ejecutar otras tareas propias de su inicializacin.
La BIOS recorre este rea en incrementos de 2 Kb buscando la signatura 55h, 0AAh: estos dos bytes
consecutivos tienen que aparecer al principio para considerar que ah hay una ROM. El tercer byte, que va
detrs de stos, indica el tamao de esa extensin ROM en bloques de 512 bytes. Por razones de seguridad,
se realiza una suma de comprobacin de toda la extensin ROM y si el resultado es 0 se considera una
autntica ROM vlida. En ese caso, se entrega el control (con un CALL entre segmentos) al cuarto byte de
la extensin ROM. Ah habr de estar ubicado el cdigo de la extensin ROM (habitualmente un salto a
donde realmente comienza). Al final del todo, el cdigo de la extensin ROM debe devolver de nuevo el
control a la BIOS del sistema, por medio de un retorno lejano (RETF).
El cdigo almacenado en estas extensiones ROM puede contener accesos directos al hardware y
llamadas a la ROM BIOS del sistema. Sin embargo, conviene recordar que el DOS no ha sido cargado an
y no se pueden emplear sus funciones. La ventaja de las extensiones ROM es que aumentan las prestaciones
del sistema antes de cargar el DOS. El inconveniente es que en otros sistemas operativos (UNIX, etc.) que
emplean el modo protegido, estas memorias ROM en general no son accesibles. En la actualidad, con la
disponibilidad de memoria superior bajo DOS, resulta ms conveniente que las extensiones de hardware
vengan acompaadas de drivers para DOS, WINDOWS, OS/2,... que no con una ROM, mucho ms difcil
de actualizar. Un ejemplo de memoria ROM podra ser:
bios DB 55h, 0AAh
DB 32 ; 16 Kb de ROM
JMP inicio
...
...
fin_bios ... ; la suma de todos los bytes = 0
Los primeros ordenadores de IBM incorporaban una memoria ROM con el BASIC. El COMMAND
de aquellas versiones del DOS (desconozco si el actual tambin) era capaz de ejecutar comandos internos
definidos en estas ROM, al igual que un CLS o un DIR, vamos. El formato era, por ejemplo:
bios_basic DB 55h, 0AAh
DB 64 ; 32 Kb de ROM-BASIC
JMP inicio
DB 5 ; longitud del siguiente comando
DB "BASIC"
JMP basic ; salto al comienzo del BASIC
DB 6 ; longitud del siguiente comando
DB "BASICA"
JMP basic ; salto al comienzo (el mismo del BASIC)
DB 0 ; no ms comandos
basic ...
...
fin_bios ... ; la suma de todos los bytes = 0
Si esto le parece una tontera al lector, es que no ha visto lo que vamos a ver ahora. Resulta que
tambin se pueden almacenar programas en BASIC (el cdigo fuente, aunque tokenizado) en las BIOS. S,
un listado en ROM!:
mortgagebas DB 55h, 0AAh
DB 48 ; 24 Kb de contabilidad
RETF ; nada que hacer
DB 0AAh, 55h ; esto es un listado BASIC
... ; aqu, el programa
fin_bios ... ; la suma de todos los bytes = 0
7.10. - FORMATO FSICO DE LOS FICHEROS EXE.
Los ficheros EXE poseen una estructura en el disco distinta de su imagen en memoria, al contrario
que los COM. Es conveniente conocer esta estructura para ciertas tareas, como por ejemplo la creacin de
antivirus -y tambin la de virus-, que requiere modificar un fichero ejecutable ya ensamblado o compilado.
141 ARQUITECTURA DEL PC, AT Y PS/2 BAJO DOS
Analizaremos como ejemplo de programa EXE el del captulo 6, que rene las principales caractersticas
necesarias para nuestro estudio. Se comentarn los principales bytes que componen el fichero ejecutable en
el disco (1088 en total). A continuacin se lista un volcado del fichero ejecutable a estudiar. Todos los datos
estn en hexadecimal (parte central) y ASCII (derecha); la columna de la izquierda es el offset del primer
byte de la lnea. Donde hay puntos suspensivos, se repite la lnea 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 ........>.....{0
0020 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 imprim
0210 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 .3@P8...X:..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 cdigo, datos y pila; esta
cabecera se carga en un buffer auxiliar y no formar parte de la imagen definitiva del programa en memoria.
A continuacin se explica el contenido de los bytes de la cabecera:
Offset 0 (2 bytes): Valores fijos 4Dh y 5Ah (en ASCII, MZ) 5Ah y 4Dh (ZM); esta informacin indica
que el fichero es realmente de tipo EXE y no lleva esa extensin por antojo de nadie.
Offset 2 (2 palabras): Tamao del fichero en el disco. La palabra ms significativa (offset 4) da el
nmero 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 ltimo
sector slo tiene ocupados los primeros 40h bytes. Por tanto, el tamao efectivo del fichero es de
1024 + 64 = 1088 bytes, lo que se corresponde con la realidad.
Offset 6 (1 palabra): Nmero de reubicaciones a realizar. Indica cuntas veces se hace referencia a un
segmento absoluto: el montador del sistema operativo tendr que relocalizar en memoria todas las
referencias a segmentos absolutos segn en qu direccin se cargue el programa para su ejecucin.
En el ejemplo slo hay 1 (correspondiente a la instruccin MOV AX,datos).
Offset 8 (1 palabra): Tamao de esta cabecera del fichero EXE. La cabecera que estamos analizando y que
precede al cdigo y datos del programa ser ms o menos larga en funcin del tamao de la tabla
de reubicaciones, como luego veremos. En el ejemplo son 200h (=512) bytes, el tamao mnimo,
habida cuenta que slo hay una reubicacin (de hecho, an cabran muchas ms).
Offset 0Ah (1 palabra): Mnima cantidad de memoria requerida por el programa, en prrafos, en adicin al
tamao del mismo. En el ejemplo es 0 (el programa se conforma con lo que ocupa en disco).
Offset 0Ch (1 palabra): Mxima cantidad de memoria requerida (prrafos). Si es 0, el programa se cargar
lo ms alto posible en la memoria (opcin /H del LINK de Microsoft); si es 0FFFFh, como en el
ejemplo, el programa se cargar lo ms abajo posible en la memoria -lo ms normal-.
Offset 0Eh (2 palabras): Valores para inicializar SS (offset 0Eh) y SP (offset 10h). Evidentemente, el valor
para SS est an sin reubicar (habr de sumrsele el segmento en que se cargue el programa). En el
ejemplo, el SS relativo es 4 y SP = 200h (=512 bytes de tamao de pila definido).
Offset 12h (1 palabra): Suma de comprobacin: son en teora los 16 bits de menos peso de la negacin de
la suma de todas las palabras del fichero. El DOS debe hacer poco caso, porque TLINK no se
molesta ni en inicializarlo (El LINK de Microsoft s). Olvidar este campo.
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 an
sin reubicar y habr de sumrsele el segmento definitivo en que se cargue el programa. En el
ejemplo, el valor relativo de CS es 2, siendo IP = 0.
Offset 18h (1 palabra): Inicio de la tabla de reubicacin, expresado como offset. En el ejemplo es 3Eh, lo
que indica que la tabla comienza en el offset 3Eh. Cada entrada en la tabla ocupa 4 bytes. La nica
entrada de que consta este programa tiene el valor 0002:0005 = 25h, lo que indica que en el offset
200h+25h (225h) hay una palabra a reubicar -se suma 200h que es el tamao de la cabecera-. En
efecto, en el offset 225h hay una palabra a cero, a la que habr de sumrsele el segmento donde sea
cargado el programa. Esta palabra a cero es el operando de la instruccin MOV AX,datos (el cdigo
de operacin de MOV AX,n es 0B8h).
Offset 1Ah (1 palabra): Nmero de overlay (0 en el ejemplo, es un programa principal).
Offset 1Ch al 3Dh: Valores desconocidos (dependientes de la versin de LINK o TLINK).
143 LA GESTIN DE MEMORIA DEL DOS
Captulo VIII: LA GESTIN 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 la
actualidad. Conviene tambin echar un vistazo al apndice I, donde se describe de manera ms esquemtica,
para completar la explicacin.
8.1.1. - Memoria convencional.
Es la memoria RAM comprendida entre los 0 y los 640 Kb; es la memoria utilizada por el DOS para
los programas de usuario. Los 384 Kb restantes hasta completar el megabyte se reservan para otros usos,
como memoria para grficos, BIOS, etc. En muchas mquinas, un buen fragmento de esta memoria est
ocupado por el sistema operativo y los programas residentes, quedando normalmente no ms de 560 Kb a
disposicin del usuario.
8.1.2. - Memoria superior.
Este trmino, de reciente aparicin, designa el rea comprendida entre los 640 y los 1024 Kb de
memoria del sistema. Entre 1989 y 1990 aparecieron programas capaces de gestionar este rea para
aprovechar los huecos de la misma que no son utilizados por la BIOS ni las tarjetas grficas. La memoria
superior no se toma de la memoria instalada en el equipo, sino que est en ciertos chips aparte relacionados
con la BIOS, los grficos, etc. Por ello, un AT con 1 Mb de RAM normalmente posee 640 Kb de memoria
convencional y 384 Kb de memoria extendida. Los segmentos A0000 y B0000 estn reservados para grficos,
aunque rara vez se utilizan simultneamente. El segmento C0000 contiene la ROM del disco duro en XT (en
AT el disco duro lo gestiona la propio BIOS del sistema) y/o BIOS de tarjetas grficas. El segmento D0000
es empleado normalmente para el marco de pgina de la memoria expandida. El segmento E0000 suele estar
libre y el F0000 almacena la BIOS del equipo. Los modernos sistemas operativos DOS permiten (en los
equipos 386 386sx y superiores) colocar memoria fsica extendida en el espacio de direcciones de la
memoria superior; con ello es factible rellenar los huecos vacos y aprovecharlos para cargar programas
residentes. Ciertos equipos 286 tambin soportan esta memoria, gracias a unos chips de apoyo, pero no es
frecuente.
8.1.3. - Memoria de vdeo.
El primer adaptador de vdeo de IBM era slo para texto y empleaba 4 Kb. Despus han ido
apareciendo la CGA (16 Kb), EGA (64-256 Kb), VGA (256 Kb) y SVGA (hasta 2 Mb). Como slo hay 128
Kb reservados para grficos en el espacio de direcciones del 8086, las tarjetas ms avanzadas tienen paginada
su memoria y con una serie de puertos de E/S se indica qu fragmento del total de la memoria de vdeo est
siendo direccionado (en la VGA, slo 64 Kb en A0000).
8.1.4. - Memoria expandida.
Surgi en los PC/XT como respuesta a la necesidad de romper el lmite de los 640 Kb, y se trata de
un sistema de paginacin. Consiste en aadir chips de memoria en una tarjeta de expansin, as como una
cierta circuitera que permita colocar un fragmento de esa memoria extra en lo que se denomina marco de
pgina de memoria expandida, que normalmente es el segmento D0000 del espacio de direcciones del 8086
144 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
(64 Kb). Este marco de pgina est dividido en 4 bloques de 16 Kb. All se pueden colocar bloques de 16
Kb extrados de esos chips adicionales por medio de comandos de E/S enviados a la tarjeta de expansin.
Para que los programas no tengan que hacer accesos a los puertos y para hacer ms cmodo el trabajo, surgi
la especificacin LIM-EMS (Lotus-Intel-Microsoft Expanded Memory System) que consiste bsicamente en
un driver instalable desde el config.sys que pone a disposicin de los programas un amplio abanico de
funciones invocables por medio de la interrupcin 67h. La memoria expandida est dividida en pginas
lgicas de 16 Kb que pueden ser colocadas en las normalmente 4 pginas fsicas del marco de pgina. Los
microprocesadores 386 (incluido obviamente el SX) permiten adems convertir la memoria extendida en
expandida, gracias a sus mecanismos de gestin de memoria: en estas mquinas la memoria expandida es
emulada por EMM386 o algn gestor similar.
8.1.5. - Memoria extendida.
Es la memoria ubicada por encima del primer mega en los procesadores 286 y superiores. Slo se
puede acceder a la mayora de esta memoria en modo protegido, por lo que su uso queda relegado a
programas complejos o diversos drivers que la aprovechen (discos virtuales, cachs de disco duro, etc.). Hace
ya bastante tiempo se dise una especificacin para que los programas que utilicen la memoria extendida
puedan convivir sin conflictos: se trata del controlador XMS. Este controlador implementa una serie de
funciones normalizadas que adems facilitan la utilizacin de la memoria extendida, optimizando las
transferencias de bloques en los 386 y superiores (utiliza automticamente palabras de 32 bits para acelerar
el acceso). La especificacin XMS viene en el programa HIMEM.SYS, HIDOS.SYS y en algunas versiones
del EMM386. El controlador XMS tambin aade funciones normalizadas para acceder a la memoria
superior.
8.1.6. - Memoria cach.
Desde el punto de vista del software, es memoria (convencional, expandida o extendida) empleada
por un controlador de dispositivo (driver) para almacenar las partes del disco de ms frecuente uso, con objeto
de acelerar el acceso a la informacin. A nivel hardware, la memoria cach es una pequea RAM ultrarrpida
que acompaa a los microprocesadores ms avanzados; los programas no tienen que ocuparse de la misma.
Tambin incorporan memorias cach algunos controladores de disco duro, aunque se trata bsicamente de
memoria 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 que
un 486 a 66 MHz tenga una BIOS de slo 8 bits a 8 Mhz. A partir de los procesadores 386 (tambin 386sx)
y superiores, existen unos mecanismos de gestin de memoria virtual que permiten colocar RAM en el
espacio lgico de direcciones de la ROM. Con ello, es factible copiar la ROM en RAM y acelerar
sensiblemente el rendimiento del sistema, especialmente con los programas que se apoyan en la BIOS.
Tambin los chipset de la placa base pueden aadir soporte para esta caracterstica. La shadow RAM
normalmente son 384 Kb que reemplazan cualquier fragmento de ROM ubicado entre los 640-1024Kb de
RAM durante el proceso de arranque (boot) del sistema. En ocasiones, el usuario puede optar entre 384 Kb
de shadow 384 Kb ms de memoria extendida en el programa SETUP de su ordenador.
8.1.8. - Memoria CMOS RAM.
Son 64 bytes de memoria (128 en algunas mquinas) ubicados en el chip del reloj de tiempo real de
la placa base de los equipos AT y superiores. A esta memoria se accede por dos puertos de E/S y en ella se
almacena la configuracin y fecha y hora del sistema, que permanecen tras apagar el ordenador (gracias a
las pilas). Evidentemente no se puede ejecutar cdigo sobre la RAM CMOS (Ni pueden esconderse virus,
al contrario de lo que algunos mal informados opinan. Otra cosa es que utilicen algn byte de la CMOS para
controlar su funcionamiento).
145 LA GESTIN 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 tipo
FFFF:1000 = 100FF0) un artificio de hardware lo impeda, convirtiendo esa direccin en la 0:0FF0 por el
simple procedimiento de poner a cero la lnea A20 de direcciones del microprocesador en los 286 y
superiores. Ese artificio de hardware lo protagoniza el chip controlador del teclado (8042) ya que la lnea A20
pasa por sus manos. Si se le insta a que conecte los dos extremos (enviando un simple comando al
controlador del teclado) a partir de ese momento es el microprocesador quien controla la lnea A20 y, por
tanto, en el ejemplo anterior se hubiera accedido efectivamente a la memoria extendida. Los nuevos sistemas
operativos DOS habilitan la lnea A20 y, gracias a ello, estn disponibles otros 64 Kb adicionales. Para ser
exactos, como el rango va desde FFFF:0010 hasta FFFF:FFFF se puede acceder a un total de 65520 bytes
(64 Kb menos 16 bytes) de memoria. Tngase en cuenta que las direcciones FFFF:0000 a la FFFF:000F estn
dentro del primer megabyte. En el HMA se cargan actualmente el DR-DOS 5.0/6.0 y el MS-DOS 5.0 y
posteriores; evidentemente siempre que el equipo, adems de ser un AT, disponga como mnimo de 64 Kb
de memoria extendida. En ciertos equipos poco compatibles es difcil habilitar la lnea A20, por lo que el
HIMEM.SYS de Microsoft dispone de un parmetro que se puede variar probando docenas de veces hasta
conseguirlo, si hay suerte (adems, hay BIOS muy intervencionistas que dificultan el control de A20).
8.2. - BLOQUES DE MEMORIA.
Vamos ahora a conocer con profundidad la manera en que el sistema operativo DOS gestiona la
memoria; un tema poco tratado, ya que esta informacin no est oficialmente documentada por Microsoft.
Los bloques de memoria en el DOS son agrupaciones de bytes siempre mltiplos enteros de 16 bytes:
en realidad son agrupaciones de prrafos. La memoria de un PC -siempre bajo DOS- est, por tanto, dividida
en grupos de prrafos. Por tanto, una palabra de 16 bits permite almacenar la direccin del prrafo de
cualquier posicin de memoria dentro del megabyte direccionable por el 8086. Todo bloque de memoria tiene
asociado un propietario, que bien puede ser el DOS o un programa residente que haya solicitado al DOS
el control de dicho bloque. Cuando se ejecuta un programa, el sistema crea dos bloques para el mismo: el
bloque de memoria del programa y el bloque de memoria del entorno.
8.2.1. - El bloque de memoria del programa.
Cuando se ejecuta un programa, el DOS busca el mayor bloque de memoria disponible (convencional
o superior, segn sea el caso) y se lo asigna -y no el bloque ms cercano a la direccin 0, como algunos
afirman-. Este rea recibe el nombre de bloque de programa o segmento de programa. La direccin del primer
prrafo del mismo es de suma importancia y se denomina PID (Process ID, identificador de proceso). En los
primeros 256 bytes de este rea el DOS crea el PSP ya conocido -256 bytes- formado por varios campos de
informacin relacionada con el programa. Tras el PSP viene el cdigo del programa ejecutable. Para los
objetivos de este captulo basta con conocer dos campos del PSP: el primero est en su offset 0 y son dos
bytes (por tanto, los primeros dos bytes del PSP) que contienen la palabra 20CDh ( 27CDh en algunos
casos). Esto se corresponde con el cdigo de operacin de la instruccin ensamblador INT 20h (o INT 27h);
esto es as por razones histricas heredadas del CP/M. Por ello, cuando un programa finaliza, puede hacerlo
con un salto al inicio del PSP (un JMP 0 en los programas COM) donde se ejecuta el INT 20h, aunque
normalmente el programador ejecuta directamente el INT 20h que es ms seguro. El otro campo del PSP que
nos interesa es el offset 2Ch: en l hay una palabra que indica el prrafo donde comienza el bloque de
entorno 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 (que
podemos considerar como un programa residente). Es una zona de memoria donde se almacenan las variables
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 anloga a SET PATH=C:\DOS. Las variables de
entorno pueden consultarse con SET (sin parmetros). las variables de entorno sirven para crear informacin
que puedan usar mltiples programas, aunque se usan poco en la realidad. Cuando un programa es cargado,
adems del bloque de memoria del programa se crea el bloque del entorno. Se trata de una vulgar copia del
espacio de entorno del COMMAND.COM; de esta manera, el programa en ejecucin tiene acceso a las
variables de entorno del sistema aunque no las puede modificar (estara modificando una mera copia). Las
variables de entorno se almacenan en formato ASCIIZ ordinario (esto es, terminadas por un byte a cero) y
tienen una sintaxis del tipo VARIABLE=SU VALOR. Tras la ltima de las variables hay otro byte ms a
cero para indicar el final. Despus de esto, y slo a partir del DOS 3.0, viene una palabra que indica el
nmero de cadenas ASCIIZ especiales que vienen a continuacin: normalmente 1, que contiene una
informacin muy til: la especificacin completa del nombre del programa que est siendo ejecutado -incluida
la unidad y ruta de directorios- lo que permite a los programas saber su propio nombre y desde qu directorio
estn siendo ejecutados y, por tanto, dnde deben abrir sus ficheros (por educacin no es conveniente hacerlo
en el directorio raz o en el actual). En el espacio de entorno del COMMAND, este aadido del DOS 3.0 y
posteriores parece no estar definido.
8.2.3. - Los bloques de control de memoria (MCBs).
Todos los bloques de memoria (tanto programa como entorno) vienen precedidos por una cabecera
de un prrafo (16 bytes) que almacena informacin relativa al mismo. Esta cabecera recibe el nombre tcnico
de MCB (Memory Control Block) y tiene la siguiente estructura:
offset 0 1 3 5 8 15
byte PID Nombre del propietario
de propietario Tamao ... (slo en bloque de programa
marca y MS-DOS 4.0 DRDOS 5.0)
En el offset 0 se sita el byte de marca (4Dh si no es el ltimo MCB de la cadena de MCBs en
memoria, 5Ah si es el ltimo), en el offset 1 hay una palabra que indica el PID del programa propietario del
bloque, en el offset 3 otra palabra indica el tamao (como siempre, prrafos) del bloque, sin incluir este
prrafo del MCB. Los bytes que van del 5 al 7 estn reservados. Entre el 8 y el 15 se sita el nombre del
programa propietario, aunque esta informacin slo existe en los bloques de programa y con MS-DOS 4.0
posterior (tambin en DR-DOS 5.0/6.0, aunque este operativo es aparentemente un DOS 3.31). El nombre
acaba con un cero si tiene menos de 8 caracteres (en DR-DOS 5.0 acaba siempre con un cero, truncndose
el 8 carcter si lo haba; 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 ejecucin, normalmente el DOS libera su bloque de memoria y de
entorno. Sin embargo, los programas residentes permanecen con el bloque de memoria y de entorno en la
RAM del sistema, hasta que se les desinstale o se reinicialice el equipo. Los buenos programas residentes
suelen liberar el bloque de memoria del entorno antes de terminar, con objeto de economizar una memoria
que normalmente no usan (entre otras razones porque tiene un tamao variable e impredecible). Como
mnimo existen dos programas residentes en todo momento: el ncleo (kernel) del sistema operativo y el
COMMAND.COM, aunque los usuarios suelen aadir el KEYB y, en muchos casos, el PRINT, APPEND,
GRAPHICS, GRAFTABL, NLSFUNC, SHARE, etc.
Como todos los bloques de memoria estn ubicados unos tras otros, y adems se conoce el tamao
de los mismos, es factible hacer un programita que recorra la cadena de bloques de memoria hasta que se
encuentre uno cuyo byte de marca valga 5Ah (ltimo MCB), pudindose identificar los programas residentes
cargados y la memoria que emplean. La direccin del primer MCB era al principio un secreto de Microsoft,
aunque hoy casi todo el mundo sabe que las siguientes lneas:
147 LA GESTIN DE MEMORIA DEL DOS
MOV AH,52h
INT 21h
MOV AX,ES:[BX-2]
devuelven en AX la direccin del primer MCB de la cadena, utilizando la funcin indocumentada
52h del sistema operativo.
8.2.5. - Relacin entre bloque de programa y de entorno.
El siguiente esquema aclarar la relacin existente entre el bloque de programa y el de entorno. Los
valores numricos que figuran son arbitrarios (pero correctos).
Bloque del entorno
1DB7 Marca PID Tamao (reservados)
4Dh 316F 000B
1DB8 variable 1 00 variable 2 00 variable 3 00
... (ms variables terminadas en 0) ... ltima variable 00
00 0001 C:\UTIL\VARIOS\PROGRAMA.EXE 00
Bloque del programa
316E Marca PID Tamao (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.
Bsicamente existen cinco tipos de bloques de memoria: bloques de programa, de entorno, del
sistema, bloques de datos y bloques libres. Los dos primeros ya han sido ampliamente explicados. Los
bloques del sistema se corresponden con el kernel o ncleo del sistema operativo o los dispositivos
instalables; normalmente tienen su PID como 0008. En los nuevos sistemas operativos y en las mquinas
donde la cadena de bloques de memoria puede avanzar por encima de los 640 Kb, las zonas correspondientes
a RAM de vdeo 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 slo sea para saltarlos
de alguna manera. Los bloques libres tienen un PID 0000. El PID 0006 (slo aparece en DR-DOS) indica
que se trata de un bloque de memoria superior XMS.
Los bloques de datos aparecen en raras ocasiones, debido al uso de las funciones del sistema
operativo para localizar bloques de memoria. Cuando un programa se ejecuta, tiene asignada la mayor parte
de la memoria para s, pero es perfectamente factible que solicite al DOS una reduccin de la memoria
asignada (funcin 4Ah) y, con los Kb que haya liberado, puede volver a llamar al DOS para crear bloques
de memoria (funcin 48h) o destruirlos (con la funcin 49h).
A la hora de recorrer la cadena de bloques de memoria, si se sigue el siguiente orden de evaluacin
el 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 de
un bloque del sistema. Si el PID apunta al MCB+1, se trata de un bloque del programa (recurdese que el
MCB lo precede inmediatamente). Si el PID apunta a un PSP en cuyo offset 2Ch una palabra apunta al
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, por
eliminacin 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 autorrelocalizarse
en memoria machacando parte del PSP con objeto de economizar algunos bytes; despus un alto porcentaje
de los mismos se olvida de liberar el espacio de entorno, que para nada utilizan y que suele ocupar incluso
ms memoria que todo el PSP.
La manera de liberar el espacio de entorno antes de que un programa quede residente es la siguiente
(necesario DOS 3.0 como mnimo si se obtiene la direccin del PSP utilizando la funcin 62h):
MOV AH,62h
INT 21 ; obtener direccin del PSP en BX
MOV ES,BX
MOV ES,ES:[2Ch] ; direccin del espacio de entorno
MOV AH,49h ; funcin para liberar bloque
INT 21h ; bloque destruido
Alternativamente, se puede liberar directamente el bloque de memoria del entorno poniendo
directamente un 0 en su PID, aunque es menos elegante. Si ES apunta al PSP:
MOV AX,ES:[2Ch] ; direccin del espacio de entorno
DEC AX ; apuntar a su MCB
MOV ES,AX
MOV WORD PTR ES:[1],0 ; liberar bloque (PID=0)
8.2.8. - Peculiaridades del MS-DOS 4.0 y posteriores.
La informacin siguiente explica las particularidades de los bloques de memoria con MS-DOS 4.0
y posteriores; no es vlida para DR-DOS aunque algunos aspectos concretos puedan ser comunes. Desde el
MS-DOS 3.1, el primer bloque de memoria es un segmento de datos del sistema, que contiene los drivers
instalados 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" - extensin 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 cdigo y datos de las pilas del sistema)
"T" - INSTALL= (rea transitoria de este mandato)
offset 1: Palabra, indica dnde comienza el subsegmento (normalmente a continuacin)
offset 3: Palabra, indica el tamao del subsegmento (en prrafos)
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 que
viene inmediatamente a continuacin (prrafo siguiente) para recorrer los subsegmentos conectados. En el
DOS 5.0 y siguientes, los bloques propiedad del sistema tienen el nombre "SC" (System Code, cdigo del
149 LA GESTIN DE MEMORIA DEL DOS
sistema o reas de memoria superior excluidas) o bien "SD" (System Data, con controladores de dispositivo,
etc.). Desde la versin 5.0 del DOS, estos bloques "SD" contienen subbloques con las mismas caractersticas
que los del DOS 4.0.
Adicionalmente, el DOS 5.0 introdujo los bloques denominados UMB que recorren la memoria
superior, en las diferentes reas en que puede estar fragmentada. Acceder a estos bloques de control de
memoria es bastante complicado: el segmento donde empiezan est almacenado en el offset 1Fh de la tabla
de informacin sobre buffers de disco, cuya direccin inicial a su vez se obtiene en el puntero largo que
devuelve en ES:BX+12h la funcin indocumentada Get List of Lists (52h): normalmente el resultado es el
segmento 9FFFh. En general, es ms sencillo ignorar la memoria superior como una entidad independiente
y recorrer toda la memoria sin ms. Sin embargo, para poder acceder a los bloques de memoria superior stos
han de estar ligados a los de la memoria convencional: para conectarlos, si no lo estn, puede emplearse la
funcin, tradicionalmente indocumentada (aunque recientemente ha dejado de serlo) Get or Set Memory
Allocation Strategy (58h) del DOS: es conveniente preservarla antes y volver a restaurar esta informacin
despus de alterarla. En cualquier caso, el formato de los bloques de control UMB es el siguiente:
offset 0: Byte con valor 5Ah para el ltimo bloque y 4Dh en otro caso.
offset 1: Palabra con el PID.
offset 3: Palabra con el tamao del bloque en prrafos.
offset 8: 8 Bytes: "UMB" si es el primer bloque UMB y "SM" si es el ltimo.
8.2.9. - Cmo recorrer los bloques de memoria.
La organizacin de la memoria vara segn la versin del sistema operativo instalada. En lneas
generales, todo lo comentado hasta ahora -excepto lo del apartado anterior- es vlido para cualquier versin
del DOS. Sin embargo, en las mquinas que tienen memoria superior, las cosas pueden cambiar un poco en
esta zona de memoria: si tienen instalado algn gestor de memoria extrao, este rea puede estar desconectada
por completo de los primeros 640 Kb. Con DR-DOS el usuario puede utilizar el comando MEMMAX para
habilitar o inhibir el acceso a la memoria superior; desde el MS-DOS 5.0 existen funciones especficas del
sistema para estas tareas.
El programa de ejemplo listado ms abajo recorre toda la memoria sin adentrarse en las
particularidades de ningn sistema operativo. Tan slo se toma la molestia de intentar detectar si existe
memoria superior y, en ese caso, mostrar tambin su contenido. Este algoritmo puede no ensear todo lo que
podra ensear gracias a las ltimas versiones del DOS, pero s gran parte, y funciona en todas las versiones.
Para comprobar si existe memoria superior utiliza una tcnica muy sencilla: al alcanzar el ltimo bloque de
memoria, se comprueba si el siguiente empezara en el segmento 9FFFh en vez del A000h como cabra
esperar en una mquina de 640Kb (slo suelen tener memoria superior las mquinas que al menos tienen 640
Kb). Si esto es as no se considera que el bloque sea el ltimo y se prosigue con el siguiente, saltando la
barrera de los 640 Kb. En este caso, obviamente, los 16 bytes que faltan para completar los 640 Kb de
memoria son precisamente un MCB. Esta tcnica funciona slo a partir del MS-DOS 5.0; en DR-DOS 6.0,
si la memoria superior est inhibida con MEMMAX -U, no funciona (DR-DOS 6.0 se encarga de machacar
el ltimo MCB de la memoria convencional y no deja ni rastro) aunque s con MEMMAX +U. Tambin se
imprime el nombre de los programas, aunque en DOS 3.30 y versiones anteriores salga basura. Adems, el
PID de tipo 6 se interpreta como un bloque de memoria superior XMS -que se estudiar en el siguiente
apartado de este mismo captulo- bajo DR-DOS 6.0, imprimindose tambin el nombre.
La primera accin de MAPAMEM al ser ejecutado es rebajar la memoria que tiene asignada hasta
el mnimo necesario; por ello en el resultado figura ocupando slo 1440 bytes y teniendo tras de s un gran
bloque libre. Es conveniente que los programas rebajen al principio la memoria asignada con objeto de
facilitar el trabajo bajo ciertos entornos pseudo-multitarea soportados por el DOS; de hecho, es norma comn
en el cdigo generado por los compiladores realizar esta operacin al principio. Sin embargo, no todo el
mundo se preocupa de ello y, a fin de cuentas, tampoco es tan importante.
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 mquina
con memoria superior y bajo los dos sistemas operativos ms comunes (aunque en los ejemplos los espacios
de entorno han coincidido junto al bloque de programa, ello no siempre sucede as). Las diferentes
ocupaciones de memoria de los programas en ambos sistemas operativos se deben frecuentemente a que se
trata de versiones distintas:
DR-DOS 6.0 MS-DOS 5.0
MAPAMEM 2.2 MAPAMEM 2.2
- Informacin sobre la memoria del sistema. - Informacin sobre la memoria del sistema.
Tipo Ubicacin Tamao PID Propietario Tipo Ubicacin Tamao PID Propietario
-------- --------- ------- ----- --------------- -------- --------- ------- ----- ---------------
Sistema 0000-003F 1.024 Interrupciones Sistema 0000-003F 1.024 Interrupciones
Sistema 0040-004F 256 Datos del BIOS Sistema 0040-004F 256 Datos del BIOS
Sistema 0050-023C 7.888 Sistema Operat. Sistema 0050-0252 8.240 Sistema Operat.
Sistema 023E-02FD 3.072 0008 Sistema 0254-045F 8.384 0008
Programa 02FF-031E 512 02FF COMMAND Sistema 0461-0464 64 0008
Entorno 0320-033F 512 02FF COMMAND Programa 0466-050E 2.704 0466 COMMAND
Datos 0341-0358 384 02FF COMMAND Libre 0510-0513 64 0000 <Nadie>
Programa 035A-03EE 2.384 035A MATAGAME Entorno 0515-0544 768 0466 COMMAND
Entorno 03F0-0408 400 040A KEYRESET Entorno 0546-0567 544 0569 MAPAMEM
Programa 040A-041D 320 040A KEYRESET Programa 0569-05C2 1.440 0569 MAPAMEM
Entorno 041F-0437 400 0439 MAPAMEM Libre 05C4-9FFE 631.728 0000 <Nadie>
Programa 0439-0492 1.440 0439 MAPAMEM Sistema A000-D800 229.392 0008
Libre 0494-9FFE 636.592 0000 <Nadie> Sistema D802-E159 38.272 0008
Sistema A000-DEFF 258.048 0007 Libre E15B-E17F 592 0000 <Nadie>
Sistema DF01-E477 22.384 0008 Programa E181-E18D 208 E181 DOSVER
Sistema E479-E483 176 0008 Programa E18F-E23C 2.784 E18F NLSFUNC
Sistema E485-E48D 144 0008 Programa E23E-E3AF 5.920 E23E GRAPHICS
Sistema E48F-E591 4.144 0008 Programa E3B1-E533 6.192 E3B1 SHARE
Sistema E593-E7DA 9.344 0008 Programa E535-E637 4.144 E535 DOSKEY
Sistema E7DC-E806 688 0008 Programa E639-E7E2 6.816 E639 PRINT
Sistema E808-E810 144 0008 Programa E7E4-E840 1.488 E7E4 RCLOCK
Sistema E812-E81A 144 0008 Programa E842-E862 528 E842 DISKLED
Sistema E81C-E8DE 3.120 0008 Programa E864-ECF0 18.640 E864 DATAPLUS
Programa E8E0-EA51 5.920 E8E0 GRAPHICS Programa ECF2-ED59 1.664 ECF2 HBREAK
Programa EA53-EA60 224 EA53 CLICK Programa ED5B-ED7E 576 ED5B ANSIUP
Programa EA62-EA6E 208 EA62 DOSVER Programa ED80-ED8C 208 ED80 PATCHKEY
Programa EA70-EA7F 256 EA70 ALTDUP Programa ED8E-ED93 96 ED8E TDSK
Area XMS EA81-EA8F 240 0006 B1M92VAC Datos ED95-F6D4 37.888 ED8E TDSK
Programa EA91-EAC0 768 EA91 VSA Libre F6D6-F6FF 672 0000 <Nadie>
Area XMS EAC2-EB17 1.376 0006 RCLOCK
Area XMS EB19-EB30 384 0006 DISKLED
Programa EB32-EDB4 10.288 EB32 VWATCH
Area XMS EDB6-EEEC 4.976 0006 DATAPLUS
Area XMS EEEE-EF4F 1.568 0006 HBREAK
Libre EF51-EFFE 2.784 0000 <Nadie>
Sistema F000-F5FF 24.576 0007
Sistema F601-F6FF 4.080 0008
; ********************************************************************
; * *
; * MAPAMEM 2.2 - Utilidad para listar los bloques de memoria. *
; * *
; ********************************************************************
mapamem SEGMENT
ASSUME CS:mapamem; DS:mapamem
ORG 100h ; programa tipo COM
mapa PROC
MOV BX,tam_mapmem ; tamao de este programa
MOV AH,4Ah ; modificar memoria asignada
INT 21h ; ejecutar funcin del DOS
LEA DX,cabecera_txt
CALL print
MOV AH,52h ; funcin "Get List of Lists"
INT 21h
MOV AX,ES:[BX-2] ; segmento del primer M.C.B.
MOV ES,AX
DEC AX
CALL print16hex ; imprimir dnde acaba el DOS
INC AX
SUB AX,50h
MOV DX,16
MUL DX ; pasar prrafos a bytes
MOV CL,8+16
CALL print_32 ; imprimir tamao zona del DOS
LEA DX,cabx_txt
CALL print
otro_mcb: MOV BX,WORD PTR ES:[1] ; P.I.D. (Process ID)
MOV DL,0 ; supuesta zona libre (tipo DL)
CMP BX,0
JE tipo_ok ; lo es (PID = 0)
MOV DL,1 ; supuesto bloque XMS de DR-DOS
CMP BX,6
JE tipo_ok ; lo es (PID = 6)
MOV DL,2 ; supuesta zona del sistema
PUSH DS
MOV DS,BX
MOV AX,WORD PTR DS:[0] ; AX = [PID:0000]
MOV CX,WORD PTR DS:[2Ch] ; CX = [PID:002C]
POP DS
CMP AX,20CDh
JE no_tipo_sys ; es un PSP
CMP AX,27CDh
JNE tipo_ok ; no es un PSP
no_tipo_sys: MOV DL,3 ; supuesta zona de programa
MOV AX,ES
INC AX
CMP BX,AX ; PID=MCB+1?
JE tipo_ok ; lo es
MOV DL,4 ; supuesta zona de entorno
CMP CX,AX
JE tipo_ok
INC DL ; por eliminacin zona de datos
tipo_ok: MOV pid,BX
MOV tipo,DL
CALL imprime_tipo ; tipo del bloque
CALL imprime_rango ; ubicacin y tamao
CALL imprime_pid
CALL imprime_nombre
MOV AL,13 ; retorno de carro
CALL printAL
MOV AL,10 ; salto de lnea
CALL printAL
MOV AX,ES ; MCB ya tratado
ADD AX,ES:[3] ; tamao del bloque
INC AX ; apuntar al siguiente MCB
CMP BYTE PTR ES:[0],5Ah ; es el ltimo?
MOV ES,AX ; puntero al siguiente MCB
JNE otro_mcb ; no, no era el ltimo
PUSH AX
INT 12h
MOV BX,64
MUL BX
DEC AX
MOV BX,AX
POP AX
CMP AX,BX ; hay RAM superior (DOS 5)?
JE otro_mcb ; as es
MOV AX,4C00h
INT 21h ; fin del programa
mapa ENDP
imprime_tipo PROC
LEA SI,tabla_tipos
MOV AL,tipo
151 LA GESTIN DE MEMORIA DEL DOS
XOR AH,AH
SHL AX,1 ; AX = tipo * 2
ADD SI,AX
MOV DX,[SI] ; direccin del mensaje
CALL print ; imprimirlo
RET
imprime_tipo ENDP
imprime_rango PROC
MOV AX,ES
INC AX
CALL print16hex ; imprimir inicio del bloque
MOV AL,-
CALL printAL ; imprimir guin
MOV AX,ES
ADD AX,ES:[3]
CALL print16hex ; imprimir final del bloque
MOV AX,ES:[3]
MOV DX,16
MUL DX ; pasar bytes a prrafos
MOV CL,8+16
CALL print_32 ; imprimir tamao del bloque
RET
imprime_rango ENDP
imprime_pid PROC
MOV AL,
CALL printAL
CALL printAL
MOV AX,pid
CALL print16hex
MOV AL,
CALL printAL
CALL printAL
RET
imprime_pid ENDP
imprime_nombre PROC
PUSH ES
LEA DX,libre_txt
CMP tipo,0 ; bloque libre?
JNE no_libre ; no
CALL print ; imprimirlo
JMP nombre_ok
no_libre: CMP tipo,1
JE nombre_listo ; bloque XMS: nombre de ES:8 a ES:16
CMP tipo,2
JE nombre_ok ; nombre del propietario desconocido
MOV BX,ES:[1] ; segmento del PSP dueo del bloque
DEC BX ; apuntar al MCB
MOV ES,BX
nombre_listo: MOV BX,7 ; nombre de ES:BX+1 a ES:BX+9
MOV CX,8 ; mximo tamao del nombre
otra_letra: INC BX
MOV AL,ES:[BX] ; carcter del nombre
AND AL,AL
JZ nombre_ok ; es cero: fin del nombre
CMP AL,
JAE cod_normal
MOV AL,? ; evitar cdigos raros en DOS < 4.0
cod_normal: CALL printAL ; imprimirlo
LOOPNZ otra_letra ; a por otro (8 como mximo)
nombre_ok: POP ES
RET
imprime_nombre ENDP
print PROC ; imprimir cadena en DS:DX con
PUSH AX ; el final delimitado por un $
PUSH CX
MOV AH,9
INT 21h
POP CX
POP AX
RET
print ENDP
printAL PROC ; imprimir carcter en AL
PUSH AX
PUSH DX ; registros usados preservados
MOV AH,2 ; funcin de impresin del DOS
MOV DL,AL ; carcter a imprimir
INT 21h ; llamar al sistema
POP DX
POP AX ; recuperar registros
RET ; retornar
printAL ENDP
print4hex PROC ; imprimir carcter hexadecimal (AL)
PUSH AX ; preservar AX
ADD AL,0 ; pasar binario a ASCII
CMP AL,9
JBE no_sup9 ; no es letra
ADD AL,A-9-1 ; lo es
no_sup9: CALL printAL ; imprimir dgito hexadecimal
POP AX ; restaurar AX
RET
print4hex ENDP
print8hex PROC ; imprimir byte hexadecimal en AL
PUSH CX
PUSH AX
MOV CL,4
SHR AL,CL ; pasar bits 4..7 a 0..3
CALL print4hex ; imprimir nibble ms significativo
POP AX ; restaurar AL
PUSH AX ; y preservarlo de nuevo
AND AL,1111b ; dejar nibble menos significativo
CALL print4hex ; e imprimirlo
POP AX
POP CX
RET
print8hex ENDP
print16hex PROC ; imprimir palabra hexadecimal (AX)
PUSH AX
MOV AL,AH
CALL print8hex ; imprimir parte alta
POP AX
CALL print8hex ; imprimir parte baja
RET
print16hex ENDP
; -------------------------- PRINT-32 v3.1 --------------------------
;
; Subrutina para imprimir n decimal de 32 bits en DXAX formateado.
;
; No requiere ningn registro de segmento apuntndola; se apoya en
; la rutina print para imprimir la cadena DS:DX delimitada por $.
;
; Entradas:
; Si bit 4 = 1 --> se imprimirn signos separadores de millar
; bits 0-3 = n total de dgitos (incluyendo separadores de
; millar y parte fraccional)
; bits 5-7 = n de dgitos de la parte fraccional (cuantos
; dgitos de DXAX, empezando por la derecha, se
; consideran parte fraccional, e irn precedidos
; del correspondiente separador)
;
; Salidas:
; n impreso, ningn 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 nmero a imprimir. Si se especifican, por ej., pocos dgitos en
; la parte entera (=demasiados en la fraccional) no tiene sentido
; imprimir el separador de millares. Si se intenta, la rutina podra
; colgarse porque no valida el formato.
print_32 PROC
PUSHF
PUSH AX ; preservar registros
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
MOV BX,CS
MOV DS,BX
MOV ES,BX
MOV formato_pr32,CL ; byte del formato de impresin
MOV BX,OFFSET tabla_pr32
MOV CX,10
digit_pr32: PUSH CX
PUSH AX
PUSH DX
XOR DI,DI
MOV SI,1 ; DISI = 1
DEC CX ; CX - 1
JCXZ hecho_pr32
factor_pr32: SAL SI,1
RCL DI,1 ; DISI * 2
MOV DX,DI
MOV AX,SI
SAL SI,1
RCL DI,1
SAL SI,1
RCL DI,1 ; DISI * 8
ADD SI,AX
ADC DI,DX ; DISI=DISI*8+DISI*2=DISI*10
LOOP factor_pr32 ; DISI=DISI*(10^(CX-1))
hecho_pr32: POP DX
POP AX ; CX se recuperar ms tarde
MOV CL,0FFh
rep_sub_pr32: INC CL
SUB AX,SI
SBB DX,DI ; DXAX = DXAX - DISI
JNC rep_sub_pr32 ; restar factor cuanto se pueda
ADD AX,SI ; subsanar el desbordamiento:
ADC DX,DI ; DXAX = DXAX + DISI
ADD CL,0 ; pasar binario a ASCII
MOV [BX],CL
POP CX ; CX se recupera ahora
INC BX
LOOP digit_pr32 ; prximo dgito del nmero
STD ; transferencias hacia atrs
DEC BX ; BX apunta al ltimo dgito
MOV final_pr32,BX ; ltimo dgito
MOV ent_frac_pr32,BX ; frontera parte entera/fracc.
MOV CL,5
MOV AL,formato_pr32
SHR AL,CL ; AL = n de decimales
AND AL,AL
JZ no_frac_pr32 ; ninguno
MOV CL,AL
XOR CH,CH
MOV SI,final_pr32
MOV DI,SI
INC DI
REP MOVSB ; cadena arriba (hacer hueco)
INC final_pr32
MOV AL,fracc_pr32
MOV [DI],AL ; separador de parte fraccional
MOV ent_frac_pr32,SI ; indicar nueva frontera
no_frac_pr32: MOV AL,formato_pr32
TEST AL,16 ; interpretar el formato
JZ poner_pr32 ; imprimir como tal
entera_pr32: MOV CX,final_pr32 ; aadir separadores de millar
SUB CX,ent_frac_pr32
ADD CX,3
MOV SI,final_pr32
MOV DI,SI
INC DI
REP MOVSB ; cadena arriba (hacer hueco)
MOV AL,millares_pr32
MOV [DI],AL ; poner separador de millares
INC final_pr32
MOV ent_frac_pr32,SI ; usar la variable como puntero
SUB SI,OFFSET tabla_pr32
CMP SI,3
JAE entera_pr32 ; prximo separador
poner_pr32: MOV BX,final_pr32
MOV BYTE PTR [BX+1],"$" ; delimitador fin de cadena
MOV BX,OFFSET tabla_pr32
MOV 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 izda
JE blanco_pr32
CMP AL,fracc_pr32
JNE acabar_pr32
MOV BYTE PTR [BX-1],0 ; reponer 0 antes de la coma
DEC principio_pr32
acabar_pr32: MOV AL,formato_pr32 ; imprimir
AND AL,00001111b
XOR AH,AH
MOV DX,final_pr32
SUB DX,AX
INC DX ; DX = offset principio
AND AX,AX
JNZ format_pr32 ; longitud solicitada
MOV DX,principio_pr32 ; longitud obtenida del nmero
152 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
format_pr32: CALL print ; imprimir cadena en DS:DX
POP ES
POP DS ; restaurar todos los registros
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POPF
RET ; salida del procedimiento
blanco_pr32: MOV BYTE PTR [BX], ; quitar 0 / separador millares
INC BX ; sustituyendo por espacios
INC principio_pr32
CMP BX,final_pr32
JB limpiar_pr32
MOV DX,BX ; es el nmero 0.000.000.00X
JMP SHORT acabar_pr32 ; imprimir
formato_pr32 DB 0
DB 5 DUP ( ) ; rea de trabajo
tabla_pr32 DT 0
DW 0,0
millares_pr32 EQU . ; separador de millares
fracc_pr32 EQU , ; " parte fraccional
final_pr32 DW 0 ; offset ltimo byte a imprimir
principio_pr32 DW 0 ; " " primer " " "
ent_frac_pr32 DW 0 ; offset frontera entero-fracc.
print_32 ENDP
; ------------ Datos
cabecera_txt LABEL BYTE
DB 13,10,"MAPAMEM 2.2"
DB 13,10," - Informacin sobre la memoria del sistema.",13,10,10
DB "Tipo Ubicacin Tamao PID Propietario",13,10
DB "-------- --------- ------- ----- ---------------"
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_sistema
DW 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 0
pid DW 0
tam_mapmem EQU ($-OFFSET mapamem)/16+1 ; tamao de MAPAMEM
mapamem ENDS
END mapa
8.3. - MEMORIAS EXTENDIDA Y SUPERIOR XMS.
El controlador XMS implementa una serie de funciones para acceder de manera sencilla a la memoria
extendida. En principio, hay funciones para asignar y liberar el HMA (frecuentemente ya estar ocupado por
el sistema operativo), para controlar la lnea 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 la
direccin fsica para quien desee realizar transferencias directas y lo ms interesante: para mover bloques,
bien sea entre zonas de la memoria extendida o entre la memoria convencional y la extendida, de la manera
ms ptima y rpida segn el tipo de CPU que se trate. Digamos que la memoria extendida XMS es como
un gran banco o almacn de memoria torpe, del que podemos traer o llevar datos y nada ms.
Adicionalmente, el controlador XMS aade funciones para gestionar la memoria superior. Los bloques
de memoria superior no son accesibles de manera directa por los programas, a menos que stos sean
expresamente cargados en este rea con HILOAD LOADHIGH. Sin embargo, los programas pueden
solicitar zonas de memoria superior al controlador XMS, que adems de la memoria extendida gestiona
tambin estas reas. Estos bloques de memoria son gestionados de manera independiente a los de la memoria
convencional, existiendo funciones especficas del controlador XMS para localizar y liberar los bloques. Con
DR-DOS 6.0 y algunos gestores de memoria, en la memoria superior pueden residir tanto bloques de memoria
DOS gestionados por el sistema (normalmente, como consecuencia de un HILOAD para instalar programas
residentes), as como autnticos bloques de memoria XMS. Realmente, las zonas que emplea el DR-DOS no
son sino bloques de este tipo de memoria.
El MS-DOS 5.0 y posteriores, sin embargo, reservan toda la memoria superior para sus propios usos
-cargar programas residentes- cuando se indica DOS=UMB en el CONFIG.SYS; por lo que si alguna
aplicacin solicita memoria superior XMS no la encontrar. Pero se puede emplear la funcin 58h para
conectar la memoria superior y a continuacin, con la misma funcin, cambiar la estrategia de asignacin de
memoria para que el sistema asigne memoria superior en respuesta a las funciones ordinarias de asignacin
de memoria. Despus es conveniente restaurar la estrategia de asignacin y el estado de la memoria superior
a la situacin inicial (tambin se puede consultar previamente con la funcin 58h).
La hecho de que un programa pueda solicitar memoria superior al sistema es una posibilidad
interesante: ello permite a los programas residentes auto-relocalizarse de una manera sencilla a estas zonas,
anticipndose a la actuacin de usuarios inexpertos que podran olvidarse del HILOAD o el LOADHIGH.
Por otra parte, se economiza algo de memoria al poder suprimirse el PSP en la copia. Con MS-DOS 5.0 y
posteriores, no obstante, el programa deber dejar algo residente en memoria convencional (si no se termina
residente, el sistema libera los bloques asignados en memoria superior) o bien modificar el PID de los bloques
en memoria superior para que al terminar sin quedar residente el DOS no los libere.
153 LA GESTIN DE MEMORIA DEL DOS
Para poder emplear los servicios del controlador XMS hay que verificar primero que est instalado
el programa HIMEM.SYS o alguno equivalente (el EMM386 del DR-DOS 6.0 integra tambin las funciones
del HIMEM.SYS, as como el QEMM386). Para ello se chequea la entrada 43h en la interrupcin Multiplex,
comprobando si devuelve 80h en el registro AL (y no 0FFh como otros programas residentes):
MOV AX,352Fh ; obtener vector de INT 2Fh en ES:BX
INT 21h
MOV AX,ES
CMP AX,0
JE no_hay_XMS ; en DOS 2.x la INT 2Fh est indefinida
MOV AX,4300h ; chequear presencia de XMS
INT 2Fh ; interrupcin Multiplex
CMP AL,80h
JE hay_XMS
JNE no_hay_XMS
Antes de llamar a la INT 2Fh se comprueba que esta interrupcin est apuntando a algn sitio (con
el segmento distinto de 0) ya que en algunas versiones 2.x del DOS est sin inicializar y el sistema se cuelga
si se invoca sin precauciones. Las funciones del controlador XMS no se invocan por medio de ninguna
interrupcin, como sucede con las del DOS o la BIOS. En su lugar, una vez detectada la presencia del mismo
se le debe interrogar preguntndole dnde est instalado, por medio de la subfuncin 10h:
MOV AX,4310h ; preguntar direccin del controlador
INT 2Fh
MOV XMS_seg,ES ; almacenarla
MOV XMS_off,BX
donde XMS_seg y XMS_off es una estructura del tipo:
gestor_XMS LABEL DWORD
XMS_off DW 0
XMS_seg DW 0
Posteriormente, cuando haya que utilizar un servicio o funcin del controlador XMS se colocar el
nmero del mismo en AH y se ejecutar un CALL gestor_XMS. Para utilizar las llamadas al XMS es
preciso que en la pila queden al menos 256 bytes libres. En un apndice al final del libro se listan y
documentan todas las funciones XMS.
Si por cualquier motivo fuera necesario en un programa residente interceptar las llamadas al
controlador XMS realizadas por los programas de aplicacin, hay que decir que ello es posible. Por supuesto,
no es tan sencillo como desviar un vector de interrupcin: hay que modificar el cdigo del propio controlador.
Por fortuna, todos los controladores XMS suelen comenzar con una instruccin de salto larga o corta (JMP
XXXX:XXXX, JMP XXXX, JMP SHORT XX) y, si sta ocupa menos de 5 bytes, los restantes estn
cubiertos de instrucciones NOP (cdigo de operacin 90h). Se pueden modificar los primeros bytes del mismo
para poner un salto hacia nuestra propia rutina, que luego acabe llamando a su vez al controlador previo (el
RAMDRIVE de Microsoft, por ejemplo, realiza esta complicada maniobra).
8.4.- MEMORIA EXPANDIDA EMS.
La memoria expandida, como se coment al principio del captulo, es una tcnica de paginacin para
solventar la limitacin de 640 Kb de memoria de los PC. Hasta la versin 3 del controlador de memoria
expandida, esta extensin consiste en un segmento de memoria de 64 Kb (en la direccin 0D0000h o
0E0000h, a veces otras como 0C8000h, etc.) dividido en cuatro pginas adyacentes de 16 Kb. Ese segmento
se denomina marco de pgina de la memoria expandida. Las cuatro pginas son las pginas fsicas
numeradas entre 0 y 3. Cuando un programa solicita memoria expandida, se le asigna un handle de control
(un nmero de 16 bits) que la referencia, as como cierto nmero de pginas lgicas asociado al mismo. A
partir de ese momento, cualquier pgina lgica puede ser mapeada sobre una de las cuatro pginas fsicas.
De este modo, es factible acceder simultneamente a cuatro pginas lgicas entre todas las disponibles. Por
154 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
ello es posible incluso asignar la misma pgina lgica a ms de una pgina fsica, aunque es un tanto absurdo.
La principal utilidad de la memoria expandida es de cara a almacenar grandes estructuras de datos evitando
en lo posible un acceso a disco. La memoria expandida se implementa con una extensin del hardware,
aunque algunos equipos 286 ya la tienen integrada en la placa base. En los 386 y superiores, la CPU puede
ser colocada en modo virtual 86, una variante del modo protegido en la que la memoria expandida puede ser
emulada por las tcnicas de memoria virtual de este microprocesador, sin necesidad de una extensin
hardware. Algunos sistemas de memoria expandida real (no emulada) pueden soportar incluso una
reinicializacin del PC sin perder el contenido de esa memoria.
DFFFF
16 Kb 3
DC000 A
2 B
D8000 C
1 D
D4000 E
0 F
D0000 G
MARCO DE PGINA DE MEMORIA EXPANDIDA PGINAS DE MEMORIA EXPANDIDA ASIGNABLES
(PGINAS FSICAS) (PGINAS LGICAS)
En este ejemplo se ha solicitado al EMM 8 pginas (numeradas en el
grfico A-G) y cualquiera de ellas puede ser colocada (paginada)
en cualquiera de las 4 pginas fsicas, a elegir.
Para utilizar la memoria expandida hay que invocar la interrupcin 67h. Para detectar la presencia
del controlador hay dos mtodos. El primero consiste en buscar un dispositivo "EMMXXXX0", ya que el
gestor de memoria expandida se carga desde el CONFIG.SYS y define un controlador de dispositivo de
caracteres con ese nombre. Es tan sencillo como intentar abrir un fichero con ese nombre y comprobar si
existe. Desde la lnea de comandos del DOS se puede hacer as:
IF EXIST EMMXXXX0 ECHO HAY CONTROLADOR EMS
Existe el riesgo de que en lugar de un controlador con ese nombre se trate de un fichero que algn
gracioso haya creado!: para cerciorarse, hay unas funciones de control IOCTL en el DOS para asegurar que
se trata de un dispositivo y no de un fichero. Sin embargo, no es recomendable este mtodo para detectar el
EMM en los programas residentes y en los controladores de dispositivo: existe otro medio ms conveniente
para esos casos, que tambin puede ser empleado de manera general en cualquier otra aplicacin. Consiste
en buscar la cadena "EMMXXXX0" en el offset 10 del segmento apuntado por el vector 67h (despreciando
el offset de dicho vector) as de sencillo!.
Las funciones del EMM se invocan colocando en AH el nmero de funcin y ejecutando la INT 67h:
a la vuelta, AH normalmente valdr 0 para indicar que todo ha ido bien. En un apndice al final del libro
se listan y documentan todas las funciones EMS. Estas funciones se numeran a partir de 40h, aunque desde
la 4Fh slo estn disponibles a partir de la versin 4.0 del controlador, si bien en muchos casos no son
necesarias. 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 pgina (no tiene por qu se 0D000h ni 0E000h).
42h - Preguntar el nmero de pginas libres que an no estn asignadas.
43h - Asignar pginas (esta funcin devuelve un handle de control, igual que cuando se abre un fichero).
44h - Mapear pginas (colocar una cierta pgina lgica 0..N en una de las fsicas 0..3).
45h - Liberar las pginas asignadas, para que puedan usarlas futuros programas (es vital!).
46h - Preguntar la versin del controlador de memoria expandida.
47h - Salvar el contexto del mapa de pginas (usado por los TSR para no alterar el marco de pgina).
48h - Restaurar el contexto del mapa de pginas (usado por los TSR para no alterar el marco de pgina).
4Dh - Obtener informacin de todos los handles que hay y las pginas que tienen asignadas.
155 LA GESTIN DE MEMORIA DEL DOS
La memoria expandida, lejos de ser slo un invento obsoleto para superar los 640K en los viejos
ordenadores, es una de las memorias ms verstiles disponibles bajo DOS. Muchos programas pueden ver
incrementado notablemente el rendimiento si se desarrollan empleando esta memoria en lugar de la XMS.
La razn es que, con la memoria extendida, hay que traerla (copiarla) a la memoria convencional, procesarla
y volverla a copiar a la memoria extendida. Sin embargo, con la memoria expandida EMS, una rapidsima
funcin coloca en el espacio de direcciones del 8086 la memoria que va a ser accedida: all mismo puede ser
procesada sin necesidad de movimiento fsico. Esto es debido a que la conmutacin pginas de memoria
expandida se hace, dicho entre comillas, seleccionando el chip de RAM que se utiliza, sin existir movimiento
fsico de datos. En algunos casos, sin embargo, la EMS no aumenta el rendimiento: por ejemplo, al construir
un disco virtual, habr que transferir datos desde la memoria convencional a la XMS la EMS; en cualquier
caso se va a producir un movimiento fsico (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.0
del EMM (Expanded Memory Manager) cubre un amplio espectro del espacio de direcciones dentro del
megabyte gestionado por el MS-DOS. Aqu, las pginas no han de ser necesariamente consecutivas; son ms
de 4 y tampoco tienen que ser necesariamente de 16 Kb. Sin embargo, por defecto -y por razones de
compatibilidad- las cuatro primeras pginas fsicas estn colocadas adyacentemente por encima de los 640K
y son de 16 Kb, no siendo recomendable modificar esta especificacin. Por ejemplo, en el sistema 386 en
que se escribieron las primeras versiones de este libro, con un EMM 4.0, las pginas fsicas 0 a la 3 estaban
ubicadas a partir de la direccin 0C8000h; las pginas 4 a la 27h estaban ubicadas entre la direccin 10000h
a la 9FFFFh, cubriendo tambin los primeros 640 Kb (excepto los primeros 64 Kb).
Si alguien est pensando en desviar la interrupcin 67h desde un programa residente, para interceptar
y manipular las llamadas de los programas de aplicacin a esa interrupcin, ya puede ir olvidndose. La razn
es que los 386 y superiores estn en modo virtual 86 con los controladores EMS instalados. Esto significa
que cuando un programa invoca una interrupcin, como la INT 67h, la CPU -de la manera que est
programada- pasa inmediatamente a continuacin a ejecutar una rutina en modo protegido fuera del espacio
de direcciones del MS-DOS. Con algunos gestores de memoria, como el EMM386 del DR-DOS 6.0, no
sucede nada: ese programa supervisor retorna a la tarea virtual y ejecuta el cdigo ubicado en el espacio de
direcciones del MS-DOS. Sin embargo, con QEMM386, el controlador de memoria est ubicado fuera de ese
espacio de direcciones, y ya no vuelve a l. Si se mira con el DEBUG a donde apunta la INT 67h en una
mquina con QEMM (por ejemplo, traceando una llamada a la interrupcin), se ver que este vector apunta
al siguiente cdigo:
INT 28h
IRET
Evidentemente, ese no es el controlador de memoria!. Para acceder a l hay que ejecutar una
interrupcin de verdad. Supongo que a travs de la especificacin VCPI (Virtual Control Program Interface)
que regula el acceso a los modos extendidos del 386, habr algn medio de poder acceder al cdigo del
controlador EMS, o interceptar las llamadas. Sin embargo, no es tan fcil como cambiar un vector...
157 SUBPROCESOS, RECUBRIMIENTOS Y FILTROS
Captulo IX: SUBPROCESOS, RECUBRIMIENTOS Y FILTROS
9.1. - LLAMADA A SUBPROCESOS Y RECUBRIMIENTOS U OVERLAYS.
La funcin EXEC del DOS (4Bh) es el pilar que sustenta la ejecucin de programas desde dentro de
otros programas, as como la carga de subrutinas de un mismo programa desde disco (overlays). Si no
existiera la funcin EXEC, el proceso sera arduo: habra que reservar memoria, cargar el fichero ejecutable
en memoria, relocalizarlo si es de tipo EXE, crear su PSP y dems reas de datos (entorno, etc)... por fortuna,
la funcin EXEC se ocupa de todo ello. Adems, esta funcin posee una caracterstica no documentada hasta
el DOS 5.0 (s ha sido documentada desde dicha versin), que es la posibilidad de cargar un programa sin
ejecutarlo, lo cual puede ser interesante de cara a la creacin de depuradores de cdigo.
Para llamar a la funcin EXEC para cargar y ejecutar un programa se pone un 0 en AL. Hay que
apuntar DS:DX a la direccin del nombre del programa (una cadena ASCIIZ, esto es, terminada por cero)
que puede incluir la ruta de directorios y debe incluir la extensin. Tambin hay que apuntar en ES:BX a una
estructura de datos (bloque de parmetros) 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 el
del programa padre. Los programas hijos siempre accedern a una copia y no al original.
offset 2: Doble palabra que apunta a los parmetros del programa a ejecutar (los que ese programa
admite, por s solo, en la lnea de comandos). Tiene el mismo formato que el contenido de PSP:80h.
offset 6: Doble palabra que apunta al primer FCB a copiar en el proceso hijo.
offset 10: Doble palabra que apunta al segundo FCB a copiar en el proceso hijo.
offset 14: Si se carga sin ejecutar, devuelve el SS:SP inicial del subprograma.
offset 18: Si se carga sin ejecutar, devuelve el CS:IP inicial del subprograma.
El subprograma cargado hereda los ficheros abiertos del programa padre. Antes de llamar a esta
funcin, el ordenador debe tener suficiente memoria libre. Cuando se ejecuta un programa COM ordinario,
toda la memoria del sistema est asignada al mismo (el mayor bloque en realidad, lo que en la prctica
significa toda la memoria). Por tanto, un programa COM que desee cargar otros programas debe primero
rebajar la memoria que el DOS le ha asignado y quedarse slo con la que necesita. Con los programas EXE,
la cantidad de memoria que les asigna el DOS inicialmente depende del compilador y las opciones de
compilacin; en ensamblador suele ser tambin toda la memoria, por lo que es deber de ste liberar la que
no necesita. Para ello, se calcula cuanta memoria necesita el programa y se llama a la funcin del sistema
para modificar el tamao del bloque de memoria del propio programa (funcin 4Ah del DOS, pasando en
ES la direccin del PSP).
En los programas COM, la pila est apuntando al final del segmento (SP est prximo a 0FFFEh).
Por ello, si el programa va a ocupar menos de 64 Kb, ser preciso mover SP ms abajo para que no se salga
del futuro bloque de memoria del programa. Si no se toma esta precaucin, SP apuntar dentro del siguiente
bloque de memoria, que es ms que probablemente el que utilizar EXEC, con lo que el ordenador debera
colgarse a no ser que haya mucha suerte.
Tras llamar a la funcin EXEC, en teora todos los registros son destruidos, segn la documentacin
oficial, incluidos SS:SP. Esto significa que antes de llamar a EXEC deben apilarse los registros que no se
desee alterar y guardar en un par de variables SS y SP. Tras llamar a EXEC, inmediatamente a continuacin
y antes de hacer nada se deben recargar SS y SP, para proceder despus a recuperar de la pila los dems
158 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
registros. Este comportamiento de EXEC parece romper la tnica habitual de comportamiento del DOS. Sin
embargo, lo cierto es que esto slo suceda en el DOS 2.X: aunque Microsoft no lo diga oficialmente, las
versiones posteriores del sistema slo corrompen DX y BX al llamar a EXEC.
El siguiente programa de ejemplo, de tipo COM, realiza todas las tareas necesarias para cargar otro
programa. Como ejemplo, he decidido cargar el COMMAND.COM, aunque el programa a ejecutar podra
ser cualquier otro; la ventaja de COMMAND es que crea una nueva sesin de intrprete de comandos y
permite comprobar con comodidad qu ha sucedido con la memoria.
; ********************************************************************
; * *
; * SHELL.ASM 1.0 - Demostracin de carga de subprograma. *
; * *
; ********************************************************************
TAMTOT EQU 1024 ; este programa y su pila caben en 1 Kb.
shell SEGMENT
ASSUME CS:shell, DS:shell
ORG 100h
inicio:
MOV SP,TAMTOT ; redefinir la pila
MOV AH,4Ah
MOV BX,TAMTOT/16
INT 21h ; redimensionar bloque memoria
LEA DX,hola_txt
MOV AH,9
INT 21h ; mensaje de bienvenida
LEA BX,exec_info
MOV WORD PTR [BX],0
MOV WORD PTR [BX+2],80h ; PSP
MOV WORD PTR [BX+4],CS
MOV WORD PTR [BX+6],5Ch ; FCB 0
MOV WORD PTR [BX+8],CS
MOV WORD PTR [BX+0Ah],6Ch ; FCB 1
MOV WORD PTR [BX+0Ch],CS
LEA DX,nombre
MOV AX,4B00h
INT 21h ; cargar y ejecutar programa
PUSH CS
POP DS ; DS = CS
LEA DX,adios_txt
MOV AH,9
INT 21h ; mensaje de despedida
MOV AX,4C00h
INT 21h ; terminar
nombre DB "C:\DOS\COMMAND.COM",0 ; programa a ejecutar
exec_info DB 22 DUP (0)
hola_txt DB 13,10
DB "Ests dentro de SHELL.COM ...",13,10,"$"
adios_txt DB 13,10
DB "... Acabas de abandonar SHELL.COM",13,10,"$"
shell ENDS
END inicio
Al ejecutar el programa anterior, y suponiendo que el ordenador tenga el COMMAND.COM en
C:\DOS (es ms cmodo que andar buscando la variable de entorno COMSPEC), se puede generar una sesin
de trabajo como la que se muestra a continuacin, en la que la utilidad MAPAMEM permite verificar la
estructura de la memoria tras la ejecucin de SHELL.COM:
C:\COMPILER\86\AREA>shell
Ests dentro de SHELL.COM ...
Microsoft(R) MS-DOS(R) Versin 5.00
(C)Copyright Microsoft Corp 1981-1991.
C:\COMPILER\86\AREA>mapamem
MAPAMEM 2.2
- Informacin sobre la memoria del sistema.
Tipo Ubicacin Tamao PID Propietario
-------- --------- ------- ----- ---------------
Sistema 0000-003F 1.024 Interrupciones
Sistema 0040-004F 256 Datos del BIOS
Sistema 0050-0B59 45.216 Sistema Operat.
Sistema 0B5B-0CF1 6.512 0008
Programa 0CF3-0E1C 4.768 0CF3 COMMAND
Libre 0E1E-0E21 64 0000 <Nadie>
Entorno 0E23-0E52 768 0CF3 COMMAND
Entorno 0E54-0E6D 416 0E6F SHELL
Programa 0E6F-0EAE 1.024 0E6F SHELL
Datos 0EB0-0EC8 400 0ECA COMMAND
Programa 0ECA-0F72 2.704 0ECA COMMAND
Entorno 0F74-0F8B 384 0ECA COMMAND
Entorno 0F8D-0FA5 400 0FA7 MAPAMEM
Programa 0FA7-0FFA 1.344 0FA7 MAPAMEM
Libre 0FFC-9FFE 589.872 0000 <Nadie>
Sistema A000-D800 229.392 0008
Sistema D802-E159 38.272 0008
Libre E15B-E179 496 0000 <Nadie>
Programa E17B-E187 208 E17B DOSVER
Programa E189-E5B7 17.136 E189 BUFFERS
Programa E5B9-E617 1.520 E5B9 FILES
Programa E619-E663 1.200 E619 LASTDRIV
Programa E665-E712 2.784 E665 NLSFUNC
Programa E714-E885 5.920 E714 GRAPHICS
Programa E887-EA09 6.192 E887 SHARE
Programa EA0B-EB0D 4.144 EA0B DOSKEY
Programa EB0F-ECB8 6.816 EB0F PRINT
Programa ECBA-ED17 1.504 ECBA RCLOCK
Programa ED19-ED39 528 ED19 DISKLED
Programa ED3B-F1C7 18.640 ED3B DATAPLUS
Programa F1C9-F230 1.664 F1C9 HBREAK
Programa F232-F255 576 F232 ANSIUP
Programa F257-F25C 96 F257 TDSK
Datos F25E-F65D 16.384 F257 TDSK
Libre F65F-F6FF 2.576 0000 <Nadie>
C:\COMPILER\86\AREA>exit
... Acabas de abandonar SHELL.COM
C:\COMPILER\86\AREA>_
159 SUBPROCESOS, RECUBRIMIENTOS Y FILTROS
La subfuncin EXEC para cargar un programa sin ejecutarlo se selecciona con AL=1; ES:BX apunta
al bloque de parmetros que se defini para el caso normal de carga+ejecucin. Esta subfuncin asigna el
PID, no obstante, al PSP del subprograma cargado.
La subfuncin de EXEC para cargar un overlay o recubrimiento, se llama con los mismos valores
en los registros que la anterior, exceptuando AL (que ahora vale 3). Sin embargo el bloque de parmetros
apuntado por ES:BX es ahora mucho ms sencillo:
Offset 0: Segmento donde cargar el overlay (la memoria ha de asignarla el programa principal).
Offset 2: Factor de reubicacin, si se trata de un fichero EXE (normalmente el mismo valor que el
anterior, si el subprograma va a correr en el mismo segmento en que es cargado).
El overlay puede haber sido ensamblado, por ejemplo, con un desplazamiento relativo nulo (ORG
0) de manera que para llamarlo hay que hacer un CALL FAR al segmento donde ha sido cargado, con un
offset 0. Claro que tambin se puede calcular la distancia que hay entre el segmento del programa principal
y el del overlay, multiplicarlo por 16 y utilizarlo como offset en la llamada al mismo segmento del programa
principal. Sin embargo, esto requiere que el overlay sea ensamblado con cierto offset ... a calcular. Quienes
proponen este segundo mtodo -que los hay- andaban ese da ms bien despistados. En general, la
programacin con overlays es compleja, y ms an si los overlays constan de varios segmentos internos.
Para conocer si la funcin EXEC se ha realizado correctamente o ha fracasado, se puede utilizar la
funcin 4Dh del DOS (Obtener cdigo de retorno), que devuelve en AH: 0 (terminacin normal), 1
(programa abortado por Ctrl-Break), 2 (terminacin por error crtico) 3 (terminacin residente). Al llamar
a la funcin 4Dh, se borra la informacin que devuelve (slo funciona la primera llamada). En AL se
devuelve 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, sin
embargo, muy limitadas. La razn es la ineficiencia del sistema en las operaciones de entrada y salida, que
obliga a las aplicaciones a hacer accesos directos al hardware. Por ejemplo: con el comando interno CTTY,
a travs de un puerto serie es factible poner a un PC como servidor remoto de otro. Esto permite operar en
la lnea de comandos desde el terminal remoto ubicado a varios metros de distancia. Sin embargo, nada ms
ejecutar un programa, el teclado del PC con el emulador de terminal dejar de funcionar y ser preciso
utilizar el del propio servidor!: la razn es que muy pocos programas usan el DOS para leer el teclado; no
digamos para escribir en la pantalla...
Sin embargo, an en la actualidad muchos usuarios de PC trabajan en la lnea de comandos, donde
s es posible, como se ha mencionado, utilizar el DOS como un sistema con dispositivos de entrada y salida
estndar que soportan el redireccionamiento. El redireccionamiento bajo DOS es empleado sobre todo para
procesar ficheros de texto.
Un filtro es un programa normal que lee datos de la entrada estndar (por defecto, el teclado), los
procesa de alguna manera y los deposita en la salida estndar (por defecto, la pantalla). Tanto la entrada como
la salida estndar, popularmente conocidas como STDIN y STDOUT, respectivamente, as como la salida
estndar para errores (STDERR) son dispositivos permanentemente abiertos en el DOS. Tienen asociados un
handle de control, como cualquier fichero: 0 para STDIN (denominado CON), 1 para STDOUT (tambin
conocido por CON), 2 para STDERR (tambin CON), 3 para la salida serie (denominada AUX) y 4 para la
impresora (conocida por PRN).
Por tanto, un filtro normal debe limitarse a leer, con las funciones de manejo de ficheros ordinarias,
informacin procedente del handle 0; tras procesarla debe escribirla en el handle 1. Si se produce un error
en el proceso, o hay una salida de log que no deba mezclarse con la salida deseada por el usuario, se puede
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 a
partir del DOS 2.0 (en versiones anteriores no hay siquiera subdirectorios).
Cuando se ejecuta una orden del tipo COMANDO | FILTRO, el intrprete de comandos cierra la
salida estndar y crea un fichero auxiliar (de nombre extrao); a continuacin abre ese fichero para salida:
como al cerrar la salida estndar se haba liberado el handle 1, ese handle ser asignado al nuevo fichero. Esto
significa que toda la salida de COMANDO no ir a la pantalla (CON) sino al fichero auxiliar. Cuando se
acabe de ejecutar COMANDO, el intrprete de mandatos cerrar el fichero auxiliar y volver a abrir la salida
estndar, restaurando el sistema al estado normal. Pero la cosa no queda ah, evidentemente: a continuacin
se cierra la entrada estndar y se abre como entrada el fichero auxiliar recin creado, que pasar a ser el
nuevo dispositivo de entrada por defecto. Seguidamente, se carga y ejecuta FILTRO, que tomar los datos
del fichero auxiliar en lugar del teclado. Al final, el fichero auxiliar es cerrado y borrado, abrindose y
restaurndose la entrada por defecto normal. Si se ejecuta DIR | SORT, aparte del directorio ordenado
aparecern dos extraos ficheros con 0 bytes (este era su tamao cuando se ejecut DIR): el DOS crea dos
ficheros auxiliares para sustituir la entrada y salida estndar, aunque en este ejemplo slo se emplee uno de
ellos. Actuarn los dos si se utilizan filtros encadenados que obliguen a redireccionar simultneamente tanto
la entrada como la salida a ficheros auxiliares, en una orden del tipo DIR | SORT | MORE. A partir del
DOS 5.0, si est definida la variable de entorno TEMP los ficheros auxiliares se crean donde sta indica y
no en el directorio activo, por lo que a simple vista podran no verse dichos ficheros.
Cuando se utilizan los redirectores habituales (<, >, << y >>) suceden procesos similares, todos
ellos desencadenados por COMMAND.COM, con objeto de alterar la salida y entrada por defecto para
trabajar con un fichero en su lugar. Por tanto, los filtros son programas que no tienen que preocuparse de cual
es la entrada o salida; su codificacin es extremadamente sencilla y puede realizarse en cualquier lenguaje
de alto o bajo nivel. El siguiente programa en C estndar, NULL.C, es un filtro nulo que no realiza tarea
alguna: se limita a enviar todo lo que recibe (por tanto, DIR es lo mismo que DIR | NULL):
#include <stdio.h>
void main()
{
int c;
do putchar(c=getchar()); while (c!=EOF);
}
El siguiente filtro, algo ms til, transforma en minsculas todo lo que pasa por l, teniendo cuidado
con los caracteres espaoles (, , , etc.). Lee bloques de medio Kbyte de una sola vez para reducir el
nmero de llamadas al DOS y ganar velocidad. Si se ejecuta sin ms (sin emplear | ni < ni ningn smbolo
de redireccionamiento o filtro) se limita a leer lneas del teclado y a reescribirlas en minsculas, hasta que
se acaba la entrada estndar (teclear Ctrl-Z y Return al final).
; ********************************************************************
; * *
; * MIN.ASM 1.0 - Filtro para poner en minsculas ASCII Espaol. *
; * *
; ********************************************************************
segmento SEGMENT
ASSUME CS:segmento, DS:segmento
STDIN EQU 0
STDOUT EQU 1
ORG 100h
inicio:
CALL lee_entrada ; leer de STDIN
JCXZ fin_filtro ; en CX, bytes ledos
PUSHF
CALL pon_minusculas
CALL escribe_salida ; escribir en STDOUT
POPF
JNC inicio
fin_filtro: MOV AX,4C00h ; CF = 1 si fin de fichero
INT 21h
lee_entrada PROC
LEA DX,buffer
MOV CX,512
MOV BX,STDIN
MOV AH,3Fh
INT 21h ; leer
MOV CX,AX
RET
lee_entrada ENDP
escribe_salida PROC
LEA DX,buffer
MOV BX,STDOUT
MOV AH,40h
INT 21h ; escribir
RET
escribe_salida ENDP
pon_minusculas PROC
PUSH CX
LEA BX,buffer
procesa_car: MOV AL,[BX]
CMP AL,A
JB car_ok
CMP AL,128
JAE car8
CMP AL,Z
JA car_ok
OR AL,32
car_ok: MOV [BX],AL
INC BX
LOOP procesa_car
POP CX
RET
car8: MOV AH,
CMP AL,
JE trad_ok
MOV AH,
CMP AL,
JE trad_ok
MOV AH,
CMP AL,
JE trad_ok
MOV AH,
CMP AL,
JE trad_ok
MOV AH,AL
trad_ok: MOV AL,AH
JMP car_ok
pon_minusculas ENDP
buffer DB 512 DUP (?)
segmento ENDS
END inicio
161 PROGRAMAS RESIDENTES
Captulo X: PROGRAMAS RESIDENTES
En este captulo vamos a abordar uno de los temas ms estrechamente relacionados con la
programacin de sistemas: la creacin de programas residentes. El DOS es un sistema monousuario y
monotarea, diseado para atender slo un proceso en un momento dado. Los programas residentes, aquellos
que permanecen en memoria tras ser ejecutados, surgieron como intento de superar esta limitacin. Algunos
de estos programas residentes proporcionan en la prctica multitarea real (tales como colas de impresin o
relojes), pero otros estn muertos a menos que el usuario los active. A la hora de construir programas
residentes el ensamblador es el lenguaje ms apto: es el ms potente, el programador controla totalmente la
mquina sin depender de facetas ocultas del compilador y, adems, es el lenguaje ms sencillo para crear
programas residentes (en ingls, TSR: Terminate and Stay Resident). Para los programas ms complejos
puede ser necesario, en cambio, utilizar algn lenguaje de alto nivel prximo a la mquina. Sin duda, los
programas residentes que pretendan captar gran nmero de usuarios, deben cumplir dos requisitos: por un
lado, ocupar poca memoria; por otro, estar disponibles rpidamente cuando son requeridos y, tambin, ser
fiables y crear pocos conflictos. Esto ltimo es importante, ya que un programa residente puede funcionar
ms o menos bien pero no del todo: si bien la mquina puede resistirse a colgarse, pueden aparecer anomalas
o conflictos con algunas aplicaciones. En particular, es muy comn la circunstancia de que dos programas
residentes sean incompatibles entre s.
10.1. - PRINCIPIOS BSICOS.
Un programa residente o TSR es un programa normal y corriente que, tras ser cargado, permanece
parcial o totalmente en memoria al finalizar su ejecucin. Ello es posible utilizando una funcin especfica
del sistema operativo. Los programas residentes pueden ser activados mediante una combinacin de teclas
o bien actuar con cierta periodicidad, asociados a la interrupcin del temporizador. Tambin pueden
interceptar funciones del DOS o de la BIOS para cambiar o modificar su funcionamiento. Al final, casi
siempre resulta totalmente inevitable desviar alguna interrupcin hacia una nueva rutina que la gestione, con
objeto de activar el programa residente. Como en casi todos los aspectos de la programacin, existen unos
cuantos principios fundamentales que conviene respetar:
1) Los programas residentes no deben alterar el funcionamiento normal del resto del ordenador. Esto
significa que deben preservar el estado de todo lo que van a modificar durante su ejecucin, restaurndolo
despus antes de retornar al programa principal, lo cual no se limita por supuesto a los registros de la CPU,
sino que incluye tambin la pantalla, los discos, el estado de la memoria expandida y extendida, etc. Cuando
se produce la interrupcin que activa el programa residente, los registros de la CPU pueden tener un valor
que hay que interpretar o bien pueden ser aleatorios. Este ltimo es el caso de la interrupcin peridica del
temporizador: el programa residente slo puede fiarse de CS:IP, los dems registros debern ser inicializados
antes de empezar a operar (lgicamente, habrn de ser primero preservados para ser restaurados al final).
2) No se pueden invocar libremente desde un programa residente los servicios del sistema operativo.
Si el lector es la primera vez que oye esto, quiz se quede extraado. Tal vez se pregunte qu sucedera si
desde un programa residente se llama (pongamos por ejemplo, una vez cada segundo) a la funcin de
impresin del DOS para sacar una A por la pantalla. Lo que puede suceder -y acabar sucediendo, si no
a la primera A, a la segunda o la tercera- es que el ordenador se cuelgue. Esto es debido a que el DOS es
un sistema operativo no reentrante, entre otras razones porque conmuta a una pila propia al ser invocado.
Por ello, si se llama a un servicio del DOS desde un programa residente, es posible que en ese momento el
162 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
DOS ya estuviese realizando otra funcin del programa principal y lo que vamos a conseguir es que se vuelva
loco y pierda el control cuando se acabe la tarea residente (el contenido previo de la pila ha sido destrozado).
Para utilizar el DOS desde un programa residente hay que conocer cmo estn organizadas las pilas del
sistema operativo, as como determinar el estado del DOS para saber si se puede interrumpir en ese momento
o si hay que esperar. Utilizar el DOS es prcticamente indispensable a la hora de acceder al disco, por lo que
ms adelante en este captulo lo veremos con detenimiento. Para utilizar el DOS hay que emplear funciones
ms o menos secretas del sistema no documentadas por Microsoft, si bien esto no es peligroso: esta empresa
las utiliza y las ha utilizado siempre profusamente en sus propios programas, por lo que resulta ms que
seguro esperar que futuras versiones del DOS sigan soportndolas.
3) La BIOS no es tampoco completamente reentrante. Por fortuna, la BIOS utiliza la pila del
programa que le llama. Por ello, para utilizar funciones de la BIOS desde un programa residente basta con
asegurar que el sistema no est ya ejecutando una funcin BIOS incompatible (normalmente, una interrupcin
10h en el caso de las funciones de vdeo o la 13h en las de disco).
4) El hardware puede ser accedido sin limitaciones desde los programas residentes, si bien el nivel
de uso que puede hacerse est limitado por el sentido comn (puede haber problemas, por ejemplo, si un
programa residente cambia la posicin del cabezal de un disquete cuando el programa principal estaba
ejecutando una funcin del DOS o la BIOS para acceder al disquete).
5) Los programas residentes tienen una causa que provoca su activacin. Si cuando ya estn activos,
se vuelve a reproducir la causa, estamos ante un problema de reentrada que compete exclusivamente al
programador. Por lo general, se suele denegar una demanda de activacin cuando el programa residente ya
estaba activo (si el programa tiene pila propia esto es adems obligatorio). Pongamos por caso que se pulsa
CTRL-ALT-R para mostrar un reloj residente en pantalla, qu suceder si se vuelve a pulsar CTRL-ALT-R
con el reloj ya activado?. Para solucionar esto, existen dos caminos: uno de ellos es utilizar una variable que
indique que el programa ya est activo. El otro, es utilizar para desactivar el programa la misma secuencia
de teclas que para activarlo. Lgicamente, los programas que realicen algo peridicamente (pongamos por
caso 18,2 veces por segundo) basta con que se limiten a no pillarse los dedos, esto es, utilizar menos de
1/18,2 segundos de tiempo de CPU para sus tareas.
10.2. - UN EJEMPLO SENCILLO.
El siguiente programa residente no realiza tarea alguna, tan slo es una demostracin de la manera
general de proceder para crear un programa residente. En principio, el cdigo de instalacin est colocado
al final, con objeto de no dejarlo residente y economizar memoria. La rutina de instalacin (MAIN) se
encarga de preservar el vector de la interrupcin peridica y desviarlo para que apunte a la futura rutina
residente. Tambin se instala una rutina de control de la interrupcin 10h. Finalmente, se libera el espacio
de entorno para economizar memoria y se termina residente. El procedimiento CONTROLA_INT8 puede
ser 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 funcin de vdeo de la BIOS (que no se ha interrumpido la ejecucin de una INT 10h).
Esto significa que el lector podr utilizar libremente los servicios de vdeo de la BIOS, si bien para utilizar
por ejemplo los de disquetes habra que desviar y monitorizar tambin INT 13h; por supuesto adems que
no se puede llamar al DOS en este TSR (no se puede hacer INT 21h directamente desde el cdigo residente).
Por cierto, si se fija el lector en la manera de controlar la INT 10h ver que al final se retorna al programa
principal con IRET: los flags devueltos son los del propio programa que llam y no los de la INT 10h real.
Con la INT 10h se puede hacer esto, ya que los servicios de vdeo de la BIOS no utilizan el registro de
estado para devolver ninguna condicin. Sin embargo, con otras interrupciones BIOS (ej. 16h) o las del DOS
habra que actuar con ms 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 precaucin de monitorizar
la interrupcin 10h o la 13h de la BIOS, y tal vez se pregunte si ello es realmente necesario. La respuesta
163 PROGRAMAS RESIDENTES
es tajantemente que s. Como se ver en el futuro en otro programa de ejemplo, reentrar a la BIOS sin ms
puede provocar conflictos.
demores SEGMENT
ASSUME CS:demores, DS:demores
ORG 100h
inicio:
JMP main
controla_int08 PROC
PUSHF
CALL CS:ant_int08 ; llamar al gestor normal de INT 8
STI
CMP CS:in10,0
JNE fin_int08 ; estamos dentro de INT 10h
;
; Colocar aqu el proceso a ejecutar 18,2 veces/seg.
; que puede invocar funciones de INT 10h
fin_int08:
IRET
controla_int08 ENDP
controla_int10 PROC
INC CS:in10 ; indicar entrada en INT 10h
PUSHF
CALL CS:ant_int10
DEC CS:in10 ; fin de la INT 10h
IRET
controla_int10 ENDP
in10 DB 0 ; mayor de 0 si hay INT 10h
ant_int08 LABEL DWORD
ant_int08_off DW ?
ant_int08_seg DW ?
ant_int10 LABEL DWORD
ant_int10_off DW ?
ant_int10_seg DW ?
; Dejar residente hasta aqu.
main: PUSH ES
MOV AX,3508h
INT 21h ; obtener vector de INT 8
MOV ant_int08_seg,ES
MOV ant_int08_off,BX
MOV AX,3510h
INT 21h ; obtener vector de INT 10h
MOV ant_int10_seg,ES
MOV ant_int10_off,BX
POP ES
LEA DX,controla_int08
MOV AX,2508h
INT 21h ; nueva rutina de INT 8
LEA DX,controla_int10
MOV AX,2510h
INT 21h ; nueva rutina de INT 10h
PUSH ES
MOV ES,DS:[2Ch] ; direccin del entorno
MOV AH,49h
INT 21h ; liberar espacio de entorno
POP ES
LEA DX,main ; fin del cdigo residente
ADD DX,15 ; redondeo a prrafo
MOV CL,4
SHR DX,CL ; bytes -> prrafos
MOV AX,3100h ; terminar residente
INT 21h
demores ENDS
END inicio
10.3. - LOCALIZACIN DE UN PROGRAMA RESIDENTE.
Un programa residente que ya est instalado en memoria puede volver a ser cargado desde disco y
esto hay que tenerlo en cuenta. Puede que el programa sea de stos que se cargan una sola vez y carecen de
parmetros. En ese caso, no suceder nada porque sea creada en memoria una nueva copia del mismo: es
problema 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 parmetros 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 - MTODO DE LOS VECTORES DE INTERRUPCIN.
El mtodo ms simple es tambin el ms simpln -intil- y consiste en apoyarse en los vectores de
interrupcin. Por ejemplo, si el programa qued residente interceptando la interrupcin 9, basta con mirar a
dnde apunta dicha interrupcin y comprobar un grupo de bytes o alguna identificacin que permita
determinar si el programa que la gestiona es ya una copia de l mismo. El inconveniente de este mtodo, fcil
de deducir, es que si se carga ms de un programa residente que emplee la INT 9, slo el ltimo cargado ser
capaz de encontrarse a s mismo en memoria.
10.3.2. - MTODO DE LA CADENA DE BLOQUES DE MEMORIA.
Otro mtodo alternativo es rastrear la cadena de bloques de memoria del sistema operativo buscando
programas residentes y comprobndolos uno por uno. Este mtodo es bastante rpido, habida cuenta de que
no van a existir ms de 20-50 bloques de memoria. Sin embargo, la organizacin de la memoria en los PCs
es a veces tan anrquica que este mtodo (que debera ser el ms elegante) es un poco peligroso en cuanto
a la seguridad, aunque mucho menos que el anterior. Lo cierto es que puede ser difcil intentar recorrer la
memoria superior, habida cuenta del desigual tratamiento que recibe en las diversas versiones del DOS y con
los diversos controladores de memoria que pueden estar instalados.
Por cierto, la idea de rastrear toda la memoria (1 Mb), buscando desesperadamente una cadena de
identificacin, no es nueva. Sin embargo es tremendamente lenta llevada a la prctica. Es incmoda (hay que
considerar el caso de que el propio programa que busca se encuentre a s mismo, en particular en reas como
los buffers de transferencia con disco del DOS) y bastante salvaje.
164 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
10.3.3. - MTODO DE LA INTERRUPCIN MULTIPLEX.
Finalmente, existe la posibilidad de utilizar el mismo sistema que emplea el DOS para comprobar
la presencia de sus propios programas residentes (como el KEYB, GRAPHICS, GRAFTABL, SHARE,
PRINT, etc) basado en la interrupcin Multiplex (2Fh). Este sistema es el ms seguro, aunque un tanto
laborioso. Consiste en llamar a la INT 2F con un valor en el registro AH que indica quin est llamando, y
otro valor en AL para decir por qu est llamando (normalmente 0). Los valores 00-BFh en AH estn
reservados para el DOS, y de C0h-FFh para las aplicaciones. A la vuelta, AL devuelve un valor 0 para indicar
que el programa no est instalado pero est permitida la instalacin, un valor 1 para decir que no est
instalado ni tampoco est permitida la instalacin. Si devuelve FFh, significa que el programa ya estaba
instalado. Por ejemplo, el KEYB del DOS llama a INT 2Fh con AX=AD80h, donde ADh significa que quien
pregunta es el KEYB -y no otro programa- para conocer si ya est instalado o no. En caso de que lo est
(AL=FFh a la vuelta), tambin se devuelve en ES:DI la direccin del KEYB ya residente (que es lo solicitado
con AL=80h). En el caso concreto del KEYB, si a la vuelta AL<>FFh se interpreta que el programa no est
an residente, por lo que se procede a su instalacin (en este caso, curiosamente incluso aunque AL=1).
Esta tcnica cuenta con la complicacin que supone decidir qu valor emplear en la interrupcin
multiplex. Es evidente que dos programas residentes no pueden utilizar el mismo. Los programas menos
eficientes utilizan un valor fijo predeterminado, con lo que limitan las posibilidades del usuario. Sin embargo,
para solucionarlo existen varias alternativas, que se vern ms adelante.
Aviso: Aunque no es frecuente, algunas versiones 2.X del sistema no tienen inicializado el vector de
la INT 2Fh. Por ello, es una buena prctica asegurarse de que esta interrupcin apunta a algo antes de
llamarla (por ejemplo, verificando que el segmento es distinto de cero). Por otro lado, el comando PRINT
del DOS en las versiones 2.X del sistema gestiona de tal manera la INT 2Fh que ninguna otra aplicacin
puede emplearla. Por ello, el mtodo de la interrupcin Multiplex est ms bien reservado para versiones 3.0
o superiores (tambin la 2.X si el usuario prescinde de PRINT).
10.4. - EXPULSIN DE UN PROGRAMA RESIDENTE DE LA MEMORIA
Se trata de una tarea bastante sencilla en s, aunque hay que tener en cuenta una serie de factores.
En primer lugar, el programa debe restaurar todos los vectores de interrupcin que haba interceptado. Ello
significa que si ha sido instalado tras l otro programa residente que modifica uno de los vectores que l
interceptaba, ya no es posible restaurarlo. Por ello, un primer requisito para permitir la desinstalacin es que
sea el ltimo programa residente cargado que utiliza un vector de interrupcin dado. Esto es fcil de
verificar, basta con comprobar que todas las interrupciones interceptadas siguen apuntando a una copia de
l. Si esta prueba es superada satisfactoriamente, puede procederse a restaurar los vectores de interrupcin
y liberar la memoria ocupada de una de las dos siguientes maneras:
1) Pasando en ES el segmento donde est cargado el programa y llamando a la funcin 49h del DOS
para liberar el bloque de memoria.
2) Liberando directamente el bloque de memoria al colocar una palabra a cero en los bytes del MCB
que identifican al propietario del bloque. Este mtodo puede ser ms seguro si est instalado un
gestor de memoria expandida extrao, aunque es menos elegante y quiz menos recomendable.
Por lo general, no tiene mucho sentido que un usuario elimine un programa residente despus de
haber cargado otro -aunque ello sea posible- ya que se origina un hueco en la memoria que normalmente no
se utilizar para nada -el DOS asigna siempre el mayor bloque disponible al cargar cualquier aplicacin-,
aunque esto es realmente problema exclusivo del usuario.
Como se ver despus, ciertos programas residentes sofisticados permiten ser desinstalados an sin
ser los ltimos instalados; sin embargo, estos programas residentes tienen que tener algo en comn:
165 PROGRAMAS RESIDENTES
comportarse de la misma manera y actuar tambin de una manera definida. Ello significa que si entre dos
programas residentes que cumplen el mismo convenio el usuario instala un programa que no lo respeta, se
pierden todas las posibilidades.
10.5.- GESTIN AVANZADA DE LA INTERRUPCIN MULTIPLEX.
10.5.1. - EL CONVENIO BMB COMPUSCIENCE.
Para solucionar el problema de que dos programas residentes no pueden utilizar el mismo valor de
identificacin en la interrupcin Multiplex, los seores de BMB Compuscience Canada pensaron un buen
sistema, publicado en el INTERRUP.LST de Ralf Brown, que expongo a continuacin.
La idea consiste en asignar dinmicamente el valor del registro AH empleado al llamar a la
interrupcin Multiplex. Para ello se empieza, por ejemplo, con AH=0C0h. Se coloca un 0 en AL para solicitar
chequeo de instalacin y se hace que los registros ES:DI valgan 0EBEBh:0BEBEh (porque s), llamando a
continuacin a la INT 2Fh. A la vuelta se devuelve en 0 en AL para indicar programa no instalado, un 1
para sealar adems que no se debe instalar, y FFh para decir que ya est instalado... quin?: un programa
cuyo nombre de fabricante abreviado (MMMM), nombre de producto (PPPPPPPP) y versin (NNNN) estn
en ES:DI de la forma "BMB MMMMPPPPPPPPvNNNN". Si se comprueba que ese programa no es el
buscado, se incrementa AH y si AH es menor o igual a 0FFh se repite el proceso. De este bucle puede salirse
de dos maneras: encontrando el programa buscado (y su ubicacin en memoria) o sin encontrarle, en cuyo
caso tambin se habr localizado algn valor de AH an no utilizado por ninguna tarea residente (a no ser
que el usuario haya instalado ya 64 programas residentes con esta tcnica). Lgicamente, el programa
residente debe interceptar tambin INT 2Fh y devolver (cuando alguien pregunta por l) un valor FFh en AL
y, si adems el que preguntaba llamaba con ES:DI=0EBEBh:0BEBEh entonces debe devolver en ES:DI la
informacin antes mencionada. Lo de emplear 0EBEBh y 0BEBEh constituye un mecanismo similar a un
password, 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 el
fabricante, nombre y versin del programa, por qu no colocar ms informacin til?. Por ejemplo, sera
interesante disponer de informacin sobre los contenidos previos de los vectores de interrupcin que el
programa ha desviado, lo cual permitira su desinstalacin aunque no sea el ltimo cargado, ser desinstalado
por parte de otros programas o incluso emplear ciertas tcnicas de relocalizacin en memoria para evitar la
fragmentacin de la misma cuando es desinstalado. Con objeto de aumentar la eficacia, el autor de este libro
desarroll un mtodo nuevo, extensin del expuesto en el apartado anterior, que permitiera sacar mayor
partido de la interrupcin Multiplex. Al igual que el anterior, el nuevo convenio tambin est publicado en
el INTERRUP.LST, lo que garantiza su difusin y la inversin de quienes decidan emplearlo.
El mtodo es similar al anterior, con la diferencia de que en ES:DI est almacenado en el momento
de llamar el valor 1492h:1992h. En AH se indica, como siempre, el nmero de entrada de la interrupcin
Multiplex y en AL se coloca un 0 solicitando chequeo de instalacin. Tras llamar, si AL devuelve un 1 un
0FFh significa que esa entrada ya est empleada, si devuelve un 0 significa que est libre y que puede ser
utilizada. Hasta ahora, todo sucede como es costumbre en los programas que utilizan la interrupcin
Multiplex. Sin embargo, por el hecho de haber llamado con ES:DI=1492h:1992h, el programa residente sabe
que quien lo llama es alguien que respeta el convenio. Por ello, adems de devolver un 0FFFFh en AX,
modifica ES y DI para apuntar a una tabla con la siguiente informacin:
Offset Tamao Descripcin
-16 WORD segmento donde realmente comienza el cdigo del TSR (CS en programas
con PSP, segmento de memoria superior XMS si instalado como UMB...)
-14 WORD offset donde realmente comienza el cdigo del TSR (frecuentemente 100h
en programas *.COM y 0 en TSRs en memoria superior).
-12 WORD memoria empleada por el TSR (en prrafos). Conociendo la memoria que
emplea el TSR es posible determinar si los vectores que intercepta estn
166 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
an apuntndolo (y si es seguro el proceso de desinstalacin).
-10 BYTE de caractersticas
bits 0-2: 000 programa normal (con PSP)
001 bloque de memoria superior XMS (se necesita funcin de HIMEM.SYS
para liberar la memoria al desinstalar)
010 device driver (*.SYS)
011 device driver en formato EXE
1xx otros (reservados)
bits 3-6 reservados
bit 7 activo si tabla_extra definida y soportada
-9 BYTE nmero de entrada en la interrupcin Multiplex (redefinible por un agente
externo). Notar que el TSR debe usar ESTA variable en su rutina de control
de INT 2Fh.
-8 WORD offset a la tabla area_vectores (se ver despus)
-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 su
versin; el carcter : se utiliza como delimitador).
El valor ubicado en ES:DI-14 puede ser til de cara a deducir el tamao de la parte del PSP que
permanece residente, ya que se considera que la ubicacin del programa comienza en el offset 0 relativo al
segmento definido en ES:DI-16 y, por tanto, el tamao del programa definido en ES:DI-12 es relativo
tambin con offset 0 a ese segmento. Si bien se puede opinar que son demasiados campos, son slo poco ms
de 16 bytes los que se aaden al programa residente. Adems, muchas de las variables anteriores han de estar
definidas necesariamente: por qu no juntarlas de una manera convenida?. En la tabla anterior se define un
puntero a una estructura con informacin sobre los vectores interceptados. No se respeta sin embargo el
formato de los encabezamientos de interrupcin propuesto en la BIOS del PS/2 (la intencin de IBM es
buena, pero ha llegado demasiado tarde).
Formato de la tabla area_vectores:
Offset Tamao Descripcin
-1 BYTE nmero de vectores interceptados por el TSR
00h BYTE nmero del primer vector
01h DWORD puntero al primer vector antes de instalar el TSR
05h BYTE nmero del segundo vector
06h DWORD puntero al segundo vector antes de instalar el TSR
. . (y as sucesivamente). Notar que el TSR debe usar ESTAS variables para
invocar las anteriores rutinas de control de esas interrupciones, ya que un
. . agente externo podra actualizarlas.
En las primeras versiones de este convenio ya no existan ms reglas. Sin embargo, al final comprend
la necesidad de ampliar las prestaciones. Por ello, el convenio fue ampliado con dos tablas ms, opcionales,
que es conveniente rellenar incluso tambin en aquellos TSR ms sencillos que ocupan menos de 64 Kb y
son totalmente reubicables (no contienen referencias absolutas a segmentos). Estas tablas permitiran a un
hipottico sistema operativo mover los programas residentes para evitar la fragmentacin de la memoria, tarea
que mientras tanto puede realizar algn programa de utilidad. Aquellos TSR que contengan referencias en
su propio cdigo o datos cambiando el segmento (slo puede ocurrir normalmente en los programas EXE)
el convenio establece que deben soportar el parmetro /SR: ante l, al ser recargados en memoria desde disco
(necesario para la reubicacin) deben instalarse silenciosamente sin chitar, autoinhibindose a continuacin.
En general, la mayora de los programas residentes escritos en ensamblador son relocalizables, as como los
elaborados en el modelo Tiny del C, por lo que no es muy complejo realizar esta tarea. La nica pega que
se puede poner es que, por desgracia, pocos programas usan este convenio!.
Formato de la tabla area_extra (opcional):
Offset Tamao Descripcin
00h WORD offset a la tabla control_externo (0 si no soportada)
02h WORD reservado para futuro uso (0)
Formato de la tabla control_externo (opcional):
Offset Tamao Descripcin
00h BYTE bit 0: activo si el TSR es relocalizable (sin referencias a segmentos)
01h WORD offset a una variable que puede inhibir o activar el TSR
---Si el bit 0 en el offset 00h est a 0:
03h DWORD puntero a cadena ASCIIZ con el nombre del fichero ejecutable que
soporta el parmetro /SR (instalacin e inhibicin silenciosa)
07h DWORD puntero a la primera variable a inicializar en la copia recargada
de disco desde el TSR an residente.
0Bh DWORD puntero a la ltima variable (todas estn en el mismo bloque).
167 PROGRAMAS RESIDENTES
La variable que activa o inhibe el TSR permite paralizarlo momentneamente antes de realizar ciertas
tareas crticas, si bien no est pensada su utilizacin de cara a relocalizarlo en memoria o a desinstalarlo.
A continuacin se listan dos rutinas que habr de incorporar todo programa que desee emplear este
convenio (u otras equivalentes). Las rutinas las he denominado mx_get_handle y mx_find_tsr. La primera
permite buscar un valor para la interrupcin Multiplex an no empleado por otra tarea residente, tanto si sta
es del convenio como si no. La segunda sirve para que el programa residente se busque a s mismo en la
memoria. En esta segunda rutina se indica el tamao de la cadena de identificacin (la que contiene el
nombre del fabricante, programa y versin) en CX. Si no se encuentra el programa residente en la memoria,
puede repetirse la bsqueda con CX indicando slo el tamao del nombre del fabricante y el programa, sin
incluir el de la versin: as se podra advertir al usuario que tiene instalada ya otra versin distinta.
; ------------ Buscar entrada no usada en la interrupcin Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta tcnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
mx_no_hueco: STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
; ------------ Buscar un TSR por la interrupcin Multiplex. A la
; entrada, DS:SI cadena de identificacin del programa
; (CX bytes) y ES:DI protocolo de bsqueda (normalmente
; 1492h:1992h). A la salida, si el TSR ya est instalado,
; CF=0 y ES:DI apunta a la cadena de identificacin del
; mismo. Si no, CF=1 y ningn registro alterado.
mx_find_tsr PROC
MOV AH,0C0h
mx_rep_find: PUSH AX
PUSH CX
PUSH SI
PUSH DS
PUSH ES
PUSH DI
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl ; no hay TSR ah
CLD
PUSH DI
REP CMPSB ; comparar identificacin
POP DI
JE mx_tsr_found ; programa buscado hallado
mx_skip_hndl: POP DI
POP ES
POP DS
POP SI
POP CX
POP AX
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4 ; sacar ES y DI de la pila
POP DS
POP SI
POP CX
POP AX
CLC
RET
mx_find_tsr ENDP
La rutina mx_unload desinstala un programa residente que verifique el convenio; basta con indicar
el nmero de interrupcin Multiplex que emplea el TSR. El proceso de desinstalacin falla si se ha instalado
despus un TSR que no verifica el convenio y tiene alguna interrupcin en comn, ya que la rutina no puede
en ese caso recorrer la cadena de vectores para modificarla anulando la tarea residente. Para que un TSR se
auto-desinstale basta con que suministre a esta rutina su propio nmero de identificacin. El mtodo empleado
por la rutina para cambiar los vectores de interrupcin no es muy ortodoxo, pero simplifica el algoritmo y
posee un nivel de seguridad razonable. Esta rutina da dos pasadas: el objeto de la primera es slo asegurar
que el TSR puede ser desinstalado antes de empezar a cambiar ningn vector. En la segunda, se cambian los
enlaces entre los vectores y se libera la memoria, bien llamando al DOS o al controlador XMS (segn quin
la haya asignado). Hay una maniobra ms o menos complicada para hacer que el vector 2Fh sea el ltimo
restaurado, con objeto de poder seguir la cadena de interrupciones hasta el propio TSR invocando la INT 2Fh.
; ------------ Eliminar TSR del convenio si es posible. A la entrada,
; en AH se indica la entrada Multiplex; a la salida, CF=1
; si fue imposible y CF=0 si se pudo. Se corrompen todos
; los registros salvo los de segmento. En caso de fallo
; al desinstalar, AL devuelve el vector culpable.
mx_unload PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX ; BP=entrada Multiplex del TSR
MOV CX,2
mx_ul_pasada: PUSH CX ; siguiente pasada
LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0 ; CX = n vectores
mx_ul_masvect: POP AX
PUSH AX ; pasada en curso
DEC AL
PUSH CX
mx_ul_2f: MOV AL,ES:[SI] ; vector en curso
JNZ mx_ul_pasok
CMP CX,1 ; ltimo vector?
JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL ; INT 2Fh?
JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f
mx_ul_noult: CMP AL,2Fh ; restaurar INT 2Fh?
JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f
mx_ul_pasok: PUSH ES
PUSH AX
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h ; vector en ES:BX
POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX ; INT xx en DX (aprox.)
MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
168 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI
PUSH ES:[DI-12]
MOV DI,ES:[DI-8] ; offset a la tabla de vectores
MOV CL,ES:[DI-1]
MOV CH,0 ; nmero de vectores en CX
mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect ; este TSR usa vector analizado
ADD DI,5
LOOP mx_ul_buscav
ADD SP,4 ; no lo usa
JMP mx_ul_otro
mx_ul_usavect: POP CX ; tamao del TSR
POP BX ; segmento del TSR
CMP DX,BX
JB mx_ul_otro ; la INT xx no le apunta
ADD BX,CX
CMP DX,BX
JA mx_ul_otro ; la INT xx le apunta
PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP ; es el propio TSR?
POP AX
JNE mx_ul_chain ; no
POP ES ; s: posible reponer vector!
POP CX
POP BX
PUSH BX
PUSH CX
PUSH ES
DEC BX
JNZ mx_ul_norest ; no es la segunda pasada
POP ES ; segunda pasada...
PUSH ES
PUSH DS
MOV BX,CS:mx_ul_tsroff ; restaurar INTs
MOV DS,CS:mx_ul_tsrseg
CLI
MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: POP ES
POP CX
ADD SI,5 ; siguiente vector
DEC CX
JZ mx_unloadable ; no ms, desinstal-ar/ado!
JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direccin
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX ; INT xx en DX (aprox.)
MOV AH,0BFh
mx_ul_otro: INC AH ; a por otro TSR
JZ mx_ul_exitnok ; se acabaron!
JMP mx_ul_masmx
mx_ul_exitnok: ADD SP,6 ; equilibrar pila
POP ES
STC
RET ; imposible desinstalar
mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok ; desinstalado
JMP mx_ul_pasada ; 1 pasada exitosa: por la 2
mx_ul_exitok: TEST ES:info_extra,111b ; tipo de instalacin?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml ; cargado en RAM convencional
CMP xms_ins,1
JNE mx_ul_freeml ; no hay controlador XMS (?)
MOV DX,ES
MOV AH,11h
CALL gestor_XMS ; liberar memoria superior
POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h ; liberar bloque de memoria ES:
POP ES
CLC
RET
mx_ul_tsrcv?: PUSH AX ; es TSR del convenio?...
PUSH ES
PUSH DI
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4 ; CF=0
POP AX
RET
mx_ul_ncvexit: POP DI ; ...no es TSR del convenio
POP ES
POP AX
STC ; CF=1
RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload ENDP
Los dos programas siguientes constituyen dos pequeas utilidades de apoyo a los TSR de este
convenio. TSRLIST lista los TSR del convenio que estn instalados en el ordenador, con informacin
detallada; TSRKILL permite eliminar uno o todos los TSR que estn instalados en cualquier orden, no slo
necesariamente el ltimo que fue cargado. Lgicamente, si entre varios programas que respetan el convenio
hay uno que lo viola, TSRKILL puede no ser capaz de desinstalar un TSR del convenio. En ese caso, se
informa de qu vector ha sido el culpable. Ejemplo de salida de TSRLIST /V:
TSRLIST 1.3 (c) Febrero 1994 CiriSOFT.
Listado de tareas residentes normalizadas:
Programa Ver. Direccin Tamao Mx. ID Vectores interceptados
-------- ----- --------- ------ -------- -------------------------------------
RCLOCK 2.3 E8A3:0000 1424 192 08 09 10 2F
KEYBFIX 1.0 E15B:0000 208 193 09 2F
DISKLED 2.1 E8FD:0060 528 194 08 09 13 2F
DATAPLUS 2.4 E91F:0060 18640 195 09 2F
ANSIUP 1.0 EDAD:0060 576 196 29 2F
HBREAK 4.1 EDD2:0000 1584 197 08 09 20 21 27 2F 70
SCRCAP 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; TSRLIST
tambin informa de las entradas que estn siendo utilizadas por programas que no respetan el convenio,
aunque lgicamente no da ms informacin.
/********************************************************************/
/* */
/* TSRLIST 1.3 - Utilidad de listado de TSRs 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 parmetro /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 */
169 PROGRAMAS 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) /* funcin 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. Direccin Tamao Mx. ID ");
if (vect)
printf (" Vectores interceptados\n");
else
printf (" 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: versin */
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 lnea */
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 tamao */
}
######################################################################
/********************************************************************/
/* */
/* TSRKILL 1.3 - Utilidad de desinstalacin de TSRs normalizados. */
/* Compilar en el modelo Large de Borland C. */
/* */
/********************************************************************/
#include <dos.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct tsr_info {
unsigned segmento_real;
unsigned offset_real;
unsigned ltsr;
unsigned char info_extra;
unsigned char multiplex_id;
unsigned vectores_id;
unsigned extension_id;
unsigned long validacion;
char autor_nom_ver[80];
};
int tsr_convenio(),
mx_unload(),
existe_xms();
void liberar_umb(),
desinstalar();
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 nmero 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);
}
else
desinstalar (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); /* versin */
strcat (cadena, " de ");
strcat (cadena, cadaux); /* autor */
}
if (correcto)
printf(" - Desinstalado el %s\n", cadena);
else {
if (vector==0x100)
printf (" - No hay TSR %u o no es del convenio.\n", mxid);
else if (vector==0x101)
printf (" - HBREAK es demasiado fuerte para TSRKILL.\n");
else if (vector==0x102)
printf (" - 2MGUI es demasiado fuerte para TSRKILL.\n");
else {
printf (" - El %s no se puede desinstalar: ", cadena);
printf ("fallo en el vector %02X.\n", vector);
}
}
}
int mx_unload (int mxid, int *interrupcin, 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)) {
*interrupcin=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++;
}
}
*interrupcin = vector;
*tsrnombre = tsr->autor_nom_ver;
if (strstr(*tsrnombre, "HBREAK")!=NULL) {
posible=0; *interrupcin=0x101; }
if (strstr(*tsrnombre, "2MGUI")!=NULL) {
posible=0; *interrupcin=0x102; }
if (posible) {
for (i=0; i<numvect; i++) {
vector = peekb(FP_SEG(tsr), tsr->vectores_id+5*i);
sgm = peek(FP_SEG(tsr), tsr->vectores_id+5*i+3);
ofs = peek(FP_SEG(tsr), tsr->vectores_id+5*i+1);
if ((tablaptr[i][0]==0) && (tablaptr[i][1]==0)) {
interr=MK_FP(sgm, ofs);
setvect (vector, interr);
}
else {
asm cli
poke (tablaptr[i][0], tablaptr[i][1], ofs);
poke (tablaptr[i][0], tablaptr[i][1]+2, sgm);
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,4310h
int 2Fh
mov word ptr controlador,bx
mov word ptr controlador+2,es
mov ah,11h
mov dx,segmento
call controlador
pop di; pop si; pop es;
}
}
10.5.3.- LA PROPUESTA AMIS.
La interrupcin Multiplex presenta un elevado nivel de polucin debido al gran nmero de programas
que la utilizan incorrectamente. En algunos casos se soluciona el problema instalando primero los programas
conflictivos y despus los que trabajan bien. Lo mnimo que se puede exigir a un programa residente que
utilice esta interrupcin es que soporte el chequeo de instalacin (la llamada con AL=0) y devuelva una seal
de reconocimiento afirmativo (AL=0FFh) si est empleando esa entrada en cuestin. Sin embargo, algunos
no llegan ni a eso. Por fortuna, son tan malos que casi nadie los emplea. Sin embargo, con objeto de
solucionar estos casos, Ralf Brown -autor del INTERRUP.LST- ha desarrollado un mtodo alternativo basado
en la interrupcin 2Dh. Esta interrupcin no ha sido empleada hasta ahora por el DOS ni por ninguna
aplicacin importante. La propuesta AMIS (Alternate Multiplex Interrupt Specification) implementa un
sistema estandarizado de interface con los programas residentes. Habida cuenta de que las principales
empresas desarrolladoras de software de sistemas ojean el INTERRUP.LST antes de utilizar una interrupcin,
para evitar conflictos entre aplicaciones, es de esperar que la propia Microsoft no utilice tampoco la INT 2Dh
para sus propsitos en futuras versiones del DOS. Por tanto, no es muy arriesgado seguir este convenio. La
informacin que expongo a continuacin se corresponde con la versin 3.4 de la especificacin.
Los programas que emplean la INT 2Dh deben interceptarla e implementar una serie de funciones.
Como luego veremos, no es necesario que soporten todas las que propone el convenio. A la hora de llamar
a la INT 2Dh se indicar en AH, tal como se haca con la interrupcin Multiplex, el nmero de entrada y en
AL la funcin. Todo el funcionamiento se basa en invocar funciones en el programa residente. El
inconveniente de ejecutar cdigo en la copia residente es que ocupa algo ms de memoria, y la necesidad de
implementar dichas funciones. La ventaja de ejecutar cdigo en la copia residente es que sta puede, en donde
sea procedente, restaurar el estado del sistema de manera ms completa o realizar tareas especficas que sean
necesarias. Por citar un ejemplo, TSRKILL no puede desinstalar las conocidas utilidades HBREAK o 2MGUI,
que, en cambio, con la propuesta AMIS podran haber soportado una funcin de desinstalacin accesible por
cualquier agente externo. Existen las siguientes funciones:
- Funcin 0: Chequeo de instalacin. Si no hay un TSR utilizando ese nmero se devuelve un 0 en
AL. En caso contrario se devuelve un 0FFh en AL; en CX se devuelve adems el nmero de versin del
interface AMIS que soporta el TSR (ej. CX=340h para la v3.4); en DX:DI se entrega la direccin de la
cadena de identificacin, 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 descripcin del producto;
este campo puede constar simplemente de un cero si no se desea inicializarlo.
- Funcin 1: Obtener punto de entrada. Como llamar a la INT 2Dh puede ser relativamente lento
(debido al elevado nmero de programas residentes que puede haber instalados) con esta funcin se solicita
al TSR un punto de entrada alternativo para poder llamarlo de una manera ms directa sin la INT 2Dh. Si
171 PROGRAMAS RESIDENTES
devuelve un 0 en AL, significa que el TSR debe ser invocado obligatoriamente va INT 2Dh. Si devuelve
un 0FFh en AL ello implica que soporta una llamada directa, cuyo punto de entrada devuelve en DX:BX.
- Funcin 2: Desinstalacin. A la entrada, se indica al TSR en DX:BX el punto donde deber saltar
tras su autodesinstalacin (si la soporta). A la vuelta, el TSR devuelve un cdigo en AL que se interpreta:
0 - Funcin 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 an habilitado y
devuelve 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 y
devuelve en BX el segmento del bloque de memoria donde reside.
5 - No es seguro desinstalar ahora. Intentar de nuevo ms tarde.
0FFh - Todo ha ido bien, TSR desinstalado: retorna con AX corrompido a la direccin DX:BX.
- Funcin 3: Solicitud de POP-UP. Esta funcin est diseada slo para los programas residentes que
muestran mens en pantalla al ser activados (normalmente con una combinacin de teclas). El valor que
devuelve en AL se interpreta:
0 - Funcin no implementada, el TSR no es de tipo POP-UP.
1 - No es posible el POP-UP ahora, intentar solicitud ms 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 intervencin del usuario. En BX se devuelve la causa
genrica del fallo: 0-Desconocido, 1-La cadena de interrupciones se solapa con memoria que debe
ser desalojada para el POP-UP, 2-Fallo en las operaciones de swapping necesarias para el POP-UP.
Adems, en CX se devuelve un cdigo de error exclusivo de la aplicacin que se trate.
0FFh - El TSR fue correctamente POP-UPado y posteriormente abandonado por el usuario. A la
vuelta, BX entrega un 0 para no indicar nada, un 1 para indicar que el TSR fue descargado por el
usuario y los valores 2 al 0FFh estn reservados para futuros usos. Los valores 100h al 0FFFFh en
BX estn a disposicin del programa que se trate.
- Funcin 4: Determinar los vectores interceptados. A la entrada se indica en BL el nmero de la
interrupcin (excepto 2Dh). A la vuelta, AL devuelve un cdigo:
0 - Funcin no implementada.
1 - Imposible determinar.
2 - La interrupcin indicada ha sido interceptada.
3 - La interrupcin 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 interrupcin 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 slo estn definidos por compatibilidad con versiones anteriores
de la especificacin (v3.3), el autor del convenio avisa que no sern quiz soportados en otras versiones. Por
tanto, lo ms normal es que el TSR devuelva un valor 4 sin hacer caso del valor de BL (de lo contrario, el
programa que llama tendra que hacer un molesto bucle comprobando todas las interrupciones). Sera una
lstima que un TSR devolviera un valor 0. El formato de la lista de interrupciones interceptadas es:
Offset 0 (1 bytes): Nmero del vector (el ltimo de la lista es siempre 2Dh).
Offset 1 (2 bytes): Offset a la rutina de control de interrupcin.
La rutina de control de interrupcin respeta este formato, propuesto por IBM en las BIOS de PS/2:
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): Direccin previa de ese vector de interrupcin.
Offset 6 (2 bytes): Valor 424Bh (consejo de IBM).
Offset 8 (1 byte): Bandern de EOI, 0 si es interrupcin software o controlador secundario de la
interrupcin hardware, 80h si es el controlador primario de la interrupcin hardware (debe enviar un
comando EOI al controlador de interrupciones 8259).
Offset 9 (2 bytes): Salto corto a la rutina de reset hardware (que retornar con RETF).
Offset 0Bh (7 bytes): Reservados (a 0).
Offset 12h: Rutina que controla la interrupcin.
- Funciones 5 y siguientes: Reservadas para futuras versiones del convenio, devuelven 0 al no estar
implementadas.
Por supuesto, los programas que cumplan la propuesta AMIS deben asignar dinmicamente el nmero
de entrada que van a utilizar en la INT 2Dh, buscando uno libre. Para chequear su instalacin han de emplear
los 16 bytes que indican el nombre del fabricante y el programa. Como dije al principio, no es preciso que
un programa soporte todas estas funciones: para cumplir con la versin 3.4 de la especificacin basta con
implementar las funciones 0, 2 (sin obligacin de disponer de rutina de desinstalacin) y la 4 (devolviendo
un valor 4).
10.5.4.- COMPARACIN ENTRE MTODOS.
Cualquiera de los tres mtodos expuestos es vlido para lograr una correcta localizacin del programa
residente en memoria. El ms sencillo es el primero (aunque ES:DI puede estar asignado de la manera que
el lector considere oportuna, por supuesto). Sin embargo, son los dos ltimos los ms recomendables, por las
prestaciones que ofrecen. El ms completo es la propuesta AMIS.
10.6. - MTODOS ESPECIALES PARA ECONOMIZAR MEMORIA.
De cara a aumentar el nmero potencial de usuarios de un programa residente es fundamental
considerar el aspecto de la ocupacin de memoria. El mtodo ms sencillo es implementar el programa como
falso controlador de dispositivo (se vern en el captulo siguiente) con objeto de evitar el PSP; sin embargo,
estos programas slo pueden ser ejecutados una vez en el momento de arranque del sistema. No obstante, con
los programas COM y EXE normales tambin se pueden tomar una serie de medidas para reducir la
ocupacin de memoria: la primera y ms efectiva es no dejar residente el inservible espacio de entorno, como
se vio en captulos anteriores. Otra de ellas consiste en emplear el PSP para almacenar datos; esto ltimo slo
debe hacerse despus de finalizada la ejecucin del programa -despus de haber entregado el control al
sistema-, ya que el PSP es utilizado por el DOS al terminar la ejecucin. En todo caso conviene respetar al
menos los dos primeros bytes (y a ser posible tambin los dos situados en el offset 2Ch) con objeto de que
no se vuelvan locos los programas del sistema que informan sobre el estado de la memoria
(fundamentalmente el comando MEM). Si el programa utiliza pocos datos como para cubrir el PSP, cabe la
posibilidad de colocar cdigo en el mismo, para lo cual el programa puede auto-relocalizarse hacia atrs en
la memoria, machacando los 171 ltimos bytes del PSP que no son vitales para el sistema: en efecto, en el
offset 5Ch comienza el primer FCB; los 7 bytes anteriores corresponden al FCB extendido -circunstancia que
poco suelen poner de relieve los libros tcnicos- por lo que el nico rea que es obligatorio respetar es la
zona 00-54h: 85 bytes (incluso este rea podra ser tambin casi totalmente ocupada, como se dijo antes, pero
despus de finalizar la ejecucin del programa). Por comodidad, se respetarn los primeros 96 bytes, justo
6 prrafos: moviendo el programa hacia atrs un nmero entero de prrafos, al final resulta sencillo desviar
los vectores de interrupcin decrementando su segmento en 6 unidades menos antes de desviarlos. Esta treta
slo es factible, por supuesto, en programas de un solo segmento, tipo COM. Los de tipo EXE normalmente
dejarn residente todo el PSP, ya que es un segmento previo al programa (de hecho, al terminar residente hay
que aadir el tamao del PSP) y sera complicada la reubicacin.
173 PROGRAMAS RESIDENTES
Es cierto que estas tcnicas, con programas que se mueven a si mismos dando vueltas por la memoria,
automodificndose ... no son consideradas elegantes por los programadores conservadores, y no se pueden
hacer estas salvajadas en entornos con proteccin de memoria (UNIX, etc.); de hecho, Niklaus Wirth se
llevara sin duda las manos a la cabeza. Sin embargo el DOS y el 8086 las permiten y pueden ser bastante
tiles, en especial para los programadores de sistemas. Adems, escondiendo bien los fuentes, lo ms probable
es que nadie se entere de ello...
10.7. - PROGRAMAS AUTOINSTALABLES EN MEMORIA SUPERIOR.
Los TSR ms eficientes deben detectar la presencia de memoria superior e instalarse automticamente
en ella, por varios motivos. Por un lado, se mejora el rendimiento en aquellas mquinas con usuarios
inexpertos que no emplean el HILOAD o el LOADHIGH del sistema. Por otro, un programa residente puede
ocupar mucho ms espacio en disco que lo que luego ocupar en memoria. Si se utiliza LOADHIGH o
HILOAD, el sistema intenta reservar memoria para poder cargar el fichero desde disco. Esto significa que
puede haber casos en que no tenga suficiente memoria para cargar el programa, con lo que lo cargar en
memoria convencional. Sin embargo, ese TSR tal vez hubiera cabido en la memoria superior: si es el propio
TSR el que se auto-relocaliza (copindose a s mismo) hacia la memoria superior, este problema desaparece.
Tratndose de programas de un solo segmento real, como los COM, no es problema alguno realizar la
operacin de copia.
Con DR-DOS y, en general, con ciertos controladores de memoria (tales como QEMM) la memoria
superior es gestionada por la especificacin de memoria extendida XMS (vase apartado 8.3). Para utilizar
la memoria superior en estos sistemas hay que detectar la presencia del controlador XMS y pedirle la
memoria (tambin habr que llamarle despus para liberarla). Con MS-DOS 5.0 y posteriores slo existe
memoria superior XMS si NO se indica DOS=UMB en el CONFIG.SYS; sin embargo, la mayora de los
usuarios suelen indicar esta orden con objeto de que el MS-DOS permita emplear LOADHIGH y
DEVICEHIGH. Por desgracia, con MS-DOS, cuando el DOS gestiona la memoria superior, se la roba toda
al controlador XMS. Por tanto, habr que pedrsela al DOS. Con MS-DOS, el procedimiento general es el
siguiente: Primero, preservar el estado de la estrategia de asignacin de memoria y el estado de los bloques
de memoria superior (si estn o no conectados con los de la memoria convencional). A continuacin, se
conectan los bloques de memoria superior con los de la convencional, por si no lo estaban. Seguidamente,
se modifica la estrategia de asignacin de memoria, estableciendo -por ejemplo- un best fit en memoria
superior. Finalmente, se asigna memoria utilizando la funcin convencional de asignacin (48h). Tras estas
operaciones, habr de ser restaurada la estrategia de asignacin de memoria y el estado de los bloques de
memoria superior.
Es conveniente intentar primero asignar memoria superior XMS: si falla, se puede comprobar si la
versin del DOS es 5 (o superior) y aplicar el mtodo propio que requiere este sistema. De esta manera, los
TSR podrn asignar memoria superior sea cual sea el sistema operativo, controlador de memoria o
configuracin del sistema activos. Sin embargo, con el mtodo propio del DOS 5.0 hay un inconveniente:
al acabar la ejecucin del cdigo de instalacin del TSR, el DOS libera el bloque de memoria que se asign
con la funcin 48h!. Para evitar esto, hay dos mtodos: uno, consiste en terminar residente (aunque sea
dejando slo los primeros 96 bytes del PSP) con objeto de que el sistema respete el bloque de memoria
creado. Si no se desea este ligero derroche de memoria convencional, hay un mtodo ms contundente.
Consiste en engaar al DOS y, tras asignar el bloque de memoria, modificar en su correspondiente bloque
de control la informacin del propietario (PID), hacindole apuntar -por ejemplo- a s mismo. De esta manera,
al acabar el programa, el DOS recorrer la cadena de bloques de memoria y no encontrar ninguno que
pertenezca al programa que finaliza... conviene tambin, en este caso, que los dos primeros bytes del bloque
de memoria superior contengan la palabra 20CDh (ubicada al inicio de los PSP), con objeto de que algunos
programas de diagnstico lo confundan con un programa (no obstante, el comando MEM del DOS no
requiere este detalle y lo tomara directamente por un programa). Tambin hay que crear el nombre del
programa en los 8 ltimos bytes del MCB manipulado. Las siguientes rutinas asignan memoria superior XMS
(UMB_alloc) o memoria superior DOS 5 (UPPER_alloc):
174 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
; ------------ Reservar bloque de memoria superior del n prrafos AX,
; devolviendo en AX el segmento donde est. CF=1 si no
; est instalado el gestor XMS (AX=0) o hay un error (AL
; devuelve el cdigo de error del controlador XMS).
UMB_alloc PROC
PUSH BX
PUSH CX
PUSH DX
CMP xms_ins,1
JNE no_umb_disp ; no hay controlador XMS
MOV DX,AX ; nmero de prrafos
MOV AH,10h ; solicitar memoria superior
CALL gestor_XMS
CMP AX,1 ; ha ido todo bien?
MOV AX,BX ; segmento UMB/cdigo de error
JNE XMS_fallo ; fallo
POP DX ; ok
POP CX
POP BX
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo: POP DX
POP CX
POP BX
STC
RET
UMB_alloc ENDP
; ------------ Reservar memoria superior, con DOS 5.0, del tamao
; solicitado (AX prrafos). Si no hay bastante CF=1,
; en caso contrario devuelve el segmento en AX.
UPPER_alloc PROC
PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin ; necesario DOS 5.0 mnimo
UPPER_existe: PUSH AX ; preservar prrafos...
MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL ; preservar estado UMB
MOV AX,5803h
MOV BX,1
INT 21h ; conectar cadena UMBs
MOV AX,5801h
MOV BX,41h
INT 21h ; High Memory best fit
POP BX ; ...prrafos requeridos
MOV AH,48h
INT 21h ; asignar memoria
PUSHF
PUSH AX ; guardado el resultado
MOV AX,5801h
MOV BX,alloc_strat
INT 21h ; restaurar estrategia
MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h ; restaurar estado cadena UMB
POP AX
POPF
JC UPPER_fin ; hubo fallo
PUSH DS
DEC AX
MOV DS,AX
INC AX
MOV WORD PTR DS:[1],AX ; manipular PID
MOV WORD PTR DS:[16],20CDh ; simular PSP
PUSH ES
MOV CX,DS
MOV ES,CX
MOV CX,CS
DEC CX
MOV DS,CX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB ; copiar nombre de programa
POP ES
POP DS
CLC
UPPER_fin: RET
UPPER_alloc ENDP
La rutina UMB_alloc requiere una variable (xms_ins) que indique si est instalado el controlador de
memoria extendida, as como otra (gestor_XMS) con la direccin del mismo. La rutina UPPER_alloc necesita
una variable de palabra (alloc_strat) y otra de tipo byte (umb_state) en que apoyarse. El mtodo expuesto
consiste en modificar el PID para evitar que el DOS desasigne la memoria al acabar la ejecucin del
programa; tambin se coloca oportunamente la palabra 20CDh para simular un PSP y se asigna al nuevo
bloque de programa el mismo nombre que el del bloque de programa real. Los programas con autoinstalacin
en memoria superior deberan tener un parmetro (al estilo del /ML de los de DR-DOS) para forzar la
instalacin en memoria convencional si el usuario as lo requiere.
10.8. - PROGRAMAS RESIDENTES EN MEMORIA EXTENDIDA CON DR-DOS 6.0
El autntico empleo de memoria extendida para instalar programas residentes, aprovechando el modo
protegido en que est el ordenador con el controlador de memoria expandida instalado, no ser tratado en este
libro. En particular, algn emulador de coprocesador para 386 emplea esas tcnicas. Aqu nos limitaremos
a un objetivo ms modesto, en los primeros 64 Kb de memoria extendida accesibles desde DOS.
El DR-DOS 6.0 fue el primer sistema operativo DOS que permita instalar programas residentes en
los primeros 64 Kb de la memoria extendida, zona comnmente conocida por HMA. La ventaja de cargar
aqu las utilidades residentes es que no ocupan memoria, dicho entre comillas (al menos, no memoria
convencional ni superior). El inconveniente principal es que este rea es bastante limitada (en la prctica, algo
menos de 20 Kb libres) y la instalacin un tanto compleja. Ciertos programas del sistema (COMMAND,
KEYB, NLSFUNC, SHARE, TASKMAX) se pueden cargar en esta zona -algunos incluso lo hacen
automticamente-. Otro inconveniente es la complejidad de la instalacin: normalmente los programas se
cargarn en el segmento 0FFFEh con un offset variable y dependiente de la zona en que sean instalados. Por
ello, el primer requisito que han de cumplir es el de ser relocalizables: en la prctica, la rutina de instalacin
habr de montar el cdigo en memoria asignando posiciones absolutas a ciertos modos de direccionamiento.
El MS-DOS 5.0 tambin utiliza el HMA para cargar programas residentes; sin embargo no est tan
normalizado como en el caso del DR-DOS y es probable que en futuras versiones cambie el mtodo. De una
manera torpe, Microsoft eligi a DISPLAY.SYS para ocupar parte del rea que el propio DOS deja libre en
el HMA tras instalarse. Este fichero es utilizado en la conmutacin de pginas de cdigos (factible en
175 PROGRAMAS RESIDENTES
mquinas con EGA y VGA) para adaptar el juego de caracteres a ciertas lenguas. Hubiera sido mucho ms
inteligente elegir el KEYB y otros programas similares que casi todo el mundo tiene instalados.
Por consiguiente, limitaremos el estudio al caso del DR-DOS. La informacin que viene a
continuacin fue obtenida por la labor investigadora del autor de este libro, que la envi posteriormente a Ralf
Brown para incluirla en el Interrupt List. Conviene hacer ahora hincapi en que esta manera de gestionar el
HMA, a nivel de bloques de memoria, es propia del DR-DOS 6.0, y no de otras versiones anteriores de este
sistema, aunque probablemente s de las posteriores. Para comprobar que en una mquina est presente el DR-
DOS puede verificarse la presencia de una variable de entorno del tipo OS=DRDOS y otra VER=X.XX
con la versin. En todo caso, es mucho ms seguro utilizar una funcin del sistema al efecto:
MOV AX,4452h ; funcin exclusiva del DR-DOS
INT 21h
JC no_es_drdos ; probablemente es MS-DOS
CMP AX,1063h
JE drdos341
CMP AX,1065h
JE drdos5
CMP AX,1067h
JE drdos6
JA drdos_futuro
El DR-DOS 6.0 implementa un nuevo servicio para gestionar la carga de programas en el HMA. Con
las siguientes lneas:
MOV AX,4458h
INT 21h
MOV SI,ES:[BX+10h] ; variable exclusiva de DR-DOS
MOV DI,ES:[BX+14h] ; otra variable de DR-DOS
se obtiene en SI el offset al primer bloque libre de memoria en el HMA (ubicado en 0FFFFh:SI), y en DI
el offset al primer bloque ocupado de memoria en el HMA (en 0FFFFh:DI). Si el offset al primer bloque de
memoria libre es 0, significa que el DR-DOS no est instalado en el HMA o que no est instalado el
EMM386.SYS, con lo que no es posible instalar programas en el HMA. Slo si el kernel del DR-DOS reside
en el HMA se puede utilizar esta tcnica, para compartir la memoria con el sistema operativo.
En el HMA los bloques de memoria forman una cadena pero mucho ms simple que en los dems
tipos de memoria. En concreto, tienen una cabecera de slo 5 bytes: los dos primeros apuntan al offset del
siguiente bloque de memoria (cero si ste era el ltimo) y los dos siguientes el tamao de este bloque.
Tngase en cuenta que los bloques no han de estar necesariamente seguidos, por lo que la informacin del
tamao no debe emplearse para direccionar al siguiente bloque: para algo estn los primeros dos bytes!. El
quinto byte puede tomar un valor entre 0 y 5 para indicar el tipo de programa, por este orden: System,
KEYB, NLSFUNC, SHARE, TaskMAX, COMMAND. Como se ve, no se almacena el nombre en formato
ASCII sino con un cdigo. Los programas creados por el usuario pueden utilizar cualquiera de los cdigos,
aunque quiz el ms recomendable sea el 0 (de todas maneras, puede haber varios bloques con el mismo
cdigo).
Para cargar un programa residente aqu, primero se recorre la cadena de bloques libres hasta encontrar
uno del tamao suficiente -si lo hay, claro est-. A continuacin, se rebaja el tamao de este bloque
modificando su cabecera. Despus, se crea una cabecera para el nuevo bloque (que se sita al final del bloque
libre empleado, siempre tendiendo hacia direcciones altas) y se consulta la variable del DOS que indica el
primer bloque ocupado: el nuevo bloque creado habr de apuntarle; a su vez, esta variable del DOS ha de
ser actualizada ya que desde ahora el primer bloque ocupado (bueno, en realidad el ltimo) es el recin
creado. Ha de tenerse en cuenta que si lo que sobra del bloque libre que va a ser utilizado son menos de 16
bytes, se le debe desechar -porque as lo establece el sistema-, eliminndolo de la lista encadenada por el
simple procedimiento de hacer apuntar su predecesor a su sucesor. Lgicamente, si el bloque no tena
predecesor -si era el primer bloque- lo que hay que hacer es modificar la variable del DOS que indica el
primer bloque libre para que apunte a su sucesor. En general, se trata de gestionar una lista encadenada, lo
que ms que un problema de ensamblador lo es de sentido comn. No eliminar los posibles bloques libres
de menos de 16 bytes es saltarse una norma del sistema operativo y podra tener consecuencias imprevisibles
con futuros programas cargados.
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 memoria
convencional hacia el HMA, con una simple instruccin de transferencia. All -o antes de realizar la
transferencia- habr de relocalizarse el cdigo. Lo normal en los programas del sistema -y, por consiguiente,
lo ms recomendable- es que nuestras aplicaciones corran en la direccin 0FFFEh:XXXX y no la
0FFFFh:XXXX como en principio podra suponerse, aunque quiz se trate de un detalle irrelevante. Por
ltimo, se han de desviar los correspondientes vectores de interrupcin a las nuevas rutinas del programa
residente. Obviamente, el programa principal instalador deber acabar normalmente -y no residente-.
En general, la gestin del HMA es engorrosa porque el sistema realiza poco trabajo sucio,
delegndoselo al programa que quiera emplear este rea.
10.9. - EJEMPLO DE PROGRAMA RESIDENTE QUE UTILIZA LA BIOS.
El programa de ejemplo es un completo reloj-alarma residente. No posee intuitivas ventanas de
configuracin ni cientos de opciones, pero es sencillo y muy econmico en cuanto a consumo de memoria
se 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 opcin /A permite indicar una hora concreta para activar la alarma sonora o bien desactivar una
alarma (/A=OFF) previamente programada -por defecto, no hay alarma definida-. Los parmetros ON y OFF,
por s solos, se emplean para controlar la aparicin en pantalla o no del reloj -por defecto aparece nada ms
ser instalado-. El parmetro /T puede tomar un valor 1 para activar la seal horaria -por defecto-, 2 para
avisar a las medias, 4 para pitar a los cuartos y 5 para avisar cada cinco minutos; si vale 0 no se harn
seales de ninguna clase. Los parmetros opcionales X e Y permiten colocarlo en la posicin deseada dentro
de la pantalla: si /X=72 (valor por defecto), el reloj no aparecer realmente en esa coordenada sino lo ms
a la derecha posible en cada tipo de pantalla activa. Con /C se puede modificar el valor del byte de atributos
empleado para colorear el reloj. /ML fuerza la instalacin en memoria convencional. Por ltimo, con /U se
puede desinstalar de la memoria, en los casos en que sea posible.
Es posible ejecutarlo cuando ya est instalado con objeto de cambiar sus parmetros o programar la
alarma. Si las coordenadas elegidas estn fuera de la pantalla -ej., al cambiar a un modo de menos columnas
o filas- el resultado puede ser decepcionante (esto no sucede si /X=72). Si se produce un cambio de modo
de pantalla o una limpieza de la misma, el reloj seguir apareciendo correctamente casi al instante -se refresca
su impresin 4 veces por segundo-.
Una vez cargado, se puede controlar la presencia o no en pantalla pulsado Ctrl-Alt-R o AltGr-R (sin
necesidad de volver a ejecutar el programa con los parmetros ON u OFF). Cuando se expulsa el reloj de
la pantalla, se restaura el contenido anterior a la aparicin del reloj. Por ello, si se han producido cambios
en el monitor desde que apareci el reloj, el fragmento de pantalla restaurado puede quedar feo, aunque
tambin quedara feo de todas maneras si se rellenara de espacios en blanco. De hecho, esto ltimo es lo que
sucede cuando se trabaja con pantallas grficas.
Cuando comienza a sonar la alarma, estando o no el reloj en pantalla, se puede pulsar Ctrl-Alt-R o
AltGr-R para cancelarla; de lo contrario avisar durante 15 segundos. Este es el nico caso en que AltGr-R
o Ctrl-Alt-R no servir para activar o desactivar el reloj (una posterior pulsacin, s). Despus de haber
sonado, la alarma quedar desactivada y no volver a actuar, ni siquiera al cabo de 24 horas.
El programa utiliza el convenio CiriSOFT para detectar su presencia en memoria, por lo que es
desinstalable incluso aunque no sea el ltimo programa residente cargado, siempre que tras l se hayan
instalado slo programas del convenio (o al menos otros que no utilicen las mismas interrupciones). Posee
su propia rutina de desinstalacin (opcin /U), con lo que no es necesario utilizar la utilidad general de
desinstalacin. Tambin est equipado con las rutinas que asignan memoria superior XMS o, en su defecto,
177 PROGRAMAS RESIDENTES
memoria superior solicitada al DOS 5.0: por ello, aunque el fichero ejecutable ocupa casi 6 Kb, slo hacen
falta 1,5 Kb libres de memoria superior para instalarlo en este rea, lo que se realiza automticamente en
todos los entornos operativos que existen en la actualidad. Evidentemente, tambin se instala en memoria
convencional y sus requerimientos mnimos son un PC/XT y (recomendable) DOS 3.0 o superior.
Se utiliza la funcin de impresin en pantalla de la BIOS, con lo cual el reloj se imprime tambin
en las pantallas grficas (incluida SuperVGA). Por ello, es preciso desviar la INT 10h con objeto de detectar
su invocacin y no llamarla cuando ya se est dentro de ella (el reloj funciona ligado a la interrupcin
peridica y es impredecible el estado de la mquina cuando sta se produce). Si se anula la rutina que
controla INT 10h, en los modos grficos SuperVGA de elevada resolucin aparecen fuertes anomalas al
deslizarse la pantalla (por ejemplo, cuando se hace DIR) e incluso cuando se imprime; sin embargo, la BIOS
es dura como una roca (no se cuelga el ordenador, en cualquier caso). En los modos de pantalla normales
no habra tanta conflictividad, aunque conviene ser precavidos. La impresin del reloj se produce slo 4 veces
por segundo para no ralentizar el ordenador; aunque se realizara 18,2 veces por segundo tampoco se notara
un retraso perceptible. La interrupcin peridica es empleada no slo para imprimir el reloj sino tambin para
hacer sonar la msica, enviando las notas adecuadamente al temporizador a medida que se van produciendo
las interrupciones. No se utiliza INT 1Ch porque la considero menos segura y fiable que INT 8; sin embargo
se toma la precaucin de llamar justo al principio al anterior controlador de la interrupcin. De la manera que
est diseado el programa, es sencillo modificar las melodas que suenan, o crear una utilidad de msica
residente por interrupciones para amenizar el uso del PC. Los valores para programar el temporizador, segn
la nota que se trate, se obtienen de una tabla donde estn ya calculados, ya que sera difcil utilizar la coma
flotante al efecto. Al leer el teclado, se tiene la precaucin de comprobar si al pulsar Ctrl-Alt-R o AltGr-R
la BIOS o el KEYB han colocado un cdigo Alt-R en el buffer. Esto suele suceder a menos que el KEYB
no sea demasiado compatible (Ctrl-Alt equivale, en teora, a Alt a secas). Si as es, ese carcter se saca del
buffer para que no lo detecte el programa principal (si se sacara sin cerciorarse de que realmente est, en caso
de no estar el ordenador se quedara esperando una pulsacin de tecla). El mtodo utilizado para detectar la
pulsacin de AltGr en los teclados expandidos no funciona con el KEYB de DR-DOS 5.0/6.0 (excepto en
modo KEYB US), aunque esto es un fallo exclusivo de dicho controlador.
Sin duda, la parte ms engorrosa del programa es la interpretacin de los parmetros en la lnea de
comandos, tarea incmoda en ensamblador. An as, el programa es bastante flexible y se puede indicar, por
ejemplo, un parmetro /A=000020:3:48 para programar la alarma a las 20:03:48. Sin embargo, el uso del
ensamblador para este tipo de programas es ms que recomendable: adems de aumentar la fiabilidad del
cdigo, el consumo de memoria es ms que asequible, incluso en mquinas modestas.
;*********************************************************************
;* *
;* RCLOCK v2.3 (c) Septiembre 1992 CiriSOFT *
;* (c) Grupo Universitario de Informtica - Valladolid *
;* *
;* Utilidad de reloj-alarma residente *
;* *
;*********************************************************************
; ------------ Macros de propsito general
XPUSH MACRO RM
IRP reg, <RM>
PUSH reg
ENDM
ENDM
XPOP MACRO RM
IRP reg, <RM>
POP reg
ENDM
ENDM
; ------------ Programa
rclock SEGMENT
ASSUME 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
; ------------ Identificacin estandarizada del programa
program_id LABEL BYTE
segmento_real DW 0 ; segmento real donde ser cargado
offset_real DW 0 ; offset real " " "
longitud_total DW 0 ; zona de memoria ocupada (prrafos)
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 ; nmero Multiplex de este TSR
vectores_id DW tabla_vectores
extension_id DW tabla_extra
DB "*##*"
autor_nom_ver DB "CiriSOFT:RCLOCK:2.3",0
DB 4 ; nmero de vectores de interrupcin usados
tabla_vectores EQU $
DB 8 ; INT 8
ant_int08 LABEL DWORD ; direccin original
ant_int08_off DW 0
ant_int08_seg DW 0
DB 9 ; INT 9
ant_int09 LABEL DWORD ; direccin original
ant_int09_off DW 0
ant_int09_seg DW 0
DB 10h ; INT 10h
ant_int10 LABEL DWORD ; direccin original
ant_int10_off DW 0
ant_int10_seg DW 0
DB 2Fh ; INT 2Fh
ant_int2F LABEL DWORD ; direccin original
ant_int2F_off DW 0
ant_int2F_seg DW 0
tabla_extra LABEL BYTE
DW ctrl_exterior ; permitido control exterior
DW 0 ; campo reservado
ctrl_exterior LABEL BYTE
reubicabilidad DB 1 ; programa 100% reubicable
activacion DW visibilidad
; ------------ Tabla de perodos de las notas
;
178 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
; Datos para el perodo de las 89 notas, tomando como base un reloj de
; 1,19318 MHz (el del 8253). Las notas estn ordenadas ascendentemente
; como las de un piano, aunque las de cdigo 0 al 6 son silenciosas.
; Los datos (para notas mayores de 6) se han calculado con la frmula:
;
; 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 WORD
DW 37,37,37,37,37,37,37,30603
DW 28885,27264,25734,24290,22926,21640,20425,19279
DW 18197,17175,16211,15301,14442,13632,12867,12145
DW 11463,10820,10212,9639,9098,8587,8105,7650
DW 7221,6816,6433,6072,5731,5410,5106,4819
DW 4549,4293,4052,3825,3610,3408,3216,3036
DW 2865,2705,2553,2409,2274,2146,2026,1912
DW 1805,1704,1608,1518,1432,1352,1276,1204
DW 1137,1073,1013,956,902,852,804,759
DW 716,676,638,602,568,536,506,478
DW 451,426,402,379,358,338,319,301
DW 284
; ------------ Sonido
; formato de la msica:
; nmero de nota (0-88), duracin (en 1/18,2 seg.)
;
; Las primeras 7 notas son inaudibles y sirven para
; hacer pausas; si al byte de duracin 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,1
DB 47,2,52,2,54,3,1,1,51,2,54,2,59,3,1,1
DB 49,2,54,2,59,3,1,1,49,2,54,2,57,3,1,1
DB 49,2,52,2,56,3,1,1,52,2,56,2,61,3,1,1
DB 51,2,56,2,61,3,1,1,51,2,56,2,59,3,1,1
DB 51,2,54,2,57,3,1,1
DB 255
; tpica msica de las iglesias:
musica_horas DB 61,10,57,10,59,10,52,20,1,7,52,10,59,10,61,10,57
DB 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
; ------------ Parmetros bsicos del reloj
alarm_enable DB 0 ; por defecto, alarma OFF
hora_alarma LABEL BYTE
alarm_h DW "0 "
DB ":"
alarm_m DW "00"
DB ":"
alarm_s DW "00"
visibilidad DB 1 ; por defecto, reloj aparece
tipo_aviso DB 1 ; 1 -> seal horaria; 2 -> a las medias
; 4 -> a los cuartos; 5 -> cada 5 min.,
; 0 -> sin seal
c_x DB 72 ; coordenada X para el reloj
c_y DB 0 ; coordenada Y
color DB 14+4*16 ; tinta amarilla y fondo rojo
refresco DB 4 ; cada 4/18,2 sg. se reimprime el reloj
; ------------ Variables de control general
in10 DW 0 ; flag contador de entradas en INT 10h
cont_refresco DB 1 ; contador de INTs 8 a saltar
pagina DB 0 ; pgina de vdeo activa
modo_video DB 255 ; modo de vdeo activo (valor imposible
; para provocar inicializacin)
operacion DB 0 ; 8/9 para preservar/restaurar la zona
; de pantalla ocupada por el reloj
visible DB 1 ; 1 si el reloj est en pantalla
c_xx DB 0 ; coordenada X real del reloj
musica_sonando DB 0 ; a 1 si msica sonando
puntero_notas DW 0 ; apunta a la siguiente nota musical
; que va a sonar
contador_nota DB 0 ; INTs 8 que le quedan por sonar a la
; nota que est en curso
turno_blanco DB 0 ; a 1 si se procesa la nota separadora
; de notas
parando DB 0 ; contador para detener el sonido
; ------------ Cadenas para imprimir
hora_actual LABEL BYTE
horasH DB 0
horasL DB 0
DB ":"
minutosH DB 0
minutosL DB 0
DB ":"
segundosH DB 0
segundosL DB 0
DB 0
restaurar DB 8 DUP ( ) ; para almacenar el contenido previo
DB 8 DUP (7) ; de la pantalla (slo modo texto)
; ***************************************
; * *
; * C O D I G O R E S I D E N T E *
; * *
; ***************************************
; ------------ Rutina de gestin de INT 2Fh
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; s llama: darle informacin
LEA 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 FAR
INC CS:in10 ; indicar entrada en INT 10h
PUSHF
CALL CS:ant_int10
DEC CS:in10 ; fin de la INT 10h
IRET
ges_int10 ENDP
; ------------ Rutina de gestin de INT 9
ges_int09 PROC FAR
PUSH AX
IN AL,60h ; espiar cdigo de rastreo
PUSHF
CALL CS:ant_int09 ; llamar al KEYB
CMP AL,13h ; tecla R?
JNE fin_int09 ; no
PUSH DS
MOV AX,40h
MOV DS,AX
MOV AL,DS:[17h]
XOR AL,12 ; invertir bits de Ctrl y Alt
TEST AL,12
JZ ctrl_alt ; pulsado Ctrl-Alt
TEST BYTE PTR DS:[96h],8
JZ fin_int09ds ; no pulsado AltGr
ctrl_alt: STI
PUSH CS
POP DS
MOV AH,1
CMP musica_sonando,AH
JNE no_sonando ; no hay sonido
DEC AH
MOV parando,19 ; en 1 segundo, no ms notas
MOV musica_sonando,AH ; parar msica
MOV alarm_enable,AH ; desactivar alarma
CALL chiton ; silenciar altavoz
JMP ret_int09
no_sonando: XOR visibilidad,AH ; invertir visibilidad reloj
MOV cont_refresco,AH ; acelerar presencia/ausencia
ret_int09: XPUSH <BX, CX, BP>
MOV AH,1
INT 16h ; consultar estado del buffer
JZ no_hay_alt_r ; no se coloc Alt-R en buffer
MOV AH,0 ; este KEYB es ms compatible:
INT 16h ; sacar cdigo Alt-R del buffer
no_hay_alt_r: XPOP <BP, CX, BX>
fin_int09ds: POP DS
fin_int09: POP AX
IRET
ges_int09 ENDP
; ------------ Rutina de gestin de INT 8
ges_int08 PROC FAR
PUSHF
CALL CS:ant_int08 ; llamar al controlador previo
STI
XPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>
MOV AX,CS
MOV DS,AX
MOV ES,AX
CALL avisos_sonoros ; darlos si es necesario
DEC cont_refresco ; contador de INTs 8 a saltar
JNZ fin_int08 ; no han pasado las suficientes
MOV AL,refresco
MOV cont_refresco,AL ; recargar cuenta
CMP CS:in10,0
JNE fin_int08 ; estamos dentro de INT 10h
CALL obtiene_hora ; crear cadena con la hora
CMP visibilidad,1 ; reloj visible?
JNE restaurar? ; no
CMP visible,1 ; s, acaba de aparecer?
JE scr_getted ; no
MOV visible,1 ; en efecto: es preciso
MOV operacion,8 ; entonces tomar el contenido
CALL bios_scr_proc ; previo de la pantalla
scr_getted: CALL gestiona_fondo ; detectar cambio en pantalla
CALL print_reloj ; imprimir reloj
JMP fin_int08
restaurar?: CMP visible,1 ; reloj oculto recientemente?
JNE fin_int08 ; no, ya haba desaparecido
MOV visible,0 ; s:
MOV operacion,9
CALL 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 generacin de seales sonoras
avisos_sonoros PROC
CMP parando,0 ; "callar" durante 1 segundo?
JE avisos_on ; no
DEC parando ; s
JMP fin_avisos
avisos_on: CMP musica_sonando,1
JNE no_mas_notas ; no hay sonido en curso
DEC contador_nota
JNZ misma_nota ; sigue sonando todava la nota
CMP turno_blanco,0 ; pausa entre notas?
JE otra_nota ; no
MOV turno_blanco,0 ; s, slo una vez
MOV contador_nota,1 ; y durante una interrupcin
MOV AX,0 ; perodo inaudible
CALL programar_8253
179 PROGRAMAS RESIDENTES
misma_nota: JMP fin_avisos
otra_nota: MOV BX,puntero_notas ; puntero a la siguiente nota
INC BX
INC BX
MOV puntero_notas,BX ; actualizarlo
MOV BX,[BX] ; siguiente nota
MOV AL,BH
AND AL,128 ; aislar bit ms significativo
ROL AL,1 ; ahora el menos significativo
MOV turno_blanco,AL ; bit de separacin entre notas
AND BH,127 ; el resto de BH es la duracin
CMP BL,255 ; se acabaron las notas?
JNE sonar ; no, luego tocar esta nota
MOV musica_sonando,0 ; s
MOV alarm_enable,0 ; desactivar alarma
CALL chiton ; acallar altavoz
JMP no_mas_notas
sonar: INC BH
MOV contador_nota,BH ; INTs 8 que dura esa nota
XOR BH,BH ; BX = posicin en la tabla
SHL BX,1 ; la tabla es de palabras
MOV AX,[BX+tabla_periodos] ; perodo del sonido
CALL programar_8253
JMP fin_avisos
no_mas_notas: CMP alarm_enable,0
JE no_alarma ; alarma desactivada
LEA SI,hora_actual
LEA DI,hora_alarma
MOV CX,8
CLD
REP CMPSB ; hora actual = hora alarma?
JNE no_alarma ; no es la hora de la alarma
LEA AX,musica_alarma-2 ; s lo es
JMP fin_avisando
no_alarma: MOV CL,tipo_aviso
MOV SI,WORD PTR minutosH
MOV DI,WORD PTR segundosH
CMP SI,"00" ; hora en punto?
JNE media?
CMP DI,"00"
JNE media?
LEA AX,musica_horas-2 ; hora en punto
CMP 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 exactos
CMP 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 exactos
CMP CL,4 ; avisar a los cuartos?
JAE fin_avisando ; en efecto
cinco_min?: CMP minutosL,5 ; minutos mltiplos de 5?
JE cinc_quiza?
CMP minutosL,0
JNE fin_avisos
cinc_quiza?: CMP DI,"00"
JNE fin_avisos
LEA AX,musica_5min-2 ; minutos mltiplo exacto de 5
CMP CL,5 ; avisar cada 5 minutos?
JB fin_avisos ; pues no
fin_avisando: MOV puntero_notas,AX ; inicio de la meloda
MOV contador_nota,1 ; compensar futuro decremento
MOV musica_sonando,1 ; activar msica
fin_avisos: RET
avisos_sonoros ENDP
; ------------ Detener sonido por el altavoz
chiton PROC
IN AL,61h
AND AL,0FCh
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL ; altavoz silenciado
RET
chiton ENDP
; ------------ Preparar la produccin de sonido
programar_8253 PROC
PUSH AX
MOV AL,182 ; preparar canal 2
OUT 43h,AL
POP AX
JMP SHORT $+2
JMP SHORT $+2
OUT 42h,AL
MOV AL,AH
JMP SHORT $+2
JMP SHORT $+2
OUT 42h,AL ; canal #2 del 8253 programado
JMP SHORT $+2
JMP SHORT $+2
IN AL,61h
OR AL,3
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL ; activar sonido
RET
programar_8253 ENDP
; ------------ Controlar posible cambio de modo de pantalla o pgina
; de visualizacin activa, que afectan al fragmento de
; pantalla preservado antes de imprimir el reloj.
gestiona_fondo PROC
MOV AH,15
INT 10h ; modo de vdeo AL y pgina BH
CMP AL,modo_video ; ha cambiado modo de vdeo?
JNE clr_fondo? ; en efecto
CMP BH,pagina ; ha cambiado la pgina?
JNE clr_fondo? ; as es
RET ; no ha cambiado nada
clr_fondo?: MOV modo_video,AL ; actualizar nuevos parmetros
MOV pagina,BH
MOV BL,c_x ; coordenada X terica
CMP BL,72 ; es la 72?
JNE dejar_c_x ; no: se deja como tal
MOV BL,AH ; s: ajustar posicin lo ms
SUB BL,8 ; a la derecha posible
dejar_c_x: MOV c_xx,BL ; coordenada X real
CMP AL,3 ; modo de texto de color?
JBE get_fondo ; s: preservar rea pantalla
CMP AL,7 ; modo de texto monocromo?
JE get_fondo ; s: preservar rea pantalla
MOV CX,8 ; modo grfico: 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 blancos
INC BX
LOOP fondo_clr_ar ; acabar buffer
RET
get_fondo: MOV operacion,8 ; preservar zona de la pantalla
CALL bios_scr_proc
RET
gestiona_fondo ENDP
; ------------ Imprimir reloj en pantalla
print_reloj PROC
MOV AH,3
MOV BH,pagina
INT 10h ; coordenadas del cursor en DX
PUSH DX ; guardarlas para restaurarlas
MOV AH,2
MOV DL,c_xx
MOV DH,c_y ; coordenadas del reloj
MOV BH,pagina
INT 10h ; ubicar cursor
LEA BX,hora_actual ; cadena a imprimir
CALL bios_print ; imprimir reloj
POP DX ; recuperar posicin del cursor
MOV BH,pagina ; y pgina activa
MOV AH,2
INT 10h ; restaurar posicin del cursor
RET
print_reloj ENDP
; ------------ Crear cadena de caracteres con la hora actual
obtiene_hora PROC
PUSH DS
XOR AX,AX
MOV DS,AX
MOV SI,DS:[46Ch]
MOV DI,DS:[46Eh] ; contador de hora del BIOS
POP DS
MOV AX,1080
CALL mult32x16 ; DXDISI = DISI * 1080
MOV AX,19663
CALL divi48x15 ; DXDISI = DXDISI / 19663
PUSH DI
PUSH SI ; DISI = tics/18,2065 = seg.
MOV AX,3600
CALL divi48x15
MOV AX,SI ; AX = SI = horas
MOV CL,10
DIV CL ; pasar a BCD no empaquetado
OR AX,"00" ; pasar BCD a ASCII
CMP AL,0
JNE no_cero_izda
MOV AL, ; evitar cero a la izda en hora
no_cero_izda: MOV horasH,AL
MOV horasL,AH
MOV AX,3600
MUL SI ; DXAX = horas*3600
POP SI
POP DI
SUB SI,AX
SBB DI,DX ; DISI = segundos+minutos*60
MOV AX,SI
MOV CL,60
DIV CL ; AL = minutos
PUSH AX
MOV AH,0
MOV CL,10
DIV CL ; pasar binario a BCD
OR AX,"00" ; pasar BCD a ASCII
MOV minutosH,AL
MOV minutosL,AH
POP AX
MOV CL,60
MUL CL
SUB SI,AX ; SI = segundos restantes
MOV AX,SI
MOV CL,10
DIV CL ; pasar binario a BCD
OR AX,"00" ; pasar BCD a ASCII
MOV segundosH,AL
MOV segundosL,AH
RET
obtiene_hora ENDP
; ------------ Imprimir en color usando BIOS; sera ms rpido acceder
; a la memoria de vdeo, pero as tambin funciona en los
; modos grficos y en cualquier tarjeta (includo SVGA).
; La cadena ASCIIZ se entrega en DS:BX.
bios_print PROC
MOV AL,[BX] ; primer carcter a imprimir
INC BX
AND AL,AL
JZ fin_print ; byte 0 -> fin de cadena
PUSH BX
MOV AH,9 ; funcin de impresin
MOV BH,pagina
MOV BL,color
MOV CX,1 ; nmero de caracteres
INT 10h
CALL cursor_derecha ; avanzar cursor
POP BX
JMP bios_print ; siguiente carcter
fin_print: RET
bios_print ENDP
; ------------ Avanzar cursor a la derecha
cursor_derecha PROC
MOV BH,pagina
MOV AH,3
INT 10h ; DX = coordenadas actuales
INC DL ; incrementar X (sin controlar
MOV AH,2 ; posible desbordamiento)
MOV BH,pagina
INT 10h ; actualizar posicin cursor
RET
cursor_derecha ENDP
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 operacin inversa.
bios_scr_proc PROC
MOV AH,3
MOV BH,pagina
INT 10h ; obtener posicin del cursor
PUSH DX ; y preservarla para el final
MOV AH,2
MOV DL,c_xx
MOV DH,c_y ; coordenadas del reloj
MOV BH,pagina
INT 10h ; mover cursor
LEA SI,restaurar ; direccin del buffer
MOV CX,8 ; 8 caracteres
proximo_car: PUSH CX
MOV AH,operacion ; 8 ->preservar, 9 ->restaurar
MOV BH,pagina
MOV BL,[SI+8] ; preparar BL por si AH=9
MOV AL,[SI] ; preparar AL por si AH=9
MOV CX,1 ; preparar CX por si AH=9
INT 10h ; leer/escribir carcter
CMP operacion,8 ; se trataba de leer?
JNE opcont ; no
MOV [SI],AL ; s, guardar carcter ledo
MOV [SI+8],AH ; y su atributo
opcont: CALL cursor_derecha ; siguiente posicin
INC SI ; prximo carcter
POP CX
LOOP proximo_car ; acabar caracteres
POP DX ; recuperar coordenadas
MOV BH,pagina
MOV AH,2
INT 10h ; y reponer posicin del cursor
RET
bios_scr_proc ENDP
; ------------ Rutina para multiplicar nmeros de 32 por nmeros de 16
; bits generando resultado de 48 bits: DISI * AX = DXDISI
mult32x16 PROC
PUSH AX
XCHG SI,AX ; multiplicador en SI
MUL SI ; AX (parte baja) * SI --> DXAX
PUSH DX ; preservar resultado parcial
PUSH AX
MOV AX,DI
MUL SI ; AX (parte alta) * SI --> DXAX
POP SI ; parte baja del resultado
POP DI ; parte media del resultado
ADD DI,AX ; acumular resultado intermedio
ADC DX,0 ; arrastrar posible acarreo
POP AX
RET
mult32x16 ENDP
; ------------ Rutina para dividir nmeros de 48 por nmeros 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 PROC
PUSH BX
PUSH CX
XOR BX,BX
MOV CX,49 ; rotar 49 veces
divi48_15_cmp: CMP AX,BX
JA divi48_nosub
SUB BX,AX
STC
divi48_nosub: RCL SI,1
RCL DI,1
RCL DX,1
PUSHF
CMP CX,1
JE divi48_resto ; no rotar el resto al final!
POPF
RCL BX,1
PUSHF
divi48_resto: POPF
LOOP divi48_15_cmp
MOV AX,BX
POP CX
POP BX
RET
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 PROC
LEA DX,rclock_txt ; nombre del programa
CALL print
CALL obtener_param ; analizar posibles parmetros
JNC params_ok ; son correctos
CALL print_err ; no: informar del error/ayuda
JMP fin_noresid
params_ok: CALL inic_XMS ; considerar presencia de XMS
CALL residente? ; programa ya residente?
JC no_residente ; todava no
CMP param_u,1 ; s: solicitan desinstalarlo?
JE desinst ; as es
CALL adaptar_param ; parmetros en copia residente
JMP fin_noresid
desinst: MOV ES,tsr_seg
CALL rclock_off
MOV AH,ES:multiplex_id
CALL mx_unload ; desinstalarlo:
LEA DX,des_ok_txt
JNC mens_ok ; ha sido posible
LEA DX,des_no_ok_txt ; es imposible
mens_ok: CALL print
JMP fin_noresid
no_residente: CMP AX,0 ; reside una versin distinta?
JE instalable ; no: se admite instalacin
CALL error_version ; error de versin incompatible
JMP fin_noresid
instalable: CMP param_u,1 ; no residente: desinstalar?
JNE instalar ; no lo piden
LEA DX,imp_desins_txt ; lo piden, sern despistados!
CALL print
JMP fin_noresid
instalar: CALL mx_get_handle ; obtener entrada Multiplex
JNC handle_ok
LEA DX,nocabe_txt ; no quedan entradas
CALL print
JMP fin_noresid
handle_ok: MOV multiplex_id,AH ; entrada multiplex para RCLOCK
LEA DX,instalado_txt ; mensaje de instalacin
CALL print
CALL preservar_ints ; tomar nota de vectores
CMP param_ml,0 ; se indic parmetro /ML?
JNE instalar_ml ; en efecto
MOV AX,parrafos_resid ; prrafos de memoria precisos
CALL UMB_alloc ; pedir memoria superior XMS
JNC instalar_umb ; hay la suficiente
MOV AX,parrafos_resid
CALL UPPER_alloc ; pedir memoria superior DOS 5
JC instalar_ml ; no hay la suficiente
STC ; indicar que usa memoria DOS
instalar_umb: MOV ES,AX ; segmento del bloque UMB
MOV DI,0 ; ES:0 zona a donde reubicar
CALL inicializa_id ; inicializar identificacin
CALL reubicar_prog ; reubicar el programa a ES:DI
CALL activar_ints ; interceptar vectores
JMP fin_noresid ; programa instalado arriba
instalar_ml: STC ; indicar que usa memoria DOS
MOV DI,60h ; instalacin mem. convencional
CALL inicializa_id ; inicializar identificacin
CALL reubicar_prog ; reubicar programa a ES:DI
CALL activar_ints ; interceptar vectores
CALL free_environ ; liberar espacio de entorno
MOV DX,parrafos_resid ; tamao zona residente, desde
ADD DX,6 ; PSP:60h bytes (6 prrafos)
MOV AX,3100h
INT 21h ; terminar residente
fin_noresid: MOV AX,4C00h
INT 21h ; terminar no residente
main ENDP
;*********************************************************
;* *
;* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION *
;* *
;*********************************************************
; ------------ Admitir posibles parmetros en la lnea de comandos
obtener_param PROC
MOV BX,81h ; apuntar a zona de parmetros
otro_pmt_mas: CALL saltar_esp ; saltar delimitadores
JNC otro_pmt ; quedan ms parmetros
JMP fin_proc_pmt ; no ms parmetros
otro_pmt: CMP AL,/
JE pmt_barrado ; parmetro precedido por /
CMP AL,?
MOV DH,128 ; cdigo de error para ayuda
JNE pmt_nobarrado
JMP mal_proc_pmt ; error de solicitud de ayuda
pmt_nobarrado: OR WORD PTR [BX]," " ; pasar a minsculas
CMP WORD PTR [BX],"no" ; parmetro ON?
JNE pmt_off?
MOV visibilidad,1
MOV visible,1
MOV param_onoff,1
ADD BX,2
JMP otro_pmt_mas
pmt_off?: CMP WORD PTR [BX],"fo" ; parmetro OFx?
MOV DH,0 ; cdigo de error
JNE mal_proc_pmt
OR BYTE PTR [BX+2], ; pasar a minsculas
CMP BYTE PTR [BX+2],f ; parmetro OFF?
JNE mal_proc_pmt
MOV visibilidad,0
MOV visible,0
MOV param_onoff,1
ADD BX,3
JMP otro_pmt_mas
pmt_barrado: INC BX
MOV AL,[BX] ; letra del parmetro
CMP AL,13 ; fin de mandatos?
MOV DH,0
JE mal_proc_pmt ; falta parmetro
CMP AL,?
MOV DH,128 ; cdigo de error para ayuda
JE mal_proc_pmt
OR AL, ; poner en minsculas
CMP AL,h
JE mal_proc_pmt
CMP AL,a
JNE pmt_no_A
JMP pmt_A ; parmetro /A=hh:mm:ss|ON|OFF
pmt_no_A: CMP AL,u
JE pmt_U
MOV SI,[BX] ; parmetro de dos caracteres?
OR SI," " ; mayusculizar
CMP SI,"lm" ; parmetro /ML?
JNE no_ml
MOV param_ml,1 ; en efecto
ADD BX,2
JMP otro_pmt_mas
no_ml: PUSH AX
CALL get_num ; obtener valor del parmetro
POP CX ; CL tipo de parmetro
MOV DH,7 ; cdigo de error
JC mal_proc_pmt ; parmetro incorrecto
CMP CL,t
JE pmt_T
CMP CL,x
JE pmt_X
CMP CL,y
JE pmt_Y
CMP CL,c
MOV DH,2 ; cdigo de error
JE pmt_C
mal_proc_pmt: STC ; error en parmetro(s)
RET
fin_proc_pmt: CLC ; parmetros procesados
RET
pmt_U: MOV param_u,1
INC BX
JMP otro_pmt_mas
181 PROGRAMAS RESIDENTES
pmt_T: MOV param_t,1
MOV tipo_aviso,AL
CMP AX,5
MOV DH,3
JA mal_proc_pmt
CMP AL,3
JE mal_proc_pmt
JMP otro_pmt_mas
pmt_X: MOV param_x,1
MOV c_x,AL
CMP AX,124 ; admitir hasta 132 columnas
MOV DH,4
JA mal_proc_pmt
JMP otro_pmt_mas
pmt_Y: MOV param_y,1
MOV c_y,AL ; y hasta 60 lneas
CMP AX,59
MOV DH,5
JA mal_proc_pmt
JMP otro_pmt_mas
pmt_C: MOV param_c,1
MOV color,AL
CMP AX,255
MOV DH,6
JA mal_proc_pmt
JMP otro_pmt_mas
pmt_A: PUSH BX
CALL get_num
JNC bien_pmt_A
POP BX
ADD BX,2
OR WORD PTR [BX]," " ; pasar a minsculas
CMP WORD PTR [BX],"no" ; parmetro ON?
JNE pmt_A_off?
MOV alarm_enable,1
MOV param_a_onoff,1
ADD BX,2
JMP otro_pmt_mas
pmt_A_off?: CMP WORD PTR [BX],"fo" ; parmetro OFx?
MOV DH,0 ; cdigo de error
JNE mal_proc_pm
OR BYTE PTR [BX+2], ; pasar a minsculas
CMP BYTE PTR [BX+2],f ; parmetro OFF?
JNE mal_proc_pm
MOV alarm_enable,0
MOV param_a_onoff,1
ADD BX,3
JMP otro_pmt_mas
bien_pmt_A: MOV param_a,1
ADD SP,2 ; sacar BX de la pila
CMP AX,23
JA mal_pmtA
MOV CL,10
DIV CL ; pasar binario a BCD
ADD AX,"00" ; pasar BCD a ASCII
CMP AL,0
JNE no_cero_izda2
MOV AL, ; evitar cero a la izda. hora
no_cero_izda2: MOV BYTE PTR alarm_h,AL
MOV BYTE PTR alarm_h+1,AH
DEC BX
CALL get_num
JC mal_pmtA
CMP AX,59
JA mal_pmtA
MOV CL,10
DIV CL ; pasar binario a BCD
ADD AX,00 ; pasar BCD a ASCII
MOV BYTE PTR alarm_m,AL
MOV BYTE PTR alarm_m+1,AH
DEC BX
CALL get_num
JC mal_pmtA
CMP AX,59
JA mal_pmtA
MOV CL,10
DIV CL ; pasar binario a BCD
ADD AX,00 ; pasar BCD a ASCII
MOV BYTE PTR alarm_s,AL
MOV BYTE PTR alarm_s+1,AH
MOV alarm_enable,1
JMP otro_pmt_mas
mal_pmtA: MOV DH,1
mal_proc_pm: JMP mal_proc_pmt
obtener_param ENDP
; ------------ Saltar espacios, tabuladores, ... buscando un parmetro
saltar_esp: MOV AL,[BX]
INC BX
CMP AL,9 ; carcter tabulador
JE saltar_esp
CMP AL,32 ; espacio en blanco
JE saltar_esp
CMP AL,0Dh ; fin de zona de parmetros
JE fin_param
DEC BX ; puntero al primer carcter
CLC ; hay parmetro
RET
fin_param: STC ; no hay parmetro
RET
; ------------ Obtener nmero chequeando delimitadores /= y /:
get_num: INC BX
MOV AL,[BX]
INC BX
CMP AL,=
JE delimit_ok
CMP AL,:
JE delimit_ok
err_sintax: STC ; sintaxis incorrecta
RET
delimit_ok: MOV AL,[BX]
CALL obtener_num
JC err_sintax
INC BX
RET
; ------------ Extraer n de 16 bits y depositarlo en AX; al final, el
; puntero (BX) apuntar al final del nmero y CF=1 si el
; nmero era incorrecto.
obtener_num PROC
CMP AL,0Dh ; fin zona parmetros y nmero
JE fin_num
CMP AL,32 ; fin nmero
JE fin_num
CMP AL,9 ; fin nmero
JE fin_num
CMP AL,/ ; fin nmero (otro parmetro)
JE fin_num
CMP AL,: ; fin nmero (otro dato)
JE fin_num
INC BX
MOV AL,[BX]
JMP obtener_num
fin_num: MOV SI,BX
DEC SI
XOR DX,DX
MOV AX,1 ; AX = 10 elevado a la 0 = 1
otro_car: DEC BX ; prximo carcter a procesar
MOV CL,[BX]
CMP CL,=
JE ok_num ; delimitador: fin de nmero
CMP CL,:
JE ok_num ; delimitador: fin de nmero
CMP CL,.
JNE no_millar ; saltar los puntos de millar
CMP AX,1000
JE otro_car
JMP mal_num ; separador millar descolocado
no_millar: CMP CL,0
JB mal_num
CMP CL,9
JA mal_num
SUB CL,0 ; pasar ASCII a binario
MOV CH,0 ; CX = 0 .. 9
PUSH AX ; AX = 10 elevado a la N
AND AX,AX
JNZ multiplica
AND CL,CL
JNZ mal_num_pop ; a la izda slo permitir ceros
multiplica: PUSH DX ; tras completar 5 dgito
MUL CX
POP DX
JC mal_num_pop
ADD DX,AX ; DX = DX + digito (CX) * 10 ^ N (AX)
JC mal_num_pop
POP AX
CMP AX,10000
JNE potencia ; AX*10 no se desbordar
MOV AX,0 ; como prximo dgito<>0 a
JMP otro_car ; la izda ... pobre usuario
potencia: MOV DI,10
PUSH DX ; no manchar DX al multiplicar
MUL DI ; AX = AX elevado a la (N+1)
POP DX
JMP otro_car
mal_num_pop: POP AX ; reequilibrar pila
mal_num: MOV BX,SI ; nmero mayor de 65535
STC ; condicin de error
RET
ok_num: MOV BX,SI ; nmero correcto
MOV AX,DX ; resultado
CLC ; condicin de Ok.
RET
obtener_num ENDP
; ------------ Imprimir errores en los parmetros
print_err PROC
CMP DH,128 ; error: DH cdigo de error
JNE no_ayuda
LEA DX,ayuda_txt
JMP pr_ret
no_ayuda: MOV AH,DH
MOV AL,CL ; CL=parmetro en errores 1..6
LEA DX,ini_err_txt
CALL print
LEA BX,tabla_err ; tabla de mensajes de error
PUSH AX
MOV AL,AH
SHL AL,1 ; AL = AL * 2
XOR AH,AH ; AX = AL
ADD BX,AX
MOV DX,[BX] ; direccin del texto
CALL print
POP AX ; recuperar cdigo y parmetro
CMP AH,1
JBE no_pr_pmt ; error 0 1
MOV DL,AL
MOV AH,2
INT 21h ; imprimir letra del parmetro
no_pr_pmt: LEA DX,fin_err_txt
pr_ret: CALL print
RET
print_err ENDP
; ------------ Ya est instalada otra versin distinta del programa
error_version PROC
PUSH ES
LEA DX,mal_ver_txt1
CALL print
LES DI,tsr_dir
MOV AL,:
MOV CL,255
CLD
REPNE SCASB
REPNE SCASB
MOV DL,ES:[DI] ; nmero de versin
MOV AH,2
INT 21h
MOV DL,.
MOV AH,2
INT 21h
MOV DL,ES:[DI+2] ; revisin
MOV AH,2
INT 21h
LEA DX,mal_ver_txt2
CALL print
POP ES
RET
error_version ENDP
; ------------ Considerar presencia de controlador XMS
inic_XMS PROC
MOV AX,4300h
INT 2Fh ; chequear presencia XMS
CMP AL,80h
JNE XMS_ausente ; no instalado
PUSH ES
MOV AX,4310h
INT 2Fh ; s: obtener su direccin
182 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV XMS_off,BX ; y preservarla
MOV XMS_seg,ES
MOV xms_ins,1
POP ES
RET
XMS_ausente: MOV xms_ins,0
RET
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
; identificacin de la copia residente. Si CF=1, el
; programa no reside an (AX=0) o reside pero en otra
; versin distinta (AX=1).
residente? PROC
PUSH CX
PUSH SI
PUSH DI
PUSH ES
PUSH AX
LEA DI,autor_nom_ver ; identificacin del programa
MOV SI,DI
MOV AL,0
MOV CL,255
CLD
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamao autor+programa+versin
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de bsqueda
CALL mx_find_tsr ; buscar si est en memoria
MOV tsr_off,DI ; anotar la direccin programa
MOV tsr_seg,ES ; por si estaba instalado
POP AX
JNC resid_ok ; CF=0 -> programa ya residente
POP ES
PUSH ES
LEA DI,autor_nom_ver
MOV SI,DI
MOV AL,:
MOV CL,255
REPNE SCASB
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamao autor+programa
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de bsqueda
CALL mx_find_tsr ; buscar si est en memoria
MOV tsr_off,DI ; anotar direccin del programa
MOV tsr_seg,ES ; por si instalada otra versin
MOV AX,0
JC resid_ok ; CF=1, AX=0 -> no residente
MOV AX,1
STC ; CF=1, AX=1 -> s: otra vers.
resid_ok: POP ES
POP DI
POP SI
POP CX
RET
residente? ENDP
; ------------ Adaptar parmetros de un RCLOCK ya instalado.
; Slo se adaptan los indicados, testeando la variable
; que indica si se han especificado.
adaptar_param PROC
LEA DX,ya_install_txt
CALL print
MOV ES,tsr_seg
CMP param_onoff,1
JNE param_a?
MOV AL,visibilidad ; parmetros ON u OFF:
MOV ES:visibilidad,AL ; adaptar visibilidad del reloj
param_a?: CMP param_a,1
JNE param_aonoff?
LEA SI,alarm_enable ; parmetro /A=hh:mm:ss
MOV DI,SI ; programar nueva alarma
MOV CX,9
CLD
REP MOVSB
param_aonoff?: CMP param_a_onoff,1
JNE param_t?
MOV AL,alarm_enable ; parmetro /A=ON o /A=OFF:
MOV ES:alarm_enable,AL ; actualizar estado alarma
param_t?: CMP param_t,1
JNE param_x?
MOV AL,tipo_aviso ; parmetro /T:
MOV ES:tipo_aviso,AL ; actualizar byte
param_x?: CMP param_x,1
JNE param_y?
MOV AL,ES:visibilidad ; parmetro /X:
MOV ES:visibilidad,0 ; eliminar reloj de pantalla
CALL espera_reloj ; esperar a que se vaya
MOV AH,c_x
MOV ES:c_x,AH ; actualizar coordenada X
MOV ES:c_xx,AH
MOV ES:visibilidad,AL ; restaurar visibilidad
param_y?: CMP param_y,1
JNE param_c?
MOV AL,ES:visibilidad ; parmetro /Y:
MOV ES:visibilidad,0 ; eliminar reloj de pantalla
CALL espera_reloj ; esperar a que se vaya
MOV AH,c_y
MOV ES:c_y,AH ; actualizar coordenada Y
MOV ES:visibilidad,AL ; restaurar visibilidad
param_c?: CMP param_c,1
JNE param_adapted
MOV AL,color ; parmetro /C:
MOV ES:color,AL ; actualizar byte de atributos
param_adapted: RET
adaptar_param ENDP
; ------------ Eliminar el RCLOCK de la pantalla
rclock_off PROC
MOV ES:visibilidad,0
CALL espera_reloj ; eliminarlo de la pantalla
MOV ES:musica_sonando,0
IN AL,61h ; parar posible sonido
AND AL,0FCh
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL
RET
rclock_off ENDP
; ------------ Esperar una INT 8 que refresque la impresin del reloj
; en pantalla si sta -la impresin- est habilitada.
espera_reloj PROC
PUSH DS
PUSH AX
PUSH CX
MOV CL,refresco ; n tics suficientes para que
MOV CH,0 ; aparezca en pantalla
ADD CX,2 ; redondear hacia arriba
MOV AX,40h
MOV DS,AX
STI
espera_tics: MOV AX,DS:[6Ch]
espera_tic: CMP AX,DS:[6Ch]
JE espera_tic
LOOP espera_tics
POP CX
POP AX
POP DS
RET
espera_reloj ENDP
; ------------ Preservar vectores de interrupcin previos
preservar_INTs PROC
PUSH ES
PUSH DI
LEA DI,tabla_vectores
MOV CL,[DI-1]
MOV CH,0 ; CX vectores interceptados
otro_vector: PUSH CX
PUSH DI
MOV AH,35h
MOV AL,[DI]
INT 21h ; obtener vector de INT xx
POP DI
POP CX
MOV [DI+1],BX ; anotar donde apunta
MOV [DI+3],ES
ADD DI,5
LOOP otro_vector ; repetir con los restantes
POP DI
POP ES
RET
preservar_INTs ENDP
; ------------ Liberar espacio de entorno
free_environ PROC
PUSH ES
MOV ES,DS:[2Ch] ; direccin del entorno
MOV AH,49h
INT 21h ; liberar espacio de entorno
POP ES
RET
free_environ ENDP
; ------------ Reservar bloque de memoria superior del n prrafos AX,
; devolviendo en AX el segmento donde est. CF=1 si no
; est instalado el gestor XMS (AX=0) o hay un error (AL
; devuelve el cdigo de error del controlador XMS).
UMB_alloc PROC
PUSH BX
PUSH CX
PUSH DX
CMP xms_ins,1
JNE no_umb_disp ; no hay controlador XMS
MOV DX,AX ; nmero de prrafos
MOV AH,10h ; solicitar memoria superior
CALL gestor_XMS
CMP AX,1 ; ha ido todo bien?
MOV AX,BX ; segmento UMB/cdigo de error
JNE XMS_fallo ; fallo
POP DX ; ok
POP CX
POP BX
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo: POP DX
POP CX
POP BX
STC
RET
UMB_alloc ENDP
; ------------ Reservar memoria superior, con DOS 5.0, del tamao
; solicitado (AX prrafos). Si no hay bastante CF=1,
; en caso contrario devuelve el segmento en AX.
UPPER_alloc PROC
PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin ; necesario DOS 5.0 mnimo
UPPER_existe: PUSH AX ; preservar prrafos...
MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL ; preservar estado UMB
MOV AX,5803h
MOV BX,1
INT 21h ; conectar cadena UMBs
MOV AX,5801h
MOV BX,41h
INT 21h ; High Memory best fit
POP BX ; ...prrafos requeridos
MOV AH,48h
INT 21h ; asignar memoria
PUSHF
PUSH AX ; guardado el resultado
MOV AX,5801h
MOV BX,alloc_strat
INT 21h ; restaurar estrategia
MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h ; restaurar estado cadena UMB
183 PROGRAMAS RESIDENTES
POP AX
POPF
JC UPPER_fin ; hubo fallo
PUSH DS
DEC AX
MOV DS,AX
INC AX
MOV WORD PTR DS:[1],AX ; manipular PID
MOV WORD PTR DS:[16],20CDh ; simular PSP
PUSH ES
MOV CX,DS
MOV ES,CX
MOV CX,CS
DEC CX
MOV DS,CX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB ; copiar nombre de programa
POP ES
POP DS
CLC
UPPER_fin: RET
UPPER_alloc ENDP
; ------------ 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 PROC
PUSHF
MOV segmento_real,ES ; anotar segmento del bloque
MOV offset_real,DI ; dem con el offset
MOV longitud_total,parrafos_resid
MOV CL,4
MOV AX,DI
SHR AX,CL
ADD longitud_total,AX ; consumir desde offset=0
MOV AL,1
POPF ; CF=0: usar memoria UMB XMS
JNC info_ok
DEC AL ; usar memoria convencional
info_ok: OR info_extra,AL
RET
inicializa_id ENDP
; ------------ Reubicar programa residente a su direccin definitiva.
reubicar_prog PROC
PUSH DI
LEA SI,ini_residente
MOV CX,bytes_resid
CLD
ADD SI,2 ; no copiar primera palabra
ADD DI,2 ; respetar primera palabra
SUB CX,2
REP MOVSB
POP DI
RET
reubicar_prog ENDP
; ------------ Desviar vectores de interrupcin 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 atrs (DI se supone
; mltiplo de 16). El segmento inicial es ES.
activar_INTs PROC
PUSH CX
PUSH DS ; preservar DS para el retorno
MOV AX,100h
SUB AX,DI ; AX = 100h-DI
MOV CL,4
SHR AX,CL ; AX = (100h-DI)/16
MOV CX,ES
SUB CX,AX
MOV DS,CX
LEA SI,offsets_ints
MOV CX,CS:[SI] ; CX vectores a desviar
ADD SI,2
desvia_otro: MOV AL,CS:[SI] ; nmero del vector en curso
MOV DX,CS:[SI+1] ; obtener offset
MOV AH,25h
INT 21h ; desviar INT xx a DS:DX
ADD SI,3
LOOP desvia_otro
POP DS
POP CX
RET
activar_INTs ENDP
; ------------ Buscar entrada no usada en la interrupcin Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta tcnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
mx_no_hueco: STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
; ------------ Buscar un TSR por la interrupcin Multiplex. A la
; entrada, DS:SI cadena de identificacin del programa
; (CX bytes) y ES:DI protocolo de bsqueda (normalmente
; 1492h:1992h). A la salida, si el TSR ya est instalado,
; CF=0 y ES:DI apunta a la cadena de identificacin del
; mismo. Si no, CF=1 y ningn registro alterado.
mx_find_tsr PROC
MOV AH,0C0h
mx_rep_find: PUSH AX
PUSH CX
PUSH SI
PUSH DS
PUSH ES
PUSH DI
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl ; no hay TSR ah
CLD
PUSH DI
REP CMPSB ; comparar identificacin
POP DI
JE mx_tsr_found ; programa buscado hallado
mx_skip_hndl: POP DI
POP ES
POP DS
POP SI
POP CX
POP AX
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4 ; sacar ES y DI de la pila
POP DS
POP SI
POP CX
POP AX
CLC
RET
mx_find_tsr ENDP
; ------------ Eliminar TSR del convenio si es posible. A la entrada,
; en AH se indica la entrada Multiplex; a la salida, CF=1
; si fue imposible y CF=0 si se pudo. Se corrompen todos
; los registros salvo los de segmento. En caso de fallo
; al desinstalar, AL devuelve el vector culpable.
mx_unload PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX ; BP=entrada Multiplex del TSR
MOV CX,2
mx_ul_pasada: PUSH CX ; siguiente pasada
LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0 ; CX = n vectores
mx_ul_masvect: POP AX
PUSH AX ; pasada en curso
DEC AL
PUSH CX
mx_ul_2f: MOV AL,ES:[SI] ; vector en curso
JNZ mx_ul_pasok
CMP CX,1 ; ltimo vector?
JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL ; INT 2Fh?
JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f
mx_ul_noult: CMP AL,2Fh ; restaurar INT 2Fh?
JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f
mx_ul_pasok: PUSH ES
PUSH AX
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h ; vector en ES:BX
POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX ; INT xx en DX (aprox.)
MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI
PUSH ES:[DI-12]
MOV DI,ES:[DI-8] ; offset a la tabla de vectores
MOV CL,ES:[DI-1]
MOV CH,0 ; nmero de vectores en CX
mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect ; este TSR usa vector analizado
ADD DI,5
LOOP mx_ul_buscav
ADD SP,4 ; no lo usa
JMP mx_ul_otro
mx_ul_usavect: POP CX ; tamao del TSR
POP BX ; segmento del TSR
CMP DX,BX
JB mx_ul_otro ; la INT xx no le apunta
ADD BX,CX
CMP DX,BX
JA mx_ul_otro ; la INT xx le apunta
PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP ; es el propio TSR?
POP AX
JNE mx_ul_chain ; no
POP ES ; s: posible reponer vector!
POP CX
POP BX
PUSH BX
PUSH CX
PUSH ES
DEC BX
JNZ mx_ul_norest ; no es la segunda pasada
POP ES ; segunda pasada...
PUSH ES
PUSH DS
MOV BX,CS:mx_ul_tsroff ; restaurar INTs
MOV DS,CS:mx_ul_tsrseg
CLI
184 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: POP ES
POP CX
ADD SI,5 ; siguiente vector
DEC CX
JZ mx_unloadable ; no ms, desinstal-ar/ado!
JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direccin
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX ; INT xx en DX (aprox.)
MOV AH,0BFh
mx_ul_otro: INC AH ; a por otro TSR
JZ mx_ul_exitnok ; se acabaron!
JMP mx_ul_masmx
mx_ul_exitnok: ADD SP,6 ; equilibrar pila
POP ES
STC
RET ; imposible desinstalar
mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok ; desinstalado
JMP mx_ul_pasada ; 1 pasada exitosa: por la 2
mx_ul_exitok: TEST ES:info_extra,111b ; tipo de instalacin?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml ; cargado en RAM convencional
CMP xms_ins,1
JNE mx_ul_freeml ; no hay controlador XMS (?)
MOV DX,ES
MOV AH,11h
CALL gestor_XMS ; liberar memoria superior
POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h ; liberar bloque de memoria ES:
POP ES
CLC
RET
mx_ul_tsrcv?: PUSH AX ; es TSR del convenio?...
PUSH ES
PUSH DI
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4 ; CF=0
POP AX
RET
mx_ul_ncvexit: POP DI ; ...no es TSR del convenio
POP ES
POP AX
STC ; CF=1
RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload ENDP
; ------------ imprimir cadena en DS:DX delimitada por un $
print PROC
PUSH AX
MOV AH,9
INT 21h
POP AX
RET
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 XMS
gestor_XMS LABEL DWORD ; direccin del controlador XMS
XMS_off DW 0
XMS_seg DW 0
alloc_strat DW 0 ; estrategia asignacin (DOS 5)
umb_state DB 0 ; estado de bloques UMB (DOS 5)
tsr_dir LABEL DWORD ; direccin de la copia residente
tsr_off DW 0
tsr_seg DW 0
offsets_ints DW 4 ; nmero de vectores interceptados
DB 8 ; tabla de offsets de los vectores
DW ges_int08 ; de interrupcin interceptados
DB 9
DW ges_int09
DB 10h
DW ges_int10
DB 2Fh
DW ges_int2F
param_ml DB 0 ; a 1 si se indic /ML
param_u DB 0 ; a 1 si se indic /U
param_onoff DB 0 ; a 1 si se indic ON u OFF
param_a DB 0 ; a 1 si se indic /A
param_a_onoff DB 0 ; a 1 si se indic /A=ON o /A=OFF
param_t DB 0 ; a 1 si se indic /T
param_x DB 0 ; a 1 si se indic /X
param_y DB 0 ; a 1 si se indic /Y
param_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,10
DB " - Parmetros indicados actualizados."
DB 13,10,"$"
tabla_err DW err0_txt, err1_txt, err2_txt, err3_txt
DW 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 "parmetro no admitido: /$"
err3_txt DB "parmetro distinto de 0, 1, 2, 4 5: /$"
err4_txt DB "parmetro fuera del rango 0..124: /$"
err5_txt DB "parmetro fuera del rango 0..59: /$"
err6_txt DB "parmetro fuera del rango 0..255: /$"
err7_txt DB "necesario numro en el parmetro /$"
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 versin $"
mal_ver_txt2 DB " de este programa.",13,10,7,"$"
des_ok_txt DB " desinstalado.",13,10,"$"
des_no_ok_txt DB 13,10," - Desinstalacin imposible (se ha "
DB "instalado despus un programa"
DB 13,10," que no respeta el convenio y tiene "
DB "alguna interrupcin comn).",13,10,7,"$"
imp_desins_txt DB 13,10," - Programa an no instalado: "
DB "imposible desinstalarlo.",13,10,"$"
nocabe_txt DB ": Instalacin imposible.",13,10
DB " Ya hay 64 programas residentes con la "
DB "misma tcnica.",13,10,"$"
ayuda_txt LABEL BYTE
DB 13,9,9,"RCLOCK v2.3 - Utilidad de reloj-alarma residente.",13,10
DB " (c) 1992 CiriSOFT, (c) Grupo Universitario de Informtica - "
DB "Valladolid.",13,10,10
DB " RCLOCK [/A=hh:mm:ss|OFF|ON] [ON|OFF] [/T=] [/X=] [/Y=] [/C=] "
DB "[/U] [/ML] [/?|H]",13,10,10
DB " /A Indica una hora de alarma y activa la misma; con /A=ON o "
DB "/A=OFF se puede",13,10
DB " controlar a posteriori la habilitacin de la alarma. Tras "
DB "sonar, quedar",13,10
DB " desactivada (hasta un posterior /A=ON o bien /A=hh:mm:ss). "
DB "Se puede can-",13,10
DB " celar siempre el sonido pulsando Ctrl-Alt-R o AltGr-R "
DB "durante el mismo.",13,10
DB " ON y OFF Controlan la aparicin del reloj en pantalla. "
DB "Equivalente a pulsar",13,10
DB " AltGr-R Ctrl-Alt-R con el reloj ya instalado y sin "
DB "sonido en curso.",13,10
DB " /T Indica el nivel de avisos sonoros del reloj: 0 ninguno; 1 "
DB "seal horaria;",13,10
DB " 2, a las medias; 4 a los cuartos y 5 cada cinco minutos. "
DB "Cada uno de los",13,10
DB " niveles incluye a su vez a los anteriores. Por defecto, "
DB "/T=1.",13,10
DB " /X e /Y Indican las coordenadas de pantalla donde se "
DB "imprimir el reloj; su",13,10
DB " valor vara segn el modo de pantalla. Las coordenadas son "
DB "siempre refe-",13,10
DB " ridas al modo texto, aunque la pantalla est en modo "
DB "grfico. Para /X=72",13,10
DB " (valor por defecto) el reloj no se imprimir realmente en "
DB "la columna 72,",13,10
DB " sino lo ms a la derecha posible segn el modo de vdeo "
DB "activo.",13,10
DB " /C Indica los atributos de color en que aparece el reloj."
DB 13,10
DB " /U Permite desinstalar el programa de la memoria si ello es "
DB "posible.",13,10
DB " /ML Fuerza la instalacin en memoria convencional -por defecto "
DB "se cargar en",13,10
DB " memoria superior XMS o en su ausencia en la administrada "
DB "por el DOS 5.0-",13,10,"$"
rclock ENDS
END inicio
10.10. - USO SIN LIMITES DE SERVICIOS DEL DOS EN PROGRAMAS RESIDENTES.
Como se dijo al principio del captulo, desde un programa residente no se pueden emplear
directamente los servicios del DOS. Si se salta esta norma se pueden crear programas que funcionen bajo
determinadas circunstancias, pero nada robustos. Por ejemplo, una utilidad para volcar la pantalla a un fichero
en disco al pulsar una cierta combinacin de teclas, podra funcionar correctamente si es ejecutada desde la
lnea de comandos, o desde dentro de un editor de texto. Sin embargo, si es invocada mientras se ejecuta un
185 PROGRAMAS RESIDENTES
comando DIR o mientras el programa principal est accediendo al disco o, simplemente, ejecutando cualquier
funcin del DOS tal como consultar la fecha, nuestra utilidad dejara de funcionar correctamente. Y el fallo
no consiste en que la pantalla no se vuelque en disco, o se vuelque mal: el problema es que el ordenador se
cuelga, siendo preciso reinicializarlo.
Aunque es fcil y, en ocasiones ms cmodo y recomendable acceder directamente a la pantalla y
al teclado, el DOS es la herramienta ms potente para acceder al disco y su utilidad en este campo es
prcticamente insustituble. Para la BIOS o el hardware no existen los discos virtuales ni las unidades de
disco en red; por otra parte, el DOS constituye un soporte bsico que permite a los programas ignorar la
evolucin futura de las unidades de almacenamiento. Por consiguiente, poder utilizar el DOS desde los
programas residentes es algo ms que interesante. Con este objetivo, la propia Microsoft tuvo que enfrentarse
a las limitaciones del sistema para desarrollar el comando PRINT desde la versin 2.0; en la actualidad es
casi universalmente conocido lo que hay que hacer para emplear el DOS desde un programa residente, aunque
una gran mayora de los libros an no expliquen estas tcnicas. Algunos de ellos, incluso muestran programas
residentes que llaman descaradamente al DOS, sin tomar precauciones de ninguna clase por algo no los he
incluido en la bibliografa!.
El trmino no reentrante que se aplica al DOS significa que no puede ser empleado simultneamente
por dos procesos, sin embargo se trata de un cdigo serialmente reusable como veremos. El DOS posee tres
pilas internas: la pila de E/S (I/O Stack), la pila de disco (Disk Stack) y la pila auxiliar (Auxiliary Stack).
Las funciones 0 a la 0Ch utilizan la pila de E/S; las restantes utilizan la pila de disco. Si se llama al DOS
durante un error crtico (por ejemplo, DIR B: cuando no hay disquete en la unidad) se utiliza la pila auxiliar.
La existencia de estas pilas locales significa que si el DOS es llamado cuando ya estaba ejecutando una
funcin (y ya haba conmutado a la pila interna correspondiente) volver a inicializar el puntero de pila y en
la nueva reentrada se cargar el contenido previo de la pila. Si estaba ejecutando una funcin 0-0Ch y se
le llama solicitando una 0Dh o superior, no habr problemas, ya que hay dos pilas separadas para cada caso;
sin embargo no suele haber tanta suerte. Algunas funciones del DOS son tan simples que ste no conmuta
a ninguna pila interna: la 33h, 50h, 51h, 62h y 64h: con ellas s es reentrante; con las dems (que adems
son la mayora y las ms interesantes) por desgracia no lo es.
Para solucionar este problema hay dos mtodos: interrumpir al DOS slo cuando no est ejecutando
alguna funcin; esto es, cuando no est dentro de una INT 21h. Alternativamente, el programa residente
puede salvar todo el contexto del DOS, incluyendo las tres pilas internas, para restaurarlas despus de haber
realizado su tarea. En este libro trataremos especialmente el primer mtodo, tradicionalmente el ms empleado
y el ms probado.
10.10.1. - UNA PRIMERA APROXIMACION.
Para detectar si el ordenador est ejecutando cdigo del DOS (si est dentro de una INT 21h) se
podra desviar esta interrupcin y colocar una nueva rutina que incrementara una variable indicativa al
principio, llamara a la INT 21h original y despus volviera a decrementar la variable antes de retornar. As,
por ejemplo, desde una interrupcin de teclado o peridica, se podra comprobar si el DOS ya est trabajando
antes de llamarle (variable distinta de cero). Sin embargo, ms que una variable habra que tener dos (una
para indicar que la pila E/S est en uso y otra para la pila de disco). Por otro lado, la rutina debera ser algo
ms sofisticada todava, ya que hay funciones del DOS que no retornan (las de terminar programa: la 0, 31h
y 4Ch) y esto, si no se tiene cuidado, significara no decrementar como es debido la variable que indica que
se ha abandonado la INT 21h. Adems, para liar an ms el asunto, qu hacer con los errores crticos?. Y,
para colmo, todava hay ms: si el DOS est dentro de la INT 21h, funcin 0Ah (entrada en buffer por
teclado), nuestra variable dira que no es posible usar el DOS en ese momento, ya que est ya en uso, cuando
est cientficamente demostrado que en este caso s es reentrante si se utiliza una funcin 0Dh o superior (en
la lnea de comandos, el DOS est ejecutando precisamente esa funcin de entrada por teclado).
Por fortuna, el DOS viene aqu en nuestro socorro: no ser preciso disear la compleja rutina
propuesta, ya que el propio sistema posee una variable interna que indica si en ese momento puede ser
186 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
interrumpido. Se trata de la variable no documentada InDOS. Existe una funcin secreta del DOS para
obtener la direccin de esta variable, de un byte, que valdr 0 en el caso de que el DOS est libre y pueda
ser llamado desde un programa residente. Esa variable se incrementa automtica y adecuadamente con las
llamadas a la INT 21h, y se decrementa al salir.
No hay mejor manera de aprender a construir programas residentes fiables y eficientes que espiar
cmo lo hace el fabricante del sistema operativo con los suyos propios. El comando PRINT del DOS, cuando
se queda residente, desva un montn de interrupciones, entre ellas la 1Ch (equivalente a la 8) y la 28h. La
interrupcin 28h (Idle) es invocada por el DOS en las operaciones de entrada por teclado, cuando se
encuentra libre de otras tareas, para permitir a los programas residentes aprovechar ese tiempo muerto de
CPU. Desde dentro de una INT 28h se puede usar el DOS incluso aunque InDOS sea igual a 1. El comando
PRINT, cuando entra en accin, realiza adems una serie de tareas adicionales: preserva el DTA activo (rea
de transferencia a disco), el PSP del programa interrumpido, los vectores de INT 1Bh (Ctrl-Break), INT 23h
(Ctrl-C), INT 24h (manipulador de errores crticos); desva esos vectores hacia unas rutinas propias; a
continuacin establece un DTA y un PSP propios. Tras enviar los caracteres a la impresora, leyndolos del
disco (con las funciones del DOS, por supuesto) vuelve a restaurar todo lo salvado. Pero vayamos ms
despacio.
10.10.2. - PASOS A REALIZAR PARA USAR EL DOS.
Para obtener la direccin de InDOS se puede emplear la funcin 34h del DOS, que devuelve un
puntero en ES:BX a dicha variable. La direccin de InDOS es constante, por lo que se puede inicializar al
instalar el programa residente (no cambiar de lugar en toda la sesin de trabajo). Como luego nos ser de
utilidad, conviene decir aqu ahora que el Bandern de Errores Crticos del DOS est situado justo despus
de InDOS en las versiones 2.x y justo antes en la 3.0 (en la 3.1 y siguientes, la funcin 5D06h permite
obtener su direccin en DS:SI). Por tanto, desde los programas residentes bastar, en principio, comprobar
que InDOS es igual a cero antes de llamar al DOS (y, de paso, que el Bandern de Errores Crticos es
tambin cero). En caso contrario, se puede inicializar una variable que indique que el programa residente tiene
an pendiente su ejecucin: desde la interrupcin peridica se puede comprobar si est pendiente la activacin
del programa residente y se puede verificar el estado del DOS hasta que ste est listo para ser llamado, lo
que suceder tarde o temprano. Adems de la interrupcin peridica, tambin se puede desviar la INT 28h:
desde esta interrupcin se puede llamar al DOS, como dije antes, incluso aunque InDOS sea igual a 1 (pero
no mayor) siempre que la funcin del DOS a ejecutar sea superior a la 0Ch (lo ms normal). Sin embargo,
cuando sea seguro llamar al DOS, habr que hacer algunas cosas ms antes de empezar a realizar la labor
propia del programa residente.
En el PSP se almacena mucha informacin vital para la ejecucin de los programas. Una de las reas
ms importantes es el JFT (Job File Table) que contiene informacin referida a los ficheros del programa
que se ejecuta. No es conveniente, desde un programa residente, modificar el PSP del programa principal.
Por tanto, habr que anotar la direccin del PSP actual y conmutar al del programa residente; al final del
trabajo se proceder a restaurar el PSP del programa principal. Si no se toma esta precaucin, podra suceder
de todo. Por ejemplo: si el programa residente abre un fichero usando el PSP del programa principal, cuando
ste termine (el programa principal) ese fichero ser probablemente cerrado sin que el programa residente se
entere. Para obtener la direccin del PSP activo se puede utilizar la funcin Get PSP (50h; la 62h,
totalmente equivalente) que devuelve en BX su segmento; la funcin Set PSP (51h) permite establecer un
nuevo PSP indicando en BX el segmento. Si se desea mantener la compatibilidad con el DOS 2.x, hay que
tener en cuenta adems un error de este sistema operativo. La errata consiste en que las funciones 50h y 51h
no operan bien en el DOS 2.x a menos que el sistema use la pila de errores crticos. Por tanto, con esta
versin del sistema se puede forzar el Bandern de Errores Crticos a un valor 0FFh antes de llamar a las
funciones 50h y 51h, para volverlo a poner a cero despus: as, el DOS cree que el sistema est en medio
de un error y usa la pila que queremos.
Adems del PSP se debe cambiar el DTA (Disk Transfer Area) que utiliza el DOS para acceder al
disco: este rea est normalmente en el offset 80h del PSP (sobrescribe el campo de parmetros de la lnea
187 PROGRAMAS RESIDENTES
de comandos cuando el programa accede a disco) y ocupa 128 bytes. Basta con preservar el DTA del
programa principal, cuya direccin se obtiene en ES:BX con la funcin Get DTA (2Fh), y activar un nuevo
DTA (por ejemplo, en el offset 80h del PSP de programa residente) utilizando la funcin Set DTA (1Ah),
pasando su direccin en DS:DX.
La informacin extendida de errores es otro punto a tener en consideracin. Supongamos que el
programa principal comete un error y el DOS genera la correspondiente informacin extendida de errores (a
partir de la versin 3.0). Si en ese momento se activa el programa residente, puede que realice alguna funcin
del DOS con xito y el DOS sobrescribir la condicin de error previa. Por tanto, es deber del programa
residente preservar y restaurar la informacin extendida de errores antes de actuar. La funcin Get Extended
Error Information (59h) devuelve en AX, BX y CX la informacin extendida de errores. Con la funcin
Set Extended Error Information (5D0Ah), en DS:DX se suministra al DOS la direccin de una tabla que
contiene el AX, BX y CX con la informacin extendida de errores a establecer.
Como complemento, si se van a emplear las funciones de acceso a disco del DOS, tambin es
conveniente monitorizar la INT 13h para evitar un acceso a disco cuando no ha finalizado el anterior (aunque
el DOS est en posicin correcta). Si se van a emplear las INT 25h/26h, convendra monitorizarlas; as como
la INT 10h si se utilizan servicios de vdeo (aunque sean del DOS). Por monitorizar se entiende interceptar
esa interrupcin e instalar una rutina de control que incremente y decremente una variable cada vez que
empieza o termina una de esas interrupciones, con objeto de saber cundo se est dentro de ellas. En general,
los programas residentes que accedan demasiado intensivamente al disco (en una especie de multitarea)
deberan monitorizar no slo INT 13h sino tambin INT 25h e INT 26h.
10.10.3. - RESUMIENDO, NO ES TAN DIFICIL!.
El procedimiento a seguir, por tanto, para activar un programa residente respondiendo por ejemplo
a la pulsacin de una combinacin de teclas, es el siguiente:
- Desde la interrupcin del teclado, y una vez detectada la combinacin de teclas, intentar activar el
programa residente. Ser posible activarlo si: no estaba ya activo, no hay una INT 13h en curso, InDOS=0
y el Bandern de Errores Crticos tambin es igual a 0.
- Por si falla, desde la interrupcin del temporizador se puede comprobar si est pendiente an la
activacin del programa residente (por si no se pudo cuando se pulsaron las teclas); en ese caso, volverlo a
intentar de nuevo, con los mismos pasos que en el caso anterior.
- Desde la interrupcin 28h comprobar si est pendiente an la activacin del programa residente:
en ese caso, si no estaba ya activo e InDOS<=1 y el Bandern de Errores Crticos es igual a 0 se puede
proceder a activar el programa residente.
- Como mnimo habrn de existir dos variables de control: Una que indica si el programa residente
ya est activo (y se deben rechazar o posponer nuevas activaciones, ya que ste se supone no reentrante).
Otra, que indique si el programa residente va a ser activado en breve (en cuanto el DOS nos deje). Ambas
variables son semforos que conviene tratar con cuidado, para evitar reentradas en el programa residente:
cuando desde una interrupcin son comprobadas (ej., desde una INT 28h) podra producirse otra interrupcin
(como INT 8) lo que complica ligeramente la programacin. Aunque no lo he dicho antes, todos los
programas residentes que usan el DOS deben definir una pila propia, ya que la del programa interrumpido
puede no ser suficientemente grande. Por el hecho de definir una pila propia, los programas residentes que
usan funciones del DOS no son reentrantes; lo cual no es, por lo general, una limitacin muy importante.
- Por supuesto, antes de ejecutar su cdigo propiamente dicho, el programa residente deber preservar
el DTA, el PSP y la informacin extendida de errores, as como los vectores de INT 1Bh/23h/24h. Despus
deber desviar las INT 1Bh e INT 23h hacia un IRET (para evitar un Ctrl-Break Ctrl-C) y la INT 24h, para
implementar una gestin propia de los errores crticos. Al final, deber restaurar todo de nuevo.
188 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
Toda la informacin vertida hasta ahora procede de la versin original del libro Undocumented DOS,
citado en la bibliografa. Sin embargo, en mi experiencia personal con los programas residentes he sacado
la conclusin de que es conveniente tambin desviar la INT 21h e intentar desde la misma activar el
programa residente, tal como si se tratara de una interrupcin peridica ms. El motivo es que desde la INT
8 la INT 1Ch hay que tener bastante suerte para que el DOS est desocupado cuando se producen, ya que
estas interrupciones slo suceden 18 veces cada segundo. Esto significa que, por ejemplo, mientras se
formatea un disco y se intenta activar el programa residente, puede que ste no responda hasta haberse
formateado medio disco o, incluso, hasta finalizar el formateo. Sin embargo, mientras se formatea el disco,
se producen miles de llamadas a la INT 21h: cuando InDOS sea cero tras acabar una sola de estas llamadas,
podremos darnos cuenta; sin embargo, utilizando slo la interrupcin peridica estaremos a merced de la
suerte. Desviar la INT 21h e intentar activar el programa residente desde ella permite por ejemplo que ste
acte, en medio de un formateo de disco, de manera casi instantnea cuando se le requiere. Otro ejemplo:
con el mtodo normal, sin controlar la INT 21h, mientras se saca un directorio por pantalla y se intenta
activar el programa residente, cada cierto nmero de lneas ste responde; controlando la INT 21h, responde
cada dos o tres caracteres impresos. Es evidente que la INT 21h pone a nuestra disposicin un mtodo mucho
ms efectivo a menudo que la interrupcin peridica; sin embargo, tampoco es conveniente prescindir de esta
ltima ya que la INT 21h slo funciona cuando alguien llama al DOS (y no siempre alguien lo est
llamando). En general, conviene utilizar las dos interrupciones a la vez: si bien interceptar la INT 21h no est
recomendado en ningn sitio excepto en este libro, puedo asegurar que he tenido bastantes ocasiones de
comprobar que es completamente fiable.
10.10.4.- UN METODO ALTERNATIVO: EL SDA.
Hasta ahora hemos visto el mtodo ms comn para poder emplear el DOS desde un programa
residente. Sin embargo, este mtodo depende de la molesta variable InDOS. Esto limita la efectividad de los
programas residentes, que no pueden ser activados por ejemplo cuando se ejecuta un comando TYPE. La
solucin alternativa que se apuntaba al principio de este apartado consiste en salvar el contexto del DOS y
restaurarlo despus, algo factible desde el DOS 3.0. Esto supone bastantes diferencias respecto al mtodo
estudiado hasta ahora. En lugar de chequear InDOS se debe verificar que el DOS no est en una seccin
crtica (que por fortuna es lo ms normal) como luego veremos; y esto tanto desde la interrupcin del teclado
como desde la peridica o desde la INT 28h. Al comienzo del cdigo del programa residente, se debe salvar
el estado del DOS: esto significa que hay que pedir memoria al sistema (o tenerla reservada de antemano en
cantidad suficiente) para contener esa informacin. Tambin hay que instalar las nuevas rutinas de control
de INT 1Bh, 23h y 24h; no es necesario preservar el PSP activo (ya incluido en el rea salvada): lo que s
es preciso es activar el PSP propio. Tampoco es preciso preservar el DTA ni la informacin extendida de
errores: aunque se debe establecer un nuevo DTA, al restaurar el estado del DOS ms tarde ste ser tambin
automticamente restablecido. Y bien, en qu consiste el estado o contexto del DOS?: se basa en un rea
de datos, el SDA (Swappable Data Area), cuyo tamao oscila entre 24 bytes y 2 Kbytes. Este rea almacena
el PSP activo y las tres pilas del DOS, as como la direccin del DTA...
Para manipular el SDA se puede emplear la funcin del sistema Get Address of DOS Swappable
Data Area (5D06h), que devuelve en DS:SI un puntero al SDA, en DX el nmero mnimo de bytes a
preservar cuando el DOS est libre y en CX el nmero de bytes a preservar cuando el DOS est ocupado
(InDOS distinto de cero). Desde la versin 4.0 del DOS se debe utilizar en su lugar la funcin Get DOS
Swappable Data Areas (5D0Bh), ya que este sistema no posee un nico rea de datos sino mltiples. El
procedimiento general consistir, simplemente, en salvar el SDA al principio y restaurarlo al final.
Como se dijo antes, el SDA slo puede ser accedido cuando el DOS no est en un momento crtico.
Cuando el DOS entra y sale de los momentos crticos, llama a la INT 2Ah con AX=8000h (inicio de
momento crtico) o bien AX=8100h o AX=8200h (fin de momento crtico). Se debe interceptar la INT 2Ah
e incrementar/decrementar una variable que indique las entradas/salidas del DOS en fase crtica.
Este mtodo para gestionar los programas residentes requiere algo ms de memoria: en especial, si
se quiere asegurar la compatibilidad con futuras versiones del sistema, habr que reservar mucho ms de 2Kb
189 PROGRAMAS RESIDENTES
para almacenar el SDA (intentar utilizar memoria convencional puede fallar, ya que el programa principal
puede tenerla toda asignada) aunque este problema es menor en mquinas con memoria expandida o
extendida. No hay que olvidar que el SDA no se puede grabar en disco (para eso hay que usar el DOS, y el
DOS no se puede emplear hasta no haber salvado el SDA). Tambin es quiz algo ms complejo. Sin
embargo, aade algo ms de potencia a los programas residentes, ya que pueden ser activados casi en
cualquier momento y prcticamente en cualquier circunstancia. El autor de este libro nunca ha empleado este
mtodo.
10.10.5.- METODOS MENOS ORTODOXOS.
Hay programadores que utilizan mtodos muy curiosos para emplear los servicios del DOS desde los
programas residentes. Un ejemplo, expuesto por Douglas Boling en su artculo de la revista RMP (Ed. Anaya,
Marzo-Abril de 1992) consiste en activar el Bandern de Errores Crticos antes de llamar a las funciones
ordinarias del DOS: de esta manera, se utiliza la pila de errores crticos en lugar de la de disco, con lo que
no hay conflictos. Esto, por supuesto, sin que el DOS estuviera antes en estado crtico (en caso de estarlo hay
que esperar). El inconveniente de este mtodo es que slo un programa residente de este tipo puede estar
activo en un momento dado en el ordenador. Evidentemente, tambin hay que desviar la INT 24h para
controlar un posible error crtico de verdad.
10.11. - EJEMPLO DE PROGRAMA RESIDENTE QUE UTILIZA EL DOS.
El programa propuesto de ejemplo (SCRCAP) es el tradicional capturador de pantallas, en este caso
de texto. El mtodo que emplea es el clsico de comprobar la variable InDOS. Al pulsar Alt-SysReq
(combinacin por defecto) comienza a actuar. Emite un sonido ascendente que precede la grabacin y otro
descendente que la sucede, para confirmar que ha grabado. Los ficheros que genera tienen por nombre
SCRxx-nn.SCR, donde xx es la anchura de la pantalla en columnas (en hexadecimal) y nn el nmero de
fichero, entre 00 y 99. Los ficheros se crean a partir de 00 cuando se instala el programa, sobrescribiendo
otros existentes con anterioridad. Al almacenar en el nombre del fichero la anchura del modo de vdeo, es
fcil despus procesar la imagen al conocer sus dimensiones. El programa no comprueba el modo de vdeo,
por lo que en pantallas grficas se obtienen resultados desconcertantes. Sin embargo, la ventaja de ello es que
de esta manera puede salvar pantallas extraas no estndar (como 132x60, etc.) que pueden poseer ciertas
tarjetas. El fichero es creado en el directorio activo por defecto; si se invoca la utilidad mientras se ejecuta
un DIR, el fichero podra crearse en el directorio visualizado (algunas versiones del COMMAND cambian
el directorio activo momentneamente). Como caba esperar, el programa se autoinstala automticamente en
memoria superior y tiene opcin de desinstalacin, siendo tambin configurables las teclas de activacin.
Entre los aspectos tcnicos, decir que se desva la INT 21h como se coment con anterioridad. En
ese sentido, SCRCAP puede ser invocado con xito mientras se formatea un disquete (bueno, pero tampoco
para grabar precisamente sobre ese disquete). Se define una pila interna de 0,75 Kbytes, suficiente para el
programa que graba la pantalla y para dar cabida a todas las interrupciones hardware que puedan anidarse
durante el proceso (examinando la memoria con DEBUG se puede observar qu cantidad mxima de pila es
consumida tras un rato de trabajo, ya que los caracteres PILA permanecen en la zona de la misma an no
empleada). Desde la rutina de control de INT 8 e INT 9 se llama a una subrutina, proceso_tsr, que toma la
decisin de activar el programa residente si el DOS est preparado, o lo pospone en caso contrario. Desde
la INT 28h se hace la comprobacin ms relajada de InDOS (basta con que sea no mayor de 1) y se toma
tambin la decisin de activar el programa residente o seguir esperando: en el primer caso se llama a
proceso_tsr con una variable (in28) que indica que ya no hay que hacer ms comprobaciones. En proceso_tsr
se comprueba la variable activo para evitar una reentrada al programa residente: como es un semforo, es
preciso inhibir las interrupciones con objeto de que entre su consulta y ulterior hipottica modificacin no
pueda ser modificado por nadie (por otro proceso lanzado por interrupciones). Al final, la rutina tarea_TSR
es el autntico programa residente. Simplemente modificando esta rutina se pueden crear programas residentes
que realicen cualquier funcin, pudiendo llamar para ella al DOS.
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 sern empleados como rea de transferencia a disco
(DTA). Conviene ahora hacer un pequeo apunte importante: cuando el programa es relocalizado a la
memoria superior, hay que actualizar un campo en el PSP relocalizado (rutina reubicar_prog): se trata del
campo que apunta a la JFT (offset 36h del PSP), con objeto de que apunte correctamente al nuevo segmento
en que reside el PSP. Si no se tomara esta precaucin, no se accedera al disco correctamente.
Si se compara el listado de SCRCAP con el de RCLOCK, el lector comprobar que tienen comn
cerca del 50% de las lneas. Slo cambia la ayuda, algn parmetro, alguna subrutina de la instalacin y, por
supuesto, el cdigo residente. En general, las subrutinas que componen ambos programas son lo
suficientemente generales como para acomodar mltiples soluciones informticas: se puede considerar que
ambos programas son una especie de plantillas para crear utilidades residentes. Para hacer nuevos programas
residentes que hagan otras tareas, basta con cambiar slo la parte residente y poco ms. Esto permite trabajar
con comodidad, pese a tratarse del lenguaje ensamblador, y producir mltiples programas en tiempo rcord.
; ********************************************************************
; * *
; * SCRCAP 1.0 *
; * *
; * Utilidad residente de captura de pantallas de texto. *
; * *
; ********************************************************************
; ------------ Macros de propsito general
XPUSH MACRO RM
IRP reg, <RM>
PUSH reg
ENDM
ENDM
XPOP MACRO RM
IRP reg, <RM>
POP reg
ENDM
ENDM
; ------------ Programa
scrcap SEGMENT
ASSUME CS:scrcap, DS:scrcap
ORG 100h
ini_residente EQU $
inicio: JMP main
; ------------ Identificacin estandarizada del programa
program_id LABEL BYTE
segmento_real DW 0 ; segmento real donde ser cargado
offset_real DW 0 ; offset real " " "
longitud_total DW 0 ; zona de memoria ocupada (prrafos)
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 ; nmero Multiplex de este TSR
vectores_id DW tabla_vectores
extension_id DW tabla_extra
DB "*##*"
autor_nom_ver DB "CiriSOFT:SCRCAP:1.0",0
DB 6 ; vectores de interrupcin interceptados
tabla_vectores EQU $
DB 8 ; INT 8
ant_int08 LABEL DWORD ; direccin original
ant_int08_off DW 0
ant_int08_seg DW 0
DB 9 ; INT 9
ant_int09 LABEL DWORD ; direccin original
ant_int09_off DW 0
ant_int09_seg DW 0
DB 13h ; INT 13h
ant_int13 LABEL DWORD ; direccin original
ant_int13_off DW 0
ant_int13_seg DW 0
DB 21h ; INT 21h
ant_int21 LABEL DWORD ; direccin original
ant_int21_off DW 0
ant_int21_seg DW 0
DB 28h ; INT 28h
ant_int28 LABEL DWORD ; direccin original
ant_int28_off DW 0
ant_int28_seg DW 0
DB 2Fh ; INT 2Fh
ant_int2F LABEL DWORD ; direccin original
ant_int2F_off DW 0
ant_int2F_seg DW 0
tabla_extra LABEL BYTE
DW ctrl_exterior ; permitido control exterior
DW 0 ; campo reservado
ctrl_exterior LABEL BYTE
reubicabilidad DB 1 ; programa 100% reubicable
activacion DW act
act DW 1
; ------------ Variables internas
dosver DW ? ; versin del DOS
ega DB ON ; a ON si EGA o superior
activo DB OFF
inminente DB OFF
marcas DB 8 ; Por defecto, Alt...
cod_rastreo DB 54h ; ...SysReq (PetSys)
in13 DW 0
in28 DW 0
indos LABEL DWORD
indos_off DW ?
indos_seg DW ?
crit_err LABEL DWORD
crit_err_off DW ?
crit_err_seg DW ?
ant_pila_off DW ?
ant_pila_seg DW ?
mainpsp DW ? ; PSP del programa principal
maindta LABEL DWORD ; DTA del programa principal
maindta_off DW ?
maindta_seg DW ?
errinfo LABEL DWORD ; Extended error information
errinfo_ax DW ? ; del programa principal
errinfo_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 pila
pila_ini EQU $
fich_nom DB "SCRxx-00.SCR",0
fich_handle DW ?
local_ints DW 3
DB 1Bh ; INT 1Bh
DW ges_int1B ; nueva direccin
ant_int1B LABEL DWORD ; direccin original
ant_int1B_off DW 0
ant_int1B_seg DW 0
DB 23h ; INT 23h
DW ges_int23 ; nueva direccin
ant_int23 LABEL DWORD ; direccin original
ant_int23_off DW 0
ant_int23_seg DW 0
DB 24h ; INT 24h
DW ges_int24 ; nueva direccin
ant_int24 LABEL DWORD ; direccin original
ant_int24_off DW 0
ant_int24_seg DW 0
; ------------ Rutina de gestin de INT 2Fh
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; s llama: darle informacin
LEA DI,autor_nom_ver
ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"
IRET
ges_int2F ENDP
; ------------ Rutina de gestin de INT 8
ges_int08 PROC
PUSHF
CALL CS:ant_int08
STI
CMP CS:inminente,ON
JNE exit_08 ; no hay ejecucin pendiente
CALL proceso_tsr ; ejecutar TSR si es posible
exit_08: IRET
ges_int08 ENDP
; ------------ Rutina de gestin de INT 9
ges_int09 PROC
STI
PUSH AX
IN AL,60h
PUSHF
CALL CS:ant_int09
CMP AL,CS:cod_rastreo ; tecla de activacin?
JNE fin_09
MOV AX,40h
PUSH DS
MOV DS,AX
MOV AL,DS:[17h]
POP DS
191 PROGRAMAS RESIDENTES
AND AL,15
CMP AL,CS:marcas ; marcas de activacin?
JNE fin_09
CALL proceso_tsr ; ejecutar TSR si es posible
fin_09: POP AX
IRET
ges_int09 ENDP
; ------------ Rutina de gestin de INT 13h
ges_int13 PROC FAR ; gestionar INT 13h
STI
PUSHF
INC CS:in13 ; indicar entrada en INT 13h
CALL CS:ant_int13
PUSHF ; mucho cuidado con los flags
DEC CS:in13 ; salida de INT 13h
POPF
RET 2 ; retornar sin tocar flags
ges_int13 ENDP
; ------------ Rutinas de gestin de INT 1Bh, 23h y 24h.
ges_int1B EQU THIS BYTE ; gestionar INTs 1Bh/23h
ges_int23 PROC
IRET ; ignorar Ctrl-C y Ctrl-Break
ges_int23 ENDP
ges_int24 PROC ; gestionar INT 24h
STI
MOV AX,3 ; funcin de fallo
CMP CS:dosver,300h
JAE ret_int24
XOR AX,AX ; 0 en DOS 2.x
ret_int24: IRET
ges_int24 ENDP
; ------------ Rutina de gestin de INT 21h
ges_int21 PROC FAR
POP CS:ret_off ; offset de retorno
POP CS:ret_seg ; segmento de retorno
POP CS:ret_flags ; flags de retorno
PUSH CS:ret_seg
PUSH CS:ret_off ; dejar slo segmento:offset
PUSH CS:ret_flags
CALL CS:ant_int21
PUSHF
CMP CS:inminente,ON
JNE exit_21 ; no hay ejecucin pendiente
CALL proceso_tsr ; ejecutar TSR si es posible
exit_21: POPF
RET ; retornar sin alterar flags
ges_int21 ENDP
; ------------ Rutina de gestin de INT 28h
ges_int28 PROC ; gestionar INT 28h
STI
CMP CS:activo,ON
JE exit_28 ; TSR ya activo
CMP CS:inminente,ON
JNE exit_28 ; no hay que activarlo
CMP CS:in13,0
JA exit_28 ; INT 13h en curso
XPUSH <DS, BX>
LDS BX,CS:crit_err
CMP BYTE PTR [BX],0 ; error crtico?
XPOP <BX, DS>
JNE exit_28
XPUSH <DS, BX>
LDS BX,CS:indos
CMP BYTE PTR [BX],1 ; Indos>1?
XPOP <BX, DS>
JA exit_28
INC CS:in28 ; dentro de INT 28h
CALL proceso_tsr ; ejecutar cdigo del TSR
DEC CS:in28 ; fuera de INT 28h
exit_28: JMP CS:ant_int28
ges_int28 ENDP
; ------------ Rutina de control de ejecucin del TSR
proceso_tsr PROC ; ejecutar TSR si se puede
CMP CS:in28,0
JNE proceder ; dentro de INT 28h
CMP CS:in13,0
JA no_proceder ; INT 13h en curso
XPUSH <DS, BX, AX>
LDS BX,CS:crit_err
MOV AL,[BX]
LDS BX,CS:indos
OR AL,[BX] ; crit_err OR indos
AND AL,AL
XPOP <AX, BX, DS>
JZ proceder ; se cumple que ambos a 0
no_proceder: MOV CS:inminente,ON ; esperar prxima INT 8/28h
RET
proceder: CLI ; a comprobar semforo...
CMP CS:activo,ON ; ya estaba activo?
JE exit_proceso ; evitar reentrada
MOV CS:activo,ON ; ahora s, activo
STI ; ...semforo comprobado
MOV CS:inminente,OFF ; ya atendida la peticin
MOV CS:ant_pila_off,SP
MOV CS:ant_pila_seg,SS ; preservar pila
CLI
MOV SP,CS
MOV SS,SP
LEA SP,pila_ini ; nueva pila habilitada
STI
XPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>
XPUSH <CS, CS>
XPOP <DS, ES> ; DS y ES apuntan al TSR
CALL pushset_ints
CALL pushset_psp
CALL pushset_dta
CALL push_crit_err
CALL kbuff_limp
CALL tarea_TSR ; ejecutar proceso residente
CALL pop_crit_err
CALL pop_dta
CALL pop_psp
CALL pop_ints
XPOP <ES, DS, BP, DI, SI, DX, CX, BX, AX>
CLI
MOV SP,CS:ant_pila_seg
MOV SS,SP
MOV SP,CS:ant_pila_off ; pila restaurada
MOV CS:activo,OFF
exit_proceso: STI
RET
proceso_tsr ENDP
; ------------ Subrutinas de apoyo
pushset_ints PROC ; interceptar INT 1Bh/23h/24h
PUSH ES
LEA SI,local_ints
MOV CX,[SI]
phst_otro: PUSH CX
MOV AL,[SI+2]
MOV AH,35h
INT 21h
MOV [SI+5],BX
MOV [SI+7],ES ; INT xx preservada
MOV DX,[SI+3]
MOV AL,[SI+2]
MOV AH,25h
INT 21h ; INT xx desviada
ADD SI,7
POP CX
LOOP phst_otro
POP ES
RET
pushset_ints ENDP
pop_ints PROC ; restaurar vectores INT 1Bh/23h/24h
PUSH DS
LEA SI,local_ints
MOV CX,[SI]
pop_otro: PUSH CX
MOV AL,CS:[SI+2]
MOV AH,25h
MOV DX,CS:[SI+5]
MOV DS,CS:[SI+7]
INT 21h ; INT xx restaurada
ADD SI,7
POP CX
LOOP pop_otro
POP DS
RET
pop_ints ENDP
pushset_psp PROC ; preservar PSP y activar el nuevo
MOV AX,dosver
CMP AH,2
JA getpsp3
PUSH DS ; en DOS 2.x ...
LDS DI,crit_err
MOV BYTE PTR [DI],0FFh ; forzar error crtico
MOV AH,51h
INT 21h ; BX = PSP activo (DOS 2.x)
PUSH BX
MOV AH,50h
MOV BX,CS:segmento_real
INT 21h ; activar nuevo PSP
MOV BYTE PTR [DI],0 ; anular error crtico
POP BX
POP DS
JMP psp_ok
getpsp3: MOV AH,62h
INT 21h ; BX = PSP activo (DOS 3+)
PUSH BX
MOV AH,50h
MOV BX,segmento_real
INT 21h ; activar nuevo PSP
POP BX
psp_ok: MOV mainpsp,BX
RET
pushset_psp ENDP
pop_psp PROC ; restaurar PSP programa principal
PUSH DS
MOV AX,dosver
CMP AH,2
JA setpsp3
LDS BX,crit_err ; en DOS 2.x ...
MOV BYTE PTR [BX],0FFh ; forzar error crtico
PUSH BX
MOV AH,50h
MOV BX,CS:mainpsp
INT 21h ; restaurar PSP
POP BX
MOV BYTE PTR [BX],0 ; anular error crtico
JMP psp_poped
setpsp3: MOV AH,50h ; DOS 3+
MOV BX,mainpsp
INT 21h ; restaurar PSP
psp_poped: POP DS
RET
pop_psp ENDP
pushset_dta PROC
XPUSH <DS, ES>
MOV AH,2Fh
INT 21h
MOV maindta_off,BX
MOV maindta_seg,ES ; almacenar DTA activo
MOV AH,1Ah
MOV DX,80h
MOV DS,segmento_real
INT 21h ; establecer nuevo DTA
XPOP <ES, DS>
RET
pushset_dta ENDP
pop_dta PROC
PUSH DS
MOV AH,1Ah
MOV DX,maindta_off
MOV DS,maindta_seg
INT 21h ; restaurar DTA
POP DS
RET
pop_dta ENDP
push_crit_err PROC
CMP dosver,300h
JB push_crit_fin ; necesario DOS 3.0+
MOV AH,59h
MOV BX,0
INT 21h
MOV errinfo_ax,AX ; preservar informacin de
MOV errinfo_bx,BX ; errores crticos
MOV errinfo_cx,CX
192 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
push_crit_fin: RET
push_crit_err ENDP
pop_crit_err PROC
CMP dosver,300h
JB pop_crit_fin ; necesario DOS 3.0+
MOV AX,5D0Ah
MOV BX,0
LEA DX,errinfo
INT 21h ; restaurar informacin de
pop_crit_fin: RET ; errores crticos
pop_crit_err ENDP
kbuff_limp PROC ; limpiar buffer del teclado
MOV AH,1
INT 16h
JZ kbuff_limpio
MOV AH,0
INT 16h
JMP kbuff_limp
kbuff_limpio: RET
kbuff_limp ENDP
; ------------ Proceso residente que puede emplear el DOS
tarea_TSR PROC
CALL sonidoUp
CALL init_nomfich
LEA DX,fich_nom
MOV CX,0
MOV AH,3Ch
INT 21h ; abrir fichero
JC tarea_err
MOV fich_handle,AX
CALL dscx_eq_video
MOV BX,CS:fich_handle
XOR DX,DX
MOV AH,40h
INT 21h ; grabar pantalla
JC tarea_err
PUSH CS
POP DS
MOV BX,fich_handle
MOV AH,3Eh
INT 21h ; cerrar fichero
JC tarea_err
CALL inc_nombre ; preparar futuro nombre
CALL sonidoDown
RET
tarea_err: PUSH CS
POP DS
RET
tarea_TSR ENDP
; ------------ Inicializar nombre de fichero con anchura de pantalla.
init_nomfich PROC
PUSH DS
MOV AX,40h
MOV DS,AX
MOV AX,DS:[4Ah] ; anchura de pantalla
POP DS
MOV AH,AL
SHR AH,1
SHR AH,1
SHR AH,1
SHR AH,1
AND AL,15
ADD AX,00 ; binario -> hex
CMP AL,9
JBE al_es_hex
ADD AL,A-9-1
al_es_hex: CMP AH,9
JBE ah_es_hex
ADD AH,A-9-1
ah_es_hex: XCHG AH,AL
MOV WORD PTR fich_nom+3,AX ; anchura de pantalla
RET
init_nomfich ENDP
; ------------ Obtener segmento de vdeo y tamao de la pantalla
dscx_eq_video PROC ; devolver CX = tamao pantalla
MOV AX,40h ; y apuntar DS a la misma
MOV DS,AX
MOV AL,DS:[49h] ; modo de pantalla
MOV BX,0B000h ; supuesto adaptador monocromo
MOV CX,4000 ; nmero de bytes
CMP AL,7
JE video_ok
MOV BX,0B800h ; adaptador de color
MOV AX,DS:[4Eh] ; offset de la pgina activa
MOV CL,4
SHR AX,CL ; bytes -> prrafos
ADD BX,AX ; segmento de vdeo efectivo
MOV AX,25 ; 25 lneas
CMP CS:ega,ON
JNE modo_ok ; tarjeta modesta
XOR AH,AH
MOV AL,DS:[84h]
INC AL ; AX = lneas EGA/VGA
modo_ok: MUL WORD PTR DS:[4Ah] ; lneas*columnas = caracteres
SHL AX,1 ; AX = tamao buffer de vdeo
MOV CX,AX
video_ok: MOV DS,BX
RET
dscx_eq_video ENDP
; ------------ Incrementar nmero de fichero para siguiente vez
inc_nombre PROC
LEA BX,fich_nom
MOV AX,[BX+6]
INC AH
CMP AH,9
JBE inc_ok
MOV AH,0
INC AL
CMP AL,9
JBE inc_ok
MOV AL,9
inc_ok: MOV [BX+6],AX
RET
inc_nombre ENDP
; ------------ Sonido ascendente
sonidoUp PROC
CALL espera55ms
CALL sonidoON
MOV AX,2400
MOV CX,18
sonar_arriba: CALL sonidoAX
CALL espera55ms
SUB AX,30
LOOP sonar_arriba
CALL sonidoOFF
RET
sonidoUp ENDP
; ------------ Sonido descendente
sonidoDown PROC
CALL espera55ms
CALL sonidoON
MOV AX,3000
MOV CX,18
sonar_abajo: CALL sonidoAX
CALL espera55ms
ADD AX,30
LOOP sonar_abajo
CALL sonidoOFF
RET
sonidoDown ENDP
; ------------ Pausa de 55 milisegundos
espera55ms PROC
XPUSH <AX, DS>
MOV AX,40h
MOV DS,AX
STI ; por si acaso
MOV AL,DS:[6Ch]
espera_tic: CMP AL,DS:[6Ch]
JE espera_tic
XPOP <DS, AX>
RET
espera55ms ENDP
; ------------ Activar sonido
sonidoON PROC
PUSH AX
IN AL,61h
OR AL,3
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL ; activar sonido
MOV AL,182
JMP SHORT $+2
JMP SHORT $+2
OUT 43h,AL ; preparar canal 2
POP AX
RET
sonidoON ENDP
; ------------ Inhibir sonido
sonidoOFF PROC
PUSH AX
IN AL,61h
AND AL,255-3
JMP SHORT $+2
JMP SHORT $+2
OUT 61h,AL ; desactivar sonido
POP AX
RET
sonidoOFF ENDP
; ------------ Programar la nota AX en el temporizador
sonidoAX PROC
PUSH AX
OUT 42h,AL
MOV AL,AH
JMP SHORT $+2
JMP SHORT $+2
OUT 42h,AL ; canal 2 del 8253 programado
POP AX
RET
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 PROC
LEA DX,scrcap_txt ; mensaje inicial
CALL print
CALL inic_general ; inicializar ciertas variables
CALL detectarEGA
CALL obtener_param ; analizar posibles parmetros
JNC params_ok ; son correctos
CALL info_err_param ; no: informar del error/ayuda
JMP fin_noresid
params_ok: CALL residente? ; programa ya residente?
JC no_residente ; an no
CMP param_u,1 ; se solicita desinstalarlo?
JE desinst ; as es
CALL adaptar_param ; parmetros en copia residente
LEA DX,ya_install_txt
CALL print
CALL info_ya_ins ; informar de teclas activacin
JMP fin_noresid
desinst: MOV ES,tsr_seg
MOV AH,ES:multiplex_id
CALL mx_unload ; desinstalarlo:
LEA DX,des_ok_txt
JNC no_pesame ; ha sido posible
LEA DX,des_no_ok_txt ; no es posible
no_pesame: CALL print
JMP fin_noresid
no_residente: CMP AX,0 ; reside una versin distinta?
JE instalable ; no: se admite instalacin
CALL error_version ; error de versin incompatible
193 PROGRAMAS RESIDENTES
JMP fin_noresid
instalable: CMP param_u,1 ; no residente: desinstalar?
JNE instalar ; no lo piden
LEA DX,imp_desins_txt ; lo piden, sern despistados!
CALL print
JMP fin_noresid
instalar: MOV AX,parrafos_resid ; rea residente
ADD AX,16 ; 256 bytes de PSP (completo)
MOV memoria,AX
CALL mx_get_handle ; obtener entrada Multiplex
JNC handle_ok
LEA DX,nocabe_txt ; no quedan entradas
CALL print
JMP fin_noresid
handle_ok: MOV multiplex_id,AH ; entrada multiplex para SCRCAP
LEA DX,instalado_txt ; mensaje de instalacin
CALL print
CALL info_ya_ins ; informar teclas activacin
CALL preservar_ints ; tomar nota de vectores
CMP param_ml,0 ; se indic parmetro /ML?
JNE instalar_ml ; en efecto
MOV AX,memoria ; prrafos de memoria precisos
CALL UMB_alloc ; pedir memoria superior XMS
JNC instalar_umb ; hay la suficiente
MOV AX,memoria
CALL UPPER_alloc ; pedir memoria superior DOS 5
JC instalar_ml ; no hay la suficiente
STC ; indicar que usa memoria DOS
instalar_umb: MOV ES,AX ; segmento del bloque UMB
MOV DI,256 ; ES:256 zona a donde reubicar
CALL inicializa_id ; inicializar identificacin
CALL reubicar_prog ; reubicar el programa a ES:DI
CALL activar_ints ; interceptar vectores
JMP fin_noresid ; programa instalado arriba
instalar_ml: STC
MOV DI,256 ; instalacin mem. convencional
CALL inicializa_id ; inicializar identificacin
CALL reubicar_prog ; reubicar programa a ES:DI
CALL activar_ints ; interceptar vectores
CALL free_environ ; liberar espacio de entorno
MOV DX,memoria ; tamao zona residente
MOV AX,3100h
INT 21h ; terminar residente
fin_noresid: MOV AX,4C00h
INT 21h ; terminar no residente
main ENDP
; *************************************
; * *
; * SUBRUTINAS PARA LA INSTALACION *
; * *
; *************************************
; ------------ Extraer posibles parmetros de la lnea de comandos
obtener_param PROC
MOV BX,81h ; apuntar a zona de parmetros
otro_pmt_mas: CALL saltar_esp ; saltar delimitadores
JNC otro_pmt ; quedan ms parmetros
JMP fin_proc_pmt ; no ms parmetros
otro_pmt: CMP AL,/
JE pmt_barrado ; parmetro precedido por /
CMP AL,?
JE pmt_hlp
JMP mal_proc_pmt
pmt_barrado: INC BX
MOV AL,[BX] ; letra del parmetro
CMP AL,13 ; fin de mandatos?
JE mal_proc_pmt ; falta parmetro
CMP AL,?
JE pmt_hlp
OR AL, ; poner en minsculas
CMP AL,h
JE pmt_hlp
CMP AL,s
JE pmt_S ; parmetro /S=
CMP AL,t
JE pmt_T ; parmetro /T=
CMP AL,u
JE pmt_U
MOV SI,[BX] ; parmetro de dos caracteres?
OR SI," " ; mayusculizar
CMP SI,"lm" ; parmetro /ML?
JE pmt_ML
mal_proc_pmt: STC ; error en parmetro(s)
RET
fin_proc_pmt: CLC ; parmetros procesados ok.
RET
pmt_hlp: MOV param_ayuda,1
JMP mal_proc_pmt ; error de ayuda
pmt_S: MOV param_s,1
CALL get_num
JC mal_proc_pmt
MOV marcas,AL
CMP AX,15
JA fuera_rango
AND AL,AL
JZ fuera_rango
JMP otro_pmt_mas
fuera_rango: MOV marcas,255
JMP mal_proc_pmt
pmt_T: MOV param_t,1
CALL get_num
MOV cod_rastreo,AL
JMP otro_pmt_mas
pmt_U: MOV param_u,1
INC BX
JMP otro_pmt_mas
pmt_ML: MOV param_ml,1 ; en efecto
ADD BX,2
JMP otro_pmt_mas
obtener_param ENDP
; ------------ Saltar espacios, tabuladores, ... buscando un parmetro
saltar_esp: MOV AL,[BX]
INC BX
CMP AL,9 ; carcter tabulador
JE saltar_esp
CMP AL,32 ; espacio en blanco
JE saltar_esp
CMP AL,0Dh ; fin de zona de parmetros
JE fin_param
DEC BX ; puntero al primer carcter
CLC ; hay parmetro
RET
fin_param: STC ; no hay parmetro
RET
; ------------ Obtener nmero chequeando delimitadores /= y /:
get_num: INC BX
MOV AL,[BX]
INC BX
CMP AL,=
JE delimit_ok
CMP AL,:
JE delimit_ok
err_sintax: STC ; sintaxis incorrecta
RET
delimit_ok: MOV AL,[BX]
CALL obtener_num
JC err_sintax
INC BX
RET
; ------------ Extraer n de 16 bits y depositarlo en AX; al final, el
; puntero (BX) apuntar al final del nmero y CF=1 si el
; nmero era incorrecto.
obtener_num PROC
CMP AL,0Dh ; fin zona parmetros y nmero
JE fin_num
CMP AL,32 ; fin nmero
JE fin_num
CMP AL,9 ; fin nmero
JE fin_num
CMP AL,/ ; fin nmero (otro parmetro)
JE fin_num
CMP AL,: ; fin nmero (otro dato)
JE fin_num
INC BX
MOV AL,[BX]
JMP obtener_num
fin_num: MOV SI,BX
DEC SI
XOR DX,DX
MOV AX,1 ; AX = 10 elevado a la 0 = 1
otro_car: DEC BX ; prximo carcter a procesar
MOV CL,[BX]
CMP CL,=
JE ok_num ; delimitador: fin de nmero
CMP CL,:
JE ok_num ; delimitador: fin de nmero
CMP CL,.
JNE no_millar ; saltar los puntos de millar
CMP AX,1000
JE otro_car
JMP mal_num ; separador millar descolocado
no_millar: CMP CL,0
JB mal_num
CMP CL,9
JA mal_num
SUB CL,0 ; pasar ASCII a binario
MOV CH,0 ; CX = 0 .. 9
PUSH AX ; AX = 10 elevado a la N
AND AX,AX
JNZ multiplica
AND CL,CL
JNZ mal_num_pop ; a la izda slo permitir ceros
multiplica: PUSH DX ; tras completar 5 dgito
MUL CX
POP DX
JC mal_num_pop
ADD DX,AX ; DX = DX + digito (CX) * 10 ^ N (AX)
JC mal_num_pop
POP AX
CMP AX,10000
JNE potencia ; AX*10 no se desbordar
MOV AX,0 ; como prximo dgito<>0 a
JMP otro_car ; la izda ... pobre usuario
potencia: MOV DI,10
PUSH DX ; no manchar DX al multiplicar
MUL DI ; AX = AX elevado a la (N+1)
POP DX
JMP otro_car
mal_num_pop: POP AX ; reequilibrar pila
mal_num: MOV BX,SI ; nmero mayor de 65535
STC ; condicin de error
RET
ok_num: MOV BX,SI ; nmero correcto
MOV AX,DX ; resultado
CLC ; condicin de Ok.
RET
obtener_num ENDP
; ------------ Mensajes de error / ayuda
info_err_param PROC
CMP param_ayuda,1
JNE otro_error
LEA DX,ayuda_txt
CALL print
RET
otro_error: LEA DX,err_sintax_txt
CMP marcas,255
JNE err_ok
LEA DX,err_tec_txt
err_ok: CALL print
LEA DX,err_sintax_fin
CALL print
RET
info_err_param ENDP
; ------------ Ya est instalada otra versin distinta del programa
error_version PROC
PUSH ES
LEA DX,mal_ver_txt1
CALL print
LES DI,tsr_dir
MOV AL,:
MOV CL,255
CLD
REPNE SCASB
REPNE SCASB
MOV DL,ES:[DI] ; nmero de versin
MOV AH,2
INT 21h
MOV DL,.
MOV AH,2
INT 21h
MOV DL,ES:[DI+2] ; revisin
MOV AH,2
INT 21h
194 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
LEA DX,mal_ver_txt2
CALL print
POP ES
RET
error_version ENDP
; ------------ Considerar presencia de controlador XMS
inic_XMS PROC
MOV AX,4300h
INT 2Fh ; chequear presencia XMS
CMP AL,80h
JNE XMS_ausente ; no instalado
PUSH ES
MOV AX,4310h
INT 2Fh ; s: obtener su direccin
MOV XMS_off,BX ; y preservarla
MOV XMS_seg,ES
MOV xms_ins,1
POP ES
RET
XMS_ausente: MOV xms_ins,0
RET
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
; identificacin de la copia residente. Si CF=1, el
; programa no reside an (AX=0) o reside pero en otra
; versin distinta (AX=1).
residente? PROC
PUSH CX
PUSH SI
PUSH DI
PUSH ES
PUSH AX
LEA DI,autor_nom_ver ; identificacin del programa
MOV SI,DI
MOV AL,0
MOV CL,255
CLD
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamao autor+programa+versin
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de bsqueda
CALL mx_find_tsr ; buscar si est en memoria
MOV tsr_off,DI ; anotar la direccin programa
MOV tsr_seg,ES ; por si estaba instalado
POP AX
JNC resid_ok ; CF=0 -> programa ya residente
POP ES
PUSH ES
LEA DI,autor_nom_ver
MOV SI,DI
MOV AL,:
MOV CL,255
REPNE SCASB
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamao autor+programa
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de bsqueda
CALL mx_find_tsr ; buscar si est en memoria
MOV tsr_off,DI ; anotar direccin del programa
MOV tsr_seg,ES ; por si instalada otra versin
MOV AX,0
JC resid_ok ; CF=1, AX=0 -> no residente
MOV AX,1
STC ; CF=1, AX=1 -> s: otra vers.
resid_ok: POP ES
POP DI
POP SI
POP CX
RET
residente? ENDP
; ------------ Inicializar ciertas variables
inic_general PROC
XPUSH <ES, DS> ; **
MOV AH,30h
INT 21h
XCHG AH,AL
MOV dosver,AX ; versin del DOS
CALL inic_XMS ; detectar controlador XMS
MOV AH,34h
INT 21h
MOV indos_off,BX
MOV indos_seg,ES ; direccin de InDOS
INC BX
CMP dosver,300h
JB crit_ok ; Critical Error detrs en 2.x
SUB BX,2
CMP dosver,300h
JE crit_ok ; Critical Error antes en 3.0
MOV AX,5D06h
INT 21h
XPUSH <DS, SI>
XPOP <BX, ES>
crit_ok: POP DS ; *
MOV crit_err_off,BX
MOV crit_err_seg,ES ; direccin de ese flag
POP ES ; *
RET
inic_general ENDP
; ------------ Detectar EGA o tarjeta superior
detectarEGA PROC
MOV BL,10h
MOV AH,12h
INT 10h ; pedir informacin EGA al BIOS
CMP BL,10h
MOV AL,OFF
JE ega_ini ; no es EGA
MOV AL,ON
ega_ini: MOV ega,AL
RET
detectarEGA ENDP
; ------------ Informar de las teclas que activan SCRCAP
info_ya_ins PROC
PUSH DS
CALL residente?
JC tec_no_res
MOV DS,tsr_seg
tec_no_res: MOV AL,marcas
MOV AH,cod_rastreo
POP DS
LEA DX,act_teclas_txt
CALL print
TEST AL,4
JZ alt?
LEA DX,act_ctrl
CALL print_
alt?: TEST AL,8
JZ shift_izq?
LEA DX,act_alt
CALL print_
shift_izq?: TEST AL,2
JZ shift_der?
LEA DX,act_shift_izq
CALL print_
shift_der?: TEST AL,1
JZ fin
LEA DX,act_shift_der
CALL print_
fin: CMP cod_rastreo,0
JE no_mas_teclas
LEA DX,act_c_txt
CMP AH,54h
JE act_ok
LEA DX,act_otra_txt
act_ok: CALL print_
no_mas_teclas: LEA DX,act_fin_txt
CALL print
RET
print_: CALL print
PUSH AX
MOV DL,-
MOV AH,2
INT 21h
POP AX
RET
info_ya_ins ENDP
; ------------ Adaptar parmetros de un SCRCAP ya instalado en memoria
adaptar_param PROC
PUSH ES
MOV ES,tsr_seg
CMP param_s,1
JNE s_ok
MOV AL,marcas
MOV ES:marcas,AL
s_ok: CMP param_t,1
JNE c_ok
MOV AL,cod_rastreo
MOV ES:cod_rastreo,AL
c_ok: POP ES
RET
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 PROC
PUSHF
MOV segmento_real,ES ; anotar segmento del bloque
MOV offset_real,DI ; dem con el offset
MOV AX,memoria
MOV longitud_total,AX
MOV AL,1
POPF ; CF=0: usar memoria UMB XMS
JNC info_ok
DEC AL ; usar memoria convencional
info_ok: OR info_extra,AL
RET
inicializa_id ENDP
; ------------ Preservar vectores de interrupcin previos
preservar_INTs PROC
PUSH ES
PUSH DI
LEA DI,tabla_vectores
MOV CL,[DI-1]
MOV CH,0 ; CX vectores interceptados
otro_vector: PUSH CX
PUSH DI
MOV AH,35h
MOV AL,[DI]
INT 21h ; obtener vector de INT xx
POP DI
POP CX
MOV [DI+1],BX ; anotar donde apunta
MOV [DI+3],ES
ADD DI,5
LOOP otro_vector ; repetir con los restantes
POP DI
POP ES
RET
preservar_INTs ENDP
; ------------ Liberar espacio de entorno
free_environ PROC
PUSH ES
MOV ES,DS:[2Ch] ; direccin del entorno
MOV AH,49h
INT 21h ; liberar espacio de entorno
POP ES
RET
free_environ ENDP
; ------------ Reservar bloque de memoria superior del n prrafos AX,
; devolviendo en AX el segmento donde est. CF=1 si no
; est instalado el gestor XMS (AX=0) o hay un error (AL
; devuelve el cdigo de error del controlador XMS).
UMB_alloc PROC
PUSH BX
PUSH CX
PUSH DX
CMP xms_ins,1
JNE no_umb_disp ; no hay controlador XMS
MOV DX,AX ; nmero de prrafos
MOV AH,10h ; solicitar memoria superior
CALL gestor_XMS
195 PROGRAMAS RESIDENTES
CMP AX,1 ; ha ido todo bien?
MOV AX,BX ; segmento UMB/cdigo de error
JNE XMS_fallo ; fallo
POP DX ; ok
POP CX
POP BX
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo: POP DX
POP CX
POP BX
STC
RET
UMB_alloc ENDP
; ------------ Reservar memoria superior, con DOS 5.0, del tamao
; solicitado (AX prrafos). Si no hay bastante CF=1,
; en caso contrario devuelve el segmento en AX.
UPPER_alloc PROC
PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin ; necesario DOS 5.0 mnimo
UPPER_existe: PUSH AX ; preservar prrafos...
MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL ; preservar estado UMB
MOV AX,5803h
MOV BX,1
INT 21h ; conectar cadena UMBs
MOV AX,5801h
MOV BX,41h
INT 21h ; High Memory best fit
POP BX ; ...prrafos requeridos
MOV AH,48h
INT 21h ; asignar memoria
PUSHF
PUSH AX ; guardado el resultado
MOV AX,5801h
MOV BX,alloc_strat
INT 21h ; restaurar estrategia
MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h ; restaurar estado cadena UMB
POP AX
POPF
JC UPPER_fin ; hubo fallo
PUSH DS
DEC AX
MOV DS,AX
INC AX
MOV WORD PTR DS:[1],AX ; manipular PID
MOV WORD PTR DS:[16],20CDh ; simular PSP
PUSH ES
MOV CX,DS
MOV ES,CX
MOV CX,CS
DEC CX
MOV DS,CX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB ; copiar nombre de programa
POP ES
POP DS
CLC
UPPER_fin: RET
UPPER_alloc ENDP
; ------------ Reubicar programa residente a su direccin definitiva.
; Se copia tambin el PSP.
reubicar_prog PROC
PUSH DI
LEA SI,ini_residente
MOV CX,bytes_resid
CLD
REP MOVSB
XOR SI,SI
XOR DI,DI
MOV CX,256
REP MOVSB
POP DI
MOV ES:[36h],ES ; nuevo segmento de la JFT
RET
reubicar_prog ENDP
; ------------ Desviar vectores de interrupcin 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 atrs (DI se supone
; mltiplo de 16). El segmento inicial es ES.
activar_INTs PROC
PUSH CX
PUSH DS ; preservar DS para el retorno
MOV AX,100h
SUB AX,DI ; AX = 100h-DI
MOV CL,4
SHR AX,CL ; AX = (100h-DI)/16
MOV CX,ES
SUB CX,AX
MOV DS,CX
LEA SI,offsets_ints
MOV CX,CS:[SI] ; CX vectores a desviar
ADD SI,2
desvia_otro: MOV AL,CS:[SI] ; nmero del vector en curso
MOV DX,CS:[SI+1] ; obtener offset
MOV AH,25h
INT 21h ; desviar INT xx a DS:DX
ADD SI,3
LOOP desvia_otro
POP DS
POP CX
RET
activar_INTs ENDP
; ------------ Buscar entrada no usada en la interrupcin Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta tcnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
mx_no_hueco: STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
; ------------ Buscar un TSR por la interrupcin Multiplex. A la
; entrada, DS:SI cadena de identificacin del programa
; (CX bytes) y ES:DI protocolo de bsqueda (normalmente
; 1492h:1992h). A la salida, si el TSR ya est instalado,
; CF=0 y ES:DI apunta a la cadena de identificacin del
; mismo. Si no, CF=1 y ningn registro alterado.
mx_find_tsr PROC
MOV AH,0C0h
mx_rep_find: PUSH AX
PUSH CX
PUSH SI
PUSH DS
PUSH ES
PUSH DI
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl ; no hay TSR ah
CLD
PUSH DI
REP CMPSB ; comparar identificacin
POP DI
JE mx_tsr_found ; programa buscado hallado
mx_skip_hndl: POP DI
POP ES
POP DS
POP SI
POP CX
POP AX
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4 ; sacar ES y DI de la pila
POP DS
POP SI
POP CX
POP AX
CLC
RET
mx_find_tsr ENDP
; ------------ Eliminar TSR del convenio si es posible. A la entrada,
; en AH se indica la entrada Multiplex; a la salida, CF=1
; si fue imposible y CF=0 si se pudo. Se corrompen todos
; los registros salvo los de segmento. En caso de fallo
; al desinstalar, AL devuelve el vector culpable.
mx_unload PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX ; BP=entrada Multiplex del TSR
MOV CX,2
mx_ul_pasada: PUSH CX ; siguiente pasada
LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0 ; CX = n vectores
mx_ul_masvect: POP AX
PUSH AX ; pasada en curso
DEC AL
PUSH CX
mx_ul_2f: MOV AL,ES:[SI] ; vector en curso
JNZ mx_ul_pasok
CMP CX,1 ; ltimo vector?
JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL ; INT 2Fh?
JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f
mx_ul_noult: CMP AL,2Fh ; restaurar INT 2Fh?
JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f
mx_ul_pasok: PUSH ES
PUSH AX
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h ; vector en ES:BX
POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX ; INT xx en DX (aprox.)
MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI
PUSH ES:[DI-12]
MOV DI,ES:[DI-8] ; offset a la tabla de vectores
196 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV CL,ES:[DI-1]
MOV CH,0 ; nmero de vectores en CX
mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect ; este TSR usa vector analizado
ADD DI,5
LOOP mx_ul_buscav
ADD SP,4 ; no lo usa
JMP mx_ul_otro
mx_ul_usavect: POP CX ; tamao del TSR
POP BX ; segmento del TSR
CMP DX,BX
JB mx_ul_otro ; la INT xx no le apunta
ADD BX,CX
CMP DX,BX
JA mx_ul_otro ; la INT xx le apunta
PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP ; es el propio TSR?
POP AX
JNE mx_ul_chain ; no
POP ES ; s: posible reponer vector!
POP CX
POP BX
PUSH BX
PUSH CX
PUSH ES
DEC BX
JNZ mx_ul_norest ; no es la segunda pasada
POP ES ; segunda pasada...
PUSH ES
PUSH DS
MOV BX,CS:mx_ul_tsroff ; restaurar INTs
MOV DS,CS:mx_ul_tsrseg
CLI
MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: POP ES
POP CX
ADD SI,5 ; siguiente vector
DEC CX
JZ mx_unloadable ; no ms, desinstal-ar/ado!
JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la direccin
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX ; INT xx en DX (aprox.)
MOV AH,0BFh
mx_ul_otro: INC AH ; a por otro TSR
JZ mx_ul_exitnok ; se acabaron!
JMP mx_ul_masmx
mx_ul_exitnok: ADD SP,6 ; equilibrar pila
POP ES
STC
RET ; imposible desinstalar
mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok ; desinstalado
JMP mx_ul_pasada ; 1 pasada exitosa: por la 2
mx_ul_exitok: TEST ES:info_extra,111b ; tipo de instalacin?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml ; cargado en RAM convencional
CMP xms_ins,1
JNE mx_ul_freeml ; no hay controlador XMS (?)
MOV DX,ES
MOV AH,11h
CALL gestor_XMS ; liberar memoria superior
POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h ; liberar bloque de memoria ES:
POP ES
CLC
RET
mx_ul_tsrcv?: PUSH AX ; es TSR del convenio?...
PUSH ES
PUSH DI
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4 ; CF=0
POP AX
RET
mx_ul_ncvexit: POP DI ; ...no es TSR del convenio
POP ES
POP AX
STC ; CF=1
RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload ENDP
; ------------ Imprimir cadena en DS:DX delimitada por un 0
print PROC
XPUSH <AX, BX, CX, DX>
MOV BX,DX
print_mas: MOV AL,[BX]
AND AL,AL
JZ fin_print
MOV DL,AL
MOV AH,2
PUSH BX
INT 21h
POP BX
INC BX
JMP print_mas
fin_print: XPOP <DX, CX, BX, AX>
RET
print ENDP
; **********************************
; * *
; * DATOS PARA LA INSTALACION *
; * *
; **********************************
ON EQU 1 ; constantes booleanas
OFF EQU 0
xms_ins DB 0 ; a 1 si presente controlador XMS
gestor_XMS LABEL DWORD ; direccin del controlador XMS
XMS_off DW 0
XMS_seg DW 0
alloc_strat DW 0 ; estrategia asignacin (DOS 5)
umb_state DB 0 ; estado de bloques UMB (DOS 5)
tsr_dir LABEL DWORD ; direccin de la copia residente
tsr_off DW 0
tsr_seg DW 0
memoria DW 0 ; prrafos que ocupar SCRCAP
offsets_ints DW 6 ; nmero de vectores interceptados
DB 8 ; tabla de offsets de los vectores
DW ges_int08 ; de interrupcin interceptados
DB 9
DW ges_int09
DB 13h
DW ges_int13
DB 21h
DW ges_int21
DB 28h
DW ges_int28
DB 2Fh
DW ges_int2F
param_ml DB 0 ; a 1 si se indic parmetro /ML
param_s DB 0 ; a 1 si se indic parmetro /S
param_t DB 0 ; a 1 si se indic parmetro /T
param_u DB 0 ; a 1 si se indic parmetro /U
param_ayuda DB 0 ; a 1 si se indicaron parmetros /? /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 ",0
act_ctrl DB "Ctrl",0
act_alt DB "Alt",0
act_shift_der DB "ShiftDer",0
act_shift_izq DB "ShiftIzq",0
act_c_txt DB "SysReq",0
act_otra_txt DB 8," y la tecla elegida",0
act_fin_txt DB 8," para activarlo.",13,10,0
nocabe_txt DB ": Instalacin imposible.",13,10
DB " Ya hay 64 programas residentes con la "
DB "misma tcnica.",13,10,0
err_sintax_txt DB 13,10," - Parmetro(s) incorrecto(s).",0
err_tec_txt DB 13,10," - Parmetro /S fuera de rango.",0
err_sintax_fin DB 13,10," Ejecute SCRCAP /? para obtener "
DB "ayuda.",13,10,7,0
mal_ver_txt1 DB 13,10
DB " - Error: ya est instalada la versin ",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," - Desinstalacin imposible (se ha "
DB "instalado despus un programa"
DB 13,10," que no respeta el convenio y tiene "
DB "alguna interrupcin comn).",13,10,7,0
imp_desins_txt DB 13,10," - Programa an no instalado: "
DB "imposible desinstalarlo.",13,10,0
ayuda_txt LABEL BYTE
DB 13,9," SCRCAP 1.0 - Utilidad de captura de pantallas de texto."
DB 13,10
DB " (c) 1992 CiriSOFT, (c) Grupo Universitario de Informtica - "
DB "Valladolid.",13,10,10
DB 9," SCRCAP [/ML] [/S=marcas] [/T=codigo de rastreo] [/U] [/?|H]"
DB 13,10,10
DB " Una vez instalado, al pulsar Alt-SysReq (Alt-PetSis) la "
DB "pantalla actual se",13,10
DB " salvar en disco con nombre SCRxx-nn.SCR, donde xx es la "
DB "anchura hexadecimal",13,10
DB " de la misma (en columnas) y nn el nmero de fichero; ya que, "
DB "partiendo de 00",13,10
DB " tras instalar el programa, se crean sucesivamente cada vez "
DB "que se invoca la",13,10
DB " utilidad. Se salvan tambin pantallas de texto no estndar "
DB "(ms de 25 lneas",13,10
DB " u 80 columnas); las pantallas grficas generan ficheros "
DB "inservibles. Lo que",13,10
DB " se almacena en los ficheros es exactamente el contenido del "
DB "buffer de vdeo;",13,10
DB " la captura va precedida y sucedida de un sonido de aviso "
DB "durante 1 segundo.",13,10,10
DB " Por defecto se instala residente en memoria superior (si la "
DB "hay) de manera",13,10
DB " automtica, sea cual sea la versin del sistema o el "
DB "controlador de memoria",13,10
DB " (incluso sin indicar DOS=UMB en el CONFIG del DOS 5.0): con "
DB "/ML se fuerza la",13,10
DB " instalacin en memoria convencional. Consumo: 2208 bytes (2,16 "
DB "Kb).",13,10,10
DB " El parmetro /S permite elegir la combinacin de teclas de "
DB "activacin (se",13,10
DB " obtiene sumando: 1-shift derecho, 2-shift izdo, 4-Ctrl, "
DB "8-Alt); con /T puede",13,10
DB " cambiarse opcionalmente la tecla de activacin. Se puede "
DB "desinstalar con /U,",13,10
DB " siendo a menudo posible incluso aunque no sea el ltimo TSR "
DB "instalado.",13,10,0
fin_prog EQU $
scrcap ENDS
END inicio
197 PROGRAMAS RESIDENTES
Para visualizar las pantallas capturadas puede utilizarse la utilidad SCRVER.C, que admite comodines
para poder ver cualquier conjunto de ficheros. Con SCR2TXT.C se convierten las pantallas capturadas (de
40/80/94/100/120/132 160 columnas) a modo texto: se suprimen los colores, se eliminan la mayora de los
cdigos de control, se quitan los espacios en blanco al final de las lneas y se aaden retornos de carro para
separarlas. Esto ltimo provoca, en pantallas que ocupan justo las 80 columnas, que al emplear el TYPE del
DOS las lneas queden separadas por una lnea extra en blanco (si tuvieran 79 columnas o si se carga desde
un 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= ; /* carcter 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 GRFICOS.
La mayora de los programas residentes prefieren operar con pantallas de texto: ocupan menos
memoria, son totalmente estndar y ms rpidas. En la prctica, la dificultad asociada al proceso de preservar
el contenido de una pantalla grfica y despus restaurarla lleva a muchos programas residentes a no dejarse
activar cuando la pantalla est en modo grfico. Sin embargo, existe una tcnica sencilla que permite
simplificar este proceso, siendo operativa en todos los modos de la EGA y VGA estndar, aunque presenta
alguna dificultad en ciertos modos de la VGA.
10.12.1 - CASO GENERAL.
En los modos estndar de IBM (y en general tambin en los no estndar) cuando se solicita a la BIOS
que establezca el modo de vdeo (vanse las funciones de la BIOS en los apndices) si el bit ms significativo
del modo se pone a 1, al cambiar de modo no se limpia la pantalla. Esta caracterstica est disponible slo
198 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
en mquinas con tarjeta EGA o VGA (tanto XT como AT). Se trata de una posibilidad muy interesante, que
permite a los programas residentes activar momentneamente una pantalla de texto, preservar el fragmento
de la misma que van a emplear y, al final, restaurarlo y volver al modo grfico como si no hubiera sucedido
nada, sin necesidad de preservar ni restaurar zonas grficas. Tambin habrn de preservar la posicin inicial
del cursor y la pgina de vdeo activa inicialmente (que habrn de restaurar junto con el modo de vdeo), as
como las paletas de la EGA y VGA, tareas stas que puede simplificar la BIOS.
Por ejemplo: si la pantalla estaba en modo 12h (VGA 640x480 con 16 colores) se puede activar el
modo 83h (el 3 con el bit 7 activo) de texto de 80x25 y, cuando halla que restaurarla, activar el modo 92h
(el 12h con el bit 7 activo). Evidentemente, despus habr que engaar de alguna manera a la BIOS para que
crea que la pantalla est en modo 12h y no 92h (sutil diferencia, no?) y ello se consigue borrando el bit ms
significativo de la posicin 40h:87h (la variable de la BIOS 40h:49h indica siempre el nmero de modo de
pantalla con el bit ms significativo borrado: este bit se almacena separadamente en 40h:87h). Esta operacin
es segura, ya que la diferencia entre el modo 12h y el 92h es slo a nivel de software y no de hardware. Un
programa residente elegante, adems, se tomar la molestia de dejar activo el bit de 40h:87h si as lo estaba
al principio, antes de restaurar el modo grfico (poco probable, pero posible -sobre todo cuando el usuario
activa ms de un programa residente de manera simultnea-).
10.12.2 - CASO DEL MODO 13H DE LA VGA Y MODOS SUPERVGA.
Esta tcnica presenta, sin embargo, una ligera complicacin al trabajar en el modo 13h de la VGA
(320x200 con 256 colores) o en la mayora de los modos SuperVGA. El problema consiste en que, al pasar
a modo texto, la BIOS define el juego de caracteres -que en la EGA/VGA es totalmente programable-
utilizando una cierta porcin de la memoria de vdeo de la tarjeta. Por desgracia, esa porcin de la memoria
de la tarjeta grfica es parte de la pantalla en el modo 13h y en los modos SuperVGA. La solucin no es muy
complicada, aunque s un poco engorrosa. Ante todo, recordar que esto slo es necesario en modos de
pantalla avanzados o en el 13h. Una posible solucin consiste en preservar la zona que va a ser manchada
(8 Kb) en un buffer, pasar a modo texto y, antes de volver al modo grfico, redefinir el juego de caracteres
de texto de tal manera que al volver a modo grfico ya est restaurada la zona manchada. Este orden de
operaciones no es caprichoso y lo he elegido para reducir los accesos al hardware, como se ver. El problema
principal radica en el hecho de que la arquitectura de la pantalla en los modos grficos y de texto vara de
manera espectacular. Por ello, no hay un algoritmo sencillo para acceder a la zona de memoria de grficos
que hay que preservar. Para no desarrollar complicadas rutinas -por si fuera poco, una para cada modo
grfico- es ms cmodo programar el controlador de grficos para configurar de manera cmoda la memoria
de vdeo y preservar sin problemas los 8 Kb deseados. Despus, no hace falta restaurar el estado de ningn
controlador de vdeo, ya que la BIOS lo reprogramar correctamente al pasar a modo texto. Por ltimo, y
estando an en modo texto, se redefinir el juego de caracteres con los 8 Kb preservados. Como
inmediatamente despus se vuelve al modo grfico, el usuario no notar la basura que aparezca en la pantalla
durante breves instantes y, de nuevo, la BIOS reprogramar adecuadamente el controlador de grficos. El
siguiente ejemplo prctico parte de la suposicin de que nos encontramos en el modo 13h:
CALL def_car_on ; habilitar acceso a tabla de caracteres
CALL preservar8k ; guardar 8 Kb de A000:0000 en un buffer
MOV AX,83h
INT 10h ; pasar a modo texto 80x25
; ... operar en modo texto ...
CALL def_car_on ; habilitar acceso a tabla de caracteres
CALL restaurar8k ; copiar el buffer de 8 Kb en A000:0000
MOV AX,93h ; 13h + 80h
INT 10h ; restaurar de nuevo el modo grfico
Las rutinas preservar8k y restaurar8k son tan obvias que, evidentemente, no las comentar. Sin
embargo, la rutina que prepara el sistema de vdeo de tal manera que se pueda redefinir el juego de caracteres
de texto, requiere conocimientos acerca de la arquitectura de las tarjetas grficas EGA y VGA a bajo nivel.
Esta informacin puede obtenerse en libros especializados sobre grficos (consltese la bibliografa) aunque
a continuacin expongo el listado de def_car_on; eso s, sin entrar en detalles tcnicos acerca de su
funcionamiento:
199 PROGRAMAS RESIDENTES
def_car_on PROC
MOV DX,3C4h ; puerto del secuenciador
LEA SI,car_on ; cdigos a enviarle
MOV CX,4
CLD
CLI ; precauciones
def_on_1: LODSW
OUT DX,AX ; programar registro
LOOP def_on_1
STI ; no ms precauciones
MOV DL,0CEh ; 3CEh = puerto del controlador de grficos
MOV CX,3
def_on_2: LODSW
OUT DX,AX ; programarlo
LOOP def_on_2
RET
car_on DW 100h, 402h, 704h, 300h, 204h, 5, 6 ; datos
def_car_on ENDP
10.12.3 - ALGUNOS PROBLEMAS.
En la aplicacin prctica de las rutinas expuestas se han detectado algunos problemas de
compatibilidad con algunas tarjetas. El ms grave se produjo con una OAK SuperVGA: en algunos modos
de 800 y 1024 puntos, se colgaba el ordenador al ejecutar def_car_on. La solucin adoptada consisti en dar
un paso intermedio: antes de llamar a def_car_on se puede poner la pantalla en un modo no conflictivo y que
sea grfico 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 mtodo propuesto es ciertamente sencillo, aunque se complique un poco ms en algunos modos
de la VGA. Tiene requerimientos (como el buffer de 8 Kb) que no estn quiz al alcance de los programas
residentes menos avanzados. Los ms avanzados pueden grabar los 8 Kb en disco duro, si la mquina est
dotada del mismo, as como toda la memoria de pantalla CGA (unos modestos 16 Kb) en las mquinas que
no estn dotadas de EGA o VGA y no pueden conmutar el modo de pantalla sin borrar la misma. Las
mquinas que no tengan disco duro aumentarn el consumo de memoria del programa residente en 8/16 Kb,
aunque peor sera tener que preservar hasta 1 Mb de memoria de vdeo!. El problema est en las tarjetas no
compatibles VGA: mucho cuidado al utilizar la rutina def_car_on (hay que detectar antes la presencia de una
autntica 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 (slo acompaa al PS/2-30, en sus primeros modelos
un compatible XT); los ms perfeccionistas siempre pueden consultar bibliografa especializada en grficos
para tratar de manera especial este adaptador de vdeo, aunque sera incluso ms recomendable ocuparse antes
de la Hrcules. Otro premio reservado para estos perfeccionistas ser la posibilidad de conmutar los modos
de pantalla accediendo al hardware y sin apoyo de la BIOS, para que no borre la pantalla en las CGA.
Tngase en cuenta que esta operacin sera mucho ms delicada en las EGA y VGA (es ms difcil restaurar
todos los parmetros hardware del modo grfico activo inicialmente) en las que adems habra que definir
un juego de caracteres de texto. Por cierto, el estndar VESA posee tambin funciones para preservar y
restaurar el estado del adaptador de vdeo; el lector podra 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 importante
ya que, en teora, desde dentro de Windows no es necesario tener instalados programas residentes, al tratarse
de un entorno multitarea que permite tener varios programas activos en pantalla a la vez. Sin embargo, puede
ser interesante en ocasiones crear programas residentes que tambin operen bajo Windows, de cara a no tener
que desarrollar una versin especfica no residente para este entorno.
Un problema importante de los programas residentes consiste en la dificultad para leer el teclado. La
razn es que Windows reemplaza totalmente al controlador del DOS, anulando los TSR que se activan por
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 no
es recomendable porque la prctica reiterada de este mtodo provoca anomalas en el mismo (tales como
aparicin de nmeros en los cursores, estado de Shift que se engancha, etc.) debido a las limitaciones del
hardware. Un mtodo ms recomendable, aunque menos potente, consiste en comprobar las variables de la
BIOS que indican el estado de maysculas, bloque numrico, shift, ... ya que estas variables son
correctamente actualizadas desde dentro de Windows. El nico problema es la limitacin de combinaciones
posibles que se pueden realizar con estas teclas, de cara a permitir la convivencia de varios programas
residentes (problema que se puede solventar permitiendo al usuario elegir las teclas de activacin).
El otro problema est relacionado con la multitarea de Windows. Si se abren varios procesos DOS
desde este entorno y se activa el programa residente en ms de uno de ellos, pueden aparecer problemas de
reentrada (la segunda ejecucin estropear los datos de la primera). La solucin ms sencilla consiste en no
permitir la invocacin del programa residente desde ms de una tarea; sin embargo, en algunos TSR (tales
como utilidades de macros de teclado, etc.) esto supone una grave e intolerable restriccin. Otra solucin
sencilla consiste en obligar al usuario a instalar el TSR en cada sesin de DOS abierta, con lo que todo el
entorno de operacin ser local a dicha sesin. Para los casos en que no sea recomendable esto ltimo, se
puede quemar el ltimo y ms efectivo cartucho: comunicar el TSR con el conmutador de tareas de Windows
para emplear memoria instantnea. El nico inconveniente es que Windows slo facilita memoria instantnea
en el modo extendido 386, no en el modo estndar ni -en el caso de la versin 3.0- en el real. Sin embargo,
con la versin 3.1 de Windows, en el modo estndar se puede emplear el conmutador de tareas del DOS 5.0,
que es el que utiliza dicho modo. No deja de ser una pena tener que utilizar un mtodo diferente para el
modo estndar que para el extendido, aunque la recompensa para quien implemente soporte en sus TSR para
los dos mtodos es que les har compatibles tambin con el conmutador de tareas del MS-DOS 5.0. Se puede
interceptar el arranque de Windows y comprobar si lo hace en modo real, en cuyo caso se puede abortar su
ejecucin y emitir un mensaje de error para solicitar al usuario que no desinstale el TSR antes de entrar en
ese modo de Windows.
Cuando Windows arranca, llama a la INT 2Fh con AX=1605h: un TSR puede interceptar esta llamada
(como en cualquier otra interrupcin, llamando primero al controlador previo) y comprobar si el bit 0 de DX
est a cero (en ese caso se estar ejecutando en modo extendido): si se desea abortar la ejecucin de Windows
bastar cargar un valor distinto de 0 en CX antes de retornar.
Si el TSR necesita reas de datos locales a cada sesin en el modo extendido, puede indicrselo a
Windows con un puntero a un rea de datos denominado SWSTARTUPINFO en ES:BX. Para ello, y
teniendo en cuenta que puede haber varios TSR que intercepten las llamadas a la INT 2Fh con AX=1605h,
este rea ha sido diseada para almacenar una cadena de referencias entre todos ellos; por ello es preciso
almacenar primero el ES:BX inicial de la rutina en dicha estructura y cargar ES:BX apuntndola antes de
retornar. El formato de SWSTARTUPINFO es el siguiente:
DW 3 ; versin de la estructura
DD ? ; puntero a la prxima estructura SWSTARTUPINFO (ES:BX inicial)
DD 0 ; puntero al nombre ASCIIZ del dispositivo virtual ( 0)
DD 0 ; datos de referencia del dispositivo virtual (si tiene nombre)
DD ? ; puntero a la tabla de registros de datos locales ( 0)
El formato de la tabla de registros de datos locales, que define las estructuras de datos que sern
locales a cada sesin, es el siguiente:
DD ? ; direccin de memoria de la estructura
DW ? ; tamao de la estructura
. . .
. . .
DD 0 ; estructura NULL
DW 0 ; (fin de lista)
En los momentos crticos en que el TSR deba evitar una conmutacin de tareas, puede emplear las
funciones BeginCriticalSection (llamar a INT 2Fh con AX=1681h) y EndCriticalSection (llamar a INT 2Fh
con AX=1682h); el TSR debe estar poco tiempo en fase crtica para no ralentizar Windows.
201 PROGRAMAS RESIDENTES
Para detectar la presencia del conmutador de tareas del MS-DOS 5.0 se debe llamar a la INT 2Fh
con AX=4B02h: si a la vuelta AX es 0, significa que est cargado y ES:DI apunta a la rutina de servicio del
mismo, que pone varias funciones a disposicin de los TSR: los TSR debern ejecutar la funcin AX=4
(Conectar a la cadena de Notificacin) al instalarse en memoria y la funcin AX=5 (Desconectar de la
Cadena de Notificacin) al ser desinstalados, para informar al conmutador. Una vez enganchado, el TSR ser
llamado por el conmutador de tareas para ser informado de todo lo interesante que suceda (de cosas tales
como la creacin y destruccin de sesiones, suspensin del conmutador, etc.) por medio de la ejecucin de
la rutina de notificacin del mismo, pudiendo el TSR permitir o no, por ejemplo, la suspensin de la sesin...
el aviso de inicio de sesin es fundamental para los TSR que tienen reas de datos temporales que inicializar
al comienzo de cada sesin. El procedimiento general lo inicia el conmutador de tareas llamando a la INT
2Fh con AX=4B01h: los TSR sern invocados unos tras otros (pasndose mutuamente el control). Para
gestionar esto existe una estructura de datos denominada SWCALLBACKINFO (apuntada por ES:BX al
llamar a INT 2Fh con AX=4B01h):
DD ? ; puntero a la estructura SWCALLBACKINFO anterior
DD ? ; puntero a la rutina de notificacin del TSR
DD ? ; rea reservada
DD ? ; puntero a la lista de estructuras SWAPINFO
La lista de estructuras SWAPINFO tiene a su vez el siguiente formato:
DW 10 ; longitud de la estructura
DW ? ; identificador del API (1-NETBIOS, 2-802.2, 3-TCP/IP, 4-Tuberas
LanManager, 5-NetWare IPX)
DW ? ; nmero de la mayor versin del API soportada
DW ? ; nmero de la menor versin del API soportada
DW ? ; nivel de soporte: 1-mnimo (el TSR impide la conmutacin de la tarea
incluso tras finalizar sus funciones), 2-soporte a nivel API (el TSR
impide la conmutacin de tareas si las peticiones son importantes), 3-
Compatibilidad de conmutacin (se permite conmutar de tarea incluso con
peticiones importantes, aunque algunas podran fallar), 4-Sin
compatibilidad (se permite siempre la conmutacin).
Cuando el conmutador de tareas arranca, ejecuta una INT 2Fh con AX=4D05h para tomar nota de
los bloques de datos locales a cada sesin, llamada que los TSR debern detectar del mismo modo que
cuando comprobaban la ejecucin de Windows en modo extendido: la estructura de datos es adems, por
fortuna, la misma en ambos casos.
Las funciones que debe soportar la rutina de notificacin, apuntada por la estructura
SWCALLBACKINFO, son las siguientes:
0000h inicializacin del conmutador
Devuelve: AX = 0000h si permitido
= no cero si no permitir iniciar el conmutador
0001h pregunta de suspensin del conmutador
BX = Identificacin de sesin
Devuelve: AX = 0000h si permitir conmutacin (el TSR no est en regin crtica)
= 0001h si no
0002h suspensin del conmutador
BX = Identificacin de sesin
interrupciones inhibidas
Devuelve: AX = 0000h si permitido conmutar de sesin
= 0001h si no
0003h activando conmutador
BX = Identificacin de sesin
CX = banderines de estado de la sesin
bit 0: activo si primera activacin de la sesin
bits 1-15: reservado (0)
interrupciones inhibidas
Devuelve: AX = 0000h
0004h sesin activa del conmutador
BX = Identificacin de sesin
CX = banderines de estado de la sesin
bit 0: activo si primera activacin de la sesin
bits 1-15: reservado (0)
Devuelve: AX = 0000h
0005h crear sesin del conmutador
BX = Identificacin de sesin
DEVUELVE: AX = 0000h si permitido
= 0001h si no
202 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
0006h destruir sesin
BX = Identificacin de sesin
Devuelve: AX = 0000h
0007h salida del conmutador
BX = banderines
bit 0: activo si el conmutador que llama es el nico cargado
bits 1-15: reservados (0)
Devuelve: AX = 0000h
203 CONTROLADORES DE DISPOSITIVOS
Captulo XI: CONTROLADORES DE DISPOSITIVO
11.1. - INTRODUCCIN.
Los controladores de dispositivo (device drivers en ingls) son programas aadidos al ncleo del
sistema operativo, concebidos inicialmente para gestionar perifricos y dispositivos especiales. Los
controladores de dispositivo pueden ser de dos tipos: orientados a caracteres (tales como los dispositivos
NUL, AUX, PRN, etc. del sistema) o bien orientados a bloques, constituyendo las conocidas unidades de
disco. La diferencia fundamental entre ambos tipos de controladores es que los primeros reciben o envan la
informacin carcter a carcter; en cambio, los controladores de dispositivo de bloques procesan, como su
propio nombre indica, bloques de cierta longitud en bytes (sectores). Los controladores de dispositivo,
aparecidos con el DOS 2.0, permiten aadir nuevos componentes al ordenador sin necesidad de redisear el
sistema operativo.
Los controladores de dispositivo han sido tradicionalmente programas binarios puros, similares a los
COM aunque ensamblados con un ORG 0, a los que se les colocaba una extensin SYS. Sin embargo, no
hay razn para que ello sea as ya que un controlador de dispositivo puede estar incluido dentro de un
programa EXE, con la condicin de que el cdigo del controlador sea el primer segmento de dicho programa.
El EMM386.EXE del MS-DOS 5.0 sorprendi a ms de uno en su da, ya que llamaba la atencin observar
cmo se poda cargar con DEVICE: lo cierto es que esto es factible incluso desde el DOS 2.0 (pese a lo que
pueda indicar algn libro), pero ha sido mantenido casi en secreto. Actualmente es relativamente frecuente
encontrar programas de este tipo. La ventaja de un controlador de dispositivo de tipo EXE es que puede ser
ejecutado desde el DOS para modificar sus condiciones de operacin, sin complicar su uso por parte del
usuario con otro programa adicional. Adems, un controlador de dispositivo EXE puede superar el lmite de
los 64 Kb, ya que el DOS se encarga de relocalizar las referencias absolutas a segmentos como en cualquier
programa EXE ordinario. Por cierto, el RAMDRIVE.SYS de WINDOWS 3.1 (no el de MS-DOS 5.0) y el
VDISK.SYS de DR-DOS 6.0 son realmente programas EXE, aunque renombrados a SYS (aviso: no
recomiendo a nadie ponerles extensin EXE y ejecutarlos despus).
11.2.- ENCABEZAMIENTO Y PALABRA DE ATRIBUTOS.
Todo controlador de dispositivo de bloques comienza con una cabecera estndar, mostrada a
continuacin:
CABECERA DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES
offset 0 DD 0FFFFFFFFh ; doble palabra de valor -1
offset 4 DW 0 ; palabra de atributos (ejemplo arbitrario)
offset 6 DW estrategia ; desplazamiento de la rutina de estrategia
offset 8 DW interrupcion ; desplazamiento de la rutina de interrupcin
offset 10 DB 1 ; nmero de discos definidos: 1 por ejemplo
offset 11 DB 7 DUP (0) ; 7 bytes no usados
Al principio, una doble palabra con el valor 0FFFFFFFFh (-1 en complemento a 2) ser modificada
posteriormente por el DOS para enlazar el controlador de dispositivo con los dems que haya en el sistema,
formando una cadena. No fue una ocurrencia muy feliz elegir precisamente ese valor inicial como obligatorio
para la copia en disco, dado que la instruccin de cdigo de operacin 0FFFFh es ilegal y bloquea la CPU
si es ejecutada. Esto significa que un controlador de dispositivo binario puro no puede ser renombrado a
204 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
COM y ejecutado tambin desde el DOS (habr de ser necesariamente de tipo EXE). A continuacin, tras
esta doble palabra viene una palabra de atributos, cuyo bit ms significativo est borrado en los dispositivos
de bloques para diferenciarlos de los dispositivos de caracteres. Tras ello, aparecen los offsets a las rutinas
de estrategia e interrupcin, nicas de las que consta el controlador. Por ltimo, un byte indica cuntas
nuevas unidades de disco se definen y detrs hay 7 bytes reservados -ms bien no utilizados-.
PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE BLOQUES
bit 15: borrado para indicar dispositivo de bloques
bit 14: activo si se soporta IOCTL
bit 13: activo para indicar disco de formato no-IBM
bit 12: reservado
bit 11: en DOS 3+ activo si soportadas rdenes OPEN/CLOSE y REMOVE
bit 10: reservados
bit 9: no documentado. Al parecer, el DRIVER.SYS del DOS 3.3 lo emplea para
indicar que no est permitida una E/S directa en las unidades nuevas
bit 8: no documentado. El DRIVER.SYS del DOS 3.3 lo pone activo para las
unidades nuevas
bit 7: en DOS 5+ activo si soportada orden 19h (CHECK GENERIC IOCTL SUPPORT)
bit 6: en DOS 3.2+ activo si soportada orden 13h (GENERIC IOCTL)
bits 5-2: reservados
bit 1: activo si el driver soporta direccionamientos de sector de 32 bits
(unidades de ms de 65536 sectores y, por ende, ms de 32 Mb).
bit 0: reservado
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 cul es el nombre del
dispositivo:
CABECERA DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES
offset 0 DD 0FFFFFFFFh ; doble palabra de valor -1
offset 4 DW 8000h ; palabra de atributos (ejemplo arbitrario)
offset 6 DW estrategia ; desplazamiento de la rutina de estrategia
offset 8 DW interrupcion ; desplazamiento de la rutina de interrupcin
offset 10 DB "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 sea
lo que realmente se desea hacer (se est creando un dispositivo AUX que ya existe, con lo que se sobrescribe
y anula el puerto serie original). En general, adems de los nombres de los dispositivos del sistema, no
deberan utilizarse los que crean ciertos programas (como el EMMXXXX0 del controlador EMS, etc.).
Conviene decir aqu que muchos de los controladores de dispositivo de caracteres instalados en el ordenador
no lo son tal realmente, sino que se trata de simples programas residentes que se limitan a dar error a quien
intenta acceder a ellos (pruebe el lector a ejecutar la orden COPY *.* EMMXXXX0: con el controlador de
memoria expandida instalado) aunque algunos implementan ciertas funciones va IOCTL.
La palabra de atributos del controlador de dispositivo de caracteres tambin cambia respecto al de
bloques, pero sustancialmente:
PALABRA DE ATRIBUTOS DEL CONTROLADOR DE DISPOSITIVO DE CARACTERES
bit 15: activo para indicar dispositivo de caracteres
bit 14: activo si se soporta IOCTL
bit 13: en DOS 3+ activo si se soporta orden 10h (OUTPUT UNTIL BUSY)
bit 12: reservado
bit 11: en DOS 3+ activo si soportadas rdenes OPEN/CLOSE y REMOVE)
bits 10-8: reservados
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)
bit 5: reservado
bit 4: activo si el dispositivo es especial y utiliza la INT 29h (llamada
por el DOS para imprimir e carcter 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 en
cualquier momento la hora del sistema con la siguiente secuencia de 6
bytes: DW dias_transcurridos_desde_1980
DB minutos
DB horas
DB centsimas de segundo
DB segundos
bit 2: activo si es el dispositivo NUL
bit 1: activo si es el dispositivo de salida estndar
bit 1: activo si es el dispositivo de entrada estndar
205 CONTROLADORES DE DISPOSITIVOS
11.3. - RUTINAS DE ESTRATEGIA E INTERRUPCIN.
Cuando el DOS va a acceder a un dispositivo (debido a una peticin de un programa de usuario)
ejecuta, de manera secuencial, las rutinas de estrategia e interrupcin, que son de tipo FAR. Hay que recordar
que el paso del MS-DOS 1.0 al 2.0 supuso una emigracin de la filosofa del CP/M a la del UNIX. La razn
de la existencia separada de las rutinas de estrategia e interrupcin se inspira en la filosofa de diseo del
UNIX y su arquitectura multitarea, aunque para el DOS hubiera sido suficiente una sola rutina. De hecho,
la rutina de estrategia tiene como nica misin recoger la direccin de la cabecera de peticin de solicitud
que el DOS enva al driver, en ES:BX. Las 3 lneas de cdigo siguientes constituyen una rutina de estrategia,
ya que son prcticamente idnticas en todos los controladores de dispositivo:
RUTINA DE ESTRATEGIA
estrategia PROC FAR ; de tipo FAR
MOV CS:pcab_pet_desp,BX
MOV CS:pcab_pet_segm,ES
RET
estrategia ENDP
pcab_peticion LABEL DWORD
pcab_pet_desp DW 0
pcab_pet_segm DW 0
Para qu sirve la cabecera de peticin de solicitud?: sencillamente, es un rea de datos que el DOS
utiliza para comunicarse con el controlador de dispositivo. Por medio de este rea se envan las rdenes y
los parmetros que el dispositivo soporta, y se recogen ciertos resultados. La rutina de interrupcin del
dispositivo, adems de preservar todos los registros que va a alterar para restaurarlos al final, se encarga de
consultar la direccin de la cabecera de peticin de solicitud que almacen la rutina de estrategia y comprobar
qu le est pidiendo el DOS. No es realmente una rutina de interrupcin ya que retorna con RETF, en vez
de con IRET, por lo que nunca podr ser invocada por una interrupcin hardware. Aunque segn la orden
a procesar el tamao de la cabecera de peticin de solicitud puede variar, los primeros 13 bytes son:
CABECERA DE PETICIN DE SOLICITUD (13 PRIMEROS BYTES) COMN A TODAS LAS RDENES
offset 0 DB longitud_bloque ; longitud total de la cabecera
offset 1 DB num_disco ; disco implicado (slo en disp. bloques)
offset 2 DB orden ; orden solicitada por el sistema
offset 3 DW palabra_estado ; donde devolver la palabra de estado
offset 5 DD pun_dos ; apuntador usado por el DOS
offset 9 DD encadenamiento ; usado por el DOS para encadenar
En general, la rutina de interrupcin suele multiplicar por dos el nmero de la orden (almacenada en
el offset 2 de la cabecera de peticin), para as acceder indexadamente a una tabla de palabras que contiene
los desplazamientos a las rutinas que procesan las diversas rdenes: aunque esto no ha de ser necesariamente
as, casi todos los controladores de dispositivo se comportan de esta manera.
11.4. - ORDENES A SOPORTAR POR EL CONTROLADOR DE DISPOSITIVO.
00h INIT
01h MEDIA CHECK (dispositivos de bloque)
02h BUILD BPB (dispositivos de bloque)
03h IOCTL INPUT
04h INPUT
05h NONDESTRUCTIVE INPUT, NO WAIT (dispositivos de caracteres)
06h INPUT STATUS (dispositivos de caracteres)
07h INPUT FLUSH (dispositivos de caracteres)
08h OUTPUT
09h OUTPUT WITH VERIFY
0Ah OUTPUT STATUS (dispositivos de caracteres)
0Bh OUTPUT FLUSH (dispositivos de caracteres)
0Ch IOCTL OUTPUT
0Dh (DOS 3+) DEVICE OPEN
0Eh (DOS 3+) DEVICE CLOSE
0Fh (DOS 3+) REMOVABLE MEDIA (dispositivos de bloques)
10h (DOS 3+) OUTPUT UNTIL BUSY (dispositivos de caracteres)
11h-12h no usada
13h (DOS 3.2+) GENERIC IOCTL
14h-16h no usadas
17h (DOS 3.2+) GET LOGICAL DEVICE
18h (DOS 3.2+) SET LOGICAL DEVICE
19h (DOS 5.0+) CHECK GENERIC IOCTL SUPPORT
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 no
ser preciso implementar todas: de hecho, incluso para un disco virtual basta con algunas de las primeras 16.
Todas las rdenes devuelven una palabra de estado al sistema operativo, cuyo formato puede consultarse a
continuacin. En general, las ordenes no soportadas pueden originar un error o bien ser sencillamente
ignoradas (en ese sentido, crear un dispositivo NUL es tarea realmente sencilla).
FORMATO DE LA PALABRA DE ESTADO
bit 15: Activo si hay error, en ese caso los bits 0-7 indican el tipo de error
bits 14-10: Reservados
bit 9: Activo si el controlador de dispositivo no est listo. En las operaciones
de entrada est listo si hay un carcter en el buffer de entrada o si tal
buffer no existe; en las de salida cuando el buffer an no est lleno.
bit 8: Activo si el controlador de dispositivo ha acabado de ejecutar la orden.
Hasta el DOS 5.0 al menos, esto es siempre as (en un hipottico sistema
multitarea, una orden podra ejecutarse en varias rfagas de CPU).
bits 7-0: Cdigo de error, si el bit 15 est activo:
00h disco protegido contra escritura
01h unidad desconocida
02h unidad no preparada
03h orden desconocida
04h error de CRC
05h longitud invlida de la cabecera de peticin
06h fallo en el posicionamiento del cabezal
07h medio fsico desconocido
08h sector no encontrado
09h impresora sin papel
0Ah error de escritura
0Bh error de lectura
0Ch anomala general
0Dh reservado
0Eh (CD-ROM) medio fsico no disponible
0Fh cambio de disco no permitido
La construccin de rutinas de gestin para las diversas rdenes que han de soportarse no es un
proceso muy complicado, pese a que est envuelto en una leyenda negra. Sin embargo, puede que parte de
la explicacin que viene a continuacin sobre dichas rdenes sea difcil de entender al lector poco iniciado.
No hay que olvidar que los controladores de dispositivo respetan unas normas de comportamiento definidas
por el fabricante del DOS, y ms que de intentar comprender por qu una cosa es de una manera
determinada, de lo que se trata es de obedecer. En general, lo que no se entienda puede ser pasado por alto
ya que probablemente no es estrictamente necesario conocerlo. Adems, casi ningn controlador necesita
soportar todas las rdenes, como se ver al final en los programas de ejemplo.
11.4.0. - Orden 0 o INIT.
CABECERA DE PETICIN 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 instala
ante algn fallo, para no quedar residente basta indicar
un offset 0 (el segmento es vital inicializarlo con CS).
offset 12h: DWORD: A la entrada, el DOS indica dnde comienza la lnea de
parmetros del CONFIG.SYS. A la salida se indica al DOS
la direccin de la tabla de apuntadores a estructuras BPB
(esto ltimo slo en los dispositivos de bloques).
offset 16h: BYTE: Desde el DOS 3.0, nmero de discos lgicos existentes
hasta ese momento ej. 3 para A: B: y C: (solo en los
dispositivos de bloque).
Esta es la primera de todas las rdenes y se ejecuta siempre una vez cuando el dispositivo es cargado
en memoria, con objeto de que ste se inicialice. Aqu s se pueden emplear libremente las funciones del DOS
(en el resto de las rdenes no: el driver es un programa residente ms). En su inicializacin el driver decide
qu cantidad de memoria se queda residente y puede analizar la lnea de comandos del CONFIG.SYS para
comprobar los parmetros del usuario. En los dispositivos de bloque se indica tambin al sistema el nmero
de unidades definidas por el controlador y la direccin de una tabla de punteros a estructuras BPB, ya que
207 CONTROLADORES DE DISPOSITIVOS
existe una de estas estructuras para cada unidad lgica. El BPB (BIOS Parameter Block) es una estructura
que contiene informacin sobre las unidades; puede consultarse en el captulo 7. Aunque el BPB ha sido
ampliado en las ltimas versiones del DOS, para construir discos de menos de 65536 sectores solo hace falta
completar los primeros campos (solo hasta los relacionados con el DOS 2.0 o, como mucho, el 3.0).
Los parmetros en la lnea de comandos del CONFIG.SYS son similares a los de un programa
ordinario, aunque como se observa en el cuadro anterior su direccin se obtiene en el puntero de 32 bits
ubicado en el offset 12h de la cabecera de peticin de solicitud. Por ello, si ES:BX apunta a dicha cabecera,
la instruccin LES BX,ES:[BX+12h] tiene como resultado alterar el valor de ES:BX para que ahora apunte
a la zona de parmetros. En ella, aparece todo lo que haba despus del = o el que segua al DEVICE.
Por ejemplo, para una lnea de config.sys como la siguiente:
DEVICE \DOS\VDISK.SYS 128
el contenido de la zona de parmetros sera \DOS\VDISK.SYS 128 -sin incluir las comillas,
lgicamente-. Como se puede observar, el nombre y ruta del programa estn separados de sus parmetros por
uno o ms delimitadores (espacios en blanco o tabuladores -ASCII 9-); al final se encuentra el cdigo de
retorno de carro -ASCII 13- aunque quiz en algunas versiones del DOS podra estar indicado el final de la
cadena por un salto de lnea -ASCII 10- en lugar del retorno de carro. Aviso: tras el nombre/ruta del fichero,
las versiones ms antiguas del DOS colocan un byte a cero. No se debe modificar la lnea de parmetros:
adems de improcedente puede ser peligroso, al tratarse de un rea de datos del sistema. En los dispositivos
de bloque, el mismo campo donde se obtiene la direccin de los parmetros ha de ser empleado para devolver
al DOS la direccin de los punteros a los BPB: el sentido comn indica que primero debe leerse la direccin
de los parmetros y despus puede modificarse dicho campo.
11.4.1. - Orden 1 o MEDIA CHECK.
Esta orden slo es preciso implementarla en los dispositivos de bloques, sirve para que el sistema
pregunte al controlador si se ha producido un cambio en el soporte: por ejemplo, si se ha cambiado el
disquete de la disquetera. En general, los discos fijos y virtuales suelen responder que no, ya que es seguro
que nadie puede haberlos cambiado; en los disquetes suele responderse que s (ante la duda). En caso de que
el 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 informacin de sus buffers internos evitando en lo posible un acceso
al disco.
CABECERA DE PETICIN 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 equivale
al 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 de
soporte). El DOS necesita entonces averiguar las caractersticas del nuevo soporte, para lo que pide al driver
que le suministre un BPB con informacin. De nuevo, esta orden solo ha de implementarse en los dispositivos
de bloques. Desde el DOS 3.0 se recomienda anotar la etiqueta de volumen del disco cuando se ejecuta esta
orden para detectar un posible cambio ilegal del mismo, aunque lo cierto es que este mtodo es bastante
ineficiente (discos sin etiquetar, con la misma etiqueta...); desde el DOS 4.0 se mejora este asunto con los
nmeros de serie, pero pocos drivers se molestan en comprobarlos. Las versiones ms 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 identificacin comunes para disquetes diferentes, no requieren que el byte
descriptor cambie para aceptar el cambio y confan en la informacin que suministra MEDIA CHECK.
208 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
En los discos de tipo IBM, los ms comunes, el DOS intenta cooperar con el controlador de
dispositivo en los cambios de disco. Por ello, se las apaa para leer el primer sector de la FAT y se lo pasa
al driver, que as tiene ms fcil la tarea de detectar el tipo de disco y suministrar al DOS el BPB adecuado,
ya que el primer byte de la FAT contiene el tipo de disco (byte descriptor de medio). En los discos que no
son de tipo IBM es el driver quien, por sus propios medios, ha de aparselas para detectar el tipo de disco
introducido en la unidad correspondiente: por ejemplo, leyendo el sector de arranque. En algunos casos puede
resultar til indicar que el disco es de tipo no IBM; por ejemplo en un controlador para un soporte fsico que
necesite detectar el medio introducido para poder acceder al mismo. Por ejemplo en una disquetera: al
introducir un nuevo disco de densidad diferente al anterior, el intento por parte del DOS de leer la FAT en
los discos tipo IBM provocara un fallo (si esto no sucede con el controlador del propio sistema para las
disqueteras es porque la BIOS suplanta al DOS, realizando quiz algunas tareas ms de las que debera tener
estrictamente encomendadas al detectar un cambio de disco).
CABECERA DE PETICIN DE SOLICITUD PARA LA ORDEN 2 (BUILD BPB)
offset 0 13 BYTES: Ya vistos con anterioridad.
offset 13 BYTE: A la entrada, el DOS indica el descriptor del soporte.
(solo en dispositivos de bloque)
offset 14 DWORD: A la entrada, el DOS apunta a un buffer que contiene el
primer sector de la FAT (cuyo 1 byte es el descriptor de
soporte) si el disco es de tipo IBM; de lo contrario el
buffer est vaco y puede emplearse para otro propsito.
offset 18 DWORD: A la vuelta, el driver devuelve aqu la direccin del BPB
del nuevo disco (no la de ninguna tabla de punteros).
11.4.3. - Orden 3 o IOCTL INPUT.
Puede ser soportada tanto por los dispositivos de caracteres como por los de bloque, el sistema solo
la utiliza si as se le indic en la palabra de atributos del dispositivo (bit 14). El IOCTL es un mecanismo
genrico de comunicacin de las aplicaciones con el controlador de dispositivo; por medio de esta funcin,
los programas de usuario solicitan informacin al controlador (subfunciones 2 y 4 de la funcin 44h del DOS)
sin tener que emplear el canal normal por el que se envan los datos. Es frecuente que no est soportada en
los dispositivos ms simples. La cabecera de peticin de solicitud de esta orden y de varias de las que
veremos a continuacin es la siguiente:
CABECERA DE PETICIN 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, direccin del rea de transferencia a memoria
offset 18 WORD: En entrada, nmero de sectores (dispositivos de bloques)
o bytes (dispositivos de caracteres) a transferir.
A la salida, sectores/bytes realmente transferidos.
offset 20 WORD: Nmero de sector de comienzo (solo en los dispositivos de
bloques y de menos de 32 Mb)
offset 22 DWORD: En las rdenes 4 y 8 y desde el DOS 3.0 se devuelve al
DOS un puntero a la etiqueta de volumen del disco en el
caso de un error 0Fh.
offset 26 DWORD: Nmero de sector de comienzo en discos de ms de 32Mb
(ver bit 1 de palabra de atributos). En cualquier caso,
solo debe considerarse este campo si la longitud de la
cabecera de peticin (byte 0) es mayor de 1Ah.
11.4.4. - Orden 4 o INPUT.
Esta orden es una de las ms importantes. Sirve para que el sistema lea los datos almacenados en el
dispositivo. Si el dispositivo es de caracteres, los almacenar en un buffer de entrada a medida que le van
llegando del perifrico y los enviar en respuesta a esta orden (si no los tiene, espera un tiempo razonable
a que le lleguen antes de "fallar"). Si el dispositivo es de bloque, no se envan bytes sino sectores completos.
209 CONTROLADORES DE DISPOSITIVOS
En los dispositivos de caracteres, lo ms normal es que el DOS solicite transferir slo 1 en cada vez, aunque
en teora podra solicitar cualquier cantidad. En el caso de los dispositivos de bloque esta orden es ejecutada
por el DOS cuando se accede a disco va INT 25h/26h.
11.4.5. - Orden 5 o NONDESTRUCTIVE INPUT.
Solo debe ser soportada por los dispositivos de caracteres. Es anloga a INPUT, con la diferencia de
que no se avanza el puntero interno al buffer de entrada de datos tras leer el carcter. Por ello, tras utilizar
esta orden ser preciso emplear despus la 4 para leer realmente el carcter. La principal utilidad de esto es
que el sistema puede saber si el dispositivo tiene ya un nuevo carcter disponible antes de llamarle, para
evitar que ste se quede parado hasta que le llegue. El bit 9 de la palabra de estado devuelta indica, si est
activo, que el dispositivo est ocupado (sin caracteres).
11.4.6. - Orden 6 o INPUT STATUS.
Es totalmente anloga a NONDESTRUCTIVE INPUT, con la salvedad de que ni siquiera se enva
el siguiente carcter del buffer de entrada. Slo sirve para determinar el estado del controlador, indagando
si tiene caracteres disponibles o no.
11.4.7. - Orden 7 o INPUT FLUSH.
Solo disponible en dispositivos de caracteres, vaca el buffer del dispositivo. Lo que ste suele hacer
es sencillamente igualar los punteros al buffer de entrada interno (el puntero al ltimo dato recibido del
perifrico y el puntero al prximo carcter a enviar al sistema cuando se lo pida).
11.4.8. - Orden 8 u OUTPUT.
Es otra de las rdenes ms importantes, anloga a INPUT pero actuando al revs. Permite al sistema
enviar datos al dispositivo, bien sean caracteres o sectores completos, segn el tipo de dispositivo.
11.4.9. - Orden 9 u OUTPUT VERIFY.
Es anloga a OUTPUT, con la salvedad de que el dispositivo efecta, tras escribir, una lectura
inmediata hacia un buffer auxiliar, con la correspondiente comprobacin de que lo escrito es correcto al
comparar ambos buffers. Resulta totalmente absurdo implementarla en un disco virtual (el 11% de la memoria
del sistema podra estar ya destinada a detectar un fallo en cualquier byte de la misma, y adems es igual de
probable el error durante la escritura que durante la verificacin) por lo que en este caso debe comportarse
igual que la orden anterior. En los discos fsicos de verdad, sin embargo, conviene tomarla en serio.
11.4.10. - Orden 0Ah u OUTPUT STATUS.
Es similar a INPUT STATUS y, como sta, propia de los dispositivos de caracteres. Su misin es
anloga, pero relacionada con el buffer de salida en vez del buffer de entrada.
11.4.11. - Orden 0Bh u OUTPUT FLUSH.
Tambin exclusiva de dispositivos de caracteres, es equivalente a INPUT FLUSH, vacindose el
buffer 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 informacin a travs
de la funcin 44h del DOS (subfunciones 3 y 5). Es til para lograr una comunicacin de ciertas
informaciones con el controlador a travs de otro canal, sin tener que mezclarla con los datos que se le
210 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
envan. Algunos programas residentes, instalados como falsos controladores de dispositivo de caracteres
soportan ciertos comandos va IOCTL, evitando a las aplicaciones acceder directamente a la zona de memoria
donde 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 almacenado
en l ha sido abierto. El controlador se limita a incrementar un contador. Esta orden y las dos siguientes no
han de estar necesariamente soportadas.
11.4.14. - Orden 0Eh o DEVICE CLOSE.
Solo implementada desde el DOS 3.0 y superior, indica que el dispositivo o un fichero almacenado
en l ha sido cerrado. El controlador se limita a decrementar un contador: si ste llega a cero, se reinicializan
los buffers internos, si los hay, para permitir por ejemplo un posible cambio de disco.
11.4.15. - Orden 0Fh o REMOVABLE MEDIA.
Solo implementada tambin desde el DOS 3.0 y superior, indica al sistema si el dispositivo es
removible o no, apoyndose en los resultados de las dos rdenes anteriores.
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 ms de un
carcter al perifrico. En concreto, se envan todos los que sean posibles (de la cantidad solicitada) hasta que
el perifrico est ocupado: entonces se retorna. Aqu no se considera un error no haber podido transferir todo.
Esta funcin es til para acelerar el proceso de salida.
11.4.17. - Otras rdenes.
Las rdenes 11h, 12h, 14h, 15h y 16h no han sido an definidas, ni siquiera en el DOS 5.0. La
orden 13h o GENERIC IOCTL, disponible desde el DOS 3.2 permite un mecanismo ms sofisticado de
comunicacin IOCTL. Tambin en el DOS 3.2 han sido definidas las rdenes 17h (GET LOGICAL
DEVICE) y 18h (SET LOGICAL DEVICE). El DOS 5.0 aade una nueva: la 19h (CHECK GENERIC
IOCTL SUPPORT). Por cierto, las ordenes 80h y superiores estn destinadas a la comunicacin con los
dispositivos CD-ROM...
11.5. - LA CADENA DE CONTROLADORES DE DISPOSITIVO INSTALADOS.
Los controladores de dispositivo forman una cadena en la memoria, una lista conectada por los 4
primeros 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 operativo
apunta el dispositivo NUL al ltimo dispositivo instalado, colocndose NUL al final de la cadena. Por tanto,
averiguando la direccin del dispositivo NUL y siguiendo la cadena de apuntadores obtenida en los primeros
4 bytes de cada uno (en la forma segmento:offset) se puede recorrer la lista de dispositivos (ya sean de
caracteres o de bloque) en orden inverso al que fueron instalados en memoria. El ltimo de ellos estar
apuntando a XXXX:FFFF. La lista de controladores de dispositivo puede pasar por la memoria convencional
o por la superior, saltando de una a la otra mltiples veces. Algunos gestores de memoria, como QEMM
cuando se utiliza LOADHI.SYS (en lugar del DEVICEHIGH del DOS) colocan la cadena de dispositivos en
memoria convencional, aunque luego instalen el mismo en memoria superior. Esto quiere decir que para
acceder al cdigo o datos internos del dispositivo conviene tomar precauciones, de cara a averiguar la
direccin donde realmente reside. El programa TURBODSK que veremos ms adelante utiliza la cadena de
controladores de dispositivo para buscarse a s mismo en memoria e identificar todas las posibles unidades
211 CONTROLADORES DE DISPOSITIVOS
que controla. Por desgracia, la manera de obtener la direccin del dispositivo NUL vara de unas versiones
del DOS a otras, aunque solo ligeramente. Hay que utilizar la funcin indocumentada Get List of Lists
(servicio 52h del DOS) e interpretar la informacin que devuelve: En ES:BX ms un cierto offset comienza
la cabecera del dispositivo NUL (el propio dispositivo, no un puntero al mismo). Ese offset es 17h para las
versiones 2.X del DOS, 28h para la 3.0X y 22h para todas las dems, habidas y por haber. La utilidad
DRV.C listada ms abajo recorre los dispositivos instalados, informando de ellos. Adicionalmente, excepto
en las versiones ms antiguas del DOS, DRV.C accede a los bloques de control de memoria que preceden
a los dispositivos que estn ubicados en un offset 0 respecto al segmento, con objeto de indicar el consumo
de memoria de los mismos y el nombre del fichero ejecutable. Con DR-DOS 5.0 no se informa correctamente
del nombre, ni tampoco del tamao (excepto si el dispositivo est instalado en memoria superior); no hay
problemas sin embargo con DR-DOS 6.0 ni, por supuesto, con MS-DOS 4.0 posterior. A continuacin,
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 CiriSOFT
Direccin Tipo Nombre Estrat. Interr. Atributo Programa Tamao
0116:0048 Carcter NUL 0DC6 0DCC 8004
E279:0000 Bloque Unidad I: 00CB 00D6 0800 RAMDRIVE 1184
E22B:0000 Bloque Unidad H: 00CB 00D6 0800 RAMDRIVE 1232
E1A7:0000 Bloque Unidad G: 0086 0091 0800 VDISK 2096
E103:0000 Bloque Unidad F: 0086 0091 0800 VDISK 2608
E0E6:0000 Bloque Unidad E: 005A 0065 0800 TDSK 448
E0BE:0000 Bloque Unidad D: 005A 0065 0800 TDSK 624
E013:0000 Carcter CON 0078 0083 8013 ZANSI 2720
E003:0000 Carcter ALTDUP$ 00C2 00CD 8000 ALTDUP 240
DFD8:0000 Carcter KEYBSP50 0012 0018 8000 KEYBSP 672
DD90:0000 Carcter gmouse 0012 0021 8000 GMOUSE 9328
DD85:0000 Carcter ACCESOS$ 0013 001A 8000 ACCESOS 160
DD7C:0000 Carcter &FDREAD2 0012 0012 8000 FDREAD 128
0316:0000 Carcter KEYBUF21 0012 0018 8000 KEYBUFF 160
D803:0000 Carcter SMARTAAR 00A2 00AD C800 SMARTDRV 22400
0255:003F Carcter QEMM386$ 0051 007D C000
0255:0000 Carcter EMMXXXX0 0051 0064 C000 QEMM386 3072
0070:0023 Carcter CON 06F5 0700 8013
0070:0035 Carcter AUX 06F5 0721 8000
0070:0047 Carcter PRN 06F5 0705 A0C0
0070:0059 Carcter CLOCK$ 06F5 0739 8008
0070:006B Bloque Unidades A:-C: 06F5 073E 08C2
0070:007B Carcter COM1 06F5 0721 8000
0070:008D Carcter LPT1 06F5 070C A0C0
0070:009F Carcter LPT2 06F5 0713 A0C0
0070:00B8 Carcter LPT3 06F5 071A A0C0
0070:00CA Carcter COM2 06F5 0727 8000
0070:00DC Carcter COM3 06F5 072D 8000
0070:00EE Carcter 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 versin del DOS */
dosver=(r.r_ax << 8) | (r.r_ax >> 8);
if ((dosver & 0xFF00)==0x200) i=0x17; /* DOS 2.XX */
else if ((dosver>0x2FF) && (dosver<0x30A)) i=0x28; /* DOS 3.0X */
else i=0x22; /* otra versin */
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(" Direccin Tipo Nombre Estrat. Interr.
Atributo Programa Tamao \n");
printf("
");
while (FP_OFF(siguiente)!=0xffff) {
disp = (unsigned char huge *) siguiente;
printf(" \n %04X:%04X ", FP_SEG(disp), FP_OFF(disp));
if (disp[5] & 0x80) {
printf("Carcter ");
for (i=10; i<18; i++) printf("%c",disp[i]); printf(" ");
}
else {
printf("Bloque ");
if (disp[10]==1)
printf("Unidad %c: ", disco--);
else {
printf("Unidades %c:-%c:",disco-disp[10]+1, disco);
disco-=disp[10];
}
}
printf(" %04X %04X %04X ", disp[6] | (disp[7]<<8),
disp[8] | (disp[9]<<8), disp[4] | (disp[5]<<8));
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);
}
else
printf(" ");
siguiente = (unsigned long huge *) *siguiente;
}
printf(" \n "); for (i=1; i<78; i++) printf(" "); printf(" \n");
}
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 en
hexadecimal todo lo que recibe. Por supuesto, el programa se instala en el CONFIG.SYS con una orden del
tipo DEVICE=HEX.SYS. En principio, sera un programa mucho ms simple si se limitara a imprimir los
caracteres que recibe, aunque ello no tendra utilidad alguna. De hecho, la mayor parte de la complejidad del
listado no se debe al controlador de dispositivo, sino al resto. Para empezar, las rdenes Open, Close o
Remove, en un hipottico dispositivo que simplemente sacara por pantalla lo que recibe estn de ms.
Adems, la rutina que procesa los caracteres (procesa_AL) se limitara a imprimirles; tambin se eliminaran
todas las dems subrutinas de apoyo. Sin embargo, el hecho de realizar un volcado hexadecimal complica
bastante 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 fiche
00000010 72 6F 20 64 65 20 70 72 - 75 65 62 61 73 2E 20 53 ro de pruebas. S
00000020 A2 6C 6F 20 73 69 72 76 - 65 20 70 61 72 61 20 70 lo sirve para p
00000030 72 6F 62 61 72 2E 0A 0D robar...
Es preciso implementar la orden Open para detectar el inicio de la transferencia, inicializando a cero
el contador de offset relativo de la izquierda. Los caracteres se imprimen unos tras otros en hexadecimal (con
un guin separador tras el octavo) y se van almacenando en un buffer hasta completar 16: entonces, se
imprimen de nuevo pero en ASCII (sustituyendo por puntos los cdigos de control). La orden Close sirve
para detectar el final de la operacin: ante ella se escriben los espacios necesarios y se vuelcan los cdigos
ASCII acumulados hasta el momento (entre 0 y 15) que restasen por ser imprimidos. Por emplear Open y
Close 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 suele
encadenarles en uno solo y el offset es relativo al primero enviado (esto depende de la versin del intrprete
de comandos). Aunque se supone que el DOS va a enviar los caracteres de uno en uno, el dispositivo se toma
la molestia de prever que esto pueda no ser as, procesando en un bucle todos los que se le indiquen. Para
imprimir se utiliza la INT 29h del DOS (fast console OUTPUT), ms recomendable que llamar a un servicio
del sistema operativo (que a fin de cuentas va a parar a esta interrupcin). No hay que olvidar que los
controladores de dispositivo son tambin programas residentes a todos los efectos, con las mismas
limitaciones. Sin embargo, desde los programas normales no es recomendable utilizar la INT 29h, entre otras
razones porque esos programas, adems de imprimir a poca velocidad, no soportaran redireccionamiento en
la salida (la INT 29h no es precisamente rpida, aunque s algo ms que llamar al DOS).
El dispositivo HEX$ slo acta en salida, imprimiendo en pantalla lo que recibe. Si se intenta leer
desde l devuelve una condicin de error (por ejemplo, al realizar COPY HEX$ FICH.TXT). Para visualizar
ficheros binarios que puedan contener la marca de fin de fichero (^Z) no basta hacer TYPE o COPY a secas:
en estos casos se debe emplear COPY /B FICHERO.EXT HEX$, la opcin /B sirve para que la salida no se
detenga ante el ^Z. La operacin de impresin en pantalla se supone siempre exitosa; por ello el dispositivo
no modifica la variable que indica el nmero de caracteres a procesar: al devolverla precisamente como estaba
al principio indica que se han procesado sin problemas todos los solicitados. En la instalacin se comprueba
la versin del DOS, para cerciorarse de la presencia de un 3.0 o superior. Este driver de ejemplo slo
consume 464 bytes de memoria bajo MS-DOS 5.0. Tras ensamblarlo y linkarlo hay que aplicar EXE2BIN
para pasarlo de EXE a SYS (TLINK /t slo opera cuando hay un ORG 100h).
Como se puede verificar observando el listado, las nicas rdenes realmente soportadas por el
dispositivo son, aparte de OPEN, CLOSE y REMOVE, las rdenes WRITE y WRITE VERIFY. Todas las
dems, en este controlador que no depende del hardware tpico de entrada/salida, son innecesarias. Como el
proceso de escritura en pantalla se supone siempre con xito, WRITE VERIFY es idntica a WRITE, sin
realizar verificacin alguna. Las rdenes no soportadas pueden ser ignoradas o bien desembocar en un error,
segn sea el caso.
213 CONTROLADORES DE DISPOSITIVOS
; ********************************************************************
; * *
; * HEX$ 1.0 - (c) 1992 Ciriaco Garca de Celis. *
; * *
; * Controlador de dispositivo para volcado hexadecimal en salida. *
; * *
; ********************************************************************
; ------------ Macros de propsito general
XPUSH MACRO RM ; apilar lista de registros
IRP reg, <RM>
PUSH reg
ENDM
ENDM
XPOP MACRO RM ; desapilar lista de registros
IRP reg, <RM>
POP reg
ENDM
ENDM
; ************ Inicio del rea residente.
HEXSEG SEGMENT
ASSUME CS:HEXSEG, DS:HEXSEG
DD -1 ; encadenamiento con otros drivers
tipo_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 estrategia
DW interrupcin ; rutina de interrupcin
DB "HEX$ " ; nombre del dispositivo
; ------------ Variables y tablas de datos globales fijas.
pcab_peticion LABEL DWORD ; puntero a la cabecera de peticin
pcab_pet_desp DW 0
pcab_pet_segm DW 0
p_rutinas LABEL WORD ; tabla de rutinas del controlador
DW init
DW media_check
DW build_bpb
DW ioctl_input
DW read
DW read_nowait
DW input_status
DW input_flush
DW write
DW write_verify
DW output_status
DW output_flush
DW ioctl_output
DW open
DW close
DW 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 carcter del
dirh DW 0 ; fichero o canal en proceso
; ------------ Rutina de estrategia.
estrategia PROC FAR
MOV CS:pcab_pet_desp,BX
MOV CS:pcab_pet_segm,ES
RET
estrategia ENDP
; ------------ Rutina de interrupcin.
interrupcin PROC FAR
XPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>
LDS BX,CS:pcab_peticion
MOV AL,[BX+2] ; AL = orden
CBW ; AX = orden (AH = 0)
CMP AL,0Fh
JBE orden_ok ; orden correcta
MOV AX,8102h
JMP exit_interr
orden_ok: SHL AX,1 ; orden = orden * 2
LEA SI,p_rutinas
ADD SI,AX
XPUSH <BX,DS>
CALL CS:[SI] ; ejecutar orden
XPOP <DS,BX>
exit_interr: MOV [BX+3],AX ; devolver palabra de estado
XPOP <ES,DS,BP,DI,SI,DX,CX,BX,AX>
RET
interrupcin ENDP
; ------------ Las rutinas que controlan el dispositivo devuelven AX
; con la palabra de estado. Pueden cambiar todos los
; registros (de 16 bits), includos los de segmento.
input_status: ; conjunto de rdenes con
output_status: ; tratamiento idntico
input_flush:
output_flush:
ioctl_output:
retorno_ok: MOV AX,100h ; no hay error, ignorar orden
RET
media_check:
build_bpb:
read: ; slo soportada la salida
read_nowait:
ioctl_input: MOV AX,8103h ; rdenes no soportadas
RET
open PROC ; inicio de transferencia:
MOV CS:puntero,OFFSET ini_buffer ; inicializa puntero
MOV CS:dirl,0
MOV CS:dirh,0 ; offset relativo a cero
JMP retorno_ok
open ENDP
close PROC ; fin de transferencia:
MOV AX,CS
MOV DS,AX
LEA CX,fin_buffer
MOV BX,puntero
SUB CX,BX ; CX caracteres faltan
MOV AX,CX ; para un prrafo
ADD CX,CX
ADD CX,AX ; CX = CX * 3
CMP BX,OFFSET med_buffer
JA tam_ok
ADD CX,2 ; dos espacios de separacin
tam_ok: MOV AL,
JCXZ esp_escr
escr_esp: CALL print_AL
LOOP escr_esp
esp_escr: MOV BX,puntero
limpia_buffer: CMP BX,OFFSET fin_buffer
JAE fin_buff
MOV BYTE PTR [BX],
INC BX
JMP limpia_buffer
fin_buff: LEA BX,ini_buffer ; acabado el buffer:
MOV puntero,BX
CALL imprimir_asc ; imprimirlo en ASCII
JMP retorno_ok
close ENDP
remove PROC
MOV AX,300h ; indicar
RET ; controlador ocupado
remove ENDP
write PROC
write_verify: MOV CX,[BX+12h] ; bytes a transferir
LES DI,[BX+0Eh] ; direccin inicial
MOV AX,CS
MOV DS,AX ; DS: -> HEX$
otro_car: MOV AL,ES:[DI]
XPUSH <CX, DI>
CALL procesa_AL ; procesar carcter
XPOP <DI, CX>
INC DI ; otro carcter
LOOP otro_car
JMP retorno_ok ; siempre Ok.
write ENDP
procesa_AL PROC ; permitido corromper registros
MOV BX,puntero
MOV [BX],AL ; guardar carcter
INC puntero
CMP BX,OFFSET ini_buffer
JNE no_direcc ; no es inicio de prrafo
CALL imprimir_desp ; imprimir desplazamiento
no_direcc: CMP BX,OFFSET med_buffer
JNE no_sep ; an no alzanzada la mitad
CALL imprimir_sep
no_sep: ADD dirl,1 ; INC no afecta al acarreo
ADC dirh,0 ; incrementada direccin
CALL print_8hex ; imprimir byte en hexadecimal
MOV AL,
CALL print_AL ; espacio separador
CMP puntero,OFFSET fin_buffer
JB AL_procesado
LEA BX,ini_buffer ; acabado el buffer:
MOV puntero,BX
CALL imprimir_asc ; imprimirlo en ASCII
AL_procesado: RET
procesa_AL ENDP
imprimir_desp PROC ; imprimir desplazamiento
PUSH AX
MOV AL,
CALL print_AL
CALL print_AL ; dos espacios al principio
MOV AX,dirh
XCHG AH,AL
CALL print_8hex ; byte alto palabra alta
XCHG AH,AL
CALL print_8hex ; byte bajo palabra alta
MOV AX,dirl
XCHG AH,AL
CALL print_8hex ; byte alto palabra baja
XCHG AH,AL
CALL print_8hex ; byte bajo palabra baja
MOV AL,
CALL print_AL
CALL print_AL ; dos espacios separadores
POP AX
RET
imprimir_desp ENDP
imprimir_sep PROC ; imprimir guin separador
PUSH AX
MOV AL,-
CALL print_AL
MOV AL,
CALL print_AL
POP AX
RET
imprimir_sep ENDP
imprimir_asc PROC ; imprimir en ASCII 16 bytes
MOV AL, ; a partir de DS:BX
CALL print_AL ; espacio separador
MOV CX,16
asc_dump: MOV AL,[BX]
CMP AL,
JAE asc_ok
MOV AL,. ; no imprimir los de control
asc_ok: CALL print_AL
INC BX
LOOP asc_dump
MOV AL,0Dh
CALL print_AL ; retorno de carro
MOV AL,0Ah
CALL print_AL ; salto de lnea
RET
imprimir_asc ENDP
print_8hex PROC ; imprimir byte hexad. en AL
PUSH AX
MOV AH,AL
MOV CL,4
SHR AL,CL
CALL print_4hex
214 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV AL,AH
AND AL,00001111b
CALL print_4hex
POP AX
RET
print_8hex ENDP
print_4hex PROC ; imprimir nibble hexad. en AL
PUSH AX
ADD AL,0
CMP AL,9
JBE hex_AL
ADD AL,A-9-1
hex_AL: CALL print_AL
POP AX
RET
print_4hex ENDP
print_AL PROC ; imprimir ASCII en AL
INT 29h
RET
print_AL ENDP
; ************ Instalacin invocada desde el CONFIG.SYS
init PROC
PUSH BX
MOV AH,30h
INT 21h ; obtener versin del DOS
POP BX
CMP AL,3
JAE dos_ok
MOV WORD PTR [BX+0Eh],0 ; OFFSET 0: terminar
MOV [BX+10h],CS ; sin quedar residente
LEA BX,mal_dos_txt
CALL print
MOV AX,100h
RET
dos_ok: LEA AX,retorno_ok
MOV CS:p_rutinas,AX ; anular rutina INIT
MOV WORD PTR [BX+0Eh],OFFSET init
MOV [BX+10h],CS ; indicado rea residente
LEA BX,instalado_txt
CALL print
MOV AX,100h ; instalacin siempre Ok.
RET
init ENDP
print PROC ; imprimir cadena en CS:BX
MOV DL,CS:[BX]
AND DL,DL
JZ fin_print
MOV AH,2
PUSH BX
INT 21h
POP BX
INC BX
JMP print
fin_print: RET
print ENDP
instalado_txt DB 13,10,"Dispositivo HEX$ instalado.",13,10,0
mal_dos_txt DB 13,10,"Error: HEX$ necesita DOS 3.0 o superior."
DB 13,10,0
HEXSEG ENDS
END
11.7. - EJEMPLO DE CONTROLADOR DE DISPOSITIVO DE BLOQUES.
11.7.1. - DISCO VIRTUAL TURBODSK: CARACTERSTICAS.
El disco virtual propuesto no es el clsico minidisco de ejemplo, de un segmento de 64 Kb. Por el
contrario, 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 sido
dotado de varias comodidades adicionales no disponibles en los discos del DOS. Por un lado, es posible
modificar su tamao una vez que ha sido instalado, sin necesidad de arrancar de nuevo el ordenador. Esta
asignacin dinmica de la memoria significa que, en la prctica, es factible tener instalado el controlador sin
reservar memoria: cuando es preciso utilizar el disco, se le formatea; despus de ser usado, se puede
desasignar la memoria extendida, expandida o convencional que ocupaba. Esto ltimo es ms que
recomendable si, por ejemplo, se va a ejecutar WINDOWS a continuacin y ya no se necesita el disco virtual.
Otra ventaja es que es mucho ms flexible que los discos virtuales que acompaan al sistema
operativo, permitiendo definir con mayor libertad los parmetros e incluyendo uno nuevo (el tamao de
cluster). Los usuarios avanzados nunca estuvieron contentos con los discos del sistema que abusaban
demasiado del ajuste de parmetros. Aunque una eleccin torpe de parmetros de TURBODSK puede crear
un disco prcticamente intil, e incluso incompatible con algunas versiones del DOS, tambin es cierto que
los usuarios con menos conocimientos pueden dejar a ste que elija los parmetros por ellos, con excepcin
del tamao del disco. Los usuarios ms informados, en cambio, no tendrn ahora trabas.
Sin embargo, la pretensin inicial de hacer TURBODSK ms rpido que los discos del sistema, de
la que hereda su peculiar nombre, ha tenido que enfrentarse a la elevada eficiencia de RAMDRIVE. Las
ltimas versiones de este disco ya apuran bastante el rendimiento del sistema, por lo que superarle slo ha
sido posible con un truco en la memoria expandida/convencional y en mquinas 386DX y superiores:
TURBODSK detecta estas CPU y aprovechar su bus de 32 bits para realizar las transferencias de bloques de
memoria. La velocidad es sin duda el factor ms importante de un disco virtual, con mucho, por lo que no
se deben ahorrar esfuerzos para conseguirla.
A continuacin se resumen las caractersticas de TURBODSK, comparndolo con los discos virtuales
del sistema: RAMDRIVE en representacin del MS-DOS 5.0 (aunque se incluye una versin ms reciente
que viene con WINDOWS 3.1) y el VDISK de DR-DOS 6.0. Como puede observarse, la nica caracterstica
que TURBODSK no presenta es el soporte de memoria extendida va INT 15h de VDISK, tampoco
implementado ya en RAMDRIVE. El motivo es simplificar el programa, ya que en la actualidad es difcil
encontrar mquinas con memoria extendida que no tengan instalada la especificacin XMS que implementa
HIMEM.SYS o algunas versiones del EMM386.
215 CONTROLADORES DE DISPOSITIVOS
CARACTERSTICAS
RAMDRIVE VDISK TURBODSK
(WINDOWS 3.1) (DR-DOS 6.0) v2.3
Capacidad mxima: 32 Mb 32 Mb 64 Mb
Soporte de memoria convencional: S S S
Soporte de memoria EMS: S S S
Soporte de memoria extendida INT 15h: No S No
Soporte de memoria extendida XMS: S No S
Tamao de sector soportado: 128-1024 128-512 32-2048
Ficheros en directorio raz: 4-1024 4-512 1-65534
Asignacin dinmica de la memoria: No No S
Tamao 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 ms
abajo. Los resultados de KBSEC pueden variar espectacularmente en funcin del fabricante del controlador
de memoria o del sistema operativo. Este programa de test es til para analizar el rendimiento de un disco
virtual en fase de desarrollo o para que el usuario elija la memoria ms rpida segn la configuracin de su
equipo. Dicho programa bloquea todas las interrupciones excepto IRQ 0 (INT 8), la cual a su vez desva con
objeto de aumentar la precisin del clculo; por ello es exclusivo para la comprobacin de discos virtuales
y no flexibles. Debe ser ejecutado sin tener instalado ningn cach. KBSEC fuerza el buffer de transferencia
a una direccin de memoria determinada, con objeto de no depender aleatoriamente de la velocidad dispar
de la memoria y los controladores XMS/EMS en funcin del segmento que sea utilizado. La fiabilidad de
KBSEC est avalada por el hecho de que siempre da exactamente el mismo resultado al ser ejecutado en las
mismas condiciones. Para hacerse una idea de la potencia de los discos virtuales, conviene tener en cuenta
que un disco fijo con 19 ms de tiempo de acceso e interface IDE, en un 386-25 puede alcanzar una velocidad
de transferencia de casi un megabyte, 17 veces menos que la mejor configuracin de disco virtual -que
adems posee un tiempo de acceso prcticamente nulo- en esa misma mquina.
Velocidad del disco bajo MS-DOS 5.0, calculada por KBSEC, con los buffers que
establece el DOS por defecto (aunque esto no influye en KBSEC) y con slo KEYB y
DOSKEY instalados. Para evaluar la memoria convencional no estaba instalado ningn
controlador de memoria; para la memoria XMS estaba instalado slo HIMEM.SYS y para
la EMS, tanto HIMEM.SYS como EMM386.EXE a la vez (los resultados varan bastante
en funcin de la gestin de memoria del sistema). Datos en Kb/segundo.
VDISK RAMDRIVE TURBODSK
8088-8 MHz:
- Memoria convencional: 563 573 573
286-12 Mhz (sin estados de espera):
- Memoria extendida/XMS: 1980 4253 4253
- Memoria convencional: 4169 4368 4368
386-25 MHz (sin cach):
- Memoria extendida/XMS: 6838 17105 17095
- Memoria expandida EMS: 1261 8308 14937
- Memoria convencional: 7297 6525 14843
486-25 MHz sin cach externa:
- Memoria extendida/XMS: 7370 10278 10278
- Memoria expandida EMS: 2533 7484 9631
- Memoria convencional: 8256 8454 11664
/*********************************************************************
* *
* KBSEC 1.2 - Utility to calc with high precision the data transfer *
* rate (the read data transfer read) in a ramdisk. *
* *
* (C) 1992-1995 Ciriaco Garca de Celis *
* *
* - Do not run this program with a cache program loaded; compile *
* it in LARGE memory model with Test stack overflow option *
* disabled. Use Borland C. This program has english messages. *
* *
*********************************************************************/
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#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. peridica */
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)
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. peridica */
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 sin
demasiados parmetros especiales. Este programa puede ser perfectamente ensamblado de manera indistinta
por MASM 6.X (con el parmetro de compatibilidad con versiones anteriores) o por TASM, aunque
preferiblemente por el segundo. Versiones de MASM anteriores a la citada no tienen potencia suficiente,
bsicamente porque no permiten emplear la directiva .386 dentro de los segmentos. Con TASM conviene
emplear la opcin /m5 para que el ensamblador ejecute todas las pasadas necesarias para optimizar el cdigo
al mximo (como mnimo habra que solicitar 2, en cualquier caso, para que no emita errores).
11.7.3. - ANLISIS DETALLADO DEL LISTADO DE TURBODSK.
El listado completo de TURBODSK puede consultarse al final de este apartado. Se describirn paso
a paso todas las peculiaridades del programa, por lo que el listado debera ser comprensible prcticamente
al 100%. A lo largo de la explicacin aparecen numerosas alusiones al comportamiento de RAMDRIVE y
VDISK. Por supuesto, los detalles referidos a RAMDRIVE o VDISK se refieren exclusivamente a la versin
de los mismos que acompaa a Windows 3.1 y a DR-DOS 6.0, respectivamente, no siendo necesariamente
aplicable a otras anteriores o futuras de dichos programas. Evidentemente, la informacin sobre ambos no
ha sido obtenida escribiendo al fabricante para solicitarle el listado fuente, por lo que es un tanto difusa e
incompleta, aunque s suficiente para complementar la explicacin de TURBODSK y dar una perspectiva ms
amplia.
LA CABECERA DE TURBODSK
El inicio de TURBODSK es el clsico de todos los controladores de dispositivo de bloques. La
palabra de atributos es idntica a la de VDISK o RAMDRIVE. Hay que hacer aqu una breve mencin al bit
13 que indica si el dispositivo es de tipo IBM o no: la verdad es que en nuestro caso dara igual elegir un
tipo que otro (la diferencia es que en los de tipo IBM el DOS accede a la FAT antes que al propio sector de
arranque para verificar el tipo de disco). 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 cmo est
este bit de la palabra de atributos, tan slo existe una sutil diferencia en la orden BUILD BPB.
A continuacin vienen las variables de TURBODSK, la mayora de las cuales son intuitivas. Sin
embargo, las dos primeras son algo especiales. La primera (cs_tdsk) est destinada a almacenar el valor del
registro CS, que indica dnde reside el disco virtual. Aunque en principio puede parecer redundante, esta
operacin es necesaria para lograr la compatibilidad con algunos gestores de memoria, como QEMM, que
pueden cargar la cabecera del dispositivo en memoria convencional y el resto del mismo en la superior: a
nosotros nos interesa conocer la direccin donde reside todo el dispositivo, con objeto de acceder a l para
ulteriores modificaciones de sus condiciones de operacin. Cuando se utiliza el LOADHI de QEMM, el
dispositivo es cargado en memoria superior, pero despus QEMM se encarga de copiar la cabecera en
memoria convencional, pasando la cadena de controladores de dispositivo del DOS por dicha memoria. Como
nosotros buscaremos a un posible TURBODSK residente siguiendo esa cadena, gracias a la variable cs_tdsk
217 CONTROLADORES DE DISPOSITIVOS
podemos saber la direccin real del disco virtual. QEMM crea adems unas falsas rutinas de estrategia e
interrupcin en memoria convencional que luego llaman a las de la memoria superior. Sin embargo, esto no
es relevante para nosotros. Por fortuna, QEMM 6.0 tambin soporta el DEVICEHIGH del DOS, en cuyo caso
la totalidad del dispositivo es cargado en memoria superior; sin embargo, no est de ms tomar precauciones
para los casos en que no sea as.
La segunda variable es id_tdsk y su utilidad es fundamental: sirve para certificar que el controlador
de dispositivo es TURBODSK, indicando adems la versin. Esta variable est ubicada en los primeros 18
bytes de la cabecera, que son los que QEMM copia en memoria convencional. Si algn gestor de memoria
extrao realizara la misma maniobra de QEMM y copiase menos de 18 bytes en memoria convencional, no
pasara nada: TURBODSK sera incapaz de hallarse a s mismo residente en la memoria superior, por lo que
no habra riesgo alguno de provocar un desastre. Por fortuna, estas complicadas argucias de los controladores
de memoria tienden a desaparecer desde la aparicin del DOS 5.0 que, de alguna manera, ha normalizado
el uso de la memoria superior.
Existe otra variable importante, tipo_soporte, que indica en todo momento el estado del disco. En
general, las variables ms importantes de TURBODSK han sido agrupadas al principio y el autor del
programa se ha comprometido a no moverlas en futuras versiones. Esto significa que otros programas podrn
detectar la presencia de TURBODSK e influir en sus condiciones de operacin.
Ms adelante hay otras variables internas al programa: por un lado, la tabla de saltos para las rutinas
que controlan el dispositivo; por otro, un BPB con informacin vlida (si no fuera correcto, el DOS se podra
estrellar al cargar el dispositivo desde el CONFIG). Este BPB ser modificado cuando se defina el disco, se
defina ste desde el CONFIG o no (esto ltimo es lo ms normal y recomendable). En el BPB solo se han
completado los campos correspondientes al DOS 2.x; la razn es que los dems no son necesarios ni siquiera
para el DOS 5.0: la informacin adicional de las ltimas versiones de los BPB es empleada por las rutinas
de ms 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 de
TURBODSK no merece ningn comentario, pero s la de interrupcin. Es bastante parecida a la de los discos
del sistema, pero con una diferencia: si el disco no est an preparado y no se ha reservado memoria para
l (esto sucede con la variable tipo_soporte igual a cero) hay que rechazar todos los accesos al disco
devolviendo un cdigo de unidad no preparada, algo as como decir que no hay disquete dentro de la
disquetera virtual. En cualquier otro caso, y valindose de la tabla de saltos, llamamos a la subrutina
adecuada que gestiona cada orden. Estas subrutinas devuelven en AX la palabra de estado que hay que
devolver al sistema, por lo que al final se realiza esta operacin. En el caso de un error de transferencia
(debido al fallo de algn controlador de memoria o a un intento de acceso fuera de los lmites del disco), se
indica al DOS que se han transferido 0 sectores; de lo contrario, esta variable de la cabecera de peticin
queda como estaba al principio, indicando que se han transferido tantos sectores como fueron solicitados.
Las rdenes READ NOWAIT, INPUT STATUS, INPUT FLUSH, OUTPUT STATUS, OUTPUT
FLUSH, IOCTL OUTPUT, OPEN y CLOSE no estn realmente soportadas. Sin embargo, si el DOS las
invoca, TURBODSK se limita a terminar como si nada hubiera sucedido, devolviendo una palabra de estado
100h que indica funcin terminada. A la orden IOCTL INPUT, en cambio, se responde con un error (orden
no soportada) ya que TURBODSK no est preparado para enviar cadenas IOCTL a nadie (una cosa es no
hacer caso de las que envan, pero cuando adems las solicitan!); en general, el comportamiento hasta el
momento es 100% idntico al de RAMDRIVE.
Sin embargo, la orden MEDIA CHECK es totalmente diferente de la de los discos virtuales del DOS.
A la pregunta de ha habido cambio de disco?, tanto VDISK como RAMDRIVE responden siempre que no.
En cambio, TURBODSK puede haber sido modificado por el usuario, debido a la asignacin dinmica de
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 TURBODSK
cuando el usuario define un disco) colocar la variable cambiado a un valor 0FFh. Este valor es el que se
devolver la primera vez al DOS, indicando que se ha producido un cambio de disco. Las siguientes veces,
TURBODSK no volver a cambiar (no hasta otro formateo), motivo por el cual la variable se redefine a 1.
En el momento en que el disco es cambiado, el DOS ejecuta la orden BUILD BPB, con la que se
le suministra la direccin del nuevo BPB (la misma de siempre, pero con un BPB actualizado).
La orden REMOVE se limita a devolver una condicin de controlador ocupado. No estaba muy claro
qu haba que hacer con ella, por lo que se opt por imitar el funcionamiento de RAMDRIVE. Lo cierto es
que hay rdenes que casi nunca sern empleadas, o que no tiene sentido que sean utilizadas, pero conviene
considerarlas en todo caso.
Las ltimas rdenes que implementa TURBODSK son las de lectura y escritura o escritura con
verificacin. En estas rdenes simplemente se inicializa un flag (el registro BP) que indica si se trata de leer
o escribir: si BP es 0 es una escritura, si es 1 una lectura. Finalmente, se salta a la rutina Init_io que se
encarga de preparar los registros para la lectura o escritura, consultando el encabezamiento de peticin de
solicitud para estas rdenes.
Ms o menos mezclada con estas rdenes est la rutina que gestiona la interrupcin 19h. Esta
interrupcin es necesario desviarla para mejorar la convivencia con algunos entornos multitarea basados en
el modo virtual del 386. En principio, cuando una tarea virtual es cancelada (debido a un CTRL-ALT-DEL
o a un cuelgue de la misma) el sistema operativo debera desasignar todos los recursos ligados a ella, incluida
la memoria expandida o extendida que tuviera a su disposicin. Sin embargo, parece que existen entornos
no muy eficientes en los que al anular una tarea no se recupera la memoria que ocupaba. Por tanto, es deber
de la propia tarea, antes de morir, el devolver la memoria a los correspondientes controladores. La
interrupcin 19h se ejecuta en estos momentos crticos, por lo que TURBODSK aprovecha para liberar la
memoria EMS/XMS ocupada y, tras restaurar el vector previo de INT 19h (para mejorar la compatibilidad)
contina el flujo normal de la INT 19h. La mayora de los discos virtuales no desvan la INT 19h; sin
embargo, RAMDRIVE s y TURBODSK no quera ser menos... aunque, en el caso de utilizar memoria
convencional no se realiza ninguna tarea (RAMDRIVE ejecuta una misteriosa y complicada rutina).
La rutina Init_io se ejecuta inmediatamente antes de una lectura o escritura en el disco, preparando
los registros. Se controla aqu que el primer y ltimo sector a ser accedido estn dentro del disco: en caso
contrario se devuelve un error de sector no encontrado. En realidad, TURBODSK no comprueba si el primer
sector est en el disco, para ahorrar memoria; al contrario que la mayora de los discos virtuales. La razn
es que si el ltimo sector est dentro del disco como no lo va a estar tambin el primero!. Tambin hay que
tener en cuenta la histrica leyenda de los 64 Kb. En concreto, el problema reside en la direccin donde
depositar o leer los datos. Pongamos por ejemplo que un programa pretende leer del disco virtual 48 Kb de
datos en la direccin DS:A000h. En principio, el manual de referencia para programadores de Microsoft dice
que el dispositivo solo est obligado a transferir cuanto pueda sin cambiar de segmento. Sin embargo, el
RAMDRIVE de Microsoft no considera esta circunstancia, por lo que si un programa intenta hacer un acceso
ilegal de este tipo se corromper tambin una parte indeseada del segmento de datos, ya que al llegar al final
de un segmento se comienza por el principio del mismo otra vez (esto no es as en el caso de emplear
memoria extendida, pero s en la convencional y expandida). En TURBODSK se prefiri limitar la
transferencia al mximo posible antes de que se desborde el segmento: hay que tener en cuenta que un
desbordamiento en el segmento de datos puede llegar a afectar al de cdigo, con todo lo que ello implica.
Cierto es que un acceso incorrecto a disco es una circunstancia crtica de la que no se puede responsabilizar
al mismo, pero a mi juicio es mejor no poner las cosas todava peor.
Otro asunto es controlar el tamao absoluto del rea a transferir: en ningn caso debe rebasar los 64
Kb, aunque no est muy claro si los puede alcanzar o no. RAMDRIVE opera con palabras de 16 bits,
permitiendo un mximo de 8000h (exactamente 64 Kb), excepto en el caso de trabajar con memoria
extendida: al pasar el n de palabras a bytes, unidad de medida del controlador XMS, el 8000h se convierte
219 CONTROLADORES DE DISPOSITIVOS
en 0 (se desborda el registro de 16 bits al multiplicar por 2): con este tipo de memoria RAMDRIVE no
soporta transferencias de 64 Kb exactos (por ello, KBSEC.C emplea un buffer de 63 y no de 64 Kb). En
TURBODSK se decidi transferir 64 Kb inclusive como lmite mximo, en todos los casos. En memoria
expandida y convencional, por otro lado, existe el riesgo de que el offset del buffer sea impar y, debido al
tamao del mismo, se produzca un acceso de 16 bits en la direccin 0FFFFh, ilegal en 286 y superiores. Esto
provoca un mensaje fatal del controlador de memoria, preguntando si se desea seguir adelante o reinicializar
el sistema (QEMM386), o simplemente se cuelga el ordenador (con el EMM386 del MS-DOS 5.0 o en
mquinas 286). Por ejemplo, pruebe el lector a leer justo 32 Kb en un buffer que comience en 8001h con
RAMDRIVE en memoria EMS: RAMDRIVE no pierde el tiempo comprobando estas circunstancias crticas,
aunque VDISK parece que s. En TURBODSK se opt tambin por ser tolerante a los fallos del programa
que accede al disco: adems de limitar el acceso mximo a 64 Kbytes, y de transferir slo lo que se pueda
antes del desbordamiento del segmento, puede que todava se transfiera entre uno y tres bytes menos, ya que
se redondea por truncamiento la cuenta de palabras que faltan para el final del segmento para evitar un
direccionamiento ilegal en el offset 0FFFFh (estas circunstancias crticas deben evaluarse utilizando las
interrupciones 25h/26h, ya que al abrir ficheros ordinarios el DOS es siempre suficientemente cauto para no
poner a prueba la tolerancia a fallos de las unidades de disco).
Inmediatamente despus de la rutina Init_io de TURBODSK est colocada la que gestiona el disco
en memoria expandida. No existe ningn nexo de unin y ambas se ejecutan secuencialmente. Al final de
Init_io hay una instruccin para borrar el acarreo. Esto es as porque la rutina que gestiona el disco puede
ser accedida, adems de desde Init_io, desde el gestor de la interrupcin 19h. El acarreo sirve aqu para
discernir si estamos ante una operacin normal de disco o ante una inicializacin del sistema. En el caso de
una operacin de disco, BP indica adems si es lectura o escritura. TURBODSK soporta tambin memoria
extendida XMS y convencional: cuando se utilizan estas memorias, la rutina correspondiente sustituye a la
de memoria EMS por el simple y efectivo procedimiento de copiarla encima. Esta tcnica, que horrorizar
a ms de un programador, es frecuente en la programacin de sistemas bajo MS-DOS. De esta manera,
TURBODSK y RAMDRIVE (que tambin comete esta inmoralidad) economizan memoria, ya que solo queda
residente el cdigo necesario. El hecho de que por defecto est colocada la rutina de memoria expandida es
debido a que es, con diferencia, la ms larga de todas y as siempre queda hueco para copiar encima las otras.
A la hora de terminar residente, si la mquina tiene memoria extendida y no se indica /A, no se dejar
espacio ms que para las rutinas de memoria extendida y convencional, para economizar ms memoria.
ANLISIS DE LAS RUTINAS DE GESTIN DE MEMORIA.
Las rutinas que gestionan los diversos tipos de memoria tienen los mismos parmetros de entrada
(obtenidos de Init_io) y sirven para leer/escribir en el disco segn lo que indique BP, as como para liberar
la memoria asignada en respuesta a una interrupcin 19h. Retornan devolviendo en AX el resultado de la
operacin, que ser normalmente exitoso. En caso de fallo de algn controlador de memoria, devolveran un
cdigo de error de anomala general.
Trabajando con memoria EMS.
La rutina ms compleja es la que gestiona la memoria expandida EMS. Adems, un disco virtual que
se precie debe soportar transferencias incluso en el caso de que el buffer donde leer/escribir los datos est
tambin en la memoria expandida y se solape con el propio disco. Este aspecto no es tenido en cuenta por
ningn disco virtual de dominio pblico con soporte de memoria EMS que yo conozca, aunque s por los del
DOS; a esto se debe que algunas aplicaciones que trabajan con memoria expandida adviertan que pueden
operar mal con ciertos discos virtuales.
En el caso de VDISK, el algoritmo es muy poco eficiente: este disco virtual realiza un bucle, con una
vuelta para cada sector, donde hace todas estas tareas: preservar el contexto del mapa de pginas, calcular
las direcciones, transferir a un buffer auxiliar, recuperar el contexto del mapa de pginas y transferir del
buffer auxiliar hacia donde solicita el DOS. Ello significa que, para transferir 32 Kb en sectores de 0,5 Kb,
se salva y restaura 64 veces! el contexto del mapa de pginas. No digamos si los sectores son ms pequeos,
220 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
adems del hecho (mucho ms grave) de que transfiere dos veces y de la cantidad de veces que calcula las
direcciones. Cierto es que salvar el contexto del mapa de pginas y volverlo a restaurar es necesario, de cara
a que el disco virtual (un programa residente a todos los efectos) no afecte al programa de usuario que se est
ejecutando, por si ste utiliza tambin memoria expandida. La pregunta es, por qu no sacaron los autores
de VDISK esas operaciones fuera del bucle?, y por qu utilizar un buffer auxiliar?. Lgicamente hay una
respuesta. Piense el lector qu suceder si el buffer donde leer o escribir que suministra el programa principal,
est en memoria expandida: se solapa con el disco virtual!. Para solucionar este posible solapamiento,
VDISK se ve obligado a realizar esas operaciones con objeto de permitir una transferencia de la memoria
expandida a la propia memoria expandida, a travs de un buffer auxiliar. Este algoritmo provoca que VDISK
sea prcticamente tan lento como un buen disco duro cuando trabaja con memoria expandida y sectores de
512 bytes, y bastante ms lento si se utilizan los sectores de 128 bytes que suele establecer por defecto!.
Adems, el buffer del tamao de un sector incrementa el consumo de memoria en 512 bytes.
ESQUEMA DE FUNCIONAMIENTO DE LA RUTINA DE GESTIN DE MEMORIA EMS DE TURBODSK
Analizaremos el caso ms conflictivo:
Cuando el rea a transferir ocupa los 16 Kbytes mximos.
- - - - - - - - - - - - - - - - - - - - - - - -
M
Pgina 3 E Pgina 3
M --
O 16
Pgina 2 R Pgina 2 Kb
I --
A
Pgina 1 Pgina 1
E caso B
M
Pgina 0 S Pgina 0
- - - - - - - - - - - - - - - - - - - - - - - -
caso A
Resulta evidente, en el caso A, que si el buffer donde leer/escribir los datos comienza por
debajo de la direccin marcada por la flecha (o justo en esa direccin) no colisionar con la
pgina 0, ya que no excede de 16 Kb de longitud. Como al convertir la direccin segmentada a
prrafos se pierde precisin, TURBODSK se asegura que la direccin est 401h prrafos (16 Kb
ms 1 prrafo) por debajo del inicio de la pgina 0.
En el caso B, el buffer est en memoria expandida pero comienza justo detrs de la pgina 0
y, por lo que no hay colisin con esta pgina. Una vez ms, por razones de redondeo, TURBODSK
comprueba que el buffer comience al menos 401h prrafos por encima del inicio de la pgina 0.
En realidad, bastara con comprobar si dista al menos 400h bytes, ya que el redondeo al
convertir la direccin segmentada se hace truncando.
Conclusin: para que no haya colisin, el buffer ha de estar a 401h prrafos de distancia
(expresada en valor absoluto) del inicio de la pgina 0. Qu sucede si hay colisin?. Pues que
no se puede emplear la pgina 0, que se solapa con el buffer. En ese caso, bastara con elegir
la pgina 2 ya que si el buffer empieza justo donde apunta la flecha del caso B, como su tamao
es de no ms de 16 Kb, no puede invadir... s, s puede invadir la pgina 2, aunque slo un
prrafo! (no olvidar que si empieza por encima de la flecha no colisiona con la pgina 0). Por
tanto, tenemos que utilizar la pgina 3. En general, en un sistema con memoria EMS 4.0 donde
las pginas pueden ser definidas por el usuario en la direccin que desee (parmetros /Pn= del
EMM386 del MS-DOS 5.0), basta con asegurarse que la pgina alternativa a la 0, para los casos
en que hay colisin, est alejada al menos 48 Kb de la pgina 0 (esto es, que entre ambas
pginas hay una distancia absoluta de 32 Kb).
Se comprende ahora la necesidad de restaurar el contexto del mapa de pginas antes de pasar
utilizar una nueva pgina para las transferencias: el hecho de necesitar una nueva pgina viene
determinado porque la hasta entonces utilizada se solapa con el buffer y es preciso restaurar
el contenido del buffer!. Adems, hay que volver a salvar el contexto de manera inmediata para
que quede salvado para otra ocasin (o para cuando se acabe el acceso al disco y haya de ser
restaurado).
En principio, no se recomienda a nadie intentar comprender la rutina de TURBODSK para la
memoria EMS (Procesa_ems): dada su complejidad, es ms fcil para un programador desarrollar la suya
propia que intentar entender la actual: fundamentalmente, porque los escasos 247 bytes que ocupa evidencian
en qu medida el autor se ha decantado por la eficiencia en detrimento de la claridad al disearla. Sin
embargo, las pautas que se darn pueden ser tiles. TURBODSK utiliza una tcnica totalmente diferente a
221 CONTROLADORES DE DISPOSITIVOS
la de VDISK, para evitar el buffer auxiliar. En principio, debido a que TURBODSK transfiere bloques de
hasta 16 Kb en cada iteracin, el bucle no dar nunca ms de 5 vueltas (un bloque de disco de 64 Kb puede
estar comprendido en 5 pginas EMS). Al principio se salva una sola vez el contexto de la memoria
expandida, antes de entrar en el bucle, volvindose a restaurar al final del todo, tambin una sola vez. No se
realizar esto ms veces si no hay solapamientos. Por otra parte, como slo se utiliza una pgina de memoria
expandida a un tiempo, TURBODSK elige inteligentemente una que no colisione con la del buffer del
programa principal a donde enviar/recibir los datos. En el caso en que haya colisin con la pgina 0,
TURBODSK restaura el contexto y lo vuelve a salvar, con objeto de devolver la memoria expandida a la
situacin inicial y mantener la primera copia que se hizo del contexto; adems, elige otra pgina que diste
al menos 32 Kb de la pgina 0 (bastara con 16 Kb, pero se hace as para evitar problemas en los redondeos
si los buffers no empiezan en posiciones alineadas a prrafo). El esquema grfico lo explica con mayor
claridad.
Tras la transferencia, si haba habido colisin 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 sea
ligeramente ms lento cuando el buffer de lectura/escritura est en memoria expandida, pero probablemente
la diferencia no llegue al 1% al caso en que no hay solapamientos. El funcionamiento general consiste en ir
mapeando las pginas de memoria expandida una a una, considerando las tres posibilidades: al principio,
puede ser necesario transferir un fragmento del final de la primera pgina mapeada; despus, puede ser
preciso transferir algunas pginas enteras y, por ltimo, una parte inicial de la ltima pgina. Esto significa
que TURBODSK slo mapea (y una sola vez) las pginas estrictamente necesarias para la transferencia;
adems, no transfiere sector a sector sino el mayor nmero posible que pueda ser transferido de una sola vez
y se evita la necesidad de hacer doble transferencia (con el consiguiente ahorro, adems, del buffer de 512
bytes). Este algoritmo permite que TURBODSK sea tan rpido como cabra esperar de un disco virtual,
incluso al trabajar con memoria EMS. De hecho, al transferir 32 bits en los 386 y superiores, la velocidad
que desarrolla en memoria EMS no se queda muy por detrs de la que consigue el controlador de memoria
XMS en estas mquinas. El inconveniente de la rutina de gestin de memoria EMS en TURBODSK es, como
se dijo antes, la complejidad: est optimizada para reducir en lo posible el tamao, por lo que puede resultar
de difcil comprensin. Por ejemplo, posee una subrutina encargada de acceder al controlador de memoria
que, en caso de fallo, altera la pila para retornar directamente al programa principal y no al procedimiento
que la llam. Estas maniobras que aumentan la complejidad y dificultan posteriores modificaciones del
cdigo, estn bastante documentadas en el listado, por lo que no habr ms referencias a ellas. Hay que
reconocer que por 30 40 bytes ms la rutina podra haber sido todo un ejemplo de programacin
estructurada, pero cuando se escribi TURBODSK, entre los principales objetivos estaba reducir el consumo
de memoria. Esta rutina es adems la misma para leer que para escribir: en el caso de la escritura, se limita
simplemente a intercambiar la pareja DS:SI con la ES:DI antes y despus 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 pginas consecutivas, lo
que le permitira en ocasiones ser levemente ms rpido que TURBODSK; sin embargo, como no transfiere
con 32 bits, en los 386 y superiores es notablemente ms lento que TURBODSK. RAMDRIVE necesita que
las pginas de memoria expandida sean contiguas (podran no serlo en EMS 4.0), emitiendo un error de
instalacin en caso contrario; el mtodo de TURBODSK es algo ms tolerante: no necesita que sean
estrictamente contiguas, basta solo con que entre las 4 primeras haya alguna que diste de la primera al menos
32 Kb, la cual asigna dinmicamente.
Para terminar con el anlisis de la gestin de este tipo de memoria, hablaremos algo acerca de la
manera de comunicarse con el controlador de memoria. En principio, lo ms normal es cargar los registros
e invocar la INT 67h, analizando el valor en AH para determinar si ha habido error. Sin embargo, se ha
constatado que RAMDRIVE, ante un cdigo de error 82h (EMM ocupado) vuelve a reintentar de manera
indefinida la operacin, excepto en el caso de la funcin 40h (obtener el estado del gestor) utilizada en la
instalacin, en la que hay slo 32768 intentos. Este comportamiento parece estar destinado a mejorar la
convivencia con entornos multitarea, en los que en un momento dado el controlador de memoria puede estar
ocupado pero algo ms tarde puede responder. Por tanto, tambin se incorpor esta tcnica a TURBODSK.
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 rutinas
de TURBODSK: en principio han sido cuidadosamente elegidas con el objetivo de economizar memoria. Por
ello, la instruccin PUSHAD (equivalente a PUSHA, pero con los registros de 32 bits) vena muy bien para
apilar de una sola vez todos los registros de propsito general. Sin embargo, la correspondiente instruccin
POPAD no opera correctamente, por desgracia, en la mayora de los 386, aunque el fallo fue corregido en
las ltimas versiones de este procesador (los 386 de AMD tambin lo tienen, qu curioso!). Se trata de un
fallo conocido por los fabricantes de software de sistemas, pero poco divulgado, aunque tampoco es muy
grave: bsicamente, el problema reside en que EAX no se restaura correctamente. El fallo de esta instruccin,
al parecer descubierto por Jeff Prothero est ligado a las instrucciones que vienen inmediatamente a
continuacin, y est demostrado que poniendo un NOP detrs -entre otros- nunca falla. En las rutinas de
TURBODSK se observa tambin que los registros de 32 bits empleados en la transferencia son enmascarados
para que no excedan de 0FFFFh, ya que podran tener la parte alta distinta de 0 y ello provocara una trgica
excepcin del controlador de memoria al intentar un acceso -por otra parte, de manera incorrecta- fuera de
los segmentos de 64Kb.
Trabajando con memoria XMS.
La memoria extendida va XMS, implementada por HIMEM.SYS y algn controlador de memoria
expandida, es notablemente ms sencilla de manejar que la expandida. En el caso de VDISK, se emplea el
tradicional mtodo de la INT 15h de la BIOS para transferir bloques en memoria extendida. Pese a ello, el
VDISK de DR-DOS 6.0 es una versin moderna del legendario controlador, y puede convivir
satisfactoriamente con WINDOWS y con los programas que soportan la especificacin XMS debido a que
toma 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 mquinas que
poseen memoria extendida en la actualidad tienen instalado el controlador XMS. Las que no lo tienen
instalado, se les puede aadir fcilmente (solo requiere al menos DOS 3.0). Las ventajas del controlador XMS
son mltiples. Por un lado, la velocidad es bastante elevada, ya que en los 386 y superiores utiliza
automticamente instrucciones de transferencia de 32 bits. Por otro, es extraordinariamente sencillo el
proceso: basta crear una estructura con la informacin del bloque a mover de la memoria convencional
hacia/desde la extendida e invocar la funcin 0Bh. La diferencia entre TURBODSK y RAMDRIVE es que
el primero crea la estructura sobre la pila (solo son 8 palabras). La ventaja de ello es que las instrucciones
PUSH consumen mucha menos memoria que las MOV; por otro lado as no hace falta reservar el buffer para
la estructura. Hablando de pila: todos los programas residentes que utilizan servicios XMS suelen definir una
pila interna, ya que la llamada al controlador XMS puede crear una trama de pila de hasta 256 bytes!. Sin
embargo, RAMDRIVE no define una pila propia, y no es difcil deducir por qu: el DOS, antes de acceder
a los controladores de dispositivo, conmuta a una de sus pilas internas, que se supone suficientemente grande
para estos eventos. Por el mismo motivo, se decidi no incorporar una pila a TURBODSK, aunque hay discos
virtuales de dominio pblico que s lo hacen. Es fcil comprobar la pila que el DOS pone a disposicin de
los drivers: basta hacer un pequeo programa en DEBUG que acceda al disco virtual (por ejemplo, va INT
25h) y, sabiendo dnde reside ste, poner un punto de ruptura en algn lugar del mismo con una INT 3. Al
ejecutar 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, todava quedaban ms de 2 Kb de pila
en el momento del acceso al disco virtual (el tamao de la pila es el valor de SP). Finalmente, decir que
debido a que utilizan la misma memoria de la misma manera, TURBODSK y RAMDRIVE desarrollan
velocidades prcticamente idnticas al operar en memoria extendida.
Hay sin embargo un detalle curioso que comentar: RAMDRIVE instala una rutina que intercepta las
llamadas al controlador XMS. Hacer esto es realmente complicado, teniendo en cuenta que el controlador
XMS no se invoca por medio de una interrupcin, como los dems controladores, sino con un CALL
inter-segmento. Por ello, es preciso modificar parte del cdigo ejecutable del propio controlador de memoria.
Esto es posible porque el controlador XMS siempre empieza tambin por una instruccin de salto lejana de
cinco bytes (o una corta de dos o tres, seguida de NOPs, considerando RAMDRIVE todas estas diferentes
posibilidades). RAMDRIVE intercepta la funcin 1 (asignar el HMA), pero comprobando tambin si AL
vale 40h: esto significa que est intentando detectar la llamada de algn programa en concreto, ya que el
223 CONTROLADORES 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 retornar
con un error 91h (el HMA ya est asignado). Todo parece destinado a mejorar la compatibilidad con algn
programa, probablemente tambin de Microsoft, aunque ningn otro disco virtual -TURBODSK entre ellos-
realiza estas extraas maniobras. Esta forma de trabajar es lo que podramos denominar programacin a nivel
de cloacas, usando cdigo basura para tapar la suciedad de otros programas previos.
Trabajando con memoria convencional.
En memoria convencional hay pocas diferencias entre todos los discos virtuales. Como no hay
controladores de memoria por el medio, la operacin del disco siempre resultar exitosa. La diferencia de
TURBODSK frente a RAMDRIVE y VDISK es que en los 386 y superiores utiliza de nuevo transferencias
de 32 bits. Sin embargo, esto no es demasiado importante, ya que estas mquinas suelen tener la memoria
convencional destinada a cosas ms tiles que un disco. En los PC/XT el rendimiento de todos los discos
virtuales suele ser muy similar, excepto algn despistado de dominio pblico que mueve palabras de 8 bits.
La rutina Procesa_con ubicada al final de TURBODSK se encarga de gestionar esta memoria.
LA SINTAXIS DE TURBODSK.
TURBODSK puede ser ejecutado desde el DOS o el CONFIG.SYS indistintamente, y adems en el
primer caso de manera repetida, para cambiar las caractersticas de un disco ya definido. En cualquier caso,
el programa habr de ser instalado obligatoriamente en el CONFIG.SYS. Repasaremos la sintaxis que admite
antes de proceder a estudiar la instalacin del programa:
DEVICE=TDSK.EXE [tamao [tsect [nfich [tclus]]]] [/E] [/A|X] [/M] [/F]
Alternativamente, desde el DOS:
TDSK [U:] [tamao [tsect [nfich [tclus]]]] [/E] [/A|X] [/C] [/M] [/F]
El tamao del disco ha de estar entre 8 y 65534 Kb (para exceder de 32 Mb hacen falta sectores de
al menos 1024 bytes). Se puede omitir en el CONFIG si no se desea definir el disco en ese momento, y desde
el DOS si solo se quiere obtener informacin del disco definido. Tsect es el tamao de sector, entre 32 y
2048 bytes en potencias de dos. Sin embargo, DR-DOS no opera correctamente con sectores de menos de
128 bytes, aunque s el MS-DOS 5.0, que por otro lado no soporta sectores de ms de 512 bytes (DR-DOS
s). El nmero de ficheros del directorio raz viene a continuacin (nfich) y ha de estar comprendido entre
1 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 raz como mdulo 256. El
tamao de cluster (sectores/cluster) es el ltimo parmetro numrico, debiendo estar comprendido entre 1 y
255. Sin embargo, el MS-DOS no soporta tamaos de cluster que no sean potencia de 2 (DR-DOS s). Los
parmetros numricos intermedios que se desee omitir se pueden poner a cero, para que TURBODSK tome
valores por defecto.
TURBODSK slo necesita que se indique el tamao del disco, ajustando los dems parmetros de
la manera ms aconsejable. De lo expuesto anteriormente se deduce que es sencillo crear discos que no
operen correctamente, si no se tienen en cuenta las limitaciones de los diversos sistemas operativos, aunque
esto es responsabilidad del usuario y el programa no limita su libertad. Con /E se fuerza la utilizacin de
memoria extendida, aunque es un parmetro un tanto redundante (TURBODSK utiliza por defecto esta
memoria). /A y /X sirven, indistintamente, para utilizar memoria expandida.
Hasta ahora, la sintaxis de TURBODSK es idntica a la de RAMDRIVE y VDISK, si se excepta
el parmetro adicional del tamao de cluster. Sin embargo, TURBODSK soporta la presencia de varias
unidades instaladas simultneamente: desde el DOS puede ser preciso indicar tambin la letra de la unidad
a tratar, aunque por defecto se acta siempre sobre la primera. Tambin se puede indicar /C desde el DOS
224 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
para forzar el empleo de memoria convencional en mquinas con memoria expandida y/o extendida. /M
genera una salida menos espectacular, en monocromo y redireccionable (desde el CONFIG se imprime en
monocromo por discrecin y este conmutador acta al revs, forzando una salida en color). La opcin /F, no
documentada en la ayuda del programa, permite elegir el nmero de FATS (1 2). Lo normal es trabajar con
una FAT, pero TURBODSK soporta la definicin de 2 con objeto de permitir la creacin de discos idnticos
a los estndar del DOS. As, con un pequeo programa de utilidad es fcil montar ficheros imagen de
disquetes (creados con el DISKCOPY de DR-DOS 6.0, con DCOPY o con otras utilidades) en un disco
virtual de tamao suficiente. Dicho volcado debe hacerse justo tras redefinir el disco y antes de realizar
ningn acceso al mismo, para aprovechar el hecho de que el DOS va a ser informado de un cambio de
soporte. Ejemplo de lo que puede aparecer en pantalla al definir un disco:
TURBODSK 2.3 - Unidad D: Tamao de sector: 512
N entradas raiz: 128
Tamao: 512 Kbytes Sectores/cluster: 1
Memoria: Extendida XMS 1012 clusters (FAT12)
EL PROCESO DE INSTALACIN DE TURBODSK.
Casi el 80% del listado de TURBODSK est destinado a instalar y mantener el disco virtual en
memoria. TURBODSK puede ser ejecutado desde la lnea de comandos y desde el CONFIG.SYS; los
procedimientos Main e Init, respectivamente, constituyen el programa principal en ambos casos. El
funcionamiento del programa es muy similar en los dos casos, aunque hay ciertas diferencias lgicas. Al
principio de ambas rutinas se inicializa una variable que indica si estamos en el CONFIG o en el
AUTOEXEC (ms en general, en la lnea de comandos). Algunas subrutinas concretas actuarn de manera
diferente segn 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 cabra esperar de la misma: inicializar el puntero a la tabla de BPBs (solo
uno, ya que cada TURBODSK instalado controla un solo disco), el nmero de unidades (una), as como la
memoria que ocupa el programa: al final de Init, si no se va utilizar memoria expandida se reserva espacio
slo para las rutinas de memoria convencional y extendida. Se puede definir el disco desde el CONFIG o,
sin indicar capacidad o indicando un tamao 0, instalar el driver sin reservar memoria: para definir el disco
se puede ejecutar TURBODSK despus desde el DOS. En cualquier caso, desde el CONFIG no se permite
definir el disco en memoria convencional, ya que si as fuera no se podra desasignar en el futuro. Tampoco
es muy recomendable reservar memoria extendida o expandida, para evitar una posible fragmentacin de la
misma (esto depende de la eficacia de los controladores de memoria) aunque s se permite definir un disco
de estos desde el CONFIG. Tambin es vital considerar el parmetro de tamao de sector que el usuario
pueda definir, incluso aunque no se cree el disco al indicar un tamao 0. La razn es que el DOS asigna el
tamao de sus buffers de disco para poder soportar el sector ms grande que defina algn controlador de
dispositivo de bloques. El MS-DOS 5.0 no soporta sectores de ms de 512 bytes, pero DR-DOS opera
satisfactoriamente con sectores de uno o dos Kbytes, e incluso ms. Sin embargo, no es recomendable utilizar
sectores de ms de 512 bytes, ya que el tamao de los buffers aumenta y se consume ms memoria. Empero,
TURBODSK, gracias a los sectores de ms de 512 bytes permitira operar con discos de ms de 32 Mb sin
rebasar el lmite mximo de 65535 sectores. Otro pequeo detalle: si la versin 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 objeto
de parecerse lo ms posible a un controlador del DOS 2.X (RAMDRIVE tambin se toma esta molestia).
Tambin desde el CONFIG se desva la INT 19h.
El procedimiento Main es muy similar al Init, la principal diferencia radica en que en el caso de
utilizar memoria convencional hay que terminar residente, para que el DOS respete el bloque de memoria
creado para contener el disco. Sin embargo, se dejan residentes slo los primeros 96 bytes del PSP. Tambin
desde Main puede ser necesario desalojar la memoria de un disco previo, si se indica uno nuevo. Es preciso,
as mismo, considerar ciertas circunstancias nuevas que no podan darse desde el CONFIG: una versin del
DOS anterior a la 2.0, que el driver no haya sido instalado antes desde el CONFIG, que se indique una letra
225 CONTROLADORES DE DISPOSITIVOS
de unidad que no se corresponda con un driver TURBODSK, que el tamao de sector exceda el mximo que
permite la configuracin del DOS, que se solicite memoria expandida y no se halla reservado espacio para
la rutina que la soporta o que se intente redefinir el disco desde WINDOWS. Este ltimo aspecto se consider
a raiz de los riesgos que conlleva. Supongamos, por ejemplo, que el usuario abre una sesin DOS desde
WINDOWS y define un disco de media mega en memoria convencional, volviendo despus a WINDOWS:
WINDOWS recupera toda la memoria convencional que haba asignado para su propio uso, pero
TURBODSK no puede darse cuenta de esta circunstancia y, si el usuario intenta grabar algo en el disco
virtual, el sistema se estrellar. La memoria virtual de WINDOWS tambin da problemas al crear discos en
memoria expandida o extendida. Por tanto, las definiciones del disco han de hacerse antes de entrar en
WINDOWS. Tampoco conviene definir el disco desde DESQVIEW, aunque si se anula de nuevo antes de
abandonar DESQVIEW no habr problemas, por lo que TURBODSK s permite modificar el disco desde el
interior de este entorno.
Tanto Init como Main leen la lnea de parmetros indicados por el usuario y ejecutan ordenadamente
los procedimientos necesarios para definir el disco, si sto es preciso.
LAS PRINCIPALES SUBRUTINAS PARA LA INSTALACIN.
Veremos ahora con detalle algunas rutinas importantes ejecutadas durante la instalacin del disco
virtual.
La rutina Gestionar_ram, ejecutada slo desde la lnea de comandos del DOS, rebaja la memoria
asignada al TDSK.EXE en ejecucin a 96 bytes. Esto se hace as para poder utilizar despus las funciones
estndar del sistema para asignar memoria. Esta acrobacia provoca la creacin de un bloque de control de
memoria (MCB) en el offset 96 del PSP, lo cual es inocuo; tambin se libera el espacio de entorno por si
acaso se fuera a terminar residente.
Los procedimientos Errores_Dos y Errores_config comprueban algunos errores que pueden
producirse al ejecutar el programa desde la lnea de comandos del DOS o desde el CONFIG. En el
procedimiento Max_sector invocado desde Errores_Dos se comprueba si el tamao de sector indicado
excede el mximo que soporta el DOS, para lo que se utiliza la funcin 52h (Get List of Lists); si es as se
indica al usuario que ese tamao de sector debe definirse previamente desde el CONFIG.
En la rutina TestWin se comprueba si Windows est activo, para evitar en ese caso una modificacin
del disco por parte del usuario. Por desgracia, hay que chequear en dos interrupciones distintas las presencia
de Windows. Antes de llamar a la INT 2Fh se comprueba que esta interrupcin est apuntando a algn sitio:
en el sistema DOS 2.11 en que se prob TURBODSK esa interrupcin estaba apuntando a 0000:0000 y el
ordenador se colgaba si no se tomaba esta precaucin.
Tambin desde el DOS, el procedimiento Reside_tdsk? busca la primera unidad TURBODSK
residente de todas las que puede haber en la memoria. Para ello crea una tabla con todos los dispositivos de
bloque del sistema (rutina Lista_discos) y empieza a buscar desde el final hacia atrs (se trata de encontrar
la primera unidad TURBODSK y no la ltima). Alternativamente, si se haba indicado una letra de unidad,
el procedimiento Obtener_segm recorre la tabla de discos para asegurarse de que esa letra de unidad es un
dispositivo TURBODSK, as como para anotar la direccin 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 peticin de solicitud
de la orden INIT almacena este dato. Dado que DR-DOS 6.0 no inicializa correctamente el tamao del
encabezamiento de solicitud de esta orden, es ms seguro verificar la versin del DOS que comprobar si este
dato est definido o no, en funcin de las longitudes, que sera lo normal. En el caso del DOS 2.X, no hay
ms remedio que crear una tabla con los dispositivos de bloque del sistema y contarlos (a que ya sabe por
qu RAMDRIVE y VDISK no informan o informan incorrectamente de la letra de unidad al instalarse en
estas versiones del DOS?).
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 dispositivos
de bloque del sistema. Para ello utiliza la valiosa funcin indocumentada 52h (Get List of Lists) del DOS.
Por desgracia, la manera de acceder a la cadena de controladores de dispositivo vara segn la versin del
DOS, por lo que TURBODSK tiene en cuenta los tres casos posibles (DOS 2.X, 3.0 y versiones posteriores).
En la tabla creada, con cuatro bytes por dispositivo: los dos primeros indican el segmento donde reside, el
segundo el nmero de unidades que controla y el tercero puede valer 1 0 para indicar si se trata de una
unidad TURBODSK o no. El final de la tabla se delimita con un valor de segmento igual a cero. En el caso
de un dispositivo TURBODSK no se anota el segmento donde reside sino la variable cs_tdsk del mismo, que
indica la direccin real incluso en el caso de que el dispositivo haya sido relocalizado por QEMM a la
memoria superior.
La rutina Desinstala libera la memoria que ocupa un disco residente con anterioridad, inhabilitando
el driver. En el caso de la memoria convencional hay que liberar tanto el segmento que ocupaba el disco
como el del PSP previamente residente.
El procedimiento Mem_info evala la memoria disponible en el sistema y toma la decisin de qu
tipo y cantidad de la misma va a ser empleada. En principio se procura utilizar la memoria que el usuario
indica. De lo contrario, por defecto se intenta emplear, en este orden, memoria extendida, expandida o
convencional. En el caso de que no haya suficiente memoria se rebaja la cantidad solicitada, generndose un
mensaje 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. A
la memoria expandida se le asigna menos prioridad que a la extendida debido a que, en equipos 386 y
superiores, normalmente es memoria extendida que emula por software la expandida: suele ser ms rpido
dejar directamente al controlador XMS la tarea de realizar las transferencias de bloques de memoria. El
procedimiento Mem_info se apoya en tres subrutinas que calculan la cantidad disponible de cada tipo de
memoria, despreciando longitudes inferiores a 8 Kb que es el tamao mnimo del disco. La subrutina
Eval_xms chequea la presencia de un controlador de memoria extendida; sin embargo, antes de llamar a INT
2Fh se toma una vez ms la precaucin de comprobar que esta interrupcin est apuntado a algo. La
subrutina Eval_ems detecta la presencia del controlador de memoria expandida buscando un dispositivo
"EMMXXXX0". El mtodo ordinario suele ser intentar abrir ese dispositivo y despus comprobar por IOCTL
que no se trata de un fichero con ese nombre; sin embargo, los controladores de dispositivo invocados desde
el CONFIG.SYS no deben acceder a las funciones IOCTL, por lo que se utiliza el algoritmo alternativo de
comprobar si esa cadena est en el offset 10 del vector 67h. En esta subrutina se comprueba adems la
versin del controlador: en la 4.0 y posterior hay que buscar, recurdese, dos pginas de memoria expandida
(una de ellas la 0) que disten entre s 32 Kb. Finalmente, la subrutina Eval_con determina la memoria
convencional disponible. Al principio le solicita casi 1 Mb al DOS, con objeto de que ste falle e indique
cual es la cantidad mxima de memoria disponible. Seguidamente se procede a pedir justo esa memoria, para
que el DOS devuelva el segmento en que est disponible, volvindose a liberarla inmediatamente a
continuacin. Al final, al tamao de ese bloque de memoria se le restan 128 Kb ya que, con memoria
convencional, hay que tener la precaucin de no ocuparla toda y dejar algo libre. Adems, en esos 128 Kb
que se perdonan ser preciso que TDSK.EXE se autoreubique antes de formatear el disco, como veremos
despus. Con MS-DOS 5.0 se puede crear un disco virtual en memoria superior, cargando TDSK.EXE con
el comando LOADHIGH: sin embargo, hay que pedir slo exactamente la cantidad de memoria superior
disponible en la mquina (o algo menos); de lo contrario el DOS asignar memoria convencional para
satisfacer la demanda: dado que normalmente hay ms memoria convencional libre que superior, no ser
preciso solicitar en estos casos, afortunadamente, 128 Kb de menos para lograr que sea asignada memoria
superior (TDSK.EXE se autorelocalizar hacia la memoria convencional y permitir emplear toda la memoria
superior libre que quede).
El procedimiento Mem_reserva procede a la efectiva asignacin de memoria al disco, en el caso de
que finalmente ste se instale, y una vez que ya se haba decidido el tipo de memoria a emplear. Si se utiliza
memoria expandida, desde la versin 4.0 del controlador se asigna un nombre al handle con objeto de que
los programas de diagnstico muestren una informacin ms detallada al usuario. El afn de informacin no
se detiene aqu: en el caso de emplear memoria extendida, TURBODSK comprueba si la creacin de un
227 CONTROLADORES DE DISPOSITIVOS
handle XMS implica la aparicin de otro handle EMS, lo busca y le renombra. Esto sucede con QEMM y
otros 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 del
disco va a ocupar el directorio, la FAT, el tipo de FAT, etc. Se toman valores por defecto o, en caso
contrario, los que el usuario haya indicado, considerando todas las posibilidades de error. TURBODSK
permite un elevado grado de libertad. Por ejemplo, es factible definir un directorio raz que consuma la mitad
de la capacidad del disco, clusters de hasta 31 Kbytes... evidentemente, los valores que TURBODSK asigna
por defecto suelen ser bastante ms operativos; pero en principio hay, como se dijo, libertad total para las
decisiones del usuario. En el caso de versiones 2.X del sistema se establece un tamao de cluster por defecto
tal que nunca sea necesaria una FAT de 16 bits (no soportada por estas versiones). El algoritmo para
determinar el tipo de FAT del disco consiste en considerar el nmero de sectores libres que quedan despus
de descontar el sector de arranque y el directorio raz. Teniendo en cuenta el tamao de cluster en bytes y
que la FAT de 12 bits aade 1,5 bytes adicionales para cada cluster, se aplica esta frmula:
nmero de sectores libres * tamao de sector
+ 1
tamao de cluster + 1,5
que devuelve el nmero de cluster ms alto del disco (se aade uno ya que los clusters se numeran
desde dos; por ejemplo, 100 clusters se numeraran entre 2 y 101 inclusive). Si el resultado es mayor o igual
que 4086, la FAT no puede ser de 12 bits, por lo que se debe recalcular la frmula sustituyendo el 1,5 por
2 y definiendo una FAT de 16 bits. Hay casos crticos en que una FAT de 12 bits no alcanza, pero al
definirla de 16 el tamao adicional que ella misma ocupa hace que el nmero de cluster ms alto baje de
4086: en estos casos se reserva espacio para una FAT de 16 bits que luego ser realmente de 12; sin
embargo, se trata de una circunstancia muy puntual y poco probable. En principio, con los tamaos de cluster
y sector que TURBODSK asigna por defecto, la FAT ser de 12 bits a menos que el disco exceda los 8 Mb.
Conviene hacer hincapi en que los discos con 4085 clusters o ms (con nmero de cluster ms alto
4086 o superior) tienen una FAT de 16 bits. Por desgracia, casi todos los libros consultados (y ya es mala
suerte) tienen esta informacin incorrecta: para unos, la FAT16 empieza a partir de 4078 clusters; para otros,
a partir de 4086; otros, no distinguen entre n de clusters y n ms alto de cluster... hay un autntico caos ya
que las fuentes de informacin se contradicen. Al final, lo ms sencillo es crear discos virtuales con
4084/4085 clusters y espiar qu hace el DOS. Es muy fcil: se graban algunos ficheros y se mira la FAT con
algn programa de utilidad (PCTOOLS, DISKEDIT). A simple vista se deduce si el DOS asigna una FAT
de 12 o de 16 bits. Tanto el MS-DOS 3.1 como el 3.3, 4.0 y 5.0; as como el DR-DOS 3.41, 5.0 y 6.0
asignan FATs de 16 bits a partir de 4085 clusters inclusive. Por fortuna, todas las versiones del DOS parecen
comportarse igual. Asignar el tipo de FAT correcto es vital por muchos motivos; entre otros por que si fuera
excesivamente pequea el disco funcionara mal. Sin embargo, los CHKDSK de casi todas las versiones del
DOS (excepto el del MS-DOS 3.30 y el de DR-DOS 6.0), incluido el de MS-DOS 5.0, poseen una errata por
la que suponen que los discos de 4085 a 4087 clusters tienen una FAT de 12 bits, con lo que pueden
estropear el disco si el usuario ejecuta un CHKDSK/F. Esto es un fallo exclusivo de CHKDSK que debera
ser corregido en el futuro, por lo que no se ha evitado estos tamaos de disco (casi nadie ejecuta CHKDSK
sobre un disco virtual, y en ese caso no va a tener tan mala suerte). Resulta curioso este fallo de CHKDSK,
teniendo en cuenta que es un programa que accede a la FAT y que 4087 (0FF7h) es precisamente la marca
de cluster defectuoso en una FAT de 12 bits, nunca un nmero de cluster cualquiera!. Por ejemplo, con un
comando del tipo TDSK 527 128 0 1 /E (no vale la memoria expandida, ya que redondeara a 528 Kb), se
puede crear un disco de 4087 clusters en el que los CHKDSK de las versiones del DOS sealadas informen
incorrectamente de la presencia de errores (si decide hacer pruebas, retoque el nmero de entradas del
directorio para variar ligeramente el nmero de clusters).
Una vez definidos los parmetros bsicos de la estructura del disco, el procedimiento Preparar_bpb
inicializa el BPB, actualizndolo al nuevo disco; tambin se indica que ha habido cambio de disco. El
procedimiento Prep_driver se encarga de copiar el BPB recin creado sobre el del driver residente en
memoria, as como de actualizar las variables de la copia residente en memoria, copiando simplemente las
228 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
del TDSK.EXE en ejecucin. Tambin se instala la rutina necesaria para gestionar el disco, segn el tipo de
memoria a emplear por el mismo: esta rutina se instala por partida doble, tanto en la copia residente como
en el propio cdigo del TDSK.EXE que se ejecuta (la rutina de gestin de memoria ser accedida
directamente al formatear el disco virtual).
En el caso de emplear memoria convencional, antes de formatear el disco hay que tomar
precauciones. El motivo radica en el hecho de que el disco probablemente comience en el offset 96 del PSP.
Por tanto, si se inicializa sin ms el sector de arranque, la FAT y el directorio raz (en eso consiste
simplemente el formateo) el propio TDSK.EXE se autodestruir. Para evitarlo, TDSK.EXE se copia a s
mismo en esos 128 Kb libres que siempre hay, incluso en el peor de los casos, pasando a ejecutarse en ese
nuevo destino por medio de una instruccin RETF que carga CS al retornar (procedimiento Relocalizar). Se
copia todo, pila incluida (se actualiza tambin SS). No habr problemas, ya que TDSK.EXE es realmente un
programa COM disfrazado de EXE, que carece de referencias absolutas a segmentos. Se toma la precaucin
de relocalizar TDSK.EXE (que no ocupa ms de 12 Kb) justo a la mitad de ese rea de 128 Kb, para evitar
solapamientos consigo mismo en casos crticos. Se puede llegar a sobreescribir parte de la zona transitoria
del COMMAND.COM, lo cual provoca simplemente su recarga desde disco. Ciertamente, no es muy
ortodoxo que un programa en ejecucin vaya dando paseos por la memoria del PC, pero estas cosas se
pueden hacer en MS-DOS y nadie puede cuestionar la efectividad del mtodo. Los programadores ms
conservadores han tenido suerte de que el adaptador de vdeo monocromo cuente con slo 4 Kb.
ESQUEMA DE LA AUTORELOCALIZACIN DE TDSK.EXE (UN CASO CONCRETO)
Casi todas las cifras son arbitrarias, a modo de ejemplo prctico.
1 Mb 1 Mb
640 Kb 640 Kb
aprox. 588 Kb
nueva pila de TDSK.EXE
128 Kb
TDSK.EXE
PSP TDSK.EXE (256 bytes)
576 Kb
64 Kb libres (rea de
seguridad)
512 Kb
. . . . . .
. . . . . .
. . . . . .
Futuros programas
pila de TDSK.EXE
rea de almacenamiento
TDSK.EXE del disco virtual
PSP TDSK.EXE (256 bytes) PSP TDSK.EXE (96 bytes)
DOS/BIOS DOS/BIOS
0 Kb 0 Kb
Antes Despus
En este esquema se muestra la autorelocalizacin de TDSK.EXE en memoria en el caso de
definirse el disco en memoria convencional. No estn reflejados los bloques de control de
memoria ni otros detalles. Si la memoria est suficientemente fragmentada (por haber instalado
programas residentes tras definir algn disco) puede que no fuera estrictamente necesario
respetar 128 Kb al final del bloque que nos asigna el DOS ni tampoco quiz relocalizar TDSK.EXE;
sin embargo, el programa no est optimizado hasta ese extremo. El hecho de relocalizar TDSK
hacia la frontera de los 576 Kb en lugar de los 512 se debe a evitar problemas de colisiones en
casos crticos de cantidad de memoria libre y tamao de disco solicitado por el usuario.
229 CONTROLADORES DE DISPOSITIVOS
El procedimiento Formatear_tdsk es extraordinariamente sencillo: se encarga de realizar lo que desde
hace algn tiempo ha dado en llamarse formateo rpido. Evidentemente, en un disco virtual no es preciso
verificar la memoria buscando posibles sectores defectuosos. Basta copiar un sector de arranque y poner a
0 la FAT y el directorio raz, con la excepcin de los primeros 3 bytes de la FAT (4 si es de 16 bits) y los
32 primeros bytes del directorio raz, que contienen una entrada con la etiqueta de volumen. TURBODSK
se toma la molestia de consultar la fecha y hora actuales para inicializar la etiqueta de volumen. Para grabar
los sectores en el disco no se puede emplear el elegante mtodo de llamar a la INT 26h: aunque el driver
residente ya est totalmente preparado para operar, si se reserva memoria desde el CONFIG.SYS el DOS no
est an listo para ejecutar la INT 26h ya que el driver an no est encadenado a la lista de dispositivos; por
ello es preciso acceder directamente al mismo (sin embargo, una vez terminado el arranque del ordenador
no hubiera habido problema alguno).
Hablando de acceso directo al disco, otra ventaja de no utilizar INT 25h/INT 26h es que Windows 95
no permite un uso directo de estas funciones. Los programas que acceden a estas interrupciones son
considerados inadecuados. TURBODSK puede funcionar bajo Windows 95, sin obligar al usuario a
reconfigurar nada, gracias entre otros motivos a que no utiliza INT 26h.
Con MS-DOS 2.11 y 3.1 hubo bastantes problemas, ya que estos sistemas no detectan muy bien el
cambio de disco aunque la rutina MEDIA CHECK del controlador de dispositivo se lo indique: son versiones
del DOS muy desconfiadas que adems comprueban el byte descriptor de medio. Es de suponer que cuando
el disco informa que ha habido cambio, estas versiones invalidarn los buffers asociados a l; sin embargo,
si creen que se trata de un disco del mismo tipo no se molestan en actualizar el BPB. Por ello, con estas
versiones, tras el formateo TURBODSK hace dos cambios de disco consecutivos, con modificacin del byte
descriptor de medio entre ambos. El hecho de hacer un segundo cambio se debe al inters de restaurar el byte
descriptor de medio inicial. Adems, el DOS 2.11 probado necesitaba dos cambios en cualquier caso: si no,
no se tomaba en serio el cambio de disco. Entre cambio y cambio, se pregunta al sistema el espacio libre en
disco para forzar un acceso al mismo.
El procedimiento renombrar_mcb cambia el nombre del bloque de memoria de TDSK.EXE: en el
caso de que el disco ocupe memoria convencional/superior, el comando MEM del sistema operativo indicar
claramente que se trata de TDSK y adems qu unidad controla. Es una tontera, pero mola.
AMPLIACIONES DE TURBODSK
Despus de esta completa exposicin sobre las rutinas que componen TURBODSK, espero que el
lector est suficientemente preparado para entender en conjunto el funcionamiento del programa y para crear
unidades de disco por su cuenta. Una posible mejora de TURBODSK sera evitar la prdida de datos al
redefinir el disco, tratndose por ejemplo de aumentar su capacidad. Es complejo aadir esta optimizacin,
ya que la arquitectura del nuevo disco puede cambiar demasiado (nuevo tamao de FAT e incluso tipo de
la misma). Adems, el usuario iba a tener muchos problemas siempre, ya que sera muy frecuente que cuando
tratase de reducir el tamao del disco ste estuviera demasiado lleno. En general, los discos virtuales
redimensionables que soportan una redefinicin sin prdida de datos, suelen permitir esto de manera limitada
y bajo circunstancias concretas. Lo que s sera ms interesante es crear un disco virtual con asignacin de
memoria en tiempo real: cuando el usuario pretende crear un fichero, habilitar el espacio suficiente. Sin
embargo, esto significa unir las complicaciones anteriores a otras nuevas, complicaciones que restaran
velocidad al disco virtual, adems de la dificultad de implementarlas que desanima al programador ms audaz.
Por otra parte, no est muy claro que el MS-DOS sea un sistema adecuado para soportar tal disco: al final,
el proyecto podra quedar descartado en la fase de anlisis (si es que alguien acepta el reto).
230 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
;
;
;
;
;
;
;
;
;
;
;
;
;
; Versin 2.3
;
;
; CONTROLADOR DE DISCO VIRTUAL PARA SISTEMAS DOS Y WINDOWS 3
;
; * * * Programa de Dominio Pblico * * *
;
; (C) 1992-1995 Ciriaco Garca de Celis.
; Grupo Universitario de Informtica. Facultad de Ciencias.
; Apartado 6062 - Valladolid (Espaa)
;
; Internet Email: ciri@gui.uva.es
; FidoNet: 2:341/21.8
;
; Mensajes en alemn cortesa de Axel Christoph Frinke
; Internet Email: acfrinke@uni-bonn.de
;
;
; 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 generaran
; un disco virtual que requerira un 386 o superior,
; adems habra 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 quin lo ha realizado.
; ------------ Macros de propsito general
XPUSH MACRO regmem ; apilar lista de registros
IRP rm, <regmem>
PUSH rm
ENDM
ENDM
XPOP MACRO regmem ; desapilar lista de registros
IRP rm, <regmem>
POP rm
ENDM
ENDM
; ------------ Estructuras de datos
cab_PETICION STRUC ; parte inicial comn a todos
tamano DB ? ; los comandos de la cabecera
unidad DB ? ; de peticin
orden DB ?
estado DW ?
dos_info DB 8 DUP (?)
cab_PETICION ENDS
cab_INIT_BBPB STRUC ; para comandos INIT/BUILD_BPB
DB (TYPE cab_PETICION) DUP (?)
num_discos DB ? ; nmero de unidades definidas
fin_resid_desp DW ? ; rea que quedar residente
fin_resid_segm DW ?
bpb_cmd_desp DW ? ; lnea de rdenes del CONFIG
bpb_cmd_segm DW ? ; y puntero al BPB
nuevo_disco DB ? ; (DOS 3+) (0-A:, 1-B:,...)
cab_INIT_BBPB ENDS
cab_MEDIACHECK STRUC ; estructura para MEDIA CHECK
DB (TYPE cab_PETICION) DUP (?)
media_descrip DB ? ; descriptor de medio
cambio DB ? ; 1: no cambiado, 0FFh:s, 0:?
cab_MEDIACHECK ENDS
cab_READ_WRITE STRUC
DB (TYPE cab_PETICION) DUP (?)
DB ? ; descriptor de medio
transfer_desp DW ? ; direccin de transferencia
transfer_segm DW ?
transfer_sect DW ? ; n de sectores a transferir
transfer_sini DW ? ; primer sector a transferir
cab_READ_WRITE ENDS
; ************ Disco virtual: inicio del rea residente.
_PRINCIPAL SEGMENT
ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
DD -1 ; encadenamiento con otros drivers
tipo_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 estrategia
DW interrupcion ; rutina de interrupcin
DB 1 ; nmero de unidades
; ------------ Variables y tablas de datos globales fijas. Estas
; variables no sern 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 instalacin, QEMM copia en
; memoria convencional los primeros 18 bytes de
; la cabecera, entre los que est esta palabra,
; actualizndola. Pese a que la cadena de
; dispositivos del sistema pasa por la memoria
; convencional en este caso, esta variable nos
; permite conocer la direccin 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: an 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 pgina EMS (si se emplea)
ems_paginai DW ? ; segmento alternativo
ems_pagni DB ? ; n de pgina fsica alternativa
xms_driver LABEL DWORD ; direccin del controlador XMS, en el
xms_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
; gestin de memoria EMS.
; ------------ Variables internas de TURBODSK; su ubicacin podra
; cambiar en futuras versiones del programa.
pcab_peticion LABEL DWORD ; puntero a la cabecera de peticin
pcab_pet_desp DW 0
pcab_pet_segm DW 0
p_rutinas LABEL WORD ; tabla de rutinas del controlador
DW init
DW media_check
DW build_bpb
DW ioctl_input
DW read
DW read_nowait
DW input_status
DW input_flush
DW write
DW write_verify
DW output_status
DW output_flush
DW ioctl_output
DW 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 estndar del
; dos y al ser mayor de 0F7h no provoca
; mensajes extraos con antiguos CHKDSKs.
bpb LABEL BYTE ; Estos valores del BPB son arbitrarios:
bytes_sector DW 512 ; se inicializarn si se define el disco
sect_cluster DB 1 ; al instalar desde el CONFIG; en caso
sect_reserv DW 1 ; contrario, como son correctos, el DOS
num_fats DB 1 ; no tendr problemas para realizar sus
entradas_raiz DW 128 ; clculos internos iniciales al instalar
num_sect DW 128 ; el driver. En concreto, el tamao de
media_byte DB media ; sector influye de manera directa en el
sectores_fat DW 4 ; tamao de los buffers de disco del DOS.
fin_bpb EQU $
; ------------ Rutina de estrategia del disco virtual.
estrategia PROC FAR
MOV CS:pcab_pet_desp,BX
MOV CS:pcab_pet_segm,ES
RET
estrategia ENDP
; ------------ Rutina de interrupcin del disco virtual. TURBODSK,
; al igual que RAMDRIVE o VDISK, no define una pila
; interna. Es responsabilidad del DOS que sta tenga el
; tamao adecuado (con el disco en memoria XMS, el
; controlador XMS puede requerir hasta 256 bytes de
; pila). TURBODSK no consume ms de 64 bytes de pila en
; ningn momento, y slo alrededor de 48 antes de llamar
; al controlador XMS cuando se emplea esta memoria.
interrupcion PROC FAR
231 CONTROLADORES DE DISPOSITIVOS
XPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>
LDS BX,CS:pcab_peticion
MOV AL,[BX].orden ; AL = orden
MOV AH,0 ; AX = orden
CMP AL,CS:num_ordenes
JB orden_ok ; orden soportada
MOV AL,3 ; " desconocida (IOCTL INPUT)
orden_ok: CMP CS:tipo_soporte,AH
JNE no_test_fmt ; tipo_soporte distinto de 0
MOV AX,8102h ; disco no formateado: error
JMP exit_interr
no_test_fmt: SHL AX,1 ; orden = orden * 2
MOV SI,AX
XPUSH <BX,DS>
XOR BP,BP
MOV AX,100h
CALL CS:[SI+OFFSET p_rutinas] ; ejecutar orden
XPOP <DS,BX>
AND AH,AH
JNS exit_interr ; no hubo error (bit 15 = 0)
CMP AL,3
JE exit_interr ; error de orden desconocida
MOV [BX].transfer_sect,0 ; otro: movidos 0 sectores
exit_interr: MOV [BX].estado,AX
XPOP <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), includos los de segmento. A la
; entrada, BP=0 y AX=100h.
media_check: MOV AL,CS:cambiado ; condicin de disco cambiado
MOV CS:cambiado,AH ; de momento ya no cambiar ms
MOV [BX].cambio,AL
read_nowait: ; conjunto de rdenes con
input_status: ; tratamiento idntico
input_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 bpb
MOV [BX].bpb_cmd_segm,CS
JMP retorno_ok
ioctl_input: MOV AX,8103h ; orden no soportada
RET
remove: MOV AH,3 ; fin de funcin, indicar
RET ; controlador ocupado
nueva_int19 PROC ; Interceptar reinicializacin
.286
PUSHA
XPUSH <DS,ES> ; Esto es una interrupcin
XOR AX,AX
MOV ES,AX
CMP AL,CS:tipo_soporte ; Disco formateado?
JE no_lib ; no
MOV CS:tipo_soporte,AL ; s: anularlo
CALL procesa_io ; CF=1: liberar memoria EMS/XMS
no_lib: LEA SI,ant19off
MOV DI,64h ; desplazamiento de INT 19h
PUSH CS
POP DS
CLI
MOVSW
MOVSW
XPOP <ES,DS>
POPA
DB 0EAh ; cdigo de JMP FAR SEG:OFF
ant19off DW ?
ant19seg DW ?
.8086
nueva_int19 ENDP
read: INC BP ; indicar lectura (BP=1)
write: ; escritura (BP=0)
write_verify:
init_io PROC ; preparar registros E/S
LES DI,DWORD PTR [BX].transfer_desp ; * direc. ES:DI
LDS AX,DWORD PTR [BX].transfer_sect ; n sectores AX
MOV BX,DS ; 1 sector DS indefinido!
io_proc: MOV SI,CS:bytes_sector
ADD AX,BX
JNC io_ok? ; ltimo sector < 65536
io_no_ok: MOV AX,8108h ; sector no encontrado
RET
io_ok?: CMP AX,CS:num_sect
JA io_no_ok ; sector final fuera!
SUB AX,BX
MUL SI ; DX(CF):AX = tamao bloque
RCR AX,1 ; CF:AX/2 -> AX = palabras
MOV CX,DI
NEG CX ; 10000h-CX: CF=1 si CX<>0
CMC ; CF:CX bytes hasta fin de
RCR CX,1 ; segmento = (10000h-DI)/2
CMP AX,CX
JAE io_cx_ok
MOV CX,AX ; * tamao: CX palabras
io_cx_ok: JCXZ io_no_ok ; CX=0 si DI=0FFFFh (fatal)
MOV AX,BX ; sector inicial
MUL SI ; * desplazamiento en DX:AX
CLC ; no reinicializando!
init_io ENDP
; ------------ Area residente dependiente del tipo de memoria empleada
; por el disco. La rutina instalada por defecto es la ms
; 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, convendra medirlas por si
; acaso la de memoria EMS deja de ser la ms larga...
procesa_io EQU $
; ---- La rutina de gestin de memoria EMS transfiere
; bloques de hasta 16Kb de una vez. Intenta mapear
; en la pgina fsica 0: si no puede, debido a un
; solapamiento con el buffer de transferencia del
; programa principal (si est tambin en memoria
; EMS), utiliza otra pgina 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 prrafos
; (16400 bytes, 16 para redondeo) no hay problema.
; Ante un solapamiento se procede a restaurar el
; contexto de las pginas mapeadas, antes y
; despus de la transferencia, para poder acceder
; a la memoria expandida donde est el buffer del
; programa principal.
procesa_ems PROC
JNC no_emslib
MOV DH,45h ; sistema reinicializando:
CALL llama_EMM ; liberar memoria EMS
RET
no_emslib: MOV SI,DX ; preservar DX
MOV DH,47h
CALL llama_EMM ; DH=47h -> salvar contexto EMS
MOV DX,SI ; recuperar DX
MOV BX,4000h ; tamao de pgina (16 Kb)
DIV BX ; AX = 1 pgina EMS a mapear
MOV SI,DX ; offset relativo en 1 pgina
procesa_pag: PUSH CX ; **
MOV BX,DI
MOV CL,4
SHR BX,CL ; bytes del offset -> prrafos
MOV CX,ES
ADD BX,CX ; AX = segmento de datos
MOV CX,CS:ems_pagina0
MOV DS,CX
XOR DL,DL ; intentar emplear pgina 0
SUB BX,CX
JNC rpos
NEG BX ; valor absoluto
rpos: CMP BX,401h ; distancia respecto pgina EMS
JAE no_conflicto ; ms de 16 Kb: no solapamiento
CALL copia_contexto ; est CX apilado
MOV DS,CS:ems_paginai
MOV DL,CS:ems_pagni ; usar pgina alternativa
OR BP,8000h ; indicar su uso
no_conflicto: POP CX ; * pila totalmente equilibrada
MOV BX,AX
MOV DH,44h ; DL = 0 2 (pgina fsica)
CALL llama_EMM ; DH = 44h -> mapear pgina EMS
XPUSH <CX,SI> ; ++
SUB SI,4000h
NEG SI ; SI = 4000h - SI: resto
SHR SI,1 ; bytes -> palabras
CMP CX,SI
JB cx_ok ; no ocupada toda la pgina
MOV CX,SI
cx_ok: POP SI ; + SI=desplazamiento relativo
CLD
POP BX ; + palabras restantes
SUB BX,CX ; descontar las que se movern
PUSH BX ; * volver a apilar el viejo CX
CALL coloca_regs
CMP CS:cpu386,ON ; 386 o superior?
JNE trans_16bit
.386
PUSHAD
SHR CX,1 ; n palabras de 32 bit a mover
JCXZ transferido ; evitar desgracia
XOR EAX,EAX ; asegurar no violacin
DEC AX ; de segmento-64K
AND ECX,EAX ; EAX = 0FFFFh
AND ESI,EAX
AND EDI,EAX
REP MOVSD ; transferencia ultrarrpida
transferido: POPAD ; POPAD falla en muchos 386
.8086
NOP ; arreglar fallo de POPAD
ADD CX,CX
ADD DI,CX ; simular cambio normal de DI
ADD SI,CX ; y de SI
JMP fin_trans
trans_16bit: REP MOVSW ; mover palabras de 16 bit
fin_trans: CALL coloca_regs
AND BP,BP ; se us pgina alternativa?
JNS ahorra_ms
CALL copia_contexto ; est CX apilado
AND BP,1 ; de momento, no se usar ms
ahorra_ms: POP CX ; **
JCXZ fin_leer ; no quedan ms palabras
INC AX ; prxima pgina EMS
XOR SI,SI ; ahora desde inicio pgina EMS
JMP procesa_pag
fin_leer: MOV DH,48h
CALL llama_EMM ; DH=47h restaurar contexto EMS
MOV AX,100h ; no hubo problemas
RET
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 tambin (devuelve 810Ch).
llama_EMM PROC
XPUSH <AX,BX,CX,BP>
MOV AX,DX ; funcin en AX
llama_denuevo: MOV DX,CS:mem_handle ; handle EMS
XPUSH <AX,BX>
INT 67h ; llamar al EMM
MOV CL,AH
XPOP <BX,AX>
AND CL,CL
JZ llama_ok ; adems, ZF = 1
CMP CL,82h
JE llama_denuevo ; intentarlo hasta que funcione
llama_ok: XPOP <BP,CX,BX,AX>
JNE ret_atras
RET
ret_atras: POP AX ; sacar direccin de retorno
MOV AX,810Ch ; error de anomala general
RET ; retornar dos niveles atrs
llama_EMM ENDP
; ---- Cuidado!: esta rutina debe ser invocada siempre
; con CX (y slo CX) apilado: recarga CX desde la
; pila y corrompe BX dejando an en la pila CX.
copia_contexto PROC
XPOP <BX,CX> ; equilibrar pila a llama_EMM
232 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV DH,48h
CALL llama_EMM ; restaurar contexto EMS
MOV DH,47h
CALL llama_EMM ; preservarlo de nuevo
PUSH CX
JMP BX ; ms rpido que PUSH BX/RET
copia_contexto ENDP
coloca_regs PROC ; invertir sentido?
TEST BP,1
JNZ colocados
XCHG SI,DI ; escritura: invertir sentido
XPUSH <DS,ES>
XPOP <DS,ES>
colocados: RET
coloca_regs ENDP
tam_proc_ems EQU $-OFFSET procesa_ems ; tamao de esta rutina
; <<< Fin del cdigo residente del disco virtual >>>
; ************ Instalacin (invocada desde CONFIG.SYS).
init PROC
MOV CS:modo,CONFIG ; ejecutando desde CONFIG
CALL obtDosVer ; obtener versin del DOS
LEA AX,retorno_ok
MOV CS:p_rutinas,AX ; anular rutina INIT
INC CS:tipo_soporte ; 0: disco no formateado
MOV CS:cs_tdsk,CS ; inicializar esa variable
CMP CS:dosver,300h ; DOS inferior al 3.0?
JAE dos_ok ; DOS 3.0+
AND CS:tipo_drive,0F7FFh ; ajustar atributos
MOV CS:num_ordenes,0Dh ; y nmero de rdenes
dos_ok: MOV SI,[BX].bpb_cmd_desp
MOV ES,[BX].bpb_cmd_segm ; ES:SI -> parmetros
MOV [BX].num_discos,1 ; una unidad de disco
LEA AX,bpb_ptr
MOV [BX].bpb_cmd_desp,AX
MOV [BX].bpb_cmd_segm,CS ; inicializado puntero BPB
CALL desvia_int19 ; controlar INT 19h
CALL inic_letra ; obtener letra de unidad
MOV BX,CS
MOV DS,BX ; DS: -> _PRINCIPAL
MOV BX,SI ; ES:BX -> parmetros
CALL salta_nombre ; buscar inicio parmetros
CALL procesar_param ; procesar parmetros
PUSH DS ;
POP ES ; ES: -> _PRINCIPAL
CMP param_b,ON
JNE pet_ayuda?
MOV AH,8 ; opcin /B
MOV DL,80h
PUSH ES
INT 13h ; n de discos duros?
POP ES
AND DL,3
JZ pet_ayuda? ; no existe disco duro
LEA AX,procesa_io
NEG AX
JMP bytes_res_ok ; no quedar residente
pet_ayuda?: CMP param_h,ON
JE fin_instalar ; piden ayuda
CALL max_sector ; obtener mayor sector
MOV BX,param_tsect
CMP BX,AX ; el nuestro es mayor?
JBE sect_def_ok ; no
MOV bytes_sector,BX ; s: ajustar BPB
sect_def_ok: CALL errores_config
TEST lista_err,ERROR0+ERROR1
JNZ fin_instalar ; algn error importante
CMP param_tdisco,0 ; se define disco ahora?
JE fin_instalar ; no: no hay ms que hacer
CALL mem_info ; evaluar memoria del PC
CMP tdisco,0 ; se reservar memoria?
JE fin_instalar ; no: no hay ms que hacer
CALL mem_reserva ; reservar memoria
JC fin_instalar ; fallo al reservarla
CALL test_CPU ; detectar 386 superior
CALL adaptar_param ; adaptar parmetros disco
CALL preparar_BPB ; BPB del nuevo disco
CALL prep_driver ; preparar el driver
CALL formatear_tdsk ; inic. BOOT, FAT y ROOT
fin_instalar: CALL info_disco ; informar sobre el disco
CMP tipo_soporte,2
JE res_largo ; se utiliza memoria EMS
CMP param_a,ON
JE res_largo ; se indic /A
CALL eval_xms
CALL eval_ems
CMP ems_kb,0
JE res_corto ; no hay memoria EMS
CMP xms_kb,0
JNE res_corto ; la hay, pero tambin XMS
res_largo: MOV AX,tam_proc_ems
MOV rutina_larga,ON ; dejar sitio a rutina EMS
JMP bytes_res_ok
res_corto: MOV AX,tam_proc_xms ; dejar sitio a XMS/conv.
MOV BX,tam_proc_con
CMP AX,BX
JAE bytes_res_ok
XCHG AX,BX
bytes_res_ok: LDS BX,CS:pcab_peticion
ADD AX,OFFSET procesa_io
MOV [BX].fin_resid_desp,AX ; reservar memoria para
MOV [BX].fin_resid_segm,CS ; las rutinas a usar
MOV AX,100h ; instalacin siempre Ok.
RET
init ENDP
; ------------ Redefinicin (invocada desde el AUTOEXEC.BAT o el DOS).
main PROC FAR
MOV CS:modo,AUTOEXEC ; ejecutando desde el DOS
CALL obtDosVer ; obtener versin del DOS
CALL gestionar_ram ; gestin de memoria
MOV AX,_PRINCIPAL ; programa de un segmento
MOV DS,AX ; DS: -> _PRINCIPAL
MOV BX,81h ; ES:BX lnea de rdenes
CALL procesar_param ; procesar parmetros
CMP param_h,ON
JE exit_instalar ; piden ayuda
PUSH DS
POP ES ; ES: --> _PRINCIPAL
CALL errores_Dos
TEST err_grave,0FFFFh
JNZ exit_instalar ; algn error grave
MOV ES,segm_tdsk ; ES: --> disco residente
CMP param_a,ON
JNE cabria_ems
CMP ES:rutina_larga,ON
JE cabria_ems ; cabe la rutina EMS
OR lista_err,ERROR2
cabria_ems: TEST lista_err,ERROR0+ERROR2 ; error sintaxis EMS?
JNZ exit_instalar ; s: no modificar disco
CMP param_tdiscof,ON
JNE exit_instalar ; no indicado nuevo tamao
CMP ES:tipo_soporte,0
JE cont_instalar ; no estaba formateado an
CALL desinstala ; liberar memoria ocupada
cont_instalar: CALL mem_info ; evaluar memoria del PC
CMP tdisco,0 ; se reservar memoria?
JE exit_instalar ; no: no hay ms que hacer
CALL mem_reserva ; reservar memoria
JC exit_instalar ; fallo reservando memoria
CALL test_CPU ; detectar 386 superior
CALL adaptar_param ; adaptar parmetros disco
CALL preparar_BPB ; BPB del nuevo disco
CALL relocalizar ; autoreubicacin de TDSK
CALL prep_driver ; preparar el driver
CALL formatear_tdsk ; BOOT, FAT y ROOT
exit_instalar: CALL info_disco ; informar sobre el disco
CMP tipo_soporte,3 ; memoria convencional?
JNE fin_no_res ; no usada
CALL renombrar_mcb ; cambiar nombre del MCB
MOV DX,6 ; usada: 96 bytes de PSP
MOV AX,3100h
INT 21h ; terminar residente
fin_no_res: CALL set_errorlevel ; preparar ERRORLEVEL
MOV AH,4Ch
INT 21h ; final normal
main ENDP
; ------------ Inicializar la variable con la versin del DOS
obtDosVer PROC
XPUSH <AX,BX,CX,DX>
MOV AH,30h
INT 21h
XCHG AH,AL
MOV CS:dosver,AX
XPOP <DX,CX,BX,AX>
RET
obtDosVer ENDP
; ------------ Determinar segmento del PSP, ltimo segmento de memoria
; y liberar espacio de entorno. Se modifica tambin el
; bloque de memoria de TDSK reducindolo a 96 bytes: esto
; provoca la creacin 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 despus (slo si hace falta memoria convencional)
; usando los servicios estndar del DOS.
gestionar_ram PROC
MOV CS:segm_psp,DS ; indicar segmento del PSP
MOV AX,DS:[2] ; segmento ms alto
MOV CS:top_ram,AX ; indicar tope de memoria
PUSH ES
MOV ES,DS:[2Ch] ; segmento del entorno
MOV AH,49h
INT 21h ; liberar rea de entorno
POP ES ; ES: -> PSP
MOV BX,6
MOV AH,4Ah ; hacer creer al DOS que
INT 21h ; TDSK ocupa slo 96 bytes
RET
gestionar_ram ENDP
; ------------ Leer los parmetros de la lnea de comandos (ES:BX).
; Se inicializan las correspondientes variables. En caso
; de error, se dejan a cero las variables y se acumula en
; lista_err un ERROR0 (error de sintaxis).
procesar_param PROC
CALL busca_param ; saltar delimitadores
JC fin_param ; no hay ms parmetros
CALL param_barra ; gestionar parmetro tipo "/A"
JC procesar_param ; era parmetro tipo "/A"
MOV param_tdisco,AX ; es numrico: tamao del disco
MOV param_tdiscof,ON ; parmetro de tamao indicado
p_param2: CALL busca_param
JC fin_param
CALL param_barra
JC p_param2
MOV param_tsect,AX ; tamao de sector
p_param3: CALL busca_param
JC fin_param
CALL param_barra
JC p_param3
MOV param_tdir,AX ; entradas al directorio
p_param4: CALL busca_param
JC fin_param
CALL param_barra
JC p_param4
MOV param_tcluster,AX ; tamao de cluster
p_param5: CALL busca_param
JC fin_param
CALL param_barra ; ltimas opciones posibles
JC p_param5
fin_param: CALL validacion ; validacin de parmetros
RET
procesar_param ENDP
param_barra PROC
CMP AX,"e/" ; indicado /E?
JNE p_exp1?
MOV param_e,ON
JMP p_barra_exit
p_exp1?: CMP AX,"a/" ; indicado /A?
JNE p_exp2?
p_exp: MOV param_a,ON
JMP p_barra_exit
p_exp2?: CMP AX,"x/" ; /A y /X son equivalentes
JE p_exp
CMP AX,"c/" ; indicado /C?
JNE p_ayuda?
MOV param_c,ON
JMP p_barra_exit
p_ayuda?: CMP AX,"h/" ; indicado /H?
JNE p_exit?
p_ayuda: MOV param_h,ON
JMP p_barra_exit
p_exit?: CMP AX,"?/" ; /H y /? son equivalentes
JE p_ayuda
233 CONTROLADORES DE DISPOSITIVOS
CMP AX,"m/" ; indicado /M?
JNE param_id?
MOV param_m,ON
JMP p_barra_exit
param_id?: CMP AX,"i/" ; indicado /I= o /I:?
JNE param_fats?
ADD BX,3
CMP BYTE PTR ES:[BX-1],=
JE p_id_ok
CMP BYTE PTR ES:[BX-1],:
JNE param_b_mal
p_id_ok: CALL obt_num ; leer cdigo telefnico
MOV param_i,ON
MOV codigo_tfno,AX
SUB BX,2
JMP p_barra_exit
param_fats?: CMP AX,"f/" ; indicado /F= o /F:?
JNE param_b?
ADD BX,3
CMP BYTE PTR ES:[BX-1],=
JE p_f_ok
CMP BYTE PTR ES:[BX-1],:
JNE param_b_mal
p_f_ok: CALL obt_num ; leer nmero de FATs
MOV param_f,AX
SUB BX,2
JMP p_barra_exit
param_b?: CMP AX,"b/" ; indicado /B?
JNE param_unidad?
MOV param_b,ON
JMP p_barra_exit
param_unidad?: CMP AH,: ; parmetro de unidad?
JNE param_num?
AND AL,255-32 ; poner en maysculas
MOV param_unidad,AL
JMP p_barra_exit
param_num?: CMP AL,/
JNE param_num ; puede ser nmero
param_b_mal: OR lista_err,ERROR0
param_num: CALL obt_num ; es parmetro numrico: leerlo
CLC ; no es parmetro barrado
RET
p_barra_exit: ADD BX,2 ; saltar este parmetro
STC ; es parmetro barrado
RET
param_barra ENDP
validacion PROC
MOV AX,0FFFFh
CMP AX,param_tdisco ; nmeros correctos?
JE sintax_err
CMP AX,param_tsect
JE sintax_err
CMP AX,param_tdir
JE sintax_err
CMP AX,param_tcluster
JE sintax_err
CMP param_tdisco,0
JE valida_tsect ; no indicado tamao (o 0)
CMP param_tdisco,8
JB sintax_err
CMP param_tdisco,65534
JA sintax_err
valida_tsect: MOV AX,param_tsect
CMP AX,0
JE valida_tclus ; no indicado tamao de sector
CMP AX,32
JE valida_tclus
CMP AX,64
JE valida_tclus
CMP AX,128
JE valida_tclus
CMP AX,256
JE valida_tclus
CMP AX,512
JE valida_tclus
CMP AX,1024
JE valida_tclus
CMP AX,2048
JNE sintax_err
valida_tclus: CMP param_tcluster,256
JAE sintax_err ; debe estar entre 0..255
CMP param_f,1
JB pf_a1 ; /F=1 /F=2 exclusivamente
CMP param_f,2 ; si no, forzarlo y perdonar
JBE fin_validar
MOV param_f,2
JMP fin_validar
pf_a1: MOV param_f,1
JMP fin_validar
sintax_err: MOV param_tdiscof,OFF ; no definir disco ahora
XOR AX,AX
MOV param_tdisco,AX
MOV param_tsect,AX
MOV param_tdir,AX
MOV param_tcluster,AX
OR lista_err,ERROR0 ; aviso de error de sintaxis
fin_validar: RET
validacion ENDP
salta_nombre PROC ; saltar nombre del driver en
MOV AL,ES:[BX] ; lnea de rdenes del CONFIG
INC BX
CMP AL,
JE fin_nombre
CMP AL,9
JE fin_nombre
CMP AL,0Dh
JE fin_nombre
CMP AL,0Ah
JE fin_nombre
AND AL,AL
JZ fin_nombre ; necesario para DOS 2.x
JMP salta_nombre
fin_nombre: DEC BX
RET
salta_nombre ENDP
busca_param PROC ; saltar delimitadores
DEC BX
p_delimit: INC BX
MOV AX,ES:[BX]
CMP AL,
JE p_delimit ; espacio en blanco
CMP AL,9
JE p_delimit ; tabulador
CMP AL,13
JE p_final ; CR LF indican el final
CMP AL,10
JE p_final
OR AX," " ; poner en minsculas
CLC
RET
p_final: STC ; se acabaron los parmetros
RET
busca_param ENDP
obt_num PROC ; leer nmero: devolver 65535
XPUSH <CX,DX,SI> ; si hay error
XOR AX,AX ; nmero en proceso de creacin
otro_digito: MOV CL,ES:[BX]
CMP CL,0
JB no_digito
CMP CL,9
JBE digito_ok
no_digito: CMP CL, ; posibles delimitadores...
JE fin_num
CMP CL,9
JE fin_num
CMP CL,13
JE fin_num
CMP CL,10
JE fin_num
CMP CL,/
JE fin_num
JMP num_incorr
digito_ok: XOR DX,DX
MOV SI,10
MUL SI ; AX = AX * 10
JC num_incorr
XOR CH,CH
SUB CL,0
ADD AX,CX ; AX = AX + dato
JC num_incorr
INC BX
JMP otro_digito
num_incorr: MOV AX,65535 ; indicar valor incorrecto
fin_num: XPOP <SI,DX,CX>
RET
obt_num ENDP
; ------------ Detectar errores que se pueden producir slo en la
; lnea de comandos.
errores_Dos PROC
PUSH ES
CMP dosver,200h ; necesario DOS 2.x+
JAE existe_tdsk?
OR err_grave,ERROR0 ; error de DOS incorrecto
JMP fin_err_Dos
existe_tdsk?: CALL reside_tdsk? ; instalado TURBODSK?
CMP segm_tdsk,0
JNE busca_unidad ; ya instalado
OR err_grave,ERROR1 ; error: TURBODSK no instalado
JMP fin_err_Dos
busca_unidad: MOV ES,segm_tdsk ; ES: -> disco virtual
CMP param_unidad,0
JE disco_defecto ; no se indic letra de unidad
CALL obtener_segm ; segmento del TDSK indicado
JC fin_err_Dos ; fallo (no es unidad TDSK)
disco_defecto: CALL max_sector ; obtener mayor sector
MOV BX,param_tsect
CMP BX,AX
JBE fin_err_Dos ; tamao de sector correcto
OR lista_err,ERROR3 ; el tamao no definible ahora
MOV param_tsect,0 ; ignorar tamao indicado
fin_err_Dos: CALL test32Mb
CALL testWin
POP ES
RET
errores_Dos ENDP
; ------------ Detectar errores que se pueden producir slo desde
; el CONFIG.SYS
errores_config PROC
CMP param_unidad,0
JE no_unidad
OR lista_err,ERROR1
no_unidad: CMP param_c,ON
JNE fin_err_con
OR lista_err,ERROR1
fin_err_con: CALL test32Mb
RET
errores_config ENDP
; ------------ Preparar valor de ERRORLEVEL para el retorno.
set_errorlevel PROC
MOV AL,255
TEST err_grave,ERROR1 ; TDSK no instalado?
JNZ fin_cod_ok
DEC AL
TEST err_grave,ERROR2 ; unidad incorrecta?
JNZ fin_cod_ok
DEC AL
TEST err_grave,ERROR3 ; dentro de Windows?
JNZ fin_cod_ok
DEC AL
TEST lista_err,ERROR0 ; error de sintaxis
JNZ fin_cod_ok
CMP param_h,ON ; ayuda: handle desconocido
JE fin_cod_ok
MOV AL,BYTE PTR ES:mem_handle ; handle XMS/EMS
CMP ES:tipo_soporte,0
JNE fin_cod_ok
MOV AL,0 ; disco no formateado
fin_cod_ok: RET
set_errorlevel ENDP
; ------------ Obtener mayor tamao de sector definido en el sistema.
max_sector PROC
XPUSH <BX,ES>
MOV AH,52h
INT 21h ; Get List of Lists
ADD BX,10h
CMP CS:dosver,30Ah
JAE psect_ok
INC BX ; DOS anterior al 3.1
psect_ok: MOV AX,ES:[BX] ; mayor tamao de sector
XPOP <ES,BX> ; definido por cualquier disp.
RET
max_sector ENDP
; ------------ Si el disco es de ms de 32 Mb, comprobar si el sector
234 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
; es de al menos 1024 bytes.
test32Mb PROC
CMP param_tdisco,32768
JBE fin32mb
CMP param_tsect,1024
JAE fin32mb
OR lista_err,ERROR15 ; sector de menos de 1024
MOV param_tdisco,32768 ; evitar fallo posterior
fin32mb: RET
test32Mb ENDP
; ------------ Desde Windows, no se permite redefinir el disco.
testWin PROC
CMP param_tdiscof,ON
JNE fin_testWin ; no redefinido el disco
CMP dosver,300h
JB fin_testWin ; no buscar Windows en DOS 2.x
MOV AX,1600h
INT 2Fh
AND AL,AL ; Windows en modo extendido?
JZ noWinEnh
CMP AL,80h ; Windows en modo extendido?
JE noWinEnh
siWin: OR err_grave,ERROR3 ; estamos dentro de Windows
JMP fin_testWin
noWinEnh: MOV AX,4680h
INT 2Fh
AND AX,AX
JZ siWin ; Windows en modo real/estndar
fin_testWin: RET
testWin ENDP
; ------------ Verificar la presencia en memoria de TURBODSK. Se
; inicializa segm_tdsk y letra_unidad indicando dnde
; reside el primer dispositivo TURBODSK de todos los que
; puede haber instalados. La letra de la unidad se halla
; del propio TDSK residente, para evitar conflictos con
; programas que manipulan ilegalmente la lista de
; unidades, del tipo de Stacker o Smartdrive.
reside_tdsk? PROC
XPUSH <AX, SI>
CALL lista_discos
LEA SI,area_trabajo-4
busca_final: ADD SI,4
CMP WORD PTR [SI],0
JNE busca_final ; ir al final de la tabla
busca_tdsk: SUB SI,4
CMP SI,OFFSET area_trabajo
JB fin_busca ; no reside (segm_tdsk = 0)
CMP BYTE PTR [SI+3],1
JNE busca_tdsk
MOV AX,[SI] ; encontrada unidad TURBODSK
MOV segm_tdsk,AX
PUSH DS
MOV DS,AX
MOV AL,letra_unidad ; con esta letra de unidad
POP DS
MOV 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 PROC
CALL lista_discos
LEA SI,area_trabajo-4
busca_ultimo: ADD SI,4
CMP WORD PTR [SI],0
JNE busca_ultimo ; realmente, el primero
recorre_dsks: SUB SI,4
CMP SI,OFFSET area_trabajo
JB tdsk_no_hay
CMP BYTE PTR [SI+3],1
JNE recorre_dsks
PUSH DS
MOV DS,[SI]
MOV AL,letra_unidad ; unidad del TDSK residente
POP DS
CMP AL,param_unidad ; disco TDSK: es el buscado?
JNE recorre_dsks
MOV letra_unidad,AL ; inicializar letra de unidad
MOV AX,[SI]
MOV segm_tdsk,AX ; inicializar segmento
MOV ES,AX
CLC
RET
tdsk_no_hay: OR err_grave,ERROR2 ; unidad indicada no es TDSK
STC
RET
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 cancelacin de la
; tarea virtual, sta queda permanentemente ocupada hasta
; un reset fro del sistema, sin poder ser aprovechada
; por los dems 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 cdigo de 286, por lo que se
; chequea la presencia de este procesador.
desvia_int19 PROC
XPUSH <BX,DS,ES>
MOV BX,CS
MOV DS,BX
CALL test_CPU
CMP cpu286,ON
JNE fin_desvia19 ; no es 286 superior
MOV AX,3519h
INT 21h ; ES:BX anterior INT 19h
MOV ant19off,BX
MOV ant19seg,ES
LEA DX,nueva_int19
MOV AX,2519h
INT 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 slo desde CONFIG.SYS con DS:BX
; apuntando a la cabecera de peticin de la orden INIT.
inic_letra PROC
XPUSH <AX,BX,SI,DS>
MOV AL,[BX].nuevo_disco ; unidad en DOS 3.0+
ADD AL,A
PUSH CS
POP DS ; DS -> _PRINCIPAL
CMP dosver,300h
JAE letra_ok
CALL lista_discos ; hallar unidad en DOS 2.x
LEA SI,area_trabajo
XOR AL,AL ; cuenta de discos
cuenta_discos: ADD AL,[SI+2]
ADD SI,4
CMP WORD PTR [SI],0
JNE cuenta_discos
ADD AL,A
letra_ok: MOV letra_unidad,AL ; guardar letra de unidad
XPOP <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 nmero 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 sealiza un segmento igual a 0.
lista_discos PROC
XPUSH <AX,BX,CX,DX,SI,DI,ES>
MOV AH,52h ; "Get list of lists"
INT 21h ; obtener puntero en ES:BX
MOV CX,17h ; supuesto DOS 2.x
CMP dosver,300h
JB pdisp_ok
MOV CX,28h ; supuesto DOS 3.0x
CMP dosver,30Ah
JB pdisp_ok
MOV CX,22h ; versiones del DOS superiores
pdisp_ok: ADD BX,CX
LEA DI,area_trabajo-4 ; tabla de dispositivos-4
disp_otro: ADD DI,4
disp_skip: LES BX,ES:[BX] ; siguiente dispositivo
CMP BX,-1
JE disp_fin
TEST BYTE PTR ES:[BX+5],80h
JNZ disp_skip ; es dispositivo de caracteres
MOV CL,ES:[BX+10] ; es de bloques
MOV [DI],ES ; anotar direccin
MOV [DI+2],CL
MOV BYTE PTR [DI+3],0 ; de momento, no es TDSK
PUSH DI
LEA SI,id_tdsk ; identificacin de TURBODSK
MOV DI,SI
MOV CX,5
CLD
REP CMPSB ; es TURBODSK?
POP DI
JNE disp_otro ; es de bloques, pero no TDSK
MOV AX,ES:cs_tdsk ; segmento real de TDSK
MOV [DI],AX ; corregir direccin en tabla
INC BYTE PTR [DI+3] ; indicar dispositivo TDSK
JMP disp_otro ; buscar hasta completar tabla
disp_fin: MOV WORD PTR [DI],0 ; final de la lista
XPOP <ES,DI,SI,DX,CX,BX,AX>
RET
lista_discos ENDP
; ------------ Liberar la memoria ocupada por un TURBODSK residente.
desinstala PROC
MOV DX,ES:mem_handle
MOV AL,ES:tipo_soporte
DEC AL
JZ libera_ext ; liberar memoria extendida
DEC AL
JZ libera_exp ; liberar memoria expandida
PUSH ES
MOV ES,DX
MOV AH,49h ; liberar memoria convencional:
INT 21h
POP ES
PUSH ES
PUSHF ; condicin de error
MOV ES,ES:tdsk_psp ; liberar PSP residente
MOV AH,49h
INT 21h
PUSHF
CMP dosver,31Eh
JA mcb_ok ; DOS 3.31+: el MCB es correcto
MOV AX,ES
DEC AX
MOV ES,AX
MOV DI,8
MOV CX,DI
CLD
MOV AL,
REP STOSB ; hasta DOS 3.30 borrar nombre
mcb_ok: POPF
JNC lib_con_ok? ; liberado correctamente
POPF
POP ES
STC ; ha habido fallo
JMP desinstalado
lib_con_ok?: POPF ; recuperar condicin de error
POP ES
JMP desinstalado
libera_ext: MOV AH,0Ah
CALL ES:xms_driver
CMP AX,1
JE desinstalado ; xito al liberar memoria XMS
STC
JMP desinstalado ; fallo
libera_exp: MOV AH,45h
INT 67h
CMP AH,0
JE desinstalado
CMP AH,82h ; EMM ocupado?
JE libera_exp
STC ; fallo al liberar memoria EMS
desinstalado: MOV ES:tipo_soporte,0 ; disco no formateado
JNC desins_ok
OR lista_err,ERROR14 ; fallo al liberar memoria
235 CONTROLADORES DE DISPOSITIVOS
STC
desins_ok: RET
desinstala ENDP
; ------------ Determinar la configuracin del sistema: tipos de
; memoria y cantidad de la misma. Se indica en tdisco
; un valor 0 si no se define ahora el disco, sea cual sea
; el motivo del fallo, y se actualiza la variable que
; indica los mensajes de error y advertencia a imprimir.
mem_info PROC
MOV tdisco,0 ; ley de Murphy
CALL eval_xms ; inicializar xms_kb
CALL eval_ems ; inicializar ems_kb
CALL eval_con ; inicializar con_kb
MOV AX,param_tdisco ; cantidad de memoria necesaria
CMP param_a,ON
JNE no_ems ; no solicitan memoria EMS
MOV BX,ems_kb ; solicitan memoria EMS...
AND BX,BX
JNZ usara_ems
OR lista_err,ERROR7 ; no hay memoria EMS disponible
JMP mem_infoado
usara_ems: CMP AX,BX
JBE usar_ems ; piden algo razonable
MOV AX,BX
OR lista_err,ERROR4 ; rebajado el tamao
usar_ems: MOV tdisco,AX
MOV tipo_soporte,2 ; indicar memoria expandida
JMP mem_infoado
no_ems: CMP param_e,ON
JNE no_xms ; no solicitan memoria XMS
MOV BX,xms_kb ; solicitan memoria XMS...
AND BX,BX
JNZ usara_xms
OR lista_err,ERROR6 ; no hay memoria XMS disponible
JMP mem_infoado
usara_xms: CMP AX,BX
JBE usar_xms ; piden algo razonable
MOV AX,BX
OR lista_err,ERROR4 ; rebajado el tamao
usar_xms: MOV tdisco,AX
MOV tipo_soporte,1 ; indicar memoria extendida
JMP mem_infoado
no_xms: CMP param_c,ON
JNE no_con ; no solicitan memoria conv.
forzar_con: MOV BX,con_kb ; solicitan memoria conv. ...
AND BX,BX
JNZ usara_con
OR lista_err,ERROR10 ; no hay memoria conv. libre
JMP mem_infoado
usara_con: CMP AX,BX
JBE usar_con ; piden algo razonable
MOV AX,BX
OR lista_err,ERROR4 ; rebajado el tamao
usar_con: MOV tdisco,AX
MOV tipo_soporte,3 ; indicar memoria convencional
JMP mem_infoado
no_con: CMP AX,xms_kb ; no indicado tipo de memoria
JBE usar_xms ; intentar emplear memoria XMS
CMP ES:rutina_larga,ON
JE valdria_ems
MOV BX,xms_kb
CMP BX,0 ; imposible usar EMS
JNE usara_xms ; queda algo de XMS
JMP usar_con?
valdria_ems: MOV BX,ems_kb
CMP AX,BX
JA nv_ems
JMP usar_ems ; emplear memoria EMS
nv_ems: MOV BX,ems_kb
OR BX,xms_kb
JZ usar_con? ; no hay un pice de XMS ni EMS
OR lista_err,ERROR4 ; rebajado el tamao solicitado
MOV AX,xms_kb
CMP AX,ems_kb
JAE usar_xms ; hay ms o igual XMS que EMS
MOV AX,ems_kb
JMP usar_ems ; hay algo de EMS (ms que XMS)
usar_con?: CMP modo,AUTOEXEC
JE forzar_con ; slo se puede usar mem. conv.
OR lista_err,ERROR5 ; ho hay memoria EMS ni XMS
mem_infoado: RET
mem_info ENDP
; ---- Calcular memoria extendida disponible
eval_xms PROC
PUSH ES
MOV AX,352Fh
INT 21h ; direccin de INT 2Fh en ES:BX
MOV AX,ES
AND AX,AX
JZ xms_ok ; apunta a 0000:XXXX (DOS 2.x)
MOV AX,4300h
INT 2Fh
CMP AL,80h ; hay controlador XMS?
JNE xms_ok
MOV AX,4310h ; obtener su direccin
INT 2Fh
MOV xms_segm,ES
MOV xms_desp,BX
MOV AH,8
CALL xms_driver ; preguntar memoria libre
AND AX,AX
JNZ xms_kb_ok ; no hubo fallo
CMP BL,0A0h
JE xms_kb_ok ; asignada ya toda la memoria
TEST BL,80h
JZ xms_kb_ok ; no hay memoria XMS disponible
OR lista_err,ERROR8 ; fallo real del controlador
xms_kb_ok: CMP AX,8 ; mayor bloque XMS disponible
JB xms_ok
MOV xms_kb,AX ; mnimo necesario: 8 Kb
xms_ok: POP ES
RET
eval_xms ENDP
; ---- Calcular memoria expandida disponible. Si la
; versin del EMM es 4.0 o superior, las pginas
; de memoria expandida pueden no ser contiguas:
; buscar una que diste 32 Kb de la pgina 0.
eval_ems PROC
PUSH ES
MOV AX,3567h
INT 21h ; vector de INT 67h en ES:BX
MOV DI,10
LEA SI,emm_id
MOV CX,8
CLD
REP CMPSB ; instalado controlador EMS?
JE ems_existe
JMP ems_ok
ems_existe: MOV CX,8000h ; n de intentos prudente
emm_llama: MOV AH,40h
INT 67h
AND AH,AH
JZ emm_responde
CMP AH,82h
LOOPE emm_llama
emm_fatal: OR lista_err,ERROR9 ; fallo del EMM
JMP ems_ok
emm_responde: MOV AH,41h
INT 67h
AND AH,AH
JZ emm_pag_ok
CMP AH,82h
JE emm_responde ; reintentar (EMM ocupado)
JMP emm_fatal
emm_pag_ok: MOV ems_pagina0,BX ; inicializar pgina EMS
ADD BX,0C00h
MOV ems_paginai,BX
MOV ems_pagni,3 ; pgina alternativa: la 3
MOV AH,46h
INT 67h ; obtener versin del EMM
CMP AL,40h
JB emm_obt_kb ; versin anterior a la 4.0
MOV ems4,ON
emm_obt_pag: XPUSH <ES,DS>
POP ES
MOV AX,5800h ; obtener direccin de pginas
LEA DI,area_trabajo
INT 67h
POP ES
AND AH,AH
JZ emm_pags_ok
CMP AH,82h
JE emm_obt_pag
JMP emm_fatal
emm_pags_ok: XOR DX,DX
CALL emm_busca_pag ; buscar pgina 0
JC emm_fatal
MOV ems_pagina0,BX
ems_busca_i: INC DX ; buscar la siguiente
CMP DX,5 ; la 5 y siguientes no valen
JE emm_fatal ;
CALL emm_busca_pag ;
JC emm_fatal ; > > <-- pg i
MOV ems_paginai,BX ;0C00h 32
MOV ems_pagni,DL ; p Kb
SUB BX,ems_pagina0 ; rra
JNC bxpositivo ; fos >
NEG BX ;
bxpositivo: CMP BX,0C00h ; > <-- pg 0
JB ems_busca_i ; no distan 32 Kb: buscar otra
emm_obt_kb: MOV AH,42h
INT 67h
AND AH,AH
JZ emm_kb_ok
CMP AH,82h
JE emm_obt_kb
JMP emm_fatal
emm_kb_ok: MOV CL,4
SHL BX,CL ; pginas EMS disponibles
MOV ems_kb,BX ; Kb EMS disponibles (0,16,...)
ems_ok: POP ES
RET
eval_ems ENDP
emm_busca_pag PROC ; buscar pgina n DX (EMS 4.0)
LEA SI,area_trabajo
PUSH CX
emm_otra_pag: LODSW
MOV BX,AX ; BX = segmento de la pgina
LODSW ; AX = n de la pgina
CMP AX,DX
JE hallada_pag
LOOP emm_otra_pag
STC
hallada_pag: POP CX
RET
emm_busca_pag ENDP
; ---- Calcular el tamao del mayor bloque de memoria
; convencional disponible. Como mnimo se dejarn
; 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 PROC
CMP modo,AUTOEXEC ; se ejecuta desde el DOS?
JNE conv_ok ; no, desde el config
MOV AH,48h
MOV BX,0FFFFh ; pedir 1 Mb al DOS (fallar)
INT 21h
MOV DX,BX ; tamao del mayor bloque
MOV CL,6
SHR BX,CL ; BX = Kb del mayor bloque
SUB BX,128 ; restar 128 Kb
JC conv_ok ; no quedan ni 128 Kb
CMP BX,8
JB conv_ok ; no quedan siquiera 8 Kb
MOV con_kb,BX
MOV BX,DX ; tamao del mayor bloque
MOV AH,48h
PUSH BX
INT 21h ; localizarlo (AX=segmento)
POP BX
XPUSH <ES,AX> ; preservar ES y segmento (AX)
ADD AX,BX ; aadir longitud
SUB AX,1024/16*64 ; restar 64 Kb
MOV segm_reubicar,AX ; segmento de autoreubicacin
POP ES ; recuperar segmento del bloque
MOV AH,49h
INT 21h ; liberarlo
POP ES ; recuperar ES
conv_ok: RET
eval_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 creaccin de otro
; en EMS (caso de QEMM386 y otros emuladores de EMS) y en
236 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
; ese caso se le renombra, para mejorar la informacin de
; los programas de diagnstico.
mem_reserva PROC
MOV AL,tipo_soporte ; tipo de memoria empleada
DEC AL
JZ mem_r_xms ; 1: memoria extendida XMS
DEC AL
JZ mem_r_ems ; 2: memoria expandida EMS
MOV CL,6
MOV BX,tdisco ; 3: memoria convencional
SHL BX,CL
MOV AH,48h
INT 21h
MOV mem_handle,AX ; segmento del disco virtual
MOV BX,segm_psp
MOV tdsk_psp,BX ; inicializar esta variable
RET
mem_r_xms: CMP ems4,ON
JNE skip_lst_hndl
LEA BX,area_trabajo
CALL lista_handles ; EMS 4.0+: listado de handles
skip_lst_hndl: MOV AH,9
MOV DX,tdisco
CALL xms_driver ; pedir memoria XMS
AND AX,AX
JNZ mem_rda_xms
OR lista_err,ERROR8 ; fallo del controlador XMS
STC ; indicar error
mem_rda_xms: MOV mem_handle,DX
PUSHF ; preservar condicin de error
CMP ems4,ON
JNE skip_ren_hndl
CALL ren_handle ; en EMS 4.0+ renombrar handle
skip_ren_hndl: POPF
RET
mem_r_ems: MOV BX,tdisco
ADD BX,15
AND BL,11110000b ; redondear para arriba
MOV tdisco,BX
MOV CL,4
SHR BX,CL ; Kb -> n pginas de 16 Kb
MOV AH,43h
INT 67h ; pedir memoria EMS
AND AH,AH
JZ mem_rda_ems
OR lista_err,ERROR9 ; fallo del controlador EMS
STC ; indicar error
RET
mem_rda_ems: MOV mem_handle,DX
CMP ems4,ON
JNE nhandle_ok
CALL nombrar_hndl ; en EMS 4.0+ nombrar handle
nhandle_ok: CLC
RET
mem_reserva ENDP
ren_handle PROC ; detectar el handle EMS ligado
XPUSH <ES,DS> ; al handle XMS y renombrarlo
POP ES
LEA BX,area_trabajo[512]
CALL lista_handles ; crear nueva lista de handles
LEA SI,area_trabajo
LEA DI,area_trabajo[512]
MOV CX,256
CLD
REP CMPSW ; comparar con vieja lista
JE ren_hnld_fin
MOV DX,[DI-2] ; handle nuevo
CALL nombrar_hndl
ren_hnld_fin: POP ES
RET
ren_handle ENDP
lista_handles PROC ; crear en DS:BX una lista con
MOV CX,256 ; los 256 posibles handles
XOR DX,DX ; activos indicando los usados
listar_h: MOV AX,5300h
LEA DI,area_trabajo[tam_a_trabajo-8] ; zona no usada
XPUSH <BX,CX,DX>
INT 67h
XPOP <DX,CX,BX>
CMP AH,0
JE handle_usado
MOV WORD PTR [BX],0 ; error (handle no usado)
JMP lista_h
handle_usado: MOV [BX],DX ; anotar nmero de handle
lista_h: ADD BX,2
INC DX
LOOP listar_h
RET
lista_handles ENDP
nombrar_hndl PROC ; nombrar handle (EMS 4.0+)
MOV AX,5301h
LEA SI,nombre_tdsk
MOV BL,letra_unidad
MOV [SI+5],BL
INT 67h ; dar nombre al handle
RET
nombrar_hndl ENDP
; ------------ Detectar 286 y 386 o superior.
test_CPU PROC
PUSHF
POP AX
OR AH,70h ; intentar activar bit 12, 13 14
PUSH AX ; del registro de estado
POPF
PUSHF
POP AX
AND AH,0F0h
CMP AH,0F0h
JE fin_test_CPU ; es 8086 o similar
MOV cpu286,ON ; es 286 o superior
AND AH,70h ; 286 pone bits 12, 13 y 14 a cero
JZ fin_test_CPU ; es 286
MOV cpu386,ON ; 386 o superior
fin_test_CPU: RET
test_CPU ENDP
; ------------ Definir valores por defecto y adaptar los parmetros
; 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 ms de
; medio disco. Para determinar el tipo de FAT se halla el
; n de sectores libres del disco (llammoslo nsect),
; descontanto el sector de arranque y el directorio raiz;
; y se aplica la siguiente frmula, que devuelve el n de
; cluster ms alto del disco al considerar tambin la
; ocupacin 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
; ms alto del disco. Si ese nmero es 4086 o ms habr
; de utilizarse una FAT de 16 bits, recalculndose la
; frmula 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 nmero de cluster ms alto,
; ya que hay casos crticos en que una FAT12 no sirve
; pero al aplicar una FAT16 el nmero de clusters baja de
; nuevo de 4085 (debido al mayor consumo de disco de la
; FAT16) resultado de ello la asignacin de una FAT12,
; pese a que se reserva espacio para la de 16. Hay que
; considerar adems el caso de que el disco tenga 2 FAT.
adaptar_param PROC
MOV AX,tdisco ; en Kb
MOV BX,AX ; entradas de directorio propuestas
MOV CL,1 ; sectores por cluster propuestos
CMP AX,128 ; disco de 128 Kb o menos?
JBE prop_ok
MOV BX,128
CMP AX,512 ; disco de 512 Kb o menos?
JBE prop_ok
MOV BX,256
CMP AX,2042 ; disco de casi 2 Mb o menos?
JBE prop_ok
MOV CL,2 ; evitar FAT16
CMP AX,4084 ; disco de casi 4 Mb o menos?
JBE prop_ok
MOV CL,4 ; evitar FAT16 hasta 8 Mb
MOV BX,384
CMP AX,16384 ; disco de menos de 16 Mb?
JB prop_ok
MOV BX,512
prop_ok: CMP dosver,300h
JAE prop_valido
CMP AX,4084*2 ; en DOS 2.xx evitar FAT16
JB prop_valido
MOV CL,8
CMP AX,4084*4
JB prop_valido
MOV CL,16
CMP AX,4084*8
JB prop_valido
MOV CL,32
prop_valido: MOV tdir,BX
MOV tcluster,CL ; inicializar valores recomendados
MOV DX,1024 ; AX = tamao del disco en Kb
MUL DX ; DX:AX = bytes totales del disco
MOV CX,param_tsect
AND CX,CX
JNZ tsect_def ; se ha definido tamao de sector
tsect_rec: MOV CX,tsect ; tamao por defecto
tsect_def: CALL divCX
JNC nsect_ok ; menos de 65536 sectores: correcto
OR lista_err,ERROR11
JMP tsect_rec ; asumir por defecto y recalcular
nsect_ok: MOV tsect,CX
MOV numsect,AX
MOV BX,AX
SHR BX,1 ; BX = 1/2 del n total de sectores
MOV CX,param_tdir
AND CX,CX
JNZ tdir_def ; se ha definido n entradas
tdir_rec: MOV CX,tdir ; n por defecto
tdir_def: MOV AX,tsect
XOR DX,DX
MOV SI,32 ; 32 bytes = tamao entrada direct.
DIV SI ; AX n entradas direct. por sector
XCHG AX,CX
XOR DX,DX ; DX:AX = n de entradas
DIV CX ; CX = entradas en cada sector
AND DX,DX ; AX = n sectores del ROOT
JZ dir_ok?
INC AX ; redondear tamao de ROOT
dir_ok?: CMP AX,BX ; BX = 1/2 n sectores del disco
JB dir_ok
OR lista_err,ERROR12 ; directorio excesivo
JMP tdir_rec ; directorio por defecto
dir_ok: MOV sdir,AX
MUL tsect
MOV CX,32
CALL divCX
MOV tdir,AX ; optimizar tamao de directorio
MOV AX,512
XOR DX,DX
DIV tsect ; 512 / tamao de sector
MOV BL,tcluster
XOR BH,BH
MUL BX ; ajustar tamao de cluster
AND AL,AL
JZ propclus_ok
MOV tcluster,AL
propclus_ok: MOV BX,param_tcluster
AND BX,BX
JNZ tcluster_def ; se ha definido tamao de cluster
tcluster_rec: MOV BL,tcluster ; tamao por defecto
XOR BH,BH
tcluster_def: SHL BX,1
CMP BX,numsect ; cabe seguro un cluster?
JB tcluster_ok
tcluster_mal: OR lista_err,ERROR13 ; tamao de cluster incorrecto
JMP tcluster_rec
tcluster_ok: SHR BX,1
MOV AX,tsect
MUL BX ; DX:AX = tamao de cluster
JC tcluster_mal
CMP AX,31*1024
JA tcluster_mal ; cluster de ms de 31 Kb
MOV tcluster,BL ; sectores por cluster
MOV tamcluster,AX ; tamao de cluster
MOV CX,param_f ; considerar nmero de FATs
MOV nfats,CL
MOV SI,3
MOV CX,param_f
SHL SI,CL
SHR SI,1
CALL eval_clust ; obtener n ms alto de cluster
CMP AX,4086
237 CONTROLADORES DE DISPOSITIVOS
JAE fat16 ; el n ms alto supera 4085
MOV CX,3
MUL CX ; clusters * 3
SHR DX,1
RCR AX,1 ; clusters * 3 / 2 = clusters * 1,5
JMP calc_sfat
fat16: MOV SI,4
MOV CX,param_f ; considerar nmero de FATs
SHL SI,CL
SHR SI,1
CALL eval_clust
SHL AX,1
RCL DX,1 ; clusters * 2
calc_sfat: DIV tsect ; AX = n sectores de FAT aprox.
AND DX,DX
JZ fat_ok
INC AX ; redondeo
fat_ok: MOV sfat,AX
MOV AX,numsect ; n total de sectores
DEC AX ; descontar BOOT
SUB AX,sdir ; descontar ROOT
SUB AX,sfat ; descontar FAT
MOV CL,tcluster
XOR CH,CH
XOR DX,DX
DIV CX ; AX = nmero real de clusters
INC AX ; se numeran desde 2
MOV ultclus,AX
RET
adaptar_param ENDP
eval_clust PROC ; obtener el n ms alto de cluster
MOV AX,numsect
DEC AX ; restar BOOT
SUB AX,sdir ; restar ROOT
MUL tsect ; DX:AX = nsect * tamsect
SHL AX,1
RCL DX,1 ; DX:AX = nsect * tamsect * 2
MOV CX,tamcluster
SHL CX,1
ADD CX,SI ; CX = 2 * tamcluster + SI
DIV CX
INC AX ; los clusters se numeran desde 2
AND DX,DX ; sobra un cacho de cluster?
JZ clust_eval ; redondear: es preferible que
INC AX ; sobre un poco de FAT a que falte!
clust_eval: XOR DX,DX ; resultado en DX:AX
RET
eval_clust ENDP
; ------------ Preparar el BPB del disco virtual segn los parmetros
; y forzar que el DOS lo lea indicando cambio de disco.
preparar_BPB PROC
MOV AX,tsect
MOV bytes_sector,AX
MOV AL,tcluster
MOV sect_cluster,AL
MOV AX,tdir
MOV entradas_raiz,AX
MOV AX,numsect
MOV num_sect,AX
MOV AL,nfats
MOV num_fats,AL
MOV AX,sfat
MOV sectores_fat,AX
MOV cambiado,0FFh ; ha habido cambio de disco
RET
preparar_BPB ENDP
; ------------ Preparar el disco para operar. ES apunta al disco al
; entrar. Se proceder a copiar la rutina necesaria en
; funcin del tipo de memoria que gestiona el disco.
; Despus, se copiarn las variables que gestionan TDSK
; sobre la copia residente, as como el nuevo BPB.
prep_driver PROC
MOV AL,tipo_soporte
LEA SI,procesa_xms
MOV CX,tam_proc_xms
DEC AL
JZ prep_mem ; instalar rutina XMS
LEA SI,procesa_ems
MOV CX,tam_proc_ems
DEC AL
JZ prep_mem ; instalar rutina EMS
LEA SI,procesa_con
MOV CX,tam_proc_con ; instalar rutina memoria conv.
prep_mem: LEA DI,procesa_io
CLD
XPUSH <SI,DI,CX>
REP MOVSB ; instalar rutina en el disco
XPOP <CX,DI,SI>
XPUSH <ES,DS>
POP ES
REP MOVSB ; y en el propio TDSK.EXE (para
POP ES ; usarla despus al formatear)
LEA CX,f_tdsk_ctrl
LEA SI,i_tdsk_ctrl
SUB CX,SI
MOV DI,SI
REP MOVSB ; actualizar variables
LEA CX,fin_bpb
LEA SI,bpb
SUB CX,SI
MOV DI,SI
REP MOVSB ; actualizar BPB
RET
prep_driver ENDP
; ------------ Autorelocalizacin 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 slo alrededor de 16 Kb en memoria).
relocalizar PROC
CMP tipo_soporte,3
JE procede_reloc ; usada memoria convencional
RET
procede_reloc: PUSH ES ; * preservar ES
MOV ES,segm_reubicar ; segmento de reubicacin
XOR SI,SI
XOR DI,DI
MOV BX,SS ; final de TURBODSK (pila)
MOV CX,DS ; inicio de _PRINCIPAL
SUB BX,CX ; tamao de TDSK en prrafos
MOV CL,4
SHL BX,CL ; ahora en bytes
ADD BX,tam_pila+16 ; 16 por si acaso
MOV CX,BX ; CX = bytes a relocalizar
CLD
REP MOVSB ; auto-copiaje arriba
MOV AX,ES
MOV DS,AX ; nuevo segmento de datos
POP ES ; * restaurar ES
MOV BX,CS
SUB AX,BX ; ES - CS --> cuanta del salto
MOV BX,SS
ADD BX,AX
MOV SS,BX ; actualizar segmento de pila
POP AX ; direccin de retorno cercano
PUSH DS ; segmento de retorno
PUSH AX ; offset
RETF ; 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 extraa maniobra. Para que el DOS acceda al disco,
; se le pregunta simplemente el espacio libre del mismo.
formatear_tdsk PROC
PUSH ES ; *
PUSH DS
POP ES
LEA SI,sector_cero
LEA DI,area_trabajo
MOV CX,128
CLD
REP MOVSB ; primeros 128 bytes del BOOT
XOR AX,AX
MOV CX,tam_a_trabajo-128
REP STOSB ; a 0 resto del rea de trabajo
LEA DI,area_trabajo
ADD DI,tsect
MOV [DI-2],0AA55h ; marca de sector vlido
CALL escribe_sectAX ; escribir sector BOOT (AX=0)
LEA DI,area_trabajo
MOV CX,tsect
REP STOSB ; borrar area de trabajo
MOV AX,sfat
MOV CX,param_f ; considerar nmero de FATs
SHL AX,CL
SHR AX,1
ADD AX,sdir ; AX = sectores fat + dir. raiz
ini_fat: CMP AX,1
JE pfat
CALL escribe_sectAX ; inicializar directorio raiz
DEC AX ; y ltimos sectores de la FAT
JMP ini_fat
pfat: LEA DI,area_trabajo
MOV BYTE PTR [DI],media
MOV AX,0FFFFh ; inicializar 3 bytes FAT...
MOV DS:[DI+1],AX
CMP ultclus,4086 ; menos de 4085 clusters?
JB pfat_ok
MOV DS:[DI+3],AL ; inicializar 4 byte FAT
pfat_ok: MOV AX,1
CALL escribe_sectAX ; primer sector FAT preparado
CALL fecha_hora
LEA SI,dir_raiz
MOV [SI+22],AX ; hora actual
MOV [SI+24],DX ; fecha actual
LEA DI,area_trabajo
MOV CX,32
REP MOVSB
MOV AX,sfat
MOV CX,param_f ; considerar nmero de FATs
SHL AX,CL
SHR AX,1
INC AX
CALL escribe_sectAX ; primer sector raiz preparado
POP ES ; *
CMP dosver,31Eh
JAE formateado ; DOS 3.3+
NOT ES:media_byte ; cambiar descriptor de medio
MOV AH,36h ; obtener espacio libre
MOV DL,ES:letra_unidad
SUB DL,A-1 ; unidad de disco virtual
PUSH DX
INT 21h ; primer acceso al disco
POP DX
NOT ES:media_byte ; restaurar descriptor de medio
MOV ES:cambiado,0FFh ; nuevo cambio de disco
MOV AH,36h
INT 21h ; acceder otra vez al disco
formateado: RET
formatear_tdsk ENDP
; ---- Escribir el sector n AX del disco virtual. No
; se utiliza INT 26h (imposible desde el CONFIG).
escribe_sectAX PROC
PUSHF ; preservar bit DF
XPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>
XOR BP,BP ; indicar escritura
LEA DI,area_trabajo ; ES:DI buffer
MOV BX,AX ; nmero de sector
MOV AX,1 ; 1 sector
CALL io_proc ; acceder al disco directamente
XPOP <ES,DS,BP,DI,SI,DX,CX,BX,AX>
POPF
RET
escribe_sectAX ENDP
; ---- Obtener fecha y hora del sistema en DX y AX
fecha_hora PROC
MOV AH,2Ah
INT 21h ; obtener fecha del sistema
MOV AL,32
MUL DH ; AX = mes * 32
238 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
SUB CX,1980
SHL CL,1 ; (ao-1980)*2
ADD AH,CL ; sumar (ao-1980)*512
MOV CL,DL ; CX = dia (CH=0)
ADD AX,CX
PUSH AX ; * guardar fecha
MOV AH,2Ch
INT 21h ; obtener hora del sistema
MOV AL,32
MUL CL ; AX = minutos*32
MOV CL,3
SHL CH,CL
XOR CL,CL ; CX = hora*2048
ADD AX,CX
SHR DH,1 ; segundos/2
ADD AL,DH
ADC AH,0
POP DX ; * recuperar fecha
RET
fecha_hora ENDP
; ------------ Cambiar el nombre al bloque de control de memoria para
; mejorar la informacin del comando MEM del sistema si
; el disco se define en memoria convencional/superior.
renombrar_mcb PROC
PUSH ES
MOV AL,letra_unidad
MOV BYTE PTR nombre_tdsk+5,AL
MOV BYTE PTR nombre_tdsk+4,(
MOV BYTE PTR nombre_tdsk+6,)
MOV AX,segm_psp
DEC AX
MOV ES,AX
LEA SI,nombre_tdsk
MOV DI,8
MOV CX,DI
CLD
REP MOVSB
POP ES
RET
renombrar_mcb ENDP
; ------------ Informar sobre el disco virtual instalado.
info_disco PROC
CALL InitMultiPrint
LEA DX,ayuda_txt ; ayuda en espaol
CMP param_h,ON ; solicitud de ayuda?
JNE cont_info ; no
JMP info_exit
cont_info: TEST err_grave,0FFFFh
JZ info_no_fatal
LEA DX,err_grave_gen ; texto de encabezamiento
CALL imprimir ; imprimir errores graves:
LEA DX,e0
TEST err_grave,ERROR0
JZ otro_fallo ; no es error de DOS incorrecto
CALL imprimir
MOV SP,tam_pila
PUSH segm_psp ; en DOS 1.x hay que terminar
XOR AX,AX ; con CS = PSP
PUSH AX
RETF ; ejecutar INT 20h de PSP:0
otro_fallo: LEA DX,e1
TEST err_grave,ERROR1
JNZ info_g
LEA DX,e2
TEST err_grave,ERROR2
JNZ info_g
LEA DX,e3
info_g: JMP info_exit
info_no_fatal: CMP ES:tipo_soporte,0 ; error no fatal
JNE info_reporte
LEA DX,info_ins ; disco no formateado
CALL imprimir
CALL impr_unidad
LEA DX,info_ins2
CMP lista_err,0
JE info_exit ; sin mensajes de advertencia
CALL imprimir ; ... o con ellos
JMP info_err
info_reporte: CALL pr_info ; disco formateado
CMP lista_err,0
JE info_ret ; sin mensajes de advertencia
LEA DX,cab_adv_txt ; ... o con ellos
CALL imprimir ; cabecera de advertencias
info_err: MOV AX,lista_err
LEA BX,tabla_mens-2 ; tabla de mensajes
MOV CX,16 ; 16 posibles mensajes
busca_err: ADD BX,2
SHR AX,1
JC informa
mas_mens: LOOP busca_err ; no se produce ese error
JMP info_ret
informa: LEA DX,mens_cabec ; inicio comn a los mensajes
CALL imprimir
MOV DX,[BX] ; direccin de ese mensaje
CALL imprimir
JMP mas_mens ; acabar con todos
info_exit: CALL imprimir
info_ret: RET
info_disco ENDP
pr_info PROC
LEA DX,info_txt
CALL imprimir
CALL impr_unidad
LEA DX,inf_tsect
CALL imprimir
MOV AX,ES:bytes_sector
XOR DX,DX
MOV CL,5
CALL print_32
LEA DX,inf_tdir
CALL imprimir
MOV AX,ES:entradas_raiz
XOR DX,DX
MOV CL,5
CALL print_32
LEA DX,inf_tdisco
CALL imprimir
MOV AX,ES:num_sect
MUL ES:bytes_sector
MOV BX,1024
DIV BX
MOV CL,5
CALL print_32
LEA DX,inf_tcluster
CALL imprimir
MOV AL,ES:sect_cluster
XOR AH,AH
XOR DX,DX
MOV CL,5
CALL print_32
LEA DX,inf_mem
CALL imprimir
MOV AL,ES:tipo_soporte
LEA DX,inf_mem_xms
DEC AL
JZ mem_ifdo ; memoria XMS
LEA DX,inf_mem_ems
DEC AL
JZ mem_ifdo ; memoria EMS
LEA DX,inf_mem_con
CMP ES:mem_handle,0A000h
JB mem_ifdo ; memoria convencional
LEA DX,inf_mem_sup ; memoria superior
mem_ifdo: CALL imprimir
LEA DX,inf_nclusters
CALL imprimir
MOV AX,ES:entradas_raiz
MOV BX,32
MUL BX ; bytes ocupados por directorio
DIV ES:bytes_sector ; AX = sectores del directorio
ADD AX,ES:sect_reserv
ADD AX,ES:sectores_fat
SUB AX,ES:num_sect
NEG AX ; AX = sectores libres
XOR DX,DX
MOV BL,ES:sect_cluster
XOR BH,BH
DIV BX ; AX = n de clusters
XOR DX,DX
MOV CL,5
CALL print_32
LEA DX,inf_tfat
CALL imprimir
LEA DX,inf_tfat12
CMP AX,4085 ; FAT12?
JB ifat_ok
LEA DX,inf_tfat16
ifat_ok: CALL imprimir
LEA DX,inf_final
CALL imprimir
RET
pr_info ENDP
; --- Imprimir letra de unidad en AL.
impr_unidad PROC
XPUSH <AX, DX>
MOV AL,letra_unidad
MOV AH,0
MOV WORD PTR area_trabajo,AX
LEA DX,area_trabajo
CALL imprimir
XPOP <DX, AX>
RET
impr_unidad ENDP
; --- Imprimir un n decimal de 32 bits en DXAX formateado por CL.
;
; Entradas:
; Si bit 4 = 1 --> se imprimirn signos separadores de millar
; bits 0-3 = n total de dgitos (incluyendo separadores de
; millar y parte fraccional)
; bits 5-7 = n de dgitos de la parte fraccional (cuantos
; dgitos de DXAX, empezando por la derecha,
; se consideran parte fraccional, e irn precedidos
; del correspondiente separador)
;
; Salidas: n impreso, ningn registro modificado.
;
; * Ejemplo, si DXAX=9384320 y CL=010 1 1011
; se imprimir ( _ representa un espacio en blanco ): __93.843,20
print_32 PROC
PUSH DS
PUSH ES
PUSH CS
PUSH CS
POP DS
POP ES
PUSH AX ; preservar todos los registros
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSHF
MOV formato_pr32,CL ; byte del formato de impresin
elegido
MOV CX,idioma_seps
separ_pr32: MOV millares_pr32,CH ; separador de millares
MOV fracc_pr32,CL ; separador parte fraccional
MOV BX,OFFSET tabla_pr32
MOV CX,10
digit_pr32: PUSH CX
PUSH AX
PUSH DX
XOR DI,DI
MOV SI,1 ; DISI = 1
DEC CX ; CX - 1
JCXZ hecho_pr32
factor_pr32: SAL SI,1
RCL DI,1 ; DISI * 2
MOV DX,DI
MOV AX,SI
SAL SI,1
RCL DI,1
SAL SI,1
RCL DI,1 ; DISI * 8
ADD SI,AX
ADC DI,DX ; DISI = DISI*8 + DISI*2 = DISI*10
LOOP 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 ms tarde
MOV CL,0FFh
rep_sub_pr32: INC CL
SUB AX,SI
SBB DX,DI ; DXAX = DXAX - DISI
JNC rep_sub_pr32 ; restar el factor cuanto se pueda
ADD AX,SI ; subsanar el desbordamiento:
239 CONTROLADORES DE DISPOSITIVOS
ADC DX,DI ; DXAX = DXAX + DISI
ADD CL,0 ; pasar binario a ASCII
MOV [BX],CL
POP CX ; CX se recupera ahora
INC BX
LOOP digit_pr32 ; prximo dgito del nmero
STD ; transferencias (MOVS) hacia
atrs
DEC BX ; BX apunta al ltimo dgito
MOV final_pr32,BX ; ltimo dgito
MOV ent_frac_pr32,BX ; frontera parte entera/fraccional
MOV CL,5
MOV AL,formato_pr32
SHR AL,CL ; AL = n de decimales
AND AL,AL
JZ no_frac_pr32 ; ninguno
MOV CL,AL
XOR CH,CH
MOV SI,final_pr32
MOV DI,SI
INC DI
REP MOVSB ; correr cadena arriba (hacer
hueco)
INC final_pr32
MOV AL,fracc_pr32
MOV [DI],AL ; poner separador de parte
fraccional
MOV ent_frac_pr32,SI ; indicar nueva frontera
no_frac_pr32: MOV AL,formato_pr32
TEST AL,16 ; interpretar el formato
especificado
JZ poner_pr32 ; imprimir como tal
entera_pr32: MOV CX,final_pr32 ; aadir separadores de millar
SUB CX,ent_frac_pr32
ADD CX,3
MOV SI,final_pr32
MOV DI,SI
INC DI
REP MOVSB ; correr cadena arriba (hacer
hueco)
MOV AL,millares_pr32
MOV [DI],AL ; poner separador de millares
INC final_pr32
MOV ent_frac_pr32,SI ; usar esta variable como puntero
SUB SI,OFFSET tabla_pr32
CMP SI,3
JAE entera_pr32 ; prximo separador
poner_pr32: MOV BX,final_pr32
MOV BYTE PTR [BX+1],0 ; delimitador de fin de cadena
MOV BX,OFFSET tabla_pr32
MOV 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 izda
JE blanco_pr32
CMP AL,fracc_pr32
JNE acabar_pr32
MOV BYTE PTR [BX-1],0 ; reponer 0 antes de la coma
DEC principio_pr32
acabar_pr32: MOV AL,formato_pr32 ; imprimir
AND AL,00001111b
XOR AH,AH
MOV DX,final_pr32
SUB DX,AX
INC DX ; DX = offset principio
AND AX,AX
JNZ format_pr32 ; longitud especificada por el
usuario
MOV DX,principio_pr32 ; longitud obtenida del nmero
format_pr32: CALL imprimir
POPF ; restaurar todos los registros
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
RET ; salida del procedimiento
blanco_pr32: MOV BYTE PTR [BX], ; sustituir 0 separador de
millares
INC BX ; a la izda. por espacio en blanco
INC principio_pr32
CMP BX,final_pr32
JB limpiar_pr32
MOV DX,BX ; es el nmero 0.000.000.00X
JMP SHORT acabar_pr32 ; imprimir
formato_pr32 DB 0
DB 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 ms ., ms
ASCIIZ)
DW 0,0 ; aqu se solapa un buffer de 32
bytes
millares_pr32 DB . ; separador de millares
fracc_pr32 DB , ; " parte fraccional
final_pr32 DW 0 ; offset al ltimo byte a imprimir
principio_pr32 DW 0 ; " " primer " " "
ent_frac_pr32 DW 0 ; offset a la frontera
entero-fracc.
DT 0 ; $ - tabla_pr32 = 32 bytes usados
por
; INT 21h al principio de print_32
print_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 PROC
XPUSH <BX,SI,CX,AX,DX>
MOV SI,32
XOR BX,BX
divmas: SHL AX,1
RCL DX,1
RCL BX,1
CMP BX,CX
JB dividido ; "no cabe"
SUB BX,CX
INC AL ; 1 al cociente
dividido: DEC SI
JNZ divmas
AND DX,DX
JZ div_ok
XPOP <DX,AX> ; error
STC
JMP div_fin
div_ok: MOV DX,BX ; resto en DX y cociente en AX
ADD SP,4 ; sacar sin sacar DX y AX
CLC
div_fin: XPOP <CX,SI,BX> ; recuperar CX, SI y BX
RET
divCX ENDP
; ------------ Impresin en color o monocroma (esta ltima
; redireccionable). Desde el CONFIG.SYS se imprime en
; monocromo para no llamar la atencin, a menos que
; indiquen /M, al contrario que desde el DOS.
imprimir PROC
PUSH AX
MOV AL,param_m
CMP modo,CONFIG ; en CONFIG.SYS?
JNE m_ok ; no
XOR AL,ON ; s: /M opera al revs
m_ok: MOV pr_mono,AL
CALL print
POP AX
RET
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 multilinge y los diversos
; idiomas (1, 2, ... N) separan sus cadenas por sucesivos
; cdigos 255. El carcter de control 127 realiza una
; pausa hasta que se pulsa una tecla.
print PROC
XPUSH <AX, BX, CX, DX, SI, DI, ES>
CMP idioma,0
JNE pr_decidir
PUSH DX ; *
MOV AH,30h
INT 21h
XCHG AH,AL
MOV CX,AX ; CX = versin del DOS
CMP param_i,ON
MOV AX,codigo_tfno
MOV BX,1234h
JNE pr_busca_cod ; parmetro /I=cod no indicado
MOV BX,AX
MOV AL,0FFh
CMP BX,255
JAE pr_cod ; cdigo mayor o igual de 255
MOV AL,BL ; cdigo menor de 255
pr_cod: CMP CX,200h
JAE pr_cod_tfno ; DOS >= 2.X
pr_busca_cod: CMP CX,200h
MOV AX,1 ; ingls para DOS < 2.X
JB pr_habla_ax
MOV AL,0
pr_cod_tfno: LEA DX,area_trabajo
MOV AH,38h
XPUSH <BX, CX>
INT 21h ; obtener informacin del pais
XPOP <CX, AX>
JC pr_habla_ax ; fallo en la funcin
CMP CX,20Bh
JE pr_habla_ax ; DOS 2.11: AX cd. telefnico
CMP CX,300h
MOV AX,1
JB pr_habla_ax ; 2.x excepto 2.11: mala suerte
MOV AX,BX
LEA BX,area_trabajo
MOV CH,[BX+7] ; separador de millares
MOV CL,[BX+9] ; separador de decimales
MOV idioma_seps,CX
pr_habla_ax: LEA BX,info_paises-2
MOV CX,1 ; supuesto idioma 1
pr_busca_idi: ADD BX,2
MOV DX,[BX]
CMP AX,DX
JE pr_habla_ese
AND DX,DX
JNZ pr_busca_idi
INC CX ; ser otro idioma
CMP [BX+2],DX
JNE pr_busca_idi ; no es fin de la tabla
pr_habla_ese: MOV idioma,CL
POP DX ; *
pr_decidir: MOV CL,idioma
MOV CH,0 ; n de idioma a usar (1..N)
MOV BX,DX
pr_busca_msg: MOV DX,BX
DEC BX
pr_busca_ter: INC BX
CMP BYTE PTR [BX],0
JE pr_usar_ese ; acaba en 0: no buscar ms
CMP BYTE PTR [BX],255
JNE pr_busca_ter
INC BX
LOOP pr_busca_msg ; acaba en 255 pero no es ese
pr_usar_ese: MOV BX,DX
DEC BX
pr_cad_lon: INC BX
CMP BYTE PTR [BX],0
JE prlong_ok
CMP BYTE PTR [BX],127 ; carcter de pausa
JE prpausa
CMP BYTE PTR [BX],255
JNE pr_cad_lon ; calcular longitud
JMP prlong_ok
prpausa: PUSH BX
MOV CX,BX
SUB CX,DX
CALL pr_cad ; imprimir hasta el cdigo 127
pr_limpbuf: MOV AH,1
INT 16h
JZ pr_notec
MOV AH,0
INT 16h ; limpiar buffer del teclado
JMP pr_limpbuf
pr_notec: MOV AH,0
INT 16h ; esperar tecla
POP BX
INC BX
MOV DX,BX
CMP AL,27 ; tecla ESC?
STC
240 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
JE pr_ret
JMP pr_cad_lon ; imprimir el resto
prlong_ok: MOV CX,BX
SUB CX,DX
CALL pr_cad ; terminar impresin
CLC
pr_ret: XPOP <ES, DI, SI, DX, CX, BX, AX> ; CF=1 si se puls ESC
RET
pr_cad: ; MOV AH,40h
; MOV BX,1
; INT 21h ; imprimir con el DOS
MOV SI,DX
LEA DI,area_trabajo
PUSH DS
POP ES ; por si acaso
CLD
REP MOVSB
MOV [DI],CL ; ASCIIZ
LEA DX,area_trabajo
CALL MultiPrint ; imprimir en color
RET
print ENDP
; ------------ Impresin 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.
; - Cdigos de control soportados:
;
; 0 -> final de cadena
; 1 -> el siguiente carcter indica el color (BIOS)
; 2 -> el siguiente carcter indica el n de veces que
; se imprimir el que viene detrs
; 3 -> avanzar cursor a la derecha
; 10 -> retorno de carro y salto de lnea estilo UNIX
MultiPrint PROC
XPUSH <AX,BX,CX,DX,SI,DI,BP,DS,ES>
PUSH DS
POP ES
PUSH CS
POP DS
LEA AX,pr_AL_dos
CMP pr_mono,ON
JE pr_rut_ok
LEA AX,pr_AL_bios
pr_rut_ok: MOV pr_rut,AX ; instalar rutina de impresin
MOV BX,DX
pr_otro: MOV AL,ES:[BX]
PUSH BX
CMP AL,
JAE pr_ASCII ; no es un cdigo de control
AND AL,AL
JZ pr_exit ; cdigo de control 0: final
CMP AL,1
JE pr_setcolor ; cdigo de control 1: color
CMP AL,2
JE pr_setveces ; cdigo de control 2: repetir
pr_ASCII: CALL pr_rut
POP BX
INC BX
JMP pr_otro
pr_setcolor: MOV AL,ES:[BX+1]
MOV pr_color,AL ; actualizar color
POP BX
ADD BX,2
JMP pr_otro
pr_setveces: MOV AL,ES:[BX+1]
MOV pr_veces,AL ; actualizar repeticiones
POP BX
ADD BX,2
JMP 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 BIOS
PUSH AX
MOV AH,3
MOV BH,pr_pagina
INT 10h ; DX = coordenadas del cursor
POP AX
CMP AL,3
JE pr_derecha ; cdigo de control 3: avanzar
CMP AL,10
JE pr_crlf ; cdigo de control 10: CR & LF
MOV AH,9
MOV BH,pr_pagina
MOV BL,pr_color
MOV CL,pr_veces
XOR CH,CH
PUSH DX
INT 10h ; imprimir carcter
POP DX
pr_derecha: ADD DL,pr_veces
MOV pr_veces,1
CMP DL,pr_maxX
JBE pr_av
pr_crlf: XOR DL,DL ; volver al inicio de lnea
INC DH ; salto a la siguiente
CMP DH,pr_maxY
JBE pr_av
DEC DH
PUSH DX ; es preciso hacer scroll
MOV AX,601h
MOV BH,pr_colorb ; color por defecto
XOR CX,CX
MOV DL,pr_maxX
MOV DH,pr_maxY
INT 10h ; hacer scroll usando BIOS
POP DX
pr_av: MOV BH,pr_pagina
MOV AH,2
INT 10h ; posicionar cursor
RET ; retorno del procedimiento
pr_AL_bios ENDP
pr_AL_dos PROC ; imprimir usando DOS
CMP AL,3
JNE pr_no_der
MOV AL, ; cdigo de control 3: avanzar
pr_no_der: CMP AL,10
JNE pr_dos
MOV AL,13 ; cdigo de control 10: CR & LF
CALL pr_dos ; llamada "recursiva"
MOV AL,10
pr_dos: MOV CL,pr_veces
XOR CH,CH
MOV pr_veces,1
MOV DL,AL
pr_chr: XPUSH <DX,CX>
MOV AH,2
INT 21h ; imprimir carcter
XPOP <CX,DX>
LOOP pr_chr
RET
pr_AL_dos ENDP
InitMultiPrint PROC
XPUSH <AX,BX,CX,DX,BP,DS,ES>
PUSH CS
POP DS
MOV pr_veces,1
MOV pr_color,15 ; valores por defecto
MOV pr_mono,OFF
pr_i_80?: MOV AH,0Fh
INT 10h
CMP AH,80 ; 80 ms columnas?
JAE pr_i_video_ok ; as es
MOV AX,3
INT 10h ; forzar modo de 80 columnas
JMP pr_i_80?
pr_i_video_ok: MOV pr_maxX,AH ; inicializar mxima coord. X
MOV pr_pagina,BH ; inicializar pgina activa
MOV AX,40h
MOV ES,AX ; ES: -> variables del BIOS
MOV AL,ES:[84h] ; variable de n lneas - 1
CMP AL,24 ; el BIOS define la variable?
JB pr_i_maxy_ok ; no
MOV pr_maxY,AL ; inicializar mxima coord. Y
pr_i_maxy_ok: MOV AH,8 ; (BH = pgina)
INT 10h ; obtener color por defecto
MOV pr_colorb,AH
XPOP <ES,DS,BP,DX,CX,BX,AX>
RET
InitMultiPrint ENDP
pr_pagina DB 0 ; pgina de visualizacin activa
pr_veces DB 1 ; veces que se imprime cada carcter
pr_color DB 15 ; color BIOS para imprimir
pr_colorb DB ? ; color por defecto en pantalla
pr_maxX DB 80 ; mxima coordenada X en pantalla
pr_maxY DB 24 ; mxima coordenada Y en pantalla
pr_mono DB OFF ; a ON si imprimir en monocromo
pr_rut DW ? ; apunta a pr_AL_bios / pr_AL_dos
; ------------ Rutina de gestin 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
; parmetros al controlador XMS.
procesa_xms PROC
MOV DS,CS:mem_handle
JNC no_xmslib
.286 ; rutina ejecutada desde 286+
PUSHA ; sistema reinicializando:
MOV AH,0Dh
CALL llama_XMS ; desbloquear EMB (prudente)
MOV AH,0Ah
CALL llama_XMS ; liberar EMB
POPA
.8086
RET
no_xmslib: DEC BP ; leer/escribir en el disco
JNZ xms_escribe
PUSH ES
PUSH DI ; segmento:offset destino
PUSH BP ; handle destino (BP=0)
xms_escribe: PUSH DX
PUSH AX ; desplazamiento DX:AX
PUSH DS ; handle fuente/destino
JZ xms_general
INC BP ; hacer BP = 0
PUSH ES
PUSH DI ; segmento:offset fuente
PUSH BP ; handle fuente (BP=0)
xms_general: SHL CX,1 ; palabras -> bytes
RCL BP,1 ; BP era 0
PUSH BP ; tamao bloque (parte alta)
PUSH CX ; tamao bloque (parte baja)
MOV SI,SP
PUSH SS
POP DS ; DS:SI apuntando a la pila
MOV AH,0Bh ; funcin para mover EMB
CALL llama_XMS ; mover EMB (DS no importa)
ADD SP,16 ; equilibrar pila
CMP AL,1 ; fall el controlador?
JE xms_proc_ok
MOV AX,0C81h ; anomala general
xms_proc_ok: XCHG AH,AL ; colocar resultado
RET
procesa_xms ENDP
llama_XMS PROC
MOV DX,DS ; handle en DS (si utilizado)
CALL CS:xms_driver ; ejecutar funcin XMS
RET
llama_XMS ENDP
tam_proc_xms EQU $-OFFSET procesa_xms ; tamao de esta rutina
; ------------ Rutina de gestin de memoria convencional. Se copiar
; sobre la de memoria EMS si se utiliza memoria conv.
procesa_con PROC
JC con_exit ; sistema inicializndose
MOV BX,16 ; bytes por prrafo
DIV BX ; AX = segmento, DX = offset
ADD AX,CS:mem_handle ; segmento de inicio datos
MOV DS,AX
MOV SI,DX ; DS:SI inicio de datos
DEC BP ; y ES:DI destino del buffer
JZ con_general ; es lectura
XCHG SI,DI ; escritura: intercambiar
XPUSH <DS,ES>
XPOP <DS,ES>
con_general: CLD
CMP CS:cpu386,ON
JE con_tr32bit
REP MOVSW
JMP con_tr_fin
con_tr32bit: SHR CX,1 ; n palabras de 32 bit a mover
JCXZ con_trdo ; evitar desgracia
241 CONTROLADORES DE DISPOSITIVOS
.386
PUSHAD
XOR EAX,EAX ; asegurar no violacin
DEC AX ; de segmento-64K
AND ECX,EAX ; EAX = 0FFFFh
AND ESI,EAX
AND EDI,EAX
REP MOVSD ; transferencia ultrarrpida
con_trdo: POPAD ; POPAD falla en muchos 386
NOP ; arreglar fallo de POPAD
.8086
con_tr_fin: MOV AX,100h ; todo fue bien, por supuesto
con_exit: RET
procesa_con ENDP
tam_proc_con EQU $-OFFSET procesa_con ; tamao de esta rutina
; ************ Datos no residentes para la instalacin
ON EQU 1 ; constantes booleanas
OFF EQU 0
CONFIG EQU 1 ; TURBODSK ejecutado desde el CONFIG
AUTOEXEC EQU 2 ; TURBODSK se ejecuta desde el DOS
emm_id DB "EMMXXXX0" ; identificacin del controlador EMS
nombre_tdsk DB "TDSK U: " ; para nombrar handle EMS y el MCB
modo DB ? ; CONFIG/AUTOEXEC
dosver DW ? ; versin del DOS
top_ram DW 0 ; segmento ms alto de la RAM
segm_psp DW 0 ; segmento del PSP
segm_tdsk DW 0 ; segmento donde reside TURBODSK
segm_reubicar DW 0 ; segmento donde reubicar TURBODSK
ems4 DB OFF ; a ON si EMS versin 4.0+
cpu286 DB OFF ; a ON si 286 superior
idioma DB 0 ; selecciona el nmero 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 tamao de disco
param_tdisco DW 0 ; tamao de disco (si se define)
param_tsect DW 0 ; tamao de sector (si se define)
param_tdir DW 0 ; nmero de entradas (si se define)
param_tcluster DW 0 ; tamao de cluster (si se define)
param_a DB OFF ; a ON si indicado parmetro /A o /X
param_e DB OFF ; a ON si indicado parmetro /E
param_b DB OFF ; a ON si indicado parmetro /B
param_c DB OFF ; a ON si indicado parmetro /C
param_h DB OFF ; a ON si indicado parmetro /? o /H
param_m DB OFF ; a ON si indicado parmetro /M
param_i DB OFF ; Y ON si indicado parmetro /I
param_f DW 1 ; n de FATs (1-2): parmetro /F=
codigo_tfno DW ? ; valor de /I= si se indica
tdisco DW ? ; tamao de disco (Kb)
ultclus DW ? ; nmero ms alto de cluster
tamcluster DW ? ; tamao de cluster (bytes)
sdir DW ? ; sectores para directorio raiz
xms_kb DW 0 ; Kb de memoria XMS libres
ems_kb DW 0 ; Kb de memoria EMS libres
con_kb DW 0 ; Kb de memoria convencional libres
sector_cero LABEL BYTE
JMP SHORT botar
NOP
DB "TDSK 2.3" ; identificacin del sistema
tsect DW 512 ; tamao de sector por defecto
tcluster DB ? ; sectores por cluster
DW 1 ; sectores reservados
nfats DB ? ; nmero de FATs
tdir DW ? ; nmero de entradas al dir. raiz
numsect DW ? ; n sectores del disco (<=32Mb)
DB media ; descriptor de medio
sfat DW ? ; sectores por FAT
DW 1, 1 ; sectores por pista / cabezas
DD 0 ; sectores ocultos
DD 0 ; n total de sectores (si > 32Mb)
DB 7 DUP (0) ; 7 bytes reservados
botar: DB 0EAh ; cdigo de JMP FAR...
DW 0,0FFFFh ; ...FFFF:0000 (programa BOOT)
DB "(C)1992 CiriSOFT"; resto de primeros 64 bytes
DB ". Grupo Universi"
DB "tario de Inform"
DB "tica (GUI) - Val"
DB "ladolid (Espaa)"; resto de primeros 128 bytes
dir_raiz DB "TURBODSK "; Directorio raiz: primera entrada
DB 8 ; etiqueta de volmen
DB 10 DUP (0) ; reservado
DW ? ; hora (inicializado al formatear)
DW ? ; fecha
DW 0,0,0 ; ltimos bytes (hasta 32)
; ------------ Areas de datos para informacin del disco virtual
; --- Cdigo telefnico de pases de habla
; hispana (mucha o poca).
info_paises DW 54 ; Argentina
DW 591 ; Bolivia
DW 57 ; Colombia
DW 506 ; Costa Rica
DW 56 ; Chile
DW 593 ; Ecuador
DW 503 ; El Salvador
DW 34 ; Espaa
DW 63 ; Filipinas
DW 502 ; Guatemala
DW 504 ; Honduras
DW 212 ; Marruecos
DW 52 ; Mxico
DW 505 ; Nicaragua
DW 507 ; Panam
DW 595 ; Paraguay
DW 51 ; Per
DW 80 ; Puerto Rico
DW 508 ; Repblica Dominicana
DW 598 ; Uruguay
DW 58 ; Venezuela
DW 3 ; Latinoamrica
DW 0 ; fin de la informacin
; --- Cdigo telefnico de pases de habla alemana.
DW 41 ; Switzerland
DW 43 ; Austria
DW 49 ; Germany
DW 0 ; fin de la informacin
DW 0 ; no ms idiomas
; ------------ Mensaje de no formateado
info_ins DB 10,1,10,"TURBODSK 2.3 - Unidad ",255
DB 10,1,10,"TURBODSK 2.3 - Laufwerk ",255
DB 10,1,10,"TURBODSK 2.3 - Drive ",0
info_ins2 DB ": sin formatear.",10,1,14,255
DB ": nicht formatiert.",10,1,14,255
DB ": unformatted.",10,1,14,0
; ------------ Cuadro de informacin
colA EQU 11+1*16 ; color del recuadro y los mensajes
colB EQU 15+1*16 ; color de los parmetros de operacin del disco
colC EQU 15+0*16 ; color de lo que rodea a la ventana
colD EQU 10+1*16 ; color de TURBODSK
info_txt DB 10,2,12,3,1,colA," ",2,27," ",2,25," ",1,colC
DB 10,2,12,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colA
DB " - Unidad ",1,colB
DB 255
DB 10,2,10,3,1,colA," ",2,28," ",2,28," ",1,colC
DB 10,2,10,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colA
DB " - Laufwerk ",1,colB
DB 255
DB 10,2,12,3,1,colA," ",2,26," ",2,25," ",1,colC
DB 10,2,12,3,1,colA," ",1,colD,"TURBODSK 2.3",1,colA
DB " - Drive ",1,colB
DB 0
inf_tsect DB ":",1,colA," Tamao de sector:",1,colB," ",255
DB ":",1,colA," Sektorgre:",2,8," ",1,colB," ",255
DB ":",1,colA," Sector size:",2,5," ",1,colB," ",0
inf_tdir DB " ",1,colA," ",1,colC,10,2,12,3
DB 1,colA," ",2,27," N entradas raiz:",1,colB," "
DB 255
DB " ",1,colA," ",1,colC,10,2,10,3
DB 1,colA," ",2,28," Verzeichniseintrge:",1,colB,
" "
DB 255
DB " ",1,colA," ",1,colC,10,2,12,3
DB 1,colA," ",2,26," Root entries:",2,4," ",1,colB,"
"
DB 0
inf_tdisco DB " ",1,colA," ",1,colC,10
DB 2,12,3,1,colA," Tamao: ",1,colB," "
DB 255
DB " ",1,colA," ",1,colC,10
DB 2,10,3,1,colA," Gre:",2,10," ",1,colB," "
DB 255
DB " ",1,colA," ",1,colC,10
DB 2,12,3,1,colA," Size:",2,4," ",1,colB," "
DB 0
inf_tcluster DB " Kbytes ",1,colA," Sectores/cluster:",1,colB,"
"
DB 255
DB " KB ",1,colA," Sektoren/Cluster:",2,3,"
",1,colB," "
DB 255
DB " Kbytes ",1,colA," Sectors/cluster: ",1,colB,"
"
DB 0
inf_mem DB " ",1,colA," ",1,colC,10
DB 2,12,3,1,colA," Memoria: ",1,colB
DB 255
DB " ",1,colA," ",1,colC,10
DB 2,10,3,1,colA," Speicher: ",1,colB
DB 255
DB " ",1,colA," ",1,colC,10
DB 2,12,3,1,colA," Memory: ",1,colB
DB 0
inf_nclusters DB " ",1,colA," ",1,colB," ",255
DB " ",1,colA," ",1,colB," ",255
DB 1,colA," ",1,colB," ",0
inf_tfat DB 1,colA," clusters (",1,colB,"FAT",255
DB 1,colA," Cluster (",1,colB,"FAT",255
DB 1,colA," clusters (",1,colB,"FAT",0
inf_tfat12 DB "12",0
inf_tfat16 DB "16",0
inf_final DB 1,colA,") ",1,colA," ",1,colC,10,2,12,3
DB 1,colA," ",2,27," ",2,25," ",1,colC,10
DB 255
DB 1,colA,")",2,5," ",1,colA," ",1,colC,10
DB 2,10,3,1,colA," ",2,28," ",2,28," ",1,colC,10
DB 255
DB 1,colA,") ",1,colA," ",1,colC,10,2,12,3
DB 1,colA," ",2,26," ",2,25," ",1,colC,10
DB 0
inf_mem_xms DB "Extendida (XMS)",255
DB "Erweitert (XMS)",255
DB "Extended (XMS) ",0
inf_mem_ems DB "Expandida (EMS)",255
DB "Expansion (EMS)",255
DB "Expanded (EMS) ",0
inf_mem_sup DB " Superior (UMB) ",255
DB "Oberer Sp. (UMB)",255
DB " Upper (UMB) ",0
inf_mem_con DB " Convencional ",255
DB " Konventionell",255
DB " Conventional ",0
242 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
; ------------ Errores leves
ERROR0 EQU 1
ERROR1 EQU 2
ERROR2 EQU 4
ERROR3 EQU 8 ; TURBODSK es muy flexible y se instala
ERROR4 EQU 16 ; casi de cualquier forma, aunque a
ERROR5 EQU 32 ; veces no se reserve memoria y sea
ERROR6 EQU 64 ; necesario volver a ejecutarlo despus
ERROR7 EQU 128 ; desde el DOS para formatearlo.
ERROR8 EQU 256
ERROR9 EQU 512
ERROR10 EQU 1024
ERROR11 EQU 2048
ERROR12 EQU 4096
ERROR13 EQU 8192
ERROR14 EQU 16384
ERROR15 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,m7
DW m8,m9,m10,m11,m12,m13,m14,m15
cab_adv_txt DB 10,2,8,3,1,12
DB "Advertencias y/o errores de TURBODSK:",2,27,"
",10,1,10
DB 255
DB 10,2,8,3,1,12
DB "Warnungen und Fehlermeldungen von TURBODSK:",2,27,"
",10,1,10
DB 255
DB 10,2,8,3,1,12
DB "Warnings and errors of TURBODSK:",2,32," ",10,1,10
DB 0
m0 DB "- Error de sintaxis o parmetro fuera de rango.
No se define el",10,2,8,3
DB " disco virtual ahora o no se modifica el que
estaba definido. ",10
DB 255
DB "- Syntaxfehler oder ungltiger Parameter. Die
RAM-Disk ist zur ",10,2,8,3
DB " Zeit nicht definiert bzw. wurde nicht
modifiziert.",10
DB 255
DB "- Syntax error and/or parameter out of range. The
Ramdisk is not",10,2,8,3
DB " defined now or the previous one is not
modified.",2,14," ",10
DB 0
m1 DB "- El parmetro /C o la letra de unidad slo han de
emplearse",2,4," ",10,2,8,3
DB " desde la lnea de comandos o el AUTOEXEC (les
ignorar).",2,6," ",10
DB 255
DB "- Parameter /C und Laufwerksbuchstaben knnen nur
bei Aufrufen ",2,4," ",10,2,8,3
DB " von TURBODSK in der AUTOEXEC verwendet werden.
",2,6," ",10
DB 255
DB "- The /C parameter and the driver letter only can
be 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 que
incluir la opcin",10,2,8,3
DB " /A en CONFIG.SYS, con objeto de dejar espacio
para las rutinas",10,2,8,3
DB " de control EMS: la memoria ocupada crecer de 432
a 608 bytes.",10
DB 255
DB "- Zur Verwendung von EMS mssen Sie Option /A in
CONFIG.SYS ",10,2,8,3
DB " setzen, um Speicher fr die EMS-Untersttzung zu
reservieren. ",10,2,8,3
DB " Dadurch erhht sich der Speicherbedarf von 432
auf 608 Bytes. ",10
DB 255
DB "- In order to use expanded memory you must include
the /A option",10,2,8,3
DB " in CONFIG.SYS, needed to reserve too space for
the EMS support",10,2,8,3
DB " routines: the memory used will increase from 432
to 608 bytes.",10
DB 0
m3 DB "- El tamao de sector es mayor que el definido en
cualquier otro",10,2,8,3
DB " controlador de dispositivo: indquese ese tamao
en CONFIG.SYS",10,2,8,3
DB " para que el DOS ajuste sus buffers (ms consumo
de memoria!).",10
DB 255
DB "- Die Sektorengre ist grer als in allen anderen
Treibern; ",10,2,8,3
DB " Sie mssen die Sektorgre in CONFIG.SYS
festlegen, da DOS die",10,2,8,3
DB " Puffergre anpassen mu (hherer
Speicherverbrauch) ",10
DB 255
DB "- Sector size is greater than any other defined
by any device",10,2,8,3
DB " driver loaded: you must indicate the sector size
in CONFIG.SYS",10,2,8,3
DB " because DOS need adjust buffers length (more
memory spent!). ",10
DB 0
m4 DB "- La cantidad de memoria solicitada no existe, se
ha rebajado. ",10
DB 255
DB "- Die gewnschte Speichergre existiert nicht und
wurde 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 memoria
convencional.",2,5," ",10
DB 255
DB "- Kein XMS/EMS verfgbar: Fhren Sie TDSK nochmals
von der ",10,2,8,3
DB " Kommandozeile aus und benutzen Sie
konventionellen Speicher. ",2,5," ",10
DB 255
DB "- There is not XMS/EMS memory available: execute
TDSK again from",10,2,8,3
DB " DOS command line or AUTOEXEC and use conventional
memory.",2,5," ",10
DB 0
m6 DB "- No existe memoria XMS: pruebe a indicar EMS en
su lugar (/A) ",10
DB 255
DB "- Kein XMS verfgbar: Versuchen Sie, EMS zu
verwenden (/A). ",10
DB 255
DB "- There is not XMS memory available: try to request
EMS (/A). ",10
DB 0
m7 DB "- No existe memoria EMS: pruebe a indicar XMS en
su lugar (/E) ",10
DB 255
DB "- Kein EMS verfgbar: Versuchen Sie, XMS zu
verwenden (/E). ",10
DB 255
DB "- There is not EMS memory available: try to request
XMS (/E). ",10
DB 0
m8 DB "- Fallo del controlador XMS: imposible usar memoria
extendida. ",10
DB 255
DB "- Fehler des XMS-Managers: Verwendung von XMS
unmglich. ",10
DB 255
DB "- XMS controller failure: imposible to use extended
memory.",2,5," ",10
DB 0
m9 DB "- Fallo del controlador EMS: imposible usar memoria
expandida. ",10
DB 255
DB "- Fehler des EMS-Managers: Verwendung von EMS
unmglich. ",10
DB 255
DB "- EMS controller failure: imposible to use expanded
memory.",2,5," ",10
DB 0
m10 DB "- No existe suficiente memoria convencional para
TURBODSK.",2,6," ",10
DB 255
DB "- Nicht gengend konventioneller Speicher fr
TURBODSK verfgbar.",2,6," ",10
DB 255
DB "- There is not sufficient conventional memory for
TURBODSK.",2,5," ",10
DB 0
m11 DB "- Tamao de sector incorrecto: lo establezco por
defecto.",2,7," ",10
DB 255
DB "- Ungltige Sektorengre angegeben, Vorgabewert
wird verwendet.",2,7," ",10
DB 255
DB "- Incorrect sector size indicated: default values
assumed.",2,6," ",10
DB 0
m12 DB "- Nmero de entradas incorrecto: lo establezco por
defecto.",2,5," ",10
DB 255
DB "- Ungltige Anz. von Verzeichnisantrgen,
Vorgabewert wird verwendet.",2,5," ",10
DB 255
DB "- Incorrect number of root entries: default value
assumed.",2,6," ",10
DB 0
m13 DB "- Tamao de cluster incorrecto: lo establezco por
defecto.",2,6," ",10
DB 255
DB "- Ungltige Clustergre angegeben, Vorgabewert
wird verwendet.",2,6," ",10
DB 255
DB "- Incorrect cluster size indicated: default value
assumed.",2,6," ",10
DB 0
m14 DB "- FATAL: fallo al liberar la memoria que ocupaba
el disco.",2,6," ",10
DB 255
DB "- ACHTUNG: Freigabe des belegten Speichers
gescheitert.",2,6," ",10
243 CONTROLADORES DE DISPOSITIVOS
DB 255
DB "- FATAL: imposible to free memory alocated by
TURBODSK.",2,9," ",10
DB 0
m15 DB "- Para discos de ms de 32 Mb, hace falta un tamao
de sector de",10,2,8,3
DB " al menos 1024 bytes.",2,42," ",10
DB 255
DB "- Laufwerke mit mehr als 32 MB erfordern eine
Sektorgre",10,2,8,3
DB " von mindestens 1024 Bytes.",2,42," ",10
DB 255
DB "- In drives over 32 Mb, sector size must be at
least 1024 bytes.",10
DB 0
; ------------ Errores graves (se imprime slo el ms 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 o
superior.",10,255
DB " - Diese RAM-Disk erfordert mindestens DOS
2.0.",10,255
DB " - This Ram Disk needs at least DOS 2.0 or
above.",10,0
e1 DB " - Instale primero TURBODSK desde CONFIG.SYS (con
DEVICE).",10
DB " - Puede solicitar ayuda con TDSK /?",10
DB 255
DB " - Sie mssen zuerst TURBODSK von der CONFIG.SYS
aus installieren",10
DB " (mit DEVICE). Hilfe erhalten Sie durch Eingabe
von TDSK /?",10
DB 255
DB " - You must install first TURBODSK from CONFIG.SYS
(using DEVICE).",10
DB " - Help is available with TDSK /?",10
DB 0
e2 DB " - La unidad indicada no es un dispositivo TURBODSK
2.3",10,255
DB " - Angegebener Laufwerksbuchstabe bezeichnet keinen
Treiber von TURBODSK.", 10,255
DB " - Drive letter indicated does not is a TURBODSK 2.3
device.",10,0
e3 DB " - No pueden modificarse las caractersticas de
operacin de",10
DB " TURBODSK dentro de WINDOWS. Configrelo con
anterioridad.",10
DB 255
DB " - TURBODSK kann nicht innerhalb einer
WINDOWS-Sitzung modifiziert werden.",10
DB " Sie mssen die Einstellungen vorher
durchfhren.",10
DB 255
DB " - Operational characteristics of disk can not be
altered inside",10,2,4
DB " a WINDOWS session. You must configure TURBODSK
before.",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 fecha
colorC EQU 10+1*16 ; color de sintaxis y parmetros
colorD EQU 15+1*16 ; color principal del texto
colorDm EQU 11+1*16 ; color del marco de fondo
colorDmx EQU 11+0*16 ; color de la esquina del marco
colorE EQU 11+1*16 ; color del nombre del autor
colorF EQU 14+1*16 ; color para llamar la atencin
colorG EQU 12+1*16 ; color para la direccin de mail
colorH EQU 9+1*16 ; color para mensaje de dominio pblico
ayuda_txt LABEL BYTE
DB 10,3,1,colorDm," ",1,colorA," TURBODSK 2.3
",1,colorAm," "
DB 1,colorB,2,51," 12/12/95 ",1,colorDmx," ",10
DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorE
DB " (C) 1995 Ciriaco Garca de Celis. ",1,colorG
DB "(Mail: ciri@gui.uva.es).",1,colorDm," ",10
DB 3,1,colorE," (C) Grupo Universitario de Informtica.
"
DB "Apartado 6062, Valladolid (Espaa).
",1,colorDm," ",10
DB 3,1,colorH,2,18," ","* * * Programa de Dominio
Pblico * * *"
DB 2,18," ",1,colorDm," ",10
DB 3,1,colorD," Bienvenido al disco virtual
",1,colorF,"ms rpido"
DB 1,colorD,", con soporte de memoria EMS, XMS y
",1,colorDm," ",10
DB 3,1,colorD," convencional; redimensionable, fcil
de usar. En DOS "
DB "5 ocupa 432-608 bytes. ",1,colorDm," ",10
DB 3,1,colorC,2,77," ",1,colorDm," ",10
DB 3,1,colorC," DEVICE=TDSK.EXE [tamao [tsector "
DB "[nfich [scluster]]]] [/E] [/A|X] [/C] [/M]
",1,colorDm," ",10
DB 3,1,colorC,2,77," ",1,colorDm," ",10
DB 3,1,colorD," ",1,colorF," ",1,colorD," El tamao
debe de estar en "
DB "el rango 8 - 65534 Kb; son vlidos sectores de
",1,colorDm
DB " ",10
DB 3,1,colorD," 32 a 2048 bytes (en potencias de dos,
aunque algn "
DB "sistema slo los soporta ",1,colorDm," ",10
DB 3,1,colorD," de 128 a 512). El nmero de ficheros
del directorio "
DB "raiz debe estar entre 1 ",1,colorDm," ",10
DB 3,1,colorD," y 65534 y el de sectores por cluster
entre 1 y 255 ("
DB "en algn sistema han de ",1,colorDm," ",10
DB 3,1,colorD," ser potencia de dos). Segn el tamao
se ajustar "
DB "lo dems automticamente. ",1,colorDm," ",10
DB 3,1,colorD," ",1,colorF," ",1,colorD," Se puede
indicar ",1,colorC
DB "/E",1,colorD," para emplear memoria extendida XMS,
y ",1,colorC
DB "/A",1,colorD," o ",1,colorC,"/X",1,colorD," para
la ",1,colorDm
DB " ",10
DB 3,1,colorD," expandida EMS; aunque por defecto,
TURBODSK utilizar"
DB " automticamente estas ",1,colorDm," ",10
DB 3,1,colorD," memorias si puede. Con la opcin
",1,colorC,"/C"
DB 1,colorD," se pide el uso de memoria convencional.
",1,colorDm
DB " ",10
DB 3,1,colorD,32,1,colorF," ",1,colorD," Tras ser
instalado, se puede"
DB " ejecutar desde el DOS para cambiar el tamao
",1,colorDm
DB " ",10
DB 3,1,colorD," del disco (perdindose los datos
almacenados): con "
DB "un tamao 0 se anula el ",1,colorDm," ",10
DB 3,1,colorD," disco por completo, liberndose la
memoria. "
DB "Utilizando memoria convencional ",1,colorDm," ",10
DB 3,1,colorD," es ",1,colorF,"MUY",1,colorD,"
conveniente anular el "
DB "disco previo antes de modificar su tamao. Con
",1,colorDm
DB " ",10
DB 3,1,colorD," ms de un disco presente se pueden
distinguir "
DB "indicando la letra de unidad. ",1,colorDm," ",10
DB 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," ",10
DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorE
DB " (C) 1995 Ciriaco Garca de Celis. ",1,colorG
DB "(Mail: ciri@gui.uva.es). ",1,colorDm," ",10
DB 3,1,colorE," (C) Grupo Universitario de Informtica.
"
DB "Apartado 6062, Valladolid (Spanien).
",1,colorDm," ",10
DB 3,1,colorC,2,78," ",1,colorDm," ",10
DB 3,1,colorD," Willkommen bei der
",1,colorF,"schnelleren"
DB 1,colorD," RAM-Disk, die auch EMS-, XMS- und
konven- ",1,colorDm," ",10
DB 3,1,colorD," tionellen Speicher untersttzt;
grenverstellbar,"
DB " einfache Bedienung wie ",1,colorDm," ",10
DB 3,1,colorD," bei DOS-RAM-Disks, erfordert maximal
608 Bytes. "
DB 1,colorH," Das Programm ist Freeware!.
",1,colorDm," ",10
DB 3,1,colorC,2,78," ",1,colorDm," ",10
DB 3,1,colorC," DEVICE=TDSK.EXE [Gre [Sekt. [Dateien
[Cluster]]]]"
DB " [/E] [/A|X] [/C] [/M] ",1,colorDm," ",10
DB 3,1,colorC,2,78," ",1,colorDm," ",10
DB 3,1,colorD," ",1,colorF," ",1,colorD," Zulssig fr
Gre: 8-65534 KB;"
DB " zulssig fr Sektoren: 32-2048 Bytes (2er-
",1,colorDm," ",10
DB 3,1,colorD," Potenz), obwohl einige DOS-Versionen
nur 128,"
DB " 256 und 512 untersttzen. ",1,colorDm," ",10
DB 3,1,colorD, " Zulssige Anzahl der
Verzeichniseintrge: "
DB "1-65534, Sektoren/Cluster: 1-255
",1,colorDm," ",10
DB 3,1,colorD," (einige Systeme erforden
2er-Potenzen)."
DB " Nur die Grenangabe ist notwendig.
",1,colorDm," ",10
DB 3,1,colorD," ",1,colorF," ",1,colorD," Bei
",1,colorC, "/E",1,colorD
DB " wird XMS, bei ",1,colorC, "/A",1,colorD," oder
",1,colorC,"/X",1,colorD
DB " wird EMS, und bei ",1,colorC, "/C",1,colorD,"
wird konventioneller ",1,colorDm
DB " ",10,3,1,colorD, " Speicher benutzt. Normalerweise
versucht TURBODSK,"
DB " XMS oder EMS zu benutzen. ",1,colorDm," ",10
DB 3,1,colorD,32,1,colorF," ",1,colorD," Nach der
Installation in"
DB " CONFIG.SYS sollte TURBODSK spter nochmal ausge-
",1,colorDm," ",10
DB 3,1,colorD," fhrt werden, um die Gre zu ndern
(den"
DB " Speicherverbrauch); dadurch wird
",1,colorDm," ",10
DB 3,1,colorD," der Inhalt der RAM-Disk gelscht.
Durch Gre 0 wird"
DB " die RAM-Disk komplett ",1,colorDm," ",10
DB 3,1,colorD," gelscht, bei Verwendung von
konventionellem Speicher"
DB " kann eine Annulierung ",1,colorDm," ",10
DB 3,1,colorF," VOR",1,colorD," der Grenvernderung
sinnvoll sein. Wenn mehrere"
DB " TURBODSKs installiert ",1,colorDm," ",10
DB 3,1,colorD," sind, knnen diese durch ihren
Laufwerksbuchstaben"
DB " angesteuert werden. ",2,6," ",1,colorDm," ",10
DB 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," ",10
DB 3,1,colorE," ",1,colorAm,2,14," ",1,colorE
DB " (C) 1995 Ciriaco Garcia de Celis. ",1,colorG
DB "(Mail: ciri@gui.uva.es).",1,colorDm," ",10
DB 3,1,colorE," (C) Grupo Universitario de
Informtica. "
DB "Apartado 6062, Valladolid (Spain).
",1,colorDm," ",10
DB 3,1,colorC,2,77," ",1,colorDm," ",10
DB 3,1,colorD," Welcome to the
244 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
",1,colorF,"faster",1,colorD
DB " RAM disk!, which includes support of both EMS,
XMS "
DB 1,colorDm," ",10
DB 3,1,colorD," and conventional memory. Full
resizeable, easy to "
DB "use like DOS RAM disks, ",1,colorDm," ",10
DB 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," ",10
DB 3,1,colorC,2,77," ",1,colorDm," ",10
DB 3,1,colorC," DEVICE=TDSK.EXE [size [s_sector [files
[s_cluster]]]]"
DB " [/E] [/A|X] [/C] [/M] ",1,colorDm," ",10
DB 3,1,colorC,2,77," ",1,colorDm," ",10
DB 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,colorDm
DB " ",10
DB 3,1,colorD," bytes (in power of 2), though some DOS
versions only "
DB "support 128, 256 & 512 ",1,colorDm," ",10
DB 3,1,colorD," bytes. Files of root may be 1 to 65534
and sectors "
DB "by cluster can vary from ",1,colorDm," ",10
DB 3,1,colorD," 1 to 255 (some systems need a power of
2). Only the "
DB "size is necessary.",2,6," ",1,colorDm," ",10
DB 3,1,colorD," ",1,colorF," ",1,colorC,"
/E",1,colorD," force the "
DB "use of XMS memory, ",1,colorC,"/A",1,colorD," and
",1,colorC
DB "/X",1,colorD," indicates the use of EMS memory
",1,colorDm
DB " ",10
DB 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 " ",10
DB 3,1,colorD," ",1,colorF," ",1,colorD," After been
installed in "
DB "CONFIG.SYS, TURBODSK must be executed in AUTOEXEC
",1,colorDm
DB " ",10
DB 3,1,colorD," or command line in order to vary the
disk size (the "
DB "amount of memory used); ",1,colorDm," ",10
DB 3,1,colorD," this operation erase the disk contents.
A size 0 "
DB "can be used to complitely ",1,colorDm," ",10
DB 3,1,colorD," anulation of the disk freezen the
memory: when using "
DB "conventional memory it ",1,colorDm," ",10
DB 3,1,colorD," is useful to annulate the disk
",1,colorF,"BEFORE"
DB 1,colorD," resizing. When more than one TURBODSK
",1,colorDm
DB " ",10
DB 3,1,colorD," is installed, they can be identified
using in "
DB "adition the drive letter.",2,5," ",1,colorDm," ",10
DB 3,1,1*16," ",1,colorDm,2,76," ",10
DB 0
tam_a_trabajo EQU 4096 ; tamao del mayor sector
soportado
; 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 usuario
interaccionar con l. Para ello se ha definido oficialmente un mecanismo de comunicacin: el control IOCTL.
En principio, un controlador de dispositivo puede ser hallado recorriendo la cadena de controladores de
dispositivo para localizarlo y acceder directamente a su cdigo y datos. Sin embargo, en los controladores
ms evolucionados, el mtodo IOCTL es el ms recomendable.
El control IOCTL (que permite separar el flujo de datos con el dispositivo de la informacin de
control) se ejerce por medio de la funcin 44h del DOS, siendo posible lo siguiente:
- Averiguar los atributos de un controlador de dispositivo, a partir del nombre. Esto permite, entre
otras cosas, distinguir entre un dispositivo real y un fichero con el mismo nombre. Seguro que el lector ha
construido alguna vez un programa que abre un fichero de salida de datos con el nombre que indica el
usuario: hay usuarios muy pillines que en lugar del clsico PEPE.TXT prefieren indicar, por ejemplo, CON,
estropeando la bonita pantalla que tanto trabajo haba costado pintar. Una solucin consiste, antes de abrir
el fichero de salida, en asegurarse de que es realmente un fichero.
- Leer del controlador o enviarle una tira de caracteres de control. Esto slo es posible si el
controlador soporta IOCTL. Por ejemplo, un driver encargado de gestionar un puerto serie especial podra
admitir cadenas del tipo "9600,n,8,1" para fijar la velocidad de transmisin, paridad, etc. El trabajo que
requiere codificar la rutina IOCTL OUTPUT, encargada de recibir estos datos, puede en muchos casos
merecer la pena.
- Averiguar el estado del controlador: saber si tiene caracteres disponibles, o si ya ha transmitido el
ltimo enviado. Esta caracterstica, entre otras, es implementada por la orden IOCTL INPUT del controlador.
Para obtener informacin detallada acerca de la funcin 44h del DOS hay que consultar, lgicamente,
la bibliografa al respecto (recomendable el INTERRUP.LST).
245 EL HARDWARE DE APOYO AL MICROPROCESADOR
Captulo XII: EL HARDWARE DE APOYO AL MICROPROCESADOR
En este captulo se mostrar detenidamente el funcionamiento de todos los chips importantes que lleva
el ordenador en la placa base y alguno de los colocados en las tarjetas de expansin.
Nota: Por limitaciones tcnicas, al describir los circuitos integrados las seales que son activas
a nivel bajo no tendrn la tradicional barra negadora encima; en su lugar aparecern
precedidas del signo menos: -CS, -WR, -MEMR, ...
En algunos casos, acceder directamente a los chips no es necesario: en general, es mejor dejar el
trabajo al DOS, o en su defecto a la BIOS. Sin embargo, hay casos en que es estrictamente necesario hacerlo:
por ejemplo, para programar temporizaciones, hacer sonidos, comunicaciones serie por interrupciones, acceso
a discos de formato no estndar, etc. Algunas veces bastar con la informacin que aparece en el apartado
donde se describe la relacin del chip con los PC; sin embargo, a menudo ser necesario consultar la
informacin tcnica del apartado ubicado inmediatamente antes, para lo que bastan unos conocimientos
razonables de los sistemas digitales. Los ordenadores modernos normalmente no llevan los integrados
explicados en este captulo; sin embargo, poseen circuitos equivalentes que los emulan por completo.
12.1. - LAS CONEXIONES DEL 8088.
Resulta interesante tener una idea global de las conexiones del 8086 con el exterior de cara a entender
mejor la manera en que interacciona con el resto de los elementos del ordenador. Se ha elegido el 8088 por
ser el primer procesador que tuvo el PC; a efectos de entender el resto del captulo es suficiente con el 8088.
El 8088 puede trabajar en dos modos: mnimo (pequeas aplicaciones) y mximo (sistemas
multiprocesador). Los requerimientos de conexin con el exterior cambian en funcin del modo que se decida
emplear, aunque una parte de las seales es comn en ambos.
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
LNEAS COMUNES AL MODO MXIMO Y MNIMO DEL 8088.
AD7..0: Address Data Bus. Son lneas multiplexadas, que pueden actuar
como bus de datos o de direcciones, evidentemente en tiempos
distintos.
A15..8: Address Bus. En todo momento almacenan la parte media del bus
de direcciones.
A19..16/S6..3: Address/Status. Parte alta del bus de direcciones, multiplexada:
cuando no salen direcciones, la lnea S5 indica el estado del
bandern de interrupciones; las lneas S4:S3 informan del registro
de 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 de
entrada/salida.
READY: Ready. Lnea de entrada que indica el final de la operacin de
memoria o E/S.
INTR: Interrupt Request. Lnea de peticin de interrupciones
enmascarables; el 8088 la observa peridicamente.
-TEST: Test. En respuesta a la instruccin mquina WAIT (no TEST!), el
8088 se para a comprobar esta lnea hasta que se ponga a 0.
NMI: Non-maskable Interrupt. Lnea de peticin de la interrupcin de
tipo 2, que no puede ser enmascarada.
RESET: Provoca una inicializacin interna que culmina saltando a FFFF:0.
MN/-MX: Esta lnea indica si se trata de un sistema mnimo o mximo.
246 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
LNEAS EXCLUSIVAS DEL MODO MNIMO DEL 8088.
IO/-M: Status Line. Indica si se trata de un acceso a memoria o a un puerto de entrada/salida. No es vlida todo el tiempo (solo a ratos).
-wr: Write. Indica una escritura en memoria o en un dispositivo de entrada/salida (segn el estado de IO/-M).
-INTA: Interrupt Acknowledge. Es la seal de reconocimiento de interrupcin (solicitada a travs de INTR o NMI).
ALE: Address Latch Enable. Indica al exterior que las lneas de direccin contienen una direccin vlida, con objeto de que la
circuitera externa la almacene en una pequea memoria (latch). Seal necesaria slo por culpa de la multiplexacin.
DT/-R: Data Transmit/Receive. Seal necesaria para emplear un transceiver 8286/8287 en el bus, con objeto de controlar el flujo de
datos a travs del mismo (si se recibe/transmite).
-DEN: Data Enable. Necesario tambin para emplear el transceiver: sirve como entrada de habilitacin para el mismo.
HOLD: Hold. Lnea de entrada para solicitar al 8088 que se desconecte de los buses. Empleada por los controladores de DMA.
HLDA: Hold Acknowledge. Lnea complementaria de HOLD: el 8088 enva una seal de reconocimiento cuando se desconecta del bus.
-SS0: Status Line. Lnea de apoyo que, junto con IO/-M y DT/-R, permite determinar con precisin el estado del bus:
IO/-M DT/-R -SS0 Estado del bus
1 0 0 Reconocimiento de interrupcin
1 0 1 Lectura de puerto E/S
1 1 0 Escritura en puerto E/S
1 1 1 Estado Halt
0 0 0 Acceso a cdigo
0 0 1 Lectura de memoria
0 1 0 Escritura en memoria
0 1 1 Inactivo
LNEAS EXCLUSIVAS DEL MODO MXIMO DEL 8088.
-S0/-S1/-S2: Status. Estas lneas indican el estado del bus:
-S2 -S1 -S0 Estado del bus
0 0 0 Reconocimiento de interrupcin
0 0 1 Lectura de puerto E/S
0 1 0 Escritura en puerto E/S
0 1 1 Estado Halt
1 0 0 Acceso a cdigo
1 0 1 Lectura de memoria
1 1 0 Escritura en memoria
1 1 1 Inactivo
-RQ/-GT0..1: Request/Grant. Estas patillas bidireccionales permiten a los dems procesadores conectados al bus forzar al 8088 a que libere
el bus al final del ciclo en curso.
-LOCK: Lock. Lnea que sirve al 8088 para prohibir el acceso al bus a otros procesadores (se activa tras la instruccin mquina LOCK
y dura mientras se ejecuta la siguiente instruccin -la que sigue a LOCK, que es realmente un prefijo-). Tambin se activa
automticamente en los momentos crticos de un ciclo de interrupcin.
QS1/QS0: Queue Status. Permite determinar el estado de la cola de instrucciones del 8088.
DIFERENCIAS IMPORTANTES CON EL 8086.
El 8086 cambia el patillaje sensiblemente, aunque la mayora de las seales son similares. En lugar de 8 lneas de datos y direcciones
multiplexadas (AD0..7) el 8086 posee 16, ya que el bus de datos es de 16 bits. Existe una lnea especialmente importante en el 8086, -BHE/S7 (Bus
High Enables/Status), que normalmente indica si se accede a la parte alta del bus de datos o no (operaciones 8/16 bits). El 8086 posee una cola de
instrucciones de 6 bytes, en lugar de 4.
FORMATO DE LAS INSTRUCCIONES DEL 8086.
Resulta absurdo estudiar la composicin binaria de las instrucciones mquina de ningn procesador;
en los casos en que sea necesario se pueden ver los cdigos con alguna utilidad de depuracin. Sin embargo,
a ttulo de curiosidad, se expone a continuacin el formato general de las instrucciones (aunque hay algunas
excepciones y casos especiales).
Cdigo de Operacin D W MOD REG REG/MEM byte/palabra despl. byte/palabra inmed.
El cdigo de operacin ocupa 6 bits; el bit D indica si es el operando fuente (=0) el que est en el campo registro (REG)
o si lo es el operando destino (=1): la razn es que el 8086 slo admite un operando a memoria, como mucho (o el fuente, o el
destino, no los dos a la vez). El bit W indica el tamao de la operacin (byte/palabra). MOD indica el modo de direccionamiento:
00-sin desplazamiento (no existe campo de desplazamiento), 01-desplazamiento de 8 bits, 10-desplazamiento de 16 bits y 11-registro
(tanto fuente como destino estn en registro). El campo REG indica el registro involucrado en la instruccin, que puede ser de 8
16 bits (segn indique W): 0-AX/AL, 1-CX/CL, 2-DX/DL, 3-BX/BL, 4-SP/AH, 5-BP/CH, 6-SI/DH, 7-DI/BH; en el caso de registros
de segmento slo son significativos los dos bits de menor peso: 00-ES, 01-CS, 10-SS, 11-DS. El campo R/M, en el caso de modo
registro (MOD=11) se codifica igual que el campo REG; en caso contrario se indica la forma en que se direcciona la memoria: 0:
[BX+SI+desp], 1: [BX+DI+desp], 2: [BP+SI+desp], 3: [BP+DI+desp], 4: [SI+desp], 5: [DI+desp], 6: [BP+desp], 7: [BX+desp].
247 EL HARDWARE DE APOYO AL MICROPROCESADOR
12.2. - EL INTERFAZ DE PERIFRICOS 8255.
El PPI 8255 es un dispositivo de E/S general, programable, capaz de controlar 24 lneas con
diferentes configuraciones (entrada/salida) y en hasta 3 modos de operacin.
12.2.1 - DESCRIPCIN 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 seal 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 informacin de estado o datos
procedentes del 8255.
-WR: Utilizada por la CPU para enviar palabras de control o datos al
8255.
A0..A1: Lneas de direccin: 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.
DESCRIPCIN FUNCIONAL
Las dos lneas de direcciones definen cuatro puertos de E/S en
el ordenador: los tres primeros permiten acceder a los puertos A, B y
C; el cuarto sirve para leer o escribir la palabra de control. El 8255
est dividido en dos grupos internos: el grupo A, formado por el
puerto A y los 4 bits ms significativos del puerto C; y el grupo B,
constituido por el puerto B junto a los 4 bits menos significativos del
puerto C. El puerto C est especialmente diseado para ser dividido en
dos mitades y servir de apoyo a los puertos A y B en algunos sistemas.
PROGRAMACIN DEL 8255
El 8255 soporta 3 modos de operacin: el modo 0 (entrada y salida bsica), el modo 1 (entrada y
salida con seales de control) y el modo 2 (bus bidireccional de comunicaciones). Tras un Reset, los 3
puertos quedan configurados en modo entrada, con las 24 lneas puestas a "1" gracias a la circuitera interna.
Esta configuracin por defecto puede no obstante ser alterada con facilidad. El modo para el puerto A y B
se puede seleccionar por separado; el puerto C est dividido en dos mitades relacionadas con el puerto A y
el B. Todos los registros de salida son reseteados ante un cambio de modo, incluyendo los biestables de
estado. 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 travs de la direccin E/S que les corresponde,
como se vio en el apartado anterior. La palabra de control a enviar a la 4 direccin es:
1 D6 D5 D4 D3 D2 D1 D0
GRUPO A: GRUPO B:
-------- --------
Puerto C (parte baja)
Modo 1 - Entrada, 0 - Salida
00 - 0, 01 - 1, 1X - 2 Puerto B
Puerto A 1 - Entrada, 0 - Salida
1 - Entrada, 0 - Salida Modo
Puerto C (Parte alta) 0 1
1 - Entrada, 0 - Salida
Si el bit ms significativo de la palabra de control est borrado, es tratada entonces como un comando
especial que permite activar o inhibir selectivamente los bits del puerto C:
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 lneas
del puerto C pueden ser activadas o inhibidas simplemente poniendo a 1 0, respectivamente, el flip-flop
interno INTE correspondiente a la interrupcin que se trate. Todos son puestos a cero tras establecer el modo.
MODOS DE OPERACIN DEL 8255
MODO 0: Esta configuracin 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 ledos y escritos sin ms, sin ningn tipo de control adicional. Los puertos pueden ser configurados de entrada (sin
latch) o salida (los datos permanecen memorizados en un latch).
MODO 1: Este modo es el strobed input/output (entrada/salida a travs de un protocolo de seales). Existen dos grupos (A y B) formados por
los puertos A y B ms el puerto C, que es repartido a la mitad entre ambos grupos para gestionar las seales de control. Tanto si
se configura de entrada como de salida, los datos permanecen en un latch. Con este modo es factible conectar dos 8255 entre s para
realizar transferencias de datos en paralelo a una velocidad considerable, con posibilidad de generar interrupciones a la CPU en el
momento en que los datos son recibidos o hay que enviar uno nuevo (consltese documentacin tcnica).
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 flujo
regulado de nuevo por seales de control a travs del puerto C. Este modo slo puede operar en el Grupo A. Tanto las entradas como
salidas son almacenadas en latch.
NOTA: Existen varias combinaciones posibles de estos modos, en las que las lneas del puerto C que no son empleadas como seales de
control pueden actuar como entradas o salidas normales, quedando las lneas de control fuera del rea de influencia de los comandos
que 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 que
ciertos registros realizan algunas funciones que en los PC/XT realiza el 8255; por ello, en estas mquinas NO
se puede programar el 8255 (ha sido eliminado y no existe nada equivalente). El 8255 de los PC/XT est
conectado a la direccin base E/S 60h; por ello, los puertos A, B y C se acceden, respectivamente, a travs
de los puertos de E/S 60h, 61h y 62h; la palabra de control se enva por el puerto 63h: la BIOS del PC y XT
programa el 8255 con una palabra de control 10011001b, que configura todos los puertos en el modo 0, con
el A y C de entrada y el B de salida. El 8255 es empleado, bsicamente, para almacenar los datos que llegan
del teclado (puerto A), para leer la configuracin del ordenador en los conmutadores de la placa base (puerto
C) y para controlar el altavoz y la velocidad en los XT-Turbo (puerto B).
12.2.3 - UN MTODO PARA AVERIGUAR LA CONFIGURACIN DEL PC/XT.
Aviso: los PC tienen un byte de identificacin 0FFh; los XT 0FEh (este byte est en la posicin de memoria 0FFFF:0Eh); por otro
lado, parte de esta informacin es accesible tambin por medio de la variable BIOS ubicada en 40h:10h, mtodo mucho ms recomendable.
Puerto A (60h): tiene una doble funcin: cuando el bit 7 del puerto B est a 1, el puerto A recibe el cdigo de rastreo de la tecla
pulsada, que luego puede ser ledo desde la interrupcin del teclado. Si el bit 7 del puerto B est a 0, entonces el puerto A devuelve informacin
sobre la configuracin del sistema en los PC (no en los XT): en el bit 0 (a 1 si hay disqueteras), bits 2..3 (nmero de bloques de 16 kb de
memoria que obsoleto e intil!), bits 4..5 (tipo de pantalla: 11 MDA, 10 Color 80x25, 01 Color 40x25) y bits 6..7 (nmero de unidades de disco,
si el bit 0=1).
Puerto B (61h): bit 0 (PC/XT: conectado a la lnea GATE del contador 2 del 8253), bit 1 (PC/XT: conectado al altavoz), bit 2 (slo
PC: 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 seales de error en el slot de expansin), bit 6 (PC/XT: a 1 activa la seal
de reloj del teclado), bit 7 (en PC: empleado para seleccionar la funcin del puerto A; tanto en PC como en XT sirve adems para enviar una
seal de reconocimiento al teclado).
Puerto C (62h):
Si el bit 2 del puerto B (PC) o el bit 3 del puerto B (XT) estn a 1:
- En los PC: los bits 0..3: mitad inferior del 2 banco de conmutadores de la placa base (RAM en slots de expansin); 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 expansin), bit 7 (1 si comprobar error de paridad).
Si el bit 2 del puerto B (PC) o el bit 3 del puerto B (XT) estn a 1:
- En los PC: bits 0..3 parte alta del segundo banco de conmutadores de configuracin (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 estn igual que en el caso anterior (no dependen del bit 2 3 del puerto B).
249 EL 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 de
sucesos, generador de ritmo programable, generador de onda cuadrada, etc. En este captulo, la informacin
vertida estar relacionada con el 8254 que equipa a los AT, algo ms potente que el 8253 de los PC/XT; sin
embargo, las pocas diferencias sern comentadas cuando llegue el caso.
12.3.1 - DESCRIPCIN DEL INTEGRADO.
Este circuito integrado posee 3 contadores totalmente independientes, que pueden ser programados
de 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: Lneas de direccin para seleccionar uno de los tres
contadores o el registro de la palabra de control.
-CS: Habilita la comunicacin con la CPU.
-WR: Permite al 8254 aceptar datos de la CPU.
-RD: Permite al 8254 enviar datos a la CPU.
DESCRIPCIN FUNCIONAL
El diagrama funcional del 8254, con la estructura interna de las diversas partes que lo componen, se
muestra a la izquierda. A la derecha, diagrama de los bloques internos de un contador:
BUFFER CLK 0 REGISTRO DE LATCH DE
DEL BUS CONTADOR 0 GATE 0 LA PALABRA ESTADO
DE DATOS OUT 0 DE CONTROL
CR CR
D0..D7 M L
REGISTRO
-RD DE ESTADO
-WR LGICA CLK 1
DE LECTURA CONTADOR 1 GATE 1
A0 Y ESCRITURA OUT 1 LGICA
A1 DE CE
CONTROL
-CS
REGISTRO DE CLK 2
LA PALABRA CONTADOR 2 GATE 2
DE CONTROL OUT 2 CLK n OL OL
M L
GATE n
OUT n
El buffer del bus de datos, de 8 bits y tres estados, comunica el 8254 con la CPU. La lgica de
lectura y escritura acepta entradas del bus y genera seales de control para las partes funcionales del 8254.
Las lneas A0..A2 seleccionan uno de los tres contadores o el registro de la palabra de control, para poder
leerlos o escribirlos. El registro de la palabra de control es seleccionado cuando A0=A1=1, este registro
slo puede ser escrito (se puede obtener informacin de estado, como se ver ms adelante, con el comando
read-back del 8254, no disponible en el 8253). Los contadores 1, 2 y 3 son idnticos en su funcionamiento,
por lo que slo se describir uno; son totalmente independientes y cada uno de ellos puede ser programado
en una modalidad diferente. Si se observa el esquema de un contador, a la derecha, se ver el registro de la
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 registro
de la palabra de control y alguna informacin adicional (como se ver despus en el comando read-back).
El contador propiamente dicho est representado en la figura por CE (Counting Element) y es un contador
descendente sncrono de 16 bits que puede ser inicializado. OL
M
y OL
L
son dos latch de 8 bits (OL significa
Output Latch; los subndices M y L estn relacionados con el ms y el menos significativo byte,
respectivamente); ambos son referenciados normalmente como un conjunto denominado OL a secas. Estos
latches siguen normalmente la cuenta descendente de CE, pero la CPU puede enviar un comando para
congelarlos y poder leerlos; tras la lectura continuarn siguiendo a CE. La lgica de control del contador se
encarga de que un slo latch est activo a un tiempo, ya que el bus interno del 8254 es de 8 bits. CE no
puede ser nunca ledo directamente (lo que se lee es OL). De manera anloga, existen un par de registros
CR
M
y CR
L
(CR significa Count Register) que almacenan la cuenta del contador y se la transmiten
convenientemente a CE. Los valores de cuenta se escriben siempre sobre CR (y no directamente sobre CE).
La lgica de control gestiona la conexin con el exterior a travs de las lneas CLK, GATE y OUT.
DESCRIPCIN OPERACIONAL
Tras el encendido del ordenador, el 8254 est en un estado indefinido; con un modo, valor de cuenta
y 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.
Programacin del 8254.
Para programar un contador del 8254 hay que enviar primero una palabra de control y, despus, un
valor de cuenta inicial. Los contadores se seleccionan con las lneas A0 y A1; el valor A0=A1=1 selecciona
la escritura de la palabra de control (en la que se identifica el contador implicado). Por tanto, el 8254 ocupa
normalmente 4 direcciones de E/S consecutivas ligadas a los contadores 0, 1, 2 y al registro de la palabra de
control. Para enviar la cuenta inicial se utiliza simplemente el puerto E/S ligado al contador que se trate. El
formato 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 dcadas
0 0 Contador 0 Operacin: Modo:
0 1 Contador 1 0 0 Comando de enclavamiento 0 0 0 Modo 0
1 0 Contador 2 0 1 Leer/escribir byte bajo 0 0 1 Modo 1
1 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 3
y despus 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 un
lado, 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 slo
byte bajo, enviar slo byte alto, o bien enviar ambos consecutivamente). Teniendo en cuenta que cada
contador tiene su propio puerto y que la palabra de control indica el contador al que est asociada, no hay
que seguir un orden especial a la hora de programar los contadores. Esto significa que, por ejemplo, se puede
enviar la palabra de control de cada contador seguida de su cuenta inicial, o bien enviar todas las palabras
de control para los 3 contadores y despus las 3 cuentas iniciales; tambin es vlida cualquier combinacin
intermedia de estas secuencias (por ejemplo: enviar la palabra de control para el contador 0, despus la
palabra de control para el contador 1, despus la parte baja de la cuenta para el contador 0, luego la parte
baja de la cuenta para el contador 1, la parte alta de la cuenta para el contador 0, etc...).
251 EL HARDWARE DE APOYO AL MICROPROCESADOR
Un nuevo valor de cuenta inicial puede ser almacenado en un contador en cualquier momento, sin que ello
afecte al modo en que ha sido programado (el resultado de esta operacin depender del modo, como se ver
ms adelante). Si se programa el contador para leer/escribir la cuenta como dos bytes consecutivos (bajo y
alto), el sentido comn indica que entre ambos envos/recepciones no conviene transferir el control a una
subrutina que utilice ese mismo contador para evitar un resultado incorrecto.
Operaciones de lectura.
Existen tres posibles mtodos para leer el valor de un contador en el 8254. El primero es el comando
Read-Back, slo disponible en el 8254 (y no en el 8253), como luego veremos. El segundo consiste en leer
simplemente el contador accediendo a su puerto correspondiente: este mtodo requiere inhibir la entrada CLK
al contador (por ejemplo, a travs de la lnea GATE o utilizando circuitera exterior de apoyo) con objeto de
evitar leer la cuenta en medio de un proceso de actualizacin de la misma, lo que dara un resultado
incorrecto. El tercer mtodo consiste en el comando de enclavamiento.
Comando de enclavamiento (Counter Latch Command).
Este comando se enva cual si de una palabra de control se tratara (A1=A0=1): para diferenciarlo de
ellas los bits 5 y 4 estn a cero. En los bits 7 y 6 se indica el contador afectado. Los dems bits deben estar
a cero para compatibilizar con futuras versiones del chip. Cuando se enva el comando, el OL del contador
seleccionado queda congelado hasta que la CPU lo lee, momento en el que se descongela y pasa de nuevo
a seguir a CE. Esto permite leer los contadores al vuelo sin afectar la cuenta en curso. Se pueden enviar
varios de estos comandos a los diversos contadores, cuyos OLs quedarn enclavados hasta ser ledos. Si se
envan varios comandos de enclavamiento al mismo contador, separados por un cierto intervalo de tiempo,
slo se considerar el primero (por tanto, la cuenta leda corresponder al valor del contador cuando fue
enclavado por vez primera).
Por supuesto, el contador debe ser ledo utilizando el formato que se defini al enviar la palabra de
control; aunque en el caso de leer 16 bits, las dos operaciones no han de ser necesariamente consecutivas (se
pueden insertar en el medio otras acciones relacionadas con otros contadores).
Otra caracterstica interesante (disponible tal vez slo en el 8254?) consiste en la posibilidad de
mezclar lecturas y escrituras del mismo contador. Por ejemplo, si ha sido programado para cuentas de 16 bits,
es vlido hacer lo siguiente: 1) leer el byte menos significativo, 2) escribir el nuevo byte menos significativo,
3) leer el byte ms significativo, 4) escribir el nuevo byte ms significativo.
Comando Read-Back.
Slo est disponible en el 8254, no en el 8253. Este comando permite leer el valor actual de la
cuenta, as como averiguar tambin el modo programado para un contador y el estado actual de la patilla
OUT, adems de verificar el bandern de cuenta nula (Null Count) de los contadores que se indiquen. El
formato 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 cuenta
de los contadores a 1 los contadores seleccionados
seleccionados
0 Si enclavar el byte de estado del contador seleccionado
El comando Read-Back permite enclavar la cuenta en varios OLs de mltiples contadores de una
sola vez, sin requerir mltiples comandos de enclavamiento, poniendo el bit 5 a cero. Todo funciona a partir
de aqu como cabra esperar (los contadores permanecen enclavados hasta ser ledos, los que no son ledos
252 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
permanecen enclavados, si el comando se reitera slo acta la primera vez reteniendo la primera cuenta...).
Tambin es posible enviar informacin de estado al latch OL, enclavndola para que puede ser leda con
comodidad 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 BCD
COUNT
Contador:
valor de la patilla OUT 0 Binario 16 bits
modo activo 1 BCD 4 dcadas
1 "Null Count"
0 Cuenta disponible para ser leda
En D0..D5 se devuelve justo la misma informacin que se envi en la ltima palabra de control; en
el bit D7 se entrega el estado actual de la patilla OUT del 8254, lo que permite monitorizar por software las
salidas del temporizador economizando hardware en ciertas aplicaciones. El bit NULL COUNT (D6) indica
cundo la ltima cuenta escrita en CR ha sido transferida a CE: el momento exacto depende del modo de
funcionamiento del contador. Desde que se programa un nuevo valor de cuenta, pasa un cierto tiempo hasta
que ste valor pasa de CR a CE: leer el contador antes de que se haya producido dicha transferencia implica
leer un valor no relacionado con la nueva cuenta. Por ello, segn las aplicaciones, puede llegar a ser necesario
esperar a que NULL COUNT alcance el valor 0 antes de leer. El funcionamiento es el siguiente:
Operacin Consecuencias
A - Escribir al registro de la palabra de control (1) NULL COUNT = 1
B - Escribir al registro contador (CR) (2) NULL COUNT = 1
C - Nueva cuenta cargada en CE (CR - CE) NULL COUNT = 0
Notas: (1) Slo el contador especificado por la palabra de control tiene su NULL COUNT a 1; los
dems contadores, lgicamente, no ven afectado su correspondiente bit NULL COUNT.
(2) Si el contador es programado para cuentas de 16 bits, NULL COUNT pasa a valer 1
inmediatamente despus de enviar el segundo byte.
Si se enclava varias veces seguidas la palabra de estado, todas sern ignoradas menos la primera, por
lo que el estado ledo ser el correspondiente al contador en el momento en que se enclav por vez primera
la palabra de estado.
Se pueden enclavar simultneamente la cuenta y la palabra de estado (en un comando Read-Back con
D5=D4=0), lo que equivale a enviar dos Read-Back consecutivos. En este caso, y con independencia de quin
de los dos hubiera sido enclavado primero, la primera lectura realizada devolver la palabra de estado y la
segunda la cuenta enclavada (que automticamente quedar de nuevo desenclavada).
MODOS DE OPERACIN DEL 8254
MODO 0: Interrupt On Terminal Count (Interrupcin al final de la cuenta).
Es empleado tpicamente 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 1
y no volver a bajar hasta que se escriba una nueva cuenta o una nueva palabra de control. La entrada GATE
puesta 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 en
el prximo pulso del reloj conectado (CLK), pulso que no decrementa el contador: para una cuenta inicial
N, 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 prximo pulso del reloj y el
contador comenzar a decrementarse; si se enva una cuenta de dos bytes, el primer byte enviado inhibe la
cuenta y OUT es puesto a cero inmediatamente (sin esperar a CLK): tras escribir el segundo byte, la cuenta
ser cargada en el siguiente pulso del reloj. Esto permite sincronizar la secuencia de conteo por software.
253 EL HARDWARE DE APOYO AL MICROPROCESADOR
Si se escribe una nueva cuenta mientras GATE=0, sta ser cargada en cualquier caso en el siguiente
pulso 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
OUT
5 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 hasta
el pulso del reloj que siga al prximo flanco de subida de GATE.
Tras escribir la palabra de control y la cuenta inicial, el contador est preparado. Un flanco de subida
de GATE provoca la carga del contador (CR CE) y que OUT baje en el prximo pulso del reloj,
comenzando el pulso One-Shot de N ciclos de reloj de duracin; el contador vuelve a ser recargado si se
produce un nuevo flanco de subida de GATE, de ah que OUT permanezca en bajo durante N pulsos de reloj
tras la ltima vez que suceda esto. El pulso One-Shot puede repetirse sin necesidad de recargar el contador
con 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, lgicamente, que se produzca un nuevo flanco de subida de GATE: en ese caso, el contador sera
recargado con el nuevo valor.
CLK
(N=4)
-WR
GATE
OUT
4 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 tpicamente 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 cuenta
inicial, repitindose el proceso. Este modo es, por tanto, peridico, y la misma secuencia se repite
indefinidamente. 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, OUT
sube inmediatamente. Un flanco de subida en GATE provoca una recarga del contador con el valor de cuenta
inicial en el siguiente pulso del reloj (despus, como cabra esperar, OUT bajar tras los N pulsos del reloj
correspondientes): GATE puede ser utilizado para sincronizar el contador.
Tras escribir la palabra de control y la cuenta inicial, el contador ser cargado en el prximo pulso
del reloj: OUT bajar N pulsos de reloj despus, lo que permite tambin una sincronizacin por software.
Escribir un nuevo valor de cuenta durante el funcionamiento del contador no afecta a la actual
secuencia de cuenta; si se recibe un flanco de subida de GATE antes del final del perodo el contador se
recargar con ese nuevo valor de cuenta inicial tras el prximo pulso del reloj y volver a comenzar, en caso
contrario se recargar con el nuevo valor tras finalizar con normalidad el ciclo en curso.
CLK
(N=4) (N=3)
-WR
OUT
4 3 2 1 0(4) 3 2 1 0(3) 2 1 0
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 generacin de una seal 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 tambin peridico: la onda resultante para una cuenta inicial N tiene un perodo de N ciclos.
Si GATE=0 la cuenta descendiente se detiene: si GATE es bajado durante un pulso de salida, OUT
sube inmediatamente sin esperar ningn CLK. Un flanco de subida en GATE provoca una recarga del
contador con el valor de cuenta inicial en el siguiente pulso del reloj: GATE puede ser utilizado para
sincronizar el contador.
Tras escribir la palabra de control y la cuenta inicial, el contador ser cargado en el prximo pulso
del reloj: tambin puede ser sincronizado por software.
Escribir un nuevo valor de cuenta durante el funcionamiento del contador no afecta a la actual
secuencia de cuenta; si se recibe un flanco de subida de GATE antes del final del medio-perodo el contador
se recargar con ese nuevo valor de cuenta inicial tras el prximo pulso del reloj y volver a comenzar, en
caso contrario se recargar con el nuevo valor tras finalizar con normalidad el medio-ciclo en curso.
Para valores de cuenta impares, la duracin a nivel alto de OUT ser un perodo de reloj mayor que
la duracin a nivel bajo.
CLK
4 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 prximo pulso del reloj: como ese pulso no
decrementa el contador, para una cuanta inicial N, OUT no bajar hasta N+1 pulsos de CLK. Si se escribe
una nueva cuenta durante el proceso, se cargar en el prximo pulso CLK y continuar el proceso de cuenta
con la nueva cuenta escrita; si la cuenta es de 2 bytes, al escribir el primero no se altera el funcionamiento
del contador hasta que se enve 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 seal GATE, el contador comienza a
decrementar la cuenta. Cuando llega a cero, OUT baja durante un pulso CLK y luego vuelve a subir.
Despus 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 despus
de que expire la cuenta actual, el contador ser cargado con la nueva cuenta en el prximo pulso del reloj.
CLK
GATE 4 3 4 3 2 1 0
OUT
255 EL HARDWARE DE APOYO AL MICROPROCESADOR
Operacin de GATE Rango de las cuentas
MODO Bajo o Bajando Subiendo Alto Mnima Mxima
0 Desactiva la cuenta -- Activa la cuenta 1 0
1) Inicia la cuenta
1 -- 2) Resetea OUT tras -- 1 0
el siguiente CLK
1) Desactiva cuenta 1) Carga contador
2 2) Pone OUT en alto 2) Inicia la cuenta Activa la cuenta 2 0
inmediatamente
1) Desactiva cuenta 1) Carga contador
3 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 un
8253, algo menos verstil; los PS/2 ms avanzados tienen un temporizador con un cuarto contador ligado a
la interrupcin no enmascarable, si bien no lo consideraremos aqu. Todos los contadores van conectados a
un reloj que oscila a una frecuencia de 1.193.180 ciclos por segundo (casi 1,2 Mhz). La direccin base en
el espacio de E/S del ordenador elegida por IBM cuando dise el PC es la 40h. Por tanto, los tres contadores
son accedidos, respectivamente, a travs de los puertos 40h, 41h y 42h; la palabra de control se enva al
puerto 43h.
La seal GATE de los contadores 0 y 1 est siempre a 1; en el contador 2 es seleccionable el nivel
de la lnea GATE a travs de bit 0 del puerto E/S 61h. La BIOS programa por defecto el contador 0 en el
modo 3 (generador de onda cuadrada) y el contador 1 en el modo 2 (generador de ritmo); el usuario
normalmente 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 cadencia
de los pulsos es de 1.193.180/65.536 = 18,2 veces por segundo, valor que determina la precisin del reloj
del sistema, ciertamente demasiado baja. Se puede modificar el valor de recarga de este contador en un
programa, llamando a la vieja INT 8 cada 1/18,2 segundos para no alterar el funcionamiento normal del
ordenador, si bien no es conveniente instalar programas residentes que cambien permanentemente esta
especificacin: los programas del usuario esperan encontrarse el temporizador a la habitual y poco til
frecuencia de 18,2 interrupciones/segundo.
La salida del contador 1 controla el refresco de memoria en todas las mquinas, su valor normal para
el divisor es 18; aumentndolo se puede acelerar el funcionamiento del ordenador, con el riesgo -eso s- de
un fallo en la memoria, detectado por los chips de paridad -si los hay-, que provoca generalmente el bloqueo
del equipo. De todas maneras, en los PC/XT se puede aumentar entre 19 y 1000 sin demasiados riesgos,
acelerndose en ocasiones hasta casi un 10% la velocidad de proceso del equipo. En los AT la ganancia de
velocidad es mucho menor y adems este es un punto demasiado sensible que conviene no tocar para no
correr riesgos, aunque se podra bajar hasta un valor 2-17 para ralentizar el sistema. Sin embargo, no es
conveniente alterar esta especificacin porque, como se ver ms adelante, hay un mtodo para realizar
retardos (empleado por la BIOS y algunas aplicaciones) que se vera afectado.
El contador 2 puede estar conectado al altavoz del ordenador para producir sonido; alternativamente
puede emplearse para temporizar. Es el nico contador que queda realmente libre para el usuario, lo que suele
dar quebraderos de cabeza a la hora de producir sonido.
256 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
12.3.3 - TEMPORIZACIN.
Los contadores 0 y 1, especialmente este ltimo, ya estn ocupados por el sistema; en la prctica el
nico disponible es el 2. Este contador ha sido conectado con el doble propsito de temporizar y de generar
sonido. Para emplearlo en las temporizaciones, es preciso habilitar la puerta GATE activando el bit 0 del
puerto 61h; tambin hay que asegurarse de que la salida del contador no est conectada al altavoz (a menos
que se desee msica mientras se cronometra) poniendo a 0 el bit 1 del mismo puerto (61h):
IN AL,61h
AND AL,11111101b ; borrar bit 1 (conexin contador 2 con el altavoz)
OR AL,00000001b ; activar bit 0 (lnea GATE del contador 2)
JMP SHORT $+2 ; estado de espera para E/S
OUT 61h,AL
El siguiente programa de ejemplo, CRONOS.ASM, incluye dos subrutinas para hacer retardos de alta
precisin. La primera de ellas, inic_retardo, hay que llamarla al principio para que programe el contador 2
del temporizador; la rutina retardo se encarga de hacer el retardo que se indique en AX (en unidades de
1/1193180 segundos).
; ********************************************************************
; * *
; * CRONOS.ASM - Subrutinas para hacer retardos de precisin. *
; * *
; * INIT_RETARDO: llamarla al principio del todo. *
; * RETARDO: Entregar en AX el n de 1193180-avos de *
; * segundo que dura el retardo (mximo 65400). *
; * *
; ********************************************************************
programa SEGMENT
ASSUME CS:programa, DS:programa
ORG 100h
inicio:
CALL inic_retardo
MOV CX,20 ; 20 retardos
MOV AX,59659 ; de 50 milisegundos
retard: CALL retardo
LOOP retard
INT 20h
inic_retardo PROC
PUSH AX
IN AL,61h
AND AL,11111101b
OR AL,1
JMP SHORT $+2
OUT 61h,AL
MOV AL,10110100b ; contador 2, modo 2, binario
JMP SHORT $+2
OUT 43h,AL
POP AX
RET
inic_retardo ENDP
retardo PROC
PUSH AX
PUSH BX
CLI
OUT 42h,AL ; parte baja de la cuenta
MOV AL,AH
JMP SHORT $+2
OUT 42h,AL ; parte alta
JMP SHORT $+2
IN AL,61h
XOR AL,1 ; bajar GATE
JMP SHORT $+2
OUT 61h,AL
XOR AL,1 ; subir GATE
JMP SHORT $+2
OUT 61h,AL
STI
JMP SHORT $+2
MOV BX,0FFFFh
retardando: MOV AL,10000000b
OUT 43h,AL ; enclavamiento
JMP SHORT $+2
IN AL,42h ; leer contador
MOV AH,AL
JMP SHORT $+2
IN AL,42h
XCHG AH,AL ; AX = valor del contador
CMP AX,BX
MOV BX,AX
JBE retardando
POP BX
POP AX
RET
retardo ENDP
programa ENDS
END inicio
El procedimiento inic_retardo programa el contador 2 en el modo 2, con datos en binario y dejndolo
listo para enviar/recibir secuencias de 2 bytes para la cuenta (primero el byte menos significativo y luego el
alto). Las instrucciones JMP SHORT $+2 colocadas oportunamente (para saltar a la siguiente lnea) evitan
que las mquinas AT ms antiguas fallen en dos operaciones de E/S consecutivas demasiado rpidas. El
procedimiento retardo enva el nuevo valor de cuenta. A continuacin baja y vuelve a subir la seal GATE,
con objeto de provocar un flanco de subida en esta lnea, lo cual provoca que el contador se cargue con el
valor recin enviado de manera inmediata (de lo contrario, no se recargara hasta acabar la cuenta anterior).
Finalmente, entramos en un bucle donde se enclava continuamente la cuenta y se espera hasta que acabe. Lo
ms intuitivo sera comprobar si la cuenta es cero, pero esto es realmente difcil ya que cambia nada menos
que ms de 1 milln de veces por segundo!. Por tanto, nos limitamos a comprobar si tras dos lecturas
consecutivas la segunda es mayor que la primera ...no puede ser!... s, si puede ser, si tras llegar a 0 el
contador se ha recargado. De esta manera, el mayor valor admitido en AX al llamar es 65535, aunque no
conviene que sea superior a 65400, para permitir que las recargas puedan ser detectadas en la mquina ms
lenta (un XT a 4.77 y en 135/1193180 segundos dispone de unos 540 ciclos, en los que holgadamente cubre
este bucle).
A la hora de emplear las rutinas anteriores hay que tener en cuenta dos consideraciones. Por un lado,
estn diseadas para hacer pequeos retardos: llamndolas repetidamente, el bucle que hay que hacer (y las
interrupciones que se producen durante el proceso) provoca que retarden ms de la cuenta. Por ejemplo, en
257 EL HARDWARE DE APOYO AL MICROPROCESADOR
el programa principal, poniendo 1200 en CX en lugar de 20, el retardo debera ser de 60 segundos; sin
embargo, comparando este dato con el contador de hora de la BIOS (en una versin ligeramente modificada
del programa) resulta ser de casi 60,2 segundos. La segunda consideracin est relacionada con las
interrupciones: de la manera que est el listado, se puede producir una interrupcin en la que algn programa
residente utilice el contador 2 del temporizador, alterando el funcionamiento de las rutinas de retardo (por
ejemplo, una utilidad de click en el teclado) o incluso provocando un fallo en la misma (si a sta no le da
tiempo a comprobar que ya es la hora): este es un aspecto a tener en cuenta en un caso serio. Se puede, por
ejemplo, inhibir todas las interrupciones (o enmascar slo las ms molestas), aunque anular la interrupcin
del temporizador, la ms peligrosa, provocara un retraso de la hora del ordenador.
Para hacer retardos o temporizaciones de ms de 50 milisegundos, es ms conveniente emplear el
contador de hora de la BIOS (variable de 32 bits en 0040h:006Ch) que la INT 8 se encarga de incrementar
18,2 veces cada segundo y de volver a ponerlo a cero cada 24 horas. No es conveniente mirar el valor del
contador de hora de la BIOS, sumarle una cantidad y esperar a que alcance dicha cantidad fija: la experiencia
demuestra que eso produce a veces cuelgues del ordenador, no solo debido a que suele fallar cuando son las
23:59:59 sino tambin porque cuando se alcanza el valor esperado, por cualquier motivo (tal como un
alargamiento excepcional de la rutina que controla INT 8 INT 1Ch debido a algn programa residente)
puede que el programa principal no llegue a tiempo para comprobar que ya es la hora... y haya que esperar
otras 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 ordenador
vaya diez veces ms rpida -poco recomendable, aunque muy divertido- programando el contador 0 con un
valor de cuenta 6553 (frente al 0=65536 habitual), de la siguiente manera:
MOV AL,00110110b ; contador 0, operacin 11b, datos binarios
OUT 43h,AL
MOV BX,6553 ; valor de cuenta
MOV AL,BL
JMP SHORT $+2
OUT 40h,AL ; enviar byte bajo
MOV AL,BH
JMP SHORT $+2
OUT 40h,AL ; enviar byte alto
Un mtodo genial para hacer retardos y controlar timeouts en AT.
Aunque ausente en todos los manuales de referencia tcnica y en todos los libros relacionados con
la programacin de PC, existe un mtodo muy fcil y eficiente para temporizar disponible en todos los
ordenadores AT. Pese a no estar documentado, un programa muy usual como es el KEYB del MS-DOS (a
partir de la versin 5.0 del sistema) lo utiliza en todos los AT, sin importar el modelo. Por ello, cabe suponer
que seguramente los futuros equipos mantendrn la compatibilidad en este aspecto. Sucede que la salida del
contador 1 del 8254, encargada del refresco de la memoria, controla de alguna manera desconocida (tal vez
a travs de un flip-flop) la generacin de una onda cuadrada de unos 33 KHz que puede leerse a travs del
bit 4 del puerto 61h (no se trata de la salida OUT del contador 1: ste est programado en modo 2 y no
genera precisamente una onda cuadrada). El contador 1 es programado por la BIOS en todos los PC con una
cuenta 18, conmutando el nivel de la salida cada segundo 1193180/18 = 66287,77 veces. Para hacer un
determinado retardo basta con contar las veces que el bit cambia de nivel: la funcin en ensamblador
retardo_asm() del programa de ejemplo lo ilustra. Este mtodo es especialmente interesante en los programas
residentes que precisen retardos de precisin, para sonido u otras tareas, tales como limitar la duracin
mxima de una comprobacin en un bit de estado a unos milisegundos o microsegundos (control de
timeouts); la principal ventaja es que no se modifica en absoluto la configuracin de ningn chip que pueda
estar empleando el programa principal, empezando por el 8254. Adems, no requiere preparacin previa
alguna. Para los ms curiosos, decir que el bit 5 del puerto 61h es la salida OUT del contador 2 del 8254 (la
lnea OUT del contador 2 del 8253 de los PC/XT tambin puede consultarse a travs del bit 5, pero del
puerto 62h).
El nico inconveniente del mtodo es la alta frecuencia con que cambia el bit: esta misma rutina
escrita en C podra no ser suficientemente gil para detectar todas las transiciones en las mquinas AT ms
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 hay
que extremar las precauciones para que el cdigo compilado sea lo bastante rpido: utilizar las dos variables
registro que realmente soportan los compiladores y huir de la aritmtica de 32 bits, como puede observarse
en la funcin retardo_c() del programa de ejemplo. Una mala codificacin o compilador podran hacer
inservible el mtodo incluso en una mquina a 16 20 MHz. Para no tener problemas, es mejor emplear la
versin en ensamblador, escrita en un C no mucho menos estndar. La macro MICRO() ayuda a seleccionar
con ms comodidad el retardo, indicndolo en s, aunque implica una operacin en coma flotante que por
s sola aade unos 100 s de retardo adicionales en un 386-25 sin coprocesador y con las libreras de Borland.
Ancdota: Para los ms curiosos, decir que los programadores de Microsoft emplean este mtodo en el KEYB en dos ocasiones: para
limitar a un tiempo razonable la espera hasta que el registro de entrada del 8042 se llene (15 ms) y, en otra ligera variante, para
controlar la duracin del pitido de error. Los aficionados al ensamblador pueden comprobarlo personalmente aplicando el comando
U del DEBUG sobre el KEYB para desensamblar a partir de los offsets 0E39 y 0D60, respectivamente: en el primer caso, la subrutina
slo es ejecutada en AT; en el segundo, veris como el KEYB se asegura de que el equipo es un AT comprobando el valor de BP
antes de saltar a 0D70 (ejecuta un bucle vaco en las dems mquinas). Esta nueva tcnica ha permitido eliminar respecto a anteriores
versiones del programa algunos test sobre tipos de ordenadores, cuya finalidad ms comn era ajustar las constantes de retardo. Son
vlidos tanto el KEYB del MS-DOS 5.0 castellano como el del MS-DOS 6.0 en ingls o castellano indistintamente (las direcciones
indicadas coinciden!). Tambin en las BIOS modernas suele haber ejemplos de esta tcnica, aunque las direcciones ya no coinciden...
/********************************************************************/
/* */
/* Programa de demostracin del mtodo de retardo basado en la */
/* monitorizacin 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 precisin */
retardo_asm (66267L); /* un segundo */
retardo_asm (MICRO(1000000L)); /* otro segundo (ms claro!) */
retardo_c (66267L); /* ahora en C */
retardo_c (MICRO(1000000L)); /* la otra alternativa */
}
void retardo_asm (long cuenta) /* mtodo ensamblador recomendado */
{
asm push ax
asm push cx
asm push dx
asm 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,ah
asm je esp_ref /* esperar cambio de nivel */
asm mov ah,al
asm loop esp_ref /* completar cuenta baja */
fin_l: asm and dx,dx
asm jz fin_ret /* posible cuenta alta nula */
asm dec dx
asm jmp esp_ref /* completar cuenta alta */
fin_ret: asm pop dx
asm pop cx
asm pop ax
}
void retardo_c (long cuenta) /* mtodo en C no recomendado */
{
register a, b;
unsigned cuenta_h, cuenta_l;
cuenta_h=cuenta >> 16; cuenta_l=cuenta & 0xFFFF;
do
do {
while (a==(b=inportb(0x61) & 0x10));
a=b;
} while (cuenta_l--);
while (cuenta_h--);
}
12.3.4 - SNTESIS DE SONIDO.
La produccin de sonido es uno de los puntos ms dbiles de los ordenadores compatibles, que slo
superan por muy escaso margen a alguno de los micros legendarios de los 80, si bien las tarjetas de sonido
han solventado el problema. Pero aqu nos conformaremos con describir la programacin del altavoz. En
todos los PCs existen dos mtodos diferentes para generar sonido, con la utilizacin del 8254 o sin l, que
veremos por separado.
Control directo del altavoz.
El altavoz del ordenador est ligado en todas las mquinas al bit 1 del puerto E/S 61h. Si se hace
cambiar este bit (mantenindolo durante cierto tiempo alto y durante cierto tiempo bajo, repitiendo el proceso
a gran velocidad) se puede generar una onda cuadrada de sonido. Cuanto ms deprisa se realice el proceso,
mayor ser la frecuencia del sonido. Por fortuna, la baja calidad del altavoz del PC redondea la onda cuadrada
y produce un sonido algo ms musical de forma involuntaria. No existe, en cualquier caso, control sobre el
volumen, que dada la calidad del altavoz tambin est en funcin de la frecuencia. Este mtodo de produccin
de 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 ms o menos ligada a la capacidad de proceso del ordenador:
esto significa que el sonido es ms grave en mquinas lentas y ms agudo en las rpidas. Esto es
particularmente grave y evidente cuando las temporizaciones se hacen con bucles de retardo con registros de
259 EL HARDWARE DE APOYO AL MICROPROCESADOR
la CPU: la frecuencia del sonido est totalmente a merced de la velocidad de la mquina 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 mtodo es que las interrupciones,
fundamentalmente la del temporizador, producen fuertes interferencias sobre el sonido. Por ello, es normal
tenerlas inhibidas, con el consiguiente retraso de la hora. Por ltimo, un tercer gran inconveniente es que la
CPU est completamente dedicada a la produccin de sonido, sin poder realizar otras tareas mientras tanto.
Antes de comenzar a producir el sonido con este mtodo hay que bajar la lnea GATE del 8254, ya
que cuando est en alto y se activa tambin el bit 1 del puerto E/S 61h, el temporizador es el encargado de
producir el sonido (este es el segundo mtodo, como veremos). Por tanto, es preciso poner primero a cero
el bit 0 del mismo puerto (61h):
CLI ; evitar posible INT 8, entre otras
IN AL,61h
AND AL,11111110b
JMP SHORT $+2 ; estado de espera para E/S
OUT 61h,AL ; bajar GATE del contador 2 del 8254
MOV CX,100h ; 256 vueltas
otro_ciclo: PUSH CX
IN AL,61h
XOR AL,2 ; invertir bit 1
JMP SHORT $+2
OUT 61h,AL
MOV CX,300 ; constante de retardo
retardo: LOOP retardo
POP CX
LOOP otro_ciclo
STI
Control del altavoz por el temporizador.
El otro mtodo posible consiste en emplear el contador 2 del temporizador conectado al altavoz; as,
enviando el perodo del sonido (1.193.180/frecuencia_en_Hz) a dicho contador (programado en modo 3), ste
se encarga de generar el sonido. Esto permite obtener sonidos idnticos en todos los ordenadores. Existe el
pequeo problema de que la duracin del sonido ha de ser mltiplo de 1/18,2 segundos si se desea utilizar
el reloj del sistema para determinarla (un bucle de retardo sera, una vez ms, dependiente de la mquina)
ya que el contador 2 est ahora ocupado en la produccin de sonido y no se puede usar para temporizar (al
menos, no sin hacer malabarismos). Alternativamente, se podra evaluar la velocidad de la CPU para ajustar
las constantes de retardo o aumentar la velocidad de la interrupcin peridica.
Para emplear este sistema, primero se prepara el contador 2 para temporizar (poniendo a 1 el bit 0
del 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 calidad
del sonido:
MOV AL,10110110b ; contador 2, modo 3, operacin 11b, datos binarios
OUT 43h,AL ; programar contador 2
MOV AX,2711 ; 1.193.180 / 440 Hz (nota LA) = 2711
JMP SHORT $+2
OUT 42h,AL
MOV AL,AH
JMP SHORT $+2
OUT 42h,AL ; frecuencia programada
JMP SHORT $+2
IN AL,61h
OR AL,00000011b
JMP SHORT $+2
OUT 61h,AL ; altavoz sonando
MOV CX,0
demora: LOOP demora ; esperar un cierto tiempo por el peor mtodo
IN AL,61h
AND AL,11111100b
JMP SHORT $+2
OUT 61h,AL ; altavoz callado
Las frecuencias en Hz de las distintas notas musicales estn oficialmente definidas y los msicos
suelen tenerlas en cuenta a la hora de afinar los instrumentos. La escala cromtica temperada, adoptada por
la American Standards Asociation en 1936, establece el LA
4
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
260 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
multiplicando y dividiendo por dos, respectivamente. La frmula de abajo permite obtener las frecuencias de
las notas asignndolas un nmero (a partir de 6 y hasta 88; el LA de 440 Hz es la nota 49) con una precisin
razonable, mxime teniendo en cuenta que van a ir a parar al altavoz del PC. Tal curiosa relacin se verifica
debido a que la respuesta del odo humano es logartmica, lo que ha permitido reducir a simples matemticas
el viejo saber milenario de los msicos.
41 43 46 48 50 53 55 58 60 62
... ...
... 40 42 44 45 47 49 51 52 54 56 57 59 61 63 ...
261 EL 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. Estos
dispositivos son normalmente lentos en comparacin con la elevada velocidad de la unidad central. Un
ejemplo tpico puede ser el teclado: entre las pulsaciones de cada tecla hay un espacio de tiempo impredecible
y dependiente del usuario. Una manera simple de gestionar los dispositivos de E/S consiste en comprobar
continuamente si alguno de ellos tiene un dato disponible o lo est solicitando. Sin embargo, esto supone una
importante prdida de tiempo para el microprocesador, que mientras tanto podra estar haciendo otras cosas.
En una mquina multitarea y/o multiusuario, resulta ms interesante que los perifricos puedan interrumpir
al microprocesador para solicitarle una operacin de entrada o salida en el momento necesario, estando la
CPU liberada de la misin de comprobar cundo llega ese momento. Cuando se produce la interrupcin, el
microprocesador ejecuta la correspondiente rutina de servicio y despus contina con su tarea normal. Los
compatibles PC poseen un hardware orientado por completo a la multitarea (otra cosa es que el 8086 y el
DOS no la aprovechen) y la entrada/salida se gestiona casi por completo mediante interrupciones en todas
las mquinas. Por ejemplo, en las operaciones de disco, cuando acaba la transferencia de datos se produce
una interrupcin de aviso y una rutina de la BIOS activa una variable que lo indica, en el segmento de
memoria 40h. Las propias funciones de la BIOS para acceder al disco se limitan a chequear continuamente
esa variable hasta que cambie, lo que significa un evidente desaprovechamiento de las posibilidades que la
gestin por interrupciones pone a nuestra disposicin.
Las interrupciones aaden cierta complejidad al diseo del hardware: en principio, es necesario
jerarquizarlas de alguna manera para decidir cul se atiende en el caso de que se produzcan dos
simultneamente. Tambin es importante el control de prioridad para el caso de que se produzca una
interrupcin mientras se est procesando otra: slo se la atender si es de mayor prioridad. En este captulo
slo consideraremos las interrupciones hardware, no las de software ni las excepciones del procesador.
12.4.2. - DESCRIPCIN DEL INTEGRADO 8259.
Este circuito integrado est especialmente diseado para controlar las interrupciones en sistemas
basados en el 8080/8085 y en el 8086. Puede controlar hasta 8 interrupciones vectorizadas. Adems, a un
8259 se le pueden conectar en cascada un mximo de 8 chips 8259 adicionales, lo que permite gestionar
sistemas 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 interpretacin
de las seales se muestra a la derecha:
-CS: Habilita la comunicacin con la CPU.
-WR: Permite al 8259 aceptar comandos de la CPU.
-RD: Permite al 8259 dejar la informacin en el bus de datos.
D7..D0: Bus de datos bidireccional, por el que se transmite la
informacin de control/estado y el nmero de vector de
interrupcin.
CAS0..CAS2: Lneas de cascada, actan como salida en el 8259 maestro y
como entrada en los 8259 esclavos, en un sistema con varios
8259 interconectados, constituyendo un bus local.
-SP/-EN: Pin de doble funcin: en el buffered mode del 8259 actuar
como -EN, para habilitar los buffers del bus; en el modo
normal indicar si el 8259 es maestro o esclavo (-SP).
INT: Conectado a la patilla INT de la CPU para producir la
interrupcin cuando llegue el momento.
IR0..IR7: Lneas asncronas de peticin de interrupcin. Una peticin de
interrupcin se ejecuta manteniendo IR en alto hasta que se
recibe el reconocimiento (modo por flancos) o simplemente
poniendo en alto la lnea IR (modo por niveles).
-INTA: Lnea de reconocimiento de interrupcin, por medio de esta
lnea se fuerza al 8259 a depositar en el bus la informacin
del vector de interrupcin. INTA es independiente de -CS.
A0: En conjuncin con -CS, -WR y -RD es empleada para enviar
las palabras de comando al 8259 y para solicitar informacin
al mismo. Suele ir conectada a la lnea A0 de la CPU.
262 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
DESCRIPCIN FUNCIONAL
El diagrama funcional del 8259, con la estructura interna de las diversas partes que lo componen, es
el siguiente:
INT
INTA
BUFFER DEL LGICA
BUS DE DATOS DE CONTROL
D0..D7
-RD LGICA DE IR0
-WR LECTURA Y I.S.R. LGICA I.R.R. IR1
A0 ESCRITURA DE IR2
(In GESTIN (Interrupt IR3
-CS Service DE Request IR4
Register) PRIORIDAD Register) IR5
IR6
IR7
CAS 0 BUFFER DE
CAS 1 CASCADA Y
CAS 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 (In
Service Register). El IRR almacena todas las peticiones de interrupcin pendientes; el ISR almacena todas
las interrupciones que estn siendo atendidas en un momento dado. La lgica de gestin de prioridad
determina qu interrupcin, de las solicitadas en el IRR, debe ser atendida primero: cuando lleguen las seales
INTA dicha interrupcin ser la primera procesada y su bit correspondiente se activar en el ISR. El buffer
del bus de datos conecta el 8259 con el bus de datos de la placa principal del ordenador: su diseo en 3
estados permite desconectarlo cuando sea necesario; a travs de este bus circulan las palabras de control y
la informacin de estado. La lgica de lectura y escritura acepta los comandos que enva la CPU: aqu hay
registros para almacenar las palabras de inicializacin y operacin que enva el procesador; tambin sirve para
transferir el estado del 8259 hacia el bus de datos. El buffer de cascada/comparador almacena y compara
las identificaciones de todos los 8259 que posea el sistema: el 8259 maestro enva la identificacin del 8259
esclavo en las lneas CAS, los 8259 esclavos la leen y el implicado en la operacin coloca en el bus de datos
la direccin (vector) de la rutina que atender la interrupcin en los 2 prximos (o el prximo) ciclos INTA.
FUNCIONAMIENTO DEL 8259
El funcionamiento del 8259 vara ligeramente en funcin del sistema en que est instalado, segn sea
este un 8086 o un 8080/8085. Veremos primero el caso del 8086:
1) Una o ms lneas IR son activadas por los perifricos, lo que pone a 1 el correspondiente bit del IRR.
2) El 8259 evala la prioridad de estas interrupciones y solicita la interrupcin a la CPU (lnea INT) si
es necesario.
3) Cuando la CPU reconoce la interrupcin, enva la seal -INTA.
4) Nada ms recibida la seal -INTA de la CPU, el 8259 activa el bit correspondiente a la interrupcin
de mayor prioridad (la que va a ser procesada) en el ISR y lo borra en el IRR. En este ciclo, el 8259
an no controla el bus de datos.
5) Cuando la CPU enva un segundo ciclo -INTA, el 8259 deposita en el bus de datos un valor de 8 bits
que indica el nmero de vector de interrupcin del 8086, para que la CPU lo pueda leer.
6) En el modo AEOI del 8259, el bit de la interrupcin en el ISR es borrado nada ms acabar el
segundo pulso -INTA; en caso contrario, ese bit permanece activo hasta que la CPU enve el
comando EOI al final de la rutina que trata la interrupcin (caso ms normal).
263 EL HARDWARE DE APOYO AL MICROPROCESADOR
En el caso de sistemas basados en el 8080/8085, el funcionamiento es idntico hasta el punto (3),
pero a continuacin sucede lo siguiente:
4) Nada ms recibida la seal -INTA de la CPU, el 8259 activa el bit correspondiente a la interrupcin
de mayor prioridad (la que va a ser procesada) en el ISR y lo borra en el IRR. En este ciclo, el 8259
deposita en el bus de datos el valor 11001101b, correspondiente al cdigo de operacin de la
instruccin CALL del 8080/85.
5) Esta instruccin CALL provoca que la CPU enve 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 direccin de memoria del ordenador de la rutina de servicio de la interrupcin (16 bits).
7) Esto completa la instruccin CALL de 3 bytes. En el modo AEOI del 8259, el bit de la interrupcin
en el ISR es borrado nada ms acabar el tercer pulso -INTA; en caso contrario, ese bit permanece
activo hasta que la CPU enve el comando EOI al final de la rutina que trata la interrupcin.
Si en el paso (4), con ambos tipos de microprocesador, no est presente la peticin de interrupcin
(por ejemplo, porque ha sido excesivamente corta) el 8259 enva una interrupcin de nivel 7 (si hubiera un
8259 conectado en IR7, las lneas CAS permaneceran inactivas y la direccin de la rutina de servicio de
interrupcin sera suministrada por el 8259 maestro).
PROGRAMACIN DEL 8259
El 8259 acepta dos tipos de comandos generados por la CPU: los ICW (Inicialization Command
Word) que inicializan el 8259, y los OCW (Operation Command Word) que permiten programar la
modalidad de funcionamiento. Antes de que los 8259 de un sistema comiencen a trabajar deben recibir una
secuencia de ICW que los inicialice. Los ICW y OCW constan de secuencias de 2 a 4 comandos consecutivos
que el 8259 espera recibir secuencialmente, unos tras otros, a travs del bus de datos, segn sea necesario
(el propio 8259 se encarga de contarlos midiendo los pulsos de la lnea -WR). Los OCW pueden ser enviados
en cualquier momento, una vez realizada la inicializacin.
La comunicacin con el 8259 emplea las lneas -WR y -RW, as como A0. El hecho de que exista
una sola lnea de direcciones implica que el 8259 slo ocupa dos direcciones de puerto de E/S en el espacio
de 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 palabra
de la inicializacin (ICW1) e inicia dicha secuencia de inicializacin, lo que implica lo siguiente:
- Se resetea el circuito sensible a los niveles, lo que quiere decir que hasta nueva orden las lneas IR
sern sensibles por flancos de transicin bajo-alto.
- Se limpia el IMR.
- A la lnea IR7 se le asigna un nivel de prioridad 7.
- Se desactiva el Special Mask Mode. Se queda listo para devolver IRR en la prxima lectura OCW3.
- Si IC4 (bit D0) es 0, todas las funciones seleccionadas en ICW4 sern puestas a 0 (non buffered
mode, 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 ICW4
direccin del vector de interrupcin, interval": innecesaria
lneas A7..A5 (slo 8080/85) 1 - 4 bytes
0 - 8 bytes
1 - IR por niveles 1 modo single
0 - IR por flancos 0 en cascada
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 IC4
es 0, tampoco ser enviada ICW4. En el 8080/85, las diversas interrupciones generan
CALLs a 8 direcciones adyacentes separadas 4 u 8 bytes (segn indique ADI): para
componer la direccin, el 8259 inserta A0..A4 (o A0..A5) convenientemente, segn la
interrupcin que se trate. En el 8086, A7..A5 y ADI son ignoradas.
ICW2: Se enva con A0=1, para diferenciarlo de ICW0 (hacer OUT a la siguiente direccin de puerto).
A0 D7 D6 D5 D4 D3 D2 D1 D0
A15 A14 A13 A12 A11
1 A10 A9 A8
T7 T6 T5 T4 T3
Notas: En el 8080/85, A15..A8 completan la direccin de la rutina de servicio; en el 8086, T7..T3
determinan los cinco bits ms significativos del nmero de vector de interrupcin a invocar
(los 3 bajos los suministra el 8259 segn la interrupcin que se trate).
ICW3: Se enva slo en el caso de que haya ms de un 8259 en el sistema (bit SNGL de ICW1 a cero), en
caso contrario en su lugar se enviara 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 lnea IR correspondiente no tiene conectado un 8259 esclavo
1 - La lnea IR correspondiente va conectada a un 8259 esclavo
Formato de ICW3 a enviar a un 8259 esclavo para que memorice de qu lnea IR del maestro cuelga:
A0 D7 D6 D5 D4 D3 D2 D1 D0
1 0 0 0 0 0 ID2 ID1 ID0
ID (identificacin) del esclavo (0..7)
ICW4: Se enva slo si IC4=1 en ICW1, con objeto de colocar el 8259 en un modo de operacin distinto
del 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 8086
1 Special Fully Nested Mode 0 - " 8080/85
0 Not Special Fully Nested Mode
0 X non buffered mode
1 0 buffered mode esclavo
1 1 buffered mode maestro
1 - Auto EOI
0 - EOI normal
Notas: El Special Fully Nested Mode, el buffered mode y la modalidad AEOI sern explicadas ms
tarde. Ntese que con el 8086 es obligatorio enviar ICW4 para seleccionar esta CPU.
265 EL HARDWARE DE APOYO AL MICROPROCESADOR
OCWS (Operation Command Words).
Una vez inicializado, el 8259 est listo para procesar las interrupciones que se produzcan. Sin
embargo, durante su funcionamiento normal est capacitado para recibir comandos de control por parte de
la 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 de
OCW1 se corresponden con sus correspondientes bits del IMR. Un bit a 1 significa interrupcin
enmascarada (inhibida) y a 0, interrupcin 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 especfico Fin de interrupcin
0 1 1 (*) EOI especfico
1 0 1 Rotar en comando EOI no especfico
1 0 0 Activar rotacin en modo AEOI Rotacin automtica
0 0 0 Desactivar rotacin en modo AEOI
1 1 1 (#) EOI especfico asignando prioridad Rotacin especfica
1 1 0 (#) Comando para asignar prioridad
0 1 0 No operacin
(*) Usados L0..L2
(#) en L0..L2 se indica la lnea 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 mscara especial: Comando de lectura de registro:
------------------------- -------------------------------
0 X - No actuar 0 X - No actuar
1 0 - Inhibir Special Mask Mode 1 0 - Leer IRR en prximo pulso -RD
1 1 - Activar Special Mask Mode 1 1 - Leer ISR en prximo pulso -RD
1 - Comando POLL
0 - No es comando POLL
TRABAJANDO CON EL 8259
En las ICW y, sobre todo, en las OCW, se han introducido un aluvin de elementos nuevos que sern
explicados a continuacin.
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 (mxima)
a 7 (mnima). Cuando se produce un reconocimiento de interrupcin por parte de la CPU, el 8259 evala cul
es la interrupcin pendiente de mayor prioridad, coloca su nmero de vector en el bus y activa su bit
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 (situacin ms
normal); sin embargo, en el modo AEOI, ese bit se bajara inmediatamente despus del ltimo -INTA.
Mientras el bit del ISR est activo, todas las interrupciones de igual o menor prioridad que lleguen
permanecen inhibidas; sin embargo, las de mayor prioridad podrn interrumpir. En el caso del 8086, cuando
comienza el tratamiento de la interrupcin, un bit del registro de estado de la CPU mantiene inhibidas todas
las interrupciones: lo normal es que el programa de control comience con STI para permitir que el 8086 enve
nuevas seales INTA al 8259, as el 8259 podr enviar las interrupciones de mayor prioridad que le lleguen.
Tras la secuencia de inicializacin, 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 de
asignacin de prioridad. Ntese que cuando se utiliza el modo AEOI o el Special Mask Mode no se respeta
el modo Fully Nested Mode (debido a que una interrupcin de menor prioridad podra interrumpir a una
rutina que gestiona otra de mayor prioridad).
Special Fully Nested Mode.
Se emplea en sistemas que tienen varios 8259 conectados. Slo el 8259 maestro es programado en
este modo, lo que implica las siguientes diferencias respecto al Fully Nested Mode normal:
- Cuando se atiende una interrupcin de un 8259 esclavo, si viene otra de mayor prioridad de ese
mismo 8259 esclavo, se provoca una interrupcin al maestro (normalmente, el 8259 esclavo estara
enmascarado mientras se procesa una de sus interrupciones).
- Cuando acaba la rutina de servicio de interrupcin, hay que enviar un EOI no-especfico al 8259
esclavo; adems hay que leer a continuacin su ISR y comprobar si es cero: en ese caso, hay que enviar
adems otro EOI al 8259 maestro (si no es cero significa que an hay interrupciones en proceso en el 8259
esclavo).
Modos de EOI.
El EOI (End Of Interrupt) sirve para bajar el bit del ISR que representa la interrupcin que est
siendo procesada. El EOI puede producirse automticamente (AEOI) al final de la ltima seal INTA que
enva la CPU al 8259 para una interrupcin dada (tercer ciclo INTA en el 8080/85 y segundo en el 8086);
sin embargo, la mayora de los sistemas requieren una gestin de prioridades en las interrupciones, lo que
significa que es ms conveniente que EOI lo enve el propio procesador al 8259, a travs de OCW2, cuando
acabe la rutina de gestin de interrupcin, para evitar que mientras se gestiona esa interrupcin se produzcan
otras de igual o menor prioridad. En un sistema con varios 8259, el EOI debe ser enviado no slo al 8259
esclavo implicado sino tambin al maestro. Hay dos modalidades de EOI: la especfica y la no-especfica.
En el EOI no especfico, el 8259 limpia el bit ms significativo que est activo en el ISR, que se supone que
es el correspondiente a la ltima interrupcin 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, el
8259 es incapaz de determinar cul fue el ltimo nivel de interrupcin procesado, por lo que la rutina que
gestiona la interrupcin debe enviar un EOI especfico al 8259 indicndole qu bit hay que borrar en el ISR.
Rotacin de prioridades.
Hay sistemas en que varios perifricos tienen el mismo nivel de prioridad, en los que no interesa
mantener un orden de prioridades en las lneas IR. En condiciones normales, nada ms atender una
interrupcin de un perifrico, podra venir otra que tambin se atendera, mientras los dems perifricos se
cruzaran de brazos. La solucin consiste en asignar el menor nivel de prioridad a la interrupcin recin
atendida para permitir que las dems pendientes se procesen tambin. Para ello se enva un EOI que rote las
prioridades: si, por ejemplo, se haba procesado una IR3, IR3 pasar al menor nivel de prioridad e IR4 al
mayor, quedando las prioridades ordenadas (de mayor a menor): IR4, IR5, IR6, IR7, IR0, IR1, IR2, IR3.
Existe tambin una rotacin especfica de prioridades, a travs de OCW2, que puede realizarse en un
comando 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 interrupcin es necesario
permitir que se produzcan ciertas interrupciones de menor prioridad en algunos momentos, o prohibirlo en
otros, sin ser quiz interesante enviar el EOI antes de tiempo. Esto implica alterar la estructura normal de
267 EL HARDWARE DE APOYO AL MICROPROCESADOR
prioridades. La manera de realizar esto es activando el Special Mask Mode a travs de OCW3 durante la
rutina de servicio de interrupcin (es ms que conveniente inhibirlo de nuevo al final). Una vez activado este
modo, el IMR indica qu interrupciones estn permitidas (bit a 0) y cules inhibidas (bit a 1). Por ello, suele
ser conveniente activar el bit del IMR correspondiente a la IR en servicio (para evitar que se produzca de
nuevo cuando an no ha sido procesada). Al final hay que enviar un EOI especfico, ya que este modo de
trabajo altera el Fully Nested Mode habitual.
Comando POLL.
En esta modalidad poco habitual, habilitada a travs de OCW3, no se emplea la salida INT del 8259
o bien el microprocesador trabaja con las interrupciones inhibidas. El servicio a los perifricos es realizado
por software utilizando el comando POLL. Una vez enviado el comando POLL, el 8259 interpreta la prxima
lectura que se realice como un reconocimiento de interrupcin, actualizando el ISR y consultando el nivel
de prioridad. Durante esa lectura, la CPU obtiene en el bus de datos la palabra POLL que indica (en el bit
7) si hay alguna interrupcin pendiente y, en ese caso, cul es la de mayor prioridad (bits 0-2).
Lectura de informacin del 8259.
El IMR puede ser ledo a travs 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 continuacin
se puede leer el bus de datos (A0=0) sin necesidad de enviar ms OCW3 (el 8259 es capaz de recordar si
tiene que leer el IRR o el ISR). Esto ltimo no es as, evidentemente, en el caso de utilizar el comando POLL
(tras enviarlo, la prxima lectura se interpreta como un INTA). Tras inicializarse, el 8259 queda preparado
por 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 habilitacin de los buffers. Cuando se programa el modo
buffer, la patilla -SP/-EN del 8259 acta automticamente como seal de habilitacin del los buffers cada vez
que 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 direccin base E/S 20h; este controlador
de interrupciones es accedido, por tanto, por los puertos 20h (A0=0) y 21h (A0=1). En los AT y mquinas
superiores, adicionalmente, existe un segundo 8259 conectado en cascada a la lnea IR2 del primero. Este
segundo controlador es accedido a travs de los puertos 0A0h y 0A1h. La BIOS del ordenador, al arrancar
la mquina, coloca la base de interrupciones del primer controlador en 8, lo que significa que las respectivas
IR0..IR7 estn ligadas a los vectores de interrupcin 8..15; el segundo 8259 de los AT genera las
interrupciones comprendidas entre 70h y 77h. La asignacin de lneas IR para los diversos perifricos del
ordenador 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 Simulacin de IRQ2 (INT 71h)
IRQ 10 Reservado (INT 72h)
IRQ 11 Reservado (INT 73h)
IRQ 12 Reservado (INT 74h) Slo AT y PS/2
IRQ 13 Coprocesador aritmtico (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 lnea IR2 del 8259 maestro es empleada para colgar de ella el segundo 8259 esclavo.
Como la lnea IR2 est en el slot de expansin de 8 bits, por razones de compatibilidad los AT tienen
conectado en su lugar la IR9 que simula la IR2 original. Cuando se produce una IR9 debido a un perifrico
268 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
de XT que pretenda generar una IR2, el AT ejecuta una rutina de servicio en INT 71h que salta simplemente
a la INT 0Ah (tras enviar un EOI al 8259 esclavo).
La colocacin de IRQ0-IRQ7 en el rango INT 8-INT 15 fue bastante torpe por parte de IBM, al
saltarse la especificacin de Intel que reserva las primeras 32 interrupciones para el procesador. En modo
protegido, algunas de esas excepciones es estrictamente necesario controlarlas. Por ello, los sistemas
operativos que trabajan en modo extendido y ciertos extensores del DOS (como las versiones 3.x de
WINDOWS) se ven obligados a mover de sitio estas interrupciones. En concreto, WINDOWS 3.x las coloca
en INT 50h-INT 57h (por software, las mquinas virtuales 8086 emulan las correspondientes INT 8-INT 15).
Adems, en el modo protegido del 286/386 (o el virtual-86 del 386) la tradicional tabla de vectores de
interrupcin es sustituida por otra de descriptores, aunque el funcionamiento global es similar.
La interrupcin no enmascarable del 80x86 no est controlada por el 8259: es generada por la
circuitera que controla la memoria si se detecta un error de paridad. La interrupcin no enmascarable puede
ser enmascarada en los ordenadores compatibles gracias a la circuitera de apoyo al procesador, aunque no
es frecuente; en los AT el bit 7 del puerto 70h controla su habilitacin (si es cero, la NMI est habilitada)
sin embargo tambin se podra inhibir el control de paridad directamente (activando los bits 2 y 3 de la
direccin E/S 61h, respetando el resto de los bits de ese puerto por medio de una lectura previa). En los
PC/XT, es el puerto 0A0h el que controla la habilitacin de la NMI, tambin con el bit 7 (con la diferencia
de que debe estar a cero para inhibirla).
Durante la inicializacin del ordenador, la BIOS enva sucesivamente al 8259 las palabras ICW1 a
ICW4 de la siguiente manera (listado extrado directamente de la BIOS):
; Inicializacin del 8259 maestro (XT/AT)
MOV AL,10001b ; funcionamiento por flancos, cascada, ICW4 necesaria
OUT 20h,AL ; enviar ICW1
JMP SHORT $+2 ; estado de espera para E/S
MOV AL,8 ; base de interrupciones en INT 8
OUT 21h,AL ; enviar ICW2
JMP SHORT $+2
MOV AL,4 ; hay un esclavo en IR2 (S2=1) poner 0 en PC/XT!
OUT 21h,AL ; enviar ICW3
JMP SHORT $+2
MOV AL,1 ; modo 8086, EOI normal, not buffered mode
OUT 21h,AL ; enviar ICW4: completada la inicializacin del 8259-1
JMP SHORT $+2
MOV AL,255
OUT 21h,AL ; enmascarar todas las interrupciones
JMP SHORT $+2 ; Inicializacin del 8259 esclavo (slo AT)
MOV AL,10001b ; funcionamiento por flancos, cascada, ICW4 necesaria
OUT 0A0h,AL ; enviar ICW1
JMP SHORT $+2
MOV AL,70h ; base de interrupciones en INT 70h
OUT 0A1h,AL ; enviar ICW2
JMP SHORT $+2
MOV AL,2 ; es el esclavo conectado a IR2
OUT 0A1h,AL ; enviar ICW3
JMP SHORT $+2
MOV AL,1 ; modo 8086, EOI normal, not buffered mode
OUT 0A1h,AL ; enviar ICW4: completada la inicializacin del 8259-2
JMP SHORT $+2
MOV AL,255
OUT 0A1h,AL ; enmascarar todas las interrupciones
Como se puede observar, la rutina de arriba enmascara todas las interrupciones a travs del IMR. El
objetivo de esta medida es evitar que se produzcan interrupciones antes de desviar los correspondientes
vectores, pudiendo incluso mientras tanto estar habilitadas las interrupciones con STI.
Cuando se produce una interrupcin de la CPU (bien por software o por hardware), el indicador de
interrupciones del registro de estado del 8086 se activa para inhibir otra posible interrupcin mientras se
procesa esa (la instruccin IRET recuperar los flags del programa principal devolviendo las interrupciones
a su estado previo). Lo normal suele ser que las rutinas que gestionan una interrupcin comiencen por un STI
con objeto de permitir la generacin de otras interrupciones; las interrupciones slo deben estar inhibidas en
brevsimos momentos crticos. Sin embargo, cuando se procesa una interrupcin hardware, el registro de
interrupciones activas (ISR) indica qu interrupcin en concreto est siendo procesada; si en ese momento
llega otra interrupcin hardware de menor o igual prioridad le ser denegada la peticin, si es de mayor
prioridad le ser concedida (si la rutina comenzaba por STI). Cuando acaba de procesarse la interrupcin
hardware, la instruccin IRET no le dice nada al 8259, por lo que el programador debe preocuparse de borrar
el ISR antes de acabar. Si, por ejemplo, se gestiona la interrupcin del temporizador sin limpiar al final el
269 EL HARDWARE DE APOYO AL MICROPROCESADOR
ISR, a partir de ese momento quedarn bloqueados el teclado, los discos ... Conviene aqu sealar que una
rutina puede apoyarse en una interrupcin hardware sin necesidad de reprogramarla por completo. Ejemplo:
STI
PUSH AX
IN AL,puerto_teclado
CALL anterior_int9
;
; procesar tecla
;
POP AX
IRET
Al producirse la INT 9 se lee el cdigo de rastreo de la tecla y luego se
llama a la rutina que gestionaba con anterioridad a sta la INT 9: ella se
encargar de limpiar el ISR que, por tanto, no es tarea de nuestra rutina. Si
hubiera que limpiar el ISR, bastara con un EOI no especfico (OCW2: enviar
un valor 20h al puerto 20h para el 8259-1 y al puerto 0A0h para el 8259-2; en
las IRQ8-IRQ15 hay que enviar el EOI a ambos controladores de interrupcin).
Aviso: Aunque el funcionamiento del 8259 es suficientemente lgico como para pasar casi inadvertido, hay veces en que
hay que tenerlo en cuenta. Por ejemplo, al utilizar el servicio 86h de la INT 15h del AT (con objeto de hacer retardos) desde
una interrupcin hardware comprendida entre IRQ 0 e IRQ 7, conviene limpiar el ISR antes de llamar: no basta con hacerlo
al 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 mquinas 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 vectores
INT 50h-INT 57h (que invocan a los originales, para que el sistema siga funcionando con normalidad). Esta
nueva ubicacin no ha sido elegida por capricho, y es la misma que emplea WINDOWS 3.x. La razn es que
el 386 trabaja normalmente en modo virtual-86 bajo MS-DOS 5.0; cuando se produce una interrupcin se
ejecuta una rutina en modo protegido. El EMM386 del MS-DOS 5.0 no est preparado para soportar las
IRQ0-IRQ7 en otra localizacin 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 memoria
expandida instalado, no habra problemas y se podra elegir otro lugar distinto. Por cierto: si se entra y se
sale de WINDOWS, la nueva localizacin establecida, ya sea en 50h o en otro sitio, deja de estar vigente:
esto significa que WINDOWS reprograma la interrupcin base al volver al DOS. Personalmente he
comprobado que aunque IRQDEMO fuera ms elegante (empleando funciones de la especificacin VCPI),
nuestro querido WINDOWS no lo sera: para qu molestarse!. Sin embargo, IRQDEMO s se toma la
molestia de comprobar si la mquina es un XT o un AT para enviar correctamente la ICW3 del 8259.
; ********************************************************************
; * IRQDEMO.ASM - Utilidad residente de demostracin, que desva *
; * las interrupciones hardware INT 8-INT 15 hacia *
; * los vectores INT 50h a INT 57h. *
; ********************************************************************
irqdemo SEGMENT
ASSUME CS:irqdemo, DS:irqdemo
ORG 100h
inicio:
JMP main
; ------------ Area residente
irq0: INT 8 ; simular IRQs normales (se
IRET ; podra aprovechar tambin
irq1: INT 9 ; para hacer algo ms til).
IRET
irq2: INT 10
IRET
irq3: INT 11
IRET
irq4: INT 12
IRET
irq5: INT 13
IRET
irq6: INT 14
IRET
irq7: INT 15
IRET
tam_resid EQU ($-OFFSET inicio+256+15)/16
; ------------ Cdigo de instalacin
main PROC
LEA BX,tabla_ints
MOV AL,50h ; nueva base para IRQs 0-7
otra_int: PUSH AX
PUSH BX
MOV AH,25h
MOV DX,[BX]
INT 21h ; desviar INT 50h-57h
POP BX
ADD BX,2
POP AX
INC AL
CMP AL,58h
JB otra_int
CALL es_AT?
MOV BL,4
MUL BL
MOV BL,AL ; BL = 4 en AT y 0 en PC/XT
CALL inic_8259
LEA DX,texto_txt
MOV AH,9
INT 21h ; mensaje de instalacin
MOV ES,ES:[2Ch]
MOV AH,49h
INT 21h ; liberar entorno
MOV AH,31h
MOV DX,tam_resid
INT 21h ; terminar residente
main ENDP
; ------------ Subrutinas de apoyo a la instalacin.
inic_8259 PROC ; Inicializacin 8259 maestro
MOV AL,0FFh
OUT 21h,AL ; enmascarar todas las IRQ
JMP SHORT $+2
MOV AL,10001b ; flancos, maestro, s ICW4
OUT 20h,AL ; enviar ICW1
JMP SHORT $+2 ; estado de espera E/S
MOV AL,50h ; base interrupciones INT 50h
OUT 21h,AL ; enviar ICW2
JMP SHORT $+2
MOV AL,BL ; 4 en AT y 0 en PC/XT
OUT 21h,AL ; enviar ICW3
JMP SHORT $+2
MOV AL,1 ; modo 8086, EOI normal
OUT 21h,AL ; enviar ICW4
JMP SHORT $+2
MOV AL,0
OUT 21h,AL ; permitir todas las IRQ
RET
inic_8259 ENDP
es_AT? PROC ; comprobar si es XT AT
PUSHF
POP AX
AND AX,0FFFh
PUSH AX
POPF
PUSHF
POP AX
AND AX,0F000h
CMP AX,0F000h
MOV AX,1 ; indicar AT
JNE es_AT
DEC AX ; indicar PC/XT
es_AT: RET
es_AT? ENDP
tabla_ints DW irq0, irq1, irq2, irq3, irq4, irq5, irq6, irq7
texto_txt DB 13,10,"Las interrupciones 8-15 son ahora 50-57h."
DB 13,10,"$"
irqdemo ENDS
END inicio
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 tcnica de diseo del hardware que permite a los perifricos
conectados a un sistema realizar transferencias sobre la memoria sin la intervencin del procesador. De esta
manera, las lentas operaciones de entrada y salida de bloques de datos, se pueden realizar en la sombra
mientras la CPU se dedica a otras tareas ms tiles. Como la memoria del ordenador slo puede ser accedida
a un tiempo por una fuente, en el momento en que el DMA realiza las transferencias el microprocesador se
desconecta de los buses, cedindole el control. El funcionamiento del controlador de DMA se basa en unos
registros que indican la direccin de memoria a ser accedida y cuntas posiciones de memoria quedan an
por transferir. La transferencia de datos entre los perifricos y la memoria por DMA no suele efectuarse de
golpe, sino ms bien poco a poco, robndole algunos ciclos a la CPU. Los controladores de DMA suelen
disponer de varias lneas de peticin de DMA, pudiendo atender las necesidades de varios perifricos que
soliciten una transferencia, quienes deben haber sido diseados expresamente para soportar el DMA.
12.5.2 - DESCRIPCIN DEL INTEGRADO 8237.
El 8237 es un controlador de DMA de 4 canales programables en 3 modos diferentes, con posibilidad
de ser conectado en cascada con otros de su misma especie. Adems de las funciones tradicionales, el 8237
soporta tambin transferencias memoria-memoria, incluyendo la posibilidad de rellenar un rea de la memoria
con cierto dato. La arquitectura es de 16 bits, tanto para direcciones como datos, por lo que est
especialmente diseado para sistemas basados en el Z80 y 8085; aunque puede operar tambin con
procesadores ms 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: Seal de reloj bsica.
-CS: Lnea de habilitacin del chip.
RESET: Esta seal provoca la limpieza de los registros de comando, estado, solicitud y los temporales;
borra el bandern last/first y el contador de registro de modo; el registro de mscara se asigna
para ignorar las solicitudes. El 8237 queda en Ciclo Inactivo.
READY: Seal que puede ser empleada para extender los pulsos de lectura y escritura en memoria del
8237 para trabajar con memorias lentas.
HLDA: Hold Acknowledge, lnea por la que la CPU indica que ha liberado los buses.
DREQ0..3: DMA Request; son 4 lneas asncronas de peticin de DMA. En el modo de prioridad fija,
DREQ0 tiene la mxima y DREQ3 la mnima. Los perifricos solicitan el servicio de DMA
en estas lneas y esperan a bajarlas hasta el correspondiente DACK. La polaridad de DREQ
es programable. Las lneas no usadas deben ser enmascaradas.
DB0..DB7: BUS de datos bidireccional y triestado. Durante los ciclos de DMA, los 8 bits ms
significativos de la direccin son colocados en el bus de datos con objeto de ser almacenados
en un latch exterior controlado por ADSTB. En las operaciones memoria-memoria, el bus de
datos recibe y enva los bytes a transferir.
-IOR: I/O Read. Lnea bidireccional de 3 estados. En el ciclo inactivo es una entrada empleada por
la CPU para leer los registros de control; en el ciclo activo acta como lnea de salida para
que el 8237 controle la lectura de datos de los perifricos.
-IOW: I/O Write. Lnea bidireccional de 3 estados. En el ciclo inactivo es una entrada empleada por
la CPU para escribir los registros del 8237; en el ciclo activo acta como lnea de salida para
que el 8237 controle la escritura de datos en los perifricos.
-EOP: End Of Process. Lnea bidireccional que informa de la finalizacin del servicio DMA. El 8237
permite que un ente exterior fuerce el final de un servicio bajando esta lnea. El propio 8237
genera un pulso en ella cuando se alcanza un TC (Terminal Count, fin de cuenta) en algn
canal, salvo en el modo memoria-memoria del canal 0 (en ese caso, la seal se produce al
alcanzarse el TC del canal 1). Esta patilla est conectada en el interior del chip a un transistor
en colector abierto, por lo que requiere una resistencia externa. Cuando llega una seal -EOP,
el 8237 finaliza el servicio aunque en el modo de autoinicializacin los registros base volvern
a ser escritos en los registros en curso del canal implicado. El canal resulta enmascarado salvo
en el caso del modo de autoinicializacin.
A0..A3: Lneas bidireccionales triestado de direcciones. En el ciclo inactivo son entradas empleadas para direccionar los registros internos a leer o escribir. En
el ciclo activo, son salidas y proveen los 4 bits menos significativos de la direccin.
A4..A7: Lneas triestado de salida de direcciones. Proveen los 4 bits altos de la direccin durante el ciclo activo.
HRQ: Hold Request. Lnea 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 perifricos de que ha sido atendida su peticin. El nivel de operacin de esta lnea es programable. RESET las baja.
AEN: Address Enable. Habilita el latch de 8 bits que guarda la parte alta de la direccin. Sirve tambin para inhibir el acceso al bus por parte de otras fuentes.
ADSTB: Address Strobe. Lnea que controla el almacenamiento de la parte alta de la direccin, 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.
271 EL HARDWARE DE APOYO AL MICROPROCESADOR
DESCRIPCIN FUNCIONAL
Los modos de operacin del 8237 estn diseados para soportar transferencias de una sola palabra
de datos y flujos de datos discontinuos entre la memoria y los perifricos. El controlador de DMA es
realmente un circuito secuencial generador de seales de control y direcciones que permite la transferencia
directa de los datos sin necesidad de registros temporales intermedios, lo que incrementa drsticamente la tasa
de transferencia de datos y libera la CPU para otras tareas. Las operaciones memoria-memoria precisan de
un registro temporal intermedio, por lo que son al menos dos veces ms lentas que las de E/S, aunque en
algunos casos an ms 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 las
seales de tiempo internas y las seales de control externas; un bloque de gestin de prioridades, que resuelve
los conflictos de prioridad
cuando varios canales de DMA
son accedidos a la vez; tambin
posee un elevado nmero de
registros para gestionar el
funcionamiento. Los registros
internos del 8237 estn
resumidos en la figura de la
derecha.
Tipo de registro Tamao N registros
Registro base de direccin 16 bits 4
Registro base contador de palabras 16 bits 4
Registro de direccin en curso 16 bits 4
Registro contador de palabras en curso 16 bits 4
Registro temporal de direccin 16 bits 1
Registro temporal contador de palabras 16 bits 1
Registro de estado 8 bits 1
Registro de comandos 8 bits 1
Registro temporal 8 bits 1
Registro de modo 6 bits 4
Registro de mscara 4 bits 1
Registro de peticin 4 bits 1
OPERACIN DEL DMA
En un sistema, los buses del 8237 estn conectados en paralelo al bus general del ordenador, siendo
necesario un latch externo para almacenar la parte alta de la direccin de memoria. Cuando est inactivo, el
8237 est desconectado de los buses; cuando se produce una peticin de DMA pasa a controlar los buses y
a generar las seales necesarias para realizar las transferencias. La operacin que realiza el 8237 es
consecuencia de la programacin realizada previamente en los registros de comando, modo, base de direccin
y contador de palabras a transferir.
Para comprender mejor el funcionamiento del 8237 es conveniente considerar los estados generados
por cada ciclo. El DMA opera bsicamente en dos ciclos: el activo y el inactivo (o idle). Tras ser
programado, el DMA permanece normalmente inactivo hasta que se produce la solicitud de DMA en algn
canal o va software. Cuando sta llega, si ese canal no estaba enmascarado (es decir, inhibido) el 8237
solicita los buses a la CPU y se pasa al ciclo activo. El ciclo activo se compone de varios estados internos,
en funcin 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 reloj
completo. El estado 1 (S1) es el estado inactivo o idle. En l se entra cuando no hay pendiente una peticin
de DMA vlida, al final de la secuencia de transferencia, o tras un reset o un Master Clear (que se ver ms
adelante). En S1 el DMA est inactivo pero puede ser programado por el microprocesador del sistema. El
estado 0 (S0) es el primer estado de servicio DMA. El 8237 ha solicitado los buses a la CPU a travs de la
lnea HRQ pero la CPU an no ha respondido a travs de HLDA. En esta situacin, el 8237 puede an
todava 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 ms tiempo, est prevista la posibilidad
de insertar estados de espera entre S2 S3 y S4 a travs de la patilla READY.
Tngase en cuenta que los datos son pasados directamente de la memoria hacia/desde los perifricos,
por lo tanto no cruzan a travs del DMA (las lneas -IOR y -MEMW, o -IOW y -MEMR, son activadas al
mismo tiempo). El caso de las operaciones memoria-memoria es especial, ya que para cada palabra a mover
hay que realizar la operacin de lectura (en unos estados denominados S11, S12, S13 y S14) y despus la
de escritura (estados S21, S22, S23, S24).
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 lneas DREQ en los flancos de bajada de las seales de reloj: en esto consisten los estados
S1. En esta situacin, el 8237 puede ser programado por la CPU. Para ello, las lneas A0..A3 seleccionan el
registro interno y -IOR e -IOW indican si se trata de leer o escribir. Como algunos de los registros internos
son de 16 bits, existe un flip-flop interno que conmuta en cada operacin de escritura sobre ellos, para que
el 8237 sepa si est recibiendo el byte alto o el bajo (este flip-flop es puesto a cero en un Reset o en un
comando Master Clear, existiendo tambin comandos especiales para controlarlo). Algunas combinaciones
de A0..A3 y las lneas -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 peticin 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 palabras
es decrementado y el de direcciones se incrementa/decrementa segn ha sido programado. Cuando el registro
contador de palabras se desborda (pasa de 0 a 0FFFFh) se activa el bit Terminal Count (fin de cuenta) en
el registro de estado y la patilla -EOP genera un pulso. Si el canal estaba programado para autoinicializarse
esto es lo que realiza; en caso contrario, se activa automticamente el bit de mscara para inhibir hasta nueva
orden ese canal.
DREQ debe permanecer activo hasta que DACK responda. Sin embargo, si DREQ permanece activo
hasta que acaba el proceso de transferencia, la lnea HRQ baja y se ceden momentneamente los buses al
sistema. Despus, vuelve a subir, y cuando se recibe el HLDA de la CPU se pueden realizar ms
transferencias de este tipo. En la serie 8080 y 80x86, esto asegura al menos un ciclo para la CPU entre las
sucesivas 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 las
necesarias hasta que el registro contador de palabras se desborda. Lgicamente, tambin se acaba el proceso
si alguien acta sobre la patilla -EOP. DREQ slo 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 slo mientras DREQ permanece activo.
Esto significa que se pueden transferir datos hasta agotar las posibilidades del dispositivo; cuando el
dispositivo tenga ms datos listos puede volver a activar DREQ para continuar donde lo dej. Esta modalidad
permite dejar ciclos a la CPU cuando no es realmente necesario que el DMA opere. Adems, en los perodos
de inactividad, los valores de direccin en curso y contador de palabras son almacenados en el Registro de
direcciones 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.
Conexin en cascada de varios 8237.
Esta conexin es empleada para conectar ms de un 8237 en el sistema. La lnea HRQ de los 8237
hijo es conectada a la DREQ del 8237 padre; la HLDA lo es a la DACK. Esto permite que las peticiones en
los diversos 8237 se propaguen de uno a otro a travs 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 es
empleado slo para priorizar el 8237 adicional que cuelga (hijo), no puede emitir direcciones ni seales de
control por s mismo: esto podra 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
273 EL HARDWARE DE APOYO AL MICROPROCESADOR
inhibidas las dems seales. El -EOP externo ser ignorado por el 8237 padre, pero s tendr efecto en el
8237 hijo correspondiente.
Cuando de un 8237 cuelga otro, estamos ante un sistema DMA de dos niveles. Si del DMA hijo
cuelga a su vez otro, sera un sistema DMA de tres niveles, como el mostrado a continuacin:
C.P.U.
8237
DREQ HRQ
DACK HLDA
HRQ
HLDA DREQ HRQ
DACK HLDA
8237
8237
DREQ HRQ
DACK 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 salidas
DACK son programadas por defecto para ser activas a nivel bajo y son colocadas en alto. Si estn conectadas
directamente a HLDA, el segundo nivel de 8237 no puede ser programado hasta que la polaridad de DACK
no se cambie para que sea activa a nivel alto. Los bits de mscara de canales del 8237 padre funcionan como
cabra esperar, permitiendo inhibir 8237s de niveles inferiores.
Modos de transferencia.
Cada uno de los 3 modos de transferencia puede realizar 3 tipos distintos de transferencias: lectura,
escritura y verificacin. 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). Las
transferencias de tipo verificacin son pseudotransferencias: el funcionamiento es similar a la lectura o
escritura pero sin tocar las lneas de control de la memoria ni de los perifricos; durante el modo de
verificacin se ignora la lnea READY; este modo no es permitido en las operaciones memoria-memoria.
Autoinicializacin.
Cualquier canal puede ser programado para incluir esta caracterstica. En el momento de programar
el chip, los registros base de direccin y base contador de palabras son cargados a la vez y con el mismo
valor que los registros de direccin en curso y contador de palabras en curso. Los registros base permanecen
inalterados en todo momento, por lo que al final del servicio sirven, en este modo de trabajo, para recargar
de nuevo los registros en curso. Esto sucede justo tras la seal -EOP, quedando el 8237 listo para repetir de
nuevo la misma transferencia (cuando se solicite a travs de la lnea DREQ o por software). En esta
modalidad, los bits de mscara estn a 0.
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 comienza
activando la lnea DREQ del canal 0, bien por hardware o por software. El 8237 solicita entonces un servicio
de DMA ordinario, con el que lee el byte de la memoria a travs de 4 estados y empleando el Block Transfer
Mode visto con anterioridad. El registro de direccin en curso del canal 0, que indica la direccin origen en
la memoria, es incrementado/decrementado (segn haya sido programado) y el dato es almacenado en el
registro temporal del 8237. En otros 4 estados ms, el dato es pasado del 8237 de nuevo a la memoria,
usando la direccin del registro de direccin en curso del canal 1, que indica la direccin destino en memoria,
el cual es tambin incrementado/decrementado segn proceda. Adems, se decrementa el registro contador
de palabras en curso del canal 1: si al decrementar se desborda (pasa de 0 a 0FFFFh) se activa el bit TC del
registro de estado (Terminal Count, fin de cuenta) y se genera un pulso -EOP, finalizando el proceso. En el
caso de que el valor del registro contador de palabras del canal 0 pase de 0 a 0FFFFh, sin embargo, no se
acta sobre TC ni sobre EOP (no finaliza el proceso) aunque este canal se autoinicializa si as estaba
programado.
Si se desea una autoinicializacin total en este tipo de transferencias, los registros contadores de
palabras del canal 0 y 1 han de ser programados con el mismo valor inicial; de lo contrario, slo uno de los
dos canales se autoinicializar (el que primero desborde su registro contador de palabras).
El canal 0 puede ser tambin programado para retener siempre la misma direccin durante todas las
transferencias, lo que permite copiar un mismo byte en todo un bloque de la memoria.
El 8237 puede responder a seales -EOP externas durante este tipo de transferencias, pero slo cede
el control de los buses despus de completar la transferencia de la palabra que tenga entre manos. Los
circuitos para comparar datos en bsquedas de bloques pueden emplear -EOP para terminar la operacin tras
encontrar lo que buscan. Las operaciones memoria-memoria se pueden detectar por hardware como una
combinacin 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 la
prioridad fija, basada en el nmero del canal (0-mxima, 3-mnima). Una vez que un canal es atendido, los
dems esperan hasta que acabe. La segunda modalidad es la prioridad rotatoria: el ltimo canal servido pasa
a tener la menor prioridad y el que le sigue la mxima. La rotacin de prioridades se produce cada vez que
se devuelven los buses a la CPU. Esta ltima modalidad de prioridad asegura que un canal sea atendido al
menos despus de haber atendido los otros 3, evitando que un solo canal monopolice el uso del DMA. Con
independencia del tipo de prioridad programada, sta es evaluada cada vez que el 8237 recibe un HLDA.
Compresin de tiempo.
De cara a mejorar el rendimiento en los sistemas ms potentes, el 8237 puede ser programado para
comprimir el tiempo de transferencia a dos ciclos de reloj. En cualquier caso, esta posibilidad no est
disponible en las transferencias memoria-memoria.
Generacin de direcciones.
Para reducir el nmero de pines, el 8237 tiene multiplexada la parte alta del bus de direcciones. En
el estado S1, los 8 bits ms significativos de la direccin son depositados en un latch externo a travs del bus
de datos. La lnea AEN indica a la circuitera externa que debe habilitar el latch como parte alta del bus de
direcciones cuando llega el momento (la parte baja la suministra directamente el 8237). En el Block Transfer
Mode y en el Demand Transfer Mode, que implican mltiples transferencias, el 8237 es suficientemente
inteligente como para generar estados S1 slo cuando hay acarreo en la parte baja del bus de direcciones (1
de cada 256 veces) evitando acceder al latch externo cuando no es necesario modificarlo y ahorrando tiempo.
275 EL HARDWARE DE APOYO AL MICROPROCESADOR
PROGRAMACIN DEL 8237
El 8237 puede ser programado cuando HLDA est inactivo, siendo responsabilidad del programador
que esto sea as (es decir, programarlo antes de que comience a operar). En cualquier caso, puede existir el
riesgo de que mientras se programa un canal, se produzca una peticin de DMA en el mismo antes de acabar
la programacin, y probablemente en un punto crtico (cuando, por ejemplo, se acababa de enviar la mitad
de un valor de 16 bits). Para evitar este riesgo, antes de comenzar a programar un canal puede ser necesario
enmascararlo, desinhibindolo despus.
Registros internos del 8237.
Current Address Register (Registro de direccin en curso).
Cada canal tiene un registro de direccin en curso que almacena la direccin de memoria empleada
durante las transferencias del DMA. Su contenido es incrementado/decrementado despus de cada
transferencia. Este registro es inicializado por la CPU enviando dos bytes consecutivos; en modo
autoinicializacin, 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 nmero de bytes a
transferir en la operacin menos uno (para un valor inicial 100, por ejemplo, se transmiten 101 bytes). Tras
cada transferencia se decrementa: cuando pasa de 0 a 0FFFFh se genera el TC (Terminal Count) y el proceso
finaliza. Este registro es inicializado por la CPU enviando dos bytes consecutivos; en modo autoinicializacin,
su contenido inicial se restaura cuando sta se produce; de lo contrario contina con un valor 0FFFFh.
Base Address & Base Word Count Registers (Registros base de direccin y base contador de palabras).
Cada canal tiene tambin un registro base de direccin y otro base contador de palabras. Estos
registros almacenan el valor inicial de los registros de direccin en curso y contador de palabras en curso,
ya que ambos tipos de registros se cargan simultneamente durante la programacin. El valor almacenado
en estos registros se emplea en la autoinicializacin, para recargar los registros en curso.
Canal Registro(s) Direccin
A3 A2 A1 A0
Base de direccin y de direccin en curso Escribir 0 0 0 0
0 De direccin en curso Leer 0 0 0 0
Base contador de palabras y contador de palabras en curso Escribir 0 0 0 1
Contador de palabras en curso Leer 0 0 0 1
Base de direccin y de direccin en curso Escribir 0 0 1 0
1 De direccin en curso Leer 0 0 1 0
Base contador de palabras y contador de palabras en curso Escribir 0 0 1 1
Contador de palabras en curso Leer 0 0 1 1
Base de direccin y de direccin en curso Escribir 0 1 0 0
2 De direccin en curso Leer 0 1 0 0
Base contador de palabras y contador de palabras en curso Escribir 0 1 0 1
Contador de palabras en curso Leer 0 1 0 1
Base de direccin y de direccin en curso Escribir 0 1 1 0
3 De direccin en curso Leer 0 1 1 0
Base contador de palabras y contador de palabras en curso Escribir 0 1 1 1
Contador 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 un
comando Master Clear:
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-memoria
a nivel bajo 1 modo memoria-memoria
1 DACK sensible
a nivel alto 0 no fijar direccin en canal 0
1 fijar direccin canal 0
X si bit 0 = 0
0 controlador habilitado
0 DREQ sensible en alto 1 controlador inhibido
1 DREQ sensible en bajo
0 compresin de tiempo inhibida
1 compresin de tiempo activada
X si bit 0 = 1
0 prioridad fija
1 prioridad rotatoria
0 escritura posterior activa
1 escritura extendida activa
X 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, se
enva 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. Cuando se lee, dichos bits estarn a 1 (para leer un registro
de modo hay que utilizar antes el comando Clear Mode Register Counter, como se ver en la seccin de
comandos).
7 6 5 4 3 2 1 0
0 sin autoinicializacin 00 seleccionar canal 0
1 con autoinicializacin 01 seleccionar canal 1
10 seleccionar canal 2
0 modo incremento de direcciones 11 seleccionar canal 3
1 modo decremento de direcciones
00 transferencia de verificacin
00 Demand Transfer Mode 01 transferencia de escritura
01 Single Transfer Mode 10 transferencia de lectura
10 Block Transfer Mode 11 ilegal
11 Conexin en cascada XX si bits 6 y 7 ambos activos
Request Register (Registro de peticin de DMA).
El 8237 puede responder a peticiones de DMA tanto por hardware (lnea DREQ) como por software.
En este registro posee un bit para cada canal de DMA. Las peticiones por software no se pueden enmascarar,
aunque estn sujetas a la lgica de evaluacin de prioridades. Cada bit de este registro es activado o borrado
selectivamente por software. Todo el registro es borrado ante un Reset. Para modificar sus bits, se debe enviar
el comando Write Request register. Si se lee el registro, los bits 0 al 3 muestran el estado de las peticiones
en los canales 0 al 3 (los dems bits estn a 1). Las peticiones de DMA por software pueden serlo
indistintamente en el modo single o en el block. Para operaciones memoria-memoria, hay que hacer una
peticin de DMA por software en el canal 0.
Mask Register (Registro de mscara de DMA).
Cada canal tiene asociado un bit de mscara que puede ser activado para inhibir las solicitudes de
DMA a travs de la lnea DREQ. Este bit es automticamente activado cada vez que se produce un -EOP (al
final de la transferencia) a menos que el canal est en modo autoinicializacin. Cada bit de mscara puede
ser modificado por separado, o todos a la vez, con el comando apropiado. Todo el registro es puesto a 1 a
277 EL HARDWARE DE APOYO AL MICROPROCESADOR
travs del comando Master Clear o debido a un Reset, lo que inhibe las solicitudes de DMA por hardware
hasta que se enva un comando para limpiar el registro de mscara (o se borran los bits que se desee en el
mismo). Existen tres rdenes para actuar sobre el registro de mscara; la primera es a travs del comando
Clear Mask Register, que borra todos los bits de mscara; la segunda es por medio del comando Write Single
Mask 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 mscara a la vez.
Status Register (Registro de estado).
Contiene informacin de estado lista para ser leda por la CPU. Los bits 0 al 3 indican si los
respectivos canales han alcanzado un TC (Terminal Count) o se les ha aplicado una seal -EOP externa. Estos
bits 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 estn solicitando servicio, con independencia de que estn enmascarados
o no. De esta manera, enmascarando todos los canales y leyendo el registro de estado, por software se puede
decidir qu canales conviene desenmascarar, pudiendo el sistema operativo aplicar la gestin de prioridades
que desee llegado el caso. Estos bits (4 al 7) son actualizados cuando el reloj est en alto; un Reset o un
comando 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 peticin 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. Tras
completar el proceso de transferencia, la CPU puede averiguar la ltima palabra transferida leyendo este
registro, a no ser que el registro haya sido borrado por un Reset o un comando Master Clear.
Comandos del 8237.
A continuacin se citan algunos comandos especiales que pueden ser ejecutados leyendo o escribiendo
sobre el 8237. A diferencia de cuando hay que acceder a los registros de direcciones y contadores, aqu el
bit A3 est activo. Por tanto, de los 16 puertos de E/S que ocupa el 8237 en cualquier sistema, los 8 ltimos
estn relacionados con los comandos y los registros especiales. En el siguiente cuadro se recogen todos, y
despus se explican los ms confusos.
Comando Modo de Direccin
u operacin acceso A3 A2 A1 A0
Read Status Register (leer registro de estado) Leer 1 0 0 0
Write Command Register (escribir registro de comandos) Escribir 1 0 0 0
Read Request Register (leer registro de peticin de DMA) Leer 1 0 0 1
Write Request Register (escribir registro de peticin de DMA) Escribir 1 0 0 1
Read Command Register (leer registro de comandos) Leer 1 0 1 0
Write Single Mask Bit (escribir un solo bit de mscara de DMA) Escribir 1 0 1 0
Read Mode Register (leer registro de modo) Leer 1 0 1 1
Write Mode Register (escribir registro de modo) Escribir 1 0 1 1
Set Byte Pointer F/F (activar flip-flop primero/ltimo) Leer 1 1 0 0
Clear Byte Pointer F/F (borrar flip-flop primero/ltimo) Escribir 1 1 0 0
Read Temporary Register (leer registro temporal) Leer 1 1 0 1
Master Clear (inicializacin principal) Escribir 1 1 0 1
Clear Mode Register Counter (limpiar contador de registro de modo) Leer 1 1 1 0
Clear Mask Register (borrar registro de mscara de DMA) Escribir 1 1 1 0
Read All Mask Bits (leer todos los bits de mscara de DMA) Leer 1 1 1 1
Write All Mask Bits (escribir todos los bits de mscara de DMA) Escribir 1 1 1 1
Direcciones E/S de los comandos
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 envan de dos veces, existe un flip-flop interno que permite al
8237 conocer si lo que le llega es la primera mitad del dato o la segunda. Por precaucin, se puede borrar
primero para asegurar que el primer byte enviado se interprete como el menos significativo y, el segundo,
como el ms significativo.
Set first/last flip-flop (activar flip-flop primero/ltimo).
Dado que los valores de 16 bits se envan de dos veces, existe un flip-flop interno que permite al
8237 conocer si lo que le llega es la primera mitad del dato o la segunda. Por precaucin, se puede activar
primero para asegurar que el primer byte enviado se interprete como el ms significativo y, el segundo, como
el menos significativo.
Master Clear (inicializacin principal).
Este comando tiene el mismo efecto que un Reset hardware. Los registros de comando, estado,
peticin de DMA, temporales y los flip-flops internos (first/last y mode register counter) son puestos a cero,
siendo el registro de mscaras rellenado con bits a 1 (inhibir canales). El 8237 entra en estado inactivo.
Read/Write Request Register (leer/escribir registro de peticin de DMA).
El comando Write es empleado para escribir al registro de peticin de DMA y provocar una peticin
de DMA por software; tambin se puede utilizar Read para consultar su estado: los bits 0 al 3 muestran
entonces el estado de las peticiones en los canales 0 al 3 (los dems bits estn a 1). El formato para escribir
es el siguiente:
7 6 5 4 3 2 1 0
00 seleccionar canal 0
No importa su valor al escribir 01 seleccionar canal 1
Bits 4..7 a 1 al leer 10 seleccionar canal 2
11 seleccionar canal 3
0 borrar bit de peticin
1 activar bit de peticin
Clear Mask Register (borrar registro de mscara de DMA).
Este comando limpia los bits de mscara de los 4 canales, habilitndoles para recibir peticiones de
DMA por hardware.
Write Single Mask bit (escribir un slo bit de mscara de DMA).
Con este comando se puede seleccionar el bit de mscara que se desea modificar (activndolo o
borrndolo).
7 6 5 4 3 2 1 0
00 seleccionar canal 0
No importa su valor al escribir 01 seleccionar canal 1
10 seleccionar canal 2
11 seleccionar canal 3
0 borrar bit de mscara
1 activar bit de mscara
279 EL HARDWARE DE APOYO AL MICROPROCESADOR
Read/Write All Mask bits (leer/escribir todos los bits de mscara de DMA).
Este comando permite consultar o establecer el estado de todos los bits de mscara de DMA a la vez,
en los 4 canales.
7 6 5 4 3 2 1 0
canal 3 canal 2 canal 1 canal 0
No importa al escribir
Todos a 1 al leer
0 limpiar su bit de mscara
1 activar su bit de mscara
Clear Mode Register Counter (limpiar contador de registro de modo).
Cuando se escribe el registro de modo, se enva 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. Sin
embargo, al leer, cmo seleccionar el canal cuyo registro de modo se desea leer?. La solucin consiste en
hacer 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 continuacin. Con este comando
se borra dicho contador, de manera que a la siguiente lectura el 8237 devuelva el registro de modo del canal
0 (habr que seguir leyendo ms hasta obtener el registro de modo del canal deseado). Cuando se lee el
registro 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 direccin
E/S base 0. Es por tanto el chip del ordenador donde resulta ms fcil traducir las direcciones E/S de las
tablas tcnicas del fabricante a la direccin del espacio de E/S del PC.
Los AT y PS/2 poseen un 8237 adicional, accesible a partir de la direccin E/S 0C0h. Los puertos
estn direccionados en intervalos de 2, al repetirse en dos direcciones adyacentes (esto permite en los IBM
y otros muchos hacer un OUT de 16 bits en lugar de dos consecutivos de 8, pero no todas las mquinas lo
soportan). En los AT, este 2 controlador de DMA acta como maestro y est encargado de las operaciones
de 16 bits; su canal 0 es empleado para colgar de l otro 8259 que realiza las operaciones de 8 bits, por
compatibilidad 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 ambos
controladores de DMA (el de 16 bits, recurdese, slo disponible en AT):
Comando o registro Modo de acceso 8 bits 16 bits
Registro direccin canal 0 lectura y escritura 00 C0
Registro de cuenta canal 0 lectura y escritura 01 C2
Registro direccin canal 1 lectura y escritura 02 C4
Registro de cuenta canal 1 lectura y escritura 03 C6
Registro direccin canal 2 lectura y escritura 04 C8
Registro de cuenta canal 2 lectura y escritura 05 CA
Registro direccin canal 3 lectura y escritura 06 CC
Registro de cuenta canal 3 lectura y escritura 07 CE
Status Register lectura 08 D0
Command Register escritura 08 D0
Request Register lectura y escritura 09 D2
Command Register lectura 0A D4
Single Mask Bit escritura 0A D4
Mode Register lectura y escritura 0B D6
Set Byte Pointer F/F lectura 0C D8
Clear Byte Pointer F/F escritura 0C D8
Temporary Register lectura 0D DA
Master Clear escritura 0D DA
Clear Mode Register Counter lectura 0E DC
Clear Mask Register escritura 0E DC
Read/Write All Mask bits lectura y escritura 0F DE
Direcciones E/S de los controladores de DMA
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 y
el 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 8
bits (esclavo) que cuelga del que controla las transferencias de 16 bits (maestro) define los canales 0 al 3,
de los cules slo el canal 2 est ocupado en las operaciones de disquetes, al igual que los PC/XT. El 8237
encargado de las operaciones de 16 bits define los canales 5, 6 y 7 (el 4 est ocupado en colgar de l el otro
8237), estando todos ellos libres. La razn es que en los AT la memoria no se refresca por el DMA y el disco
duro por lo general se accede directamente, tambin sin DMA. Por tanto, en estas mquinas quedan nada
menos 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 16
bits, mientras que la serie 80x86 puede direccionar entre 1 Mb y 4 Gb
de memoria. Si tiene algo de sentido comn, se le habr ocurrido la
pregunta: Cmo es posible entonces que el DMA acceda a la
memoria del ordenador, con direcciones de 20 a 32 bits?. La solucin
tcnica adoptada por los diseadores del PC consisti en aadir unos
registros externos, ubicados fuera del 8237, que se encargan de
suministrar los bits de direcciones que faltan: son los denominados
registros de pgina de DMA, habiendo uno por cada canal.
Canal Puerto E/S del
DMA registro de pgina
0 87h (slo AT)
1 83h
2 81h
3 82h
5 8Bh (slo AT)
6 89h (slo AT)
7 8Ah (slo AT)
En los PC/XT, los registros de pgina de DMA poseen slo 4 bits significativos y generan la parte
alta de la direccin de memoria. En los AT, son significativos los 8 bits completos del registro de pgina de
DMA en el 8237 que controla las operaciones de 8 bits y 7 en el que gestiona las operaciones de 16 bits. El
siguiente esquema muestra cmo se generan las direcciones de memoria:
Registro de pgina 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 pgina 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 pgina 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 D1
siempre a cero
Los restantes bits del espacio de direcciones (lneas A24 a A31 del 386) no se pueden emplear, de
ah que algunas implementaciones de Unix tuvieran problemas para soportar ms de 16 Mb de memoria.
En general, desde el punto de vista del DMA, se puede imaginar la memoria como 16 bloques de
64 Kb (caso del PC/XT), como 256 bloques de 64 Kb (en accesos de 8 bits en el AT) o bien como 128
bloques de 128 Kb (en accesos de 16 bits tambin en el AT). En el DMA que trabaja con 16 bits, se
transfieren slo 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 diseadores
han sido suficientemente cautos como para colocar los primeros 640 Kb de la memoria
virtual justo en los primeros 640 Kb de memoria fsica del ordenador. La memoria de
pantalla y la de la tarjeta VGA tambin estn en su sitio. Por tanto, bajo las ltimas versiones
del DOS es factible (y probablemente lo seguir siendo) programar directamente el DMA
para realizar transferencias sobre la memoria normal. Sin embargo, sobre la memoria superior
281 EL HARDWARE DE APOYO AL MICROPROCESADOR
tampoco hay problemas. Aunque la direccin virtual ya no coincide con la fsica, cuando se
ejecuta una instruccin OUT sobre un registro de pgina, el controlador de memoria detecta
la circunstancia, ya que al parecer est protegido el acceso a esos puertos. A continuacin,
averigua qu instruccin ha provocado la excepcin y modifica convenientemente el valor
con el que se pretenda hacer OUT para adecuarlo a la direccin de memoria fsica y permitir
que siga funcionando. Esto explica por qu una instruccin de E/S sobre uno de estos puertos
puede 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 canales
son establecidos por defecto (y as permanecen cuando no se usan) en el modo single, transferencia de
verificacin, autoinicializacin inhibida y modo incremento. Por ello, en el 8237 esclavo se escribe el valor
40h 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. En
el 8237 maestro, el registro de modo del canal 4 (canal 0 de este chip) se programa con 0C0h, que equivale
al modo cascada; los dems canales se programan como en el otro 8237. El siguiente listado ha sido extrado
directamente de la BIOS del AT:
SUB AL,AL ; DACK sensible en bajo, DREQ sensible en alto
OUT 8,0 ; Escritura posterior, prioridad fija, sin compresin,
; controlador habilitado, sin fijar direccin en canal 0,
; memoria-memoria deshabilitado
OUT 0D0h,AL ; lo mismo con el segundo controlador
MOV AL,40h ; establecer modo para el canal 0
OUT 0Bh,AL
MOV AL,0C0h ; modo cascada en el canal 4
OUT 0D6h,AL
JMP SHORT $+2
MOV AL,41h ; establecer modo para el canal 1
OUT 0Bh,AL
OUT 0D6h,AL ; y para el 5
JMP SHORT $+2
MOV AL,42h ; establecer modo para el canal 2
OUT 0Bh,AL
OUT 0D6h,AL ; y para el 6
JMP SHORT $+2
MOV AL,43h ; establecer modo para el canal 3
OUT 0Bh,AL
OUT 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 las
memorias dinmicas consiste en ir leyndolas con suficiente rapidez como para que no se borre su contenido;
en realidad, dada su organizacin en filas y columnas, se puede refrescar a la vez un gran nmero de bytes
leyendo uno slo. Para una memoria de 1 Mb, basta con acceder a cualesquiera 1024 posiciones de memoria
consecutivas, cada menos de 4 milisegundos, para garantizar la fiabilidad del sistema. Para ello, el canal 0
del DMA es colocado en modo single, en modo incremento de direcciones, con autoinicializacin y en modo
transferencia de lectura (enviando el valor 58h al registro de modo). A continuacin, dicho canal es
desenmascarado, comenzando el refresco de la memoria. La razn es que la salida del contador 1 del
temporizador 8253 est conectada a la lnea de peticin del canal 0 del DMA, por lo que peridicamente el
8237 sustrae el control de los buses al 8086 para continuar el refresco por la direccin de memoria en que
se 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 pgina
del canal 0 no existe en los PC/XT; sin embargo, debido al diseo de la placa, es el registro de pgina del
canal 3 el que acta. En cualquier caso, es indiferente la direccin de memoria base empleada para refrescar.
Los restantes canales DMA, as como el Command Register, son programados del mismo modo que sus
colegas 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 ordenadores
compatibles, a travs de los canales 0 y 1, es poco atractiva. En los PC/XT es factible, como demuestran
algunas rutinas de dominio pblico, aunque ello suponga anular momentneamente el refresco de la memoria
dinmica (la propia transferencia de bytes la refresca); sin embargo es ms complicado y el movimiento se
realizara dentro de un nico segmento de 64 Kb. En los AT no existen todas estas complicaciones, pero aqu
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 byte
a transferir es de 8 bits (es decir, nada de emplear un canal de DMA de 16 bits, que sera mucho ms rpido)
y, por otro lado, el ms modesto 286 es bastante ms rpido que el DMA (por algo el disco duro del AT se
lee sin DMA). No digamos un 386 u otra mquina superior.
Cierto clebre libro de soluciones para programadores de compatibles afirma en la pgina 328 que
los AT emplean el DMA automticamente en las instrucciones MOVS para mejorar el rendimiento. Fuera
del mbito de la ciencia-ficcin, aqu propondremos otro uso no ms comn pero, en cambio, factible:
ralentizar el funcionamiento de los ordenadores AT. La autntica utilidad del DMA, conviene recordarlo, est
ligada al acceso a los disquetes, aunque de ello hay ejemplos en el apartado donde se trata la programacin
del NEC765.
El truco, cuya idea original hay que atribuir a Jess Arias, consiste en programar un canal en modo
autoinicializacin, para que se ponga a trabajar continuamente. Programndolo en modo single, le va robando
ciclos a la CPU de manera continua. En teora, en el modo block se debera quedar bloqueado el ordenador,
aunque las mquinas en donde lo he probado esto no sucede. En los PC/XT no consegu un resultado exitoso,
adems de que no tiene mucho sentido hacerlos ms lentos. Sin embargo, en los AT es bastante sencillo el
proceso y funciona en todas las mquinas en que se prob. A la hora de elegir un canal, se puede optar por
el 0, 1, 3, 5, 6 7. Casi todos son vlidos, pero el 0 y 1 no son recomendables: son los canales de ms
prioridad 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 disponible
es el 1). Por tanto, la eleccin 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 necesario
debido a su mayor prioridad.
Resulta interesante observar cmo ralentiza ms emplear un canal de 8 bits que uno de 16: en el
sistema 386-25 donde lo prob, el famoso test de velocidad de LANDMARK estima la velocidad
habitualmente en 27,8 MHz. Poniendo en marcha el canal 7, de 16 bits, la velocidad cae nada menos que a
7,3 MHz; utilizando el 3 (de 8 bits) baja a 6,3 MHz. Combinando ambos canales a la vez, el descenso es an
mayor, hasta los 4,3 MHz.
Las tradicionales utilidades de dominio pblico para ralentizar los AT suelen emplear la interrupcin
del temporizador, parando por completo el ordenador durante algunos instantes y dejndole a toda velocidad
el resto del tiempo. La ventaja de ralentizar por DMA es que el ordenador baja la velocidad de una manera
uniforme y no va a saltitos. Por otro lado, ralentiza tambin los juegos que controlan por su propia cuenta
la interrupcin del temporizador. Adems, casi ningn programa comercial se ocupa de programar los canales
del DMA, ni el propio BIOS toca los que no le incumben; por ello, una vez activado, es seguro que el efecto
durar cuanto desee el usuario. Por ltimo, el mtodo es an ms elegante porque ni siquiera se trata de un
programa residente: consume 0 bytes!.
Combinando el mtodo de ralentizacin por DMA con un aumento de los ciclos de refresco de la
memoria (a travs del canal 1 del 8254) se puede bajar todava an ms la velocidad, de manera tambin
uniforme. En concreto, en la mquina citada anteriormente, si se programa el canal 1 del 8254 con un valor
de cuenta 2 la velocidad cae a 1,4 MHz, segn el test de Landmark: los ciclos de refresco de memoria
castigan mucho a la CPU cuando la restan pocos MHz...
El inconveniente de ralentizar demasiado, combinando los dos mtodos citados, es que el teclado
comienza a fallar en mayor o menor medida (se enganchan las teclas de Shift y Ctrl, siendo preciso pulsarlas
de vez en cuando para desengancharlas; aparecen nmeros en los cursores expandidos...). En el siguiente
programita de demostracin, existen dos niveles de freno seleccionables. Utiliza el peor mtodo para
comprobar si el ordenador es un AT, a travs del byte de identificacin de la ROM (es 0FCh en un gran
nmero de ATs y 0F8h en los PS/2-80), aunque es sin duda una de las maneras ms rpidas de hacerlo. Las
funciones dmako() se encargan de poner K.O. el canal correspondiente, activando el DMA. Las recprocas
dmaok() devuelven el canal asociado a la normalidad, inhibiendo el DMA.
283 EL HARDWARE DE APOYO AL MICROPROCESADOR
#include <dos.h>
#include <string.h>
void
dmacnt(), 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 mquina 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 Ralentizacin moderada activa.\n");
}
else if (nivel==2) {
dmaok3(); dmaok7(); dmako3();
printf ("\n Ralentizacin elevada activa.\n");
}
else if (nivel==3) {
dmako3(); dmako7();
printf ("\n Ralentizacin mxima activa.\n");
}
else {
dmaok3(); dmaok7();
printf ("\n Ralentizacin 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 estimada
tras la ejecucin
de DMAKO.C en un
AT 386-25. Datos
calculados con el
test de LANDMARK 7,3
6,3
4,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 de
la pgina de 64K empleada (DMA 8 bits). Esto se ver con ms detalle en el apartado dedicado al controlador
de disquetes. Hay que tener en cuenta que una direccin segmentada aparentemente inocente puede estar
cruzando una frontera de DMA. Por ejemplo, 512 bytes contenidos a partir de 3FF2:0000 (que llegan hasta
3FF2:01FF) ocupan las direcciones fsicas 3FF20 a la 4011F, estando contenidos en las pginas 3 y 4.
Un intento de acceso DMA al lmite
de una pgina no produce error alguno, pero
el resultado es la corrupcin indeseada de
zonas de memoria no previstas, ya que al
llegar al final del segmento se vuelve de
nuevo a pasar por el principio del mismo.
Tratndose del DMA de 16 bits, el
problema estara en rebasar una frontera de
128 Kb. Realmente, estos problemas no se
deben al propio DMA en s y no suelen
presentarse en los sistemas que emplean el
DMA. Lo que sucede es que los IBM PC,
AT, etc. utilizan un DMA con direcciones
de 16 bits concebido para mquinas con
64K de memoria...
284 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
12.6 - EL CONTROLADOR DE DISQUETES NEC 765
12.6.1 - LA TECNOLOGA DE GRABACIN EN DISCO
Simple y Doble densidad: MF y MFM.
La superficie magntica de un disco est dividida en pistas concntricas, en cualquiera de las cuales
el cabezal de lectura/escritura puede ser posicionado con ayuda de un motor paso a paso. Los nicos datos
que se almacenan en el disco son bits, como se ver. El cabezal de la unidad de disco es, en esencia, una
bobina en la que se verifican dos leyes fundamentales de la fsica electrnica: por un lado, una corriente
alterna en dicha bobina provoca un campo magntico que vara al mismo ritmo que la corriente (lo que
permite magnetizar la superficie del disco para grabar los datos); por otro lado, aplicando un campo
magntico variable de manera constante a la bobina se genera una tensin constante en la misma (lo que
permite leer los datos previamente registrados sobre esa superficie magntica, dejando el cabezal deslizarse
sobre la misma).
A simple vista, por tanto, se podra intuir que registrar datos en un disco es una tarea sencilla: se
podran representar los bits (a 1 0) segn la presencia/ausencia de magnetizacin en cada punto de la
superficie. Sin embargo, la electrnica y mecnicas de precisin necesarias para este tipo de grabacin se
escapan an de las posibilidades tecnolgicas actuales. La solucin adoptada consiste en registrar, junto a los
bits de datos, una frecuencia de reloj de referencia que permita localizar los bits sin problemas: entre dos
registros magnticos de referencia en el disco (marcados con *), puede existir o no otro registro (que es lo
que implica que el dato sea un 1 un 0):
* * * * * * * *
1 1 0 1 0 0 0 1
Esto es lo que se denomina grabacin en simple densidad (MF). Al final, la superficie magntica
se puede considerar como un conjunto de pequeos imanes magnetizados en un sentido u otro: cuando se
recorra el disco con el cabezal en modo lectura, la variacin magntica inducir una corriente cuya
interpretacin permitir recuperar los datos grabados.
La electrnica de este sistema trabaja con dos tiempos bsicos diferentes: el que transcurre entre dos
impulsos 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 8
microsegundos. Por ello, para un byte de datos son necesarios 64 microsegundos: como la disquetera da 300
vueltas por minuto, emplea 200 milisegundos en cada vuelta; esto significa que en cada pista podra
almacenar tericamente 200000/64 = 3125 bytes. En un disco convencional de 80 cilindros y dos caras (160
pistas), 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 (tpicamente 720 Kb y 1,44 Mb tras el
formateo). Cmo se las apaan para doblar o cuadruplicar los discos actuales esta capacidad?. La respuesta
consiste en emplear los formatos de doble y alta densidad, respectivamente.
La tcnica de grabacin en doble densidad (MFM) consiste en prescindir de los impulsos de
referencia en la medida de lo posible. El mtodo se basa en no emplearlos para registrar bits a 1, o bien bits
a 0 aislados: tan solo se usarn para registrar secuencias de varios bits consecutivos a 0 (de lo contrario, una
secuencia de bits a 0, sin impulsos de referencia, implicara una prdida de sincronizacin). Aqu existen
ahora tres tiempos diferentes: el intervalo elemental es el lapsus de tiempo entre dos bits a 1; un intervalo
de doble duracin que ste representa la secuencia de bits 1-0-1; por ltimo, un tercer lapso de tiempo
correspondiente a 1,5 intervalos de tiempo elementales es empleado para crear los impulsos de referencia
285 EL HARDWARE DE APOYO AL MICROPROCESADOR
(marcados con *) o abandonar su generacin. Aunque en el grfico no queda quiz muy claro, este mtodo
permite grabar el doble de datos en un mismo intervalo de tiempo que el mtodo 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 mayor
depuracin de la electrnica de control, que permite reducir los tiempos de los diversos intervalos.
El formateo del disco: Ejemplo con el NEC 765.
La divisin del disco en pistas no es suficiente, ya que la cantidad de datos que almacenan es
demasiado 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 lgicas menores (sectores) de un tamao
razonable, que puedan ser accedidas por separado. En esto consiste el proceso de formateo, en el que el disco
queda estructurado como se describir a continuacin. Se ha tomado como referencia el proceso de formateo
que realiza el FDC (Floppy Disk Controller) 765 de NEC en MFM (en MF vara ligeramente).
El disco posee una perforacin de ndice (el pequeo agujerito de la superficie) que es comprobada
por un sensor ptico, lo que permite detectar el inicio de la informacin grabada en cada pista. Nada ms
comenzar la pista, hay 80 bytes con el valor 4Eh (ver esquema de la pgina siguiente): es lo que se denomina
el GAP 4A (GAP significa algo as como hueco o espacio). La razn de existencia de este pequeo rea se
debe a la necesidad de sincronizar las distintas unidades de disco, ya que no todos los sensores pticos actan
de manera totalmente idntica. Tras el GAP 4Ah se escriben 12 bytes a 0 en un rea denominada SYNC. La
misin de estos bytes a cero es crear un rea de marcas de sincronismo para que el controlador de disco se
sincronice con el reloj de referencia. Tras el campo SYNC viene un rea especial de tres bytes denominada
Index Address Mark o IAM (marca de direccin ndice), que existe slo al principio de la pista. Tras ella
aparece un byte 0FCh y, detrs, un GAP 1, en esta ocasin de 50 bytes con el valor 4Eh: su misin es dar
tiempo a que el FDC procese la marca de direccin ndice, que ser decodificada e interpretada por hardware.
Despus, a continuacin 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 direccin de identificacin), tambin de 3 bytes. Detrs, un byte 0FEh. Tras todo esto, aparece
el campo de ID: son 4 bytes que contienen la siguiente informacin: nmero de cilindro, cara del disco,
nmero de sector y tamao de sector (en la forma (LOG
2
bytes_por_sector)-7). Esto permite identificar a cada
sector por separado. Por razones de seguridad, se realiza una comprobacin CRC (especie de suma de
seguridad) de 16 bits entre la ID-AM y los 4 bytes del campo ID, cuyo resultado se almacena en los dos
bytes inmediatamente siguientes, con objeto de detectar futuros fallos en la integridad de la informacin. Para
dar tiempo al FDC a que se prepare para leer los datos que se vienen encima, hay despus un nuevo GAP
2 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).
Detrs 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 del
sector: puede tener una longitud de 128, 256, 512, 1024, 2048 4096 bytes (segn haya sido definido) que
nada ms ser formateado es inicializado con un valor seleccionable por el usuario. Por supuesto, a este rea
de datos se le aplica tambin un algoritmo CRC (junto con los bytes de la DATA AM y el byte 0FBh) y los
2 bytes que se obtienen se graban a continuacin. Finalmente, aparece el GAP 3, formado por cierto nmero
de bytes 4Eh seleccionable por el usuario al formatear (tpicamente entre 54 y 116). Este ltimo GAP tiene
una funcin muy importante: al escribir un sector en el disco, es difcil que la velocidad de la unidad sea
totalmente idntica a la de la unidad que formate el disco: si es menor, no sucede nada (el sector ocupara
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 se
escriben datos, el GAP 3 es mucho menor que cuando se formatea (del orden de la mitad de tamao), para
asegurar que no se invadir la zona del siguiente sector si la unidad es algo ms rpida de lo previsto. Los
sectores se suceden unos tras otros hasta completar la pista. Despus, el resto del espacio hasta que aparezca
de nuevo la perforacin de ndice se rellena con el GAP 4B final. Todo esto, en MFM (en MF, por ejemplo,
los bytes aadidos 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, tamao
SECTOR
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 - DESCRIPCIN DEL FDC (Floppy Disk Controller) 765.
Este controlador de disquetes es un chip muy evolucionado que realiza tareas de un nivel
relativamente alto. Fabricado inicialmente por NEC, tambin lo comercializan Rockwell (R 6765) e Intel
(i8272). Sus principales caractersticas son: tamao de sector programable (128, 256, 512, 1024, 2048 4096
bytes), posibilidad de programar todos los datos de las unidades, capacidad para controlar 4 disqueteras,
transferencia con o sin DMA, generacin de interrupciones; es compatible con mltiples microprocesadores
(Z80, 8086,...) y trabaja con un reloj sencillo de una sola fase (4 u 8 Mhz). Soporta densidades MF (simple
densidad) y MFM (doble densidad) en unidades estndar 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
SEALES DEL 765
Interface con la CPU.
RESET: Reset. Lnea de reinicializacin al estado por defecto.
-CS: Chip Selection. Lnea de seleccin 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 lnea de direccin define dos direcciones de E/S para
comunicar con la CPU. Suele ir conectada al A0 de la CPU.
DB0..7: Data Bus. 8 lneas de datos bidireccionales.
INT: Interrupt. Salida de peticin de interrupcin a la CPU del FDC, por
cada byte transferido.
Seales para el modo DMA.
DRQ: DMA Request. Solicitud de DMA al controlador de DMA.
-DACK: DMA Acknowledge. Seal de reconocimiento de solicitud concedida.
TC: Terminal Count. Lnea que indica el final de la cuenta de transferencia
en modo DMA; cuando no se emplea el DMA sirve tambin para
acabar la transferencia en sistemas controlados por interrupciones.
Seales para el interface con la disquetera.
DS0-1: Drive Select 0-1. Tambin 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 fsico del cabezal
sobre el disquete o levantarlo.
IDX: Index. Entrada del sensor ptico que detecta el inicio de la pista gracias a la perforacin de ndice del disquete.
RDY: Ready. Seal 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.
287 EL HARDWARE DE APOYO AL MICROPROCESADOR
-RW/SEEK: Read Write/Seek. Algunas de las lneas que comunican el FDC con la disquetera tienen doble funcin (para ahorrar
patillas en el chip): esta seal permite elegir la funcin de las 4 siguientes patillas.
FR/STP: Fit Reset/Step. La funcin FR permite borrar el error de flip-flop de algunas unidades. La funcin STP, mucho ms
utilizada, mueve un paso (un cilindro) la cabeza de lectura/escritura (en la direccin que indica LCT/DIR).
FLT/TRK0: Fault/Track0. La seal FLT es generada por algunas disqueteras en caso de error, pudiendo borrarse
a travs de la patilla anterior (FR/STP). La salida TRK0 indica cundo el cabezal alcanza el cilindro
0, gracias a un sensor ptico o mecnico, tras el comando de programacin Seek o el de
recalibracin.
LCT/DIR: Low Current/Direction. La seal LCT es necesaria para limitar la corriente de escritura al acceder a los cilindros ms
internos, por razones fsicas. DIR indica en modo Seek el sentido del movimiento del cabezal.
WP/TS: Write protect/Two Side. La seal WP indica si el disco est protegido contra escritura y es comprobada en las
operaciones de lectura/escritura; la seal TS se comprueba en las operaciones Seek y slo es necesaria en unidades de
dos cabezales.
WR DATA: Write Data. Lnea 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 circuitera electrnica adecuada cmo debe
ser escrito el flujo de datos: para la precompensacin 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 ledos del disquete.
DW: Data Window. Seal obtenida en un separador de datos a partir de los datos ledos.
VCO: VCO Syn. Esta seal 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.
Alimentacin y seales de reloj.
Vcc: Entrada de +5v, el chip no suele consumir ms 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.
PROGRAMACIN DEL 765
La nica lnea de direcciones del integrado (A0) define dos nicos puertos de E/S: el primero es el
registro principal de estado que slo puede ser ledo. A travs del segundo puerto, de lectura/escritura, se
accede al registro de datos, a travs del cual se programa el FDC, se envan y reciben los datos y se
obtienen los resultados.
Con el FDC se trabaja en tres fases diferenciadas: la fase de comando u orden es empleada para
enviar al FDC informacin sobre lo que tiene que hacer, lo que puede implicar enviar hasta 9 bytes en
algunos comandos. A continuacin viene la fase de ejecucin. Finalmente, la fase de resultados puede
obligar a leer del FDC hasta siete informaciones de estado diferentes (hasta que no se leen, el FDC no admite
ms rdenes). Este es el esquema general, si bien algunas rdenes carecen de fase de resultados, otras no
tienen fase de ejecucin...
El FDC dispone de 5 registros de estado internos. El principal puede ser accedido directamente como
se vio (A0=0) en cualquier momento. Los otros 4 registros (ST0, ST1, ST2 y ST3) slo son accesibles en
algunas 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 informacin en la fase de
rdenes. Este activa la seal Head Load y espera el tiempo de Head Load programado. El FDC comienza a
leer los IDs (identificadores) de los sectores hasta encontrar el sector buscado, con lo que pasa a la fase de
ejecucin, o hasta encontrar por segunda vez la perforacin de ndice del disco (en ese caso se pasa a la fase
de resultados para dar el error). En la fase de ejecucin, los datos son ledos del disco y enviados al
procesador o al DMA, a razn de un byte cada 8, 16, 26.67 32 microsegundos (segn la densidad
empleada: 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 leyendo
los sectores que van detrs en el proceso denominado multi-sector-read (se leen ms sectores hasta llegar al
final de la pista). En este comando, al igual que en alguno ms, se puede igualar el ltimo sector de la pista
al primero a ser accedido, pudindose prescindir en ese caso de la seal TC al acceder a un solo sector. De
todas maneras, al emplear el DMA, la transferencia finalizar realmente cuando el registro contador del DMA
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 seal TC, pudindose leer por
tanto el nmero de sectores deseado. Personalmente he comprobado que el ltimo nmero de sector en la
pista es ms bien el ltimo sector al que se desea acceder. Este comando produce 7 bytes en la fase de
resultados, que deben ser ledos obligatoriamente para que el FDC pueda admitir ms 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 funcin multi-sector debe
continuar 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 Nmero de cilindro
Byte 3 Nmero de cabeza
Byte 4 Nmero de sector
Byte 5 Tamao de sector: (LOG2 nbytes)-7
Byte 6 Ultimo nmero de sector en la pista
Byte 7 Tamao del GAP 3
Byte 8 Longitud de datos (si tamao de sector = 0)
RESULTADO (OBLIGATORIO LEERLO):
Byte 0 Registro de estado 0
Byte 1 Registro de estado 1
Byte 2 Registro de estado 2
Byte 3 Nmero de cilindro
Byte 4 Nmero de cabeza
Byte 5 Nmero de sector
Byte 6 Tamao de sector
2) COMANDO ESCRIBIR DATOS.
Este comando es totalmente anlogo al de lectura, pero actuando en escritura sobre el disco. La
secuencia de bytes a enviar y recibir es idntica: slo 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 dicho
sector con el comando Escribir Datos Borrados): estos sectores son ignorados en las operaciones normales
de lectura y escritura, aunque esta orden tambin permite leerlos. Por supuesto, esto no tiene relacin alguna
con la recuperacin 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 slo puedan ser ledos
con el comando Leer Datos Borrados. La secuencia de bytes a enviar/recibir es idntica al comando Leer
289 EL HARDWARE DE APOYO AL MICROPROCESADOR
Datos: slo 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 (si
el ltimo nmero de sector se indica correctamente) empezando cuando se detecta el paso de la perforacin
de ndice (si el sector inicial indicado no es realmente el primer sector de la pista, se producir error). An
en caso de error de CRC en el campo de ID o en el de datos, se contina 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 automtica y sin dar trabajo al programador todas las
tareas necesarias para inicializar una pista del disquete. Tras enviar el comando, habr que pasar al FDC 4
bytes por cada sector que haya en la pista a formatear: en ellos, para cada sector se indica el nmero de
sector deseado, lo que permite numerar los sectores de manera no consecutiva. El factor de Interleave 1:N
de un disco equivale al nmero N de vueltas que hay que dar para acceder una vez a toda la pista (depende
de que los sectores estn numerados consecutivamente o no); elegir un interleave ptimo es decisivo para
mejorar el rendimiento (si la unidad gira lo bastante rpida como para que no de tiempo a acceder a dos
sectores fsicamente consecutivos, el interleave debera ser mayor de 1:1; de lo contrario sera necesaria una
vuelta completa del disco cada vez que se accede a dos sectores de nmero consecutivo, que resulta ser
adems lo ms frecuente). El formateo comienza cuando el sensor correspondiente detecta el inicio de la pista
(por la perforacin de ndice), por ello todas las pistas quedan con los sectores colocados exactamente en la
misma posicin fsica: as, el sector N en una cara del disco coincide en su posicin con el de la otra y con
el del cilindro adyacente (si se numeran todas las pistas igual, claro).
Byte 0 0 MF 0 0 1 1 0 1
Byte 1 Idntico al byte 1 del comando LEER DATOS
Byte 2 Tamao de sector: (LOG2 nbytes)-7
Byte 3 Sectores por pista
Byte 4 Tamao del GAP 3
Byte 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 Nmero de cilindro
2 Byte Nmero de cabeza
3 Byte Nmero de sector
4 Byte Tamao de sector: (LOG2 nbytes)-7
7) COMANDO LEER ID.
Este comando permite leer del disquete el siguiente ID que aparezca. El ID asociado a cada sector
son los 4 bytes asignados durante el formateo, y consiste en informacin relativa al nmero de cilindro,
nmero de cabeza, nmero de sector y tamao del mismo. Estos nmeros suelen coincidir con los valores
fsicos reales relacionados con la posicin que ocupa el sector en el disco, si bien se pueden falsear en
290 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
tcnicas de proteccin de datos, aunque los copiones ms ordinarios esquivan sin problemas estas trampas
tan simples. Este comando consta de slo 2 bytes; en la fase de resultado devuelve la misma informacin que
el comando Leer Datos (precisamente, la informacin 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 con
un byte enviado por el procesador. Hay 3 comandos Scan de verificacin, que indican el modo de
comparacin por cada byte cotejado: igual, menor o igual, mayor o igual. El comando finaliza cuando se
cumple el criterio de comparacin elegido en todo el sector dado, cuando se comprueba el ltimo sector de
la pista o bien cuando se activa la patilla TC. La secuencia de bytes a enviar (9 en total) y a recibir es casi
idntica 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 comparacin.
11) COMANDO DE RECALIBRADO.
Este comando mueve el cabezal al cilindro 0 del disco. El FDC comienza a generar impulsos (por
medio de la lnea ST) para mover el motor paso a paso hasta que se le informe que ya se ha alcanzado el
cilindro 0 (a travs de la patilla TRK0 del 765); en cualquier caso, el comando finaliza tras enviar un mximo
de 77 impulsos a la unidad (de ah que pueda ser preciso repetirlo en las actuales unidades de 80 cilindros,
que siguen comportndose as por compatibilidad). Este comando carece de fase de resultados (puede
evaluarse el resultado por medio del registro de estado) y consta de slo 2 bytes.
Byte 0 0 0 0 0 0 1 1 1
Byte 1 Idntico al byte 1 del comando LEER DATOS
12) COMANDO DE POSICIONAMIENTO DEL CABEZAL (SEEK).
El 765 posee 4 registros internos que memorizan la posicin 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 enva este
comando al FDC, para colocar el cabezal sobre un cierto cilindro, ste comprueba si ya se encuentra sobre
el mismo: en caso contrario, genera las seales de control necesarias para instruir a la disquetera. Este
comando no posee fase de resultados: para comprobar el xito de la operacin hay que emplear la orden Leer
Estado de Interrupciones obligatoriamente (de lo contrario, el FDC no aceptar ms rdenes de lectura o
escritura). En cualquier caso, si la siguiente operacin es de escritura, tras este comando hay que hacer una
breve pausa (15 ms vale) porque si el cabezal no ha dejado de vibrar acarreara una escritura incorrecta (se
detectara gracias al CRC en una lectura posterior, pero casi nadie verifica tras escribir!: mejor asegurar que
no hay error). Si la siguiente operacin es de lectura, no es necesaria dicha pausa ya que en caso de fallar,
sera reintentada y no tendra mayor consecuencia. Si se trata de seleccionar el otro cabezal en el mismo
cilindro, despus de haber posicionado el otro, tampoco es necesaria pausa alguna. Abusar de las pausas
podra acarrear una ralentizacin del acceso, al no hallarse en ocasiones el sector buscado hasta la siguiente
vuelta del disco. 3 bytes:
Byte 0 0 0 0 0 1 1 1 1
291 EL HARDWARE DE APOYO AL MICROPROCESADOR
Byte 1 Idntico al byte 1 del comando LEER DATOS
Byte 2 Nmero 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 la
seal RDY (Ready) de alguna unidad; en modo NO-DMA las genera adems al inicio de la fase de resultados
y durante la fase de ejecucin. Las dos ltimas causas pueden ser reconocidas con facilidad por el
microprocesador, 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 fase
de resultado:
Byte 0 0 0 0 0 1 0 0 0
RESULTADO (OBLIGATORIO LEERLO):
Byte 0 Registro de estado 0
Byte 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 ste
el nico medio de conseguirlo. Consta de slo dos bytes, obtenindose un solo byte de resultado:
Byte 0 0 0 0 0 0 1 0 0
Byte 1 Idntico 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 debera ser el primero ejecutado antes de comenzar
las operaciones de disco. Sirve para indicar si se va a trabajar con DMA o no, as como los tres tiempos
bsicos que regirn la operacin del chip. Estos tiempos estn en funcin 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 seal que mueve el motor paso a paso del cabezal (lo que
determina el tiempo de acceso cilindro-cilindro). Depende de las caractersticas fsicas de la unidad. El valor para los bits SR se calcula
con la frmula (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 seal Head Load, slo relevante por lo general en unidades de 8" (en las dems suele
cargarse el cabezal nada ms activarse la seal Motor On). El tiempo Head Load (bits HL) se calcula con la frmula (HL+1)*4 en
unidades 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 seal Head Load vuelva a ser inactiva (slo suele ser
realmente significativo, una vez ms, en las unidades de 8"). Las viejas unidades de 8" normalmente estaban girando continuamente (para
evitar sus lentas aceleraciones y frenados por la inercia) y levantar o bajar el cabezal era un medio de proteccin de la superficie
magntica. El tiempo Head Unload (bits HU) se calcula con la frmula HU*32 en unidades DD y con HU*16 en unidades HD. La
unidad 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
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 puede
ser 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 (slo se obtienen como
resultado de algunas rdenes), pero ST0 y ST3 pueden ser ledos con un comando al efecto.
El Registro Principal de Estado.
En este registro se representan en todo momento los datos ms importantes sobre el estado del FDC.
Sirve tambin para regular la comunicacin 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 enviar
bytes a travs 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 un
byte preparado para el procesador. Cuando est a 0, quiere decir que est esperando un byte del
procesador. Este bit no es vlido 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 ejecucin; pasa
a 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 escritura
y, por tanto, no puede procesar ms comandos. Este bit se pone a 1 nada ms recibir el primer byte
de un comando, y baja cuando es ledo 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 inicia
un comando Seek o un recalibrado en alguna unidad, su bit se activa: mientras alguno de estos bits
est a 1, no se podrn enviar rdenes de lectura o escritura al FDC, pero s ms comandos Seek o de
recalibrado de las dems unidades. Estos bits no se ponen a 0 por s solos: se borran enviando el
comando Leer Estado de Interrupciones (si haba finalizado ya el comando Seek o el recalibramiento).
El Registro de Estado 0 (ST0).
Este registro se denomina tambin registro de estado de interrupciones, ya que en modo no DMA
permite identificar la causa de las interrupciones.
Bits 7, 6: Interrupt Code (cdigo de interrupcin). Con la notacin Bit7-Bit6 se tiene: 00 - Normal Termination
NT: comando finalizado con xito. 01 - Abnormal Termination AT: terminacin brusca (comando
iniciado pero no terminado): puede deberse a un error real o puede que no, ya que algunos sistemas
no emplean la seal TC y es necesario programar en ellos el ltimo sector de la pista como el ltimo
sector a acceder. 10 - Invalid Command Issue (IC): comando invlido (comando que no puede
empezar al ser ilegal; puede producirse tambin si se ejecuta el comando Leer estado de
Interrupciones sin haber ninguna en ese momento). 11 - Terminacin anormal (esta seal se produce
ante una variacin de la lnea RDY (Ready) durante el comando, que empieza pero no finaliza -por
ejemplo, si se retira el disquete de la unidad en medio de una operacin-).
Bit 5 (SE): Seek End (Fin de Seek). Este bit se pone a 1 cuando acaba la operacin Seek.
Bit 4 (EC): Equipment Check (comprobacin de equipo). Este bit se pone a 1 si la unidad informa de un error;
tambin puede ponerse a 1 si, tras un recalibrado, no aparece an la seal TRK0 que indica que se
ha alcanzado el cilindro 0. Esto puede suceder si el cabezal est sobre un cilindro superior al 77, ya
que el obsoleto FDC (y las ms modernas controladoras de disco, por compatibilidad) slo lo mueven
un mximo de 77 cilindros antes de considerar que el intento ha fallado (reptase el recalibrado).
Bit 3 (NR): Not Ready (no preparado). Se activa cuando la unidad informa de esta condicin; tambin cuando se
intenta acceder al segundo cabezal en unidades que solo tienen uno.
Bit 2 (HD): Head Address (direccin de cabezal). Indica el cabezal activo en el momento de la interrupcin.
Bits 1, 0 (US): Unit Select (Unidad activa): unidad activa durante la interrupcin (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 ejecucin de los
diversos comandos.
293 EL 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 pista
programado.
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 IDs
cuando fueron grabados.
Bit 4 (OR): Overrun (excedido el tiempo de transferencia). Los datos transitan entre el microprocesador y el FDC
a una velocidad mnima determinada (8, 16, 26.67 32 microsegundos). Si al leer datos del FDC el
procesador no es suficientemente rpido, puede llegar un dato sobrescribiendo el anterior cuando an
no haba sido ledo, lo que provoca que este bit se ponga a 1 para sealar 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 tambin a 1 con el comando leer ID si el FDC no puede leer sin errores el campo
ID (si falla el CRC). Por ltimo, tambin se pone a 1 si en el comando leer pista el sector inicial no
es encontrado.
Bit 1 (NW): Not Writable (escritura no permitida). Se pone a 1 al ejecutar algn comando que implique modificar
el 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 la
ausencia tambin de una Data Address Mark borrada) pone a 1 este bit (junto al bit MD del registro
de 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 slo en
el CRC correspondiente al campo de datos.
Bit 4 (WC): Wrong Cylinder (cilindro errneo). Al formatear la pista, se graba para cada sector informacin
relativa al nmero de cilindro, nmero de cabeza, nmero de sector y tamao del mismo. Si al leer
despus dicha pista hay contradiccin entre el n de cilindro solicitado y el n de cilindro que fue
registrado al formatear (debido normalmente a un posicionamiento del cabezal en un cilindro errneo),
este bit se pone a 1.
Bit 3 (SH): Scan Equal Hit (resultado de scan igual). Tras un comando de scan con la condicin de igual, este bit
se pone a 1 para indicar que la comparacin 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 ningn
sector 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 1
si el nmero de cilindro ledo 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 si
en la lectura de datos no aparece una Data Address Mark (ni siquiera borrada).
El Registro de Estado 3 (ST3).
Este registro de estado slo puede ser consultado por medio de la orden Leer estado de unidad. Se
obtiene la siguiente informacin:
Bit 7 (FT): Fault (fallo). Este bit se corresponde con la lnea Fault de algunas unidades.
Bit 6 (WP): Write protected (proteccin 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 lnea 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 lnea 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 (direccin del cabezal). Este bit se corresponde con la lnea Head Select del FDC.
Bits 1, 0 (US): Unit Select (unidad seleccionada). Estos bits se corresponden con el estado de dichas lneas del FDC.
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 travs de dos puertos de E/S, en la direccin 3F4h (registro
de estado) y en la 3F5h (datos). Adicionalmente, existe un registro denominado Registro de Salida Digital,
en la direccin E/S 3F2h, que controla los motores de las unidades y permite reinicializar el sistema de disco
y seleccionar la modalidad de operacin (con o sin DMA). Los valores de bits establecidos para el registro
de salida digital son los siguientes (los PS/2 slo 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 interrupciones
habilitadas en el bit 3) esperar la interrupcin de disquete que vendr (IRQ6 INT 0Eh) ejecutando despus
el comando leer estado de interrupciones; tambin hay que recalibrar, ya que el registro interno del FDC que
indica el cilindro actual es puesto a 0. En las mquinas 486 en particular, es necesario hacer una leve pausa
tras bajar este bit, ya que devolvindolo inmediatamente a 1 sucede que en ocasiones el 765 no se entera del
cambio y no se resetea! (algunos microsegundos bastan). Efectuar un reset es conveniente tras un error de
disco. En las mquinas AT o con controladoras de alta densidad existe otro registro ms al que se accede en
lectura, el Registro de Entrada Digital (3F7h). Su bit ms significativo indica si ha habido cambio de disco
en la ltima unidad seleccionada a travs del registro de salida digital; los restantes bits se emplean para
gestionar el disco duro. Una vez detectada la condicin de cambio de disco, hay que bajar este bit para
detectar futuros nuevos cambios por el procedimiento, un tanto extrao y quiz absurdo de llevar el cabezal
al cilindro 1 y despus al 0. Para leer la lnea de cambio de disco el motor debe estar encendido (se puede
encender, leer la lnea y volver a apagarlo despus tan deprisa que el usuario no note siquiera parpadear el
led de la disquetera). Si no se puede bajar este bit ser debido a que no hay disquete introducido. Tambin
a travs del puerto 3F7h, pero actuando como salida, se accede al Registro de Control del Disquete, que
permite 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 enviar
y recibir datos del disco. Las unidades de alta densidad de 1.2M siempre trabajan con 80 cilindros, lo que
sucede es que pueden leer discos de doble densidad saltando los cilindros de dos en dos. Esto significa que
para leer el cilindro 15 de un disco de 360K, ser necesario mover el cabezal al cilindro 30 (y programar el
765 para leer el 15, por supuesto, ya que ha sido formateado con ese nmero). La BIOS automatiza este tipo
de operaciones, pero cuando se accede directamente al disco no queda ms remedio que considerarlas. En
los discos de 3 nunca es necesario esto, ya que tienen siempre 80 cilindros. En la terminologa anglosajona,
la velocidad de transferencia se denomina data transfer rate y el movimiento doble del cabezal en los discos
de doble densidad recibe el nombre de double stepping. Los PS/2 poseen en 3F0h y en 3F1h dos registros
de estado adicionales que no es preciso considerar.
Un consejo til para los programadores en ensamblador es que realicen siempre una pequea pausa
de algunos microsegundos (40-60) entre bytes sucesivos de un comando enviado al 765. La razn para ello
no est muy clara, pero las BIOS AMI de 486 hacen esto y sus motivos tendrn. Accediendo desde un
lenguaje de alto nivel o en procesadores 386 o inferiores esto probablemente no es necesario.
12.6.4 - DENSIDADES DE DISCO Y FORMATOS ESTNDAR.
Las unidades de 5 de doble densidad giran a 300 r.p.m. (revoluciones por minuto); esto significa
que dan una vuelta cada 200 milisegundos. La velocidad de transferencia empleada es de 250 Kbit/segundo.
295 EL HARDWARE DE APOYO AL MICROPROCESADOR
Echando cuentas, en 200 ms se pueden registrar unos 250000*0,2 = 50000 bits de datos = 6250 bytes por
pista. Los disquetes de 360K poseen 9 sectores de 512 bytes; por cada sector hacen falta adems 62 bytes
que aade el NEC765 (ver al final del apartado 12.6.1) y otros 80 de GAP 3 que estima oportuno IBM: en
total, 654 bytes. As, en la pista no caben 10 sectores pero s los 9 citados. Como hay 40 cilindros en estos
disquetes (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 que
realizan ciertos formateadores avanzados. Sin embargo, IBM fue excesivamente conservadora al principio,
ya que slo formateaba 8 sectores por pista; luego se dio cuenta y rectific. Eran los viejos discos de 320
Kb, totalmente obsoletos aunque soportados an por el FORMAT del DOS. Tambin han existido antao
formatos de 180 e incluso 160 Kb, basados en unidades de una sola cabeza. Las unidades de 5 de alta
densidad giran a 360 r.p.m.; esto supone 166,66 ms por cada vuelta del disco. El aumento de velocidad se
decidi por motivos de fiabilidad. A nadie se le escapa que si el disco girara ms lento y se le enviaran los
datos a la misma velocidad, cabran ms datos... pero todo tiene un lmite (lo contrario sera un chollo). La
pretensin de IBM de elevar excesivamente -para la tecnologa 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 doble
densidad de 5 se emplea la tasa de 300 Kbit/segundo: la mayor velocidad de rotacin del disco es
compensada exactamente por la proporcionalmente mayor velocidad de transferencia, resultando posible de
esta 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 360
r.p.m. y no es posible alterar la velocidad para leer los viejos formatos, como indican otras publicaciones lo
que cambia es la tasa de transferencia!. Las controladoras de alta densidad pueden, por lo tanto, emplear
velocidades 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 el
FORMAT 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 a
los discos de 360K saltando los cilindros de dos en dos).
Las ms modernas unidades de 3 permitieron mantener la velocidad de 500 Kbit/seg con la
velocidad de rotacin clsica de 300 r.p.m., sin problemas de fiabilidad, lo que eleva an ms 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 tanto
512+62+108 = 682 bytes, con lo que caben 18 por pista en estas condiciones, lo que genera los conocidos
discos de 1440 Kb. Antes de las unidades de alta aparecieron las de doble densidad de 3: estas emplean
una velocidad de 250 Kbit/segundo, con lo que slo admiten 6250 bytes por pista (los mismos que un
disquete de doble densidad de 5) y 720 Kb por disco (tambin emplean un GAP 3 de 80 bytes). Con
controladoras 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. Sin
embargo, el FORMAT del DOS y las rutinas de la BIOS slo soportan en estos discos la velocidad de 250
Kbit/segundo al ser la nica que los PC/XT normalmente admiten. Por supuesto, el usuario siempre puede
perforar el disco para convertirlo en uno de alta densidad: la calidad de la superficie magntica en los discos
de 360K es suficientemente baja para que den errores en las ltimas pistas (las ms prximas al centro y con
menor longitud de circunferencia) al formatearles en alta densidad; sin embargo, en 3 los fabricantes no se
han complicado la vida y es probable que a veces se puedan formatear los discos de doble densidad como
de alta sin problemas, algo que pese a todo no es quiz recomendable. Las unidades de 3 detectan el tipo
FORMATOS DE DISCO ESTNDAR 5 Doble Densidad 5 Alta Densidad 3 Doble Densidad 3 Alta Densidad 3 Extra Alta D.
Velocidad de rotacin (R.P.M.) 300/360(*) 360 300 300 300
Velocidad de transferencia (bits/seg.) 250000/300000(**) 500.000 250.000 500.000 1.000.000
Esquema de codificacin de informacin MFM MFM MFM MFM MFM
Bytes brutos por pista 6.250 10.416 6.250 12.500 25.000
Tamao de sector en bytes [1] 512 512 512 512 512
GAP 3 al formatear con FORMAT [2] 80 84 80 108 80
Bytes que usa el 765 entre sectores [3] 62 62 62 62 62
Bytes ocupados por sector ([1]+[2]+[3]) 654 658 654 682 654
Sectores por pista 9 15 9 18 36
Bytes que usa el 765 en inicio de pista 146 146 146 146 146
Bytes aproximados que restan en GAP 4B 218 400 218 78 1310
Cilindros 40 80 80 80 80
Caras o cabezales 2 2 2 2 2
Sectores en el disco 720 2400 1440 2880 5760
Kbytes 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
296 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
de disco y las perforaciones del mismo slo sirven para que la disquetera sepa qu velocidad de transferencia
emplear (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 velocidad
de transferencia, con 25000 bytes por pista y 36 sectores: el doble de datos que en alta densidad, pero a un
precio mucho ms del doble, lo que les ha convertido en un lujo y un fracaso comercial. Existen unidades
de 3 perfeccionadas por medios pticos que almacenan 20 megabytes por disco, y que tambin admiten
disquetes de 720K y 1.44M (y a menudo, no los de 2.88M). El secreto de estos discos pticos (flopticals)
es la precisin en el posicionamiento del cabezal, lo que permite almacenar cientos de cilindros en lugar de
las 80 habituales. Tambin hay unidades ZIP que admiten disquetes (aproximadamente de 3) con capacidad
de 100 Mb 1 Gb, pero menos convencionales (estn sectorizadas por hardware).
Los discos normales estn formateados con sectores de 512 bytes en todos los casos. Estos sectores
son numerados a partir de 1 (y no a partir de 0) en el momento del formateo, y as habrn de ser accedidos
en el futuro. En una sola vuelta del disco es factible escribir o leer todos los sectores de una pista si se hace
de una vez con el comando apropiado, ya que accediendo de sector en sector podra no dar tiempo a acceder
al siguiente sector cuando el anterior acaba de pasar por delante del cabezal, lo que adems obligara a dar
una vuelta al disco por cada sector, con un desplome en picado del rendimiento. Lo mismo puede suceder
si los sectores estn excesivamente prximos debido al empleo de un formato no estndar de ms capacidad:
normalmente, los GAP 3 que separan los sectores son bastante amplios como para dar tiempo al 765, en las
operaciones de escritura, a conmutar entre la escritura de los ltimos bytes del sector (junto al CRC que va
detrs) y la lectura de los ID del sector siguiente; en caso contrario la operacin de escritura de mltiples
sectores terminara con error (sector no encontrado), a no ser que fueran escritos de uno en uno, con la
consiguiente ralentizacin del acceso. Experimentalmente se puede afirmar que el GAP 3 en alta densidad
no debera ser inferior a 32, ni tampoco inferior a 40 en doble densidad, lo que parece indicar que la unidad
necesita que los sectores estn separados al menos entre 0.5 y 1 ms, respectivamente; aunque estas cifras se
pueden rebajar incluso casi a la mitad, esos valores son los mnimos recomendados. En caso de tener que
infringir esta regla, la solucin sera emplear un interleave distinto del 1:1 habitual: en otras palabras, los
sectores pueden ser numerados de manera no consecutiva. Por ejemplo, con 9 sectores, se les puede colocar
en la pista, sucesivamente, con los nmeros 1, 6, 2, 7, 3, 8, 4 ,9, 5. As, entre dos sectores de nmero
consecutivo hay otro, y se gana tiempo para poder pillarlo; este ejemplo en concreto corresponde a un
interleave 1:2, ya que hay que dar dos vueltas al disco para poder acceder una vez a toda la pista. Hay casos
en que al juntar mucho los sectores e intentar escribir una pista no se produce el error: esto puede ocurrir
sobre todo con sectores de ms de 512 bytes, ya que cuando el cabezal acaba de acceder a un sector y va a
por el siguiente (que acaba de pasar de largo), no encuentra los ID del que va detrs hasta pasado un buen
rato; de ah a volver a encontrarse con el sector buscado puede transcurrir bastante menos de una vuelta del
disco y finalmente lo encontrara sin devolver error. Naturalmente, esto sigue sin ser interesante, una vez ms,
por razones de velocidad. Finalmente sealar que el GAP mnimo para operaciones de lectura multisector es
mucho menor que para las operaciones de escritura (bastara con un GAP de 1 2 bytes), ya que la unidad
no pierde tiempo en conmutar entre la escritura del sector y la lectura de IDs del siguiente.
Un pequeo detalle ms: conviene recordar que al formatear una pista, la controladora espera al paso
de la marca de ndice -el pequeo agujerito del disquete- lo que provoca que si todas las pistas se numeran
por igual, en ambas caras del disco estn colocados fsicamente en la misma posicin los mismos nmeros
de sector, gracias a esta sincronizacin, 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 del
mismo y nico sector N que le ha tocado. En la operacin habitual del disco, cuando se acaba de acceder a
una pista, lo ms probable es que haya que continuar en la siguiente (bien en el otro cabezal o en el cilindro
adyacente). Esta conmutacin 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 tambin del principio (a nadie se le escapa
que las pistas son circulares); si se conmuta de cabezal y el disco ya ha girado lo suficiente como para pasar
por delante del primer sector de la nueva pista, habr que volver a dar una vuelta entera. Esto puede suceder
si el GAP que hay al final de la pista no es lo suficientemente grande. Y, por desgracia, de hecho sucede con
todos los formatos de disco del DOS. Al pasar de una pista a la adyacente, en operaciones de escritura, se
297 EL 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 se
acaba con el sector 18 de una pista y se pasa a la siguiente, el cabezal est sobre algn 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 para
llegar al 1. La solucin a este problema pasa por numerar los sectores, de una pista a otra, deslizando la
numeracin (tcnica 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 N
16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Pista N+1
13 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 slo tres pistas, pero las siguientes tendran un tratamiento anlogo.
Realmente, al conmutar de un cabezal a otro en el mismo cilindro no hace falta deslizar tanto la numeracin,
ya que es una operacin ms gil y con menos retardos. En el ejemplo, experimentalmente se puede
determinar que en vez de 3 bastara con desplazar 2 sectores la numeracin. En los discos de 5 de alta
densidad se pueden recomendar los mismos desplazamientos de numeracin. Sin embargo, en los de 5 y
doble densidad bastara con desplazar un sector el orden al conmutar de cabezal (y los mismos 3 al cambiar
de cilindro). En los de doble densidad de 3 conviene desplazar un sector la numeracin al conmutar de
cabezal y 2 al cambiar de cilindro. Por supuesto, estos valores son los ms convenientes en general, si bien
algn ordenador en concreto podra operar mejor con otra numeracin similar a sta aunque no idntica. En
cualquier caso, numerar todos los sectores de las pistas por igual, que es lo que hacen todas las versiones del
FORMAT del DOS (al menos hasta la versin 6.0 del sistema), resulta extremadamente ineficiente y puede
reducir a la mitad la velocidad de los disquetes. Algunos buenos formateadores (como FDFORMAT con sus
opciones /X e /Y) suelen tener en cuenta estos factores. Por supuesto, esta numeracin de los sectores no
implica la ms mnima prdida de compatibilidad en los disquetes estndar: lo que sucede es que los
creadores 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 mquinas empleando el DMA, por medio del
canal 2 del 8237. Sin embargo, como veremos en un apartado posterior, es factible realizar las operaciones
directamente, sin ayuda del DMA. Al emplear el modo DMA, se produce una interrupcin IRQ6 (INT 0Eh)
para avisar del trmino de la operacin de disco realizada. Al emplear el DMA conviene tener cuidado con
evitar un desbordamiento en el offset 0FFFFh de la pgina empleada. Por ejemplo, intentar leer o grabar un
sector normal de 512 bytes entre las direcciones de memoria 3FF2:0000 y la 3FF2:01FF (direcciones
absolutas 3FF20 a la 4011F) resultar fallido al estar implicadas las pginas de DMA 3 y 4, cuando slo
puede estarlo una de las dos. En la prctica, ser necesario reservar memoria por importe del doble del
tamao del (o los) sector(es) a ser accedido(s) y hacer clculos para establecer una direccin de transferencia
que coincida dentro de una sola pgina de DMA. No tener en cuenta este factor es jugar a la lotera con los
discos. La BIOS del sistema se encarga de comprobar por software si el buffer facilitado cruza una frontera
de 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 operacin: se
corrompen zonas de memoria no previstas y
el resultado probable es disfuncin y/o
cuelgue del sistema (a no ser que haya
mucha suerte). Sin embargo, cuando el DOS
se carga en memoria al principio del
arranque, modifica la INT 13h de la BIOS
para que esta interrupcin nunca devuelva
un error debido a este motivo (en cambio, la
INT 40h, que es quien realmente controla
los disquetes en la inmensa mayora de los
ordenadores AT y que es invocada desde
INT 13h, s puede devolver errores de
frontera de DMA).
765DEBUG 3.1 - UTILIDAD PARA ANALISIS AVANZADO A BAJO NIVEL DE DISQUETES.
Programacin 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 Garca de Celis.
F2 - Seleccionar unidad/densidad y resetear.
F3 - Recalibrar cabezal (necesario tras F2).
F4 - Cambiar de cabezal.
F5 - Posicionar cabezal.
F6 - Leer IDs.
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 opcin: _
Figura 12.6.5.1 PANTALLA PRINCIPAL DEL PROGRAMA
298 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
El siguiente programa de ejemplo ha sido realizado ntegramente en Borland C (compilable tambin
sin errores en Turbo C 2.0) y permite practicar al lector con la operacin a bajo nivel del disco. Se pueden
leer y escribir sectores (con tamaos normales o no), formatear pistas, leer los ID de una pista, y todas las
operaciones auxiliares necesarias (seleccionar unidad, velocidad de transferencia, recalibrar, seleccionar
cabezal, posicionar cabezal, elegir MF/MFM). La opcin de leer IDs es especialmente til para analizar
discos con protecciones anticopia; se trata adems de una tarea inevitable que ha de realizar necesariamente
cualquier copin, como paso previo a la duplicacin del disquete. En esta opcin se utiliza una interesante
rutina de temporizacin de alta precisin, empleando el 8254, para poder medir con exactitud los
milisegundos de disco que ocupa cada sector en la pista y poder hacerse una idea de cmo est organizada
y aprovechada. El formateo tambin es especialmente verstil, ya que permite editar, sin lujos pero con
eficacia, los bytes de los sectores
propuestos por defecto -los ms razonables
por otra parte- antes de enviarlos al
controlador. Este programa es un til banco
de pruebas para medir la fiabilidad de
tcnicas de formateo especial, para idear y
probar mtodos de proteccin anticopia y,
en general, para aprender sobre el
funcionamiento a bajo nivel de los discos.
El dato de la velocidad de transferencia no
es relevante por lo general en los PC/XT.
La seleccin incorrecta de una sola opcin
puede provocar que el programa falle,
aunque al cabo de unos segundos se
recupera el control. Las dos primeras
opciones del men no son obligatorias;
pero conviene seleccionarlas al principio y,
en general, cada vez que se cambie de
disco. Una lnea inferior informa
permanentemente de los principales
parmetros activos, si bien no conviene
creer ciegamente en ella. Por ejemplo, si se
ha intentado posicionar el cabezal en el
cilindro 120 de un disco formateado, y
luego se le vuelve a posicionar en el 70, en
esa lnea aparecer el valor 70 aunque al
leer los ID podramos descubrir que est
realmente sobre el cilindro 31, ya que esa
unidad no soporta ms de 82 cilindros
(numerados de 0 a 81) y no pudo pasar del
81 cuando se le orden ir al 120. En este
ejemplo particular, lo ms aconsejable
despus sera recalibrar, ya que el programa
cree que est sobre el cilindro 70 y las
opciones de leer y escribir sector fallarn;
ya que no preguntan el nmero de cilindro
y emplean el que se supone activo al enviar
el comando al controlador.
Sector a leer: 1
Tamao de sector:
0 -> 1-128 bytes
1 -> 256 bytes
2 -> 512 bytes
3 -> 1024 bytes
4 -> 2048 bytes
5 -> 4096 bytes
Elige: 2
Resultado de la operacin:
[ST0=0x01] [ST1=0x00] [ST2=0x00]
[Cilindro 1] [Cabezal 0] [Sector 1] [Tamao 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 NA
0030: 4D 45 20 20 20 20 46 41 - 54 31 36 20 20 20 FA 33 ME FAT16 3
0040: C0 8E D0 BC 00 7C 16 07 - BB 78 00 36 C5 37 1E 56 .|.. x.6 7.V
0050: 16 53 BF 3E 7C B9 0B 00 - FC F3 A4 06 1F C6 45 FE .S >| .. .. E
0060: 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|.R
00A0: 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 XXXG.HH..|2
0110: 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.YZX
0130: 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 disco
01B0: 64 65 20 73 69 73 74 65 - 6D 61 0D 0A 52 65 65 6D de sistema..Reem
01C0: 70 6C 61 63 65 20 79 20 - 70 72 65 73 69 6F 6E 65 place y presione
01D0: 20 63 75 61 6C 71 75 69 - 65 72 20 74 65 63 6C 61 cualquier tecla
01E0: 0D 0A 00 49 4F 20 20 20 - 20 20 20 53 59 53 4D 53 ...IO SYSMS
01F0: 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 de
transferencia 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 prctica se reserva el doble
de la memoria necesaria y se asigna el puntero de tal manera que esto no suceda en ningn caso. El programa
consta de un men desde el que se accede a las diversas opciones que desembocan finalmente en funciones
299 EL HARDWARE DE APOYO AL MICROPROCESADOR
independientes. La funcin seleccionar() permite elegir la unidad activa, resetendola y enviando el comando
specify al FDC.
La funcin recalibrar() enva este comando al FDC y lo repite si falla, por si estaba sobre un cilindro
superior al 77; en esta funcin y en las restantes, para detectar el fin de la operacin se espera la llegada de
la interrupcin de disco correspondiente (IRQ 6, ligada a INT 0Eh). La BIOS se encarga en esta interrupcin
de activar el bit ms significativo de la posicin 40h:3Eh. La funcin esperar_int() espera la llegada de la
interrupcin comprobando dicho bit durante un par de segundos antes de considerar que la operacin ha
fallado, devolviendo despus dicho bit a 0. Realmente, aunque haya un error la interrupcin debe llegar y el
comando ha de finalizar. Sin embargo, el FDC es a veces demasiado flexible: por ejemplo, si la portezuela
de la unidad (en 5) est abierta y hay un disco introducido, se puede quedar esperando indefinidamente.
Adems, en general, en la programacin a bajo nivel es conveniente no hacer nunca bucles infinitos para
esperar a que suceda algo. Tras el comando de recalibrado hay que ejecutar el de lectura de estado de
interrupciones, cuyo resultado es adems impreso en pantalla durante 1,5 segundos para dar tiempo a leerlo
sin tener que pulsar teclas (es muy poca informacin y se puede leer en menos de un segundo...).
La funcin posicionar() lleva el cabezal sobre el cilindro solicitado. Si se est trabajando con una
velocidad de 300 Kbit/seg, correspondiente normalmente a un disco de 5 y doble densidad (360K), se
pregunta 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 nmero de cilindro ser multiplicado por dos al enviar el comando seek al FDC, ya que es
un disco formateado con 40 pistas. Al final se ejecuta nuevamente el comando de lectura de estado de
interrupciones, imprimiendo el resultado y haciendo una pausa para que de tiempo a leerlo, aunque si se
omitiera este paso y la siguiente operacin fuera de escritura al menos habra que esperar 15 milisegundos
para dar tiempo al cabezal a asentarse y dejar de vibrar. Realmente, en este programa ni eso hara falta, ya
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
Figura 12.6.5.3 LECTURAS CORRECTA E INCORRECTA DE IDs
que no hay humano tan rpido que en
menos de 15 ms despus de haber escogido
la opcin de posicionar cabezal pueda
elegir la de escribir sector en el men
principal. Pero en otros programas, donde
se posicione repetidamente el cabezal y se
acceda al disco en escritura repetitivamente,
conviene no olvidar hacer la pausa. Bueno,
si se olvida, no sucede nada: slo se podra
producir algn error al escribir que no se
detectara hasta una posterior lectura. Lo
malo es que estos errores son espordicos
y resulta muy difcil localizar su origen.
Las funciones leer_sector() y
escribir_sector() son muy parecidas. La
principal diferencia es que la primera
muestra el sector ledo (ver figura 12.6.5.2)
y la segunda tiene que preguntar el byte
con que rellenar el sector escrito, ya que
no permite editarlo. Antes de leer el sector
se rellena el buffer en memoria con la
signatura 5AA5h. Tras la lectura, el sector
es mostrado -incluso si se produjo error-
aunque si el usuario observa que contiene
precisamente 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 luego
veremos. La lectura y escritura de los sectores se realiza por DMA, el cual es programado por
prepara_dma().
300 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
La funcin leer_id() enva 22 veces dicho comando al FDC, para leer los ID (los 4 bytes con que
se formate cada sector) y la informacin de estado (registros ST0..ST2). Probablemente no habr ms de
21 sectores en una pista, por lo que ser posible echar un vistazo detallado a la misma. El primer sector en
aparecer no es el 1 ni el de nmero ms
bajo: sencillamente, el primero en pasar por
el cabezal al ejecutar el comando; como la
unidad estaba girando con antelacin y el
usuario elige la opcin cuando quiere, el
primer sector visualizado ser cualquier
sector de la pista aleatoriamente. Si hubiera
ms de 21 sectores en la pista, se
visualizaran slo los 21 primeros en pasar
delante del cabezal. Resulta interesante
saber cunto tiempo transcurre entre el
paso de un sector y otro, lo que permite
conocer su tamao real (interesante en
discos con proteccin anticopia) y tambin
ensayar nuevos formatos de disco. Por
ejemplo, si se formatean ms sectores de
los que caben en una pista, el comando de
formatear termina siempre con xito, pero
alguno de los ltimos sectores habr
machacado a los primeros, y la manera ms
sencilla de verlo es examinando los ID a
ver si estn todos. De hecho, entre el
ltimo sector de la pista y el primero
debera existir una mayor separacin que
entre otros dos sectores cualquiera, debido
a los GAP ubicados al final de la pista y al
principio de la misma (que conviene no
reducir demasiado). Para medir el tiempo,
se programa el 8254 (u 8253 en los
PC/XT) con una cuenta 0xFFFF. A partir
de ese momento, se espera que llegue la
interrupcin de disco y se comprueba si el
contador se ha decrementado hasta 0 y se
ha vuelto a recargar con 0xFFFF: en ese
caso, la variable cnth se incrementa para
indicar que han pasado 65535/1193180
segundos ms; si llegara a valer ms de 8
se abortara el proceso al considerar que la
interrupcin tarda demasiado en llegar (ms
de 0,4 segundos en los que el disco ms
lento ya ha dado dos vueltas). Tras el final
de cada comando de lectura de ID, se
recarga inmediatamente la cuenta inicial (el
valor 0xFFFF) en el contador 2, por el
procedimiento de bajar y subir la lnea
GATE del mismo, con objeto de que
empiece a contar el tiempo para el prximo
sector desde ya mismo. Se lee la
informacin que devuelve el FDC pero no
Tamao de sector:
0 -> 128 bytes
1 -> 256 bytes
2 -> 512 bytes
3 -> 1024 bytes
4 -> 2048 bytes
5 -> 4096 bytes
Elige: 0
Nmero 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 cuatro
bytes que hay que enviar al controlador, por cada uno
de los sectores de la pista, que estn numerados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
21 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 estn ahora.
Elige opcin.
Sector a alterar: 6
N Cilindro (anterior=0): 0
N cabezal (anterior=0): 0
N sector (anterior=6): 6
Tamao sector (anterior=0): 1
De acuerdo (S/N)?
Resultado de la operacin:
[ST0=0x01] [ST1=0x00] [ST2=0x00]
[Cilindro 65] [Cabezal 1] [Sector 0] [Tamao 0]
Formateo correcto. Pulsa una tecla.
Longitud (ms) Sector Tamao 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 ms IDs [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 ltimo
valor de cuenta ledo del 8254 permiten determinar con precisin milimtrica el tiempo que ha pasado desde
301 EL HARDWARE DE APOYO AL MICROPROCESADOR
el envo del comando de lectura de IDs hasta la obtencin del resultado. El primer dato de tiempo ledo es
incorrecto por doble motivo: por un lado, el cabezal poda estar en medio de un sector cuando se envi el
comando y el tiempo medido no sera la longitud del sector anterior sino de medio sector anterior; por otro
lado, la cuenta es recargada (cambio de la lnea GATE) al final de cada comando en lugar de al principio,
por razones de precisin. Por ello, se imprimirn los resultados de las 21 ltimas muestras, descartando la
primera. En la figura 12.6.5.3 hay dos ejemplos de lectura de ID, de la primera pista de un disquete de 1.44M
creado por el FORMAT del DOS. En el primero el resultado es correcto; en el segundo, la velocidad
seleccionada era incorrecta (no los 500 Kbit/seg necesarios) y el FDC no ha podido encontrar los sectores,
teniendo adems que dar dos vueltas al disco (200 ms en cada una de ellas). Si no hubiera disquete o la
portezuela estuviera abierta, al cabo de un minuto y medio aparecera una pantalla con datos de tiempo N.D.
(no determinado) y todos los dems bytes con ?? para indicar el error. Resulta increble la precisin media
de la medida: 399,5 ms frente a los 400 reales: una desviacin media de 0,5 milisegundos!, si bien esto
depender del ordenador: cuanto ms rpido, ms exacta resulta la medida.
La funcin formatear_pista() pregunta los parmetros bsicos (nmero de sectores, tamao, GAP
y byte de inicializacin) y genera una tabla con los 4 bytes que hay que enviar al FDC por cada sector. Sin
embargo, permite al usuario editar rudimentariamente dicha tabla con la funcin editar_tabla_fmt(), para
permitir a ste ensayar trucos, ya que los valores propuestos por defecto son por lo general los ms
convenientes. En esos 4 bytes que hay por cada sector se almacenan el nmero de cilindro, el de cabezal, el
nmero de sector y el tamao. En la funcin de edicin se permite cambiar los bytes de un slo sector, o
cambiar uno de los 4 bytes en todos los sectores. Estos 4 bytes identifican cada sector y son comparados con
los que se envan en el futuro comando de
lectura o escritura de sector, debiendo
coincidir plenamente para que el FDC
encuentre el sector. El nmero de cilindro
y el de cabezal suelen coincidir -y as son
propuestos por defecto- con el cilindro y el
cabezal en que est dicho sector; cambiar
esto puede ser interesante en tcnicas de
proteccin de informacin, ya que el sector
desaparece pero realmente sigue estando
ah: la diferencia es que a la hora de leerlo
hay que indicar al FDC no el cilindro real
sobre el que est posicionado el cabezal
sino el nmero de cilindro y cabezal que se
programaron al formatear el sector, que
pueden ser cualquier otro. Este programa,
a la hora de leer los sectores no pregunta el
nmero de cilindro ni cabezal -para ahorrar
tiempo- por lo que no permite verificar esta
propiedad, pero con una pequea y sencilla
modificacin el lector podra comprobarlo
por s mismo. Lo que s puede resultar ms
interesante es cambiar el nmero de sector
propuesto por defecto o, mejor an: su
tamao. Al formatear la pista, el tamao de
Sector a leer: 6
Tamao de sector:
0 -> 1-128 bytes
1 -> 256 bytes
2 -> 512 bytes
3 -> 1024 bytes
4 -> 2048 bytes
5 -> 4096 bytes
Elige: 1
Resultado de la operacin:
[ST0=0x41] [ST1=0x20] [ST2=0x20]
[Cilindro 0] [Cabezal 0] [Sector 6] [Tamao 1]
Error de lectura (el sector puede estar mal ledo).
Nota: el buffer de lectura contena el patrn 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 AAAAAAAAAAAAAAAA
0010: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0020: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0030: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0040: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0050: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0060: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0070: 41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0080: 6B 70 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E kpNNNNNNNNNNNNNN
0090: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN
00A0: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN
00B0: 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 ....@NNNNNN
00D0: 4E 4E 4E 4E 4E 4E 4E 4E - 4E 4E 4E 4E 4E 4E 4E 4E NNNNNNNNNNNNNNNN
00E0: 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 TAMAO TRUCADO
los sectores es asignado al enviar el comando de formateo al FDC: todos los sectores tendrn dicho tamao,
con independencia del tamao particular que se asigne al enviar los 4 bytes especficos. En otras palabras,
si se programa un tamao 2 (de 512 bytes) en el comando de formateo, todos los sectores sern de 512 bytes,
aunque alguno est definido como de 1024, de 256 bytes,... en el 4 byte de informacin enviado por cada
sector al FDC. Por tanto, Para que sirve este byte?: una vez ms, para posibilitar la lectura. Si un sector est
programado con tamao 3 (1024 bytes) habr de ser ledo indicando tamao 3. Si era de 512 bytes, lo que
sucede es que adems del sector se leen, ni ms ni menos, los GAPs que van detrs, los IDs e incluso parte
del siguiente sector; por supuesto que se produce un lgico error de CRC al leer, pero los datos ledos son
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 25
sectores de 128 bytes con un GAP 3 de 50 bytes, rellenndolos al formatear con el byte 65 (41h, cdigo
ASCII de la A). Teniendo en cuenta los 62 bytes que el FDC aade entre sectores en MFM,
(128+62+50)*25=6000, por debajo del lmite de 6250 en este tipo de disquetes. Los 4 bytes del sector 6
resultan modificados para asignarle un tamao 1 (256 bytes), aunque el sector es realmente de 128 bytes. La
posterior lectura de IDs demuestra cmo ha quedado la pista, si bien slo se pueden ver en una pantalla los
ID de 21 sectores. En la figura 12.6.5.5 se intenta leer dicho sector y, pese al error de CRC, resulta evidente
que es bien ledo (junto con todo lo que va detrs). La ltima lnea del volcado hexadecimal es el inicio del
siguiente sector de la pista. El lector puede verificar que el esquema del final del apartado 12.6.1 es rigurosa
y milimtricamente cierto: todos los GAPs, ID y bytes introducidos por el FDC entre sectores aparecen
claramente reflejados en la figura. Por supuesto, una posterior escritura del sector 6 pisara el 7. De ah que,
ancdotas a parte, no suele resultar muy til generalmente hacer este tipo de maniobras... o tal vez si?.
La funcin mostrar_resultados() es invocada desde las anteriores, con objeto de leer los 7 bytes que
devuelve el FDC al trmino de los principales comandos e imprimirles en pantalla. La funcin
mostrar_sector() ensea en pantalla el volcado hexadecimal del buffer donde se leen los sectores, en pginas
de 256 bytes, teniendo en cuenta el tamao de los mismos y permitiendo cierta movilidad.
La funcin motor_on() arranca el motor de la unidad si an no estaba en marcha, ajustando al valor
mximo la variable que indica cundo se detendr, con objeto de evitarlo en lo posible. Al menos estar
girando durante 14 segundos en el peor de los casos. La funcin motor_off() ajusta dicha variable para que
el motor se pare en unos 3 segundos. La funcin outfdc() enva bytes al FDC pero sin esperar ms de 440
ms en caso de que ste, por cualquier error, no est dispuesto a recibirlos. Su recproca infdc() lee un byte
del FDC considerando un fracaso la operacin si ste no responde en menos de 440 ms (en estos casos
devuelve un valor negativo para que la funcin que llama advierta el error). La funcin esperar_int() ya fue
comentada anteriormente. Por ltimo, la funcin prepara_dma() programa el 8237 para transferir el nmero
de bytes indicado, en el modo apropiado (lectura/escritura) y en la direccin del buffer empleado.
/*********************************************************************
* *
* 765DEBUG 3.1 - Programa de anlisis 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 pginas 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(" Programacin 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 Garca
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 IDs.");
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 opcin:");
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,
303 EL 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 interrupcin */
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);
else
outfdc (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 interrupcin */
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\tEs disco 5-360K en unidad 1.2M-HD? (S/N): ");
r=((getch() | 0x20)==s)+1;
printf("%c\n", r==1?N:S);
}
else
r=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 interrupcin */
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\nTamao 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 tamao (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); /* tamao si tsector=0 */
esperar_int(); /* esperar interrupcin */
mostrar_resultados (&r);
motor_off();
if (r & 0xC0) {
printf("Error de lectura (el sector puede estar mal ledo).\n");
printf("Nota: el buffer de lectura contena el patrn 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\nTamao 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 tamao (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); /* tamao si tsector=0 */
esperar_int(); /* esperar interrupcin */
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;
{
304 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
int r, tsector, sectores, gap, pokete, i;
clrscr();
printf("\n\nTamao 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("\nNmero 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 alteracin */
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 interrupcin */
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 estn 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 estn ahora.\n");
printf("\n Elige opcin.");
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("Tamao 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\nCaracterstica a cambiar: \n");
printf(" (0) N Cilindro, (1) N cabezal,");
printf(" (2) N sector, (3) Tamao 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 IDs...");
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 interrupcin */
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 Tamao 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);
}
else
printf(" 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 ms IDs [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 operacin:\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("[Tamao %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 */
305 EL HARDWARE DE APOYO AL MICROPROCESADOR
if (tecla==0x47) i=0; /* Inicio */
if (tecla==0x4f) i=vv-1; /* Fin */
if (tecla==0x49) i-=2; /* Re Pg */
if (tecla==0x51) i+=2; /* Av pg */
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 ms 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 ms 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 interrupcin no ms 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 pgina del canal 2 */
outportb (0xB, rmodo); /* programar registro de modo */
outportb (0xC, 0); /* clear first/last flip-flop */
outportb (4,dmaoff & 0xFF); /* direccin base (parte baja) */
outportb (4,dmaoff >> 8); /* direccin 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 estrictamente
necesario (excepto en los autnticos PS/2): generalmente tambin se puede acceder enviando directamente
los bytes al FDC, aunque sera ms til emplear el DMA (la CPU no tendra tiempos muertos de espera para
mover los bytes). Realmente, bajo DOS da lo mismo acceder con el DMA que sin el, ya que an cuando se
emplea el DMA la pobre CPU se queda esperando a que llegue la interrupcin que indica el final de la
operacin!. La nica ventaja real de utilizar el DMA, que motiv su uso por parte de los programadores de
IBM, es que el contador de hora de la BIOS sigue avanzando (y el reloj no se atrasa), mientras que sin el
DMA se parara al tener que inhibir las interrupciones en el momento crtico de la transferencia del sector,
con objeto de no perder datos. En otros sistemas operativos multitarea, el DMA permite a la CPU continuar
trabajando (perdiendo slo los ciclos estrictamente necesarios para la transferencia) a la par que es realizada
la operacin de disco: aunque el rendimiento global del sistema se degrada durante la operacin, al menos
no se detienen todos los procesos.
El siguiente programa de ejemplo, realizado ntegramente en ensamblador, permite leer y escribir
sectores de disco aislados en el formato MFM habitual. Soporta las unidades A: y B:, as como discos y
disqueteras de todos los formatos y densidades -incluidos los no estndar-. Se preguntan todos y cada uno
de los parmetros 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 de
tiempo 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 encargadas
de tomar datos del teclado ni tampoco las de impresin en pantalla, bastante obvias. Sin embargo, daremos
un 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 travs de la
subrutina reset_drv, esperando despus a que el motor alcance un rgimen de rotacin adecuado. En
reset_drv se selecciona adems el modo NO DMA en el registro de salida digital, se espera por la
interrupcin que indica el fin del reset y se enva el comando specify al FDC; tambin se establece la
velocidad de transferencia apropiada para el tipo de disquete a ser accedido. El procedimiento recalibrar
ejecuta dicho comando del FDC hasta un mximo de dos veces en caso de fallo, entre otros motivos para
prevenir que el cabezal estuviera inicialmente en una pista superior a la 77. Tanto en este procedimiento como
en el seek_drv se detecta el inicio de la fase de resultados esperando la pertinente interrupcin de disco (en
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 el
registro de salida digital, por algn oscuro motivo que desconozco, es preciso establecer momentneamente
el modo DMA a travs del bit 3 de dicho registro (rutina habilita_int) y volverlo a desactivar una vez que
llega la interrupcin; realmente, an seleccionando esta modalidad, el DMA no ser empleado ya que no se
utiliza en los comandos de recalibracin ni en el de posicionamiento del cabezal. En esta ltima rutina se
tiene en cuenta el caso especial que supone un disquete de 40 pistas en una unidad de 80, multiplicndose
entonces por 2 el nmero 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 comando
al 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 manera
directa para acelerarlo. Ms que para acelerarlo, para que no nos pille: la velocidad es aqu crtica (el proceso
se realiza con las interrupciones apagadas) ya que cada 16-32 microsegundos hay que transferir un byte entre
la CPU y el FDC y dormirse en los laureles supondra un error irrecuperable. Si se est escribiendo un sector
y se produce un fallo, es fcil detectarlo (el FDC deja de recibir datos e intenta enviar los bytes de la fase
de resultados) pero en la lectura de sectores seran ledos dichos resultados confundidos como datos del
sector, aunque al terminar el comando (y bajar el bit CB del registro de estado) se detectara afortunadamente
el final de la operacin y se podra suponer que los ltimos 7 bytes ledos no eran del sector sino la fase de
resultados. En general, si el usuario ha indicado bien todos los parmetros y el disquete no est defectuoso,
no habr problemas. Estas rutinas de lectura de sectores no estn diseadas de manera tolerante a fallos, ya
que realizan saltos condicionales comprobando los bits del registro de estado, que en caso de quedarse
congelados y no cambiar supondran un cuelgue del sistema. Sin embargo, aadir controles de timeout
alargara los tiempos de ejecucin y podra provocar, si no se tiene cuidado, que los PC/XT ms lentos no
fueran bastante potentes para acceder al disco con la suficiente rapidez. Adems, la mejor tcnica para
controlar los timeout es, indiscutiblemente, la monitorizacin de los ciclos de refresco de la memoria
dinmica de los AT (ese bit del puerto 61h que cambia 66287 veces por segundo): en los PC/XT sera ms
complicado...
Por ltimo, las rutinas fdc_read y fdc_write se encargan de la comunicacin CPU-FDC en ambos
sentidos, aunque aqu s se han establecido unos rudimentarios controles de timeout, de esos que tardan ms
tiempo en recuperar el control en las mquinas ms lentas. De ah que estas subrutinas no sean empleadas
desde sector_io, por razones de velocidad.
Acceder a disco sin DMA es ms incmodo y problemtico que hacerlo a travs del DMA, y no
ofrece absolutamente ninguna ventaja adicional, a no ser que el 8237 est averiado en el ordenador. De hecho,
yo personalmente dej de utilizar durante algn tiempo el DMA en los accesos de disco (me hice un
controlador especial que adems me ayud a subir nota en una asignatura), creyendo que los errores en la
transferencia de datos en mis disqueteras se deban a este integrado. Sin embargo, finalmente averigu que
la causa estaba en los SIPPs de memoria un tanto flojos (por fortuna, resulta que un amigo mo s tena
estropeado el DMA de verdad en las operaciones de escritura, y ese driver le vino muy bien para poder
escribir en sus disquetes). Ancdotas aparte, este programa es meramente educativo y no un modelo a seguir.
; ********************************************************************
; * *
; * 765NODMA.ASM 2.0 - Programa de demostracin de acceso a *
; * bajo nivel al disquete sin emplear DMA. *
; * *
; ********************************************************************
; ************ Macros de propsito general.
XPUSH MACRO regmem ; apilar lista de registros
IRP rm, <regmem>
PUSH rm
ENDM
ENDM
XPOP MACRO regmem ; desapilar lista de registros
IRP rm, <regmem>
POP rm
ENDM
ENDM
; ************ Programa principal.
fdc_test SEGMENT
ASSUME CS:fdc_test, DS:fdc_test
ORG 100h
main PROC
CALL menu ; opciones
DEC AL
JZ leer ; opcin de leer sector
DEC AL
JZ escribir ; opcin de escribirlo
LEA DX,adios_txt
CALL print ; opcin de salir:
MOV AX,40h
MOV DS,AX
MOV AL,DS:[8Bh] ; velocidad previa al programa
MOV CL,6
SHR AL,CL ; pasarla a bits 0..1
MOV DX,3F7h
OUT DX,AL ; restaurar velocidad previa
INT 20h
leer: LEA DX,cls_txt
CALL print ; borrar pantalla
LEA DX,lectura_txt
CALL print ; mensaje inicial
LEA DX,aviso_txt
CALL print
CALL pide_sector ; pedir pista, cabeza, ...
MOV orden,F_READ
CALL init_drv
CALL recalibrar
JC fallo
CALL seek_drv
JC fallo
LEA DI,buffer
307 EL HARDWARE DE APOYO AL MICROPROCESADOR
CALL sector_io ; cargar dicho sector
JC fallo
CALL imprime_sector ; mostrar su contenido
JMP main
escribir: LEA DX,cls_txt
CALL print ; limpiar pantalla
LEA DX,escritura_txt
CALL print ; mensaje inicial
LEA DX,aviso_txt
CALL print
CALL pide_sector ; pedir pista, cabeza, ...
CALL pide_relleno ; pedir byte de relleno
MOV orden,F_WRITE
CALL init_drv
CALL recalibrar
JC fallo
CALL seek_drv
JC fallo
LEA DI,buffer
CALL sector_io ; grabar dicho sector
JC fallo
JMP main
fallo: LEA DX,fallo_txt ; mensaje de error
CALL print
CALL getch
JMP main
main ENDP
; ************ Subrutinas de apoyo
menu PROC
LEA DX,cls_txt
CALL print
LEA DX,opciones_txt ; texto del men
CALL print
espera_opc: CALL getch
CMP AL,1
JE opc_ok ; elegida opcin 1
CMP AL,2
JE opc_ok ; elegida opcin 2
CMP AL,27
JE opc3_ok ; ESC (opcin 3)
CMP AX,2D00h
JNE espera_opc ; no es ALT-X
opc3_ok: MOV AL,3
opc_ok: SUB AL,0
RET
menu ENDP
; ------------ Solicitar informacin del sector a ser accedido.
pide_sector PROC
LEA DX,unidad_txt
CALL input_AL ; pedir unidad
MOV unidad,AL
LEA DX,vunidad_txt
CALL input_AL ; seleccionar velocidad
MOV vunidad,AL
LEA DX,tdisco_txt
CALL input_AL ; problema de 40/80 pistas
MOV tunidad,AL
LEA DX,tamano_txt
CALL input_AL ; preguntar tamao sector
MOV tsector,AL
LEA DX,gap_rw_txt
CALL input_AL ; preguntar tamao sector
MOV gap,AL
LEA DX,pista_txt
CALL input_AL ; pedir pista
MOV cilindro,AL
LEA DX,cabeza_txt
CALL input_AL ; pedir cabeza
MOV cabezal,AL
LEA DX,sector_txt
CALL input_AL ; pedir sector
MOV sector_ini,AL
MOV sector_fin,AL
MOV CL,tsector
MOV CH,0
INC CX ; CX: 1-128 bytes, 2-256, ...
MOV AX,64
computab: SHL AX,1
LOOP computab
MOV bsector,AX ; bytes/sector
MOV AL,cabezal
SHL AL,1
SHL AL,1
OR AL,unidad
MOV byte1,AL ; byte 1 comn a muchas rdenes
RET
pide_sector ENDP
; ------------ Imprimir sector en hex/ASCII en bloques de 256 bytes.
imprime_sector PROC
LEA BX,buffer
MOV AX,bsector
MOV CL,AH
MOV CH,0 ; CX secciones de 256 bytes
AND CX,CX
JNZ otra_mitad
INC CX ; al menos imprimir una vez
otra_mitad: PUSH CX
LEA DX,cls_txt
CALL print
MOV CX,16 ; 16 lneas
otra_linea: PUSH CX
MOV CX,16 ; de 16 caracteres
pr_hexa: MOV AL,
CALL printAL
MOV AL,[BX]
INC BX
CALL print8hex
LOOP pr_hexa
MOV AL,
CALL printAL
CALL printAL
SUB BX,16
MOV CX,16
pr_ascii: MOV AL,[BX]
INC BX
CMP AL,
JAE ascii_ok
MOV AL,.
ascii_ok: CALL printAL
LOOP pr_ascii
MOV AL,13
CALL printAL
MOV AL,10
CALL printAL
POP CX
LOOP otra_linea
LEA DX,ptecla_txt
CALL print
CALL getch
POP CX
LOOP otra_mitad
RET
imprime_sector ENDP
; ------------ Pedir byte para llenar el sector a grabar.
pide_relleno PROC
LEA DX,relleno_txt
CALL input_AL
LEA DI,buffer
MOV CX,bsector ; tamao de sector en bytes
CLD
REP STOSB
RET
pide_relleno ENDP
; ------------ Imprimir cadena en DS:DX terminada en un $.
print PROC
PUSH AX
MOV AH,9 ; funcin de impresin
INT 21h ; llamar al sistema
POP AX
RET
print ENDP
; ------------ Imprimir carcter en AL
printAL PROC
PUSH AX
PUSH DX ; registros usados preservados
MOV AH,2 ; funcin de impresin del DOS
MOV DL,AL ; carcter a imprimir
INT 21h ; llamar al sistema
POP DX
POP AX ; recuperar registros
RET ; retornar
printAL ENDP
; ------------ Imprimir carcter hexadecimal (AL).
print4hex PROC
PUSH AX ; preservar AX
ADD AL,0 ; pasar binario a ASCII
CMP AL,9
JBE no_sup9 ; no es letra
ADD AL,A-9-1 ; lo es
no_sup9: CALL printAL ; imprimir dgito hexadecimal
POP AX ; restaurar AX
RET
print4hex ENDP
; ------------ Imprimir byte hexadecimal en AL.
print8hex PROC
PUSH CX
PUSH AX
MOV CL,4
SHR AL,CL ; pasar bits 4..7 a 0..3
CALL print4hex ; imprimir nibble ms
significativo
POP AX ; restaurar AL
PUSH AX ; y preservarlo de nuevo
AND AL,1111b ; dejar nibble menos significativo
CALL print4hex ; e imprimirlo
POP AX
POP CX
RET
print8hex ENDP
; ------------ Esperar pulsacin de tecla y devolverla en AX.
getch PROC
MOV AH,1 ; esperar carcter (algunos
INT 16h ; KEYB de XT se cuelgan
JZ getch ; al usar directamente el
MOV AH,0 ; servicio 0).
INT 16h
RET
getch ENDP
; ------------ Leer n decimal de hasta 3 dgitos y devolverlo en AL.
input_AL PROC
PUSH BX
PUSH CX
PUSH DX
PUSH AX
pedir_dato: CALL print
MOV AH,0Ah ; funcin de entrada (teclado)
LEA DX,buffer
MOV BX,DX
MOV WORD PTR [BX],4 ; (inicializar dos variables)
INT 21h ; llamar al sistema
MOV CL,[BX+1]
XOR CH,CH ; nmero de caracteres pulsados
POP AX
POP DX
PUSH DX
PUSH AX
JCXZ pedir_dato ; se puls RETURN: reiterar
XOR DX,DX
gen_num: MOV AX,10
MUL DX
MOV DX,AX
MOV AL,[BX+2]
SUB AL,0
INC BX
XOR AH,AH
ADD DX,AX
LOOP gen_num ; conversin ASCII -> binario
POP AX
MOV AL,DL ; resultado
POP DX
POP CX
POP BX
RET
input_AL ENDP
; ------------ Encender motor y esperar a que tome cierta velocidad.
308 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
init_drv PROC
PUSH CX
CALL reset_drv
MOV CX,18
CALL retardo ; esperar aceleracin disco
POP CX
RET
init_drv ENDP
; ------------ Establecer modalidad de operacin del controlador
; y asegurar que el motor est en marcha.
reset_drv PROC
XPUSH <DS, AX, BX, CX, DX>
PUSH DS
MOV BX,40h ; engaar al BIOS para
MOV DS,BX ; que no pare el motor al
MOV BYTE PTR DS:[BX],255 ; menos durante 14 seg.
POP DS
MOV DX,3F2h ; registro de salida digital
MOV CL,unidad
ADD CL,4
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,unidad ; seleccionar unidad; NO DMA
OUT DX,AL ; reset
OR AL,00000100b
JMP SHORT $+2
OUT DX,AL ; fin del reset
CALL espera_int
MOV AL,3
CALL fdc_write ; Comando Specify:
MOV AL,0DFh
CALL fdc_write
MOV AL,3 ; modo NO DMA
CALL fdc_write ; head load y modo
PUSH DS
MOV BX,40h
MOV DS,BX
MOV CL,CS:unidad
MOV AL,1
SHL AL,CL
AND BYTE PTR DS:[BX-1],11110000b
OR DS:[BX-1],AL ; indicar motor ON
POP DS
MOV DX,3F7h
MOV AL,vunidad ; velocidad de transferencia
OUT DX,AL
XPOP <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 ms de 77 pistas).
recalibrar PROC
XPUSH <AX, CX>
MOV CX,2 ; dos veces como mucho
recalibra: CALL habilita_int
MOV AL,7
CALL fdc_write ; comando de recalibrado
JZ fallo_recal
MOV AL,byte1
CALL fdc_write ; enviar HD, US1, US0
JZ fallo_recal
CALL espera_int ; esperar interrupcin
JZ fallo_recal
MOV AL,8
CALL fdc_write ; comando leer estado int...
JZ fallo_recal
CALL fdc_read ; leer registro de estado 0
JZ fallo_recal
MOV AH,AL
CALL fdc_read ; leer cilindro actual
XOR AH,00100000b ; bajar bit de seek end
TEST AH,11110000b ; comprobar resultado y ST0
JNZ fallo_recal ; sin seek end o sin TRK0
XPOP <CX, AX>
CLC ; Ok.
RET
fallo_recal: LOOP recalibra ; reintentar comando
XPOP <CX, AX>
STC ; condicin de fallo
RET
recalibrar ENDP
; ------------ Llevar el cabezal a la pista indicada.
seek_drv PROC
XPUSH <AX, CX>
CLI
CALL habilita_int ; usar interrupciones
MOV AL,0Fh
CALL fdc_write ; comando seek
JZ fallo_seek
MOV AL,byte1
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CMP tunidad,0
JE pista_ok ; es unidad de doble densidad
CMP vunidad,1 ; es de alta:
JNE pista_ok ; no es disco 5-360
SHL AL,1 ; cilindro=cilindro*2
pista_ok: CALL fdc_write ; enviar cilindro
CALL espera_int ; esperar interrupcin
CLI
MOV AL,8
CALL fdc_write ; comando leer estado int...
JZ fallo_seek
CALL fdc_read ; leer registro de estado 0
CALL fdc_read ; leer cilindro actual
STI
MOV CX,1
CALL retardo ; esperar asentamiento cabezal
XPOP <CX, AX>
CLC ; retornar con xito
RET
fallo_seek: STI
XPOP <CX, AX>
STC ; retornar indicando fallo
RET
seek_drv ENDP
; ------------ Habilitar interrupcin disquete (y modo DMA).
habilita_int PROC
XPUSH <AX, CX, DX>
MOV CL,unidad
ADD CL,4
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,unidad ; seleccionar unidad
OR AL,00000100b ; no hacer reset
MOV DX,3F2h
OUT DX,AL
OR AL,00001000b ; modo DMA
JMP SHORT $+2
OUT DX,AL
XPOP <DX, CX, AX>
RET
habilita_int ENDP
; ------------ Esperar interrupcin de disquete y volver de nuevo al
; modo NO DMA (lo que inhibe interrupcin disquete).
espera_int PROC
STI
XPUSH <AX, CX>
XPUSH <DS, 40h>
POP DS
MOV AH,0FFh
esperar_int: CMP AL,DS:[6Ch]
JE mira_int
MOV AL,DS:[6Ch]
INC AH
CMP AH,37 ; no esperar ms de 2 segundos
JA fin_espera ; timeout
mira_int: TEST BYTE PTR DS:[3Eh],80h
JZ esperar_int
fin_espera: AND BYTE PTR DS:[3Eh],127 ; resetear flag
POP DS ; para futura interrupcin
MOV CL,unidad
ADD CL,4
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,unidad ; seleccionar unidad
OR AL,00000100b ; no hacer reset y no DMA
MOV DX,3F2h
OUT DX,AL
XPOP <CX, AX>
RET
espera_int ENDP
; ------------ Cargar o escribir CX sector(es) del disco en ES:DI,
; actualizando la direccin en ES:DI pero sin alterar
; ningn otro registro. Si hay error se devuelve CF=1 y
; no se modifica ES:DI. En el momento crtico en que se
; leen/escriben los sectores, no se llama a las
; subrutinas habituales por razones de velocidad, lo
; que implica duplicar cdigo y alargar el programa.
sector_io PROC
XPUSH <AX, BX, CX, DX, DI>
MOV AL,orden
CLI
CALL fdc_write ; comando leer/escribir del 765
JNZ io_proc
JMP sector_io_ko
io_proc: MOV AL,byte1
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
MOV AL,cabezal
CALL fdc_write ; enviar cabezal
MOV AL,sector_ini
CALL fdc_write ; enviar n sector
MOV AL,tsector
CALL fdc_write ; longitud sector
MOV AL,sector_fin
CALL fdc_write ; ltimo sector
MOV AL,gap
CALL fdc_write ; GAP de lectura/escritura
MOV AL,128
CALL fdc_write ; tamao sector si longitud=0
CLD
MOV AL,sector_fin
SUB AL,sector_ini
INC AL
XOR AH,AH ; AX = n de sectores
MUL bsector
MOV CX,AX ; bytes a leer/escribir
MOV DX,3F4h ; registro de estado del FDC
espera_exec: IN AL,DX
TEST AL,80h ; alcanzada fase ejecucin?
JZ espera_exec
CMP orden,F_WRITE
JE fdc_wr_sect
fdc_rd_sect: IN AL,DX
TEST AL,80h ; listo para E/S?
JZ fdc_rd_sect
TEST AL,16
JZ sector_io_ko ; fallo en lectura
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte del sector
DEC DX
STOSB ; ES:[DI++] <-- AL
LOOP fdc_rd_sect ; repetir hasta fin sector(es)
JMP sect_io_fin
fdc_wr_sect: IN AL,DX
TEST AL,80h ; listo para E/S?
JZ fdc_wr_sect
TEST AL,64
JNZ sector_io_ko ; fallo en escritura
MOV AL,ES:[DI]
INC DX ; apuntar al registro de datos
OUT DX,AL ; escribir byte del sector
DEC DX
INC DI
LOOP fdc_wr_sect ; hasta acabar sector(es)
sect_io_fin: MOV CX,7
sect_io_rx: CALL fdc_read ; leyendo resultados del xito
LOOP sect_io_rx
STI ; ...fin de la fase crtica
POP CX ; sacar DI sin cambiarlo
CLC ; indicar xito
JMP sector_io_fin
sector_io_ko: MOV DX,3F4h ; leer resultados del fallo
kill_info: IN AL,DX
TEST AL,80h ; listo para E/S?
JZ kill_info
TEST AL,64
JZ info_killed ; el 765 no devuelve datos
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte de resultados
DEC DX
309 EL HARDWARE DE APOYO AL MICROPROCESADOR
JMP kill_info
info_killed: STI
POP DI ; anular cambio de DI
STC ; 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 operacin fracas (el FDC no estaba listo).
fdc_read PROC
PUSH CX
PUSH DX
MOV DX,3F4h ; registro de estado del FDC
XOR CX,CX ; evitar cuelgue total si falla
espera_rd: IN AL,DX ; leer registro de estado
TEST AL,80h ; bit 7 inactivo?
LOOPZ espera_rd ; as es: el FDC est ocupado
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte del FDC
AND CX,CX ; ZF = 1 si fallo al leer
POP DX
POP CX
RET
fdc_read ENDP
; ------------ Enviar byte AL al FDC. A la vuelta, ZF = 1 si
; la operacin fracas (el FDC no estaba listo).
fdc_write PROC
PUSH AX
PUSH CX
PUSH DX
MOV DX,3F4h ; registro de estado del FDC
XCHG AH,AL ; preservar AL en AH
XOR CX,CX ; evitar cuelgue total si falla
espera_wr: IN AL,DX ; leer registro de estado
TEST AL,80h ; bit 7 inactivo?
LOOPZ espera_wr ; as es: el FDC est ocupado
XCHG AH,AL ; recuperar el dato de AL
INC DX ; apuntar al registro de datos
OUT DX,AL ; enviar byte al FDC
AND CX,CX ; ZF = 1 si fallo al escribir
POP DX
POP CX
POP AX
RET
fdc_write ENDP
; ------------ Esperar CX 1/18,2 avos de segundo.
retardo PROC
PUSH DS
PUSH AX
PUSH CX
MOV AX,40h
MOV DS,AX
STI
espera_tics: MOV AX,DS:[6Ch] ; esperar que el contador
espera_tic: CMP AX,DS:[6Ch] ; de hora del BIOS...
JE espera_tic
LOOP espera_tics ; ... cambie lo suficiente
POP CX
POP AX
POP DS
RET
retardo ENDP
; ************ Mensajes
cls_txt DB 10,10,10,10,10,10,10,10,10,10,10,10
DB 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 Jess Arias Alvarez."
DB 13,10," (c) 1992, 1993 Ciriaco Garca 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 opcin: $"
lectura_txt DB 13,10,"Lectura de sector.$"
escritura_txt DB 13,10,"Escritura de sector.$"
aviso_txt DB 13,10,"--------------------",13,10
DB "Aviso: No se validan las entradas.",10,"$"
adios_txt DB 13," Hasta luego. ",13,10,"$"
ptecla_txt DB 13,10,"- Ests 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,"Tamao de sector (2->512 bytes): $"
gap_rw_txt DB 13,10,"Tamao 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 FDC
F_WRITE EQU 01000101b ; orden de escritura del FDC
orden DB ? ; orden a procesar
unidad DB ?
vunidad DB ? ; velocidad de transferencia
tunidad DB ? ; control de salto de pista
cilindro DB ? ; pista del disco a usar
cabezal DB ? ; cabeza
sector_ini DB ? ; sector inicial
sector_fin DB ? ; sector final
tsector DB ? ; tamao de sector (logaritmo)
bsector DW ? ; tamao de sector (bytes)
gap DB ? ; GAP para lectura/escritura
byte1 DB ? ; bits HD, US1, US0
relleno DB ? ; byte de relleno (al escribir)
buffer EQU $ ; para leer/escribir sector
fdc_test ENDS
END 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 informacin a un caso prctico real, con un programa. Ciertas aplicaciones
comerciales de backup ya emplean formatos de disco de ms capacidad para almacenar los datos, adems de
manera comprimida. Sin embargo, estos disquetes no pueden ser empleados directamente por el DOS. Por
el contrario, la utilidad que desarrollaremos, 2M, es un programa residente que permite gestionar disquetes
con sectores de ms de 512 bytes e, incluso, con sectores de distinto tamao en las pistas. Este ltimo
formato obtendr algo ms de capacidad, pero menos velocidad y fiabilidad. En 3", los disquetes ms
comunes de 1.44M (1440K) se podrn formatear a 1804K y 1886K, respectivamente. Los de 720K alcanzarn
los 984/1066K. En 5" los de 1.2M pasan a 1476/1558K y los de 360K a 820/902K. Los formatos de
1886K, 1066K y 1558K no pueden ser reproducidos por la versin de enero de 1992 del poderoso copin
COPYWRITE; 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 pequeo
programa escrito en C (2MF.C) que se limitar a llamar a las funciones de INT 13h reforzadas por 2M; dicho
programa ser descrito ms adelante.
Los programas que formatean los discos a mayor capacidad de la normal suelen limitarse a reducir
el GAP 3 al formatear, colocando gracias a ello ms sectores en las pistas. Sin embargo, la utilidad propuesta
aqu rompe con el tamao estndar de 512 bytes: al colocar sectores de mayor tamao, existen menos sectores
y tambin menos GAP de separacin. El inconveniente de este mtodo es que difcilmente sectores de 1024,
2048 ms bytes pueden encajar aprovechando ptimamente la capacidad de la pista. Por ello se han
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 medio
magntico):
Empleo de sectores de 1 Kb. Pese a ser ms grandes, se pueden colocar ms o menos bien en los 4
tipos de disco (360-1.2-720-1.44) aprovechando ms la capacidad de la pista, ya que al haber menos
sectores tambin se derrocha menos espacio en GAPs sin necesidad de reducirlos excesivamente ni,
por tanto, degradar la fiabilidad de los discos. Esta solucin, si se tiene cuidado de optimizar el
formateo de las pistas (con la numeracin adecuada de los sectores en las mismas) permite obtener
disquetes de mayor capacidad de la normal, tan fiables como los estndar del DOS y sensiblemente
ms rpidos que los creados por el FORMAT debido a dos motivos: en estos formatos el disco da
slo las vueltas necesarias para acceder a los datos y, adems, se leen ms datos en dichas vueltas.
La otra solucin alternativa consiste en emplear sectores an de mayor tamao, hasta 2 Kb (mayores
no permitiran una ventaja significativa) y rellenar el hueco restante de la pista, donde no cabe otro
sector de 2 Kb, con sectores menores. Esto implica colocar sectores de distinto tamao en las pistas,
lo cual escapa en teora de las posibilidades del controlador de disquetes, si se repasa la
documentacin de las pginas anteriores. Sin embargo, slo en teora, ya que existen programas
Parmetros /X e /Y
de FDFORMAT para un
formateo correcto.
/X /Y
5-DD 1 3
5-HD 2 3
3-DD 1 2
3-HD 2 3
comerciales con proteccin anticopia que realizan esta tarea. La tcnica
que veremos permite realizar esto, pese a lo cual estos formatos de
disco no son recomendados: son poco seguros en cuanto a portabilidad
-disquetes creados en una mquina podran tener problemas para ser
reconocidos en otro ordenador o incluso ser destruidos al escribir- y
aumentan poco la capacidad respecto a la 1 solucin; pese a todo han
sido calibrados de tal manera que se puede afirmar que en un
elevadsimo porcentaje de veces el funcionamiento y la portabilidad
sern satisfactorios.
A lo largo de este apartado se har alguna referencia al popular programa de formateo FDFORMAT
creado por Christoph H. Hochsttter; esta utilidad permite formatear disquetes normales desplazando los
sectores de manera ptima (opciones /X e /Y) y tambin aadir ms sectores (estrechando el GAP 3). Para
superar las limitaciones de flexibilidad de la BIOS es preciso tener residente un pequeo programa de slo
128 bytes de cara a soportar los formatos extendidos. Este programa, bastante superior al FORMAT en todos
los aspectos, con el que adems es compatible, est muy extendido en las principales BBS (su cdigo fuente
en Turbo Pascal viene incluido) y aborda desde otro punto de vista la ampliacin de la capacidad normal de
los disquetes, respetando los sectores de 512 bytes.
No hay que olvidar que este programa permite
crear, adems de algunos formatos extendidos,
disquetes totalmente estndar de 360K, 1.2M, 720K
y 1.44M que, por supuesto, no necesitan soporte
residente y son mucho ms rpidos que los creados
por el FORMAT del DOS. Mientras el FORMAT
del sistema operativo no corrija la numeracin
incorrecta de sectores, que lleva practicando desde
1981, y a la espera de que David Astruga saque la
prxima versin de su programa de copia y
formateo (a finales del 94 o comienzos del 95); por
el momento, FDFORMAT y sus parmetros /X e
/Y constituyen la nica solucin para los usuarios
ms entendidos (aquellos que usan 4DOS en vez de
COMMAND.COM, QEMM en lugar de EMM386,
etc): emplear el FORMAT actual no es de
conservadores sino de no informados. 2M
(abreviatura de 2 megas, aunque no se alcanza esa
capacidad por disco) es un programa residente que
[1867/1867] B:\>dir
Volume in drive B is unlabeled Serial number is 2FE6:7632
File not found "B:\*.*"
0 bytes in 0 file(s)
1.912.320 bytes free
[1867/1867] B:\>chkdsk
Nmero de serie de volumen es 2FE6-7632
1912320 bytes de espacio total en disco
1912320 bytes disponibles en disco
512 bytes en cada unidad de asignacin
3735 total de unidades de asignacin en el disco
3735 unidades de asignacin disponibles en disco
655360 bytes de memoria total
649760 bytes libres
[1867/1867] B:\>testdisk
TD-Test Disco, Edicin Estandar 4.50, (C) Copr 1984-88, Peter Norton
Traduccin Castellano, Copyright (C) 1989 ANAYA Multimedia, S.A.
Verificar DISCO, ARCHIVO, o AMBOS
Pulse D, F, o A ... D
Puede pulsar BREAK (Ctrl-C) durante la
verificacin para interrumpir Test Disco
Test leyendo el disco B:, zonas del sistema y de datos
La zona del sistema consta de boot, FAT, y directorio
Zona del sistema sin errores
La zona de datos consta de clusters numerados 2 - 3.736
Zona de datos sin errores
[1867/1867] B:\>_
EJEMPLO DE ACCESO A DISQUETE 2M DE 1.44 FORMATEADO A CASI 1.90
311 EL HARDWARE DE APOYO AL MICROPROCESADOR
da soporte a los nuevos formatos de disco. Una vez instalado 2M en memoria, los nuevos disquetes sern
reconocidos sin problemas: se podr hacer DIR, COPY, CHKDSK,... e incluso DISKCOPY hacia un disco
destino ya formateado. El cdigo residente de 2M funciona tambin 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 an, creando una imagen en disco duro
de ese disquete. De esta ltima manera, el usuario ni siquiera nota al diferencia entre estas ventanas de DOS
y las normales. Tal vez alguien escriba algn da el driver oportuno para facilitar la operacin en este
sistema... de momento, 2M est diseado slo para los sistemas ms extendidos. En WINDOWS NT, donde
no ha sido probado, probablemente existirn problemas y limitaciones mayores de las que se producen bajo
OS/2. Al momento de escribirse estas lneas, el autor de 2M tiene constancia de que hay intentos de portarlo
al sistema operativo Linux por parte de Alain Knaff y David Niemi, si bien desconoce el grado de avance
en esta materia.
2M aade un nuevo servicio a la INT 13h para poder formatear los nuevos disquetes. No es probable
que gracias a ello la prxima versin de PC-TOOLS soporte los nuevos formatos, pero aadir rutinas de
formateo apenas alargaba el cdigo residente (slo 0.75 Kb ms hasta alcanzar los 5 Kb) y se trataba de la
solucin ms elegante. Para formatear los nuevos disquetes se ha creado un programa en C de alto nivel, que
sencillamente invoca la INT 13h sin verse obligado a realizar ni un solo acceso directo al hardware, pese a
que el cdigo residente de 2M accede siempre a disco a travs del controlador de disquetes, sin una sola
Ensamblador Comentario Offset
JMP SHORT BootP ; 2 bytes 0
NOP ; 1 byte 2
DB "2M-STV08" ; ID sistema 3
DW 512 ; bytes/sector 11
DB 1 ; sectores por cluster 13
DW 1 ; sectores reservados al principio 14
DB 2 ; n copias de la FAT 16
DW 224 ; entradas al directorio raz 17
DW 3608 ; n total de sectores del disco 19
DB 0F0h ; byte descriptor de medio 21
DW 11 ; sectores ocupados por la FAT 22
DW 22 ; sectores por pista 24
DW 2 ; n de cabezales 26
DD 0 ; sectores especiales reservados 28
DD 0 ; n sectores (unidad 32 bit) 32
DB 0 ; unidad fsica 36
DB 0 ; reservado 37
DB 29h ; disco con nmero de serie 38
DD 8BC1AD20h ; nmero de serie provisional 39
DB "NO NAME " ; ttulo del disco 43
DB "FAT12 " ; tipo de FAT 54
DB Flags ; bit 0 = 1 si FechaF/HoraF definido 62
DB ? ; checksum de la informacin vital 63
DB 7 ; versin formato (>=7 si BOOT virtual) 64
DB 0 ; a 1 si escribir al formatear 65
DB 0 ; velocidad transferencia pista 0 66
DB 0 ; velocidad transf. dems pistas 67
DW BootP ; offset al programa de arranque 68
DW Infp0 ; T1: informacin para pista 0 70
DW InfpX ; T2: informacin dems pistas 72
DW InfTm ; T3: tabla tamaos dems pistas 74
DW FechaF ; Fecha de formateo (2M 3.0+) 76
DW HoraF ; Hora de formateo (2M 3.0+) 78
Infp0 DB 19, 70 ; n sectores / GAP de formateo
DB 1,2,3,4,5,6,7,8 ; sectores ordenados (20..22 no existen)
DB 9,10,11,12,13,14
DB 15,16,17,18,19
InfpX DB 11, 40 ; n sectores / GAP de formateo
DB 3 ; tamao
DB 1, 2 ; desplazamiento numeracin
InfTm DB 3,3,3,3,3,3 ; tamao 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 ningn momento.
La capacidad obtenida por 2M
supera la conseguida por los programas
comerciales de backup en los formatos
especiales para almacenar slo datos. Con la
ayuda de un compresor de datos de dominio
pblico lder (PKZIP, ARJ, etc) tambin
superior en rendimiento a los programas de
backup, se puede conseguir el mtodo de
backups que, indiscutiblemente, ms
aprovecha los disquetes, con una aplastante
diferencia -y adems el ms barato-. Sin
embargo, el usuario debera tener cuidado
con el tipo de datos que almacena en estos
discos, ya que no son tan portables como los
estndar y sera problemtico migrarlos
despus a otros entornos.
Existen versiones de 2M tanto para
sistemas AT como para PC/XT, con el nico
requisito de que la controladora y las
unidades 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 sectores
de 512 bytes, contenindolos en cantidad tambin ms o menos normal. Uno de los motivos es permitir que
la FAT, zona del disco en la que a menudo cambia un slo sector (y no varios consecutivos) tenga un acceso
ms gil. En algunos formatos de disco, parte del directorio raz tambin cabe en esta pista; en cualquier caso,
esto no es demasiado importante porque slo se accede al directorio raz una vez por cada fichero.
Debido al empleo en la primera pista de sectores fsicos de 512 bytes, no se pueden emular todos los
sectores virtuales. En 3-HD por ejemplo, los nuevos formatos de disco contarn aparentemente con 22-23
sectores por pista. Realmente sern muchos menos y de ms de 512 bytes, pero se engaar al DOS para
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. En
cualquier caso, esta cifra es muy superior a los 18 sectores habituales en este tipo de disco. Como la primera
pista contiene sectores reales de 512 bytes, no se pueden meter tantos (no caben ms de 21 y eso juntando
excesivamente los sectores, como hace FDFORMAT en el formato 1.72M).
Para arreglar este problema, el cdigo residente de 2M se extralimita en sus funciones y, suponiendo
que los discos se emplean bajo DOS, ignora las escrituras sobre la segunda copia de la FAT (que estara
sobre alguno de los sectores que no existen en la primera pista) devolviendo la primera copia de la FAT a
quien quiera leer la segunda. As se consigue adems una pequea velocidad extra, ya que la escritura sobre
la segunda copia de la FAT que realiza el DOS al crear ficheros resulta ignorada. Realmente, es un poco
innecesaria la presencia de 2 FAT en un disquete, mxime teniendo en cuenta que su adyacencia fsica
propicia que en caso de dao se estropeen las dos (cuntas veces el lector ha tenido que echar mano de la
segunda copia de la FAT para recuperar sus
datos?). El MS-DOS, incluso en la versin
6.0 no respeta sus propias especificaciones y
asume que los disquetes tienen 2 copias de la
FAT: aunque se indique slo una en el sector
de arranque, har caso omiso. Esta es, por un
lado, una buena manera de darle el corte de
mangas; por otro, un medio ideal para
simular ms sectores en la primera pista
fsica.
El sector de arranque de los nuevos
disquetes es en principio similar al de
cualquier otro disco, pero contiene ms
informacin adicional para describir el
formato fsico de disco que se trate y as
poder gestionarlo luego. De esta manera, se
sistematiza el soporte de los nuevos formatos
y se simplifica el programa residente. Detrs
de los primeros 62 bytes, donde va la
informacin colocada por el FORMAT
normal del DOS (incluyendo las ltimas
modas, como campos para etiqueta de disco,
nmero de serie, etc.) existen unos campos
con informacin adicional, que describiremos
ms adelante. Detras de este rea est el
Ensamblador Comentario Offset
JMP SHORT BootP ; 2 bytes 0
NOP ; 1 byte 2
DB "2M-STV04" ; ID sistema 3
DW 512 ; bytes/sector 11
DB 1 ; sectores por cluster 13
DW 1 ; sectores reservados al principio 14
DB 2 ; n copias de la FAT 16
DW 224 ; entradas al directorio raz 17
DW 3772 ; n total de sectores del disco 19
DB 0F0h ; byte descriptor de medio 21
DW 11 ; sectores ocupados por la FAT 22
DW 23 ; sectores por pista 24
DW 2 ; n de cabezales 26
DD 0 ; sectores especiales reservados 28
DD 0 ; n sectores (unidad 32 bit) 32
DB 0 ; unidad fsica 36
DB 0 ; reservado 37
DB 29h ; disco con nmero de serie 38
DD 4B368A0Eh ; nmero de serie (aleatorio) 39
DB "NO NAME " ; ttulo del disco 43
DB "FAT12 " ; tipo de FAT 54
DB Flags ; bit 0 = 1 si FechaF/HoraF definido 62
DB ? ; checksum de la informacin vital 63
DB 7 ; versin formato (>=7 si BOOT virtual) 64
DB 1 ; a 1 si escribir al formatear 65
DB 0 ; velocidad transferencia pista 0 66
DB 0 ; velocidad transf. dems pistas 67
DW BootP ; offset al programa de arranque 68
DW Infp0 ; T1: informacin para pista 0 70
DW InfpX ; T2: informacin dems pistas 72
DW InfTm ; T3: tabla tamaos dems pistas 74
DW FechaF ; Fecha de formateo (2M 3.0+) 76
DW HoraF ; Hora de formateo (2M 3.0+) 78
Infp0 DB 19, 70 ; n sectores / GAP de formateo
DB 1,2,3,4,5,6,7,8 ; sectores ordenados (20..23 no existen)
DB 9,10,11,12,13,14
DB 15,16,17,18,19
InfpX DB 64, 3 ; n sectores / GAP de formateo
DB 7 ; n sectores a renumerar
DB 128+1, 4, 4 ; tabla de renumeracin formateo:
DB 128+12, 1, 4 ; n sector, nuevo nmero, tamao
DB 128+23, 5, 4
DB 128+34, 2, 4
DB 128+45, 6, 3
DB 128+51, 3, 4
DB 128+62, 7, 2
InfTm DB 4,4,4,4,4,3,2 ; tamao 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 un
mensaje diciendo que el disco no es de arranque; actualmente arranca desde el disco duro si ste existe y,
desde 2M 2.0, carga el cdigo SuperBOOT almacenado en el disco si es de alta densidad. Los discos 2M de
alta densidad utilizan 5 sectores libres de la segunda copia de la FAT (ubicados en la primera pista) para
almacenar gran parte del cdigo 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 autntico sector de arranque, y los 5
restantes almacenan el cdigo residente de 2M. As, cuando 2M est instalado, el comando SYS y cualquier
aplicacin que acceda al sector de arranque estar accediendo realmente a un falso sector de arranque que
est fsicamente colocado en la FAT2. Y podr modificarlo sin riesgo alguno para 2M, ya que el autntico
sector de arranque permanece inmutable; las versiones anteriores de 2M necesitaban proteger este sector
restringiendo de alguna manera su acceso (para evitar que un simple SYS lo modificara y borrara la
informacin vital que contiene). La denominacin SuperBOOT para el cdigo de 2M almacenado en la
primera pista de los discos se debe exclusivamente a cuestiones de marketing. Debido a que se necesita un
tamao mnimo de FAT, modificar el tamao de cluster en el sector de arranque no es conveniente, aunque
est permitido y puede generar discos que no funcionen. Sin embargo, la utilidad estndar de formateo no
313 EL HARDWARE DE APOYO AL MICROPROCESADOR
deja cambiar el tamao de cluster (por otra parte de slo 512 bytes) y no hay muchos programas conocidos
que alteren estos parmetros de los disquetes ya formateados.
Cuando el sistema arranca de un disco 2M de alta densidad, el cdigo SuperBOOT rebaja la memoria
libre en 5 Kbytes (normalmente, de 640K a 635K) ubicndose al final de la memoria convencional y se
instala en la INT 13h. Despus, se carga el sector de arranque va INT 13h (que en adelante ser el falso
sector de arranque emulado, al que pudo acceder el SYS) y se ejecuta, procedindose al arranque normal del
sistema, ya que la nueva BIOS soporta discos 2M... este sector de arranque ubicado en la FAT2 es
denominado sector de arranque virtual en la documentacin de 2M. Como puede observar el lector, dejar
la primera pista con sectores de 512 bytes y emular la segunda copia de la FAT sobre la primera fue una idea
primitiva que luego ha permitido muchas aplicaciones interesantes.
Naturalmente, est previsto un mecanismo para poder acceder a los sectores fsicos sin emulaciones:
esto es til adems para permitir al programa de formateo grabar el cdigo SuperBOOT y acceder al sector
de arranque fsico, ya que los programas normales no tienen motivos especiales para necesitar un acceso a
dichas reas. Cuando 2M est instalado, cualquier acceso al cabezal 128 129 en lugar del 0 el 1 permite
acceder al disco sin realizar ningn tipo de emulacin; si bien esto slo funciona con discos 2M (con un disco
estndar en la unidad, aunque 2M est instalado, el acceso a estos cabezales devuelve un error).
En adelante nos referiremos al sector de arranque fsico, no al virtual (que puede ser distinto si el
disco es de sistema o ha sido alterado por alguna utilidad). El primer campo propio de 2M en el sector de
arranque es una variable con flags, empleada slo desde 2M 3.0 para indicar si se almacena la fecha y hora
de formateo en el sector de arranque (bit 0 = 1 en caso afirmativo). Detrs hay un checksum o suma de
comprobacin de la zona vital del sector de arranque. El algoritmo empleado ha variado en las sucesivas
versiones del programa. Desde la versin 6 del formateador (byte ubicado justo despus del checksum) la
zona total afectada por el checksum va desde el offset 64 hasta justo antes del programa de arranque del
disco. Las versiones anteriores de 2M realizaban un checksum distinto, por lo que los discos formateados por
ellas no estn sujetos a la comprobacin de checksum para evitar problemas. La suma total de este rea (en
nmero de 8 bits) debe dar un resultado 0. Por tanto, se permite modificar el programa de arranque e incluso
los campos del principio.
Cualquier otro cambio no
permitido har que 2M falle
en la comprobacin del
checksum la primera vez que
el disco es introducido en la
unidad; en este caso INT 13h
GAPs y /X e /Y probados en 2MF /F
5-DD 5-HD 3-DD 3-HD
GAP mnimo de lectura soportado en las pruebas 1 2 1 2
GAP mnimo de escritura soportado en las pruebas 13 26 20 28
GAP mximo de escritura soportado en las pruebas 197 76 187 49
GAP 3 de formateo adoptado finalmente 100 50 100 40
Valor ptimo obtenido experimentalmente para /X 1 1 1 1
Valor ptimo obtenido experimentalmente para /Y 1 2 1 2
2MF ES EL FORMATEADOR PARA 2M. CON /F SE CREAN DISCOS NORMALES Y /M INDICA MXIMA CAPACIDAD.
devuelve un Seek Error poco habitual para sealizar la circunstancia. Sin embargo, un cambio en el campo
ID (bytes 3 al 10) podra acarrear que 2M no reconociera el disco como suyo. Quiz el lector opine que
hubiera sido mejor ser ms tolerantes, pero yo opino que no: si el sector de arranque est corrompido, el
cdigo residente de 2M, que no valida nada de dicho sector, podra estrellarse si se fa de la informacin del
mismo. As nadie podr decir: se me cuelga al hacer DIR A:, como mucho: me dice Seek Error y no me
deja acceder al disco. En realidad, es difcil que se produzcan estos errores porque nadie que intente alterar
el sector de arranque fsico lo podr conseguir con 2M en memoria, sin saber como hacerlo o sin acceder
directamente a la controladora.
Tras el checksum hay un byte que indica la versin del formateador, de cara a permitir que futuras
versiones de 2M sepan con qu formato de disco se enfrentan para respetar los viejos formatos (en caso de
que surjan otros nuevos). El siguiente byte indica si es necesaria una escritura tras el formateo: en los
formatos de ms capacidad, trasformatear la pista hay que escribirla para evitar que una lectura posterior
produzca errores de CRC, como luego veremos y explicaremos. En los formatos normales este byte estar
a 0, y a 1 en los de ms capacidad.
Los siguientes 2 bytes indican la velocidad de transferencia a emplear en la primera pista (cilindro
y cabezal 0) y en las dems; el dato no est, por supuesto, en Kbit/seg sino que se trata del valor que hay
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/seg
en la primera pista y 300 Kbit/seg en las dems. El motivo es que las primeras versiones de 2M delegaban
parte del trabajo de reconocer la densidad de disco a la BIOS, la cual slo soporta 250 Kbit/seg en estas
unidades. Actualmente no sera necesario, ya que 2M detecta la densidad de los discos (y de hecho, sustituye
a la BIOS original en esta tarea), pero se ha mantenido por compatibilidad con los primeros formatos de disco
de 2M. Tras estos campos hay unos punteros a diversas reas interesantes: el primero apunta al programa de
arranque y ser empleado por dicho programa para conocer con comodidad su propia ubicacin; despus hay
un puntero a una tabla con informacin sobre la estructura de la primera pista del disco, otro puntero apunta
a una tabla con informacin de las dems pistas y, finalmente, un ltimo puntero referencia una tabla de
tamaos de los sectores de las pistas (excepto la primera). Los ltimos campos slo se emplean desde 2M
3.0 y almacenan la fecha y hora de formateo.
La primera tabla contiene un byte que indica el nmero real de sectores de la primera pista, seguido
de otro byte con el valor de GAP 3 empleado al formatear. Despus vienen los nmeros de sectores, uno tras
otro, lo que permite elegir lbremente el interleave. Las ltimas versiones de 2M acceden de manera eficiente
a la primera pista (y a todas las dems) soportando perfectamente un interleave 1:1, si bien los primeros
disquetes 2M fueron formateados con un factor 1:2. En los formatos de 1.80/1.88M la FAT ocupa 11
sectores, y otro el sector de arranque fsico. Los sectores que van del 1 al 12 estn, 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 cdigo SuperBOOT (5 sectores). El sector restante se debe a que en discos de 1.88M con 84
pistas la FAT1 ocupara un sector ms.
Capacidad bruta real antes de Bytes netos obtenidos por los principales formateadores
formatear (con 82 pistas y en
controladora 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)
(*) Tambin FDFORMAT cuando se emplean los formatos estndar del DOS.
(**) Formatos de mxima capacidad soportados (820-1.48-1.72).
La segunda tabla contiene informacin de las dems pistas del disco. El contenido y el formato de
esta tabla vara segn el tipo de disco: los formatos normales (como el caso de 1.80M) poseen 5 bytes: el
primero indica el nmero de sectores de la pista, el siguiente el GAP 3 al formatear, otro byte indica el
tamao de sector empleado (siempre 3, esto es, 1024 bytes) y los dos ltimos bytes son equivalentes a los
parmetros /X e /Y de FDFORMAT para desplazar de manera ptima la numeracin de los sectores en las
pistas consecutivas. Estos valores de /X e /Y son sensiblemente menores que los de FDFORMAT, pero no
hay que olvidar que aqu los sectores son dos veces ms grandes. En los formatos de disco de mxima
capacidad (como en 1.88M) esta tabla cambia radicalmente de estructura: el primer byte sigue siendo el
nmero de sectores, pero ahora son sectores de 128 bytes. Esto se debe a que en estos formatos, las pistas
son preformateadas (en una primera pasada) con sectores de 128 bytes. El siguiente byte es el GAP 3, que
como se puede observar es muy pequeo (de 3 a 5 bytes). Finalmente, viene el nmero de sectores a
renumerar. La razn es que, durante el formateo, se asignan nmeros a partir de 129 a la mayora de los
sectores; sin embargo, algunos de ellos no se llevan el que les correspondera sino que siguen otra numeracin
ms baja a partir de 1. En estos sectores, adems, al ser enviada su informacin al FDC durante el formateo,
se indicar un tamao distinto de 128 (512, 1024 2048). As, por ejemplo, en 1.88M la pista queda
formateada con nada menos que 64 sectores de 128 bytes numerados desde 129, habiendo sin embargo
algunos de ellos con nmeros ms bajos (1, 2,..., 7) y definidos con mayor tamao. Al ser escritos dichos
sectores (segunda fase del formateo) se machacarn los sectores de 128 bytes que les siguen y quedarn slo
ellos en la pista. Esto permite colocar sectores de distinto tamao en la pista. El GAP 3 definitivo ser mayor
(13 bytes en el peor de los casos). Ahora comprender el lector por qu haba que escribir la pista, despus
del formateo, en estos formatos de disco... Por ltimo, sealar que en esta tabla se elige un factor de
interleave adecuado, que si se echa un vistazo resulta ser de 1:2, ya que los sectores estn demasiado
prximos para numerarlos consecutivamente (por razones de velocidad, si bien al ser accedidos uno a uno
la controladora no tendra problemas para encontrarlos). En el caso del formato 1.88M, por ej., quedan
numerados: 4,1,5,2,6,3,7.
315 EL HARDWARE DE APOYO AL MICROPROCESADOR
La ltima tabla es la nica que realmente emplea 2M para acceder a todas las pistas, con excepcin
de la primera. Se trata de una lista ordenada de los tamaos de los sectores. En los formatos de disco
normales es una lista de treses, ya que todos los sectores son iguales y de 1024 bytes. En los formatos de
mxima capacidad, como 1.88M, se puede comprobar que la lista es ms variada. Las otras dos tablas vistas
con anterioridad slo 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 ms adelante, 2MF.EXE, que
permite elegir entre formatos normales (2MF sin parmetros o con la opcin /F) y formatos de mxima
capacidad (2MF /M). Como se vio en la descripcin del sector de arranque, el formato de mxima capacidad
logra introducir sectores de distinto tamao en la misma pista. Seguramente la descripcin dada en el apartado
anterior no ha quedado muy clara, por lo que ahora puntualizaremos un poco ms.
Uno de los principales objetivos al realizar 2M fue conseguir un nivel de compatibilidad lo
suficientemente alto, incluso en los formatos menos seguros como el que se describir a continuacin, al
menos en comparacin con los ya estudiados de sectores de 1 Kb. Hay disqueteras de 1.44M que soportan
el formateo de 3 sectores de 4096 bytes en una pista, lo que permitira obtener 1968K (en 82 cilindros,
soportados por prcticamente todas las unidades). Sin embargo, hay muchos ordenadores en que esto no es
posible, por tanto esta solucin fue descartada. En los casos en que es posible, lo es adems a costa de
rebasar con creces los mnimos niveles de seguridad (machacando no slo el GAP ubicado al final de la pista,
sino tambin el del principio e incluso el IAM; resulta increble que algunas controladoras de disquete
continen reconociendo los sectores). Adems, se tratara de una solucin exclusiva para disquetes de 1.44M.
El truco explicado con anterioridad consiste en formatear los discos con sectores muy pequeos de
128 bytes, pero definindoles con tamaos de 512, 1024 y 2048 bytes al enviar la informacin de cada sector
al controlador, de cara a agruparles posteriormente para obtener sectores de mayor tamao. Echando cuentas,
con un GAP 3 provisional de slo 3 bytes (podramos 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 un
sector de 2048 bytes, los 60 bytes aadidos al principio del primer sector de 128 bytes por el FDC, los 2
bytes aadidos al final del ltimo sector por el FDC y otros 13 bytes de GAP 3. Agrupando 6 sectores se
obtienen 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 GAP
3 de 5 bytes. As, en un disquete estndar de 1.44M, con 12500 bytes por pista, donde caben bastante
holgadamente 64 sectores de 128 bytes de las caractersticas 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 vez
formateada la pista, es conveniente escribir todos los sectores (la primera lectura dara error de CRC en caso
contrario), de paso se asegura de esta manera, en una posterior lectura, que la escritura no ha provocado que
ningn sector pise a otro, asegurando la fiabilidad del mtodo. Una vez que el disco ha sido formateado, la
verificacin realizada durante el formateo garantiza que es seguro; la separacin o GAP 3 medio menor es
de 13 bytes y puede considerarse bastante razonable (el sector de 512 bytes con un GAP 3 de slo 5 es
colocado siempre al final de la pista); en los disquetes de doble densidad es adems superior, al emplearse
un 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 para
verificar; cada una de ellas, dada la proximidad de los sectores, requiere de dos vueltas del disco (los sectores
estarn numerados alternamente con un razonable interleave 1:2); en total, 6 vueltas en un disco de 1.44M
por cada pista, lo que equivale a 1,2 segundos por pista y 3:17 minutos en el conjunto del disquete (2 caras
y 82 cilindros). Este es el precio que hay que pagar para obtener 1.912.320 bytes libres netos (los que
aparecen 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 del
cilindro 0, el cdigo de 2M se saltar el acceso al primer sector de la pista (al estar la FAT2 en l, por regla
general, y debido a las emulaciones). Por tanto, en este caso, es necesario escribir en el cabezal 129 para
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, se
puede escribir en el cabezal 128/129 de todas las pistas (salvo la primera, que no tiene realmente tantos
sectores como las dems y que adems 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 desva la INT 13h/40h. En las mquinas AT con disco
duro de tipo IDE (los ms extendidos actualmente) o con una controladora de disco duro ordinaria de AT,
la BIOS desva a INT 40h los servicios de disquete, siendo invocada esta interrupcin desde la INT 13h para
atender las funciones de disquete. Sin embargo, si el ordenador no tiene disco duro o incorpora una
controladora de disco duro de XT, es la INT 13h quien podra controlar los disquetes. La versin 1.0 de 2M
desviaba la INT 40h en lugar de la INT 13h, por el motivo que ahora analizaremos (ayuda en la cuestin del
DMA); sin embargo, sto hacia que el programa no funcionara en algunas mquinas AT sin disco duro o con
controladora de XT. Por ello, en la versin 1.1 se volvi a trabajar con INT 13h. Pero desde 2M 2.0+, aunque
ahora ms por razones de seguridad que de comodidad, se utiliza una tcnica mixta: si el ordenador emplea
la INT 40h, 2M se instala desde esta interrupcin; en caso contrario, lo hace desde INT 13h (actundo desde
INT 40h el programa toma el control de los discos antes que otros TSR instalados despus). Y volvamos
sobre la cuestin del DMA, que motiv el uso de INT 40h en 2M 1.0. Como el lector recordar, a la hora
de transferir con la disquetera hay que tener cuidado con las fronteras de DMA. Sin embargo, resultara muy
engorroso tener que tener esto en cuenta en los programas de alto nivel. El propio DOS considera que es un
autntico fastidio tener que comprobar esto cada vez que se accede al disco. Por ello, cuando el sistema
operativo se carga en el ordenador desva la INT 13h y la modifica para arreglar de un plumazo los
problemas con el DMA: a partir de ese momento, la INT 13h es realmente controlada por el DOS, aunque
se trate de una interrupcin BIOS. Las nuevas rutinas de la INT 13h colocadas por el DOS se limitan a llamar
a la vieja INT 13h (nadie ha hablado an de INT 40h) y, cuando se produce un error de frontera de DMA,
la operacin de disco que lo haba provocado es segmentada probablemente en tres fases: los sectores que
estaban antes de la frontera, los que quedan por detrs y el que cae justo en medio; este sector es
probablemente transferido a travs 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 control
evidentemente corre a cargo de 2M) todas las llamadas del DOS a la INT 13h seran llamadas a 2M, que ha
sido instalado despus de que el DOS arregle la INT 13h. Por tanto, 2M debe en ese caso ocuparse de la
engorrosa gestin de errores de DMA, ya que el DOS no espera nunca este tipo de error de una llamada a
la INT 13h. En la prctica, 2M a partir de la versin 1.1, en las operaciones que afectan a varios sectores de
disco consecutivos, se ve obligado a detectar con antelacin el futuro cruce de una frontera de DMA: en caso
de que se vaya a producir, el sector problemtico es transferido a travs del buffer intermedio del programa.
La versin 1.0 de 2M desviaba INT 40h en vez de INT 13h y se limitaba a devolver la condicin de error
cuando se iba a producir, para que el propio DOS en INT 13h llamara de nuevo con ms cuidado.
2M podra haber sido creado como controlador de dispositivo que definiera nuevas letras de unidad
para soportar los nuevos disquetes; sin embargo resulta ms intuitivo para el usuario continuar empleando
las unidades A: y B: habituales. Esto se consigue, como hemos visto, modificando la INT 13h de la BIOS,
lo que adems 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 los
copiones, la mayora de los llamados programas de bajo nivel en materia de disquetes se limitan a llamar a
317 EL HARDWARE DE APOYO AL MICROPROCESADOR
la BIOS. La tcnica de ampliar la funcionalidad de la INT 13h de la BIOS es, por tanto, la ms eficiente.
El listado que comentaremos es slo la parte importante del programa. Desde 2M 3.0 ya no hay
listados con partes repetidas: un nico fichero 2M.ASM produce 2M.COM (sistemas AT) y 2MX.COM (en
PC/XT) por medio del ensamblaje condicional. Para ello se apoya en 2MKERNEL.INC, ncleo principal con
todo el cdigo de acceso a la controladora para soportar los discos 2M, y tambin empleado para generar
2M.SYS (versin driver para AT) y 2MFBOOT.BIN (con cdigo SuperBOOT para el formateador). Tambin
se utiliza 2MUTIL.INC para englobar ciertas rutinas de utilidad comunes a ms programas de la aplicacin.
Aqu nos limitaremos a comentar 2MKERNEL.INC, ya que lo restante no est relacionado con la
controladora de discos.
2M puede controlar las unidades de disco A: y B: si son de alta densidad (de lo contrario se limita
a invocar a la INT 13h original). Por ello, adems de un juego de variables globales, hay una estructura que
define las variables propias de una unidad que se emplea para crear dos reas de datos particulares, una para
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
LECTURA DE IDs EN 3-HD (FORMATO NORMAL Y DE MAXIMA CAPACIDAD)
cada disquetera. A lo largo de la mayora
del cdigo residente, el registro SI estar
apuntando a esa zona de variables locales
de la disquetera que se trate. Al principio
del programa est la rutina que controla la
interrupcin 2Fh, empleada para gestionar
la autodeteccin en memoria del programa
residente y permitir su posible futura
desinstalacin.
La rutina que controla la INT 13h
INT 40h es ms importante. Su labor
consiste en pasar el control de las
funciones 2 (lectura), 3 (escritura), 4
(verificacin) y 5 (formateo) a 2M (si el
disquete introducido es de este tipo) o a la
interrupcin original (si el disquete
introducido no es de tipo 2M). Existe una
variable por cada unidad que indica en todo
momento si el disquete introducido es de
tipo 2M (control2m_flag=ON) o no. Otro
cometido consiste en detectar los cambios
de disco, para actualizar dicha variable en
consecuencia. Ante el primer cambio de
disco detectado se retorna con un error 6
(porque as lo hace la BIOS original).
En el caso de la funcin de formateo (no implementada en el cdigo SuperBOOT por falta de
espacio), se mira si quien la invoca solicita un formateo normal o si se trata de una peticin de formateo de
disquete 2M. Esto es debido a que 2M aumenta la funcionalidad de la funcin 5 original de la BIOS para
soportar los nuevos disquetes. En la funcin de la BIOS, se indica en AL el nmero 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 informacin para
formatear. Cuando est 2M residente y se invoca la funcin 5 con el registro SI=324Dh (SI="2M") y con
AL=7Fh, se le indica a 2M que no llame a la funcin de formateo original de la BIOS y que formatee l la
pista en la unidad y cabezal indicados. En este caso AL es ignorado, ya que en ES:BX lo que se le pasa a
la BIOS (es decir, a 2M) no es la direccin de tabla alguna sino el sector de arranque del futuro disquete,
que contiene toda la informacin necesaria sobre la estructura del disco para poder clonarlo. No hay que crear
tablas ni emplear otras funciones BIOS para seleccionar densidad ni nada por el estilo. Tampoco hay que
considerar la complejidad de los formatos 2M (en los que difiere la primera pista de las restantes): de todo
se ocupa el cdigo residente del propio 2M. La rutina format_2m invocada desde ges_int13 se encarga del
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 rpidamente ante el error. As, se avisa a todos los dems programas
residentes 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 recin formateado aparecera an, falsamente, su
contenido previo. A continuacin realiza las siguientes tareas: toma nota de los parmetros del futuro disco,
pone en marcha el motor, lleva el cabezal a la pista, crea la tabla con informacin para el formateo, formatea
la pista y retorna con el cdigo de error o xito correspondiente. En los formatos de mxima capacidad,
recurdese que haba que escribir la pista tras el formateo, para evitar que la primera lectura diera error y para
completar realmente el proceso. Sin embargo, el cdigo 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 vez
que formatean; lo contrario sera una prdida de tiempo con una escritura muerta. En el caso de programas
que slo formateen, tendrn adems que escribir; esto implica que esos programas deben estar diseados para
formatear disquetes 2M (nadie ha dicho que el FORMAT del DOS pudiera hacerlo por s solo).
El procedimiento detecta_cambio
determina si se ha producido un cambio de
disco. En caso de que se haya producido (o
la primera vez absoluta que se ejecuta la
rutina tras haber instalado 2M en memoria)
se intenta leer el sector de arranque del
mismo para determinar la densidad del
mismo y averiguar si es de tipo 2M.
Primero se intenta bajar la lnea de cambio
de disco: si no fuera posible, es que la
unidad est sin disquete introducido. El
acceso se intenta tres veces, con todas las
densidades posibles (500, 300, 250 Kbit/seg
y finalmente 1 Mbps). Si no se pudiera leer
el sector de arranque, podra deberse a que
es un disco sin formatear, o tratarse de otro
medio fsico, por lo que se le devuelve el
control a la INT 13h original hasta un
futuro nuevo cambio de disco. Esto mismo
puede suceder si se consigue leer el sector
de arranque y la rutina set_info comprueba
que el disco es estndar del DOS. Cuando
no hay disco en la unidad y se falla al
bajar la lnea de cambio, se delega el
control a la BIOS pero si sta logra bajarla
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
Longitud (ms) Sector Tamao 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 ms IDs [ESC=salir].
LECTURA DE IDs EN 5-DD (FORMATO NORMAL Y DE MAXIMA CAPACIDAD)
(controladora no compatible?) se le vuelve a robar el control al siguiente acceso. Esta artimaa permiti a
versiones antiguas de 2M funcionar en mquinas 486 (cuando no se tomaba la precaucin de hacer un retardo
al resetear la controladora y sta quedaba en ocasiones atontada, hasta que la BIOS del sistema la reseteaba
bien). En caso de ser un disco 2M se anotan las caractersticas del mismo, teniendo en cuenta que lo que
acabamos de leer es precisamente su sector de arranque... Como 2M es el encargado de detectar la densidad
del disco, es necesario que ajuste las variables de la BIOS indicando dicha densidad, ya que ella ser la
encargada de controlar los disquetes normales. En realidad, la densidad slo se ajusta en el primer acceso al
disco, existiendo dos variables en el rea de datos de la BIOS, en el segmento 40h, que indican la densidad
a emplear en cada disquetera: si dichas variables no estn correctamente inicializadas, al conmutar de una
unidad a otra la BIOS no seleccionara la velocidad correcta y se producira un error. Como al introducir un
disco nuevo en la unidad lo primero que hace el DOS es consultar su sector de arranque, las primeras
versiones de 2M dejaban la tarea de detectar la densidad del disco a la propia BIOS (espiando las lecturas
del 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 slo conozco una) hacan cosas muy raras para
determinar la densidad de los discos (como ir leyendo varias pistas consecutivas) y tropezaban con los
319 EL HARDWARE DE APOYO AL MICROPROCESADOR
disquetes 2M. Esto es un botn de muestra de lo que pasa cuando los fabricantes europeos modifican mal
las BIOS de los taiwaneses, para no copiarlas del todo. De ah que la versin definitiva del programa
reemplace en esta tarea a la BIOS. Sin embargo, en caso de que 2M no pueda determinar la densidad de la
unidad sique delegando el control a la BIOS: el motivo es mantener la compatibilidad con otros soportes
extraos. Este es tambin el motivo por el que 2M no sustituye totalmente el cdigo BIOS de INT 13h, que
hubiera dado menos problemas a la hora de programar (aunque el programa resultante ocupara tambin algo
ms 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.62
Indice 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 consista en COPY C:\TEST\*.* B: y la de lectura consista en COPY /B *.* NUL
Al 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 parmetros /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, comprobando
adems si el disco es de tipo 2M. La rutina set_err, invocada al final del formateo y desde la rutina que
accede directamente a los sectores de disco, analiza el cdigo de error devuelto por el controlador de
disquetes y lo convierte a la notacin de errores de la BIOS. Set_bios_err copia el resultado del acceso a
disco 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 gestin a alto nivel del acceso a disco: es aqu donde
se emula la existencia de la segunda copia de la FAT apoyndose en la primera, as como el sector de
arranque virtual ubicado en el primer sector fsico de la FAT2. Como 2M 2.0 apareci cuando ya estaba
bastante extendida la versin anterior, se hizo necesario (y lo sigue siendo en 2M 3.0) continuar soportando
los discos antiguos. En ellos, se sigue leyendo el sector de arranque fsico 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 se
tiene en cuenta el acceso al cabezal 128 129 para acceder en ese caso al 0 al 1 sin emulaciones. Las
coordenadas de la BIOS, en la forma cilindro-cabezal-sector son traducidas momentneamente a las del DOS
para simplificar el proceso. Tambin se comprueba si el checksum (o suma de comprobacin) del sector de
arranque, realizado con anterioridad en set_info, es correcto. Es difcil que no lo sea, porque el cdigo de
2M no deja a cualquiera escribir sobre el sector de arranque fsico. Pero si no lo fuera, se devuelve un seek
error al programa que llama a la INT 13h, habindose elegido este cdigo porque no haba otro ms
descriptivo en la lista de errores de disco de la BIOS. Si al ejecutar un comando DIR sobre un disquete 2M
aparecen errores de seek ya sabr el lector por qu...
El procedimiento ejecuta_io es llamado repetidamente desde control_2m para realizar la lectura o
escritura de un conjunto de sectores. Este procedimiento es el ms complicado de todo el programa, pero la
tarea que realiza es relativamente sencilla. Primero, vuelve a pasar las coordenadas del disco del formato DOS
al formato fsico propio de la BIOS. Hay que tener en cuenta que un sector fsico en un disquete 2M puede
ser de 512 bytes, pero tambin de 1024 2048. Por tanto, introducimos aqu el concepto de seccin para
hacer referencia a 512 bytes, que a fin de cuentas es la unidad de medida a emplear.
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 fsico, ya que
las operaciones de lectura/escritura de varios sectores en bloque slo tienen sentido cuando stos estn lo
suficientemente separados pero sin pasarse. En nuestro caso estn excesivamente separados, ya que la
numeracin es discontinua (interleave 1:2) y entre dos sectores de nmero consecutivo hay otro; por tanto,
no se ganara rendimiento en un acceso multisector; por otro lado, algunos formatos de disco tienen un
nmero par de sectores en las pistas y dos de ellos tienen que tener forzosamente el nmero consecutivo, con
lo que fallara el acceso multisector debido a la excesiva proximidad en este caso; adems, no est muy claro
si se podrn acceder de esta manera sectores que no sean del mismo tamao (no me molest en probarlo).
La lectura es la operacin ms sencilla: se extrae del disco el sector fsico donde est incluida la seccin que
toca leer y despus se copia a la direccin de memoria definitiva. No se puede leer el sector directamente en
el buffer requerido por el programa que invoca la INT 13h, ya que ste podra requerir slo 512 bytes (o un
mltiplo impar de esta cifra) y los sectores fsicos podran exceder este tamao, afectando a zonas no
permitidas de la memoria ubicadas tras el buffer. Por tanto se utiliza un buffer intermedio (definido con un
tamao de 2 Kb para acomodar el mayor sector posible). El movimiento de la seccin a su ubicacin
definitiva no es una tarea muy costosa, ya que en un ordenador medio se ejecuta unas cien veces ms rpido
MAPAMEM 2.1
- Informacin sobre la memoria del sistema.
Tipo Ubicacin Tamao PID Propietario
-------- --------- ------- ----- ---------------
Sistema 0000-003F 1.024 Interrupciones
Sistema 0040-004F 256 Datos del BIOS
Sistema 0050-0105 2.912 Sistema Operat.
Sistema 0107-0143 976 0008
Sistema 0145-0144 0 0008
Sistema 0146-0149 64 0008
Programa 014B-015A 256 014B 4DOS
Entorno 015C-0174 400 0176 MAPAMEM
Programa 0176-01C9 1.344 0176 MAPAMEM
Libre 01CB-9FFE 648.000 0000 <Nadie>
Sistema A000-D3B4 211.792 0008
Sistema D3B6-D3C2 208 D3B6
Sistema D3C4-D50D 5.280 D3C4
Sistema D50F-E437 62.096 0008
Sistema E439-E49C 1.600 E439
Sistema E49E-E4AD 256 E49E
Sistema E4AF-E4CE 512 E4AF
Sistema E4D0-E55E 2.288 E4D0
Sistema E560-E568 144 E560
Datos E56A-E631 3.200 014B 4DOS
Entorno E633-E672 1.024 014B 4DOS
Libre E674-E68C 400 0000 <Nadie>
Programa E68E-E810 6.192 E68E SHARE
Programa E812-E97A 5.776 E812 PRINT
Entorno E97C-E996 432 E998 VIDRAM
Programa E998-EA04 1.744 E998 VIDRAM
Entorno EA06-EA1F 416 EA21 UNIVESA
Programa EA21-EBF1 7.440 EA21 UNIVESA
Programa EBF3-EC1D 688 EBF3 KEYBSP
Programa EC1F-EC77 1.424 EC1F RCLOCK
Programa EC79-EDBB 5.168 EC79 2M
Programa EDBD-EDD8 448 EDBD DISKLED
Libre EDDA-EDF3 416 0000 <Nadie>
Programa EDF5-F281 18.640 EDF5 DATAPLUS
Programa F283-F34D 3.248 F283 HBREAK
Programa 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 proceso
de lectura se repite tantas veces como secciones haya que
transferir. En todo momento, unas variables indican qu sector
fsico (y de qu cilindro, cabezal y unidad) est en el buffer.
De este modo, por ejemplo, cuando se lee un sector de 2 Kb
para transferir su primera seccin, se traen a la memoria 4
secciones de golpe y ya no sern necesarios ms accesos a
disco si hubiera que transferir tambin las 3 restantes, porque
el sector en que estn ya se encuentra en el buffer. La escritura
es algo ms compleja, y hay que distinguir dos casos: por un
lado, cuando hay que volcar a disco un nmero de secciones
consecutivas suficientes para completar un sector fsico; por
otro, cuando hay que escribir una o varias secciones que no
completan un sector fsico. En el primer caso, se escribe sin
ms; en el segundo caso es necesario leer el sector al buffer,
modificar slo la(s) seccion(es) afectada(s) y escribirlo en el
disco. Este ltimo caso supone una fuerte degradacin de la
velocidad, ya que tras leer un sector del disco habr que volver
a escribirlo, hecho que no ocurrir hasta la siguiente vuelta
del mismo. Por fortuna, cuando se hace un COPY el DOS
enva grandes bloques, lo que en la mayora de los casos (no
en todos) provoca escrituras de pistas completas, tarea en la
que no se pierde un pice de rendimiento. No obstante, esta
arquitectura de los disquetes 2M provoca que sean notablemente ms lentos escribiendo que leyendo.
En los formatos normales (2MF /F) todos los sectores de la pista son del mismo tamao, lo que
tambin sucede en la primera pista de los formatos de ms capacidad. Estn suficientemente separados y
numerados consecutivamente. Por tanto, una acceso multisector es posible y ms que interesante. Aqu no
slo no se emplea el buffer intermedio sino que adems no se puede, porque el acceso multisector puede
superar los 2 Kb de capacidad del buffer. La transferencia se hace directamente sobre la direccin deseada
por el programa que invoca la INT 13h. Slo hay un par de excepciones: cuando la primera seccin a
transferir es la segunda mitad de un sector (recordemos que son de 1 Kb) y cuando la ltima seccin es la
primera mitad de un sector. En ambos casos se emplea el buffer intermedio por el mismo motivo de siempre:
evitar la alteracin de zonas de memoria que vayan detrs del buffer suministrado por el programa que llama
a la INT 13h. Sobre la escritura se podran hacer las mismas consideraciones que hacamos con los formatos
de mxima capacidad. En la operacin de acceso multisector hay que considerar tambin el posible cruce del
buffer suministrado por el programa principal con una frontera de DMA: la rutina acceso_multi se encarga,
llegado el momento, de transferir el sector crtico a travs del buffer intermedio, segmentando la operacin
en tres fases (los sectores anteriores, el sector que cruza la frontera y los restantes). No controlar los
321 EL HARDWARE DE APOYO AL MICROPROCESADOR
problemas con el DMA provoca que el ordenador se cuelgue al hacer COPY de un fichero mediano (o que
lo copie mal en cualquier caso). Obviamente, el buffer intermedio se inicializa para que nunca cruce una
frontera de DMA. El nico caso en que acceso_multi no necesita tomar precauciones con el DMA es en el
cdigo SuperBOOT: aunque se instale desde la INT 13h, lo hace antes de la carga del sistema operativo (que
ser el encargado de arreglar los problemas con el DMA).
Por tanto, en ejecuta_io es donde se toman todas las complicadas decisiones sobre cmo y dnde
cargar/grabar de disco. He de agradecer aqu a Edgar Swank su colaboracin en detectar y corregir errores
en esta compleja rutina, proponindome adems las modificaciones en el listado: antes de 2M 2.0, los discos
2M no soportaban realmente la escritura con verificacin (VERIFY ON a nivel DOS). La variable sector_fin
est a 0 para indicar el acceso a un solo sector (sector_ini) o es distinta de cero para indicar el ltimo sector
involucrado en el caso de accesos multisector (junto a sector_ini). Dentro de este procedimiento, la subrutina
acceso_secc se encarga de la transferencia de una sola seccin.
El procedimiento trans_secc realiza las transferencias entre el buffer interno y la direccin
suministrada por el programa que llama a la INT 13h. La rutina leido? comprueba si el prximo sector fsico
a 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 del
sector a ser ledo (para poder programar luego correctamente el FDC), llevar el cabezal a la pista adecuada,
cargar los registros convenientemente segn haya que emplear el buffer intermedio o no, llamar a la rutina
que accede realmente al disco y tomar nota de qu sector ha sido recin ledo (para evitar futuras lecturas
innecesarias).
En num_secciones se calcula el
nmero de secciones o bloques de 512
bytes del sector fsico en curso, apoyndose
en la informacin del sector de arranque
del disquete que fue anotada cuando se le
reconoci por vez primera.
La rutina motor_ok arranca el
motor de la unidad si an no estaba en
marcha. En caso de estar parado, o de
llevar poco tiempo encendido a causa de
una reciente lectura de la lnea de cambio
[14464/109040] C:\>dir b:
Volume in drive B is unlabeled Serial number is EA82:3F1B
File 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 pistas
23 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 detencin es an muy alto) se hace la pausa pertinente para
que alcance el rgimen de rotacin adecuado. Esta rutina es invocada en varias ocasiones; entre otras, desde
ejecuta_io.
En reset_drv se inicializa el FDC envindole el comando Specify; la situacion de reset es mantenida
durante unos microsegundos, pausa que tambin realizan las BIOS modernas, ya que en algunas versiones
de 2M anteriores a la 1.3 se comprob que no lograban resetear la controladora en algunas mquinas 486 (en
estos casos no se detectaba el tipo del nuevo disco introducido en la disquetera y, al delegar el control a la
BIOS, sta generaba errores de sector no encontrado y anomala 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 deje
de vibrar; en caso de que haya que hacer esta pausa se establecen 1 ms para el caso de la lectura (no es muy
peligroso que se produzca un error, ya que la operacin se reintentara) y 15 ms para la escritura, asegurando
en este ltimo caso el xito de la operacin, ya que escribir con el cabezal no asentado podra daar la
informacin del disco. El disco est formateado (salvo en los los formatos de mxima capacidad, que son un
mundo aparte) con ciertos deslizamientos en la numeracin 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
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 tiene
por objeto asegurar esto mucho ms todava. As, algn ordenador muy extrao que pinchara en los ndices
de rendimiento a la hora de escribir probablemente no lo hara, al menos, al leer. Como un posicionamiento
del cabezal precede siempre a las operaciones de lectura o escritura (seek_drv), se selecciona aqu la
velocidad de transferencia a emplear, acorde con la densidad de la pista a ser accedida (set_rate). En caso
de que la unidad precisara recalibracin (debido a algn reset anterior) se llama desde aqu al procedimiento
recalibrar.
El procedimiento sector_io es quien finalmente se encarga de hacer la lectura o escritura del sector
o sectores necesarios, programando el FDC. Se calcula el tamao en bytes del bloque a transferir, se
programa el DMA por medio de las rutinas calc_dir_DMA y prepara_DMA y se enva el comando
adecuado al FDC (lectura/escritura). Al final, se anotan los resultados. La subrutina calc_dir_DMA traduce
la direccin segmentada al formato necesario para programar el DMA; en el cdigo SuperBOOT tiene que
devolver adems un posible error de cruce de frontera de DMA, ya que el cdigo de 2M no evita las llamadas
ilegales en este caso.
En genera_info se construye la tabla de informacin a enviar al DMA para formatear la pista
solicitada en la funcin de formateo de 2M. Esta informacin se obtiene a partir del sector de arranque del
futuro disco, suministrado por el programa que intenta formatear. Conociendo cmo esta estructurado dicho
sector, la arquitectura de los disquetes 2M y qu necesita el comando del FDC para formatear se puede
entender cmo funciona la rutina, por lo que no nos detendremos en analizarla. Es formatea_pista el
procedimiento que formatea la pista a partir de la tabla creada por la rutina anterior.
La subrutina espera_int espera durante no ms de 2 segundos la llegada de una interrupcin de
disquete que sealice el final de una operacin con el FDC. Conviene no esperar indefinidamente porque si
la unidad no est preparada podra tardar muchsimo en devolver la interrupcin. As, se detecta en un tiempo
razonable la circunstancia y posteriormente se resetear la controladora (ante el error) para arreglar el
problema de la interrupcin pendiente (y del FDC que no responda). Fdc_read y fdc_write se encargan de
recibir y enviar bytes al FDC, tpicamente rdenes y resultados. Ambas rutinas tambin tienen control timeout,
en este caso de 2 milisegundos; al principio de las mismas se realiza una brevsima pausa al igual que hacen
las BIOS AMI de 486 (que para algo servir). Finalmente, las subrutinas fdc_respiro y retardo efectan una
pausa de 60 s y AX milisegundos, respectivamente, apoyndose repetitivamente en la macro pmicro, que
pierde unos 15,09 microsegundos muestreando los ciclos de refresco de memoria del AT. Pmicro no es una
subrutina (salvo en el caso del cdigo SuperBOOT, por razones de espacio) porque el CALL y RET
asociados podran ralentizar la monitorizacin de los ciclos de refresco de manera excesiva en los ordenadores
ms lentos, deparando un retardo efectivo superior.
Finalmente, initcode ser invocada slo desde el sector de arranque fsico durante el arranque desde
disquete, con objeto de inicializar ciertas variables y activar el cdigo SuperBOOT. Una precaucin
importante es que, ensamblando para obtener cdigo SuperBOOT, ste tiene que ocupar exactamente 2560
bytes (5 sectores). Ciertamente, entra muy justo... pero cabe, con alguna que otra artimaa (excluir rutinas
de formateo, utilizar subrutinas en vez de macros, simplificar la gestin de las fronteras de DMA, etc) aunque
los 5 sectores que ocupa impiden ubicarlo en discos de doble densidad. Pero, quin va a querer hacer botable
un disco 2M de doble densidad, cuando uno estndar de alta tiene ms capacidad?.
;
;
;
;
;
;
;
;
; 2MKERNEL - (C) Ciriaco Garca de Celis.
;
; NUCLEO RESIDENTE DE 2M UTILIZADO POR SUS PRINCIPALES EJECUTABLES
;
; Los siguientes smbolos se utilizan
; para el ensamblaje condicional:
;
; XT -> Indica que el cdigo ejecutable es para PC/XT y no posee
; instrucciones de 286 ni utiliza recursos hardware de AT.
;
; SUPERBOOT -> Indica que el cdigo ejecutable se ensambla para
; ocupar 2560 bytes exactamente (para autoarranque).
;
;
; ------------ Cdigos de modos y rdenes del DMA y del FDC.
F_READ EQU 46h ; modo DMA para lectura
F_WRITE EQU 4Ah ; modo DMA para escritura
F_VERIFY EQU 42h ; modo DMA para verificacin
F_FORMAT EQU 01001101b ; orden de formateo del FDC
; ------------ Estructura de datos con informacin para cada unidad.
info_drv STRUC
maxs EQU 13 ; mximo 13 sectores fsicos/pista
tipo_drv DB ? ; tipo de la disquetera (0 = no hay)
control2m_flag DB OFF ; a ON si 2M controla la unidad
cambio DB ON ; a ON indica cambio de soporte
version_fmt DB ? ; versin del formato de disco 2M
multi_io DB ? ; a 0 si posible acceso multi-sector
chk DB ? ; a 0 si checksum del sector 0 Ok
vunidad EQU THIS WORD
vunidad0 DB ? ; velocidad pista 0
vunidadx DB ? ; velocidad dems pistas
gap DB ? ; GAP entre sectores (leer/escribir)
sectpista DB ? ; sectores lgicos por pista
323 EL HARDWARE DE APOYO AL MICROPROCESADOR
tabla_tsect DB maxs DUP (?) ; tamaos de sectores 1, 2, ..., N
tam_fat DB ? ; sectores/FAT en la unidad
ENDS
; ------------ Variables del programa.
info_ptr DW info_A ; punteros a datos de las unidades
DW info_B
IFDEF SUPERBOOT
DB "30" ; Versin 2MFBOOT 3.0
ENDIF
id_sistema DB "2M-STV" ; identificacin de disco 2M
IFDEF XT
tbase DW ? ; base de tiempos para retardos
ENDIF
unidad DB ? ; unidad fsica de disco en curso
numsect DW ? ; sectores a transferir
sectini DW ? ; primer sector DOS a transferir
cilindro DB ? ; cilindro del disco a acceder
cabezal DB ? ; cabezal a emplear
sector DB ? ; nmero de sector fsico
sector_ini DB ? ; nmero de sector fsico inicial
sector_fin DB ? ; nmero de sector fsico final
seccion DB ? ; parte del sector fsico en curso
secciones DB ? ; sectores lgicos a transferir
tsector DB ? ; LOG2 (tamao de sector) - 7
buffer DW buffer_io ; puntero al buffer intermedio
buf_unidad DB ? ; unidad del sector en el buffer
buf_cilcab DW ? ; cilindro/cabezal de sector buffer
buf_sector DB ? ; nmero de sector en el buffer
status DB ? ; resultado de los accesos a disco
fdc_result DB 7 DUP (?) ; bytes de resultados del FDC
orden DB ? ; operacin F_READ/F_WRITE/F_VERIFY
tab_ordenes DB F_READ
DB F_WRITE
DB F_VERIFY ; rdenes 2, 3 y 4
; --- Interpretacin BIOS de los bits de ST1
lista_errs DB 4 ; sector not found
DB 0
DB 10h ; bad CRC
DB 8 ; DMA overrun
DB 0
DB 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 gestin de INT 2Fh.
IFNDEF SUPERBOOT ; Cdigo SuperBOOT no soporta INT 2Fh
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; s llama: darle informacin
LEA DI,autor_nom_ver
ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"
IRET
ges_int2F ENDP
ENDIF
; ------------ Nueva rutina de gestin 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 verificacin (AH=4)
; segn el tipo de disco introducido. Ante una funcin 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
; cdigo SuperBOOT no hay soporte para formatear.
IFNDEF SUPERBOOT
ges_int13 PROC FAR
STI
CLD
PUSHF
CMP DL,2
JAE ges13bios ; no es disquetera A: B:
PUSH SI
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; unidad 1.44/2.88M?
ges_2m: POP SI
JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
CALL detecta_cambio ; cambio de disco?
JNC sin_cambio
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
sin_cambio: CMP AH,5
JNE dilucida ; no es orden de formateo
CALL leer_lin_camb
JNZ format_bios ; no hay disquete en la unidad
CMP AL,7Fh
JNE format_bios ; no es orden formateo de 2M
CMP SI,"2M"
JE format_2m ; es orden de formateo de 2M
format_bios: CLC
CALL set_flag_STV ; CF = 0 -> indicar no 2M
dilucida: PUSH SI
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].control2m_flag,OFF
POP SI
JE ges13bios ; la unidad la controla la BIOS
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POPF
JMP CS:ant_int13 ; saltar al gestor de INT 13h
; --- Funcin de formateo implementada por 2M. En los
; disquetes creados con /M todas las pistas salvo
; la 0 deberan ser formateadas invocando INT 13h
; de manera directa (con CALL) para evitar que se
; ejecute cierto cdigo de WINDOWS en el modo
; protegido que provoca errores al formatear. Antes
; de formatear la primera pista fsica del disco se
; invoca la funcin de formateo de la INT 13h
; original (con un error para provocar un rpido
; 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: POPF
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv
MOV cilindro,CH
MOV cabezal,DH
OR CH,DH
JNZ format_trx ; no es cilindro 0 y cabezal 0
INC CL
JNZ no_f_chg
MOV [SI].cambio,ON ; simular cambio de disco
JMP fmt_exit
no_f_chg: XPUSHA
MOV AL,1 ; formatear (AH=5)
MOV CH,0
MOV DH,2 ; en cabezal 2 (incorrecto)
PUSHF
CALL ant_int13 ; avisar al DOS del nuevo disco
CLD ; mantener DF=0
STC
CALL reset_drv ; asegurar aceleracin motor
XPOPA
CALL set_info ; caractersticas nuevo soporte
format_trx: CALL genera_info ; tabla de informacin formateo
CALL motor_ok ; asegurar que est en marcha
CALL seek_drv
CALL formatea_pista
PUSHF
CLC
CALL motor_off_cnt ; cuenta normal detencin motor
POPF
CALL set_err
CALL set_bios_err ; no altera flags
fmt_exit: XPOPA ; **
MOV AH,status
POP DS ; *
RET 2
ges_int13 ENDP
ELSE ; El cdigo SuperBOOT no formatea
ges_int13 PROC FAR
STI
CLD
PUSHF
PUSH SI
CMP DL,2
JAE ges13bios ; no es disquetera A: B:
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; unidad 1.44/2.88M?
ges_2m: JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
JNE no_format
CALL set_flag_STV ; CF = 0 -> "disco no 2M"
JMP ges13bios
no_format: CALL detecta_cambio ; cambio de disco?
JNC dilucida
POP SI
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
dilucida: CMP CS:[SI].control2m_flag,OFF
JE ges13bios ; la unidad la controla la BIOS
POP SI
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POP SI
POPF
JMP 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 PROC
PUSHF
PUSH BX
MOV BL,DL
MOV BH,0
SHL BX,1
MOV SI,CS:[BX+OFFSET info_ptr]
POP BX
POPF
RET
set_SI_drv ENDP
; ------------ Si CF=1, indicar disquete 2M presente. A la
; entrada, DL indica la unidad de disco.
set_flag_STV PROC
XPUSHA
CALL set_SI_drv
MOV AL,ON ; indicar 2M
JC tipo_stv_ok
324 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV AL,OFF ; indicar no 2M
tipo_stv_ok: MOV CS:[SI].control2m_flag,AL
XPOPA
RET
set_flag_STV ENDP
; ------------ Devolver ZF=1 si cilindro y cabezal 0.
pista0? PROC
PUSH AX
MOV AL,cabezal
OR AL,cilindro
POP AX
RET
pista0? ENDP
; ------------ Devolver ZF=1 si la lnea 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 an). En la prctica, invocando esta rutina
; desde INT 13h nunca ser necesario arrancar el motor
; ya que el DOS ejecuta antes la funcin equivalente,
; la 16h, que lo pone en marcha. Es simplemente una
; medida de seguridad contra las BIOS de marca.
leer_lin_camb PROC
XPUSHA ; *
PUSH DS
DDS
MOV AL,1
MOV CL,DL
SHL AL,CL ; bit de motor en 0..3
TEST DS:[3Fh],AL
JNZ rodando ; el motor ya est girando
CLC
CALL motor_off_cnt ; cuenta normal detencin motor
rodando: MOV AH,DL
XSHL AH,4
OR AH,AL ; AH = byte BIOS
XSHL AL,4
OR AL,00001100b ; modo DMA, no hacer reset
OR AL,DL ; AL para reg. salida digital
MOV DX,3F2h
CLI
MOV DS:[3Fh],AH ; actualizar variable BIOS
OUT DX,AL ; arrancado motor en la unidad
ADD DX,5
DELAY
IN AL,DX ; leer lnea de cambio de disco
STI
TEST AL,80h ; ZF=0 -> cambio de disco
POP DS
XPOPA ; *
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 lnea 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 deteccin del tipo de disco introducido; se
; pone en ON tambin si no se logra bajar la lnea de
; cambio de disco por si fuera un soporte raro y la BIOS
; s lo lograra -forzando as una deteccin posterior-).
detecta_cambio PROC
XPUSHA ; *
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].cambio,ON ; cambio de soporte?
MOV CS:[SI].cambio,OFF
JE hubo_cambio
CALL leer_lin_camb ; leer lnea de cambio de disco
JNZ hubo_cambio
XPOPA
CLC ; no hay cambio de disco
RET
hubo_cambio: CLC
CALL set_flag_STV ; CF = 0 -> supuesto no 2M
XPUSH <DS, ES> ; **
MOV BX,90h
ADD BL,DL
DDS
AND BYTE PTR [BX],255-16 ; densidad no determinada
XPUSH <CS, CS>
XPOP <DS, ES>
MOV unidad,DL
STC ; asegurar motor en marcha
CALL reset_drv
MOV cilindro,1
MOV cabezal,0
CALL seek_drv ; bajar lnea cambio de disco
DEC cilindro
CALL seek_drv
CLC
CALL motor_off_cnt ; cuenta normal detencin motor
CALL leer_lin_camb ; bajada lnea cambio disco?
JZ disco_dentro ; se pudo: hay disco dentro
MOV [SI].cambio,ON ; futura deteccin tipo disco
CLC ; NO indicar cambio de disco...
JMP fin_detecta ; ...para pasar control a BIOS
disco_dentro: PUSH DS
DDS
MOV BYTE PTR DS:[41h],6 ; error media changed
POP DS
IFNDEF SUPERBOOT
CMP AH,5 ; funcin de formateo?
JE fin_detecta_c ; no perder el tiempo
ENDIF
MOV buf_unidad,-1 ; invalidar buffer
MOV [SI].gap,20 ; GAP provisional
MOV CX,3 ; 3 intentos
intenta_io0: PUSH CX
CMP CX,2 ; CF=1 la 3 vez (a 0 si CX<>1)
CALL reset_drv
MOV [SI].vunidad0,0 ; empezar con 500 Kbit/seg.
intenta_io: MOV AL,0
MOV cilindro,AL
MOV cabezal,AL
MOV sector,1 ; sector de arranque
MOV seccion,AL
MOV secciones,1
MOV orden,F_READ
MOV DI,buffer
CALL direct_acceso
JNE otra_densidad ; es otra densidad de disco
POP CX
MOV BX,buffer
CALL set_info ; caractersticas nuevo soporte
CLC
JMP fin_detecta_c ; indicar cambio de disco
otra_densidad: MOV AL,[SI].vunidad0
INC AX ; prxima velocidad
CMP AL,3
JA otro_intento
MOV [SI].vunidad0,AL
JMP intenta_io ; probar otra velocidad
otro_intento: MOV [SI].vunidad0,0
POP CX
LOOP intenta_io0 ; reintento
fin_detecta_c: STC ; indicar cambio de disco
fin_detecta: XPOP <ES, DS> ; **
XPOPA ; *
RET
detecta_cambio ENDP
; ------------ Anotar la informacin 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
; tambin la variable BIOS de tipo de densidad (la BIOS
; no se da cuenta del cambio de disco y conviene ayudar).
set_info PROC
XPUSHA
CALL calc_chk
JC set_info_exit ; no es disco 2M
MOV [SI].chk,AL ; anotar checksum
MOV [SI].version_fmt,CL ; y versin del formato
MOV DL,unidad
STC
CALL set_flag_STV ; CF = 1 -> indicar disco 2M
MOV AL,ES:[BX+22] ; tamao de FAT
MOV [SI].tam_fat,AL
MOV CL,ES:[BX+65] ; CL a 0 si acceso multi-sector
MOV [SI].multi_io,CL
MOV AX,ES:[BX+66]
MOV [SI].vunidad,AX ; velocidad pista 0 / dems
MOV AL,ES:[BX+24]
MOV [SI].sectpista,AL ; sectores/pista
MOV DI,ES:[BX+72]
MOV AL,ES:[BX+DI+1] ; GAP de formateo
MOV AH,AL
AND CL,CL ; CL a 0 si acceso multi-sector
JZ gap_rw_ok ; GAP R/W para /F
ADD AH,190
MOV AL,11
MUL AH ; AX = (190+GAP)*11
SUB AX,2048+62
gap_rw_ok: SHR AL,1 ; GAP R/W para /M
MOV [SI].gap,AL
MOV CX,maxs
MOV DI,ES:[BX+74]
ADD DI,BX
LEA BX,[SI].tabla_tsect
genera_ts: MOV AL,ES:[DI]
MOV [BX],AL
INC BX
INC DI
LOOP genera_ts ; informacin estructura pistas
set_info_exit: MOV AL,[SI].vunidad0
IFDEF XT
MOV CL,6
SHL AL,CL
ELSE
XSHL AL,6 ; velocidad en bits 7:6
ENDIF
OR AL,00010111b ; establecido otro medio fsico
CMP [SI].tipo_drv,2
JA modo_ok ; es unidad de 3
AND AL,11111000b
OR AL,00000101b ; 1.2 en 1.2
TEST AL,01000000b
JZ modo_ok
XOR AL,00100001b ; 360 en 1.2 y seek * 2
modo_ok: PUSH DS
MOV BX,90h
ADD BL,unidad
DDS
AND BYTE PTR DS:[BX],8 ; respetar bit de 2.88M
OR DS:[BX],AL ; actualizar variable BIOS
POP DS ; con el tipo de densidad
XPOPA
RET
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 versin del formato de disco en CL.
calc_chk PROC
XPUSH <SI, DI>
LEA DI,[BX+3] ; DI=BX+3
LEA SI,id_sistema
MOV CX,6
REP CMPSB ; comparar identificacin
STC
JNE chk_ret ; el disco no es 2M
XOR AX,AX
MOV CL,ES:[BX+64] ; versin del formateador
CMP CL,6
JB chk_ok ; no usaba este checksum
MOV DI,ES:[BX+68]
chk_sum: DEC DI
ADD AL,ES:[BX+DI]
CMP DI,63
JA chk_sum
chk_ok: CLC
chk_ret: XPOP <DI, SI>
RET
calc_chk ENDP
; ------------ Determinar el tipo de error producido en el acceso.
set_err PROC
XPUSHA
JNC err_ret ; no hay error
CMP status,0 ; status ya asignado?
JNE err_retc ; no cambiarlo si es as
MOV AL,BYTE PTR fdc_result+1
AND AL,10110111b ; aislar condiciones de test
LEA BX,lista_errs
MOV CX,9
325 EL HARDWARE DE APOYO AL MICROPROCESADOR
busca_err: MOV AH,[BX] ; cdigo de error BIOS
SHL AL,1
JC err_ok ; es ese error
INC BX
LOOP busca_err ; buscar otro error
err_ok: OR status,AH
err_retc: STC ; condicin de error
err_ret: XPOPA
RET
set_err ENDP
; ------------ Actualizar variables de error de la BIOS.
set_bios_err PROC
PUSHF ; *
XPUSHA ; **
PUSH ES ; ***
DES
MOV DI,41h ; bytes de resultados del 765
LEA SI,status ; variable BIOS de status y 7
MOV CX,4 ; bytes: 4 palabras
REP MOVSW
POP ES ; ***
XPOPA ; **
POPF ; *
RET
set_bios_err ENDP
; ------------ Realizar lecturas, escrituras y verificaciones: rutina
; que sustituye el cdigo de la BIOS para poder soportar
; los formatos 2M. La operacin 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 fsico de la FAT2 (aunque en
; los discos de versin de formato anterior a la 7 se usa
; el sector de arranque verdadero -permitiendo escribirlo
; slo si es vlido-). En cualquier caso, si el nmero 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 cdigo SuperBOOT. Las coordenadas de la BIOS
; se traducen a las unidades del DOS por mayor comodidad.
control2m PROC
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv ; SI -> variables de la unidad
CMP [SI].chk,0
JE chk_valido ; checksum correcto en sector 0
MOV status,40h ; devolver Seek Error al DOS
JMP exit_2m_ctrl
chk_valido: PUSH AX ; ***
MOV AH,0
MOV numsect,AX ; n sectores
MOV AL,CH ; cilindro
SHL AL,1
MOV DL,DH
AND DH,01111111b
ADD AL,DH ; cabezal fsico
MUL [SI].sectpista
ADD AL,CL ; sector
ADC AH,0
DEC AX ; AX = n sector DOS
MOV sectini,AX ; 0FFFFh si sector 0 (error)
MOV DI,BX ; ES:DI -> direccin
POP BX ; ***
MOV BL,BH
MOV BH,0
MOV CL,[BX+OFFSET tab_ordenes-2]
MOV orden,CL
SHL DL,1
JC acceso_final ; cabezal >= 128: no emular
AND AX,AX ; comienza en sector 0?
JNZ io_emula ; no
CMP [SI].version_fmt,7
JB boot_real ; no soportado BOOT virtual
MOV AL,[SI].tam_fat ; AH = 0
INC AX
MOV CX,1 ; sector BOOT emulado en
CALL ejecuta_io ; el primer sector FAT2
JNE fin_ctrl
boot_fin_op: DEC numsect
INC sectini
MOV AX,sectini
JMP io_emula
boot_real: CMP orden,F_WRITE
JNE io_emula
MOV BX,DI ; BOOT de 2M 1.3 y anteriores
CALL calc_chk
JC si_skip ; no es de tipo 2M
AND AL,AL
JZ io_emula ; lo es y con checksum correcto
si_skip: ADD DI,512
JMP boot_fin_op ; impedir estropicio de BOOT
io_emula: MOV CL,[SI].tam_fat
MOV CH,0 ; CX = primer sector FAT2 - 1
CMP AX,CX
JA en_fat2? ; la operacin afecta a FAT2?
CALL calc_iop ; calcular sectores antes FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
CMP numsect,0
JE fin_ctrl ; fin de la transferencia
en_fat2?: MOV AX,sectini
MOV CL,[SI].tam_fat
MOV CH,0
SHL CX,1 ; CX = ltimo sector FAT2
CMP AX,CX
JA acceso_final ; la operacin es tras la FAT2
CALL calc_iop ; sectores hasta fin de FAT2
CMP orden,F_WRITE
JNE emula_fat1
IFDEF XT
XCHG CH,CL
SHL CH,1
ELSE
XSHL CX,9 ; CX = CX * 512
ENDIF
ADD DI,CX ; ES:DI actualizado
JMP acceso_final
emula_fat1: MOV DL,[SI].tam_fat
MOV DH,0
SUB AX,DX ; leer de FAT1 y no de la FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
acceso_final: CMP numsect,0
JE fin_ctrl ; fin de la transferencia
MOV AX,sectini
MOV CX,numsect
CALL ejecuta_io
fin_ctrl: CLC
CALL motor_off_cnt ; cuenta normal detencin motor
CALL set_bios_err ; actualizar variables BIOS
exit_2m_ctrl: XPOPA ; **
MOV AH,status
POP DS ; *
AND AH,AH
JZ st_ok ; resultado correcto (CF=0)
STC ; error
MOV AL,0 ; 0 sectores movidos
st_ok: RET
calc_iop: SUB CX,AX
INC CX ; CX sectores
CMP CX,numsect
JBE nsect_ok
MOV CX,numsect ; slo quedan CX
nsect_ok: SUB numsect,CX
ADD sectini,CX
RET
control2m ENDP
; ------------ A la entrada, AX indica el sector inicial (coordenadas
; del DOS) y CX el nmero de sectores a procesar.
; * Definiciones: Sector fsico es un sector del disco
; de 512, 1024 2048 bytes (nmeros de sector del 1 al N
; en la pista). Este sector fsico est dividido en
; secciones de 512 bytes, constando por tanto de 1, 2
; 4 secciones. Sector virtual es el nmero de sector
; del programa que llama a INT 13h, comprendido entre 1 y
; M. Esta estructura de N sectores por pista de distintos
; tamaos, se verifica en todo el disco con excepcin del
; cabezal y cilindro 0 (con un formato ms 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 fsico (1..J en la pista 0 y 1..N en
; las dems), deduciendo qu porcin de 512 bytes (o
; seccin) es afectada. Un sector virtual (512 bytes)
; simulado suele ser parte de un sector fsico de 2048
; bytes en muchos casos. Si dicho sector fsico ya haba
; sido ledo al buffer en anteriores accesos, se extrae
; la seccin necesaria. Si no, se carga del disco y se
; extrae dicho fragmento. El nmero de sectores virtuales
; que se solicitan (=secciones) permite realizar un bucle
; hasta completar la transferencia; el interleave 1:2 de
; los sectores fsicos en /M permite acceder sector a
; sector sin prdida de rendimiento. En el caso de la
; escritura, se estudia primero si hay varios sectores
; virtuales consecutivos que escribir, completando entre
; todos un sector fsico: en ese caso, se prepara el
; mismo y se escribe sin ms. En caso de que haya que
; modificar slo una nica seccin de un sector fsico,
; salvo si ste es de 512 bytes, no hay ms remedio que
; cargarlo al buffer (realizar una prelectura),
; actualizar la seccin correspondiente y volverlo a
; escribir.
; * En el formato /F se realiza una operacin multisector
; si es posible y sin emplear el buffer intermedio (si
; bien podra ser preciso emplearlo con la primera y
; ltima seccin); en los dos formatos de disco se hace
; la operacin 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 travs del buffer intermedio) y los que estn detrs.
ejecuta_io PROC
MOV BX,AX ; AX = sector DOS inicial
CMP AH,0FFh
JE no_cabe ; (acceso a sector BIOS 0)
MOV secciones,CL ; CX sectores (CL realmente)
DIV [SI].sectpista
INC AH ; numerado desde 1...
MOV sector,AH ; ...el resto es el sector
SHR AL,1
MOV cilindro,AL ; cilindro
RCL AL,1
AND AL,1
MOV cabezal,AL ; cabezal
MOV AL,sector
ADD AL,secciones
JC no_cabe ; sector+secciones > 255
DEC AX ; DEC AX = DEC AL
CMP AL,[SI].sectpista
JBE si_cabe
no_cabe: MOV status,4 ; sector no encontrado
JMP fin_io
si_cabe: MOV AL,AH ; sector en AL
CBW ; seccin 0 (AH = 0)
CALL pista0?
JZ s_xx ; sector fsico en pista/cara 0
LEA BX,[SI].tabla_tsect-1
DEC AX ; AH = 0
resta_secc: INC BX
INC AH
MOV CL,[BX]
SUB CL,2
MOV CH,1
SHL CH,CL
SUB AL,CH
JNC resta_secc ; en las dems pistas
ADD AL,CH
XCHG AH,AL
s_xx: MOV sector,AL ; sector lgico convertido a
MOV seccion,AH ; sector y seccin fsicas
direct_acceso: CALL motor_ok ; asegurar que est en marcha
MOV AH,0
MOV sector_fin,AH ; no acceder a ms de 1 sector
CALL pista0? ; (al menos de momento)
JNZ decide_multi ; no es pista 0
MOV AL,secciones
MOV secciones,AH ; las que restan (AH = 0)
JMP multi_proc
decide_multi: CMP [SI].multi_io,AH ; AH = 0
JNE io_pasos ; acceso sector a sector
CMP seccion,AH
JE multi_acc
CALL acceso_secc ; no acceso a inicio sector
JC fin_io
multi_acc: CMP secciones,AH ; AH = 0
326 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
JE fin_io
CALL num_secciones
MOV CL,AL
MOV AL,secciones ; AH = 0
DIV CL
AND AL,AL
JZ io_pasos ; no quedan sectores enteros
MOV secciones,AH ; las que restan
multi_proc: CALL acceso_multi ; de AL sectores
JC fin_io
io_pasos: CMP secciones,0
JE fin_io ; no restan secciones finales
CALL acceso_secc
JNC io_pasos
fin_io: CMP status,0 ; ZF = 1 -> operacin correcta
RET
acceso_secc: PUSH AX
CMP orden,F_WRITE ; acabar transferencia sector
JE escritura
CMP orden,F_VERIFY
JE verificacion
CALL leido? ; realizar lectura...
JNC ya_leido ; sector ya en el buffer
hay_que_leer: CALL acceso_sector ; efectuar E/S
JC acc_ret ; ha habido fallo
ya_leido: CALL trans_secc ; buffer -> memoria
JMP acc_ret
escritura: CMP seccion,0
JNE prelectura ; slo parte del sector cambia
CALL num_secciones
CMP secciones,AL
JAE escribir ; Todo el sector fsico cambia
prelectura: CALL leido? ; Leer el sector fsico para
JNC escribir ; cambiar slo una parte de l
MOV orden,F_READ ; de momento leer...
CALL acceso_sector ; efectuar E/S
MOV orden,F_WRITE ; ... restaurar orden original
JC acc_ret ; ha habido fallo
escribir: CALL trans_secc ; memoria -> buffer
CALL acceso_sector ; volcar buffer al disco
JMP acc_ret
verificacion: PUSH BX
MOV BL,seccion
CALL num_secciones
dec_sec_veri: DEC secciones
JZ verifica
INC BX
CMP BL,AL
JB dec_sec_veri
verifica: POP BX
CALL acceso_sector ; leer para forzar verificacin
acc_ret: PUSHF
INC sector ; preparado para otro sector
MOV seccion,0 ; desde su primera seccin
POPF
POP AX
RET
IFDEF SUPERBOOT ; SuperBOOT: vlido el cruce del DMA
acceso_multi: PUSH AX ; AL = sectores a transferir
AND AL,AL
JZ acc_mult_fin
MOV AH,sector
MOV sector_ini,AH
ADD AL,AH
DEC AX
MOV sector_fin,AL
INC AL
CALL acceso_sector ; sectores no problemticos
MOV sector,AL
acc_mult_fin: POP AX
RET
ELSE ; No es SuperBOOT: Evitar cruce frontera DMA
acceso_multi: PUSH AX ; AL = sectores a transferir
MOV BX,ES ; desde sector teniendo
XSHL BX,4 ; cuidado con el DMA
ADD BX,DI
NEG BX ; BX = bytes hasta frontera DMA
CALL num_secciones
MOV CH,AL ; AL secciones de 512 bytes
MOV CL,0
SHL CX,1 ; CX = bytes por sector
XCHG AX,BX ; BL = secciones por sector
XOR DX,DX
DIV CX
MOV CL,AL ; CL = sectores que caben
POP AX ; AL = sectores a transferir
CMP AL,CL
JA acc_mult2 ; no hay problemas con el DMA
acc_mult1: MOV CL,AL
acc_mult2: AND CL,CL
JZ acc_mult3 ; primer sector problemtico
MOV AH,sector
MOV sector_ini,AH
ADD AH,CL
DEC AH
MOV sector_fin,AH
INC AH
SUB AL,CL
CALL acceso_sector ; sectores no problemticos
MOV sector,AH
JC acc_mult_fin
acc_mult3: AND AL,AL ; ahora el sector problemtico
JZ acc_mult_fin
ADD secciones,BL ; compensar futuro decremento
CALL acceso_secc ; a travs del buffer auxiliar
JC acc_mult_fin
DEC AL
JMP 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) despus de la lectura o de la memoria al
; buffer (orden F_WRITE) antes de la escritura. En la
; verificacin (orden F_VERIFY) no se mueve nada porque
; esta subrutina no es invocada.
trans_secc PROC
XPUSH <AX, BX, CX, SI> ; *
MOV BL,seccion ; desde esta seccin
CALL num_secciones ; n secciones del sector
otra_secci: PUSH BX
IFDEF XT
MOV BH,BL
SHL BH,1
MOV BL,0
ELSE
XSHL BX,9 ; seccin * 512
ENDIF
ADD BX,buffer ; direccin
MOV SI,BX
MOV CX,256 ; tamao seccin (palabras)
CALL swap_reg ; intercambiar origen-destino?
REP MOVSW ; copiar 512 bytes
CALL swap_reg ; intercambiar origen-destino?
POP BX
DEC secciones ; una menos
JZ fin_secc
INC BX ; otra seccin del sector
CMP BL,AL ; sector agotado?
JB otra_secci ; an no
fin_secc: XPOP <SI, CX, BX, AX> ; *
RET
swap_reg: CMP CS:orden,F_WRITE
JE interc
CLC
RET
interc: XCHG SI,DI ; en escritura, invertir el
XPUSH <ES, DS> ; sentido de la operacin
XPOP <ES, DS>
RET
trans_secc ENDP
; ------------ Comprobar si el sector ya est en el buffer.
leido? PROC
PUSH AX
MOV AL,buf_unidad
CMP AL,unidad
JNE no_leido ; es en otra unidad
MOV AL,cilindro
MOV AH,cabezal
CMP AX,buf_cilcab
JNE no_leido ; es en otro cilindro/cabezal
MOV AL,buf_sector
CMP AL,sector
JNE no_leido ; es otro sector
POP AX
RET ; est en el buffer
no_leido: STC
POP AX
RET ; sector no ledo
leido? ENDP
; ------------ Leer o escribir sector(es). Se selecciona el tamao de
; sector correcto antes de llamar a sector_io. En esta
; rutina se actualiza la variable status en funcin de
; los posibles errores de acceso. Si sector_fin es
; distinto de 0 se accede a los sectores indicados, si es
; 0 se accede slo al sector sector a travs 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 lgicos consecutivos.
acceso_sector PROC
XPUSH <AX, BX>
CALL seek_drv ; posicionar el cabezal
JNC en_pista
CMP status,0 ; error ya determinado?
JNE acc_fin_err
OR status,40h ; no: pues seek error
acc_fin_err: STC
JMP acceso_fin
en_pista: CALL pista0?
MOV AL,2
JZ tam_acc_ok ; sectores 512 en cil./cab. 0
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV AL,[BX-1]
tam_acc_ok: MOV tsector,AL
CMP sector_fin,0 ; usar buffer intermedio?
JE acceso_buffer
CALL sector_io
MOV sector_fin,0 ; no acceder a ms de 1 sector
PUSHF ; **1
JMP acceso_rep ; en el futuro (por defecto)
acceso_buffer: XPUSH <ES, DI>
PUSH CS
POP ES
MOV DI,buffer ; acceso con buffer auxiliar
MOV AL,sector ; mismo sector inicial/final
MOV sector_ini,AL
MOV sector_fin,AL
CALL sector_io
MOV sector_fin,0
XPOP <DI, ES>
PUSHF ; **2
MOV AL,-1 ; invalidar contenido buffer
JC acceso_anota ; si hay error
CMP orden,F_VERIFY
JE acceso_rep ; nada ledo fsicamente
MOV AL,unidad
acceso_anota: MOV buf_unidad,AL
MOV AL,cilindro
MOV AH,cabezal
MOV buf_cilcab,AX
MOV AL,sector
MOV buf_sector,AL ; anotado el sector en buffer
acceso_rep: POPF ; ** mucho cuidado con la pila
CALL set_err ; ajustar variable status
acceso_fin: XPOP <BX, AX>
RET
acceso_sector ENDP
; ------------ Devolver el nmero de secciones del sector en curso.
num_secciones PROC
CALL pista0?
MOV AL,1
JZ num_secc_ok ; sectores 512 en cil./cab. 0
XPUSH <BX, CX>
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV CL,[BX-1]
SUB CL,2
327 EL HARDWARE DE APOYO AL MICROPROCESADOR
MOV AL,1
SHL AL,CL
XPOP <CX, BX>
num_secc_ok: RET ; resultado en AL
num_secciones ENDP
; ------------ Asegurar que el motor est en marcha.
motor_ok PROC
XPUSHA ; *
PUSH DS ; **
MOV BX,40h
PUSH BX
POP DS
MOV CH,255-18 ; CH = 255 - 1 segundo
CLI
MOV CL,CS:unidad
MOV AL,1
SHL AL,CL
TEST [BX-1],AL ; motor en marcha?
JZ arrancarlo ; arrancarlo
CMP [BX],CH ; Si encendido y acelerado...
JBE ok_motor ; ...seguir
arrancarlo: MOV AH,CL
MOV CL,4
SHL AH,CL ; unidad << 4
OR AL,AH
MOV [BX-1],AL ; nuevo estado motores
MOV BYTE PTR [BX],255 ; asegurar que no se pare
MOV DX,3F2h ; registro de salida digital
ADD CL,CS:unidad
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,CS:unidad ; seleccionar unidad
OR AL,00001100b ; modo DMA, no hacer reset
OUT DX,AL ; poner en marcha el motor
STI
MOV AX,90FDh
CLC
INT 15h ; permitir multitarea
JC ok_motor ; timeout
MOV AX,1000 ; 1 segundo aceleracin
CALL retardo ; esperar aceleracin disco
ok_motor: MOV [BX],CH ; cuenta mxima detencin motor
STI ; sin forzar futura aceleracin
POP DS ; **
XPOPA ; *
RET
motor_ok ENDP
; ------------ Establecer modalidad de operacin del controlador
; y poner el motor en marcha. Si CF=1 se le da tiempo
; adems a la unidad para que acelere.
reset_drv PROC
XPUSHA
CALL motor_off_cnt ; cuenta detencin motor
MOV CL,unidad
MOV AL,CL
XSHL AL,4 ; unidad seleccionada
MOV AH,1 ; bit de motor
SHL AH,CL ; colocar dicho bit
OR AL,AH
PUSH DS ; *
DDS
CLI
MOV DS:[3Fh],AL
AND BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
POP DS ; *
XSHL AL,4 ; bits motor en nibble alto
OR AL,CL ; seleccionar unidad
OR AL,00001000b ; interrupciones+DMA y reset
MOV DX,3F2h ; registro de salida digital
OUT DX,AL ; seal de reset
IFDEF XT
MOV CX,50
respiro: LOOP respiro
ELSE
CALL fdc_respiro ; tiempo reconocer reset en 486
ENDIF
OR AL,00000100b
OUT DX,AL ; fin de seal de reset
CALL espera_int ; rehabilitar interrupciones
MOV AL,8
CALL fdc_write ; comando leer estado int...
CALL fdc_read
CALL fdc_read
CALL envia_specify ; comando specify adecuado
XPOPA
RET
reset_drv ENDP
; ------------ Enviar comando specify a la controladora. El step-rate
; se selecciona segn la densidad, para evitar un sonido
; extrao al posicionar o recalibrar el cabezal.
envia_specify PROC
PUSH AX
PUSH DS
DDS
MOV AH,DS:[8Bh]
POP DS
MOV AL,3 ; comando specify
CALL fdc_write
MOV AL,0BFh ; step rate para 500 kbps
AND AH,11000000b
JZ spec1_ok
MOV AL,0AFh ; step rate para 1 Mbps
CMP AH,11000000b
JE spec1_ok
MOV AL,0DFh ; step rate para 250/300 Kbps
spec1_ok: CALL fdc_write
MOV AL,2
CALL fdc_write ; head load y modo DMA
POP AX
RET
envia_specify ENDP
; ------------ Recargar cuenta para la detencin 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 PROC
XPUSHA
PUSH DS
MOV AL,0FFh ; valor mximo
JC motor_off_ok
IFDEF XT
XOR BX,BX
MOV DS,BX
ELSE
PUSH 0
POP DS
ENDIF
LDS BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
MOV AL,[BX+2] ; byte 2 tabla base disco
motor_off_ok: DDS
MOV BYTE PTR DS:[40h],AL ; cuenta parada motor
POP DS
XPOPA
RET
motor_off_cnt ENDP
; ------------ Llevar el cabezal a la pista indicada, recalibrando si
; hubo un reset (se invoc la funcin 0 de la INT 13h o
; se ejecut reset_drv) antes de esta operacin. Primero
; se selecciona la velocidad de transferencia y se borra
; el resultado de cualquier operacin anterior, para que
; todo quede listo para el prximo acceso a disco.
seek_drv PROC
XPUSHA
CALL set_rate ; velocidad / borrar resultados
CALL envia_specify ; comando specify adecuado
MOV AH,1
MOV CL,unidad
SHL AH,CL ; AH = 1 (A:) 2 (B:)
PUSH DS
DDS
TEST AH,DS:[3Eh]
POP DS
JNZ do_seek ; la unidad ya fue recalibrada
CALL recalibrar
JC fallo_seek ; fallo al recalibrar
do_seek: MOV BX,94h
ADD BL,unidad
MOV AL,cilindro
PUSH DS ; *
DDS
OR DS:[3Eh],AH ; unidad ya recalibrada
MOV AH,DS:[41h] ; cdigo de error previo
CMP AL,[BX]
MOV [BX],AL
POP DS ; *
JNE hacer_seek ; seek necesario
CMP AH,40h ; error de seek previo?
JNE seek_ok ; no, evitar seek innecesario
hacer_seek: MOV AL,0Fh
CALL fdc_write ; comando seek
JC fallo_seek
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
CALL espera_int ; esperar interrupcin
JC fallo_seek
MOV AL,8
CALL fdc_write ; comando leer estado int...
JC fallo_seek
CALL fdc_read ; leer registro de estado 0
JC fallo_seek
MOV AH,AL
CALL fdc_read ; leer cilindro actual
TEST AH,11000000b ; comprobar ST0
JNZ fallo_seek
MOV AL,15 ; estabilizacin para escritura
CMP orden,F_WRITE
JE rseek_ok
MOV AL,1 ; estabilizacin para lectura
rseek_ok: CBW ; AH = 0
CALL retardo ; esperar asentamiento cabezal
seek_ok: XPOPA
CLC ; retornar con xito
RET
fallo_seek: XPOPA
STC ; retornar indicando fallo
RET
seek_drv ENDP
; ------------ Establecer velocidad de transferencia correcta si an
; no ha sido seleccionada y borrar el resultado de otra
; operacin previa.
set_rate PROC
XPUSHA
CALL pista0?
MOV AX,[SI].vunidad ; velocidad pista 0 / dems
JZ vel_ok
MOV AL,AH
vel_ok: PUSH DS ; *
DDS
MOV AH,DS:[8Bh]
IFDEF XT
MOV CL,6
SHR AH,CL
ELSE
SHR AH,6 ; aislar bits de velocidad
ENDIF
CMP AL,AH
JE vel_set ; velocidad ya seleccionada
MOV DX,3F7h
OUT DX,AL ; seleccionarla
XSHL AL,6
AND BYTE PTR DS:[8Bh],00111111b
OR DS:[8Bh],AL
vel_set: POP DS ; *
LEA DI,status
MOV CX,8
borra_status: MOV [DI],CH ; borrar informacin de estado
INC DI
LOOP borra_status
XPOPA
RET
set_rate ENDP
; ------------ Recalibrar la unidad (si hay error se intenta otra vez
; para el caso de que deba moverse ms de 77 pistas).
recalibrar PROC
XPUSHA
MOV BX,94h
ADD BL,unidad
PUSH DS ; *
DDS
328 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
MOV [BX],BH ; pista actual = 0
POP DS ; *
MOV CX,2 ; dos veces como mucho
recalibra: MOV AL,7
CALL fdc_write ; comando de recalibrado
JC fallo_recal
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
JC fallo_recal
CALL espera_int ; esperar interrupcin
JC fallo_recal
MOV AL,8
CALL fdc_write ; comando leer estado int...
JC fallo_recal
CALL fdc_read ; leer registro de estado 0
JC fallo_recal
MOV AH,AL
CALL fdc_read ; leer cilindro actual
XOR AH,00100000b ; bajar bit de seek end
TEST AH,11110000b ; comprobar resultado y ST0
JNZ fallo_recal ; sin seek end o TRK0
MOV AX,1 ; pausa de 1 ms
CALL retardo
JMP recal_ret
fallo_recal: LOOP recalibra ; reintentar comando
STC ; condicin de fallo
recal_ret: XPOPA
RET
recalibrar ENDP
; ------------ Cargar o escribir sector(es) del disco en ES:DI,
; actualizando la direccin en ES:DI pero sin alterar
; ningn 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 verificacin (F_VERIFY) se programa el DMA
; para que no realice transferencia fsica (convenio de
; las BIOS con fecha 15/11/85 y posterior).
sector_io PROC
XPUSH <AX, BX, CX, DX>
MOV CL,tsector
MOV CH,0
STC
RCL CH,CL
MOV CL,0 ; n de bytes por sector
MOV AL,sector_fin
SUB AL,sector_ini
INC AX
CBW ; AX sectores (AH = 0)
MUL CX
MOV DX,AX ; bytes totales
MOV CX,AX
DEC CX ; bytes totales - 1
MOV AX,ES
CALL calc_dir_DMA ; AX:DI -> base BX y pgina AH
IFDEF SUPERBOOT
JC sector_io_ko ; chequear cruce frontera DMA
ENDIF
MOV AL,orden ; modo DMA necesario
CALL prepara_DMA
CMP AL,F_WRITE
MOV AL,11000101b ; comando de escritura del FDC
JE orden_io_ok
MOV AL,11100110b ; comando leer (verif.) del FDC
orden_io_ok: CALL fdc_write ; comando leer/escribir del FDC
JC sector_io_ko
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
MOV AL,cabezal
CALL fdc_write ; enviar cabezal
MOV AL,sector_ini
CALL fdc_write ; enviar n sector
MOV AL,tsector
CALL fdc_write ; longitud sector
MOV AL,sector_fin
CALL fdc_write ; ltimo sector
MOV AL,[SI].gap
CALL fdc_write ; GAP de lectura/escritura
MOV AL,128
CALL fdc_write ; tamao sector si longitud=0
CALL espera_int
PUSHF ; *
LEA BX,fdc_result
MOV CX,7
sect_io_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP sect_io_res
POPF ; *
JC sector_io_ko
TEST fdc_result,11000000b
JNZ sector_io_ko
ADD DI,DX ; actualizar direccin
CLC ; Ok
JMP sector_io_fin
sector_io_ko: STC ; indicar fallo
sector_io_fin: XPOP <DX, CX, BX, AX>
RET
sector_io ENDP
; ------------ Devolver en AH la pgina de DMA y en BX la base. A la
; entrada, AX:DI -> direccin de memoria y CX = bytes-1.
; Se supone que el buffer no cruza una frontera de DMA,
; aunque el cdigo SuperBOOT devuelve error en ese caso.
calc_dir_DMA PROC
PUSH DX
MOV BX,16
MUL BX
ADD AX,DI
ADC DX,0 ; DX:AX = direccin 20 bits
MOV BX,AX ; base en BX
MOV AH,DL ; pgina
IFDEF SUPERBOOT
MOV DX,CX ; comprobar cruce en SuperBOOT
ADD DX,BX
JNC dir_DMA_ok
MOV status,9 ; error de frontera de DMA
ENDIF
dir_DMA_ok: POP DX
RET
calc_dir_DMA ENDP
; ------------ Crear tabla con informacin para formatear. En ES:BX
; est el futuro sector de arranque del disquete.
IFNDEF SUPERBOOT ; en SuperBOOT no se formatea
genera_info PROC
XPUSHA
MOV buf_unidad,-1 ; invalidar contenido buffer
MOV SI,buffer
MOV DI,BX
CALL pista0?
JNZ no_cilcab0 ; no es cilindro/cabezal 0
ADD DI,ES:[BX+70] ; DI -> datos pista 0
MOV CL,ES:[DI]
MOV CH,0 ; CX sectores en pista 0
INC DI
MOV AL,ES:[DI] ; GAP para pista 0
MOV AH,0 ; byte de relleno
INC DI
MOV BYTE PTR [SI],2 ; tamao de sector
MOV BYTE PTR [SI+1],CL ; nmero de sectores
MOV [SI+2],AX ; GAP / byte de relleno
genera_0: ADD SI,4
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV AL,ES:[DI]
MOV AH,2 ; LOG2 (tamao)-7
INC DI
MOV [SI+2],AX ; n de sector / tamao
LOOP genera_0
XPOPA
RET
no_cilcab0: ADD DI,ES:[BX+72]
CMP BYTE PTR ES:[BX+65],1
JE info_stv
MOV DL,ES:[DI+2] ; tamao /F
MOV DH,ES:[DI] ; n sectores
MOV [SI],DX
XCHG DH,DL ; tamao en DH
MOV CL,DL
MOV CH,0 ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,0 ; byte relleno /F
MOV [SI+2],AX ; GAP / byte de relleno
PUSH DX
MOV AX,ES:[DI+3]
PUSH AX
ADD AL,AH
MUL cilindro
MOV DX,AX
POP AX
MUL cabezal
ADD AX,DX
XOR DX,DX
MOV BL,ES:[DI]
MOV BH,0
DIV BX ; DL = mdulo
SUB DL,ES:[DI]
NEG DL
MOV AL,DL
POP DX ; restaurar tamao en DH
MOV DL,AL ; primer sector de la pista - 1
MOV BL,ES:[DI] ; n sectores en la pista
genera_pn: ADD SI,4
INC DX
CMP DL,BL
JBE ns_ok
MOV DL,1 ; empezar desde el 1
ns_ok: MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV [SI+2],DX ; n sector / LOG2 (tamao)-7
LOOP genera_pn
XPOPA
RET
info_stv: MOV CH,ES:[DI] ; n sectores
MOV CL,0 ; CL:CH sectores
MOV [SI],CX ; tamao (CL=0) y nmero
XCHG CH,CL ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,4Eh ; byte de relleno /M
MOV [SI+2],AX ; GAP / byte de relleno
MOV DL,128
genera_otro: ADD SI,4
INC DX
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
XPUSH <CX, DI> ; *
MOV CL,ES:[DI+2] ; CH est a 0
busca_num: ADD DI,3
CMP DL,ES:[DI]
MOV AX,ES:[DI+1] ; nmero de sector / tamao
JE hallado ; es sector a cambiar nmero
LOOP busca_num
MOV AL,DL ; no cambiar nmero
MOV AH,0 ; e indicar tamao 128
hallado: XPOP <DI, CX> ; *
MOV [SI+2],AX ; n sector / LOG2 (tamao)-7
LOOP genera_otro
XPOPA
RET
genera_info ENDP
; ------------ Formatear una pista.
formatea_pista PROC
XPUSHA
MOV BX,buffer
MOV DI,BX
MOV CL,[BX+1]
MOV CH,0 ; CX sectores
XSHL CX,2
DEC CX ; n de bytes - 1
MOV AX,DS
CALL calc_dir_DMA ; AX:DI -> base BX y pgina AH
MOV AL,4Ah ; modo DMA para escribir
ADD BX,4 ; saltar primeros 4 bytes
CALL prepara_DMA
MOV BX,buffer
MOV AL,F_FORMAT
CALL fdc_write
JC fallo_fmt
MOV AL,cabezal
XSHL AL,2
329 EL HARDWARE DE APOYO AL MICROPROCESADOR
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
JC fallo_fmt
MOV CX,4
format_cmd: MOV AL,[BX]
CALL fdc_write
INC BX
LOOP format_cmd
CALL espera_int
fallo_fmt: PUSHF
LEA BX,fdc_result
MOV CX,7
format_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP format_res
POPF
JC fallo_format
TEST fdc_result,11000000b
JZ format_ret
fallo_format: STC ; fallo
format_ret: XPOPA
RET
formatea_pista ENDP
ENDIF
; ------------ Esperar interrupcin de disquete durante casi 2
; segundos antes de considerar que ha sido un fracaso.
IFNDEF XT
espera_int PROC
STI
XPUSHA
PUSH DS
DDS
MOV AX,9001h
CLC
INT 15h ; permitir multitarea
MOV DX,0280h
MOV BX,3Eh
JC timeout_int
esp_int_1s: XOR CX,CX
esp_int: TEST [BX],DL ; lleg la interrupcin?
JNZ fin_espera
PMICRO
LOOP esp_int ; esperar durante casi 1 seg.
DEC DH
JNZ esp_int_1s
timeout_int: OR CS:status,DL ; timeout
STC
fin_espera: PUSHF
AND BYTE PTR [BX],7Fh ; para la prxima vez
POPF
POP DS
XPOPA
RET
espera_int ENDP
ELSE ; Si es XT...
espera_int PROC
STI
XPUSHA
XPUSH <DS, 40h>
POP DS
MOV AH,0FFh
esperar_int: CMP AL,DS:[6Ch]
JE mira_int
MOV AL,DS:[6Ch]
INC AH
CMP AH,37 ; ms de 2 segundos?
JB mira_int
timeout_int: OR CS:status,80h ; timeout
STC
JMP fin_espera
mira_int: TEST BYTE PTR DS:[3Eh],80h
JZ esperar_int
AND BYTE PTR DS:[3Eh],7Fh ; CF=0
fin_espera: POP DS
XPOPA
RET
espera_int ENDP
ENDIF
; ------------ Preparar DMA para E/S. A la entrada, BX = direccin de
; base, AH = registro de pgina y CX = n bytes - 1.
prepara_DMA PROC
PUSH AX
CLI
OUT 0Bh,AL ; registro de modo del DMA
MOV AL,0
DELAY
OUT 0Ch,AL ; clear first/last flip-flop
MOV AL,BL
DELAY
OUT 4,AL
MOV AL,BH
DELAY
OUT 4,AL ; enviada direccin base
DELAY
MOV AL,AH
OUT 81h,AL ; registro de pgina del DMA
MOV AL,CL
DELAY
OUT 5,AL
MOV AL,CH
DELAY
OUT 5,AL ; enviada cuenta de bytes
STI
MOV AL,2
DELAY
OUT 0Ah,AL ; habilitar canal 2 de DMA
POP AX
RET
prepara_DMA ENDP
; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
; la operacin fracas (el FDC no estaba listo) y
; se indica la condicin de timeout en status.
IFNDEF XT
fdc_read PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,3F4h ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_rd: DELAY
IN AL,DX
AND AL,11000000b
CMP AL,11000000b ; dato listo?
JE fdc_rd_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_rd ; reintentarlo durante 15,09 s
MOV AH,AL
LOOP espera_rd
XPOP <AX, DX, CX>
OR status,80h ; timeout
MOV AL,0
STC ; fallo
RET
fdc_rd_ok: POP AX
INC DX ; apuntar al registro de datos
DELAY
IN AL,DX ; leer byte del FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_read ENDP
ELSE
fdc_read PROC
XPUSH <CX, DX>
MOV DX,3F4h ; registro de estado del FDC
XOR CX,CX ; evitar cuelgue total si falla
espera_rd: IN AL,DX ; leer registro de estado
TEST AL,80h ; bit 7 inactivo?
LOOPZ espera_rd ; as es: el FDC est ocupado
JCXZ fdc_rd_nok
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte del FDC
CLC
XPOP <DX, CX>
RET
fdc_rd_nok: OR status,80h ; timeout
STC
XPOP <DX, CX>
RET
fdc_read ENDP
ENDIF
; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
; la operacin fracas (el FDC no estaba listo) y
; se indica la condicin de timeout en status.
IFNDEF XT
fdc_write PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,3F4h ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_wr: DELAY
IN AL,DX
TEST AL,80h ; listo para E/S?
JNZ fdc_wr_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_wr ; reintentarlo durante 15,09 s
MOV AH,AL
LOOP espera_wr
XPOP <AX, DX, CX>
OR status,80h ; timeout
STC ; fallo
RET
fdc_wr_ok: INC DX ; apuntar al registro de datos
POP AX
DELAY
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_write ENDP
ELSE
fdc_write PROC
XPUSH <AX, CX, DX>
MOV DX,3F4h ; registro de estado del FDC
XCHG AH,AL ; preservar AL en AH
XOR CX,CX ; evitar cuelgue total si falla
espera_wr: IN AL,DX ; leer registro de estado
TEST AL,80h ; bit 7 inactivo?
LOOPZ espera_wr ; as es: el FDC est ocupado
JCXZ fdc_wr_nok
XCHG AH,AL ; recuperar el dato de AL
INC DX ; apuntar al registro de datos
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX, AX>
CLC
RET
fdc_wr_nok: OR status,80h ; timeout
XPOP <DX, CX, AX>
STC
RET
fdc_write ENDP
ENDIF
; ------------ Retardo de 60 s para dar tiempo al FDC en 486 rpidos.
IFNDEF XT
fdc_respiro PROC
XPUSH <AX, CX>
MOV CX,4
fdc_ret: PMICRO
LOOP fdc_ret
XPOP <CX, AX>
RET
fdc_respiro ENDP
ENDIF
; ------------ Esperar exactamente AX milisegundos.
330 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
IFNDEF XT
retardo PROC
PUSHF
XPUSHA
MOV DX,16970 ; 16970 = 1193180/18*256/1000
MUL DX
MOV CL,AH ; dividir DX:AX entre 256 y
MOV CH,DL ; dejar el resultado en DX:CX
MOV DL,DH
MOV DH,0 ; DX:CX 15,09 s-avos
retardando: PMICRO
LOOP retardando
AND DX,DX
JZ retardado
DEC DX
JMP retardando
retardado: XPOPA
POPF
RET
retardo ENDP
ELSE
retardo PROC
PUSHF
XPUSH <AX, BX, CX, DX>
retarda_mas: CMP AX,54 ; como mximo 54 ms cada vez
JBE retarda_fin
PUSH AX
MOV AX,54
CALL rt_ax
POP AX
SUB AX,54
JMP retarda_mas
retarda_fin: CALL rt_ax
XPOP <DX, CX, BX, AX>
POPF
RET
rt_ax: MOV DX,1000 ; retardo de hasta 54 ms
MUL DX
MUL CS:tbase
MOV CX,54925
DIV CX ; AX = contador iteraciones
MOV CX,AX
EVEN ; forzar alineamiento
retarda: DEC CX
JMP SHORT $+2
JNZ retarda
RET
retardo ENDP
ENDIF
IFDEF SUPERBOOT
; ------------ Esta subrutina sustituye a la macro PMICRO en el
; cdigo SuperBOOT por razones de espacio.
pmicro_iter: DELAY ; retardo de aprox. 15,09 s
IN AL,61h ; (exactamente 18/1193180 sg.)
AND AL,10h ; La rutina se puede ejecutar
CMP AL,AH ; repetitivamente (se apoya en
JE pmicro_iter ; AX) para hacer retardos a
MOV AH,AL ; travs de la temporizacin
RET ; del refresco de la memoria
; ------------ Cdigo invocado durante el SuperBOOT desde 2MFBOTHD.ASM
; A la entrada: CS=ES, SS=0 y AX = tipo unidades.
initcode: PUSH DS
PUSH SS
POP DS
MOV ES:[info_A.tipo_drv],AL ; anotar tipo de A:
MOV ES:[info_B.tipo_drv],AH ; anotar tipo de B:
LEA DI,ant_int13
MOV SI,13h*4 ; vector de INT 13h
CLD
CLI
MOVSW
MOVSW ; anotada direccin INT 13h
MOV WORD PTR [SI-4],OFFSET ges_int13
MOV [SI-2],ES
STI ; desviada INT 13h
POP DS
RETF ; volver a 2MFBOTHD
DB 4 DUP(0) ; esto ocupa 2560 bytes exactos
ant_int13 LABEL DWORD ; vector de la INT 13h previa
ant_int13_off DW initcode
ant_int13_seg DW 0AA55h ; significa "2MFBOOT correcto"
ENDIF
; --- Ubicacin del sector de hasta 2048 bytes.
EVEN
buffer_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 las
funciones de la BIOS implementadas por 2M cuando est residente. El siguiente programa de ejemplo
demuestra lo sencilla que es esta tarea. El nico problema importante que se present durante su desarrollo
fueron los conflictos que generaba WINDOWS al intentar formatear un disco en el formato de mxima
capacidad (opcin /M): por algn motivo, era imposible crear este tipo de pistas al producirse un extrao error
en la funcin de formatear. Este problema ya se haba presentado en versiones anteriores de 2M, que tambin
formateaban los discos. La solucin adoptada es, sencillamente, invocar la INT 13h mediante un CALL a la
direccin del vector de interrupcin. De este modo no se ejecuta el cdigo WINDOWS responsable de la
incompatibilidad, que entraba en marcha al llamar a la INT 13h en modo protegido. Tenga en cuenta el lector
que una inocente instruccin INT es mucho ms que eso bajo WINDOWS o con un controlador de memoria
instalado. Este fragmento de cdigo de 2MF ha sido codificado en ensamblador, entre otros motivos porque
antes de llamar con CALL a una interrupcin hay que apilar los flags y eso resulta difcil en C. Durante las
restantes fases del formateo (lectura para verificar y la escritura previa en los formatos de mxima capacidad)
se utilizan las funciones estndar de la BIOS va INT 13h. Aunque WINDOWS no estorbara, tampoco
hubiera sido posible llamar con la funcin de formateo BIOS del compilador, ya que los parmetros cambian
ligeramente, si bien se podra haber hecho con cdigo C.
El programa admite varios parmetros para controlar el formateo. Por defecto realiza el formateo
normal, ms fiable (o indicando la opcin /F). Para seleccionar el formateo de mxima capacidad hay que
indicar /M. Desde 2MF 3.0, el programa es capaz de detectar la densidad en discos de 3 vrgenes (con la
excepcin de las unidades que permiten formatear en alta densidad los discos de doble) y lo intenta en los
de 5 (slo funciona si ya tenan algn tipo de formato previo). En cualquier caso, siempre se puede indicar
la opcin /HD, /DD /ED para seleccionar la densidad necesaria y evitar la pequea prdida de tiempo en
detectarla.
El nmero de pistas, por defecto 82, puede elegirse con /T, ya que muchas unidades soportan 84
pistas o ms; de todas maneras, 2MF 3.0 no permite formatear ms pistas de las que admita la unidad, al
331 EL HARDWARE DE APOYO AL MICROPROCESADOR
contrario que las versiones anteriores. Los ficheros permitidos en el directorio raz se indican con /R. El
parmetro /S evita la produccin de sonido. Con /N se evita la verificacin, /K y /J eliminan la pausa inicial
y final, respectivamente; /Z anula el parpadeo del led mientras se cambia el disco y /L y /V permiten poner
etiquetas de volumen (serializadas en el ltimo caso) al disco destino.
Finalmente, hay varios parmetros no documentados oficialmente que no deberan ser alterados, salvo
quiz en algn ordenador muy concreto y por parte de usuarios muy especializados, que permiten elegir los
factores de desplazamiento en la numeracin de los sectores al conmutar de cabezal (/X) y de cilindro (/Y)
en el formato normal (/F); en el formato de mxima capacidad (/M) no tienen efecto. El parmetro /G permite
indicar el GAP o separacin 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 128
bytes). Con /D0 se formatea en 3-DD con 820/902K (en lugar de 984/1066K), algo necesario en las
controladoras de algunos porttiles que no soportan la densidad de 300 Kbps (propia exclusivamente de las
unidades de 5); si bien no es preciso emplearlo ya que por defecto el programa formatea de esta manera
en esas unidades al autodetectar la densidad del disco destino. /D1 formatea 1148K en lugar de 1066K, pero
el disco resultante es poco seguro y extremadamente lento. Por ltimo, la opcin /W hace que se marquen
slo 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:37
5-HD 1:13 1:24 1:52 1:29 2:38
3-DD 1:24 1:38 1:46 1:39 2:51
3-HD 1:34 1:42 2:17 1:47 3:22
(*) Usando el formato estndar del DOS (360-720-1.2-1.44) y los parmetros /X e /Y adecuados.
(**) Formatos de mxima capacidad soportados (820-1.48-1.72) y los parmetros /X e /Y adecuados.
La parte ms compleja del programa es la funcin CrearSector0(), que como su propio nombre
indica se encarga de crear el sector de arranque del futuro disquete. En un programa de copia de discos esta
funcin no sera necesaria, ya que al leer el disquete origen tendramos ya el sector de arranque del futuro
disquete destino y, por tanto, podramos formatearle directamente (recordar que la funcin de formateo de
discos 2M slo necesita como parmetro el sector de arranque del futuro disco). Sin embargo, aqu nos vemos
obligados a crear dicho sector, lo cual es una tarea un tanto engorrosa, teniendo en cuenta la variedad de
formatos. Una tabla ms o menos complicada, de 5 dimensiones, contiene toda la informacin necesaria para
la tarea. Adems, el cdigo ejecutable del sector de arranque resultaba difcil incluirlo dentro del listado C
y finalmente se opt por crear un fichero proyecto e incluir en l 2MF.C y 2MFKIT.ASM (este ltimo integra
los sectores de arranque para alta y doble densidad -con y sin soporte SuperBOOT, respectivamente- as como
el cdigo SuperBOOT y las rutinas de utilidad).
Durante el proceso de formateo, en FormatearDisco() se est pendiente de una posible pulsacin de
la tecla ESC. Se controlan los posibles errores fatales, tales como unidad protegida de escritura o no
preparada, que suponen el fin del proceso de formateo, pero se toleran los dems errores -si no afectan a las
reas del sistema del disco- marcando los clusters afectados como defectuosos si al tercer intento de formateo
siguen fallando. Al final del formateo, en InformeDisco() se imprimen las caractersticas del nuevo disquete
pero sin emplear funciones del DOS. Realmente, el DOS ya se ha dado cuenta del cambio de disco e
informara correctamente, pero de esta manera se asegura a ultranza que la informacin es correcta.
La funcin TipoDrive() devuelve el tipo de la disquetera que se le indique consultando esta
informacin a travs de la BIOS. La funcin InicializaDisco() escribe, al final del formateo, el sector de
arranque fsico, el virtual, el cdigo SuperBOOT (si el disco es de alta densidad) y la FAT; de esta ltima
slo la primera copia, ya que 2M emular la segunda.
Las funciones de sonido crean efectos especiales bastante atractivos gracias al empleo de retardos de
medio milisegundo con la funcin PicoRetardo(); este retardo es idntico en todas las mquinas, con total
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 no
se realiza retardo alguno y, curiosamente, el sonido suena igual que en los AT (en mquinas de 8 MHz).
La funcin EsperarCambioDisco() espera a que se retire el disquete de la unidad y se introduzca
uno nuevo, o bien a que se pulse una tecla (considerando el caso de ESC, para abortar el formateo). Esto
permite formatear varios disquetes introducindolos unos tras otros en la unidad sin necesidad de pulsar
teclas. En WINDOWS se puede abrir una ventana para formatear disquetes 2M y, dejndola en la sombra,
cada vez que se oiga el sonido de fin de formateo, sin abandonar lo que se tenga en ese momento entre
manos, se puede sacar el disco e introducir otro para que el proceso contine automticamente sin tener que
activar la ventana para pulsar una tecla. El sonido de final de formateo permite distinguir entre un formateo
correcto y otro con errores (se considera correcto aunque haya sectores defectuosos, lo de errores va por lo
de disco protegido contra escritura, etc.). Las rutinas de bajo nivel que acceden a la controladora de disco
en 2MF lo hacen, exclusivamente, para conseguir el efecto intermitente en el led de la unidad mientras se
cambia de disco (y para reducir el ruido que emite la funcin de deteccin de nuevo disco de la BIOS).
Para fomentar que los usuarios enven la postal al autor, el programa tiene un contador de discos
formateados aadido cuando formatea el primer disco por el mtodo de alargar el tamao del fichero EXE.
Al cabo de 100 discos, imprime un mensaje recordando al usuario su deber. Naturalmente, si 2MF se ejecuta
desde una unidad protegida contra escritura, no ser posible actualizar el contador...
Finalmente, la funcin HablaSp() comprueba el pas en que se ejecuta el programa para inicializar
una variable global que indique si los mensajes han de ser imprimidos en castellano o en ingls.
/* \
2MF.C 3.0 - UTILIDAD DE FORMATEO DE DISQUETES 2M
(C) 1994 Ciriaco Garca de Celis.
- Para cualquier Turbo C o Borland C en modelo de memoria LARGE.
- Este programa se compila abriendo un proyecto e introduciendo
en l 2MF.C y 2MFKIT.OBJ
- Importante: no activar ciertas optimizaciones que no lo estn
por defecto (como la de alineamiento a palabra o la de salto).
- NOTA: Las funciones de bajo nivel que acceden directamente a
la controladora de disquetes no son indispensables, tan
slo se emplean para producir menos ruido al detectar
la introduccin de un nuevo disquete en la unidad.
Este programa detecta adems la presencia de una posible
utilidad de intercambio de unidades A:-B: llamada FDSWAP
para que en caso de estar activado dicho intercambio sea
posible acceder a la unidad fsica 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 /* mximo nmero 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 tamao 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 { /* parmetros en lnea 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-espaol 0-ingls */
unsigned long far *cbios=MK_FP(0x40, 0x6C); /* reloj del sistema */
unsigned char far *irq6=MK_FP(0x40, 0x3E); /* flag BIOS de IRQ6 */
333 EL 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 pas */
ProcesarParametros (argc, argv, &cmd);
if (!Hay2m())
if (!Hay2mBoot()) {
if (sp)
printf(" 2M 2MX 3.0 no est instalado, imposible
formatear.\n");
else
printf(" 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");
else
printf(" 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");
else
printf(" Press any key to format on");
printf(" %c:", cmd.Unidad+A);
salir=getch()==27;
}
else
salir=0;
/* si no se indica densidad detectarla */
detectar = (cmd.HD==-1);
/* formateo de mltiples 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");
else
printf("\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 crtico */
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; /* parmetros 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 parmetros deben separarse por
espacios.\n");
else
printf(" Error: Parameters must be separated by blank
spaces.\n");
exit (2);
}
if (TipoDrive(cmd->Unidad)==0) {
if (sp)
printf(" La unidad fsica 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: Nmero 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 errneo.\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 DISQUETES
PARA 2M\n"
" (C) 1994 Ciriaco Garca de Celis - Grupo Universitario de
Informtica\n"
" C/Renedo, 2, 4-C; 47005 Valladolid (Espaa) - ciri@gui.uva.es
- 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/o
velocidad de la\n"
" normal. Para que estos nuevos disquetes funcionen debe estar
instalado 2M en\n"
" memoria. Alternativamente, si son de alta densidad se pueden
dejar dentro de\n"
" la unidad A: y reinicializar el ordenador, que botar pese
a todo del disco\n"
" duro y podr acceder a los disquetes 2M sin problemas en
lectura/escritura.\n\n"
" /HD Formateo en alta densidad (por defecto si 2MF no detecta
la densidad).\n"
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 3772K
indicando /M).\n"
" /F Disquetes rpidos y seguros -por defecto- (5:820-1476K,
3:984-1804K).\n"
" /M Formatear disquetes a la mxima capacidad (5:902-1558K,
3:1066-1886K).\n"
" /N No verificar el disquete destino (peligroso en modo
/M).\n"
" /L Poner etiqueta de volmen al disco destino (minsculas
permitidas).\n"
" /V Etiqueta incremental en series de discos (si termina en
nmero).\n"
" /S Funcionamiento silencioso /Z Evitar parpadeo de
led de disco.\n"
" /R Elegir n ficheros raz (1-240) /T Cambiar nmero de
pistas (80-86).\n"
" /K No realizar pausa inicial /J No realizar pausa
final.\n");
}
else {
printf("\n\n"
" 2MF 3.0 - STANDARD FORMAT UTILITY FOR 2M
DISKETTES\n"
" (C) 1994 Ciriaco Garca de Celis - Grupo Universitario de
Informtica\n"
" C/Renedo, 2, 4-C; 47005 Valladolid (Spain) - ciri@gui.uva.es
- 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 cant 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, /Y}
Significado de la tabla /M:
{SectLogPistaX, fichraiz, verFmt, flagWr, velpista0, velpistaX},
{sectpista0, GAP3pista0, primsectpista0, interleavepista0},
{Sectpreformat, GAP3pistaX, SectFisPistaX, sects numerados...},
{tamaos 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];
else
if (cmd.FichRaiz % 16)
s0->FichRaiz=((cmd.FichRaiz >> 4) + 1) << 4;
else
s0->FichRaiz=cmd.FichRaiz;
if (ch==6)
s0->MediaId=0xF0; /* compatible SCANDISK */
else
s0->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);
else
strncpy (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;
335 EL 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];
else
for (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 inicializacin 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...
");
else
printf("\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 pacfico */ {
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 opcin /G!\n");
else
printf("\r WARNING: GAP value modified with /G switch!\n");
if (cmd->HD>1)
if (sp)
printf("\r AVISO: Parmetro indocumentado /D%d activo!\n",
cmd->HD-2);
else
printf("\r WARNING: Undocumented /D%d switch activated!\n",
cmd->HD-2);
if (cmd->MarcaPoco)
if (sp)
printf("\r AVISO: Parmetro indocumentado /W activo!\n");
else
printf("\r WARNING: Undocumented /W switch activated!\n");
if ((cmd->X!=-1) || (cmd->Y!=-1))
if (sp)
printf("\r AVISO: Parmetro indocumentado /X /Y activo!\n");
else
printf("\r WARNING: Undocumented /X or /Y switch activated!\n");
if (sp)
printf("\r Formateo de disquete ");
else
printf("\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 parmetro */
}
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 (" Volmen con nmero de serie %04X-%04X",
(int) (s0->NumSerie >> 16), (int) s0->NumSerie);
336 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
if (strstr(label, "NO NAME ")==NULL)
printf (" y etiqueta %11s\n", label);
else
printf("\n");
printf ("%9d ficheros permitidos en el raz.\n",
s0->FichRaiz);
printf ("%9d unidades de asignacin.\n", ua);
printf ("%9d bytes por unidad de asignacin.\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 slo"
" 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);
else
printf("\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 Anomala 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);
else
malclus=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) { /* posicin impar */
fat [ini*3/2] = fat [ini*3/2] & 0x0F | 0x70;
fat [ini*3/2+1] = 0xFF;
}
else { /* posicin 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 fsico */
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); /* cdigo 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;
337 EL 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 lnea de cambio */
outportb (FD_DOR, unidad | 4+8); /* apagar motor */
i = (i >> 7) | kbhit();
} while (!i);
if (flash) /* intento de bajar la lnea de cambio */
iter=2000000000L;
else
iter=8L;
while (i && !kbhit()) { /* y parpadeo del LED */
hora=*cbios+6;
pokeb (0x40, 0x40, 0xFF); /* para BIOS pelmas no estndar */
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 lnea 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 lnea */
biosdsk (5, disquetera, 0, 0, 0xFF, 0x7F, NULL); /* funcin de 2M */
/* 3 segundos para detencin 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 interrupcin */
outfdc (8); /* comando leer estado de interrupciones */
(void) infdc(); (void) infdc();
}
void outfdc (unsigned char dato) /* enviar byte al FDC */
{ /* no esperando ms 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 ms 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 interrupcin no ms 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 extrao */
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 an");
gotoxy (15,9); cputs ("no has enviado tu ");
textcolor (LIGHTMAGENTA); cputs ("tarjeta
postal"); textcolor (LIGHTRED);
cputs (" al autor, no");
gotoxy (15,10); cputs ("deberas 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 ("havent send still your ");
textcolor (LIGHTMAGENTA); cputs ("postcard");
textcolor (LIGHTRED);
cputs (" to the author,");
gotoxy (15,10); cputs ("you musnt continue on using this
diskettes.");
gotoxy (15,12); cputs ("If you have send it yet, Im ");
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 ingls */
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
;
; Cdigo 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
338 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
PUBLIC _Boot2mCode, _Boot2mLong
PUBLIC _biosdsk, _PicoRetardo, _NuevaInt24
PUBLIC _BootHDPrg, _BootHDPrgLong
PUBLIC _BootDDPrg, _BootDDPrgLong
; ------------ Cdigo 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 va BIOS. No se utiliza la
; funcin biosdisk() del compilador porque en algunas
; versiones del mismo hace tonteras que no debe. As,
; adems, se puede llamar a INT 13h con CALL (bueno,
; con RETF) para que dentro de WINDOWS 2MF /M no de
; problemas; adems, la funcin de formateo de 2M
; requiere SI="2M" al llamar.
_biosdsk PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH SI
PUSH DI
PUSHF ; estructura para futuro IRET
PUSH CS
LEA AX,bdsk_ret
PUSH AX
XOR AX,AX
MOV ES,AX
PUSH ES:[13h*4+2] ; INT 13h -> pila
PUSH 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 DI
POP SI
POP ES
POP BP
MOV AL,AH ; resultado
MOV AH,0
RET
_biosdsk ENDP
; ------------ Pequeo retardo de medio milisegundo.
_PicoRetardo PROC FAR
PUSH AX
PUSHF
POP AX
OR AH,70h
PUSH AX
POPF
PUSHF
POP AX
AND AH,0F0h
CMP AH,0F0h ; es PC/XT?
JE xt
MOV CX,33 ; 181193180*33*1000 = 0.5 ms
wrf: IN AL,61h
AND AL,10h
CMP AL,AH
JE wrf ; esperar pulso refresco memoria
MOV AH,AL
LOOP wrf
xt: POP AX
RET
_PicoRetardo ENDP
; ------------ Nuevo gestor de errores crticos.
_NuevaInt24 PROC
MOV AL,3 ; error en la funcin DOS invocada.
IRET
_NuevaInt24 ENDP
_DATA ENDS
END
12.6.7.5 - UN PROGRAMA PARA MEDIR EL RENDIMIENTO DE LOS DISQUETES.
En las pginas donde se describa el funcionamiento de 2M apareca una tabla con los tiempos
cronometrados de un COPY de mltiples ficheros, desde y hacia un disquete en los formatos de disco ms
comunes. Sin embargo, resulta interesante conocer la velocidad real del sistema de disco cuando ste es
utilizado ptimamente: acceso a mltiples pistas completas y consecutivas en el disco. Los buenos programas
de copia de discos, que leen de un golpe todas las pistas consecutivas que pueden antes de guardarlas en un
fichero auxiliar (o que las almacenan en EMS XMS), dependern de la velocidad que sea capaz de dar el
formato de disco empleado, ya que las disqueteras giran a una velocidad fija en todos los ordenadores. Si
pierden tiempo entre pista y pista (tal vez por escribirlas en el fichero auxiliar una por una) la velocidad
obtenida podra dividirse por dos, al intentar pillar el primer sector de la siguiente pista justo cuando acaba
de pasar de largo por delante del cabezal.
Velocidad mxima terica sin Velocidad real en Kb/seg estimada por 2M-FDTR (nivel BIOS).
considerar tiempos de acceso
pista-pista ni el porcentaje FORMAT FDFORMAT (**) FDFORMAT (***) 2MF 3.0 /F 2MF 3.0 /M
de superficie magntica que
se 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.49
5-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.47
3-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.25
3-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 estndar del DOS (360-720-1.2-1.44) y los parmetros /X e /Y adecuados.
(***) Formatos de mxima capacidad soportados (820-1.48-1.72) y los parmetros /X e /Y adecuados.
Con objeto de uniformizar los ndices, el siguiente programa de ejemplo realiza la lectura y escritura
completa de un disco (en este ltimo caso, si no contena datos, ya que se estropearan) llamando a la BIOS.
La primera versin del programa empleaba el DOS (funciones absread() y abswrite() del C) y obtena
exactamente 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 la
incomodidad que supone buscar el sector de arranque a travs de la tabla de particiones). Se recorren en
lectura y escritura todos los cilindros del disco, a partir del 1 y llegando hasta el ltimo que exista. El motivo
de saltar el cilindro 0 es doble: por un lado, saltar las reas del sistema (de cara a no escribir sobre el sector
de arranque, por ejemplo, ya que por simplicidad se escribe basura y no lo que se ha ledo al principio); por
otro lado, los tiempos de este cilindro pueden ser diferentes de los obtenidos en los dems cilindros, bien
339 EL HARDWARE DE APOYO AL MICROPROCESADOR
debido a la interferencia del sistema o los programas de cach o, simplemente, porque tiene un formato fsico
muy especial (como es el caso de los disquetes 2M). En el caso de los disquetes 2M, de esta forma no se
tiene en cuenta el tiempo extra que se pierde en este primer cilindro debido a la extraa maniobra que supone
simular la existencia de la segunda copia de la FAT (que implica volver momentneamente al primer cabezal
despus de haber pasado al segundo).
El programa, 2M-FDTR (2M Floppy Data Transfer Rate), utiliza el contador de hora de la BIOS
unido al temporizador 8254 para cronometrar. Antes de comenzar el test y arrancar el cronmetro se lee uno
de los ltimos sectores del cilindro 1 para asegurar que el cabezal est ya sobre el mismo y a punto de pillar
el primer sector. El buffer donde se realizar la lectura/escritura es asignado de tal manera que no cruce una
frontera de DMA (para que INT 13h no tenga que segmentar en varias fases la operacin, lo que disminuira
la velocidad). El acceso a INT 13h se realiza de manera directa, ya que la versin 3.1 del compilador hace
alguna oscura maniobra con biosdisk y al final termina perdiendo demasiado tiempo (lo suficiente como para
que en alguna mquina el disco aparente ser ms lento de lo que realmente es). Con Borland C 2.0 no hay
problemas, pero...
NOTA: Los resultados de 2M-FDTR contradicen los que facilitan muchos afamados programas comerciales de test, sencillamente porque dichos
programas no miden correctamente (y de hecho dan en cada ordenador, e incluso en la misma mquina entre ejecuciones consecutivas,
resultados diferentes y contradictorios). Si estuviera instalado un programa de cach, los resultados podran verse alterados por lo que se
recomienda no instalarlos para la prueba. De todas maneras, con un disquete recin introducido no hay programa alguno de cach que pueda
disminuir el tiempo de lectura del mismo (quiz s la escritura). Insisto en que los resultados de 2M-FDTR son reales y cualquier programa
de aplicacin que acceda a disco a medio o bajo nivel, como el propio 2M-FDTR, puede lograrlos si utiliza correctamente las funciones
de acceso a sectores del DOS o de la BIOS.
/*********************************************************************
* *
* 2M-FDTR 2.2 - Clculo de la tasa de transferencia de disquetes. *
* (C) 1994 Ciriaco Garca de Celis. *
* *
* Para Borland C++ 2.0 superior en modelo de memoria large. *
* *
*********************************************************************/
#define SMAX 23*512L /* mximo 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-espaol 0-ingls */
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 pas */
if ((!strcmp(strupr(argv[1]),"/I")) || (!strcmp(strupr(argv[2]),"/I")))
sp^=1; /* parmetro /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");
else
printf(" Error on drive access.\n");
exit (3);
}
if ((long) dsk.df_total*dsk.df_sclus>65535L) {
if (sp)
printf(" Unidades de ms de 32M no soportadas.\n");
else
printf(" Drive above 32M can not be tested.\n");
exit (1);
}
if ((buf=farmalloc (SMAX << 1))==NULL) {
if (sp)
printf(" Memoria insuficiente!.\n");
else
printf(" 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 ms de 63 sectores por pista!.\n");
else
printf(" 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 vaco -> test de escritura
omitido.\n");
else
printf(" + Diskette not empty -> write test skipped.\n");
exit (4);
}
if (sp)
printf(" + Rendimiento en escritura:\n");
else
printf(" + Write performance:\n");
evalua_io (WR, buf, unidad, cilindros, sectores, cabezales);
}
}
void ayuda()
{
printf(" (C) 1994 Ciriaco Garca de Celis.\n");
if (sp) {
printf(" Indica la unidad A: o B: para medir su
velocidad.\n");
printf(" - El test se realiza accediendo a travs de las
funciones BIOS.\n");
printf(" - El buffer E/S no cruza nunca una frontera de DMA
de 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);
}
340 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
unsigned long tiempo()
{
unsigned long tm;
asm {
cli
mov al,6
out 43h,al /* enclavamiento contador 0 */
in al,40h
mov ah,al
in al,40h
xchg ah,al
neg ax /* ax = valor del contador 0 del 8254 */
push ds
mov bx,40h
mov ds,bx
mov bx,ds:[6ch] /* bx = contador hora BIOS */
sti
pop ds
mov word ptr tm,ax
mov 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 autntico 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");
else
printf("\r Failure on disk access!.\n");
goto aborta_io;
}
if (sp)
printf("\r\r Cilindro %2d - Cara %d", cilindro, cabezal);
else
printf("\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);
else
printf("\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 ingls */
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 mquinas AT, a partir de la versin 1.2 ha estado
acompaado de una versin para PC/XT. El nico requisito es que el ordenador est equipado con una
controladora y unidades de alta densidad. Algunas mquinas modernas de tipo subnotebook, que caben en
la palma de la mano, vienen preparadas para conectar una de estas disqueteras externas. Otros PC/XT de
reciente fabricacin traen ya controladoras de alta densidad y BIOS que las soportan, aunque luego el tacao
fabricante haya colocado una unidad de doble densidad que el usuario puede sustituir. Finalmente, a aquellas
mquinas ms antiguas que no pertenecen a ninguna de estas dos categoras, se les puede sustituir la
controladora y unidades de doble densidad por otras de alta, que en el futuro el usuario podr colocar en su
mquina AT cuando se la compre; se trata por tanto de una inversin rentable. Si bien resulta difcil encontrar
actualmente en el mercado controladoras de alta densidad para PC/XT, el usuario puede optar por poner una
de AT. Yo, por ejemplo, para probar 2MX me vi obligado a pinchar una controladora de 16 bits en un slot
de 8 bits. La tarjeta era una IDE multi-io; sin embargo, la parte alta del bus (que no se puede pinchar al ser
de 8 bits el slot) slo se utiliza para acceder al disco duro bus AT, pudiendo ser inhibida con el jumper de
marras (si bien ni esto result necesario). La parte correspondiente al control de disquetes, y probablemente
los puertos serie/paralelo, era totalmente funcional, ya que slo 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 preparada
para soportar alta densidad. Al hacer DIR sobre un disquete de alta densidad nada ms encender el ordenador,
lo ms probable es que funcione, ya que sta es la densidad por defecto normalmente. Sin embargo, con los
discos de doble densidad (donde tiene que seleccionar 250 300 Kbit/seg) es imposible sacar el DIR. En
cualquier caso, sacar un DIR es una cosa y otra muy diferente conseguir que el disco funcione. Como la
BIOS informa siempre que todo es de doble densidad, el muy patoso del DOS modifica la tabla base del
disco para indicar como 9 el ltimo nmero de sector en la pista (quin le mandar tocar las variables de
la BIOS?) por lo que ni los discos de alta densidad funcionan a nivel de COPY (el directorio s aparece
porque coincide en los primeros sectores de las pistas). La solucin en este tipo de mquinas pasa por instalar
una BIOS ms moderna... pero sin tener que regrabar la eprom. Basta con cargar 2M-XBIOS.EXE, un
programa residente que emula la BIOS AMI de AT en los XT. De hecho, 2MX solicita al usuario la
341 EL HARDWARE DE APOYO AL MICROPROCESADOR
instalacin de este driver cuando advierte que no puede detectar el tipo de las unidades.
En ese sentido, la combinacin 2M-XBIOS + 2MX permite a cualquier mquina PC/XT obsoleta
equipada con una barata controladora de disquetes de AT trabajar con discos de cualquier densidad y
cualquier formato (estndar/2M). Los problemas de versiones anteriores de 2MX han sido eliminados gracias
a la extensin BIOS en que se apoya. De hecho, 2MX es en sus ltimas versiones prcticamente idntico a
2M, slo cambia en algunos aspectos puntuales relacionados con la diferente arquitectura de los XT respecto
a los AT.
12.6.7.7 - LA OPCION BIOS DE 2M: 2M-ABIOS Y 2M-XBIOS [Listados no includos en este libro].
Algunos ordenadores poseen una BIOS antigua o con un diseo propio poco compatible en el control
de disco. En estas mquinas, 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 al
subsistema de disco. La versin 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 con
ellos instalados 2M no opera de manera totalmente correcta (aunque en general 2M depende realmente muy
poco de la BIOS, pero ya conozco algn caso al respecto) y en la mquina no est instalado algn otro
software de disco incompatible con 2M, entonces el ordenador no es 100% compatible hardware con el
estndar; esto es particularmente cierto si ni siquiera se reconocen los discos estndar del DOS.
Esta utilidad tambin sirve para aadir soporte de 1.44M a mquinas con BIOS antigua, algunas de
ellas incluso AT. En estos casos, el usuario debe ignorar la informacin sobre el tipo de la unidad que pueda
reportar 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 emulacin al 100% del
eficaz cdigo de control de disco de las BIOS AMI, relevando as por completo de esta tarea a la BIOS del
sistema. De ah que haya sido diseado en este formato, para forzar al usuario a instalarlo antes de los dems
programas de disco, a los que anulara por completo (ya que nunca ms vuelve a llamar a la interrupcin de
disco 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 opcin /C evita en los equipos
AT ajustar la CMOS, por si la mquina en cuestin tiene un algoritmo no estndar para calcular el checksum
de la misma y aparece un "Incorrect CMOS checksum" al arrancar (muy poco probable). As mismo, si en
algn momento el usuario dudara acerca de si 2M-ABIOS est controlando realmente las unidades, puede
utilizar la opcin /13 para asegurarlo, si bien esta opcin es poco recomendable cuando no es estrictamente
necesaria (se desva tambin INT 13h adems 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 includo en este libro].
Debido a la ineficiencia de FORMAT a la hora de crear discos rpidos y teniendo en cuenta la
limitacin de DISKCOPY en el sentido de no poder formatear discos destino en formato 2M, se comprende
la necesidad de un sustituto de FORMAT y DISKCOPY. Sin embargo, todos los programas al respecto
existentes en la actualidad, a mi juicio, son un perfecto desastre. La mayora no son rpidos incluso con
discos optimizados, por una cuestin elemental: no colocar los buffers de transferencia de manera que no
crucen las fronteras de DMA (para evitar que el DOS tenga que hacer accesos redundantes para salvarlas).
La mayora, de hecho, no generan discos optimizados con la clsica tcnica de Sector Sliding (que en
absoluto implica reduccin de compatibilidad o fiabilidad; ms bien al contrario: es como se debe formatear
correctamente un disco y como de hecho se hace con los discos duros). Otros son poco flexibles y no
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 virus
en sectores de arranque poco oficiales, o necesitan VGA y ratn (aparte de ser lentos), o no son fiables...
La solucin adoptada ha sido crear un programa residente que haga trabajar a todos los dems (con
la excepcin de los que tambin acceden directamente a la controladora de disco) de la manera adecuada. Se
trata de crear una utilidad para que FORMAT o cualquier otro programa que llame a la BIOS formatee discos
optimizados (an sin saberlo) y que ample 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 automticamente de manera ptima y DISKCOPY
soporta el formateo de discos 2M. Incluso FORMAT puede crear discos 2M (indicando pistas y sectores) si
bien el de MS-DOS (no DR-DOS) tiene problemas con los de alta densidad y necesita un parmetro opcional
(de todas maneras, 2MF sigue siendo ms eficiente). Adems 2MDOS da soporte por defecto a disquetes no
estndar, creados por la utilidad FDFORMAT y permite a FORMAT poder crear disquetes FDREAD. El
programa consume 5,7 Kb en equipos sin memoria extendida o 2,5 Kb con ella (slo 1,7 Kb si no est activo
el soporte para hacer DISKCOPY hacia un disco 2M sin formato; esto es, con slo las opciones de
optimizacin de formateo y soporte FDREAD activas).
Por si esto fuera poco, 2MDOS incorpora una nueva tcnica para acelerar an ms los discos estndar
de 1.2M y 1.44M, que recibe el nombre de DiskBoost por razones de marketing. El truco consiste en evitar
la necesidad de Sector Sliding, para de esta manera alcanzar, por ejemplo, una tasa de transferencia de datos
de 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 aadir un sector adicional en el cabezal 1 y dos en el cabezal 0, que no se usan, algo
que no reduce sensiblemente el nivel de seguridad del disco (sera el equivalente en seguridad a un disco de
1.64M, por ejemplo). Los sectores adicionales, no usados, son colocados al principio de la pista. De esta
manera, cuando la controladora acaba de acceder a una pista completa en el cabezal 0 (y est al inicio justo
de la pista tras completar una vuelta) se conmuta al cabezal 1 para acceder a la pista siguiente. Recordemos
que en el cabezal 1 haba un sector no utilizado al principio: este sector pasar por delante del cabezal
mientras se conmuta, pero no transcurrir demasiado tiempo como para que no se pueda pillar el primer
sector de la pista que viene inmediatamente a continuacin. Cuando se acabe de leer la pista en el cabezal
1 (y se est de nuevo al inicio justo de la pista tras completar la vuelta) se conmuta al cabezal 0 pero del
siguiente cilindro, algo que lleva ms tiempo que antes... pero para eso ya habamos dejado dos sectores no
utilizados al inicio del cabezal 0. Por tanto, tambin da tiempo a pillar el primer sector.
Con la tcnica DiskBoost es factible leer o escribir un disco completo de 1.44M en poco ms de 31
segundos, al emplear slo una vuelta por cada pista. La diferencia de velocidad, contra todo pronstico, es
an ms espectacular en las operaciones COPY o XCOPY normales. Los discos de 1.2M y 1.44M creados
por FORMAT con 2MDOS instalado son un 50% ms rpidos en el uso normal.
Sin embargo, 2MDOS no es la solucin definitiva. Aunque es til para que cada cual utilice sus
programas de copia/formateo favoritos de manera ptima, lo ideal sera un programa de copia/formateo
realmente eficiente. Con dicho programa, 2MDOS no sera necesario...
El listado de 2MDOS tampoco se incluye en estas pginas. 2MDOS tambin incorpora el cdigo
SuperBOOT a los discos 2M de alta densidad que se formatean bajo su control, aunque su tarea es ampliar
la 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 includo en el libro].
En cierta ocasin un programa llamado 1968 lleg a mis manos. Se trataba de una utilidad para
formatear discos de 1.44M a esa capacidad. Sin embargo, no funcionaba en mi unidad, ni tampoco en la de
mis mquinas 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
343 EL HARDWARE DE APOYO AL MICROPROCESADOR
se que si lo hubiera conseguido, ese disco -como bien deca el autor en la documentacin- s podra ser ledo
en las dems unidades.
El mtodo de este programa consista en introducir 3 sectores de 4 Kb en cada pista. El problema
es que eso requiere (4096+62)*3 = 12474 bytes, sin contar los GAP entre sectores, y la mayora de las
unidades giran algo ms deprisa de lo normal (y por tanto, se alejan del lmite terico de 12500 bytes por
pista). Por otro lado, 26 bytes son incluso pocos para respetar las marcas de inicio de pista y dems. Al final,
el tercer sector suele acabar pisando al primero.
Despus de algn tiempo, han aparecido ms formateadores que soportan (o dicen soportar) este
formato, alguno incluso en nuestro pas. Sin embargo, todos tienen el mismo problema: no hay unidades que
soporten a esos programas. Por tanto, todo pareca indicar que el lmite de capacidad se quedara para siempre
en los 1.72M del FDFORMAT los 1.88M de 2M, nicos formatos soportados por todas las unidades y
ordenadores (eso s, compatibles). Pues no. Cierto da, Jess Arias tuvo una idea genial y me la cont. A raz
de esa idea, y tras superar numerosas y difciles trabas tcnicas, finalmente ha sido posible el milagro: lograr
utilizar 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 su
desarrollo se han puesto de relieve circunstancias curiosas. Por ejemplo, una determinada unidad admite
12440 bytes por pista al grabar informacin aleatoria, pero si se escribe toda la pista con bits a 0 a 1 slo
caben 12405 bytes. Por qu?: la respuesta sigue siendo un misterio. Las rutinas residentes de 2MGUI
aprovechan las terminaciones normales de error de la controladora (disco protegido contra escritura, sector
no encontrado, etc.) para la deteccin de errores, aunque graban adicionalmente, en cada pista de datos, un
checksum de la informacin almacenada junto al nmero de pista y cabezal reales, para realizar el control
de errores cuando la controladora no puede devolver condiciones de error (debido a una serie de factores
tcnicos). De esta manera, la informacin se graba y recupera con la seguridad de que es correcta -en caso
contrario, se detectara el fallo-.
Realizando pruebas, la capacidad admitida por diversas unidades se mostr directamente relacionada
con la velocidad de rotacin de la misma. Por ejemplo, una unidad de 3-HD que gire cada 199,9 ms admite
12405 bytes, mientras que otra que lo hace cada 199,1 ms slo admite 12348 bytes. Ambas son casos
realmente extremos, ya que la inmensa mayora se encuentra entre estas dos. An as, la capacidad finalmente
adoptada por 2MGUI sern 12329 bytes. El objetivo es permitir que los discos puedan ser intercambiados
entre unidades. En lectura nunca hay problemas, ya que la peor unidad puede leer los datos de la mejor (la
que ms lentamente gire) porque la seal de reloj la obtiene de los propios datos registrados en disco. Sin
embargo, al escribir, la seal de reloj la extrae de su base de tiempos propia (casi igual en todos los
ordenadores) y al girar ms deprisa se le acaba la pista antes y sobreescribe el principio. Por tanto, los discos
que apuren demasiado la capacidad de una buena unidad sern estropeados al ser escritos (no ledos) en otra
unidad peor.
Doble Alta Extraalta
Rcord absoluto previo a 2M 820.0 Kb 1394.0 Kb --
Capacidad mxima 2M (2MF /M) 902.0 Kb 1558.0 Kb -- 5.25
Capacidad mnima de 2MGUI 979.0 Kb 1642.4 Kb -- (5)
Capacidad lmite terica (82p) 1001.0 Kb 1668.2 Kb --
Rcord absoluto previo a 2M 984.0 Kb 1722.0 Kb 2880.0 Kb
Capacidad mxima 2M (2MF /M) 1066.0 Kb 1886.0 Kb 3772.0 Kb* 3.5
Capacidad mnima de 2MGUI 1178.3 Kb 1974.5 Kb 3949.0 Kb* (3)
Capacidad lmite terica (82p) 1201.2 Kb 2002.0 Kb 4003.9 Kb
(*) No probado. En esta lista estn recogidos slo los formatos soportados
por prcticamente todas las unidades y en casi todos los ordenadores.
Hay tambin otro pequeo problema tcnico: si la capacidad de la pista es mltiplo del tamao de
sector lgico empleado (aunque ese sector sea de 128 bytes en lugar de 512) se derrocha espacio al redondear
344 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
hacia abajo. La tentacin era fuerte: permitir que un sector lgico pueda estar entre dos pistas. De esta
manera, la capacidad total de un disco no puede ser mltiplo entero del nmero de pistas y cabezales.
Solucin: crear un controlador de dispositivo que trate al disco como un array de sectores (un dispositivo con
un sector por pista, un cabezal, y muchsimas pistas, igual que un disco virtual). As, por ejemplo, los discos
de 3-HD con 12329 bytes por pista tienen en total (con las 82 pistas habituales) 2.021.956 bytes (que
equivalen a 15.796 sectores de 128 bytes, totalizando 2.021.888 bytes con un desperdicio de slo 68).
Utilizando una sola FAT, un nmero razonable de entradas al directorio y clusters de 2048 bytes (que en las
pruebas han demostrado generar discos notablemente ms rpidos que los de 512 bytes) el espacio disponible
para 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 adems en todas las unidades (y no en casi ninguna).
En escritura, estos discos son 2 3 veces ms lentos que en lectura, aproximadamente. En lectura
son sin embargo algo ms rpidos que los discos estndar 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 al
principio y escribirla de nuevo...todo eso para cambiar un sector lgico de 128 bytes!. Sin embargo, tampoco
es para tanto, ya que por lo general el
DOS enva bloques grandes a los
dispositivos y esto supone la escritura
directa e inmediata de las pistas
completas... que adems utilizan la
tcnica de Sector Sliding (la posicin
inicial del sector-pista est desplazada
segn la ubicacin en el disco). De
hecho, cacheando las reas del sistema,
la velocidad de escritura seria
probablemente muy superior, al agilizar
el cuello de botella que supone el
acceso a la FAT. Sin embargo, el
consumo de memoria del programa
(unos 17 Kb) ya es respetable sin cach,
y no se llega tampoco al extremo del
viejo 1968 de reservar 240 Kb de XMS.
El programa (un nico fichero
EXE que se carga en el CONFIG.SYS
y luego se puede ejecutar desde la lnea
de comandos para formatear) es
totalmente flexible tanto a nivel lgico
(posibilidad de reprogramar el tamao
de cluster, el nmero de entradas al
directorio y el nmero de FATs) como
fsico (posibilidad de elegir nmero de
pistas, densidades, Sector Sliding X e Y
(expresado adems en grados angulares)
e incluso un parmetro nada menos que
para indicar los bytes por pista (por si el
usuario tiene una unidad que admite
ms). Dispone tambin de una opcin
para medir con precisin la velocidad de
rotacin de la unidad y para calcular
qu capacidad mxima soporta. La
flexibilidad de un disco virtual... pero
en un disquete; el nmero de formatos
C:\AUXI>2mgui
2MGUI instalado en memoria.
- Nueva unidad E: 1.2M (unidad fsica A:)
- Nueva unidad F: 1.44M (unidad fsica B:)
Ejecute 2MGUI /? si desea obtener ayuda.
C:\AUXI>dir e:
Volume in drive E is unlabeled
File not found "E:\*.*"
0 bytes in 0 file(s)
997.376 bytes free
C:\AUXI>chkdsk e:
997.376 bytes total disk space
997.376 bytes available on disk
2.048 bytes in each allocation unit
487 total allocation units on disk
487 available allocation units on disk
655.360 total bytes memory
649.776 bytes free
C:\AUXI>dir f:
Volume in drive F is unlabeled
File not found "F:\*.*"
0 bytes in 0 file(s)
2.015.232 bytes free
C:\AUXI>chkdsk f:
2.015.232 bytes total disk space
2.015.232 bytes available on disk
2.048 bytes in each allocation unit
984 total allocation units on disk
984 available allocation units on disk
655.360 total bytes memory
649.776 bytes free
C:\AUXI>_
EJEMPLOS DE ACCESO A UN DISCO DE 360K
Y OTRO DE 1.44M FORMATEADOS CON 2MGUI
345 EL HARDWARE DE APOYO AL MICROPROCESADOR
es prcticamente infinito, segn la voluntad del usuario. Una de las opciones es formatear las 28 pistas ms
externas en alta densidad y las 54 restantes en doble, en un disco de 360K, obtenindose 1.2M bastante ms
fiables de lo que se podra 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 acceso
directo a memoria que establece por defecto es de slo 12 Kbytes (EMM386 establece 32 Kb).
Las nuevas letras de unidad 2MGUI tambin soportan discos estndar e incluso 2M (teniendo
instalado tambin 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. Para
empezar, es necesario arrancar el DOS desde un disquete o desde un fichero imagen de disco, ya que en las
ventanas DOS ordinarias 2M no puede controlar los accesos a disco. Curiosamente, s se puede formatear en
estas ventanas, pero no trabajar con el disco: lo que sucede es que el sistema de ficheros de la emulacin
DOS que incorpora OS/2 est gestionado al parecer sin llamadas a la BIOS, precisamente las que intercepta
2M, 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 funcionara perfectamente. Pero lo ms
probable es que el usuario tenga instalada la utilidad FSFILTER.SYS para poder acceder a las particiones
HPFS y, sobre todo, para poder escribir sobre las particiones FAT ordinarias, que seran de slo lectura en
caso contrario. Y aqu vuelven los problemas: al instalar este driver que altera la INT 21h, 2M deja de nuevo
de funcionar.
La solucin ms rpida consiste en crear un driver que implemente 2 nuevas unidades lgicas (como
la 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 sera un programa enteramente DOS, que sin
embargo no se puede instalar en las ventanas DOS normales de OS/2, ya que en ellas estn prohibidos los
dispositivos de bloque. Por tanto, su utilizacin queda restringida a las ventanas de DOS que incorporen una
autntica versin de este sistema (obtenidas con VMDISK sobre un disquete de arranque, a menos que el
usuario desee arrancarlas desde disquete cada vez que vaya a emplearlas).
Pese a la solucin de dicho driver (en nuestro caso 2MGUI), existe algn problema relativamente
importante que comentar. El ms interesante consiste en que OS/2 comprueba peridicamente si ha habido
un cambio de disco en alguna unidad, accediendo a la misma en ese caso para comprobar su contenido -con
independencia de que el usuario est haciendo otra cosa en ese momento, como jugar a los marcianitos
mientras espera los resultados de un programa de clculo-. Si no hay disco introducido no sucede nada, pero
si lo hay y es de tipo 2M, OS/2 se queda intentando leerlo de manera obsesiva hasta el punto de colapsar la
ventana DOS, que queda literalmente colgada (aunque no el resto de las ventanas ni el sistema en conjunto).
La solucin, si se estaba trabajando en esta ventana, es retirar el disquete de la unidad y esperar un segundo
o dos. Ah, y no volver a introducirlo hasta que no se vaya a utilizar, para evitar nuevas molestias. Por
fortuna, OS/2 suele tener cuidado de no fisgar por las disqueteras cuando estn siendo usadas. La solucin
ideal sera un driver que integrara en OS/2 el soporte de estos disquetes, pero eso requiere saber construir
controladores para OS/2.
Las primeras versiones de 2M venan acompaadas de un driver DOS que realizaba la tarea descrita;
sin embargo, desde 2M 1.3+ fue sustituido incorrectamente por una recomendacin al usuario acerca de la
instalacin de DRIVER.SYS, programa que no llama a la BIOS (sino al propio DOS; por tanto, con efectos
nulos). Por consiguiente, con 2M 3.0+ aparece de nuevo soporte oficial para este sistema.
346 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
12.7. - EL DISCO DURO DEL AT (IDE, MFM, BUS LOCAL).
La informacin aqu vertida se aplica al tradicional controlador de disco duro ST506, que ha equipado
a los discos duros MFM/RLL de los AT, con el que es compatible en lneas generales tanto el interface de
los ESDI como el de los IDE (ISA, PCI o Bus Local). Sin embargo, los discos SCSI no son compatibles con
la informacin 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 travs de dos cables: uno con las seales de control y
otro con las de datos. El de seales de control consta de 34 conectores, y el de datos de 20.
Nombre seal Pin seal 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 seal Pin seal
- 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 SEALES PARA TRANSFERENCIA DE DATOS
- DRIVE SELECT 3 30 29
- DRIVE SELECT 4 32 31
- DIRECTION IN 34 33
SEALES DE CONTROL
Significado de las seales 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 seales codifican un nmero de 4 bits para referenciar a un cabezal.
-Direction In: Esta seal define la direccin en que se mueven los cabezales con la seal Step. Un valor inactivo indica
direccin Out y los pulsos Step alejan los cabezales del centro del disco; un valor activo se interpreta como In
y dichos pulsos acercan los cabezales al centro.
-Step: Esta seal provoca un movimiento de los cabezales en la direccin que indica la anterior.
-Drive Select: Lneas de seleccin de unidad. Las unidades poseen jumpers para dejarse seleccionar con ciertas combinaciones.
Significado de las seales de control (salida):
-Seek Complete: Informan del final del posicionamiento de los cabezales. Si esta seal no indica que est terminada dicha
operacin, no se puede leer ni escribir.
-Track 000: Indica que los cabezales estn sobre la pista 0.
-Write Fault: Seala una condicin que est provocando una operacin no correcta del disco.
-Index: La unidad indica aqu al exterior el comienzo de una pista (a cada revolucin).
-Ready: Seal necesaria junto con Seek Complete para poder leer/escribir/posicionar cabezales.
12.7.2 - PROGRAMACIN DE LA CONTROLADORA.
El disco duro trabaja
con sectores de 512 bytes;
soporta correccin de errores
ECC, operaciones multisector
rebasando fronteras de pista y
cilindro y funciones de
autodiagnstico. El lmite de
capacidad est en 1024 cilindros
y 16 cabezales. Los registros de
operacin son los mostrados en
Direccin E/S hex. Significado
Primaria Secund. Lectura Escritura
1F0 170 Data registers Data register
1F1 171 Error register Write precomp
1F2 172 Sector count Sector count
1F3 173 Sector number Sector number
1F4 174 Cylinder low Cylinder low
1F5 175 Cylinder high Cylinder high
1F6 176 Drive/Head Drive/Head
1F7 177 Status register Command register
la figura, estando la controladora ubicada normalmente en la localizacin E/S primaria.
347 EL 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 el
modo PIO (esto es, sin DMA). No debera ser accedido a menos que haya una
operacin de lectura o escritura en curso. Implementa una direccin de 16 bits dentro
del buffer de la controladora que contiene al sector para las operaciones de lectura
y escritura normales. Para una lectura/escritura largas 4 bytes ECC son transferidos
por byte con al menos 2 microsegundos entre transferencias (la lnea DRQ debe estar
activa antes de transferir los bytes ECC).
Error Register: De slo lectura, contiene informacin sobre el comando previo. El dato es vlido
slo 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, se
encuentra en modo diagnstico: en esos casos, el registro debe ser comprobado diga
lo que diga el bit del registro de estado (con el significado en estos casos de 01-No
hay error, 02-Fallo del controlador, 03-Error en el buffer del sector, 04-Error en el
dispositivo ECC, 05-Error en el procesador de control).
Cuando no est en modo diagnstico, caso ms comn, 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 seal Track 000 no
se activa despus de 1023 pulsos de retroceso.
bit 2: Comando abortado. En estos casos, se debe mirar los registros de Status y
Error para determinar con precisin la causa (que estar en Write Fault, Seek
Complete, Drive ready -- o comando invlido 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 estn activos los reintentos, el controlador lo
reintenta 16 veces antes de dar error, en caso contrario slo explora la pista
como 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 intentarn en l ni lecturas ni escrituras.
Write
Precompensation: El valor almacenado es el cilindro de comienzo para la escritura precompensada
dividido por 4.
Sector Count: Indica el nmero de sectores a transferir durante la lectura, escritura, verificacin o
formateo. En las operaciones multisector, este registro se decrementa y el Sector
Number se incrementa; al formatear, antes de enviar cada comando de formateo debe
cargarse aqu el nmero de sectores en la pista. Se soportan operaciones multisector
que crucen fronteras de pista y cilindro. Las caractersticas de la unidad deben
establecerse con el comando Set Parameters antes de una transferencia multisector.
Este registro debe cargarse con el nmero de sectores antes de cualquier comando
relacionado con datos. Un valor 0 representa 256 sectores.
Sector Number: Nmero de sector para la lectura, escritura y verificacin. El sector inicial se carga
aqu en las operaciones multisector.
Cylinder Number: Nmero de cilindro para los comandos de lectura, escritura, verificacin y
posicionamiento de cabezales. Entre el registro que almacena la parte baja y el de la
parte alta (low y high respectivamente) se guarda un nmero 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 el
primer disco duro y 1 el segundo) y los bits 0-3 el nmero de cabezal de
lectura/escritura deseado. Para acceder a las cabezas 8-15, es necesario adems
activar el bit 3 del puerto 3F6h. Importante: este registro debe cargarse con el
nmero mximo de cabezales antes de enviar un comando Set Parameters.
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 para
conocer el resultado. Si el bit busy (7) est activo, los dems bits no son vlidos.
Una lectura de este registro borra la peticin de interrupcin IRQ 14. Si write-fault
(bit 5) o error (bit 0) estn activos, o si seek-complete (bit 4) o drive-ready (bit 6)
estn inactivos, la operacin 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 estn 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 estn 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) estn activos, hay
un comando en ejecucin. Hasta recibir algn comando, este bit est a 0.
bit 2: Corrected-data. Un 1 indica que los datos ledos del disco fueron corregidos
de error ECC con xito. Errores suaves no abortan la operacin multisector.
bit 1: Index. Este bit se pone a 1 tras cada revolucin del disco.
bit 0: Error. Un 1 indica que el comando previo termin en error, y uno o ms bits
del Error register estn activos. El prximo comando enviado al controlador
borra este bit. Si este bit se activa, la operacin multisector es abortada.
Command Register: Acepta 8 diferentes comandos. Los comandos se programan cargando primero los
dems registros necesarios y escribiendo despus el comando en ste mientras el
registro de estado devuelve una condicin de no busy. Un comando no legal provoca
un error de comando abortado. La solicitud de interrupcin IRQ 14 se borra al
escribir un comando. Los comandos soportados son:
Comando bit 7 6 5 4 3 2 1 0
Restore 0 0 0 1 R3 R2 R1 R0
Seek 0 1 1 1 R3 R2 R1 R0
Read sector 0 0 1 0 0 0 L T
Write sector 0 0 1 1 0 0 L T
Format track 0 1 0 1 0 0 0 0
Read verify 0 1 0 0 0 0 0 T
Diagnose 1 0 0 1 0 0 0 0
Set Parameters 1 0 0 1 0 0 0 1
R3 R2 R1 R0 Stepping rate
0 0 0 0 35 s
0 0 0 1 0.5 ms
0 0 1 0 1.0 ms
0 0 1 1 1.5 ms
0 1 0 0 2.0 ms
0 1 0 1 2.5 ms
0 1 1 0 3.0 ms
0 1 1 1 3.5 ms
1 0 0 0 4.0 ms
1 0 0 1 4.5 ms
1 0 1 0 5.0 ms
1 0 1 1 5.5 ms
1 1 0 0 6.0 ms
1 1 0 1 6.5 ms
1 1 1 0 7.0 ms
1 1 1 1 7.5 ms
Bit 0 1
L Modo de datos Slo datos Datos y 4 bytes ECC
T Modo de reintentos Reintentos habilitados Reintentos inhibidos
Nota: Despus de un reset o un comando Diagnose, el step rate queda en 7.5 ms.
Por otro lado, el sistema verifica la operacin ECC leyendo y escribiendo
estos bytes: cuando los reintentos estn deshabilitados, los reintentos de ECC
e ID estn limitados a menos de dos vueltas completas del disco.
349 EL HARDWARE DE APOYO AL MICROPROCESADOR
Explicacin de los comandos.
Restore: Enva los cabezales a la pista 0 (hasta que la seal Track 000 es activa). Si Track 000 no se
activa tras 1023 pulsos de step activa el bit de error en el registro de estado y deja el error
TR 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 simultneo en dos unidades.
Al final del comando se produce una interrupcin.
Read sector: Cierto nmero de sectores (1-256) pueden ser ledos del disco duro con o sin el campo ECC
aadido, en el modo PIO (entrada-salida programada, sin DMA). Si los cabezales no estn
sobre la pista necesaria, el controlador enva pulsos step para posicionarlo, utilizando el step
rate del ltimo seek o restore. Los errores de datos de hasta 5 bits son corregidos
automticamente en los comandos de lectura corta. Si un error no corregible tiene lugar, se
contina leyendo el sector donde apareci pero ya no se leen ms sectores en el caso de los
accesos multisector. Se produce una interrupcin por cada sector cuando est preparado para
ser transferido, pero no al final del comando.
Write sector: Cierto nmero de sectores (1-256) pueden ser escritos a disco duro con o sin el campo ECC
aadido, en el modo PIO (entrada-salida programada, sin DMA). Realiza los seeks que sea
necesario hacer. Las interrupciones suceden cada vez que es transferido un sector al buffer
(salvo el primero) y al final del comando. El primer sector debera ser escrito en el buffer
inmediatamente despus de que el comando ha sido enviado y "Data-request" es activo.
Format track: Se formatea la pista indicada segn la tabla de interleave que se transfiere. Hay 2 bytes por
cada sector: 0, N sector. As se puede elegir la numeracin deseada. Hay que enviar 512
bytes 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 interrupcin al final del comando de formateo. Los sectores defectuosos se
marcan 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 envan datos al ordenador; de
esta manera simplemente se verifica la integridad de los mismos. Una nica interrupcin se
genera 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 una
interrupcin cuando completa el comando.
Set Parameters: Establece los parmetros de la unidad: mximo nmero de cabezales y sectores/pista. El
registro drive/head indica qu unidad es afectada. Hay que actualizar los registros sector
count y drive/head antes de enviar este comando. Estos parmetros sern empleados para
cruzar los cilindros en las operaciones multisector. Se genera una interrupcin cuando se
completa el comando. Este comando debe ser enviado antes de intentar alguna operacin
multisector. Se soportan dos discos duros, con diferentes caractersticas cada uno, definidas
por este comando.
Registro del controlador de disco duro (3F6h) y Registro de entrada digital (3F7h).
Adems de informar de la lnea de cambio de disco en los disquetes, los bits 0-5 del registro de
entrada digital (3F7h) estn relacionados con el disco duro.
3F6h - bits 7-4:Reservados 3F7h - bit 7: Lnea 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 Current
bit 2: 1 - Disk reset enable bit 4: Head select 2
0 - Disk reset disable bit 3: Head select 1
bit 1: 0 - Disk initialization enable bit 2: Head select 0
1 - Disk initialization disable bit 1: Drive select 1
bit 0: Reservado bit 0: Drive select 0
12.7.3 - EJEMPLO PRACTICO DE PROGRAMACIN.
En los AT la interrupcin de disco duro es la IRQ 14 (INT 76h). La BIOS, en caso de producirse
esta interrupcin, almacena un valor 0FFh en 40h:8Eh con el gestor que tiene por defecto. Las transferencias
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 que
la controladora tiene un buffer interno con capacidad para algn sector y, por tanto, cuando hay que
transferirlo, no hay que esperar a que venga del disco mientras este gira lentamente (como en el caso de los
disquetes): una transferencia con el DMA ordinario aqu sera ms lenta que a travs de la CPU.
Parte de la documentacin vista con anterioridad es slo oficial. Por ejemplo, los discos IDE suelen
venir formateados de fbrica a bajo nivel e ignoran el comando de formateo: estas unidades son bastante
inteligentes y llevan su propia gestin de sectores defectuosos (reemplazndolos por otros que tienen libres
para simular que todo est correcto) as como de interleaves (generalmente 1:1, valores peores se deben a
controladoras obsoletas que no tenan un buffer con capacidad para una pista) y skews ptimos.
El programa de ejemplo es el embrin de una utilidad para acceder directamente a la controladora
de disco duro. La funcin operahd() solo implementa realmente el comando de leer sector. En el ejemplo,
se lee el primer sector absoluto del disco, donde suele ser fcil reconocer la tabla de particiones. En nuestro
caso, en lectura, justo antes de enviar el comando se borra el flag de interrupcin. Una vez enviado, cuando
llegue la interrupcin se procede a leer el sector. Para ello se utiliza la rpida instruccin REP INSW del 286
y procesadores superiores, para E/S repetitiva, que lo trae a gran velocidad desde la memoria de la
controladora a la del sistema.
Un acceso directo a bajo nivel puede tener mucho inters para ciertas aplicaciones. Por ejemplo, un
antivirus puede asegurarse de que ha reparado la tabla de particiones (o cualquier otra zona del disco) sin
temor a que en su llamada a INT 13h el virus residente le haya estropeado el trabajo (aunque si el virus
trabaja 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 slo implementa la funcin 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 interrupcin a 0 */
outportb (HDR_CMD, operacion); /* comando */
while (!peekb(0x40, 0x8e)); /* esperar interrupcin 76h */
/* (convendra poner un timeout) */
/* por eficiencia, el siguiente cdigo est en ensamblador */
asm {
push es /* mxima lectura soportada: casi 64 Kb */
push cx
push dx
push di
mov cx,numsect
xchg ch,cl /* CX = numsect * 256 = n palabras */
les di,direccion
cld
mov dx,HDR_DATA
db 0F3h, 6Dh /* instruccin 286+ rep insw */
pop di
pop dx
pop cx
pop 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 particin */
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- Ests viendo 256 bytes del sector.\n");
printf("- Pulsa una tecla para continuar.");
getch();
}
}
351 EL 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 ordenadores
compatibles, 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 hbiles: dos que
conducen la corriente, uno para datos y otro para reloj. El teclado es en realidad un pequeo microordenador;
de hecho muchos teclados llevan en su interior el chip 8049 de Intel (el microprocesador esclavo del viejo
QL de Sinclair) que consta de unos 2 Kb de memoria ROM y 128 bytes de RAM (las 8 primeras posiciones
son empleadas como registros). Este procesador se encarga de detectar la pulsacin de las teclas, generando
unos bytes que las identifican y envindolos a continuacin por el cable a travs de un protocolo de
comunicacin en serie que en el AT consta de 11 bits por cada dato (1 de inicio, 8 de datos, 1 de paridad
y otro de stop) y 9 en los XT (entre otras razones, porque no se controla la paridad). Los teclados de AT y
de XT generan cdigos diferentes para las mismas teclas. Adems, al soltar una tecla, los teclados de XT
generan el mismo cdigo que al pulsarla pero con el bit 7 activo; sin embargo, en AT se generan dos cdigos
que se envan consecutivamente (0F0h y despus el mismo cdigo que al pulsarla). El teclado se encarga de
repetir los cdigos de una tecla cuando sta lleva cierto tiempo pulsada, en el conocido mecanismo autorepeat
de la mayora de los teclados. Muchos teclados tienen debajo un interruptor que permite seleccionar su modo
de funcionamiento (XT o AT).
El teclado en los PC y XT.
Los datos, cuando llegan al ordenador, reciben un tratamiento diferente en funcin de si el ordenador
es un XT o un AT, mucho ms sencillo en el primero. En los XT se van colocando los bits que llegan en
un simple registro de desplazamiento conectado al puerto 60h; al completarse los 8 se produce una
interrupcin de tipo IRQ 1 (INT 9), la segunda de mayor prioridad despus de la del temporizador. No
obstante, el teclado es capaz de memorizar hasta 8 pulsaciones cuando la CPU no tiene tiempo para atenderle.
Despus de leer el cdigo de la tecla, el programa que la gestione habr de enviar una seal de
reconocimiento a la circuitera del ordenador para permitir que contine la recepcin 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,
despus de traducirles adecuadamente para compatibilizar con los XT si as ha sido programado, enviarles
a la CPU: el 8042 de Intel. Tambin sirve de intermediario a las transmisiones de datos de la CPU al teclado,
que en el AT es un perifrico bidireccional que puede recibir comandos para configurar los LEDs, entre otras
tareas. Cuando el 8042 recibe un byte entero del teclado, inhibe la comunicacin hasta que la CPU lo acepta.
Si el dato se recibe con error de paridad, automticamente el 8042 lo solicita de nuevo al teclado enviando
un comando de reenvo al mismo y un byte 0FFh a la CPU indicando esta circunstancia, activando tambin
el bit 7 del registro de estado del 8042. Adems, chequea que no pasen ms de 2 milisegundos durante la
recepcin: si se excede este lmite se enva tambin un 0FFh a la CPU y se activa el bit 6 en el registro de
estado. Cuando la CPU enva algo al teclado, el 8042 inserta el bit de paridad automticamente. Si el teclado
no empieza la comunicacin en menos de 15 milisegundos o tarda en recibir el dato ms de 2 milisegundos,
se enva un 0FEh a la CPU y se activa el bit 5 en el registro de estado. Adems, el teclado ha de responder
a todas las transmisiones con un byte de reconocimiento, si en esta operacin hay un error de paridad se
activarn los bits 5 y 7 en el registro de estado; si tarda ms de 25 milisegundos en responder tambin se
enva el byte 0FEh a la CPU y se activan los bits 5 y 6 del registro de estado.
La comunicacin teclado-CPU puede ser inhibida por hardware por medio de la llave que incorpora
la unidad central, aunque la comunicacin CPU-teclado sigue habilitada. El 8042 se apoya en tres registros
352 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
bsicos: uno de estado, uno de salida y otro de entrada. El registro de estado, del que ya se ha explicado parte
de su funcionalidad, se encuentra en el puerto de E/S 64h y puede ser ledo en cualquier momento. El
significado de sus bits se explica en el cuadro 1.
El registro de salida est ubicado en el puerto 60h y es de slo lectura; el 8042 lo usa para enviar
los cdigos de las teclas a la CPU y los bytes de datos de los comandos que los soliciten. Debera ser ledo
slo cuando el bit 0 del registro de estado est activo.
El registro de entrada del 8042 es de slo escritura y puede ser accedido por los puertos 60h y 64h
segn que lo que se quieran enviar sean datos o comandos al 8042, respectivamente; los datos sern
reenviados por el 8042 hacia el teclado a menos que el propio 8042 est esperando un dato de la CPU a
consecuencia de un comando previo enviado por sta. Los datos deben ser escritos en este registro slo
cuando 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 y
entrada son accedidos por el mismo puerto (60h), siendo la lectura y escritura las que seleccionan el acceso
a uno u otro respectivamente.
BIT SIGNIFICADO
Registro de salida lleno. Un 1 indica que el 8042 ha colocado un dato en el registro de
0 salida y la CPU an no lo ha ledo. 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 de
entrada y el 8042 an no lo ha ledo.
2 Bandern 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: de
3 esta manera, el 8042 sabe si lo que se le enva son rdenes o datos (rdenes= 1). Ambos
puertos conectan con el registro de entrada.
4 Bit de inhibicin. Este bit se actualiza siempre que se coloca un dato en el registro de
salida, un 0 indica teclado inhibido.
5 Transmisin fuera de tiempo. Indica que la transmisin de un dato hacia el teclado no ha
sido respondida en los mrgenes de tiempo adecuados.
6 Recepcin fuera de tiempo. Indica si el teclado ha enviado un dato y sigue enviando ms
despus 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 parte
del ordenador. Estudiaremos ahora cules son esos comandos. En primer lugar, tras el arranque del ordenador
y al recibir la alimentacin el teclado, ste realiza un autotest denominado BAT (Basic Assurance Test) donde
chequea su ROM, RAM y enciende y apaga todos los LED. Esta operacin emplea entre 600 y 900
milisegundos; al acabar el BAT y cuando sea posible establecer la comunicacin con el ordenador (lneas de
reloj y datos en alto) enva un byte 0AAh si todo ha ido bien y un 0FCh si ha habido fallos; inicializando
despus los parmetros de autorepeticin de las teclas.
El teclado tiene un buffer interno con capacidad para 17 bytes (unas 8 teclas) con objeto de almacenar
las ltimas teclas pulsadas cuando no puede enviarlas al 8042. Cuando este buffer se llena, su ltima posicin
(17) se rellena con 0 y se ignoran las siguientes pulsaciones.
12.8.3 - COMUNICACIN CPU TECLADO
Los comandos al teclado pueden ser enviados en cualquier momento al puerto 60h: a menos que el
8042 est esperando por un byte de datos en el registro de entrada, como consecuencia de un comando
previo, redireccionar todo lo que se le enve por el puerto 60h hacia el teclado. El teclado responder en
menos de 20 milisegundos, devolviendo una seal de reconocimiento por medio de un byte 0FAh. Los
353 EL HARDWARE DE APOYO AL MICROPROCESADOR
principales comandos (diferenciados de los datos por tener el bit 7 activo) son:
- Reset (0FFh): Al recibirlo enva una seal de reconocimiento y se asegura de que la CPU se de por enterada
poniendo en alto las lneas de reloj y datos un mnimo de 500 microsegundos; el teclado permanece inhibido
hasta que la CPU acepta la seal de reconocimiento o enva otro comando que sobreescribe y anula ste.
Llegados a este punto, el teclado ejecuta de nuevo el BAT, estableciendo valores por defecto para la
autorepeticin y limpiando su registro de salida.
- Reenvo (0FEh): El sistema puede enviar este comando al teclado cuando detecta un fallo en la recepcin
desde el teclado. Este comando slo puede ser enviado despus de una transmisin del teclado y antes de
habilitar la comunicacin para la siguiente recepcin. 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 enva al registro de salida (en
el puerto 60h) dicho byte para que sea ledo.
60h Escribir el byte de comando del 8042. El siguiente byte que se enve al registro de entrada
(puerto 60h) ser el byte de comando del 8042.
AAh Autotest. El 8042 realiza un diagnstico interno y coloca un 55h en el registro de salida
si todo va bien.
Test del interface. El controlador chequea las lneas de reloj y datos devolviendo: 0 si no
ABh hay errores; 1: el reloj est demasiado en bajo, 2: est demasiado en alto; 3: la lnea de
datos est demasiado en bajo y 4: la lnea de datos est demasiado en alto.
ACh Volcado de diagnstico. Enva al registro de salida, sucesivamente, 16 bytes de la RAM del
8042, 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 (vase cuadro 4). Esto obliga al 8042 a leer el puerto de entrada
C0h y colocar lo que lee en el registro de salida; slo ha de emplearse este comando cuando el
registro de salida est vaco.
D0h Leer el puerto de salida. El 8042 lee el puerto de salida y lo coloca en el registro de sa-
lida; slo debe emplearse este comando si dicho registro est vaco.
D1h Escribir el puerto de salida (ver cuadro 5). El siguiente byte que se enve al registro de
entrada (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 comunicacin directa con el teclado.
Los bits 0 al 3 de este comando (la parte baja de este mismo comando) se relacionan con los
Fxh bits 0 al 3 del puerto de salida del 8042; un 0 indica bit pulsado durante 6 microsegundos
(aprx.) 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 autorepeticin a los valores habituales, limpia su registro
de salida y contina 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 las
teclas y permaneciendo inhibido hasta recibir ms instrucciones.
- Habilitar (0F4): Reanuda el funcionamiento interrumpido por el comando anterior o algn otro.
- Establecer ratio y retardo de autorepeticin (0F3h): Tras este comando debe enviarse otro inmediatamente
a continuacin, que se interpretar como dato, estableciendo los valores de autorepeticin. De este segundo
byte, el bit 7 estar siempre a cero; el valor de los bits 5 y 6, sumndole una unidad, indica el tiempo que
ha 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 nmero A; los bits 4 y 3 forman otro nmero B; por medio de la
siguiente frmula se obtiene la tasa o ratio de autorepeticin en teclas por segundo:
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 enva la acostumbrada seal de reconocimiento, deja de rastrear
las teclas y espera por el parmetro de autorepeticin, respondiendo al mismo con otra seal de
reconocimiento y volviendo a rastrear las teclas. Si en lugar de recibir el parmetro recibe otro comando (bit
7 activo) dejar inalterados los valores de autorepeticin y procesar dicho comando, aunque cuidado!:
permanecer inhibido hasta que se le habilite con el comando 0F4h. Por defecto, el sistema establece una tasa
de 10 caracteres por segundo y 0,5 segundos de espera (parmetro 4Ch).
BIT SIGNIFICADO
0 Activar la interrupcin del registro de salida lleno: un 1 indica que el 8042 genere
una IRQ1 (INT 9) tras colocar un dato en el registro de salida (esto es lo normal).
1 Reservado (escribir 0).
2 Bandern del sistema. Este bit define el bit 2 del registro de estado.
3 Ignorar inhibicin: con 1 se ignorar la funcin de inhibir el teclado.
4 Deshabilitar el teclado: un 1 baja la lnea de reloj inhibiendo la comunicacin del
8042 con el teclado.
5 Modo IBM PC. Con 1 no se traducen los cdigos del teclado ni se controla la paridad.
IBM PC compatibilidad. Un 1 selecciona la conversin de los cdigos del teclado para
6 emular los del PC y XT, traduciendo los cdigos 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- Lnea A20: 0 fuerza la lnea A20 de la CPU a 0, con lo que
lada la extensin de 256 Kb. 1 se prohbe acceder a la memoria por encima de 1 Mb lo cual
emula el direccionamiento de los PC/XT; un 1 deja que A20
5 A 0 si presente el puente (o la controle la CPU aunque hay PCs 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 vaco.
0: el teclado ha sido bloquea-
7 do con la llave externa de la 6 Lnea de reloj (comunicacin directa con el teclado).
unidad central.
7 Lnea de datos (comunicacin directa con el teclado).
CUADRO 4: BYTE RECIBIDO POR EL
PUERTO DE ENTRADA CUADRO 5: BYTE A ENVIAR AL PUERTO DE SALIDA
- No operacin (0F7h a 0FDh y 0EFh al 0F2h): Son cdigos reservados; el teclado al recibirlos enva la seal
de reconocimiento de siempre y no realiza ninguna accin.
- Eco (0EEh): Si el teclado recibe este comando, lo reenva a continuacin. Es una ayuda al diagnstico.
- Encender/apagar los LED (0EDh). Tras este comando se ha de enviar otro byte de datos, cuyos bits 0, 1
y 2 estn ligados al estado de los LED de Scroll Lock, Num Lock y Caps Lock, respectivamente; los dems
estn reservados. Al recibir el comando enva la correspondiente seal de reconocimiento y deja de rastrear
las 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 ocasin). El siguiente
ejemplo muestra cmo establecer los LED configurados en AH:
CLI
MOV AL,0EDh
OUT 60h,AL ; enviar comando
XOR CX,CX
355 EL HARDWARE DE APOYO AL MICROPROCESADOR
espera: JMP SHORT $+2 ; insertar estados de espera para AT obsoleto
JMP SHORT $+2
IN AL,64h
TEST AL,2
LOOPNZ espera ; esperar que reciba comando
MOV AL,AH
OUT 60h,AL ; establecer los LED
STI
En general, este ser el procedimiento a seguir para cualquier comando que requiera parmetros: hay
que esperar el momento adecuado para enviarlos; el LOOPNZ evita que la CPU se quede colgada si por
cualquier motivo fallara el teclado o el 8042. Como se ve, se establecen los 3 LED a la vez, aunque si slo
se desea cambiar uno habr que consultar el estado actual de los otros en las variables de la BIOS. No
obstante, este cambio es slo puntual ya que al pulsar las teclas que actan sobre los LED, la BIOS o el
KEYB los reajustarn anulando el cambio, siendo necesario reprogramar parcialmente la interrupcin del
teclado si se desea evitarlo.
12.8.4 - COMUNICACIN TECLADO CPU
Ms bien cabra llamarla la comunicacin teclado 8042: aunque muchos de estos cdigos acaben
siendo interpretados por la CPU, algunos se los queda el 8042 que siempre es el primero en enterarse. A
continuacin se listan los valores que el teclado puede enviar a la CPU o al 8042 en un momento dado.
- Reenvo (0FEh): El teclado puede enviar este comando a la CPU para solicitar el reenvo cuando detecta
un fallo en la recepcin (normalmente de paridad) o una entrada incorrecta.
- Reconocimiento ACK (0FAh): El teclado devuelte este valor cada vez que la CPU le enva algo, para
indicar que lo ha recibido (excepto en el caso de los comandos Eco y Reenvo de la CPU).
- Desbordamiento (0): Cuando la CPU intenta leer el teclado directamente sin haber cdigos en el buffer del
teclado (el buffer interno del propio teclado, se entiende) acceder a la posicin 17 del mismo, encontrndose
este valor.
- Fallo en el diagnstico (0FDh): El teclado peridicamente se autochequea y enva este cdigo si detecta
algn fallo. Si el fallo sucede durante el BAT, dejar de rastrear las teclas en espera de un comando de la
CPU; en cualquier otro momento continuar rastreando las teclas.
- Cdigo de tecla soltada break code (0F0h): El teclado enva este cdigo a la CPU para indicar que el
siguiente cdigo que enviar a continuacin corresponder a una tecla soltada. Bajo MS-DOS este cdigo lo
intercepta el 8042 y se lo oculta a la CPU, con objeto de emular el cdigo de tecla soltada de los PC/XT.
- BAT completado (0AAh): Despus de realizar el BAT el teclado enva un 0AAh para indicar que ha salido
bien, o un 0FCh (u otro valor) si ha habido fallos.
- Respuesta al eco (0EEh): El teclado enva este valor a la CPU si sta se lo ha enviado a l.
la comunicacin directa CPU teclado.
Debido a la presencia del 8042, normalmente no ser preciso que la CPU se comunique directamente
con el teclado a travs de las lneas de reloj y datos. No obstante, este captulo est explicado en el manual
de referencia tcnico del IBM AT, al menos en la edicin de 1984; por tanto, aquellos aficionados que estn
pensando construirse su propio ordenador y acoplarle un teclado ordinario de PC podran consultar ese libro.
Por cierto, en los PC y XT no es preciso tampoco realizar esta tarea, ya que el teclado con el conmutador
de seleccin de la parte inferior en modo XT no es realmente bidireccional (de hecho, lleva un control
autnomo de los LED) por lo que no tiene sentido intentar enviar nada. Y a la hora de recibir, hay mtodos
mucho ms cmodos...
356 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
12.9. - EL PUERTO SERIE: UART 8250.
La transmisin de datos en serie es una de las ms comunes para aquellas aplicaciones en las que la
velocidad no es demasiado importante, o no es posible conseguirla (por ejemplo, va red telefnica). Para
simplificar el proceso de enviar los bits uno por uno han surgido circuitos integrados que realizan la funcin,
teniendo en cuenta todos los tiempos necesarios para lograr una correcta comunicacin y aliviando a la CPU
de esta pesada tarea. El circuito que estudiaremos es el 8250 de National, fabricado tambin por Intel, aunque
las diferencias respecto al 16550 sern brevemente sealadas. Esta ltima UART es ms reciente y mucho
ms potente -aunque solo sea por unos pequeos detalles- y cada vez est ms extendida, en particular en
las actuales placas base.
La lnea que transmite los datos en serie est inicialmente en estado alto. Al comenzar la
transferencia, se enva un bit a 0 bit de inicio. Tras l irn los 8 bits de datos a transmitir (en ocasiones
son 7, 6 5): estos bits estn espaciados con un intervalo temporal fijo y preciso, ligado a la velocidad de
transmisin que se est empleando. Tras ellos podra venir o no un bit de paridad generado automticamente
por la UART. Al final, aparecer un bit (a veces un bit y medio dos bits) a 1, que son los bits de parada
o bits de stop. Lo de medio bit significa que la seal correspondiente en el tiempo a un bit dura la mitad;
realmente, en comunicaciones se utiliza el trmino baudio para hacer referencia a las velocidades, y
normalmente un baudio equivale a un bit. La presencia de bits de inicio y parada permite sincronizar la
estacin emisora con la receptora, haciendo que los relojes de ambas vayan a la par. A la hora de transmitir
los bytes de datos unos tras otros, existe flexibilidad en los tiempos, de ah que este tipo de comunicaciones
se consideren asncronas. La transmisin de los 8 bits de datos de un byte realmente es sncrona, pero las
comunicaciones en serie siempre han sido consideradas asncronas.
Para una transmisin en serie bsica bastan tres hilos. Sin embargo, el software que controla el puerto
serie a travs de la interfaz RS-232-C podra requerir ms seales de control para establecer la comunicacin,
al igual que para controlar un modem telefnico pueden hacer falta ms lneas (de control, no telefnicas...).
Bromas aparte, sobre comunicaciones en serie existe todo un mundo; acerca de este tema se han escrito
muchos libros completos. Lgicamente, aqu no vamos a dar ningn curso de comunicaciones en serie. Sin
embargo, los menos introducidos en la materia no deben temer: qu mejor manera de aprender sobre las
comunicaciones en serie que examinar cmo funciona un chip que las soporta?. Desde luego, tambin se
podra partir desde el punto de vista contrario, pero como entendido en sistemas digitales, el lector puede que
tenga menos problemas con este interesante enfoque.
12.9.1. - DESCRIPCIN 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 en
un solo chip una UART (Universal Asynchronous Receiver/Transmitter)
y un BRG (Baud Rate Generator). Soporta velocidades de hasta 625000
baudios con relojes de hasta 10 MHz. El BRG incorporado divide la
frecuencia base para conseguir las velocidades estndar de la RS-232-C.
SIGNIFICADO DE LAS LNEAS DEL 8250
DISTR: Data In Strobe. Lnea 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..CS2
para habilitar DISTR. En vez de DISTR se puede usar -DISTR, pero slo una de las dos.
DOSTR: Data Out Strobe. Idntico a DISTR pero en salida.
D0..D7: Data Bits 0..7: Bus triestado bidireccional de 8 lneas para transmitir datos, informacin
de control y de estado entre la CPU y el 8250. El primer bit enviado/recibido es D0.
A0..A2: Register Select. Lneas de entrada que indican el registro del 8250 usado en la operacin.
XTALx: Crystal/Clock: Conexiones para el cristal del cuarzo del BRG. XTAL1 puede actuar como
entrada de reloj externa, en cuyo caso XTAL2 debera 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 registro
de transmisin est vaco o en el modo lazo (LOOP) del 8250. No es afectado por -CTS.
-CTS: Clear To Send: Lnea de entrada. El estado lgico de esta seal puede consultarse en el
bit CTS del Modem Status Register (MSR) -como el bit CTS es el bit 4 del MSR se
357 EL 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: Lnea de entrada. El estado lgico de esta seal puede consultarse en MSR(5). DDSR (bit MSR(1)) indica si -DSR ha
cambiado 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 comunicacin con la estacin remota.
-DTR: Data Terminal Ready. Lnea de salida que puede activarse (poner a 0) escribiendo un 1 en MCR(0), y desactivarse escribiendo un 0 en
dicho bit o ante la activacin del pin MR. Con -DTR activo se indica al DCE que el 8250 puede recibir datos. En algunas circunstancias,
esta seal se usa como LED de power on. Si est inactivo, el DCE desconecta el modem del circuito de telecomunicaciones.
-RTS: Request To Send. Lnea de salida que habilita el modem. Se activa (poner a 0) escribiendo un 1 en MCR(1). Esta seal se pone en alto
en respuesta a MR. -RTS indica al DCE que el 8250 tiene un dato listo para transmitir. En la modalidad half-duplex, esta seal se utiliza
para controlar la direccin de la lnea.
-BAUDOUT: Esta lnea de salida contiene una seal de reloj 16 veces mayor que la frecuencia usada para transmitir. Equivale a la frecuencia de entrada
en el oscilador dividida por el BRG. La estacin receptora podra emplear esta seal conectndola a RCLK (para compartir el mismo reloj).
-OUTx: Estas dos salidas de propsito general se pueden activar (poner a 0) escribiendo un 1 en MCR(2) y MCR(3). Son desactivadas por la seal
MR. En el modo lazo (LOOP o bucle), estn tambin inactivas.
-RI: Ring Indicator. Esta lnea de entrada indica si el modem ha detectado que llaman por la lnea y puede consultarse en MSR(6). El bit TERI
(MSR(2)) indica si esta lnea ha cambiado desde la ltima lectura del MSR. Si las interrupciones estn habilitadas (IER(3) activo) esta
patilla provoca una interrupcin al activarse. -RI permanece activo durante el mismo intervalo de tiempo que la zona activa del ciclo de
llamada 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. Lnea de entrada que indica si el modem ha detectado portadora. Se puede consultar su estado lgico en MSR(7). El
bit MSR(3) indica si esta lnea ha cambiado desde la ltima lectura del MSR. Esta lnea no tiene efecto sobre el receptor. Si las
interrupciones estn permitidas, una interrupcin ser generada ante el cambio de esta lnea.
MR: Master Reset. Esta lnea de entrada lleva el 8250 a un estado inactivo interrumpiendo su posible actividad. El MCR y las salidas ligadas
al mismo son borradas. El LSR es borrado en todos sus bits salvo THRE y TEMT (que son activados). El 8250 permanece en este estado
hasta volver a ser programado.
INTRPT: Interrupt Request. Lnea de salida que se activa cuando se produce una interrupcin de alguno de estos tipos y est permitida: Recepcin
de bandern de error, dato recibido disponible, registro de retencin de transmisin vaco, y estado del modem. Esta lnea se desactiva con
el apropiado servicio de la interrupcin o ante MR.
SIN: Serial Data Input. Es la lnea de entrada de datos desde el modem. En el modo lazo (LOOP o bucle) estn inhibidas las entradas en SIN.
CS0..2: Chip Select. Estas entradas actan como lneas de habilitacin para las seales de escritura (DOSTR, -DOSTR) y lectura (DISTR, -DISTR).
CSOUT: Chip Select Out. Esta lnea 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 lnea de entrada est activa se enclavan las lneas A0..A2 y CS0..2; esto puede ser necesario si los pines de
seleccin de registro no son estables durante la duracin de la operacin de lectura o escritura (modo multiplexado). Si esto no es preciso,
esta seal se puede mantener inactiva (modo no-multiplexado).
RCLK: Esta lnea se corresponde con la entrada de reloj para la seccin receptora, equivalente a 16 veces la frecuencia empleada en la transmisin
y puede proceder del BAUDOUT de la estacin remota o de un reloj externo.
REGISTROS DEL 8250
El 8250 dispone de 11 registros (uno ms el 16550) pero slo 3 lneas de direccin para
seleccionarlos. Lo que permita distinguir unos de otros ser, aparte de las lneas de direcciones, el sentido
del 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 notacin para hacer referencia a un bit de un registro se escribe REG(i);
en este ejemplo, el bit DLAB sera LCR(7). Realmente, DLAB se emplea slo puntualmente para poder
acceder y programar los registros que almacenan el divisor de velocidad; el resto del tiempo, DLAB estar
a 0 para acceder a otros registros ms importantes.
A2 A1 A0 DLAB MODO NOMBRE SIGNIFICADO
0 0 0 0 R RBR Receiver Buffer Register (Registro buffer de recepcin)
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 retencin de transmisin)
0 0 1 0 R/W IER Interrupt Enable Register (Registro de habilitacin 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 identificacin 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 lnea) 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 lnea)
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)
358 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
1) LCR (Line Control Register). Controla el formato del carcter de datos.
Break Stick
DLAB Control Parity EPS PEN STB WLS1 WLS0
7 6 5 4 3 2 1 0
0 0 0 Sin paridad Word Length Select
0 0 1 Paridad impar 0 0 Datos de 5 bits
0 1 1 Paridad par 0 1 Datos de 6 bits
1 0 1 Marca (1) 1 0 Datos de 7 bits
1 1 1 Espacio (0) 1 1 Datos de 8 bits
Los bits WLS seleccionan el tamao del dato empleado. STB indica el nmero de bits de stop, que
pueden 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 generacin de bit de paridad, EPS (Even Parity Select) selecciona
paridad par si est a 1 (o impar en caso contrario). Stick Parity permite forzar el bit de paridad a un estado
conocido segn el valor de EPS. Cuando Break Control es puesto a 1, la salida SOUT se pone en estado
espacio (a 0), slo afecta a SOUT y no a la lgica de transmisin. Esto permite a la CPU alertar a un terminal
del sistema sin transmitir caracteres errneos o extraos si se siguen estas fases: 1) cargar un carcter 0 en
respuesta a THRE, 2) activar Break Control en respuesta al prximo THRE, 3) esperar a que el transmisor
est inactivo (TEMT=1) y bajar Break Control. Durante el Break, el transmisor puede usarse como un preciso
temporizador de carcter.
El bit DLAB (Divisor Latch Access Bit) puesto a 1 permite acceder a los Latches divisores DLL y
DLM 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 interrupcin.
0 TEMT THRE BI FE PE OE DR
7 6 5 4 3 2 1 0
Data Ready
Transmitter Transmitter Overrun Error
Empty Holding Parity Error
Register Framing Error
Empty Break Interrupt
DR est activo cuando hay un carcter listo en el RBR y es puesto a 0 cuando se lee el RBR. Los
bits 1 al 4 de este registro (OE, PE, FE y BI) son puestos a 0 al consultarlos -cuando se lee el LSR- y al
activarse pueden generar una interrupcin de prioridad 1 si sta interrupcin est habilitada. OE se activa para
indicar que el dato en el RBR no ha sido ledo por la CPU y acaba de llegar otro que lo ha sobreescrito. PE
indica si hay un error de paridad. FE indica si el carcter recibido no tiene los bit de stop correctos. BI se
activa cuando la entrada de datos es mantenida en espacio (a 0) durante un tiempo superior al de transmisin
de un carcter (bit de inicio + bits de datos + bit de paridad + bit de parada).
THRE indica que el 8250 puede aceptar un nuevo carcter para la transmisin: este bit se activa
cuando el THR queda libre y se desactiva escribiendo un nuevo carcter en el THR. Se puede producir, si
est habilitada; la interrupcin THRE (prioridad 3); INTRPT se borra leyendo el IIR. El 8250 emplea un
registro interno para ir desplazando los bit y mandarles en serie (el Transmitter Shift Register), dicho registro
se carga desde el THR. Cuando ambos registros (THR y el Transmitter Shift) estn vacos, 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 DTR
7 6 5 4 3 2 1 0
Data Terminal Ready
Request To Send
359 EL HARDWARE DE APOYO AL MICROPROCESADOR
Las lneas de salida -DTR, -RTS, -OUT1 y -OUT2 estn directamente controladas por estos bits;
como se activan a nivel bajo, son puestas a 0 escribiendo un 1 en estos bits y viceversa. Estas lneas sirven
para establecer diversos protocolos de comunicaciones.
El bit LOOP introduce el 8250 en un modo lazo (o bucle) de autodiagnstico. Con LOOP activo,
SOUT pasa a estado de marca (a 1) y la entrada SIN es desconectada. Los registros de desplazamiento
empleados en la transmisin y la recepcin 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 de
control del modem (-DTR, -RTS, -OUT1 y -OUT2) cuyos pines son puestos en estado inactivo (alto). En esta
modalidad de operacin (modo lazo o bucle), los datos transmitidos son inmediatamente recibidos, lo que
permite comprobar el correcto funcionamiento del integrado. Las interrupciones son completamente operativas
en este modo, pero la fuente de estas interrupciones son ahora los 4 bits bajos del MCR en lugar de las cuatro
entradas de control. Estas interrupciones estn an controladas por el IER.
4) MSR (Modem Status Register).
DCD RI DSR CTS DDCD TERI DDSR DCTS
7 6 5 4 3 2 1 0
Delta Trailing Delta Delta
Data Data Clear Data Edge Data Clear
Carrier Ring Set To Carrier of Ring Set To
Detect Indicator Ready Send Detect Indicator Ready Send
Adems de la informacin de estado del modem, los 4 bits bajos (DDCD, TERI, DDSR, DCTS)
indican si la lnea correspondiente, en los 4 bits superiores, ha cambiado de estado desde la ltima lectura
del MSR; en el caso de TERI slo indica transiciones bajo- alto en -RI (y no las de sentido contrario). La
lnea CTS del modem indica si est listo para recibir datos del 8250 a travs de SOUT (en el modo lazo este
bit equivale al bit RTS del MCR). La lnea DSR del modem indica que est listo para dar datos al 8250 (en
el modo lazo -o LOOP- equivale al bit DTR del MCR). RI y DCD indican el estado de ambas lneas (en el
modo lazo se corresponden con OUT1 y OUT2 respectivamente). Al leer el MSR, se borran los 4 bits
inferiores (que en una lectura posterior estaran a 0) pero no los bits de estado (los 4 ms significativos).
Los bits de estado (DCD, RI, DSR y CTS) reflejan siempre la situacin de los pines fsicos
respectivos (estado del modem). Si DDCD, TERI, DDSR DCTS estn a 1 y se produce un cambio de
estado durante la lectura, dicho cambio no ser reflejado en el MSR; pero si estn a 0 el cambio ser
reflejado despus de la lectura. Tanto en el LSR como en el MSR, la asignacin de bits de estado est
inhibida durante la lectura del registro: si se produce un cambio de estado durante la lectura, el bit
correspondiente ser activado despus de la misma; pero si el bit ya estaba activado y la misma condicin
se 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 la
frecuencia 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 emplear
menos de 8, los bits superiores de este registro quedan a 0. Los datos entran en serie por SIN (comenzando
por el bit D0) en un registro de desplazamiento gobernado por el reloj de RCLK, sincronizado con el bit de
360 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
inicio. Cuando un carcter completa el registro de desplazamiento de recepcin, sus bits son volcados al RBR
y el bit DR del LSR es activado para indicar a la CPU que puede leer el RBR. El diseo del 8250 permite
la recepcin continua de datos sin prdidas: el RBR almacena siempre el ltimo carcter recibido dando
tiempo suficiente a la CPU para leerlo mientras simultneamente est cargando el registro de desplazamiento
con el siguiente; si la CPU tarda demasiado un nuevo dato podra aparecer en el RBR antes de haber ledo
el anterior (condicin de overrun, bit OE del LSR).
8) THR (Transmitter Holding Register).
El registro de retencin de transmisin almacena el siguiente carcter que va a ser transmitido en serie
mientras el registro de desplazamiento de transmisin est enviando el carcter actual. Cuando el registro de
desplazamiento se vace, ser cargado desde el THR para transmitir el nuevo carcter. Al quedar vaco THR,
el bit THRE del LSR se activa. Cuando estn vacos tanto el THR como el registro de desplazamiento de
transmisin, el bit TEMT del LSR se activa.
9) SCR (Scratchpad Register).
Este registro no es empleado por el 8250, y de hecho no exista en las primeras versiones del
integrado. 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 lnea de recepcin.
2) Dato recibido disponible.
3) Registro de retencin de transmisin vaco.
4) Estado del modem.
La informacin que indica que hay una interrupcin pendiente y el tipo de la misma es almacenada
en el IIR. El IIR indica la interrupcin de mayor prioridad pendiente. No sern reconocidas otras
interrupciones hasta que la CPU enve la seal de reconocimiento apropiada. En el registro IIR, el bit 0 indica
si hay una interrupcin pendiente (bit 0=0) o si no la hay (bit 0=1), esto permite tratar las interrupciones en
modo polled consultando este bit. Los bits 1 y 2 indican el tipo de interrupcin. Los restantes estn a 0 en
el 8250, pero el 16550 utiliza alguno ms.
DCD RI DSR CTS DDCD
7 6 5 4 3 2 1 0
1 - Interrupcin pendiente
1 1 - Colas FIFO activadas en 16550 X X - Identificacin de la Interrupcin
0 0 - Colas FIFO no activadas A 1 en el 16550 si pendiente la interrupcin TIMEOUT
IDENTIFICACIN DE LA INTERRUPCIN ACTIVACIN / RECONOCIMIENTO (RESET) DE LA INTERRUPCIN
Bit 2 Bit 1 Bit 0 Prioridad Flag Fuente Reconocimiento
X X 1 Ninguno Ninguna
1 1 0 Primera Lnea de estado OE, PE, Leer LSR
del receptor FE BI
1 0 0 Segunda Recibido dato Recibido dato Leer RBR
disponible disponible
0 1 0 Tercera THRE THRE Leer IIR si es la fuente de
interrupcin, o escribir THR
0 0 0 Cuarta Estado del modem -CTS, -DSR Leer MSR
-RI, -DCD
361 EL HARDWARE DE APOYO AL MICROPROCESADOR
11) IER (Interrupt Enable Register).
Este registro de escritura se utiliza para seleccionar qu interrupciones activan INTRPT y, por
consiguiente, van a ser solicitadas a la CPU. Deshabilitar el sistema de interrupciones inhibe el IIR y
desactiva 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 interrupcin de dato disponible.
IER1: A 1 si habilitar interrupcin de registro de retencin de transmisin vaco.
IER2: A 1 si habilitar interrupcin de error de recepcin (bits 1 al 4 del LSR).
IER3: A 1 si habilitar interrupcin ante el cambio del MSR (Registro de estado del modem).
El 16550 genera tambin una interrupcin de TIMEOUT (prioridad 1) si hay datos en la cola FIFO
y no son ledos dentro del tiempo que dura la recepcin de 4 bytes o si no se reciben datos durante el tiempo
que tomara recibir 4 bytes.
12) FCR (FIFO Control Register). Slo disponible en el 16550, no en el 8250.
7 6 5 4 3 2 1 0
1 - Habilita el
Tamao 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 1
1 0 - 8 bytes 1 - Borrar cola RCVR
1 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 cola
afectada es borrada y el bit es devuelto a 0. Los registros de desplazamiento de la transmisin y la recepcin,
en cada caso, no resultan afectados.
LA TRANSMISIN Y LA RECEPCIN EN EL 8250
La seccin de transmisin del 8250 consiste en el Registro de Retencin de transmisin (THR), el
Registro de Desplazamiento de la Transmisin (TSR) y en la lgica de control asociada. Dos bits en el LSR
indican si est vaco el THR (bit THRE) o el TSR (bit TEMT). El carcter de 5-8 bits a ser transmitido es
escrito en el THR; la CPU debera realizar esta operacin slo si THRE est activo: este bit es activado
cuando el carcter es copiado del THR al TSR durante la transmisin del bit de inicio.
Cuando el transmisor est inactivo, tanto THRE como TEMT estn activos. El primer carcter escrito
provoca que THRE baje; tras completarse la transferencia vuelve a subir aunque TEMT permanecer bajo
mientras dure la transferencia en serie del carcter a travs de TSR. Si un segundo carcter es escrito en THR,
THRE vuelve a bajar y permanecer bajo hasta que el TSR termine la transmisin, porque no es posible
volcar el contenido de THR en TSR hasta que este ltimo no acabe con el carcter que estaba transmitiendo.
Cuando el ltimo carcter ha sido transmitido fuera del TSR, TEMT vuelve a activarse y THRE tambin lo
har tras un cierto tiempo (el que tarda en escribirse THR en TSR).
En la recepcin, los datos en serie asncronos entran por la patilla SIN. El estado inactivo de la lnea
se considera el 1 lgico. Un circuito de deteccin de bit de inicio est continuamente buscando una
transicin alto bajo que interrumpa el estado inactivo. Cuando la detecta, se resetea un contador interno
y cuenta 7 pulsos de reloj (tener en cuenta que la frecuencia base es dividida por 16), posicionndose en
el centro del bit de inicio. El bit de inicio se considera vlido si SIN contina an bajo en ese momento. La
validacin del bit de inicio evita que un ruido espreo en la lnea sea confundido con un nuevo carcter.
362 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
El LCR tiene toda la informacin necesaria para la recepcin: tamao del carcter (5-8 bits), nmero
de bits de stop, si hay paridad o no... la informacin de estado que se genere ser depositada en el LSR.
Cuando un carcter es transmitido desde el Registro de Desplazamiento de la Recepcin (RSR) al Registro
Buffer de Recepcin (RBR), el bit DR del LSR se activa. La CPU lee entonces el RBR, lo que hace bajar
de nuevo DR. Si el carcter no es ledo antes de que el siguiente carcter que se est formando pase del RSR
al RBR, el bit OE (overrun) del LSR se activa. Tambin se puede activar PE en el LSR si hay un error de
paridad. Finalmente, la circuitera que chequea la validez del bit de stop podra activar el bit FE del LSR en
caso de error.
El centro del bit de inicio se define como 7 pulsos de reloj; si los datos que entran por SIN
constituyen una onda cuadrada simtrica, el centro de las celdas que contienen los bits se desviar a lo sumo
un 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 seales de reloj para el funcionamiento de la UART, permitiendo los ratios de
transferencia del estndar ANSI/CCITT. Se puede conectar un cristal a XTAL1 y XTAL2 una seal de reloj
a XTAL1. La salida -BAUDOUT puede excitar la lnea XTAL1 de otro 8250.
La velocidad es determinada por los registros DLL y DLM almacenando un valor divisor de la
frecuencia del reloj conectado al 8250. El resultado debe ser 16 veces mayor que la frecuencia en baudios
deseada, ya que el 8250 utiliza 16 pulsos de reloj para cada bit. El siguiente cuadro resume los valores que
hay que asignar al divisor para lograr las frecuencias ms usuales con los cristales ms 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 % error
finales para 16xReloj si lo hay para 16xReloj si lo hay para 16xReloj si lo hay
50 2304 3072 3840
75 1536 2048 2560
110 1047 0.026 1396 0.026 1745 0.026
134.5 857 0.058 1142 0.0007 1428 0.034
150 768 1024 1280
300 384 512 640
600 192 256 320
1200 96 128 160
1800 64 85 0.392 107 0.315
2000 58 0.69 77 0.260 96
2400 48 64 80
3600 32 43 0.775 53 0.628
4800 24 32 40
7200 16 21 1.587 27 1.23
9600 12 16 20
19200 6 8 10
38400 3 4 5
56000 2 2.86 - -
RESET DEL 8250
Tras dar corriente al 8250 hay que tenerlo unos 500 ns con MR alto para resetearlo. Un nivel alto
en MR provoca:
1) Se inicializan los contadores internos de transmisin y recepcin.
2) Se limpia el LSR salvo en sus bits TEMT y THRE (que son puestos a 1).
MCR, todas las lneas discretas, elementos de memoria y dems 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.
363 EL HARDWARE DE APOYO AL MICROPROCESADOR
Por software se puede forzar al 8250 a retornar a un estado totalmente conocido. Dicho reset consiste
en escribir el LCR, DLL y DLM, as como MCR. LSR y RBR deberan ser ledos antes de habilitar las
interrupciones para borrar cualquier informacin residual (datos o estado) de las operaciones anteriores.
REGISTRO / SEAL 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, dems siempre a 0
LCR MR Todos los bits a 0
MCR MR Todos los bits a 0
LSR MR Todos los bits a 0, salvo el 5 y el 6 (a 1)
MSR MR Bits 0..3 a 0, bits 4..7 seal de entrada
SOUT MR En alto
INTRPT (RCVR error) Leer LSR / MR En bajo
INTRPT (RCVR dato listo) Leer RBR / MR En bajo
INTRPT (THRE) Leer IIR / Escribir THR / MR En bajo
INTRPT (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
PROGRAMACIN DEL 8250
El 8250 se programa a travs de los registros de control LCR, IER, DLL, DLM y MCR. Aunque los
registros de control pueden ser escritos en cualquier orden, IER debe ser escrito al final porque controla la
habilitacin de las interrupciones. Una vez que el 8250 ha sido programado, los registros pueden ser
actualizados 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 direccin de memoria base de los puertos serie. A esta direccin de memoria base habr
que sumar el desplazamiento relativo del nmero de registro a ser accedido.
El principal problema reside en que slo estn previstas 2 interrupciones para los puertos serie. Ello
implica que generalmente slo 2 de los puertos podrn emplear interrupciones a un tiempo, debido a la
arquitectura del bus ISA. Generalmente COM1 y COM3 compartirn la IRQ4 (INT 0Ch) y COM2/COM4
la IRQ3 (INT 0Bh). Estas asignaciones pueden ser cambiadas por el usuario actuando sobre los switches de
configuracin de las tarjetas (que en ocasiones permiten incluso elegir la IRQ5). Por tanto, no est de ms
tener cuidado en los programas y permitir un cierto grado de configuracin en estas cuestiones.
OFFSET DLAB MODO NOMBRE SIGNIFICADO
0 0 R RBR Receiver Buffer Register (Registro buffer de recepcin)
0 1 R/W DLL Divisor Latch LSB (Divisor de velocidad, parte baja)
0 0 W THR Transmitter Holding Register (Registro de retencin de transmisin)
1 0 R/W IER Interrupt Enable Register (Registro de habilitacin de interrupciones)
1 1 R/W DLM Divisor latch MSB (Divisor de velocidad, parte alta)
2 X R IIR Interrupt Identification Register (Registro de identificacin 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 lnea) 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 lnea)
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 direccin E/S base
del puerto serie para acceder a sus registros. COM1 suele estar en 3F8h, COM2 en 2F8h, COM3 en 3E8h
y COM4 en 2E8h. Sin embargo, es mejor acceder a las variables de la BIOS para obtener la direccin.
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 travs de los
dispositivos COM1: (conocido tambin como AUX:) al COM4: se realiza tambin apoyndose en esta
interrupcin. 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 la
transmisin 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 frecuencia
dividida por 16 de cara a calcular el valor para el divisor. Por tanto, la velocidad mxima que puede alcanzar
Baudios Divisor a emplear en el PC
ms
comunes Divisor DLM DLL
50 2304 9 0
110 1047 4 23
150 768 3 0
300 384 1 128
1200 96 0 96
2400 48 0 48
4800 24 0 24
9600 12 0 12
14400 8 0 8
19200 6 0 6
28800 4 0 4
38400 3 0 3
57600 2 0 2
115200 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 10
baudios (1 bit de inicio, 8 de datos, 1 de stop), lo que permite
alcanzar 11520 bytes/seg (11.25 Kb/seg). Para distancias de
pocos metros (no decenas ni centenas) no habr problemas,
incluso para distancias algo mayores si los cables se disean con
cuidado. La programacin del puerto serie en el PC a nivel de
hardware es necesaria a menudo por dos razones de mucho
peso: poder utilizar interrupciones y emplear velocidades
superiores a 9600 baudios. Por supuesto, en estas transferencias
los paquetes deberan llevar algn control de errores, aunque no
precisamente basado en la paridad.
Nota: El bit OUT2 del MCR controla en los PC la salida de la lnea INTRPT. Esto significa que si dicho bit, por defecto
inicializado 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. Tambin se podra inhibir la INTRPT a travs del 8259, por lo que este
dato no es muy importante, con la excepcin de evitar que una involuntaria e incorrecta asignacin de OUT1 y OUT2
inhiba las interrupciones. La ventaja de inhibir las interrupciones en el 8250 radica en la posibilidad de utilizar
plenamente todas sus funciones incluso en el modo de no interrupciones: el olvido del diseador de incluir esta
caracterstica oblig a IBM a utilizar para este fin OUT2. Realmente, el 8250 est concebido para ser utilizado por
medio de interrupciones, y hay quien duda incluso de la veracidad de la afirmacin del fabricante acerca del double
buffering (buffers duplicados) que son muy aconsejables al trabajar sin interrupciones.
12.9.3. - EJEMPLO: AUTODIAGNSTICO DEL 8250.
El siguiente programa de ejemplo coloca el 8250 en modo lazo (LOOP) y seguidamente comienza
a transmitir datos de 8 bits (desde 0 hasta 255) comprobando que le llegan los mismos datos que enva y sin
que se produzcan errores. Se permite elegir el puerto deseado as como la velocidad de transmisin.
/*********************************************************************
* *
* 8250T.C 1.0 - UTILIDAD DE AUTODIAGNOSTICO DEL 8250 EN TURBO C *
* *
* (c) 1993 Ciriaco Garca de Celis. *
* *
*********************************************************************/
#include <dos.h>
#include <conio.h>
#define LCR (base+3) /* registro de control de lnea */
#define IER (base+1) /* registro de activacin 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 lnea */
#define RBR (base+0) /* registro buffer de recepcin */
#define THR (base+0) /* registro de retencin de transmisin */
#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 vaco */
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 Garca 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 vaco */
lsr=inportb(LSR);
if (lsr & (OE|PE|FE|BI)) error();
} while (!(lsr & THRE));
outportb (THR, dato); /* enviar carcter */
do { /* esperar por RBR lleno */
lsr=inportb(LSR);
if (lsr & (OE|PE|FE|BI)) error();
} while (!(lsr & DR));
entrada=inportb (RBR); /* recibir carcter */
if (dato!=entrada) error();
printf ("\rEnviado y recibido byte %d",dato);
}
if (!kbhit())
printf("\rAutodiagnstico del 8250 en COM%d superado.\n", com);
else
{ getch(); printf("\rTecla pulsada - prueba abortada.\n");}
}
365 EL HARDWARE DE APOYO AL MICROPROCESADOR
12.10. - EL PUERTO DE LA IMPRESORA.
La impresora se controla desde el DOS referencindola como dispositivo LPT1 (PRN) LPT2. La
BIOS utiliza la INT 17h para los servicios de impresora. En ambos casos, el funcionamiento es realmente
trivial 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 acompaadas
de un manual tcnico de programacin (o en su defecto se puede adquirir opcionalmente). Lo que veremos
a continuacin son los registros a bajo nivel del puerto paralelo, as como pistas para una utilizacin algo ms
all de la impresora: la comunicacin entre ordenadores.
12.10.1. - LOS REGISTROS DEL PUERTO PARALELO.
La direccin base del puerto paralelo en los ordenadores compatibles depende del tipo de adaptador
que incorporen. Las primeras mquinas traan un puerto paralelo en el adaptador de vdeo monocromo, cuya
direccin base es 3BCh. Sin embargo, otros adaptadores utilizan la direccin base 378h para LPT1 y 278h
para LPT2. Por fortuna, la BIOS tiene en el rea de datos una tabla con las direcciones base de los 4 posibles
puertos paralelos. Dicha tabla comienza en 40h:8 y consta de 1 palabra por puerto (a 0 si ese puerto no
existe). La asignacin que realizan diversas BIOS puede ser un tanto discutible, pero si el usuario no ve salir
los 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 direccin base (3BCh, 378h, 278h, etc.). Este registro es de slo escritura, para enviar los
caracteres a la impresora. El siguiente registro, de slo lectura, es el registro de estado, inmediatamente a
continuacin del anterior (3BDh, 379h, 279h). Finalmente, tras ellos hay un registro de slo escritura, el
registro de control (en 3BEh, 37Ah, 27Ah). Aunque en los tres casos he indicado la direccin, hay que tener
en cuenta que lo correcto es consultar la variable de la BIOS y tomarla como punto de partida.
Los registros de estado y control estn asociados a unas lneas fsicas del puerto paralelo estndar,
y poseen un significado concreto que resumimos a continuacin. En el valor pin se hace referencia al pin del
puerto paralelo del ordenador y al correspondiente en la impresora (ordenador/impresora). Las lneas o pines
que no aparecen aqu son las de datos (lneas 2 a la 9, conectadas tambin con las lneas 2 a la 9 del lado
de la impresora; las restantes estn 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 recepcin del carcter.
- 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 carcter en el registro de datos.
- Bit 1: pin 14/14 (-AUTO FEED). A 1 si la impresora debe saltar lnea tras cada cdigo 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 interrupcin de la impresora.
- Bits 5-7: no utilizados.
La posibilidad de emplear interrupciones es realmente interesante: cuando la seal -ACK se pone a
nivel 0 (esto es, se activa) viene una IRQ7 una IRQ5 (segn cmo est configurada la tarjeta). De todos
modos, habr que mandar primero un carcter por el mtodo tradicional para iniciar la transmisin. La BIOS,
sin embargo, no utiliza la interrupcin de la impresora.
12.10.2. - ENVO 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 seales, para evitar lios: los nombres de las
366 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
seales les tomaremos como referencia, sin considerar su polaridad. Para enviar un carcter, primero se le
coloca en el registro de datos. A continuacin se pone a 0 en el registro de control el bit de STROBE. Este
bit debe estar muy poco tiempo activo, para evitar que la impresora lea dos veces el mismo carcter (del
orden de un microsegundo). Como la impresora no tiene una capacidad de aguante ilimitada, se puede
defender poniendo el bit de BUSY en el registro de estado a 0 para poder leer con tranquilidad el STROBE
que le llega. Cuando lo haya ledo, pondr un 0 en ACK para indicar que ya ha recibido el carcter.
Este es el esquema bsico del envo de caracteres. Sin embargo, hay que tener en cuenta que la
impresora puede devolver ciertas condiciones de error, tanto leves (falta de papel) como ms graves, como
el caso de ERROR. Tambin el ordenador puede provocar ciertos efectos en la impresora, a travs del registro
de control, como vimos anteriormente. Quiz el ms curioso es el del AUTO FEED: ya se podan haber
puesto de acuerdo el primer da, resulta triste que adems de perder horas configurando impresoras y
programas, hasta el propio puerto pueda meter las narices en el control del salto de lnea...
12.10.3. - CABLE NULL-MODEM PARA CONECTAR DOS ORDENADORES.
Anteriormente hemos visto una descripcin de patillas del puerto paralelo suficiente para que
cualquiera se pueda construir su propio cable centronics. De todas formas, estos cables afortunadamente se
venden ya construidos por un precio poco aceptable. Los que no se venden, aunque s acompaan a ciertas
aplicaciones software e incluso hardware (como disqueteras externas va puerto de impresora) permiten una
comunicacin bidireccional. El truco consiste en utilizar las lneas del registro de estado para recibir datos,
aunque esto limita la transferencia a 5 bits (realmente 4, ms otro para el protocolo de transferencia).
Se toman dos conectores centronic 25-pin machos. Se unen los pins de la siguiente forma:
2 15
3 13
4 12
5 10
6 11
10 5
11 6
12 4
13 3
15 2
18 18
El motivo de emplear esta asignacin y no otra se debe a que es la ya utilizada por ciertas
aplicaciones comerciales, como LAPLINK. Es por razones de compatibilidad, para que no pase como con
los saltos de lnea. La lnea comn (18) es masa, aunque valdra cualquier patilla entre la 18 y la 25; si se
emplea un cable de 10 hilos ms malla, esta ltima es la ms adecuada para hacer de masa.
Con este cable, para enviar datos se utilizan las lneas D0 a D4 del registro de datos y para recibirlos
las 5 lneas tiles del registro de estado. Como D0-D1-D2-D3-D4 estn conectados en este mismo orden a
ERROR-SLCT-PE-ACK-BUSY, lo ideal es utilizar D0-D3 para transmitir datos y ERROR-SLCT-PE-ACK
para recibirlos. Las seales BUSY y D4 sirven para establecer el protocolo de transmisin. La transferencia
puede ser bidireccional y adems de forma simultnea. En realidad, cuando se mande un dato y el ordenador
remoto indique con BUSY que ya lo tiene (a travs de su lnea D4), de paso nos puede haber reenviado el
dato en D0-D3 para que veamos si es correcto: un control de errores bastante fiable y rpido. Sin embargo,
se podra aprovechar quiz para enviar otro medio byte en sentido contrario en el caso de que las dos
mquinas se estn pasando informacin simultneamente la una a la otra; el control de errores ya se hara
de otra manera, a nivel de bloques con checksum, etc. Conviene aprovechar y mandar otros 4 bits de datos
cada vez que se enva un reconocimiento (al informar al receptor de que ya se ha recibido su seal de "dato
recibido"), lo que permite transferir un byte completo en cada ciclo del protocolo de transferencia. Ah, no
hay que olvidar la polaridad de las lneas: 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 ms la transmisin ser fiable, y adems bastante rpida:
4 bits en paralelo, a la velocidad que pueda alcanzar la CPU del ordenador ms lento. No emplear el
ensamblador sera un acto imperdonable.
367 EL HARDWARE DE APOYO AL MICROPROCESADOR
12.11. - EL RATN.
El ratn se controla normalmente a travs de llamadas a la INT 33h. Existen toda suerte de funciones
para controlar su posicin, el estado de los botones, el puntero que se visualiza... todas ellas son bastante
intuitivas y aptas para un programador en lenguajes de alto nivel. Aqu estudiaremos, sin embargo, el
funcionamiento a bajo nivel del ratn. En concreto, del ratn de Microsoft, el ms extendido y con el que
son compatibles casi todos los dems (aunque sea accionando el correspondiente conmutador).
La mayora de los ratones se conectan va puerto serie a 1200 baudios, 7 bits y sin paridad. Para
detectar la presencia del ratn, hay que poner la lnea DTR del puerto serie a 1. Al cabo de un rato, el ratn
devuelve el cdigo ASCII de la letra M (ser por lo de Mouse o por Microsoft?). Los controladores de
Microsoft son un poco estrictos en esta comprobacin, y si el ratn no responde en unos mrgenes de tiempo
muy concretos consideran que no existe, de ah que en ocasiones haya que emplear otro controlador un poco
ms flexible.
Llegados a este punto, el funcionamiento se establece a partir de interrupciones de puerto serie. Se
trasmiten 3 bytes cada vez que hay un envo: en ellos se indica cunto se ha movido el ratn en los ejes X
e Y desde la ltima vez, as como el estado de los botones. La unidad de medida, cmo no, son los Mickeys,
que segn la resolucin del aparato sern 1/200 1/400 pulgadas.
Los desplazamientos se toman en complemento a dos; como hay 8 bits por cada eje, el movimiento
puede oscilar en el rango +128 a -127. Hay adems un bit por cada botn. De los 7 bits recibidos en cada
interrupcin, el ms significativo (bit 6) est a 1 en el primer envo y a 0 en los restantes, con objeto de
evitar malas interpretaciones de la secuencia si se pierde alguna interrupcin por cualquier motivo. El formato
empleado para codificar la informacin 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 estndar de ratn, el Mouse Systems, permite trabajar hasta con tres botones. Estos
ratones envan (cuando estn en modo Mouse) 5 bytes por cada evento. En el primero hay informacin sobre
el estado de los botones; los 4 siguientes parecen contener el desplazamiento relativo en los ejes X e Y. El
funcionamiento es, por tanto, similar, y al parecer quiz todava con 7 bits. Curiosamente, al conmutar el
selector de modo (Microsoft-Mouse) aparece una secuencia de bytes un tanto especial, distinta segn el
sentido de la conmutacin, para ayudar al controlador de ratn a detectar el paso al nuevo protocolo con
objeto de poder adaptarse al mismo.
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. - DESCRIPCIN DEL INTEGRADO.
El MC146818 incorpora un completo reloj con alarma, calendario, interrupcin peridica
programable, generador de onda cuadrada y 64 bytes libres de RAM esttica de bajo consumo. Los primeros
10 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 disposicin 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 lnea OSC1 (de entrada) puede conectarse a seales
cuadradas de 4.194304 Mhz, 1.048576 Mhz y 32768 Hz. La
frecuencia de esta base de tiempos, como se ver, ha de
indicarse en el registro A (bits DV0 a DV2). El chip provee una
til salida de reloj en CKOUT dependiente del nivel de la
entrada CKFS, segn la siguiente tabla:
Seal en OSC1 Nivel de CKFS Seal en CKOUT
4,194304 Mhz 1 4,194304 Mhz
4,194304 Mhz 0 1,048576 Mhz
1,048576 Mhz 1 1,048576 Mhz
1,048576 Mhz 0 262,144 KHz
32,768 Khz 1 32,768 Khz
32,768 Khz 0 8,192 Khz
La salida SQW genera una onda cuadrada, cuya
frecuencia es programable (til para alarmas). La lnea -IRQ se
encarga de solicitar las interrupciones peridicas si estn habilitadas. La lnea de entrada -RESET reinicializa
el integrado asignando valores por defecto a ciertos bits de los registros B
y C, aunque no afecta a la fecha/hora ni a la memoria. La entrada PS debe
mantenerse a nivel bajo cuando se alimenta el chip hasta que la tensin se
estabilice, ponindose despus en alto; esta entrada est asociada al bit
VRT del registro D que indica si el integrado est en condiciones de
operar. El bus bidireccional de direcciones y datos est multiplexado
(lneas AD0..AD7): en los flancos de bajada de la entrada de validacin de
direcciones (lnea AS) contiene direcciones, y datos en los flancos de
subida de la entrada de validacin de datos (lnea DS). La lnea -R/-W
indica si la operacin es de entrada o salida; -CE permite habilitar el chip
o desconectarlo de los buses.
El cuadro de la derecha refleja la estructura de la memoria del
MC146818. 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 Ao
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 interrupcin peridica programable y la de la salida SQW. Tambin contiene un bit que indica si hay
una actualizacin del reloj en curso, lo que sucede una vez cada segundo, ya que en ese preciso instante no
se 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 MHz
0 0 1 Reloj de 1,048576 MHz Tipo de reloj conectado
0 1 0 Reloj de 32768 Hz
369 EL HARDWARE DE APOYO AL MICROPROCESADOR
El bit UIP (Update In Progress), de slo lectura, se pone a 1 mientras se actualizan los primeros 14
bytes de la memoria y poco tiempo antes de que comience dicha actualizacin. Antes de acceder a estos
bytes, hay que esperar a que el bit UIP se ponga a cero (si no lo estaba ya): con el bit UIP a 0, es seguro
que en un intervalo de al menos 244 microsegundos no se va a producir ninguna actualizacin, por lo que
hay tiempo suficiente para acceder (sin prisas, pero tampoco con pausas). La actualizacin dura 248
microsegundos (1984 con relojes de 32768 Hz).
Los bits RS0..RS3, de seleccin de velocidad, definen la frecuencia de la onda cuadrada generada
en SQW y/o la de la interrupcin peridica, 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 acta) (nula) (no acta) (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 inicializacin de la fecha y hora, para
habilitar o inhibir las diversas interrupciones y para establecer ciertas caractersticas de operacin.
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 actualizacin de los primeros 14
bytes de la RAM resulta abortado: de este modo, es factible proceder a inicializar la fecha y la hora sin el
riesgo de que se produzca en medio una actualizacin. Este bit no se ve afectado por la seal -RESET.
El bit PIE (Periodic Interrupt Enable) sirve para permitir la interrupcin peridica cuando es puesto
a 1; tras una seal -RESET es puesto a 0. El bit AIE (Alarm Interrupt Enable) ha de estar a 1 para habilitar
la interrupcin de alarma; tambin es puesto a cero tras un -RESET. El bit UIE (Update Interrupt Enable)
sirve para habilitar o inhibir la interrupcin de fin de actualizacin, que se producira tras cada actualizacin
del reloj; la seal -RESET baja el bit UIE. Por ltimo, el bit SQWE (Square Wave Enable) permite habilitar
o inhibir la seal de onda cuadrada de la salida SQW; tambin es borrado ante una seal -RESET.
El bit DM (Data Mode) permite seleccionar datos en binario (1) o BCD (0) en los bytes de fecha y
hora; la seal -RESET no afecta a este bit. El bit 24/12 sirve para elegir entre el modo 12 horas del reloj (bit
a 0) o el de 24 (bit a 1): en el modo de 12 horas, el bit ms significativo del byte de la hora estar activo
para indicar "PM". Si bit DSE est activo, el ltimo domingo de abril la hora pasa de 1:59:59 AM a 3:00:00
AM; en el ltimo domingo de octubre pasa de 1:59:59 AM a 1:00:00 AM (slo la primera vez, claro) para
ajustarse al cambio de hora oficial; este bit no es afectado por -RESET.
370 EL UNIVERSO DIGITAL DEL IBM PC, AT Y PS/2
REGISTRO C (slo lectura).
Este registro contiene bits que informan de las interrupciones que se producen. Permite identificar
al ordenador qu o cules interrupcin(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) estn activos,
o bien cuando el bit AF y el AIE (registro B) estn activos, o bien cuando UF y el bit UIE (registro B) estn
activos. Es decir, IRQF se pone en alto cuando es necesario que se produzca una interrupcin: la lnea -IRQ
se 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 interrupcin correspondiente. Todos los bits de este registro son
borrados ante una seal -RESET, pero tambin ante una lectura por software del registro C.
REGISTRO D (slo lectura).
Este registro contiene slo 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 tensin de alimentacin es correcta). Por software, el bit VRT
puede ser puesto a 1 mediante una simple lectura del registro D (si la patilla PS=1), con objeto de indicar
que la fecha y hora establecidas son correctas; si fallara la alimentacin, al caer la tensin en la patilla PS
este bit pasara 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 interrupcin de alarma se produce todos los das cuando llega la hora en que ha sido programada
y el bit que permite esta interrupcin est habilitado. Existe un mtodo alternativo para programar la alarma,
basado en los cdigos indiferentes almacenables en los bytes de la alarma. Un cdigo indiferente es cualquier
valor comprendido entre 0C0h y 0FFh. Si la hora de alarma es un cdigo indiferente, la alarma se producir
cada hora. Si la hora y minuto de alarma son cdigos indiferentes, sta se producir cada minuto. Si tanto
la hora como el minuto y segundo de la alarma son cdigos 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, la
implementacin fsica se realiza con circuitos totalmente compatibles que incluyen 128 bytes de RAM en
lugar de 64. En la RAM que sobra por encima de los primeros 14 bytes se almacenan parmetros de la
configuracin 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 1024
interrupciones peridicas por segundo (cuando estn habilitadas), al escribir el valor 26h en el registro A. De
la misma manera, el registro B se carga con 2 (modo 24 horas, datos en BCD y sin horario verano/invierno).
El MC146818 est diseado para ser conectado a un bus multiplexado, por lo que la circuitera de
apoyo de los AT se encarga de gestionar la comunicacin con el microprocesador, estableciendo dos puertos
de entrada/salida en las direcciones 70h y 71h. Para leer o escribir cualquier registro de la RAM CMOS, basta
con enviar al puerto 70h el nmero de registro y, a continuacin, leer o escribir del puerto 71h. Entre los
accesos a ambos puertos debe mediar un tiempo mnimo; de lo contrario la operacin fallar. En particular,
las ltimas versiones de los compiladores de Borland no permiten acceder al reloj de tiempo real en la
mayora de las mquinas a travs de las funciones outportb() e inportb(). La razn es que esas funciones estn
en una librera y es preciso llamarlas con paso de parmetros a travs de la pila, lo que ralentiza
excesivamente el proceso. Desde el lenguaje ensamblador, nunca hay problemas, aunque como es costumbre
371 EL HARDWARE DE APOYO AL MICROPROCESADOR
es conveniente insertar algn estado de espera (JMP SHORT $+2) entre dos operaciones E/S consecutivas,
precaucin necesaria en los ordenadores ms antiguos.
A nivel de interrupciones, la salida -IRQ del MC146818 est conectada a IRQ8 (INT 70h) a travs
del segundo controlador de interrupciones (vase la documentacin del mismo).
Desde la interrupcin 1Ah, la BIOS implementa una serie de servicios para acceder al reloj de tiempo
real, 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 tambin en el reloj de tiempo real.
Conviene tener presente que es de vital importancia acceder a los primeros 14 bytes de la CMOS slo
si el bit UIP del registro A (bit 7) est a cero. Tambin es necesario poner a 1 el bit SET del registro B (bit
7) antes de modificar dichos bytes, devolvindolo a 1 despus. No respetar este principio puede provocar la
lectura de fechas u horas incorrectas o una errnea asignacin de valores. Para los dems bytes de la CMOS
no es necesario tomar esta precaucin.
12.12.3. - UN MTODO PARA AVERIGUAR LA CONFIGURACIN DEL AT Y PS/2.
Como se dijo antes, los AT y superiores almacenan en los 50 114 ltimos bytes de RAM libres de
la CMOS informacin relativa a la configuracin del sistema. Los bytes ms importantes y comunes a todas
las mquinas se muestran a continuacin.
Byte 0Eh: Diagnostics Status Byte. El bit 7 indica (si vale 1) que el MC146818 tiene un dficit de corriente elctrica.
El bit 6 indica (si es 1) que el chechsum o suma de comprobacin de la CMOS ha fallado. El bit 5 indica
(si vale 1) que la configuracin del sistema es incorrecta (no hay al menos una disquetera presente o el modo
de vdeo de la configuracin no coincide con el detectado en el hardware). El bit 4 es puesto a 1 si el tamao
de la memoria detectado no coincide con el indicado en la configuracin. El bit 3 activo indica que el
adaptador o el disco fijo C: fall en la inicializacin, siendo imposible botar desde l. El bit 2 activo indica
que la hora del reloj es incorrecta. Los bits 1 y 0 estn reservados.
Byte 0Fh: Shutdown Status Byte. Los bits de este byte son asignados durante la inicializacin del sistema por parte
de la BIOS, informando de su desarrollo (vase 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 la
disquetera 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 en
dia est en la tabla, por lo que es frecuente que estos campos estn inicializados con el valor 1111b ( 0 si
no 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 nmero de disquetes instalados; los bits 5 y 4 el tipo de adaptador
de vdeo primario (00: EGA/VGA, 01: CGA-80, 10: CGA-40, 11: MDA); los bits 3 y 2 no se emplean. El
bit 1 indica si hay coprocesador aritmtico 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 de
16 bits que indica la cantidad de memoria convencional (tpicamente 640 Kb).
Byte 17h-18h: Low and High Memory Expansion Bytes. El 17h es el bajo y el 18h el alto. Entre ambos forman una
palabra de 16 bits que indica la cantidad de memoria extendida, en Kbytes.
Byte 19h: Nmero del primer disco duro. Nmero de identificacin 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 comprobacin 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 slo la memoria extendida ubicada por encima del primer megabyte que
detecta 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 opcin de ampliacin de 128 Kb (hasta los 640
Kb) del IBM AT original: hoy en da suele estar siempre activo. El bit 6 es empleado por el programa SETUP
para eliminar el mensaje inicial al usuario tras el primer SETUP. Los dems bits estn reservados.
Byte 34h-3Fh: Reservados.

También podría gustarte