Un conjunto de instrucciones, repertorio de instrucciones, juego de instrucciones o ISA (del inglés instruction set architecture, «arquitectura del conjunto de instrucciones») es una especificación que detalla las instrucciones que una unidad central de procesamiento puede entender y ejecutar, o el conjunto de todos los comandos implementados por un diseño particular de una CPU. El término describe los aspectos del procesador generalmente visibles para un programador, incluidos los tipos de datos nativos, las instrucciones, los registros, la arquitectura de memoria y las interrupciones, entre otros aspectos. Existen principalmente tres tipos: CISC (Complex Instruction Set Computer), RISC (Reduced Instruction Set Computer) y SISC (Simple Instruction Set Computing).
La arquitectura del conjunto de instrucciones (ISA) se emplea a veces para distinguir este conjunto de características de la microarquitectura, que son los elementos y técnicas que se emplean para implementar el conjunto de instrucciones. Entre estos elementos se encuentran las microinstrucciones y los sistemas de caché.
Procesadores con diferentes diseños internos pueden compartir un conjunto de instrucciones; por ejemplo, el Intel Pentium y AMD Athlon implementan versiones casi idénticas del conjunto de instrucciones x86, aunque tienen diseños diferentes.
El lenguaje máquina está construido a partir de los estados discretos o instrucciones. En la arquitectura de procesamiento, una instrucción dada puede especificar muy bien:
Las operaciones más complejas se construyen a partir de estas, que (en una máquina Von Neumann) se ejecutan secuencialmente, o según el control de flujo.
Muchas o pocas de las operaciones disponibles incluidas en la mayoría de conjuntos son:
Algunas computadoras incluyen instrucciones "complejas". Dichas instrucciones pueden tomar muchas instrucciones en otros equipos. Estas se caracterizan por instrucciones que necesitan varios pasos, como el control de múltiples unidades funcionales.
Algunos ejemplos son:
Un tipo complejo de la instrucción que ha llegado a ser particularmente popular recientemente es SIMD (Single Instruction, Multiple Data), una operación que realice la misma operación aritmética en pedazos múltiples de datos al mismo tiempo. SIMD tienen la capacidad de manipular vectores y matrices grandes en tiempo mínimo. Las instrucciones de SIMD permiten la paralelización fácil de los algoritmos implicados comúnmente en sonido, imagen, y el proceso video. Varias implementaciones de SIMD se han traído al mercado bajo nombres comerciales tales como MMX, 3DNow! y AltiVec.
Diseñar sistemas de instrucciones es muy complejo. En un principio existieron dos tipos de sistemas, un modo de clasificarlos es por complejidad arquitectónica. El primer tipo era el CISC (Complex Instruction Set Computer) que tenía muchas instrucciones diferentes. En los años 70s, en IBM hicieron una investigación en la que encontraron que muchas instrucciones en el sistema podrían ser eliminadas. El resultado fue el segundo tipo, el RISC (Reduced Instruction Set Computer), una arquitectura que utiliza un sistema más pequeño de instrucciones. Un conjunto de instrucciones más simple puede ofrecer el potencial para velocidades más altas, tamaño reducido del procesador, y un consumo de energía menor. Sin embargo, un conjunto más complejo puede optimizar operaciones comunes, mejorar memoria/eficiencia de caché, o simplificar la programación.
Cualquier conjunto de instrucciones se puede implementar de varias maneras. Todas las maneras de implementar un conjunto de instrucciones dan el mismo modelo programado, y todas pueden hacer funcionar los mismos ejecutables binarios. Las varias maneras de implementar un conjunto de instrucciones dan diversas compensaciones entre el coste, el funcionamiento, el consumo de energía, el tamaño, etc.
Al diseñar microarquitecturas, los ingenieros usaron bloques de circuitos electrónicos “duramente-conectados” (diseñado a menudo por separado) por ejemplo, los multiplexores, los contadores, los registros, las ALU, etcétera. Un cierto tipo del lenguaje de transferencia de registros es a menudo usado para describir la codificación y la secuencia de cada instrucción de ISA usando esta microarquitectura física.
Hay también algunos nuevos diseños de CPU que compilan el conjunto de instrucción a una RAM escribible o FLASH dentro de la CPU (tal como el procesador Recursiv y el Imsys Cjip), o FPGA (computación reconfigurable). Western Digital MCP-1600 es un ejemplo antiguo, usando una ROM dedicada, separada del microcódigo.
ISA se puede también emular en software por un intérprete. Naturalmente, debido a la interpretación de “overhead”, es más lento que ejecutar programas directamente sobre el hardware emulado. Hoy, es práctica para los vendedores de nuevos ISA o microarquitecturas poner emuladores del software a disposición de los desarrolladores de programas informáticos antes de que la implementación del hardware esté lista.
Los detalles de la implementación tienen una influencia fuerte en las instrucciones particulares seleccionadas para el conjunto de instrucción. Por ejemplo, muchas implementaciones de la instrucción “pipline” permiten solamente una carga de memoria (load) o almacén en memoria (store) por instrucción, llevando a carga-almacena arquitectura (RISC). Por otro ejemplo, algunas maneras de implementar la instrucción “pipline” llevaron a una ranura de retardo.
La demanda de procesamiento de señal digital de alta velocidad ha empujado en el sentido contrario, forzando la implementación de instrucción de manera particular. Por ejemplo, para realizar los filtros digitales es bastante insuficiente, la instrucción del MAC en un procesador típico de señal digital (DSP) se debe implementar usando una arquitectura de Harvard que pueda traer una instrucción y dos palabras de datos simultáneamente, y requiere un solo ciclo.
En computadoras antiguas, la memoria del programa era costosa, así que minimizar el tamaño de un programa para asegurar que va a caber en la memoria limitada era a menudo central. Así el tamaño combinado de todas las instrucciones necesitó realizar una tarea particular, la densidad del código, era una característica importante de cualquier sistema de instrucción. Las computadoras con alta densidad del código también tenían a menudo instrucciones complejas para la entrada del procedimiento, los retornos parametrizados, los lazos, etc. Sin embargo, "instrucciones CISC" combinan simplemente una operación básica de la ALU, tal como "add", con el acceso de uno o más operandos en memoria (usando modos de dirección tales como directo, indirecto, indexado). Ciertas arquitecturas pueden permitir dos o tres operandos (incluido el resultado) directamente en memoria o pueden permitir realizar funciones tales como el incremento automático del puntero.
RISC, fueron los primeros implementados con profundidad en el período de rápido crecimiento de las memorias de subsistemas, se reduce el código con el fin de simplificar el circuito de aplicación y con ello tratar de aumentar el rendimiento a través de las frecuencias de reloj más elevadas y el uso de más registros. Las instrucciones RISC suelen realizar sólo una operación, como una "suma" de registros o una "carga" de una posición de memoria en un registro, también suelen utilizar una longitud de instrucciones fijas, mientras que un conjunto de instrucciones típicas CISC tiene instrucciones muchos más cortas que esta longitud fija. Las instrucciones de longitud fija son menos complicadas de manejar que las instrucciones de ancho variable, por varias razones (por ejemplo: no tener que comprobar si una instrucción se extiende a ambos lados de una línea de caché o el límite de memoria virtual de la página), y por lo tanto algo más fácil de optimizar la velocidad. Sin embargo, como los equipos RISC normalmente requieren más y más para implementar las instrucciones que ejecutan una determinada tarea, hacen menos óptimo el uso del ancho de banda y de la memoria caché.
Las computadoras mínimas del conjunto de instrucciones (MISC) son una forma de máquina apilada, donde hay pocas instrucciones separadas (16-64), para poder caber instrucciones múltiples en una sola palabra de máquina. Estos tipos de núcleos llevan a menudo poco silicio para implementarse, así que pueden ser observadas fácilmente en un FPGA o en una forma multinúcleo. La densidad del código es similar al RISC; la densidad creciente de la instrucción es compensada requiriendo más de las instrucciones primitivas para hacer una tarea.
El conjunto de instrucciones puede ser clasificado por el número máximo de operandos explícitamente especificados en las instrucciones (en los ejemplos que siguen, a, b y c se refieren a celdas de memoria, mientras que reg1 y sucesivos se refieren a los registros de la máquina).
0-operando, también llamada máquina de pila: todas las operaciones aritméticas se ejecutan en la parte superior de una o dos posiciones de la pila, push y pop son las instrucciones utilizadas para acceder a la memoria: push a, push b, add, pop c.
1-operando (máquinas de una dirección), también llamadas máquinas de acumulador, incluida en la mayoría de las primeras computadoras y muchos microcontroladores pequeños: la mayoría de instrucciones especifican un operando explícito a la derecha (un registro, una posición de memoria, o una constante) y un operando a la izquierda: load a, add b, store c.
2-operando, la mayoría de las máquinas CISC y RISC entran en esta categoría:
CISC – load a, reg1, add reg1, b; store reg1, c
RISC – cargas que requieren la memoria explícita, las instrucciones serían: load a,reg1; load b,reg2; add reg1,reg2; store reg2,c
3-operando, permite una mejor reutilización de los datos: CISC - bien una sola instrucción: add a, b, c, o más generalmente: move a,reg1; add reg1,b,c como la mayoría de las máquinas se limitan a dos operandos de memoria. RISC - Debido a la gran cantidad de bits necesarios para codificar los tres registros, este esquema no suele estar disponible en los procesadores RISC con pequeñas instrucciones de 16 bits: load a,reg1; load b,reg2; add reg1+reg2->reg3; store reg3,c.
Más operandos, algunas máquinas CISC permiten una variedad de modos de direccionamiento que permiten más de 3 operandos (registros o accesos a memoria), como el VAX "POLY", instrucción de evaluación de polinomio.
Las características que se pretende que tenga un conjunto de instrucciones son cuatro, principalmente:
Se puede comprobar que para que un conjunto de instrucciones sea completo solo se necesitan cuatro instrucciones:
En esta idea se basan las arquitecturas RISC, no obstante, con este conjunto no se puede conseguir la eficiencia del repertorio de instrucciones por lo que en la práctica el conjunto suele ser más amplio en aras de conseguir un mejor rendimiento, tanto en uso de recursos como en consumo de tiempo.
Copian datos de un origen a un destino, sin modificar el origen y normalmente sin afectar a los flags o indicadores de condición. Pueden transferir palabras, fracciones de palabras (bytes, media palabra) o bloques completos de n bytes o palabras.
Estas operaciones pueden ser:
Son efectuadas por la ALU y suelen cambiar los flags o indicadores de condición.
Pueden tener instrucciones para tratar con números en BCD e incluyen operaciones en coma flotante, lo cual se identifica con una 'f' antes del nombre del nemotécnico como por ejemplo: fabsolute
Suelen preceder a una instrucción de bifurcación condicional y modifican los flags. No hay que pensar que las instrucciones de salto condicional dependen de este repertorio, ya que lo único que hace el salto condicional es consultar los flags y salta si precede, pero no depende de ninguna instrucción de comparación. (de hecho cualquier operación aritmética realizada anteriormente a un salto condicional puede provocar que este "salte").
Realizan operaciones booleanas "bit a bit" entre dos operandos. Como las aritméticas también modifican los flags.
Pueden ser aritmético o lógico y pueden incluir o no rotaciones. Pueden ser de izquierda a derecha.
Comprueban un bit del operando y su valor lo reflejan en el indicador de cero. Pueden poner un bit a 0 o complementarlo.
Permiten modificar la secuencia normal de ejecución de un programa, puede hacerse por salto condicional relativo o absoluto.
Se clasifican en cuatro grupos:
Pueden ser condicionales o incondicionales, se suelen especificar como jump o branch, y en el caso de los condicionales se suele llamar jcond o bcond, donde cond es una o más letras que indican la condición que ha de cumplirse para que el salto se produzca.
Salta sin comprobar ninguna condición.
Salta si la condición se cumple.
Invoca la ejecución de funciones anteriormente definidas.
Se usan para llamar a las rutinas de servicio de interrupción y esto se puede hacer por hardware o bien por software. Necesita una instrucción similar a return para retornar al contexto anterior pero restableciendo el estado de la máquina, para no afectar a la aplicación a la cual se interrumpió (iret).
Son instrucciones de transferencia salvo que el origen/destino de dicho flujo es un puerto de un dispositivo de entrada y salida. Estas instrucciones pueden darse mediante dos alternativas:
Nemoténicos más frecuentes:
Se usan como semáforos, esto es, se declara una variable entera que tendrá el valor 0 si el recurso esta libre y 1 si está siendo utilizado, de manera que si un procesador comprueba y el semáforo está en 1 tendrá que esperar hasta que este cambie a 0 (1 = semáforo rojo y 0 = semáforo verde).