Assembly NumaBoa - Capítulo 3

ORGANIZAÇÃO DE SISTEMAS VI

E/S (Entrada/Saída)

E/S corresponde a I/O (Input/Output). Existem três formas básicas de entrada e saída que um sistema de computador típico usa: E/S mapeada, entrada/saída mapeada na memória e acesso direto à memória (DMA - Direct Memory Access). A entrada/saída do tipo E/S mapeada usa instruções especiais para transferir dados entre o sistema do computador e o mundo exterior; a E/S mapeada na memória usa locais especiais da memória no espaço de endereços normal da CPU para se comunicar com dispositivos externos; a DMA é uma forma especial da E/S mapeada na memória onde os dispositivos periféricos lêem e escrevem na memória sem passarem pela CPU. Cada mecanismo de E/S possui um conjunto de vantagens e desvantagens, as quais serão objeto de discussão deste texto.

A primeira coisa a aprender sobre os subsistemas de entrada/saída é que a E/S num sistema de computador típico é radicalmente diferente da E/S de uma linguagem de programação de alto nível típica. Num sistema de computador real raramente são encontradas instruções de máquina que se comportem como writeln, printf ou mesmo como as instruções Get e Put do x86. Na verdade, a maioria das instruções de entrada/saída se comportam exatamente como a instrução mov do x86. Para enviar dados para um dispositivo de saída, a CPU simplesmente move estes dados para um local especial na memória (no espaço de endereços de E/S se for E/S mapeada ou para um endereço no espaço de endereços de memória se estiver usando E/S mapeada na memória). Para ler dados de um dispositivo de entrada, a CPU simplesmente move os dados do endereço (E/S ou memória) deste dispositivo para a CPU. Apesar de geralmente existirem mais estados de espera associados a dispositivos periféricos típicos do que à memória, as operações de entrada ou saída são muito semelhantes às operações de leitura ou escrita na memória.


E/S

Fig.27 - Porta de E/S

Para o computador, uma porta E/S é um dispositivo que se parece com uma célula de memória que tenha conexões com o mundo exterior. Uma porta de E/S usa tipicamente um latch, ao invés de um flip-flop, para implementar a célula de memória. Quando a CPU escreve num endereço associado ao latch, o dispositivo latch captura os dados e os disponibiliza num conjunto de fios externos, independentes da CPU e do sistema de memória (Fig.27).

Observe que as portas de E/S podem ser apenas para leitura, apenas para escrita ou para leitura e escrita. A porta da Fig.27, por exemplo, é apenas para escrita. Como as saídas do latch não voltam para o barramento de dados da CPU, a CPU não é capaz de ler os dados que o latch contém. As linhas de decodificação de endereços e de controle de escrita precisam estar ativas para que o latch funcione - a linha de decodificação de endereços está ativa quando estiver sendo feita a leitura do endereço do latch, mas a linha de controle de escrita não.


E/S

Fig.28 - Porta de E/S para leitura e escrita

A Fig.28 mostra como criar uma porta para leitura/escrita. Os dados escritos na porta de saída retornam para um latch transparente. Sempre que a CPU lê o endereço decodificado, as linhas de leitura e de decodificação estão ativas e isto ativa o latch inferior. Os dados escritos previamente na porta de saída são colocados no barramento de dados da CPU, permitindo que a CPU leia estes dados. Uma porta apenas para leitura (entrada) é simplesmente a metade inferior desta figura - o sistema ignora qualquer dado escrito na porta de entrada.

Um exemplo perfeito de uma porta de saída é a porta paralela da impressora. Tipicamente, a CPU escreve um caracter ASCII numa porta de saída da largura de um byte que está conectada a um soquete DB-25F no fundo do gabinete do computador. Um cabo transmite estes dados para a impressora onde uma porta de entrada (para a impressora) recebe os dados. Um processador dentro da impressora normalmente converte este caracter ASCII para uma sequência de pontos que são impressos no papel.

Geralmente os dispositivos periféricos usam mais do que uma única porta E/S. Uma interface de impressora paralela típica, por exemplo, usa três portas: uma porta para leitura/escrita, uma porta de entrada e uma porta de saída. A porta para leitura/escrita é a porta de dados (ele é para leitura/escrita para permitir que a CPU leia o último caracter ASCII enviado para a porta da impressora). A porta de entrada retorna sinais de controle da impressora. Estes sinais indicam se a impressora está pronta para aceitar outro caracter, se está off-line, se o papel acabou, etc. A porta de saída transmite informação de controle para a impressora como, por exemplo, se há dados disponíveis para serem impressos.

Para o programador, a diferença entre as operações de E/S mapeada e entrada/saída mapeada para a memória é a instrução que precisa ser usada. Para a E/S mapeada para a memória, qualquer instrução que acesse a memória também pode acessar a porta de E/S mapeada para a memória. No x86, as instruções mov, add, sub, cmp, and, or e not podem ler a memória; as instruções mov e not podem escrever dados na memória. A E/S mapeada usa instruções especiais para acessar as portas E/S. Por exemplo, as CPUs x86 usam as instruções get e put, a família 80x86 da Intel usa as instruções in e out. As instruções in e out do 80x86 funcionam como a instrução mov, com a diferença de que colocam seus endereços no barramento de endereços E/S ao invés de colocá-los no barramento de endereços de memória.

Os subsistemas E/S mapeada para a memória e E/S mapeada necessitam da CPU para mover dados entre o dispositivo periférico e a memória principal. Por exemplo, para passar uma sequência de dez bytes de uma porta de entrada para a memória, a CPU precisa ler os valores um a um e armazená-los na memória. Para dispositivos de E/S de velocidade muito alta, a CPU pode ser muito lenta processando os dados um byte por vez. Tais dispositivos geralmente possuem uma interface para o barramento da CPU para que possam ler e escrever diretamente na memória. Este acesso é conhecido como acesso direto à memória porque o dispositivo periférico acessa a memória diretamente sem usar a CPU como intermediário. Isto frequentemente permite que a operação de E/S ocorra em paralelo com outras operações da CPU, aumentando a velocidade geral do sistema. Note, entretanto, que a CPU e o dispositivo DMA não podem usar simultaneamente os barramentos de endereços e de dados. Portanto, o processamento concorrente apenas ocorre quando a CPU possuir uma cache e estiver executando código e acessando dados encontrados na cache (que é quando o barramento está liberado). Apesar disso, mesmo se a CPU precisar parar e esperar que a operação DMA seja completada, ainda assim a E/S é muito mais rápida porque muitas das operações no barramento durante a E/S ou a entrada/saída mapeada para a memória são instruções de busca ou de acesso à porta de E/S, as quais não estão presentes durante operações DMA.

Interrupções e apuração de E/S

Muitos dispositivos de E/S não aceitam dados numa taxa arbitrária. Por exemplo, um PC Pentium é capaz de enviar vários milhões de caracteres por segundo para uma impressora, mas a impressora (provavelmente) não será capaz de imprimir tantos caracteres por segundo. Do mesmo modo, um dispositivo de entrada como o teclado não é capaz de fornecer alguns milhões de teclas digitadas por segundo (porque opera em velocidades humanas e não em velocidades de computador). A CPU precisa de algum mecanismo que coordene a transferência de dados entre o sistema do computador e seus dispositivos periféricos.

Um jeito comum de coordernar uma trasnferência de dados é fornecer alguns bits de status para uma porta de entrada secundária. Por exemplo, um 1 num único bit de uma porta E/S pode informar a CPU de que a impressora está pronta para aceitar mais dados, um 0 (zero) indicaria que a impressora está ocupada e que a CPU não deveria enviar novos dados para a impressora. Da mesma forma, um bit 1 numa porta diferente poderia informar a CPU de que uma tecla digitada no teclado está disponível na porta de dados do teclado e um 0 (zero) no mesmo bit poderia indicar que não há teclas digitadas disponíveis. A CPU pode testar estes bits antes de ler uma tecla do teclado ou de enviar um caracter para a impressora.

Imagine que a porta de dados da impressora seja mapeada para a memória no endereço 0FFE0h e que o status da porta da impressora seja o bit zero da porta mapeada para a posição de memória 0FFE2h. O código seguinte espera até que a impressora esteja pronta para aceitar um byte de dados e depois o byte menos significativo de ax é enviado para a porta da impressora:

	0000:	mov	bx, [FFE2]
	0003:	and	bx, 1
	0006:	cmp	bx, 0
	0009:	je	0000
	000C:	mov	[FFE0], ax

A primeira instrução busca o dado da porta de entrada de status. A segunda instrução faz um and lógico deste valor com 1 para clarear os bits de número um até quinze e liga o bit zero do status atual da porta da impressora. Observe que isto produz o valor zero em bx se a impressora estiver ocupada e produz o valor um em bx se a impressora estiver pronta para aceitar dados adicionais. A terceira instrução checa bx para verificar se ele contém zero (isto é, a impressora está ocupada). Se a impressora estiver ocupada, este programa salta de volta para o endereço zero e repete o processo tantas vezes quantas forem necessárias até que o bit de status da impressora seja 1.

O código seguinte fornece um exemplo de leitura de teclado. Ele considera que o bit de status do teclado seja o bit zero do endereço 0FFE6h (valor zero significa que nenhuma tecla foi digitada) e que o código ASCII da tecla apareça no endereço 0FFE4h quando o bit zero localizado em 0FFE6h contenha o valor 1:

	0000:	mov	bx, [FFE6]
	0003:	and	bx, 1
	0006:	cmp	bx, 0
	0009:	je	0000
	000C:	mov	[FFE4], ax

Este tipo de operação de E/S, onde a CPU fica testando constantemente uma porta para verificar se há dados disponíveis é a apuração (polling), isto é, a CPU faz a apuração (faz a contagem de votos) da porta para saber se há dados disponíveis ou se a porta é capaz de aceitar dados. A E/S apurada é inerentemente ineficiente. Imagine o que acontece se o usuário demorar 10 segundos para apertar uma tecla - a CPU fica presa no loop sem fazer nada (além de testar a porta de status do teclado) durante estes 10 segundos.

Em sistemas de computadores pessoais antigos (por exemplo, o Apple II), era exatamente desta forma que o programa lia dados do teclado - se quisesse ler uma tecla do teclado, iria fazer a apuração da porta de status do teclado até que uma tecla estivesse disponível. Estes computadores não podiam realizar outras operações enquanto estivessem esperando que uma tecla fosse digitada. Mais importante ainda é que, se passasse muito tempo entre a checagem da porta de status do teclado, o usuário poderia digitar uma segunda tecla e a primeira seria perdida.

A solução para este problema é fornecer um mecanismo de interrupção. Uma interrupção é um evento de harware externo (como apertar uma tecla) que faz com que a CPU interrompa a sequência de instruções atual e chame uma rotina de serviço de interrupção especial (ISR - Interrupt Service Routine). Uma rotina de serviço de interrupção tipicamente salva todos os registradores e flags (para não causar distúrbio na computação que está sendo interrompida), realiza as operações que sejam necessárias para manipular a fonte da interrupção, restaura os registradores e flags e depois retoma a execução do código que foi interrompida. Em muitos sistema de computador (por exemplo o PC), muitos dispositivos de E/S geram uma interrupção sempre que tiverem dados disponíveis ou estiverem prontos para aceitar dados da CPU. A ISR processa rapidamente a requisição num segundo plano, permitindo que alguma outra computação prossiga no primeiro plano.

CPUs que permitem interrupções precisam fornecer algum mecanismo para que o programador possa especificar o endereço da ISR que deve ser executada quando ocorrer uma interrupção. Um vetor de interrupção é, tipicamente, uma localização especial de memória que contém o endereço da ISR que corresponde à interrupção. As CPUs x86, por exemplo, possuem dois vetores de interrupção: um para uma interrupção de propósito geral e um para uma interrupção de reset (a interrupção de reset corresponde a pressionar o botão de reset da maioria dos PCs). A família 80x86 da Intel possui até 256 vetores de interrupção diferentes.

Após uma ISR completar sua operação, ela devolve o controle para a tarefa do primeiro plano com uma instrução especiald de "retorno de interrupção". No x86 a instrução iret (interrupt return) realiza esta tarefa. Uma ISR deveria sempre terminar com esta instrução para que a ISR possa devolver o controle ao programa que ela interrompeu.

Um sistema de entrada baseado em interrupções típico usa a ISR para ler dados de uma porta de entrada e os coloca num buffer sempre que os dados estiverem disponíveis. O programa no primeiro plano pode ler estes dados do buffer à vontade, sem que qualquer dado da porta seja perdido. Da mesma forma, um sistema de saída baseado em interrupções típico (que recebe uma interrupção assim que um dispositivo de saída esteja pronto para aceitar mais dados) pode remover dados do buffer sempre que o dispositivo periférico estiver pronto para receber novos dados.

Comentários

Ufa! Isto é (quase) tudo o que se poderia dizer sobre a organização de sistemas. Texto longo, mas interessante, principalmente porque é a base de qualquer projeto de programação. Dever cumprido, agora vamos ao laboratório. Prepare-se para as surpresas.



| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > Assembly NumaBoa > Processadores 8486 > Entrada e Saída > Laboratório cap.3
Autoria: Randall Hyde - Art of Assembly Language Programming. Tradução: vovó Vicki

webdesign sobMedida by vickiSoft - /informatica/assembly/cap3_6.php (23.01.04) versão 1.0 de 23.01.04
Licença Creative Commons 1998-2006 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.