Código da máquina - Machine code

Monitor de linguagem de máquina em um computador de placa única W65C816S , exibindo desmontagem de código , bem como registro de processador e despejos de memória.

Na programação de computadores , código de máquina é qualquer linguagem de programação de baixo nível , consistindo em instruções em linguagem de máquina , que é usada para controlar a unidade de processamento central (CPU) de um computador . Cada instrução faz com que a CPU execute uma tarefa muito específica, como uma operação de carga, armazenamento, salto ou unidade de lógica aritmética (ALU) em uma ou mais unidades de dados nos registros ou na memória da CPU .

O código de máquina é uma linguagem estritamente numérica projetada para ser executada o mais rápido possível e pode ser considerada como a representação de nível mais baixo de um programa de computador compilado ou montado ou como uma linguagem de programação dependente de hardware primitiva . Embora seja possível escrever programas diretamente em código de máquina, gerenciar bits individuais e calcular endereços numéricos e constantes manualmente é tedioso e sujeito a erros. Por esse motivo, muito raramente os programas são escritos diretamente em código de máquina em contextos modernos, mas podem ser feitos para depuração de baixo nível , patching de programa (especialmente quando o código-fonte assembler não está disponível) e desmontagem de linguagem assembly .

A maioria dos programas práticos hoje são escritos em linguagens de nível superior ou linguagem assembly. O código-fonte é então traduzido para código de máquina executável por utilitários como compiladores , montadores e linkers , com a importante exceção de programas interpretados , que não são traduzidos em código de máquina. No entanto, o próprio intérprete , que pode ser visto como um executor ou processador executando as instruções do código-fonte, normalmente consiste em código de máquina diretamente executável (gerado a partir do código-fonte do assembly ou da linguagem de alto nível).

O código de máquina é, por definição, o nível mais baixo de detalhes de programação visível para o programador, mas internamente muitos processadores usam microcódigo ou otimizam e transformam instruções de código de máquina em sequências de micro-operações . Geralmente, isso não é considerado um código de máquina.

Conjunto de instruções

Cada processador ou família de processadores tem seu próprio conjunto de instruções . As instruções são padrões de bits , dígitos ou caracteres que correspondem aos comandos da máquina. Assim, o conjunto de instruções é específico para uma classe de processadores usando (principalmente) a mesma arquitetura. Projetos de processadores sucessores ou derivados geralmente incluem instruções de um predecessor e podem adicionar novas instruções adicionais. Ocasionalmente, um projeto sucessor irá descontinuar ou alterar o significado de algum código de instrução (normalmente porque ele é necessário para novos propósitos), afetando a compatibilidade do código até certo ponto; mesmo os processadores compatíveis podem apresentar um comportamento ligeiramente diferente para algumas instruções, mas isso raramente é um problema. Os sistemas também podem diferir em outros detalhes, como disposição da memória, sistemas operacionais ou dispositivos periféricos . Como um programa normalmente depende de tais fatores, sistemas diferentes normalmente não executarão o mesmo código de máquina, mesmo quando o mesmo tipo de processador é usado.

O conjunto de instruções de um processador pode ter todas as instruções do mesmo comprimento ou pode ter instruções de comprimento variável. A maneira como os padrões são organizados varia de acordo com a arquitetura e o tipo de instrução em particular. A maioria das instruções tem um ou mais campos de opcode que especificam o tipo básico de instrução (como aritmética, lógica, salto , etc.), a operação (como adicionar ou comparar) e outros campos que podem fornecer o tipo de operando (s ), o (s) modo (s) de endereçamento, o (s) deslocamento (ões) ou índice de endereçamento ou o próprio valor do operando (tais operandos constantes contidos em uma instrução são chamados de imediatos ).

Nem todas as máquinas ou instruções individuais têm operandos explícitos. Uma máquina acumuladora possui um operando esquerdo combinado e resulta em um acumulador implícito para a maioria das instruções aritméticas. Outras arquiteturas (como 8086 e a família x86) têm versões de acumulador de instruções comuns, com o acumulador considerado como um dos registradores gerais por instruções mais longas. Uma máquina de pilha tem a maioria ou todos os seus operandos em uma pilha implícita. Freqüentemente, as instruções para fins especiais também carecem de operandos explícitos (CPUID na arquitetura x86 grava valores em quatro registradores de destino implícitos, por exemplo). Essa distinção entre operandos explícitos e implícitos é importante em geradores de código, especialmente na alocação de registro e partes de rastreamento de faixa ao vivo. Um bom otimizador de código pode rastrear operandos implícitos e explícitos que podem permitir propagação constante mais frequente , dobramento constante de registradores (um registrador atribuído ao resultado de uma expressão constante liberada pela substituição por essa constante) e outros aprimoramentos de código.

Programas

Um programa de computador é uma lista de instruções que podem ser executadas por uma unidade de processamento central (CPU). A execução de um programa é feita para que a CPU que o está executando resolva um problema e obtenha um resultado. Enquanto os processadores simples são capazes de executar instruções uma após a outra, os processadores superescalares são capazes de executar muitas instruções simultaneamente.

O fluxo do programa pode ser influenciado por instruções especiais de 'salto' que transferem a execução para um endereço (e, portanto, instrução) diferente do próximo endereço sequencial numericamente. A ocorrência desses saltos condicionais depende de uma condição, como um valor ser maior, menor ou igual a outro valor.

Linguagens de montagem

Uma versão muito mais amigável da linguagem de máquina, chamada linguagem assembly , usa códigos mnemônicos para se referir às instruções do código de máquina, em vez de usar os valores numéricos das instruções diretamente, e usa nomes simbólicos para se referir a locais de armazenamento e, às vezes, registros . Por exemplo, no processador Zilog Z80 , o código de máquina 00000101, que faz com que a CPU diminua o B registro do processador , seria representado em linguagem assembly como DEC B.

Exemplo

A arquitetura MIPS fornece um exemplo específico para um código de máquina cujas instruções são sempre de 32 bits. O tipo geral de instrução é dado pelo campo op (operação), os 6 bits mais altos. As instruções do tipo J (salto) e do tipo I (imediato) são totalmente especificadas por op . As instruções do tipo R (registro) incluem uma função de campo adicional para determinar a operação exata. Os campos usados ​​nesses tipos são:

   6      5     5     5     5      6 bits
[  op  |  rs |  rt |  rd |shamt| funct]  R-type
[  op  |  rs |  rt | address/immediate]  I-type
[  op  |        target address        ]  J-type

rs , rt e rd indicam operandos de registro; shamt dá um valor de deslocamento; e o endereço ou campos imediatos contêm um operando diretamente.

Por exemplo, adicionar os registros 1 e 2 e colocar o resultado no registro 6 é codificado:

[  op  |  rs |  rt |  rd |shamt| funct]
    0     1     2     6     0     32     decimal
 000000 00001 00010 00110 00000 100000   binary

Carregue um valor no registro 8, retirado da célula de memória 68 células após a localização listada no registro 3:

[  op  |  rs |  rt | address/immediate]
   35     3     8           68           decimal
 100011 00011 01000 00000 00001 000100   binary

Pulando para o endereço 1024:

[  op  |        target address        ]
    2                 1024               decimal
 000010 00000 00000 00000 10000 000000   binary

Relação com o microcódigo

Em algumas arquiteturas de computador , o código de máquina é implementado por uma camada subjacente ainda mais fundamental chamada microcódigo , fornecendo uma interface de linguagem de máquina comum em uma linha ou família de diferentes modelos de computador com fluxos de dados subjacentes amplamente diferentes . Isso é feito para facilitar a transferência de programas em linguagem de máquina entre diferentes modelos. Um exemplo desse uso é a família de computadores IBM System / 360 e seus sucessores. Com larguras de caminho de fluxo de dados de 8 bits a 64 bits e além, eles apresentam uma arquitetura comum no nível da linguagem de máquina em toda a linha.

O uso de microcódigo para implementar um emulador permite que o computador apresente a arquitetura de um computador totalmente diferente. A linha System / 360 usava isso para permitir a transferência de programas de máquinas IBM anteriores para a nova família de computadores, por exemplo, um emulador IBM 1401/1440/1460 no IBM S / 360 modelo 40.

Relação com bytecode

O código de máquina geralmente é diferente do bytecode (também conhecido como p-code), que é executado por um interpretador ou compilado em código de máquina para uma execução mais rápida (direta). Uma exceção é quando um processador é projetado para usar um bytecode específico diretamente como seu código de máquina, como é o caso dos processadores Java .

O código de máquina e o código de montagem às vezes são chamados de código nativo quando se referem a partes dependentes da plataforma de recursos de linguagem ou bibliotecas.

Armazenando na memória

A arquitetura Harvard é uma arquitetura de computador com armazenamento fisicamente separado e caminhos de sinal para o código (instruções) e dados . Hoje, a maioria dos processadores implementa esses caminhos de sinal separados por motivos de desempenho, mas implementam uma arquitetura Harvard modificada , para que possam oferecer suporte a tarefas como carregar um programa executável do armazenamento em disco como dados e depois executá-lo. A arquitetura de Harvard é contrastada com a arquitetura de Von Neumann , onde os dados e o código são armazenados na mesma memória que é lida pelo processador, permitindo que o computador execute comandos.

Do ponto de vista de um processo , o espaço de código é a parte de seu espaço de endereço onde o código em execução é armazenado. Em sistemas multitarefa , isso compreende o segmento de código do programa e, geralmente, bibliotecas compartilhadas . Em um ambiente multi-threading , diferentes threads de um processo compartilham o espaço do código junto com o espaço dos dados, o que reduz consideravelmente a sobrecarga da troca de contexto em comparação com a troca do processo.

Legibilidade por humanos

Pamela Samuelson escreveu que o código de máquina é tão ilegível que o Escritório de Direitos Autorais dos Estados Unidos não consegue identificar se um programa codificado em particular é uma obra de autoria original; no entanto, o US Copyright Office que permitem o registro de direitos autorais de programas de computador e código de máquina de um programa às vezes pode ser compilado de forma a tornar o seu funcionamento mais facilmente compreensível para os seres humanos. No entanto, a saída de um descompilador ou desmontador não terá os comentários e referências simbólicas, portanto, embora a saída possa ser mais fácil de ler do que o código-objeto, ainda será mais difícil do que o código-fonte original. Esse problema não existe para formatos de código-objeto como SQUOZE , onde o código-fonte está incluído no arquivo.

O professor de ciência cognitiva Douglas Hofstadter comparou o código de máquina ao código genético , dizendo que "Olhar para um programa escrito em linguagem de máquina é vagamente comparável a olhar para uma molécula de DNA átomo por átomo."

Veja também

Notas e referências

Leitura adicional