College Online
0%

Instruções e Registradores

Módulo 2 · Aula 1 ~18 min de leitura Nível: Intermediário

Vídeo da aula estará disponível em breve

O Conjunto de Instruções MIPS

Até agora, discutimos arquitetura em termos abstratos: modelos, filosofias de projeto, RISC vs CISC. A partir deste módulo, colocamos a mão na massa: vamos programar em linguagem de montagem (Assembly) usando a arquitetura MIPS.

MIPS (Microprocessor without Interlocked Pipeline Stages) é a ISA clássica usada como veículo de ensino por Patterson & Hennessy. A escolha é pedagógica: MIPS é limpa, regular, e cada instrução tem formato previsível. Os conceitos que você aprende aqui transferem diretamente para ARM, RISC-V, e até x86 (com mais complexidade).

Uma instrução MIPS sempre opera sobre registradores — posições de armazenamento dentro do processador, extremamente rápidas (acesso em 1 ciclo de clock). Diferente da memória principal (centenas de ciclos), registradores são o recurso mais precioso do processador.

i
Por que registradores e não memória? O princípio de projeto RISC é: operações aritméticas só entre registradores. Carregar dados da memória é uma operação separada (load/store). Isso simplifica o hardware e permite pipeline eficiente. Patterson chama isso de design principle #3: make the common case fast.

Os 32 Registradores do MIPS

MIPS possui exatamente 32 registradores de propósito geral, cada um com 32 bits (4 bytes). A convenção de uso segue o padrão ABI (Application Binary Interface):

Registradores MIPS — Convenção de Uso
Reg    Nome       Uso                                    Preservado?
─────  ────────   ─────────────────────────────────────   ──────────
$0     $zero      Constante 0 (hardwired)                 N/A
$1     $at        Reservado para o assembler               Não
$2-3   $v0-$v1    Valores de retorno de funções            Não
$4-7   $a0-$a3    Argumentos de funções (1º a 4º)          Não
$8-15  $t0-$t7    Temporários (caller-saved)               Não
$16-23 $s0-$s7    Salvos (callee-saved)                    Sim
$24-25 $t8-$t9    Temporários adicionais                   Não
$26-27 $k0-$k1    Reservados para o kernel do SO           Não
$28    $gp        Global pointer                           Sim
$29    $sp        Stack pointer                            Sim
$30    $fp        Frame pointer                            Sim
$31    $ra        Return address (endereço de retorno)     Sim

Registradores especiais (NÃO são dos 32):
  HI, LO  — Resultado de multiplicação/divisão
  PC       — Program Counter (endereço da instrução atual)

Alguns pontos fundamentais:

Formatos de Instrução

Toda instrução MIPS ocupa exatamente 32 bits (4 bytes). Existem três formatos:

Formato R-type (Register)
┌────────┬───────┬───────┬───────┬───────┬────────┐
│ opcode │  rs   │  rt   │  rd   │ shamt │ funct  │
│ 6 bits │5 bits │5 bits │5 bits │5 bits │ 6 bits │
└────────┴───────┴───────┴───────┴───────┴────────┘
  Total: 6+5+5+5+5+6 = 32 bits

Usado por: add, sub, and, or, slt, sll, srl, jr
  opcode = 0x00 (R-type usa o campo funct para diferenciar)
  rs, rt = registradores fonte
  rd     = registrador destino
  shamt  = shift amount (para instruções de deslocamento)
  funct  = código da operação

Exemplo: add $t0, $s1, $s2
  opcode=000000  rs=$s1(10001)  rt=$s2(10010)  rd=$t0(01000)  shamt=00000  funct=100000
  Binário: 000000 10001 10010 01000 00000 100000
  Hex:     0x02324020
Formato I-type (Immediate)
┌────────┬───────┬───────┬──────────────────────┐
│ opcode │  rs   │  rt   │     immediate        │
│ 6 bits │5 bits │5 bits │      16 bits         │
└────────┴───────┴───────┴──────────────────────┘
  Total: 6+5+5+16 = 32 bits

Usado por: addi, andi, ori, lw, sw, beq, bne, slti
  rs  = registrador fonte
  rt  = registrador destino (ou segundo fonte em beq/bne)
  imm = constante de 16 bits (com ou sem extensão de sinal)

Exemplo: addi $t0, $s1, 42
  opcode=001000  rs=$s1(10001)  rt=$t0(01000)  imm=0000000000101010
  Binário: 001000 10001 01000 0000000000101010
Formato J-type (Jump)
┌────────┬────────────────────────────────────┐
│ opcode │            address                 │
│ 6 bits │            26 bits                 │
└────────┴────────────────────────────────────┘
  Total: 6+26 = 32 bits

Usado por: j (jump), jal (jump and link)
  address = endereço alvo (os 2 bits menos significativos
            são sempre 00 porque instruções são alinhadas
            em 4 bytes, então não precisam ser armazenados)

Endereço efetivo = PC[31:28] | address[25:0] | 00
  Os 4 bits mais significativos vêm do PC atual.
i
Design Principle #1: Simplicity favors regularity Ter apenas 3 formatos de instrução, todos com 32 bits, simplifica enormemente o hardware de decodificação. Compare com x86, onde instruções variam de 1 a 15 bytes.

Instruções Básicas: add, sub, and, or, slt

As instruções aritméticas e lógicas do MIPS operam exclusivamente sobre registradores. Veja os exemplos fundamentais:

Assembly MIPS
# Soma: $t0 = $s1 + $s2
add  $t0, $s1, $s2      # R-type: rd=$t0, rs=$s1, rt=$s2

# Subtração: $t1 = $s3 - $s4
sub  $t1, $s3, $s4      # R-type: rd=$t1, rs=$s3, rt=$s4

# AND bit a bit: $t2 = $s1 AND $s2
and  $t2, $s1, $s2      # Cada bit: 1 AND 1 = 1, resto = 0

# OR bit a bit: $t3 = $s1 OR $s2
or   $t3, $s1, $s2      # Cada bit: 0 OR 0 = 0, resto = 1

# Set on Less Than: $t4 = ($s1 < $s2) ? 1 : 0
slt  $t4, $s1, $s2      # Comparação: resultado é 0 ou 1

Vamos ver um exemplo prático. Considere a expressão em C: f = (g + h) - (i + j), onde f, g, h, i, j estão nos registradores $s0 a $s4:

Assembly MIPS
# f = (g + h) - (i + j)
# Mapeamento: f=$s0, g=$s1, h=$s2, i=$s3, j=$s4

add  $t0, $s1, $s2      # $t0 = g + h
add  $t1, $s3, $s4      # $t1 = i + j
sub  $s0, $t0, $t1      # f = (g + h) - (i + j)

# Nota: precisamos de registradores temporários ($t0, $t1)
# para resultados intermediários. É por isso que existem
# 10 temporários ($t0-$t9) na convenção MIPS.

Outro exemplo com operações lógicas — extrair e manipular bits:

Assembly MIPS
# Exemplo: verificar se o bit 3 de $s0 está ligado
# Máscara: 0000...01000 = 8 (em decimal)

andi $t0, $s0, 8        # Isola o bit 3
slt  $t1, $zero, $t0    # $t1 = 1 se bit 3 ligado, 0 caso contrário

# Exemplo: limpar os 4 bits mais baixos de $s1
# Máscara: 1111...10000 = 0xFFFFFFF0

lui  $t0, 0xFFFF        # $t0 = 0xFFFF0000
ori  $t0, $t0, 0xFFF0   # $t0 = 0xFFFFFFF0
and  $s1, $s1, $t0      # Limpa bits 0-3 de $s1

O Registrador $zero e Pseudo-instruções

O registrador $zero (sempre contendo 0) permite construir operações que outras ISAs precisam de instruções dedicadas:

Assembly MIPS
# MOVE: copiar um valor de um registrador para outro
# Não existe instrução "move" no MIPS! O assembler traduz:
move $t0, $s1            # Pseudo-instrução
# para:
add  $t0, $s1, $zero     # $t0 = $s1 + 0 = $s1

# CLEAR: zerar um registrador
# Não existe instrução "clear"!
add  $t0, $zero, $zero   # $t0 = 0 + 0 = 0

# NEGATE: negar um valor
sub  $t0, $zero, $s1     # $t0 = 0 - $s1 = -$s1

# LOAD IMMEDIATE: carregar constante (pseudo-instrução)
li   $t0, 42             # O assembler converte para:
# addi $t0, $zero, 42    # $t0 = 0 + 42 = 42

# NOP (no operation): não faz nada
sll  $zero, $zero, 0     # Desloca $zero por 0, resultado em $zero
                          # Codificação: 0x00000000 (32 zeros)
i
Pseudo-instruções O assembler MIPS aceita instruções que não existem no hardware. move, li, la, blt, bge são exemplos de pseudo-instruções — o assembler as converte em sequências de instruções reais. Isso simplifica a programação sem complicar o hardware.

Da Linguagem de Alto Nível ao Assembly

Vejamos a tradução completa de um trecho de C para MIPS. Suponha um array A[] de inteiros com endereço base em $s6, e h em $s2:

Assembly MIPS
# Código C: A[12] = h + A[8];
# $s6 = endereço base de A, $s2 = h

lw   $t0, 32($s6)       # $t0 = A[8]  (offset = 8 * 4 = 32 bytes)
add  $t0, $s2, $t0      # $t0 = h + A[8]
sw   $t0, 48($s6)       # A[12] = $t0 (offset = 12 * 4 = 48 bytes)

# Por que offset = índice * 4?
# Cada inteiro ocupa 4 bytes (32 bits).
# A[0] está no endereço base ($s6 + 0)
# A[1] está no endereço base ($s6 + 4)
# A[8] está no endereço base ($s6 + 32)
# A[12] está no endereço base ($s6 + 48)

Cada instrução tem uma responsabilidade única e clara: lw carrega da memória, add soma, sw armazena na memória. Essa separação é a essência da filosofia RISC.

No harness.os

Registradores são variáveis de estado local — como o contexto que cada agente mantém durante uma sessão no harness.os.

Mapeamento: Registradores MIPS → harness.os
Registrador MIPS               Equivalente no harness.os
──────────────────────────────  ────────────────────────────────────
$s0-$s7 (callee-saved)         Estado persistente da sessão — dados
                                  que sobrevivem entre tool calls

$t0-$t9 (temporários)          Variáveis locais de uma tool call —
                                  descartadas após a execução

$a0-$a3 (argumentos)           Parâmetros de entrada de uma tool
                                  (ex: project_slug em start_session)

$v0-$v1 (retorno)              Resultado retornado pela tool call

$ra (return address)           Contexto de retorno: de onde veio
                                  a chamada, para onde voltar

$sp (stack pointer)            Context window: ponteiro para o
                                  "topo" do contexto disponível

Princípio: assim como MIPS tem apenas 32 registradores
(recurso escasso que deve ser gerenciado com cuidado),
a context window de um agente é finita. Cada token
carregado é um "registrador" ocupado. Gerenciar esse
recurso é a essência da token economy.

Homework

  1. Traduza para MIPS: f = 2*g + h - 1, onde f=$s0, g=$s1, h=$s2. Use apenas instruções reais (não pseudo-instruções).
  2. Identifique o formato (R, I, ou J) de cada instrução: add $t0, $s1, $s2, addi $t0, $s1, 10, j 0x00400000, lw $t0, 0($sp).
  3. Explique por que MIPS tem um registrador hardwired em zero ($zero). Dê três exemplos de como ele simplifica o ISA.
  4. No harness.os, identifique o equivalente dos registradores "callee-saved" ($s0-$s7): quais dados de uma sessão precisam ser preservados quando um agente chama outro?

Resumo

Verifique seu entendimento

Qual é a principal razão pela qual todas as instruções MIPS têm exatamente 32 bits de tamanho?

  • Para economizar memória no armazenamento de programas
  • Para permitir endereçamento de até 4 GB de memória
  • Para simplificar o hardware de busca e decodificação, favorecendo regularidade
  • Para manter compatibilidade com processadores x86