Programar em Assembly com GAS/Memória modo real
Modo Real (Real Mode ou RM )
Todos sabemos que a memória serve para guardar valores.
Faz alguns tempos atrás a memória era tão pequena, claro vendo do ponto de vista de hoje e lógico que para eles era enorme.
Naquela época os endereços da memória não era um problema, a cada valor do tamanho de um octeto ou byte era atribuído um numero em hexadecimal que representava seu endereço absoluto.
No ano 1974 os processadores intel 8080 de 8-bits e um barramento de endereços 16-bits podiam manipular 65536 endereços.
E a memória vai de 0x0000 a 0xFFFF sabendo que 16 bits e igual a 4 dígitos hexadecimal.
Então FFFF sendo igual a 65536 ou 64k mesmo tendo mais memória o processador seria incapaz de manipular mais que 64K .
Veja isto como uma rua de FFFF quilômetros.
Para pegar um passageiro e só saber o lugar aonde ele esta entre a distancia 0000 e FFFF.
Deve-se lembrar que cada endereço de memória armazana apenas um byte (0x00 a 0xFF), o que corresponde a um caractere no sistema AscII. Segue um exemplo de memória ocupada com a cadeia de caracteres "Aprender Assembly".
0x0000 (0) a 0x0012 (18) | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Endereço | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0F | 10 | 11 | 12 |
Valor | A | p | r | e | n | d | e | r | A | s | s | e | m | b | l | y | 0 |
Como disse quanto mais temos mais queremos .
Os programas começaram a ficar cada vez mais pesados ao ponto que 64k não era mais suficientes.
Os desenvolvedores queriam fazer possível a utilização de "1MB" da memória ou seja 16 vezes
mais de o que era então "64K".
Por exemplo, queremos utilizar endereços que vão de 00000 a FFFFF para esse tamanho de endereço é necessário
usar 20 bits mais se o registro é de 16 bits a solução poderia ter sido usar registros maiores .
Mais por alguma razão eles não queriam modificar o sistema de endereçamento de "16 bits".
A solução foi compor um sistema de endereçamento que utilizasse "2 vezes 16 bits" ou de "0000:0000" a "FFFF:FFFF".
No ano 1978 a intel lançou no mercado o processador 8086 16-bit capaz de endereçar 1 MiB de memória física usando registros de segmentos esses registros indicam os lugares da memória que devem ser usado para o código,pilha,dados,dados suplementares .
Esses segmentos de 64K podem ser colocados em qualquer lugar da memória um programa do utilizador pode os mudar de lugar se necessário.
Isto marca a aparição do primeiro "sistema de segmentação" no mundo da informática.
Pode nos dizer como funciona o novo sistema de endereçamento?
Sim, então vamos esquecer o sistema hexadecimal vou lhes explicar em decimais.
Vamos tentar fazer em decimais a mesma coisa que as maquinas do ano 1981.
Então nos temos duas cifras decimais com essas duas cifras podemos formar números de
00 à 99 ate agora tudo bem, só precisamos de números até 99.Mais se precisarmos mais e tivermos que passar a barra dos 99?.
Poderíamos usar em vez de duas, três cifras que nos permitiriam ir de 000 ate 999.
Mais em vez de colocar uma cifra suplementar os engenheiros decidirão inventar um sistema de numeração composto, com 2 números ordinários e 2 cifras.
Agora temos o sistema de numeração seguinte 00:00 .Vamos dizer que as 2 cifras da esquerda
representa "múltiplos de dez" 01:00 , 02:00 , 99:00 e igual a 10, 20, 990 ..etc.
Então com estas duas primeiras cifras podemos ir ate 990.
Sim, mais agora queremos os números ; 111, 225 , 999.
Para obter o resto do trajeto é só adicionar as cifras que estão a direita.
Sabendo que os numero a direita vão só até 99.
10:11 , 20:25 , 90:99 .
Então vamos formar o numero 863 .
86:03
85:13
84:23
83:33
82:43
81:53
80:63
79:73
78:83
77:93
O primeiro e um "múltiplo de 10" e o segundo vai ate 99.Isto quer dizer que para um local 873
na memória você teria 10 endereços possíveis.
Para saber qual é o endereço absoluto fazemos o inverso.
Pegamos as primeiras duas cifras e "multiplicamos por 10" e somamos as duas cifras da esquerda
Como são múltiplos de 10 é só colocar um zero depois das 2 primeiras cifras e somar com as
cifras da esquerda. Assim 860 + 03 = 863
Endereço Lógico (Segmento e offset)
Este sistema de endereçamento é representado da seguinte forma segment:offset (segmento:deslocamento) e o dois pontos ":" chamado separador.
Segmento: São blocos de 64 KiB ou 16 bits (FFFFh).
Offset: O deslocamento é utilizado para determinar uma posição dentro do bloco do segmento.
Na linguagem C o segmento poderia ser comparado a um vetor e o offset ao índice ou posição.
Ex: segmento[offset];
Os endereços lógicos diferentes que nos dão um mesmo endereço absoluto na memória são chamados de segmento:offset pares.
Voltamos a falar da memória e o sistema de endereçamento agora com o sistema segment:offset.
No primeiro exemplo que vimos a memória vai de 0000 a FFFF.
Mais com os programas de mais em mais pesados ele ficou pequena.
O sistema de segment:offset esta representado com 2 porções de 16 bits .
Segment:offset ou Segmento:Deslocamento FFFF:FFFF 16-bits:16-bits
Quando combinamos um segmento e um deslocamento, um endereço físico ou absoluto é criado.
O primeiro segmento "0000" vai até o deslocamento "FFFF", ou seja, 0000:FFFF.
O deslocamento nos permite acessar com precisão um byte dentro de um segmento.
Exemplo:
Segmentos↓ | DESLOCAMENTOS → | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0000 | 0001 | 0002 | 0003 | 0004 | 0005 | 0006 | 0007 | 0008 | 0009 | 000A | 000B | 000C | 000D | 000E | 000F | |
0000 | E | u | d | e | t | e | s | t | o | . | A | s | s | e | m | . |
0010 | D | e | . | 0 | . | a | . | F | F | F | F | . | . | . | . | . |
0020 | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
0030 | . | . | . | . | . | . | . | . | . | ? | . | . | . | . | . | . |
Endereço lógico de 'E' 0000:0000
Endereço lógico de 'D' 0000:0010
Endereço Físico (Endereço Absoluto)
Vimos que o endereço lógico utiliza o sistema hexadecimal, e se compõe de quatro cifras a direita e quatro cifras a esquerda, indo de 0000:0000 a FFFF:FFFF.
Para calcular o endereço físico a partir de um endereço lógico "segmneo:offset".
É semelhante a forma que calculamos o endereço com decimal, pegamos o valor do segmento e acrecentamos 0 no final, agora fica com um valor de 5 cifras e somamos ao valor de deslocamento.
Exemplo 1:
Endereço lógico de '?' é 0000:0039 na tabela acima
pegamos seu segmento e multiplicamos por 16 ou 0010h (apenas incluimos 0 ao final: 00000)
somamos ao valor do deslocamento(Veja sobre Adição Hexadecimal aqui):
00000
+0039
00039
Exemplo 2:
O endereço lógico 923F:E2FF
923F0
+E2FF
A06EF
Exemplo 3:
Caso especial são os endereços de 100000 a 10FFEF, como o endereço lógico FFFF:FFFF
FFFF0
+FFFF
10FFEF
equivale a 1114095 Bytes = 1 MiB + 64 KiB - 16 B
" Devido a limitações dos processadores 8086 que podiam trabalhar apenas com 20 bits de endereçamento (1MiB), endereços apartir de 100000 a 10FFEF não podiam ser acessados. Os novos processadores passaram a endereçar mais bits, e para manter compatibilidade com software anterior foi desenvolvido uma maneira de emular o 8086 desativando a 6º cifra, ignorando o valor '1' eram redirecionados a outros endereços, por exemplo o 10FFEF apontava para 0FFEF. A area acima de FFFFF (1 MiB) é chamada de HMA (high memory area). Em processadores X86-64 no "modo legado" ainda é possivel emular as caracteristicas do processador 8086, isso é chamado de vm86 (virtual mode 8086) permitindo o uso de endereçamento com 20 bits no modo protegido (32 bits) ,mas não são possiveis de executar no modo longo (64 bits). A quantidade real de memória endereçável pelos processadores 80286 e posteriores CPUs x86 em modo real é de 1 MiB + 64 KiB - 16 B = 1.114.096 B"
Vamos falar do segundo segmento.
O primeiro segmento 0000:0000 vai até 0000:FFFF ele pode conter 65536 bytes ou 64KiB.
Agora o segundo segmento 0001:0000.
Você deve estar imaginando duas caixas de 64 quilos uma sobre a outra.
Erro na verdade um segmento e formado a cada 16 bytes na memória.
Veja a tabela acima quando o deslocamento "offset" 0000 chegar a 000F temos 16 bytes e 0010(hexa) é igual a 17 Bytes é o lugar de onde partira o segundo segmento.
Para comprovar a idéia vamos pegar os dois endereços lógicos e calcular o endereço físico.
6
00000+0010=00010 ou Byte 17 00010+0000=00010 ou Byte 17 00000+000F=00010 ou Byte 17
Isto quer dizer que a partir de 16 bytes ou do offset 000F o segundo segmento vai sobrepor o primeiro.
O segmento 3,4,5 etc.. fazem a mesma coisa com seus antecessores.
Sabemos que o offset do primeiro segmento vai até FFFF , na sua calculadora científica selecione hexadecimal .
Calcule FFFF+1 vemos que e igual a 10000h isto quer dizer que o segmento 1000:0000 não sobrepõe o primeiro segmento.
Pois o primeiro byte do segmento 1000 que é igual ao endereço físico 0x10000 é 65537.
Em 1MB temos exatamente 16 endereços lógicos que formam blocos de 64k que não se sobrepõem uns aos outros.
1000:0000 2000:0000 3000:0000 4000:0000 5000:0000 ... etc
Os registradores de segmento de 16 bit são os seguintes :
%ds : Segmento de dados.
%cs : Segmento de código.
%ss : Segmento da pilha.
%es,%fs,%gs : Segmentos extra.
Antes de usar um registro de segmento é necessário passar por um acumulador ou registro de base.
Exemplo:
movw $0x2000,%bx movw %bx,%ss
Os redistradores de offset de 16 bit são os seguintes :
%sp (stack pointer - apontador de pilha): é utilizado em conjunto com %ss,para acessar a área de pilha na memória; aponta para o topo da pilha.
%bp (base pointer - apontador de base): é o ponteiro que, em conjunto com %ss,permite acesso de dados dentro do segmento de pilha.
%ip (instruction pointer - ponteiro de instrução): utilizado em conjunto com %cs para localizar a posição, dentro do segmento de código corrente,
da próxima instrução a ser executada. %ip é automaticamente incrementado em função do número de bytes da instrução executada.
%si (source index - índice fonte): usado como registrador índice em alguns modos de endereçamento indireto, em conjunto com %ds.
%di (destination index - índice destino): similar ao %si, atuando em conjunto com %es.
Modo real modelo flat
- Todos os registros de segmento apontam para o mesmo endereço que será o inicio de um bloco de 64k de memoria .
- O programa inicial inicializa os registros de segmento quando ele carrega o programa.
- O segmento físico não muda durante a execução do programa.
- Os registradores de segmento funcionam mais não são requeridos para nem uma operação.
- O registrador %ip aponta para a primeira instrução a ser executada pelo processador.
- A região de dados é apontada por um registrador de propósito general. Exemplo %ax,%bx
- O inicio da pilha é apontado pelo registrador %sp.
Modo real modelo segmentado
No modo segmentado cada registrador de segmento contem um endereço diferente é recomendado usar espaços de 64k.
Para percorrer o segmento usamos os registradores de offset.