Linguagem de montagem

Uma linguagem de montagem é uma linguagem de programação que pode ser usada para dizer diretamente ao computador o que fazer. Uma linguagem de montagem é quase exatamente como o código da máquina que um computador pode entender, exceto que ele usa palavras no lugar de números. Um computador não pode realmente entender um programa de montagem diretamente. Entretanto, ele pode facilmente transformar o programa em código de máquina substituindo as palavras do programa pelos números que elas representam. Um programa que faz isso é chamado de assembler.

Os programas escritos em linguagem de montagem são geralmente feitos de instruções, que são pequenas tarefas que o computador executa quando está executando o programa. São chamadas instruções porque o programador as utiliza para instruir o computador sobre o que fazer. A parte do computador que segue as instruções é o processador.

A linguagem de montagem de um computador é uma linguagem de baixo nível, o que significa que ele só pode ser usado para fazer as tarefas simples que um computador pode entender diretamente. Para realizar tarefas mais complexas, é preciso dizer ao computador cada uma das tarefas simples que fazem parte da tarefa complexa. Por exemplo, um computador não entende como imprimir uma frase em sua tela. Em vez disso, um programa escrito em conjunto deve dizer-lhe como fazer todas as pequenas etapas que estão envolvidas na impressão da sentença.

Tal programa de montagem seria composto de muitas, muitas instruções, que juntos fazem algo que parece muito simples e básico para um humano. Isto torna difícil para os humanos a leitura de um programa de montagem. Em contraste, uma linguagem de programação de alto nível pode ter uma única instrução, como PRINT "Olá, mundo!" que dirá ao computador para executar todas as pequenas tarefas para você.

Desenvolvimento da linguagem de montagem

Quando os cientistas da computação construíram as primeiras máquinas programáveis, eles as programaram diretamente em código de máquina, que é uma série de números que instruíam o computador sobre o que fazer. A linguagem da máquina de escrever era muito difícil de fazer e demorou muito tempo, então, eventualmente, a linguagem de montagem foi feita. A linguagem assembly é mais fácil de ler para um humano e pode ser escrita mais rapidamente, mas ainda é muito mais difícil para um humano usar do que uma linguagem de programação de alto nível que tenta imitar a linguagem humana.

Programação em código de máquina

Para programar em código de máquina, o programador precisa saber como é cada instrução em binário (ou hexadecimal). Embora seja fácil para um computador descobrir rapidamente o que significa código de máquina, é difícil para um programador. Cada instrução pode ter várias formas, todas elas parecendo apenas um monte de números para as pessoas. Qualquer erro que alguém cometa enquanto escreve código de máquina só será notado quando o computador fizer a coisa errada. Descobrir o erro é difícil porque a maioria das pessoas não consegue dizer o que significa código de máquina ao olhar para ele. Um exemplo de como é o código da máquina:

05 2A 00

Este código hexadecimal da máquina diz a um processador de computador x86 para adicionar 42 ao acumulador. É muito difícil para uma pessoa lê-lo e entendê-lo, mesmo que ela saiba o código da máquina.

Usando a linguagem de montagem em vez disso

Com a linguagem de montagem, cada instrução pode ser escrita como uma palavra curta, chamada mnemônica, seguida por outras coisas como números ou outras palavras curtas. A mnemônica é usada para que o programador não tenha que se lembrar dos números exatos em código de máquina necessários para dizer ao computador para fazer algo. Exemplos de mnemônicos em linguagem de montagem incluem adicionar, que adiciona dados, e mover, que move dados de um lugar para outro. Como 'mnemônica' é uma palavra incomum, a frase tipo de instrução ou apenas instrução é às vezes usada em seu lugar, muitas vezes incorretamente. As palavras e números após a primeira palavra dão mais informações sobre o que fazer. Por exemplo, as coisas que se seguem a uma adição podem ser o que duas coisas devem ser adicionadas juntas e as coisas que se seguem ao movimento dizem o que mover e onde colocá-lo.

Por exemplo, o código da máquina na seção anterior (05 2A 00) pode ser escrito na montagem como:

 adicionar eixo,42

A linguagem de montagem também permite que os programadores escrevam os dados reais que o programa utiliza de maneira mais fácil. A maioria das linguagens de montagem tem suporte para fazer números e textos facilmente. Em código de máquina, cada tipo diferente de número como positivo, negativo ou decimal, teria que ser convertido manualmente em binário e o texto teria que ser definido uma letra de cada vez, como números.

A linguagem de montagem fornece o que é chamado de abstração do código da máquina. Ao utilizar a montagem, os programadores não precisam saber os detalhes do que os números significam para o computador, os números do assembler que, em vez disso, saem. A linguagem assembly ainda permite que o programador utilize todas as características do processador que poderia com o código da máquina. Neste sentido, a linguagem de montagem tem uma característica muito boa e rara: ela tem a mesma capacidade de expressar as coisas como aquilo que está abstraindo (código de máquina) ao mesmo tempo em que é muito mais fácil de usar. Por causa disso, o código de máquina quase nunca é usado como uma linguagem de programação.

Desmontagem e Depuração

Quando os programas são finalizados, eles já foram transformados em código de máquina para que o processador possa realmente executá-los. Algumas vezes, porém, se o programa tiver um bug (erro) nele, os programadores vão querer poder dizer o que cada parte do código da máquina está fazendo. Desassembladores são programas que ajudam os programadores a fazer isso, transformando o código da máquina do programa de volta em linguagem de montagem, o que é muito mais fácil de entender. Os desmontadores, que transformam o código da máquina em linguagem de montagem, fazem o oposto dos montadores, que transformam a linguagem de montagem em código de máquina.

Organização de computadores

Para entender como os computadores são organizados, como eles parecem funcionar em um nível muito baixo, é necessário entender como funciona um programa de linguagem de montagem. No nível mais simplista, os computadores têm três partes principais:

  1. memória principal ou RAM que contém dados e instruções,
  2. um processador, que processa os dados através da execução das instruções, e
  3. entrada e saída (às vezes abreviada para E/S), que permitem ao computador se comunicar com o mundo exterior e armazenar dados fora da memória principal para que possa recuperar os dados mais tarde.

Memória principal

Na maioria dos computadores, a memória é dividida em bytes. Cada byte contém 8 bits. Cada byte na memória também tem um endereço que é um número que diz onde o byte está na memória. O primeiro byte na memória tem um endereço 0, o próximo tem um endereço 1, e assim por diante. A divisão da memória em bytes a torna endereçável porque cada byte recebe um endereço único. Os endereços das memórias de bytes não podem ser usados para se referir a um único bit de um byte. Um byte é o menor pedaço de memória que pode ser endereçado.

Embora um endereço se refira a um byte específico na memória, os processadores permitem o uso de vários bytes de memória em uma linha. O uso mais comum deste recurso é usar 2 ou 4 bytes em fila para representar um número, geralmente um número inteiro. Bytes individuais também são às vezes usados para representar números inteiros, mas como eles têm apenas 8 bits de comprimento, eles só podem conter 28 ou 256 valores possíveis diferentes. O uso de 2 ou 4 bytes em uma linha eleva o número de diferentes valores possíveis para 216, 65536 ou 232, 4294967296, respectivamente.

Quando um programa usa um byte ou um número de bytes em uma linha para representar algo como uma letra, um número ou qualquer outra coisa, esses bytes são chamados de objeto porque todos fazem parte da mesma coisa. Mesmo que todos os objetos sejam armazenados em bytes idênticos de memória, eles são tratados como se tivessem um "tipo", que diz como os bytes devem ser entendidos: ou como um inteiro ou um personagem ou algum outro tipo (como um valor não-inteiro). O código da máquina também pode ser pensado como um tipo que é interpretado como instruções. A noção de um tipo é muito, muito importante porque define o que pode ou não ser feito ao objeto e como interpretar os bytes do objeto. Por exemplo, não é válido armazenar um número negativo em um objeto de número positivo e não é válido armazenar uma fração em um número inteiro.

Um endereço que aponta para (é o endereço de) um objeto multi-byte é o endereço do primeiro byte desse objeto - o byte que tem o endereço mais baixo. Como um aparte, uma coisa importante a notar é que você não pode dizer qual é o tipo de objeto - ou mesmo seu tamanho - por seu endereço. Na verdade, você não pode nem mesmo dizer que tipo é um objeto, olhando para ele. Um programa de linguagem de montagem precisa acompanhar quais endereços de memória contêm quais objetos, e qual o tamanho desses objetos. Um programa que o faz é do tipo seguro porque só faz coisas a objetos que são seguros para fazer em seu tipo. Um programa que não funciona provavelmente não funcionará corretamente. Note que a maioria dos programas não armazena explicitamente qual é o tipo de objeto, eles apenas acessam objetos de forma consistente - o mesmo objeto é sempre tratado como o mesmo tipo.

O Processador

O processador executa (executa) as instruções, que são armazenadas como código de máquina na memória principal. Além de poder acessar a memória para armazenamento, a maioria dos processadores tem alguns espaços pequenos, rápidos e de tamanho fixo para guardar objetos que estão sendo trabalhados atualmente. Estes espaços são chamados de registros. Os processadores normalmente executam três tipos de instruções, embora algumas instruções possam ser uma combinação destes tipos. Abaixo estão alguns exemplos de cada tipo em linguagem de montagem x86.

Instruções que lêem ou escrevem memória

A seguinte instrução de linguagem de montagem x86 lê (carrega) um objeto de 2 bytes do byte no endereço 4096 (0x1000 em hexadecimal) em um registro de 16 bits chamado 'ax':

        mov ax, [1000h]

Nesta linguagem de montagem, colchetes em torno de um número (ou um nome de registro) significam que o número deve ser usado como um endereço para os dados que devem ser usados. O uso de um endereço para apontar para os dados é chamado de indireção. Neste próximo exemplo, sem os colchetes, outro registro, bx, na verdade recebe o valor 20 carregado nele.

        mov bx, 20

Como não foi utilizada nenhuma indireção, o valor real em si foi colocado no registro.

Se os operandos (as coisas que vêm depois da mnemônica), aparecem na ordem inversa, uma instrução que carrega algo da memória, em vez disso, grava-o na memória:

        mov [1000h], eixo

Aqui, a memória no endereço 1000h recebe o valor do eixo. Se este exemplo for executado logo após o anterior, os 2 bytes em 1000h e 1001h serão um inteiro de 2 bytes com o valor de 20.

Instruções que realizam operações matemáticas ou lógicas

Algumas instruções fazem coisas como subtração ou operações lógicas como não:

O exemplo de código de máquina anteriormente apresentado neste artigo seria este na linguagem de montagem:

        adicionar eixo, 42

Aqui, 42 e eixo são adicionados juntos e o resultado é armazenado de volta no eixo. Na montagem x86 também é possível combinar um acesso à memória e uma operação matemática como esta:

        adicionar eixo, [1000h]

Esta instrução acrescenta o valor do inteiro de 2 bytes armazenados a 1000h ao eixo e armazena a resposta no eixo.

        ou machado, bx

Esta instrução calcula o ou do conteúdo do eixo e bx dos registros e armazena o resultado de volta ao eixo.

Instruções que decidem qual vai ser a próxima instrução

Normalmente, as instruções são executadas na ordem em que aparecem na memória, que é a ordem em que são digitadas no código de montagem. O processador apenas as executa uma após a outra. Entretanto, para que os processadores possam fazer coisas complicadas, eles precisam executar instruções diferentes com base nos dados que lhes foram fornecidos. A capacidade dos processadores de executar instruções diferentes dependendo do resultado de algo é chamada de ramificação. As instruções que decidem qual deve ser a próxima instrução são chamadas de branch instructions.

Neste exemplo, suponha que alguém queira calcular a quantidade de tinta que precisará para pintar um quadrado com um determinado comprimento lateral. Entretanto, devido à economia de escala, a loja de tintas não os venderá menos do que a quantidade de tinta necessária para pintar um quadrado de 100 x 100.

Para descobrir a quantidade de tinta que eles precisarão obter com base no comprimento do quadrado que desejam pintar, eles inventam este conjunto de etapas:

  • subtrair 100 do comprimento lateral
  • se a resposta for menor que zero, ajuste o comprimento do lado para 100
  • multiplicar o comprimento lateral por si só

Esse algoritmo pode ser expresso no seguinte código, onde o eixo é o comprimento lateral.

        mov bx, eixo   sub bx, 100    jge continuar mov ax, 100 continua: eixo mul

Este exemplo introduz várias coisas novas, mas as duas primeiras instruções são familiares. Elas copiam o valor do eixo em bx e depois subtraem 100 de bx.

Uma das novidades deste exemplo é chamado de rótulo, um conceito encontrado nas linguagens de montagem em geral. Os rótulos podem ser qualquer coisa que o programador queira (a menos que seja o nome de uma instrução, o que confundiria o montador). Neste exemplo, o rótulo é "continuar". É interpretado pelo assembler como o endereço de uma instrução. Neste caso, é o endereço de mult ax.

Outro novo conceito é o das bandeiras. Nos processadores x86, muitas instruções colocam 'bandeiras' no processador que podem ser usadas pela próxima instrução para decidir o que fazer. Neste caso, se bx era inferior a 100, o sub colocará uma bandeira que diz que o resultado foi inferior a zero.

A instrução seguinte é jge que é abreviatura de "Jump if Greater than or Equal to" (Saltar se for maior ou igual a). É uma instrução de ramo. Se as bandeiras no processador especificarem que o resultado foi maior ou igual a zero, em vez de apenas ir para a próxima instrução o processador saltará para a instrução na etiqueta continue, que é mul-eixo.

Este exemplo funciona bem, mas não é o que a maioria dos programadores escreveria. A instrução de subtração define a bandeira corretamente, mas também muda o valor em que ela opera, o que exigia que o eixo fosse copiado para bx. A maioria dos idiomas de montagem permite uma instrução de comparação que não altera nenhum dos argumentos que são passados, mas ainda assim define as bandeiras corretamente e a montagem x86 não é exceção.

        eixo cmp, 100 jge continuar mov ax, 100 continua: eixo mul

Agora, em vez de subtrair 100 do eixo, vendo se esse número é inferior a zero, e atribuí-lo de volta ao eixo, o eixo é deixado inalterado. As bandeiras ainda são colocadas do mesmo modo, e o salto ainda é dado nas mesmas situações.

Entrada e Saída

Embora a entrada e a saída sejam uma parte fundamental da computação, não há uma forma de serem feitas em linguagem de montagem. Isto porque a forma de E/S funciona depende da configuração do computador e do sistema operacional em funcionamento, não apenas do tipo de processador que ele possui. Na seção de exemplo, o exemplo do Hello World usa chamadas de sistema operacional MS-DOS e o exemplo depois usa chamadas de BIOS.

É possível fazer E/S em linguagem de montagem. De fato, a linguagem de montagem pode geralmente expressar qualquer coisa que um computador é capaz de fazer. Entretanto, mesmo que haja instruções para adicionar e ramificar na linguagem de montagem que sempre farão a mesma coisa, não há instruções na linguagem de montagem que sempre façam E/S.

O importante é observar que a forma como as E/S funcionam não faz parte de nenhuma linguagem de montagem, pois não faz parte de como o processador funciona.

Línguas de montagem e portabilidade

Mesmo que a linguagem de montagem não seja executada diretamente pelo processador - o código da máquina é, ainda tem muito a ver com isso. Cada família de processadores suporta diferentes características, instruções, regras para o que as instruções podem fazer, e regras para que combinação de instruções é permitida onde. Devido a isso, diferentes tipos de processadores ainda precisam de diferentes linguagens de montagem.

Como cada versão da linguagem de montagem está ligada a uma família de processadores, falta-lhe algo chamado portabilidade. Algo que tenha portabilidade ou seja portátil pode ser facilmente transferido de um tipo de computador para outro. Enquanto outros tipos de linguagens de programação são portáteis, a linguagem de montagem, em geral, não o é.

Linguagem de montagem e idiomas de alto nível

Embora a linguagem de montagem permita uma maneira fácil de usar todas as características do processador, ela não é usada para projetos de software modernos por vários motivos:

  • É preciso muito esforço para expressar um programa simples na montagem.
  • Embora não tão propensa a erros como o código da máquina, a linguagem de montagem ainda oferece muito pouca proteção contra erros. Quase todas as linguagens de montagem não reforçam a segurança do tipo.
  • A linguagem de montagem não promove boas práticas de programação como a modularidade.
  • Embora cada instrução individual de idioma de montagem seja fácil de entender, é difícil dizer qual foi a intenção do programador que a escreveu. De fato, a linguagem de montagem de um programa é tão difícil de entender que as empresas não se preocupam com as pessoas desmontando (obtendo a linguagem de montagem de) seus programas.

Como resultado destes inconvenientes, línguas de alto nível como Pascal, C, e C++ são utilizadas para a maioria dos projetos. Elas permitem que os programadores expressem suas idéias mais diretamente ao invés de terem que se preocupar em dizer ao processador o que fazer em cada passo do caminho. Eles são chamados de alto nível porque as idéias que o programador pode expressar na mesma quantidade de código são mais complicadas.

Os programadores que escrevem código em idiomas de alto nível compilados usam um programa chamado compilador para transformar seu código em linguagem de montagem. Os compiladores são muito mais difíceis de escrever do que os montadores são. Além disso, as linguagens de alto nível nem sempre permitem que os programadores utilizem todas as características do processador. Isto ocorre porque as linguagens de alto nível são projetadas para suportar todas as famílias de processadores. Ao contrário das linguagens de montagem, que suportam apenas um tipo de processador, as linguagens de alto nível são portáteis.

Embora os compiladores sejam mais complicados do que os montadores, décadas de fabricação e pesquisa de compiladores os tornaram muito bons. Agora, não há mais muita razão para usar a linguagem de montagem para a maioria dos projetos, porque os compiladores podem normalmente descobrir como expressar programas em linguagem de montagem também ou melhor do que os programadores.

Programas de exemplo

Um Programa Hello World escrito em x86 Assembly:

adosseg .model small .stack 100h .data hello_message db 'Hello, World! ',0dh,0ah,'$' .code main proc mov ax,@data mov ds,ax mov ah,9 mov dx,offset hello_message int 21h mov ax,4C00h int 21h main endp end main

Uma função que imprime um número para a tela usando BIOS interrompe o conjunto escrito no NASM x86. O código modular é possível de escrever na montagem, mas é necessário um esforço extra. Note que qualquer coisa que venha depois de um ponto-e-vírgula em uma linha é um comentário e é ignorado pelo assembler. Colocar comentários em código de linguagem assembly é muito importante porque os grandes programas de linguagem assembly são muito difíceis de entender.

; número int, int base); printn:      empurrar       bp      mov     bp, sp empurrar        machado        empurrar       bx      empurrar       cx      empurrar       dx         empurrar       si      mov     si, 0   mov     eixo, [bp + 4] número mov     cx, [bp + 6]       ; luva de base: inc     si             comprimento do fio     mov     dx, 0              ; zero dx      div     cx             ; dividir por base     cmp     dx, 10             É ge 10?       jge     num     adicionar      dx, '0'.               ; adicionar zero a dx    jmp     anum num:      adicionar      dx, ('A'- 10)  valor hexadecimal, acrescente 'A' a dx - 10. anum:  pressione      dx             ; colocar dx na pilha.      cmp     machado, 0             Devemos continuar?     jne     gloop   mov        bx, 7h         para interromper a inclinação: pop     machado        obter seu valor   mov     ah, 0eh        para interrupção       int     10h            ; escrever caráter        dez     si             ; livre-se do caráter jnz     tloop  pop     si         pop     dx      pop     cx      pop     bx      pop     machado        pop     bp         ret     4

Perguntas e Respostas

P: O que é uma língua da assembléia?


R: Uma linguagem de assembléia é uma linguagem de programação que pode ser usada para dizer diretamente ao computador o que fazer. É quase exatamente como o código de máquina que um computador pode entender, exceto que ele usa palavras no lugar de números.

P: Como um computador entende um programa da assembléia?


R: Um computador não pode realmente entender um programa de montagem diretamente, mas pode facilmente mudar o programa em código de máquina, substituindo as palavras do programa pelos números que eles representam. Esse processo é feito usando um assembler.

P: O que são instruções em uma linguagem de assembler?


R: As instruções em uma linguagem de montagem são pequenas tarefas que o computador executa quando está executando o programa. Elas são chamadas instruções porque instruem o computador sobre o que fazer. A parte do computador responsável por seguir essas instruções é chamada de processador.

P: Que tipo de linguagem de programação é a de assembléia?


R: A linguagem assembly é uma linguagem de programação de baixo nível, o que significa que ela só pode ser usada para fazer tarefas simples que um computador possa entender diretamente. Para realizar tarefas mais complexas, é preciso dividir cada tarefa em seus componentes individuais e fornecer instruções para cada componente separadamente.

P: Como isso se diferencia das línguas de alto nível?


R: Os idiomas de alto nível podem ter comandos únicos, tais como PRINT "Olá, mundo!", que dirá ao computador para executar automaticamente todas essas pequenas tarefas sem precisar especificá-las individualmente, como o senhor teria que fazer com um programa de montagem. Isso torna as línguas de alto nível mais fáceis de serem lidas e compreendidas pelos humanos do que os programas de assembléia compostos de muitas instruções individuais.

P: Por que poderia ser difícil para os seres humanos ler um programa de assembléia?


R: Porque muitas instruções individuais devem ser especificadas para uma tarefa complexa como imprimir algo na tela ou fazer cálculos sobre conjuntos de dados - coisas que parecem muito básicas e simples quando expressas em linguagem humana natural - então pode haver muitas linhas de código que compõem uma instrução que torna difícil para os humanos que não sabem como os computadores funcionam internamente em um nível tão baixo seguir e interpretar o que está acontecendo dentro deles.

AlegsaOnline.com - 2020 / 2023 - License CC3