
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ This is the DPMIMOUS.PAS unit used with the MOUSE.PAS unit.               }
{ It contains DPMI mouse routines used by the Mouse unit in protected mode. }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ Writen by Michael Day as of 03/20/93 }
{ This unit is released to the public domain. }

{$C FIXED PRELOAD PERMANENT}
UNIT DPMIMOUS;
interface
uses DOS,WinAPI;

var OldMouseInt : pointer;

function DPMIMouseInstall:integer;

implementation
const MSegSel : longint = 0;
      MSegSelSize : word = 16000;
      MaskSize = 64;  {size of graphic mouse cursor mask array}

type  RealRegsType = record
        rDI,rSI,rBP,rXX,rBX,rDX,rCX,rAX : longint;
        rFlags,rES,rDS,rFS,rGS,rIP,rCS,rSP,rSS:word;
      end;

var RealRegs : RealRegsType;
procedure SimRealMouseInt; assembler;
asm
  PUSH AX
  PUSH BX
  PUSH CX
  PUSH DI
  PUSH ES
  MOV AX,SEG @DATA
  MOV ES,AX
  LEA DI,[RealRegs]
  MOV AX,$0300
  MOV BX,$0033
  MOV CX,0
  INT 31H
  POP ES
  POP DI
  POP CX
  POP BX
  POP AX
end;


procedure MouseInt9; assembler;
asm
  PUSH AX
  PUSH BX
  PUSH CX
  PUSH DX
  PUSH SI
  PUSH DI
  PUSH ES
  PUSH DS

  PUSH CX
  MOV AX,SEG @DATA
  MOV DS,AX
  MOV CX,ES
  MOV ES,word ptr [MSegSel]
  MOV DS,CX
  MOV SI,DX                     {copy mask data to real memory}
  MOV DI,0
  CLD
  MOV CX,MaskSize
  REP MOVSB                     {ES:DI=DEST, DS:SI=SRC}
  POP CX

  MOV AX,SEG @DATA
  MOV DS,AX
  MOV AX,word ptr [MSegSel+2]
  MOV word ptr [RealRegs.rES],AX
  MOV word ptr [RealRegs.rDX],0
  MOV word ptr [RealRegs.rAX],9
  MOV word ptr [RealRegs.rBX],BX
  MOV word ptr [RealRegs.rCX],CX
  MOV word ptr [RealRegs.rSS],0	 {let DPMI make it's own stack}
  MOV word ptr [RealRegs.rSP],0
  MOV word ptr [RealRegs.rXX],0
  MOV word ptr [RealRegs.rXX+2],0
  CALL SimRealMouseInt
  POP DS
  POP ES
  POP DI
  POP SI
  POP DX
  POP CX
  POP BX
  POP AX
end;

{save/restore mouse driver state}
procedure MouseInt2223; assembler;
asm
  PUSH AX
  PUSH BX
  PUSH CX
  PUSH DX
  PUSH ES
  PUSH DS
  MOV AX,SEG @DATA
  MOV DS,AX
  MOV AX,word ptr [MSegSel+2]
  MOV word ptr [RealRegs.rES],BX {real seg of storage assumed to be in BX}
  MOV word ptr [RealRegs.rDX],DX {offset assumed to be in DX}
  MOV word ptr [RealRegs.rAX],22 {Note: selector in ES is ignored}
  MOV word ptr [RealRegs.rSS],0	 {let DPMI make it's own stack}
  MOV word ptr [RealRegs.rSP],0
  MOV word ptr [RealRegs.rXX],0
  MOV word ptr [RealRegs.rXX+2],0
  CALL SimRealMouseInt
  POP DS
  POP ES
  POP DX
  POP CX
  POP BX
  POP AX
end;


{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{$F+}  {must be far}
procedure LocalMouseService; assembler;  {dpmi mouse service}
asm
  cmp ax,9
  jz @MyInt9      {mouse graphic cursor configure}
  cmp ax,22
  jz @MyInt2223   {save/restore mouse state info}
  push ds
  push ax
  mov ax,seg @data
  mov ds,ax
  pop ax
  pushf
  call [OldMouseInt]  {call old service routine for all other functions}
  pop ds
  iret

 @MyInt9:
  call MouseInt9
  iret

 @MyInt2223:
  call MouseInt2223
  iret
end;

{---------------------------------------------------------------------------}
{create a selector pointing at a real mode 64k segment}
{returned selector = 0 on failure}

function RealSegmentToSelector(Rseg:word):word;
var Sel : word;
begin
  RealSegmentToSelector := 0;  {presume failure to start}
  asm
    mov ax,0      {try to get a selector}
    mov cx,1
    int $31
    jc  @Done     {eek!}
    mov [Sel],ax  {save the selector}
    mov bx,ax
    mov cx,[Rseg] {set base address of selector}
    rol cx,4
    mov dx,cx
    and cx,$000f
    and dx,$fff0
    mov ax,7
    int $33
    jc @Release   {release the selector if call fails}
    mov dx,$ffff
    mov cx,0      {set segment size to 64k}
    mov ax,8
    int $31
    jnc @Done     {release the selector if call fails}
   @Release:
    mov bx,[Sel]  {release DPMI selector on failure}
    mov ax,1
    int $31
    mov [Sel],0   {zero out selector to indicate failure}
   @Done:
  end;
  RealSegmentToSelector := Sel;
end;

procedure ReleaseRealSelector(Sel:word); assembler;
asm
  mov bx,[Sel]    {release the DPMI selector, we are done with it}
  mov ax,1
  int $31
end;


{---------------------------------------------------------------------------}
{check if the mouse is installed and ready to use }
{ returns 1 if no driver, 0 if driver is available, }
{ returns 2 if DPMI failure while trying to check mouse }

function DPMIMouseInstall:integer;
var Mint,Dest  : pointer;
    Iseg,Iofs,Isel : word;
    Temp : byte;
begin
  DPMIMouseInstall := 1;         {presume driver is not installed as default}
  Isel := 0;                                          {zero out the selector}
  asm
    mov ax,$200
    mov bl,$33                {grab a copy of the real mode interrupt vector}
    int $31
    mov [Iofs],dx                                  {save the seg:ofs pointer}
    mov [Iseg],cx
  end;
  if (Iofs = 0) and (Iseg = 0) then Exit;   {<-- if vector is nil, no driver}

  Isel := RealSegmentToSelector(Iseg);
  if Isel = 0 then
  begin
    DPMIMouseInstall := 2;               {Sel=0 means DPMI allocation failed}
    Exit;
  end;
  Temp := mem[Isel:Iofs];             {read the first byte of the mouse code}
  ReleaserealSelector(Iseg);          {release selector, we are done with it}
  if Temp = $CF then Exit;           {if mouse int points to IRET, no driver}

  if MSegSel = 0 then             {check of mask memory previously allocated}
  begin
    MSegSel := GlobalDosAlloc(MaskSize);       {try to alloc cursor mask mem}
    if MSegSel = 0 then
    begin
      DPMIMouseInstall := 2;          {SegSel=0 means DPMI allocation failed}
      Exit;
    end;
  end;
  SetIntVec($33,@LocalMouseService);      {install our own DPMI mouse server}

  DPMIMouseInstall := 0;                     {Driver is presumed to be there}
end;

{---------------------------------------------------------------------------}
{Exit procedure}
{ restores stuff on exit from main program }

{ special global variables localized to this area}
const MouseVect = $33;                        {mouse driver interrupt number}
var ExitSave : pointer;                      {saved previous ExitPoc pointer}

{$F+} procedure DpmiMouseExit;                              {<-- must be far}
begin
   ExitProc := ExitSave;                   {restore next exit procedure call}
   SetIntVec(MouseVect,OldMouseInt);          {restore original mouse vector}
   if MSegSel <> 0 then
     GlobalDosFree(lo(MSegSel));
end;

{---------------------------------------------------------------------------}
{ End Of DPMIMOUS.PAS }
begin
  GetIntVec(MouseVect,OldMouseInt); {save current vector for mouse interrupt}
  ExitSave := ExitProc;                          {hook up the Exit procedure}
  ExitProc := @DpmiMouseExit;
end.

