UNIT NWintr;

{ DPMI Protected mode calls: Hubert Plattfaut of 2:2447/203.4
  Windows Protected Mode calls:
    -Based on EZDPMI by Julian M. Bucknall [1993: 100116.1572@Compuserve.Com]
    -Based on the NetCalls and WinDPMI units by Siebrand Dijkstra [1995: 2:512/250.595]
    -Corrections by Berend de Boer [1995: berend@beard.nest.nl or 2:281/527.23]

  NwTP Version 0.6, 950301, Copyright 1993,1995 R. Spronk
}

INTERFACE

{$B-,F+,O-,R-,S-,X+}

{$DEFINE ProtMode}
{$IFDEF MSDOS}
{$DEFINE RealMode}
{$UNDEF ProtMode}
{$ENDIF}

uses
{$IFDEF RealMode} Dos
{$ENDIF}
{$IFDEF DPMI} Dos,WinApi   { we need the GlobalDosAlloc-Function}
{$ENDIF}
{$IFDEF WINDOWS} WinTypes,WinDOS,WinProcs
{$ENDIF};

CONST VLM_ID_UNKNOWN  = $0000;   {  non-VLM application }
      VLM_ID_VLM      = $0001;
      VLM_ID_CONN     = $0010;
      VLM_ID_TRAN     = $0020;
      VLM_ID_IPX      = $0021;
      VLM_ID_TCP      = $0022;
      VLM_ID_NWP      = $0030;
      VLM_ID_BIND     = $0031;
      VLM_ID_NDS      = $0032;
      VLM_ID_PNW      = $0033;
      VLM_ID_RSA      = $0034;
      VLM_ID_REDIR    = $0040;
      VLM_ID_FIO      = $0041;
      VLM_ID_PRINT    = $0042;
      VLM_ID_GENR     = $0043;
      VLM_ID_NETX     = $0050;
      VLM_ID_AUTO     = $0060;
      VLM_ID_SECURITY = $0061;
      VLM_ID_NMR      = $0100;
      VLM_ID_DRVPRN   = $09F2;
      VLM_ID_SAA      = $09F5;  { SAA Client API for NetWare }
      VLM_ID_IPXMIB   = $09F6;
      VLM_ID_PNWMIB   = $09F7;
      VLM_ID_PNTRAP   = $09F8;
      VLM_ID_MIB2PROT = $09F9;
      VLM_ID_MIB2IF   = $09FA;
      VLM_ID_NVT      = $09FB;
      VLM_ID_TRAP     = $09FC;
      VLM_ID_REG      = $09FD;
      VLM_ID_ASN1     = $09FE;
      VLM_ID_SNMP     = $09FF;

Type
{$ifdef ProtMode}

	TTregisters= Record				{This is the data-structure for the}
		Case Byte Of        			{real-mode-interrupts in DPMI-mode}
		  0:     {32 bit registers}
			(EDI,ESI,EBP,Reserved,EBX,EDX,
			 ECX,EAX:LongInt);
		  1:     {16 bit registers}
			(DI,DIHigh,SI,SIHigh,
			 BP,BPHigh,ReservedLow,ReservedHigh,
			 BX,BXHigh,DX,DXHigh,
			 CX,CXHigh,AX,AXHigh,
			 Flags,ES,DS,FS,GS,IP,
			 CS,SP,SS:Word);
		  2:     {8 bit registers}
			(DILowLow,DILowHigh,DIHighLow,DIHighHigh,
			 SILowLow,SILowHigh,SIHighLow,SIHighHigh,
			 BPLowLow,BPLowHigh,BPHighLow,BPHighHigh,
			 ReservedLowLow,ReservedLowHigh,ReservedHighLow,ReservedHighHigh,
			 BL,BH,BXHighLow,BXHighHigh,
			 DL,DH,DXHighLow,DXHighHigh,
			 CL,CH,CXHighLow,CXHighHigh,
			 AL,AH,AXHighLow,AXHighHigh:Byte)
		End;

{$else} {RealMode}

         TTregisters= Record
                      case Integer of
			  0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word);
			  1: (AL, AH, BL, BH, CL, CH, DL, DH: Byte);
	              end;
{$endif}

     TPtrRec=record Ofs,Seg:word end;

     TintrBuffer=array[1..576] of byte;
     TPintrBuffer=^TintrBuffer;

     TVLMheader=record
                unknown1             :array[1..4] of byte;
                ptr1ofs,ptr1seg,      { pointers to 'procedures' }
                ptr2ofs,ptr2seg,
                ptr3ofs,ptr3seg,
                ptr4ofs,ptr4seg      :word;
                unknown2             :array[1..4] of byte; { 00 00 00 00 }
                HeaderLen            :byte;  { 1.11-> 4E; 1.20-> 4E}
                MultiplexIDstring    :array[1..3] of char; { 56 4C 4D 'VLM' }
                unknown3             :array[1..4] of byte; { 01 00 80 00 }

                TransientSwitchCount :word;
                CallCount            :word;
                ControlBlockOfs      :word; { in same segment as this header }
                CurrentVLMID         :word;
                MemoryType           :byte; { 04 = XMS }
                ModulesLoaded        :byte;
                BlockId              :word;
                TransientBlock       :word;
                GlobalSegment        :word;
                AsyncQueue           :array[1..3] of record { head, tail, s }
                                                     pqofs,pqseg:word;
                                                     end;
                BusyQueue            :array[1..3] of record { head, tail, s }
                                                     pqofs,pqseg:word;
                                                     end;
                ReEntranceLevel      :word;
                FullMapCount         :word;
                unknown5             :word; { 00 00 }
                end;

     TVLMcontrolBlockEntry=record
                           Flag                      :word;
                           ID                        :word;
                           Func                      :word;
                           Maps                      :word;
                           TimesCalled               :word;
                           unknown1                  :word;  { SSeg ? }
                           TransientSeg,GlobalSeg    :word;
                           AddressLow,AddressHi      :word;
                           TsegSize,GSegSize,SSegSize:word; { in 16 byte paragraphs }
                           VLMname                   :array[1..9] of char;
                                                      { null terminated string }
                           end;

Var GlobalReqBuf,GlobalReplyBuf:TPintrBuffer;

    { real-mode only, DPMI: all flags are set to false }
    VLM_EXE_loaded :Boolean;
    NETX_VLM_loaded:Boolean; { if true, then VLM_EXE_loaded must also be true. }
    NETX_EXE_loaded:Boolean;

Function  RealModeIntr(intNo:byte;Var regs:TTregisters):boolean;
Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word);
Procedure nwMsDos(VAR R:ttregisters);
Function  InRealMode:Boolean;

Function  MapRealmodeSegment(RSeg:Word):Word;
Function  nwPtr(s,o:word):Pointer;
Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word);

{$IFDEF RealMode}
Function GetVLMheader(Var VLMheader:TVLMheader):Boolean;
Function GetVLMControlBlock(Entry:Byte;
                        Var ControlBlock:TVLMControlBlockEntry):Boolean;
                        { entry: 0 .. VLMheader.ModulesLoaded }
{$ENDIF}

IMPLEMENTATION {===========================================================}

Var GlobalRegisters:TTregisters;  { all Modes ! }

    VLMCall:Procedure;

{$IFDEF RealMode}

Var VLMtransientSeg:word;

{ ---------- Real mode procedures ------------------------------------}

{$F+}

Var RequesterProc:Procedure(Var regs:Registers);
   { VLMCall:Procedure; }

Procedure VlmSystemCall(Var regs:registers); assembler;
asm
push ds

   { check if VLMCall known. If not, return error $FF in fake AL }
xor ah,ah
mov al,$FF
les di,VLMCall
mov bx,es
cmp bx,$0000
je  @1
   { move fake regs registers to 'real' registers }
   { AX, CX, DX, DS, SI, DI, ES only. }
les di,regs
mov ax,es:[di+16]
push ax            { push new es }
mov ax,es:[di+12]
push ax            { push new di }
mov ds,es:[di+14]
mov ax,es:[di]
mov cx,es:[di+4]
mov dx,es:[di+6]
mov si,es:[di+10]
pop di
pop es
   { farr call to VLM handler }
push bp
CALL VLMCall
pop bp
@1: { move 'real' registers to fake regs registers }

{push es
push di}
les di,regs
mov es:[di],ax
{mov es:[di+4],cx
mov es:[di+6],dx
mov es:[di+10],si
pop ax              ax:= 'di'
mov es:[di+12],ax
pop ax              ax:= 'es'
mov es:[di+16],ax }

pop ds
end;

Procedure VLMcheck;
CONST DOS_MULTIPLEX =$2F;
Var regs:registers;
    ccode:byte;
  Function getBinderyAccessLevel:boolean;  { to be replaced by a non-bindery call }
  Type Treq=record
            len      :word;
            subF     :byte;
            end;
       Trep=record
            accLeveL:byte;
            _objId:longInt;
            fill:array[1..20] of byte;
            end;
       TPreq=^Treq;
       TPrep=^Trep;
  Var result:word;
  BEGIN
  With TPreq(GlobalReqBuf)^
   do begin
      subF:=$46;
      len:=sizeOf(Treq)-2;
      end;
  F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result);
  GetBinderyAccessLevel:=(result=0);
  end;

Var phdr:^TVLMHeader;
    pVLMcbl:^TVLMcontrolBlockEntry;
    t:word;

begin
VLM_EXE_Loaded:=false;
Regs.AX:=$7A20;
Regs.BX:=$0000;
Regs.CX:=$0000;
Intr($2F,Regs);
if regs.AX=$0000
 then begin
      { OK. AX=0000. All seems well. But is it really the 2F VLM handler? }
      phdr:=ptr(regs.es,$0000);
      VLM_EXE_Loaded:=(phdr^.MultiplexIdString[1]='V')
                  and (phdr^.MultiplexIdString[2]='L')
                  and (phdr^.MultiplexIdString[3]='M');

      IF VLM_EXE_Loaded
       then begin
            NETX_EXE_loaded:=False;

            { Determine whether netx.vlm is loaded }
            NETX_VLM_Loaded:=False;
            t:=0;
            While t<phdr^.ModulesLoaded
             do begin
                pVLMcbl:=ptr(regs.es,phdr^.ControlBlockOfs+(t*SizeOf(TVLMControlBlockEntry)));
                IF pVLMcbl^.ID=VLM_ID_NETX
                 then begin
                      t:=$0100; { end of iteration }
                      NETX_VLM_Loaded:=True;
                      end;
                inc(t);
                end;

            { Set requester proc to VLM entry point }
            @VLMcall:=Ptr(Regs.es,Regs.bx);
            VLMtransientSeg:=regs.es;

 { @requesterProc:=@VLMsystemCall; ---------- ERR ------}
   @RequesterProc:=@dos.msdos;

            end
      end;
if NOT VLM_EXE_Loaded
 then begin
      NETX_VLM_loaded:=false;
      @RequesterProc:=@dos.msdos;
      NETX_EXE_loaded:=GetBinderyAccessLevel;
      end;
end;

Function RealModeIntr(intNo:byte;Var regs:TTregisters):boolean;
begin
Intr(intNo,registers(regs));
RealModeIntr:=true;
end;



Procedure nwMsDos(VAR R:ttregisters);
begin
	msDos(registers(R));
end;


Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word);
begin
With GlobalRegisters
 do begin
	CX := Req_size;
	DX := rep_size;
	AH := $f2;
	AL := subf;
	DS := Seg(GlobalReqBuf^);  SI := Ofs(GlobalReqBuf^);
	ES := Seg(GlobalReplyBuf^);DI := Ofs(GlobalReplyBuf^);
	MSDOS(registers(GlobalRegisters));
	Result:=al;
	end;
end;

Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word);
begin
Sreq := Seg(GlobalReqBuf^);
Oreq := Ofs(GlobalReqBuf^);
Srep := Seg(GlobalReplyBuf^);
Orep := Ofs(GlobalReplyBuf^);
end;


Function GetVLMheader(Var VLMheader:TVLMheader):Boolean;
Var p:^TVLMHeader;
begin
if VLMtransientSeg<>$0000
 then begin
      p:=ptr(VLMtransientSeg,$0000);
      move(p^,VLMheader,SizeOf(TVLMHeader));
      end;
GetVLMHeader:=(VLMtransientSeg<>$0000);
end;

Function GetVLMControlBlock(Entry:Byte;
                        Var ControlBlock:TVLMControlBlockEntry):Boolean;
                        { entry: 0 .. VLMheader.ModulesLoaded }
Var ph:^TVLMHeader;
    pcb:^TVLMControlBlockEntry;
begin
if VLMtransientSeg<>$0000
 then begin
      ph:=ptr(VLMtransientSeg,$0000);
      pcb:=ptr(VLMtransientSeg,ph^.ControlBlockOfs+entry*SizeOf(TVLMControlBlockEntry));
      move(pcb^,ControlBlock,SizeOf(TVLMControlBlockEntry));
      end;
GetVLMControlBlock:=(VLMtransientSeg<>$0000);
end;

Function nwPtr(s,o:word):Pointer;
begin
nwPtr:=Ptr(s,o);
end;

Function MapRealmodeSegment(RSeg:Word):Word;
begin
MapRealmodeSegment:=RSeg;
end;

{$ENDIF} {------------- end of real-mode procedures -------------------}

{$IFDEF ProtMode}

Type    pRealSegItem=^tRealSegItem;
	tRealSegItem=record  {structure to store information}
                     Seg:word;    {about allocated selectors}
                     Sel:Word;
                     prev,next:pRealSegItem;
                     end;
	{we need to allocate selectors which map real-mode segments.}
	{all these selectors are stored in an dynamic list}
	{and are cleand up them at then end of the program}

Var GlobalRealReqSeg,
    GlobalRealReplySeg:Word;
    SelectorList:pRealSegItem;

Function RealModeIntr (IntNo:Byte;VAR Regs:ttregisters):Boolean;Assembler;
{Simulate a call to the spectified real mode interrupt. The registers passed
 to the real mode code are held in RealModeRegisters. This structure contains
 the register content upon termination of the real mode ISR.
 Returns False if there was an error.}

  ASM
		push di
		push es

		mov  bh,00               {For DOSX to reset the int controller and A20 line.  Windows ingores it.}
		mov  bl,IntNo              {Tell DPMI which interrupt to simulate}
		xor  cx,cx               {0 bytes to copy to real mode stack}
		les  di,Regs             {Get the real mode structure}
		mov word ptr es:[di+$c],0		{reserved to 0}
		mov word ptr es:[di+$c+2],0
		mov word ptr es:[di+$26],0		{fs to 0}
		mov word ptr es:[di+$28],0		{gs to 0}
		mov word ptr es:[di+$2e],0		{sp to 0}
		mov word ptr es:[di+$30],0		{ss to 0}

		mov  ax,$0300            {Function 0300h is simulate real mode interrupt}
		int  31h

		jc   @Error              {The carry flag was set, so there was an error}
		mov  ax,True         {Return no error}
		jmp  @AllDone

	  @Error:
		mov  ax,False        {Return false indicating an error}

	  @AllDone:
		pop  es
		pop  di
  End;

Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word);
begin
With GlobalRegisters
 do begin
	CX := Req_size;
	DX := rep_size;
	AH := $f2;
	AL := subf;
	DS := GlobalRealReqSeg;				{Use then REAL-MODE segments}
	ES := GlobalRealReplySeg;			{of the global buffers}
	DI := 0;							{OFFSET always 0 for}
	SI := 0;                            {'GlobalDosAlloc'ated memory}
	if not RealModeIntr($21,GlobalRegisters)
         then RUNERROR(217);
        {DPMI-ERRORS, maybe we should stop the system with the new Errorcode 217}
	Result:=al;
	end;
end;

Procedure nwMsDos(VAR R:ttregisters);
begin
if not RealModeIntr($21,R)
 then RUNERROR(217);
 {DPMI-ERRORS, maybe we should stop then system with the new Errorcode 217}
end;

Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word);
begin
Sreq := GlobalRealReqSeg;      {Use the REAL-MODE segments}
Srep := GlobalRealReplySeg;    {of the global buffers}
Oreq := 0;                     {OFFSET always 0 for}
Orep := 0;                     {'GlobalDosAlloc'ated memory}
end;

{----- Some low-level functions for DPMI -----------}
TYPE os = record
          o, s : Word;
          end;			{for typecasts}
     LDTStr = record            {Structure of LDT-Elements}
              limit : Word;
              base  : Word;
              data : Array[0..1] of Word;
              end;

Procedure Halt218; {runError 218: low-level DPMI-Errors}
begin
RunError(218);
end;

{DMPI-Function 0: Allocate LDT Descriptor}
function AllocLDTD(var NEWD : Word) : Word; Assembler;
asm
      xor     ax,ax
      mov     cx,1          {only 1 descriptor needed}
      int     31h           {Call DPMI}
      jnc     @@ok
      Call Halt218          {Error on carry}
@@ok:
      les     di,NEWD       {save descriptor to VAR NEWD}
      mov     es:[di],ax
      xor     ax,ax
end;

{DMPI-Function 1: Free LDT Descriptor}
function FreeLDTD(D : Word) : Word; Assembler;
asm
      mov     ax,0001h
      mov     bx,D
      int     31h
      jc      @@Ex          {carry: return Error in ax}
      xor     ax,ax
@@Ex:
end;

{DMPI-Function 7: Set Segment Base Address}
function SetSBA(S: Word; BA: LongInt) : Word; Assembler;
asm
      mov     ax,0007h
      mov     bx,S
      mov     cx,word ptr BA+2
      mov     dx,word ptr BA
      int     31h
      jc      @@Ex          {carry: return Error in ax}
      xor     ax,ax
@@Ex:
end;

{DMPI-Function 8: Set Segment Limit}
function SetSL(S: Word; L: LongInt) : Word; Assembler;
asm
      mov     ax,0008h
      mov     bx,S
      mov     dx,word ptr L
      mov     cx,word ptr L+2
      int     31h
      jc      @@Ex        {carry: return Error in ax}
      xor     ax,ax
@@Ex:
end;

{DMPI-Function 9: Set Descriptor Access Rights}
function SetDAS(S: Word; R: Word) : Word; Assembler;
asm
      mov     ax,0009h
      mov     bx,S
      mov     cx,R
      int     31h
      jc      @@Ex        {carry: return Error in ax}
      xor     ax,ax
@@Ex:
end;

{DMPI-Function 11: Get Descriptor}
function GetD(S: Word; var D : LDTStr) : Word; Assembler;
asm
      mov     ax,000Bh
      mov     bx,S
      les     di,D
      int     31h
      jc      @@Ex         {carry: return Error in ax}
      xor     ax,ax
@@Ex:
end;


{Set then Length of the Descriptor-Segment}
function SetLimit(Sele: Word; L: LongInt) : Word;
var St,R: Word;
    Des : LDTStr;
begin
St:= GetD(Sele, Des);       {get the Descriptor-Entry from LDT}
if St <> 0
 then begin
      SetLimit:= St;        {not in LDT, return Error}
      Exit;
      end;
with Des
 do R := (Data[0] shr 8) or ((Data[1] and $00F0) shl 8);
 	{form then rights for the DPMI-9-Call, register cl}
if L > $FFFFF
 then begin                      {> 1MB: Page aligned}
      if L and $FFF <> $FFF
       then begin                {Limit=Length-1!}
            SetLimit := $8021;   {return Error: not page aligned}
            Exit;
            end;
       R:= R or $8000;           {set Page granularity}
       end
 else R:= R and $7FFF;           {set Byte granularity}
St := SetSL(Sele, 0);                 {fist set limit to 0}
if St = 0 then St := SetDAS(Sele, R); {ok, set the new rights}
if St = 0 then St:= SetSL(Sele, L);   {ok, set then limit}
SetLimit := St;	                      {return errorcode}
end;


{get a Selector for a part of then real-mode memory}
function RealMemSel(RealP : Pointer; Limit : LongInt; var Sele : Word) : Word;
  function NP(P : Pointer) : LongInt;
  VAR TC:OS absolute P;
  begin
  NP := (LongInt(TC.S) shl 4)+LongInt(TC.O);
  end;
var St : Word;
begin
St := AllocLDTD(Sele);					{get a new Selector}
if St = 0
 then begin
      St := SetSBA(Sele, NP(RealP));        {set base addresse to the linear}
      if St = 0
       then begin					{address of the Real-Segment}
            St := SetLimit(Sele, Limit);		{set the selector-limit}
            if St <> 0
             then if FreeLDTD(Sele)<>0 then;		{on error: free selector}
            end
       else if FreeLDTD(Sele)<>0 then;          {on error: free selector}
      end;
RealMemSel := St;						{return errorcode}
end;

{check if the required selector is already allocated}
Function InSelectorList(S:Word):pRealSegItem;
VAR	li:pRealSegItem;
begin
li:=SelectorList;
while li<>NIL
 do begin
    if li^.Seg=S
     then begin
          InSelectorList:=Li;
          exit;
          end;
    li:=li^.Next;
    end;
InSelectorList:=NIL;
end;

{insert a new SelectorItem at start of the list}
Procedure AddToSelectorlist(Segment,Selector:Word);
VAR	li:pRealSegItem;
begin
new(li);
with li^
 do begin
    Seg:=segment;
    Sel:=Selector;
    next:=Selectorlist;
    prev:=NIL;
    end;
Selectorlist^.prev:=li;
Selectorlist:=li;
end;

{clean up}
Procedure FreeSelectorList;
VAR li:pRealSegItem;
begin
while Selectorlist<>NIL
 do begin
    li:=selectorlist;
    selectorlist:=li^.next;
    if li^.sel<>0
     then FreeLDTD(li^.Sel);
    dispose(li);
    end;
end;

Function MapRealmodeSegment(RSeg:Word):Word;
VAR sel:Word;
    li:pRealSegItem;
begin
li:=InSelectorList(RSeg);
if li=NIL
 then begin
      if RealMemSel(Ptr(RSeg,0),$ffff,Sel)<>0
       then RUNERROR(217);		 	{something's wrong: Errorcode 217}
      MapRealModeSegment:=Sel;
      AddToSelectorList(Rseg,Sel);
      end
 else MapRealModeSegment:=li^.Sel;
end;


Function nwPtr(s,o:word):Pointer;
begin
      nwPtr:=Ptr(MapRealModeSegment(s),o);
end;


{$ENDIF} {----------------- end of protected mode procedures -------------}

Var OldExitProc:pointer;

Function InRealMode:Boolean;
begin
{$IFDEF Windows}
InRealMode:=(GetWinFlags and wf_PMode)=0;
{$ELSE}
 {$IFDEF ProtMode}
 InRealMode:=False;
 {$ELSE}
 InRealMode:=True;
 {$ENDIF}
{$ENDIF}
end;


{$F+}
Procedure IntrExit;
begin
ExitProc:=OldExitProc;
{$IFDEF ProtMode}
if GlobalDosFree(Seg(GlobalReqBuf^))<>0 then; 	{ignore Errors}
if GlobalDosFree(Seg(GlobalReplyBuf^))<>0 then;
FreeSelectorList;
{$ELSE} {RealMode}
FreeMem(GlobalReqBuf,SizeOf(TintrBuffer));
Freemem(GlobalReplyBuf,Sizeof(TintrBuffer));
{$ENDIF}
end;
{$F-}


{$IFDEF ProtMode}
VAR w1:Longint absolute GlobalRegisters;
{ we only need w1 during the initialisation, so we use the static
  var GlobalRegisters to save 4 bytes of memory :-) }
{$ENDIF}

begin
VLM_EXE_Loaded:=false;
NETX_EXE_loaded:=false;
NETX_VLM_loaded:=false;
{$IFDEF ProtMode}
new(SelectorList);
fillchar(Selectorlist^,Sizeof(Selectorlist^),0);
w1:=GlobalDosAlloc(Sizeof(tIntrBuffer));        {alloc REQ-Buffer}
if w1=0
 then runerror(217);                            {DPMI-ERROR, no free Memory}
GlobalReqBuf:=Ptr(loWord(w1),0);                {buffer-address for protected Mode}
GlobalRealReqSeg:=hiWord(w1);                   {REAL-Mode-Segment of the buffer-address}
w1:=GlobalDosAlloc(Sizeof(tIntrBuffer));        {alloc REPLY-Buffer}
if w1=0
 then runerror(217);
GlobalReplyBuf:=Ptr(loWord(w1),0);
GlobalRealReplySeg:=hiWord(w1);
{$else} {RealMode}
new(GlobalReqBuf);
if GlobalReqBuf=NIL
 then RunError(203); {where has all the memory gone?? /Heap-Overflow}
new(GlobalReplyBuf);
if GlobalReplyBuf=NIL
 then RunError(203);
VLMtransientSeg:=$0000;
VLMcheck;
{$endif}
OldExitProc:=ExitProc;
ExitProc:=@IntrExit;
end.


