Escola Superior de Tecnologia e Gestão Instituto Politécnico de Leiria Microprocessador Intel 8086 Apontamentos de Arquitectura de Computadores Arquitectura de Computadores / ESTG-IPL 1 - MICROPROCESSADOR 8086 Motivação Embora já hajam microprocessadores derivados do 8088/8086 muito mais evoluídos (80188, 80186, 80286, 8386 e 80486), vai-se começar um estudo de base pelo 8086 da Intel devido a duas razões essenciais: - foi aquele que deu origem ao aparecimento em 1978 do chamado PC através do seu uso pela IBM no chamado IBM PC/XT; - o seu conjunto de instruções continua totalmente compatível, inclusivé com o Pentium. ESTRUTURA DE UM MICROCOMPUTADOR BASEADO NO 8086 MICROCOMPUTADOR Sistema constituído basicamente por uma unidade de processamento central (CPU), memória e portos. A respectiva interligação é feita por intermédio de 3 barramentos: barramento de endereços, de controlo e de dados. MICROCOMPUTADOR BASEADO NO 8086 Se o pino MN / MX permanecer ao nível lógico "0", o 8086 opera no seu modo máximo; se o nível lógico for "1" então a operação será no modo mínimo. É de notar que o 8088 apenas difere do 8086 no barramento de dados, que é de 8 bits. (Figura 1- "Pin-out" e operação em modo máximo.) (Figura 2- operação em modo mínimo.) CPU Unidade de Processamento Central - microprocessador. RAM "Random-Acess Memory" - memória volátil para escritas/leituras. ROM "Read-Only Memory" - memória não volátil para leituras. Portos Portos para ligação de dispositivos em paralelo (p. ex. uma PRN) e em série (p. ex. um rato) e também a ligação de teclados e CRTs. BUS de controlo No modo mínimo este é constituído basicamente pelos sinais: 2 Arquitectura de Computadores / ESTG-IPL M / IO - permite escolher entre operações de memória e de I/O; RD - vai a "0" para operações de leitura; WR - vai a "0" para operações de escrita. No modo máximo os sinais de controlo vão codificados nas linhas de estado S 0 , S1, S2. BUS de endereços/dados À saída do P este barramento tem 20 linhas (16 linhas de endereços multiplexadas nas 16 linhas de dados AD0-AD15 em conjunto com 4 linhas de endereços adicionais A16-A19). O 8086 é um P de 16 bits porque tem um BUS de dados de 16 bits. O 8086 tem a capacidade de endereçar até 1 Mbyte de memória pois o BUS de endereços é de 20 bits. Latch Quando o sinal ALE ("Address Latch Enable") vai a "1" o endereço é colocado nas saídas das "latches" e portanto o barramento ADDR/DATA já pode ser usado para escrita/leitura de dados. Fazendo-se esta desmultiplexagem, evita-se a existência de pinos adicionais e a cápsula do P não necessita de ser maior. Transceiver Costituído por "buffers tri-state" bidireccionais e tem como objectivo fornecer corrente para regenerar os níveis lógicos do barramento de dados que se vão degradando à medida que se interligam mais dispositivos. A bidireccionalidade é escolhida pelo P através do sinal DT / R (transmite dados/recebe dados). A característica "tri-state" é seleccionada pela linha DEN quando se pretende colocar o BUS de dados em alta impedância, como p. ex. quando o P está a exteriorizar os 16 bits menos significativos de um endereço no BUS ADDR/DATA. Controlador externo É usado no modo máximo de operação do 8086 para gerar um conjunto mais completo de sinais de controlo. Gerador de relógio Usa um cristal para produzir um sinal (CLK) com uma frequência de relógio estável que faz avançar o P de instrução em instrução. O sinal RESET inicializa o sistema colocando o P no endereço FFFF0h para ir buscar a sua próxima instrução. O sinal READY quando está a "1" origina que o 8086 opere normalmente. Se estiver a "0" no instante certo, são inseridos um ou mais "wait states" no ciclo de máquina (aumenta o período de tempo desde endereços estáveis a dados válidos e portanto as 3 Arquitectura de Computadores / ESTG-IPL memórias poderão ter tempos de acesso maiores e consequentemente serão mais baratas). ARQUITECTURA INTERNA O P divide-se internamente em duas unidades separadas que funcionam de forma independente: BIU e EU. (Figura 3 - arquitectura interna do 8086) BIU ("BUS INTERFACE UNIT") - retira instruções da memória, lê operandos, escreve resultados; Fila de instruções Grupo de registos tipo FIFO (First-In-First-Out) que podem comportar até 6 Bytes de instruções retiradas antecipadamente ("prefetched") da memória. Isto servirá para diminuir o tempo de execução dos programas sobrepondo ("overlapping") a retirada de instruções da memória com a respectiva execução - "pipelining". Somador dedicado Usado para calcular endereços físicos de 20 bits. Lógica para o BUS de controlo Gera todos os sinais do barramento de controlo, tais como M / IO , RD e WR . Registos de segmento O 8086 pode endereçar até 1 Mbyte de memória, contudo de uma só vez apenas pode endereçar 254 Kbytes em blocos de 64 Kbytes (segmentos) CS ("code segment register") - aponta para o segmento de código; SS ("stack segment register") - aponta para o segmento de pilha; DS ("data segment register") - aponta para o segmento de dado; ES ("extra segment register") - aponta para o segmento de dados extra. Nota: Cada um destes registos contém os 16 bits mais significativos do endereço físico correspondente a cada um dos segmentos. EU ("EXECUTION UNIT") - descodifica e executa as instruções que o BIU retira da memória. Unidade aritmética e lógica ALU Responsável por: - operações aritméticas e lógicas; - manutenção do estado do P e das "flags" de controlo; - manipulação dos registos de uso geral e dos operandos das instruções. Registos de uso geral 4 Arquitectura de Computadores / ESTG-IPL Os registos de 16 bits de uso geral AX, BX, CX, DX podem ser usados como registos de 8 bits (AH, AL; BH, BL; CH, CL; DH, DL), que representam respectivamente o byte mais e menos significativo do correspondente conteudo. Podem ser usados para funcionalidades genéricas, contudo têm utilizações especificas que os caracterizam. AX ("Accumulator Register") - é assumido por algumas instruções como p. ex. IN, OUT, MUL, DIV, etc.. BX ("Base Register") - é o único registo que além de ser de uso geral, também pode ser usado para endereçamento de memória. CX ("Counter Register") - é utilizado como contador em instruções como p. ex. SHIFT, ROTATE, LOOP. DX ("Data Register") - é utilizado em instruções como p. ex. MUL e DIV. Registos de pilha São registos de 16 bits que se usam para aceder a dados no segmento de pilha. SP ("Stack Pointer Register") - é usado como "offset" ou deslocamento a partir de um endereço corrente no segmento de pilha em instruções como p. ex. POP e PUSH. BP ("Base Pointer Register") - é usado como "offset" ou deslocamento a partir de um endereço corrente no segmento de pilha em instruções que o modo de endereçamento baseado. Registos de índice São registos de 16 bits que se usam para endereçamentos indexados. SI ("Source Index Register") - é usado no modo de endereçamento indexado e utilizase em instruções de processamento de "strings" no segmento de dados para indicar o endereço fonte. DI ("Destination Index Register") - é usado no modo de endereçamento indexado e utiliza-se em instruções de processamento de "strings" no segmento extra de dados para indicar o endereço destino. Registo das "flags" Este registo de 16 bits contém o estado das várias "flags". 15 14 13 12 11 10 9 8 7 6 OF DF IF TF SF ZF 5 4 AF 3 2 PF 1 0 CF AF ("Auxiliary Carry Flag") - está a "0" e só é colocada a "1" se há um "carry" do "nibble" menos significativo no mais significativo ou um "borrow" do "nibble" mais significativo no menos significativo relativamente nos 8 bits de um registo de 16 bits. Esta "flag" é usada em instruções aritméticas BCD. 5 Arquitectura de Computadores / ESTG-IPL CF ("Carry Flag") - vai a "1" se há um "carry" numa adição ou um "borrow" numa subtracção. OF ("Overflow Carry Flag") - está a zero e só vai a "1" se acontecer algum "overflow" após uma instrução aritmética, isto é, quando o tamanho do resultado excede a capacidade do local de destino. SF ("Sign Flag") - está a zero e só vai a "1" se o bit mais significativo de um resultado for "1". PF ("Parity Flag") - vai a "0" quando um resultado tem paridade "even" e vai a "1" quando a paridade for "odd". ZF ("Zero Flag") - está a "0" se um resultado for não nulo e vai a "1" se for nulo. O 8086 tem 3 "flags" adicionais para controlo de bits que podem ser alteradas pelo programador: DF ("Direction Flag") - colocando-a a "0" possibilita-se que as instruções para "strings" auto-incrementem, caso contrário auto-decrementam. IF ("Interrupt Flag") - colocando-a a "1" possibilita-se ao 8086 que este reconheça interrupções externas mascaráveis. TF ("Trap Flag") - colocando-a a "1" coloca-se o 8086 no modo passo-a-passo, gerando uma interrupção após a execução de cada instrução. Assim o utilizador pode fazer uma rotina de serviço para que após cada interrupção se visualize o conteúdo de registos e posições de memória desejados - "debugging". SEGMENTAÇÃO DE MEMÓRIA O 8086 pode endereçar até 1 Mbyte de memória, contudo de uma só vez apenas pode endereçar 254 Kbytes em blocos de 64 Kbytes (segmentos): segmento de código segmento de pilha segmento de dados segmento de dados extra Em qualquer instante que o 8086 acede à memória, é calculado no BIU o necessário endereço físico de 20 bits a partir do valor base (ou de início) de um segmento e de um "offset", pois o P não tem registos de 20 bits. Os 4 segmentos da memória podem eventualmente estar totalmente sobrepostos, parcialmente sobrepostos, contíguos ou disjuntos. (Figura 4 - disposição dos segmentos de forma disjunta) (Figura 5 - exemplos de cálculo de endereços físicos) Endereço físico no segmento de código 6 Arquitectura de Computadores / ESTG-IPL Cada uma das instruções de um programa encontram-se localizadas no segmento de código, num endereço calculado a partir do par de registos CS:IP. Endereço físico no segmento de pilha É calculado a partir do par de registos SS:SP, em instruções de pilha como p. ex. PUSH e POP ou então a partir do par de registos SS:BP, no caso de se usar um endereçamento baseado. Endereço físico no segmento de dados É calculado a partir dos pares de registos DS:SI, DS:DI ou DS:BX. Endereço físico no segmento extra de dados É calculado a partir do par de registos ES:DI. Nota: Para representar endereços físicos têm-se 2 alternativas (p. ex. o endereço 5FFE0h pode ser representado também por 5000:FFE0h). PROGRAMAÇÃO EM ASSEMBLER PORQUÊ PROGRAMAR EM ASSEMBLER? Permite uma maior aproximação à máquina (o código aproxima-se do código de máquina); Os programas em assembler ocupam menos espaço (cerca de 10 vezes menos que os equivalentes em C ou Pascal); Os programas em assembler são mais rápidos (2 a 3 vezes mais rápidos que os equivalentes em C ou Pascal). Existe a possibilidade de programas elaborados em alto nível usarem pequenas rotinas feitas em assembler. DADOS, ENDEREÇOS DE DADOS E DE INSTRUÇÃO Dados São números de 8, 16 ou 32 bits, com ou sem sinal, sobre os quais as operações são imediatas (sem haver descodificação de endereços). Note-se que os caracteres são representados com números de 8 bits sem sinal por meio de um código ASCII). Endereços de dados São endereços que permitem determinar onde se irão buscar os dados a manipular (são os "offsets" nos segmentos de dados). Estes "offsets" serão o conteúdo de vários ponteiros para dados (como p. ex. ponteiro para byte - DB, ponteiro para palavra DW, ponteiro para palavra dupla - DD, etc.). Endereços de instrução 7 Arquitectura de Computadores / ESTG-IPL São endereços que permitem ir buscar as instruções à memória e são definidos por etiquetas "Labels" DIRECTIVAS PARA OS SEGMENTOS As directivas, tal como o próprio nome indica, são direccionamentos para o assembler e não são instruções. Existem dois tipos de directivas para os segmentos: directivas simplificadas; directivas "standard". Directivas simplificadas Facilitam o controlo dos segmentos e são ideais para "linkar" módulos em assembler às linguagens de alto nível. No entanto, apenas suportam algumas características de controlo dos segmentos que o "Turbo Assembler" pode proporcionar. .DOSSEG - origina um agrupamento dos segmentos num programa em assembler segundo uma ordem convencionada pela "Microsoft" (alguns programas não funcionam bem quando se omite esta directiva). .MODEL - especifica o modelo de memória para um módulo em assembler que use directivas simplificadas. Os modelos de memória disponíveis são: Tiny - o código e os dados cabem num mesmo segmento de 64 K (ambos "near"); Small - o código e os dados cabem em segmentos separados de 64 K (ambos "near"); Medium - o código pode exceder 64 K e os dados devem caber num segmento de 64K (código "far", dados "near"); Compact - o código cabe em 64 K e os dados podem exceder (código "near", dados "far"); Large - o código e os dados podem exceder 64 K, mas nenhum array de dados singular pode exceder 64 K (ambos "far"); Huge - o código e os dados podem exceder 64 K e os arrays de dados podem exceder 64 K (ambos "far"); Nota: "near" e "far" indicam respectivamente que os endereços físicos nos respectivos segmentos podem ser obtidos a partir de "offsets" ou a partir de pares "segment:offset". 8 Arquitectura de Computadores / ESTG-IPL .STACK - define o tamanho da pilha para programas que façam uso dela (é normal encontrar o tamanho de 200h (512 bytes) para programas não muito "pesados", como é o caso dos que usam recursividade). Exemplo: .STACK 200H .DATA - define o início do segmento de dados ou do segmento extra de dados. Exemplo: .DATA variavel_x DB 'Ola' Nota: Para se aceder a posições de memória nestes segmentos de dados deve-se carregar o registo DS com o símbolo @data: mov AX,@data ; para o segmento de dados mov DS,AX mov DX,@data ; para o segmento extra de dados mov ES,DX .CODE - define o início do segmento de código (diz ao turbo assembler onde deve colocar as instruções). Exemplo: .CODE Inicio_Codigo: (...) Directivas "standard" São mais complicadas de usar, mas permitem explorar todas as capacidades de controlo dos segmentos por parte do TASM. SEGMENT - define o início de um segmento e a "label" que a acompanha é o nome dado ao segmento. ENDS - define o fim de um segmento e a "label" que a acompanha é o nome dado ao segmento. ASSUME - indica ao TASM a que registo de segmento está atribuído um segmento. Exemplos: pilha SEGMENT DB 200 DUP(?) pilha ENDS dados SEGMENT 9 Arquitectura de Computadores / ESTG-IPL variavel_x DB 'Ola' dados ENDS codigo SEGMENT ASSUME CS:codigo,DS:dados,SS:pilha (...) codigo ENDS OUTRAS DIRECTIVAS DB ("Define Byte") - usada para declarar uma variável do tipo byte ou então reservar uma ou mais posições de memória do tipo byte. Exemplos: PRECOS DB 49H,98H,29H ;declara um array de 3 ;bytes chamado PRECOS e inicializa-o ;daquela forma. NOME DB 'Sergio' ;declara um array de 6 bytes ;com aquele nome inicializando-o com os ;códigos ASCII das letras do nome Sergio. ARMAZENA DB 100 DUP(?) ;reserva 100 bytes ;de memória não inicializados e dá-lhe ;aquele nome. ARMAZENA DB 49H DUP(0) ;declara 49h bytes ;inicializados com zeros e dá-lhe aquele ;nome. DW ("Define Word") - usada para declarar uma variável do tipo word ou então reservar uma ou mais posições de memória do tipo word inicializadas , ou não. Exemplos: NUMERO DW 437AH; declara uma variável do ;tipo word cujo nome é NUMERO e dá-lhe ;aquele valor. Nota: Para declarar variáveis existem adicionalmente as directivas: DD ("Define Double Word"); DQ ("Define Quadword"); DT ("Define Ten Bytes"). END ("End Program") - é colocada na última linha de um programa para o terminar. O assemblador ignora tudo o que estiver depois dela. ENDP ("End Program") - é usada em conjunto com o nome de um procedimento para o terminar. Esta directiva é utilizada em conjunto com a directiva PROC para delimitar um procedimento. Exemplo: 10 Arquitectura de Computadores / ESTG-IPL CALCULO PROC; início do procedimento. (...) ; instruções do procedimento. CALCULO ENDP; fim do procedimento. EQU ("Equate") - usada para dar um nome a um valor ou símbolo. Cada vez que o assembler encontra o referido nome num programa, ele substitui o valor ou símbolo com esse nome. Exemplo: FACTOR_EXEMPLO EQU 03H ;o valor 03h é substituído por aquele nome. EVEN ("Align on Even Memory Address") - é usada para dizer ao assemblador que incremente o contador de posição (que contém um "offset" num segmento) para o próximo endereço par, se este ainda não for par. Assim, possibilita-se uma leitura mais rápida daquilo que está nesse endereço. Exemplo: .DATA MEDIA DB 9 DUP(?); reserva um array de 9 ;bytes não inicializados e o contador de ;posição aponta para 0009h. EVEN; avança para 000Ah. RESULTADO DW 100 DUP(0); "array" de 100 ;words inicializados a zero que começa em ;000Ah. Neste endereço a leitura será mais ;rápida. EXTRN - ver programas multimódulo. GLOBAL - ver programas multimódulo. GROUP ("Group-Related Segments") - diz ao assemblador para agrupar os segmentos num único segmento de 64 Kbytes. Isto permite que o conteúdo desses segmentos seja acedido a partir do mesmo endereço de base. Exemplo: AGRUPA GROUP CODIGO,DADOS; agrupa os ;segmentos num único. ASSUME CS:AGRUPA, DS:AGRUPA; os ;registos CS e DS contêm o endereço base ;do segmento. INCLUDE - permite inserir um bloco de código fonte, editado num outro ficheiro, no código fonte actual. 11 Arquitectura de Computadores / ESTG-IPL Exemplo: (...) .CODE (...) INCLUDE OUTRO_FICHEIRO.ASM; inclusão ;do código fonte existente noutro ficheiro. (...) LABEL - é usada para dar um nome ao valor actual do contador de posição. Se for usada para instruções de salto ou de chamada de procedimentos, esta deve ser especificada como sendo "near" ou "far". Se for usada para referenciar um ítem de dados deve ser especificada com o tipo de dados. Exemplo: PILHA SEGMENT DW 100 DUP(0) ; reserva 100 bytes para a pilha. TOPO_PILHA LABEL WORD; dá um nome à ;proxima posição após a última palavra na ;pilha. PILHA ENDS REPETE LABEL FAR; pode-se fazer um salto ;para esta posição de um outro qualquer ;segmento. NAME - ver programas multimódulo. ORG (“Originate”) - Quando o assemblador começa a a ler um segmento, o contador de posição vai para 0000h. Com esta directiva pode-se impôr que esse valor seja outro. O símbolo “$” indica o valor actual dessa posição. Exemplos: ORG 2000H; diz ao assemblador para colocar o ;contador de posição em 2000h. ORG $+100 ; incrementa o contador de posição ;em 100 a partir do seu valor actual. PROC (“Procedure”) - indica o início de um procedimento (ver directiva ENDP). PUBLIC - ver programas multimódulo. 12 Arquitectura de Computadores / ESTG-IPL OPERADORES DUP - permite definir blocos de memória inicializados com um determinado valor (ver exemplos para a directiva DB). LENGTH - diz ao assemblador para determinar o número de elementos que existem num determinado ítem de dados. Exemplos: MOV CX,LENGTH STRING1; ao ser executada ;esta linha de código, o registo CX fica ;com o comprimento da “string” em bytes ou words. OFFSET - diz ao assemblador para determinar o “offset” ou deslocamento de uma variável de dados a partir do início do segmento que a contém. Exemplos: MOV BX,OFFSET PRECOS; determina o “offset” ;da variável PRECOS no segmento de ;dados ;ficando BX com esse valor. ADD AL,BYTE PTRBX; soma o valor de AL ao ;valor cujo endereço é dado pelo “offset” ;que está em BX. PTR (“pointer”) - atribui um tipo específico a uma variável ou “label”. Exemplos: INC BYTE PTR BX; incrementa o byte ;apontado por BX. INC WORD PTR BX; incrementa a palavra ;apontada por BX. ADD AL,BYTE PTR BX; soma a AL o valor de 8 bits ;cujo endereço é dado pelo “offset” que ;está em BX. JMP WORD PTR BX; faz um salto “near”. JMP DWORD PTR BX; faz um salto “far”. SHORT - é usado em conjunto com a instrução JMP e diz ao assemblador que apenas é necessário reservar um byte para essa instrução (em vez de dois). Contudo, este tipo de saltos apenas atinge “labels” compreendidas entre -128 bytes e +128 bytes a partir do endereço de instrução após o salto. Exemplos: JMP SHORT NOME_LABEL; faz um salto curto ;(“short”). 13 Arquitectura de Computadores / ESTG-IPL CONJUNTO DE INSTRUÇÕES O conjunto de instruções pode ser dividido em 8 grupos consoante, a funcionalidade destas: Movimentação de dados Aritméticas Lógicas e de deslocamento Controlo Derivação Manipulação de blocos e “strings” (Tabela 1 - conjunto de instruções do 8086) MODOS DE ENDEREÇAMENTO O 8086 tem 12 modos de endereçamento, que podem ser classificados em 5 grupos: Modo registado e imediato; Modo de endereçamento de memória; Modo de endereçamento de portos; Modo de endereçamento relativo; Modo de endereçamento implícito. Modo registado e imediato Registado - os operandos fonte, os operandos destino ou ambos estão contidos em registos. Exemplos: MOV AL,BL; move o conteúdo de 8 bits de BL ;para AL. Imediato - os dados na forma de 8 bits ou 16 bits podem ser especificados como fazendo parte da instrução. Exemplo: MOV CX,5062h; move o dado de 16 bits 5062h ;para o registo CX. Modo de endereçamento de memória Directo - o endereço efectivo do operando (“offset”) no correspondente segmento é obtido directamente a partir de um determinado dado, sem envolver quaisquer registos. Exemplo: .DATA START DW 39FAH 14 Arquitectura de Computadores / ESTG-IPL (...) MOV BX,START; move o conteúdo que está no ;endereço de 20 bits (calculado a partir de ;DS:START. Indirecto registado - o endereço efectivo de um operando na memória pode ser obtido a partir de um dos registos de base ou de índice (BX, BP, SI, DI). Exemplo: .DATA START DW 39FAH (...) MOV BX, OFFSET START; BX contém o ;“offset” daquela variável. MOV CX,BX; move o conteúdo que está no ;endereço de 20 bits (calculado a partir de ;DS:BX). MOV AX,ES:BX; move o conteúdo que está no ;endereço de 20 bits (calculado a partir de ;ES:BX - agora o segmento de dados é o ;extra). Baseado - o endereço efectivo é calculado a partir de uma contante e dos registos de base BX ou BP. Quando não se acede à pilha o endereço de 20 bits é calculado a partir de DS:BX. Quando se acede à pilha esse endereço é calculado a partir de SS:BP (note-se que BP é o ponteiro de pilha para o utilizador, enquanto que o SP é o ponteiro de pilha do sistema, sendo usado em instruções como p. ex. CALL). Exemplo: MOV BP,SP; inicializa BP. MOV -2BP,BX; move o conteúdo de BX para o ;endereço de 20 bits da pilha (calculado a partir ;de S:BP-2). O operando destino poder-se-ia representar alternativamente por BP-2 ou por BP2. Indexado - O endereço efectivo é obtido a partir de uma constante e dos registos de índice SI ou DI. .DATA CARACTERES DB ‘ABCD$’ (...) 15 Arquitectura de Computadores / ESTG-IPL MOV SI, OFFSET CARACTERES; SI contém o ;“offset” daquela variável no segmento de ;dados. MOV AX, 3SI; move-se o caractere ‘D’ para o ;registo AX (é o caractere que se encontra ;no endereço físico de 20 bits calculado a ;partir de DS:3+SI ). Indexado baseado - o endereço efectivo é calculado a partir de uma constante, dos registos de base (BX, BP) e dos registos de índice (SI, DI). .DATA CARACTERES DB ‘ABCD$’ (...) MOV SI, OFFSET CARACTERES; SI contém o ;“offset” daquela variável no segmento de ;dados. MOV BX,1; carrega-se BX com o valor 1. MOV AX, 2BXSI; move-se o caractere ‘D’ ;para o registo AX (é o caractere que se ;encontra no endereço físico de 20 bits ;calculado a partir de DS:2+BX+SI ). De “strings” - neste modo usa-se o registo SI para apontar para o primeiro byte ou word de uma “string” origem e o registo DI para apontar para o primeiro byte ou word do destino, quando é utilizada uma instrução para “strings” (p. ex. MOVS). Modo de endereçamento de portos Modo directo - o número da porta é um operando de 8 bits que é utilizado em conjunto com o acumulador (conseguem-se endereçar até 256 portos). Exemplo: IN AL,02; move o conteúdo do porto 2 para o ;registo AL. Modo indirecto - o número da porta provém de um do registo DX e é utilizado em conjunto com o acumulador (conseguem-se endereçar até 64Kbytes de portos). Exemplo: IN AX,DX; move o conteúdo do porto ;especificado em DX para o registo AX. Modo de endereçamento relativo Neste modo de endereçamento especifica-se um operando como sendo um deslocamento de 8 bits (positivo ou negativo), que vai ser somado ao valor actual do IP. 16 Arquitectura de Computadores / ESTG-IPL Exemplo: JNC START; se o “carry” for nulo (“not carry”) o ;programa continua a partir da “label” ;START. Por outras palavras, é somado ao ;IP o endereço de 8 bits da “label”. Modo de endereçamento implícito As instruções que usam este modo não têm operandos. Exemplo: CLC; coloca a zero a “carry flag”. PILHA E CHAMADAS DE FUNÇÕES Uma pilha é uma estrutura LIFO: o último elemento a entrar é o primeiro a sair. Existem instruções próprias para colocar e retirar elementos da pilha: PUSH e POP. O processador acede à pilha por meio do registo SP. Este registo contém o endereço do último elemento da pilha. A pilha cresce no sentido decrescente dos endereços. Topo da Memória Base da Pilha Sentido de Crescimento da Pilha Pilha Topo da Pilha [SP] Local de entrada do próximo elemento Base da Memória [0000H] O registo BP, que indexa o segmento da pilha, não tem qualquer função directa na manipulação da pilha. Utiliza-se para aceder aos elementos da pilha pela posição que nela ocupam. 17 Arquitectura de Computadores / ESTG-IPL Manipulação da pilha PUSH - Permite colocar na pilha (na posição indicada por SS:SP) uma palavra, conteúdo de um registo ou posição de memória. A instrução PUSH decrementa o registo SP em duas unidades e coloca o operando no novo topo da pilha. Para colocar o registo FLAGS na pilha existe a instrução PUSHF. PUSH AX PUSH CS PUSH [BX+SI] PUSHF POP - Permite retirar a palavra armazenada no topo da pilha, colocando-a no registo ou posição de memória especificada. De seguida incrementa o registo SP em duas unidades. Para carregar o registo FLAGS a partir da pilha existe a instrução POPF. POP CX POP DS POP [DI] POPF Chamada de funções Uma subrotina é uma parte de um programa. O programa chama a subrotina e esta devolve o controlo ao programa quando acaba. A chamada de subrotina é um salto com a particularidade de guardar o endereço onde o programa estava a executar. A instrução CALL permite chamar uma subrotina. A rotina pode ser chamada directamente, através de labels dentro do segmento de código, ou indirectamente, usando-se registos que contêm o endereço da rotina. O endereço da instrução a ser executada ao retornar da rotina (registo IP) é guardado na pilha. Nas chamadas FAR, além do IP é necessário guardar também o registo CS. Assim, numa chamada NEAR, o SP é decrementado de 2 e numa chamada FAR, o SP é decrementado de 4. A instrução RET permite fazer o retorno da subrotina, transferindo a execução para a instrução seguinte à chamada da sub-rotina. 18 Arquitectura de Computadores / ESTG-IPL O endereço dessa instrução é retirado do topo da pilha e colocado no registo IP, sendo o registo SP incrementado de 2. No caso de uma chamada far, são retiradas duas palavras que são colocadas nos registos CS e IP. Se for especificado um valor imediato como parâmetro da instrução RET, este será também adicionado ao valor do SP, servindo para libertar parâmetros colocados na pilha a quando da chamada da rotina. CALL F1 F1 RET CALL Subrotina1 CALL BX CALL double word ptr [SI] RET RET 6 Conselhos: A documentação é fundamental e deve incluir o fundamental sobre o comportamento da rotina. A rotina deve evitar acessos directos a variáveis globais. Os dados a serem acedidos devem ser passados como parâmetros A rotina deve ser de aplicação geral. Em vez de uma rotina que faça a média de 100 valores, será mais útil colocar o número de valores como parâmetro. A rotina deve ser robusta. Deve prever a ocorrência de casos estranhos, como por exemplo o número de dados a fazer a média ser 0. Modelo Black-box - a rotina deve ser conhecida apenas pelas entradas e saídas, sem necessidade de preocupação em relação ao modo como as mesmas são tratadas. Contexto de uma rotina - todas as rotinas utilizam, normalmente variáveis locais. Essas variáveis podem ser células de memória ou registos. Se as variáveis forem registos, é necessário salvaguardar os valores que esses registos continham ao ser chamada a rotina. Esses valores são guardados (normalmente na pilha) no princípio da rotina e restaurados (por ordem inversa) no final da rotina. Exemplo de uma rotina que faz uso dos registos AX, SI e DS: 19 Arquitectura de Computadores / ESTG-IPL MYPROC MYPROC PROC PUSH AX PUSH SI PUSH DS ... POP DS POP SI POP AX RET ENDP SP-> SP-> E.Ret SP-> Antes CALL E.Ret AX SI DS Contexto Passagem de parâmetros Os parâmetros de uma rotina podem ser de entrada, de saída ou entrada / saída. Passagem por registo Utiliza registos para a passagem da informação. Evita passagens pela memória mas há poucos registos disponíveis. MOV AX, 100 ;nº de elementos LEA DX,Dados ;Vector dos dados CALL Media ; Calcula a média MOV Res,AX ;Guarda-a Passagem por memória partilhada Utiliza células de memória fixas para passagem da informação. Poupa os registos mas tem pouca flexibilidade pois exige a utilização de variáveis globais. Passagem pela pilha este é o modo mais utilizado para passar parâmetros. A pilha é o local onde são postos os dados e de onde são retirados os resultados. 20 Arquitectura de Computadores / ESTG-IPL Um exemplo: esta chamada coloca 3 parâmetros na pilha e devolve 2 resultados. PUSH AX PUSH BX PUSH CX CALL Func1 POP AX POP DX SP-> Par1 Par2 Par3 SP-> Parâmetros Par1 Par2 Par3 E.Ret DX SI SP-> CALL, Contexto Res2 Res1 E.Ret Retorno Para se aceder aos parâmetros dentro da rotina, utiliza-se o registo BP, que opera sempre no segmento de pilha. Para se aceder ao 1º parâmetro Par1 do exemplo acima pode-se, por exemplo, inicializar o BP a SP no início da rotina depois de se guardar o contexto e utilizar [BP+10] (se se fizessem POPs, retirava-se também o endereço de retorno!). Nº de parâmetros de entrada e de saída Igual. Neste caso, os parâmetros de saída vão ocupar os lugares dos de entrada. Há mais parâmetros de entrada do que de saída. Neste caso, é necessário retirar alguns elementos da pilha no retorno. Há mais parâmetros de saída do que de entrada. Neste caso, é necessário mover o endereço de retorno na pilha, para células mais abaixo na memória de modo a reservar espaço na pilha para os resultados da função. Outra alternativa é reservar espaço para os parâmetros de saída antes da chamada: ... SUB SP, 6;reserva espaço para ; parâmetros de saída PUSH AX ;parâmetro de entrada 1 PUSH BX ;parâmetro de entrada 2 CALL F1 POP AX ;resultado 1 POP DX ;resultado 2 ... SP-> Par1 Par2 E.Ret Passagem na sequência da chamada Colocam-se os parâmetros no meio do código, imediatamente a seguir à instrução CALL. O endereço inicial dos parâmetros é dado pelo endereço de 21 Arquitectura de Computadores / ESTG-IPL retorno, que se encontra na pilha. O endereço de retorno real é o endereço da primeira instrução que aparece depois dos parâmetros. Este modo de passagem é pouco utilizado. CALL Funcao1 DB 63, 12, ? ; parâmetros INC AX ; instrução seguinte Funcao1 PROC POP SI ;endereço dos parâmetros MOV DI, SI ;cálculo do ADD DI, 3 ;endereço de retorno PUSH DI ... Variáveis locais a uma subrotina - Podem guardar-se em células de memória, registos do processador, espaço reservado na pilha. Células de memória Utiliza-se esta opção quando se deseja ter uma variável estática (diferente de global), i.e., uma variável que se mantém inalterada entre execuções consecutivas da função. Registos do processador O número restrito de registos disponíveis, obriga geralmente ao recurso a outros lugares para guardar variáveis locais. Espaço reservado na pilha Este é o método mais utilizado. O meio de acesso a estas variáveis é idêntico ao meio de acesso aos parâmetros passados por pilha. É aconselhável fazer um mapa da pilha na entrada da subrotina. Parâmetros End.Ret. Contexto Variáveis locais <- SP SERVIÇOS DA ROM BIOS O BIOS (Basic Input Output System) é um conjunto de rotinas (device-drivers) residentes numa memória ROM que é responsável pela inicialização de periféricos (tal como teclado, vídeo, disco, etc.) e pela carga do sistema operativo a partir do disco. O ficheiro IO.SYS é um dos componentes do DOS e contém extensões ao ROM BIOS. As funções disponibilizadas pelo BIOS são acedidas através da instrução INT nn onde nn é o número da interrupção que se pretende executar. Normalmente, cada número de interrupção disponibiliza várias funções, sendo a selecção feita através do registo 22 Arquitectura de Computadores / ESTG-IPL AH. Outros parâmetros necessários para cada função são passados através de outros registos. ( Tabela 2 - Serviços ROM BIOS ) FUNÇÕES DO DOS O ficheiro MSDOS.SYS é outro dos componentes do DOS, é responsável pela gestão de directórios e ficheiros em disco e contém as rotinas de funções do DOS. Estas funções podem ser invocadas pelo programador através de interrupções. As principais interrupções de DOS estão listadas na tabela 3. Destas, a interrupção 21H é de longe a mais útil, fornecendo acesso geral a quase todas as funções do DOS. As interrupções de terminação 20H e 27H já não se utilizam, tendo sido substituídas por funções da interrupção 21H. As interrupções de acesso absoluto a disco 25H e 26H, podem ser ocasionalmente necessárias para ultrapassar o interface habitual de acesso a ficheiros de DOS. Interrupção 20H 21H 25H 26H 27H Descrição Terminação normal de programa Funções de DOS gerais Leitura absoluta em disco Escrita absoluta em disco TSR - Termina e fica residente Tabela 3 - As cinco principais interrupções de DOS ( Tabela 4 - Funções da interrupção 21H ) Serviço Terminação com código de retorno Standard input de um caracter com eco Output de um caracter para a impressora Criar um ficheiro Função 4CH 01H 05H 3CH Registos Input Output AH=4CH AL=cod. ret. AH=01H AL=caracter AH=05H DL=caracter AH=3CH CX=atributos DS:DX -> caminho para o ficheiro se erro: CF set AX=código de erro se não: CF clear AX=handle Tabela 5 - Exemplos de funções da interrupção 21H PROGRAMAS UNIMÓDULO E MULTIMÓDULO 23 Arquitectura de Computadores / ESTG-IPL É possível partir um programa grande (unimódulo) em vários módulos ( programa multimódulo). Depois de se editar o código fonte só é necessário reassemblar o módulo alterado, em vez de todas as linhas do programa. Directiva GLOBAL - Torna as labels associadas disponíveis para vários módulos. Uma label global deve ter uma directiva GLOBAL em cada módulo onde seja utilizada. Uma label global deve ser definida (com DB, DW, PROC, LABEL, etc.) num e um só dos módulos onde é utilizada. É necessário informar o Turbo Assembler sobre o tipo de cada label. Os tipos possíveis são: ABS, BYTE, DATAPTR, DWORD, FAR, FWORD, NEAR, PROC, QWORD, Structure Name, TBYTE, UNKNOWN, WORD O tipo ABS utiliza-se para labels definidas com EQU ou =, i.e., labels constantes que não estão associadas com um endereço. Existe um esquema alternativo à directiva global formado pelas directivas EXTERN e PUBLIC, mantido apenas por questões de compatibilidade. A directiva PUBLIC utiliza-se no módulo onde é definida a label. A directiva EXTERN utiliza-se nos módulos onde é também utilizada a label. MAIN.ASM ... .DATA GLOBAL FinalString: BYTE FinalString DB 50 DUP (?) .CODE GLOBAL ConcatenateStrings:PROC ProgramStart: ... call ConcatenateStrings ... END ProgramStart SUB1.ASM ... .DATA GLOBAL FinalString: BYTE .CODE GLOBAL ConcatenateStrings:PROC ConcatenateStringsPROC ... ConcatenateStrings ENDP END 24 Arquitectura de Computadores / ESTG-IPL Directiva INCLUDE - Se não se quiser dividir o programa em vários módulos, podese utilizar a directiva INCLUDE. Quando o Turbo Assembler encontra uma directiva INCLUDE, vai a disco buscar o ficheiro especificado e trata as linhas aí contidas como se pertencessem ao módulo corrente. MAINPROG.ASM ... .CODE mov ax, 1 INCLUDE INCPROG.ASM push ax ... INCPROG.ASM mov bx, 5 add ax, bx ... Programa equivalente: .CODE mov ax, 1 mov bx, 5 add ax, bx push ax ... INTERFACE DO TURBO ASSEMBLER COM O TURBO PASCAL O Turbo Pascal permite o acesso à quase totalidade dos recursos da máquina através dos arrays Port[], Mem[], MemW[], MemL[] e permite usar a BIOS e o sistema operativo com os procedimentos Intr() e MsDos(). As razões mais prováveis para se utilizar linguagem assembly no Turbo Pascal são: executar as poucas operações que não estão directamente disponíveis a partir do Turbo Pascal e tirar vantagem da velocidade que só a linguagem assembly pode fornecer. O Turbo Pascal impõe algumas restrições a nível de utilização de registos. Quando é feita uma chamada a uma função ou procedimento, os valores dor registos SS, DS e BP devem ser preservados. O DS aponta para o segmento de dados global (DATA), o SS aponta para o segmento de pilha. O BP é usado por cada rotina para referenciar o espaço que usa na pilha para parâmetros e variáveis locais. Todas as rotinas devem também ajustar o SP antes de terminar. Para que uma subrotina seja do tipo far utiliza-se a directiva de compilador {$F}. A directiva {$L MYFILE.OBJ} faz com que o Turbo Pascal procure o ficheiro MYFILE.OBJ e a linque com o programa em Turbo Pascal. 25 Arquitectura de Computadores / ESTG-IPL Cada rotina em Turbo Assembler que se deseje tornar visível no programa Turbo Pascal deve ser declarada como um símbolo PUBLIC e ter uma declaração external correspondente no programa. Se um programa em Turbo Pascal declarar as seguintes variáveis globais: var a: byte; b: integer; c: real; d: pointer; Podem aceder-se estas variáveis dentro do programa em assembly com as declarações EXTRN: EXTRN a: byte ; 1 byte EXTRN b: word ; 2 bytes EXTRN c: fword ; 6 bytes EXTRN d: dword ; 4 bytes Convenções de passagem de parâmetros do Turbo de Pascal O Turbo Pascal passa parâmetros através da pilha. Parâmetros de 1 byte são passados como uma palavra de 16 bits sendo o byte mais significativo ignorado. Em parâmetros de 4 bytes, é colocada primeiro na pilha a palavra mais significativa. Em parâmetros do tipo ponteiro, é colocada primeiro na pilha a palavra que contém o segmento, depois a do deslocamento. No programa em Turbo Assembler pode usar-se as instruções LDS ou LES para se ter acesso a um parâmetro do tipo ponteiro. Parâmetros do tipo string, não são colocados na pilha. Em vez disso o Turbo Pascal coloca na pilha um ponteiro far para a string. Os parâmetros variáveis (var) são passados como um ponteiro para a sua localização na memória. Quando as rotinas do Turbo Assembler recebem controlo, o topo da pilha contém um endereço de retorno (uma ou duas palavras) e, acima dele, quaisquer parâmetros a serem passados. Uma forma de aceder os parâmetros passados para a rotina em Turbo Assembler é usar o registo BP para endereçar a pilha. A directiva .MODEL com o parâmetro TPASCAL fornece suporte simplificado de segmentação, modelo de memória e de linguagem. Esta directiva encarrega-se da salvaguarda e restauro do registo BP, de lhe atribuir o valor de SP e de fazer a gestão do espaço ocupado pelos parâmetros na pilha. As funções no Turbo Pascal devolvem os seus valores em registos ou na pilha. Valores de 1 byte são devolvidos no registo al, 2 bytes no ax, 4 bytes em dx:ax. 26 Arquitectura de Computadores / ESTG-IPL MICROPROCESSADORES x86 MAIS EVOLUÍDOS O Turbo Asembler pode suportar, além do processador 8086, vários outros tipos de procesadores, através de directivas como: .186, .286, .386, etc. As principais características que cada um destes processadores veio acrescentar foram novas instruções e novos modos de endereçamento. O 386 fornece, entre outras inovações: - novas instruções e extensões a instruções existentes, - um conjunto extendido de registos de 32 bits e segmentos lineares de até 4 GB (32 bits) e - instruções privilegiadas (.386P) destinadas apenas a serem usadas pelo sistema operativo. O 386 extende os registos gerais, o registo de flags e o ponteiro de instrução para 32 bits e adiciona dois novos registos de segmento: fs e gs. Surgem novos modos de endereçamento: os 8 registos de 32 bits de uso geral (eax, ebx, ecx, edx, esi, edi, ebp e esp) podem ser utilizados como registos base ou índice. Não existe acesso directo aos 16 bits mais significativsos dos registos de 32 bits. Para se usarem os 16 bits mais significativsos de eax, é necessário rodar o registo 16 bits, aceder os 16 bits (agora) menos significativos e rodar novamente 16 bits. Para, por ex., colocar ax na palavra mais significativa de edx: ror edx, 16 mov dx, ax ror edx, 16 Figura - Os registos do 80386 27