Manual Assembly - Principal
Transcrição
Manual Assembly - Principal
FACAP E FACULDADE DE CIÊNCIAS APLICADAS E SOCIAIS DE PETROLINA CIÊNCIA DA COMPUTAÇÃO Assembly MANUAL DE REFERÊNCIA Prof. Jorge Cavalcanti [email protected] Prof. Sérgio Faustino [email protected] Laboratório de Arquitetura de Computadores 1. INTRODUÇÃO Este manual objetiva abordar superficialmente diversos tópicos relacionados ao conjunto de instruções dos microprocessadores 8086/88. É o suporte necessário aos diversos experimentos que são realizados utilizando o SID como depurador assembly. Inicialmente é apresentada uma rápida revisão sobre sistemas de numeração e suas conversões, depois são feitos comentários sobre os microprocessadores apresentando a arquitetura do 8086/88. A partir de então são descritos os diversos registradores e flags utilizados pelo microprocessador, e finalmente são apresentadas as principais instruções assembly utilizadas nos experimentos com suas funções. No final são mostrados pequenos programas exemplos fazendo uso de interrupções. Qualquer conhecimento mais aprofundado consulte livros específicos indicados na bibliografia. 2. CONVERSÃO ENTRE SISTEMAS DE NUMERAÇÃO Existem vários sistemas numéricos, dentre os quais se destacam: o sistema decimal, o binário, o octal e o hexadecimal. Os sistemas binário e hexadecimal são muito importantes nas áreas de técnicas digitais e informática pois há uma forte ligação entre estes sistemas de numeração e os circuitos lógicos. Utilizando o conceito básico de formação de um número, obtém-se o equivalente decimal do número binário 10012 da seguinte forma: 23 1 22 0 21 0 20 1 1 x 23 + 0 x 22 + 0 x 21 + 1 x 20 = 1 x 8 + 1 x 1 = 910 ∴ 10012 = 910 Será visto agora a transformação inversa, ou seja, a conversão de um número do sistema decimal para o sistema binário. Para converter 4710 em binário faz-se: 47 ÷ 2 = 23 23 ÷ 2 = 11 11 ÷ 2 = 5 5÷2=2 2÷2=1 1÷2=0 1° resto = 1 2° resto = 1 3° resto = 1 4° resto = 1 5° resto = 0 6° resto = 1 O último resto será o algarismo mais significativo e ficará à esquerda. Os outros algarismos seguem-se na ordem até o primeiro resto. Logo: 4710 = 1011112 O sistema hexadecimal possui 16 algarismos, sendo sua base igual a 16. Os algarismos são assim enumerados: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, e F A tabela 1 mostra a seqüência de numeração do sistema hexadecimal ate a quantidade dezoito. A regra de conversão do sistema hexadecimal para o sistema decimal é análoga à de outros sistemas, somente neste caso, a base é 16. Como exemplo, será convertido o número 3F16 em decimal. 161 3 160 F 3 x 161 + F x 160 = sendo F16 = 1510, substituindo: 3 x 161 + 15 x 160 = 3 x 16 + 15 x 1 = 6310 ∴ 3F16 = 6310 2 Tabela 1 – Seqüência de numeração hexadecimal e binária. DECIMAL 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 HEXADECIMAL 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 BINÁRIO 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 10010 A conversão do sistema decimal para hexadecimal se dá da seguinte forma, considerando a conversão do número 100010 em hexa: 1000 ÷ 16 = 62 62 ÷ 16 = 3 3 ÷ 16 = 0 1° resto = 8 2° resto = 14 3° resto = 3 sendo 1410 = E16 , temos: 100010 = 3E816 Para converter números hexadecimais em binários necessitam-se de 4 bits para representar cada algarismo hexadecimal. Por exemplo, para converter o número C1316 para o sistema binário, faz-se: C 1100 1 0001 3 0011 ∴ C1316 = 1100000100112 A conversão do sistema binário para o sistema hexadecimal é feita agrupando-se os dígitos binários de 4 em 4 bits da direita para a esquerda. Por exemplo, para transformar o número 100110002 em hexadecimal: 1001 1000 ∴ 100110002 = 9816 9 8 3. OPERAÇÕES ARITMÉTICAS NO SISTEMA BINÁRIO Para efetuar a adição no sistema binário, deve-se agir como numa adição convencional no sistema decimal, lembrando que, no sistema binário, tem-se apenas 2 algarismos. Para exemplificar, serão somados os números binários 1102 e 1112. 3 ∴ 1102 + 1112 = 11012 verificação: (610 + 710 = 1310) O método de subtração no sistema binário é análogo a uma subtração no sistema decimal. Observa-se que para o caso 0 – 1, o resultado será igual a 1, porém haverá um transporte para a coluna seguinte que deve ser acumulado no subtraendo e, obviamente, subtraído do minuendo. Por exemplo, é mostrada abaixo a resolução da operação 10002 – 1112 passo a passo. 4. MICROPROCESSADORES Este componente é o principal responsável pelo desempenho de um microcomputador. Exemplos de microprocessadores usados nos PCs são o Pentium, o 486 e o 386, além de outros, é claro. Todos os microprocessadores usados nos PCs são descendentes do 8086, o primeiro microprocessador de 16 bits lançado pela Intel, no final dos anos 70. Antes deles, reinavam os microprocessadores de 8 bits, entre os quais pode-se citar o 8080, 0 8085, o Z80, o 6502, o 6800 e o 6809. Um microprocessador é um chip que contém o que é chamado de Unidade Central de Processamento (em inglês, Central Processing Unit, ou CPU). É responsável por buscar e executar instruções existentes na memória. Essas instruções são chamadas de linguagem assembly ou propriamente “linguagem de máquina”. São comandos muito simples, como operações aritméticas e lógicas, leituras, gravações, comparações e movimentações de dados. Essas instruções simples, quando agrupadas, formam os programas. 4.1 8086 Lançado pela Intel em 1978, o 8086 tinha um desempenho dez vezes melhor que seu antecessor o 8080. Seus registradores tinham a largura de 16 bits, o barramento de dados passou de 8 para 16 bits e o barramento de 4 endereços se tornou maior com 20 bits de largura, permitindo assim que fosse controlado mais de 1 milhão de bytes de memória. A memória passou a ser tratada de maneira diferente pois esse processador tratava a mesma como se fosse dividida em até 16 segmentos contendo 64 kilobytes cada, e não permitia que nenhuma estrutura de dados ultrapassasse a barreira entre os segmentos. 4.2 8088 O 8088 surgiu da necessidade em se criar um processador com características parecidas com as do 8086 mas que tivesse um custo menor. Dessa forma, a Intel colocou no mercado um chip que só se diferenciava do 8086 pelo fato de ter um barramento de dados de 8 bits. Em virtude de sua concepção menos avançada e do baixo custo de produção o 8088 foi escolhido pela IBM, para o projeto de seu computador pessoal, pois, além de possuir o projeto interno de 16 bits também pertencia à mesma linhagem do 8080. A figura 1 apresenta o diagrama de blocos do microprocessador 8086/88. Figura 1 – Diagrama de Blocos do 8086/88. 5. O QUE É ASSEMBLY ? Assembly é uma linguagem de baixo nível, que pode ser usado em conjunto com linguagens de alto nível para acelerar tarefas lentas. Basicamente ela consiste de sentenças que representam instruções em linguagem de máquina, e, como está próxima ao código de máquina, ela é rápida. Há décadas atrás, quando surgiu o 8086, programar não era uma tarefa fácil. Quando os primeiros computadores foram desenvolvidos, a programação tinha que ser feita em código de máquina. Como não era uma tarefa fácil criaram a linguagem Assembly. Para programar em assembly é necessário o tradutor assembler, um programa de computador que produz um código binário correspondente a cada mnemônico (instrução assembly). Os programas de tradução avisam quando encontra algum erro no programa fonte. Nos experimentos em laboratório será utilizado o SID como tradutor assembler. 5.1 Por que aprender assembly ? A primeira razão para se trabalhar com assembly é a oportunidade de conhecer melhor o funcionamento do PC, o que permite o desenvolvimento de programas de forma mais consistente. Uma outra razão é que programas assembly são mais rápidos, menores e mais poderosos do que os criados com outras linguagens. Também 5 oferece maior flexibilidade e controle sobre o PC, ao nível de hardware. Além do mais, o assembler (montador) permite uma otimização ideal nos programas, seja no seu tamanho ou execução. Uma outra vantagem é que o programador assembly tem acesso a todos os recursos e instruções disponíveis na máquina. O programador de linguagem de alto nível não tem este acesso. Por exemplo, se for necessário verificar um bit de estouro (overflow) em um registrador, um programa assembly pode testá-lo, mas não um programa Pascal. 6. REGISTRADORES E FLAGS NO 8086/88 Quando se está trabalhando com assembly, tem-se que usar registradores. Pode-se imaginá-los como sendo variáveis já definidas para o programador. O 8086/88 possui 14 registradores distribuídos nas unidades internas EU (Unidade de Execução) e BIU (Unidade de Interface com o Barramento). Segundo a Intel, os registradores podem ser agrupados da seguinte forma: Registradores de Uso Geral: AX, BX, CX, DX, SP, SI e DI; Registradores de Segmento: CS, DS, SS e ES; Apontador de Instrução: IP; Registrador de FLAGS. 6.1 Registradores de Uso Geral (AX, BX, CX, DX, SP, SI e DI) Estes registradores são definidos como registradores de uso geral pois se pode realmente armazenar qualquer coisa neles. São também registradores de 16 bits, o que significa que é possível armazenar um inteiro positivo de 0 a 65535, ou um inteiro com sinal de -32768 to 32768. Então se tiver que armazenar 0A4C (hexadecimal) em AX, AH conterá 0A, e AL conterá 4C. Os registradores de uso geral são subdivididos em dois conjuntos de 4 registradores: Registradores de Dados: AX, BX, CX e DX. Seus bytes superiores (AH, BH, CH e DH) e inferiores (AL, BL, CL e DL) podem ser acessados de modo separado. Isto significa que cada registrador pode ser usado como um registrador de 8 ou 16 bits. Isto é vantajoso porque evita o uso de registradores de 16 bits quando se realizam operações de 8 bits. Eles podem ser utilizados sem restrições na maioria das operações lógicas e aritméticas. Como será visto a seguir, adicionalmente algumas instruções usam certos registradores de forma implícita, permitindo uma poderosa forma de codificação. Registradores Apontadores e de Indexação: SP, BP, SI e DI. Todo acesso é feito em 16 bits. Podem também participar da maioria das operações lógicas e aritméticas. Registrador AX (AH + AL) Também denominado acumulador primário. Todas as operações de I/O são realizadas através deste registrador. As operações que utilizam dados imediatos necessitam de menos memória quando são feitas através de AX. Geralmente é utilizado como hospedeiro para valores retornados de sub-rotinas. Registrador BX (BH + BL) Também denominado Registrador Base. Usado preferencialmente como apontador da base de uma tabela de dados. Todas as referências à memória que usam esse registrador no cálculo do endereço usam o registrador DS como segmento padrão. Registrador CX (CH + CL) Também denominado contador, sendo usado prioritariamente para contar o número de interações no interior de um loop e também na manipulação de strings. Ele é decrementado durante as operações envolvendo loops e strings. Também utilizado na rotação e deslocamento de vários bits. 6 Registrador DX (DH + DL) Também chamado de registrador de dados ou endereçador de I/O. É usado para guardar dados de 16 bits nas operações com a ULA e controle indireto de I/O. Pode ser usado por compiladores para retornar valores de subrotinas. Registrador SP É denominado Stack Pointer (Ponteiro de Pilha). Utilizado juntamente com BP para acessar dados no segmento da pilha. Armazena o “offset” do endereço do topo da pilha. Todas as referências ao SP, por definição, utilizam juntamente o registrador de segmento SS. Também pode ser usado como operando em operações lógicas e aritméticas de 16 bits. Registrador BP É denominado Base Pointer (Ponteiro da Base). Permite acessar dados no segmento da pilha. Tipicamente é usado para acessar parâmetros que foram passados pela pilha. Também pode ser usado como operando em operações lógicas e aritméticas de 16 bits. Registradores de Indexação (SI e DI) São denominados Source Index (Índice de Origem) e Destination Index (Índice de Destino). São usados para acessar dados na memória de dados. São extensivamente usados nas operações com strings. Também podem ser usados como operando em operações lógicas e aritméticas de 16 bits. 6.2 Registradores de Segmentos A memória do 8086/88 é dividida em segmentos lógicos de até 64 Kb. A CPU tem acesso simultâneo a até 4 segmentos, utilizando os seguintes registradores como seletores de endereço: CS, DS, SS e ES. Estes registradores estão localizados na BIU, sendo acessíveis para programas, podendo ser manipulados por várias instruções. São descritas a seguir as principais funções de cada um destes registradores. Registrador CS É denominado Code Segment (Segmento de Código). É utilizado para montar o endereço de uma instrução (code) a ser buscada (fetch) na memória. CS define o endereço base (segmento) e deve ser somado ao registrador IP (também localizado na BIU) para formar o endereço de 20 bits da instrução. Em outras palavras, aponta para o segmento de código de instrução em uso. Registrador DS É denominado Data Segment (Segmento de Dados). Aponta para os segmentos de dados onde geralmente são armazenadas as variáveis de programa. Em conjunto com IP define o endereço efetivo do dado a ser lido ou escrito na memória. Registrador SS É denominado Stack Segment (Segmento de Pilha). Aponta para o segmento da pilha em uso. Todos os acessos à pilha utilizam os registradores SP e BP e utilizam como referência o registrador de segmento de pilha (SS). Registrador ES É denominado Extra Segment (Segmento Extra). É uma opção extra para apontar a base de um segmento de dados (strings). Por exemplo, ele é utilizado juntamente com o DI para formar o endereço da base de strings de dados. 7 6.3 Registrador IP É denominado Instruction Pointer (Ponteiro de Instruções). Este registrador é atualizado pela BIU e contém a distância em bytes (offset) da próxima instrução em relação ao início do código que está sendo executado, isto é, ele aponta para a próxima instrução. Os programadores não têm acesso direto ao registrador. 6.4 Registrador de FLAGs O microprocessador 8086/88 contém em seu interior um total de 9 sinalizadores, também conhecidos como flags. Eles existem para indicar resultados obtidos sempre na última operação lógica ou aritmética executada, ou para definir o comportamento do microprocessador na execução de certas instruções. Estes 9 flags estão agrupados, para facilidade de acesso, em um registrador de 16 bits, chamado de Registrador de Flags, Registrador de Estado ou Palavra de Estado do Programa, sendo localizado na EU. Como mostra a figura 2, ele utiliza bits para armazenar informações de estado (status) e de controle. 15 14 13 12 11 O 10 D 9 I 8 T 7 S 6 Z 5 4 A 3 2 P 1 0 C Figura 2 – Registrador de Flags do 8086/88. − − Flags de estado: O, S, Z, P, C e A. Flags de controle: T, D, I. 6.4.1 Flags de Estado Os bits de estado refletem certas propriedades de resultados de operações lógicas e aritméticas realizadas na EU, a qual é responsável por “setar” estes bits no registrador. A seguir é descrito o significado dos flags de estado. − Flag A (Auxiliary): reflete o “vai um” do terceiro bit de uma operação de 8 bits. É utilizado por instruções que trabalham com operações decimais. − Flag C (Carry): reflete o “vai um” do bit mais significativo nas operações aritméticas (de 8 e 16 bits). É também modificado por algumas instruções de rotação e deslocamento. − Flag O (Overflow): é setado quando o resultado de uma operação aritmética excede os limites da área destinada ao armazenamento (que pode ser 8 ou 16 bits). Ou quando ocorre um “vai um” do penúltimo bit mais significativo para o último bit mais significativo. Se o registrador ou variável for de 8 bits, e O = 1, significa que houve um “vai um” do 6º para o 7º bit do registrador ou variável. Se o registrador ou variável for de 16 bits, e O = 1, significa que houve um “vai um” do 14º para 15º bit do registrador ou variável. − Flag S (Sign): é igual ao bit de mais alta ordem do resultado de uma operação aritmética, ou seja, indica se o resultado da operação é positivo (S = 0) ou negativo (S = 1). − Flag P (Parity): indica a paridade (par) dos 8 bits menos significativos do resultado da operação realizada (aritmética ou lógica): P = 1 → número par de “1” nos 8 bits menos significativos. P = 0 → número ímpar de “1” nos 8 bits menos significativos. − Flag Z (Zero): é setado (Z = 1) se o resultado da operação for igual a zero. 6.4.2 Flags de Controle Existem 3 flags que podem ser setados pelos programas, alterando as operações do processador: − Flag D (Direction): determina se as operações de string devem ser auto-decrementadas (D = l) ou incrementadas (D = 0). Em outras palavras, determina se as operações com strings vão incrementar ou decrementar os registradores de indexação (SI e DI). 8 − Flag I (Interrupt Enable): caso a CPU esteja habilitada para receber instruções mascaráveis, então I = l. Caso contrário, I = 0 desabilita interrupções mascaráveis. − Flag T (Trap): utilizado para depuração de programa. Se T = 1, o processador executará as instruções passo a passo. 7. INSTRUÇÕES DO MICROPROCESSADOR Para que um programa possa ser executado por um computador, ele precisa ser constituído de uma série de instruções de máquina e estar armazenado em células sucessivas na memória principal. A CPU é responsável pela execução das instruções que estão na memória. Quem executa um programa é o hardware e o que ele espera encontrar é um programa em linguagem de máquina (uma seqüência de instruções de máquina em código binário). A linguagem de máquina é composta de códigos binários, representando instruções, endereços e dados e está totalmente vinculada ao conjunto de instruções do microprocessador. Um ser humano usa seu conhecimento e inteligência para traduzir uma tarefa complexa (tal como, por exemplo, a tarefa de buscar uma pasta em um arquivo) em uma série de passos elementares (identificar o móvel e gaveta onde está a pasta, andar até o móvel, abrir a gaveta. encontrar a pasta, retirar a pasta e fechar a gaveta). Para o computador, uma instrução precisa ser detalhada, dividida em pequenas etapas de operações, que são dependentes do conjunto de instruções do microprocessador e individualmente executáveis. 7.1 Conjunto de Instruções A tabela 2 apresenta um conjunto das principais instruções utilizadas nos experimentos de laboratório. Para maiores detalhes consulte livros específicos indicados na bibliografia. Tabela 2 – Conjunto de Instruções do 8086/88. INSTRUÇÃO OPERAÇÃO AAA Ascii Adjust for Addition (Ajuste Ascii para Adição): Altera o conteúdo de AL para um decimal válido. AAS Ascii Adjust for Subtraction (Ajuste Ascii para Subtração): Corrige o resultado de uma subtração de decimais em AL. ADC destino, Add with Carry (Adição com Carry): Soma dois operandos binários e põe o fonte resultado no destino. Se C estiver setado, soma 1 ao resultado. ADD destino, Arithmetic Addition (Soma Aritmética): Soma fonte e destino, repondo o fonte valor original do destino. Ambos os operandos são binários. AND destino, Logical And (E Lógico): Faz um E Lógico entre os dois operandos, fonte substituindo o destino com o resultado. Procedure Call (Procedimento de Chamada): Põe o Ponteiro de Instrução (e o CALL destino Segmento de Código para chamadas do tipo far) na pilha e carrega o Ponteiro de Instrução com o endereço do nome do procedimento. O código continua com a execução em CS:IP. CLC Clear Carry (Limpa Carry): Limpa (zera) o Flag Carry. Clear Direction Flag (Limpa o Flag de Direção): Limpa (zera) o Flag de CLD Direção levando instruções de string a incrementar os índices dos registradores SI e DI. CMP destino, Compare (Compara): Subtrai a fonte do destino, atualiza os flags mas não fonte guarda o resultado (não altera os valores de destino e fonte). Decimal Adjust for Addition (Ajuste Decimal para Adição): Corrige o DAA resultado (em AL) de uma operação BCD de adição prévia. O conteúdo de AL é modificado para um par de dígitos decimais. FLAGS Aft: A, CXXX Ind: P, S, Z, O Aft: A, C Ind: P, S, Z, O Aft: A, C, O, P, S, Z Aft: A, C, O, P, S, Z Aft: C,O,P,S,Z Ind: A AAAXXXXX – Aft: C Aft: D Aft: A, C, O, P S, Z Aft: A,C,P,S,Z Ind: O 9 Decimal Adjust for Subtraction (Ajuste Decimal para Subtração): Corrige o resultado (em AL) de uma operação BCD de subtração prévia. O conteúdo de AL é modificado para um par de dígitos decimais. DEC destino Decrement (Decrementa): Subtração binária sem sinal de um (1) do destino. Divide: Divisão sem sinal do acumulador pela fonte. Se o divisor fonte for DIV fonte um valor byte, então BL é dividido pela fonte e o quociente é colocado em BL e o resto em BH. Se o operando fonte for um valor word, então DX:AX é dividido pela fonte e o quociente é colocado em BX e o resto em DX. INC destino Increment (Incremento): adiciona um (1) ao operando binário sem sinal destino. Interrupt (Interrupção): inicia uma interrupção por software colocando (push) os flags na pilha, limpando a Trap e os flags de Interrupção, pondo (push) CS INT valor seguido do IP na pilha e carregando CS:IP com os valores encontrados na tabela de vetores de interrupção. A execução começa no ponto endereçado pelo novo CS:IP. Jxx TODOS OS SALTOS (JUMPS) CONDICIONAIS (ver tabela 3) Jump if Register CX is Zero (Salte se o Registrador CX for Zero): desvia a JCXZ label execução para “label” se CX for zero. Usa uma comparação sem sinal. Obs: “label” é o endereço da instrução a ser executada. Uncoditional Jump (Salto Incondicional): transfere o controle para “label” JMP label incondicionalmente. Os saltos estão entre -32768 e 32767 bytes distantes da instrução seguinte ao salto. Saltos NEAR e SHORT provocam a atualização do IP, enquanto que saltos FAR provocam a atualização de CS e IP. Decrement CX and Loop if CX Not Zero (Decrementa CX e faz Loop se CX LOOP label for Não Zero): decrementa CX em 1 e transfere o controle para “label” se CX não for zero. O operando “label” precisa estar a -128 ou 127 bytes da instrução que segue a instrução de loop. Loop While Equal / Loop While Zero (Loop enquanto Igual / Loop enquanto LOOPE label Zero): decrementa CX em 1 (sem modificar flags) e transfere o controle para LOOPZ label “label” se CX não for zero e o flag Zero estiver setado. O operando “label” precisa estar a -128 ou 127 bytes da instrução que segue a instrução de loop. Loop While Not Zero / Loop While Not Equal (Loop enquanto Não Zero / LOOPNZ label Loop enquanto Não Igual): decrementa CX em 1 (sem modificar flags) e LOOPNE label transfere o controle para “label” se CX não for zero e o flag Zero estiver resetado (zerado). O operando “label” precisa estar a -128 ou 127 bytes da instrução que segue a instrução de loop. Move Byte or Word (Move Byte ou Word): copia um byte ou word do MOV destino, operando fonte para o operando destino. Se o destino é SS, as interrupções fonte são desabilitadas, exceto nas CPUs 808x mais antigas e com bug. Algumas CPUs desabilitam as interrupções se o destino for um dos registradores de segmento. Não copia byte ou word de memória para memória. MOVS destino, Move String - Byte or Word (Move String - Byte ou Word): copia dados fonte endereçados por DS:SI (mesmo que um operando seja fornecido) para a localização ES:DI e atualiza SI e DI baseado no tamanho do operando ou da MOVSB MOVSW instrução usada. Se o flag de direção estiver resetado (zerado), SI e DI são MOVSD incrementados, se estiver setado, SI e DI são decrementados. Use com REP. Unsigned Multiply (Multiplicação sem Sinal): multiplicação sem sinal do MUL fonte acumulador pela fonte. Se a fonte for um valor byte, então AL é usado como o outro multiplicando e o resultado é colocado em AX. Se a fonte for um valor word, então AX é multiplicado pela fonte e EX:AX recebe o resultado. NEG destino Two's Complement Negation (Negação com Complemento de 2): subtrai o destino de 0 (zero) e salva o complemento de 2 no próprio destino. DAS Aft: A,C,P,S,Z Aft: A,O,P,S,Z Ind: A, C, O, P, S, Z Aft: A, O, P, S, Z Aft: I, T – – – – – – – – Aft: C, O Ind: A, P, S, Z Aft: A, C, O, P, S, Z 10 No Operation (Nenhuma Operação): esta é uma instrução de fazer nada. Resulta na ocupação tanto de espaço quanto de tempo e é muito útil para fazer patches em segmentos de código. One's Complement Negation - Logical NOT (Negação com Complemento de NOT destino 1 - NÃO Lógico): Inverte os bits do operando destino formando seu complemento de 1. OR destino, Inclusive Logical OR (OU Inclusive Lógico): OU Inclusive Lógico dos dois fonte operandos, retornando o resultado no destino. Todos os bits ativos em qualquer dos operandos estará ativo no resultado. POP destino Pop Word off Stack (Tire Word da Pilha): transfere o word do topo da pilha (SS:SP) para destino e incrementa SP em 2 para indicar o novo topo de pilha. POPF Pop Flags off Stack (Tire os Flags da Pilha): tira word da pilha e os transfere para os registradores de flags. Depois incrementa SP em 2. Push Word onto Stack (Ponha Word na Pilha): decrementa SP pelo tamanho PUSH fonte do operando (dois, ou quatro, valores byte são estendidos por sinal) e transfere um word da fonte para o topo da pilha (SS:SP) PUSHF Push Flags onto Stack (Ponha os Flags na Pilha): transfere os registradores de flags para a pilha. PUSHF guarda um valor de 16 bits. Rotate Through Carry Left (Rolar através do Carry para a Esquerda): Rola os RCL destino, bits do destino para a esquerda as “vezes” indicadas com todos os dados que vezes “vazarem” pela esquerda re-entrando pela direita. O flag de carry conterá o valor do último bit rolado. Rotate Through Carry Right (Rolar através do Carry para a Direita): Rola os RCR destino, bits do destino para a direita as “vezes” indicadas com todos os dados que vezes “vazarem” pela direita re-entrando pela esquerda. O flag de carry conterá o valor do último bit rolado. ROL destino, Rotate Left (Rotação para a Esquerda): Rola os bits do destino para a vezes esquerda as “vezes” indicadas com todos os dados que “vazarem” pela esquerda re-entrando pela direita. O flag C conterá o último bit rolado. ROR destino, Rotate Right (Rotação para a Direita): Rola os bits do destino para a direita as vezes “vezes” indicadas com todos os dados que “vazarem” pela direita re-entrando pela esquerda. O flag de carry conterá o valor do último bit rolado. Shift Arithmetic Right (Deslocamento Aritmético para a Direita): Desloca os SAR destino, bits do destino para a direita as “vezes” indicadas com o bit de sinal replicado vezes no último bit. O flag de carry conterá o valor do último bit deslocado. SBB destino, Subtract with Borrow (Subtração com Empréstimo): Subtrai a fonte do fonte destino, além de subtrair 1 se o flag de carry estiver setado. O resultado é armazenado no destino. SHL destino, Shift Logical Left (Deslocamento Lógico para a Esquerda): Desloca os bits vezes do destino para a esquerda as “vezes” indicadas com zeros colocados à direita. O flag de carry conterá o valor do último bit deslocado. SHR destino, Shift Logical Right (Deslocamento Lógico para a Diretita): Desloca os bits vezes do destino para a direita as “vezes” indicadas com zeros colocados à esquerda. O flag de carry conterá o valor do último bit deslocado. SUB destino, Subtraction (Subtração): Subtrai a fonte do destino e o resultado é fonte armazenado em destino. TEST destino Test (Testar): Faz um AND lógico entre os operandos, atualizando os flags e fonte sem armazenar o resultado. XCHG destino, Exchange (Troca): Troca o destino pela fonte e vice-versa. Fonte e destino fonte podem ser dois registradores ou um registrador e uma posição de memória. XOR destino, Exclusive Or (OU Exclusivo): faz um OU Exclusivo entre os operandos e fonte retorna o resultado no destino. NOP – – Aft: C,O,P,S,Z Ind: A – todos são afetados – – Aft: C, O Aft: C, O Aft: C, O Aft: C, O Aft: C, O, P, S, Z Ind: A Aft: A, C, O, P, S, Z Aft: C,O,P,S,Z Ind: A Aft: C,O,P,S,Z Ind: A Aft: A, C, O, P, S, Z Aft: C,O,P,S,Z Ind: A – XCHGpdestino, Aft: C,O,P,S,Z Ind: A 11 Obs: Aft → significa Afetado. Indica quais flags são afetados depois de executada determinada instrução. Ind → significa Indefinido. Indica quais flags são “setados” indefinidamente (aleatoriamente). A tabela 3 mostra instruções de quebra ou mudança de seqüência do tipo Jxx. Tabela 3 – Conjunto de Instruções de Transferência Condicional. Mnemônico JA JAE JB JBE JC JCXZ JE JG JGE JL JLE JMP JNA JNAE JNB JNBE JNC JNE JNG JNGE JNL JNLE JNO JNP JNS JNZ JO JP Significado Salte se Acima (Jump if Above) Salte se Acima ou Igual (Jump if Above or Equal) Salte se Abaixo (Jump if Below) Salte se Abaixo ou Igual (Jump if Below or Equal) Salte se Carry (Jump if Carry) Salte se CX for Zero (Jump if CX Zero) Salte se Igual (Jump if Equal) Salte se Maior (Jump if Greater) (COM SINAL) Salte se Maior ou Igual (Jump if Greater or Equal) (COM SINAL) Salte se Menor (Jump if Less) (COM SINAL) Salte se Menor ou Igual (Jump if Less or Equal) (COM SINAL) Salto Incondicional (Unconditional Jump) Salte se Não Acima (Jump if Not Above) Salte se Não Acima ou Igual (Jump if Not Above or Equal) Salte se Não Abaixo (Jump if Not Below) Salte se Não Abaixo ou Igual (Jump if Not Below or Equal) Salte se Não Carry (Jump if Not Carry) Salte se Não Igual (Jump if Not Equal) Salte se Não Maior (Jump if Not Greater) (COM SINAL) Salte se Não Maior ou Igual (Jump if Not Greater or Equal) (COM SINAL) Salte se Não Menor (Jump if Not Less) (COM SINAL) Salte se Não Menor ou Igual (Jump Not Less or Equal) (COM SINAL) Salte se Não Overflow (Jump if Not Overflow) (COM SINAL) Salte se Não Paridade (Jump if Not Parity) Salte se Não Sinal (Jump if Not Signed) (COM SINAL) Salte se Não Zero (Jump if Not Zero) Salte se Overflow (Jump if Overflow) (COM SINAL) Salte se Paridade (Jump if Parity) Condição de Salto C=0eZ=0 C=0 C=1 C = 1 ou Z = 1 C=1 CX = 0 Z=1 Z=0eS=O S=O S <> O Z = 1 ou S <> O C = 1 ou Z = 1 C=1 C=0 C=0eZ=0 C=0 Z=0 Z = 1 ou S <> O S <> O S=O Z=0eS=O O=0 P=0 S=0 Z=0 O=1 P=1 Uma vez apresentadas as diversas instruções reconhecidas pelo microprocessador 8086/88 da Intel, será exemplificado abaixo um pequeno programa feito no SID utilizando estas instruções, e entendendo passo a passo o que o programa faz. ENDEREÇO 0000 0002 0004 0007 0009 000B MNEMÔNICOS SUB AX, AX ADD AX, [BX] ADD BX, 2 CMP DX, BX JNS 0002 INT 3 COMENTÁRIOS Faz AX = 0 Adiciona uma Word apontada por BX em AX Adiciona 2 à BX Compara DX com BX Se DX ≥ BX salta para o endereço 0002 Finaliza o programa A programação em assembly é feita através de mnemônicos, que são, posteriormente, convertidos em opcodes pelo montador, que é a linguagem de máquina entendida pelo processador. 12 8. INTERRUPÇÕES Uma das coisas essenciais que tornam um computador diferente de qualquer outro tipo de máquina feita pelo homem é sua capacidade de responder a uma imprevisível variedade de trabalhos. O recurso de interrupção permite ao computador suspender o que estiver fazendo e passar a fazer outra coisa em resposta a uma interrupção (por exemplo, pressionar uma tecla no teclado). Os processos de interrupções permitem este tipo de resposta imediata sem ter que desperdiçar tempo de CPU e códigos de programa, verificando todas as possíveis tarefas paralelas que precisem ser realizadas em um determinado momento. 8.1 Interrupções por Hardware Interno Interrupções internas são geradas por certos eventos que ocorrem durante a execução de um programa. Estes tipos de interrupções são gerenciados, na sua totalidade, pelo hardware e não é possível modificá-las. Um exemplo claro deste tipo de interrupção é a que atualiza o contador do clock interno do computador, o hardware chama esta interrupção muitas vezes durante um segundo. Não é permitido ao programador gerenciar diretamente esta interrupção, uma vez que não se pode controlar a hora atualizada por software. Mas o programador pode usar seus efeitos no computador para o seu benefício, por exemplo para criar um virtual clock atualizado continuamente pelo contador interno de clock. Para tanto, é necessário apenas ler o valor atual do contador e o transformar num formato compreensível pelo usuário. 8.2 Interrupções por Hardware Externo Interrupções externas são geradas através de dispositivos periféricos, tais como teclados, impressoras, placas de comunicação, entre outros. São também geradas por co-processadores. Não é possível desativar interrupções externas. Estas interrupções não são enviadas diretamente para a CPU, mas, de uma forma melhor, são enviadas para um circuito integrado cuja função exclusiva é manusear este tipo de interrupção. O circuito, chamado PIC8259A, é controlado pela CPU através de uma série de comunicação chamada paths. 8.3 Interrupções por Software Interrupções por software podem ser ativadas diretamente por programas assembly, invocando o número da interrupção desejada com a instrução INT. O uso das interrupções facilita bastante a criação dos programas, tornando-os menores. Além disso, é fácil compreendê-las e geram boa performance. Estes tipos de interrupções podem ser separados em duas categorias: Interrupções do Sistema Operacional DOS e interrupções do BIOS. A diferença entre ambas é que as interrupções do sistema operacional são mais fáceis de usar, mas também são mais lentas, uma vez que acessam os serviços do BIOS. Por outro lado, interrupções do BIOS são muito mais rápidas, mas possuem a desvantagem de serem parte do hardware, o que significa serem específicas à arquitetura do computador em questão. A escolha sobre qual o tipo de interrupção usar irá depender somente das características que deseja dar ao seu programa: velocidade (use BIOS), portabilidade (use DOS). 8.4 Principais Interrupções BIOS: Interrupção 16h – serviço 0 MOV AH, 0 INT 16H 13 função: leitura do teclado sem ecoar na tela. Na saída, AL contém o código do caractere que foi digitado. Se AL = 0 então foi pressionada uma tecla especial cujo código estará em AH = SCAN CODE. Interrupção 10h – serviço 2 MOV AH, 2 MOV DH, linha MOV DL, coluna MOV BH, 0 ; pagina de video. INT 10H função: posiciona o cursor. Interrupção 10h – serviço 6 MOV AH, 6 MOV AL, numero de linhas a rolar MOV CH, linha superior esquerdo MOV CL, coluna superior esquerdo MOV DH, linha inferior direito MOV DL, coluna inferior direito MOV BH, atributo INT 10H função: limpa a tela. DOS: Interrupção 21h – serviço 1 MOV AH, 1 INT 21H função: leitura do teclado com eco na tela. O caractere lido está em AL, se AL = 0, deve repetir a leitura (tecla especial). Interrupção 21h – serviço 9 MOV AH, 9 MOV DX, offset string INT 21H função: imprime na tela uma string que inicia no endereço apontado por DX e vai até encontrar um "$". Interrupção 21h – serviço 2 MOV AH, 2 INT 21H função: escreve o caractere que está em DL na tela. 9. BIBLIOGRAFIA 1. Lance A. Leventhal - 8080/8085 Assembly Language Programming, Osborne/McGraw-Hill, Berkeley, California, 1979. 2. Lance A. Leventhal and Winthrop Saville - 8080/8085 Assembly Language Subroutines, Osborne/McGraw-Hill, Berkeley, California, 1983. 3. Antonio C. J. F. Visconti - Microprocessadores 8080 e 8085 : Software, Livros Érica Editora Ltda., Vol. 2, São Paulo, 1983. 4. Intel Corporation - MCS-85 User's Manual, 1981. 5. Intel Corporation - 8080/8085 Assembly Language Programming Manual, 1981. 14