Informática
Arquitectura de los microprocesadores
ARQUITECTURA E HISTORIA DE LOS MICROORDENADORES
El ensamblador es un lenguaje de programación que, por la traducción directa de los mnemónicos a instrucciones maquina, permite realizar aplicaciones rápidas, solucionando situaciones en las que los tiempos de ejecución constituye el factor principal para que el proceso discurra con la suficiente fluidez. Esta situación, que indudablemente sí influye sobre la elección del lenguaje de programación a utilizar en el desarrollo de una determinada rutina, y dada la aparición de nuevos compiladores de lenguajes de alto nivel que optimizan el código generado a niveles muy próximos a los que un buen programador es capaz de realizar en ensamblador, no es la única razón para su utilización.
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 programación de los drivers para los controladores de dispositivos, relacionados con las tareas de más bajo nivel de una máquina, fundamentalmente las operaciones de entrada/salida en las que es preciso actuar directamente sobre los demás chips que acompañan al microprocesador. Por ello y porque las instrucciones del lenguaje ensamblador están íntimamente ligadas a la máquina, vamos a realizar primero un somero repaso a la arquitectura interna de un microordenador.
1.1. - ARQUITECTURA VON NEWMAN.
Centrándonos 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 prácticamente 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 numeración decimal, una electrónica sumamente complicada muy susceptible a fallos y un sistema de programación cableado o mediante fichas, Von Newman propuso dos conceptos básicos que revolucionarían la incipiente informática:
a) La utilización del sistema de numeración binario. Simplificaba enormemente los problemas que la implementación electrónica de las operaciones y funciones lógicas planteaban, a la vez proporcionaba una mayor inmunidad a los fallos (electrónica digital).
b) Almacenamiento de la secuencia de instrucciones de que consta el programa en una memoria interna, fácilmente accesible, junto con los datos que referencia. De este forma la velocidad de proceso experimenta un considerable incremento; recordemos que anteriormente una instrucción o un dato estaban codificados en una ficha en el mejor de los casos.
Tomando como modelo las máquinas que aparecieron incorporando las anteriores características, el ordenador se puede considerar compuesto por las siguientes partes:
- La Unidad Central de Proceso, U.C.P., más conocida por sus siglas en inglés (CPU).
- La Memoria Interna, MI.
- Unidad de Entrada y Salida, E/S.
- Memoria masiva Externa, ME.
Realicemos a continuación una descripción de lo que se entiende por cada una de estas partes y cómo están relacionadas entre si:
- La Unidad Central de Proceso (CPU) viene a ser el cerebro del ordenador y tiene por misión efectuar las operaciones aritmético-lógicas y controlar las transferencias de información a realizar.
- La Memoria Interna (MI) contiene el conjunto de instrucciones que ejecuta la CPU en el transcurso de un programa. Es también 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 comunicación de la máquina 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 líneas 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 líneas que seleccionan desde dónde y hacia dónde va dirigida la información, también las que marcan la secuencia de los pasos a seguir para dicha transferencia.
- De datos: por él, de forma bidireccional, fluyen los datos entre las distintas partes del ordenador.
- De direcciones: como vimos, la memoria está dividida en pequeñas unidades de almacenamiento que contienen las instrucciones del programa y los datos. El bus de direcciones consta de un conjunto de líneas que permite seleccionar de qué posición de la memoria se quiere leer su contenido. También direcciona los puertos de E/S.
La forma de operar del ordenador en su conjunto es direccionar una posición de la memoria en busca de una instrucción mediante el bus de direcciones, llevar la instrucción a la unidad central de proceso -CPU- por medio del bus de datos, marcando la secuencia de la transferencia el bus de control. En la CPU la instrucción se decodifica, interpretando qué operandos necesita: si son de memoria, es necesario llevarles a la CPU; una vez que la operación es realizada, si es preciso se devuelve el resultado a la memoria.
1.2. - EL MICROPROCESADOR.
Un salto importante en la evolución de los ordenadores lo introdujo el microprocesador: se trata de una unidad central de proceso contenida totalmente en un circuito integrado. Comenzaba así la gran carrera en busca de lo más rápido, más pequeño; rápidamente el mundo del ordenador empezó a ser accesible a pequeñas empresas e incluso a nivel doméstico: es el boom de los microordenadores personales. Aunque cuando entremos en la descripción de los microprocesadores objeto de nuestro estudio lo ampliaremos, haré un pequeño comentario de las partes del microprocesador:
- Unidad aritmético-lógica: Es donde se efectúan las operaciones aritméticas (suma, resta, y a veces producto y división) y lógicas (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 información dentro del microprocesador y fuera de él.
1.3. - BREVE HISTORIA DEL ORDENADOR PERSONAL Y EL DOS.
La trepidante evolución del mundo informático podría provocar que algún recién llegado a este libro no sepa exactamente qué diferencia a un ordenador "AT" del viejo "XT" inicial de IBM. Algunos términos manejados en este libro podrían ser desconocidos para los lectores más jóvenes. Por ello, haremos una pequeña introducción sobre la evolución de los ordenadores personales, abarcando toda la historia (ya que no es muy larga).
La premonición.
En 1973, el centro de investigación de Xerox en Palo Alto desarrolló un equipo informático con el aspecto externo de un PC personal actual. Además de pantalla y teclado, disponía de un artefacto similar al ratón; en general, este aparato (denominado Alto) introdujo, mucho antes de que otros los reinventaran, algunos de los conceptos universalmente aceptados hoy en día. Sin embargo, la tecnología del momento no permitió alcanzar todas las intenciones. Alguna innovación, como la pantalla vertical, de formato similar a una hoja de papel (que desearían algunos actuales internautas para los navegadores) aún no ha sido adoptada: nuestros PC's siguen pareciendo televisores con teclas, 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 drásticamente la circuitería adicional necesaria. Sucesores de este procesador fueron el 8008 y el 8080, de 8 bits. Ed Roberts construyó en 1975 el Altair 8800 basándose en el 8080; aunque esta máquina no tenía teclado ni pantalla (sólo interruptores y luces), era una arquitectura abierta (conocida por todo el mundo) y cuyas tarjetas se conectaban a la placa principal a través de 100 terminales, que más tarde terminarían convirtiéndose en el bus estándar S-100 de la industria.
El Apple-I apareció en 1976, basado en el microprocesador de 8 bits 6502, en aquel entonces un recién aparecido aunque casi 10 veces más barato que el 8080 de Intel. Fue sucedido en 1977 por el Apple-II. No olvidemos los rudimentos de la época: el Apple-II tenía un límite máximo de 48 Kbytes de memoria. En el mismo año, 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 explosión inicial del microprocesador.
Los micros de los 80.
En 1980, Sir Clive Sinclair lanzó el ZX-80, seguido muy poco después del ZX-81. Estaban 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 aún en el 6502 y, este último, con mejores posibilidades gráficas y unos 64 Kb de memoria. Su competidor fue el ZX-Spectrum de Sinclair, también basado en el Z80, con un chip propio para gestión de gráficos y otras tareas, la ULA, que permitió rebajar su coste y multiplicó su difusión por europa, y en particular por España. Sin embargo, todos los ordenadores domésticos de la época, como se dieron en llamar, estaban basados en procesadores de 8 bits y tenían el límite de 64 Kb de memoria. Los intentos de rebasar este límite manteniendo aún esos chips por parte de la plataforma MSX (supuesto estándar mundial con la misma suerte que ha corrido el Esperanto) o los CPC de Amstrad, de poco sirvieron.
El IBM PC.
Y es que IBM también fabricó su propio ordenador personal con vocación profesional: el 12 de agosto de 1981 presentó el IBM PC. Estaba basado en el microprocesador 8088, de 16 bits, cuyas instrucciones serán las que usemos en este libro, ya que todos los procesadores posteriores son básicamente (en MS-DOS) versiones mucho más rápidas del mismo. El equipamiento de serie consistía en 16 Kbytes de memoria ampliables a 64 en la placa base (y a 256 añadiendo tarjetas); el almacenamiento externo se hacía 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 traía como novedad un disco duro de 10 Mbytes. Un año más tarde aparecería el IBM PC-AT, introduciendo el microprocesador 286, así como ranuras de expansión de 16 bits (el bus ISA de 16 bits) en contraposición con las de 8 bits del PC y el XT (bus ISA de 8 bits), además 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 categoría AT; el término XT hace referencia al 8088/8086 y similares. Finalmente, por PC (a secas) se entiende cualquiera de ambos; aunque si se hace distinción entre un PC y un AT en la misma frase, por PC se sobreentiende un XT, menos potente. El término PC ya digo, no obstante, es hoy en día mucho más general, referenciando habitualmente a cualquier ordenador personal.
Alrededor del PC se estaba construyendo un imperio de software más importante que el propio hardware: estamos hablando del sistema operativo PC-DOS. Cuando aparecieron máquinas compatibles con el PC de IBM, tenían que respetar la compatibilidad con ese sistema, lo que fue sencillo (ya que Microsoft, le gustara o no a IBM, desarrolló el MS-DOS, compatible con el PC-DOS pero que no requería la BIOS 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 repetiría en alguna manera pocos años después con el OS/2-Windows].
A partir de 1986, IBM fue paulatinamente dejando de tener la batuta del mercado del PC. La razón es que la propia IBM tenía que respetar la compatibilidad con lo anterior, y en ese terreno no tenía más facilidades para innovar que la competencia. El primer problema vino con la aparición de los procesadores 386: los demás fabricantes se adelantaron a IBM y lanzaron máquinas con ranuras de expansión aún de 16 bits, que no permitían 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 aún realmente ISA). La insolente respuesta de la competencia fue la arquitectura EISA, también de 32 bits pero compatible con la ISA anterior.
Otro ejemplo: si IBM gobernó los estándares gráficos hasta la VGA, a partir de ahí sucedió un fenómeno similar y los demás fabricantes se adelantaron a finales de los 80 con mejores tarjetas y más baratas; sin embargo, se perdió la ventaja de la normalización (no hay dos tarjetas superiores a la VGA que funcionen igual).
EISA también era caro, así que los fabricantes orientales, cruzada ya la barrera de los años 90, desarrollaron con la norma VESA las placas con bus local (VESA Local Bus); básicamente es una prolongación de las patillas de la CPU a las ranuras de expansión, lo que permite tarjetas rápidas de 32 bits pero muy conflictivas entre sí. Esta arquitectura de bus se popularizó mucho con los procesadores 486. Sin embargo, al final el estándar que se ha impuesto ha sido el propuesto por el propio fabricante de las CPU: Intel, con su bus PCI, que con el Pentium se ha convertido finalmente en el único estándar de bus de 32 bits. Estas máquinas aún admiten no obstante las viejas tarjetas ISA, suficientes para algunas aplicaciones de baja velocidad (modems,... etc).
La evolución del MS-DOS.
Una manera sencilla de comprender la evolución de los PC es observar la evolución de las 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 año apareció QDOS 0.3.
Bill Gates, dueño de Microsoft, de momento sólo poseía una versión de lenguaje BASIC para 8086 no orientada a ningún sistema operativo particular, que le gustó a algún directivo de IBM. Bill Gates ya había hecho la primera demostración mundial de BASIC corriendo en un 8086 en las placas de Seatle Computer (en julio de 1979) y había firmado un contrato de distribución no exclusiva para el QDOS 0.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 parecía tener menos futuro que el CP/M. En Julio, sin embargo, Microsoft adquiría todos los derechos del 86-DOS.
Digital Research no ocupa actualmente el lugar de Microsoft porque en 1981 era una compañía demasiado importante como para cerrar un acuerdo con IBM sin imponer sus condiciones para cederle los derechos del sistema operativo CP/M. Así que IBM optó por Bill Gates, que acababa de adquirir un sistema operativo, el 86-DOS, que pasó a denominarse PC-DOS 1.0. Las versiones de PC-DOS no dependientes de la ROM BIOS de IBM se denominarían MS-DOS, término que ha terminado siendo más popular.
A continuación se expone la evolución hasta la versión 5.0; las versiones siguientes no añaden ninguna característica interna nueva destacable (aunque a nivel de interfaz con el usuario y utilidades incluidas haya más 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 excepción: la versión 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. Presentación del MS-DOS 1.0 original.
-
Marzo de 1982. MS-DOS 1.25, añadiendo soporte para disquetes de doble cara. Las funciones del DOS (en INT 21h) sólo llegaban hasta la 1Fh (¡la 30h no estaba implementada!).
-
Marzo de 1983. MS-DOS 2.0 introducido con el XT: reescritura del núcleo en C; mejoras en el sistema de ficheros (FAT, subdirectorios,...); separación 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: eliminación de errores.
-
Agosto de 1984. MS-DOS 3.0: Añade soporte para disquetes de 1.2M y discos duros de 20 Mb. No sería necesaria una nueva versión del DOS para cada nuevo formato de disco si el controlador integrado para A:, B: y C: lo hubieran hecho flexible algún día.
-
Marzo de 1985. MS-DOS 3.1: Soporte para redes locales.
-
Diciembre de 1985. MS-DOS 3.2: Soporte para disquetes de 720K (3½-DD).
-
Abril de 1987. MS-DOS 3.3: Soporte para disquetes de 1.44M (3½-HD). Permite particiones secundarias en los discos duros. Soporte internacional: páginas de códigos.
-
Julio de 1988. MS-DOS 4.0: Soporte para discos duros de más de 32 Mb (cambio radical 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 década más tarde (con DR-DOS), obliga a Microsoft a incluir ayuda online y a ocuparse un poco más de los usuarios.
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 prácticamente 100% compatibles. El efecto del DR-DOS fue positivo, al forzar a Microsoft a mejorar la interacción del sistema operativo con los usuarios (documentación en línea, 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 integración de los diversos Windows en uno sólo, que esperemos que algún día sea suficientemente abierto para que le surjan competidores. Si en el futuro hubiera un sólo sistema operativo soportado por Microsoft, no vamos por buen camino.
En ese caso, sería de agradecer que algún juez les obligara a publicar una especificación completa de las funciones y protocolos del sistema, con objeto de que algún organismo de normalización internacional las recogiera sin ambigüedades para permitir la libre competencia de otros fabricantes. El DOS y el Windows actuales no son ningún invento maravilloso de Microsoft. Por poner un ejemplo, el MS-DOS 1.0 carecía de función para identificar la versión del sistema. Exactamente lo mismo le ha sucedido a las primeras versiones de Windows (hay varios chequeos distintos para detectarlas, según el modo de funcionamiento y la versión): el MS-DOS no lo escribió inicialmente Microsoft, pero Windows sí, y salta a la vista que sus programadores, para cometer semejante despiste, se sentaron delante del teclado antes de hacer un análisis de la aplicación a desarrollar, igual que lo hubiera hecho alguien que hubiera aprendido a programar con unos fascículos 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 década, gracias a la exclusiva de los derechos sobre un sistema operativo sin ayuda en la línea de comandos, o de un Windows cerrado íntimamente ligado al DOS (de quien sólo Microsoft tiene el código fuente) no legitima a ninguna empresa a tener tanto poder. 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 colgándose mientras Unix no tenga más aplicaciones comerciales.
Si hay alguien que puede competir con Windows es Unix. Y en Unix no dependemos de ningún fabricante concreto, ni de hardware ni de software. Probablemente, la insuficiente normalización actual la corregiría pronto el propio mercado. ¿Tiene usted Linux instalado en casa y lo utiliza al menos para conectarse a Internet por Infovía, o quizá le gustaría hacerlo algún día?. ¿O por el contrario es de los que piensan que Bill Gates es un genio?. Si se queda con la segunda opción, es que ve mucho la tele, aunque evidentemente tiene razón: y cuantos más como usted, más genio que será...
1.4 Especificaciones técnicas de los microprocesadores Intel
| Fecha de presentación | Velocidad de reloj | Ancho de bus | Número de transistores | Memoria direccionable | Memoria virtual | Breve descripción |
4004 | 15/11/71 | 108 KHz. | 4 bits | 2.300 (10 micras) | 640 byte |
| Primer chip con manipulación aritmética |
8008 | 1/4/72 | 108 KHz. | 8 bits | 3.500 | 16 KBytes |
| Manipulación Datos/texto |
8080 | 1/4/74 | 2 MHz. | 8 bits | 6.000 | 64 KBytes |
| 10 veces las (6 micras) prestaciones del 8008 |
8086 | 8/6/78 | 5 MHz. 8 MHz. 10 MHz. | 16 bits | 29.000 (3 micras) | 1 MegaByte |
| 10 veces las prestaciones del 8080 |
8088 | 1/6/79 | 5 MHz. 8 MHz. | 8 bits | 29.000 |
|
| Idéntico al 8086 excepto en su bus externo de 8 bits |
80286 | 1/2/82 | 8 MHz. 10 MHz. 12 MHz. | 16 Bits | 134.000 (1.5 micras) | 16 Megabytes | 1 Gigabyte | De 3 a 6 veces las prestaciones del 8086 |
Microprocesador Intel 386 DX® | 17/10/85 | 16 MHz. 20 MHz. 25 MHz. 33 MHz. | 32 Bits | 275.000 (1 micra) | 4 Gigabytes | 64 Terabytes | Primer chip x86 capaz de manejar juegos de datos de 32 bits |
Microprocesador Intel 386 SX® | 16/6/88 | 16 MHz. 20 MHz. | 16 Bits | 275.000 (1 micra) | 4 gigabytes | 64 Terabytes | Bus capaz de direccionar 16 bits procesando 32bits a bajo coste |
Microprocesador Intel 486 DX® | 10/4/89 | 25 MHz. 33 MHz. 50 MHz. | 32 Bits | (1 micra, 0.8 micras en 50 MHz.) | 4 Gigabytes | 64 Terabytes | Caché de nivel 1 en el chip |
Microprocesador Intel 486 SX® | 22/4/91 | 16 MHz. 20 MHz. 25 MHz. 33 MHz. | 32 Bits | 1.185.000 (0.8 micras) | 4 Gigabytes | 64 Terabytes | Idéntico en diseño al Intel 486DX, pero sin coprocesador matemático |
Procesador Pentium® | 22/3/93 | 60 MHz. 66 MHz. 75 MHz. 90 MHz. 100 MHz. 120 MHz. 133 MHz. 150 MHz. 166 MHz. 200 MHz. | 32 Bits | 3,1 millones (0.8 micras) | 4 Gigabytes | 64 Terabytes | Arquitectura escalable. Hasta 5 veces las prestaciones del 486 DX a 33 MHz. |
Procesador PentiumPro® | 27/3/95 | 150 MHz. 180 MHz. 200 MHz. | 64 Bits | 5,5 millones (0.32 micras) | 4 Gigabytes | 64 Terabytes | Arquitectura de ejecución dinámica con procesador de altas prestaciones |
Procesador PentiumII® | 7/5/97 | 233 MHz. 266 MHz. 300 MHz. | 64 Bits | 7,5 millones (0.32 micras) | 4 Gigabytes | 64 Terabytes | S.E.C., MMX, Doble Bus Indep., Ejecución Dinámica |
1.5 La ley de Moore
El Dr. Gordon Moore, uno de los fundadores de Intel Corporation, formuló en el año 1965 una ley que se ha venido a conocer como la "Ley de Moore". La citada ley que está reflejada en el gráfico adjunto, nos viene a decir que el número de transistores contenidos en un microprocesador se dobla más o menos cada dieciocho meses. Esta afirmación, que en principio estaba destinada a los dispositivos de memoria, pero también los microprocesadores han cumplido la ley. Una ley que significa para el usuario que cada dieciocho meses, de forma continua, pueda disfrutar de una tecnología mejor, algo que se ha venido cumpliendo durante los últimos 30 años, y de lo que se espera siga vigente en los próximos quince o veinte años. De modo que el usuario puede disponer de mejores equipos, aunque también significa la necesidad de cambiar de equipo cada poco tiempo, algo que no todo el mundo se puede permitir. Y eso que el precio aumenta de forma absoluta pero no relativa, puesto que la relación MIPS/dinero está decreciendo a velocidad vertiginosa. Algo que sin embargo no sucede con la industria del automóvil por ejemplo, ya que la potencia de los coches no se ha multiplicado de la misma forma que los precios. En cualquier caso, queda claro que en los próximos años nos espera una auténtica revolución en lo que a rendimiento de los procesadores se refiere, como ya predijera Moore hace más de treinta años.
Microprocesadores 8086/88, 286, 386, 486 y Pentium.
2.1. - CARACTERÍSTICAS GENERALES.
Los microprocesadores Intel 8086 y 8088 se desarrollan a partir de un procesador anterior, el 8080, que, en sus diversas encarnaciones -incluyendo el Zilog Z-80- ha sido la CPU de 8 bits de mayor éxito.
Poseen una arquitectura interna de 16 bits y pueden trabajar con operandos de 8 y 16 bits; una capacidad de direccionamiento de 20 bits (hasta 1 Mb) y comparten el mismo juego de instrucciones.
La filosofía de diseño de la familia del 8086 se basa en la compatibilidad y la creación de sistemas informáticos integrados, por lo que disponen de diversos coprocesadores como el 8089 de E/S y el 8087, coprocesador matemático de coma flotante. De acuerdo a esta filosofía y para permitir la compatibilidad con los anteriores sistemas de 8 bits, el 8088 se diseñó con un bus de datos de 8 bits, lo cual le hace más lento que su hermano el 8086, pues éste es capaz de cargar una palabra ubicada en una dirección par en un solo ciclo de memoria mientras el 8088 debe realizar dos ciclos leyendo cada vez un byte.
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 máquinas construidas entorno a estos microprocesadores no suelen emplear la entrada/salida por mapa de memoria, como veremos.
Entre esas instrucciones, las más rápidas se ejecutan en 2 ciclos teóricos de reloj y unos 9 reales (se trata del movimiento de datos entre registros internos) y las más lentas en 206 (división entera con signo del acumulador por una palabra extraída de la memoria). Las frecuencias internas de reloj típicas son 4.77 MHz en la versión 8086; 8 MHz en la versión 8086-2 y 10 MHz en la 8086-1. Recuérdese que un MHz son un millón de ciclos de reloj, por lo que un PC estándar a 4,77 MHz puede ejecutar de 20.000 a unos 0,5 millones de instrucciones por segundo, según la complejidad de las mismas (un 486 a 50 MHz, incluso sin memoria caché externa es capaz de ejecutar entre 1,8 y 30 millones de estas instrucciones por segundo).
El microprocesador Intel 80286 se caracteriza por poseer dos modos de funcionamiento completamente diferenciados: el modo real en el que se encuentra nada más ser conectado a la corriente y el modo protegido en el que adquiere capacidad de proceso multitarea y almacenamiento en memoria virtual. El proceso multitarea consiste en realizar varios procesos de manera aparentemente simultánea, con la ayuda del sistema operativo para conmutar automáticamente de uno a otro optimizando el uso de la CPU, ya que mientras un proceso está esperando a que un periférico complete una operación, se puede atender otro proceso diferente. La memoria virtual permite al ordenador usar más memoria de la que realmente tiene, almacenando parte de ella en disco: de esta manera, los programas creen tener a su disposición más memoria de la que realmente existe; cuando acceden a una parte de la memoria lógica que no existe físicamente, se produce una interrupción y el sistema operativo se encarga de acceder al disco y traerla.
Cuando la CPU está en modo protegido, los programas de usuario tienen un acceso limitado al juego de instrucciones; sólo 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 periféricos. Además, de esta manera, aunque un error software provoque el cuelgue de un proceso, los demás pueden seguir funcionando normalmente, y el sistema operativo podría 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 caída inmediata de todo el sistema.
El 8086 no posee ningún mecanismo para apoyar la multitarea ni la memoria virtual desde el procesador, por lo que es difícil diseñar un sistema multitarea para el mismo y casi imposible conseguir que sea realmente operativo. Obviamente, el 286 en modo protegido pierde absolutamente toda la compatibilidad con los procesadores anteriores. Por ello, en este libro sólo trataremos el modo real, único disponible bajo DOS, aunque veremos alguna instrucción extra que también se puede emplear en modo real.
Las características generales del 286 son: tiene un bus de datos de 16 bits, un bus de direcciones de 24 bits (16 Mb); posee 25 instrucciones más que el 8086 y admite 8 modos de direccionamiento. En modo virtual permite direccionar hasta 1 Gigabyte. Las frecuencias de trabajo típicas son de 12 y 16 MHz, aunque existen versiones a 20 y 25 MHz. Aquí, la instrucción más lenta es la misma que en el caso del 8086, solo que emplea 29 ciclos de reloj en lugar de 206. Un 286 de categoría media (16 MHz) podría ejecutar más de medio millón de instrucciones de estas en un segundo, casi 15 veces más 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 más rápido y no sólo gracias a los MHz adicionales.
Versiones mejoradas de los Intel 8086 y 8088 se encuentran también en los procesadores NEC-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 aritméticas. También poseen una cola de prebúsqueda mayor (cuando el microprocesador está ejecutando una instrucción, si no hace uso de los buses externos, carga en una cola FIFO de unos pocos bytes las posiciones posteriores a la que está procesando, de esta forma una vez que concluye la instrucción en curso ya tiene internamente la que le sigue). Además, 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 también de 32 bits (direcciona hasta 4 Gigabytes = 4096 Mb) y más 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 simultáneo de varios 8086. Una vez más, todos los modos son incompatibles entre sí y requieren de un sistema operativo específico: si se puede perdonar al fabricante la pérdida de compatibilidad del modo avanzados del 286 frente al 8086, debido a la lógica evolución tecnológica, no se puede decir lo mismo del 386 respecto al 286: no hubiera sido necesario añadir un nuevo modo protegido si hubiera sido mejor construido el del 286 apenas un par de años atrás. Normalmente, los 386 suelen operar en modo real (debido al DOS) por lo que no se aprovechan las posibilidades multitarea ni de gestión de memoria. 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. Básicamente, es un 386 con un bus de datos de sólo 16 bits -más lento, al tener que dar dos pasadas para un dato de 32 bits-. De hecho, podría haber sido diseñado 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 integración en un solo chip del coprocesador 387. También se ha mejorado la velocidad de operación: la versión de 25 MHz dobla en términos reales a un 386 a 25 MHz equipado con el mismo tamaño de memoria caché. La versión 486sx no se diferencia en el tamaño del bus, también de 32 bits, sino en la ausencia del 387 (que puede ser añadido externamente). También existen versiones de 486 con buses de 16 bits, el primer fabricante de estos chips, denominados 486SLC, ha sido Cyrix. Una tendencia iniciada por el 486 fue la de duplicar la velocidad del reloj interno (pongamos por caso de 33 a 66 MHz) aunque en las comunicaciones con los buses exteriores se respeten los 33 MHz. Ello agiliza la ejecución de las instrucciones más largas: bajo DOS, el rendimiento general del sistema se puede considerar prácticamente el doble. Son los chips DX2 (también hay una variante a 50 MHz: 25 x 2). La culminación de esta tecnología viene de la mano de los DX4 a 75/100 MHz (25/33 x 3).
El Pentium, último procesador de Intel en el momento de escribirse estas líneas, se diferencia respecto al 486 en el bus de datos (ahora de 64 bits, lo que agiliza los accesos a memoria) y en un elevadísimo nivel de optimización y segmentación que le permite, empleando compiladores optimizados, simultanear en muchos casos la ejecución de dos instrucciones consecutivas. Posee dos cachés internas, tiene capacidad para predecir el destino de los saltos y la unidad de coma flotante experimenta elevadas mejoras. Sin embargo, bajo DOS, un Pentium básico sólo es unas 2 veces más rápido que un 486 a la 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 aún más el rendimiento. Todos los equipos Pentium emplean las técnicas DX, ya que las placas base típicas 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 más rápido en aritmética entera que el IBM PC original de inicios de la década de los 80; en coma flotante la diferencia aumenta incluso algunos órdenes más de magnitud. Y a una fracción del coste (un millón de pts de aquel entonces que equivale a unos 2,5 millones de hoy en día). Aunque no hay que olvidar la revolución del resto de los componentes: 100 veces más memoria (central y de vídeo), 200 veces más grande el disco duro... y que un disco duro moderno transfiere datos 10 veces más deprisa que la memoria de aquel IBM PC original. Por desgracia, el software no ha mejorado el rendimiento, ni remotamente, en esa proporción: es la factura pasada por las técnicas de programación cada vez a un nivel más alto (aunque nadie discute sus ventajas).
Una característica de los microprocesadores a partir del 386 es la disponibilidad de memorias caché de alta velocidad de acceso -muy pocos nanosegundos- que almacenan una pequeña porción de la memoria principal. Cuando la CPU accede a una posición de memoria, cierta circuitería de control se encarga de ir depositando el contenido de esa posición y el de las posiciones inmediatamente consecutivas en la memoria caché. Cuando sea necesario acceder a la instrucción siguiente del programa, ésta ya se encuentra en la caché y el acceso es muy rápido. Lo ideal sería que toda la memoria del equipo fuera caché, pero esto no es todavía posible actualmente. Una caché de tamaño razonable puede doblar la velocidad efectiva de proceso de la CPU. El 8088 carecía de memoria caché, pero sí estaba equipado con una unidad de lectura adelantada de instrucciones con una cola de prebúsqueda de 4 bytes: de esta manera, se agilizaba ya un tanto la velocidad de proceso al poder ejecutar una instrucción al mismo tiempo que iba leyendo la siguiente.
2.2. - REGISTROS DEL 8086 Y DEL 286.
Estos procesadores disponen de 14 registros de 16 bits (el 286 alguno más, pero no se suele emplear bajo DOS). La misión de estos registros es almacenar las posiciones de memoria que van a experimentar repetidas manipulaciones, ya que los accesos a memoria son mucho más lentos que los accesos a los registros. Además, hay ciertas operaciones que sólo se pueden realizar sobre los registros. No todos los registros sirven para almacenar datos, algunos están especializados en apuntar a las direcciones de memoria. La mecánica básica de funcionamiento de un programa consiste en cargar los registros con datos de la memoria o de un puerto de E/S, procesar los datos y devolver el resultado a la memoria o a otro puerto de E/S. Obviamente, si un dato sólo va a experimentar un cambio, es preferible realizar la operación directamente sobre la memoria, si ello es posible. A continuación se describen los registros del 8086.
AX | SP | CS | IP |
BX | BP | DS | flags |
CX | SI | SS |
|
DX | DI | ES |
|
Registros de datos | Registros punteros de pila e índices | Registros de segmento | Registro puntero de instrucciones y flags |
- 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 según 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 están añadiendo 256 unidades a AX.
AX = Acumulador.
Es el registro principal, es utilizado en las instrucciones de multiplicación y división y en algunas instrucciones aritméticas especializadas, así como en ciertas operaciones de carácter específico como entrada, salida y traducción. Obsérvese que el 8086 es suficientemente potente para realizar las operaciones lógicas, 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 dirección de la base o comienzo de tablas o matrices. De esta manera, no es preciso indicar una posición de memoria fija, sino la número 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 comúnmente como contador en bucles y operaciones repetitivas de manejo de cadenas. En las instrucciones de desplazamiento y rotación se utiliza como contador de 8 bits.
DX = Datos.
Usado en conjunción con AX en las operaciones de multiplicación y división que involucran o generan datos de 32 bits. En las de entrada y salida se emplea para especificar la dirección del puerto E/S.
- Registros de segmento:
Definen áreas de 64 Kb dentro del espacio de direcciones de 1 Mb del 8086. Estas áreas pueden solaparse total o parcialmente. No es posible acceder a una posición de memoria no definida por algún segmento: si es preciso, habrá de moverse alguno.
CS = Registro de segmento de código (code segment).
Contiene la dirección del segmento con las instrucciones del programa. Los programas de más de 64 Kb requieren cambiar CS periódicamente.
DS = Registro de segmento de datos (data segment).
Segmento del área de datos del programa.
SS = Registro de segmento de pila (stack segment).
Segmento de pila.
ES = Registro de segmento extra (extra segment).
Segmento de ampliación para zona de datos. Es extraordinariamente útil actuando en conjunción con DS: con ambos se puede definir dos zonas de 64 Kb, tan alejadas como se desee en el espacio de direcciones, entre las que se pueden intercambiar datos.
- Registros punteros de pila:
SP = Puntero de pila (stack pointer).
Apunta a la cabeza de la pila. Utilizado en las instrucciones de manejo de la pila.
BP = Puntero base (base pointer).
Es un puntero de base, que apunta a una zona dentro de la pila dedicada al almacenamiento de datos (variables locales y parámetros de las funciones en los programas compilados).
- Registros índices:
SI = Índice fuente (source index).
Utilizado como registro de índice en ciertos modos de direccionamiento indirecto, también se emplea para guardar un valor de desplazamiento en operaciones de cadenas.
DI = Índice destino (destination index).
Se usa en determinados modos de direccionamiento indirecto y para almacenar un desplazamiento en operaciones con cadenas.
- Puntero de instrucciones o contador de programa:
IP = Puntero de instrucción (instruction pointer).
Marca el desplazamiento de la instrucción en curso dentro del segmento de código. Es automáticamente modificado con la lectura de una instrucción.
- Registro de estado o de indicadores (flags).
Es un registro de 16 bits de los cuales 9 son utilizados para indicar diversas situaciones durante la ejecución de un programa. Los bits 0, 2, 4, 6, 7 y 11 son indicadores de condición, que reflejan los resultados de operaciones del programa; los bits del 8 al 10 son indicadores de control y el resto no se utilizan. Estos indicadores pueden ser comprobados por las instrucciones de salto condicional, lo que permite variar el flujo secuencial del programa según 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 más habitual es lo que nos llevamos en una suma o resta.
-
OF (Overflow Flag): Indicador de desbordamiento. Indica que el resultado de una operación no cabe en el tamaño del operando destino.
-
ZF (Zero Flag): Indicador de resultado 0 o comparación igual.
-
SF (Sign Flag): Indicador de resultado o comparación negativa.
-
PF (Parity Flag): Indicador de paridad. Se activa tras algunas operaciones aritmético-lógicas para indicar que el número de bits a uno resultante es par.
-
AF (Auxiliary Flag): Para ajuste en operaciones BCD.
-
DF (Direction Flag): Indicador de dirección. Manipulando bloques de memoria, indica el sentido de avance (ascendente/descendente).
-
IF (Interrupt Flag): Indicador de interrupciones: puesto a 1 están permitidas.
-
TF (Trap Flag): Indicador de atrape (ejecución paso a paso).
2.3. - REGISTROS DEL 386 Y PROCESADORES SUPERIORES.
Los 386 y superiores disponen de muchos más registros de los que vamos a ver ahora. Sin embargo, bajo el sistema operativo DOS sólo se suelen emplear los que veremos, que constituyen básicamente una extensión a 32 bits de los registros originales del 8086.
Se amplía el tamaño de los registros de datos (que pueden ser accedidos en fragmentos de 8, 16 ó 32 bits) y se añaden dos nuevos registros de segmento multipropósito (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.
2.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:
INSTRUCCIÓN DESTINO, FUENTE
Donde destino indica dónde se deja el resultado de la operación en la que pueden participar (según casos) FUENTE e incluso el propio DESTINO. Hay instrucciones, sin embargo, que sólo tienen un operando, como la siguiente, e incluso ninguno:
INSTRUCCIÓN DESTINO
Como ejemplos, aunque no hemos visto aún las instrucciones utilizaremos un par de ellas: la de copia o movimiento de datos (MOV) y la de suma (ADD).
2.4.1. - ORGANIZACIÓN DE DIRECCIONES: SEGMENTACIÓN.
Como ya sabemos, los microprocesadores 8086 y compatibles poseen registros de un tamaño máximo de 16 bits que direccionarían hasta 64K; en cambio, la dirección se compone de 20 bits con capacidad para 1Mb, hay por tanto que recurrir a algún artificio para direccionar toda la memoria. Dicho artificio consiste en la segmentación: 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 dirección absoluta se calcula multiplicando por 16 el valor del registro de segmento y sumando el offset, obteniéndose una dirección efectiva de 20 bits. Esto equivale a concebir el mecanismo de generación de la dirección 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:
dirección = segmento * 16 + offset
En la práctica, una dirección se indica con la notación SEGMENTO:OFFSET; además, una misma dirección puede expresarse de más de una manera: por ejemplo, 3D00h:0300h es equivalente a 3D30:0000h. Es importante resaltar que no se puede acceder a más de 64 Kb en un segmento de datos. Por ello, en los procesadores 386 y superiores no se deben emplear registros de 32 bit para generar direcciones (bajo DOS), aunque para los cálculos pueden ser interesantes (no obstante, sí sería posible configurar estos procesadores para poder direccionar más memoria bajo DOS con los registros de 32 bits, aunque no resulta por lo general práctico).
2.4.3 - MODOS DE DIRECCIONAMIENTO.
- Direccionamiento inmediato: El operando es una constante situada detrás del código de la instrucción. 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 número hexadecimal 0fffh es la constante numérica que en el direccionamiento inmediato se le sumará al registro AX. Al trabajar con ensambladores, se pueden definir símbolos constantes (ojo, no variables) y es más intuitivo:
dato EQU 0fffh ; símbolo constante
MOV AX,dato
Si se referencia a la dirección de memoria de una variable de la siguiente forma, también se trata de un caso de direccionamiento inmediato:
dato DW 0fffh ; ahora es una variable
MO AX,OFFSET dato ; AX = "dirección de memoria" de dato
Porque hay que tener en cuenta que cuando traduzcamos a números el símbolo podría quedar:
17F3:0A11 DW FFF
MOV AX,0A11
- Direccionamiento de registro: Los operandos, necesariamente de igual tamaño, están contenidos en los registros indicados en la instrucción:
MOV DX,AX
MOV AH,AL
- Direccionamiento directo o absoluto: El operando está situado en la dirección indicada en la instrucción, relativa al segmento que se trate:
MOV AX,[57D1h]
MOV AX,ES:[429Ch]
Esta sintaxis (quitando la 'h' de hexadecimal) sería la que admite el programa DEBUG (realmente habría que poner, en el segundo caso, ES: en una línea y el MOV en otra). Al trabajar con ensambladores, las variables en memoria se pueden referenciar con etiquetas simbólicas:
MOV AX,dato
MOV AX,ES:dato
dato DW 1234h ; variable del programa
En el primer ejemplo se transfiere a AX el valor contenido en la dirección apuntada por la etiqueta dato sobre el segmento de datos (DS) que se asume por defecto; en el segundo ejemplo se indica de forma explícita el segmento tratándose del segmento ES. La dirección efectiva se calcula de la forma ya vista con anterioridad: Registro de segmento * 16 + desplazamiento_de_dato (este desplazamiento depende de la posición al ensamblar el programa).
- Direccionamiento indirecto: El operando se encuentra en una dirección señalada por un registro de segmento*16 más un registro base (BX/BP) o índice (SI/DI). (Nota: BP actúa por defecto con SS).
MOV AX,[BP] ; AX = [SS*16+BP]
MOV ES:[DI],AX ; [ES*16+DI] = AX
- Indirecto con índice o indexado: El operando se encuentra en una dirección determinada por la 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 dirección 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 explícitamente el registro de segmento a usar para acceder a los datos. Existen unos segmentos asociados por defecto a los registros de desplazamiento (IP, SP, BP, BX, DI, SI); sólo es necesario declarar el segmento cuando no coincide con el asignado por defecto. En ese caso, el ensamblador genera un byte adicional (a modo de prefijo) para indicar cuál 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) También por defecto en el manejo de cadenas.
Los 386 y superiores admiten otros modos de direccionamiento más sofisticados, que se verán en el próximo capítulo, después de conocer todas las instrucciones del 8086. Por ahora, con todos estos modos se puede considerar que hay más que suficiente. De hecho, algunos se utilizan en muy contadas ocasiones.
2.5. - LA PILA.
La pila es un bloque de memoria de estructura LIFO (Last Input First Output: último en entrar, primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de pila). Las posiciones individuales dentro de la pila se calculan sumando al contenido del segmento de pila SS un desplazamiento contenido en el registro puntero de pila SP. Todos los datos que se almacenan en la pila son de longitud palabra, y cada vez que se introduce algo en ella por medio de las instrucciones de manejo de pila (PUSH y POP), el puntero se decrementa en dos; es decir, la pila avanza hacia direcciones decrecientes. El registro BP suele utilizarse normalmente para apuntar a una cierta posición 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 común al procesador y al usuario, por lo que se almacenan en ella también las direcciones de retorno de las subrutinas. Esta última es, de hecho, la más 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 serán las de la siguiente instrucción que provocó la llamada a la subrutina. Así, al retornar de la subrutina se extrae de la pila la dirección a donde volver. Los compiladores de los lenguajes de alto nivel la emplean también para pasar los parámetros de los procedimientos y para generar en ella las variables automáticas -variables locales que existen durante la ejecución del subprograma y se destruyen inmediatamente después-. Por ello, una norma básica es que se debe desapilar siempre todo lo apilado para evitar una pérdida de control inmediata del ordenador.
Ejemplo de operación sobre la pila (todos los datos son arbitrarios):
2.6. - UN PROGRAMA DE EJEMPLO.
Aunque las instrucciones del procesador no serán vistas hasta el próximo capítulo, con objeto de ayudar a la imaginación del lector elaboraremos un primer programa de ejemplo en lenguaje ensamblador. La utilidad de este programa es dejar patente que lo único que entiende el 8086 son números, aunque nosotros nos referiremos a ellos con unos símbolos que faciliten entenderlos. También es interesante este ejemplo para afianzar el concepto de registro de segmento.
En este programa sólo vamos a emplear las instrucciones MOV, ya conocida, y alguna otra más como la instrucción INC (incrementar), DEC (disminuir una unidad) y JNZ (saltar si el resultado no es cero). Suponemos que el programa está ubicado a partir de la dirección de memoria 14D3:7A10 (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 más que una zona de memoria). Por cada carácter que hay en dicha pantalla, comenzando arriba a la izquierda, a partir de la dirección B800:0000 tenemos dos bytes: el primero, con el código ASCII del carácter y el segundo con el color. Lo que vamos a hacer es rellenar los 2000 caracteres (80 columnas x 25 líneas) con espacios en blanco (código ASCII 32, ó 20h en hexadecimal), sin modificar el color que hubiera antes. Esto es, se trata de poner el valor 32 en la dirección B800:0000, la B800:0002, la B800:0004... y así sucesivamente.
El programa quedaría en memoria de esta manera: La primera columna indica la dirección de memoria donde está el programa que se ejecuta (CS=14D3h e IP=7A10h al principio). La segunda columna constituye el código máquina que interpreta el 8086. Algunas instrucciones ocupan un byte de memoria, otras dos ó tres (las hay de más). La tercera columna contiene el nombre de las instrucciones, algo mucho más legible para los humanos que los números:
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 carácter 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 carácter ASCII
14D3:7A20 49 DEC CX ; CX=CX-1 -< queda un carácter menos
14D3:7A21 75 F8 JNZ -8 ; si CX no es 0, saltar 8 bytes atrás (a 14D3:7A1B)
Como se puede ver, la segunda instrucción (bytes de código máquina 0B8h, 0 y 0B8h colocados 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 apoyándonos 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 instrucción MOV BYTE PTR [BX],32 equivale a decir: «poner en la dirección de memoria apuntada por BX (DS:[BX] para ser más exactos) el byte de valor 32». El valor 0F8h del código máquina de la última instrucción es el complemento a dos (número negativo) del valor 8.
Normalmente, casi nunca habrá que ensamblar a mano consultando unas tablas, como hemos hecho en este ejemplo. Sin embargo, la mejor manera de aprender ensamblador es no olvidando la estrecha relación de cada línea de programa con la CPU y la memoria.
JUEGO DE INSTRUCCIONES 80x86
3.1. - DESCRIPCIÓN COMPLETA DE LAS INSTRUCCIONES.
Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguiente notación:
- bit no modificado
? desconocido o indefinido
x modificado según el resultado de la operación
1 puesto siempre a 1
0 puesto siempre a 0
3.1.1. - INSTRUCCIONES DE CARGA DE REGISTROS Y DIRECCIONES.
MOV (transferencia)
Sintaxis: MOV dest, origen.
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Transfiere datos de longitud byte o palabra del operando origen al operando destino. Pueden ser operando origen y operando destino cualquier registro o posición de memoria direccionada de las formas ya vistas, con la única condición de que origen y destino tengan la misma dimensión. 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 algún ensamblador). Al hacer MOV hacia un registro de segmento, las interrupciones quedan inhibidas hasta después de ejecutarse la siguiente instrucción (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 dirección de memoria o desplazamiento respecto al segmento de datos. En otras palabras, SI es un puntero a dato pero no es dato. En el próximo capítulo se verá cómo se declaran las variables.
XCHG (intercambiar)
Sintaxis: XCHG destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
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 (traducción)
Sintaxis: XLAT tabla
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Se utiliza para traducir un byte del registro AL a un byte tomado de la tabla de traducción. Los datos se toman desde una dirección de la tabla correspondiente a BX + AL, donde bx es un puntero a el comienzo de la tabla y AL es un índice. Indicar tabla al lado de xlat es sólo una redundancia opcional.
Ejemplo: mov bx,offset tabla
mov al,4
xlat
LEA (carga dirección efectiva)
Sintaxis: LEA destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Transfiere el desplazamiento del operando fuente al operando destino. Otras instrucciones pueden a continuación 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 instrucción 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 más 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 más el registro SI. Esa sola instrucción 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 (dirección completa de memoria compuesta por
segmento y desplazamiento), al destino indicado y a DS. A partir de la dirección 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 instrucción, en DS:SI se hace referencia a la posición de memoria 1234h:5678h; 'dd' sirve para definir una variable larga de 4 bytes (denominada punt en el ejemplo) y será explicado en el capítulo siguiente.
LES (carga un puntero utilizando ES)
Sintaxis: LES destino, origen
Esta instrucción es análoga a LDS, pero utilizando ES en lugar de DS.
LAHF (carga AH con los indicadores)
Sintaxis: LAHF
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Carga los bits 7, 6, 4, 2 y 0 del registro AH con el contenido de los indicadores SF, ZF, AF, PF Y CF respectivamente. El contenido de los demás bits queda sin definir.
SAHF (copia AH en los indicadores)
Sintaxis: SAHF
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - x x x x x
Transfiere el contenido de los bits 7, 6, 4, 2 y 0 a los indicadores SF, ZF, AF, PF y CF respectivamente.
3.1.2. - INSTRUCCIONES DE MANIPULACIÓN DEL REGISTRO DE ESTADO.
CLC (baja el indicador de acarreo)
Sintaxis: CLC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - 0
Borra el indicador de acarreo (CF) sin afectar a ninguno otro.
CLD (baja el indicador de dirección)
Sintaxis: CLD
Indicadores: OF DF IF TF SF ZF AF PF CF
- 0 - - - - - - -
Pone a 0 el indicador de dirección DF, por lo que los registros SI y/o DI 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. Véase STD.
CLI (baja indicador de interrupción)
Sintaxis: CLI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 - - - - - -
Borra el indicador de activación de interrupciones IF, lo que desactiva las interrupciones enmascarables. Es muy conveniente hacer esto antes de modificar la pareja SS:SP en los 8086/88 anteriores a 1983 (véase comentario en la instrucción MOV), o antes de cambiar un vector de interrupción sin el apoyo del DOS. Generalmente las interrupciones sólo se inhiben por breves instantes en momentos críticos. Véase también STI.
CMC (complementa el indicador de acarreo)
Sintaxis: CMC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - x
Complementa el indicador de acarreo CF invirtiendo su estado.
STC (pone a uno el indicador de acarreo)
Sintaxis: STC
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - 1
Pone a 1 el indicador de acarreo CF sin afectar a ningún otro indicador.
STD (pone a uno el indicador de dirección)
Sintaxis: STD
Indicadores: OF DF IF TF SF ZF AF PF CF
- 1 - - - - - - -
Pone a 1 el indicador de dirección DF, por lo que los registros SI y/o DI 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. Véase también CLD.
STI (pone a uno el indicador de interrupción)
Sintaxis: STI
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 1 - - - - - -
Pone a 1 la bandera de desactivación de interrupciones IF y activa las interrupciones enmascarables. Una interrupción pendiente no es reconocida, sin embargo, hasta después de ejecutar la instrucción que sigue a STI. Véase también CLI.
3.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 instrucción 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 continuación el puntero de pila SP se incrementa en dos.
PUSHF (introduce los indicadores en la pila)
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.
3.1.3. - 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 dirección de la instrucción 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 dirección donde se encuentra el puntero. Según la llamada sea cercana o lejana, se almacena en la pila una dirección de retorno de 16 bits o dos palabras de 16 bits indicando en este último caso tanto el offset (IP) como el segmento (CS) a donde volver.
Ejemplos: call proc1
dir dd 0f000e987h
call dword ptr dir
En el segundo ejemplo, la variable dir almacena la dirección a donde saltar. De esta última manera -conociendo su dirección- puede llamarse también a un vector de interrupción, guardando previamente los flags en la pila (PUSHF), porque la rutina de interrupción retornará (con IRET en vez de con RETF) sacándolos.
JMP (salto)
Sintaxis: JMP dirección o JMP SHORT dirección
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Transfiere el control incondicionalmente a la dirección indicada en el operando. La bifurcación puede ser también directa o indirecta como anteriormente vimos, pero además 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).
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 dirección de la siguiente dirección. 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 instrucción es colocada dentro de un bloque PROC-ENDP (como se verá en el siguiente capítulo) el ensamblador sabe el tipo de retorno que debe hacer, según el procedimiento sea NEAR o FAR. En cualquier caso, se puede forzar que el retorno sea de tipo FAR con la instrucción RETF. Valor, si es indicado permite sumar una cantidad valor en bytes a SP antes de retornar, lo que es frecuente en el código generado por los compiladores para retornar de una función con parámetros. También se puede retornar de una interrupción con RETF 2, para que devuelva el registro de estado sin restaurarlo de la pila.
Condicional
Las siguientes instrucciones son de transferencia condicional de control a la instrucción que se encuentra en la posición IP+desplazamiento (desplazamiento comprendido entre -128 y +127) si se cumple la condición. Algunas condiciones se pueden denotar de varias maneras. Todos los saltos son cortos y si no alcanza hay que apañárselas como sea. En negrita se realzan las condiciones más empleadas. Donde interviene SF se consideran con signo los operandos implicados en la última comparación u operación aritmetico-lógica, y se indican en la tabla como '±' (-128 a +127 ó -32768 a +32767); en los demás 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. |
Gestión de bucle
LOOP (bucle)
Sintaxis: LOOP desplazamiento
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Decrementa el registro contador CX; si CX es cero, ejecuta la siguiente instrucción, en caso contrario transfiere el control a la dirección resultante de sumar a IP + desplazamiento. El desplazamiento debe estar comprendido entre -128 y +127. Ejemplo:
mov cx,10
bucle: .......
.......
loop bucle
Con las mismas características que la instrucción 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 (interrupción)
Sintaxis: INT n (0 <= n <= 255)
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 0 - - - - -
Inicializa un procedimiento de interrupción de un tipo indicado en la instrucción. En la pila se introduce al llamar a una interrupción la dirección de retorno formada por los registros CS e IP y el estado de los indicadores. INT 3 es un caso especial de INT, al ensamblarla el ensamblador genera un sólo byte en vez de los dos habituales; esta interrupción se utiliza para poner puntos de ruptura en los programas. Véase también IRET y el apartado 1 del capítulo VII.
Ejemplo: int 21h
INTO (interrupción por desbordamiento)
Sintaxis: INTO
Indicadores: OF DF IF TF SF ZF AF PF CF
- - 0 0 - - - - -
Genera una interrupción de tipo 4 (INT 4) si existe desbordamiento (OF=1). De lo contrario se continúa con la instrucción siguiente.
IRET (retorno de interrupción)
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 dirección de retorno salvada en la pila por una interrupción previa y restaura los indicadores que también se introdujeron en la pila. En total, se sacan las 3 palabras que fueron colocadas en la pila cuando se produjo la interrupción. Véase también INT.
3.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 través 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 través del valor contenido en el registro DX (de 0 a 65535).
Ejemplo: out 12h,ax
out dx,al
3.1.6. - INSTRUCCIONES ARITMÉTICAS.
* * * S U M A * * *
AAA (ajuste ASCII para la suma)
Sintaxis: AAA
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - ? ? x ? x
Convierte el contenido del registro AL en un número BCD no empaquetado. Si 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 más significativos a 0.
Ejemplo: add al,bl
aaa
En el ejemplo, tras la suma de dos números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAA) sigue siendo un número BCD no empaquetado.
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 números grandes, de más de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo) de la suma anterior.
Ejemplo: adc ax,bx
ADD (suma)
Sintaxis: ADD destino, origen
Indicadores: OF DF IF TF SF ZF AF PF 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 número mayor que 9, el indicador AF se pone a 1 y se suma 6 a AL. De igual forma, si los cuatro bits más significativos de AL tras la operación anterior son un número 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 contenían dos números BCD empaquetados, DAA hace que el resultado de la suma (en AL) siga siendo también un BCD empaquetado.
INC (incrementar)
Sintaxis: INC destino
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x -
Incrementa el operando destino. El operando destino puede ser byte o palabra.
Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF).
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 sustracción de dos operandos BCD no empaquetados para que siga siendo un número 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 números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAS) sigue siendo un número BCD no empaquetado.
CMP (comparación)
Sintaxis: CMP destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x x
Resta origen de destino sin retornar ningún resultado. Los operandos quedan inalterados, paro los indicadores pueden ser consultados mediante instrucciones de bifurcación condicional. Los operandos pueden ser de tipo byte o palabra pero ambos de la misma dimensión.
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 números BCD empaquetados, convirtiéndolo también 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 después CF.
Ejemplo: sub al,bl
das
En el ejemplo anterior, si AL y BL contenían dos números BCD empaquetados, DAS hace que el resultado de la resta (en AL) siga siendo también un BCD empaquetado.
DEC (decrementar)
Sintaxis: DEC destino
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - x x x x -
Resta una unidad del operando destino. El operando puede ser byte o palabra. Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF).
Ejemplo: dec ax
dec mem_byte
NEG (negación)
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 además resta una unidad más. Los operandos pueden ser de tipo byte o palabra. Se utiliza normalmente para restar números grandes, de más 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
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 multiplicación)
Sintaxis: AAM
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
Corrige el resultado en AX del producto de dos números BCD no empaquetados, convirtiéndolo en un valor BCD también no empaquetado. En AH sitúa el cociente de AL/10 quedando en AL el resto de dicha operación.
Ejemplo: mul bl
aam
En el ejemplo, tras el producto de dos números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAA) sigue siendo, en AX, un número BCD no empaquetado.
IMUL (multiplicación entera con signo)
Sintaxis: IMUL origen (origen no puede ser operando inmediato en 8086, sí en 286)
Indicadores: OF DF IF TF SF ZF AF PF 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 más significativo) y en AL (menos significativo), si origen es una palabra el resultado es devuelto en DX (parte alta) y AX (parte baja). Si las mitades más significativas son distintas de cero, independientemente del signo, CF y OF son activados.
Ejemplo: imul bx
imul ch
MUL (multiplicación 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
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 división)
Sintaxis: AAD
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - x x ? x ?
Convierte dos números BCD no empaquetados contenidos en AH y AL en un dividendo de un byte que queda almacenado en AL. Tras la operación AH queda a cero. Esta instrucción es necesaria ANTES de la operación de dividir, al contrario que AAM.
Ejemplo: aad
div bl
En el ejemplo, tras convertir los dos números BCD no empaquetados (en AX) en un dividendo válido, la instrucción de dividir genera un resultado correcto.
DIV (división sin signo)
Sintaxis: DIV origen (origen no puede ser operando inmediato)
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - ? ? ? ? ?
Divide, sin considerar el signo, un número contenido en el acumulador y su extensión (AH, AL si el operando es de tipo byte o DX, AX si el operando es palabra) entre el operando fuente. El cociente se guarda en AL o AX y el resto en AH o DX según el operando sea byte o palabra respectivamente. DX o AH deben ser cero antes de la operación. Cuando el cociente es mayor que el resultado máximo que puede almacenar, cociente y resto quedan indefinidos produciéndose una interrupción 0. En caso de que las partes más significativas del cociente tengan un valor distinto de cero se activan los indicadores CF y OF.
Ejemplo: div bl
div mem_pal
IDIV (división entera)
Sintaxis: IDIV origen (origen no puede ser operando inmediato)
Indicadores: OF DF IF TF SF ZF AF PF CF
? - - - ? ? ? ? ?
Divide, considerando el signo, un número contenido en el acumulador y su extensión entre el operando fuente. El cociente se almacena en AL o AX según el operando sea byte o palabra y de igual manera el resto en AH o DX. DX o AH deben ser cero antes de la operación. Cuando el cociente es positivo y superior al valor máximo que puede almacenarse (7fh ó 7fffh), o cuando el cociente es negativo e inferior al valor mínimo que puede almacenarse (81h u 8001h) entonces cociente y resto quedan indefinidos, generándose una interrupción 0, lo que también sucede si el divisor es 0.
Ejemplo: idiv bl
idiv bx
* * * C O N V E R S I O N E S * * *
CBW (conversión de byte en palabra)
Sintaxis: CBW
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Copia el bit 7 del registro AL en todos los bits del registro AH, es decir, expande el signo de AL a AX como paso previo a una operación de 16 bits.
CWD (conversión de palabra a doble palabra)
Sintaxis: CWD
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Expande el signo del registro AX sobre el registro DX, copiando el bit más significativo de AH en todo DX.
3.1.7. - INSTRUCCIONES DE MANIPULACIÓN 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 según el valor del indicador DF (véanse CLD y STD) en una o dos unidades, dependiendo de si se trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar, es más cómodo colocar CMPSB o CMPSW para indicar bytes/palabras. Si se indica un registro de segmento, éste sustituirá en la cadena origen al DS ordinario. Ejemplo:
lea si,origen
lea di,destino
cmpsb
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 según el indicador DF (véanse CLD y STD) en una o dos unidades, según se estén manejando bytes o palabras. Cadena_origen es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar, es más cómodo 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 continuación los registros SI y DI según el valor de DF (véanse CLD y STD) en una o dos unidades, dependiendo de si se trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar, es más cómodo colocar MOVSB o MOVSW para indicar bytes/palabras. Si se indica 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
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 según el valor de DF (véanse CLD y STD) en una o dos unidades -según se esté trabajando con bytes o palabras- para apuntar al siguiente elemento de la cadena. Cadena_destino es un operando redundante que sólo indica el tipo del dato (byte o palabra), es más cómodo 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 operación, DI se incrementa o decrementa según el indicador DF (véanse CLD y STD) para apuntar al siguiente elemento de la cadena. Cadena_destino es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar, es más cómodo colocar STOSB o STOSW para indicar bytes/palabras.
Ejemplo: lea di,destino
mov ax,1991
stosw
REP/REPE/REPZ/REPNE/REPNZ (repetir)
REP repetir operación de cadena
REPE/REPZ repetir operación de cadena si igual/si cero
REPNE/REPNZ repetir operación de cadena si no igual (si no 0)
Estas instrucciones se pueden colocar como prefijo de otra instrucción de manejo de cadenas, con objeto de que la misma se repita un número determinado de veces incondicionalmente o hasta que se verifique alguna condición. El número de veces se indica en CX. Por sentido común sólo deben utilizarse las siguientes combinaciones:
Prefijo Función 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
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
3.1.8. - INSTRUCCIONES DE OPERACIONES LÓGICAS A NIVEL DE BIT.
AND (y lógico)
Sintaxis: AND destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operación de Y lógico entre el operando origen y destino quedando el resultado en el destino. Son válidos operandos byte o palabra, pero ambos del mismo tipo.
Ejemplos: and ax,bx
and bl,byte ptr es:[si+10h]
NOT (no lógico)
Sintaxis: NOT destino
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Realiza el complemento a uno del operando destino, invirtiendo cada uno de sus bits. Los indicadores no resultan afectados.
Ejemplo: not ax
OR (O lógico)
Sintaxis: OR destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operación O lógico a nivel de bits entre los dos operandos, almacenándose después el resultado en el operando destino.
Ejemplo: or ax,bx
TEST (comparación lógica)
Sintaxis: TEST destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Realiza una operación Y lógica entre los dos operandos pero sin almacenar el resultado. Los indicadores son afectados con la operación.
Ejemplo: test al,bh
XOR (O exclusivo)
Sintaxis: XOR destino, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
0 - - - x x ? x 0
Operación OR exclusivo a nivel de bits entre los operandos origen y destino almacenándose el resultado en este último.
Ejemplo: xor di,ax
3.1.9. - INSTRUCCIONES DE CONTROL DEL PROCESADOR.
NOP (operación nula)
Sintaxis: NOP
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Realiza una operación nula, es decir, el microprocesador decodifica la instrucción y pasa a la siguiente. Realmente se trata de la instrucción XCHG AX,AX.
ESC (salida a un coprocesador)
Sintaxis: ESC código_operación, origen
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Se utiliza en combinación con procesadores externos, tales como los coprocesadores de coma flotante o de E/S, y abre al dispositivo externo el acceso a las direcciones y operandos requeridos. Al mnemónico ESC le siguen los códigos de operación apropiados para el coprocesador así como la instrucción y la dirección del operando necesario.
Ejemplo: esc 21,ax
HLT (parada hasta interrupción o reset)
Sintaxis: HLT
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
El procesador se detiene hasta que se restaura el sistema o se recibe una interrupción. Como en los PC se producen normalmente 18,2 interrupciones de tipo 8 por segundo (del temporizador) algunos programadores utilizan HLT para hacer pausas y bucles de retardo. Sin embargo, el método no es preciso y puede fallar con ciertos controladores de memoria.
LOCK (bloquea los buses)
Sintaxis: LOCK
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Es una instrucción que se utiliza en aplicaciones de recursos compartidos para asegurar que no accede simultáneamente a la memoria más de un procesador. Cuando una instrucción va precedida por LOCK, el procesador bloquea inmediatamente el bus, introduciendo una señal por la patilla LOCK.
WAIT (espera)
Sintaxis: WAIT
Indicadores: OF DF IF TF SF ZF AF PF CF
- - - - - - - - -
Provoca la espera del procesador hasta que se detecta una señal en la patilla TEST. Ocurre, por ejemplo, cuando el copro ha terminado una operación e indica su finalización. Suele preceder a ESC para sincronizar las acciones del procesador y coprocesador.
3.1.10. - INSTRUCCIONES DE ROTACIÓN Y DESPLAZAMIENTO.
RCL (rotación a la izquierda con acarreo)
Sintaxis: RCL destino, contador
Indicadores: OF DF IF TF SF ZF AF PF CF
x - - - - - - - x
Rotar a la izquierda los bits del operando destino junto con el indicador de acarreo CF el número de bits especificado en el segundo operando. Si el número 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.
Ejemplos: rcl ax,1
rcl al,cl
rcl di,1
RCR (rotación 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 número de bits especificado en el segundo operando. Si el número de bits es 1 se puede especificar directamente; en caso contrario su valor debe cargarse en CL y especificar CL como segundo operando:
Ejemplos: rcr bx,cl
rcr bx,1
ROL (rotación 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 número de bits especificado en el segundo operando, que puede ser 1 ó CL previamente cargado con el valor del número de veces.
Ejemplos: rol dx,cl
rol ah,1
ROR (rotación 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 número de bits especificado en el segundo operando. Si el número de bits es 1 se puede poner directamente, en caso contrario debe ponerse a través de CL.
Ejemplos: ror cl,1
ror ax,cl
SAL/SHL (desplazamiento aritmético 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 número de bits especificado en el segundo operando que debe ser CL si es mayor que 1 los bits desplazados.
SAR (desplazamiento aritmético 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 número de bits especificado en el segundo operando. Los bits de la izquierda se rellenan con el bit de signo del primer operando. Si el número de bits a desplazar es 1 se puede especificar directamente, si es mayor se especifica a través de CL.
Ejemplos: sar ax,cl
sar bp,1
SHR (desplazamiento lógico 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 número de los bits especificados en el segundo operando. Los bits de la izquierda se llena con cero. Si el número de bits a desplazar es 1 se puede especificar directamente en el caso en que no ocurra se pone el valor en CL:
Ejemplos: shr ax,cl
shr cl,1
3.2. - RESUMEN ALFABÉTICO DE LAS INSTRUCCIONES Y BANDERINES. ÍNDICE.
Nota: en el efecto de las instrucciones sobre el registro de estado se utilizará la siguiente notación:
- bit no modificado
? desconocido o indefinido
x modificado según el resultado de la operación
1 puesto siempre a 1
0 puesto siempre a 0
Instrucción Sintaxis Efecto sobre los flags
---------------- --------------------- --------------------------
OF DF IF TF SF ZF AF PF CF
AAA AAA ? - - - ? ? x ? x
AAD AAD ? - - - x x ? x ?
AAM AAM ? - - - x x ? x ?
AAS AAS ? - - - ? ? x ? x
ADC dst,fnt ADC dst,fnt x - - - x x x x x
ADD dst,fnt ADD dst,fnt x - - - x x x x x
AND dst,fnt AND dst,fnt 0 - - - x x ? x 0
CALL dsp CALL dsp - - - - - - - - -
CBW CBW - - - - - - - - -
CLC CLC - - - - - - - - 0
CLD CLD - 0 - - - - - - -
CLI CLI - - 0 - - - - - -
CMC CMC - - - - - - - - x
CMP dst,fnt CMP dst,fnt x - - - x x x x x
CMPS/CMPSB
CMPSW cdst,cfnt CMPS cdst,cfnt x - - - x x x x x
CWD CWD - - - - - - - - -
DAA DAA ? - - - x x x x x
DAS DAS - - - - x x x x x
DEC dst DEC dst x - - - x x x x -
DIV fnt DIV dst ? - - - ? ? ? ? ?
ESC opcode,fnt ESC opcode,fnt - - - - - - - - -
HLT HLT - - - - - - - - -
IDIV fnt IDIV fnt ? - - - ? ? ? ? ?
IMUL fnt IMUL fnt x - - - ? ? ? ? x
IN acum,port IN acum,port - - - - - - - - -
INC dst INC dst x - - - x x x x -
INT interrup INT interrup - - 0 0 - - - - -
INTO INTO - - 0 0 - - - - -
IRET IRET x x x x x x x x x
Jcc (JA, JBE...) Jcc dsp - - - - - - - - -
JMP JMP dsp - - - - - - - - -
JCXZ dsp JCXZ dsp - - - - - - - - -
LAHF LAHF - - - - - - - - -
LDS dst,fnt LDS dst,fnt - - - - - - - - -
LEA dst,fnt LEA dst,fnt - - - - - - - - -
LES dst,fnt LES dst,fnt - - - - - - - - -
LOCK LOCK - - - - - - - - -
LODS/LODSB/
LODSW cfnt LODS mem - - - - - - - - -
LOOP LOOP dsp - - - - - - - - -
LOOPcc (LOOPE...) LOOPcc dsp - - - - - - - - -
MOV dst,fnt MOV dst,fnt - - - - - - - - -
MOVS/MOVSB/
MOVSW cdst,cfnt MOVS cdst,cfnt - - - - - - - - -
MUL fnt MUL fnt x - - - ? ? ? ? x
NEG dst NEG fnt x - - - x x x x x
NOP NOP - - - - - - - - -
NOT dst NOT dst - - - - - - - - -
OR dst,fnt OR dst,fnt 0 - - - x x ? x 0
OUT port,acum OUT port,acum - - - - - - - - -
POP dst POP dst - - - - - - - - -
POPF POPF x x x x x x x x x
PUSH dst PUSH dst - - - - - - - - -
PUSHF PUSHF - - - - - - - - -
RCL dst,cnt RCL dst,cnt x - - - - - - - x
RCR dst,cnt RCR dst,cnt x - - - - - - - x
REP/REPE/REPZ/
REPNE/REPNZ REP - - - - - - - - -
RET [val] RET [val] - - - - - - - - -
RETF [val] RETF [val] - - - - - - - - -
ROL dst,cnt ROL dst,cnt x - - - - - - - x
ROR dst,cnt ROR dst,cnt x - - - - - - - x
SAHF SAHF - - - - x x x x x
SAL/SHL dst,cnt SAL dst,cnt x - - - x x ? x x
SAR dst,cnt SAR dst,cnt x - - - x x ? x x
SBB dst,fnt SBB dst,fnt x - - - x x x x x
SCAS/SCASB/
SCASW cdst SCAS cdst x - - - x x x x x
SHR dst,cnt SHR dst,cnt x - - - x x ? x x
STC STC - - - - - - - - 1
STD STD - 1 - - - - - - -
STI STI - - 1 - - - - - -
STOS/STOSB/
STOSW cdst STOS cdst - - - - - - - - -
SUB dst,fnt SUB dst,fnt x - - - x x x x x
TEST dst,fnt TEST dst,fnt 0 - - - x x ? x 0
WAIT WAIT - - - - - - - - -
XCHG dst,fnt XCHG dst,fnt - - - - - - - - -
XLAT tfnt XLAT tfnt - - - - - - - - -
XOR dst,fnt XOR dst,fnt 0 - - - x x ? x 0
3.3. - INSTRUCCIONES ESPECIFICAS DEL 286, 386 y 486 EN MODO REAL.
3.3.1. - DIFERENCIAS EN EL COMPORTAMIENTO GLOBAL RESPECTO AL 8086.
- Excepciones de división:
Las excepciones INT 0, debidas a una división por cero o a un cociente excesivamente grande, provocan que en la pila se almacene el valor de CS:IP para la siguiente instrucción en el 8086. En el 286 y superiores se almacena el CS:IP de la propia instrucción que causa la excepción.
- Códigos de operación indefinidos.
En el 286 y superiores se produce una excepción 6 (INT 6) o, si es una instrucción con sentido para estos procesadores, se ejecuta. El 8086 se estrella.
- Valor de PUSH SP.
El valor que introduce en la pila en el 286 y superiores es el de SP antes del PUSH; en el 8086 es el de SP después del PUSH (dos unidades menos).
- Desplazamientos y rotaciones.
El valor de desplazamiento en las operaciones de manipulación de bits del 8086 es una constante de 8 bits (indicada en CL); en el 286 y superiores se toma módulo 32 (sólo se consideran los 5 bits menos significativos).
- Prefijos redundantes.
Las instrucciones tienen una longitud ilimitada en el 8086; en el 286 y superiores no pueden exceder de 15 bytes. Por tanto, los prefijos redundantes pueden producir excepciones de código de operación no válido.
- Accesos al límite del segmento.
Un acceso de 16 bits en el offset 0FFFFh en el 8086 provoca un acceso a los bytes ubicados en las posiciones 0FFFFh y 0 (se da la vuelta alrededor del segmento). En el 286 y superiores, se
produce una excepción de violación de límites. En el 386 y superiores se produce también 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 críticos.
- LOCK.
Esta instrucción no está limitada de ninguna manera en el 8086 y en el 286. En el 386 y superiores su uso está restringido a determinadas instrucciones.
- Ejecución paso a paso.
La prioridad de la excepción paso a paso en el 286 y superiores es más alta que la de una interrupción externa; por tanto, las interrupciones externas no pueden ser traceadas.
- Registro de FLAGS.
Difiere algo en los bits 12 al 15 en todos los procesadores; el 386 dispone además de un registro de flags de 32 bits.
- Interrupción NMI.
Desde el 286 y superiores, una NMI no puede interrumpir una rutina de tratamiento NMI.
- Error del coprocesador.
En el 286 y superiores se utiliza el vector 16; en el 8086 cualquier vector.
- Prefijos de las instrucciones del coprocesador.
Al producirse una excepción de error de coprocesador, en el 8086 se almacena un CS:IP que no incluye prefijos -si los había-, al contrario que en el 286 y superiores.
- Límite del primer megabyte.
En el 8086 la memoria es circular; al final del primer megabyte se vuelve a comenzar por las posiciones más bajas de la memoria. En el 286 y superiores, se accede a la memoria extendida (un artificio hardware en los PC lo impide al forzar A20 a estado bajo, pero puede ser solventado).
- Instrucciones de cadena repetitivas.
El CS:IP grabado en el 8086 no incluye el prefijo, si existe; en el 286 y superiores sí.
3.3.2. - INSTRUCCIONES ESPECIFICAS DEL 286.
A continuación se describen las instrucciones adicionales que incorporan los 286 en modo real, que también pueden ser consideradas cuando trabajamos con los microprocesadores compatibles V20 y V30, así como con los procesadores superiores al 286. Las instrucciones del modo protegido se dirigen especialmente a la multiprogramación y el tiempo compartido, siendo específicas de la conmutación 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 límites de una matriz. Los límites de la matriz los definen dos palabras consecutivas en la memoria apuntadas por mem16. Si está fuera de los límites, se produce una interrupción 5 en la que el IP apilado queda apuntando a la instrucción BOUND (¡no se incrementa!).
ENTER crea una estructura de pila para un procedimiento de alto nivel.
Las instrucciones PUSH permiten meter valores inmediatos a la pila: es válido hacer PUSH 40h.
IMUL puede multiplicar cualquier registro de 16 bits por una constante inmediata, devolviendo un resultado palabra (CF=1 si no cabe en 16 bits); por ejemplo, es válido IMUL CX,25. También se admiten tres operandos: IMUL r1, r2, imm. En este caso, se multiplica r2 por el valor inmediato
(8/16 bits) y el resultado se almacena en r1. Tanto r1 como r2 han de ser de 16 bits.
LEAVE abandona los procedimientos de alto nivel (equivale a MOV SP,BP / POP BP).
PUSHA/POPA: Introduce en la pila y en este orden los registros AX, CX, DX, BX, SP, BP, SI y DI -o los saca en orden inverso-. Ideal en el manejo de interrupciones y muy usada en las BIOS de 286 y 386.
OUTS (salida de cadenas) e INS (entrada de cadenas) repetitivas (equivalente a MOVS y LODS).
RCR/RCL, ROR/ROL, SAL/SAR y SHL/SHR admiten una constante de rotación distinta de 1.
3.3.3. - INSTRUCCIONES PROPIAS DEL 386 Y 486.
Además de todas las posibilidades adicionales del 286, el 386 y el 486 permiten utilizar cualquier registro de 32 bits de propósito general en todos los modos de funcionamiento, incluido el modo real, tales como EAX, EBX, ECX, EDX, ESI, EDI, EBP. Sin embargo no deben intentarse direccionamientos por encima de los 64K. En otras palabras, se pueden utilizar para acelerar las operaciones pero no para acceder a más memoria. Por ejemplo, si EBX > 0FFFFh, la instrucción MOV AX,[EBX] tendría un resultado impredecible. Además, estos procesadores cuentan con dos segmentos más: además de DS, ES, CS y SS se pueden emplear también 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 más de 64 Kb en modo real: en la sección 3.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 sólo están disponibles los modos tradicionales. En cambio, con los de 32 se puede utilizar en el direccionamiento indirecto cualquier registro: es válida, por ejemplo, una instrucción 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 también de 32 bits. Cuando dos registros deben sumarse para calcular la dirección efectiva, el segundo puede estar multiplicado por 2, 4 u 8; por ejemplo, es válida la instrucción MOV AL,[EDX+EAX*8]. Por supuesto, bajo DOS hay que asegurarse siempre que el resultado de todas las operaciones que determinan la dirección efectiva no excede de 0FFFFh (0FFFEh si se accede a palabras y 0FFFCh en accesos a dobles palabras en memoria).
BOUND r32, mem32: Se admiten ahora operandos de 32 bits.
BSF/BSR: Exploración de bits hacia adelante y atrás, respectivamente. La sintaxis es:
BSF reg, reg ó BSF reg, [memoria]
BSR reg, reg ó BSR reg, [memoria]
Donde reg puede ser de 16 ó 32 bits. Se comienza a explorar por el bit 0 (BSF) o por el más significativo (BSR) del segundo operando: si no aparece ningún bit activo (a 1) el indicador ZF se activa; en caso contrario se almacena en el primer operando la posición relativa de ese bit:
MOV AX,8
BSF BX,AX
JZ ax_es_0 ; no se saltará, además BX = 3
BT/BTC/BTR/BTS: Operaciones sobre bits: comprobación, comprobación y complementación, comprobación y puesta a 0, comprobación y puesta a 1. Sintaxis (ejemplo sobre BT):
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 número de bit del primer operando que indique el segundo operando (entre 0 y 31) en el acarreo. A continuación 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 también 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 también ECX) no excedan de 0FFFFh.
Jcc: Los saltos condicionales ahora pueden ser de ¡32 bits!. Mucho cuidado con la directiva .386 en los programas en que se desee mantener la compatibilidad con procesadores anteriores. JECXZ se utiliza en vez de JCXZ (mismo código de operación).
LODSD: Similar a LODSW pero empleando ESI, EDI y ECX y cargando datos de 32 bits en EAX. Se puede emplear bajo DOS siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh.
LSS, LFS, LGS: similar a LDS o LES pero con esos registros de segmento.
MOV CRx,reg / MOV DRx,reg y los recíprocos: acceso a registros de control y depuración.
MOVSD: Similar a MOVSW pero empleando ESI, EDI, ECX y moviendo datos de 32 bits. Se puede emplear bajo DOS para acelerar las transferencias siempre que ESI y EDI (utilizando REP también ECX) no excedan de 0FFFFh. Operando sobre la memoria de vídeo sólo se obtiene ventaja si la tarjeta es realmente de 32 bits.
MOVSX / MOVZX: carga con extensión 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 sólo 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 también ECX) no rebasen 0FFFFh.
Prefijos FS: y GS: en los accesos a memoria, referenciando a esos segmentos.
PUSHAD / POPAD: Similares a PUSHA y POPA pero con los registro de 32 bits. La instrucción POPAD falla en la mayoría 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 detrás de POPAD.
PUSHFD/POPFD introducen y sacan de la pila los flags de 32 bits.
SCASD: Similar a SCASW pero empleando ESI, EDI, ECX y buscando datos de 32 bits. Se puede emplear bajo DOS siempre que ESI y EDI (usando REP también ECX) no rebasen 0FFFFh.
SETcc reg8 ó mem8: Si se cumple la condición cc, se pone a 1 el byte de memoria o registro 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 precisión a la izquierda/derecha. La sintaxis es (ejemplo sobre SHLD):
SHLD regmem16, reg16, imm8 ó SHLD regmem16, reg16, C
SHLD regmem32, reg32, imm8 ó SHLD regmem32, reg32, CL
Donde regmem es un registro u operando en memoria, indistintamente, del tamaño indicado. En el caso de SHLD, se desplaza el primer operando a la izquierda tanto como indique el tercer operando (contador). Una vez desplazado, los bits menos significativos se rellenan con los más significativos del segundo operando, que no resulta alterado. SHRD es análogo pero al revés.
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 también ECX) no excedan de 0FFFFh.
3.3.3. - DETECCIÓN DE UN SISTEMA AT O SUPERIOR.
Hay casos en los que es necesario determinar si una máquina es AT o superior: no ya de cara a emplear instrucciones propias del 286 en modo real (también disponibles en los V20/V30 y 80188/80186) sino debido a la necesidad de acceder a ciertos chips (por ejemplo, el segundo controlador de interrupciones) que de antemano se sabe que sólo equipan máquinas AT o superiores. Es importante por tanto determinar la presencia de un AT, de cara a evitar ciertas instrucciones que podrían 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, además, la evolución 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 más significativo
PUSH AX
POPF ; intentar poner a 0 los 4 bits más significativos de los flags
PUSHF
POP AX
AND AH,0F0h ; seguirán valiendo 1 excepto en un 80286 o superior
CMP AH,0F0h
JE no_es_AT
JMP si_es_AT ; es 286 o superior
3.3.5. - EVALUACIÓN EXACTA DEL MICROPROCESADOR INSTALADO.
Sobra decir que las instrucciones avanzadas deben ser utilizadas con la previa comprobación del tipo de procesador, aunque sólo sea para decir al usuario que se compre una máquina más potente antes de abortar la ejecución del programa. Para averiguar el procesador de un ordenador puede emplearse el siguiente programa de utilidad, basado en el procedimiento procesador? que devuelve en AX un código numérico entro 0 y 8 distinguiendo entre los 9 procesadores más difíciles de identificar de los ordenadores compatibles. Nota: el 486 no tiene que tener coprocesador necesariamente (el 486sx carece de él).
Algunas versiones de procesador 486 y todos los procesadores posteriores soportan la instrucción 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 instrucción. De este modo, resulta trivial detectar el Pentium o cualquier procesador posterior que aparezca. Esta instrucción está documentada, por ejemplo en alguno de los ficheros que acompañan al Interrupt List. Para los propósitos de este libro no es preciso en general detectar más allá del 386.
Es normal que el lector recién iniciado en el ensamblador no entienda absolutamente nada de este programa, ya que hasta los siguientes capítulos no será explicada la sintaxis del lenguaje. En ese caso, puede saltarse este ejemplo y continuar en el capítulo siguiente, máxime si no tiene previsto trabajar con otras instrucciones que no sean las del 8086. Por último, recordar que las instrucciones específicas del 286 en modo real también están disponibles en los V20/V30 de NEC y la serie 80188/80186 de Intel.
; ********************************************************************
; * *
; * CPU v2.2 (c) Septiembre 1992 CiriSOFT *
; * (c) Grupo Universitario de Informática - Valladolid *
; * *
; * Este programa determina el tipo de microprocesador del equipo *
; * y devuelve un código ERRORLEVEL indicándolo: *
; * *
; * 0-8088, 1-8086, 2-NEC V20, 3-NEC V30, *
; * 4-80188, 5-80186, 6-286, 7-386, 8-486 *
; * *
; * Aviso: Utilizar TASM 2.0 o compatible exclusivamente. *
; * *
; ********************************************************************
cpu 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 ; número de iteración-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 ; número de CPUs tratadas-1
JBE otro_proc
LEA DX,texto_fin ; últimos caracteres
CALL print
MOV AH,4Ch ; retornar código 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 más significativo
PUSH AX
POPF ; intentar poner a 0 los 4 bits más
PUSHF ; significativos de los flags
POP AX
AND AX,0F000h ; seguirán 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 crítico
MOV EDX,ESP ; preservar ESP en EDX
AND ESP,0FFFFh ; borrar parte alta de ESP
AND ESP,0FFFCh ; forzar ESP a múltiplo 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 sólo 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 sólo 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 aún 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 García de Celis."
DB 13,10," El microprocesador de este "
DB "equipo es compatible:",10
separador_txt DB 13,10,9,9,9,"$"
texto_fin DB 13,10,"$"
cpu ENDS
END inicio
3.3.6. - MODO PLANO (FLAT) DEL 386 Y SUPERIORES.
Como ya se comentó, no es estrictamente cierto que no se pueda rebasar el límite de 64 Kb en los segmentos en modo real. El problema es que al encender el ordenador, el 386 tiene definidos por defecto dichos límites de 64 Kb. Sin embargo, se puede pasar un momento a modo protegido, ampliar el límite y volver a modo real. Entonces se consigue el llamado modo flat o plano. No solo es factible de este modo saltar la restricción de 64 Kb, sino que además 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 máquina 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 dirección absoluta 0B8000h a través de EBX) de letras 'A'.
Otra restricción de este programa de ejemplo es que no activa la línea A20 de direcciones; dicho de otro modo, el bit 21º (de los 32 bits de la dirección de memoria) suele estar forzado a 0 por defecto al arrancar. Para acceder a la memoria de vídeo esto no es problema, pero por encima del primer megabyte podría haber problemas según a qué dirección se pretenda acceder. De todos modos, sería relativamente sencillo habilitar la línea A20 directamente o a través de una función del controlador XMS.
Naturalmente, se sale de los objetivos de este libro describir el modo protegido o explicar los pasos que realiza esta rutina de demostración. Consúltese al efecto la bibligrafía recomendada del apéndice.
; +------------------------------------------------------------------+
; | Rutina para activar el modo flat del 386 y superiores (acceso |
; | a 4 Gb en modo real). |
; | |
; | TASM flat386 /m5 |
; | TLINK flat386 /t /32 |
; +------------------------------------------------------------------+
.386p ; sólo para 386 o superior
segmento SEGMENT USE16
ASSUME CS:segmento, DS:segmento
ORG 100h
prueba:
CALL flat386 ; activar modo flat
XOR AX,AX
MOV DS,AX
MOV EBX,0B8000h ; dirección de vídeo 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 momentáneamente a modo protegido de
; manera directa (necesita la CPU en modo real). No se
; activa la línea A20 (necesario hacerlo directamente
; o a través de algún servicio XMS antes de acceder a
; las áreas de memoria extendida afectadas).
flat386 PROC
PUSH DS
PUSH ES
PUSH EAX
PUSH BX
PUSH CX
MOV CX,SS
XOR EAX,EAX
MOV AX,CS
SHL EAX,4 ; dirección lineal de segmento CS
ADD EAX,OFFSET gdt ; desplazamiento de GDT
MOV CS:[gd2],EAX ; guardar dirección 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 prebúsqueda
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 prebúsqueda
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
EL HARDWARE DE APOYO AL MICROPROCESADOR
En este capítulo 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 expansión.
Nota: Por limitaciones técnicas, al describir los circuitos integrados las señales que son activas a nivel bajo no tendrán la tradicional barra negadora encima; en su lugar aparecerán 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 estándar, etc. Algunas veces bastará con la información que aparece en el apartado donde se describe la relación del chip con los PC; sin embargo, a menudo será necesario consultar la información técnica 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 capítulo; sin embargo, poseen circuitos equivalentes que los emulan por completo.
4.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 capítulo es suficiente con el 8088.
El 8088 puede trabajar en dos modos: mínimo (pequeñas aplicaciones) y máximo (sistemas multiprocesador). Los requerimientos de conexión con el exterior cambian en función del modo que se decida emplear, aunque una parte de las señales es común en ambos.
LÍNEAS COMUNES AL MODO MÁXIMO Y MÍNIMO DEL 8088.
AD7..0: | Address Data Bus. Son líneas 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 línea S5 indica el estado del banderín de interrupciones; las líneas 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. Línea de entrada que indica el final de la operación de memoria o E/S. |
INTR: | Interrupt Request. Línea de petición de interrupciones enmascarables; el 8088 la observa periódicamente. |
-TEST: | Test. En respuesta a la instrucción máquina WAIT (¡no TEST!), el 8088 se para a comprobar esta línea hasta que se ponga a 0. |
NMI: | Non-maskable Interrupt. Línea de petición de la interrupción de tipo 2, que no puede ser enmascarada. |
RESET: | Provoca una inicialización interna que culmina saltando a FFFF:0. |
MN/-MX: | Esta línea indica si se trata de un sistema mínimo o máximo. |
LÍNEAS EXCLUSIVAS DEL MODO MÍNIMO DEL 8088.
IO/-M: | Status Line. Indica si se trata de un acceso a memoria o a un puerto de entrada/salida. No es válida todo el tiempo (solo a ratos). |
-wr: | Write. Indica una escritura en memoria o en un dispositivo de entrada/salida (según el estado de IO/-M). |
-INTA: | Interrupt Acknowledge. Es la señal de reconocimiento de interrupción (solicitada a través de INTR o NMI). |
ALE: | Address Latch Enable. Indica al exterior que las líneas de dirección contienen una dirección válida, con objeto de que la circuitería externa la almacene en una pequeña memoria (latch). Señal necesaria sólo por culpa de la multiplexación. |
DT/-R: | Data Transmit/Receive. Señal necesaria para emplear un transceiver 8286/8287 en el bus, con objeto de controlar el flujo de datos a través del mismo (si se recibe/transmite). |
-DEN: | Data Enable. Necesario también para emplear el transceiver: sirve como entrada de habilitación para el mismo. |
HOLD: | Hold. Línea de entrada para solicitar al 8088 que se desconecte de los buses. Empleada por los controladores de DMA. |
HLDA: | Hold Acknowledge. Línea complementaria de HOLD: el 8088 envía una señal de reconocimiento cuando se desconecta del bus. |
-SS0: | Status Line. Línea de apoyo que, junto con IO/-M y DT/-R, permite determinar con precisión el estado del bus: |
IO/-M DT/-R -SS0 Estado del bus
------- ------- ------- ------------------------------
1 0 0 Reconocimiento de interrupción
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 código
0 0 1 Lectura de memoria
0 1 0 Escritura en memoria
0 1 1 Inactivo
LÍNEAS EXCLUSIVAS DEL MODO MÁXIMO DEL 8088.
-S0/-S1/-S2: Status. Estas líneas indican el estado del bus:
-S2 -S1 -S0 Estado del bus
------- ------- ------- ------------------------------
0 0 0 Reconocimiento de interrupción
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 código
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 demás procesadores conectados al bus forzar al 8088 a que libere el bus al final del ciclo en curso. |
-LOCK: | Lock. Línea que sirve al 8088 para prohibir el acceso al bus a otros procesadores (se activa tras la instrucción máquina LOCK y dura mientras se ejecuta la siguiente instrucción -la que sigue a LOCK, que es realmente un prefijo-). También se activa automáticamente en los momentos críticos de un ciclo de interrupción. |
QS1/QS0: | Queue Status. Permite determinar el estado de la cola de instrucciones del 8088. |
DIFERENCIAS IMPORTANTES CON EL 8086.
El 8086 cambia el patillaje sensiblemente, aunque la mayoría de las señales son similares. En lugar de 8 líneas de datos y direcciones multiplexadas (AD0..7) el 8086 posee 16, ya que el bus de datos es de 16 bits. Existe una línea especialmente importante en el 8086, -BHE/S7 (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 composición binaria de las instrucciones máquina de ningún procesador; en los casos en que sea necesario se pueden ver los códigos con alguna utilidad de depuración. Sin embargo, a título de curiosidad, se expone a continuación el formato general de las instrucciones (aunque hay algunas excepciones y casos especiales).
El código de operación ocupa 6 bits; el bit D indica si es el operando fuente (=0) el que está en el campo registro (REG) o si lo es el operando destino (=1): la razón es que el 8086 sólo admite un operando a memoria, como mucho (o el fuente, o el destino, no los dos a la vez). El bit W indica el tamaño de la operación (byte/palabra). MOD indica el modo de direccionamiento: 00-sin desplazamiento (no existe campo de desplazamiento), 01-desplazamiento de 8 bits, 10-desplazamiento de 16 bits y 11-registro (tanto fuente como destino están en registro). El campo REG indica el registro involucrado en la instrucción, que puede ser de 8 ó 16 bits (según indique W): 0-AX/AL, 1-CX/CL, 2-DX/DL, 3-BX/BL, 4-SP/AH, 5-BP/CH, 6-SI/DH, 7-DI/BH; en el caso de registros de segmento sólo son significativos los dos bits de menor peso: 00-ES, 01-CS, 10-SS, 11-DS. El campo R/M, en el caso de 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].
Descargar
Enviado por: | El remitente no desea revelar su nombre |
Idioma: | castellano |
País: | España |