Sebenta teórica on-line - Manuel Cabral Reis

Transcrição

Sebenta teórica on-line - Manuel Cabral Reis
Introdução à
Arquitectura de Computadores
Série
Didáctica
CIÊNCIAS APLICADAS
245
Manuel José Cabral dos Santos Reis
António Manuel Silva Pinto Soares
Universidade de Trás-os-Montes e Alto Douro
Vila Real
Índice
1
2
Introdução
1.1
Enquadramento e objectivos
1.2
Organização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4
. . . . . . . . . . . . . . . . . . . . . . . . . 11
Generalidades
15
2.1
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2
Conceitos e palavras chave
2.3
Descrição geral de um micro-processador
2.4
Estrutura interna de um CPU
2.5
3
11
. . . . . . . . . . . . . . . . . . . . . . . . . . 21
. . . . . . . . . . . . . . . . . . 25
. . . . . . . . . . . . . . . . . . . . . . . . 28
2.4.1
Unidade Aritmética e Lógica (ALU) . . . . . . . . . . . . . . . . . 30
2.4.2
Unidade de Temporização e Controlo
Arquitectura de um micro-processador
. . . . . . . . . . . . . . . . 31
. . . . . . . . . . . . . . . . . . . 34
2.5.1
Formato e processamento das instruções
. . . . . . . . . . . . . . 36
2.5.2
Registos internos de uma arquitectura básica . . . . . . . . . . . . 37
2.6
Arquitectura do Z80
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.7
Memória . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Ferramentas para programação de um micro-processador
47
3.1
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.2
Formato de programação . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3
Pseudo-instruções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.4
Desenvolvimento de um programa em assembly . . . . . . . . . . . . . . . 48
3.5
Assembler
3.6
Loaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.7
Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Estudo do conjunto de instruções
55
4.1
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.2
Formato simbólico das instruções
4.3
Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.4
Tipos de endereçamento
. . . . . . . . . . . . . . . . . . . . . . 55
. . . . . . . . . . . . . . . . . . . . . . . . . . . 56
i
4.5
4.4.1
Modo registo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4.2
Absoluto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4.3
Imediato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4.4
Registo indirecto
4.4.5
Auto-incremento e auto-decremento . . . . . . . . . . . . . . . . . 59
4.4.6
Indexado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.4.7
Base
4.4.8
Base-indexado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.4.9
Relativo
Instruções
4.5.1
5
. . . . . . . . . . . . . . . . . . . . . . . . . . . 59
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Transferência de informação
. . . . . . . . . . . . . . . . . . . . . 63
4.5.1.1
Modo absoluto
. . . . . . . . . . . . . . . . . . . . . . . 63
4.5.1.2
Modo imediato
. . . . . . . . . . . . . . . . . . . . . . . 64
4.5.1.3
Modo base . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.5.1.4
Modo registo indirecto . . . . . . . . . . . . . . . . . . . 64
4.5.1.5
Outros exemplos de instruções de transferência
. . . . . 65
4.5.2
Manipulação de blocos
. . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.3
Instruções aritméticas . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.5.3.1
Aritmética de 8 bits
. . . . . . . . . . . . . . . . . . . . 67
4.5.3.2
Aritmética de 16 bits . . . . . . . . . . . . . . . . . . . . 68
4.5.4
Instruções lógicas
. . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.5.5
Manipulação de bits . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.5.6
Controlo de programa . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5.7
Deslocamento e rotação . . . . . . . . . . . . . . . . . . . . . . . . 74
4.5.8
Grupo aritmético de propósito geral . . . . . . . . . . . . . . . . . 75
4.5.9
Grupo de controlo do CPU . . . . . . . . . . . . . . . . . . . . . . 76
4.6
Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.7
Subrotinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.8
Passagem de parâmetros
. . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.8.1
Registos
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.8.2
Área de memória
4.8.3
Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
. . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Entrada e Saı́da
83
5.1
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.2
Mapas de endereçamento . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.3
Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.3.1
Protocolo de programação para periféricos de saı́da
5.3.2
Protocolo de programação para periféricos de entrada . . . . . . . 91
ii
. . . . . . . . 88
5.3.3
Protocolo de programação num sistema computacional com periféricos de entrada e de saı́da . . . . . . . . . . . . . . . . . . . . . 94
5.4
Tipos de interfaces
5.4.1
Interface paralela
5.4.1.1
5.4.2
. . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Estudo da interface paralela Z80 PIO . . . . . . . . . . . 96
Interface série
5.4.2.1
6
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Estudo da interface série Am8251 . . . . . . . . . . . . . 103
Interrupções
109
6.1
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.2
Considerações gerais
6.3
Interrupções múltiplas e prioridades . . . . . . . . . . . . . . . . . . . . . 111
6.4
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.3.1
Polling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.3.2
Vector de interrupção . . . . . . . . . . . . . . . . . . . . . . . . . 113
Interrupções no Z80 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
6.4.1
Interrupções mascaráveis . . . . . . . . . . . . . . . . . . . . . . . 115
6.4.1.1
Modo 0 (interrupção vectorizada) . . . . . . . . . . . . . 117
6.4.1.2
Modo 1 (interrupção por pesquisa)
6.4.1.3
Modo 2 (interrupção vectorizada) . . . . . . . . . . . . . 117
. . . . . . . . . . . . 117
6.4.2
Interrupções não mascaráveis . . . . . . . . . . . . . . . . . . . . . 119
6.4.3
Programa de entrada/saı́da usando interrupções
iii
. . . . . . . . . . 121
iv
Índice de figuras
2.1 Diagrama de blocos de uma arquitectura tipo Harvard. . . . . . . . . . . . 17
2.2 Diagrama de blocos de uma arquitectura tipo von Newmann. . . . . . . . . 18
2.3 Diagrama de blocos do processador de sinal TMS32010. . . . . . . . . . . . 20
2.4 Diagrama de blocos de uma máquina programável tı́pica. . . . . . . . . . . 26
2.5 Diagrama de blocos de um sistema de computador tı́pico. . . . . . . . . . . 27
2.6 Diagrama temporal associado ao processo de leitura de dados da memória.
28
2.7 Processo tı́pico de transferência de informação entre dois registos. . . . . . 29
2.8 Diagrama de blocos do CPU Z80. . . . . . . . . . . . . . . . . . . . . . . . 30
2.9 Diagrama de blocos de uma secção da ALU. . . . . . . . . . . . . . . . . . 31
2.10 Unidade de temporização e controlo micro-programada. . . . . . . . . . . . 32
2.11 Exemplo de micro-programa. . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.12 Esquema de ligações externas do Z80. . . . . . . . . . . . . . . . . . . . . . 34
2.13 Diagrama temporal correspondente à fase de fetch.
. . . . . . . . . . . . . 36
2.14 Diagrama de blocos de um conjunto mı́nimo de registos. . . . . . . . . . . 38
2.15 Instruções de rotação para a esquerda e para a direita. . . . . . . . . . . . 38
2.16 Diagrama de fluxo para o algoritmo do exemplo 4. . . . . . . . . . . . . . . 40
2.17 Registos do Z80 visı́veis ao programador. . . . . . . . . . . . . . . . . . . . 41
2.18 Exemplificação da utilização dos index registers. . . . . . . . . . . . . . . . 42
2.19 Exemplificação da utilização da stack. . . . . . . . . . . . . . . . . . . . . . 42
2.20 Exemplo de memória organizada em bits. . . . . . . . . . . . . . . . . . . . 43
2.21 Exemplo de memória organizada em bytes. . . . . . . . . . . . . . . . . . . 44
2.22 Diagrama temporal dos sinais afectos ao ciclo de leitura da memória. . . . 45
2.23 Diagrama temporal dos sinais afectos ao ciclo de escrita na memória. . . . 45
3.1 Diagrama de fluxo das fases de desenvolvimento de um programa escrito
em assembly. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.2 Exemplo de funcionamento de um loader. . . . . . . . . . . . . . . . . . . . 51
4.1 Modo de endereçamento tipo registo. . . . . . . . . . . . . . . . . . . . . . 57
4.2 Modo de endereçamento tipo absoluto. . . . . . . . . . . . . . . . . . . . . 58
4.3 Modo de endereçamento tipo registo indirecto. . . . . . . . . . . . . . . . . 59
v
4.4 Modos de endereçamento tipo auto-incremento e auto-decremento. . . . . . 60
4.5 Modo de endereçamento tipo indexado. . . . . . . . . . . . . . . . . . . . . 60
4.6 Modo de endereçamento tipo base. . . . . . . . . . . . . . . . . . . . . . . 61
4.7 Modo de endereçamento tipo base-indexado. . . . . . . . . . . . . . . . . . 61
4.8 Modo de endereçamento tipo relativo. . . . . . . . . . . . . . . . . . . . . . 62
5.1 Exemplo de mapa de endereçamento. . . . . . . . . . . . . . . . . . . . . . 84
5.2 Outro exemplo de mapa de endereçamento. . . . . . . . . . . . . . . . . . . 85
5.3 Exemplo de endereçamento por linha de I/O. . . . . . . . . . . . . . . . . . 87
5.4 Esquema geral de interligação de uma interface. . . . . . . . . . . . . . . . 88
5.5 Diagrama de fluxo do protocolo de programação para saı́da de dados. . . . 89
5.6 Esquema de interligação da interface do exemplo 15. . . . . . . . . . . . . . 89
5.7 Diagrama de fluxo para saı́da de dados. . . . . . . . . . . . . . . . . . . . . 91
5.8 Diagrama de fluxo do protocolo de programação para entrada de dados. . . 92
5.9 Esquema de interligação da interface do exemplo 16 . . . . . . . . . . . . . 92
5.10 Diagrama de fluxo para entrada de dados. . . . . . . . . . . . . . . . . . . 93
5.11 Diagrama de blocos dum sistema computacional com periféricos de entrada
e periféricos de saı́da. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.12 Diagrama de fluxo do protocolo para entrada e saı́da de dados. . . . . . . . 95
5.13 Diagrama de blocos de interligação de uma interface paralela. . . . . . . . 96
5.14 Diagrama de blocos do circuito Z80 PIO. . . . . . . . . . . . . . . . . . . . 98
5.15 Diagrama de blocos das portas A e B do circuito Z80 PIO. . . . . . . . . . 99
5.16 Esquema de ligações do circuito Z80 PIO. . . . . . . . . . . . . . . . . . . 99
5.17 Programação das interrupções do circuito Z80 PIO. . . . . . . . . . . . . . 100
5.18 Exemplo de interligação de uma interface série. . . . . . . . . . . . . . . . 102
5.19 Transmissão série assı́ncrona.
. . . . . . . . . . . . . . . . . . . . . . . . . 103
5.20 Diagrama de blocos do circuito Am8251. . . . . . . . . . . . . . . . . . . . 104
5.21 Códigos de programação do Am8251 nos modos sı́ncrono ou assı́ncrono. . . 106
5.22 Códigos de programação do Am8251: palavras de comando. . . . . . . . . 107
5.23 Códigos de programação do Am8251: consulta do registo de estado. . . . . 107
6.1 Diagrama de fluxo genérico de uma rotina de serviço à interrupção. . . . . 111
6.2 Exemplo de atendimento de interrupções por polling. . . . . . . . . . . . . 112
6.3 Exemplo de atendimento de interrupções por vector de interrupção. . . . . 114
6.4 Estabelecimento de prioridades no atendimento de interrupções com recurso ao esquema Daisy Chain. . . . . . . . . . . . . . . . . . . . . . . . . 116
6.5 Esquema resumido do sistema de interrupções do Z80. . . . . . . . . . . . . 116
6.6 Diagrama de fluxo de atendimento das interrupções para o modo 0 do Z80. 118
6.7 Diagrama de fluxo de atendimento das interrupções para o modo 1 do Z80. 118
6.8 Exemplo de tabela de interrupções para o modo 2 do Z80. . . . . . . . . . 119
vi
6.9 Diagrama de fluxo de atendimento das interrupções para o modo 2 do Z80. 120
6.10 Diagrama de fluxo de atendimento das interrupções não mascaráveis do Z80.121
6.11 Diagrama de fluxo do exemplo 21. . . . . . . . . . . . . . . . . . . . . . . . 123
vii
viii
Índice de tabelas
2.1 Algumas das caracterı́sticas presentes na famı́lia de processadores Intel. . . 21
2.2 Conjunto de instruções mı́nimo para a arquitectura proposta. . . . . . . . . 39
5.1 Operação funcional do Am8251 (USART). . . . . . . . . . . . . . . . . . . 105
6.1 Sumário do processo de interrupções do Z80. . . . . . . . . . . . . . . . . . 124
ix
x
Capı́tulo 1
Introdução
1.1
Enquadramento e objectivos
Estes apontamentos foram escritos com o objectivo de servirem como “texto de apoio” à
disciplina de Arquitectura de Computadores dos cursos de Informática e de Comunicações
e Multimédia, funcionando ambos no 1o ano, 2o semestre. Não pretendem de forma
alguma ser auto-suficientes. O aluno é totalmente encorajado e incitado a consultar
outras referências bibliográficas, nomeadamente as que se apresentam de seguida e as que
constam da lista apresentada no final destes apontamentos. Claro que esta lista não é,
nem pretende ser, completa, mas sim indicativa.
Para além destas referências, tidas como tradicionais, pensamos ser conveniente indicar
locais e fontes alternativas onde pode ser encontrada informação geralmente actualizada
a uma frequência superior à que é possı́vel nos livros e revistas impressas.
Recomenda-se uma visita periódica a
http://www.cs.wisc.edu/~arch/www/online.html
onde se podem encontrar as mais diversas informações na área da arquitectura de computadores, onde se inclui bibliotecas digitais, periódicos (revistas) on-line e bibliografia.
Por outro lado, em
http://www.handshake.de/user/kroening/conferences.html
pode ser encontrada uma lista actualizada de publicações e conferências internacionais na
área da arquitectura de computadores.
A partir de
http://www.utad.pt/~mcabral/
estão diponı́veis as informações mais recentes relativas à disciplina. Nestas páginas é
possı́vel saber, por exemplo, o número de faltas, as normas de avaliação, enunciados de
exames anteriores, etc..
11
Para acompanhamento das aulas práticas também aconselhamos a consulta dos manuais [1, 2, 3].
Esta disciplina recorre a conceitos ou noções introduzidas em disciplinas ou áreas tão
diversas como a programação, a electrónica digital, processamento digital de sinal, etc.,
devendo por outro lado servir de base comum a todas elas. Os objectivos principais de
uma disciplina deste tipo são muito gerais. Assim, pretende-se que o aluno estude e
compreenda no essencial:
• O que é uma arquitectura;
• Programe essa arquitectura.
Para isso vamos realçar os princı́pios básicos de uma arquitectura genérica e muito
simples e não um conjunto detalhado de instruções de uma arquitectura particular. Contudo, sem prejuı́zo no que se refere à simplicidade, também será estudada em pormenor
a arquitectura do Z80.
Optámos pelo estudo da arquitectura do Z80 devido, essencialmente, à sua simplicidade, se encontrarem disponı́veis no mercado diferentes versões e ser utilizado na indústria
(geralmente como micro-controlador). A tı́tulo de exemplo, note-se que algumas arquitecturas de jogos assentam na emulação de dois ou mais micro-processadores Z80.
Repare-se ainda que apesar do Z80 possuir uma arquitectura simples as suas unidades
principais estão presentes nos micro-processadores mais complexos e actuais. Mais, a
forma como algumas unidades se encontram implementadas continua a manter-se actual.
Deve ter-se também sempre presente que a discussão não se limita à arquitectura do Z80,
sendo geralmente este o ponto de partida para outras soluções. Por exemplo exemplo, a
discução que será apresentada no capı́tulo 2, secção 2.4.2, sobre a unidade de temporização
e controlo não se limitará à forma como esta unidade é implementada no Z80.
Como advertência final deve ser referido que a leitura e estudo destes apontamentos
não dispensa de forma alguma a assistência às aulas, sendo considerado de primordial
importância a sua frequência.
1.2
Organização
Estes apontamentos encontram-se organizados em seis capı́tulos. Este primeiro capı́tulo
dispensa apresentação.
No capı́tulo 2 começa-se pela introdução de conceitos e definições gerais essenciais aos
conteúdos aqui tratados. Apresenta-se também um resumo da evolução dos (micro)processadores. É também feita uma descrição geral de um micro-processador tı́pico, da sua
estrutura interna e da sua arquitectura. Depois de apresentadas estas noções e conceitos
são estudadas as formas como estes são implementados no caso do Z80. Na parte final é
12
estudada a memória. O aluno é encorajado a ler integralmente este capı́tulo (idealmente
os apontamentos completos) para uma perspectiva geral da evolução e constituição de um
sistema de computador.
O capı́tulo 3 é dedicado à forma como se pode programar um (micro)processador. É
dada especial atenção à programação em assembly. São estudadas matérias como o formato de programação, as pseudo-instruções, as fases de desenvolvimento de um programa
em assembly, os assemblers, os loaders e as macros.
No capı́tulo 4 é proposto e estudado um conjunto de instruções genérico. Para este
efeito primeiro apresenta-se o formato simbólico das instruções, o significado e a forma
como as flags são afectadas pelas instruções e ainda os tipos de endereçamento. Só depois
são estudados os grupos de instruções. Na parte final deste capı́tulo é estudada a stack e
as subrotinas. Claro está que não se pode falar de subrotinas sem falar em passagem de
parâmetros.
Para que um (micro)processador efectue trabalho útil é necessária a “interligação”
deste ao mundo que o rodeia. O capı́tulo 5 é dedicado a este assunto. São estudados os
mapas de endereçamento e as interfaces, onde se incluem os protocolos de programação,
tanto para entrada como para saı́da de dados. São também estudadas tanto as interfaces
série como as interfaces paralelo. Os casos dos circuitos Z80PIO e Am8251 são vistos
detalhadamente.
Por último serão estudadas, no capı́tulo 6, as interrupções. Como será visto neste
capı́tulo, o sistema de interrupções permite agelizar fortemente, entre outros, os processos
de entrada e saı́da de dados num sistema de (micro)computador. Começa-se por definições
e conceitos gerais, sendo depois estudadas as interrupções múltiplas e as prioridades. Para
este efeito serão estudadas as técnicas de polling e de vector de interrupção. Na parte
final do capı́tulo será estudada a forma como as interrupções são implementadas no Z80.
Será feita a distinção entre interrupções mascaráveis e não mascaráveis.
Convém deixar claro que, sendo esta área da arquitectura de computadores “adulta”
e estando nós a tratar de noções consideradas introdutórias ou mesmo básicas, não é
nosso objectivo inovar no que concerne directamente ao aspecto com que os esquemas
ou diagramas de blocos de certas unidades são apresentados. Preferimos apresentá-los
reflectindo a forma como são desenhados pelos próprios fabricantes e na esmagadora
maioria da literatura nesta área. Desta forma contribuimos para não causar os conflitos
ou confusões que muitas vezes são detectados, devido essencialmente à falta de experiência
dos alunos destes estágios iniciais. Assim, o mérito (ou demérito) da maioria dos esquemas
e diagramas aqui apresentados não nos deve ser atribuı́do.
13
14
Capı́tulo 2
Generalidades
2.1
Introdução
A primeira questão que se levanta é a de saber “o que é um computador?” Obviamente que
a resposta não é simples nem única. Podemos dizer que é uma sala cheia de equipamento,
a fazer muito barulho, com muitas luzes a acender e a apagar (pode estar a controlar
uma central nuclear, ou os processos necessários a uma grande organização ou empresa,
calculando inventários, facturas, etc.; controlando os salários de milhares de funcionários,
etc.; enfim, faz tudo menos ir às compras e cozinhar!).
Também se pode dizer que é um conjunto de equipamentos, juntamente com um terminal, colocados a um canto de um laboratório de um cientista e que controla ou monitoriza
o progresso de uma determinada experiência (eventualmente, depois da experiência completa, apresenta resultados traçando gráficos e analisando resultados).
Pode ainda ser considerado como uma caixa pequena que tem um teclado, um écran,
um altifalante, uma impressora e uma ranhura onde se introduzem disquetes. Geralmente
estes equipamentos permitem guardar informação respeitante à correspondência pessoal,
contas bancárias. Pode também ser utilizado para servir de adversário, por exemplo, num
jogo de xadrez, etc..
Alternativamente, pode ser visto como um circuito electrónico miniaturizado, que
tem apenas uma função, encontrado regularmente nos electrodomésticos, automóveis,
brinquedos, etc..
Todas estas respostas são válidas se falarmos genericamente! Mas, se quisermos ser
mais precisos, temos que fazer a seguinte divisão (seguindo a mesma ordem de ideias):
• Main frames;
• Mini-computadores;
• Microcomputadores;
• Micro-controladores.
15
Neste curso vamos dar especial ênfase ao estudo dos microcomputadores. Vamos
analisar em pormenor os diferentes blocos que os constituem e os vários nı́veis em que
estão estruturados.
O nı́vel mais baixo é conhecido por hardware, ou seja, a parte fı́sica da máquina (os
componentes electrónicos). O nı́vel seguinte consiste na interconexão entre os elementos de hardware e as estruturas de interface com o utilizador/operador do equipamento.
O software constitui o nı́vel mais elevado, sendo composto pelo conjunto de instruções
(programa) que faz com que o computador execute trabalho útil! Existem autores que
consideram apenas dois nı́veis: hardware e software, classificando as estruturas de interface como pertencentes quer ao hardware quer ao software.
Quando olhamos para o desenvolvimento histórico dos circuitos especiais (num único
chip), devemos considerar a arquitectura e a tecnologia dos dispositivos separadamente.
Tal como sucedeu com os dispositivos semicondutores, as estruturas básicas já estavam
bem definidas antes da tecnologia que as podia suportar. As arquitecturas gerais para
computadores e micro-computadores (computadores baseados num único chip para a Unidade Central de Processamento — UCP ou CPU) podem ser dividias em dois grandes
grupos ou categorias.
A primeira grande arquitectura de um computador electro-mecânico possuı́a espaços
de memória separados para as instruções de programa e para os dados. Isto permitia o
acesso simultâneo aos dados e às instruções de programa (ao programa). Esta tipologia
de arquitectura, que ficou conhecida como arquitectura tipo Harvard, surgiu no final
dos anos 30 em Harvard, tendo sido desenvolvida pelo fı́sico Howard Aiken. O primeiro
computador deste tipo ficou operacional em 1944 e chamava-se Harvard Mark 1.
O primeiro computador electrónico de utilização genérica foi provavelmente o ENIAC
(Electronic Numerical Integrator and Calculator), tendo sido construı́do entre 1943 e 1946
na Universidade da Pennsylvania. A sua arquitectura era similar à usada no Harvard
Mark 1 (memória de dados e de programa separadas). Esta arquitectura é apresentada
esquematicamente na figura 2.1. Devido à complexidade das memórias de dados e programas em separado, este tipo de arquitectura não se tornou popular em computadores e
micro-computadores de utilização genérica.
Um dos consultores do ENIAC foi John von Neumann, um matemático de origem
Húngara. von Neumann foi mais tarde reconhecido como o criador de uma arquitectura
muito diferente, a qual foi publicada por Burks, Goldstine e von Neumann, em 1946.
A então chamada arquitectura de von Neumann, definiu o padrão ou standard para a
arquitectura dos computadores nos mais de quarenta anos seguintes. A ideia era muito
simples e assentava em duas premissas principais:
1. Não é feita distinção intrı́nseca entre dados e instruções;
2. As instruções podiam ser divididas em duas partes (dois grupos ou conjuntos de
16
Leitor de cartões
Impressora e perfurador
de catões
barramento de dados
Multiplicador
Router de
funções
Divisor e
extractor de
raiz quadrada
Acumuladores
(20)
barramento de programa
Unidade de programação
principal
Figura 2.1 — Diagrama de blocos de uma arquitectura tipo Harvard.
bits).
Uma parte ou grupo de bits era dedicado à codificação da instrução (representava
a instrução ou operação) e a outra parte ou grupo de bits indicava o endereço do(s)
operando(s) (os dados a serem usados). Nesta arquitectura existia, pois, um único espaço
de memória partilhado pelas instruções e pelos dados.
Nesta linha, em 1951, o Institute for Advanced Studies, Princeton, apresentou a arquitectura do computador conhecido por IAS, figura 2.2. Esta nova arquitectura simplificava
o desenho do computador, mas possuı́a a desvantagem de apenas poder aceder aos dados
ou às instruções num momento especı́fico. A História veio a revelar que esta limitação não
é muito penalizadora para computadores e micro-computadores de utilização genérica.
As famı́lias de processadores de utilização genérica MC68000 da Motorola, i86 da
Intel e a Advanced Micro Devices (AMD) partilham da arquitectura proposta por von
Neumann. Estas e outras famı́lias também possuem outras caracterı́sticas tı́picas dos
computadores dos últimos cinquenta anos.
Nos principais blocos computacionais inclui-se uma Unidade Aritmética e Lógica
(Arithmetic Logical Unit — ALU) e um shifter (“deslocador” para a esquerda ou para a
direita). Operações como a adição, subtracção e movimentação de dados são facilmente
executadas em poucos ciclos de relógio. Instruções complexas como a multiplicação e a
divisão são construı́das a partir de um conjunto de shifts (deslocamento), adições ou subtracções. Dispositivos deste tipo são conhecidos por computadores com conjunto complexo
de instruções (Complex Instruction Set Computer — CISC). Geralmente, as arquitecturas
do tipo CISC possuem a operação de multiplicação, mas na verdade esta é implementada
17
Unidade Aritmética e Lógica
Equipamento de
Entrada/Saı́da
Instruções e dados
Memória principal
Endereços
Unidade de Controlo de Programa
Figura 2.2 — Diagrama de blocos de uma arquitectura tipo von Newmann.
recorrendo a instruções em micro-código, geralmente armazenada num chip de memória
ROM. Esta instrução de multiplicação demorará por isso vários ciclos de relógio a ser
executada.
O processamento ou tratamento de dados envolve muitas operações na forma:
A = B × C + D.
Esta simples equação envolve uma operação de adição e uma multiplicação. Devido à
lenta execução da instrução de multiplicação, as arquitecturas do tipo CISC não são muito
eficientes no cálculo deste tipo de equações. Para este tipo de aplicação necessitamos
de máquinas capazes de executar instruções do tipo multiplicação e adição em apenas
um ciclo de relógio. Como tal é necessária uma aproximação diferente à arquitectura
dos computadores. Por outras palavras, necessitamos de uma arquitectura moldada à
aplicação.
Por exemplo, no processamento digital de sinal em tempo real, a maior preocupação
é com a quantidade de processamento que pode ser feito antes que novos dados estejam
disponı́veis, ou seja, à espera de serem tratados. Os primeiros processadores digitais de
sinal (Digital Signal Processors — DSPs) usavam blocos de componentes standard para
construir shift-registers (registos de deslocamento), adders (adicionadores) e multipliers
(multiplicadores). Veja-se [13], por exemplo, para alguns pormenores relacionados com a
18
forma como certos componentes ou unidades podem ser implementados.
O desenho dos multiplicadores evoluiu para a utilização de técnicas de pipe-lining,
sendo o primeiro multiplicador de ciclo único implementado no inı́cio dos anos 70. Na sua
construção foram utilizados componentes standard de alta velocidade do tipo Lógica de
Emissor Acoplado (Emitter-Coupled Logic — ECL).
Os laboratórios Lincoln lideravam nesta altura o processo de investigação em DSPs. O
Lincoln FDP (Fast Digital Processor) foi apresentado em 1971 e executava uma instrução
de multiplicação em 600 nano-segundos e era construı́do por 10 000 circuitos integrados
(separados). Também padecia do facto de tentar implementar operações paralelas usando
a arquitectura sequencial de von Neumann.
O Lincoln LSP/2 foi construı́do a partir da lição aprendida com o FDP e usava uma
arquitectura semelhante à do computador Harvard Mark 1. Como esta arquitectura é
eminentemente paralela, foi possı́vel construir um DSP quatro vezes mais rápido que o
FDP, com cerca em um terço dos circuitos integrados (CIs).
Em meados dos anos 70, graças também ao envolvimento de outras instituições de
investigação, já era possı́vel executar uma operação de multiplicação em cerca de 200
nano-segundos. Estas novas máquinas eram capazes de fazer processamento digital de
sinal em tempo real, mas eram tão volumosas e caras que a sua comercialização ainda
não era viável.
A arquitectura básica de um DSP tinha sido criada, mas teria que esperar pela tecnologia dos semicondutores evoluı́sse. Se tivesse sido possı́vel a implementação de um
DSP utilizando poucos CIs (ou mesmo num único chip) muitas oportunidades comerciais
teriam surgido.
Ao longo dos anos 70 a tecnologia de integração de circuitos (circuitos integrados) foi-se
tornando cada vez mais complexa. Com as exigências da “máquina de guerra húngara” nos
Estados Unidos e a utilização gradual da electrónica nos produtos de consumo (como nos
electrodomésticos), houve um grande incentivo para a redução dos tamanhos e aumento
da velocidade de processamento destas tecnologias.
O processo tecnológico utilizado era N-MOS (N-channel Metal Oxide Semiconductor),
que trabalhava com uma fonte de alimentação de 5 Volt e podia ser implementado em 3
mı́cron, suportando densidades de 100 000 transı́stores.
Entre 1980 e 1982 ficaram disponı́veis quatro DSPs do tipo chip-único. O primeiro
DSP deste tipo é atribuı́do à empresa American Microsystems Inc. (AMI) com o seu
S2811.
O Intel 2920 e o Nippon Electric Company (NEC) mPD7720 também estavam disponı́veis pela mesma altura.
Ligeiramente mais tarde a Texas Instruments introduziu o seu TMS32010.
Os primeiros DSPs usavam uma arquitectura do tipo Harvard para separar a memória
de programa da memória de dados. Isto permitia o acesso simultâneo a uma palavra de
19
Controlador
Contador de
programa e
stack
Endereço
A11−A0
Instrução
ROM de
programa
(1536x16)
D15−D0
Crossover de dados/programa
Memória
de dados
Unidade de endereçamento
e aritmética da
memória de dados
Unidade Central de
Processamento
Registos
auxiliares
Figura 2.3 — Diagrama de blocos do processador de sinal TMS32010 (tal como apresentado em
[11]).
dados e uma de instrução. No processamento digital de sinal em tempo real é fundamental
o fluxo eficiente de dados, de e para o processador. A utilização de uma arquitectura do
tipo Harvard permitia o fluxo de dados sem interrupção da leitura das instruções.
Na figura 2.3 apresenta-se o diagrama de blocos do TMS32010. Neste diagrama pode
ver-se a separação das memórias de dados e de programa e a existência de um crossover entre as duas memórias. Por esta razão, a arquitectura do TMS32010 é geralmente
conhecida como sendo de Harvard modificada. A inclusão de um núcleo DSP num dispositivo com muitas das facilidades existentes num computador de utilização genérica, a
Texas Instruments tornou mais simples a programação deste género de dispositivos. O
TMS32010 possuı́a uma linguagem de programação assembly, ferramentas para análise e
um emulador semelhante ao dos micro-computadores. Naturalmente, seguiu-se o desenvolvimento de dispositivos mais rápidos, simuladores, debuggers, compiladores de C, etc.,
para utilização em DSPs (tal como aconteceu para os micro-computadores).
Claro que a história não acaba aqui. A miniaturização e a indústria dos computadores
pessoais (PCs) exigem cada vez maior densidade. Por sua vez, a cada vez maior densidade
de integração, permite o aumento do número de transı́stores num único chip.
20
Ano
Processador
Transı́stores
Relógio
Bus
de
Bus
(MHz)
endereços
dados
de
Registos
Cache
1978
8086
29 mil
4,75
20 bits
16 bits
16 bits
—
1982
80286
134 mil
6-25
24 bits
16 bits
16 bits
—
1985
80386
275 mil
16-40
24 bits
32 bits
32 bits
—
1989
80486
1,2 milhões
25-100
32 bits
32 bits
32 bits
1 nı́vel
1993
Pentium
3,1 milhões
60-233
32 bits
64 bits
32 bits
1 nı́vel
1995
Pentium Pro
5,5 milhões
150-200
32 bits
64 bits
32 bits
2 nı́veis
1997
Pentium II
7,5 milhões
233-450
36 bits
64 bits
32 bits
2 nı́veis
1999
Pentium III
15 milhões
450-1000
36 bits
64 bits
32 bits
2 nı́veis
2001
Pentium 4
100 milhões
1000-3000
36 bits
64 bits
32 bits
2 nı́veis
Tabela 2.1 — Algumas das caracterı́sticas presentes na famı́lia de processadores Intel.
O desenvolvimento da tecnologia CMOS possibilitou geometrias de integração da
ordem dos 0,5 microns. Esta tecnologia não só possibilitou o aumento do número de
transı́stores e portas num único chip, mas também a redução nos tempos de comutação
e consequente aumento da cadência do relógio e maior capacidade de throughput (entrada/saı́da) dos processadores.
Por exemplo, em 1994 conseguiam-se DSPs num chip-único com cerca de 4 000 000 de
transı́stores e tempos de execução da instrução de multiplicação inferiores a 40 ns, para
instruções de 32 bits em vı́rgula-flutuante, ou inferiores a 25 ns, para alguns dispositivos
possuindo instruções de vı́rgula-fixa de 16 bits [11].
Na tabela 2.1 podem ver-se algumas das caracterı́sticas presentes na famı́lia de processadores Intel.
2.2
Conceitos e palavras chave
Convém, antes de prosseguirmos, introduzir alguns conceitos e palavras chave, pois alguns
deles podem suscitar diferentes interpretações daquelas que vão ser usadas.
Os microcomputadores que vamos estudar utilizam circuitos electrónicos de lógica
binária. Consequentemente, a unidade de informação usada só consegue distinguir apenas
duas situações. A esta unidade de informação chama-se bit. Um conjunto de 8 bit (ou bits)
é designado por byte. Com um byte consegue-se distinguir 28 = 256 situações diferentes.
Por seu lado, 210 bytes = 1024 bytes = 1 Kbyte (Kilo byte).
Existem computadores de 8, 16, 32 e 64 bits. Isto quer dizer que estas máquinas
manipulam de cada vez a quantidade de informação contida em 8, 16, 32 e 64 bits,
respectivamente. Esta quantidade é designada por palavra do computador.
O conjunto de instruções de um micro-processador também pode ser designado por
21
instruction set. Uma instrução não é mais do que uma combinação de bits. Deve ser uma
combinação lógica para poder ser reconhecida como tal.
Um programa é um conjunto de instruções que fazem com que o computador desempenhe determinada tarefa. Qualquer programa é traduzido num conjunto de zeros e uns
(porquê?).
Existem muitas dificuldades associadas à criação de programas objecto (ou programas
em linguagem máquina binária). Enumerando apenas alguns:
• São difı́ceis de compreender;
• São longos e difı́ceis de escrever;
• São morosos a introduzir, uma vez que é introduzido um bit de cada vez;
• Não descrevem a tarefa para a qual foram escritos duma forma legı́vel para qualquer
utilizador humano;
• O programador comete erros que são difı́ceis de encontrar e corrigir.
Podemos melhorar algumas destas dificuldades se escrevermos as instruções em octal
(utilizando a base 8 para a representação dos valores, consulte-se por exemplo [14] para
mais pormenores acerca das bases para representação de valores) ou hexadecimal (utilizando a base 16) em vez de números binários, mas o micro-processador só “entende” zeros
e uns. Que fazer?
Uma alternativa consiste em escrever um programa que traduza estes números (octal ou hexadecimal) para números binários. Este programa é geralmente designado por
hexadecimal loader.
Mas, como é de esperar, um outro melhoramento significativo, é atribuir um nome
a cada uma das instruções. O nome da instrução é designado por mnemónica. Estas
mnemónicas devem descrever de alguma forma o que cada instrução representa.
Um programa escrito em linguagem assembly não é mais do que um programa escrito
utilizando mnemónicas. Pode traduzir-se à mão, mnemónica a mnemónica, sendo sem
dúvida uma tarefa mais fácil (e onde se cometem menos erros) do que qualquer uma das
técnicas anteriormente apresentadas. Pode-se ainda automatizar esta tradução, encarregando o próprio computador desta tarefa. Para tal existe um programa que designamos
por assembler (ou “assemblador”) que se encarrega desta tradução para um determinado
conjunto de mnemónicas.
Ao programa escrito em assembly designa-se por programa fonte (source program). Ao
programa gerado pelo assembler, que vai ser executado pelo , chama-se programa objecto
(object program).
Deste modo tornam-se evidentes as vantagens da utilização da linguagem assembly em
relação a qualquer um dos outros métodos vistos até agora.
22
Contudo, a grande desvantagem dos assemblers reside no facto destes possuı́rem as
suas próprias regras, que devem ser muito bem conhecidas por parte do programador e
às quais deve obedecer.
Adicionalmente, podem-se indicar as vantagens seguintes dos programas escritos em
assembly:
• Permitem ao utilizador a atribuição de nomes a:
– uma dada posição de memória;
– dispositivos de entrada/saı́da;
– a conjuntos de instruções (macros).
• Permitem a conversão de dados ou endereços entre vários sistemas de numeração
e binário, e ainda a conversão de caracteres na sua representação em ASCII ou
EBCDIC;
• Efectuar alguma aritmética como parte do processo de assembling (“assemblagem”);
• Indicar ao loader os locais de memória onde os programas ou dados devem ser
colocados;
• Permitir ao utilizador a atribuição de zonas de memória temporária de dados e
colocar dados fixos nas áreas de memória do programa;
• Fornecer a informação necessária para incluir programas standard de uma dada
biblioteca, ou programas escritos noutra altura, no programa corrente;
• Permitir ao utilizador o controlo do formato da listagem do programa bem como os
dispositivos de entrada e saı́da a utilizar.
Como desvantagens dos programas escritos em assembly pode indicar-se:
• O grande fosso que existe entre as instruções assembly (o conjunto de instruções que
o micro-processador consegue realizar) e as tarefas que o microcomputador deve
desempenhar;
• Tem que se conhecer muito bem um conjunto de instruções de um micro-processador
particular (aquele que usamos);
• Não é portável.
As duas primeiras desvantagens não são propriamente inerentes à programação em assembly, sendo comuns a qualquer micro-processador. Estas desvantagens podem ser minimizadas recorrendo a linguagens de programação de alto nı́vel.
23
As linguagens de alto nı́vel (linguagens orientadas ao procedimento ou ao objecto)
possibilitam superar algumas das limitações e dificuldades da linguagem assembly, uma
vez que permitem descrever tarefas de uma forma orientada ao problema e não ao computador. Normalmente uma instrução (ou mais correctamente, uma declaração [15]) duma
linguagem de alto nı́vel corresponde a várias em linguagem assembly. Existem programas especiais para a tradução de programas escritos nestas linguagens para linguagem
máquina. Estes programas são designados por compiladores. Como exemplos de linguagens de programação de alto nı́vel indicam-se:
• PASCAL;
• FORTRAN;
• C, C++;
• COBOL;
• BASIC, VISUAL BASIC;
• ALGOL;
• APL;
• PL/1;
• JAVA.
Algumas das vantagens da utilização das linguagens de programação de alto nı́vel são:
• Descrição mais conveniente da tarefa a desempenhar;
• Codificação mais eficiente do programa;
• Documentação mais fácil;
• Sintaxe standard;
• Independente de uma arquitectura particular;
• Portabilidade;
• Existência de bibliotecas de rotinas (pequenos programas) e mesmo programas completos.
Como desvantagens indica-se:
• Regras especiais;
24
• Exigem grande suporte de hardware e software;
• Programas ineficientes;
• Dificuldade na optimização do código para satisfazer aos requisitos de tempo e
memória;
• Impossibilidade de utilização de determinada potencialidade do computador em
causa.
Qual o nı́vel que devemos escolher? Obviamente que a resposta a esta questão não é
imediata. Contudo, deve ter-se como “regra de ouro” que o nı́vel depende da aplicação
que esteja a ser desenvolvida. Assim, a linguagem máquina foi posta totalmente de parte
uma vez que o baixo custo dos assemblers e compiladores, associado a todos os outros
factores descritos anteriormente, não justificam a sua utilização. A linguagem assembly
deve ser utilizada quando pretendemos pequenos programas, aplicações onde o tamanho de
memória seja limitado, aplicações em tempo real, capacidade de processamento de dados
muito limitada, aplicações que envolvam um elevado volume de chamadas a determinada
rotina para desempenhar determinada tarefa, ou em aplicações muito mais viradas para
entrada/saı́da ou controlo.
Por seu lado, as linguagens de alto nı́vel devem ser usadas em programas longos,
aplicações que requerem grande capacidade de memória, aplicações mais vocacionadas
para o cálculo do que para entrada/saı́da ou controlo, compatibilidade entre diferentes
versões da mesma aplicação para diferentes máquinas sem necessidade de reescrever o
código todo. Enfim, onde as vantagens da utilização deste tipo de linguagens, vistas
anteriormente, possam ser evidenciadas.
Existem actualmente no mercado linguagens que cada vez se voltam mais para a resolução do problema do que para a forma como vai ser implementada. Linguagens visuais
e orientadas por objectos ou ao objecto, bem como as ferramentas CASE (Computer Aided Software Engineering), estão a conquistar cada vez mais adeptos. Requerem contudo,
muito mais do que as linguagens orientadas ao procedimento, grandes capacidades de
memória, espaço em disco, rapidez de cálculo, etc.. Repare-se ainda que são adicionadas,
cada vez mais, novas capacidades aos programas, se tornam mais baratos o hardware e
software e os programadores mais caros, são lançados para o mercado compiladores mais
versáteis, potentes, com melhor aproveitamento dos recursos dos computadores e mais
baratos.
2.3
Descrição geral de um micro-processador
Neste secção tentaremos dar uma definição de micro-processador. Pode dizer-se que um
micro-processador é um dispositivo lógico programável, que lê instruções binárias dum
25
Memória
CPU
E/S
Figura 2.4 — Diagrama de blocos de uma máquina programável tı́pica.
dispositivo de armazenamento, chamado memória, aceita dados binários como entrada, e
processa esses dados de acordo com as instruções, fornecendo resultados de saı́da.
O esquema da figura 2.4 pode ser utilizado para representar uma máquina programável
tı́pica. O bloco designado por CPU (Central Processing Unit — Unidade de Processamento Central) é o responsável pelo controlo de tudo o que se passa na máquina. As
unidades principais são uma Unidade Aritmética e Lógica (Arithmetic and Logic Unit
— ALU), uma unidade de controlo e vários registos para armazenamento de dados temporários.
Quando todos os componentes são integrados numa única pastilha, chamamos-lhe
micro-processador. Os microcomputadores possuem CPU’s com esta caracterı́stica. O
CPU é a unidade inteligente do sistema, detendo o controlo em qualquer momento de
tudo o que se passa nos restantes blocos.
A memória é também indispensável em qualquer sistema de computador. É aqui que se
encontra armazenada toda a informação que o CPU vai processar, onde vão ser colocados
os resultados (finais ou parciais) de um processamento e onde se encontram armazenados
os programas.
A memória pode ser dividida em dois grandes grupos: ROM (Read Only Memory) —
é não volátil, não sendo possı́vel alterar o seu conteúdo por programação normal; e RAM
(Random Access Memory) — é volátil, podendo ler-se e escrever-se (alterar a informação
nela contida) aleatoriamente.
O bloco de Entrada/Saı́da (E/S) também conhecido por Input/Output (I/O) é constituı́do por circuitos que fazem a adaptação entre os sinais eléctricos do sistema de computador e os periféricos que fazem a comunicação com o mundo exterior. Estes circuitos
são designados por interfaces. Existem periféricos de Entrada, Saı́da e Entrada/Saı́da.
Na figura 2.5 pode ser visto um CPU tı́pico com todas as componentes descritas aqui e
ainda alguns periféricos ligados ao sistema por meio das interfaces. Nesta figura podemos
observar três linhas. Estas linhas representam, cada uma, um conjunto de sinais eléctricos,
todos eles com funções distintas no sistema de microcomputador. Cada linha representa
um barramento (bus). Os barramentos ou apresentados são:
• Dados;
• Endereços;
26
Periféricos
CPU
ALU
Registos
Ecran
Teclado
Controlo
Saı́da
Entrada
Memória
ROM
RAM
Dados
Endereços
Controlo
BUS
Figura 2.5 — Diagrama de blocos de um sistema de computador tı́pico.
• Controlo.
O bus de dados é constituı́do por várias linhas. É comum encontrar barramentos
de dados com 8, 16, 32 e 64 linhas. Geralmente este número de linhas surge associado à
dimensão da palavra do computador. Nele transita toda a informação que é movimentada
entre os diferentes blocos. Este barramento tem que ser bidireccional (porquê?).
O barramento de endereços é constituı́do por um conjunto de linhas cujo número varia
de acordo com o número de localizações distintas que um CPU consegue referenciar. Tal
como para o barramento de dados, este número varia de sistema para sistema. Para o
micro-processador é um Z80 o bus de endereços é de 16 bits. Consegue pois diferenciar
216 = 65536 = 64K localizações. É este bus que indica onde a informação se encontra e para onde deve ser dirigida, independentemente da informação em si e onde será
transportada (bus de dados).
O barramento de controlo tem por objectivo controlar a transferência de informação
entre os diferentes blocos. É pois responsável por indicar qual o sentido da informação.
Vamos ver agora como funciona, do ponto de vista conceptual, a troca de informação
entre dois dos blocos referidos.
Suponhamos que o CPU leu da memória uma instrução que interpretou e verificou
tratar-se duma ordem para transferir informação entre determinada célula de memória
e um registo particular interno ao CPU. A execução desta instrução obriga à definição
em simultâneo, através da utilização de sinais eléctricos com suporte fı́sico em linhas
independentes, de:
• Endereço da posição de memória a aceder (bus de endereços);
• Sinal de leitura da memória (bus de controlo);
• Informação a operar (bus de dados).
27
T1
T2
T3
T4
clk
A0-A15
Mreq
Rd
Wait
Wr
D0-D7
dados
Figura 2.6 — Diagrama temporal associado ao processo de leitura de dados da memória.
Para conseguir este objectivo o CPU:
• Coloca no bus de endereços o endereço efectivo da posição de memória (fornecido
pela instrução em execução);
• Gera o sinal na linha especı́fica para o efeito no bus de controlo;
• E recebe no bus de dados a informação a manipular.
Este procedimento encontra-se exemplificado, na figura 2.6, na forma de um diagrama
temporal.
Mais adiante serão estudados em detalhe alguns dos principais diagramas temporais
presentes num micro-processador tı́pico.
2.4
Estrutura interna de um CPU
Um CPU é constituı́do por componentes electrónicos, tais como:
• Portas lógicas (gates);
• Registos;
• Somadores;
28
A
P
B
Figura 2.7 — Processo tı́pico de transferência de informação entre dois registos.
• Memórias;
• Etc..
Estes componentes encontram-se ligados por barramentos internos ao CPU que transportam a informação no seu interior. Este transporte é controlado por portas. Tomemos
como exemplo a figura 2.7.
Os registos A e B estão ligados pela porta P que controla o fluxo de informação
entre os dois registos. Ao abrir-se esta porta, uma cópia da informação existente no
registo A é colocada no registo B, permanecendo o conteúdo do registo de partida (A)
inalterado. Durante o perı́odo de tempo em que a porta P se encontra aberta e se está a
efectuar a transferência de informação (no caso do Z80, poucas dezenas de nano-segundos),
o conteúdo do registo A deve permanecer inalterado (porquê?). Quando se pretende
desenvolver uma outra operação que vá alterar o contudo do registo A deve ter-se o
cuidado de fechar previamente a porta P (porquê?).
Para que estas transferências de informação dentro dos registos do CPU se façam de
uma forma controlada e sem interferência de outras operações que pretendam utilizar os
mesmos registos, utiliza-se um relógio. Este relógio especifı́ca a duração de cada operação
elementar dentro do CPU. Um perı́odo deste relógio designa-se por ciclo de régio (clock cycle). Alguns micro-processadores mais recentes possuem relógios de sincronização interna
com uma frequência superior a 3 GHz (3 000 000 000 ciclos por segundo!).
Vamos agora considerar que cada um dos componentes vistos faz parte de um bloco
lógico, ao qual vamos atribuir um nome de acordo com as funções que desempenha dentro
do CPU. Já vimos na figura 2.5 que existem dois blocos fundamentais (para além do array
de registos):
• Unidade Aritmética e Lógica (ALU);
• Unidade de Temporização e Controlo.
Associados a estas duas unidades encontra-se um conjunto de registos. Uns são visı́veis
ao programador (o programador controla o seu conteúdo); outros servem de apoio ao
funcionamento interno das unidades referidas. A figura 2.8 exemplifica a estrutura interna
do CPU Z80.
29
+5V
GND
Descodificador
de instruções
Registos
ALU
Controlo do bus
de dados
Bus de
endereços
Relógio
Bus de
dados
Pedidos
externos
Controlo do bus
de endereços
Flags
Reconhecimento
de pedidos
Temporização
e controlo
Registo de
instruções
Bus de
controlo
Figura 2.8 — Diagrama de blocos do CPU Z80.
2.4.1
Unidade Aritmética e Lógica (ALU)
Esta unidade é responsável pela execução de todas as instruções aritméticas e lógicas. A
figura 2.9 pretende representar uma parte desta unidade, por intermédio de um diagrama
de blocos, que serve para a execução das operações aritméticas básicas.
Esta unidade é capaz de executar operações de adição (ADD) na forma ADD val,
onde val representa o valor que queremos adicionar a um registo particular que designamos
por acumulador. Assim numa ALU com esta estrutura, as operações são efectuadas tendo
sempre como um dos operandos o registo acumulador. O resultado destas operações é
depositado de novo no acumulador. O outro operando é lido de um dos restantes registos
ou de uma posição de memória e colocado no registo interno M. A ALU limita-se a operar
os conteúdos do registo M e do registo acumulador.
A execução de uma instrução do tipo ADD M, envolve:
• A leitura do registo, ou posição de memória endereçada, para o registo M. A transferência de informação só será efectuada depois de aberta a porta P1;
• Os conteúdos dos registos M e acumulador são adicionados pelo somador;
• O resultado obtido é colocado no registo T, após a abertura da porta P2;
• O resultado da operação é colocado no registo acumulador, depois de primeiro fechar
a porta P2 e depois abrir a porta P3.
O registo T mostra-se essencial para controlar situações de corrida não controlada.
30
P2
Somador
T
M
P3
P1
Acumulador
Figura 2.9 — Diagrama de blocos de uma secção da ALU.
Uma vez que uma das entradas do somador é o registo acumulador, não podemos depositar
o resultado directo no acumulador pois estarı́amos a alterar uma das suas entradas.
2.4.2
Unidade de Temporização e Controlo
Como já referimos, esta unidade gera sinais que temporizam a activação das operações
elementares dentro do CPU, bem como sinais de controlo para o exterior do CPU que são
necessários ao funcionamento de todo o sistema de microcomputador.
É também aqui que são descodificadas as instruções, isto é, as instruções codificadas
em binário são interpretadas, são gerados os sinais necessários, encaminha-se a informação
para os locais correctos e faz-se com que cada circuito dentro do CPU actue no instante
preciso.
Existem basicamente duas técnicas para a implementação destas unidades: Hardwired; e Micro-programada.
Na primeira técnica a unidade de controlo é construı́da por portas lógicas (gates). As
gates abrem-se segundo uma sequência bem determinada, dependendo da instrução em
execução.
Na segunda técnica, utiliza-se basicamente uma memória cujo conteúdo de cada uma
das suas posições determina os sinais de controlo internos e externos ao CPU que vão
estar activos em cada ciclo de relógio.
A figura 2.10 pretende exemplificar uma estrutura do tipo micro-programada. Nesta
figura encontram-se representados:
• A memória com o micro-programa;
• Um registo de endereçamento da memória de micro-programa;
31
Registo de Instrução
P
Memória de
Microprograma
“próxima”
“busca”
Sinais de
temporização
e controlo
Registo de Endereçamento
Figura 2.10 — Diagrama de blocos simplificado de uma unidade de temporização e controlo
micro-programada.
• O registo de instrução, onde é colocada a instrução a ser executada.
Como funciona o circuito apresentado? Cada posição de memória determina os sinais
de controlo para um ciclo particular de relógio de uma instrução particular. O controlo
do desenvolvimento de uma instrução é efectuado à custa de uma sequência de ciclos
de relógio micro-programados, i.é., codificados em posições consecutivas da memória do
micro-programa. A mudança de estado (novo ciclo de relógio) é efectuada, numa instrução particular em execução, actuando no sinal que permite incrementar o registo de
endereçamento da memória do micro-programa. Para que qualquer instrução seja executada, o CPU tem que ir à memória do sistema fazer a busca (fetch) do seu código. Esta
operação é comum a todas as instruções, por isso pode ser codificada nas mesmas posições
de memória de micro-programa para todas as instruções.
Normalmente as posições iniciais da memória de micro-programa contêm informação
referente aos ciclos de relógio da fase de busca das instruções. Uma vez feita a busca da
instrução, o seu código é colocado no registo de instrução, que ao ser carregado no registo
de endereços (por actuação na porta P), determina a zona de memória de micro-programa
a ser seleccionada em seguida.
Exemplo 1 (Distribuição de micro-instruções) Suponhamos que um microcomputador, com uma unidade de controlo com uma estrutura idêntica à da figura 2.10, possui
um conjunto de N instruções. Suponhamos ainda que a fase de busca das instruções
é constituı́da por quatro ciclos de relógio, o mesmo acontecendo à fase de execução das
mesmas. A figura 2.11 exemplifica, em face destes dados, como se distribuiriam as microinstruções referentes a cada instrução na memória de micro-programa.
O número de posições de memória de micro-programa está pois relacionado com o
número de instruções que determinado CPU possui e com o número de ciclos de relógio
de cada uma delas.
32
0
Busca
4 Instrução 1
8
Instrução 2
12 ...
4N
Instrução N
Figura 2.11 — Exemplo de micro-programa.
O número de bits de cada posição dessa memória é função do número de sinais de
controlo necessários aos circuitos internos do CPU e dos sinais de controlo de todo o
sistema de microcomputador que saem do CPU.
Exemplo 2 (Sinais afectos a um CPU) Como exemplo dos sinais afectos a uma pastilha de CPU, consideremos o caso do Z80, representado na figura 2.12. Este circuito
integrado é vendido na forma de uma pastilha de 40 pinos, com a seguinte distribuição:
• 8 linhas bus de dados,
• 16 linhas bus de endereços;
• 13 linhas de controlo do CPU e sistema de microcomputador;
• 1 linha de relógio;
• 2 linhas para alimentação.
Qual a melhor técnica Hard-wired ou Micro-programa? Esta pergunta não possui
resposta simples, devendo ter-se em linha de conta, entre outros, os seguintes aspectos:
• Rapidez de execução (número de ciclos de relógio por instrução);
• Complexidade dos circuitos;
• Versatilidade de implementação de novas instruções;
• Custos da memória versus custos de desenho de circuitos dedicados;
• Etc..
Para informação complementar consulte-se, por exemplo, [12].
33
Controlo do sistema
Controlo do CPU
Controlo dos barramentos
Alimentação
M1
Mreq
Iorq
Rd
Wr
Rfsh
Z80
CPU
A0
A1
..
.
Halt
Wait
Int
NMI
Reset
Busreq
Busack
A15
D0
D1
..
.
Clk
+5V
GND
Bus de endereços
Bus de dados
D7
Figura 2.12 — Esquema de ligações externas do Z80.
2.5
Arquitectura de um micro-processador
Entende-se por arquitectura de um microcomputador, não a forma como um circuito
particular, que realiza determinada função, é implementado do ponto de vista de hardware,
mas sim o conjunto de estruturas e formas de as manipular que o programador tem ao
seu dispor. Assim, fazem parte da arquitectura:
• Todos os registos visı́veis ao programador;
• O conjunto de todas as instruções.
Desta forma, pode dizer-se que, de uma forma simplista, quanto maior for o número de
registos internos ao CPU visı́veis ao programador e o número de operações que se possam
executar sobre eles, mais versátil e “poderoso” será esse CPU.
Nesta fase convém tentar deixar bem esclarecidas as seguintes questões. O que é uma
instrução? O que é um programa? Como já vimos, uma instrução é uma combinação
binária armazenada em memória que o CPU vai ler para:
• Descodificar;
• Interpretar;
• E gerar uma sequência de operações elementares cuja acção global é a preestabelecida pelo seu fabricante.
34
Código da operação
Endereço do opereando
31
8 7
0
Um programa é uma sequência de instruções armazenadas em memória por ordem
crescente dos seus endereços. A sequência de execução destas instruções, em princı́pio,
será a da ordem de armazenamento em memória.
Existem no entanto instruções que permitem alterar a ordem dessa execução, ou seja,
permitem alterar o fluxo da sequência “normal” da execução das instruções. Umas incondicionalmente (salto incondicional), outras quando se verificarem determinadas condições
(salto condicional) e outras ainda que podem alterar esse fluxo se ocorrerem determinadas situações internas ou externas ao CPU, mas cujo instante não se pode prever
(interrupções).
Visto isto, torna-se evidente a necessidade de aprofundar um pouco mais os conhecimentos acerca do modo como um CPU executa uma instrução e que ferramentas (registos)
necessita ter para processar uma determinada sequência de instruções (programa).
As instruções são códigos binários referindo:
• O código da própria instrução;
• O(s) operando(s) sobre o(s) qual(ais) vai actuar.
Uma instrução é constituı́da pelo código de operação e pelo(s) operando(s). O seu
formato varia de máquina para máquina. Na sua forma mais simples uma instrução seria
codificada numa única palavra de computador, reservando-se um determinado número de
bits para o código de operação e os restantes para a referência do operando. A figura 2.5
exemplifica este conceito.
Exemplo 3 (Formato das instruções) Uma máquina que utilizasse somente este formato de instrução poderia ter no máximo 256 instruções. Porquê?
Dica: quantos bits são usados para o código de operação?
O Z80 possui um formato variável para as instruções, existindo códigos de operação
de 1 byte a 4 bytes.
Como é que o CPU executa determinada instrução? O desenvolvimento do processamento de uma instrução pelo CPU pode ser subdividido em duas fases:
1. Busca (fetch);
2. Execução.
Estas fases são explicadas detalhadamente no próximo ponto.
35
T1
T2
T3
T4
clk
A0-A15
PC
refresh
Mreq
Rd
Wait
M1
D0-D7
dados
Rfsh
Figura 2.13 — Diagrama temporal correspondente à fase de fetch.
2.5.1
Formato e processamento das instruções
Na fase de fetch, o CPU coloca no bus de endereços o endereço da posição de memória
que contém a próxima instrução a ser executada e gera um sinal de controlo para leitura
da memória, figura 2.13.
Convém destacar as seguintes fases principais:
• É colocada no bus de dados a combinação binária que corresponde à instrução a
processar;
• O CPU recolhe essa informação e coloca-a no registo de instrução (Instruction Register);
• Esta informação vai ser agora descodificada e posteriormente executada. No caso
do Z80, esta fase também é conhecida por ciclo M1 ou ciclo de máquina (Machine
Cycle ou Machine Cycle One).
Esta fase de busca de instrução é igual para todas as instruções, o mesmo não acontece
com a fase de execução, uma vez que as instruções são diferentes umas das outras. Existem
contudo grupos de instruções idênticas em que o procedimento do CPU na fase de execução
é semelhante, diferindo apenas nos operandos a processar. Mais tarde voltaremos a este
assunto.
Já se viu que um computador para executar (correr) um programa, necessita de processar uma sequência de instruções. Por este motivo é necessário que o CPU tenha um
36
registo interno que tome conta da evolução desta sequência, armazenando em cada momento o endereço da próxima instrução a ser executada. Este registo interno designa-se
por contador de programa (Program Counter). É o conteúdo deste registo que é colocado
no bus de endereços de cada vez que o CPU faz o fetch de uma instrução.
Uma das operações elementares do CPU é a actualização do PC. Porquê? Porque é
que o PC do Z80 é um registo de 16 bits? Dica: Por quantas linhas é constituı́do o bus
de endereços? Quantos endereços de memória consegue o Z80 distinguir?
2.5.2
Registos internos de uma arquitectura básica
Já vimos que o Program Counter é um registo interno ao CPU essencial ao seu funcionamento. Para a definição de uma arquitectura elementar ficar completa, tornam-se
necessários mais dois registos, um acumulador e um registo de flags.
O acumulador terá como finalidade o armazenamento de um dos operandos e o resultado da instrução a executar. O outro operando, caso exista, poderá encontrar-se numa
posição de memória.
O registo de flags serve para memorizar um conjunto de condições relevantes que
acontecem durante a execução de algumas instruções. Alguns exemplos de condições
memorizadas neste tipo de registo são:
• O facto de ocorrer um resultado nulo ao ser processada uma instrução qualquer
aritmética;
• O facto de ocorrer um resultado negativo ao ser processada uma instrução qualquer
aritmética;
• O facto do número nele armazenado ser positivo ou negativo;
• Etc..
O tipo de conhecimento que se pretende memorizar num registo de flags é um conjunto de situações ligado/desligado (on/off). Quantos bits são necessários por situação?
Quantas situações distintas se conseguem memorizar com um registo de 8 bits?
Podem ser usadas instruções para verificar a ocorrência ou não destas situações, uma
vez que pode ser necessário tomar uma decisão acerca de “qual a próxima instrução a
executar”.
As instruções de salto condicional fazem parte deste grupo.
A figura 2.14 representa esquematicamente uma arquitectura do tipo enunciado.
Esta arquitectura “mı́nima” seria constituı́da pelos registos:
• PC (Program Counter) de 16 bits;
• A (Acumulador) de 8 bits;
37
PC
A
F
F
P C S Z
Figura 2.14 — Diagrama de blocos de um conjunto mı́nimo de registos.
Cy
Cy
76543210
76543210
Figura 2.15 — Instruções de rotação para a esquerda e para a direita (ver texto).
• F (Flags) de 8 bits. Só 4 bits seriam utilizados para sinalizar as situações de:
– Z (Zero). Resultado nulo (zero) da última operação;
– S (Sinal). Sinal do operando armazenado no registo A;
– C (Carry). Houve carry — transporte ou “vai um” — ou borrow — empréstimo
ou “falta um” — no resultado da última operação executada;
– P (Paridade). O número de bits a 1 do operando armazenado no registo A é
par.
Nesta arquitectura também seria necessário definir um instruction set básico que permitisse o funcionamento do microcomputador que nele assentasse. A tabela 2.2 mostra o
instruction set proposto.
Esta arquitectura é bastante simples. As mnemónicas da tabela 2.2 são idênticas
(sendo mesmo um subconjunto) às que iremos estudar do Z80. Esta escolha foi feita de
forma a não gerar confusão no futuro quando estudarmos as do Z80. Na figura 2.15 pode
ver-se a forma como as instruções de rotação são executadas. Por exemplo, na rotação
para a direita o bit número 7 passa para a posição do bit 6, o bit 6 passa para o bit 5, e
assim sucessivamente até ao bit 1 que passa para o bit 0. O bit 0 passa a ser o bit da flag
de carry e o bit da flag de carry passa a ser o bit 7.
Deve salientar-se que de fabricante para fabricante, de máquina para máquina, as
mnemónicas utilizadas para corresponder a determinado tipo de operação podem variar.
Com esta arquitectura poder-se-iam executar programas! Veja-se o seguinte exemplo.
Exemplo 4 (Adição de dois valores) Escreva um programa, com base no instruction
set apresentado na tabela 2.2, que adicione o conteúdo da posição de memória cujo endereço é END1 ao conteúdo da posição de memória cujo endereço é END2 e coloque o
38
Instruções de Transferência
Instrução
Operandos
Comentários
LD
A,n
;A ← n
LD
A,(nn)
;A ← (nn)
LD
(nn),A
;(nn) ← A
Instruções aritméticas e lógicas
Instrução
Operandos
Comentários
ADD
A,n
;A ← n
ADC
A,(nn)
;A ← A + n + Carry
SBC
A,(nn)
;A ← A − n − Carry
AND
(nn)
;A ← A E (nn)
OR
(nn)
;A ← A OU (nn)
XOR
(nn)
;A ← A OU EXCLUSIVO (nn)
CP
(nn)
;A − (nn)
INC
A
;A ← A + 1
DEC
A
;A ← A − 1
Instruções sobre a flag de carry
Instrução
Comentários
SCF
;Coloca flag de carry a 1
CCF
;Faz o complemento da flag de carry
Instruções de rotação
Instrução
Operandos
RLA
Comentários
;Rotação para a esquerda do conteúdo do
acumulador
RRA
;Rotação para a direita do conteúdo do acumulador
Instruções de salto
Instrução
Operandos
Comentários
JP
nn
;PC ← nn
JP
cc,nn
;PC ← nn, se a condição cc for verdadeira
Tabela 2.2 — Conjunto de instruções mı́nimo para a arquitectura proposta (ver texto).
39
INÍCIO
A ← (END1)
B ← (END2)
C←A+B
(RES1) ← C
FIM
Figura 2.16 — Diagrama de fluxo para o algoritmo do exemplo 4.
resultado na posição de memória cujo endereço é RES1. A seguinte listagem mostra-nos
a definição de variáveis para o problema dado:
p a r c e l a 1 ( END1 ) → A
p a r c e l a 2 ( END2 ) → B
r e s u l t a d o ( RES1 )
→ C.
Com base nesta listagem e no diagrama de fluxo apresentado na figura 2.16 é imediata
a seguinte codificação:
LD
A, ( END1)
; l ê a p r i m e i r a p a r c e l a
SCF
; c o l o c a f l a g de c a r r y a
CCF
; zero
ADC
A, ( END2)
; a d i c i o n a a 2 a p a r c e l a à 1 a
LD
(RES1 ) ,A
; c o l o c a o r e s u l t a d o na p o s i ç ã o
; de memória p r e t e n d i d a .
2.6
Arquitectura do Z80
O Z80 é um Micro-processador com as seguinte caracterı́sticas:
• 8 bits de bus de dados;
• 16 bits de bus de endereços.
Para além das instruções apresentadas na tabela 2.2, o Z80 possui um conjunto muito
mais poderoso que pode ser consultado, por exemplo, em [5, 10] e [6] (este último disponı́vel para download). Existem vários tipos de tabelas com o conjunto completo de
40
A
F
A’
F’
B
C
B’
C’
D
E
D’
E’
H
L
H’
L’
I
R
IX
IY
PC
SP
Figura 2.17 — Registos do Z80 visı́veis ao programador.
instruções do Z80 que incluem uma explicação mais ou menos detalhada do seu funcionamento, incluindo a forma como o registo de flags é afectado.
Os registos internos visı́veis são 22 sendo por 18 de 8 bits e 4 de 16 bits, figura 2.17.
Os pares de registo AF, BC, DE, HL podem funcionar como registos de 16 bits em
determinadas situações. O mesmo acontece com os registos auxiliares A0 F0 , B0 C0 , D0 E0 e
H 0 L0 .
O registo A (acumulador) é um registo preferencial, pois existe um muito maior número
de instruções que operam sobre este registo, nomeadamente as aritméticas e lógicas. Existem arquitecturas onde não há um registo preferencial. O programador é que decide que
registo usar, uma vez que qualquer registo pode ser usado em condições em tudo idênticas
entre eles.
Os registos I e R só podem ser acedidos individualmente. Os registos IX e IY são
designados por Index Registers. São idênticos em tudo. A sua finalidade consiste em
aceder a dados na memória. A figura 2.18 exemplifica a utilização destes registos para
este fim. Também podem ser vistos como contendo um dado de 16 bits, i.é., registos
normais de 16 bits, nas operações aritméticas de 16 bits.
Só falta falar do registo SP (Stack Pointer) que, tal como o seu nome indica, é um
ponteiro para a memória. O seu conteúdo não é mais que um endereço de memória.
Este registo controla uma estrutura definida na memória designada por stack, figura 2.19.
A stack é uma estrutura de memorização onde a última informação lá armazenada é a
primeira a sair. Este tipo de estrutura é do tipo LIFO (Last In First Out). O tipo de
memória a utilizar para implementar esta estrutura é a RAM (porquê?). Este tipo de
estrutura funciona como uma pilha de livros colocados no interior de uma caixa; quando se
41
IX ou IY
Endereço
Opcode
Deslocamento
Memória
+
E = (IX ou IY)+ deslocamento
‘E’ designa o endereço efectivo calculado
Figura 2.18 — Exemplificação da utilização dos index registers.
SP
Memória
Endereço
D
A
T
Sentido dos
endereços
crescentes
U
Figura 2.19 — Exemplificação da utilização da stack.
42
A0−An
2n × 1 2 n × 1 2 n × 1 2 n × 1 2 n × 1 2 n × 1 2 n × 1 2 n × 1
D0
D1
D2
D3
D4
D5
D6
D7
Figura 2.20 — Exemplo de memória organizada em bits.
pretender retirar algum desses livros, o primeiro a sair será o último lá colocado. A função
do SP é conter o endereço da posição de memória que contém o último valor colocado
nela. As instruções do Z80 que manipulam a stack, actualizam automaticamente o SP.
2.7
Memória
A memória é a unidade de armazenamento de informação de um sistema de computador.
É nela que o microcomputador armazena os programas, os dados e os resultados do
processamento. Desta forma torna-se necessária a existência de grandes capacidades de
memória.
Torna-se pois necessário distinguir as localizações de memória umas das outras para
se poder identificar quais as que contêm uma particular informação.
A forma de o fazer é associar a cada localização de memória uma combinação binária a
que chamamos de endereço. A informação a memorizar em cada endereço é codificada com
um determinado número de bits. Então, qual será o número de bits que cada localização
de memória contém? Este número não é fixo, sendo normalmente nos microcomputadores,
múltiplo de 8 bits.
No caso do Z80, e uma vez que o seu bus de dados é de 8 bits, é natural que a memória
esteja organizada em bytes. Existem outros microcomputadores com comprimentos de
palavra de 16, 32 e mesmo 64 bits que mantêm a memória organizada em bytes. Consultese, por exemplo, [4, 13] onde se pode ver esta (e outras) diferenças dentro da mesma famı́lia
de micro-processadores.
A memória é geralmente vendida em circuitos integrados organizados de uma determinada maneira, a qual varia de fabricante para fabricante. É possı́vel encontrar pastilhas
de memória organizada em bits, figura 2.20, e em bytes, figura 2.21.
As pastilhas de memória utilizadas nos sistemas de microcomputador baseados no Z80
estão normalmente organizadas em 32K × 8bits. Um exemplo de aplicação deste tipo de
memórias pode ser visto na figura 2.21. Cada pastilha possui:
• 15 linhas para endereçamento interno à própria pastilha;
43
A15
A0−A14
CS
CS
32K x 8 bits
32K x 8 bits
Dados
Figura 2.21 — Exemplo de memória organizada em bytes.
• 8 linhas para acesso à informação (bus de dados);
• 1 linha para selecção de pastilha;
• Linhas para alimentação e controlo (por exemplo, escrita e leitura).
Se pretendêssemos utilizar pastilhas deste tipo num sistema de microcomputador com
o Z80 podı́amos utilizar um esquema de implementação idêntico ao da figura 2.21.
Nas figuras 2.22 e 2.23 podem ver-se os diagramas temporais dos sinais afectos às
fases de leitura e escrita de dados na memória. Note-se que a diferença de velocidade de
funcionamento do circuito do CPU e das pastilhas de memória pode ser tão elevada que
seja necessária a introdução de ciclos de espera dos dados (sinal Wait).
44
T1
T2
T3
T4
clk
A0-A15
Mreq
Rd
Wait
Wr
D0-D7
dados
Figura 2.22 — Diagrama temporal dos sinais afectos ao ciclo de leitura da memória.
T1
T2
T3
T4
clk
A0-A15
Mreq
Rd
Wait
Wr
D0-D7
dados
Figura 2.23 — Diagrama temporal dos sinais afectos ao ciclo de escrita na memória.
45
46
Capı́tulo 3
Ferramentas para programação de
um micro-processador
3.1
Introdução
Até aqui a vimos estrutura básica de um microcomputador e a arquitectura interna básica
de um CPU. Agora vamos estudar as ferramentas que permitem a programação do microcomputador e o modo de utilização dessas ferramentas.
Só a tı́tulo de exemplo e antes de prosseguirmos:
• Porque é que existe a linguagem máquina e quais são os problemas (desvantagens)
da sua utilização?
• Porque é que surge a linguagem assembly e quais as sua vantagens e desvantagens?
• Como é que se chama a um programa escrito em Assembly?
• Depois de escrito este programa, porque é que se utiliza um Assembler? Qual o
código por ele gerado?
3.2
Formato de programação
Um programa escrito em linguagem assembly é composto por uma série de linhas, sendo
cada linha dividida em quatro partes ou campos, como se mostra no código seguinte:
Label
LOOP:
Mnemónica Operandos
LD
A, ( HL)
Comentários
; c o l o c a no r e g i s t o A o c o n t e ú d o
; da p o s i ç ã o de memória apontada
; por HL
LD
C, 0FCH
;H s i g n i f i c a que o v a l o r e s t á
; e x p r e s s o em h e x a d e c i m a l
47
LD
( 2 4 0 0 ) ,HL
; c o l o c a nas p o s i ç õ e s de memória
; 2 4 0 0 e 2 4 0 1 o c o n t e ú d o do par
; de r e g i s t o s de 1 6 b i t s HL.
A colocação de etiquetas permite ao programador referir-se a determinadas secções
ou instruções particulares de um programa sem se preocupar com os seus endereços de
memória, ficando os cálculos a cargo do assembler.
3.3
Pseudo-instruções
As pseudo-instruções são directivas para o assembler. Levam a que este desempenhe
determinadas tarefas durante o processo de assemblagem. Não são instruções executáveis
(ou tão pouco executadas) pelo CPU, como ilustra o seguinte exemplo utilizando a pseudoinstrução ORG:
Linha Endereço Cód . o b j e c t o Mnemónica Operandos
1
ORG
B000H
Comentários
2
B000
3E0F
LD
A, 0FH
;A ← 0F
3
B002
0165FA
LD
BC, 0 FA65H ;BC ← FA65
4
B005
C5
PUSH
BC
Na linguagem assembly do Z80 as pseudo-instruções mais utilizadas são as seguintes:
Label
VAL
Mnemónica Operando Comentários
DEFB
0F7H
;DEFB ( DEFine Byte ) . ‘ VAL’ p a s s a a
; v a l e r F7H
ADDR1
DEFW
0F507H
;DEFW ( DEFine Word ) . ‘ADDR1’ contém
; o e n d e r e ç o
MSG
DEFM
”OLA! ”
;DEFM ( DEFine Message ) . ‘MSG’ d e f i n e
; a mensagem
BUFFER
DEFS
256
;DEFS ( DEFine S t o r a g e ) . Reserva 2 5 6
; p o s i ç õ e s de memória
PUCOD
EQU
END
3.4
0A5H
;EQU ( EQUate ) . CODigo de Power Up
; F i n a l do programa .
Desenvolvimento de um programa em assembly
O desenvolvimento de um programa em assembly passa por várias fases. Na primeira fase,
que designamos por conceptual, o programador deve analisar o problema e determinar as
tarefas que o micro-processador deverá executar. Deve ser realizado um algoritmo ou um
diagrama de fluxo.
48
Na fase seguinte deve passar-se à escrita do programa em linguagem assembly, seleccionando-se da forma mais conveniente a correspondência entre os registos disponı́veis e as
variáveis a utilizar pelo programa. Deve editar-se o programa.
Terminada a fase de edição do programa fonte (source program), este deverá ser “assemblado”. Este perı́odo designa-se por assembly time. É aqui que são detectados alguns
erros como por exemplo:
• Instrução ilegal;
• Sı́mbolo não definido;
• Expressão fora da gama;
• Sı́mbolo duplicado;
• Etiqueta (label) ilegal;
• Falta de apóstrofo;
• Falta de constante.
Depois de detectados e corrigidos todos os erros identificados durante esta fase, o programa
poderá ser então executado e testado.
A fase de execução é designada por run time ou execution time. Nesta fase, que também
pode ser designada por fase de debugging, o programador poderá executar o programa
instrução a instrução e ir verificando os conteúdos dos vários registos ou posições de
memória relevantes. Poderá ainda utilizar breakpoints para parar a execução do programa
no ponto pretendido. É nesta fase que se podem corrigir erros de concepção do programa.
Na figura 3.1 podem ver-se, sob a forma de um diagrama de fluxo, as fases de desenvolvimento de um programa escrito em assembly.
3.5
Assembler
O assembler é um programa que aceita por entrada um programa escrito em linguagem
assembly e que produz o seu equivalente em linguagem máquina, gerando ainda as informações necessárias para o loader.
Num programa as etiquetas (labels) podem aparecer nas instruções antes de terem sido
definidas. Isto implica a necessidade de existência de assemblers de duas passagens. Na
primeira passagem são identificados os sı́mbolos e/ou labels e na segunda passagem são
geradas as instruções e os endereços.
Mais concretamente um assembler deverá realizar:
• A geração de instruções;
49
Sim
Escrita do programa (Editor)
Erros de sintaxe?
Sim
“Assemblagem” (Assembler)
Não
Execução e teste do programa
Erros conceptuais?
Não
Programa operacional
Figura 3.1 — Diagrama de fluxo das fases de desenvolvimento de um programa escrito em
assembly.
• Gerar o código máquina correspondente às mnemónicas utilizadas;
• Determinar o valor de cada sı́mbolo;
• Processar as constantes;
• Atribuir endereços;
• O processamento das pseudo-instruções.
Estas tarefas podem ser agrupadas em duas passagens distribuı́das do seguinte modo.
Na primeira passagem deve-se:
• Definir os sı́mbolos e constantes;
• Determinar o comprimento das instruções em código máquina;
• Actualizar o contador de referência;
• “Lembrar” dos valores dos sı́mbolos até à segunda passagem;
• Processar algumas pseudo-instruções;
• “Lembrar” das constantes.
Durante a segunda passagem deve-se:
50
Memória
Programa principal
Programa
principal
Subrotina A
Subrotina A
Loader
Subrotina B
Subrotina B
Subrotina C
Subrotina C
Figura 3.2 — Exemplo de funcionamento de um loader (ver texto).
• Gerar o programa objecto;
• Buscar os valores dos sı́mbolos;
• Gerar as instruções;
• Gerar dados;
• Processar as pseudo-instruções restantes.
Após a geração do programa objecto pelo assembler, este deve ser colocado em memória
para poder ser executado. Esta tarefa é do loader.
3.6
Loaders
O loader é um programa que coloca na memória o programa objecto, prepara-o para
ser executado e inicı́a a sua execução transferindo o controlo para ele. Em programas de
alguma complexidade, ou em situações de tarefas idênticas que tenham que ser executadas
em vários pontos do programa, é conveniente a divisão do programa em vários módulos.
Temos desta forma um programa principal que utilizará vários sub-programas ou subrotinas. Veja-se a figura 3.2.
Uma sub-rotina não passa de um conjunto de instruções que executa uma tarefa bem
definida.
Para que seja simples a utilização de subrotinas por parte do programador é necessário
que possam ser referenciadas simbolicamente, sem que o programador tenha que se preocupar com os endereços das várias partes do programa.
51
Para que estes objectivos possam ser atingidos é necessário que as subrotinas sejam
traduzidas numa forma objecto de modo a que o loader as possa colocar em posições
arbitrárias de memória e de tal forma que não exista sobreposição entre o programa
principal e as diversas subrotinas. Diz-se que os diferentes módulos do programa deverão
estar na forma relocatável. Um assembler que produza código relocatável deverá:
• Produzir o programa objecto;
• Indicar todos os outros módulos que são referenciados pelo módulo assemblado;
• Determinar todos os locais no programa que necessitem de ser alterados no caso
deste módulo ser colocado numa posição arbitrária de memória.
Depois de todos os módulos terem sido assemblados, competirá ao loader:
• Juntá-los;
• Ajustar as referências entre eles;
• Ajustar os locais dos programas que dependem dos endereços onde eles irão ser
colocados;
• Carregar a memória com todos os módulos (para que o programa possa ser executado).
Um loader relocatável deverá realizar as funções de:
• Atribuição de espaço de memória para os programas (allocation);
• Resolução das referências simbólicas entre os vários módulos (linking);
• Ajuste das posições dependentes dos endereços, tais como endereços de constantes,
de tal forma que corresponda ao espaço atribuı́do (relocation);
• Colocar fisicamente na memória as instruções em código máquina e os dados (loading) .
3.7
Macros
Para que o programador não tenha que repetir partes idênticas do seu código, existe a
facilidade de processamento de macros.
Esta facilidade permite a definição de uma abreviatura a que corresponde uma parte
do programa, permitindo a sua utilização sempre que esse conjunto de instruções seja
utilizado. No caso do assembler do Z80, uma macro define-se da seguinte forma:
52
<nome> MACRO [#<P0>, #<P1>, #<P2> , · · ·, #<Pn>]
i n s t r u ç ã o 1
i n s t r u ç ã o 2
···
i n s t r u ç ã o n
ENDM
Aqui, “<nome>” indica o nome pelo qual a macro é identificada, “[#<P 0>, #<P 1>
, #<P 2>, ..., #<P n>]” indicam os parâmetros (que podem existir ou não), “instrução
i” indica a instrução que deverá ser executada e “ENDM” indica o final de definição da
macro.
O código
MOV
MACRO #P0 , # P1
LD
#P0 , #P1
ENDM
define uma macro para utilizar a mnemónica MOV (do Intel 8086) em vez da mnemónica
LD (do Z80) podemos definir a macro.
A grande questão que se levanta neste momento é a seguinte: quando utilizar macros
e quando utilizar subrotinas? Antes de optarmos por qualquer uma das duas devemos ter
em atenção, entre outros aspectos, os seguintes:
• Quando utilizamos uma sub-rotina temos um bloco de código colocado numa determinada área da memória, sendo esse bloco executado sempre que a sub-rotina for
chamada;
• Quando utilizamos macros uma dada mnemónica (mais precisamente, um identificador) é expandida num dado bloco de instruções, que constituem a definição
da macro, sendo esse bloco repetido no programa sempre que seja encontrada a
mnemónica que identifica a macro;
• Tal como visto no exemplo anterior, a utilização das macros permite ao utilizador redefinir as mnemónicas já existentes atribuindo-lhes um outro nome, ou então
construir as novas instruções formadas à custa das instruções já existentes;
• Uma sub-rotina envolve um desvio no fluxo “normal” do programa para depois se
regressar ao ponto de onde essa sub-rotina foi invocada, logo torna-se necessário
guardar o endereço de partida;
• As subrotinas poupam espaço em memória uma vez que só existe uma única cópia
destas em memória.
53
54
Capı́tulo 4
Estudo do conjunto de instruções
4.1
Introdução
Neste capı́tulo estudaremos em detalhe um conjunto genérico de instruções, vendo a forma
como a sua execução afecta o registo de flags. Adicionalmente, estudaremos também os diferentes modos de endereçamento, isto é, as diferentes formas de obtenção de operando(s).
Na parte final estudaremos com pormenor a área de stack.
4.2
Formato simbólico das instruções
Num micro-processador existem vários tipos de instruções. Podemos distinguir entre
instruções de transferência de dados, aritméticas, lógicas, controlo do programa, rotação
e deslocamento, controlo do CPU e um grupo de propósito geral.
O comprimento da palavra do micro-processador vai condicionar o número de bytes
do código de operação (operation code — opcode) de cada instrução.
Será de toda a conveniência que logo após o acesso à memória para leitura da instrução (fetch) esta seja imediatamente descodificada, isto é, o micro-processador “saiba”
de que instrução se trata e tudo aquilo deve fazer com ela. Por exemplo, para um microprocessador de 8 bits o primeiro byte caracterizará completamente a instrução.
Consoante o modo como são acedidos os dados a processar, ou seja, o tipo de endereçamento, a instrução poderá ser composta por mais bytes.
No caso do Z80 os opcodes podem ter de 1 a 4 bytes de comprimento.
4.3
Flags
Consoante o tipo de instrução, o seu resultado pode afectar ou não o registo de flags,
permitindo assim ao programador testar os resultados e tomar as decisões convenientes.
55
No caso do Z80 o registo de flags (F e F’) contém 6 bits que são colocados a ‘1’ ou a
‘0’, sendo o seu significado o seguinte:
• Flag de carry (C) — é colocada a ‘1’ se ocorrer um carry (um “transporte” ou
“vai um”) numa instrução de soma ou um borrow (“falta um”) numa instrução de
subtracção; caso contrário é colocada a ‘0’. As instruções de rotação e deslocamento
também afectam esta flag de acordo com o bit que aı́ é armazenado como resultado
da rotação ou deslocamento. As instruções lógicas colocam esta flag sempre a ‘0’.
• Flag de adição/subtracção (N) — é utilizada nas operações que envolvem uma adição
ou subtracção de números representados em Binary Coded Decimal (BCD), permitindo distinguir estes dois tipos de operações. Nas adições é colocada a ‘0’, nas
subtracções é colocada a ‘1’.
• Flag de paridade/overflow (P/V) — o seu significado varia de acordo com o tipo de
operação que se está a realizar. Se foi realizada uma operação aritmética, detecta a
ocorrência de uma situação de overflow (V=1). Nas operações lógicas ela indica se
o número de bits a ‘1’ do resultado é par (P=1) ou ı́mpar (P=0).
• Flag de half carry ou auxiliary carry (H ou Ac) — numa instrução aritmética, esta
flag será colocada a ‘1’ caso exista carry ou borrow entre os bits 3 e 4.
• Flag de zero (Z) — esta flag é colocada a ‘1’ se o resultado da operação for zero.
As instruções aritméticas, lógicas e de comparação são exemplos de instruções que
afectam esta flag.
• Flag de sinal (S) — a flag de sinal armazena o estado do bit mais significativo do
acumulador (bit 7).
4.4
Tipos de endereçamento
Os diferentes tipos de endereçamento permitem-nos obter o endereço efectivo da instrução. Numa instrução de manipulação de informação será o endereço do dado que irá
ser processado. Numa instrução de salto será o endereço da próxima instrução ao ser
processada.
Podemos dividir os modos de endereçamento em dois tipos: modo directo; e modo
indirecto. No modo directo o endereço é retirado directamente da instrução ou calculado
combinando um valor existente na instrução com o conteúdo de um registo.
No modo indirecto o endereço calculado é o endereço de uma posição de memória que
contém o endereço efectivo (final).
Existem duas formas de calcular o endereço efectivo: o seu valor é indicado na própria
instrução; ou na instrução é indicado o registo que o contém.
56
Registos
Opcode
R
0
1
..
.
Operando
R
..
.
n
Figura 4.1 — Modo de endereçamento tipo registo.
Dependendo da flexibilidade do conjunto de instruções, poderão existir as diversas
variantes ou combinações destes modos de endereçamento.
4.4.1
Modo registo
Neste modo um dos operandos está contido num dos registos do CPU, sendo especificado
no opcode o registo onde ele se encontra, figura 4.1.
O código
LD
A, B
;A f i c a com o c o n t e ú d o de B
mostra-nos um exemplo de utilização deste modo de endereçamento em assembly do Z80.
4.4.2
Absoluto
O endereço da posição de memória a atingir é indicado na própria instrução, figura 4.2.
Se for completo, 16 bits, designa-se por absoluto longo, figura 4.2(b).
Se for indicado apenas 1 byte, designa-se por absoluto curto, não se encontrando
implementado no Z80. Veja-se a figura 4.2(a).
Pode ainda ser indicado de uma forma indirecta, figura 4.2(c) (não implementado no
Z80).
O código
LD
A, ( 0 FA80H)
mostra-nos um exemplo de utilização do modo absoluto longo em assembly do Z80.
4.4.3
Imediato
Neste modo uma constante é especificada como fazendo parte da instrução, i.é., um dos
operandos é uma constante.
O código
57
Memória
Opcode
Endereço
..
.
0
Endereço
Operando
..
.
(a) Absoluto curto
Memória
Opcode
Endereço
..
.
Operando
..
.
(b) Absoluto longo
Memória
..
.
Opcode
Endereço
Endereço
Indirecto
..
.
Operando
..
.
(c) Absoluto indirecto
Figura 4.2 — Modo de endereçamento tipo absoluto.
58
Registos
Opcode
R
End. indirecto
R
Memória
Operando
Figura 4.3 — Modo de endereçamento tipo registo indirecto.
LD
B, 2 1 0
LD
HL, 0 E642H
mostra-nos exemplos de utilização deste modo em assembly do Z80.
4.4.4
Registo indirecto
O endereço efectivo do operando está contido num registo ou par de registos, figura 4.3.
O código
LD
LD
B , ( HL)
(DE) , A
mostra-nos exemplos de utilização deste modo em assembly do Z80.
4.4.5
Auto-incremento e auto-decremento
Estes modos de endereçamento utilizam-se geralmente para manipular tabelas e/ou listas.
O modo auto-incremento é idêntico ao modo registo indirecto, só que depois de acedida
a memória, o registo que contém o endereço efectivo é incrementado, do comprimento do
operando, automaticamente, figura 4.4(a).
No modo de auto-decremento é efectuada uma subtracção ao registo que contém o
endereço efectivo, no valor igual ao comprimento do operando. A subtracção é efectuada
antes do acesso à memória, figura 4.4(b).
No Z80 as instruções que utilizam este modo são a LDI, LDD, CPI, CPD, LDIR,
LDDR, CPIR, CPDR, onde I se refere a Incremento e D a Decremento. R designa as
Repetitivas.
Estes modos podem ser implementados, para qualquer instrução, da seguinte forma:
LD
A, ( HL)
; auto−i n c r e m e n t o
INC
HL
; tamanho operando 1 b y t e
DEC
DE
; auto−decremento
LD
(DE) ,A
; tamanho operando 1 b y t e .
59
Registos
R
Opcode
R
End. indirecto
+
Memória
Operando
(a) Auto-incremento
Registos
R
Opcode
R
End. indirecto
−
Memória
Operando
(b) Auto-decremento
Figura 4.4 — Modos de endereçamento tipo auto-incremento e auto-decremento.
Registos
Opcode
R
Deslocamento
R
End. de base
Memória
+
Operando
Figura 4.5 — Modo de endereçamento tipo indexado.
4.4.6
Indexado
Neste modo o endereço é calculado adicionando um endereço de base e um deslocamento.
O endereço de base é indicado na própria instrução. O deslocamento encontra-se
armazenado num registo. Este modo de endereçamento é geralmente utilizado para aceder
a tabelas e matrizes. O endereço de base da matriz é especificado na instrução e o valor
do registo corresponde ao ı́ndice, figura 4.5.
O Z80 não implementa este tipo de endereçamento. Contudo pode ser obtido da
seguinte forma:
LD
DE, BASE ; l ê o e n d e r e ç o b a s e
LD
L ,A
; e s t e n d e o d e s l o c a m e n t o para
LD
H, 0
; 16 b i t s
ADD
HL,DE
; c a l c u l a e n d e r e ç o i n d e x a d o
LD
B , ( HL)
; t r a n s f . da memória para o r e g . B
60
Registos
Opcode
Ext. sinal
R
End. base
R
Deslocamento
Memória
Ext. sinal
+
Deslocamento
Operando
Figura 4.6 — Modo de endereçamento tipo base.
Registos
Opcode
RI
RB
End. base
RB
Deslocamento
RI
Ext. sinal
Memória
Ext. sinal
Deslocamento
+
Operando
Figura 4.7 — Modo de endereçamento tipo base-indexado.
Como poderia ser implementado se o deslocamento fosse de 16 bits e estivesse guardado
nas posições de memória INDEX e INDEX+1?
4.4.7
Base
Neste modo o endereço é formado de modo semelhante ao do indexado, figura 4.6.
O endereço de base está armazenado num registo e o deslocamento (offset), que pode
ser longo ou curto, faz parte da instrução.
O Z80 suporta este tipo de endereçamento, devendo para o efeito ser utilizado um
dos registos de 16 bits IX ou IY. É possı́vel indicar na instrução um offset, com um
comprimento de 1 byte. O código seguinte ilustra esta utilização:
LD
4.4.8
( IX +3) ,E
Base-indexado
Neste modo o endereço de base e o deslocamento encontram-se armazenados em registos,
figura 4.7. O Z80 não suporta este modo.
61
Ext. sinal
Opcode
Memória
Deslocamento
Operando
+
PC
(a) Relativo deslocamento curto.
Memória
Opcode
Operando
Deslocamento
+
PC
(b) Relativo deslocamento longo.
Figura 4.8 — Modo de endereçamento tipo relativo.
4.4.9
Relativo
O endereço é calculado somando um deslocamento, indicado na instrução, ao valor actual
do Program Counter, figura 4.8.
O programador referencia a posição de memória através da colocação de uma etiqueta, a qual é depois utilizada na instrução de referência à memória. Durante a fase de
assembling é calculado o deslocamento necessário a partir do valor actual do contador de
referência, sendo gerada uma mensagem de erro se o endereço absoluto estiver fora do
alcance do endereçamento relativo.
No Z80 este tipo de endereçamento existe unicamente nas instruções de salto, sendo
o deslocamento limitado a 1 byte (deslocamento curto, figura 4.8(a)). O deslocamento
calculado é um valor com sinal representado em complemento para dois, podendo variar
entre -128 e +127. O seguinte excerto de um programa em assembly do Z80 ilustra a
utilização deste modo de endereçamento:
JR
LAB
; s a l t a para ‘LAB’
...
LAB
LD
A, B
62
4.5
Instruções
Nesta secção iremos estudar um conjunto de instruções genérico tendo por base o Z80.
Serão dados exemplos elucidativos dos diferentes tipos de instruções. A notação que vai
sendo introduzida é válida para todas as instruções seguintes.
4.5.1
Transferência de informação
As instruções de transferência de informação permitem a movimentação de informação
do tipo: registo–registo; registo–memória; e memória–registo.
A mnemónica utilizada, tal como visto nos exemplos apresentados até ao momento, é
LD (LoaD), sendo em seguida indicados os operandos. Em primeiro lugar a origem e em
segundo o destino. São permitidas transferências de 8 bits e 16 bits.
O registo de flags só pode ser transferido de e para a memória associado ao registo
acumulador (A), utilizando para o efeito as instruções de manipulação da stack (secção 4.6.
No Z80 existem cinco modos de endereçamento nas instruções de transferência entre
registos e memória e vice-versa.
4.5.1.1
Modo absoluto
Este modo permite a transferência de 8 bits e 16 bits. O formato para a transferência de
8 bits é
LD
A , ( nn )
LD
( nn ) , A
e para 16 bits
LD
dd , ( nn )
LD
( nn ) , dd
onde ‘nn’ representa um número de 16 bits (neste caso é um endereço), ‘dd’ um par de
registos, BC, DE, HL, ou um dos registos SP ou ‘xy’, sendo ‘xy’ um dos registos IX ou
IY.
Exemplo 5 (Transferência de informação — modo absoluto) Transferir o conteúdo
da posição de memória com endereço 2345H para o registo A e o conteúdo do par de registos HL para os endereços 65FDH (L) e 65FEH (H).
LD
A, ( 2 3 4 5H)
;A ← ( 2 3 4 5H)
LD
( 6 5FDH) , HL
; ( 6 5FDH) ← L
; ( 6 5FEH ) ← H
63
4.5.1.2
Modo imediato
No modo imediato também podem ser transferidos 8 ou 16 bits. Para 8 bits
LD
r, n
LD
( ss ) , n
onde ‘r’ representa um registo A, B, C, D, E, H ou L, ‘n’ um dado de 8 bits, ‘ss’ o par de
registos HL ou ‘xy’+offset e ‘offset’ é um número de 8 bits, ou seja, ‘n’.
Para 16 bits
LD
dd , nn
Exemplo 6 (Transferência de informação — modo imediato) Transferido valor 6
para o registo C, o valor FA02H para IY e FE0FH para SP.
4.5.1.3
LD
C, 6
;C ← 6
LD
IY , 0 FA02H
; IY ← FA02H
LD
SP , 0 FE0FH
; SP ← FE0FH
Modo base
No modo base apenas podem ser transferidos 8 bits
LD
r , ( xy + o f f s e t )
LD
( xy + o f f s e t ) , r
Exemplo 7 (Transferência de informação — modo base) Transferir o conteúdo para
posição de memória indicada por IX+3 para o registo E e o conteúdo do registo A para a
posição de memória indicada por IY+50.
4.5.1.4
LD
E , ( IX+3)
; E ← ( IX+3)
LD
( IY + 5 0 ) , A
; ( IY +50) ← A
Modo registo indirecto
Neste modo também só podem ser transferidos 8 bits
LD
r , ( ss )
LD
( ss ) , r
LD
A , ( qq )
LD
( qq ) , A
onde ‘qq’ é um dos pares de registos BC ou DE.
64
Exemplo 8 (Transferência de informação — modo registo indirecto) Transferir
para o registo D o conteúdo da posição de memória apontada por HL e para a posição de
memória apontada por DE o conteúdo do registo A.
4.5.1.5
LD
D, ( HL)
;D ← ( HL)
LD
(DE) , A
; (DE) ← A
Outros exemplos de instruções de transferência
LD
HL , ADDRESS
LD
r , ( HL)
LD
A, ( ADDRESS)
LD
r ,A
LD
HL , ( INDIR )
LD
(HL ) , r
LD
HL , nn
LD
(ADDRESS) , HL
As transferências entre registos podem envolver 8 ou 16 bits, podendo ser efectuadas,
no caso de 8 bits, através das instruções
LD
r , r0
LD
A, x
LD
x, A
onde ‘r0 ’ pode tomar os mesmos valores de ‘r’ e ‘x’ é um dos registos I ou R.
No caso da transferência de 16 bits
LD
SP , xx
onde ‘xx’ é um dos registos IX, IY ou o par de registos HL.
4.5.2
Manipulação de blocos
Vamos apresentar aqui não só as instruções de manipulação de blocos, mas também as
instruções de troca de registos.
O Z80 possui um conjunto muito poderoso de instruções para manipulação de blocos
de informação. Estas instruções possibilitam ao programador o controlo automático do
número de bytes transferidos, bem como a actualização automática dos ponteiros envolvidos.
65
As instruções LDI e LDD movem um byte de dados do endereço referenciado por
HL para o endereço referenciado por DE, decrementam BC e Incrementa (LDI) ou Decrementa (LDD) DE e HL. As instruções LDIR e LDDR repetem LDI ou LDD até
BC−1 = 0.
Por seu lado, as instruções CPI e CPD comparam o conteúdo do acumulador com o
conteúdo da posição de memória apontada por HL, decrementam BC e Incrementa (CPI)
ou Decrementa (CPD) HL. Ambas colocam a flag de zero a ‘1’ se os operandos forem
iguais, ficando a ‘0’ nas outras situações. A flag de parity/overflow fica a ‘0’ se BC−1 = 0
ou a ‘1’ se BC−1 6= 0. CPIR e CPDR repetem CPI e CPD até BC−1 = 0.
Exemplo 9 (Movimentação de dados) Mover um byte da posição de memória com
endereço ADR1 para a posição de memória com endereço ADR2:
LD
BC, 1
; número de b y t e s a mover
LD
HL , ADR1
; i n i c i a l i z a origem
LD
DE, ADR2
; in ici ali za destino
; ou LDD
LDI
Mover 10 bytes de dados da posição de memória com endereço ADR1 para a posição
de memória com endereço ADR2:
LD
BC, 1 0
; número de b y t e s a mover
LD
HL , ADR1
; i n i c i a l i z a origem
LD
DE, ADR2
; in ici ali za destino
; transfere 10 bytes
LDIR
ou
LD
BC, 1 0
; número de b y t e s a mover
LD
HL , ADR1+9
; i n i c i a l i z a origem
LD
DE, ADR2+9
; in ici ali za destino
; transfere 10 bytes
LDDR
Examinar as posições de memória, começando pelo endereço ADR, até ser encontrada
uma contendo 0 ou terem sido examinadas 256:
LD
BC, 1 0 0H
; 2 5 6 p o s i ç õ e s a t e s t a r
LD
HL , ADR
; i nı́ c i o do b l o c o
SUB
A
;A=0
; Z=1 s e e x i s t i r um z e r o
CPIR
; Z=0 s e BC=0
As instruções de troca de registos são
EX
DE, HL
66
que troca os conteúdos de DE e HL,
EX
AF,AF0
que troca os conteúdos de A com A0 e F com F0 ,
EXX
que troca os conteúdos dos pares de registos BC com B0 C0 , DE com D0 E0 e HL com H0 L0 e
EX
(SP ) , xx
que troca o conteúdo dos dois bytes no topo da stack com um dos registos IX ou IY, ou
o par de registos HL.
4.5.3
Instruções aritméticas
No caso do Z80, estas instruções permitem a realização de adições e subtracções, com ou
sem carry.
4.5.3.1
Aritmética de 8 bits
As instruções aritméticas de 8 bits podem ser efectuadas entre o acumulador e um registo
de 8 bits ou entre o acumulador e um byte numa posição de memória endereçado num
modo imediato, através do par de registos HL (registo indirecto), ou através dos registos
IX ou IY (base+offset). A sua forma geral é
ADD
r
Esta instrução adiciona o conteúdo do acumulador ao conteúdo do registo indicado por
‘r’, depositando o resultado no acumulador.
ADD
n
;A ← A + n
ADD
( ss )
;A ← A + ( s s )
ADC
s
;A ← A + s + c a r r y
A última instrução adiciona o conteúdo do acumulador, o conteúdo do registo indicado
por ‘s’ e o bit da flag de carry, depositando o resultado no acumulador. Esta instrução
deve ser usada quando se pretende utilizar aritmética de precisão múltipla.
A instrução
SUB
s
;A ← A − s
subtrai o conteúdo do acumulador ao conteúdo do registo indicado por ‘s’, depositando o
resultado no acumulador.
A instrução
SBC
s
;A ← A − s − c a r r y
67
subtrai o conteúdo do acumulador, o conteúdo do registo indicado por ‘s’ e o bit da flag
de borrow (carry), depositando o resultado no acumulador. Esta instrução deve ser usada
quando se pretende utilizar aritmética de precisão múltipla.
Na instrução
INC
d
;d ← d + 1
é adicionado o valor um ao número guardado em ‘d’.
A instrução
DEC
d
;d ← d − 1
subtrai um ao número guardado em ‘d’.
Nestas instruções, ‘s’ é um dos registos de ‘r’, ‘n’, ou ‘(ss)’; e ‘d’ é um dos registos de
‘r’ ou ‘(ss)’.
4.5.3.2
Aritmética de 16 bits
No Z80, a aritmética de 16 bits só é possı́vel entre registos, não sendo possı́vel endereçar
dados em memória.
A instrução seguinte adiciona os 16 bits de HL com os 16 bits do registo (ou par de
registos) ‘ww’
ADD
HL , ww
A instrução seguinte adiciona os 16 bits de HL, os 16 bits do registo (ou par de registos)
‘ww’ e o bit da flag de carry
ADC
HL , ww
A instrução seguinte subtrai os 16 bits de HL, os 16 bits do registo (ou par de registos)
‘ww’ e o bit da flag de borrow (carry).
SBC
HL , ww
A seguinte instrução adiciona os 16 bits de ‘xy’ com os 16 bits do registo (ou par de
registos) ‘ee’. Não se pode adicionar IX a IX ou IY a IY.
ADD
xy , e e
A instrução
INC
dd
adiciona um ao registo (ou par de registos) ‘dd’.
A instrução
DEC
dd
68
subtrai um ao registo (ou par de registos) ‘dd’.
Nestas instruções ‘ww’ representa um dos pares de registos BC, DE, HL, ou o registo
SP, e ‘ee’ um dos pares de registos BC ou DE, um dos registos ‘xy’ ou SP.
Exemplo 10 (Aritmética de precisão múltipla) Efectuar a adição de dois números
de 64 bits
ADD8:
LD
B, 8
; 64 b i t s = 8 b y t e s
SUB
A
; c a r r y =0
LD
HL , NUM1
; i nı́ c i o d o s números
LD
DE, NUM2
LD
A , ( DE)
; l ê 1 b y t e de um o p e r a n d o
ADC
A , ( HL)
; soma um b y t e do o u t r o o p e r a n d o
LD
(HL ) , A
; armazena a soma de 8 b i t s
INC
DE
; incrementa os p o n t e i r o s
INC
HL
DJNZ
ADD8
; c o n t a o número de a d i ç õ e s
; efectuadas
4.5.4
Instruções lógicas
Estas instruções permitem a realização de operações lógicas elementares a conjunção
(AND), disjunção (OR) e disjunção exclusiva (XOR) (eXclusive OR).
Podem ser efectuadas entre o acumulador e um registo de 8 bits, um byte endereçado
no modo imediato, um byte endereçado através do par HL ou utilizando os registos IX
ou IY com um deslocamento.
Estas instruções não permitem o endereçamento absoluto.
A instrução
AND
s
faz o AND do acumulador com o byte indicado por ‘s’.
A instrução
OR
s
faz o OR do acumulador com o byte indicado por ‘s’.
Finalmente, a instrução
XOR
s
faz o XOR do acumulador com o byte indicado por ‘s’.
Exemplo 11 (Instruções lógicas) Alguns exemplos de utilização das instruções lógicas
são os seguintes.
69
4.5.5
AND
BIC
;AND de A com a e t i q u e t a BIC
OR
(HL)
;OR de A com o s d a d o s a p o n t a d o s p o r HL
XOR
C
;XOR de A com C
Manipulação de bits
O Z80 possui um conjunto de instruções que permitem a manipulação de bits, fazendo o
seu teste e inicialização (set ou reset), i.é., podemos testar o valor, colocar a ‘1’ ou a ‘0’
um dado bit de um registo ou de uma posição de memória.
A instrução seguinte testa o bit número ‘b’ do byte indicado por ‘d’, colocando a flag
de zero a ‘1’ se o bit testado é ‘0’ e vice-versa
BIT
b, d
A instrução seguinte coloca a ‘1’ o bit número ‘b’ do byte indicado por ‘d’
SET
b, d
A instrução
RES
b, d
coloca a ‘0’ o bit número ‘b’ do byte indicado por ‘d’.
Podemos utilizar instruções lógicas para atingirmos os mesmos objectivos. Assim,
utilizando “máscaras”, podemos:
• Colocar bits a ‘1’ fazendo OR com ‘1’s nas posições desejadas;
• Colocar bits a ‘0’ fazendo AND com ‘0’s nas posições desejadas;
• Complementar bits fazendo XOR com ‘1’s nas posições desejadas;
• Testar bits a ‘0’ fazendo AND com ‘1’s nas posições desejadas.
Exemplo 12 (Manipulação de bits) Colocar a 1 o bit 6 do acumulador
SET
6 ,A
OR
40H
ou
Colocar a 0 o bit 3 do acumulador
RES
3 ,A
AND
0F7H
ou
Complementar o bit 2 do acumulador
70
XOR
04
Testar o bit 5 dois do acumulador
BIT
5, A
AND
20H
ou
4.5.6
Controlo de programa
As instruções de controlo de programa permitem a alteração do fluxo sequencial do programa de uma forma condicional ou não.
Pode ser efectuado um salto para uma dada instrução, no caso de uma dada condição
ser verdadeira, um salto para um dado endereço de memória, no caso de uma dada
condição ser verdadeira,
Podemos basear os saltos condicionais num dos seguintes tipos de decisões:
• Teste do valor de 1 bit (‘0’ ou ‘1’);
• Igualdade ou desigualdade de dois valores;
• Comparação de dois valores.
O modo de endereçamento utilizado num salto pode ser relativo — limitado ao intervalo fechado −126 a +129 posições de memória (instruções com mnemónica JR) —, ou
absoluto (instruções com mnemónica JP).
Utilizando endereçamento relativo
JR
e
JR
cc0 , e
Utilizando endereçamento absoluto:
JP
nn
JP
cc , nn
onde ‘e’ representa um número na gama −128 a +127, ‘cc0 ’ uma das condições C, NC, Z,
NZ, ‘cc’ uma das condições C, NC, Z, NZ, PO, PE, P, M e ‘nn’ um número de 16 bits.
Exemplo 13 (Saltos condicionais) Saltar para DEST se o bit 5 do acumulador for 1
(podemos usar JP em vez de JR)
BIT
5, A
JR
NZ , DEST
Saltar para DEST se o bit 6 do registo C for 0
71
BIT
2, C
JR
Z , DEST
Saltar para DEST se o bit 5 da posição de memória com endereço ADR for 1
LD
HL , ADR
BIT
5 , (HL)
JR
NZ , DEST
Saltar para DEST se o bit 7 do acumulador for 1
AND
A
JP
M, DEST
Saltar para DEST se o acumulador contém o número VALOR
CP
VALOR
JR
Z , DEST
Saltar para DEST se o conteúdo do acumulador é diferente do conteúdo da posição de
memória com endereço ADR
LD
HL , ADR
CP
(HL)
JR
NZ , DEST
Saltar para DEST se o conteúdo do acumulador for 0
AND
A
JR
Z , DEST
Saltar para DEST se o conteúdo do acumulador não for FFh
INC
A
JR
NZ , DEST
Saltar para DEST se o conteúdo do acumulador for 1
DEC
A
JR
Z , DEST
Saltar para DEST se a posição de memória com endereço ADR contém 0
LD
HL , ADR
INC
(HL)
DEC
(HL)
JR
Z , DEST
Saltar para DEST se o conteúdo da posição de memória com endereço OPER1 for
menor do que o conteúdo da posição de memória com endereço OPER2
72
LD
A , ( OPER1)
LD
HL , OPER2
CP
(HL)
JR
C , DEST
Saltar para DEST se o conteúdo da posição de memória com endereço OPER1 for
menor ou igual ao conteúdo da posição de memória com endereço OPER2
LD
A , ( OPER2)
LD
HL , OPER1
CP
(HL)
JR
NC, DEST
Saltar para DEST se o acumulador contém um número com sinal maior ou igual ao
número VALOR
CP
VALOR
; e f e c t u a a comparação
JP
PE , FNEG
; houve o v e r f l o w ?
JP
P , DEST
; não , s a l t a s e r e s . p o s i t i v o
JR
DONE
FNEG:
JP
M, DEST
DONE:
NOP
; sim , s a l t a s e r e s . n e g a t i v o
Designamos por ciclo (loop) a repetição de uma sequência de instruções. O Z80 possui
uma instrução que nos permite implementar esta estrutura de uma forma muito simples.
Trata-se da instrução
DJNZ
e
onde ‘e’ especifı́ca um número na gama −128 a +127.
Se pretendermos utilizar esta instrução teremos que:
1. Carregar o registo B com o número de vezes que a sequência vai ser executada;
2. Executar a sequência;
3. Usar a instrução DJNZ que decrementa o registo B e volta ao passo 2 no caso do
resultado ser diferente de 0.
Esta instrução tem duas limitações:
1. O contador é de 8 bits;
• Qual o número máximo de vezes que uma sequência pode ser executada?
• Com que valor se deve carregar o registo B?
73
2. Utiliza endereçamento relativo
• Qual é a consequência? Limita o número de instruções dentro do ciclo.
Exemplo 14 (Ciclos ou loops) Ciclo simples
LD
LOOP:
B , NVEZES
···
···
DJNZ
LOOP
Dois loops, um dentro de outro
LD
C , NVEZESE
LOOPE:
LD
B , NVEZESI
LOOPI :
···
···
DJNZ
LOOPI
DEC
C
JR
NZ , LOOPE
Loop executado mais de 256 vezes:
LD
LOOP:
BC, NVEZES
···
···
4.5.7
DEC
BC
LD
A, B
OR
C
JR
NZ,LOOP
Deslocamento e rotação
As instruções de deslocamento (shift) permitem operar em qualquer registo ou posição de
memória.
Podem ser de dois tipos: shift aritmético — preservam o bit de sinal; e shift lógico —
introduzem um ‘0’.
Shift lógico, do byte especificado em ‘d’, para a direita:
SRA
d
Shift aritmético, do byte especificado em ‘d’, para a esquerda com introdução de ‘0’:
SLA
d
Shift aritmético, do byte especificado em ‘d’, para a direita com introdução de ‘0’:
74
SRL
d
As instruções de rotação (rotate) permitem a rotação de um registo ou posição de
memória, podendo esta ser efectuada através da flag de carry ou não.
Rotação para a esquerda do conteúdo do acumulador; o bit mais significativo é reintroduzido como bit menos significativo, ficando a flag de carry também afectada com
este bit:
RLCA
Rotação para a direita do conteúdo do acumulador; o bit menos significativo é reintroduzido como bit mais significativo, ficando a flag de carry também afectada com este
bit:
RRCA
Rotação para a esquerda do conteúdo do acumulador: o bit mais significativo é colocado na flag de carry, entrando como bit menos significativo o bit que estava nesta
flag:
RLA
Rotação para a direita do conteúdo do acumulador; o bit menos significativo é colocado
na flag de carry, entrando como bit mais significativo o bit que estava nesta flag:
RRA
RLC
d
idêntica a RLCA só que roda o byte indicado por ‘d’.
RRC
d
idêntica a RRCA só que roda o byte indicado por ‘d’.
RL
d
idêntica a RLA só que roda o byte indicado por ‘d’.
RR
d
idêntica a RRA só que roda o byte indicado por ‘d’.
4.5.8
Grupo aritmético de propósito geral
Este grupo contém as instruções para ajuda nos cálculos com números representados em
BCD, bem como para complementar e negar o conteúdo do acumulador e ainda para
inicialização da flag de carry.
DAA
; Decimal A d j u s t Accumulator
75
Esta instrução converte o conteúdo do acumulador para um número válido BCD compactado, após a soma ou subtracção de operandos compactados (dois dı́gitos BCD num
único byte, ou seja, 4 bits por dı́gito).
CPL
; Complement Accumulator
complementa para um o conteúdo do acumulador.
NEG
; Negate Accumulator
nega o conteúdo do acumulador. Efectua a subtracção 0−A e coloca o resultado em A.
CCF
; Complement Carry F l a g
complementa a flag de carry.
SCF
; S e t Carry F l a g
cola a ‘1’ a flag de carry.
4.5.9
Grupo de controlo do CPU
Com as instruções pertencentes a este grupo podemos controlar o modo de funcionamento
do CPU, inclusive indicar-lhe para não fazer nada!
NOP
; No OPeration !
A instrução
HALT
pára o CPU.
DI
; Disable Interrupts
Com esta instrução o flip-flop de interrupções fica com o valor ’0’, i.e., IFF ← 0.
EI
; Enable I n t e r r u p t s
Com esta instrução o flip-flop de interrupções fica com o valor ’1’, i.e., IFF ← 1.
A instrução
IM
z
coloca o CPU no modo de interrupções 0, 1 ou 2.
4.6
Stack
Durante a execução de um programa temos muitas vezes necessidade de armazenar temporariamente a informação que irá ser processada.
Para facilitar este processo é implementada, na maioria dos sistemas de computador,
uma estrutura destinada a esse fim.
76
É usada uma zona de memória de leitura/escrita (read/write) do processador, designada por stack, existindo um registo que serve de ponteiro para o topo da stack designado
por Stack Pointer.
Esta estrutura encontra-se organizada como LIFO (Last In First Out), i.é., o último
elemento a ser lá colocado será o primeiro a ser lido.
A stack ao ser preenchida vai crescendo para os endereços mais baixos de memória.
Como o stack pointer aponta para o topo da stack, ele deve ser decrementado sempre que
introduzamos um novo valor na stack e incrementado quando retiramos algum valor da
stack.
Assim para colocarmos na stack vários registos do CPU terı́amos que efectuar as
seguintes operações.
Armazenar na stack:
LD
SP , 0 FEA0H
DEC
SP
LD
(SP ) , H
DEC
SP
LD
(SP ) , L
DEC
SP
LD
(SP ) , A
DEC
SP
LD
(SP ) , F
; i n i c i a l i z a r SP
Retirar da stack:
LD
C , ( SP )
INC
SP
LD
B , ( SP )
INC
SP
LD
E , ( SP )
INC
SP
LD
D, ( SP )
INC
SP
Para facilitar esta tarefa de manipulação da stack existem habitualmente instruções
dedicadas, efectuando automaticamente o incremento ou decremento do stack pointer.
No Z80 estas instruções são a PUSH e a POP que permitem o armazenamento e a
retirada, respectivamente, da stack de um par de registos ou registo de 16 bits.
Utilizando estas instruções, os exemplos acima apresentados reduzem-se significativamente.
Armazenar na stack:
77
LD
SP , 0 FEA0H
PUSH
HL
PUSH
AF
Retirar da stack:
POP
BC
POP
DE
Num programa onde utilizemos a stack para armazenamento temporário teremos que
ter o cuidado de efectuar igual número de PUSH’s e de POP’s.
O Z80 permite colocar e retirar da stack os registos IX ou IY, ou os pares de registos
AF, BC, DE, HL, através da utilização das instruções PUSH e POP.
4.7
Subrotinas
Muitas vezes temos num programa um conjunto de instruções, desempenhando uma tarefa
bem definida, que é repetida por diversas vezes. Nesta situação será mais conveniente
considerar esse conjunto de instruções como um bloco autónomo, o qual é chamado sempre
que essa tarefa necessita de ser executada. A este bloco autónomo damos o nome de subrotina.
A utilização de subrotinas tem diversas vantagens já que conduz a programas:
• Com menos código;
• Mais legı́veis;
• De mais fácil debugging;
• Etc..
Para a chamada das subrotinas temos instruções próprias, existindo no Z80 dois formatos de instrução:
CALL
nn
; chamada i n c o n d i c i o n a l
CALL
cc , nn
; chamada c o n d i c i o n a l .
A instrução CALL, para além de saltar para o local onde se encontra a sub-rotina,
armazena previamente o endereço de retorno, de tal forma que após a execução da subrotina o controlo do programa passa para a instrução imediatamente a seguir ao CALL.
No final da sub-rotina deve ser colocada a instrução RET (return), a qual vai à stack
buscar o endereço de retorno (armazenado no topo pela instrução CALL), colocando-o
no program counter.
Podemos dizer que:
78
PUSH
PC
JP
nn
teriam o mesmo efeito que CALL nn e
POP
PC
teria o mesmo efeito que RET.
Normalmente são passados à sub-rotina um conjunto de dados que irão ser processados
por ela. Estes dados são geralmente designados por parâmetros de entrada.
A sub-rotina origina um conjunto de resultados, os parâmetros de saı́da, que deverão
ser passados de volta ao programa que chama a sub-rotina.
Diz-se que uma sub-rotina é reentrante se ela pode ser interrompida por um programa
e chamada por esse outro.
Nestas condições quando a chamada termina, a sub-rotina interrompida reinicia-se
onde tinha sido suspensa. Para que não exista destruição de informação é necessário
armazenar todos os registos antes da sub-rotina reiniciar a execução.
Uma sub-rotina recursiva é aquela que se chama a si própria.
Numa estrutura de programação com este tipo de subrotinas é necessário armazenar
na stack:
• O endereço de retorno;
• Os parâmetros de entrada e de saı́da;
• As variáveis locais.
4.8
Passagem de parâmetros
A passagem de parâmetros entre uma sub-rotina e o programa que a chama pode ser
realizada de várias formas.
Iremos referir as técnicas mais comuns, dando exemplos de aplicação com base no
conjunto de instruções do Z80.
4.8.1
Registos
Os parâmetros podem ser passados nos registos do CPU, sejam eles o dado ou o endereço
de memória onde se encontra o dado.
O programa, antes de efectuar a chamada da sub-rotina, deve colocar nos registos
escolhidos, os parâmetros de entrada.
Antes da instrução RET a sub-rotina colocará nos registos escolhidos, os parâmetros
de saı́da.
No caso do Z80 podemos utilizar:
79
• O acumulador para passar parâmetros de 8 bits;
• O par HL para endereços (16 bits);
• O par DE para um segundo endereço devido à existência da instrução EX DE,HL;
• IX ou IY como locais óbvios para a colocação de um endereço base de uma estrutura
onde são conhecidos os deslocamentos dos vários elementos.
4.8.2
Área de memória
Neste caso utiliza-se uma área de memória read/write para o armazenamento dos parâmetros.
No Z80 pode implementar-se esta técnica utilizando:
• Um index register com o endereço base dessa área, utilizando depois vários offsets
para endereçar os diferentes parâmetros;
• O par de registos HL, para guardar o endereço base, uma vez que o processo do
ponto anterior é muito lento. Através da manipulação de HL temos acesso aos
diferentes parâmetros.
O programa que chama a sub-rotina deve:
1. Colocar os diferentes parâmetros na memória;
2. Guardar num index register ou no par de registos HL o endereço de base;
3. Chamar a sub-rotina.
No caso de ser utilizada uma área de memória comum deixa de ser possı́vel a reentrância e a recursividade, já que os parâmetros anteriores serão destruı́dos em cada
chamada da sub-rotina.
4.8.3
Stack
Esta é a forma mais geral de passagem de parâmetros, possibilitando a utilização de
estruturas recursivas e reentrantes.
O programa que chama a sub-rotina deve:
• Antes da sua chamada, reservar espaço para os parâmetros de saı́da e colocar os
parâmetros de entrada na stack;
• Depois do retorno, deve remover os parâmetros da stack (limpar a stack). Este
processo é simples se os parâmetros de entrada tiverem sido colocados acima da
área vazia destinada aos resultados.
80
A implementação no caso do Z80, que vamos ver em seguida, é em tudo semelhante
à forma como é feita num PC baseado num micro-processador da famı́lia Intel 80x86.
Consulte-se [8], por exemplo, para mais pormenores.
Programa principal:
; r e s e r v a e s p a ç o para p a r â m e t r o s de s aı́ d a
XOR
A
; c o l o c a 0 em A
PUSH
AF
; parâmetro de s aı́ d a de 8 b i t s
LD
HL, 0
; c o l o c a 0 em HL
PUSH
HL
; parâmetro de s aı́ d a de 1 6 b i t s
···
; c o l o c a p a r â m e t r o s de e n t r a d a
LD
A , ( PAR8 )
; l ê parâmetro de 8 b i t s
PUSH
AF
; c o l o c a −o na s t a c k
LD
HL , ( PAR16 )
; l ê parâmetro de 1 6 b i t s
PUSH
HL
; c o l o c a −o na s t a c k
···
CALL
sub−r o t i n a
; l i m p a a s t a c k r e t i r a n d o os p a r â m e t r o s de s aı́ d a
POP
HL
; r e t i r a parâmetro de s aı́ d a de 1 6 b i t s
POP
AF
; r e t i r a parâmetro de s aı́ d a de 8 b i t s
PUSH
IX
; IX é o
LD
IX , 0
; IX ← SP
ADD
IX , SP
···
Sub-rotina:
frame
pointer
···
LD
r , ( IX+5+2×NPAR)
; em ’ r ’ f i c a um
; parâmetro de 8 b i t s
LD
rpL , ( IX+4+2×NPAR)
; LSB do parâmetro
; de 1 6 b i t s
LD
rpH , ( IX+5+2×NPAR)
;MSB do parâmetro de
; 16 b i t s
···
LD
SP , IX
; r e p õ e s t a c k p o i n t e r limpando v a r i á v e i s
; locais
POP
RET
IX
; r e p õ e frame p o i n t e r
; fim da sub−r o t i n a
De notar que IX está a ser utilizado como um segundo ponteiro para a stack. A este
81
tipo de ponteiro auxiliar dá-se o nome de frame pointer. Esta designação deve-se ao facto
deste ponteiro criar um outro ponto na stack por onde se pode “ver” o que ela contém.
Qual é o outro ponto?
Uma vez colocados os parâmetros pelo programa chamador, podemos retirá-los se
tivermos em conta que o parâmetro NPAR se encontra em IX+4 + 2×NPAR. NPAR varia
de 0 (último parâmetro colocado na stack pelo programa que chama a sub-rotina) até N
(primeiro parâmetro colocado na stack). NPAR é multiplicado por 2 porque colocamos
sempre 2 bytes na stack, mesmo quando só queremos guardar um byte (caso do registo
A que se junta sempre ao registo F). O algarismo 4 surge da adição dos 2 bytes que
são o endereço de retorno da sub-rotina (colocados pela instrução CALL) e dos 2 bytes
da instrução PUSH IX logo no inı́cio da sub-rotina. Quando queremos aceder a um
byte apenas, em vez de somar 4 somamos 5, pois este byte é sempre o mais significativo
(foi colocado na stack usando PUSH AF). Podemos usar esta técnica para chamar subrotina(s) a partir de uma sub-rotina.
82
Capı́tulo 5
Entrada e Saı́da
5.1
Introdução
Até ao presente capı́tulo foi estudado o funcionamento de um sistema computacional, sem
se atender à comunicação com o mundo exterior. Foram estudadas:
• A arquitectura básica de um CPU;
• A forma como a memória se integra no sistema;
• As estruturas que se definem na memória;
• E a forma como o CPU comunica com a memória.
Neste capı́tulo vamos estudar a forma de integração de interfaces nos barramentos
do sistema computacional e as técnicas fundamentais de ligação entre as interfaces e os
periféricos (impressoras, terminais alfa-numéricos, etc.). Consulte-se [7], por exemplo,
para mais pormenores.
5.2
Mapas de endereçamento
As estruturas fı́sicas que fazem parte de um sistema computacional (registos, memória e
interfaces), necessitam de ser identificadas com um código único.
Ao pretender-se aceder a uma em particular, o seu código deve ser lançado no local e
tempo convenientes, por forma a que a estrutura em causa fique disponı́vel.
Este código de identificação designa-se por endereço da estrutura.
Tal como estudado anteriormente, o CPU tem um conjunto de linhas de endereço
acessı́veis do exterior que lhe permitem distinguir um número mais ou menos elevado
desses elementos. Assim, conseguimos distinguir 2n elementos com n linhas de endereço.
Podemos traduzir esta ideia de uma outra forma; as n linhas que saem do CPU
possibilitam um mapa de endereçamento com N elementos, sendo N = 2n .
83
A15
Mapa de
endereçamento
A0−A14
0000
Memória
Memória
Interface
7FFF
8000
Periférico
32K x 8 bits
Interface
Registos
FFFF
Dados
Controlo
Figura 5.1 — Exemplo de mapa de endereçamento. Neste caso, a interface é acedida por qualquer
endereço dos 32K superiores (ver texto).
Este mapa pode ser ou não preenchido, consoante se coloquem estruturas fı́sicas com
que o CPU possa comunicar.
Por exemplo, no caso da memória, se tivéssemos um sistema computacional com 16
linhas de endereços, o mapa de endereçamento por elas criado teria 216 = 65536 = 64K
células. Estando este mapa totalmente disponı́vel, poder-se-ia utilizar a sua primeira metade com uma pastilha de memória de 32K elementos. Assim, este mapa de endereçamento
teria a sua metade superior livre para se utilizar, sendo possı́vel colocarmos mais memória
ou outro tipo qualquer de estrutura. Podemos, através da utilização deste método, colocar
na parte do mapa de endereçamento disponı́vel, circuitos de interface, figura 5.1.
De notar que a selecção da interface é efectuada unicamente pela linha A15 o que
implica que qualquer endereço da metade superior do mapa de endereçamento selecciona
o circuito de interface.
Apesar de se gastar metade do mapa de endereçamento com apenas um circuito de
interface, tal facto não se torna relevante se não precisarmos de introduzir mais nenhuma
estrutura no sistema.
Se pretendermos introduzir mais estruturas no sistema temos que descodificar mais
linhas de endereço, reduzindo assim o número de combinações que levam a escolher a dita
estrutura.
Compare-se a figura 5.1 com a figura 5.2. O aumento da lógica introduzida liberta
posições do mapa de endereçamento, o que permite a introdução de estruturas adicionais.
Existe um compromisso entre a quantidade de lógica utilizada e o número de posições
do mapa de endereçamento deixadas livres. O grande inconveniente da utilização desta
84
A15
A14
Mapa de
endereçamento
0000
A0−A13
Memória
Interface
Memória
Periférico
32K x 8 bits
Registos
Interface
CFFF
D000
FFFF
Dados
Controlo
Figura 5.2 — Outro exemplo de mapa de endereçamento onde a interface é acedida por qualquer
endereço dos 16K superiores (ver texto).
técnica (Input/Output memory mapped ou simplesmente memory mapped) é a necessidade
de introdução de uma quantidade apreciável de lógica exterior para descodificar interfaces colocadas no mapa de endereçamento de memória. O micro-processador 68000 da
Motorola implementa este tipo de mapa de endereçamento [9, 16].
Alguns sistemas computacionais, têm para além do mapa de endereçamento de memória,
um mapa especı́fico para as transacções de entrada/saı́da (I/O mapped).
Para tal, basta que o CPU tenha mais uma linha acessı́vel do exterior que permite
distinguir os dois mapas de endereçamento.
O Z80 tem os dois mapas de endereçamento: de memória; e de I/O. Este CPU possui
uma linha designada por ‘IO/M’ cujo estado distingue o mapa a endereçar. Quando está
“em baixo” o mapa a endereçar será o mapa de memória. Quando está “em cima” o mapa
a endereçar será o mapa de I/O.
Como é que o CPU, estando a executar um programa, pode aceder a um ou a outro
mapa de endereçamento? Muito simples: existem dois grupos de instruções para aceder
a cada um dos mapas!
Todas as instruções estudadas até aqui acedem ao mapa de endereçamento de memória.
As instruções que se referem ao mapa de endereçamento de entrada/saı́da são para entrada
IN
A, ( n )
;A ← ( n )
IN
r , ( C)
;A ← (C)
INI
; (HL) ← (C)
;HL ← HL+1
;B ← B−1
85
INIR
; (HL) ← (C)
;HL ← HL+1
;B ← B−1 a t é B= 0
IND
; (HL) ← (C)
;HL ← HL−1
;B ← B−1
INDR
; (HL) ← (C)
;HL ← HL−1
;B ← B−1 a t é B= 0
e para saı́da
OUT
(n ) , A
; (n ) ← A
OUT
(C ) , r
; (C) ← r
OUTI
; (C) ← (HL)
;HL ← HL+1
;B ← B−1
OTIR
; (C) ←(HL)
;HL ← HL+1
;B ← B−1 a t é B= 0
OUTD
; (C) ← (HL)
;HL ← HL−1
;B ← B−1
OTDR
; (C) ← (HL)
;HL ← HL−1
;B ← B−1 a t é B= 0
O grande inconveniente desta técnica é que estas instruções, como vimos, são em
número muito mais reduzido do que as que acedem ao mapa de endereçamento de memória.
Esta situação conduz a uma menor versatilidade nas condições de programação. É da
responsabilidade do programador a utilização destas instruções.
Na figura 5.3 pode ser visto um exemplo. A interface da esquerda encontra-se no mapa
de endereçamento da memória, sendo seleccionada pelo endereço 8000H. A interface da
direita encontra-se no mapa de endereçamento de I/O, sendo seleccionada pelo endereço
40H.
5.3
Interfaces
As interfaces são circuitos que se encontram ligados aos barramentos do sistema e a um
periférico particular. Têm por objectivo fazer a adaptação entre os sinais eléctricos do
sistema computacional e os sinais eléctricos dos respectivos periféricos.
86
A15
A6
IO/M
IO/M
CS
CS
Dados
Controlo
Dados
Interface
Controlo
Interface
Figura 5.3 — A interface é acedida pelo mapa de endereçamento da memória. A interface da
direita é acedida pelo mapa de endereçamento de I/O (ver texto).
Geralmente, internamente são constituı́das:
• Pela lógica que permite fazer a adaptação dos sinais referidos;
• Por registos de memorização para armazenamento temporário da informação a trocar entre o periférico e o sistema computacional.
Normalmente existe uma diferença muito grande de velocidades na troca de informação
entre o sistema computacional e a interface e entre a interface e o periférico. Esta é uma
das razões que faz com seja necessária a existência dos registos de memorização. A
utilização destes registos é necessária por causa da informação a transferir e ainda pelo
controlo dessa transferência. Assim, existem geralmente dois tipos de registos: registos
de memorização temporária da informação; e registos que guardam o estado da interface
em cada instante.
Os registos responsáveis por guardar o estado em que a interface se encontra em cada
instante são os registos de controlo e estado ou status.
Os registos de controlo devem conter informação que lhes permitam saber se os registos
de armazenamento temporário de informação (internos à interface, também designados
por registos de dados ou data registers), se encontram ou não livres para nova transacção.
O conteúdo destes registos é actualizado automaticamente pela lógica interna à interface
de acordo com o seu estado em cada instante. Estes registos podem ser lidos pelo CPU
sob o controlo de programa. Desta forma podemos saber o estado da interface nesse
instante. Na figura 5.4 pode ser visto um esquema genérico de ligação de uma interface a
um sistema computacional.
87
Barramento do
sistema computacional
Interface
Periférico
Figura 5.4 — Esquema geral de interligação de uma interface.
5.3.1
Protocolo de programação para periféricos de saı́da
Suponhamos que a um sistema computacional está ligado um periférico de saı́da, através
de um circuito de interface que contém internamente dois registos cujas funções obedecem
ao anteriormente descrito:
• Registo de dados (data register);
• Registo de estado (status register).
Quando o sistema computacional pretende transferir informação para este periférico
vê-se confrontado com as diferenças de tempo de transferência e a necessidade de assegurar
uma transferência correcta da informação.
Para assegurar esta última necessidade o sistema deve consultar o registo de estado
e quando verificar que o registo de dados se encontra livre, procede à transferência da
informação. Os circuitos internos à interface são responsáveis por colocar o registo de
estado na situação de “interface ocupada” e proceder à transferência da informação residente no registo de dados para o periférico. Finda esta operação devem colocar o registo
de estado no estado “interface livre”, permitindo ao sistema escrever nova informação no
registo de dados.
O fluxo-grama da figura 5.5 ilustra o processo aqui descrito.
Exemplo 15 (Ligação de uma interface de saı́da) A figura 5.6 esquematiza a integração de um periférico num sistema computacional cujo CPU é um Z80. Pretende-se
enviar para este periférico o conteúdo de uma zona de memória cujo endereço inicial é
BLOCO e cujo número de bytes se encontra armazenado na posição de memória com
endereço NUM.
Observando-se a figura 5.6 conclui-se que a interface se encontra ligada no mapa de
endereçamento de entrada/saı́da, uma vez que é seleccionada quando a linha IO/M estiver no nı́vel lógico ‘1’. Tanto no Z80, como em qualquer outro sistema computacional
com mapa especı́fico para I/O, esta situação só ocorre quando se utilizam instruções de
entrada/saı́da.
88
Não
Leitura do registo de estado
Interface livre?
Sim
Escrita no registo de dados
Figura 5.5 — Diagrama de fluxo do protocolo de programação para saı́da de dados.
IO/M
A7
A6
CS
Interface
C/D
A0
Dados
Controlo
Periférico
Estado
Dados
Figura 5.6 — Esquema de interligação da interface do exemplo 15.
89
Por outro lado, para seleccionarmos a interface (entrada ‘CS’ activa “baixa”) necessita
ter também, em simultâneo, as linhas A6 e A7 nos nı́veis lógicos ‘0’ e ‘1’, respectivamente.
Os endereços que seleccionam esta interface obedecem ao padrão:
A7 A6 A5 A4 A3 A2 A1 A0
1
0
X
X
X
X
X
l
onde ‘X’ representa um estado qualquer (‘0’ ou ‘1’).
O estado da linha A0 (‘l’) ao assumir o valor lógico ‘1’ selecciona o registo de estado e
ao assumir o valor lógico ‘0’ selecciona o registo de dados. Supondo que escolhemos (pura
opção) o valor lógico ‘0’ para as linhas A1 a A5, os endereços que seleccionam os registos
de estado e dados são, respectivamente, 81H e 80H. Na figura 5.7 mostra-se o algoritmo
proposto. As variáveis utilizadas são:
• PONT — ponteiro para a posição de memória cujo conteúdo vai ser transferido;
• CONT — contador do número de bytes que falta transferir;
• STATUS — endereço do registo de estado da interface. Supomos que se o registo
contém o valor ‘1’ a interface está ocupada e se for ‘0’ está livre;
• DATA — endereço do registo de dados da interface.
Na listagem seguinte apresenta-se uma implementação em assembly do Z80.
DATA
EQU
80H
STATUS EQU
81H
BLOCO
EQU
1000H
NUM
EQU
0FFH
LD
HL , BLOCO
; v a l o r i n i c i a l do p o n t e i r o
LD
A , (NUM)
; v a l o r i n i c i a l do c o n t a d o r
LD
B, A
LD
C , DATA
IN
A , ( STATUS )
; l e i t u r a do r e g i s t o de e s t a d o
AND
A
; a f e c t a a f l a g de z e r o
JR
NZ , L0
; i n t e r f a c e ocupada ?
L0 :
; s aı́ d a da i n f o r m a ç ã o e a c t u a l i z a
OUTI
; ponteiro e contador
JR
NZ , L0
; c o n t i n u a a t é t r a n s f e r i r t o d o s
; os b y t e s
END
90
INÍCIO
PONT = BLOCO
CONT = (NUM)
Sim
(STATUS) = 1?
Não
(DATA) = (PONT)
Não
PONT = PONT + 1
CONT = CONT -1
CONT = 0?
Sim
FIM
Figura 5.7 — Diagrama de fluxo para o protocolo de saı́da do exemplo 15.
5.3.2
Protocolo de programação para periféricos de entrada
De um modo idêntico aos periféricos de saı́da, as interfaces que estão ligadas a periféricos
de entrada têm também, para além dos registos de dados, um registo de estado.
O protocolo a estabelecer com este tipo de interfaces é análogo ao estudado para os
periféricos de saı́da figura 5.8.
Exemplo 16 (Ligação de uma interface de entrada) A figura 5.9 esquematiza a integração de um periférico de entrada num sistema de microcomputador controlado por um
Z80. Pretende-se ler para uma zona de memória a informação a receber do periférico de
entrada. Esta informação deve ser armazenada sequencialmente na memória do sistema
e a partir do endereço BLOCO, até que seja detectado um byte (código) com o valor 0DH.
A interface encontra-se integrada no mapa de endereçamento de I/O, podendo os seus
registos de dados e de estado ser acedidos respectivamente pelos endereços C0H e C1H. O
raciocı́nio efectuado para chegar a estas conclusões é análogo ao do exemplo 15, pelo que
se dispensa a sua apresentação.
As variáveis utilizadas no algoritmo são (figura 5.10):
• PONT — ponteiro para a posição de memória cujo conteúdo vai ser transferido;
• COD — código da informação que faz terminar a leitura do periférico;
91
Leitura do registo de estado
Não
Interface com informação válida?
Sim
Leitura do registo de dados
Figura 5.8 — Diagrama de fluxo do protocolo de programação para entrada de dados.
IO/M
A7
A6
CS
Interface
C/D
A0
Dados
Controlo
Periférico
Estado
Dados
Figura 5.9 — Esquema de interligação da interface do exemplo 16
92
INÍCIO
PONT = BLOCO
COD = 0DH
Não
(STATUS) = 0?
Sim
Não
(PONT) = (DATA)
PONT = PONT + 1
(DATA) = COD?
Sim
FIM
Figura 5.10 — Diagrama de fluxo para o protocolo de entrada de dados do exemplo 16.
• STATUS — endereço do registo de estado da interface;
• DATA — endereço do registo de dados da interface.
Na listagem seguinte apresenta-se uma implementação em assembly do Z80.
DATA
EQU
C0H
STATUS EQU
C1H
BLOCO
EQU
1000H
COD
EQU
0DH
LD
HL , BLOCO
; v a l o r i n i c i a l do p o n t e i r o
IN
A , ( STATUS )
; l e i t u r a do r e g i s t o de s t a t u s
AND
A
; a f e c t a a f l a g de z e r o
JR
NZ , L0
; i n t e r f a c e com i n f o r m a ç ã o v á l i d a ?
IN
A , (DATA)
; l e i t u r a da i n f o r m a ç ã o
LD
(HL ) , A
; armazena i n f o r m a ç ã o na memória
INC
HL
; actualiza ponteiro
CP
COD
; c ó d i g o = 0DH?
JR
NZ , L0
; c o n t i n u a a t é t r a n s f e r i r t o d o s
L0 :
; os b y t e s
END
93
Barramento do sistema computacional
Periférico 1
Interface 1
Periférico 2
Interface 2
..
.
Periférico n
Interface n
Figura 5.11 — Diagrama de blocos dum sistema computacional com periféricos de entrada e
periféricos de saı́da.
5.3.3
Protocolo de programação num sistema computacional
com periféricos de entrada e de saı́da
Nos dois últimos pontos analisámos como é que um CPU, sob controlo de um programa,
consegue transaccionar correctamente informação com periféricos de saı́da e de entrada.
Para o caso da figura 5.11, supondo que os periféricos recebem e enviam informação
mais lentamente do que o CPU a pode processar, qual será o protocolo de programação
a estabelecer, de modo a que o CPU possa efectuar transacções com todos os periféricos
simultaneamente?
Mantendo o raciocı́nio já efectuado para cada tipo de interface, o processador deve
consultar o registo de estado de cada interface e se: a interface estiver livre, então efectuar
a transacção correspondente; se a interface estiver ocupada, então consultar o registo de
estado da próxima interface.
Este raciocı́nio pode ser mais facilmente especificado na forma do fluxo-grama da
figura 5.12.
5.4
Tipos de interfaces
Até agora estudámos a ligação das interfaces aos barramentos do sistema computacional.
Mas..., quais os tipos de ligações dos circuitos de interface aos periféricos?
Existem fundamentalmente duas técnicas: ligação série; e ligação paralela.
Consoante o tipo de ligação assim se designam por interfaces série ou interfaces para94
INÍCIO
(Status 1) = Livre?
Transacção com
interface 1
Sim
Transacção com
Não
Sim
(Status 2) = Livre?
interface 2
Não
Sim
Sim
(Status n) = Livre?
Transacção com
interface n
Não
Mais transacções?
Não
FIM
Figura 5.12 — Diagrama de fluxo do protocolo para entrada e saı́da de dados.
95
Barramento de endereços
Descodificador
Barramento de dados
Interface
..
.
dados
Periférico
Paralela
handshak
Figura 5.13 — Diagrama de blocos de interligação de uma interface paralela.
lelas.
Os protocolos estudados para a comunicação entre as interfaces e o sistema computacional são mantidos válidos, independentemente do tipo de ligação destas aos periféricos.
5.4.1
Interface paralela
Estes circuitos ligam-se aos periféricos por um conjunto de linhas que transportam os
dados. A transferência de informação entre o bus de dados do sistema computacional e os
periféricos é feita usando as linhas do bus de dados, figura 5.13. Existem ainda do lado do
periférico, para além das linhas de dados, linhas de controlo (handshake) que controlam
a transferência e validam a informação presente nas suas linhas de dados.
Este tipo de interface utiliza-se para ligar periféricos que: se encontrem fisicamente
junto do sistema computacional; e/ou que necessitem de velocidades de transferência
relativamente elevadas.
5.4.1.1
Estudo da interface paralela Z80 PIO
O circuito de interface Z80 PIO (Parallel Input/Output), é um dispositivo com duas portas,
programável que fornece uma interface compatı́vel TTL.
O CPU pode configurar a PIO para interface com outros periféricos, sem necessidade
de lógica adicional, tais como:
• Teclados;
• Leitoras de papel e cartões;
• Impressoras;
• Programadores de ROMs;
• Etc..
96
É distribuı́do numa caixa com 40 pinos e entre outras facilidades apresentam-se as
seguintes:
• Duas portas de interface de 8 bits independentes bidireccionais, com handshake para
controlo da transmissão de dados;
• Handshake para interrupções para uma resposta mais rápida;
• Quatro modos distintos de funcionamento (todos eles com handshake para controlo
de interrupções):
• Saı́da de dados (byte);
• Entrada de dados (byte);
• Bus bidireccional (porta A apenas);
• Controlo (8 linhas);
• É incluı́da lógica para vectorização automática de interrupções, sem necessidade de
lógica externa, baseada no esquema Daisy Chain;
• 8 saı́das capazes de alimentar um par Darlington de transı́stores;
• Todas as entradas e saı́das são compatı́veis com os nı́veis TTL;
• Apenas uma fonte de alimentação de 5 volts e um relógio com apenas uma fase.
Um diagrama de blocos da Z80 PIO pode ser visto na figura 5.14.
A estrutura interna consiste em:
• Interface com o CPU;
• Lógica para controlo interno;
• Lógica da porta A;
• Lógica da porta B;
• Lógica para controlo de interrupções.
O diagrama de blocos de cada uma das portas, A e B, pode ser visto na figura 5.15.
Dele fazem parte:
• 6 registos com handshake incluı́do;
• Registo de 8 bits para entrada;
• Registo de 8 bits para saı́da;
97
Interface com
o CPU
+5V GND CLK
Interface com
Lógica
para
controlo
interno
Barramento
de dados
8
Barramento
de controlo
I/O com o
barramento
do CPU
os periféricos
8
barramento interno
6
Controlo de
interrupções
Dados ou
controlo
handshake
Porta
A
I/O
8
Porta
B
I/O
Dados ou
controlo
handshake
3
Linhas de controlo
das interrupções
Figura 5.14 — Diagrama de blocos do circuito Z80 PIO.
• Registo de 2 bits para controlo do modo de funcionamento;
• Registo de 8 bits para máscara (mask) de I/O (modo controlo);
• Registo de 2 bits para controlo do registo de máscara.
A figura 5.16, mostra a distribuição das linhas no chip.
Deste conjunto de linhas, fazem parte os grupos de:
• Controlo da PIO;
• Controlo de interrupções;
• Alimentação;
• Controlo da porta A;
• Controlo da porta B;
• Bus de dados.
A PIO entra no estado de reset a partir do momento que é alimentada. Neste estado
são efectuadas as operações de:
• Reset aos registo de máscara de ambas as portas para desabilitar todos os bits das
portas de dados;
• As linhas do bus de dados são colocadas num estado de alta impedância e os sinais
de READY são postos a ‘0’;
98
Registo de
selecção de
I/O (8 bits)
Registo de
controlo de
modo (2 bits)
Habilitação da saı́da
Registo de
saı́da de
dados (8 bits)
barramento interno
8 bits para o periférico
(dados ou controlo)
Registo de
controlo de
mask (2 bits)
Registo de
mask (8 bits)
dados de ent.
Registo de
entrada de
dados (8 bits)
Ready
Lógica para
controlo de
handshake
Pedidos de
interrupção
Strobe
Linhas de
handshake
Figura 5.15 — Diagrama de blocos das portas A e B do circuito Z80 PIO.
D0
D1
D2
D3
D4
D5
D6
D7
Selecciona porta (B/A)
Selecciona controlo/dados
Habilita chip
M1
IORQ
RD
+5V
GND
CLK
19
20
1
40
39
38
3
2
6
5
4
37
36
35
26
11
25
8
Linhas 7 a 15
A7−A0
18
16
A RDY
A STB
8
Z80 PIO
Linhas 27 a 34
B7−B0
21
17
B RDY
B STB
23
24
22
INT
Habilita INT IN
Habilita INT OUT
Figura 5.16 — Esquema de ligações do circuito Z80 PIO.
99
Modo 3 apenas
INT
AND/ HIGH/ Mask
Enable OR
LOW follows
0
1
1
1
MB MB MB MB MB MB MB MB
MB = 0 Bit monitorizado
MB = 1 Bit não monitorizado
Figura 5.17 — Programação das interrupções do circuito Z80 PIO.
• Selecção do modo 1 de funcionamento;
• Aos vectores de endereçamento não é feito reset;
• Reset aos flip-flops de interrupção das portas;
• É feito um reset aos registos de saı́da das portas.
A escolha do modo de funcionamento faz-se com base na palavra:
M1 M0 X X 1 1 1 1 ,
onde M1 e M0 podem ter os valores:
00 − s aı́ d a
01 − e n t r a d a
11 − b i d i r e c c i o n a l
11 − c o n t r o l o .
No caso de escolha do modo de controlo, a próxima palavra indica quais as linhas de
entra/saı́da com base na seguinte máscara:
I /O I /O I /O I /O I /O I /O I /O I /O,
onde I/O pode ter o valor ‘1’ para entra e ‘0’ para saı́da.
A palavra para leitura do vector de interrupções é a seguinte:
V7 V6 V5 V4 V3 V2 V1 0
Note-se que o bit menos significativo é zero.
As interrupções são controladas com base na palavra apresentada na figura 5.17.
É possı́vel fazer o set ou reset do flip-flop de habilitação de interrupções sem modificar
o estado do resto da palavra de controlo de interrupções, usando a palavra
IE X X E 0 0 1 1
100
onde,
IE=0 − d e s a b i l i t a i n t e r r u p ç õ e s ;
IE=1 − h a b i l i t a i n t e r r u p ç õ e s ;
E =0 − não limpa i n t e r r u p ç õ e s p e n d e n t e s ;
E =1 − limpa i n t e r r u p ç õ e s p e n d e n t e s ;
X
− tanto faz .
Exemplo 17 (Exemplos de programação da PIO) Programar a porta B da PIO para
saı́da:
LD
A, 0 FH
OUT
(PIOBP ) , A
Programar a porta A da PIO para entrada:
LD
A, 4 FH
OUT
(PIOAP ) , A
Programar a PIO para o modo bidireccional:
LD
A, 8 FH
OUT
(PIOAP ) , A
Programar a porta A da PIO para controlo com as linhas 1, 5 e 6 para entrada e as linhas
0, 2, 3, 4 e 7 para saı́da:
5.4.2
LD
A, 0CFH
OUT
(PIOAP ) , A
LD
A, 0 1 1 0 0 0 1 0B
OUT
(PIOAP ) , A
Interface série
Quando se pretendem ligar ao sistema computacional periféricos que se encontrem fisicamente afastados usam-se as interfaces série.
A razão principal é o reduzido número de linhas necessárias para esta ligação, contrariamente à ligação paralela, figura 5.18. Quantas linhas de dados eram necessárias numa
interface paralela para transmitir 8 bits de informação?
Uma outra razão para a existência da comunicação série é que normalmente os periféricos nas condições descritas atrás não necessitam de velocidades de transferência elevadas. Não se deve confundir necessidade com desejo de taxas de transferência mais
elevadas!
A ideia base assenta em transmitir por uma única linha a informação existente num
registo paralelo da interface. O tipo de funcionamento que se pretende é o de um shift
101
Barramento de endereços
Descodificador
Barramento de dados
Interface
dados
Periférico
Série
handshak
Figura 5.18 — Exemplo de interligação de uma interface série.
register, onde a informação lhe é fornecida em paralelo pelo sistema computacional e
sai para o exterior pela sua linha série à velocidade imposta por um sinal de relógio
conveniente. A transmissão pode ser half duplex ou full duplex.
Quando se pretende receber informação série, esta ideia continua válida. O sinal que
contém a informação entra pela linha de serial input do shift register e vai sendo deslocado
nos flip-flops internos do registo com o auxı́lio de um sinal de relógio cuja frequência é a
mesma que a do relógio de transmissão do sinal.
Uma vez que os dados não passam de um sinal que vai mudando o seu estado assincronamente, existem dois problemas que é necessário resolver:
• Amostrar o sinal em intervalos de tempo que correspondam aos bits individuais;
• Existência de um mecanismo que transforme os bits recebidos em caracteres válidos.
Para resolver o primeiro problema basta que exista um relógio de sincronismo que
tenha a mesma frequência no receptor e no emissor. Para garantir a integridade dos
caracteres torna-se necessário indicar quando é que um caracter começa e quando é que
ele acaba.
Se soubermos por quantos bits é formado um caracter basta, sabermos quando é que
ele começa para sabermos quando é que acaba. Na transmissão série é usual encontrar
caracteres com 5, 6, 7 e 8 bits de comprimento.
Para indicar quando é que começa (quando deve começar a contar) geralmente são
usadas duas soluções distintas: transmissão sı́ncrona e transmissão assı́ncrona.
Na transmissão série sı́ncrona é necessário existir um bit de dados válido em cada
transição activa do sinal de relógio. Uma vez garantido que as frequências dos sinais
de relógio, a menos de um certo erro, são iguais de um e do outro lado da linha, os
dados transmitidos irão ser reconhecidos adequadamente na estação receptora. Os limites
de dados válidos são indicados pela introdução de uma sequência de bits conhecida por
ambas as estações. Esta sequência de bits é designada por caracter de sincronismo (SYNC
character). Tipicamente este caracter pode ter o valor 01101001. Podem existir 1 ou 2
102
start
registo
01110011
1
1
0
0
1
1
1
0 pari. stop
relógio
Figura 5.19 — Transmissão série assı́ncrona.
caracteres de sincronismo antes de cada bloco de dados. A estação que recebe pode estar
à espera destes caracteres de sincronismo afim de reconhecer o inı́cio de um conjunto de
dados válido. O primeiro bit a seguir a um SYNC é o primeiro bit válido do primeiro
caracter. Este tipo de transmissão série é pouco utilizado. (Porquê?)
Na transmissão série assı́ncrona os caracteres só são transmitidos quando existirem
dados válidos. Entre as transmissões de dados válidos, o sinal de saı́da é mantido “alto”,
designando-se este estado por mark. Cada caracter a transmitir é “encapsulado” por um
bit de inicio de transmissão, designado por start bit, e 1, 1.5 ou 2 bits de fim, designados
por stop bits. O start bit é sempre um ‘0’ (nı́vel baixo), enquanto que o(s) stop bit(s)
é (são) ‘1’ (nı́vel alto). Entre este bits é que são transmitidos os bits que constituem o
caracter. Existe ainda a possibilidade de transmissão de um bit de paridade, para controlo
de erro, antes do(s) stop bit(s). Veja-se a figura 5.19.
Qual a máxima e mı́nimas percentagens de tempo útil numa transmissão deste tipo?
Resposta: assistam às aulas teóricas!
5.4.2.1
Estudo da interface série Am8251
O chip Am8251 foi desenhado para transmissão de dados série. É uma USART (Universal
Synchronous/Asynchronous Receiver/Transmitter) programável. Oferece as capacidades
de:
• Transferência de dados sı́ncrona ou assı́ncrona;
• Sinalização half ou full duplex;
• Os dados são transmitidos em forma de caracteres com 5, 6, 7 ou 8 bits/caracter;
• Bit de paridade par, ı́mpar ou nenhum;
• Controlo dos sinais modem efectuados pelo micro-processador;
• O caracter de SYNC programável;
• Registos separados para códigos de controlo e para escrita de dados para a lógica
de transmissão (apenas no Am9551).
103
TxRDY TxE
RD WR Reset CLK CS C/D
RxRDY SYNDET
Bus de dados
D7−D0
Buffers do
Lógica de controlo de
bus de dados
Read/Write
Interface com o
microprocessador
Secção de controlo
de registos
Bus interno
Secção receptora
RxD
RxC
Controlo de
modem
DTR RTS
DSR CTS
Secção transmissora
Conjunto da
interface de dados
TxC
TxD
Figura 5.20 — Diagrama de blocos do circuito Am8251.
Como pode ser visto na figura 5.20, este circuito é constituı́do pelos seguintes blocos:
• Control register;
• Transmitter;
• Receiver;
• Modem control;
• Read/write control.
O control register (receptor e transmissor) recebe palavras de controlo de 8 bits enviados pelo CPU, sendo estas palavras usadas para estabelecer e/ou alterar o modo de
funcionamento e controlar os nı́veis dos sinais.
Por seu lado, o transmissor (transmitter) recebe dados de 8 bits do bus de dados do
CPU e adiciona-lhes bits de formatação de acordo com o modo de operação estabelecido
pelo control register. Depois transmite a informação por uma linha série; são também
gerados sinais apropriados que indicam se os transmit registers estão vazios, por forma a
que o CPU possa enviar outro dado de 8 bits.
A secção de transmissão contém dois registos (buffers): output register — os dados são
transmitidos a partir deste registo; transmitter buffer register — onde o CPU escreve os
dados. Internamente, o Am8251, detecta quando o output register está vazio, carregando-o
em seguida com os dados do transmitter buffer register. Desta forma o CPU pode escrever
104
RD
WR
C/D
CS
Comentários
0
1
0
0
receiver register → data bus
1
0
0
0
data bus → transmitter register
0
1
1
0
status register → data bus
1
0
1
0
data bus → control register
X
X
X
1
data bus → high impedance
Tabela 5.1 — Operação funcional do Am8251 (USART).
um byte para ser transmitido (no transmitter buffer register) enquanto outro está a ser
transmitido.
O receptor (receiver) aceita dados chegados via série, convertendo-os em caracteres,
retirando-lhes os bits colocados a mais pelo transmissor do outro lado da linha, de acordo
com as condições impostas pelo seu control register, envia dados para o bus de dados do
CPU quando este o desejar e desenvolve a sinalização necessária para se saber quando
existe um dado acabado de chegar ou a não existência de qualquer dado. É constituı́do
por dois registos: receiver input register — onde vão sendo recebidos os vários bits que
constituem um caracter; e receiver buffer register — onde, depois de ter sido recebido um
caracter completo pelo receiver input register, vão ser colocados os bits automaticamente
para posterior leitura por parte do CPU.
O modem control contém sinais de controlo standard para coordenar a operação de
interface de comunicações e conjunto de dados.
O bloco read/write fornece a interface com o micro-processador.
No que se refere ao endereçamento, existem quatro linhas responsáveis pela selecção
da operação que pretendemos efectuar:
• Read (RD);
• Write (W R);
• Control/data (C/D);
• Chip select (CS).
Na tabela 5.1, mostra-se a operação funcional durante um read ou write.
A USART deve ser inicializada a seguir a um reset do sistema, antes que um dado possa
ser transmitido. Para se proceder à sua inicialização deve enviar-se duas, três ou quatro
palavras, dependendo do modo de funcionamento pretendido. Existem duas formas de
provocar o retorno da lógica de controlo ao mo-de de controlo (mode control): através de
um comando especı́fico; ou a seguir a um reset.
O Am8251 pode funcionar no modo sı́ncrono ou assı́ncrono, dependendo dos códigos
de modo de controlo. Estes códigos podem ser vistos na figura 5.21.
105
7 6 5 4 3 2 1 0
00
01
10
11
—
—
—
—
Modo
Modo
Modo
Modo
sı́ncrono
assı́ncrono baude rate factor 1
assı́ncrono baude rate factor 16
assı́ncrono baude rate factor 64
00
01
10
11
—
—
—
—
5
6
7
8
por
por
por
por
bits
bits
bits
bits
caracter
caracter
caracter
caracter
1 — habilita controlo de erro por paridade
0 — paridade ı́mpar; 1 — paridade par
Modo sı́ncrono
00 — 2 caracteres SYNC, SYNDET output
01 — 2 caracteres SYNC, SYNDET input
10 — 1 caracter SYNC, SYNDET output
11 — 1 caracter SYNC, SYNDET input
Modo assı́ncrono
00 — inválido
01 — 1 stop bit
10 — 1,5 stop bits
11 — 2 stop bits
Figura 5.21 — Códigos de programação do Am8251 nos modos sı́ncrono ou assı́ncrono.
A palavra de sincronismo (sync word), apesar de fazer parte dos códigos de controlo,
na realidade não controla nada na USART. É a palavra a ser transmitida como palavra de
sincronismo. A primeira palavra a ser enviada para a USART é a palavra de modo, se esta
por sua vez especificar que o modo de operação desta é sı́ncrono, então a(s) próxima(s)
palavra(s) será(ão) a(s) palavra(s) de sincronismo.
As palavras de comando (command words) são usadas para inicializar certas funções,
como por exemplo:
• Reset de todas as flags de erro;
• Iniciar a pesquisa de um SYNC.
Estas palavras podem ser enviadas pelo micro-processador em qualquer instante. Durante
o processo de inicialização a última palavra é de comando. Veja-se a figura 5.22.
O registo de estado (status register) contém toda a informação acerca do estado actual
do Am8251. É por consulta a este registo que o programa responsável pela transmissão
sabe se existem ou não erros. Veja-se a figura 5.23.
Exemplo 18 (Exemplo de programação do Am8251) Programar a USART para transmitir assincronamente um conjunto de 50 bytes, colocados nas posições de memória a
partir do endereço INIT, com as seguintes caracterı́sticas:
• factor de baud rate 16;
106
7
6 5
4
3
2
1 0
Txe — ‘1’ habilita a transmissão
DTR — ‘1’ a linha de saı́da DTR é forçada a ‘0’
RxE — ‘1’ habilita a recepção
SBRK — Send BreaK
ER — reset das flags de erro
RTS — ‘1’ a linha RTS é forçada a ‘0’
IR — entrada no modo “ocupado”
EH — procura por um caracter de SYNC
Figura 5.22 — Códigos de programação do Am8251: palavras de comando.
7
6 5
4
3
2
1 0
TxRDY
RxRDY
TxE
Parity error
Overrun error
Framing error (modo assı́ncrono apenas)
SYNDET
DST
Figura 5.23 — Códigos de programação do Am8251: consulta do registo de estado.
107
• 8 bits por caracter;
• controlo de erros por paridade desabilitada;
• 2 stop bits.
O código assembly apresentado em seguida mostra-nos uma solução para este problema.
L0 :
LD
A, 1 1 0 0 1 1 1 0B
OUT
(URTC) , A
LD
A, 3
OUT
(URTC) , A
LD
HL , INIT
LD
B, 5 0
IN
A , ( URTC)
BIT
2, A
JR
Z , L0
LD
A , ( HL)
OUT
(URTD) , A
DJNZ
L0
HALT
108
; mode c o n t r o l word
; command word
; status register
; t r a n s m i t e um c a r a c t e r
Capı́tulo 6
Interrupções
6.1
Introdução
No capı́tulo anterior estudámos os protocolos de comunicação entre o sistema computacional e as interfaces. Se analisarmos os referidos protocolos verificamos que o processador
gasta grande parte do seu tempo a ler o registo de estado das interfaces para conhecer o
seu estado e poder aceder-lhes no momento certo. Daqui resulta uma grande perda de
versatilidade, pois enquanto o CPU está a fazer uma coisa não pode fazer outra!
Seria muito mais interessante que quando as interfaces estivessem prontas para receber
ou enviar informação (de ou para o sistema computacional) o sinalizassem, ou seja, o
interrompessem.
Neste capı́tulo vamos estudar os problemas levantados por este tipo de abordagem,
isto é, pelas interrupções.
6.2
Considerações gerais
Num sistema de entrada/saı́da simples, a única maneira que o processador tem de verificar
o estado de determinada interface é a de amostrar continuamente o seu registo de estado.
Ora é de esperar que um sistema computacional mantenha em funcionamento um
conjunto de periféricos, tais como:
• Terminais;
• Discos;
• Impressoras;
• Etc..
Para que estes funcionem com um elevado grau de eficiência o processador não pode
perder o seu tempo a amostrar os registos de estado das diferentes interfaces.
109
Um sistema com interrupções resolve este problema ao permitir que o processador seja
sinalizado sempre que determinada interface se encontre livre. O processador inicia em
seguida um processo de entrada/saı́da com essa interface, ficando livre para outras tarefas
imediatamente de seguida (enquanto o periférico troca a informação com a interface).
Usando um sistema de interrupções:
• Cada periférico envia um pedido de interrupção;
• O processador aceita o pedido de interrupção, suspendendo momentaneamente a
execução do programa em curso;
• Executa a rotina de atendimento da interrupção, associada à interface em questão;
• Depois de terminada a execução da rotina de atendimento da interrupção, o processador regressa à execução do programa que tinha interrompido.
Do ponto de vista do programa interrompido, este facto é-lhe perfeitamente transparente, uma vez que a forma como é executado se mantém inalterada, havendo apenas um
aumento de tempo. (Porquê?)
Após a execução de uma instrução, o processador consulta sempre uma flag interna
de modo a saber se foi ou não feito um pedido de interrupção.
O CPU passa pelas seguintes fases no processamento de uma instrução:
• Busca (fetch);
• Execução;
• Teste de interrupções.
Note-se que a fase de teste de pedido de interrupção só acontece depois da instrução
ter sido executada! Que é que isto quer dizer? Se, por exemplo, o CPU se encontrava a
executar a instrução ADD 55 e houvesse um pedido de interrupção, este só seria visto, e
possivelmente atendido, depois de toda a instrução ter sido executada, ou seja, depois de
ter sido lido da memória o operando 55, ter sido adicionado ao conteúdo do acumulador
e o resultado depositado no acumulador.
Contudo, existem processadores que permitem ou aceitam interrupções durante a fase
de execução de uma instrução. Como iremos ver, esta possibilidade complica muito fortemente a arquitectura de um CPU.
O processador tem meios de inibir completamente as interrupções actuando num bit
interno de estado através de uma instrução especı́fica do seu conjunto de instruções.
Estando as interrupções habilitadas e sendo detectada a ocorrência de uma, o processador
toma, genericamente, as seguintes acções:
110
Inı́cio
Salvaguarda do estado do CPU
Tarefas de transferência de informação
Restaura o estado do CPU
Habilita as interrupções
Fim
Figura 6.1 — Diagrama de fluxo genérico de uma rotina de serviço à interrupção.
1. Inibe as interrupções. Permite ao CPU garantir que a próxima instrução a ser executada pertença à rotina de serviço à interrupção detectada, cabendo ao programador
decidir se dentro desta rotina serão permitidas ou não mais interrupções.
2. Guarda total ou parcialmente o seu estado. O CPU deve guardar o conteúdo do
PC para que lhe seja possı́vel regressar ao programa interrompido após ter servido a
interrupção. Devem ser salvaguardados todos os registos utilizados dentro desta rotina, nomeadamente o registo de flags, de modo a que a ocorrência duma interrupção
não altere a execução do programa interrompido.
3. Detecta a interface que pediu a interrupção. A identificação da interface que pediu
a interrupção pode ser feita por vários processos, como iremos ver mais à frente.
4. Identifica a razão do pedido e atende-o. Traduz-se na utilização de instruções para
permitir a transferência de informação entre o sistema computacional e a interface
(instruções de entrada/saı́da de dados).
5. Restaura o seu estado e regressa ao programa interrompido. É aqui que se faz a
restauração dos registos internos do CPU (estado do processador), a habilitação do
uso de interrupções e o regresso ao programa interrompido.
Na figura 6.1 pode ser visto o diagrama de fluxo genérico de uma rotina de serviço à
interrupção.
6.3
Interrupções múltiplas e prioridades
Quando existem vários periféricos no sistema, como é que se sabe qual o periférico que
pretende interromper? E se ocorrerem dois pedidos de interrupção simultâneos, qual o
111
Z80
ADC
0801
74LS366
D1
Dispositivo 1
D0
IORD
STATUS
A7
A6
A5
A4
A3
A2
A1
A0
Do bus de endereços
ADC
0801
Para o bus de dados
INT
Dispositivo 2
Figura 6.2 — Exemplo de atendimento de interrupções por polling (ver texto).
primeiro a ser atendido?
Para a resolução destes problemas existem essencialmente duas técnicas: método de
polling; método de vector de interrupção.
6.3.1
Polling
Neste método, o micro-processador interroga cada um dos dispositivos usando o conjunto
de instruções (software), identificando o dispositivo que pretende ser atendido. Depois
transfere a execução do programa para a rotina de serviço apropriada.
O software determina a prioridade entre os dispositivos que pedem interrupção e serveos de acordo com as prioridades estabelecidas durante o desenvolvimento do programa.
Exemplo 19 (Atendimento de interrupções por polling) A figura 6.2 mostra um
exemplo onde dois conversores A/D estão ligados por uma interface ao Z80, utilizando o
modo 1 de interrupção. (Vamos estudar este modo em pormenor mais à frente.)
A seguinte rotina de serviço à interrupção estabelece o dispositivo 1 como o que possui
mais alta prioridade, isto é, prioridade superior ao dispositivo 2.
112
MDO1:
PUSH
AF
; g u a r d a o c o n t e ú d o d o s r e g i s t o s
IN
A , ( STATUS)
; l ê a p o r t a i n v e r s o r a t r i −s t a t e
AND
00000011B
; i g n o r a o e s t a d o d a s l i n h a s D7
; a D2
; c o l o c a D0 na f l a g de c a r r y
RRA
CALL
C , DVC1
; s e D0=1 v a i p a r a o DVC1
; para l e r os dados
; c o l o c a D1 na f l a g de c a r r y
RRA
CALL
C , DVC2
; s e D1=1 v a i p a r a o DVC2
; para l e r os dados
POP
AF
; r e p õ e o c o n t e ú d o d o s r e g i s t o s
; h a b i l i t a i n t e r r u p ç õ e s
EI
RETI
DVC1:
PUSH
AF
; g u a r d a o c o n t e ú d o d o s r e g i s t o s
IN
A , ( ADC1)
; l ê d a d o s do d i s p o s i t i v o 1
LD
(HL ) , A
; g u a r d a o s d a d o s na memória
OUT
(ADC1) , A
; i n i c i a a próxima c o n v e r s ã o
POP
AF
; r e p õ e o c o n t e ú d o d o s r e g i s t o s
PUSH
AF
; g u a r d a o c o n t e ú d o d o s r e g i s t o s
IN
A , ( ADC2)
; l ê d a d o s do d i s p o s i t i v o 2
LD
(HL ) , A
; g u a r d a o s d a d o s na memória
OUT
(ADC2) , A
; i n i c i a a próxima c o n v e r s ã o
POP
AF
; r e p õ e o c o n t e ú d o d o s r e g i s t o s
RET
DVC2:
RET
Como facilmente se pode concluir por este exemplo, bastaria trocar a ordem do teste
à flag de carry (e consequente chamada à sub-rotina de atendimento) que alterarı́amos a
ordem de prioridade de atendimento dos dispositivos.
6.3.2
Vector de interrupção
Neste método, o dispositivo que interrompe indentifica-se automaticamente, fornecendo
para o efeito uma instrução ou um endereço (tudo feito por hardware). Quando surgem
dois dispositivos a pedir interrupção ao mesmo tempo, a prioridade de atendimento é
determinada também pelo hardware, utilizando, por exemplo, o método de Daisy Chain.
Exemplo 20 (Vector de interrupção) A figura 6.3 mostra um esquema para a implementação de interrupções com vários dispositivos usando o codificador 8-para-3 74LS148.
Este circuito:
113
Codificador de prioridades
8 para 3
buffer tri−state
+5V
+5V
16
I7
I6
I5
I4
I3
I2
I1
I0
4
3
2
1
10
11
12
13
Vcc
GND
8
74LS366
6
A2
2
3
4
A1
4
5
2
A0
6
7
10
9
0
GS 74LS148 E1
10K
+5V
+5V
1
15
INT
INTA
(para o Z80)
(vindo do Z80)
+5V
10K
bus de dados
D7
D6
D5
D4
D3
D2
D1
D0
Figura 6.3 — Exemplo de atendimento de interrupções por vector de interrupção (ver texto).
114
• Tem 8 linhas de entrada e 3 de saı́da;
• A saı́da compreende os valores de 000 a 111;
• As saı́das estão invertidas. Se, por exemplo, o dispositivo ligado à entrada 7 estiver
activo, a saı́da terá o valor 000;
• Determina automaticamente a mais alta prioridade. Se, por exemplo, as entradas
6 e 4 estiverem activas ao mesmo tempo, ignora a entrada 4 e coloca na saı́da o
código correspondente à entrada 6;
• Fornece as combinações apropriadas nas suas linhas de saı́da A0, A1 e A2. Estas
devem estar ligadas às linhas de dados D1, D2 e D3 do CPU. A linha D0 deve ser
forçada a ‘0’ por causa dos requisitos do modo 2 de interrupção do Z80.
Com base no esquema da figura 6.3, se o dispositivo colocado na entrada I0 pedisse interrupção, a saı́da do codificador seria 111. Esta saı́da seria invertida pelo buffer tri-state
74LS366 e colocada no bus de dados. O valor lido pelo Z80, supondo que este estava a
funcionar no modo 2 de interrupções, seria F0H.
A figura 6.4 mostra uma possı́vel implementação do controlador utilizando um esquema
Daisy Chain.
6.4
Interrupções no Z80
O Z80 tem duas linhas de pedido de interrupção, que correspondem a outros tantos tipos
de interrupção. São elas:
• Linha INT (Interrupt);
• Linha NMI (Non Maskable Interrupt).
O Z80 possui ainda dois flip-flops internos para inibição/activação de interrupções
(IFF1 - flag de permissão de interrupção e IFF2 - flag auxiliar para salvaguarda de IFF1),
um registo de dois bits que guarda o modo de interrupção actual (Interrupt Mode — IM) e
o registo I (Interrupt vector), vector de interrupção. Na figura 6.5 podemos ver o sistema
de interrupção do Z80.
6.4.1
Interrupções mascaráveis
Quando a linha de entrada INT “vem a baixo”, posta neste estado por qualquer interface, esta sinaliza o Z80 de um pedido de interrupção. Esta linha pode ser mascarada
115
5
3.ST.
EN
Prioridade decrescente
Registo de estado
Periférico 3
6
1
1
0
3.ST.
EN
Registo de estado
Periférico 2
7
1
1
1
3.ST.
EN
Bus de dados (código do periférico)
1
0
1
Registo de estado
Periférico 1
INT
INTA
Controlo por daisy chain
Figura 6.4 — Estabelecimento de prioridades no atendimento de interrupções com recurso ao
esquema Daisy Chain.
Z80
INT
Flags
NMI
IFF1
IFF2
Registos
IM
I
M1
IORQ
INTA
Figura 6.5 — Esquema resumido do sistema de interrupções do Z80.
116
activando um flip-flop interno ao CPU através de uma instrução apropriada de inibição
de interrupções — DI (Disable Interrupts).
Uma vez permitidas as interrupções, com a utilização da instrução EI (Enable Interrupts), o Z80 tem três modos de funcionamento possı́veis, sendo estes modos seleccionáveis
pelo programador através das instruções:
• IM 0 — Interrupt Mode 0;
• IM 1 — Interrupt Mode 1;
• IM 2 — Interrupt Mode 2.
Em qualquer um destes modos o retorno ao programa interrompido é feito pela instrução RETI (RETurn from Interrupt) que restaura o valor do PC a partir da stack.
6.4.1.1
Modo 0 (interrupção vectorizada)
O CPU, após verificar e aceitar o pedido de interrupção da interface, salvaguarda o
conteúdo do PC na stack e gera um ciclo de interrupt acknowledge, activando os sinais
IORQ e M1, através do qual a interface que faz o pedido coloca no bus de dados o código
da instrução, normalmente de reinicio (RST — Restart) que o CPU deve executar. O
código da instrução é então colocado no bus de dados (pode ser RST 0, RST 8, RST
10H, ..., RST 38H) e esta instrução direcciona o controlo do sistema para a rotina de
serviço à interrupção da interface, figura 6.6.
6.4.1.2
Modo 1 (interrupção por pesquisa)
Este modo é idêntico em tudo ao modo 0, sendo o endereço de restart, sempre, 38H (RST
38H). Veja-se a figura 6.7.
6.4.1.3
Modo 2 (interrupção vectorizada)
Este modo é o mais poderoso. O endereço de inı́cio da rotina de atendimento de interrupção é calculado da seguinte forma:
1. Em primeiro lugar é calculado o endereço de entrada numa tabela de interrupções
guardada na memória:
• A parte mais significativa deste endereço (8 bits) é fornecida pelo registo I;
• A parte menos significativa (designada por vector de interrupção) fornecida,
pelo periférico que interrompe, na fase de interrupt acknowledge, figura 6.8
5.3.1. - 3;
117
Inı́cio do ciclo de interrupção
IFF1 e INT activadas?
Não
Continuação
do programa
Sim
Automaticamente:
Desactivação de IFF1 e IFF2
Salto para a rotina de serviço do periférico
(normalmente RST n para salvaguardar PC na stack)
Por software:
Salvaguarda do estado do CPU
Serviço do periférico
Restauro do estado do CPU
Reactivação de IFF1 e IFF2
Regresso ao programa interrompido
Figura 6.6 — Diagrama de fluxo de atendimento das interrupções para o modo 0 do Z80.
Inı́cio do ciclo de interrupção
IFF1 e INT activadas?
Não
Continuação
do programa
Sim
Automaticamente:
Desactivação de IFF1 e IFF2
Salto para o endereço 0038H com salvaguarda do PC na stack
Por software:
Salvaguarda do estado do CPU
Pesquisa do periférico
Serviço do periférico
Restauro do estado do CPU
Reactivação de IFF1 e IFF2
Regresso ao programa interrompido
Figura 6.7 — Diagrama de fluxo de atendimento das interrupções para o modo 1 do Z80.
118
Registo I
ENDL
Vector
ENDH
..
.
Endereços crescentes
Memória
..
.
Endereço da tabela
Figura 6.8 — Exemplo de tabela de interrupções para o modo 2 do Z80.
2. Os dois bytes lidos da posição de memória com endereço calculado no ponto anterior,
constituem o endereço efectivo da rotina de atendimento da interrupção figura 6.9
5.3.1- 4.
Este endereço deve ser sempre um número par. Como vimos anteriormente, a memória
encontra-se organizada em bytes, sendo necessários dois bytes para formar um endereço.
Estes bytes devem estar agrupados dois a dois na memória por forma a facilitar a sua
manipulação. Assim, convencionou-se que o byte menos significativo deste endereço estaria guardado num endereço par. Daı́ resulta que o bit menos significativo do vector de
interrupção seja sempre ‘0’.
6.4.2
Interrupções não mascaráveis
Existe uma linha de entrada no Z80, a linha NMI, que permite interromper o seu funcionamento independentemente do estado do flip-flop de interrupt disable. Diz-se então que
este tipo de interrupção é não mascarável.
Estas interrupções estão previstas, devendo ser utilizadas em situações extremas, como
quando se detecta a falta de energia. Existem sistemas que ao detectar esta situação,
interrompem o seu funcionamento normal, guardando numa memória de suporte não
volátil todo o estado da máquina por forma a poder recuperar a situação existente mal a
energia regresse.
O Z80 ao detectar uma interrupção deste tipo salta para a posição de memória 66H
(após ter guardado na stack o conteúdo do PC) onde inicia a execução da rotina de serviço
à interrupção.
O retorno ao programa é feito pela instrução RETN (RETurn from Non-maskable
interrupt) que restaura o valor de IFF1 a partir de IFF2 e o valor do PC a partir da stack,
figura 6.10.
119
Inı́cio do ciclo de interrupção
IFF1 e INT activadas?
Não
Continuação
do programa
Sim
Automaticamente:
Desactivação de IFF1 e IFF2
Salvaguarda do PC na stack
Salto para a rotina de serviço do periférico cujo endereço se
encontra na posição de memória dada pelo registo de
interrupção I e pelo vector de interrupção colocado no bus de
dados pelo periférico que interrompe
Por software:
Salvaguarda do estado do CPU
Serviço do periférico
Restauro do estado do CPU
Reactivação de IFF1 e IFF2
Regresso ao programa interrompido
Figura 6.9 — Diagrama de fluxo de atendimento das interrupções para o modo 2 do Z80.
120
Inı́cio do ciclo de interrupção
Não
NMI activada?
Continuação
do programa
Sim
Automaticamente:
Salvaguarda IFF1 em IFF2
Desactivação de IFF1
Salto para endereço 0066H com salvaguarda do PC na stack
Por software:
Salvaguarda do estado do CPU
Pesquisa do periférico (se necessário)
Serviço do periférico
Restauro do estado do CPU
Regresso ao programa interrompido
Figura 6.10 — Diagrama de fluxo de atendimento das interrupções não mascaráveis do Z80.
6.4.3
Programa de entrada/saı́da usando interrupções
Nesta ponto vamos abordar um programa para entrada/saı́da de dados, mas que recorre à
utilização de interrupções para efectuar as transacções. Deve-se comparar este programa
com os apresentados na secção 5.3.
Exemplo 21 (Entrada/saı́da com recurso a interrupções) Suponhamos que pretendı́amos implementar o algoritmo proposto no exemplo apresentado no ponto 5.3.1. Suponhamos ainda que o registo de estado da interface tem um bit onde o CPU pode escrever e
cujo estado determina a possibilidade da interface pedir uma interrupção ao CPU. Assim,
se o bit 7 do registo de estado estiver a:
• ‘0’ — interface com interrupção inibida;
• ‘1’ — interface com interrupção permitida.
A interface gera um pedido de interrupção ao CPU quando o seu bit 0 indicar estado
livre e o bit 7 se encontra a ‘1’. As posições de memória P e P+1 contêm o ponteiro para
o bloco de memória cujo conteúdo se pretende enviar para o periférico. Na figura 6.11
pode ver-se o diagrama de fluxo correspondente. Na listagem seguinte apresenta-se uma
possı́vel implementação em assembly do Z80.
121
P
EQU
0F8FDH
INIB
EQU
80H
DATA
EQU
80H
STATUS
EQU
81H
NUM
EQU
0FFH
SAIDA :
PUSH
AF
; salvaguarda
PUSH
HL
; registos
PUSH
BC
;
LD
HL , ( P)
; l ê p o n t e i r o
LD
A , (NUM)
; l ê n . de b y t e s
LD
B, A
LD
C , DATA
; t r a n s f e r e dado
OUTI
; act . ponteiro ,
; act . n . bytes
;a transferir
LD
(P ) , HL
; memoriza p o n t e i r o
; e contador
LD
A, B
LD
(NUM) , A
JR
NZ , NAO
; se t r a n s f e r i u
; t o d o s p á r a
LD
A , INIB
; i n i b e i n t e r r u p ç õ e s
; no p e r i f é r i c o
NAO:
OUT
(STATUS ) , A
POP
BC
POP
HL
POP
AF
; restaura estado
EI
; p e r m i t e i n t e r r u p ç õ e s
RETI
; r e t o r n a da r o t i n a de
; s e r v i ç o à i n t e r r u p ç ã o
END
Na tabela 6.1 pode ver-se um sumário do processo de interrupções do micro-processador
Z80.
122
Inı́cio atendimento de interrupção
Salvaguarda registos internos
Lê ponteiro para a próxima
posição de memória a
transferir
Lê número de bytes
Sim
Tudo transferido?
Inibe interrupções da
interface
Não
Transfere byte
Actualiza ponteiro
Actualiza contador
Guarda ponteiro
Guarda contador
Restaura registos
Habilita interrupções
Retorna ao programa interrompido
Figura 6.11 — Diagrama de fluxo do exemplo 21.
123
Interrupção
Condições
para
Instrução
Hardware externo
Posições de restart
BUSRQ inactivo
EI ou DI não pro-
Não requerido
0066H
NMI activa baixa
duzem efeito
Instrução RST
Salto para um de
aceitar o pedido
de interrupção
Nonmaskable
interrupt
(NMI)
pino 17
Maskable interrupt BUSRQ inactivo
Necessita
estar
pino 16
NMI inactiva
habilitada
com
INT activa baixa
EI, podendo ser
desabilidata
Modo 0
oito endereços (00,
08, · · ·, 38H)
Modo 1
Modo 2
Utiliza o registo I
Não requerido
0038H
Vector (LSB)
Endereço
par
(MSB) para cal-
duma posição de
cular o endereço
memória
Tabela 6.1 — Sumário do processo de interrupções do Z80.
124
Referências bibliográficas
[1] Acer Incorporated. IOM-MPF-IP Experiment Manual (Software/Hardware), 1988.
[2] Acer Incorporated. IOM-MPF-IP Operation Manual, 1988.
[3] Acer Incorporated. IOM-MPF-IP User’s Manual, 1988.
[4] Barry B. Brey. The Intel Microprocessors: 8086/8088, 80186/80188, 80286, 80386,
80486, Pentium, Pentium Pro Processor, Pentium II, Pentium III, Pentium 4 —
Architecture, Programming and Interfacing. Prentice Hall, sexta edição, 2002.
[5] Ramesh Gaonkar. The Z80 Microcomputer: Architecture, Interfacing, Programming
and Design. Macmillan Publishing Company, segunda edição, 2000.
[6] Patai
Gergely.
Complete
Z80
instruction
set,
Agosto
2001.
URL:
http://www.ticalc.org/archives/files/fileinfo/195/19571.html.
[7] Douglas V. Hall. Microprocessors and Digital Systems. McGraw-Hill International
Editions, second edição, 1983.
[8] Douglas V. Hall. Microprocessors and Interfacing — Programming and Hardware.
McGraw-Hill International Editions, second edição, 1986.
[9] Thomas L. Harman e David T. Hein. The Motorola MC68000 Microprocessor Family:
Assembly Language, Interface Design and System Design. Pearson Education POD,
segunda edição, 1995.
[10] Lance A. Leventhal. Z80 Assembly Language Programming. Osborne/McGraw-Hill,
1979.
[11] Craig Marven e Gillian Ewers. A simple approach to Digital Signal Processing. Texas
Instruments, 1993.
[12] David Patterson e John Hennessy. Computer architecture: a quantitative approach.
Morgan Kaufmann Publishers, Inc., 1990.
[13] John B. Peatman. Microcomputer-based Design. McGraw-Hill International Editions,
1981.
125
[14] Manuel C. Reis, António J. Gouveia, e Francisco S. Pereira. Introdução à Progrmação.
Universidade de Trás-os-Montes e Alto Douro, ISBN 972-669-547-3, Julho 2003.
[15] Jean-Paul Tremblay, John M. DeDourek, e Richard B. Bunt. Introduction to Computer Science: An Algorithmic Approach. McGraw-Hill International Editions, 1989.
[16] John F. Wakerly. Microcomputer Architecture and Programming: The 68000 Family.
John Wiley and Sons, Inc., 1989.
126

Documentos relacionados