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:

Tabela

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:

Tabela 32

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:

dsds

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:

dsaaaa

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:

dsdsddd

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:

ewew

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

Exercicio


links úteis

Download do SPIM (simulador de MIPS)

Tutorial de como usar o MIPS

Esse tutorial foi adaptado do tutorial "Programação básica em Assembly" desenvolvido por "Marcel Vinicius"

Voltar