Basicamente, um Olá, Mundo em Assembly com 64 bits fazemos assim:
section .data
msg db "Olá, Mundo!"
section .text
global _start
_start:
mov rax, 1 ; Chamada sys_write
mov rdi, 1 ; Saída padrão
mov rsi, msg ; Ponteiro pra mensagem
mov rdx, 11 ; Número de caracteres (bytes) da mensagem
syscall ; Ordem pra executar os comandos.
mov rax, 60 ; identificador do sys_exit
mov rdi, 0 ; Valor de saída
syscall
; Registradores como ax são de 16 bits, eax de 32 bits, e rax de 64 bits.
; Registradores estão na CPU
; Dentro do rax tem eax, do eax tem ax, etc.
; rax - id do system call
; rdi - primeiro parãmetro
; rsi - segundo parâmetro
; rdx - terceiro parâmetro
; r10 - quarto parâmetro
; r9 - quinto parâmetro
; r8 - sexto parâmetro
; Chamadas syscall:
; 0 - sys_read
; 1 - sys_write
; 2 - sys_open
; 3 - sys_close
; 60 - sys_exit
; rdi:
; 0 - entrada padrão
; 1 - saída padrão
; 2 - erro
; sys_exit:
; 0 - deu certo
; 1 - deu erro
Para compilar e montar, digite nasm -f elf64 olamundo64.asm -o olamundo64.o
e ld olamundo64.o -s -o olamundo64
.
Para pular de linha, basta isso:
section .data
msg db "Olá, Mundo!", 10 ; O 10 é o número do pulo de linha na tabela ASCII, não conta no tamanho da string
Para ver os códigos ASCII, veja essa tabela: https://www.ascii-code.com/pt
Veja um exemplo de exibição de mensagens só com ASCII:
section .data
msg db 79, 105, 33, 10 ; Altere o rdx para 3. Exibirá "Oi"
Podemos criar uma macro informando o tamanho da mensagem automaticamente:
section .data
msg db 79, 105, 33, 10
tam equ 3 ; Macro que define uma constante chamada tam
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, tam ; Usando a constante
syscall
mov rax, 60
mov rdi, 0
syscall
Para ele pegar o tamanho da string automaticamente, colocamos esse parâmetro em tam:
section .data
msg db "Olá, Mundo!", 10
tam equ $- msg ; Macro que define uma constante chamada tam
Para ler dados do teclado, faça o programa assim:
section .data
perg db "Como você se chama? "
tamPerg equ $- perg
ola db "Olá, "
tamOla equ $- ola
tamNome equ 10
section .bss
nome resb tamNome
section .text
global _start
_start:
; Imprimindo a mensagem "Como você se chama"
mov rax, 1
mov rdi, 1
mov rsi, perg
mov rdx, tamPerg
syscall
; Ler o nome do usuário
mov rax, 0
mov rdi, 0
mov rsi, nome
mov rdx, tamNome
syscall
; Imprimir "Olá"
mov rax, 1
mov rdi, 1
mov rsi, ola
mov rdx, tamOla
syscall
; Imprimir nome do usuário
mov rax, 1
mov rdi, 1
mov rsi, nome
mov rdx, tamNome
syscall
; Encerrar o programa
mov rax, 60
mov rdi, 0
syscall
Compile e monte ele.
Vamos considerar esse programa:
section .data
ola db "Olá "
ana db "Ana."
section .text
global _start
_start:
; Imprimindo a mensagem "Olá"
mov rax, 1
mov rdi, 1
mov rsi, ola
mov rdx, 5
syscall
; Encerrando o programa
mov rax, 60
mov rdi, 0
syscall
Para imprimir o nome também, faça assim:
section .data
ola db "Olá "
ana db "Ana."
section .text
global _start
_start:
; Imprimindo a mensagem "Olá"
mov rax, 1
mov rdi, 1
mov rsi, ola
mov rdx, 4
syscall
; Imprimindo a mensagem "Ana"
mov rax, 1
mov rdi, 1
mov rsi, ana
mov rdx, 4
syscall
; Encerrando o programa
mov rax, 60
mov rdi, 0
syscall
Agora volte ao programa anterior, mas com 9 no tamanho da mensagem Olá:
section .data
ola db "Olá "
ana db "Ana."
section .text
global _start
_start:
; Imprimindo a mensagem "Olá"
mov rax, 1
mov rdi, 1
mov rsi, ola
mov rdx, 9
syscall
; Encerrando o programa
mov rax, 60
mov rdi, 0
syscall
Note que ele imprimiu "Ana" também, mas desta vez não deveria ser impresso por não temos dado comandos para imprimir isso. A section .data é como se fosse a memória RAM, onde ola e ana são ponteiros para determinadas posições da memória. O ponteiro de Ana será logo após o de Olá. Por isso o certo é especificar o tamanho com o "$-".
Podemos fazer os mesmos programas para serem compatíveis com 32 bits, apenas mudando os comandos de r para e (como por exemplo, rax vira eax, rdi vira edi, etc. Da mesma forma fazemos para 16 bits, usando coisas como ax e di).
Se fizermos as alterações para 32 bits e compilar para 64 bits, ele continuará compatível, no entanto, poderá ter problemas para 16 bits.
Com a depuração, podemos ver passo a passo da execução de um programa para encontrar erros.
Vamos supor esse programa:
section .data
msg db "Boa noite.", 10
tam equ $- msg
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdi, tam ; Erro proposital, o certo seria rdx
syscall
mov rax, 60
mov rdi, 0
syscall
Compile e monte. Para depurar o programa digite gdb -q ./programa
. No caso, só poderemos usar o breakpoint na parte _start, digitando dentro do gdb break _start
, depois digite run
e layout asm
, e para uma sintaxe mais parecida com o programa, digite set disassembly-flavor intel
e layout regs
. No layout regs mostrará cada instrução sendo executada passo a passo, para isso digite stepi
. Para examinar a string (como por exemplo em hexa, 0x402000) digite x/s 0x402000
. Para sair, dê Ctrl C e digite quit
.
Voltando ao nosso programa de perguntar o nome do usuário, vamos refazer ele com funções:
section .data
perg db "Como você se chama? "
tamPerg equ $- perg
ola db "Olá, "
tamOla equ $- ola
tamNome equ 10
section .bss
nome resb tamNome
section .text
global _start
_start:
call imprimePergunta ; call chama as funções
call lerNome
call imprimeOla
call imprimeNome
call encerraPrograma
imprimePergunta:
; Imprimindo a mensagem "Como você se chama"
mov rax, 1
mov rdi, 1
mov rsi, perg
mov rdx, tamPerg
syscall
ret ; Retorna para quem a chamou
lerNome:
; Ler o nome do usuário
mov rax, 0
mov rdi, 0
mov rsi, nome
mov rdx, tamNome
syscall
ret
imprimeOla:
; Imprimir "Olá"
mov rax, 1
mov rdi, 1
mov rsi, ola
mov rdx, tamOla
syscall
ret
imprimeNome:
; Imprimir nome do usuário
mov rax, 1
mov rdi, 1
mov rsi, nome
mov rdx, tamNome
syscall
ret
encerraPrograma:
; Encerrar o programa
mov rax, 60
mov rdi, 0
syscall
ret
Com isso, quando precisarmos repetir alguma coisa no programa, basta chamar a função especificada ao invés de reescrever ele. A ordem das funções não importa, apenas a ordem na qual elas são invocadas.