Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

xalloc: an Expanded Memory Manager


OCT91: XALLOC: AN EXPANDED MEMORY MANAGER

Herb is a professor of economics at the University of Massachusetts, Thompson Hall, Amberst, MA 01003.


This article describes a Turbo Pascal 5.5 unit that implements the expanded memory equivalent of the Turbo Pascal dynamic memory functions getmem, freemen, memavail, and maxavail. The new functions -- xgetmem, xfreemem, xmemavail, and xmaxavail -- mirror their main memory counterparts as much as possible (see Table 1). However, the peculiarities of expanded memory do require some special treatment.

Table 1: The Xalloc unit interface section

  Function                        Description
  ------------------------------------------------------------------------
  type                            An address in memory is a pointer, but
     xaddress = record             in expanded memory, it must hold the
     page : byte                   expanded memory page (page) and the
     offset : word                 offset from the start of the page
     end                           (offset).  We call this an xaddress.

  const                           xgetmem returns this page in an
     nilpage = $ff;                xaddress to indicate that the request
                                   was denied.

  function xalloc_init: boolean;  Initializes the expanded memory
                                   manager.  Must be called before any
                                   other routine in the unit.  Returns
                                   true if it's okay to use the model,
                                   and false otherwise.

  procedure xalloc_done;          Releases all expanded memory.  Invoked
                                   as part of a program's exit procedure.

  procedure xgetmem (var x:       Requests a block of size bytes for
     xaddress; size: word);        variable x.  The page and offset are
                                   returned in x.  Returns nilpage if
                                   denied.

  procedure xfreemem (var x:      Returns the block of expanded memory to
     xaddress; size : word);       the pool.

  function page_in (var x:        Returns the address in memory of the
     xaddress): pointer;           block of expanded memory owned by
                                   x.  Since this function also moves the
                                   page to main memory, it must be
                                   called each time the variable x is
                                   accessed, if there is a chance it
                                   was paged out of memory.

  function xmaxavail:             Returns the maximum block size
     longint;                      available.

  function xmemavail:             Returns the total amount of expanded
     longint;                      memory available.

Expanded memory consists of pages, and a variable in expanded memory is identified by its page and the offset of the variable's storage in the page. We represent an expanded memory location by an xaddress record, consisting of a page and an offset; and xgetmem returns such an xaddress to the calling program. To access a variable in expanded memory, we must move the page of expanded memory into main memory and tell the calling program its location in main memory. This is accomplished by page_in, which takes the xaddress as an argument and returns the main memory address as a pointer. At the same time, it shuffles the proper page into main memory if it isn't already there.

It's very important to call page_in every time an expanded memory location or page is addressed, because some other routine may have shuffled it back out (through an interrupt, for example). The best way to ensure this is by building expanded memory management into the objects that use xalloc. For example, see the string-type object xline created in Listing One (page 121). The xalloc unit (see Listing Two , page 121) behaves in a manner almost identical to Turbo Pascal's memory manager. When a block of memory is requested by xgetmem, and if there are no "holes" in the expanded memory the unit controls, the block is taken from the heap -- the unused memory at the top of a page. If there is not enough room on the heap (there's actually a different heap for each page), a new page is allocated. When a block is deallocated by xfreemem, there are two possibilities: If the block is at the end of the heap, the heap is simply expanded; if the block is in the middle of an allocated block of memory, it is placed on a list of free memory "holes". Before checking the heap, xgetmem checks this array for a suitable block. If it finds one, the list of free memory holes is adjusted accordingly.

There are clear ways to improve the unit. One improvement is to switch automatically to main memory or disk, if expanded memory is not implemented or is exhausted. Another is to improve garbage collection and memory compaction.


_XALLOC: AN EXPANDED MEMORY MANAGER FOR TURBO PASCAL_
by Herbert Gintis


[LISTING ONE]
<a name="0237_0006">

unit xlineobj;

{ Typical use:
        program xtest;
        uses xalloc,xlineobj;
        var
            s : xline;
        begin
            if not xalloc_init then halt;
            s.init;
            s.put_text('This goes into expanded memory');
            writeln(s.get_text);
            s.done;
            xalloc_done;
        end.
}
interface

uses xalloc;

type
    xline = object
        len : byte;
        mem : xaddress;
        constructor init;
        destructor done; virtual;
        procedure newsize(ncols : integer);
        function get_text : string;
        procedure put_text(s : string);
    end;

implementation

var
    xs : ^string;

constructor xline.init;
const
    mincols = 8;
begin
    xgetmem(mem,mincols);
    len := mincols-1;
    xs := xpage_in(mem);
    xs^ := '';
end;

destructor xline.done;
begin
    xfreemem(mem,len+1);
end;

procedure xline.newsize(ncols : integer);
begin
    xfreemem(mem,len+1);
    xgetmem(mem,ncols+1);
    xs := xpage_in(mem);
    len := ncols;
end;

function xline.get_text : string;
begin
    xs := xpage_in(mem);
    get_text := xs^;
end;

procedure xline.put_text(s : string);
begin
    if length(s) <> len then newsize(length(s));
    xs := xpage_in(mem);
    xs^ := s;
end;

end.




<a name="0237_0007">
<a name="0237_0008">
[LISTING TWO]
<a name="0237_0008">

unit xalloc;
    {-See the unit xlineobj.pas for typical use of this unit}
interface

const
    nilpage = $ff;
type
    xaddress = record
        page : byte;
        pos : word;
    end;
function xalloc_init : boolean;
procedure xgetmem(var x : xaddress;size : word);
procedure xfreemem(var x : xaddress;size : word);
function xpage_in(var x : xaddress) : pointer;
function xmaxavail : longint;
function xmemavail : longint;
procedure xalloc_done;

implementation

uses crt,dos;

const
    emm_int = $67;
    dos_int = $21;
    maxfreeblock = 4000;
    xblocksize = $4000;
    _get_frame = $41;
    _unalloc_count = $42;
    _alloc_pages = $43;
    _map_page = $44;
    _dealloc_pages = $45;
    _change_alloc = $51;
type
    xheap = array[0..1000] of word;
    fblock = record
        page : byte;
        start,stop : word;
    end;
    fblockarray = array[1..maxfreeblock] of fblock;
var
    regs : registers;
    handle,tot_pages : word;
    xheapptr : ^xheap;
    xfreeptr : ^fblockarray;
    last_page,lastptr : integer;
    map : array[0..3] of integer;
    frame : word;

function ems_installed : boolean;
const
    device_name : string[8] = 'EMMXXXX0';
var
    i : integer;
begin
    ems_installed := false;
    with regs do begin  {check for ems present}
        ah := $35;  {get code segment pointed to by interrupt 67h}
        al := emm_int;
        intr(dos_int,regs);
    for i := 1 to 8 do if device_name[i] <> chr(mem[es : i + 9]) then exit;
    end;
    ems_installed := true;
end;

function unalloc_count(var available : word): boolean;
begin
    with regs do begin
        ah := _unalloc_count;
        intr(emm_int,regs);
        available := bx;
        unalloc_count := ah = 0  {return the error code}
    end;
end;

function alloc_pages(needed: integer): boolean;
begin
    with regs do begin
        ah := _alloc_pages;
        bx := needed;
        intr(emm_int,regs);
        handle := dx;
        alloc_pages := (ah = 0);        {return the error code}
    end;
end;

function xdealloc_pages: boolean;
begin
    with regs do begin
        ah := _dealloc_pages;
        dx := handle;
        intr(emm_int,regs);
        xdealloc_pages := (ah = 0);  {return the error code}
    end;
end;


function change_alloc(needed : integer) : boolean;
begin
    with regs do begin
        ah := _change_alloc;
        bx := needed;
        dx := handle;
        intr(emm_int,regs);
        change_alloc := (ah = 0);    {return the error code}
    end;
end;

function xmap_page(l_page,p_page: integer): boolean;
begin
    xmap_page := true;
    if map[p_page] <> l_page then with regs do begin
        ah := _map_page;
        al := p_page;
        bx := l_page;
        dx := handle;
        intr(emm_int,regs);
        xmap_page := (ah = 0);
        if ah = 0 then map[p_page] := l_page;
    end;
end;

function xpage_in(var x : xaddress) : pointer;
begin
    if xmap_page(x.page,0) then xpage_in := ptr(frame,x.pos)
    else xpage_in := nil;
end;

function xget_frame(var frame: word): boolean;
begin
    with regs do begin
        ah := _get_frame;
        intr(emm_int,regs);
        frame := bx;
        xget_frame := (ah = 0); {return the error code}
    end;
end;

procedure xgetmem(var x : xaddress;size : word);
var
    i : integer;
begin
    for i := 1 to lastptr do begin
        with xfreeptr^[i] do begin
            if size <= stop - start then begin
                x.page := page;
                x.pos := start;
                inc(start,size);
                if start = stop then begin
                    xfreeptr^[i] := xfreeptr^[lastptr];
                    dec(lastptr);
                end;
                exit;
            end;
        end;
    end;
    x.page := nilpage;
    i := 0;
    repeat
        inc(i);
        if i > tot_pages then exit;
        if i > last_page then begin
            inc(last_page);
            if not change_alloc(last_page) then exit;
        end;
    until xblocksize - xheapptr^[pred(i)] > size;
    with x do begin
        page := pred(i);
        pos := xheapptr^[page];
        inc(xheapptr^[page],size);
    end;
end;

procedure xfreemem(var x : xaddress;size : word);
var
    i,xstop : integer;
begin
    xstop := x.pos + size;
    i := 0;
    while i < lastptr do begin
        inc(i);
        with xfreeptr^[i] do begin
            if x.page = page then begin
                if x.pos >= start then begin
                    if x.pos <= stop then begin
                    x.pos := start;
                    if xstop < stop then xstop := stop;
                    xfreeptr^[i] := xfreeptr^[lastptr];
                    dec(lastptr);
                    dec(i)
                    end;
                end
                else if xstop >= start then begin
                    if xstop < stop then xstop := stop;
                    xfreeptr^[i] := xfreeptr^[lastptr];
                    dec(lastptr);
                    dec(i)
                end;
            end;
        end;
    end;
    if lastptr > 0 then with xfreeptr^[lastptr] do
        if start = stop then dec(lastptr);
    if x.pos < xstop then begin
        if xstop = xheapptr^[x.page] then xheapptr^[x.page] := x.pos
        else begin
            if lastptr < maxfreeblock then begin
                inc(lastptr);
                with xfreeptr^[lastptr] do begin
                    page := x.page;
                    start := x.pos;
                    stop := xstop;
                end;
            end;
        end;
    end;
end;

function xmemavail : longint;
var
    s : longint;
    i : integer;
begin
    s := 0;
    for i := 0 to pred(tot_pages) do inc(s,$4000 - xheapptr^[i]);
    for i := 1 to lastptr do with xfreeptr^[i] do inc(s,stop - start);
    xmemavail := s;
end;

function xmaxavail : longint;
var
    s : longint;
    i : integer;
begin
    s := 0;
    for i := 0 to pred(tot_pages) do
        if $4000 - xheapptr^[i] > s then s := $4000 - xheapptr^[i];
    for i := 1 to lastptr do with xfreeptr^[i] do
        if stop - start > s then s := stop - start;
    xmaxavail := s;
end;

procedure xalloc_done;
begin
    if not xdealloc_pages then;
end;

function xalloc_init : boolean;
var
    i : word;
begin
    xalloc_init := false;
    if not ems_installed then exit;
    if not unalloc_count(tot_pages) then exit;
    if tot_pages = 0 then exit;
    if not xget_frame(frame) then exit;
    getmem(xheapptr,tot_pages*sizeof(word));
    if xheapptr = nil then exit;
    new(xfreeptr);
    if xfreeptr = nil then exit;
    for i := 0 to pred(tot_pages) do xheapptr^[i] := 0;
    if not alloc_pages(1) then exit;
    xalloc_init := true;
    lastptr := 0;
    last_page := 1;
    for i := 0 to 3 do map[i] := -1;
end;

end.


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.