Escolar Documentos
Profissional Documentos
Cultura Documentos
Microprocessadores PDF
Microprocessadores PDF
1
2 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/
Índice Geral
CAPÍTULO 1 - VISÃO GERAL................................................................................................ 8
4.3 Quantização.......................................................................................75
4.4 Codificação........................................................................................77
5.4 RESET.............................................................................................95
3
Índice de Figuras
Figura 1 – Uma pequena fração do ENIAC......................................................................................................................8
Figura 2 - A máquina original de von Neumann. .............................................................................................................9
Figura 3 - Diagrama de blocos de um sistema microprocessado. ............................................................................ 11
Figura 4 – Fluxo de dados de uma máquina de von Neumann típica.........................................................................15
Figura 5 – (a) Memória big endian. (b) Memória little endian. .................................................................................16
Figura 6 – Esquema de ligação de um dispositivo de E/S e um CPU, através de um controlador de
dispositivo. ...................................................................................................................................................................17
Figura 7 – Pinagem lógica de um MCU bastante simples (hipotético).....................................................................19
Figura 8 – (a) encapsulamento DIP, (b) encapsulamento PLCC, (c) encapsulamento PGA, (d) encapsulamento
SMD...............................................................................................................................................................................19
Figura 9 – Encaixe de um circuito integrado DIP em seu respectivo soquete. ................................................... 20
Figura 10 –(a) ALU e (b) deslocador utilizados no exemplo..................................................................................... 22
Figura 11 – (a) Relógio com 4 saídas em atraso. (b) O diagrama de temporização das saídas. ........................ 23
Figura 12 – Os registradores utilizados para acionar os barramentos de endereço e dados. ......................... 24
Figura 13 – As vias de dados da microarquitetura exemplo. ................................................................................... 25
Figura 14 – O layout e a descrição de alguns dos campos da microinstrução que controla as vias de dados
da Figura 13. ............................................................................................................................................................... 27
Figura 15 – O diagrama de blocos completo de nossa microarquitetura exemplo. ............................................. 28
Figura 16 – (a) Uma pilha. (b) A mesma pilha após o armazenamento da constante 5. .......................................31
Figura 18 – Comparações entre os endereçamentos direto e indireto. (a) Endereçamento direto. (b)
Endereçamento indireto. ......................................................................................................................................... 37
Figura 19 – Conjunto de registradores do 68HC11. ................................................................................................... 39
Figura 20 – Notação utilizada pela Tabela 4............................................................................................................... 42
Figura 21 – Esquema de compartilhamento de um pino de IRQ por até oito dispositivos. ............................... 53
Figura 22 – Seqüência temporal de um exemplo de múltiplas interrupções. (RSI = Rotina de Serviço de
Interrupção.).............................................................................................................................................................. 55
Figura 23 – Diagrama de blocos do 8259A.................................................................................................................. 56
Figura 24 – Interface padrão do 8259A com o barramento................................................................................... 58
Figura 25 – O 8259A utilizado em cascata. ................................................................................................................ 59
Figura 26 – Registrador OPTION. .................................................................................................................................61
Figura 27 – Registrador TFLG2. .................................................................................................................................... 62
Figura 28 – Registrador TMSK2.................................................................................................................................... 62
Figura 29 - (a) representação de um discretizador, (b) representação gráfica de um discretizador e (c)
sinal analógico (linha contínua) e sinal discreto (pontos grossos) . ................................................................ 73
Figura 30 – (a) sinal analógico f(t), (b) Transformada de Fourier de f(t), F(ω), (c) representação gráfica de
um discretizador de Nyquist, (d) Transformada de Fourier do discretizador de Nyquist, (e) sinal
discreto no tempo e em amplitude e (f) Transformada de Fourier do sinal discreto no tempo e em
amplitude..................................................................................................................................................................... 74
Figura 31 – (a) sinal discretizado, f(n), (b) Transformada de Fourier de f(n), (c) gráfico da função sinc, (d)
Transformada de Fourier da função sinc, (e) sinal analógico f(t) e (f) Transformada de f(t). .............. 75
Figura 32 – Diversos exemplos da Transformada de Fourier de um sinal amostrado a uma freqüência
inferior a de Nyquist................................................................................................................................................ 76
Figura 33 – Esboço da função de transferência de um filtro passa-baixas real (apenas as componentes de
freqüência positivas). ............................................................................................................................................... 76
Figura 34 – Quantização da temperatura em sete níveis de quantização. ........................................................... 77
Figura 35 – Processo de quantização e o erro intrínseco ao processo.................................................................. 78
5
6 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/
Capítulo 1 - Visão Geral
Índice de Tabelas
Tabela 1 – Exemplos dispositivos de E/S e a maneira de como eles representam as informações................. 17
Tabela 2 – O Conjunto de instruções do Mac-1. .........................................................................................................32
Tabela 3 – Significado dos bits do registrador CCR. ................................................................................................39
Tabela 4 – Conjunto de macroinstruções do 68HC11.................................................................................................44
Tabela 5 – Descrição dos pinos do 8259A. ..................................................................................................................57
Tabela 6 - Fonte de interrupção, vetor de interrupção e a sua máscara. ............................................................60
Tabela 7 – Conteúdo do vetor de interrupção da placa EVB....................................................................................63
Tabela 8 – Canais de interrupção dos dois controladores 8259A do PC AT e suas posições no vetor de
interrupção..................................................................................................................................................................64
Tabela 9 – Descrição de cada uma das posições do vetor de interrupção do PC AT..........................................67
1
A maior parte desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall.
Enquanto isso, uma das pessoas envolvidas no projeto ENIAC, John von Neumann, foi para o
Instituto de Estudos Avançados de Princeton, para construir sua própria versão do EDVAC, a máquina
IAS. Von Neumann era um gênio do tipo Leonardo da Vinci. Falava muitas línguas, era um especialista em
ciências físicas e matemáticas, e tinha total lembrança de tudo que ouvia, via ou lia. Era capaz de citar de
cor, literalmente, o texto de livros que ele havia lido anos antes. Quando ele se interessou por
computadores, já era o mais eminente matemático do mundo.
Uma das coisas óbvias para ele era que a programação de computadores com um grande número de
chaves e cabos era lenta, tediosa e inflexível. Ele começou a perceber que o programa poderia ser
representado em forma digital na memória do computador, juntamente com os dados.
Observou também que a desajeitada aritmética decimal utilizada pelo ENIAC, em que cada dígito
era representado por 10 válvulas (uma ligada e nove desligadas), poderia ser substituída por uma
aritmética binária paralela (válvula ligada ou desligada).
Seu projeto básico, agora conhecido como máquina de von Neumann, foi utilizado no EDSAC, o
primeiro computador com programa armazenado, e‚ ainda é a base de quase todos os computadores
digitais, até mesmo hoje, mais de meio século depois. Este projeto, e a máquina IAS, construída em
colaboração com Herman Goldstine, tem tido tanta influência que vale uma breve descrição. Um esboço da
arquitetura é dado na Figura 2.
O primeiro circuito integrado comercial foi inventado por Jack Kilby da Texas Instruments
(http://www.ti.com) em 1958. Esta invenção foi tão significativa que Jack Kilby recebeu o maior prêmio da
área tecnológica dos EUA, a National Medal of Science. O circuito integrado possibilitou que dezenas de
transistores fossem colocados em uma única pastilha. Este encapsulamento tornou possível construir
computadores menores, mais rápidos e mais baratos que seus predecessores transistorizados.
Em 1968, a Intel Corporation (http://www.intel.com/) foi criada para fabricar pastilhas de memória.
Logo depois, ela foi contactada por um fabricante de calculadoras que queria uma CPU em uma única
pastilha para a sua calculadora, e por um fabricante de terminais que queria um controlador em uma única
pastilha para o seu terminal. A Intel produziu ambas as pastilhas, o 4004, uma CPU de 4 bits, e o 8008,
uma CPU de 8 bits. Estas foram as primeiras CPUs numa única pastilha do mundo.
A lntel não esperava outros interessados além dos clientes originais, de maneira que estabeleceu
uma linha de produção de baixo volume. Estavam errados. Houve um interesse tremendo, por isto
começaram a projetar uma pastilha de CPU de uso geral, que resolvesse o problema do limite de 16K de
memória do 8008 (imposto pelo número de pinos da pastilha). Este projeto resultou no 8080, uma pequena
CPU de uso geral. Este produto tomou a indústria de assalto, e instantaneamente tornou-se um item de
venda em massa. Porém, ao invés de vender milhares de unidades, como outros fabricantes, a Intel vendeu
milhões.
Dois anos mais tarde, em 1976, a Intel lançou o 8085, um 8080 encapsulado com alguns detalhes
extras de entrada/saída. Depois surgiu o 8086, uma verdadeira CPU de 16 bits numa única pastilha. O
8086 foi projetado para ter uma certa semelhança com o 8080, mas não era completamente compatível
com o 8080. O 8086 foi seguido pelo 8088, que possuía a mesma arquitetura que o 8086 e executava os
mesmos programas, mas possuía um barramento de 8 bits, ao invés de um barramento de 16 bits, o que o
tornava mais lento, porém mais barato que o 8086. Quando a IBM escolheu o 8088 para CPU do IBM PC
original, esta pastilha tornou-se rapidamente o padrão da indústria de computadores pessoais.
Nos anos seguintes, a Intel lançou o 80186 e o 80188, essencialmente novas versões do 8086 e
8088, respectivamente, mas contendo também uma grande quantidade de circuitaria de entrada/saída.
Nunca foram amplamente utilizados.
Com o passar dos anos, tornou-se possível colocar dezenas de milhares, depois centenas de milhares,
e finalmente milhões de transistores em uma única pastilha. Este avanço tornou os microprocessadores
mais rápidos e principalmente mais baratos. Deste modo, eles passaram a ser muito utilizados em sistemas
baratos, para implementar soluções poderosas, para problemas simples. Isto atraiu dezenas de fabricantes
a desenvolver centenas de famílias de microprocessadores, para milhares de consumidores. Cada
fabricante necessitava atrair a atenção dos consumidores. Evidentemente, isto poderia ser conseguido
desenvolvendo-se CPUs com características especiais que facilitassem o desenvolvimento de sistemas
microprocessados. Em busca disto, vários fabricantes começaram a dotar suas CPUs com dispositivos
extras. Por exemplo, uma pastilha com uma CPU e um conversor A/D2 poupa o projetista de adquirir e
interfacear um A/D externo, ou seja, o sistema fica mais barato e mais simples. Com o passar dos anos,
isto tornou-se comum e fez nascer uma nova classe de dispositivos: a dos microcontroladores (MCU's).
2
Um conversor A/D converte uma grandeza analógica em um número digital
segundo uma arquitetura baseada no esquema da Figura 2. Esta arquitetura recebe o nome de Arquitetura
de von Neumann e é ilustrada na Figura 3.
Para entender a função de cada componente da Figura 3, temos que nos perguntar qual a função
primária de um sistema microprocessado. Como todo sistema, um sistema microprocessado
constantemente recebe dados e realiza um conjunto de operações sobre eles. Estas operações podem ser
operações lógicas (deslocamentos, operações booleanas, etc) ou aritméticas (somar, multiplicar, etc). Para
suportar tais instruções, um sistema microprocessado necessita de um bloco funcional que as execute.
Este bloco existe e é denominado de ALU (unidade lógica e aritmética). Você já deve saber que uma ALU
é um dispositivo digital que recebe dois operandos e realiza uma operação (escolhida dentre um conjunto
de possíveis) entre eles.
Os operandos recebidos pela ALU devem vir de algum lugar. Da mesma forma, o resultado da
operação deve também ser armazenado em algum lugar. Este “lugar” é um conjunto de registradores. Ou
seja, os registradores provêem operandos para a ALU e podem armazenar o resultado da operação.
Agora que temos um local para armazenar nossos operandos e podemos realizar operações,
necessitamos de um local para armazenar as instruções que definirão as operações realizadas pela ALU.
Note que, ao contrário dos dados armazenados nos registradores, estas instruções são utilizadas durante
todo o tempo no qual o sistema estiver ativo. Isto sugere a criação de um novo local para armazenar as
instruções. Este novo local, geralmente com uma capacidade de armazenamento muito maior que os
registradores, é chamado de memória principal. Eventualmente, podemos ter um conjunto de dados
(operandos) que é utilizado durante toda a execução de um programa (como uma tabela de conversão).
Além disso, podemos necessitar armazenar mais operandos que nossos registradores permitem. Por isto, a
memória principal também é utilizada para armazenar dados, porém dados mais significativos. É por esta
razão, que o conjunto de registradores de um sistema microprocessado é chamado de memória de
rascunho.
CPU
Unidade
de
Controle
Unidade
Lógica e
Aritmética
(ALU) Dispositivos de entrada e saída
Registradores
Memória Display de
Teclado Cristal Líquido
Principal
Barramento
distintos. Em oposição, arquiteturas como a de Harvard possibilitam que a CPU busque as instruções e os
dados ao mesmo tempo. Isto provoca um ganho substancial de performance. Contudo, provoca também um
aumento significativo nos custos de produção. Por esta razão, a Arquitetura de Harvard é muito pouco
utilizada.
Evidentemente, em um sistema microprocessado deve haver uma fonte externa que provenha dados
ao sistema. Além disso, após processar estes dados o sistema precisa apresentar um resultado ao mundo
exterior. Por exemplo, em um sistema de controle de temperatura em uma estufa, precisamos alimentar o
sistema de controle com dados sobre a temperatura dentro da estufa, para que este possa calcular uma
ação de controle e manter a temperatura constante. Dispositivos que desempenham estas tarefas são
chamados de dispositivos de entrada e saída, ou simplesmente, dispositivos de E/S. Exemplos são:
conversores A/D e D/A, teclado (keypad), visores de cristal líquido, push-button, porta serial, PIO, etc.
(Não se preocupe com os nomes, até o final do curso você ficará bastante íntimo deles.)
Registradores, memórias, operandos, instruções, dispositivos de E/S, todos estes itens fazem parte
do mesmo sistema. Logo, eles devem interagir entre si. Ou seja, durante a execução de um programa,
dados e instruções vêm (e vão) de (e para) todos os componentes do sistemas. Para que isto aconteça, eles
devem estar interligados por uma estrutura (caminho) comum. Este caminho é denominado barramento.
Acredito que o conceito de barramento já esteja bem definido em sua mente desde o curso
anterior. Contudo, quero reforçar o conceito comparando o barramento às estradas de uma cidade. Por
estas estradas, veículos de todos os tipos vão (e vem) de (e para) todos os lugares de uma cidade, de uma
forma bastante ordenada (pelo menos em tese), controlados por regras e sinais de controle bem
determinados. É exatamente assim que se comporta um barramento em um sistema microprocessado.
Neste ponto, pode parecer que todos os componentes do sistema estejam trabalhando em harmonia
e em perfeito sincronismo. Contudo, para que isto realmente aconteça é necessário a inclusão de mais um
componente. Este componente deve controlar todos os outros, deve sincronizar as operações e tomar
decisões baseadas em seus resultados. Este bloco é denominado unidade de controle.
Deve-se notar que, pelo que está escrito nos parágrafos anteriores, apenas a ALU, os registradores
e a Unidade de Controle estão diretamente envolvidas no processamento dos dados. Desse modo, estes
três blocos são agrupados para formar uma unidade maior e mais poderosa. É esta unidade que chamamos
de Unidade Central de Processamento (CPU).
Não se preocupe se a idéia de um circuito digital cuja função é determinada por um conjunto de
instruções, que contenha operandos trafegando como veículos em uma cidade, e de unidade funcionais
tomando decisões baseadas em acontecimentos anteriores, parecer abstrato a você. Na verdade, só quero
passar a visão do todo. As partes menores ficarão bem mais claras quando forem abordadas
individualmente de forma mais detalhada.
A seguir, vamos agora definir formalmente a função dos blocos funcionais da Figura 3.
Unidade de Lógica e Aritmética: como o próprio nome informa, realiza todas as operações lógicas e
aritméticas da CPU. Em tese, a complexidade das operações da ALU define o poder de processamento da
CPU, ou seja, uma CPU que contém uma ALU que execute um conjunto de operações fraco e irrelevante,
torna-se uma CPU muito pouco poderosa.
Registradores: são utilizados pela CPU como uma memória temporária que armazena os dados que
estão sendo (ou vão ser) utilizados pela a ALU, e os resultados intermediários das operações.
Memória Principal: armazena as instruções que serão executadas pela CPU e os dados mais
permanentes (aqueles que serão utilizados durante toda a execução do programa ou representem dados
importantes).
Dispositivos de E/S: fazem a interface entre o sistema e o mundo exterior. São eles que provêem os
dados utilizados pelo sistema e é através deles que os resultados são apresentados externamente.
Barramento: é um meio elétrico que provê um caminho para o tráfego de informações e os sinais de
controles necessários ao funcionamento do sistema. Toda a comunicação entre os componentes do sistema
microprocessado é realizada através deste dispositivo.
3
Boa parte desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall.
Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 13
Curso de Microprocessadores
seja, conseguimos emular uma operação de subtração através de duas operações de soma e uma negação. O
mesmo pode ser aplicado a operações mais complexas, por exemplo, podemos simular uma multiplicação
com uma seqüência de somas e deslocamentos. Qual a vantagem disto? Simular uma operação a partir de
outras (geralmente mais simples), diminui a complexidade da CPU. Esta diminuição de complexidade
reflete-se em um projeto mais simples (do ponto de vista do projetista da CPU), um custo menor, e até um
consumo mais baixo. Contudo, não se pode querer que todas as vezes que um programador tenha de
executar uma simples multiplicação, ele inclua um algoritmo para emula-la. Por isto, a CPU tem que ter este
algoritmo já pronto para que ele possa ser utilizado sempre que o programador necessite. O conjunto de
algoritmos internos à CPU que emulam operações complexas a partir de operações mais simples é chamado
de microprograma.
O microprograma é escrito a partir de um conjunto de instruções que podem ser executadas
diretamente pelo hardware (através da ALU), chamadas de instruções de nível 1. Na verdade, este
conjunto de instruções não é acessível ao programador. Ele é utilizado apenas pelo projetista da CPU para
escrever o microprograma. Uma vez escrito, ninguém, nem mesmo o projetista, tem acesso ao
microprograma. Os programadores de microprocessadores, utilizam um conjunto de instruções,
geralmente chamado de linguagem assembler (ou linguagem de nível 2), que, como dito anteriormente, é
emulado pelo microprograma.
A coleção de todas as instruções disponíveis ao programador em um nível é chamada conjunto de
instruções daquele nível. O número de instruções de um conjunto de instruções varia de máquina para
máquina e de nível para nível. Para o nível de máquina convencional, por exemplo, o tamanho do conjunto de
instruções está tipicamente na faixa de 20 a 300. Um conjunto de instruções numeroso não é
necessariamente melhor que um pequeno. De fato, o oposto tende a ser verdade. Um conjunto de
instruções numeroso muitas vezes significa que as instruções não são muito gerais. Os compiladores para
linguagens de alto nível, como Ada, Modula 2 e Pascal, geralmente atuam melhor em máquinas com
conjuntos de instruções pequenos e bem escolhidos do que em máquinas com conjuntos de instruções
grandes e desajeitados. Máquinas com conjunto de instruções muito pequeno, chamadas máquinas RISC,
serão discutidas em breve. Estas máquinas não usam microprogramação e são extremamente rápidas.
Certifique-se de compreender que o conjunto de instruções e a organização do nível de
microprogramação são, de fato, o conjunto de instruções e a organização do hardware (CPU). O conjunto
de instruções e a organização do nível de máquina convencional são, em contraste, determinados pelo
microprograma, e não pelo hardware.
4
Todo o conteúdo desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall.
dos sistemas. Extrapolando, ela define o que a máquina pode fazer. Voltaremos a este importante tópico
em breve.
Figura 5(a), os números e a string são armazenados da esquerda para a direita (como estamos
acostumados), como na família Motorola (http://www.mot.com/). Já na Figura 5(b), a parte mais
significativa de um operando de 16 bits é armazenada no endereço de memória mais alto (o que também
parece lógico), porém a string continua sendo armazenada da esquerda para a direita, como na família
Intel.
O primeiro sistema, onde primeiro é armazenado a parte mais significativa ("big"), é chamado de
sistema "big endian", em contraste com o "little endian" da Figura 5(b). Estes termos são devidos a
Jonathan Swift, cujo livro “Aventuras de Gulliver” satirizou políticos que fizeram guerra por causa da
disputa sobre se os ovos deviam ser quebrados do lado largo (big end) ou do lado estreito (little end). O
termo foi usado pela primeira vez no encantador artigo “On Holy Wars and a Plea for Peace" de Danny
Cohen (1980).
Os problemas com estas duas representações começam a ocorrer quando queremos trocar dados
entre duas CPUs diferentes. Por exemplo, suponha que um sistema big endian esteja conectado a um little
endian por um meio que permita a transferência de um byte por vez. Para realizar a conexão, poderíamos
transmitir os bytes começando no byte 0 e terminado no 3 (0, 1, 2, 3, 4, 5, 6, 7). Neste caso, as posições
de memória do little endian seriam idênticas as do big endian. Com isto, a string ficaria na ordem correta,
mas os números não. Caso trocássemos a ordem de envio dos dados (1, 0, 3, 2, 5, 4, 7, 6) teríamos os
números corretos, mas a string não.
Endereço Big endian Endereço Little endian
0 A0h 37h 0 37h A0h
2 12h 24h 2 24h 12h
4 ‘A’ ‘L' 4 ‘A’ ‘L'
6 ‘E’ ‘X’ 6 ‘E’ ‘X’
(a) (b)
Figura 5 – (a) Memória big endian. (b) Memória little endian.
Quero que você termine esta seção com a idéia clara de que a falta de um padrão para a ordem dos
bytes é um grande problema na troca de dados entre CPUs diferentes.
(REQuisição de Memória)] para avisar que trata-se de uma leitura/escrita em um CD. O sinal deste pino
deve então ser incluído na lógica de seleção dos chips de memória e dos controladores de dispositivos.
Tipo de Dispositivo Representação da Informação
Serial Representa uma palavra binária de n bits através de uma saída (ou entrada)
digital de um único pino, um bit por vez. A palavra é identificada observando-se
o estado do pino em n instantes de tempo.
Paralelo Representa uma palavra binária de n bits através de uma saída (ou entrada)
digital de n pinos, n bits por vez. A palavra é identificada observando-se o
estado dos n pinos em um único instante de tempo.
Transdutores Analógicos Representam uma grandeza física qualquer, através de uma grandeza elétrica
(tensão, corrente, resistência, potência, etc), geralmente na forma analógica.
Teclado Representam uma tecla através da sua localização linha-coluna em uma matriz
de teclas.
Visor de Caracteres Representa um caractere através de um conjunto de pontos em uma matriz de
pontos.
Por último, devemos saber que quando utilizamos dispositivos de E/S, como regra geral, primeiro a
CPU deve acessar os registradores de controle para configurar o dispositivo de E/S. Em seguida, caso
quisermos enviar ou receber dados, a CPU precisa ler as informações contidas nos registradores de status.
Só assim, ela poderá receber ou enviar os dados desejados, através do registrador de dados.
Preste atenção! No parágrafo anterior, falei que sempre que desejarmos receber um dado de um
dispositivo de E/S, a CPU precisa verificar o registrador de status. Agora, imagine um sistema que
contenha uma infinidade de dispositivos de E/S. Neste caso, a CPU gastaria a maior parte do tempo de
processamento verificando se existe dados novos em seus dispositivos. Na maior parte das vezes, a
resposta seria negativa. Isto significa que a maior parte do tempo de processamento seria perdida. Para
evitar esta tragédia, em alguns casos, o próprio controlador de dispositivo avisa a CPU quando existe um
dado novo no dispositivo de E/S. Quando recebe este aviso, a CPU para o que está fazendo e realiza a
operação de E/S. Este aviso é feito através da ativação de um sinal de controle do barramento chamado
de sinal de interrupção. No Capítulo 3, estudaremos está técnica de E/S em mais detalhes.
endereço também é um fator limitante. Contudo, quando trabalha com endereços uma CPU precisa realizar
operações de soma, subtração, deslocamentos, etc. Por isso, aumentando-se o número de bits dos
endereços com os quais a CPU trabalha, aumenta-se também a complexidade dessas operações. Por isso,
uma CPU com um grande número de linhas (pinos) de endereço, geralmente, é muito poderosa e altamente
complexa.
perfeitamente em um soquete apropriado, Figura 8(b). Este encapsulamento torna a retirada do chip deste
soquete uma tarefa muito delicada.
1.8 Comentários
Pronto! Você já sabe o que é um sistema microprocessado. Mais ainda, você sabe as partes que o
compõem e com elas relacionam-se. Os capítulos que seguem, simplesmente, descrevem bem mais
detalhadamente este relacionamento. Além disso, nos ensina como projetar nosso próprio sistema
microprocessado. Para isto, procurei fornecer a maior quantidade de informação possível. No entanto,
devido a grande variedade de microprocessadores e microcontroladores existentes no mercado, tenho
certeza que não será tudo (se é que “tudo” realmente existe). Como já disse, um fato está do nosso lado: a
filosofia de todas as CPUs é a mesma, o que muda entre uma família e outra é a maneira de fazer a mesma
coisa. Algumas dessas maneiras serão discutidas quando falarmos sobre arquiteturas avançadas de
microprocessadores.
Capítulo 2 - Microprogramação5
O limite entre hardware e software não é bem definido e, além disso, está constantemente se
alterando. Os primeiros computadores tinham instruções para operações aritméticas, booleanas,
deslocamentos, comparações, entre outras, que eram executadas diretamente pelo hardware. Para cada
instrução, um circuito especifico de hardware estava presente para executá-la. Poder-se-ia, pelo menos
em princípio, desparafusar o painel traseiro e apontar os componentes eletrônicos usados pela instrução
de divisão.
Nas CPUs de hoje, não é mais possível isolar os circuitos de divisão, porque não existem circuitos de
divisão. Todas as instruções disponíveis no nível de máquina convencional (por exemplo, instruções
aritméticas, booleanas, deslocamentos, comparações e MCUs) são executadas passo a passo por um
interpretador executado no nível de microprogramação. O equivalente atual de olhar para os circuitos de
divisão é obter uma listagem do microprograma e procurar pela parte que interpreta as instruções de
divisão.
Embora programas em qualquer nível possam ser executados por um software interpretador, e
embora este interpretador possa, também, ser executado por outro interpretador, essa hierarquia não
pode prosseguir indefinidamente. No nível mais baixo, deve existir fisicamente uma máquina em hardware,
com circuitos integrados e objetos "sólidos" similares. Neste capítulo, iremos estudar como os
componentes eletrônicos são controlados pelo microprograma e como este interpreta o nível de máquina
convencional. Em breve, estudaremos uma classe de máquinas que não são microprogramadas (máquinas
RISC6).
Como a arquitetura do nível de microprogramação, chamada de microarquitetura, é definida pelo
hardware, ela é normalmente primitiva e difícil de programar. As considerações de tempo são, por
exemplo, freqüentemente importantes. Na realidade, ninguém programa com microinstruções. Elas são
utilizadas apenas uma única fez pelos projetistas para escrever o microprograma. Por isso, não as trate
como as instruções que você está acostumado.
O nível de microprogramação tem uma função específica: executar interpretadores para outras
máquinas virtuais (esperançosamente mais razoáveis). Esta meta de projeto leva naturalmente a uma
organização altamente otimizada no sentido de buscar, examinar e executar instruções de máquina
convencional e, em alguns casos, instruções mais sofisticadas. Os problemas e compromissos envolvidos na
organização e projeto deste nível serão examinados neste capítulo, através do projeto de uma CPU CISC
(Complex Instruction Set Computers, ou seja, máquinas com um conjunto de instruções complexo, como
são chamadas as CPUs que possuem um microprograma) bastante simples.
5
Todo o conteúdo deste capítulo foi retirado do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall.
6
Reduced Instructions Set Computers, ou seja, computadores com um conjunto reduzidos de instruções.
Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 21
Curso de Microprocessadores
Este programa foi escrito o mais simples possível, apenas para começar uma discussão sobre as
características de um programa em C. Neste capítulo, começo com o projeto de uma CPU tão simples
quanto possível, apenas para discutir os problemas e compromissos envolvidos na organização e projeto de
uma CPU.
(a) (b)
Figura 10 –(a) ALU e (b) deslocador utilizados no exemplo.
definida. (Lembra da Seção 1.3) Lá você viu que uma instrução é executada em diversos passos.) Por
exemplo, as entradas para a ALU devem ser colocadas disponíveis e se tornarem estáveis antes que a saída
possa ser armazenada.
A Figura 11(a) mostra um relógio simbólico com quatro saídas. A de cima é a saída primária; as outras
três são derivadas dela pela inserção de diversos atrasos nas linhas de saída. O relógio primário tem uma
largura de pulso igual a um quarto do período do ciclo (Figura 11(b), linha superior). As outras três saídas
são obtidas por atraso de uma, duas e três vezes a largura do pulso. O resultado é um circuito que divide
cada ciclo em quatro subciclos de mesma duração.
Para provocar quatro transições diferentes que devem ocorrer em um ciclo em uma certa ordem, o
projetista da CPU pode fazer um AND do sinal habilitador de cada um com uma linha diferente do relógio.
A transição ligada à linha de relógio primária ocorrerá primeiro, a transição ligada à linha de relógio com o
menor atraso ocorrerá em segundo lugar, e assim por diante.
(a) (b)
Figura 11 – (a) Relógio com 4 saídas em atraso. (b) O diagrama de temporização das
saídas.
+ B. Como A está sendo armazenado, o valor no barramento A começa a se modificar, fazendo com que a
saída da ALU, e portanto também o barramento C, se modifiquem. Conseqüentemente, um valor errado
pode ser armazenado em A. Em outras palavras, na atribuição A = A + B, o A do lado direito é o valor
original de A, e não alguma mistura bit a bit dos valores velho e novo. Inserindo latches nos barramentos A
e B, podemos congelar lá os valores originais de A e B no início do ciclo, de modo que a ALU fica protegida
de mudanças nos barramentos enquanto um novo valor está sendo armazenado na memória de rascunho. A
carga dos latches é controlada por L0 e L1.
2.1.3. Microinstruções
Para controlar as vias de dados da Figura 13, precisamos de 59 sinais. Estes podem ser divididos em
oito grupos funcionais, como descrito abaixo.
16 sinais para controlar a carga do barramento A a partir da memória de rascunho;
16 sinais para controlar a carga do barramento B a partir da memória de rascunho;
16 sinais para controlar a carga da memória de rascunho a partir do barramento C;
2 sinais para controlar os lanches A e B;
2 sinais para controlar a função da ALU;
2 sinais para controlar o deslocador;
4 sinais para controlar o MAR e o MBR (sendo que RD é conectado à M2 e WR é conectado à M3,
como apresentado na Figura 12), e;
1 sinal para controlar o Amux.
Dados os valores dos 59 sinais, podemos realizar um ciclo nas vias de dados. Um ciclo consiste em
colocar valores nos barramentos A e B, armazená-los nos dois latches de barramento, passar os valores
através da ALU e do deslocador e, finalmente, armazenar o resultado na memória de rascunho e/ou MBR.
Além disso, o MAR pode também ser carregado, e um ciclo de memória iniciado. Como uma primeira
aproximação, podemos ter um registrador de controle de 59 bits, com um bit para cada sinal de controle.
Um bit em 1 significa que o sinal está ativado, e em 0 significa que ele está desativado.
Entretanto, pelo preço de um pequeno aumento na circuitaria, podemos reduzir consideravelmente o
número de bits necessário para controlar as vias de dados. Para começar, temos 16 bits para controlar a
entrada do barramento A, o que permite 216 combinações de registradores fonte. Apenas 16 dessas
combinações são permitidas - ou seja, apenas um dos 16 registradores de cada vez. Portanto, podemos
codificar a informação sobre o barramento A em 4 bits e usar um decodificador para gerar os 16 sinais de
controle. O mesmo se aplica ao barramento B.
A situação é ligeiramente diferente para o barramento C. Em princípio, são possíveis
armazenamentos simultâneos múltiplos na memória de rascunho, mas este aspecto virtualmente nunca é
utilizado na prática, e a maioria das implementações não o provê. Assim, codificaremos o controle do
barramento C em 4 bits. Tendo economizado 3 x 12 = 36 bits, precisamos agora de 23 bits de controle
para acionar as vias de dados. L0 e L1 sempre são necessários em instantes bastante determinados, de
forma que podem ser supridos pelo relógio, deixando-nos com 21 bits de controle. Um sinal adicional que
não é estritamente necessário, mas que é freqüentemente útil, é um para habilitar/inibir o armazenamento
do valor existente no barramento C na memória de rascunho. Em algumas situações, deseja-se meramente
executar uma operação da ALU para gerar os sinais N e Z, mas não se quer armazenar o resultado. (Você
consegue pensar em um caso desses?) Com este bit extra, que denominaremos ENC (ENable C, ou habilita
C), podemos indicar se o barramento C deve ser armazenado (ENC = 1) ou não (ENC = 0). Agora, podemos
controlar as vias de dados com um número de 22 bits.
para o barramento B) nos registradores. O relógio ativa os latches A e B durante este subciclo, provendo
entradas estáveis para a ALU durante o resto do ciclo. Enquanto os dados estão sendo colocados nos
barramentos A e B, a unidade de incremento na seção de controle da máquina calcula MPC + 1, preparando
a carga da próxima microinstrução da seqüência durante o ciclo seguinte. Sobrepondo estas duas
operações, a execução das instruções pode ser acelerada.
No terceiro subciclo, dá-se tempo à ALU e ao deslocador para a produção de resultados válidos. O
campo AMUX da microinstrução determina a entrada esquerda da ALU; a entrada da direita é sempre o
latch B. Embora a ALU seja um circuito combinatório, o tempo que ela gasta para calcular uma soma é
determinado pelo tempo de propagação de vai-1, e não pelo atraso normal das portas. O tempo de
propagação de vai-1 é proporcional ao número de bits da palavra. Enquanto a ALU e o deslocador estão
calculando, o MAR é carregado a partir do barramento B, se o campo MAR da microinstrução for 1 (um).
Durante o quarto e último subciclo, o valor existente no barramento C pode ser armazenado de volta
na memória de rascunho e no MBR, dependendo de ENC e MBR. O bloco rotulado "decodificador C" pega
ENC, a quarta linha do relógio, e o campo C da microinstrução como entradas e gera os 16 sinais de
controle. Internamente, ele realiza uma decodificação de 4-para-16 do campo C e então faz um AND de
cada um destes sinais com um sinal derivado do AND da linha do subciclo 4 com ENC. Assim, um
registrador da memória de rascunho somente é carregado se prevalecerem três condições:
1. ENC = 1;
2. É o subciclo 4, e;
3. registrador foi selecionado pelo campo C.
O MBR também é carregado durante o subciclo 4 se MBR = 1, os dois sinais que controlam a
memória, RD e WR, são ativados se estiverem presentes em MIR. Na verdade, os campos correspondentes
em MIR agem como latches.
Em palavras, o sinal de controle para Mmux é 1 (roteando ADDR para MPC) se LR é 01b e N = 1, ou
LR é 10b e Z = 1, ou LR é 11b. Caso contrário, ele é 0, e a próxima microinstrução na seqüência é buscada. O
circuito para a geração do sinal Mmux pode ser construído a partir de componentes SSI ou ser parte de
uma PLA.
Para tornar nossa máquina exemplo ligeiramente mais real, vamos assumir que um ciclo de memória
principal tome mais tempo que uma microinstrução. Em particular, se uma microinstrução inicia uma leitura
à memória principal, fazendo RD = 1, ela também deve ter RD = 1 na próxima microinstrução executada
(que pode ou não estar localizada no próximo endereço da memória de controle). O dado torna-se
disponível duas microinstruções após a leitura ter sido iniciada. Se o microprograma não tem nada mais útil
a fazer na microinstrução seguinte à que iniciou a leitura de memória, a microinstrução simplesmente tem
RD = 1 e é efetivamente desperdiçada. Do mesmo modo, uma escrita na memória também gasta dois
tempos de microinstrução para ser completada.
2.2.1. Pilhas
Uma das questões mais importantes de projeto de macroarquiteturas é o endereçamento. Um
programa qualquer é normalmente implementado de tal modo que, quando se sai de um procedimento ou
função, a área de memória que estava sendo utilizada para variáveis locais é liberada. A maneira mais fácil
para se conseguir isto é através de uma estrutura de dados denominada pilha. Uma pilha é um bloco
contíguo de memória contendo alguns dados e um apontador de pilha (SP, de Stack Pointer) dizendo onde
está o topo da pilha. O fundo da pilha está em um endereço fixo, e não mais nos interessará. A Figura 16(a)
mostra uma pilha ocupando seis palavras de memória. O fundo da pilha está no endereço 4020, e o topo da
pilha, para onde SP aponta, está no 4015. Nossas pilhas crescerão dos endereços de memória mais altos
para os mais baixos, mas a escolha oposta é igualmente boa.
Muitas operações são definidas para pilhas. Duas das mais importantes são PUSH X e POP Y. PUSH
avança o apontador da pilha (decrementando-o, no nosso exemplo) e então armazena X na posição de
memória agora apontada por SP. PUSH aumenta o tamanho da pilha de um item. POP Y, em contraste,
reduz o tamanho da pilha, armazenando o item do topo da pilha em Y, e então removendo-o através do
incremento do apontador de pilha com o tamanho do item retirado. A Figura 16(b) mostra como a pilha da
Figura 16(a) fica depois que uma palavra contendo 5 foi empilhada.
(a) (b)
Figura 16 – (a) Uma pilha. (b) A mesma pilha após o armazenamento da constante 5.
Outra operação que pode ser realizada numa pilha é avançar o apontador da pilha sem realmente
inserir qualquer dado. Isto é normalmente feito na entrada de um procedimento ou função, para reservar
espaço para as variáveis locais [depois que o espaço for reservado, pode-se utilizar os modos de
endereçamento (você verá em breve) para armazenar itens]. É importante observar que apenas as
instruções PUSH e POP podem armazenar e retirar itens da pilha. Por isso, não pense que você poderá
armazenar um item na pilha (utilizando a instrução MOVE, por exemplo) e avançar o ponteiro da pilha (SP)
“manualmente” (isto seria muito perigoso).
Você viu que o melhor local para armazenar dados temporários (fora da CPU) é a pilha. Contudo, a
pilha é uma estrutura dinâmica, quer dizer, seu topo está constantemente mudando de endereço (SP muda
de conteúdo). Neste caso, todas as vezes que um procedimento ou função for chamado, o topo da pilha
pode estar em um local diferente. Isto quer dizer que um item que está armazenado na pilha só pode ser
referenciado em relação ao SP. Em outras palavras, o Mac-1 precisa de um modo de endereçamento que
busque ou armazene uma palavra a uma distância conhecida relativa ao apontador de pilha (ou algum modo
de endereçamento equivalente).
Caso você não tenha entendido esta seção, pode fazer uma nova tentativa lendo o livro “Organização
Estruturada de Computadores” de Tanenbaum, páginas 142 e 143. Lá, você encontrará um exemplo mais
detalhado do uso de pilhas. Por enquanto, seguiremos com o nosso projeto.
estruturas. O endereçamento local especifica uma distância a partir de SP e é usado para acessar
variáveis locais, como acabamos de ver. Juntos, estes três modos provêem um sistema de endereçamento
simples, mas adequado.
O conjunto de instruções do Mac-1 é apresentado na Tabela 2. Cada instrução contém um código de
operação (opcode) e, algumas vezes, um endereço de memória ou uma constante. A primeira coluna mostra
o código binário da instrução. A segunda mostra seu mnemônico para a linguagem de montagem. A terceira
mostra seu nome, e a quarta descreve o que ela faz, através de um trecho em Pascal. Nestes trechos, m[x]
se refere à palavra de memória x. Assim, LODD carrega o acumulador com a palavra de memória
especificada em seus 12 bits de baixa ordem. LODD usa então endereçamento direto, enquanto LODL
carrega o acumulador com uma palavra a uma distância x acima de SP, usando portanto o endereçamento
local. LODD, STOD, ADDD e SUBD realizam quatro funções básicas usando endereçamento direto, e
LODL, STOL, ADDL e SUBL realizam as mesmas funções usando endereçamento local.
0 Mar :=que
significa pc;rd;
tir deve passar pela ALU{loop(código
principal} 40= 2),
da ALU Tirde
:= modo
lshift(tir);
que oifseu
n then goto
bit de alta{110x ou 111x?}
ordem possa ser
46;
testado. Note que o uso de alu significa que ENC = 0.
1 pc := pc + 1; rd; {incremente pc} 41 Alu := tir; if n then goto 44; {1100 ou 1101?}
Para indicar leituras e escritas à memória, vamos apenas colocar rd e wr no programa fonte. A
2 Ir := mbr; if n then goto 28 {salva, decodifica 42 Alu := ac; if n then goto 22; {1100 = JNEG}
ordem das várias partes do comando fonte é, em princípio, arbitrária, mas, para melhorar sua legibilidade,
mbr}
vamos
3 Tirtentar arranja-lasifnan ordem
:= lshift(ir+ir); then em que são executadas.
43 Goto 0;
Neste
goto 19 ponto, quero reafirmar que a microlinguagem é utilizada apenas pelo projetista para criar um
interpretador para as
4 Tir := lshift(tir); if nmacroinstruções.
then goto {000x Nós sempre 44
ou 001x?} programaremos
Alu := ac; if z utilizando
then goto 0;macroinstruções. Por isso,
{1101 = JNZE}
um único
11 parâmetro é levado em consideração na hora de criar as microinstruções: a performance da CPU.
5 Alu := tir; if n then goto 9; {0000 ou 0001?} 45 Pc := band(ir, amask); goto 0;
2.3.2.
6 Mar := ir;Um
rd; Exemplo de Microprograma
{0000 = LODD} 46 Tir := lshift(tir); if n then goto
50;
Chegamos finalmente ao ponto em que podemos juntar todos os pedaços. A figura da próxima página
7 rd; 47 Sp := sp + (-1); {1110 = CALL}
é um microprograma que roda no Mic-1 e interpreta Mac-1. É um programa surpreendentemente curto -
8 Ac := mbr; goto 0; 48 Mar := sp; mbr := pc; wr;
apenas 79 linhas. Agora a escolha dos
9 Mar := ir; mbr := ac; wr;
nomes para os49registradores
{0001 = STOP}
de rascunho na Figura 13 é óbvia: PC,
Pc := band(ir, amask); wr; goto 0;
AC
10 eWr;SPgotosão 0;usados para armazenar os três registradores 50 Tir := dolshift(tir);
Mic-1. IRifénothen registrador de instrução
goto {1111, examine e
contém a macroinstrução que está sendo correntemente65; executada. TIR é uma cópia temporária endereço} de IR,
usada
11 Alupara
:= tir;decodificar
if n then gotoo15; código {0010
de operação
ou 0011?} (opcode).
51 Tir Os três registradores
:= lshift(tir); if n then gotoseguintes contém as
constantes indicadas. AMASK é a máscara de endereços, 59; 0FFFh, e é usado para separar opcode de bits de
12 Mar := ir;
endereço. rd;
SMASK é a máscara de{0010 = ADDD}
pilha, 00FFh, e 52 Alu :=nas
é usado tir; if n then gotoINSP
instruções 56; e DESP para isolar a
13 Rd; 53 Mar := ac;
distância de 8 bits. Os seis registradores restantes não têm função assinalada e podem ser rd; {1111000
usados= PSHI}
como o
14 Ac := mbr + ac;
microprogramador quiser. goto 0; 54 Sp := sp + (-1);rd;
15 Mar := ir; rd; {0011 = SUBD} 55 Mar := sp; wr; goto 10;
Como todo interpretador, o nosso microprograma tem um loop principal que busca, decodifica e
16 Ac := ac + 1; rd; {x-y = x+1 + not y} 56 Mar := sp; sp := sp + 1; rd; {1111001 = POPI}
executa instruções do programa que está sendo interpretado, neste caso, instruções de nível 2. Seu loop
17 A := inv(mbr); 57 Rd;
principal
18 Ac := começa
ac + a; goto na 0;
linha 0, onde ele inicia buscando58 a macroinstrução
Mar := ac; wr; goto apontada
10; por PC. Enquanto espera
que a instrução
19 Tir chegue,
:= lshift(tir); o microprograma
if n then incrementa
goto {010x ou 011x?} 59 PCAlu
e :=continua a ativar
tir; if n then o sinal de barramento RD.
goto 62;
Quando
25; ela chega, na linha 2, ela é armazenada em IR e, simultaneamente, o bit de alta ordem (bit 15) é
testado.
20 Alu :=Se tir;oifbit 15 for
n then goto1, a decodificação
23; {0100 ou 0101?}continua60na Splinha
:= sp28; caso contrário, ela continua
+ (-1); {1111010na linha 3.
= PUSH}
21 Alu := ac;
Assumindo if nenquanto
por then gotoque0; a instrução
{0100 =é JPOS}
um LODD,61 Mar
o bit 14:=é sp; mbr :=na
testado ac;linha
wr; goto
3, e10;
TIR é carregado com
a22instrução
Pc := band(ir, amask);
original goto 0; de {desvie}
deslocada 2 bits para a esquerda, 62 Maruma := sp;vez
sp := sp + 1; rd;
usando o somador {1111011
e outra= usando
POP} o
23 Alu := ac; if z then goto 22; {0101 = JZER} 63 Rd;
deslocador. Note que o bit N de status da ALU é determinado pela saída da ALU, na qual o bit 14 é o de
24 Goto 0; {desvio falhou} 64 Ac := mbr; goto 0;
mais alta ordem, pois IR + IR desloca IR 1 bit para a esquerda. A saída do deslocador não afeta os bits de
25 Alu := tir; if n then goto 27; {0110 ou 0111?} 65 Tir := lshift(tir); if n then goto
status da ALU.
73;
Todas as instruções
26 Pc := band(ir, amask); goto 0;
que têm 00 em
{0110 = JUMP}
seus dois bits de alta ordem eventualmente chegam à linha 4
66 Alu := tir; if n then goto 70;
para
27 Ac ter
:= oband(ir,
bit 13 amask);
testado. As0;instruções
goto {0111 =queLOCO} começam 67comMar000 vãosppapa
:= sp; a linha
:= sp+1; rd; 5, aquelas começando
{1111100 = RETN}com
001 vão para a linha 11. A linha 5 é um
28 Tir := lshift(ir+ir); if n then {10xx ou 11xx?} exemplo de uma microinstrução
68 Rd; com ENC = 0; ela apenas testa TIR,
mas goto
não o40; altera. Dependendo do resultado deste teste, o código para LODD ou STOP é selecionado.
29 TirPara LODD, o microcódigo
:= lshift(tir); if n then gotodeve{100xprimeiro
ou 101x?} buscar 69a palavra
Pc := mbr; endereçada
goto 0; diretamente carregando os 12
35;
bits de baixa ordem de IR em MAR. Neste caso, os 4 bits de alta ordem são todos zero, mas para STOP e
30 Aluinstruções,
outras := tir; if n then não.goto 33;
Entretanto, {1000
comoouMAR 1001?}só têm 7012Abits
:= ac;de largura, os bits de opcode {1111101
não= afetam
SWAP} a
31 A := ir + sp; {1000 = LODL} 71 Ac := sp;
escolha da palavra lida. Na linha 7, o microprograma não tem nada a fazer, e portanto apenas espera.
32 Mar := a; rd; goto 7; 72 Sp := a; goto 0;
Quando a palavra chega, ela é copiada para AC e o microprograma desvia para o início do loop. STOP,
33 A := ir + sp; {1001 = STOL} 73 Alu := tir; if n then goto 76;
ADDD e SUBD são similares. O único ponto notável relativo a elas é como a subtração é feita. Ela usa o
34 Mar := a; mbr := ac; wr; goto 10; 74 A = band(ir, smask); {1111110 = INSP}
fato
35 Alude :=
que tir; if n then goto 38; {1010 ou 1011?} 75 Sp := sp + a; goto 0;
36 A := ir + sp; x – y == x
{1010 + (-y) = x 76
ADDL} + ( yA+1)
:= =band(ir,
x + 1 +smask);
y {1111111 = DESP}
37 complemento
em Mar := a; rd; goto 13; A adição de 1 a AC é feita 77
de dois. A := inv(a);
na linha 16, que de outra maneira seria desperdiçada
38 Aa:=linha
como ir + sp;
13. {1011 = SUBL} 78 A := a + 1; goto 75;
39 Mar O := a; rd; goto 16;
microcódigo para JPOS começa na linha 21. Se AC < 0, a condição de desvio falha e JPOS é
terminado imediatamente através da volta ao 17
Figura loop
– Oprincipal. Se, entretanto, AC ≥ 0, os 12 bits de baixa
microprograma.
ordem de IR são extraídos, fazendo um AND deles com a máscara 0FFFh e armazenando o resultado em
PC. Não há nenhum custo extra para remover os bits de opcode aqui, e assim podemos também fazê-lo. Se
isso tivesse custado uma microinstrução extra, no entanto, teríamos que analisar cuidadosamente para ver
se a existência de lixo nos 4 bits de alta ordem de PC poderia causar problemas mais tarde.
Em certo sentido, JZER (linha 23) funciona de maneira oposta a JPOS. Com JPOS, se a condição for
verificada, o desvio falha e o controle retorna ao loop principal. Com JZER, se a condição for verificada, o
desvio é realizado. Como o código para realizar o desvio é o mesmo para todas as instruções de desvio,
podemos economizar microcódigo simplesmente indo para a linha 22 sempre que possível. Este estilo de
programação geralmente seria considerado grosseiro em outro programa de aplicação, mas num
microprograma não existem barreiras. Desempenho é tudo.
JUMP e LOCO são imediatas, de modo que a próxima rotina interessante a ser analisada é a LODL.
Primeiro, o endereço de memória absoluto a ser referenciado é calculado somando o deslocamento contido
na instrução a SP. Então, a leitura de memória é iniciada. Como o resto do código é o mesmo para LODL e
LODD, vamos apenas usar as linhas 7 e 8 para ambos. Isto não apenas economiza memória de controle sem
perda na velocidade de execução, mas também significa menos rotinas para depurar. Código análogo é
usado para STOL, AUDL e SUBL. O código para JNEG e JNZE é similar a JZER e JPOS, respectivamente
(e não ao contrário). CALL primeiro decrementa SP, então empilha o endereço de retorno, e finalmente
desvia para o procedimento. A linha 49 é quase idêntica à linha 22; se fosse exatamente a mesma,
poderíamos ter eliminado a 49 colocando um desvio incondicional para 22 em 48. Infelizmente, precisamos
continuar ativando WR por mais uma microinstrução.
Todas as macroinstruções restantes têm 1111 nos 4 bits de alta ordem, e então a decodificação dos
"bits de endereço" é necessária para distingui-las. As rotinas reais são imediatas, de modo que não
faremos mais comentários sobre elas.
7
Técnica que permite a execução de mais de uma instrução ao mesmo tempo. Veremos mais sobre isto em breve.
Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 35
Curso de Microprocessadores
(a) (b)
Figura 18 – Comparações entre os endereçamentos direto e indireto. (a) Endereçamento
direto. (b) Endereçamento indireto.
Primeiramente, o conteúdo da posição 1000 é copiado para um registrador interno da CPU. Este
número de 16 bits (1.510) não é colocado em R1. Se fosse como na Figura 18(a) teríamos uma instrução de
endereçamento direto. Ao em vez disso, o conteúdo da posição 1510 de memória é buscado e colocado em
R1. O número contido na posição 1000 não é o operando, mas em vez disso "aponta" para o operando. Por
isso, é denominado apontador.
Alguns processadores, embora nenhum dos vistos durante o nosso curso, permitem endereçamento
indireto de múltiplos níveis. Neste modo de endereçamento, um apontador é utilizado para localizar uma
palavra de memória que por sua vez aponta para uma palavra de memória, e assim por diante.
2.4.5. Indexação
Muitos algoritmos necessitam da execução de alguma operação sobre uma seqüência de estruturas
de dados armazenadas em posições consecutivas de memória. Por exemplo, um bloco de n palavras de
máquina ocupando as posições.
A, A+1, A+2, ..., A+n-1
que devem ser copiadas nas posições
B, B+1, B+2, ..., B+n-1.
Este problema pode ser resolvido através de endereçamento indireto. Um registrador ou posição de
memória é carregado(a) com o endereço A; um(a) outro(a)‚ carregado(a) com o endereço B. As instruções
MOVE utilizam esses dois endereços como apontadores. Após cada palavra ter sido copiada, os
apontadores para elas são incrementados de 1. Os apontadores fazem parte dos dados e, obviamente, não
fazem parte do programa.
Uma outra solução é ter um ou mais registradores, denominados registradores de índice, que
funcionam da seguinte maneira. Os endereços possuem duas partes: o número de um registrador de índice
e uma constante. O endereço do operando é a soma da constante com o conteúdo do registrador de índice.
No exemplo acima, se ambos os endereços são indexados utilizando um registrador de índice que contém o
inteiro k, a instrução MOVE A,B copiará o conteúdo da posição de memória A + k para B + k. Inicializando o
registrador de índice com o valor 0 e incrementando-o do tamanho da palavra, após cada palavra ser
copiada, apenas um registrador é necessário para o loop de cópia. Além disso, incrementar um registrador
é mais rápido que incrementar uma posição de memória.
A indexação é também normalmente utilizada para endereçar um campo com um deslocamento
conhecido, em relação ao início de uma dada estrutura. Variáveis locais em um procedimento são acessadas
desta maneira.
endereços. Normalmente, o apontador de pilha SP é inicializado por uma das primeiras instruções do
macroprograma.
O HC11 utiliza E/S mapeada em memória. Como já sabemos, neste caso, a CPU não destingue entre
operações na memória e em um dispositivo de E/S. Além disso, este espaço de endereçamento é dividido
entre a memória e os dispositivos de E/S do sistema, ou seja, só teremos realmente 64 kbytes de memória
se não tivermos nenhum dispositivo de E/S.
Um registrador novo para você é o CCR (Registrador de Código de Condição). Em qualquer instante
de tempo, os bits deste registrador nos dão informações importantes sobre a CPU. Na Tabela 3 é
apresentado o significado de cada um dos bits do CCR.
O bit I (se igual a 1) impede que a CPU receba pedidos de interrupção (veja a Seção 1.6) dos
dispositivos de E/S do sistema. Isto pode ser útil, por exemplo, quando a CPU está realizando operações
críticas. O bit H indica a ocorrência de vai-1 do bit 3 para o 4, após o resultado da última operação. É útil
quando se está trabalhando com valores em BCD. A função dos bits N, Z, V e C deve ser clara para você.
Estes bits (incluindo o H) são usados para os programas de nível 2 tomarem decisões, através de
instruções de desvios condicionais que testam estes bits. Os bits S e X vão ser vistos mais adiante.
No 68HC11, assim como na maioria das CPUs, o número de registradores é bastante reduzido. Desde
que eles são, geralmente, mais rápidos que a memória principal, a tarefa de decidir quais dados ficaram na
memória e quais ficaram nos registradores é bastante “filosófica”.
2.6 Comentários
Apesar de ter pouca experiência na programação de microprocessadores, acredito que agora você
possui um conhecimento bastante sólido de como uma CPU qualquer trabalha. Até o fim do curso, com a
realização de programas com o 68HC11, este conhecimento aumentará bastante.
Não fique preocupado se você não compreendeu a maioria das instruções da Tabela 4. Acredito que a
melhor forma familiarizar-se com estas instruções é praticando. Por isto, não perdi tempo explicando-as
uma por uma. De nada adiantaria a explicação teórica sem a prática. Além disso, esta explicação poderia
deixa-lo com a falsa impressão de já saber programar em assembler. Então, acredito que esta falta inicial
de respostas o levará ao laboratório e a fazer cada vez mais perguntas. E está é a melhor forma de
aprender: praticando e perguntando.
O programa anterior utiliza a função kbhit(). Esta função verifica se alguma tecla do teclado foi
pressionada. Em caso positivo, ela retorna verdadeiro, caso contrário, retorna falso. O nosso programa
exemplo fica dentro de um loop executando várias tarefas. Uma dessas tarefas verifica se uma tecla foi
pressionada. Se positivo, o programa lê o teclado e armazena a tecla pressionada na variável key. Apenas
quando a condição key != ESC não for satisfeita o programa sai do loop.
Dois aspectos importantes do programa anterior devem ser observados. Primeiro, a instrução que
verifica se alguma tecla foi digitada (instrução de E/S) deve ser incluída entre as instruções normais do
programa. Caso contrário, a tecla (o dado) jamais será recebida. Segundo, a maior parte do tempo de
processamento da E/S é gasto na verificação da existência do dado, pois a maioria das vezes que o
programa verifica se uma tecla é pressionada o resultado é negativo. Isto quer dizer que muito tempo de
processamento é desperdiçado. Em algumas aplicações, este é o maior pecado da E/S programada.
Observe agora o próximo programa exemplo. O programa precisa receber uma tecla do teclado
(logo, realizar uma operação de E/S). Contudo, a tecla é indispensável para a realização de suas tarefas.
Por isso, ele só passa para a próxima instrução quando uma tecla houver sido digitada. Neste caso, não nos
interessa se algum tempo está sendo desperdiçado. Temos todo o tempo do mundo. Por isso, esta parece
ser a técnica mais adequada neste caso.
do{
puts(“Especifique a operação:”);
switch(getch()){
case 1 : faça algo; break;
case 2 : faça outra coisa; break;
case 3 : faça outra coisa; break;
case 4 : faça outra coisa;
}while(1);
Que fique claro que os programas em linguagem C apresentados nesta seção são apenas ilustrativos.
Na verdade, como veremos em breve, kbhit() e getch() não realizam E/S exatamente como descrito na
Seção 1.6, ou seja, através dos registradores de status e de dados do controlador de dispositivo do
teclado. Contudo, os programas ilustram bem a filosofia da E/S programada.
Vimos um caso em que a E/S programada é a mais indicada. Agora, considere um sistema
microprocessado que possua dez dispositivos de E/S. Considere também que qualquer dispositivo pode ter
um dado novo em qualquer instante de tempo. Isto significa que a CPU deve checar o registrador de status
de todos os dispositivos de E/S, o mais rápido possível. Além disso, se dois ou mais dispositivos de E/S
contiverem dados novos em um mesmo instante de tempo, existe a possibilidade de perda de dados por
parte de um dispositivo mais crítico? Se existe, posso dar prioridade a este dispositivo?
Pelo exemplo anterior, não é preciso ser nenhum gênio para descobrir que a E/S programada não é
indicada para sistemas com muitos dispositivos ou com dispositivos críticos. Neste caso, a escolha mais
indicada talvez seja E/S por interrupção.
3.2 Interrupção
A técnica de E/S por interrupção é muito importante para a operação de sistemas
microprocessados, pois ela possibilita ao microprocessador responder rapidamente aos seus dispositivos
de E/S. A importância dessa abordagem pode ser entendida comparando-a a uma campainha. Se uma porta
não tiver nenhum dispositivo para chamar a atenção (como uma campainha), talvez seja necessário irmos
até ela para verificar se existe alguém à porta. Com uma campainha, necessitamos ir até a porta apenas
quando a campainha tocar. Do mesmo modo, não é eficiente esperar que o microprocessador verifique se
seus periféricos requerem atenção (como na E/S programada). Para isto, existem as interrupções, que são
como campainhas que avisam ao microprocessador que algum dispositivo necessita dele.
3.2.1.3. Prioridade8
Se um sistema possui apenas um dispositivo de E/S, então as interrupções funcionam sempre como
descrevemos, e não há nada mais a dizer sobre elas. Entretanto, um sistema real pode possuir muitos
dispositivos de E/S e vários deles podem estar em atividade simultaneamente. Existe uma probabilidade
não nula de que, no momento em que uma rotina de interrupção está em execução, um segundo dispositivo
de E/S deseje gerar a interrupção dele.
Existem duas abordagens para este problema. Na primeira, todas as rotinas de interrupção inibem
interrupções subseqüentes antes de fazer qualquer coisa, mesmo antes de salvar os registradores. Esta
abordagem é muito simples, pois as interrupções são consideradas estritamente de maneira seqüencial,
mas pode causar problemas quando os dispositivos não podem tolerar muito atraso. Por exemplo, em uma
linha de comunicação de 9600 bps, os caracteres chegam a cada 1.042 µs, estando pronto para receber ou
não. Se o primeiro caractere não foi ainda processado quando o segundo chega, podem-se perder dados.
Quando um sistema possui dispositivos de E/S críticos em relação ao tempo, uma abordagem mais
adequada de projeto é atribuir diferentes prioridades para diferentes dispositivos de E/S, altas para
dispositivos muito críticos e baixas para dispositivos menos críticos. Quando um dispositivo de prioridade
n interrompe, a rotina de interrupção deve também executar com prioridade n.
Quando uma rotina de interrupção da prioridade n está em execução, qualquer tentativa de um
dispositivo com uma prioridade menor causar uma interrupção é ignorada até que a rotina de interrupção
tenha terminado e a CPU recomece a executar o programa principal (Prioridade 0). Por outro lado, deve-se
permitir que interrupções de dispositivos de prioridade mais altas aconteçam sem atraso.
Com as próprias rotinas de interrupção sujeitas a interrupção, a melhor maneira de administrá-las
corretamente é assegurar que todas as interrupções sejam transparentes. Consideremos um exemplo
simples de múltiplas interrupções. Um computador tem três dispositivos de E/S: uma impressora, um disco
e uma interface RS-232, com prioridades 2, 4 e 5, respectivamente. Inicialmente, em t = 0, o programa
principal está em execução quando, de repente, em t = 10, uma interrupção da impressora acontece. A
rotina de serviço de interrupção da impressora é iniciada, como mostra a Figura 22.
Em t = 15, a interface RS-232 requer atenção e gera uma interrupção. Como a interface RS-232
tem uma prioridade (5) maior que a da impressora (2), a interrupção acontece. O estado da máquina, que
está agora executando a rotina de serviço de interrupção da impressora, é empilhado e a rotina de serviço
de interrupção da interface RS232 iniciada.
Um pouco mais tarde, em t = 20, o disco está pronto e quer serviço. Entretanto, sua prioridade (4) é
menor do que a da rotina de interrupção (5) correntemente em execução, de forma que o hardware da CPU
não reconhece (acknowledge) a interrupção, e esta permanece pendente. Em t = 25, a rotina da RS232
termina, assim ela retorna ao estado em que estava logo antes da interrupção da RS232 ter acontecido, ou
seja, executando a rotina de serviço de interrupção da impressora em prioridade 2. Tão logo a CPU
chavear para a prioridade 2, antes mesmo de uma instrução poder ser executada, a interrupção de disco
com prioridade 4 é permitida, e a rotina de interrupção do disco inicia a sua execução. Quando ela termina,
a rotina de impressora retorna a sua execução. Finalmente, em t = 40, todas as rotinas de serviço de
interrupção terminaram e o programa principal continua a partir de onde tinha sido interrompido.
Por simplicidade, algumas CPUs possuem apenas um ou dois níveis de prioridade (como as CPUs da
Intel). Com apenas um nível de interrupção utilizável, não existe maneira de a CPU permitir que um
dispositivo de alta prioridade interrompa uma rotina de serviço de interrupção de média prioridade e ao
mesmo tempo proibir um dispositivo de baixa prioridade de fazê-lo. Para resolver este problema, estas
CPUs são normalmente utilizadas com um controlador de interrupção (ou árbitro de interrupção). Neste
caso, quando a primeira interrupção acontece, digamos com prioridade n, a CPU é interrompida. Se uma
interrupção subseqüente de maior prioridade acontecer, o árbitro interrompe pela segunda vez. Se a
segunda interrupção tiver prioridade menor, ela é suspensa até a primeira terminar. (Quando terminar, a
8
Todo o conteúdo desta seção foi retirado do livro Organização Estruturada de Computadores, de Tanenbaum, Ed. Prentice/Hall do
Brasil.
rotina de interrupção deve enviar explicitamente um comando ao árbitro, para permitir que interrupções
de prioridades mais baixas ocorram.)
9
Sistemas nos quais as tarefas devem ocorrer em instantes de tempo bem determinados.
Os pinos CAS X e SP/EN são utilizados quando deseja-se expandir o número de linhas de
interrupção. Eles serão abordados em um seção específica. Já as entradas RD , WR e CS são as nossas
velhas conhecidas. A novidade é o pino A0 que, junto com RD e WR , são utilizados para enviar comandos
ao 8259A, bem como ler seus registradores. Na verdade, A0 pode ser conectado diretamente às linhas de
endereço do barramento. Finalmente, D7 – D0 são utilizadas como pinos bidirecionais para termos acesso à
todos os registradores do controlador.
5. Neste passo, após receber o opcode da instrução CALL, irá pulsar INTA mais duas vezes.
6. Estes dois pulsos dizem ao 8259A para colocar o endereço de 16 bits da rotina de interrupção
no barramento (primeiro o LSB, depois o MSB).
7. No modo AEOI (Automatic End of Interrupt, ou seja, Fim de Interrupção Automático), o bit do
ISR é desligado após o terceiro pulso de INTA . Caso contrário (o modo padrão), um comando
EOI (constante $20) deve ser enviado.
Os eventos que ocorrem nos sistemas baseados no 8086/88/286 são os mesmos até o passo 4.
4. Nada ocorre durante este passo.
5. Neste passo, o 8086/88/286, irá pulsar INTA uma única vez. Durante este pulso, um bit do ISR é
ligado e um no IRR é desligado. Então, o 8259A põe um inteiro no barramento de dados da CPU. A
CPU irá utilizar este inteiro para endereçar o vetor de interrupção.
3.2.2.3. Interfaceamento
Quando utilizamos apenas um árbitro, a maneira correta de interfacear o 8259A como a CPU é
apresentada na Figura 24. Aqui, desde que o 8259A está trabalhando como mestre, SP/EN deve ser
colocado em Vcc. Fora isto, acredito que nesta altura, tudo deve parecer bastante óbvio.
3.2.2.4. Programação
O 8259A aceita dois tipos de comandos:
1. Comandos de Inicialização (Initialization Command Words, ICWs) - Antes da sua operação normal
poder ser iniciada, cada 8259A do sistema deve ser inicializado. Esta inicialização é feita enviado-
se uma seqüência de dois ou quatro bytes ao 8259A. Durante esta inicialização dizemos com qual
CPU ele está trabalhando (8080/85 ou 8086/88/286), se ele é um mestre ou um escravo, se for
escravo, qual o seu endereço (pinos CAS X, Seção 3.2.2.3), etc. Estes comandos devem ser
enviados uma única vez.
2. Comandos de Operação (Operation Command Words, OCWs) - Com este comandos definimos o
modo de operação dos controladores. Basicamente, existem quatro modos de operação: Fully
Nested (padrão), Rotating Priority, Special Mask e Polled. Em cada um destes modos a prioridade
dos dispositivos ligados ao 8259A pode mudar radicalmente. Só para dar uma idéia, no modo polled
o sistema funciona como na técnica de E/S programada. [Comandos de operação podem ser
enviados em qualquer instante de tempo (após a inicialização)].
Devido à grande capacidade de programação, a configuração do 8259A é um pouco complexa. Como
não iremos utiliza-los em nossas experiências, encerraremos o assunto por aqui. Contudo, caso haja
interesse (ou necessidade), aconselho os manuais 82C59A CMOS Priority Interrupt Controller e 82C59A
Priority Interrupt Controller, ambos podem ser conseguidos gratuitamente no site “Harris Semiconductor
– Search (http://www.semi.harris.com/search/index.htm)”.
interrupção da Tabela 6. Vamos analisar apenas as relacionadas aos pinos IRQ e XIRQ , e a do Timer
Overflow (TOI).
O pino IRQ da CPU 68HC11 possibilita uma maneira de periféricos externos requisitarem
interrupção à CPU. Trata-se de uma interrupção mascarável sensível a nível. Após o RESET, o pino IRQ é
configurado para ser ativo em nível baixo. Contudo, como mostra a Figura 26, isto pode ser modificado
através do bit 5 do registrador OPTION. Porém, como se trata do registrador que controla vários
aspectos importantes da CPU, o seu conteúdo só pode ser alterado nos primeiros 64 ciclos de instrução
após o RESET.
As interrupções de hardware são controladas por dois controladores 8259A. Os sinas de IRQ0 à
IRQ7 (Figura 23) do 8259A mestre são mapeadas nas posições de 8 à 15 do vetor de interrupção. As
IRQs do segundo controlador são numeradas de IRQ8 à IR15 e são mapeados nas posições de 112 (70h) à
119 (77h) do vetor de interrupção. Estas interrupções são associadas a periféricos específicos, como
apresentado na Tabela 8. A IRQ2 de um dos 8259A foi utilizada para interfacear o segundo controlador,
como apresentado na Figura 25. Isto garante mais oito níveis de interrupção.
Em qualquer CPU, as interrupções também são utilizadas para tratar algumas eventos que possam
perturbar o sistema. Por exemplo, caso o programa do usuário tente dividir algum número por zero, a CPU
imediatamente para o processamento e provoca uma interrupção para tratar este situação. A interrupção,
por sua vez, pode, por exemplo, exibir uma mensagem rude na tela do computador ou em um display de
cristal líquido. Este tipo de interrupção, geralmente, recebe o nome de armadilhas ou traps. A principal
diferença entre armadilhas e interrupções é o fato das armadilhas ocorrerem sempre no mesmo treco de
programa (relacionadas a uma determinada instrução), porém nem todas as vezes que o trecho é executado
(como no caso da instrução de divisão). Algumas posições do vetor de interrupção do PC AT são reservadas
especificamente para tratar com este tipo de interrupção.
As posições do vetor reservadas para interrupções de software podem ser utilizadas livremente
pelo programador. Por exemplo, pode-se escrever um programa de forma que uma interrupção seja gerada
sempre que um determinado trecho do programa seja executado. Por sinal, esta é a pricipal característica
das interrupções de software, ou seja, elas ocorrem sempre no mesmo trecho do programa e todas as
vezes que este trecho é executado.
Interrupção Posição do Vetor Controlador
IRQ0 08h Timer (18,2 vezes/s)
IRQ1 09h Teclado
Segundo Controlador de
IRQ2 0Ah
Interrupções
IRQ3 0Bh COM2/COM4
IRQ4 0Ch COM1/COM3
Disponível (geralmente
IRQ5 0Dh
utilizada pela placa de som)
IRQ6 0Eh Disco Flexível
IRQ7 0Fh Porta Paralela (impressora)
IRQ8 70h Real Time Clock
IRQ9 71h Disponível
IRQ10 72h Disponível
IRQ11 73h Disponível
IRQ12 74h Disponível
IRQ13 75h Co-Processador Matemático
IRQ14 76h Disco Rígido
IRQ15 77h Disponível
da rotina de interrupção, todos os registradores são restaurados da pilha. O trecho de código abaixo
ilustra como deve ser uma rotina de interrupção em Linguagem C.
... // As instruções que fazem parte da sua rotina deve ser colocadas aqui
Merece alguma explicação? Então vamos a ela... a palavra chave interrupt diz ao compilador C que esta
rotina salva os registradores na pilha e restaura-os antes de sair da rotina de interrupção. O código
outport(0x20, 0x20); avisa ao 8259A do computador que a requisição de interrupção já foi atendida
(Dúvidas? Consulte a Seção 3.2.2.2.). Dessa forma, outro pedido de interrupção pode, se necessário, ser
feito.
Uma vez escrita a função de tratamento de interrupção, o seu endereço deve ser posto no vetor de
interrupção. No caso da função anterior, caso desejássemos colocar o seu endereço na posição $78 do
vetor de interrupção, teríamos um código como segue:
...
disable() // inibe todas as interrupções mascaráveis
velha_int78 = getvect(0x78); // salva o conteúdo da posição 0x78 do vetor na variável velha_int78
setvect(0x78, nome_da_rotina); // põe o endereço da função “nome_da_rotina” na posição 0x78
enable(); // libera todas as interrupções mascaráveis
...
Tenho certeza que o trecho de código anterior precisa de explicação. Vamos a ela... Primeiro, é
muito perigoso alterar o conteúdo do vetor de interrupção, pois, se alguma interrupção ocorrer enquanto
estamos fazendo isto, pode causar sérios problemas. Por isso, é um ótimo hábito desabilitar as
interrupções antes de mexer no vetor. Evidentemente, quando terminarmos de altera-lo podemos tornar a
habilita-las. É exatamente isto que fazem as instruções disable() e enable(). É claro que antes de
colocarmos o endereço da nossa rotina no vetor de interrupção já existe um endereço lá. Outro bom
hábito de programação é restaurar o endereço original antes de terminarmos nosso programa. Para isto,
temos que salvar o endereço existente no vetor antes de alterá-lo. Isto é feito utilizando uma variável
global. No trecho de programa anterior, utilizamos a variável velha_int78 para salvar este endereço. As
funções getvect(0x78) e setvect(0x78, nome_da_rotina) lêem e escrevem na posição $78 do vetor de
interrupção, respectivamente.
Em um programa em C, antes de uma variável ser utilizada ela deve ser declarada. Em C, a forma de
declarar uma variável que recebe um endereço de uma rotina de interrupção é muito interessante. A forma
de declarar a variável anterior, velha_int78, é
void interrupt (*velha_int78)(); // deve ser colocada no início do programa, fora da função main
Como disse, ao final do programa deve-se restaurar o vetor de interrupção com o endereço salvo.
(No caso anterior, salvo na variável velha_int78.) O Trecho do programa que faz esta tarefa é exibido a
seguir.
Algumas vezes desejamos roubar uma rotina de interrupção pré-existente. Por exemplo, quando
digitamos uma tecla no computador, provocamos uma interrupção. Se desejarmos, podemos substituir a
rotina que trata esta interrupção esta interrupção por uma nossa. Até aqui, não há nada demais. Usamos o
procedimento visto até aqui. Contudo, vale a pena lembrar que toda rotina de interrupção executa um
conjunto de passos indispensáveis para o funcionamento de computador. Por exemplo, a rotina de
tratamento de interrupção do teclada realiza os seguites passos quando solicitada:
1. Recebe a tecla digitada pelo usuário;
2. Armazena o código da tecla no buffer do teclado;
3. Avisa ao controlador do teclado que a tecla já foi salva no bufffer, e;
4. Avisa ao 8259A que a interrupção do teclado já foi atendida.
Caso qualquer um dos passos anteriores não seja executado, nosso computador corre perigo. Isto
seguinifica que temos que garantir que os passos anteriores sejam realizados. Uma forma de garantir isto
é chamando a rotina de interrupção original. Desde que tenhamos salvado o endereço da rotina original,
está é uma tarefa fácil. Para o caso de estarmos trabalhando, por exemplo, com a interrupção de hardware
$08, nossa função de interrupção poderia ser com segue.
... // As instruções que fazem parte da sua rotina deve ser colocadas aqui
Note que agora não executamos a instrução outport(0x20, 0x20). Pois, isto é feito pela rotina original
da interrupção $08.
Por último, segue o corpo completo de um programa que trabalha com rotinas de interrupção em C.
#include <dos.h>
#include <conio.h>
#include <stdio.h>
void interrupt (*velha_int)(); // Declara a variável que guarda o endereço da rotina original
void main(void)
{
clrscr();
disable();
velha_int = getvect(??); // escolha a posição do vetor de interrupção
setvect(??, nova_int); // escolha a posição do vetor de interrupção
enable();
disable();
setvect(??, velha_int); // escolha a posição do vetor de interrupção
enable();
}
Para facilitar a escrita de futuros programas, na Tabela 9 é apresentado o conteúdo de cada posição
do vetor de interrupção. Faça bom proveito!
Posição do Vetor Interrupção Controlador
Armadilhas
00 - 01
(Traps)
Interrupção de
02 Erro de Paridade
Hardware
Armadilhas
03 - 07
(Traps)
08h IRQ0 Timer (18,2 vezes/s)
09h IRQ1 Teclado
Segundo Controlador de
0Ah IRQ2
Interrupções
0Bh IRQ3 COM2/COM4
0Ch IRQ4 COM1/COM3
Disponível (geralmente
0Dh IRQ5
utilizada pela placa de som)
0Eh IRQ6 Disco Flexível
0Fh IRQ7 Porta Paralela (impressora)
Interrupções de
10 – 6F
Software
70h IRQ8 Real Time Clock
71h IRQ9 Disponível
72h IRQ10 Disponível
73h IRQ11 Disponível
74h IRQ12 Disponível
75h IRQ13 Co-Processador Matemático
76h IRQ14 Disco Rígido
77h IRQ15 Disponível
Interrupções de
78 - FF
Software
Tanto no PC original como no AT, o limite de transferência via DMA é limitado pelas necessidades e
refresh de memória. Um ciclo de refresh é executado a cada 15 µs. Por isso, um dispositivo que utilize o
modo demand tem de interromper o sinal de DRQ e liberar o barramento a cada 15 µs. Além disso, o modo
block não é muito viável neste tipo de sistema, pois apenas 24 transferências podem ser efetuadas em 15
µs.
3.4.1. Especificações
RS-232 é um padrão completo, ou seja, ele especifica 1) características elétricas, 2) sinais de
controle e handshake, e 3) características mecânicas. Cada uma dessas três características são discutidas
a seguir.
4.1 Discretização
Sabemos que em um intervalo de tempo de 1 µs não ocorre variação de temperatura significativa.
Por isso, não adianta observar a temperatura durante este período. Neste caso, poderíamos definir um
intervalo de tempo mínimo durante o qual não observaríamos a variação na temperatura. Dependendo da
aplicação, o intervalo poderia ser escolhido como sendo 1 min. Dessa forma, a temperatura seria
considerada constante (ou mesmo inexistente) durante todo o período de 1 min após a última observação.
Desta forma, a temperatura seria considerada uma grandeza discreta no tempo, ou seja, existiria apenas
para períodos de tempo específicos.
Na Figura 29 é apresentado (a) representação gráfica de um discretizador que transforma a
grandeza analógica f(t) em um sinal discreto no tempo, (b) a representação matemática de um
discretizador e (c) o gráfico da grandeza analógica (linha contínua) e do sinal discreto (pontos grossos).
para obter o trasformada da Figura 30(f). Desse modo, a Figura 30(e) representa do sinal f(t)
discretizado e a Figura 30(f) a Transformada de Fourier do mesmo sinal após a discretização.
Figura 30 – (a) sinal analógico f(t), (b) Transformada de Fourier de f(t), F(ω), (c)
representação gráfica de um discretizador de Nyquist, (d) Transformada de Fourier do
discretizador de Nyquist, (e) sinal discreto no tempo e em amplitude e (f)
Transformada de Fourier do sinal discreto no tempo e em amplitude.
Figura 31 – (a) sinal discretizado, f(n), (b) Transformada de Fourier de f(n), (c) gráfico
da função sinc, (d) Transformada de Fourier da função sinc, (e) sinal analógico f(t) e (f)
Transformada de f(t).
O fenômeno de aliasing pode ainda ocorrer durante a amostragem de sinais com largura de faixa
infinita, ou seja, sinais cuja Transformada de Fourier ocupa toda a faixa de freqüência (todo o espectro).
Neste caso, o problema não é a função de transferência do filtro passa-baixas, mas sim a largura de faixa
infinita do sinal. Pois, segundo o Teorema de Shannon, seria necessário uma freqüência de amostragem
infinita para digitalizar o sinal. Para contornar este problema, tem-se que efetuar um estudo no sinal que
pretende-se digitalizar para descobrir qual a faixa de freqüência que realmente contribui para a sua
forma (diz-se que encontra-se a faixa de freqüência na qual concentra-se a maior quantidade de energia
do sinal). Quando a faixa de freqüência mais significante do sinal é encontrada, tem-se que aplicar um
filtro passa-baixas (algumas vezes passa-faixa), cuja freqüência de corte é igual à largura de faixa
encontrada, ao sinal antes de digitaliza-lo. Desse modo, a freqüência de Nyquist de um sinal com largura de
faixa infinita é duas vezes a maior componente de freqüência do sinal que contribui de forma significativa
pa a sua forma.
4.3 Quantização
Como visto na Seção 4.1, o processo de discretização (ou amostragem) transforma o sinal analógico
f(t) em um sinal discreto no tempo. Contudo, f(t) continua sendo contínuo em amplitude. Por exemplo, a
temperatura, assim como a maioria das grandezas existentes na natureza, é analógica. Desta forma, para a
temperatura variar de 29 para 30 OC, ela tem que passar por todos os valores intermediários. Porém,
provavelmente não é interessante saber que a temperatura variou de 29,0000000000 para
29,0000000001 OC. Pois os reflexos dessa mudança são desprezíveis. Neste caso, dependendo da
aplicação, podemos escolher a menor variação de temperatura relevante. Este valor, por exemplo, pode ser
Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 75
Curso de Microprocessadores
0,5 OC. Dessa forma, seria relevante saber se a temperatura variou, como exemplo, de 29,0 para 29,5 OC.
Contudo, qualquer valor entre estes dois extremos seria considerada uma variação desprezível e poderia
ser considerada zero, ou seja, 29,1 OC ou mesmo 29,4 OC poderia ser considerado 29,0 OC. Isto quer dizer
que estaríamos transformando a temperatura em uma grandeza discreta em amplitude, ou seja, uma
grandeza que pode assumir apenas uma quantidade finita de valores entre dois valores quaisquer. Para o
nosso exemplo, dentro do intervalo 25 OC e 30 OC a temperatura poderia assumir os valores 25,0; 25,5;
26,0; 26,5; 27,0; 27,5; 28,0; 28,5; 29,0; 29,5 e 30 OC. O processo descrito anteriormente chama-se
quantização.
O
C, considera-se a temperatura como sendo 1 OC. O mesmo raciocínio pode ser aplicado a todos os outros
níveis de quantização.Em particular, para valores de temperatura maiores que 7 OC, considera-se a
temperatura como sendo 6 OC. Assim, o sinal discretizado no tempo está agora discretizado também em
amplitude.
Em todo processo de quantização comete-se um erro. Este erro diminui à medida que o número de
níveis de quantização aumenta. Isto é bastante intuitivo, pois, à medida que aumenta-se o número de níveis
de quantização, diminui-se o tamanho dos degraus da Figura 34(a). Fazendo com que a escada Figura 34(a)
aproxime-se de uma rampa perfeita.
6
6
Níveis de Quantização
Níveis de Quantização
5
5
4
4
3
3
2
2
1
1
O O
1 2 3 4 5 6 7 C 1 2 3 4 5 6 7 C
Temperatura em graus celsius Temperatura em graus celsius
(a) (b)
Figura 34 – Quantização da temperatura em sete níveis de quantização.
Observando-se novamente a Figura 34(a), nota-se que, quando o valor de temperatura aproxima-se
da ponta de um degrau, por exemplo, em T um pouco menor que 3 OC, o erro de quantização é máximo e é
igual a 1 OC. Deslocando-se os degraus da Figura 34(a) um pouco para a esquerda (exatamente 0,5 OC),
tem-se o gráfico da Figura 34(b). Neste gráfico, o erro máximo cometido é de 0,5 OC. Por isso, no
processo de quantização, o esquema da Figura 34(b) é mais utilizado, pois reduz o erro máximo pela
metade.
Generalizando, pode-se afirmar que o processo de quantização pode ser representado pela Figura
35. Nesta figura o processo de quantização é apresentado em termos do nível de quantização q. (Para o
exemplo anterior, q seria igual a 1 OC.) Além disso, o erro máximo cometido no processo de quantização é
0,5q. Lembrando que a precisão do quantizador é dada em função do número de níveis de quantização.
4.4 Codificação
Entendido o processo de quantização, torna-se fácil o conceito de codificação. Pois, o processo de
codificação consiste em atribuir-se um número binário a cada um dos níveis de quantização. Assim, o
número de bits utilizado na representação de cada nível de quantização depende do número de níveis. Por
isso, o número de bits do codificador também é uma maneira de expressar a precisão do codificador e,
conseqüentemente, do sinal digitalizado.
O processo de codificação é ilustrado na Figura 36.
Figura 37 – (a) sinal digitalizado, quantizado e codificado, (b) sinal na saída do conversor
D/A e (c) sinal na saída do filtro "suavizador".
Neste ponto, falta ainda transformar o sinal na saída do converosr D/A discreto em aplitude.
Observando novamente a Figura 31, vê-se que os degraus da Figura 37(b) são causados por componentes
de alta freqüência contidas no sinal digitalizado. Dessa forma, para obter-se o sinal original, tem-se que
aplicar o sinal da Figura 37(b) ao filtro passa-baixas da Figura 31(b). Este filtro algumas vezes é chamado
de Filtro "Suavizador" (Smoothing Filter). Finalmente, após o Smoothing Filter, o sinal f(t) é (quase)
completamente recuperado.
4.6.1. Resolução
A resolução de um D/A é a menor variação de tensão possível sua na saída (analógica). No A/D, a
resolução é a menor variação de voltagem que pode ser detectada pelo sistema e que produz uma variação
no código digital. A resolução determina o número total de códigos digitais, ou níveis de quantização, que
são reconhecidos ou produzidos pelo circuito conversor.
A resolução de um D/A ou A/D é geralmente especificada em termos de bits no código digital. Um
código de n bits permite 2n níveis de quantização, ou 2n-1 degraus dentro da faixa dinâmica10 do
conversor, como é apresentado na Figura 38. Quando o número de bits aumenta, o tamanho dos degraus de
quantização (Seção 4.3) diminui, logo a exatidão do sistema aumenta. A resolução do sistema pode ser
especificada também em termos do "tamanho" dos degraus de quantização (Figura 35). Contudo, neste
caso, a resolução deve ser especificada em termos da faixa dinâmica do conversor. Por exemplo, resolução
de 5 mV para uma faixa dinâmica de 5 V.
4.6.2. Velocidade
A velocidade de um conversor A/D ou D/A é determinada pelo tempo que ele leva para realizar a
conversão. Para os conversores D/A, a velocidade é especificada em termos de tempo de ajuste (o tempo
necessário para que a tensão na saída do D/A torne-se estável). Já para os conversores A/D, ela é
especificada em termos de tempo de conversão (tempo necessário para que o A/D realize a conversão).
Nos conversores D/A, o tempo de ajuste depende da faixa dinâmica do conversor e da transição do código
binário, ou seja, leva mais tempo para obter-se uma saída estável em uma transição de 0000 para 1111, do
que em uma de 1110 para 1111. Por isso, o tempo de ajuste é especificado nas condições apropriadas.
Os conversores A/D têm um tempo de conversão mínimo que limita a velocidade na qual eles podem
realizar conversões contínuas. A taxa de amostragem é o número de vezes por segundo que o sinal
analógico pode ser convertido no código digital. Logo, um conversor com tempo de conversão de 1 µs pode
10
A grosso modo, a faixa dinâmica de um conversor pode ser entendida como a faixa de tensão que pode ser aplicada a entrada (A/D)
ou gerada na saída (D/A) do mesmo.
Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 79
Curso de Microprocessadores
converter um sinal a uma taxa de amostragem máxima de 1 MHz. Por isso, devido ao teorema de Shannon,
a velocidade de um conversor A/D é um critério importantíssimo na escolha do tipo de conversor.
4.6.3. Linearidade
Em termos de conversores A/D e D/A, existem dois tipos de linearidades: linearidade integral e
linearidade diferencial. A linearidade integral é definida em termos do desvio de uma linha reta da origem
ao final da faixa dinâmica do conversor. Esta linearidade pode ser conhecida aplicando-se valores da linha
reta em questão na entrada do conversor e observando a sua saída. Na Figura 39 é apresentado o
resultado da conversão de uma linha reta por um conversor A/D e o desvio dos pontos amostrados em
relação aos pontos da linha reta aplicada a sua entrada. A linearidade integral deve ser considerada o
maior desvio encontrado, neste caso, 0,5 LSB (Figura 39).
A linearidade diferencial é a linearidade entre transições de código. Ela pode ser entendida como a
medida da monotonicidade do conversor. Um conversor é dito monotônico se um aumento nos seus valores
de entrada resulta em um aumento correspondente na sua saída. A Figura 40 mostra a característica de
um conversor A/D ideal e a de um A/D com um código faltando (linearidade diferencial de 1 LSB).
Figura 39 – Linha reta aparente adquirida com um conversor A/D e o gráfico do desvio
dos postos digitalizados em relação a linha reta real.
Vr
b0 R
b1 R/2 N
+
b2 R/4 RL Vo
-
Vo =
(
RLVr
2 − 1 RL + R
n
) (
bo + 2b1 + ... + 2 n−1 bn −=1 ) (Eq. 2)
R R
Vo
(b) 2R 2R 2R 2R 2R R2
(a)
)
(b
R 2
2 R
R 2
2 R
b
0
R
2
I 1
R
1
b
R
2
R 2 V
2
2
b
R I2
+
R o
-
R 2
2 R R 2
b0 b1 b2 R1
0
b 1
b 2
b 1
R
Figura 42 – (a) Conversor R-2R de 3 bits e (b) conversor R-2R com saída amplificada.
Novamente, uma configuração como a da Figura 42b pode ser utilizada para eliminar o termo 1/12.
Generalizando, podemos escrever para um circuito semelhante ao da Figura 42a, mas com n entradas
V0 =
1
3× 2
(
( n −1)
)
b0 + 2b1 + ... + 2 n−1 bn−1 .
Finalizando, pode-se dizer que, por razões óbvias, este tipo de conversor D/A é o mais utilizado.
Observe que o tempo de ajuste do circuito da Figura 42b depende apenas do tempo que as entradas b0, b1,
etc, tornam-se estáveis e, como explicado anteriormente, da faixa dinâmica do conversor e da transição
do código binário. Isto quer dizer que não é muito difícil fabricar-se conversores D/A extremamente
rápidos.
4.096 mV
MSB
00111110
4.096 mV
+
D/A
+ - 8 bits
992 mV 2.048 mV/16 mV=128
4.096 mV
seleção do chip, por exemplo, o E1 . Outro pino interessante é o G . Este pino, quando desativado, coloca
os pinos de dados da memória em alta impedância. Por isso, sempre que desejarmos realizar alguma
operação com a memória (uma leitura ou escrita), é necessário ativar este pino. Novamente, uma outra
forma de se “trabalhar” é garantir que este pino esteja sempre ativo e colocar-se as linhas de dados em
alta impedância com o um dos pinos Chip Enable.
Preste atenção! O que eu disse no parágrafo anterior não é regra. Apenas pode ser utilizado na
maioria dos projetos. Contudo, você pode se deparar com uma aplicação na qual você precise usar todos os
pinos de controle da memória 6264. Isto também á válido para todos os outros chips discutidos neste
capítulo.
um dos pinos do dispositivo que deseja-se “interfacear” com o 68HC11 aos barramentos da Figura 51. A
Figura 52 apresenta as ligações necessárias para conectar uma memória 6264 com o 68HC11.
Endereço Endereço
Dado Dado
Escrita Leitura
E E
AS AS
(a) (b)
Figura 50 – (a) Diagrama de tempo de um ciclo de escrita típico no 68HC11; (b) Diagrama
de tempo de um ciclo de leitura típico no 68HC11
para acessar a primeira posição da memória (o seu endereço base) os pinos A13, A14 e A15 não são
relevantes. Dessa forma, o endereço base da memória será acessado quando a constante $0000 (16 bits),
$2000, $4000, $6000, $8000, $A000, $C000 ou $E000, for colocado nas linhas de endereço do 68HC11.
Contudo, o endereço base não é acessado quando, por exemplo, se a constante $B000 é posta nas linhas de
endereço, pois, neste caso, A12 é igual à “1”. Com isso, a posição acessada é a de número 2.048.
74HC373
31 3 2 10 11
30 AD0 32 4 1D 1Q 5 9 A0 D0 12
XT AD1 33 7 2D 2Q 6 8 A1 D1 13
AD2 34 8 3D 3Q 9 7 A2 D2 15
29 AD3 35 13 4D 4Q 12 6 A3 D3 16
EX AD4 36 14 5D 5Q 15 5 A4 D4 17
AD5 37 17 6D 6Q 16 4 A5 D5 18
AD6 38 18 7D 7Q 19 3 A6 D6 19
AD7 8D 8Q 25 A7 D7
26 11 1 24 A8
AS C OC 21 A9
16 23 A10
A8 15 2 A11
A9 14 A12
A10 13 20
39 A11 12 26 CS1
RESET A12 11 27 CS2 22
A13 10 WE OE
A14 9
A15
MCM6264
27
E
41 28
IRQ R/W
40
XIRQ
25
MODA 24
MODB
68HC11A8
74HC373
31 3 2 10 11
30 AD0 32 4 1D 1Q 5 9 A0 D0 12
XT AD1 33 7 2D 2Q 6 8 A1 D1 13
AD2 34 8 3D 3Q 9 7 A2 D2 15
29 AD3 35 13 4D 4Q 12 6 A3 D3 16
EX AD4 36 14 5D 5Q 15 5 A4 D4 17
AD5 37 17 6D 6Q 16 4 A5 D5 18
AD6 38 18 7D 7Q 19 3 A6 D6 19
AD7 8D 8Q 25 A7 D7
26 11 1 24 A8
AS C OC 21 A9
16 23 A10
A8 15 2 A11
A9 14 A12
A10 13 20
39 A11 12 26 CS1
RESET A12 11 1 CS2 22
A13 10 3 1 27 OE
A14 9 2 3 WE
A15 2 MCM6264
27
E
41 28
IRQ R/W
40
XIRQ
25
MODA 24
MODB
68HC11A8
Figura 53 - Interface entre o 68HC11 e uma memória 6264. Neste caso, a memória
possui endereço base $0000.
E se desejarmos acrescentar outro dispositivo ao sistema? Da mesma forma, basta conectar os
pinos do novo dispositivo aos barramentos do 68HC11. Contudo, deve-se tomar o cuidado de fazer com que
NUNCA os dois dispositivos sejam acessíveis ao mesmo tempo. Isto é possível se cada dispositivo contiver
o seu próprio endereço base. Na Figura 54 é apresentado o mesmo sistema da Figura 53 acrescido de uma
memória EPROM 2764. Foi utilizada uma porta lógica NAND de três entradas para fornecer o endereço
base $E000 à memória EPROM. Com isso, os chips nunca são acessados ao mesmo tempo.
5V
74HC373 U3
27
1
31 3 2 10 11 10
PGM
VPP
30 AD0 32 4 1D 1Q 5 9 A0 D0 12 9 A0 11
XT AD1 33 7 2D 2Q 6 8 A1 D1 13 8 A1 O0 12
AD2 34 8 3D 3Q 9 7 A2 D2 15 7 A2 O1 13
29 AD3 35 13 4D 4Q 12 6 A3 D3 16 6 A3 O2 15
EX AD4 36 14 5D 5Q 15 5 A4 D4 17 5 A4 O3 16
AD5 37 17 6D 6Q 16 4 A5 D5 18 4 A5 O4 17
AD6 38 18 7D 7Q 19 3 A6 D6 19 3 A6 O5 18
AD7 8D 8Q 25 A7 D7 25 A7 O6 19
26 11 1 24 A8 24 A8 O7
AS C OC 21 A9 21 A9
16 23 A10 23 A10
A8 15 2 A11 2 A11
A9 14 A12 A12
A10 13 20
39 A11 12 26 CS1 20
RESET A12 11 1 CS2 22 CE
A13 10 3 1 27 OE 22
A14 9 2 3 WE OE
A15 2 27C64
MCM6264
27
E
41 28
IRQ R/W
40
XIRQ 1
25 2 12
MODA 24 13
MODB
68HC11A8
Figura 54 – Interface entre o 68HC11 e uma memória 6264 (base $0000) e uma
memória EPROM 2764 ($E000).
Evidentemente, à medida que a quantidade de dispositivos existentes no sistema aumenta, o uso de
lógica adicional para fornecer endereços base únicos à cada dispositivo do sistema torna-se muito
complexa, cansativa e cara. Por isso, o mais eficiente é utilizar o decodificador 74HC138 para selecionar
cada chip individualmente.
Na Figura 55 é apresenta da um sistema que possui uma memória 6264 (endereço base $4000) e
uma memória EPROM 2764 ($E000). Neste caso, foi utilizado o 74HC138 para atribuir os endereços base.
Observa-se que os dispositivos conectados ao 74HC138 podem possuir apenas os seguintes endereços
base: $0000, $2000, $4000, $6000, $8000, $A000, $C000 ou $E000. Cada dispositivo irá ocupar uma
faixa de 8 kbytes de memória (independente de quantas posições de memória ou registradores ele
possuir). Isto é ruim, sobretudo para dispositivos de E/S. Por exemplo, um dispositivo que possua três
registradores internos (configuração, status e dados) tem apenas duas linhas de endereço. Neste caso,
como ele possui apenas três posições de memória endereçáveis, estariam sendo desperdiçados 8.192 – 3 =
8.189 endereços. Contudo, como um pouco de lógica adicional, pode-se fazer com que dois (ou mais)
dispositivos de E/S compartilhem o mesmo espaço de endereçamento de 8 kbytes. Desta forma, reduz-se
o desperdício de espaços de endereçamento. No entanto, tenha em mente que, caso o seu sistema não
precise de muitos espaços de endereçamento, ou seja, não possua muito dispositivos, você não precisa
preocupar-se com os endereços desperdiçados.
5V
74HC373 U3
27
1
31 3 2 10 11 10
PGM
VPP
30 AD0 32 4 1D 1Q 5 9 A0 D0 12 9 A0 11
XT AD1 33 7 2D 2Q 6 8 A1 D1 13 8 A1 O0 12
AD2 34 8 3D 3Q 9 7 A2 D2 15 7 A2 O1 13
29 AD3 35 13 4D 4Q 12 6 A3 D3 16 6 A3 O2 15
EX AD4 36 14 5D 5Q 15 5 A4 D4 17 5 A4 O3 16
AD5 37 17 6D 6Q 16 4 A5 D5 18 4 A5 O4 17
AD6 38 18 7D 7Q 19 3 A6 D6 19 3 A6 O5 18
AD7 8D 8Q 25 A7 D7 25 A7 O6 19
26 11 1 24 A8 24 A8 O7
AS C OC 21 A9 21 A9
16 23 A10 23 A10
A8 15 2 A11 5V 2 A11
A9 14 A12 26 A12
A10 13 20 CS2
39 A11 12 CS1 20
RESET A12 11 22 CE
A13 10 27 OE 22
A14 9 74HC138 WE OE
A15 27C64
MCM6264
27 1 15
E 2 A Y0 14
3 B Y1 13
C Y2 12
6 Y3 11
41 28 G1 Y4 10
IRQ R/W 5 Y5 9
4 G2BY6 7
40 G2AY7
XIRQ
25
MODA 24
MODB
68HC11A8
Figura 55 - Interface entre o 68HC11 e uma memória 6264 (base $4000) e uma
memória EPROM 2764 ($E000). Neste caso, foi utilizado o 74HC138 para atribuir os
endereços base.
Apesar do 74HC138 facilitar a definição dos endereços bases dos diferentes dispositivos do
sistema, geralmente, os endereços dos dispositivos internos do microcontrolador não devem conflitar com
os endereços dados aos dispositivos externos. Para evitar isto, o mapa de memória do microcontrolador
deve ser utilizado. Na Figura 56 é apresentado o mapa de memória do 68HC11. Na figura, as áreas escuras
são áreas ocupadas por dispositivos internos. Por exemplo, o endereço $0000 à $03FFF é ocupado com a
memória RAM interna ao microcontrolador. Por isso, nenhum dispositivo externo pode ocupar esta faixa de
memória. Em vista disso, os circuitos da Figura 52 e da Figura 53 estão errados, pois um dispositivo
externo está ocupando esta faixa de memória.
(a) (b)
Figura 57 – (a) esquema de ligação do cristal oscilador no 68HC11. (b) sugestão para
confecção da placa de circuito impresso.
5.4 RESET
Em qualquer sistema microprocessado pelo menos uma interrupção é utilizada: a interrupção do
RESET. Esta interrupção deve ser ativada sempre que o sistema é ligado ou o usuário aperta um botão.
Esta ativação ocorre através de um pino do microprocessador. Este pino, dependendo do
microprocessador, pode ser ativo em nível alto ou baixo. A Figura 58 apresenta várias configurações para
RESET. A saída Vo (Figura 58) deve ser conectada ao pino de reset do microprocessador (ou
microcontrolador). Para os circuitos desta figura, os três circuitos de cima devem ser utilizados para
resets ativos em nível alto. O primeiro circuito é o mais simples de todos. Simplesmente, Vo vai para 5 V
momentaneamente quando o sistema é ligado ou quando o usuário aperta o botão da figura. O circuito do
meio acrescenta um resistor de 1 kΩ para impedir que uma corrente que surja durante a descarga do
capacitor danifique o microcontrolador. Já o circuito da direita, utiliza um inversor de histerese para
limpar o sinal gerado pelo capacitor. Os circuitos na linha de baixa são os mesmos circuitos, só que para
pinos de reset ativos em nível baixo.
5V
5V 5V
10 uF 10 uF 100 k
1 2
1k
Vo Vo
100 k 100 k 10 uF Vo
5V 5V
5V
100 k 100 k
10 uF
1 2
1k
10 uF Vo 10 uF Vo Vo
100 k
5V
U3
27
1
U1 U2 U6
31 39 3 2 10 10 11
PGM
VPP
EA/VP P0.0 38 4 1D 1Q 5 9 A0 11 9 A0 D0 12
33 pF C1 P0.1 37 7 2D 2Q 6 8 A1 O0 12 8 A1 D1 13
19 P0.2 36 8 3D 3Q 9 7 A2 O1 13 7 A2 D2 15
X1 P0.3 35 13 4D 4Q 12 6 A3 O2 15 6 A3 D3 16
12 MHz P0.4 34 14 5D 5Q 15 5 A4 O3 16 5 A4 D4 17
Y2 18 P0.5 33 17 6D 6Q 16 4 A5 O4 17 4 A5 D5 18
X2 P0.6 32 18 7D 7Q 19 3 A6 O5 18 3 A6 D6 19
14 P0.7 30 8D 8Q 25 A7 O6 19 25 A7 D7
33 pF C2 15 T0 ALE/P 11 1 24 A8 O7 24 A8
T1 21 C OC 21 A9 21 A9
5V 12 P2.0 22 74HC373 23 A10 23 A10
13 INT0 P2.1 23 2 A11 2 A11
C3 INT1 P2.2 24 A12 A12
SW1 10 uF 9 P2.3 25 20
RESET P2.4 26 20 26 CS1
1 P2.5 27 CE 27 CS2
RST 2 P1.0 P2.6 28 U4 22 22 WE
3 P1.1 P2.7 1 15 OE OE
100 k R1 4 P1.2 17 2 A Y0 14 27C64 MCM6264
5 P1.3 RD 16 3 B Y1 13
6 P1.4 WR C Y2 12
7 P1.5 5V Y3 11
8 P1.6 29 U5A 6 Y4 10
P1.7 PSEN 1 4 G1 Y5 9
3 5 G2AY6 7
80C31
2 G2BY7
74HC138
74HC08
O fato do 80C31 possuir um pino que é ativado apenas quando o microcontrolador quer buscar
instruções tem uma implicação interessante. Como o pino PSEN, pode-se criar dois espaços de
endereçamento: uma para memória de dados e dispositivos de E/S e outro para memória de programa. Na
Figura 61 foi utilizado um outro 74HC138 para permitir que espaços de endereçamentos distintos. Com
isso, o microcontrolador pode endereçar até 64 kbytes de memória de programa e até 64 kbytes de
memória de dados.
5V
U3
27
1
U1 U2 U6
31 39 3 2 10 10 11
PGM
VPP
EA/VP P0.0 38 4 1D 1Q 5 9 A0 11 9 A0 D0 12
33 pF C1 P0.1 37 7 2D 2Q 6 8 A1 O0 12 8 A1 D1 13
19 P0.2 36 8 3D 3Q 9 7 A2 O1 13 7 A2 D2 15
X1 P0.3 35 13 4D 4Q 12 6 A3 O2 15 6 A3 D3 16
12 MHz P0.4 34 14 5D 5Q 15 5 A4 O3 16 5 A4 D4 17
Y2 18 P0.5 33 17 6D 6Q 16 4 A5 O4 17 4 A5 D5 18
X2 P0.6 32 18 7D 7Q 19 3 A6 O5 18 3 A6 D6 19
14 P0.7 30 8D 8Q 25 A7 O6 19 25 A7 D7
33 pF C2 15 T0 ALE/P 11 1 24 A8 O7 24 A8
T1 21 C OC 21 A9 21 A9
5V 12 P2.0 22 74HC373 23 A10 23 A10
13 INT0 P2.1 23 2 A11 2 A11
C3 INT1 P2.2 24 A12 A12
SW1 10 uF 9 P2.3 25 20
RESET P2.4 26 20 26 CS1
1 P2.5 27 CE 27 CS2
RST 2 P1.0 P2.6 28 U4 22 22 WE
3 P1.1 P2.7 1 15 OE OE
100 k R1 4 P1.2 17 2 A Y0 14 27C64 MCM6264
5 P1.3 RD 16 3 B Y1 13
6 P1.4 WR C Y2 12
7 P1.5 5V Y3 11
8 P1.6 29 6 Y4 10
P1.7 PSEN 4 G1 Y5 9
5 G2AY6 7
80C31
G2BY7
74HC138
U4
1 15
2 A Y0 14
3 B Y1 13
C Y2 12
U5A 5V Y3 11
1 6 Y4 10
3 4 G1 Y5 9
2 5 G2AY6 7
G2BY7
74HC08 74HC138
Comentário
Este apêndice contém um breve manual do BASIC Stamp II. O texto está em inglês e sem
formatação, pois foi extraído de um arquivo texto (MANUAL.TXT) que acompanha o BS2.
BS2-IC
Internal Perspective:
The BS2 has 2K bytes of EEPROM which holds the executable BASIC program and any
data. Memory not used by the BASIC program can be read and written at run-time
as a data bank, or initialized with data at download time. This memory is only
affected by downloading or run-time modification.
There are 32 bytes of RAM which serve as variable space and I/O pin interface
for the BASIC program. This memory can be accessed as words, bytes, nibbles,
or bits. Each time the BASIC program is run anew, this memory is cleared to
all zeroes.
So, the 2K byte EEPROM is for program and data, and only affected by initial
downloading or run-time modification. It survives power-down. The 32 bytes of
RAM are for run-time variables and I/O pin access. This memory is cleared each
time the BS2 is powered up, reset, or downloaded to.
Word $0 always reflects the read-state of all 16 I/O pins. Whether a pin is an
input or output, it's logical state can be read in this word. Word $0 is
accessed by the following symbolic names:
Word $1 contains the output latches for all 16 I/O pins. If a pin is in input
mode, this data is unused, but when a pin is in output mode, its corresponding
word $1 bit sets its state. The bits are all readable and writable, regardless
of pin direction. The following symbolic names access word $1:
OUTS the entire 16-bit word
OUTL the low byte of OUTS
OUTH the high byte of OUTS
OUTA the low nibble of OUTL
OUTB the high nibble of OUTL
OUTC the low nibble of OUTH
OUTD the high nibble of OUTH
OUT0 the low bit of OUTS - corresponds to pin P0
|
|
|
OUT15 the high bit of OUTS - corresponds to pin p15
Word $2 contains the direction bits for all 16 I/O pins. To place a pin in
input mode, its corresponding word $2 bit must be cleared to 0. To put a pin
into output mode, its corresponding word $2 bit must be set to 1, at which
time its word $1 bit will determine whether it is high or low. Word $2 has
Words $3-$F are for general purpose variable use and have no pre-assigned
symbolic names. The VAR statement is used to allocate this memory.
The above text has introduced the physical pin-out of the BS2 as well as
the internal EEPROM, RAM, and I/O structure.
In the BASIC Stamp II, there are two general categories of BASIC statements:
compile-time and run-time. Compile-time statements are resolved when you
compile the program (Alt-R or Alt-M) and do not generate any executable code.
Run-time statements, on the other hand, generate code and are executed at run-
time.
There are three compile-time statements. They are used for declaring
variables, constants, and data. They are:
Your program should begin with a declaration of all of its variables. VAR
statements assign symbolic names to variable RAM (RAM not used by I/O - words
$3-$F). This is done as follows:
The compiler will group all words, bytes, nibs, and bits, and respectively
arrange them into unused RAM. By pressing Alt-M, you can see a picture of
the RAM allocation. First, the three I/O words are shown, then all words,
bytes, nibs, and finally, bits, are seen. Empty RAM follows. Alt-M is a quick
way to assess how much RAM you've used.
The compiler will group all declarations by size (in the case of unique
variables) and assign them to unused RAM. Alt-M lets you see the result of
this process. Non-unique variables are in-whole or in-part derived from unique
variables and get assigned within the unique-variable memory.
Keep in mind that you may make alias names for the pin variables:
The CON statement is similar to the VAR statement, except that it is for
defining constant values and assigning them to symbolic names. This is
handy for having a single declaration which gets accessed throughout your
program. The CON syntax is as follows:
expressions after CON can contain the following binary operators and are
resolved left-to-right:
+ add
- subtract
* multiply
/ divide
<< shift left
>> shift right
& logical AND
| logical OR
^ logical XOR
example:
growth CON 100-light/gel '"light" and "gel" are CON's, too
EEPROM memory not used by your BASIC program can be used for data storage.
Keep in mind that your BASIC program builds from the end of memory towards
the start of memory. This allocation is automatic. Your data, on the other
hand, builds from the start of memory towards the end. The sum of program and
data memory cannot exceed the 2K byte limit. The compiler will always tell you
when you have a conflict.
DATA statements are used to build data into unused memory. Initially, the DATA
location is set to 0. It is advanced by 1 for each byte declared. Here is an
example DATA statement:
Usually, you will want to precede DATA statements with a unique symbol name.
The symbol name will be assigned a constant value (as if via CON) which is the
current data pointer. The text following 'DATA' is usually a list of bytes
which can be constant expressions. In the above example (assuming this was
the first DATA statement in the program), "table" becomes a constant symbol of
value 0; "Here is a string..." is broken into individual bytes and placed
into EEPROM memory sequentially. Alt-M and two <SPACE>s will show you the
result of this line.
The DATA pointer may be altered at any time by an @ sign followed by a new
pointer value:
DATA has a few variations of use to allocate defined and undefined data.
Defined data is fully declared and known at compile time. Undefined data
is the mere allocation of data space, while not assigning values into the bytes
of EEPROM (to be done at run-time, instead). Defined and undefined data are
declared as follows:
Important concept: Defined DATA and BASIC program memory are always
downloaded to the BS2. Undefined data and unused
EEPROM memory are not downloaded. This allows you
to change programs while keeping data, assuming both
programs defined the same stretch of memory as
undefined DATA. Alt-M will show you maps of EEPROM
allocation. This download/don't-download rule is
applied to 16-byte blocks. If any byte within a 16-
byte block is defined DATA or BASIC program, that
whole block is downloaded. Use Alt-M to see this.
In summary, DATA is used to define EEPROM byte usage that doesn't conflict
with the BASIC program storage:
- DATA can be preceeded by a symbol which will be assigned the constant value
of the current DATA pointer.
- Byte-size data is assumed, but 'word' can be used to break a word into two
bytes of storage.
- Defined data may be repeated at the byte or word level using (array).
Run-Time Expressions
----------------------------------------------------
Note: When more than one character is within quotes, they are
separated by the compiler as such: "DOG" becomes "D","O","G".
"String"+$80 becomes: "S","t","r","i","n","g"+$80.
Examples:
sin bytevar
sqr 50000
~ in0
Examples:
ypos * xsize + xpos
randword // 20
countacc min 200 - 200 / 200
For use within conditional expressions (IF), there is a special unary operator
and several binary operators. These conditional operators have highest
priority of all.
Note: These comparison operators return 0 for false and $FFFF for true.
Combined with NOT, AND, OR, and XOR, complex tests can be done.
Run-Time Instructions
----------------------------------------------------
Anywhere a value is requested, an expression may be placed
===============================================================================
DEBUG outputdata
===============================================================================
Show variables and messages for debugging purposes.
When executed, the data after DEBUG will be sent to the PC for
display. DEBUG data can be displayed in several modes. Straight
data can be relayed to the PC, or you can have values printed in decimal,
hex, binary, or ascii. In the number-printing modes, the result of an
expression can be printed or a complete relation can be shown between the
expression and its result. For example:
X=1
yields:
To print numbers, there are several words which can preceed expressions:
To print the literal text expression along with the result, follow any of the
words with a '?'. A lone '?' is the same as 'DEC?'. REP cannot be followed
by a '?', but ASC needs one.
DEBUG statements can contain many strings and numbers, separated by commas. In
addition, there are several special control characters which are interpreted by
the DEBUG screen:
DEBUG cls,dec ina," " 'cls, print ina in decimal, spaces too
loop: debug sdec? sin count 'show the signed-decimal sine of count
count = count + 1 'increment count
if count <> 0 then loop 'loop until count rolls over
===============================================================================
FOR...NEXT
===============================================================================
The FOR...NEXT loop.
STEP is for specifying a step value other than the default of 1; if specified,
stepval must be positive since whether to add or subtract stepval from variable
is determined dynamically at run-time (this allows '10 TO 0' without specifying
a negative STEP value.).
Note: NEXT is stand-alone and implies the variable from the last FOR statement.
===============================================================================
BRANCH
===============================================================================
Branch according to an index.
executed. If the index exceeds the number of label entries, no branch will
occur and execution will proceed at the next instruction.
===============================================================================
IF
===============================================================================
Branch conditionally.
===============================================================================
GOTO
===============================================================================
Go to a new point in the program.
A branch to joe will occur, rather than execution continuing at the next
instruction.
===============================================================================
GOSUB
===============================================================================
Go to a subroutine (and then RETURN later).
The execution point is stored and then a branch to ledset occurs. When
a RETURN is encountered (in ledset), execution continues at the instruction
following the GOSUB.
GOSUB's may be nested up to 4 deep and you are allowed 255 of them in your
program.
===============================================================================
RETURN
===============================================================================
Return from a subroutine.
usage: RETURN
The execution point is set to the instruction after the GOSUB that got into
the subroutine executing the RETURN.
===============================================================================
Variable Assignment
===============================================================================
assign a value to a variable.
===============================================================================
LOOKUP
===============================================================================
Lookup a variable according to an index.
===============================================================================
LOOKDOWN
===============================================================================
Lookdown a value and return an index.
===============================================================================
RANDOM
===============================================================================
Pseudo-randomly iterate a word variable.
===============================================================================
SEROUT tpin,baudmode,{pace,}[outputdata]
SEROUT tpin\fpin,baudmode,{timeout,tlabel,}[outputdata]
===============================================================================
Tpin is 0-15 for an I/O pin, or 16 for the internal serial port. Fpin is an
option to specify a flow-control pin. Baudmode is a composite value which
specifies baud rate, parity mode, true/inverted output, and open/driven output.
If no fpin was specified, an optional pace in milliseconds may be declared to
pace characters by a number of milliseconds. If an fpin was specified, an
optional timeout (in milliseconds) and timeout label may be specified to enable
timeout and branching for flow-control.
Baudmode is the bit period expressed in microseconds-20 (ie 300 baud is 3313).
An easy formula for computing the baud rate (baudmode bits 0-12) is:
int (1,000,000/baud) - 20
===============================================================================
SERIN rpin{\fpin},baudmode,{plabel,}{timeout,tlabel,}[inputdata]
===============================================================================
Input data serially.
Rpin is 0-15 for an I/O pin, or 16 for the internal serial port. Fpin is an
optional flow-control pin. Baudmode is described in SEROUT. Plabel is a an
option to specify a where to branch in the event of a parity error (parity mode
must be enabled). Timeout is an optional value to specify how long to wait in
milliseconds before giving up and branching to tlabel. Inputdata follows the
conventions below:
'is 0-terminated)
===============================================================================
READ
===============================================================================
Read a byte from the EEPROM.
Location is 0-2047. Variable will receive the byte read from location.
===============================================================================
WRITE
===============================================================================
Write a byte into the EEPROM.
===============================================================================
XOUT
===============================================================================
Output X-10 codes to a TW523 or TW513 module (by X-10 Powerhouse).
Mpin will be made a low output and is the modulation control. Zpin will be
made an input and is the zero-crossing detect. House is the house code (0-15
is 'A'-'P'). Keyorcommand is a key (0-15 is '1'-'16') or command (see table
below). Cycles is an optional number which overrides the default of two; this
should only be used with 'dim' and 'bright' commands.
===============================================================================
SHIFTOUT
===============================================================================
Shift bits out synchronously.
Dpin is the data output, cpin is the clock output. Mode is 0 for lsb-first or
1 for msb-first. Data is a value to be shifted out. Bits is an optional bit
count (8 is the default).
symbol value
---------------------
LSBFIRST 0
MSBFIRST 1
===============================================================================
SHIFTIN
===============================================================================
Shift bits in synchronously.
Dpin is the data input, cpin is the clock output. Mode is 0 for msb-first/
pre-clock, 1 for lsb-first/pre-clock, 2 for msb-first/post-clock, or 3 for
symbol value
---------------------
MSBPRE 0
LSBPRE 1
MSBPOST 2
LSBPOST 3
===============================================================================
COUNT
===============================================================================
Count cycles on a pin for some milliseconds.
Pin will be placed in input mode. For 'period' milliseconds, cycles will be
counted on pin. Both sides of the waveform must be at least 4us in duration,
limiting the input frequency to 125KHz (assuming 50/50 duty cycle). The result
(0-65535) will be written into variable.
===============================================================================
PULSOUT
===============================================================================
Output a timed pulse.
Pin will be made an output opposite of it's OUTx value for period*2us. Pin
will be left in output mode with OUTx state.
===============================================================================
PULSIN
===============================================================================
Input a timed pulse.
Pin will be placed in input mode. A pulse of 'state' will be waited for and
measured with 2us resolution and the result will be written into variable. If
an overflow occurs while waiting for any edge, (>65535*2us or >131ms), 0 will
be written into variable.
===============================================================================
BUTTON
===============================================================================
Debounce button, auto-repeat, and branch if button is in target state.
Pin will be placed in input mode. Downstate is the state which is read when
the button is pressed. Delay specifies down-time before auto-repeat in BUTTON
cycles. Rate specifies the auto-repeat rate in BUTTON cycles. Bytevariable is
the workspace. It must be cleared to 0 before being used by BUTTON for the
first time. Targetstate specifies what state (0=not pressed, 1=pressed) the
button should be in for a branch to occur. Label specifies where to go if the
button is in the target state.
===============================================================================
INPUT, OUTPUT, LOW, HIGH, TOGGLE, REVERSE
===============================================================================
Modify a pin's input/output high/low state.
Pin is 0-15.
===============================================================================
FREQOUT
===============================================================================
Output a sine-wave(s) for some time.
===============================================================================
DTMFOUT
===============================================================================
Output DTMF tones.
Pin will be temporarily placed in output mode for modulation. The default
on and off times are 200ms and 50ms. ontime and offtime are optional overrides
which specify milliseconds. Key(s) are 0-15; 0-9 are the digits '0'-'9'; 10 is
'*'; 11 is '#'; 12-15 are 'A'-'D' which are fourth-column codes unavailable on
normal phones.
===============================================================================
PWM
===============================================================================
Pulse-width modulate a pin for some time.
===============================================================================
RCTIME
===============================================================================
Measure an R-C charge/discharge time.
Pin will be placed in input mode and time will be measure in 2us units while
pin is in 'state'. The result will be written into variable. If an overflow
occurs (>131ms), 0 will be written.
===============================================================================
NAP
===============================================================================
Nap for a short period.
usage: NAP x
Enter low-power mode for a short period. When the period is over, the I/O's
will go high-z for ~18ms and execution will continue at the next instruction.
x ~seconds
----------------
0 .018
1 .036
2 .072
3 .14
4 .29
5 .58
6 1.2
7 2.3
===============================================================================
SLEEP
===============================================================================
Sleep for x seconds (x=0 to 65535).
usage: SLEEP x
Enter low-power mode and keep I/O's updated. Every ~2.3 seconds the I/O's
will go high-z for ~18ms. Approximately 50ua average current will be consumed.
When x seconds have been accrued in SLEEP mode, execution continues at the next
instruction. Though the granularity of SLEEP is ~2.3 seconds, the error is
within 1% over extended periods of time.
===============================================================================
END
===============================================================================
End program.
usage: END
Enter low-power mode and keep I/O's updated. Every ~2.3 seconds the I/O's
will go high-z for ~18ms. Approximately 50ua average current will be consumed.
END is terminated only by a hardware reset.
===============================================================================
PAUSE
===============================================================================
Pause for x milliseconds (x=0 to 65535).
usage: PAUSE x
===============================================================================
STOP
===============================================================================
Stop execution.
usage: STOP