Saltar a contenido

Instruction Set Architecture

Instruction Set Architectures


Una Instruction Set Architecture (ISA) especifica la sintaxis y la semántica del lenguaje ensamblador en cada arquitectura. No es solo una sintaxis diferente, sino que está integrada en el diseño central de un procesador, ya que afecta la forma y el orden en que se ejecutan las instrucciones y su nivel de complejidad. ISA consiste principalmente en los siguientes componentes:

  • Instructions
  • Registers
  • Memory Addresses
  • Data Types
Component Description Example
Instructions La instrucción que se procesará en el formato opcode operand_list. Generalmente hay 1, 2 o 3 operandos separados por comas. add rax, 1, mov rsp, rax, push rax
Registers Se utilizan para almacenar operandos, direcciones o instrucciones temporalmente. rax, rsp, rip
Memory Addresses La dirección en la que se almacenan datos o instrucciones. Puede apuntar a memoria o registros. 0xffffffffaa8a25ff, 0x44d0, $rax
Data Types El tipo de datos almacenados. byte, word, double word

Estos son los componentes principales que distinguen diferentes ISA's y lenguajes ensambladores. Cubriremos cada uno de ellos en más profundidad en las siguientes secciones y aprenderemos cómo usar varias instrucciones.

Existen dos principales Instruction Set Architectures que son ampliamente utilizadas:

  1. Complex Instruction Set Computer (CISC) - Utilizado en procesadores Intel y AMD en la mayoría de las computadoras y servidores.
  2. Reduced Instruction Set Computer (RISC) - Utilizado en procesadores ARM y Apple, en la mayoría de los teléfonos inteligentes y algunas laptops modernas.

Veamos los pros y contras de cada uno y las principales diferencias entre ellos.


CISC

La arquitectura CISC fue una de las primeras ISA's desarrolladas. Como su nombre lo sugiere, la arquitectura CISC favorece instrucciones más complejas para ser ejecutadas al mismo tiempo y así reducir el número total de instrucciones. Esto se hace para depender lo más posible de la CPU combinando instrucciones menores en instrucciones más complejas.

Por ejemplo, supongamos que queremos sumar dos registros con la instrucción 'add rax, rbx'. En este caso, un procesador CISC puede hacer esto en un solo ciclo de instrucción 'Fetch-Decode-Execute-Store', sin tener que dividirlo en múltiples instrucciones para obtener rax, luego obtener rbx, luego sumarlos y luego almacenarlos en rax, cada uno de los cuales tomaría su propio ciclo de instrucción 'Fetch-Decode-Execute-Store'.

Dos razones principales impulsaron esto:

  1. Permitir que más instrucciones se ejecuten al mismo tiempo diseñando el procesador para ejecutar instrucciones más avanzadas en su núcleo.
  2. En el pasado, la memoria y los transistores eran limitados, por lo que se prefería escribir programas más cortos combinando múltiples instrucciones en una sola.

Para habilitar a los procesadores a ejecutar instrucciones complejas, el diseño del procesador se vuelve más complicado, ya que está diseñado para ejecutar una gran cantidad de instrucciones complejas diferentes, cada una de las cuales tiene su propia unidad para ejecutarla.

Además, aunque se necesita un solo ciclo de instrucción para ejecutar una sola instrucción, como las instrucciones son más complejas, cada ciclo de instrucción toma más ciclos de reloj. Esto lleva a un mayor consumo de energía y calor para ejecutar cada instrucción.


RISC

La arquitectura RISC favorece dividir las instrucciones en instrucciones menores, y así la CPU está diseñada solo para manejar instrucciones simples. Esto se hace para delegar la optimización al software escribiendo el código ensamblador más optimizado.

Por ejemplo, la misma instrucción anterior add r1, r2, r3 en un procesador RISC obtendría r2, luego obtendría r3, los sumaría y finalmente los almacenaría en r1. Cada una de estas instrucciones toma un ciclo completo 'Fetch-Decode-Execute-Store', lo que lleva, como se puede esperar, a un mayor número total de instrucciones por programa y, por lo tanto, un código ensamblador más largo.

Al no admitir varios tipos de instrucciones complejas, los procesadores RISC solo admiten un número limitado de instrucciones (~200) en comparación con los procesadores CISC (~1500). Entonces, para ejecutar instrucciones complejas, esto debe hacerse a través de una combinación de instrucciones menores en Assembly.

Se dice que podemos construir una computadora de propósito general con un procesador que solo admita una instrucción. Esto indica que podemos crear instrucciones muy complejas utilizando únicamente la instrucción sub. ¿Puedes pensar cómo se podría lograr esto?

Por otro lado, una ventaja de dividir instrucciones complejas en menores es tener todas las instrucciones con la misma longitud, ya sea de 32 bits o 64 bits. Esto permite diseñar la velocidad del reloj de la CPU en función de la longitud de la instrucción, de modo que ejecutar cada etapa en el ciclo de instrucción siempre tome exactamente un ciclo de reloj de máquina.

El siguiente diagrama muestra cómo las instrucciones CISC toman una cantidad variable de ciclos de reloj, mientras que las instrucciones RISC toman una cantidad fija: risc vs cisc cycles

Ejecutar cada etapa de instrucción en un solo ciclo de reloj y solo ejecutar instrucciones simples lleva a que los procesadores RISC consuman una fracción de la energía consumida por los procesadores CISC, lo que hace que estos procesadores sean ideales para dispositivos que funcionan con baterías, como teléfonos inteligentes y laptops.


CISC vs. RISC

La siguiente tabla resume las principales diferencias entre CISC y RISC:

Area CISC RISC
Complexity Favorece instrucciones complejas Favorece instrucciones simples
Length of instructions Instrucciones más largas - Longitud variable 'múltiplos de 8 bits' Instrucciones más cortas - Longitud fija '32-bit/64-bit'
Total instructions per program Menos instrucciones totales - Código más corto Más instrucciones totales - Código más largo
Optimization Depende de la optimización por hardware (en CPU) Depende de la optimización por software (en Assembly)
Instruction Execution Time Variable - Múltiples ciclos de reloj Fijo - Un ciclo de reloj
Instructions supported by CPU Muchas instrucciones (~1500) Pocas instrucciones (~200)
Power Consumption Alto Muy bajo
Examples Intel, AMD ARM, Apple

En el pasado, tener un código ensamblador más largo debido a un mayor número total de instrucciones por programa era una desventaja significativa para los procesadores RISC debido a los recursos limitados en memoria y almacenamiento. Sin embargo, hoy en día esto ya no es un problema tan grande, ya que la memoria y el almacenamiento no son tan costosos ni limitados como solían ser.

Con ensambladores y compiladores escribiendo código extremadamente optimizado a nivel de software, los procesadores RISC se están volviendo más rápidos que los procesadores CISC, incluso al ejecutar y procesar aplicaciones pesadas, mientras consumen mucha menos energía.

Todo esto está haciendo que los procesadores RISC sean más comunes en los últimos años. RISC podría convertirse en la arquitectura dominante en los próximos años. Pero, mientras hablamos, la gran mayoría de las computadoras y servidores que estaremos pentesteando están ejecutándose en procesadores Intel/AMD con la arquitectura CISC, lo que hace que aprender ensamblador CISC sea nuestra prioridad. Dado que los conceptos básicos de todas las variantes de lenguaje Assembly son bastante similares, aprender ARM Assembly debería ser más sencillo después de completar este módulo.