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:
  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.


<< Anterior Pascal Próxima >>