Pascal for Experts
Acesso à Memória
Você está em: MarMSX >> Cursos >> Pascal
A memória do MSX pode ser livremente acessada pelo Pascal através de algumas instruções. Entretanto, o programador deverá estar atento ao fato de que a modificação de certas áreas de memória que estão sendo usadas pelo sistema ou programa poderão acarretar problemas.
Conceito de Heap, Stack e Área de Dados
Heap é a área de memória utilizada pelo Pascal para a alocação de variáveis dinâmicas. Os dados dos ponteiros são criados nessa região, através do comando new().
O stack é a área de pilha do programa em Pascal, utilizado nas chamadas de procedimentos funções e também para armazenar resultados intermediários na avaliação de expressões.
A área de dados é utilizada para armazenar as variáveis estáticas.
No TP 3.3.f, quando o programa é compilado, as áreas utilizadas são indicadas:
- Programa: &H2E9B até x
- Livre: x+1 até y-1 (total de 41886 - tam_dados bytes)
- Dados: y até &hD342 (Ele já usa 263 bytes)
O mapa de memória no DOS é o seguinte:
0000 ┌──────────┐
│ DOS │
2E9B ├──────────┤
│ Programa │
x ├──────────┤ +
│ Heap │ ↓
├──────────┤
│ Stack │ ↑
y ├──────────┤ +
│ Dados │
D342 └──────────┘
O heap cresce para baixo, enquanto que o stack cresce para cima.
As variáveis HeapPtr e StackPtr retornam o endereço do topo do heap e do stack, respectivamente. Assim, vamos criar um programa para retornar o endereço de cada um.
begin
writeln('Heap: ',HeapPtr);
writeln('Heap: ',StackPtr);
end.
Saída:
Heap: 12020
Stack: -11718
O heap vale &H2EF4 e o stack &HD23A
Ao compilar, temos:
Code: 88 bytes (2E9B-2EF3)
Free: 41798 bytes (2EF4-D23A)
Data: 263 bytes (D23B-D342)
Dessa forma, verificamos que o ponteiro do heap está no inicio da área livre, e o do stack no final da área livre.
Definição da localização de uma variável
A palavra reservada "absolute" permite determinar a localização na memória de uma variável. O endereço fornecido junto à declaração dessa variável é do primeiro byte ocupado por ela.
Exemplo:
var i : integer absolute $f000;
Faz:
┌────┐
F000 │ │ i
├────┤
F001 │ │
└────┘
Teste:
var i : integer absolute $f000;
e : real;
begin
e := addr(i); { Obtém o endereço de i }
if (e<0) then
e := 65536.0+e;
writeln('Endereco da variavel: ', e:5:0);
end.
Saída:
Endereco da variavel: 61440
Obs: 61440 é igual a &HF000.
Acesso à memória através do vetor pré-definido "mem"
O vetor "mem" acessa uma posição de memória, bastando para isso inserir como índice o endereço a ser lido ou modificado. O vetor "mem" é do tipo byte.
Exemplo:
mem[$f000] := CD; { Altera a posição &HF000 }
valor := mem[$a000]; { Lê dado da posição &HA000 }
Programa exemplo:
var i, e : integer;
begin
i := 112;
writeln('Valor de i: ',i);
e := addr(i); { Obtém o endereço de i }
mem[e] := 43;
writeln('Valor de i: ',i);
end.
Saída:
Valor de i: 112
Valor de i: 43
O programa faz:
I) II) e = x III)
┌────┐ ┌────┐
x │ 70 │ i x │ 2B │ mem[e]
├────┤ ├────┤
x+1 │ 00 │ x+1 │ 00 │
└────┘ └────┘
Alocando e desalocando memória - GetMem() e FreeMem()
O comando getmem(ponteiro, tamanho) aloca um espaço de memória no heap com n bytes.
Diferente do comando new(), que aloca um espaço de memória de acordo com o tamanho tipo de dado do ponteiro, o getmem() permite ao programador definir o tamanho do espaço alocado.
O ponteiro pode ser de qualquer tipo, e o tamanho é do tipo integer.
Exemplo:
var p : ^integer;
begin
getmem(p,2);
end.
Já o comando freemem(ponteiro, tamanho) libera o espaço de memória alocado por getmem(). O tamanho deverá ser exatamente igual ao declarado na alocação.
Exemplo:
var p : ^integer;
begin
getmem(p,2);
...
freemem(p,2);
end.
Movendo e preenchendo dados - Move() e FillChar()
O comando move(var1, var2, tamanho) copia um bloco contíguo de memória, com endereço de origem igual ao primeiro byte ocupado por var1 e endereço de destino igual ao primeiro byte ocupado por var2.
Exemplo:
const c : array[1..5] of byte = (1,2,3,4,5);
var v : array[1..5] of byte;
i : integer;
procedure imprime;
begin
write('c: ');
for i:=1 to 5 do
write(c[i],' ');
writeln;
write('v: ');
for i:=1 to 5 do
write(v[i],' ');
writeln;
end;
begin
writeln('Antes da copia:');
imprime;
writeln;
move(c, v, 5); { Copia de c para v }
writeln('Depois da copia:');
imprime;
end.
Saída:
Antes da copia:
c: 1 2 3 4 5
v: 109 101 116 101 114
Depois da copia:
c: 1 2 3 4 5
v: 1 2 3 4 5
O comando fillchar(var, tamanho, valor) preenche uma região de memória com um mesmo valor. No exemplo anterior, poderíamos utilizá-lo para zerar o vetor "v".
...
begin
fillchar(v, 5, 0);
...
Saída:
Antes da copia:
c: 1 2 3 4 5
v: 0 0 0 0 0
Depois da copia:
c: 1 2 3 4 5
v: 1 2 3 4 5
Referências:
[1] - Turbo Pascal - Reference Manual, 1988.