Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

mp1 0203

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 14

Microprocessadores

Apoio às Aulas Teórico-práticas

1a Edição, Setembro 2002


Departamento de Engenharia Electrotécnica e de Computadores
1
Primeiros Passos
na Programação em Linguagem Assembly

(Famı́lia 51 da Intel)
jpsousa@fe.up.pt

Conteúdo
1 Objectivos 2

2 Introdução 2

3 Programação em linguagem assembly 3


3.1 Vantagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.2 Desvantagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.3 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

4 Modelo de programação 4
4.1 Registos principais da famı́lia i51 . . . . . . . . . . . . . . . . . . . . . . . 5
4.2 Grupos de instruções da famı́lia i51 . . . . . . . . . . . . . . . . . . . . . . 5
4.3 Regras de sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

5 O assemblador 7
5.1 Caracterı́sticas gerais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.2 Alguns comandos do assemblador da KEIL . . . . . . . . . . . . . . . . . . 7
5.2.1 Indicações gerais . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.2.2 Nomes simbólicos e operadores simples . . . . . . . . . . . . . . . . 8
5.2.3 Segmentos absolutos . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.2.4 Segmentos recolocáveis . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.3 Formato das listagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

6 Problemas 10

1
1 Objectivos

Familiarização com o modelo de programação simplificado da famı́lia 51 da Intel, com a


sintaxe da sua linguagem assembly e com alguns comandos do assemblador.

2 Introdução

Trabalhar segundo o paradigma do programa residente 1 obriga, como o próprio nome


indica, a armazenar em memória um programa (conjunto de ordens) num formato que
o microprocessador entenda. No seu nı́vel mais baixo, essas ordens não são mais do
que códigos binários armazenados em memória que o microprocessador vai extraindo,
interpretando e executando. Um programa nessa forma diz-se estar em código máquina.
Por diversas razões programar directamente em código máquina não é viável mesmo para
programas pequenos. De facto, constata-se facilmente que:

• os programas são muito difı́ceis de perceber e depurar,

• não descrevem a tarefa a executar de um modo inteligı́vel,

• são muito extensos e demoram muito tempo a escrever,

• qualquer distracção do programador conduz a erros muito difı́ceis de detectar.

Os dois últimos problemas podem ser atenuados escrevendo o programa em hexadecimal


em vez de binário mas melhoria substancial é atribuir um nome (mnemónica) a cada có-
digo de instrução de modo a tornar o programa minimamente inteligı́vel – chama-se a isso
programar em linguagem assembly. A figura 1 ilustra o mesmo programa escrito directa-
mente em código máquina (binário e hexadecimal) e também em linguagem assembly da
famı́lia 51 da Intel.

Binário Hexadecimal Linguagem assembly


11100101 E5 MOV A,64
01000000 40
00100101 25 ADD A,65
01000001 41
11110101 F5 MOV 66,A
01000010 42
Figura 1: Programa em código máquina e assembly.

1
cfr. Aulas teóricas da primeira semana

2
O programa adiciona o conteúdo da posição de memória 64 com o conteúdo da posição 65
e coloca o resultado na posição 66. Numa descrição de alto nı́vel, considerando a memória
representada pelo array mem[ ], o que este programa faz é:

mem[66] = mem[64] + mem[65]

De acordo com o estabelecido pela Intel, os códigos E5h, 25h e F5h representam, respec-
tivamente, as instruções:

MOV A,n (move para o registo A o conteúdo da posição n)


ADD A,n (adiciona ao registo A o conteúdo da posição n, resultado em A)
MOV n,A (move para a posição n o valor do registo A)

onde n representa um endereço de memória e A um registo necessário para efectuar a


adição uma vez que o CPU não consegue somar directamente os conteúdos de posições de
memória. Note-se também a necessidade de converter os números 64, 65 e 66 para binário
ou hexadecimal quando se programa directamente em código máquina.

3 Programação em linguagem assembly

A linguagem assembly é, pois, o nı́vel mais baixo em que se pode programar com alguma
comodidade. A tradução da linguagem assembly de cada microprocessador para o código
máquina correspondente pode ser feita à mão, mediante uma tabela de conversão, mas
normalmente é feita recorrendo a um assemblador, programa que normalmente é oferecido
pelo próprio fabricante do microprocessador.

Um bom assemblador atenua algumas das dificuldades da programação em linguagem


assembly mas há outras que são intrı́nsecas à própria linguagem. Nas secções seguintes
apresentam-se as vantagens e desvantagens de programar em assembly.

3.1 Vantagens

A principal (e actualmente única) vantagem é poder adaptar o código de modo a aproveitar


ao máximo as caracterı́sticas particulares do hardware onde vai ser executado, conseguindo
assim resultados optimizados quer em tempo de execução quer em tamanho de código
gerado.

Outra vantagem é a existência de assembladores para a totalidade dos microprocessadores


pelo que é sempre possı́vel programar em assembly, qualquer que seja o microprocessador
escolhido. O mesmo já não acontece com linguagens de alto nı́vel, onde nem sempre é
possı́vel encontrar um compilador adequado para um dado microprocessador.

3
3.2 Desvantagens

A principal desvantagem de uma linguagem de baixo nı́vel é a enorme desproporção entre


o seu conjunto de instruções e as tarefas que o microprocessador normalmente é chamado
a executar. Esta desproporção obriga o programador a decompor cada tarefa num vasto
conjunto de operações elementares que, além de ser um processo demorado e sujeito a
erros, não ajuda a manter o código estruturado.

Outra desvantagem é a necessidade de conhecer em detalhe o modelo de programação do


microprocessador, nomeadamente no que se refere aos registos de trabalho disponı́veis,
registos privilegiados ou especiais e registo de estado. Como consequência desta depen-
dência relativamente aos detalhes internos de um microprocessador, a portabilidade dos
programas é muito reduzida.

3.3 Conclusão

A tendência actual é a favor de uma programação mista, usando principalmente linguagens


de mais alto nı́vel (C em particular) e recorrendo à linguagem assembly apenas em rotinas
onde a eficiência do código seja o objectivo principal. Esta tendência explica-se por três
motivos:

• existem actualmente bons compiladores para a maioria dos microprocessadores, al-


guns até de domı́nio público,

• os avanços na microelectrónica permitem que a rapidez de execução se consiga fa-


cilmente por aumento da frequência de funcionamento

• a pressão do mercado obriga a encurtar o tempo de desenvolvimento e a aumentar


a facilidade de manutenção do código.

Quando se está a estudar o funcionamento interno de um microprocessador o assembly é


a linguagem mais adequada pelo que será intensivamente utilizada nesta disciplina.

4 Modelo de programação

O modelo de programação de um microprocessador descreve os recursos disponı́veis para o


programador: registos de trabalho, registos especiais, eventuais agrupamentos de registos,
instruções e modos de endereçamento disponı́veis, etc. Para começar a escrever pequenos
programas basta conhecer os principais registos e instruções que serão apresentados nas
duas secções que se seguem.

4
4.1 Registos principais da famı́lia i51

A arquitectura base da famı́lia 51 da Intel disponibiliza um número apreciável de registos


dos quais se destacam, numa primeira abordagem, os seguintes:

• Registos de trabalho – R0 a R7
• Registos privilegiados – A (acumulador) e B
• Registo de estado – PSW (program status word)

Os registos A e B são privilegiados no sentido de que existem algumas instruções que só
podem ser executadas neles, por exemplo as operações aritméticas só podem ser efectuadas
sobre o acumulador. O registo de estado (PSW) dá, entre outras, indicações sobre a
paridade do valor contido no acumulador e se houve ou não transporte e/ou overflow
na última operação aritmética efectuada; instruções especı́ficas permitem testar estas
condições.

4.2 Grupos de instruções da famı́lia i51

As 256 instruções disponı́veis na famı́lia i51 podem dividir-se em cinco grupos consoante
a sua função:

1. Instruções de movimentação de dados


2. Instruções aritméticas
3. Instruções lógicas
4. Instruções de salto e chamada de subrotinas
5. Instruções booleanas

As instruções de movimentação de dados permitem copiar valores de um registo para


outro, de um registo para memória e de memória para um registo.

As instruções aritméticas permitem efectuar as quatro operações aritméticas elementares


considerando ou não a existência de eventuais transportes. É sempre necessário recorrer
ao registo acumulador.

As instruções lógicas permitem efectuar operações lógicas elementares (AND, OR, XOR,
NOT) assim como rotações de bits para a esquerda ou para a direita.

As instruções de salto e chamada de subrotinas permitem alterar a ordem de execução


de um programa de forma condicional ou incondicional.

5
As instruções booleanas permitem manipular bits individualmente. A maior parte delas
obriga a utilizar a flag CY (carry) que funciona para o processamento booleano como o
acumulador para o processamento aritmético e lógico.

O resumo de instruções [1, páginas 13..16] ou [2] dá uma panorâmica geral das instruções
disponı́veis, pelo que deve ser lido com todo o cuidado.

4.3 Regras de sintaxe

A forma geral de uma instrução assembly da famı́lia 51 é

mnemónica operando1[,operando2[,operando3]]

onde o segundo e terceiro operandos nem sempre existem. De acordo com o estabelecido
pela Intel um operando pode ser

1. um número, representando um endereço de memória,

2. o nome de um registo,

3. um valor constante (neste caso precedido do carácter #),

4. um apontador (neste caso precedido do carácter @).

Estas regras de sintaxe entendem-se melhor com a apresentação de alguns exemplos sim-
ples ilustrativos da cada uma delas:

; Copia para o registo R5...


MOV R5,40h ; ...o conteúdo da posiç~
ao de memória 64.
MOV R5,#40h ; ...o valor 64.

ADD A,R7 ; Adiciona ao acumulador (acc) o registo R7. Resultado em acc.


MOV 65,A ; Copia para a posiç~
ao de memória 65 o que está em acc.
ORL A,#10010001b ; Faz o OR lógico do acc com o valor 91h. Resultado em acc.
LJMP 4358h ; Continua a execuç~
ao do programa no endereço 4358h.

; Copia para o acumulador...


MOV A,R0 ; ...o que está em R0
MOV A,@R0 ; ...o conteúdo da posiç~
ao de memória apontada por R0.

Note-se que dos registos de trabalho R0 a R7, apenas R0 e R1 podem assumir o papel
de apontadores. Quando o manual de programação [1] emprega a designação genérica Rn
está a referir-se aos registos R0 a R7 mas quando usa a designação Ri está a referir-se
apenas aos registos R0 e R1.

6
5 O assemblador

5.1 Caracterı́sticas gerais

Uma ferramenta essencial para programar em linguagem assembly é o assemblador. Os


primeiros assembladores pouco mais faziam do que a tradução para código máquina mas
os mais recentes têm muitas outras capacidades, nomeadamente:

• permitem atribuir nomes simbólicos a endereços de memória, dispositivos de entrada


saı́da, variáveis e grupos de instruções,

• permitem trabalhar em diversas bases de numeração bem como converter caracteres


nos seus códigos ASCII,

• permitem efectuar cálculos aritméticos simples com valores constantes ou nomes


simbólicos,

• permitem definir os endereços de memória onde o programa e os dados irão ser


armazenados,

• permitem reservar áreas de memória para armazenamento temporário de informa-


ção,

• permitem a construção e utilização de bibliotecas de funções, ajudando assim a


programar de modo modular e a reutilizar código já escrito em assembly ou noutras
linguagens,

• permitem a configuração dos parâmetros que alteram a geração de código máquina


e o formato das listagens produzidas.

Existem actualmente diversos assembladores comerciais e de domı́nio público para a fa-


mı́lia 51 da Intel. Um dos melhores é o da KEIL, disponı́vel em versão de demonstração
(mas 90% funcional) no CD que acompanha o livro recomendado.

5.2 Alguns comandos do assemblador da KEIL

5.2.1 Indicações gerais

Não há distinção entre maiúsculas e minúsculas. Qualquer texto precedido do carácter ‘;’
é considerado comentário. Um comentário prolonga-se sempre até ao fim da linha. O fim
do código fonte é indicado pelo comando END. Qualquer texto que apareça depois desse
comando é ignorado pelo assemblador.

7
5.2.2 Nomes simbólicos e operadores simples

Uma das grandes vantagens de utilizar um assemblador é poder definir nomes simbólicos.
Para as constantes essa definição faz-se com os comandos EQU e DB e para as variáveis com
o comando DS cuja sintaxe se depreende dos exemplos apresentados:

; === Constantes =======================================================


Garrafas EQU 12 ; Número de garrafas por caixa
tempo equ 250 ; Tempo de engarrafamento (ms)
V1 equ 8000h ; Endereço de E/S da válvula 1
V2 EQU V1+1 ; Endereço de E/S da válvula 2
V3 EQU V1+2 ; Endereço de E/S da válvula 3
init equ 10011010b ; Comando de inicializaç~ao
prompt equ ’>’ ; Código ASCII do prompt do sistema
CR EQU 0Ah ; Carriage return (ASCII)
LF EQU 0Dh ; Line feed (ASCII)

; === Mensagens do sistema ============================================


Pin: DB "PIN: ",CR,LF ; Reserva e preenche memória de programa

; === Variáveis =======================================================


Contador: DS 1 ; Reserva um byte em memória de dados
Total: DS 2 ; Reserva dois bytes em memória de dados

A diferença entre EQU e DB é que o primeiro não gera qualquer código máquina enquanto
o segundo preenche a memória com os valores indicados. Note-se a necessidade de ‘:’ nos
comandos DB e DS.

A definição de nomes simbólicos para endereços de programa é feita implicitamente ao


colocar uma etiqueta antes da instrução que se quer referenciar:

.
.
mov a,#100
loop: dec a
jnz loop
.
.

5.2.3 Segmentos absolutos

Ao programar em assembly o programador pode escolher os endereços onde ficará o pro-


grama e onde ficarão os dados. Para isso é necessário definir segmentos de memória. Um
segmento absoluto é uma zona de memória com endereço inicial fixo. São definidos re-
correndo, entre outros, aos comandos CSEG e DSEG cuja sintaxe se depreende do exemplo
apresentado:

8
; === Variáveis ========================================================
DSEG AT 40h ; Segmento absoluto com inı́cio em 40h
total: DS 2 ; Reserva dois bytes em memória de dados
contador: DS 1 ; Reserva um byte (42H)

; === Programa principal ===============================================


CSEG AT 0000h ; Segmento no endereço 0000h da memória
. ; de programas (MP)
.
.
; --- Rotinas de E/S ---------------------------------------------------
CSEG AT 007Fh ; Segmento no endereço 007Fh da MP
.
.
.

; === Mensagens do sistema =============================================


CSEG AT 0100h ; Segmento no endereço 0100h da MP
pin_msg: DB "PIN: ",CR,LF ; Preenche 7 bytes em MP

No exemplo apresentado, a variável total ocupa os endereços 40h e 41h da memória


de dados e a variável contador fica no endereço 42h também da memória de dados. O
programa principal começa no endereço 0000h da memória de programas e há um conjunto
de rotinas que ocupam uma zona com inı́cio em 007Fh da memória de programas. No
endereço 0100h da memória de programas residem as mensagens do sistema.

5.2.4 Segmentos recolocáveis

Em programas complexos a atribuição de segmentos é uma tarefa delicada pois é necessá-


rio garantir que não haja sobreposição de segmentos. Para evitar esse trabalho e facilitar a
reutilização de código é recomendável trabalhar sempre que possı́vel com segmentos rela-
tivos (recolocáveis). A definição de um segmento relativo faz-se com o comando segment
e a sua activação com o comando rseg. O exemplo ilustra a sintaxe:

rotinas segment code ; Segmento de código recolocável


mensagens segment code ; Segmento de código recolocável
dados3 segment data ; Segmento de dados recolocável

rseg dados3 ; Activa o segmento dados3


total: DS 2 ; Reserva dois bytes em memória de dados
contador: DS 1 ; Reserva um byte

; === Programa principal ===============================================


cseg at 0000h ; Segmento no endereço 0000h da memória
. ; de programas (MP)
.
.

9
; --- Rotinas de E/S ---------------------------------------------------
rseg rotinas ; Activa o segmento rotinas
.
.
.

; === Mensagens do sistema =============================================


rseg mensagens ; Activa o segmento mensagens
pin_msg: DB "PIN: ",CR,LF ; Preenche 7 bytes algures em MP

No exemplo apresentado, só se sabe que o programa principal começa no endereço 0000h
da memória de programas pois é o único segmento absoluto existente. Os endereços dos
segmentos recolocáveis são definidos automaticamente durante o processo de geração do
código máquina.

5.3 Formato das listagens

O assemblador da KEIL permite definir vários parâmetros de formatação para as listagens


que gera. Se nada for dito em contrário, as listagens são formatadas de modo a evidenciar
o endereço de memória em que cada instrução começa e os códigos gerados para cada
instrução. Mais uma vez um exemplo é elucidativo:

LOC OBJ LINE SOURCE

---- 1 dseg at 40h


0040 2 cont: ds 1
3
---- 4 cseg at 0
0000 754064 5 mov cont,#100
0003 1540 6 loop: dec cont
0005 E540 7 mov a,cont
0007 70FA 8 jnz loop
9
10 end

Examinando a 1a coluna da listagem (endereços) facilmente se percebe que a variável cont


ficou colocada no endereço 40h e examinando 2a coluna (códigos gerados) facilmente se vê
que a primeira instrução gerou três bytes – o primeiro é o código da instrução propriamente
dita, o segundo o endereço da variável e o terceiro o valor que nela é colocado.

6 Problemas

A única maneira de aprender a programar em assembly é escrever programas em assembly!


Apresentam-se de seguida alguns problemas básicos que podem ser resolvidos recorrendo
a instruções aritméticas, lógicas e de movimentação de dados.

10
1. Escreva programas para efectuar as seguintes operações:

(a) Copiar R5 para R3


(b) Trocar R5 com R3 sem estragar mais nenhum registo

2. Apresente dois modos diferentes de negar o conteúdo da variável montante guar-


dada na posição de memória com o endereço 50. Em ambos os casos guardar o
resultado na variável jusante guardada na posição seguinte.

3. Traduza à mão, para código máquina, as duas soluções do problema anterior.

4. Apresente dois modos diferentes de multiplicar por 2 o valor da variável velocidade


guardada numa posição de memória à escolha.

5. Escreva programas para efectuar as seguintes operações:

(a) mem[22] = mem[21] – mem[20] (considere que mem[ ] representa a


memória de dados)
(b) mem[21] = 3 × mem[20], supondo que mem[20] é inferior a 86. Porquê?
(c) segundos = 60 × minutos sendo as variáveis guardadas em posições de
memória à escolha e podendo minutos variar entre 0 e 200.

Nos problemas seguintes, a não ser que seja feita referência a endereços especı́ficos, deverão
ser utilizados segmentos de dados recolocáveis.

6. Escreva um programa que calcule x = x + y para variáveis de 16 bits.

7. Escreva um programa que calcule x = x 2 considerando um valor inicial de x inferior


a 256.

8. Escreva um programa que calcule x = – x (complemento para 2) considerando que:

(a) x é de 8 bits
(b) x é de 16 bits

9. Escreva um programa que copie os 4 bits mais significativos de x para os 4 bits


menos significativos de y, considerando que são variáveis de 8 bits. Os 4 bits mais
significativos de y devem ser colocados a zero.

10. Considere que temptot contém a soma das medições de temperatura efectuadas
em 16 pontos diferentes de uma estufa. Apresente 3 modos diferentes de calcular
a temperatura média no interior da estufa. Suponha que está a trabalhar com
variáveis de 8 bits e o resultado é guardado em tempmed.

11
Referências
[1] Philips semiconductors; 80C51 family programmer’s guide and instruction set; Se-
tembro de 1997.

[2] Ferreira, José Manuel; Resumo das instruções do 80C51 ; FEUP, Setembro de 2000.

12

Você também pode gostar