Programação em Assembly

Programação do computador

Para escrevermos um programa começamos com a nossa linguagem natural para descrever o nosso objetivo seguido da sua tradução para linguagem de programação. Estes dois passos são obtidos pelos programadores. Assim que temos o nosso programa escrito, o compilador traduz para linguagem assembly e, por último, o assemblador traduz tudo para código máquina.

Traduções das linguagens

Mas como funciona a linguagem assembly? Começamos por ter uma instrução por linha, cada instrução tem uma formatação rígida, alguns comentários escritos logo durante o processo de transcrição para linguagem assembly e instruções que refletem diretamente os recursos do processador.

Se tivermos por exemplo

        conta:  ADD      R1, R2      ; soma ao saldo

Sabemos que conta representa a nossa etiqueta, ADD a mnemónica, R1 e R2 são os operandos, e soma ao saldo é o nosso comentário em relação ao que é que a nossa linha de comando faz.

Comentários

Cada comentário no nosso programa deve ser iniciado pelo carácter ";" eo comentário que se segue até ao final da linha. Praticamente todas as linhas de assembly devem ter um comentário a indicar o que está a ser feito, se não, o código torna-se praticamente impossível de ser entendido, visto que é uma linguagem de baixo nível.

Sem comentários

Como podemos ver no exemplo, sem comentários a explicar-nos o que cada linha de código faz, não sabemos qual é o objetivo do programa nem o que está a ser feito.

Registos do processador

Os registos do processador representam os recursos mais importantes que uma instrução pode manipular; são de memória interna, de acesso muito mais rápido que a memória externa e com instruções que manipulam alguns registos diretamente.

Se estivermos a avaliar em PEPE, temos dois tipos de registos distintos, cada qual com 16 bits (4 dígitos hexadecimais):

  • PC (Program Counter);
  • 16 registos (R0 a R15), alguns "especiais"

Bits de estado (flags)

Dentro do Registo de estado (RE), podemos ter flags que nos fornecem indicações sobre o resultado da operação anterior, contudo é importante notar que nem todas as instruções os alteram. Estas flags podem influenciar o resultado da operação seguinte.

Podemos ter 4 tipos de flags diferentes:

  • (Z) Zero: fica a 1 se o resultado de uma operação for zero;
  • (C) Carry, transporte: fica a 1 se o resultado de uma operação tiver transporte;
  • (V) Overflow, excesso: fica a 1 se o resultado de uma operação não couber na palavra do processador;
  • (N) Negativo: fica a 1 se o resultado de uma operação for negativo

Classe de instruções

Podemos classificar cada instrução dentro de 5 classes diferentes: instruções aritméticas, instruções lógicas, instruções de deslocamento, instruções de transferência de dados e instruções de controlo de fluxo.

As instruções aritméticas são todas as instruções que lidam com números em complemento para 2, nomeadamente ADD, SUB, CMP, MUL e DIV.

As instruções lógicas representam as instruções que lidam com sequências de bits, como é o caso do AND, OR e SET.

As instruções de deslocamento lidam com o deslocamento dos bits de um registo, ou seja, instruções como SHR e ROL.

As instruções de transferência de dados são as instruções que nos permitem transferir dados entre dois registos ou entre um registo e a memória, como fazemos com o MOV e SWAP.

Por último, as instruções de controlo de fluxo são as que controlam a sequência de execução de instruções, podendo tomar decisões, como JMP, JZ, JNZ, CALL e RET. Nestas operações, os valores dos registos são considerados endereços sem sinal, ou seja, qualquer valor de 0000H a FFFFH.

Para além disso, podemos referir-mo-nos a instruções com as lógicas e de deslocamento como instruções de bit. Nestas instruções, os registos são apenas um conjunto de bits individuais.

Ao representarmos números em assembly temos que ter em atenção que nas instruções aritméticas, os valores estão em complemento para 2, ou seja representam qualquer valor entre 8000H e 7FFFH.

Instruções aritméticas, lógicas e de deslocamento

Exemplo de instruções que implementam operações aritméticas:

Operações aritméticas

Podemos também ter operações de deslocamento, como já foi referido acima. Estas instruções apenas multiplicam (SHL) ou dividem (SHR) por 2n2^n, seja n o número dado na instrução. Estas operações efetuam-se da seguinte forma:

        SHL     a, n
        SHR     a, n

Também podemos usar as instruções ROL ou ROR quando fazemos uma multiplicação ou divisão mas o primeiro algarismo (bit mais significativo) passa a ser o último (ROL) na multiplicação, ou o último algarismo (bit menos significativo) passa a ser o primeiro (ROR) na divisão.

        ROL     a, n
        ROR     a, n

Instruções de transferência de dados

Transferências de dados (16 bits)

Transferências de dados

Acesso à memória em 8 bits

Transferências de dados

Swap

Transferências de dados

Instruções de controlo de fluxo

Quando estamos a usar uma linguagem de alto nível, a execução das nossas instruções é feita de forma sequencial, exceto se temos uma decisão (quando temos um if ou switch) ou se temos uma interação, quer seja incondicional, como é o caso do for, ou condicional, while. Este controlo de fluxo é obtido através de bits de estado que indicam o resultado da instrução anterior, ou de instruções específicas, quer sejam de saltos (in)condicionais, de chamada de rotina ou até mesmo de retorno de rotina.

Mas o que são instruções de salto? Instruções de salto, tal como o nome indica, são instruções que nos deixam "saltar" de uma posição para outra, ou seja, na prática o que estamos a fazer é alterar o PC em vez de o deixar incrementar normalmente. Estes saltos podem ser:

  • Incondicionais
  • Condicionais
  • Absolutos
  • Relativos

Diretivas

Quando nos referimos a aspetos importantes de assembly é necessário falar sobre as diretivas que são uma espécie de pseudo-instruções pois servem apenas para o assembler e não para o microprocessador. Por outras palavras, as diretivas não geram código executável.

EQU

A primeira diretiva a ser discutida é EQU. Esta diretiva, que não gera código, serve somente para definir o valor de constantes simbólicas. Ou seja, esta diretiva permite-nos atribuir o valor que nos pretendemos às constantes que vamos usando ao longo do nosso programa.

        NOME    EQU   constante-literal

Exemplo:

        DUZIA    EQU   12

PLACE

A diretiva PLACE ajuda o assembler a indicar o endereço a partir do qual as instruções ou variáveis seguintes devem ficar localizadas. Assim, até aparecer um PLACE, considera-se que há um PLACE 0 implícito, desde o inicio do programa.

        PLACE   endereço

tip

Após o reset, o PEPE inicializa o PC com o valor 0000H, por isso, tem de haver um PLACE 0000H algures no nosso programa, não sendo obrigatório estar logo no início do ficheiro.

WORD

Ao usarmos WORD estamos a reservar um espaço, define, para uma variável de 16 bits. A mesma diretiva pode definir várias variáveis de uma word consecutivas, contudo, cada variável gasta 2 bytes.

        ETIQUETA:   WORD    constante{,constante}

Exemplo:

        VAR1:   WORD    1

Ficamos com uma variável inicializada a 1 que fica localizada no endereço atribuído pelo assemblador a VAR1. WORD é diferente de EQU.

TABLE

Agora que já vimos o que é que a diretiva WORD faz, temos que saber o que é que a diretiva TABLE faz.

Ao usarmos TABLE estamos a definir, reservar espaço, para uma tabela com várias variáveis de 16 bits. É importante manter em conta que apenas reserva espaço, não a inicializa.

        ETIQUETA:   TABLE    constante

Exemplo:

        T1:   TABLE    1OH

Ficamos com um espaço para 16 words reservado, ou seja temos 32 bytes reservados. A primeira word fica reservada no endereço atribuído a T1, a segunda em T1+2, etc.

Esta diretiva é boa para reservar uma área que depois se pode ir escrevendo ao longo do programa, porém, para definir tabelas de constantes é preferível definir os vários elementos da tabela com a diretiva WORD.

BYTE

A última diretiva é BYTE, que define uma variável de 8 bits, ou seja, um byte. Para valores negativos, deve-se usar variáveis WORD e não BYTE pois estes precisam sempre dos bits todos num processador.

A mesma diretiva permite definir várias variáveis de um byte consecutivas.

        ETIQUETA:   BYTE    constante{,constante}

Exemplo:

        S1:   BYTE    'a', "ola", 12H

Modos de endereçamento

Modos de endereçamento

warning

Para ver exemplos de programas em Assembly, é recomendada a leitura dos slides das teóricas.