Unit Memory;
{
  **************************************************************************
  ***   Memory - Unit  of  PC Underground by  DATA BECKER/ABACUS         ***
  ***   Author      : Boris Bertelsons                                   ***
  ***   Created     : 01/26/94                                           ***
  ***   Last update : 03/18/94                                           ***
  *** ------------------------------------------------------------------ ***
  ***  The unit provides routines for dealing with memory.               ***
  ***  In particular, this unit contains routines for handling           ***
  ***  XMS and EMS !                                                     ***
  **************************************************************************
}

Interface

uses dos;

TYPE XMSHandle = word;

     EMSHandle = word;

     XMS_Copyblock = Record     { required for the copy routines }
       Size     : longint;
       Q_Handle : Word;
       Q_Offset : pointer;
       Z_Handle : Word;
       Z_Offset : pointer;
     end;

     EMS_Header = Record        { for recognition of EMS }
       dummy   : array[0..9] of byte;
       ID : array[1..7] of char;
     end;

VAR XMS_Available   : boolean;  { TRUE, if XMS is available }
    XMST            : pointer;  { Driver - Entry point address }
    XMS_Version     : word;     { Version of XMS driver }
    XC              : XMS_Copyblock;
    EMS_Available   : boolean;  { TRUE, if  EMS is available }
    EMS_Version     : word;     { Number of EMS version. Vers.MAJ
                                              is in the Hi-Byte and VERS.MIN is in the
                                  Lo-Byte ! }
    EMS_Pages_Free : word;     { Number of free EMS pages }
    EMS_Pages_Tot : word;      { Total number of EMS pages
                                  available }


function  base_free : longint;
{
  The function returns the size of the maximum total of available  
  main memory in bytes
}

function  XMS_free  : longint;
{
  The function returns the size of the maximum total of available
  XMS memory in bytes
}

Function Getmem_XMS(VAR H : XMSHandle; Size : longint) : byte;
{
 The function allocates a block in XMS that is Size Bytes large. Size   
 is rounded off to the nearest kilobyte limit. The number of the handle
 by which the block can be addressed is returned in H and cannot be    
 lost, because otherwise the block can only be addressed by a reset. If 
 the function was able to allocate the memory, it returns the value 0,   
 otherwise the error table shown in the book applies.
}

Function Freemem_XMS(H : XMSHandle) : byte;
{
 The function frees up a memory area in XMS that was allocated by GETMEM_XMS.        
 The meaning of the function result can be found in the XMS error table.
}

Function XMS_2_XMS(h1,h2 : XMSHandle; Size : Word) : byte;
{
 This function copies the number of bytes passed in Size in XMS from h1 to h2.
 Size must be an EVEN value. The meaning of the function result can be 
 found in the XMS error table.
}

Function RAM_2_XMS(q : pointer; h : XMSHandle; Size : Word) : byte;
{
 This function copies data from RAM to XMS. q is a pointer to the
 source data in RAM. h is the handle that you got from the GETMEM_XMS
 function. Size is the size of the block to be copied in bytes. Here again, Size
 is rounded off to the nearest kilobyte and you can check the XMS error table
 for the meaning of the function result.
}

Function XMS_2_Ram(d : pointer; h : XMSHandle; Size : Word) : byte;
{
 This function copies data from XMS to RAM. d is a pointer to the 
 destination in RAM. h is the handle that you got from the GETMEM_XMS
 function. Size is the size of the block to be copied in bytes. Once again, 
 Size is rounded off to the nearest kilobyte and you can check the XMS 
 error table for the meaning of the function result.
}

Procedure Check_for_XMS;
{
 The procedure checks whether XMS is available, and initializes the
 variables required by the unit. XMS_Available is set to TRUE when an 
 XMS driver is available. You will find the version number of the driver 
 in XMS_Version.
}

procedure Check_for_EMS;
{
 This procedure checks whether EMS is available and initializes 
 the corresponding variables
}

Function EMS_free  : longint;
{
 The function returns the size of the free EMS memory in bytes.
}

Function EMS_Segment_determine(VAR Segment : word) : byte;
{
 This function determines the segment where EMS starts being  
 overlaid into RAM.
}

Function EMS_Get_PageNumber : byte;
{
 This function determines the total number of available pages in EMS
 as well as how many pages are still free. The values are placed in
 the global variables "EMS_Pages_Tot" and "EMS_Pages_Free".
}

Function Getmem_EMS(VAR H : EMSHandle; Size : longint) : byte;
{
 This function allocates the specified amount of memory in EMS. The
 memory can be addressed via the "H" handle. Please keep in mind
 that the function allocates at least one page, that is, 16K in EMS.
 That means that you should only page larger data structures 
 in EMS.
}

Function Freemem_EMS(H : EMSHandle) : byte;
{
 This function frees up memory allocated by Getmem_EMS.
}

Function EMS_Allocation(H : EMSHandle;PagePage,EMSPage : word) : byte;
{
 Use this function to determine the allocation of EMS pages for the
 corresponding handle. PagePage can hold a value from 0 to 3, and 
 stands for the page position at whic it is overlaid in RAM. EMSPage
 is the page in EMS that is to be overlaid. Thus, if you want to assign 
 page 7 of EMS to the EMSH handle (important for blocks > 64K !),
 you need to call the function with the parameters (EMSH,0,7).
}

Function EMS_Protect_Allocation(H : EMSHandle) : byte;
{
 This function protects the order of the EMS page set by EMS_Allocation
 for the specified handle from changes.
}

Function EMS_Unprotect_Allocation(H : EMSHandle) : byte;
{
 A handle protected by EMS_Protect_Allocation must first be unprotected 
 with this function before the allocation can be changed.
}

Function RAM_2_EMS(q : pointer; H : EMSHandle; Size : longint) : byte;
{
 Use this function to copy the specified block from RAM to EMS. Size
 refers to the size in bytes, q stands for a pointer to the source area and
 H is the handle from Getmem_EMS.
}

Function EMS_2_RAM(q : pointer; H : EMSHandle; Size : longint) : byte;
{
 Similar to RAM_2_EMS, this function copies a memory area from 
 RAM to EMS.
}

Function EMS_Handles_assign(Var Number : word) : byte;
{
 This function gives you the number of EMS handles that  have already
 been assigned. A maximum of 256 handles can be assigned.
}

function XMS_lock(H : XMSHandle) : longint;
{
 The function locks an XMS block from being moved and returns
 its absolute address
}

procedure XMS_unlock(H : XMSHandle);
{
 The procedure unlocks an XMS handle that has been locked from
 being moved.
}


implementation

function base_free : longint;
begin;
  base_free := MemAvail;
end;

function XMS_free  : longint;
var xms_in_kb : word;
    xms_long: longint;
begin;
  asm
    mov ax,0800h                { 8 = Get free memory }
    call dword ptr [XMST]
    mov xms_in_kb,dx
  end;
  xms_long := xms_in_kb;
  XMS_free := xms_long * 1024;
end;

Function Getmem_XMS(VAR H : XMSHandle; Size : longint) : byte;
var bsize : word;
    Fresult : byte;
    xmsh : word;
begin;
  bsize := (size DIV 1024) + 1;
  asm
    mov ax,0900h                { 9 = allocate memory area }
    mov dx,bsize
    call dword ptr [XMST]
    cmp ax,1
    jne @Error_GetmemXms
    mov xmsh,dx
    mov Fresult,0
    jmp @End_GetmemXms
@Error_GetmemXMS:
    mov Fresult,bl
@End_GetmemXms:
  end;
  h := xmsh;
  Getmem_Xms := Fresult;
end;

Function Freemem_XMS(H : XMSHandle) : byte;
var fresult : byte;
begin;
  asm                           { A = deallocate memory area }
    mov ax,0a00h
    mov dx,h
    call dword ptr [XMST]
    cmp ax,1
    jne @Error_FreememXms
    mov Fresult,0
    jmp @End_FreememXms
@Error_FreememXms:
    mov Fresult,bl
@End_FreememXms:
  end;
end;

Function XMS_2_XMS(h1,h2 : XMSHandle; Size : Word) : byte;
VAR fresult : byte;
begin;
  XC.Size     := Size;          { size of block in bytes }
  XC.Q_Handle := h1;            { source handle }
  XC.Q_Offset := nil;           { source offset, 0 = start of block }
  XC.Z_Handle := h2;            { destination handle }
  XC.Z_Offset := nil;           { destination offset }
  asm
    mov si,offset XC
    mov ax,0B00h
    call dword ptr [XMST]
    cmp ax,1
    jne @Error_RAM2XMS
    mov fresult,0
    jmp @End_Ram2XMS
@Error_Ram2XMS:
    mov fresult,bl
@End_Ram2XMS:
  end;
end;

Function RAM_2_XMS(q : pointer; h : XMSHandle; Size : Word) : byte;
VAR fresult : byte;
begin;
  XC.Size     := Size;
  XC.Q_Handle := 0;               { 0 = RAM }
  XC.Q_Offset := q;
  XC.Z_Handle := h;
  XC.Z_Offset := nil;
  asm
    mov si,offset XC
    mov ax,0B00h
    call dword ptr [XMST]
    cmp ax,1
    jne @Error_RAM2XMS
    mov fresult,0
    jmp @End_Ram2XMS
@Error_Ram2XMS:
    mov fresult,bl
@End_Ram2XMS:
  end;
end;

Function XMS_2_Ram(d : pointer; h : XMSHandle; Size : Word) : byte;
VAR fresult : byte;
begin;
  XC.Size     := Size;
  XC.Q_Handle := h;
  XC.Q_Offset := nil;
  XC.Z_Handle := 0;               { 0 = RAM }
  XC.Z_Offset := d;
  asm
    mov si,offset XC
    mov ax,0B00h
    call dword ptr [XMST]
    cmp ax,1
    jne @Error_XMS2RAM
    mov fresult,0
    jmp @End_XMS2Ram
@Error_XMS2Ram:
    mov fresult,bl
@End_XMS2Ram:
  end;
end;

Procedure Check_for_XMS; assembler;
  asm
    mov ax,4300h              { check whether driver installed }
    int 2Fh
    cmp al,80h
    jne @No_XMSdriver
    mov ax,4310h              { get entry point address of driver }
    int 2Fh
    mov word ptr XMST + 2,es
    mov word ptr XMST + 0,bx
    xor ax,ax                 { get version numb }
    call dword ptr [XMST]
    cmp  ax,0200h
    jb   @No_XMSdriver     { if version < 2.0 then cancel ! }
    mov  XMS_Version,ax
    mov  XMS_Available,0
@No_XMSdriver:
    mov XMS_Available,1
@End_XMS_Check:
end;

procedure Check_for_EMS;
var emsseg : word;
    emsptr : pointer;
    emshead : EMS_Header;
begin;
  asm
    mov ax,3567h
    int 21h
    mov emsseg,es
  end;
  move(ptr(emsseg,0)^,emshead,17);
  if emshead.ID = 'EMMXXXX' then begin;
    EMS_Available := true;
    asm
      mov ah,40h                 { get EMS driver status }
      int 67h
      cmp ah,0
      jne @EMS_Vers_Error
      mov ah,46h                 { get EMS version }
      int 67h
      cmp ah,0
      jne @EMS_Vers_Error
      mov bl,al
      shr al,4
      mov bh,al                 { bh = Vers.maj }
      or  bl,0Fh                { bl = Vers.min }
      mov EMS_Version,bx
      jmp @EMS_Vers_End
@EMS_Vers_Error:
      mov EMS_Available,1
@EMS_Vers_End:
    end;
  end else begin;
    EMS_Available := false;
  end;
end;

Function EMS_Segment_determine(VAR Segment : word) : byte;
VAR hseg : word;
    fresult : byte;
begin;
  asm
    mov ah,41h
    int 67h
    cmp ah,0
    jne @EMS_Segdet_Error
    mov hseg,bx
    mov fresult,0
    jmp @EMS_Segdet_End
@EMS_Segdet_Error:
    mov fresult,ah
@EMS_Segdet_End:
  end;
  Segment := hseg;
  EMS_Segment_determine := fresult;
end;

Function EMS_Get_PageNumber : byte;
var fresult : byte;
begin;
  asm
    mov ah,42h
    int 67h
    cmp ah,0
    jne @EMS_GetPages_Error
    mov EMS_Pages_Free,bx
    mov EMS_Pages_Tot,dx
    mov fresult,0
    jmp @EMS_GetPages_End
@EMS_GetPages_Error:
    mov fresult,ah
@EMS_GetPages_End:
  end;
  EMS_Get_PageNumber := fresult;
end;

function EMS_free  : longint;
var    help : longint;
begin;
  EMS_Get_PageNumber;
  help := EMS_Pages_Free;
  EMS_free := help SHL 14;
end;

Function Getmem_EMS(VAR H : EMSHandle; Size : longint) : byte;
var Fresult : byte;
    EPages : word;
    Hhandle : word;
begin;
  EPages := (Size DIV 16384) + 1;
  asm
    mov ah,43h
    mov bx,EPages
    int 67h
    cmp ah,0
    jne @Getmem_Ems_Error
    mov Hhandle,dx
    mov fresult,0
    jmp @Getmem_Ems_End
@Getmem_Ems_Error:
    mov Fresult,ah
@Getmem_Ems_End:
  end;
  H := Hhandle;
  Getmem_EMS := Fresult;
end;

Function Freemem_EMS(H : EMSHandle) : byte;
var Fresult : byte;
begin;
  asm
    mov ah,45h
    mov dx,H
    int 67h
    mov Fresult,ah
  end;
  Freemem_EMS := Fresult;
end;

Function EMS_Allocation(H : EMSHandle;PagePage,EMSPage : word) : byte;
VAR Fresult : byte;
begin;
  asm
    mov ah,44h
    mov al,byte ptr PagePage
    mov bx,EMSPage
    mov dx,H
    int 67h
    mov Fresult,ah
  end;
  EMS_Allocation := Fresult;
end;

Function EMS_Protect_Allocation(H : EMSHandle) : byte;
VAR Fresult : byte;
begin;
  asm
    mov ah,47h
    mov dx,H
    int 67h
    mov Fresult,ah
  end;
  EMS_Protect_Allocation := Fresult;
end;

Function EMS_Unprotect_Allocation(H : EMSHandle) : byte;
VAR Fresult : byte;
begin;
  asm
    mov ah,48h
    mov dx,H
    int 67h
    mov Fresult,ah
  end;
  EMS_Unprotect_Allocation := Fresult;
end;

Function RAM_2_EMS(q : pointer; H : EMSHandle; Size : longint) : byte;
VAR fresult : byte;
    EMSseg    : word;
    hp        : ^byte;
    li        : word;
begin;
  EMS_Segment_determine(EMSseg);
  hp := q;
  if Size > 16384 then begin;
    { More than one page required }
    for li := 0 to (Size SHR 14)-1 do begin;
      EMS_Allocation(H,0,li);
      move(hp^,ptr(EMSseg,0)^,16384);
      dec(Size,16384);
      inc(hp,16384);
    end;
    EMS_Allocation(H,0,li+1);
    move(hp^,ptr(EMSseg,0)^,16384);
    dec(Size,16384);
    inc(hp,16384);
  end else begin;
    EMS_Allocation(H,0,0);
    move(hp^,ptr(EMSseg,0)^,16384);
    dec(Size,16384);
    inc(hp,16384);
  end;
end;

Function EMS_2_RAM(q : pointer; H : EMSHandle; Size : longint) : byte;
VAR fresult : byte;
    EMSseg    : word;
    hp        : ^byte;
    li        : word;
begin;
  EMS_Segment_determine(EMSseg);
  hp := q;
  if Size > 16384 then begin;
    { More than one page required }
    for li := 0 to (Size SHR 14)-1 do begin;
      EMS_Allocation(H,0,li);
      move(ptr(EMSseg,0)^,hp^,16384);
      dec(Size,16384);
      inc(hp,16384);
    end;
    EMS_Allocation(H,0,li+1);
    move(ptr(EMSseg,0)^,hp^,16384);
    dec(Size,16384);
    inc(hp,16384);
  end else begin;
    EMS_Allocation(H,0,0);
    move(ptr(EMSseg,0)^,hp^,16384);
    dec(Size,16384);
    inc(hp,16384);
  end;
end;

Function EMS_Pages_allocated(H : EMSHandle;var Pages : word) : byte;
var fresult : byte;
    Hs : word;
begin;
  asm
    mov ah,4Ch
    mov dx,H
    int 67h
    mov HS,bx
    mov fresult,ah
  end;
  Pages := Hs;
  EMS_Pages_allocated := Fresult;
end;

Function EMS_Handles_assign(Var Number : word) : byte;
Var Fresult : byte;
    Han       : word;
begin;
  asm
    mov ah,4Bh
    int 67h
    mov Han,bx
    mov Fresult,ah
  end;
  Number := Han;
  EMS_Handles_assign := Fresult;
end;

function XMS_lock(H : XMSHandle) : longint; assembler;
asm;
  mov ax,0c00h
  mov dx,h
  call dword ptr [XMST]
  mov ax,bx
end;

procedure XMS_unlock(H : XMSHandle); assembler;
asm;
  mov ax,0d00h
  mov dx,h
  call dword ptr [XMST]
end;


begin;
  Check_for_XMS;
  Check_for_EMS;
end.










