Tutorial de Assembler de
Adam Hyde 1.0 PARTE 2 |
Versão : 1.2
Data : 17-02-1996 / online by Renato 01-11-1998
Contato : blackcat@vale.faroc.com.au
http://www.faroc.com.au/~blackcat
;Renato :
rnbastos@ig.com.br
http://www.geocities.com/SiliconValley/Park/3174
Segmentos e Offsets | Uma Arquitetura Segmentada | A Pilha |
Olá de novo, futuros proramadores de Assembler. Para aqueles que perderam a primeira parte, pegue-a agora na minha homepage.
De qualquer modo, no último número eu disse que estaria discutindo sobre hexadecimal, segmentos + offsets, mais algumas intruções e algumas procedures contendo assembler que você poderia realmente usar.
Então, lá vamos nós, com segmentos e offsets!
Antes de explorarmos o grande e mau mundo dos segmentos e offsets, há umas terminologias que você precisar conhecer.
EX.: 0000 = 0
0100 = 4 1000 = 8 1100 = 12 10000 =
16
0001 = 1 0101 = 5 1001 = 9 1101 = 13
...Acho que você
0010 = 2 0110 = 6 1010 = 10 1110 = 14 já
sacou...
0011 = 3 0111 = 7 1011 = 11 1111 = 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
Hexadecimal é na verdade muito fácil de se usar, e, apenas como curiosidade, eu acho que os Babilônios - uma civilização antiga qualquer - usava um sistema de numeração em BASE 16. Tem algum historiador aí fora que queira confirmar isso?
IMPORTANTE >>> Um nibble pode aguentar um valor até Fh <<< IMPORTANTE
Um byte tem um valor máximo de 255 em decimal, 11111111 em binário, ou FFh em hexadecimal.
Obs.: Por causa de uma word ser quatro nibbles, é também representada por quatro dígitos hexadecimais.
Obs.: Isto é um número de 16 bits, e corresponde aos registradores de 16 bits. Ou seja, AX, BX, CX, DX, DI, SI, BP, SP, DS, ES, SS e IP.
Uma DWORD pode armazenar de 0 a 4,294,967,295, que é FFFFFFFFh, ou 11111111111111111111111111111111. Espero que haja 32 um's lá atrás.
A DWORD também é o tamanho dos registradores extendiddos de 32 BITS, ou seja, EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP e EIP.
Agora que já cobrimos a terminologia, vamos dar uma olhada mais de perto como aqueles registradores são estruturados. Nós dissemos que AL e AH eram registradores de 8 bits, logo, eles não deveriam se parecer com algo assim?
AH | AL | ||||||||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Neste caso, ambos AH e AL = 0, OU 00h e 00h. Como resultado, para calcular AX
usamos: AX = 00h + 00h. Quando digo + eu quero dizer, 'ponha junto' não AX = AH
MAIS AL.
Assim, se AH era igual a 00000011 e AL era igual a 0000100, para calcular AX nós devemos fazer o seguinte.
1) Pegue os valores hexadecimais de AH e AL.
00000011 = 03h 00010000 = 10h
2) Combine-os.
AX = AH + AL
AX = 03h + 10h
AX = 0310h
E aí você consegue o resultado. Não é tão macetoso assim.
Okay, agora vamos ver os registradores de 16 bits:
AX | |||||||||||||||
AH | AL | ||||||||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
De onde podemos ver que AX = 00000000 e 00000000, ou 0000000000000000.
Agora por último, vejamos como um registrador de 32 bits se parece:
EAX | |||||||||||||||||||||||||||||||
AX | |||||||||||||||||||||||||||||||
AH | AL | ||||||||||||||||||||||||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
(by Renato - essa tabela estava errada na versão original, colocando o AX na parte mais significtiva de EAX. Eu tomei o direito de consertar. Se estiver errado - era mesmo na parte mais significativa? - me avisem)
Não é muito difícil, espero. E se entendeu isso, você está pronto para SEGMENTOS e OFFSETS.
Há muito, muito tempo atrás, quando a IBM construiu o primeiro PC, não era costume programas terem mais de 1 megabyte - eca, os primeiros XT's tinham apenas 64K de RAM! De qualquer modo, vendo que os projetistas do XT não consideravam aplicações enormes, decidiram dividir a memória em SEGMENTOS, pequenas áreas de mem¢ria RAM que você pode colocar APENAS uma tela virtual para gráficos em modo 320x200x256.
É claro, você pode acessar mais de um megabyte de RAM, mas você tem que dividi-la em segmentos para usá-la, e esse é o problema. É obvio, com programação em 32 bits dé pra acessas até 4GB de RAM sem usar segmentos, mas isso é uma outra história.
Segmentos e offsets são apenas um método de especificar uma posição na memória.
EG: 3CE5:502A
^^^^ ^^^^
SEG OFS
Okay, aqui está a especificação:
Um OFFSET = SEGMENT X 16
Um SEGMENT = OFFSET / 16
Alguns registradores de segmento são:
CS, DS, ES, SS e FS, GF - Obs.: Os últimos 2 são registradores que só existem em 386 ou superiores.
Alguns registradores de offset são:
BX, DI, SI, BP, SP, IP - Obs.: Quando em modo protegido, você
pode usar qualquer registrador de uso geral como
um registrador de offset - EXCETO IP.
Alguns segmentos e offsets comuns são:
CS:IP - Endereço do código executando no momento.
SS:SP - Endereço da posição atual da pilha.
OBS.: NÃO SE INTROMETA COM ELES !
Assim quando nos referirmos a segmentos e offsets, faremos dessa forma:
SEGMENTO:OFFSET
Um bom exemplo seria:
A000:0000 - que na verdade corresponde ao topo esquerdo da tela VGA em modo colorido 320x200x256.
** FATO ENGRAÇADO ** A RAM da VGA começa em A000h :)
Ufa! Isso foi muito para o segundo tutorial. Contudo, ainda não terminamos.
Esse negócio de AX, AH, AL é um conceito que você pode não ter sacado ainda, então
lá vamos nós:
MOV AX, 0 ; AX = 0
MOV AL, 0 ; AL = 0
MOV AH, 0 ; AH = 0
MOV AL, FFh ; AL = FFh
; AX = 00FFh
; AH = 00h
INC AX ; AX = AX + 1
; AX = 0100h
; AH = 01h
; AL = 00h
MOV AH, ABh ; AX = AB00h
; AH = ABh
; AL = 00h
Pegou?
COISAS A FAZER:
1) Aprender aquele negócio de BIT/NIBBLE/BYTE... de cor.
2) Voltar nos exemplos de segmento e offset.
3) Tenha certeza que você entendeu a relação entre AX, AH e AL.
4) Que tal um problemas de adição hexadecimal?
A pilha é uma característica muito útil de que podemos tirar vantagem. Pense nela como uma pilha de papéis numa bandeja de ENTRADA. Se você põe algo no topo, ela será a primeira a ser tirada.
À medida que você adiciona algo à pilha, o apontador de pilha é DECREMENTADO, e quando tira, é INCREMENTADO. Para explicar isso melhor, veja o diagrama abaixo:
A PILHA |
SP |
<<<<< Quando colocamos um byte na pilha, ele vai aqui - último a
entrar, primeiro a sair.
<<<<<< O ponteiro de pilha se move para baixo.
E na prática:
MOV AX, 03h ; AX = 03h
PUSH AX ; PUSH AX na pilha
(coloca no topo)
MOV AX, 04Eh ; AX = 04Eh
; Faça alguma coisa... uma soma?
POP AX ; AX = 03h
Ou:
MOV AX, 03h ; AX = 03h
PUSH AX ; Adiciona AX à
pilha
MOV AX, 04Eh ; AX = 04Eh
; Faça alguma coisa... uma soma?
POP BX ; BX = 03h
Você acabou de aprender duas instruções:
É tudo o que você precisa de aprender sobre pilha - por enquanto.
Por último, algumas procedures que demonstram algo disso tudo. Note que os
comentários foram DELIBERADAMENTE REMOVIDOS. É seu dever tentar comentá-los. Note
também, que algumas novas instruções são introduzidas.
Procedure ClearScreen(A : Byte; Ch : Char); Assembler;
Asm { ClearScreen }
mov ax, 0B800h
mov es, ax
xor di, di
mov cx, 2000
mov ah, A
mov al, &Ch
rep stosw
End; { ClearScreen }
Procedure CursorXY(X, Y : Word); Assembler;
Asm { CursorXY }
mov ax, Y
mov dh, al
dec dh
mov ax, X
mov dl, al
dec dl
mov ah, 2
xor bh, bh
int 10h
End; { CursorXY }
Procedure PutPixel(X, Y : Integer; C : Byte; Adr : Word); Assembler;
Asm { PutPixel }
mov ax, [Adr]
mov es, ax
mov bx, [X]
mov dx, [Y]
xchg dh, dl
mov al, [C]
mov di, dx
shr di, 2
add di, dx
add di, bx
stosb
End; { PutPixel }
Procedure Delay(ms : Word); Assembler;
Asm { Delay }
mov ax, 1000
mul ms
mov cx, dx
mov dx, ax
mov ah, 86h
int 15h
End; { Delay }
COISAS A FAZER:
1) Vá ao exemplo de pilha. Faça seu próprio código exemplo.
2) Comente as procedures acima do melhor modo que puder. Tente adivinhar o que as
novas intruções fazem. Não é tão difícil.
NA PRÓXIMA SEMANA
Se você deseja ver um tópico discutido num tutorial no futuro, escreva-me, e eu vou ver o que eu posso fazer.
Não perca!!! Baixe o tutorial da próxima semana na minha homepage:
- Adam Hyde.
- Renato Nunes Bastos.