College Online
0%

Operações Aritméticas e Lógicas

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

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

Operandos Imediatos

Na aula anterior, todas as operações usavam dois registradores como fonte. Mas é muito comum operar com constantes: incrementar um contador, aplicar uma máscara de bits, comparar com um valor fixo. Para isso, MIPS oferece instruções com operando imediato — uma constante de 16 bits codificada diretamente na instrução.

Assembly MIPS
# addi: soma com imediato
addi $t0, $s1, 5        # $t0 = $s1 + 5
addi $s3, $s3, 1        # $s3++ (incremento)
addi $s3, $s3, -1       # $s3-- (decremento, imediato negativo)

# andi: AND com imediato
andi $t0, $s1, 0xFF     # $t0 = byte menos significativo de $s1
                          # Máscara: 0000...11111111

# ori: OR com imediato
ori  $t0, $s1, 0x01     # Liga o bit 0 de $s1

# slti: set on less than immediate
slti $t0, $s1, 100      # $t0 = ($s1 < 100) ? 1 : 0
i
Por que não existe subi? Porque addi com valor negativo faz a mesma coisa. addi $t0, $s1, -5 equivale a subtrair 5. MIPS evita instruções redundantes — princípio de simplicidade.

Operandos imediatos são limitados a 16 bits (valores de -32768 a 32767 para addi/slti, ou 0 a 65535 para andi/ori). Para carregar constantes de 32 bits, usamos a combinação lui + ori:

Assembly MIPS
# Carregar a constante 0x003D0900 em $s0
# 0x003D0900 = 0000 0000 0011 1101  0000 1001 0000 0000
#              ───── upper 16 ─────  ───── lower 16 ────

lui  $s0, 0x003D        # $s0 = 0x003D0000 (carrega nos 16 bits superiores)
ori  $s0, $s0, 0x0900   # $s0 = 0x003D0900 (preenche os 16 bits inferiores)

# A pseudo-instrução "li" faz isso automaticamente:
li   $s0, 0x003D0900    # O assembler gera lui + ori

Operações de Deslocamento (Shift)

Deslocamentos movem os bits de um registrador para a esquerda ou direita. São fundamentais para multiplicação/divisão por potências de 2 e para manipulação de campos de bits.

Assembly MIPS
# sll: Shift Left Logical (desloca à esquerda, preenche com 0)
sll  $t0, $s1, 4        # $t0 = $s1 << 4 (multiplica por 16)
                          # R-type: shamt = 4

# Exemplo visual:
# $s1 = 0000 0000 0000 0000 0000 0000 0000 1011  (= 11)
# sll $t0, $s1, 4:
# $t0 = 0000 0000 0000 0000 0000 0000 1011 0000  (= 176 = 11 × 16)

# srl: Shift Right Logical (desloca à direita, preenche com 0)
srl  $t0, $s1, 2        # $t0 = $s1 >> 2 (divide por 4, sem sinal)

# sra: Shift Right Arithmetic (desloca à direita, preserva o sinal)
sra  $t0, $s1, 2        # $t0 = $s1 >> 2 (divide por 4, com sinal)

# Diferença entre srl e sra:
# $s1 = 1111 1111 1111 1111 1111 1111 1111 0000  (= -16 em complemento a 2)
# srl $t0, $s1, 2:
# $t0 = 0011 1111 1111 1111 1111 1111 1111 1100  (= +1073741820, ERRADO!)
# sra $t0, $s1, 2:
# $t0 = 1111 1111 1111 1111 1111 1111 1111 1100  (= -4, CORRETO!)
i
Shift como multiplicação Deslocar à esquerda por N bits equivale a multiplicar por 2^N. Deslocar à direita por N bits (sra para números com sinal) equivale a dividir por 2^N. O hardware de shift é muito mais rápido que o multiplicador, então compiladores preferem shift quando o multiplicador é potência de 2.

Multiplicação e Divisão

Multiplicar dois números de 32 bits pode gerar um resultado de até 64 bits. MIPS lida com isso usando dois registradores especiais: HI e LO.

Assembly MIPS
# mult: multiplicação com sinal
# Resultado de 64 bits vai para HI:LO
mult $s1, $s2           # HI:LO = $s1 × $s2

# Para pegar o resultado:
mflo $t0                # $t0 = LO (32 bits inferiores do resultado)
mfhi $t1                # $t1 = HI (32 bits superiores do resultado)

# Se o resultado cabe em 32 bits, basta usar mflo.
# Se HI ≠ 0 (e HI ≠ -1 para negativos), houve overflow.

# multu: multiplicação sem sinal
multu $s1, $s2          # HI:LO = $s1 × $s2 (tratados como unsigned)

# Exemplo numérico:
# $s1 = 100, $s2 = 200
# mult $s1, $s2
# LO = 20000 (100 × 200), HI = 0
# mflo $t0  → $t0 = 20000
Assembly MIPS
# div: divisão inteira com sinal
div  $s1, $s2           # LO = $s1 / $s2 (quociente)
                          # HI = $s1 % $s2 (resto)

mflo $t0                # $t0 = quociente
mfhi $t1                # $t1 = resto

# divu: divisão sem sinal
divu $s1, $s2           # LO = quociente, HI = resto (unsigned)

# Exemplo:
# $s1 = 17, $s2 = 5
# div $s1, $s2
# LO = 3 (17 / 5), HI = 2 (17 % 5)

# CUIDADO: divisão por zero é undefined behavior no MIPS!
# O hardware NÃO gera exceção automaticamente.
# O programador deve verificar antes de dividir.

Operações Lógicas Bit a Bit

Operações lógicas manipulam bits individuais. São a base de mascaramento, extração de campos e criptografia.

Assembly MIPS
# Tabela verdade das operações:
# A  B  | AND | OR  | XOR | NOR
# ──────┼─────┼─────┼─────┼─────
# 0  0  |  0  |  0  |  0  |  1
# 0  1  |  0  |  1  |  1  |  0
# 1  0  |  0  |  1  |  1  |  0
# 1  1  |  1  |  1  |  0  |  0

# AND: extrai bits (mascaramento)
and  $t0, $s1, $s2      # $t0 = $s1 AND $s2
andi $t0, $s1, 0x0F     # Extrai os 4 bits inferiores de $s1

# OR: liga bits
or   $t0, $s1, $s2      # $t0 = $s1 OR $s2
ori  $t0, $s1, 0x80     # Liga o bit 7 de $s1

# XOR: inverte bits seletivamente
xor  $t0, $s1, $s2      # $t0 = $s1 XOR $s2
xori $t0, $s1, 0xFF     # Inverte os 8 bits inferiores de $s1

# NOR: NOT + OR (MIPS não tem NOT; usa NOR com $zero)
nor  $t0, $s1, $zero    # $t0 = NOT $s1 (inverte todos os bits)
                          # NOR(A, 0) = NOT(A OR 0) = NOT(A)

Exemplo Completo: Extraindo Campos de uma Palavra

Um cenário real: extrair um campo de bits de uma instrução MIPS. Suponha que $s0 contém uma instrução e queremos extrair o campo rs (bits 25 a 21):

Assembly MIPS
# $s0 contém a instrução: 000000 10001 10010 01000 00000 100000
# Queremos extrair rs (bits 25-21) = 10001 = 17 ($s1)

# Passo 1: deslocar à direita para alinhar o campo
srl  $t0, $s0, 21       # Move bits 25-21 para posição 4-0

# Passo 2: aplicar máscara para isolar 5 bits
andi $t0, $t0, 0x1F     # 0x1F = 0001 1111 (máscara de 5 bits)

# $t0 agora contém 10001 = 17 (número do registrador $s1)

# ─────────────────────────────────────────────
# Outro exemplo: construir um byte a partir de 2 nibbles
# nibble_alto = $s1 (valor 0-15), nibble_baixo = $s2 (valor 0-15)
# Resultado: byte = (nibble_alto << 4) | nibble_baixo

sll  $t0, $s1, 4        # Desloca nibble alto para posição 7-4
or   $t0, $t0, $s2      # Combina com nibble baixo

# Se $s1 = 0xA (1010) e $s2 = 0x3 (0011):
# $t0 = 0xA3 = 10100011

No harness.os

Operações lógicas (AND/OR) mapeiam diretamente para regras de decisão no harness.os — condições compostas que controlam workflows.

Mapeamento: Operações Lógicas → Regras do harness.os
Operação MIPS                   Equivalente no harness.os
──────────────────────────────   ────────────────────────────────────
AND (condição composta)          Regra: "SE projeto É build
                                   AND fase É testing → executar QA"
                                 Ambas as condições devem ser verdadeiras.

OR (condição alternativa)        Regra: "SE status É blocked
                                   OR prazo expirou → notificar"
                                 Basta uma condição ser verdadeira.

XOR (decisão exclusiva)          Workflow: "OU deploy staging
                                   OU deploy production" (nunca ambos
                                   simultaneamente).

NOT (negação)                    Regra: "SE NOT (testes passaram)
                                   → bloquear merge"

Shift (multiplicar contexto)     Escalar: carregar N chunks de
                                   knowledge = shift left no escopo
                                   da context window. Cada bit extra
                                   de contexto dobra a informação
                                   disponível, mas custa tokens.

Homework

  1. Escreva código MIPS para calcular $t0 = $s0 * 7 usando apenas instruções de shift e add (sem usar mult). Dica: 7 = 8 - 1.
  2. Dado que $s0 = 0xABCD1234, escreva código MIPS para extrair: (a) o byte mais significativo (0xAB), (b) o nibble menos significativo (0x4), (c) os 16 bits inferiores (0x1234).
  3. Explique a diferença entre srl e sra ao deslocar o valor 0xFFFFFFE0 por 3 posições. Qual resultado cada instrução produz?

Resumo

Verifique seu entendimento

Qual instrução MIPS você usaria para dividir um número com sinal por 8 de forma eficiente?

  • div $t0, $s1, 8
  • srl $t0, $s1, 3
  • sll $t0, $s1, 3
  • sra $t0, $s1, 3