Estábamos el otro día charlando amigablemente en el servidor de noticias de Tella y de repente surgió el tema del MS-DOS… Más bien alguien preguntó qué era el DR-DOS. Los habituales del grupo informamos que era un sistema operativo clon del MS-DOS que hizo en su momento Digital Research. También se contó la historia de por qué Windows 3.1 no era compatible con él y cómo se descubrió que todo fue una triquiñuela de Microsoft para chafar lo que en aquellos momentos era un sistema operativo mejor, más robusto, más rápido y con más opciones (por ejemplo, tenía un editor a pantalla completa, cosa que hasta el MS-DOS 5 Microsoft no se negó a incluir), y encima costaba cerca de la mitad.
Luego el que suscribe mencionó si alguien recordaba qué era la línea A20, para qué servía, cómo se usaba y qué es lo que dio origen a ella. Personalmente recordaba para qué servía, pero no la respuesta a las otras cuestiones. Pero llegó Ramón Sola, MVP, y dio una explicación magistral que considero no debe perderse en los vericuetos de un servidor de noticias, así que decidí publicarla aquí. El texto que sigue pertenece a dicho MVP, y es fiel transcripción del mismo. Simplemente para los nostálgicos.
Ahí va (firma incluída):
Je, resulta que es un error en el 80286 que se convirtió en "feature" después del artificio de la A20. ¿Recordáis el direccionamiento en modo real? La memoria se organiza en 65535 (2^16) segmentos de 65536 bytes cada uno que se solapan cada 16 bytes. Para obtener una dirección física a partir de una dirección de segmento y un desplazamiento, ambos de 16 bits, se realiza esta operación: dirección efectiva = (segmento * 16) + desplazamiento.
(En adelante, segmentos, desplazamientos y direcciones efectivas se expresan en hexadecimal.)
El bus de direcciones de los procesadores 8086 y 8088 sólo disponía de 20 líneas (de A19 a A0), por tanto tenían acceso únicamente a un megabyte de memoria (20 líneas => 2^20 bytes = 1.048.576 bytes = 1.024 MB = 1 MB). Si la combinación segmento:desplazamiento superaba el valor máximo de 20 bits, FFFFF, el hecho de tener un bus de tan sólo 20 líneas causaba que el procesador accediera realmente a memoria baja (este comportamiento se denominaba "wrap-around", algo así como "vuelta al principio"). Los programas debían considerar esta circunstancia y comprobar cuidadosamente los punteros que pudiesen superar la dirección efectiva FFFFF.
Ejemplo (se ve mejor con letra de ancho fijo):
FFF0:0100
FFF0 <— desplazado 4 bits a la izquierda (*16)
+ 0100
——-
1 00000
| ^^^^^ <– 20 bits (5 dígitos hexa) de dirección efectiva en 8086/88
+——– Ignorado en 8086/88
1 0 0 0 0 0 Hexadecimal
—- —- —- —- —- —-
0001 0000 0000 0000 0000 0000 Binario
—– —– —– —- —- —-
23-20 19-16 15-12 11-8 7-4 3-0 <— Bits
Obsérvese que el 1 es el bit 20 del resultado.
Más tarde llegó el 80286 con su bus de direcciones de 24 bits (de A23 a A0), con el que podía direccionar hasta 16 MB de memoria (2^24 = 2^[4+20] = 2^4 * 2^20 = 2^4 MB = 16 MB). Este procesador incorporaba otro modo de funcionamiento, el modo protegido. Salvo que algún programa solicitara este modo, el 80286 debía comportarse de la misma forma que un 8086/88, por tanto se inicializaba siempre en modo real. Sin embargo, debido a un error de diseño, el 80286 no truncaba en modo real las direcciones que sí se truncaban en los 8086/88. Por ejemplo, la combinación FFF0:0100 generaba la dirección 100000 (1 MB), no la 00000 como habría ocurrido en los 8086/88. Esto podía ocasionar problemas de compatibilidad con programas concebidos para los 8086/88.
La solución, o más bien el apaño, debía consistir en manipular la línea A20 de algún modo. Para emular el "wrap-around" de los 8086/88, hay que forzar la línea A20 a valor cero. Esto supone un problema en modo protegido: se alternarían secciones accesibles (0-1MB, 2MB-3MB…) con otras inaccesibles (1 MB-2 MB, 3 MB-4 MB…).
El control sobre la línea A20 se llevaba a cabo mediante una puerta lógica AND. Recordemos que una puerta AND da un 1 a su salida si y sólo si todas sus entradas son 1; análogamente, basta con que una entrada sea cero para que la salida también sea cero. La salida y una de las entradas de la puerta AND se conectaban a la línea A20. La otra entrada estaba comunicada con un pin libre del controlador de teclado. Al arrancar la máquina, el controlador de teclado forzaba un cero en su entrada de la puerta AND, lo que forzaba a su vez un cero en la línea A20. Teníamos un modo real idéntico al del 8086. Si un programa necesitaba entrar en modo protegido, debía habilitar la puerta AND enviando la orden correspondiente al controlador de teclado (poner un 1 en su entrada) para evitar el problema de las regiones inaccesibles descrito anteriormente.
¿Qué pasaba si se habilitaba la línea A20 en modo real? Pues que se tenía acceso a un área adicional por encima del primer megabyte, con un tamaño de 64 KB menos 16 bytes (direcciones físicas 100000 a 10FFEF). Se trata del área de memoria alta (HMA).
Por supuesto, todas estas ideas se mantienen prácticamente intactas en la actualidad aunque las implementaciones hardware hayan evolucionado. La palabra clave es *compatibilidad*.
Referencia en español:
Wikipedia - Área de memoria alta
http://es.wikipedia.org/wiki/%C3%81rea_de_memoria_alta
(Traducción de la Wikipedia en inglés.)
Más referencias, en inglés:
Real Adress Mode
http://esteve.tizos.net/archives/real-address-mode
A20 - a pain from the past
http://www.win.tue.nl/~aeb/linux/kbd/A20.html
Wikipedia - High Memory Area
http://en.wikipedia.org/wiki/High_Memory_Area
–
Ramón Sola (*Asterixco*) desde Málaga (España)
Lo que le pido es un poquito de barriga, señor Paciencia.
No todos repiten los chismes que oyen. Algunos los mejoran.
— Anónimo.