Assembly na prática
A melhor forma de entender assembly é executá-lo passo a passo e observar os registradores mudarem. Aqui você faz exatamente isso — sem precisar instalar nada.
Do código de alto nível ao código de máquina
Quando você escreve for (int i = 10; i > 0; i--), o compilador traduz
isso para uma sequência de instruções de máquina: inicializa um registrador, compara,
salta condicionalmente. Toda a lógica do seu programa — if, while,
chamada de função, retorno — vira MOV, CMP, JNE, CALL, RET.
A tradução é mecânica: loops viram saltos para trás, if/else viram saltos
condicionais, variáveis locais viram registradores ou posições na pilha. Entender essa
tradução ajuda a prever performance, ler disassemblies e depurar crashes.
JE vs. BEQ vs. beq) e a convenção de chamada.
Aprender a ler assembly de uma arquitetura te prepara para ler qualquer outra.
Simulador de assembly passo a passo
Escolha um programa, clique em ▶ Iniciar e use ⏭ Passo para executar uma instrução por vez. Observe EAX, EBX, ECX, EDX e as flags atualizarem. Registradores que mudaram ficam destacados em azul.
Loops e condicionais em assembly
; Tradução de um loop for em C → assembly x86 ; C: for (int i = 10; i > 0; i--) soma += i; mov ecx, 10 ; i = 10 (ECX é o contador clássico) mov eax, 0 ; soma = 0 (EAX acumula) loop_inicio: add eax, ecx ; soma += i dec ecx ; i-- (DEC atualiza ZF) jnz loop_inicio ; se ZF=0 (ECX≠0) → volta hlt ; EAX = 55 ; Note: o loop vai de 10→1; quando ECX chega a 0, ; DEC seta ZF=1 e JNZ não pula → sai do loop. ; JNZ é equivalente a JNE ("jump if not zero/equal")
Tradução de if/else
; Tradução de if/else em C → assembly ; C: if (a > b) resultado = a; else resultado = b; mov eax, 42 ; a = 42 mov ebx, 73 ; b = 73 cmp eax, ebx ; calcula flags de (a - b) jg eax_maior ; se a > b → pula (ZF=0, SF=OF) mov eax, ebx ; else: resultado = b eax_maior: ; aqui eax = max(a, b) hlt ; EAX = 73 ; O compilador com -O2 gera isso sem o JG: ; cmp edi, esi ; cmovg eax, edi ← move condicional (sem branch!)
loop_inicio: não ocupam
espaço — são apenas marcadores. O assembler substitui o label pelo deslocamento relativo
(para JMP/JE) ou pelo endereço absoluto (para CALL). Isso é por isso que mover código
pode quebrar saltos hardcoded — um dos problemas resolvidos por Position Independent Code (PIC).
Escreva seu próprio assembly
Mini projeto: usando o simulador acima, escreva um programa que calcule o fatorial de 5 sem recursão (use um loop como o exemplo de soma). O resultado deve estar em EAX quando HLT executar. Dica: inicialize EAX=1, ECX=5, multiplique EAX por ECX a cada iteração com ADD (EAX += EAX * (ECX-1) não funciona; use INC/DEC e acumulação manual ou pense em outra forma).
Projeto principal: escreva em arquivo .s (sintaxe AT&T ou Intel) uma função assembly para calcular a soma dos quadrados de 1 a N. Compile com nasm ou gas, link e chame de um programa C. Meça a performance vs. a versão em C puro com clock_gettime.
Desafio extra: use o simulador para implementar o algoritmo de Euclides para MDC: enquanto B≠0: temp=B, B=A mod B, A=temp. O mod pode ser simulado com subtração repetida (se A>=B: A-=B; repita). Qual é o MDC de 48 e 36?
Teste sua intuição
for em C é traduzido para assembly?
loop_inicio:) e como a CPU os usa?
Onde você encontra isso
Emuladores de console
Emuladores como PCSX2 (PS2) e Dolphin (GameCube/Wii) implementam a CPU do console em software: lêem instruções MIPS/PowerPC, decodificam e executam — exatamente o que o simulador acima faz, mas para outra ISA. Dolphin usa JIT (compilação just-in-time) para traduzir PowerPC → x86 em tempo real, executando 100× mais rápido que interpretação pura.
Exploits e buffer overflow
Exploits clássicos de buffer overflow sobrescrevem o endereço de retorno na pilha com o endereço de código do atacante. O payload é literalmente bytes de instrução assembly (shellcode) escrito na memória do processo. Mitigações modernas como ASLR, stack canaries e NX bit (No-eXecute) dificultam mas não eliminam esse vetor.
Tradução binária (Rosetta 2)
Quando a Apple migrou para Apple Silicon (ARM), o Rosetta 2 traduziu apps x86-64 para ARM64. Primeiro uso: AOT (ahead-of-time) durante instalação, traduzindo todo o executável. Uso dinâmico: JIT para código gerado em runtime. Resultado: apps x86 rodando em ARM quase na mesma velocidade de apps nativos.