Introdução aos Sistemas Computacionais
2008/09
Fascículo ASM1 – Introdução ao Assembly
1. Introdução ao Tema
O processador de um computador é o componente encarregado de executar instruções. Em última instância, o processador não faz mais do que executar sequencialmente instruções que não são mais do que ordens codificadas em números binários. A linguagem em que os programas são especificados para poderem ser executados por um processador denomina-se de linguagem
máquina. Como para um ser humano é difícil interpretar sequências de números, nunca se
programa em linguagem máquina mas em linguagens de nível ligeiramente superior denominadas
assembly. Todo o processador tem a sua linguagem máquina e o seu assembly. As instruções
assembly são geralmente chamadas de mnemónicas dado poderem ser entendidas como algo que nos ajuda a decorar as instruções em linguagem máquina. A tradução de um programa em assembly para linguagem máquina é feita por um programa denominado assembler. Nesta disciplina vamos considerar o Gnu ASsembler (GAS).
As instruções em assembly fazem operações muito elementares, tornando a programação nessa linguagem algo difícil, mas imprescindível em certas circunstâncias. Em última análise, qualquer instrução assembly limita-se a manipular dados armazenados em três possíveis locais: num conjunto de variáveis internas ao processador denominadas registos, em memória, em
dispositivos de entrada/saída (E/S). Nesta disciplina vão-nos interessar sobretudo os dois
primeiros locais de armazenamento (registos e memória), já que os dados dos dispositivos de E/S são geralmente geridos pelo sistema operativo.
A memória é uma parte importante do computador, que aparece de forma conspícua nos programas em assembly. Podemos considerá-la simplesmente como uma grande tabela com
células de 8 bits (1 byte). Essas células são numeradas através de endereços, começando em 0,
1, 2, 3, …, podendo chegar a 1 ou 2GiB numa configuração típica nos PCs actuais.
Um processador tem sempre um número limitado de registos. Os nomes desses registos tipicamente são acrónimos curtos, designando aquilo para que são usados. No caso da arquitectura de processadores que vamos usar como referência, a arquitectura IA-32 da Intel (que engloba os processadores Intel 80386, 80486 e as diferentes gerações de Pentium), existem quatro registos de uso geral denominados eax, ebx, ecx e edx. Existem ainda outros que iremos introduzindo à medida que forem necessários. Esses quatro registos têm 32 bits que é o comprimento dos dados geralmente manipulado pelo reportório de instruções dos processadores incluídos na arquitectura IA-32 da Intel. Por isso, diz-se que estes processadores são de 32 bits. No entanto, estes processadores foram precedidos por outros membros da mesma família, como o 80286 e o 8088, que eram processadores de 16 bits. Por isso, para continuar a poder executar programas escritos para esses processadores, por vezes esses registos são manipulados como se tivessem apenas 16 ou 8 bits. Assim, considerando por exemplo o registo eax, os 16 bits menos significativos (ou seja, com menos peso) deste registo podem ser manipulados sob o nome de ax. O byte menos significativo do ax pode por sua vez ser manipulado sob o nome de al (low) e o mais significativo sob o nome de ah (high). O mesmo se aplica para os outros três registos, podendo ser manipulados como bx, bl, bh, etc. Todos os registos em assembly (ou mais precisamente, no GAS) são antecedidos por %, por exemplo, %eax, %dh, %cx.
Daqui em diante vamos abreviar “assembly da arquitectura IA-32” ou “assembly dos processadores Intel” simplesmente para “assembly”.
Em assembly podem-se usar variáveis em memória, que são designadas por um nome (por ex., VAR1). Na realidade esse nome não é traduzido para linguagem máquina, sendo substituído pelo endereço de memória correspondente. O processador não reconhece algo tão simples como um nome de variável! Apenas registos e endereços de memória. A declaração de uma variável através do seu nome reserva o espaço de memória necessário para armazenar o tipo de dados
Departamento de Informática da Faculdade de Ciências da
declarados para a variável. A tabela seguinte enumera as palavras chave usadas para declarar cada tipo de dados em assembly:
Dimensão Palavra Chave (Directiva)
1 byte .byte
2 byte .word, .hword, .short 4 byte .int, .long
8 byte .quad 16 byte .octa
A declaração de variáveis é realizada no interior de um bloco que se inicia com a palavra chave .data e tem o seguinte formato geral:
nome_variável: declaração_dimensão valor_inicial
Em breve iremos aprender como se declaram variáveis para as quais não é necessário definir um valor inicial.
Em assembly podem-se também usar valores imediatos como operandos das instruções, que são assim chamados por serem colocados na memória imediatamente a seguir à instrução em linguagem máquina que os utiliza (por exemplo, $4, $0x10; $VAR1 denomina o endereço da variável VAR1).
Em assembly as instruções levam um sufixo que indica o tamanho dos dados a transferir: b indica byte (8 bits), w indica word ou palavra (16 bits) e l indica longo/inteiro (32 bits).
A instrução assembly mais simples é a nop que muito simplesmente não faz nada. Por vezes é útil como forma simples de “tapar” outras instruções, já que em linguagem máquina é a instrução 0 (zero).
Em seguida, em termos de simplicidade, temos a instrução mov (de move) que copia um dado de um registo para outro ou para memória (ou vice-versa). Na realidade não existe uma única instrução mov mas inúmeras variantes da instrução mov. Por exemplo:
movl %eax, %ebx # copia o valor no registo %eax para o registo %ebx movl %ebx, %eax
movw imediato, %dx movb %dh, endereço
O valor do primeiro operando é copiado para o segundo, sobrepondo o valor que estava colocado no registo ou célula de memória. Os operandos podem ser os registos de uso geral, endereços de memória e valores imediatos. Há no entanto algumas restrições: o tamanho dos dois operandos tem de ser sempre idêntico (8, 16 ou 32 bits); só um dos operandos pode ser um valor imediato ou endereço; o segundo operando não pode ser um valor imediato.
O carácter # indica que tudo o que está na mesma linha à sua direita é um comentário que não deve ser interpretado pelo assembler.
Uma instrução semelhante é a xchg (de exchange) que não apenas copia o primeiro operando para o segundo, mas também o segundo para o primeiro, ou seja, troca os valores dos dois parâmetros. As restrições são semelhantes às do mov, excepto que nenhum operando pode ser um valor imediato (pense porquê, é óbvio). Exemplos:
xchgl %eax, %ebx
xchgb %ah, VAR_B
A especificação do bloco de um programa destinado a instruções inicia-se com a palavra chave (directiva) .text e deve conter pelo menos um rótulo associado ao programa principal (main).
ISC-LEI/FCUL 3
2. Exercícios Fundamentais
1. Declare, no espaço reservado à declaração de variáveis, o seguinte conjunto de variáveis.
a) uma variável de 32 bits, de nome ac e cujo valor inicial seja 125. b) uma variável de 32 bits, de nome tp e valor inicial 32.
.data
2. Escreva um excerto de um programa assembly que execute as seguintes acções:
a) copie o conteúdo da variável ac declarada na questão anterior para o registo eax. b) copie o conteúdo do registo eax para o registo ebx.
c) copie o conteúdo da variável tp declarada na questão anterior para o registo eax
.text
.globl main main:
ret
3. Considere o seguinte programa escrito em linguagem assembly, para a família de µP80x86,
especificado segundo a notação usada nos compiladores da GNU. .data
ac: .long 125 #variável ac de 32 bits com o valor inicial 125 tp: .long 32 #variável tp de 32 bits com o valor inicial 32 .text .globl main main: movl ac,%eax movl %eax,%ebx movl tp,%eax nop movl $65535,%edx xchg %eax,%edx ret
Indique, nos espaços reservados abaixo, qual o conteúdo de cada um dos registos da CPU aí indicados, no instante imediatamente antes da execução da instrução ret. Utilize a palavra-chave “indeterminado” para indicar um valor desconhecido.
Registo EAX 10 Registo EBX 10
4. Escreva um pequeno excerto de um programa assembly que coloque em %eax o conteúdo da
célula de memória com endereço 0x100.
5. Escreva um pequeno excerto de um programa assembly que copie o valor da variável VAR1
para a variável VAR2, ambas de 8 bits.
6. Das seguintes instruções assembly da família de µP80x86, especificadas segundo a notação
usada no GAS, indique qual a única inválida.
movl %eax,%ebx
movw %ax,%bx
movl %ebx,%eax
movw %ax,%ebx
nenhuma das anteriores.7. Considere o seguinte programa escrito em linguagem assembly, para a família de µP80x86,
especificado segundo a notação usada nos compiladores da GNU. .data ac: .long 9876 tp: .long 123456 .text .globl main main: xchgl ac,%eax movl %ecx,%ebx movl %edx, %ecx movl %eax, %ecx movl tp,%edx
movl $65535,%eax
xchg %eax,%edx ret
Indique, nos espaços reservados abaixo, qual o conteúdo de cada uma das variáveis e de cada um dos registos da CPU aí indicados, no instante imediatamente antes da execução da instrução
ret. Utilize a palavra-chave “indeterminado” para indicar um valor desconhecido.
Registo EAX 10 Registo EBX 10
Registo ECX 10 Registo EDX 10
ISC-LEI/FCUL 5
8. Considere o seguinte programa escrito em linguagem assembly, para a família de µP80x86,
especificado segundo a notação usada nos compiladores da GNU. .data
ac: .long 128 #variável ac de 32 bits com o valor inicial 128 tp: .long 32 #variável tp de 32 bits com o valor inicial 32 .text .globl main main: movl ac,%eax movl tp,%ebx movl $512,%ecx movl $64,%edx nop xchg %ebx,%ecx xchg %ecx,%edx ret
Indique, de entre as opções indicadas abaixo, qual a instrução que deve substituir a instrução nop, para que o conteúdo de cada um dos registos da CPU, no instante imediatamente antes da execução da instrução ret, seja o seguinte:
EAX = 3210 EBX=51210 ECX=6410 EDX = 12810
movl %ebx,%eax
xchgw %eax,%ebx
movl $128,%edx
xchgl %eax,%ebx
xchgl %eax,%edx
nenhuma das anteriores.9. Explique qual a diferença, subtil na sintaxe, mas enorme nos resultados produzidos, entre as
instruções:
movl tp,%ebx
10. Explique qual a diferença, subtil na sintaxe, mas enorme nos resultados produzidos, entre as
instruções:
movl $512,%ecx
movl 512,%ecx
11. Explique qual a diferença na memória reservada pelas seguintes declarações de variáveis, em
três programas diferentes. Como as classificaria segundo a sua correcção? .data ig: ik: .long 24 .data ig: .long ik: .long 24 .data ig: .long 16 ik: .long 24
12. Das seguintes instruções assembly da família de µP80x86, especificadas segundo a notação
usada no GAS, indique qual a única instrução válida, sabendo que ac e tp são os nomes de duas variáveis de 32 bits.
xchgw ac,tp
xchgl ac,tp
xchgl %ebx,%eax
xchgw %ax,%ebx
xchgl %ax,%ebxISC-LEI/FCUL 7
13. Das seguintes instruções assembly da família de µP80x86, especificadas segundo a notação
usada no GAS, indique qual a única que produz um resultado diferente das restantes.
movl $65535,%eax
movl $0xFFFF,%eax
movl $0177777,%eax
movl $177777,%eax
nenhuma das anteriores.14. Das seguintes instruções assembly da família de µP80x86, especificadas segundo a notação
usada no GAS, indique qual a única inválida.
movl $65535,%eax
movl $0xFFFF,%eax
movl $0177777,%eax
movl %eax,0xFFFF
nenhuma das anteriores.15. Das seguintes instruções assembly da família de µP80x86, especificadas segundo a notação
usada no GAS, indique qual a única inválida.
movl $65535,%eax
movl $0xFFFF,%eax
movl $0177777,%eax
movl %eax,$0xFFFF
nenhuma das anteriores.3. Material de Apoio Obrigatório
GNU Assembler for 80186 and higher, Roger Jegerlehner, 1996-2003, formulário de referência rápida disponível na página da disciplina.
4. Exercícios Adicionais Recomendados
Caderno de Exercícios de ISC (5ªedição) (GU-ISC-05-10), exercícios OB1 e OB2. Documento disponível na página da disciplina.
5. Bibliografia
Gas-Gnu ASsembler e Arquitectura Intel x86 (GU-ISC-03-12): slides 1-8.
Assembly para o Assemblador da GNU, Arquitectura Intel IA-32 (TA-ISC-05-10): cap. 1, 2, 3 (até pag. 30) e 4 (até pag. 45)
Caderno de Exercícios de ISC (5ªedição), Teresa Chambel, Dulce Domingos, et.al, DI-FCUL, Outubro de 2005 (GU-ISC-05-10). Disponível na página da disciplina.