XIV.  

ALOKACE  DAT


Při spouštění je programu přidělen určitý paměťový prostor. Část tohoto prostoru se použije pro zavedení kódu a zbytek, rozdělený na datový segment, zásobníkový segment a heap, je rezervován pro alokaci vlastních dat programu. Turbo Pascal navíc implementuje i prostředky, pomocí nichž lze programu zpřístupnit ostatní části operační paměti a komunikační porty pro vstup a výstup dat.

Datový segment   V datovém segmentu jsou alokovány v pořadí svých deklarací veškeré typové konstanty programu — globální i lokální — a za nimi všechny globální statické proměnné, rovněž v pořadí svých deklarací. Počáteční hodnoty typových konstant jsou inicializovány pouze jednou, ihned po zavedení programu do operační paměti. Lokální typové konstanty jsou sice přístupné pouze ze svého deklaračního bloku (jako lokální proměnné), avšak existují a uchovávají své hodnoty po celou dobu činnosti programu (jako globální proměnné).

function Pocitadlo: Word;
{-----------------------}

 const Citac: Word = 0;

begin { Pocitadlo }
 Inc (Citac);
 Pocitadlo := Citac
end; { Pocitadlo }
Hodnota lokální typové konstanty Citac bude po startu programu inicializována nulou a při každém volání volání funkce Pocitadlo zvětšena o jedničku. Obecně bude tedy při n-tém volání funkce vrácena hodnota n.

Velikost datového segmentu je dána součtem velikostí jeho prvků, smí však dosáhnout maximálně 64 KB. Pokud je potřebný rozsah typových konstant a globálních statických proměnných programu větší, je nutno například některé z proměnných realizovat jako dynamické.

Zásobníkový segment   Na zásobníku jsou alokovány lokální proměnné momentálně aktivních podprogramů. Protože obecně nelze potřebnou velikost zásobníku dopředu odvodit, je nutno ji odhadnout a překladači explicitně zadat direktivou ve zdrojovém textu (viz požadavek na přidělení paměti). Většina programů pohodlně vystačí s implicitní velikostí zásobníkového segmentu (16 KB), zvýšené nároky plynou z přítomnosti jednak podpogramů s velkými lokálními proměnnými, jednak podprogramů s vysokou frekvencí rekurzivního volání. Maximální velikost je opět zhruba 64 KB.

Zásobník je zaplňován od dna, umístěného na nejvyšší adrese v přiděleném segmentu, směrem dolů, tj. k nižším adresám. Provádění kontroly přeplnění zásobníku lze nařídit resp. zakázat direktivou ve zdrojovém textu (viz kontrola přeplnění zásobníku). Při povolené kontrole reaguje program na přeplnění běhovou chybou, při zakázané kontrole je důsledkem přeplnění zpravidla zhroucení systému.

Heap   Na heapu jsou po dobu své existence alokovány dynamické proměnné. Jeho velikost lze spolu s velikostí zásobníku řídit direktivou ve zdrojovém textu. Maximální velikost heapu je na rozdíl od velikosti datového a zásobníkového segmentu omezena pouze volnou kapacitou konvenční operační paměti (do adresy $A0000, na níž začíná blok video RAM). Hodnotou 64 KB je omezena pouze maximální velikost jednotlivých dynamických proměnných (mechanismus přístupu k jednotlivým bytům proměnné vyžaduje konstantní segmentovou část adresy, měnit se může pouze ofset).

Absolutní proměnné   Uvedením direktivy absolute v deklaraci statické neinicializované proměnné (nikoliv typové konstanty) libovolného typu lze alokovat absolutní proměnnou. Při absolutní alokaci není pro proměnnou rezervována žádná operační paměť, program pouze přistupuje k úseku operační paměti na specifikované adrese jako k proměnné příslušného typu.

Obrázek 70: Direktiva absolute

Adresu absolutní proměnné lze v direktivě absolute specifikovat buď přímo, uvedením její segmentové a ofsetové části, nebo nepřímo, uvedením identifikátoru jiné (již deklarované) proměnné nebo typové konstanty. V prvém případě mají segment i ofset adresy tvar celočíselných konstantních výrazů, odděleny jsou dvojtečkou. Ve druhém případě je absolutní proměnná alokována na adresu počátku proměnné, uvedené v direktivě. Pokud je touto proměnnou odkazem volaný formální parametr podprogramu, je použita adresa skutečného parametru (získaná na zásobníku). Pokud je v direktivě uvedena lokální proměnná nebo hodnotový parametr podprogramu, je absolutní proměnná alokována na odpovídající adresu v zásobníku.


PŘÍKLAD: Procedura Vypln vyplní obrazovku znakem Znak s atributem Atr (srovnej s dříve uvedenou procedurou PisZnak).

procedure Vypln (Znak: Char; Atr: Byte);
{--------------------------------------}

 type VideoChar = record
                   Symbol  : Char;
                   Atribut : Byte
                  end;
      Obrazovka = array [0..24, 0..79] of VideoChar;

 var  Pozice    : Obrazovka absolute $B800:0;
      R, S      : Byte;

begin { Vypln }
 for R := 0 to 24 do                      { cyklus pro všechny řádky   }
  for S := 0 to 79 do                     { cyklus pro všechny sloupce }
   with Pozice[R, S] do                   { definice hodnoty slova     }
    begin
     Symbol  := Znak;
     Atribut := Atr
    end
end; { Vypln }

Absolutní proměnné lze také využít pro přístup k různým zajímavým údajům, které na pevně definovaných adresách v operační paměti udržuje operační systém. Přehled těchto údajů a příslušné adresy lze nalézt v různých příručkách, zabývajících se operačním systémem DOS. Za určitých (pro tento výklad nepodstatných) podmínek je například na adrese $40:$50 uložena ve dvou bytech aktuální poloha textového kurzoru na obrazovce — v nižším bytu číslo sloupce, ve vyšším číslo řádky — kterou používají nízkoúrovňové rutiny operačního systému. Tyto rutiny jsou volány i standardními vstupně-výstupními procedurami Turbo Pascalu (Write, Writeln, Read, Readln), takže následující deklarace proměnné Kurzor umožňuje programu jak detekci, tak přímé nastavení pozice kurzoru (sloupce jsou číslovány od nuly zleva, řádky od nuly shora):

var Kurzor: record
             Sloupec,
             Radek   : Byte
            end
            absolute $40:$50;


POZNÁMKA: Ve skutečnosti není změnou hodnoty proměnné Kurzor poloha kurzoru změněna přímo — toho lze dosáhnout jedině programováním videoadaptéru — změna se projeví až při zápisu na obrazovku.

Další způsob použití direktivy absolute tkví ve vícenásobném využití téhož paměťového úseku různými proměnnými, obvykle různých typů. Obsah tohoto úseku pak lze interpretovat více způsoby, aniž by bylo nutné použít typecastingu.

var S      : string;
    DelkaS : Byte absolute S;
Proměnná DelkaS je alokována na ten byte paměti, který byl přidělen prvnímu, tedy délkovému bytu proměnné S. Hodnota proměnné DelkaS je proto v každém okamžiku rovna aktuální délce řetězce S a zároveň aktuální délka řetězce je dána hodnotou proměnné DelkaS. Například následující příkazy pak mají stejný efekt (aktuální délka řetězce S jakož i hodnota proměnné DelkaS jsou každým z obou příkazů nastaveny na nulu):
S := '' <=> DelkaS := 0
Přímý přístup k operační paměti a portům   Turbo Pascal implementuje pole Mem, MemWMemL, pomocí nichž lze přímo číst obsah bytu, slova či dvojslova na zvolené adrese operační paměti resp. do nich zapisovat. Prvky pole Mem jsou byty, pole MemW slova a pole MemL dvojslova. Pro přístup k prvkům těchto polí je zavedena zvláštní syntaxe indexů — indexem prvku je dvojice celočíselných výrazů, oddělených dvojtečkou, ve významu segmentové a ofsetové části adresy prvku. Výsledkem operace přístupu k prvkům těchto polí jsou proměnné typu Byte, Word resp. LongInt. Například MemW [X:Y] je proměnná typu Word, alokovaná v operační paměti na adrese se segmentovou částí X a ofsetovou částí Y.

Pro přístup k portům jsou implementována pole PortPortW. Prvky pole Port jsou byty, pole PortW slova. Typem indexu prvku je Word. Přiřazením celočíselné hodnoty do prvku některého z polí je uvedená hodnota odeslána na port, jehož adresa je dána indexem. Naopak, uvedením prvku některého z těchto polí ve výrazu je celočíselná hodnota prvku získána čtením z příslušného portu.

Prvky polí PortPortW jsou považovány za proměnné typu Byte resp. Word pouze ve výše uvedených případech, nelze je například předávat jako odkazem volané parametry podprogramům. Není ani dovolen odkaz na kterékoliv z polí Mem, MemW, MemL, PortPortW jako celek.


PŘÍKLAD: Program KodyKlaves opakovaně čte byte z portu na adrese $60, k němuž je připojena klávesnice, a získanou hodnotu vypisuje na obrazovku. Hodnotou portu je identifikační kód posledního stisku resp. uvolnění nějaké klávesy (včetně přeřazovačů).

program KodyKlaves;
{=================}

 var ScanCode,                    { nová a původní hodnota kódu na     }
     OldCode   : Byte;            { portu $60                          }

begin { program }
 Writeln;                         { tisk titulku                       }
 Writeln('IDENTIFIKAČNÍ KÓDY STISKU A UVOLNĚNÍ KLÁVES');
 Writeln;
 Writeln('(stiskni nějakou klávesu, ESC = konec)');
 Writeln;
 OldCode := Port[$60];            { inicializace původní hodnoty kódu  }
 repeat
  repeat                          { čekání na výskyt nové hodnoty kódu }
   ScanCode := Port[$60]
  until ScanCode <> OldCode;
  Write(ScanCode:3, #13);         { výpis nového kódu a návrat kurzoru }
                                  { na začátek téže řádky na obrazovce }
  OldCode := ScanCode
 until ScanCode = 129;            { konec při uvolnění klávesy ESC     }
 Writeln
end. { program }

Čtení identifikačního kódu klávesy z portu nemá žádný vliv na činnost rutiny operačního systému, která jej zpracovává (generuje příslušný znak).