EXEMPLO DE TP1 DESCRIÇÃO RESUMIDA DE UMA ARQUITETURA DE PROCESSADOR: O ESTUDO DE CASO DA INTEL X86 EM SUA PRIMEIRA VERSÃO NEY LAERT VILAR CALAZANS [email protected] Pontifícia Universidade Católica do Rio Grande do Sul (PUCRS) PORTO ALEGRE, 09 DE ABRIL DE 2011 SUMÁRIO SUMÁRIO ................................................................................................................................................................. 2 1 INTRODUÇÃO .................................................................................................................................................... 3 2 DIAGRAMA DE BLOCOS DO PROCESSADOR 8086............................................................................................... 3 3 ELEMENTOS DEFINITÓRIOS DA ARQUITETURA .................................................................................................. 4 3.1 3.2 3.3 3.4 3.5 REGISTRADORES DO PROGRAMADOR EM LINGUAGEM DE MONTAGEM ___________________________________ 4 CONJUNTO DE INSTRUÇÕES _________________________________________________________________ 6 FORMATOS DE INSTRUÇÃO _________________________________________________________________ 6 MODOS DE ENDEREÇAMENTO _______________________________________________________________ 6 LINGUAGEM DE MONTAGEM________________________________________________________________ 8 4 EXEMPLO DE PROGRAMA – SOMA DOS ELEMENTOS DE UM VETOR ................................................................. 8 5 REFERÊNCIAS BIBLIOGRÁFICAS .......................................................................................................................... 8 3 1 INTRODUÇÃO A família x86 da Intel surgiu com o processador 8086 (também chamado iAPX86), um microprocessador de 16 bits integrado em um único circuito integrado (CI ou chip) projetado pela Intel entre o início de 1976 e a metade de 1978, quando foi colocado no mercado [WIK11]. O 8086 foi o primeiro processador da arquitetura x86 de processadores compatíveis, sendo aquela que hoje domina a maior parte do mercado de computadores pessoais, laptops e servidores. O 8088 da Intel, colocado no mercado em 1979 é uma versão levemente modificada do 8086, com um barramento de dados externo de apenas oito bits, ao contrário dos 16 bits do barramento de dados do 8086 (o que na época permitia o uso de circuitos de suporte mais baratos). Notadamente, o 8088, que é uma organização levemente diferente da mesma arquitetura x86 inicial, foi o processador usado como unidade central de processamento (UCP, em inglês CPU) do IBM PC original [WIK11]. Este documento descreve a arquitetura x86 em sua versão inicial. A descrição assume uma forma bastante resumida, apontando apenas as características principais da família, através de um Diagrama de Blocos do 8086, na Seção 2 e explorando brevemente os cinco elementos definitórios da arquitetura x86 na Seção 3. Finalmente, ilustra-se a utilização do 8086 fornecendo um programa em linguagem de montagem deste processador, na Seção 4. 2 DIAGRAMA DE BLOCOS DO PROCESSADOR 8086 A Figura 1 apresenta o diagrama de blocos da Unidade Central de Processamento (UCP, ou CPU em inglês) 8086, retirada da referência [INT79]. Figura 1 – Diagrama de blocos do processador 8086 e diagrama de pinagem para um encapsulamento de 40 pinos do mesmo processador [INT79]. Note-se a simplicidade da organização, com apenas seis blocos principais, uma unidade de controle e temporização (Control and Timing), um conjunto de registradores de 4 dados (Data Pointer and Index Registers), um conjunto de registradores de controle (Segment Registers and Intruction Pointer), uma ULA e conjunto de qualificadores (16 bit ALU -- Flags), uma unidade de controle de interface com os barramentos de acesso à memória e dispositivos de entrada e saída (Bus Interface Unit) e uma fila de instruções (6-byte Instruction Queue). Esta última unidade deriva de uma característica de paralelismo primitiva deste processador, denominada de pré-busca de instruções (em inglês, prefetch). A Bus Interface Unit adianta a busca de instruções consecutivas da memória enquanto processador decodifica e executa as instruções que já estão na fila de seis bytes. Assim, esta fila funciona como uma memória cache de instruções primitiva. Isto serve para aumentar a vazão de processamento do 8086. Todos os outros elementos do processador correspondem àqueles estudados na disciplina de Organização de Computadores, quais sejam, o Bloco de Controle, com os registradores de controle e o bloco de Dados, com os registradores acessíveis ao programador em linguagem de montagem. No diagrama de pinagem, deve-se notar o seguinte: • Existem três pinos de alimentação, um VCC (pólo positivo da alimentação) e dois GND (pinos de terra); • Existe um pino de relógio (CLK), e um pino de reinicialização do processador (RESET), características de um circuito síncrono; • São vinte pinos de endereço para a memória (AD0 a A19). Os pinos de dados são dezesseis (AD0 a AD15), multiplexados com os pinos de endereço da memória, reduzindo a pinagem de trinta e seis pinos (20+16) para apenas vinte; • Os demais quinze pinos são sinais de controle com diversas funções, incluindo controle de acesso à memória, acesso a dispositivos de entrada e saída, interrupção do processador por periféricos, etc. Mais informações sobre a função destes podem ser encontradas na referência [INT79]. 3 ELEMENTOS DEFINITÓRIOS DA ARQUITETURA Este Capítulo tem como objetivo apresentar separadamente as cinco características que definem a arquitetura x86 em sua versão inicial, implementada pela organização 8086. 3.1 REGISTRADORES DO PROGRAMADOR EM LINGUAGEM DE MONTAGEM Os registradores do processador 8086 acessíveis ao programador em linguagem de montagem são tipicamente de dezesseis bits e são mostrados na Figura 2. Nesta Figura percebe-se que os registradores são agrupados por funcionalidade em quatro grupos: • Registradores de Dados – São quatro de dezesseis bits, AX, BX, CX e DX, cada um dos quais pode ser visto como dois registradores de oito bits, separando as partes alta (H) e baixa (L) dos dezesseis bits. Assim pode-se contar com oito registradores de oito bits. Estes são os registradores primários para armazenar e manipular dados; • Registradores de Endereços – São quatro de dezesseis bits, SP, BP, SI e DI. Como os endereços de memória do 8086 são de vinte bits, conforme visto na Seção 2, só uma parte de um endereço está armazenada nestes. Um endereço efetivo de vinte bits é 5 obtido combinando um destes registradores com um dos registradores de segmento descritos abaixo. Figura 2 – Registradores do processador 8086 acessíveis ao programador em linguagem de montagem. Todos são de 16 bits, embora alguns possam ser utilizados como dois registradores de oito bits, tais como o registrador AX e os qualificadores [INT79]. • Registradores de controle – São dois de dezesseis bits, o IP, que corresponde ao contador de programa (PC) de outros processadores e os qualificadores (FLAGSH e FLAGSL) de diversas operações, as principais sendo os resultados de operações lógicas e aritméticas. Na realidade este registrador é composto de bits manipulados isoladamente; por exemplo, um deles armazena o sinal (0-positivo, 1–negativo) do resultado da última operação lógica ou aritmética que foi executada pelo processador; • Registradores de Segmento – São quatro registradores de dezesseis bits, CS, DS, SS, ES. Eles são usados para determinar o início de uma região da memória do processador 8086 onde se armazena algum bloco fundamental de informação. Por exemplo, o conteúdo do registrador CS determina onde na memória inicia a área que contém o programa ora em execução, enquanto o registrador DS determina onde na memória inicia a área que contém os dados do programa ora em execução, SS determina a região de pilha, onde se armazena informações de natureza dinâmica (aquelas que são criadas e destruídas ao longo da execução de um programa), e o registrador ES serve para qualquer outro propósito onde se queira definir o início de um bloco de informações na memória do processador. Um endereço de memória efetivo, de vinte bits, é tipicamente formado tomando o valor de um registrador de segmento, acrescentando quatro bits à direita deste valor (o que equivale a multiplicar o valor do registrador por 16) e somando este valor de vinte bits com um valor de dezesseis bits normalmente proveniente de um registrador de endereço [INT79]. 6 3.2 CONJUNTO DE INSTRUÇÕES Cada instrução da arquitetura x86 na sua versão inicial ocupa um código objeto cujo tamanho pode variar de um a cinco bytes. Em geral, o primeiro byte determina quantos bytes a mais a instrução ocupa. As principais classes de instruções do processador 8086 são [INT79]: • Transferência de Dados – Instruções que lêem ou escrevem dados na memória, na pilha em um dispositivo de entrada ou saída ou qualquer segmento do programa, como MOV; • Instruções Aritméticas – de função óbvia, exemplos são: ADD, ADC (soma com vai-um de operação anterior), SUB, etc.; • Instruções Lógicas – de função óbvia, exemplos são: NOT, SHR (deslocamento de bits para a direita ou shift), NOT, etc.; • Manipulação de Cadeias de Caracteres – exemplo SCAS (Varre cadeia procurando por bytes específicos), etc. • Controle de Fluxo de Execução – instruções de salto como JMP (Salto incondicional), JE e JZ (Saltos condicionais se igual ou se última instrução executada gerou 0 como resultado), chamada e retorno de subrotina (CALL e RET), etc. 3.3 FORMATOS DE INSTRUÇÃO Os formatos de instrução do processador 8086 são variáveis em termos de tamanho e composição. Para dar um exemplo deles, mostra-se na Figura 3 um trecho do conjunto de instruções. Aparecem ali exatamente doze instruções, incluindo três formas de ADD, três de ADC, duas de INC (incremento), duas de ajuste de formato de representação de dados (AAA e BAA) e duas de subtração (SUB). Note-se que um mesmo mnemônico pode corresponder a mais de um código objeto de instrução, o que não acontece, por exemplo, no MIPS para instruções. Note-se também que os modos de endereçamento fazem com que existam três tipos de instrução ADD, uma para somar um registrador ou um dado de uma posição de memória com um registrador, colocando o resultado ou na memória ou em outro registrador; um ADD para somar uma constante a um registrador ou ao conteúdo de uma posição da memória, e assim por diante. Finalmente, dependendo do modo de endereçamento, um ADD, por exemplo, pode gerar código de dois bytes, de quatro bytes, de três bytes ou mesmo o tamanho da instrução pode variar entre quatro e três (veja os pontos onde se diz data if, que indicam que o último byte do código objeto pode ou não existir, dependendo do que há em certos bits do início do código. 3.4 MODOS DE ENDEREÇAMENTO A família x86 provê muitas formas diferentes de fazer acesso a operandos de instruções. Isto define modos de endereçamento, ou seja, os diferentes meios de especificar como obter cada operando de uma instrução. Operandos podem se encontrar em registradores do processador, dentro da própria instrução (implicando em constantes), na memória ou em portas de entrada e saída do processador. Além disto, endereços de memória de portas de entrada e saída podem ser calculados de muitas formas diferentes. 7 Figura 3 – Trecho da especificação do conjunto de instruções do processador 8086, mostrando algumas instruções e seus formatos específicos, retirado de [INT79]. Estas características fazem da x86 uma arquitetura muito distinta, por exemplo, da arquitetura MIPS, que se baseia em apenas um modo de acesso à memória e entrada e saída (registrador-base mais deslocamento) e três modos para especificar endereços da memória de programa (relativo nos branches, pseudo-absoluto nos jumps e a registrador em instruções como JR). Discutem-se aqui apenas alguns dos principais modos de endereçamento da x86. Os mais simples são o modo a registrador e o modo imediato, equivalentes a modos de mesmo nome existentes, por exemplo, no MIPS. Os modos de acesso à memória são bem mais numerosos e vários deles são bastante elaborados. Este fato deriva da escolha do emprego de segmentos de memória para gerar endereços de memória de tamanho maior que o tamanho dos registradores da arquitetura, como discutido brevemente na Seção 3.1. Esta escolha derivou de razões tecnológicas, pois na época em que a arquitetura foi criada (anos 70 do século XX) a disponibilidade de memória externa excedia o que podia ser endereçado por registradores de um microprocessador integrado em um único chip. O modo de acesso à memória mais simples é o modo direto, com duas variantes: longo e curto. Nestes, um valor de dezesseis ou oito bits contido no código da instrução deve ser usado junto com um dos registradores de segmento para gerar o endereço efetivo de memória onde fazer acesso (seja para leitura ou escrita). Outro é o modo indireto a registrador, onde um registrador de índice ou um registrador base contém o valor a ser combinado com um registrador de segmento para obter o endereço efetivo de memória. Além destes, e não discutidos aqui, também existem os modos com endereçamento base, indexado, base indexado, cadeia de caracteres, porta direta e porta direta indexado [INT79]. 8 3.5 LINGUAGEM DE MONTAGEM A linguagem de montagem da x86 inclui diversos recursos comuns à maioria das linguagens de montagem de processadores: diretivas de controle do processo de montagem e definição de dados, instruções especificadas por mnemônicos de poucas letras, tipicamente três a cinco, primitivas muito simples para especificar dados. Uma diferença com relação a arquiteturas do tipo Conjunto de Instruções Reduzido (em inglês Reduced Instruction Set Computer, ou RISC) como o MIPS são modos mais sofisticados de especificar os operandos das instruções, tais como a possibilidade de indexar vetores diretamente no código (ver exemplo no programa da Seção a seguir). 4 EXEMPLO DE PROGRAMA – SOMA DOS ELEMENTOS DE UM VETOR Um exemplo de programa está apresentado na Figura 4. Note-se que a estrutura geral de um programa em linguagem de montagem é mantida, com segmentos de texto e de dados. Mas não existe separação explícita entre estas áreas, que podem assim ocupar regiões de memória contíguas, de acordo com o desejo do programador. Existem três diretivas usadas neste programa: name para dar um nome ao programa, que é puramente documentacional; org, cujo nome deriva de origin (origem em Português), e que serve para definir o endereço a partir do qual montar o programa abaixo desta; e db (de define byte) que serve para criar um ou mais bytes de dados na memória. Deve-se salientar que os programas nesta arquitetura costumam ser bem mais compactos que os de uma arquitetura RISC, graças à riqueza dos modos de endereçamento da x86. ; Pr ogr ama q ue ca lcu la a som a dos e lem en tos de u m v et or, ; ar maz ena o re su lta do na po si ção d e m em óri a ' m' na me "c alc -s um" or g 1 00 h ; D ir eti va pa ra co loc ar o có dig o do pro gr ama ; a p art ir do e nde reç o de me mór ia 10 0 h ex a mo v cx ,5 ; G er a n úm ero d e e lem en tos d o v et or mo v al ,0 ; R eg ist ra dor A L ( 8 b it s), v ai gu ard ar a som a mo v bx ,0 ; B X vai c ont er o índ ic e d o vet or (i nic ia liz ad o c om 0) ne xt : ad d al ,ve ct or[ bx ] ; E st a i ns tru çã o b usc a o p ró xim o ele men to do v eto r in c bx ; I nc rem en ta o índ ice lo op ne xt ; E xe cut a laç o até cx =0 (l oo p f az CX -1 e sal ta se C X / =0) mo v m, al ; A rm aze na re su lta do da so ma em A L n a p os içã o de me mór ia m en d: re t ; va ria ble s: ve ct or db m db 5, 4, 5 , 2 , 1 0 Figura 4 – Exemplo de programa para o processador 8086 e qualquer outro que implemente a arquitetura x86 original. O programa soma todos os elementos de um vetor e escreve o resultado na memória. 5 REFERÊNCIAS BIBLIOGRÁFICAS [WIK11] Wikipedia. “x86 assembly language”. Available at http://en.wikipedia.org/wiki/ X86_assembly_language, captured in March 2011. [INT79] Intel Corporation. “Intel 8086”. Family Users Manual, 1979. 208 pages. [INT90] Intel Corporation. “8086 16-BIT HMOS MICROPROCESSOR 8086/8086-2/8086-1”. Microprocessor Datasheet, September 1990. 30 pages.