Programação básica em Assembly
Rodrigo Gonçalves Ribeiro
Será abordado:
*Comandos básicos de Assembly
*Estrutura de código
pré-requisitos
Noção básica de programação e lógica
Introdução
Assembly é uma linguagem de montagem. Ou seja, diferente da maioria das outras linguagens, que são compiladas e/ou interpretadas, programar em Assembly é escrever um código que é diretamente entendido pelo hardware.
Assembly é a língua que usamos para falar com os mais diversos tipos de hardwares, como os microprocessadores e microcontroladores.
Todos sabem que em níveis mais baixos, no metal, a máquina só entende os bits: valores lógicos 1 ou 0 (na verdade, se refere a tensão e voltagem, mas podemos compreender como números 1 ou números 0), esse é código de máquina, o único que o hardware verdadeiramente entende e obedece.
Podemos programar escrevendo diretamente em binário, ou no sistema hexadecimal.
Mas seria algo extremamente complicado, confuso e as chances de errarmos seria bem maior.
E é aí que entra o Assembly: é conjunto de notação (ou símbolos, conhecidos por mnemônicos) que os humanos entendem. É a linguagem de máquina.
Registradores
Os registros podem ser visto como pequenas regiões memória dentro do microprocessador dentro das quais podemos colocar diferentes valores de diferentes tamanhos dependendo do tipo de registro.
Utilizando as instruções podemos colocar as informações contidas ate então na memória principal e colocar-las dentro dos registros do processador.
Tipos:
Os comandos em Assembly
Operações aritméticas:
ADD – Soma - Ex: ADD $s0, $s1, $s2 - O resultado da soma dos valores nos registradores $s1 e $s2 são armazenados em $s0;
SUB – Subtração - Ex: SUB $s0, $s1, $s2 - O resultado da subtração dos valores nos registradores $s1 e $s2 são armazenados em $s0;
MUL – Multiplicação - Ex: MUL $s0, $s1, $s2 - O resultado da multiplicação dos valores nos registradores $s1 e $s2 são armazenados em $s0;
DIV – Divisão - Ex: DIV $s0, $s1, $s2 - O resultado da divisão dos valores nos registradores $s1 e $s2 são armazenados em $s0.
Existe também o método de aplicar as operações aritméticas com um número imediato, ou seja um número que não esta em um registrador, basta adicionar um "I" no final do comando:
ADDI – Soma - Ex: ADDI $s0, $s1, 10 - Está armazenando em $s0 o valor do registrador $s1 + 10;
SUBI – Subtração - Ex: SUBI $s0, $s1, 5 - Está armazenando em $s0 o valor do registrador $s1 - 5;
MULI – Multiplicação - Ex: MULI $s0, $s1, 4 - Está armazenando em $s0 o valor do registrador $s1 x 4;
DIVI – Divisão - Ex: DIVI $s0, $s1, 2 - Está armazenando em $s0 o valor do registrador $s1 / 2 .
Exemplo:
Os comandos em Assembly
Acesso a memória
Para adentrarmos em acesso a memória, primeiro é necessário saber sobre a memória, e seus endereços:
Os valores da esquerda são os endereços, e os do lado é o conteúdo armazenado nesse endereço.
Comandos:
SW(store word) - Grava o valor do registrador no endereço de memória
Ex.: SW $s0, 12($zero) --> Grava o valor de $s0 no endereço 12 + $zero(0) = 12
LW(load word) - Busca o valor no endereço da memória e coloca no registrador
Ex.:Lw $s0, 16($zero) --> Pega o valor que está no endereço 16 + $zero(0) = 16 e coloca em $s0
Vamos entender alguns pontos diferentes desta parte. Quando falamos de memória lá nas operações de desvio condicional e comparações, estávamos falando de uma memória de instruções, onde ficam armazenadas apenas as linhas do código. A memória que estamos trabalhando agora é uma memória de dados, onde ficam armazenados apenas dados e valores. Portanto, são duas memórias diferentes.
E o que é aquele 12($zero)? O número 12 significa o número do endereço da memória a ser acessado. O registrador que vem depois(pode ser qualquer registrador), serve apenas como um incremento para o valor da memória, ou seja, ele soma o número com o registrador, e o resultado é o endereço exato a ser acessado na memória. Esse incremento tem suas vantagens, como guardar na ordem desejada os valores.
Exemplo:
Os comandos em Assembly
Operações de desvio (condicional ou incondicional) e comparações
Vale saber que as linhas de código também ficam em endereços de memória como foi apresentado acima:
Os números do lado esquerdo do código, representam o endereço onde cada linha de comando está guardado na memória de instruções.
E o que é aquele JR $ra no final do programa? É um comando que salta para o return adress ($ra). É utilizado para retornar de funções. O return adress armazena o endereço da última instrução executada antes de algum salto para uma função. Como esse tutorial não abordará funções, vamos fazer todos os nossos programas terminando com o JR $ra.
Dessa forma, vamos aos comandos:
BEQ(branch on equal) - Compara dois registradores e se forem iguais pula para o endereço especificado
Ex.: BEQ $s0, $s1, 12 --> Se $s0 == $s1, vá para o endereço 12
BNE(branch on not equal) - Compara dois registradores e se não forem iguais pula para o endereço especificado
Ex.: BNE $s0, $s1, 18 --> Se $s0 != $s1, vá para o endereço 18
SLT(set on less than) - Compara dois registradores e se o primeiro for menor que o segundo, armazena 1 e um outro registrador, senão armazena 0
Ex.: SLT $t0, $s0, $s1 --> Se $s0 < $s1, $t0 = 1, senão $t0 = 0
SLE(set on less or equal than) - Mesma função do SLT, porém para menor ou igual
Ex.: SLE $t0, $s0, $s1 --> Se $s0 <= $s1, $t0 = 1 senão, $t0 = 0
SGT(set on great than) - Compara dois registradores e se o primeiro for maior que o segundo, armazena 1 e um outro registrador, senão armazena 0
Ex.: SLT $t0, $s0, $s1 --> Se $s0 < $s1, $t0 = 1, senão $t0 = 0
SGE(set on great or equal than) - Mesma função do SGT, porém para maior ou igual
Ex.: SGE $t0, $s0, $s1 --> Se $s0 >= $s1, $t0 = 1 senão, $t0 = 0
J(jump) - Salta para o endereço indicado
Ex.:J 10; --> Pula para o endereço 10
Exemplo:
Qual será o resultado de $s0 no final do código?
Vamos analisar cada linha do programa para descobrirmos:
Endereço 0: ADDI $s0, $s0, 0 --> Iniciando o registrador $s0 = 0
Endereço 4: ADDI $s1, $s1, 10 --> Iniciando o registrador $s1 = 10
Endereço 8: SGE $t0, $s0, $s1 --> Se $s0 >= $s1, $t0 = 1 senão $t0 = 0
Como no primeiro momento $s0(0) é menor que $s1(10), então $t0 = 0
Endereço 12: BNE $t0, $zero, 24 --> Se $t0 != 0, então pule para o endereço 24
Como no primeiro momento $t0 = 0, então não haverá salto
Endereço 16: ADDI $s0, $s0, 2 --> $s0(0) = $s0(0) + 2 = 2
Endereço 20: J 8 --> Salto para o endereço 8
Como haverá salto, o programa volta para o endereço 8 e executa novamente o código. Podemos perceber que o programa fará um loop até quando $s0 >= $s1(10). Quando isso for verdade, no endereço 8 ocorrerá $t0 = 1 e então no endereço 12 ocorrerá o salto para o endereço 24
Endereço 24: ADD $s0, $s0, $s1 --> $s0 = $s0(10) + $s1(10) = 20
Endereço 28: JR $ra --> Salta para o return adress($ra) e finaliza o programa.
Então, $s0, no final do programa, armazena o valor 20
Exercício
Transforme o código abaixo para o Assembly baseado na arquitetura MIPS
MAIN: LW $s0, 4 ($zero)
LW $s1, 8($zero)
LW $s2, 12($zero)
ADDI $t1, $s1, 3
SLT $t0, $s1, $t1
IF: BEQ $t0, $zero, END
DIVI $t0, $s1, 7
SGE $t1, $t0, $s2
BEQ $t1, $zero, ELSE
DIV $s0, $s1, $s2
SW $s0, 4($zero)
J END
ELSE: DIVI $t0, $s1, 2
MULI $t1, $t0, 10
ADD $t0, $t1, $s2
ADD $s1, $t0, $s0
SW $s1, 8($zero)
END: JR $rA
links úteis
Download do SPIM (simulador de MIPS)
Esse tutorial foi adaptado do tutorial "Programação básica em Assembly" desenvolvido por "Marcel Vinicius"
Comentários