layout e acesso a memoria II

LAYOUT E ACESSO À MEMÓRIA II Modos de endereçamento do 80x86 Os processadores 80x86 permitem acessar a memória de vários modos diferentes. Os modos de endereçamento de memória do 80x86 oferem flexibilidade no acesso à memória, permitindo acessar facilmente variáveis, arrays, registros, ponteiros e outros tipos de dados mais complexos. Dominar os modos de endereçamento do 80x86 é o primeiro passo para dominar a linguagem Assembly 80x86. Quando a Intel projetou o processador 8086 original, ele foi lançado com um conjunto de modos de endereçamento de memória flexível, porém limitado. A Intel adicionou vários outros modos de endereçamento quando lançou o microprocessador 80386. Lembre-se de que o 80386 manteve todos os modos anteriores e que os novos modos são apenas um bonus. Se precisarmos escrever código que funcione num processador 80286 ou anterior (quem é que pode saber? talvez alguém queira ressucitar alguma sucata :), não será possível aproveitar as vantagens destes novos modos. Entretanto, eles podem ser usados em processadores a partir do 80386SX (note que eu disse a partir!). Por isso, para evitar confusões, é melhor separar a análise destes dois novos conjuntos de modos de endereçamento. Modos de endereçamento com registradores no 8086 A maioria das instruções do 8086 podem operar com seu conjunto de registradores de propósito geral. Especificando o nome do registrador como um operando da instrução, podemos acessar o conteúdo do registrador especificado. Considere a instrução mov (move) do 8086: mov destino, fonte Esta instrução copia os dados do operando fonte para o operando destino. Os registradores de 8 e 16 bits são operando válidos para esta instrução. A única restrição é que ambos os operandos precisam ter o mesmo tamanho. Vamos dar uma olhada em algumas instruções mov do 8086: mov ax, bx ; Copia o valor de BX para AX mov dl, al ; Copia o valor de AL para DL mov si, dx ; Copia o valor de DX para SI mov sp, bp ; Copia o valor de BP para SP mov dh, cl ; Copa o valor de CL para DH mov ax, ax ; Sim, é perfeitamente legal! Lembre-se, os registradores são o melhor lugar para manter variáveis usadas com frequência. Veremos um pouco mais adiante que, instruções que usem registradores são mais curtas e rápidas do que as que usam acesso à memória. Além dos registradores de própósito geral, muitas instruções 8086 (incluindo a instrução mov) permitem especificar um dos registradores de segmento como operando. Existem duas restrições quanto ao uso de registradores de segmento com a instrução mov. A primeira é que não se pode especificar cs como operando destino e, a segunda, que apenas um dos operandos pode ser um registrador de segmento. Não se pode mover dados de um registrador de segmento para outro com uma única instrução mov. Para copiar o valor de cs para ds podemos usar uma sequência como: mov ax, cs mov ds, ax Os registradores de segmento nunca devem ser usados como registradores de dados para armazenar valores arbitrários. Devem conter apenas endereços de segmentos. Mais sobre o assunto virá a seguir. Neste texto, os operando sreg identifica registradores de segmento. Modos de endereçamento de memória no 8086 O 8086 fornece 17 modos diferentes de acesso à memória. Pode parecer um exagero, mas felizmente a maioria dos modos de endereçamento são apenas variações de um outro. São fáceis de aprender e é imperioso que se aprenda! A chave para uma boa programação Assembly é o uso apropriado dos modos de endereçamento. Os modos de endereçamento do 8086 incluem apenas por deslocamento, base, deslocamento mais base, base mais índice e deslocamento mais base mais índice. As variações destas cinco formas é que resultam nos 17 modos diferentes de endereçamento. O modo de endereçamento apenas por deslocamento Fig.6 - Endereçamento apenas por deslocamento O modo de endereçamento mais comum, e o mais fácil de entender, é o modo de endereçamento apenas por deslocamento (ou direto). Consiste numa constante de 16 bits que especifica o endereço da localização alvo. A instrução mov al,ds:[8088h] carrega o registrador al com a cópia do byte residente na memória na localização 8088h. Da mesma forma, a instrução mov ds:[1234h],dl armazena o valor do registrador dl na localização de memória 1234h (veja a Fig.6). O modo de endereçamento apenas por deslocamento é perfeito para acessar variáveis simples. É claro que preferimos nomes como "I" ou "J" ao invés de "DS:[1234h]" ou "DS:[8088h]" mas, não se proecupe, logo veremos que isto também é possível. Fig.7 - Acessando words apenas por deslocamento A Intel denominou este modo de endereçamento de apenas por deslocamento porque uma constante de 16 bits (deslocamento) segue o opcode mov na memória. Neste aspecto ele é bastante parecido com o modo de endereçamento direto dos processadores x86. Entretanto, existem algumas pequenas diferenças. Antes de mais nada, um deslocamento é exatamente isto - alguma distância entre dois pontos. Nos processadores 80x86, este deslocamento é a distância entre o início de um segmento (neste exemplo, o segmento de dados) e a localização pretendida. Não se preocupe se a coisa está meio confusa. Ainda neste capítulo teremos a oportunidade de estudar segmentos até não poder mais. Por enquanto, podemos considerar o modo de endereçamento apenas por deslocamento como um modo de endereçamento direto. Os exemplos deste capítulo geralmente vão acessar bytes da memória. Não esqueça, no entanto, que também é possível acessar words com os processadores 8086 (veja a Fig.7). Como padrão, todos os valores apenas por deslocamento fornecem deslocamentos (offset) dentro do segmento de dados. Se quisermos fornecer um deslocamento dentro de um segmento diferente, precisamos usar um prefixo de cancelamento junto com o endereço. Por exemplo, para acessar a localização 1234h no segmento extra (es), usa-se uma instrução na forma de mov ax,es:[1234h]. Do mesmo modo, para acessar esta posição no segmento de código, a instrução seria mov ax,cs:[1234h]. O prefixo ds: usado nos exemplos anteriores não é um cancelamento de segmento. A CPU usa o registrador do segmento de dados como padrão. Estes exemplos específicos precisam do ds: devido a limitações da sintaxe do MASM (macro assembler da Microsoft que será usado logo a seguir). Os modos de endereçamento indireto com registradores As CPUs 80x86 permitem acessar a memória indiretamente através de um registrador usando os modos de endereçamento indireto por registradores. Existem quatro formas neste modo de endereçamento, melhor demonstradas nas seguintes instruções: mov al, [bx] mov al, [bp] mov al, [si] mov al, [di] Fig.8a - Modo indireto com registrador BX Estas quatro formas de endereçamento referenciam o byte no deslocamento indicado pelos registradores bx, bp, si ou di. Como padrão, o modo de endereçamento [bp] usa o segmento da pilha (ss). Podemos usar os símbolos dos prefixos de cancelamento de segmento se quisermos acessar dados em segmentos diferentes. As instruções seguintes demonstram o uso destes cancelamentos: mov al, cs:[bx] mov al, ds:[bp] mov al, ss:[si] mov al, es:[di] Fig.8b - Modo [BP] com pilha A Intel denomina [bx] e [bp] de modo de endereçamento base e bx e bp como registradores base (bp é base pointer - o ponteiro base). Denomina os modos de endereçamento [si] e [di] como indexados (si é source index - índice fonte e di é destination index - índice destino). Entretanto, estes modos de endereçamento são funcionalmente equivalentes. Para manter a consistência, neste texto chamaremos estas formas de modo indireto com registradores. Observação: os modos de endereçamento [si] e [di] funcionam exatamente da mesma forma, apenas substitua si e di por bx nas instruções acima. Modos de endereçamento indexado Os modos de enderaçamento indexado usam a seguinte sintaxe: mov al, desloc[bx] mov al, desloc[bp] mov al, desloc[si] Fig.9a - Modo indexado com BX Se bx contiver 1000h, então a instrução mov cl,20h[bx] carregará cl com o valor da posição de memória ds:1020h. Da mesma forma, se bp contiver 2020h, mov dh,1000h[bp] carregará dh com o valor da posição ss:3020h. Os deslocamentos gerados por estes modos de endereçamento são a soma da constante e do registrador especificado. Como padrão, os modos de endereçamento que envolvem bx, si e di usam o segmento de dados e o modo de endereçamento desloc[bp] usa o segmento da pilha. Como nos modos de endereçamento indireto com registradores, pode-se usar prefixos de cancelamento para especificar segmentos diferentes: mov al, ss:disp[bx] mov al, es:disp[bp] mov al, cs:disp[si] mov al, ss:disp[di] Fig.9b - Modo indexado com BP e pilha Podemos substituir si ou di para obter os modos de endereçamento [si+desloc] e [di+desloc]. Saiba que a Intel ainda se refere a estes modos de endereçamento como endereçamento de base e endereçamento indexado. A literatura da Intel não faz diferença entre estes dois modos, com ou sem a constante. Se verificarmos como o hardware trabalha, esta até que é uma definição razoável. Do ponto de vista do programador, entretanto, estes modos de endereçamento são úteis para coisas totalmente diversas. Este é o motivo pelo qual este texto usa termos diferentes para nominá-los. Infelizmente existe pouco consenso no uso dos termos no mundo 80x86. Modos de endereçamento de base indexada Os modos de endereçamento de base indexada são simplesmente combinações dos modos de endereçamento indireto com registradores. Este modos formam o deslocamento adicionando o registrador base (bx ou bp) ao registrador índice (si ou di). As formas permitidas destes modos de endereçamento são: mov al, [bx][si] mov al, [bx][di] mov al, [bp][si] mov al, [bp][di] Fig.10a - Modo de base indexada com BX Fig.10b - Modo de base indexada com BP e pilha Imagine que bx contenha 1000h e que si contenha 880h. Neste caso, a instrução mov al,[bx][si] carrega al com o valor da posição DS:1880h. Da mesma forma, se bp contiver 1598h e di contiver 1004, mov ax,[bp+di] carrega os 16 bits da posição SS:259C e SS:259D em ax. Como padrão, os modos de endereçamento que não envolvem bp usam o segmento de dados. Também como padrão, os que tiverem bp como operando usam o segmento da pilha. Podemos substituir si da Fig.10a por di para produzir o modo de endereçamento [bx+di] e substituir si da Fig.10b por di para produzir o modo de endereçamento [bp+di]. Modo de endereçamento de base indexada mais deslocamento Estes modos de endereçamento são ligeiras modificações dos modos de endereçamento base/índice. Adiciona-se apenas uma constante de 8 ou de 16 bits. A seguir alguns exemplos destes modos: mov al, desloc[bx][si] mov al, desloc[bx+di] mov al, [bp+si+desloc] mov al, [bp][di][desloc] Fig.11a - Modo de base indexada mais deslocamento com BX Fig.11b - Modo de base indexada mais deslocamento com BP e pilha Podemos substituir si da Fig.11a por di para produzir o modo de endereçamento [bx+di+desloc] e substituir si da Fig.11b por di para produzir o modo de endereçamento [bp+di+desloc]. Suponha que bp contenha 1000h, bx contenha 2000h, si contenha 120h e di contenha 5. Então, mov al,10h[bx+si] carrega al com o valor do endereço DS:2130; mov ch,125h[bp+di] carrega ch com o valor da posição SS:112A; e mov bx,cs:2[bx][di] carrega bx com o valor da posição CS:2007. Sintaxe MASM para os modos de endereçamento de memória 8086 O assembler da Microsoft usa diversas variações diferentes para indicar os modos de endereçamento indexado, base indexada e de dslocamento mais base indexada. Veremos todas estas formas sendo usadas, umas substituindo outras. A lista seguinte mostra algumas das combinações possíveis para os vários modos de endereçamento 80x86 que são aceitas: desloc[bx] [desloc+bx][bx][si] [si+bx]desloc[bx][si] [bx][desloc] [bx+si] desloc[bx+si] [bx+desloc] [si][bx] [desloc+bx+si] [desloc][bx] [desloc+bx][si] desloc[si][bx] [desloc+si][bx] [desloc+si+bx] [si+desloc+bx] [bx+desloc+si] etc O MASM trata os símbolos "[ ]" como o operador "+". Este operador é comutativo, como o perador da soma. É claro que esta discussão se aplica a todos os modos de endereçamento do 8086, e não apenas aos que envolvem BS e SI. Podemos substituir qualquer registrador nos modos de endereçamento acima mencionados. Um jeito fácil de decorar os modos de endereçamento de memória do 8086 Fig.12 - Diagrama para memorização Existe um total de 17 modos de endereçamento de memória diferentes aceitos pelo 8086: desloc, [bx], [bp], [si], [di], desloc[bx], desloc[bp], desloc[si], desloc[di], [bx][si], [bx][di], [bp][si], [bp][di], desloc[bx][si], desloc [bx][di], desloc[bp][si] e desloc[bp][di]. Poderíamos memorizar todas estas formas para saber quais são as válidas (e, por exclusão, as formas inválidas). Existe entretanto um modo mais fácil. Observe o diagrama da Fig.12. Se escolhermos um ou nenhum item de cada coluna de forma que obtenhamos pelo menos um item, temos um modo de endereçamento de memória 8086 válido. Eis alguns exemplos: • Escolhendo desloc da coluna um, nada da coluna dois e [di] da coluna três obtém-se o endereçamento desloc[di]. • Escolhendo desloc, [bx] e [di], obtém-se desloc[bx][di]. • Pulando as colunas um e dois e escolhendo [si] obtemos [si]. • Pulando a coluna um e escolhendo [bx] e depois [di] obtemos [bx][di]. Do mesmo modo, se tivermos um modo de endereçamento que não pode ser construído a partir deste diagrama, então ele não é válido. Por exemplo, desloc[dx][si] é ilegal porque [dx] não consta em nenhuma das colunas do diagrama. Algumas observações finais sobre os modos de endereçamento do 8086 O endereço efetivo é o deslocamento final produzido por um cálculo de modo de endereçamento. Por exemplo, se bx contiver 10h, o endereço efetivo de 10h[bx] será 20h. Você verá o termo endereço efetivo em praticamente todos os textos referentes ao modo de endereçamento do 8086. Existe até mesmo uma instrução especial, lea (load effective address - carregar o endereço efetivo), que calcula o endereço efetivo. Nem todos os modos de endereçamento são criados da mesma forma! Modos de endereçamento diferentes podem ser mais demorados do que calcular o endereço efetivo. A diferença exata depende do processador. Apesar disso, geralmente, quanto mais complexo for o modo de endereçamento, mais demorado é o cálculo do endereço efetivo. A complexidade de um modo de endereçamento está diretamente relacionada com o número dos seus termos. Por exemplo, desloc[bx][si] é mais complexo do que [bx]. O campo deslocamento em todos os modos de endereçamento, exceto no apenas por deslocamento, pode ser uma constante de 8 bits ou de 16 bits com sinal. Se o deslocamento estiver na faixa de -128 a 127, a instrução será mais curta (e, portanto, mais rápida) que uma insrução com um deslocamento fora desta faixa. O tamanho do valor no registrador não afeta o tempo de execução ou o tamanho. Assim, é preferível colocar os números maiores em registradores e usar um deslocamento pequeno do que usar constantes grandes e valores pequenos nos registradores. Se o cálculo de um endereço efetivo produzir um valor maior do que 0FFFFh, a CPU ignora o overflow e o resultado volta para zero. Por exemplo, se bx contiver 10h, então a instrução mov al,0FFFFh[bx] carregará o registrador al da com o valor da posição de memória ds:0Fh e não da posição ds:1000Fh.