home *** CD-ROM | disk | FTP | other *** search
- {-----------------------------------------------------}
- { WIN87EM.PAS - A Protected mode WIN87EM "simulator" }
- { Copyright (c) 1993, Pat Ritchey CIS:[70007,4660] }
- {-----------------------------------------------------}
-
- {$N+} { This is turned on so that the FP asm statements will compile }
-
- {$IFNDEF DPMI}
- !! Error: This is a Protected mode DLL.
- {$ENDIF}
-
- Library Win87EM;
- {$C FIXED,PRELOAD,PERMANENT} { Match SYSTEM unit attrib. }
- {$D WIN87EM DOS Protected mode code}
- {$S 65535} { All code in one segment }
-
- uses WinAPI;
-
- const
- emDataSize = 230+16; { from EI86.ASM }
- npuDataSize = 108; { Some references say 94 others say 108 }
- SaveDataSize = emDataSize+npuDataSize;
-
- LibName = 'WIN87EM';
-
- EmulatorError =
- 'Emulator code not present, compile calling program with $N+'+
- #13#10' and include a floating point statement';
-
- FloatInstErr =
- 'A DLL has performed a floating point operation in its outer block.'+
- #13#10' This DLL is not useable in DOS protected mode.';
-
- NotWindowsErr =
- 'This DLL is not a Windows DLL. It is a DOS Protected mode DLL';
-
- function EmulatorWillInstall : boolean;
- { A hack to determine if the calling app will install the
- emulator/FP support code. The logic is:
- 1. Find the limit of the stack segment.
- 2. The return address to the APP will be at Stack's seg limit - 3.
- 3. This return address will point to the FAR call that
- calls SYSTEM.InitTurbo.
- 4. IF the instruction following the APP's call to SYSTEM.InitTurbo
- is another FAR call to code in the same segment, it is call to
- SYSTEM.InitEM8x and the FP code will be installed.
- 5. IF the instruction is NOT a FAR call or is a FAR call to a different
- segment then the FP code will NOT be installed.
-
- Caveats:
- --------
- This routine assumes that WIN87EM.DLL is being loaded due to an implicit
- reference to the DLL. If this DLL is loaded via a LoadLibrary() call in
- a calling app the stack will not be in the "as expected" state and the
- result of this function is undefined
- }
- type
- TFarCallInst = record
- Opcode : byte;
- Offset : word;
- Segment : word;
- end;
- PAppEntry = ^TAppEntry;
- TAppEntry = record
- InitTurbo : TFarCallInst;
- InitEM8x : TFarCallInst;
- end;
- var
- SegLimit : word;
- AppEntry : PAppEntry;
- const
- CallFar = $9A;
- begin
- EmulatorWillInstall := false;
- asm
- mov ax,ss
- lsl ax,ax
- mov SegLimit,ax
- end;
- AppEntry := PAppEntry(Ptr(SSeg,SegLimit-3)^);
- With AppEntry^ do begin
- If (InitTurbo.Opcode <> CallFar) or (InitEM8x.Opcode <> CallFar) then exit;
- EmulatorWillInstall := InitTurbo.Segment = InitEM8x.Segment;
- end;
- end;
-
- procedure InvalidOp; far;
- begin
- asm
- sti
- mov ax,Seg @DATA
- mov ds,ax
- end;
- MessageBox(0,FloatInstErr,LibName,MB_OK);
- RunError(207);
- end;
-
- procedure InstallIntVect; assembler;
- { Yet another hack. This procedure points all of the FP emulator interrupts
- to the InvalidOp procedure. This allows WIN87EM to trap floating point
- operations that occur prior to the installation of the Borland floating
- point code (ie: A DLL that does a FP op in it's LibMain code). InvalidOp
- displays a more user-friendly message than the "Unexpected interrupt" dump
- produced by RTM. The only remaing problem is how to handle DLLs produced
- by BP7 (either Pmode or Windows) that have FP code in its outer block.
- BP7 does not generate a call to __FPMATH in a Windows target DLL.
- }
- asm
- push ds
- push cs
- pop ds
- mov dx,OFFSET InvalidOp
- mov cx,11
- mov ax,$2534
- @1:
- int $21
- inc ax
- loop @1
- pop ds
- end;
-
- function __FPMATH : word; export; external;
- {$L WIN87EM }
- { __FPMATH is passed its parameters in the CPU registers. Since one
- of the parameters is passed in AX, BASM can't be used to implement
- this function (the stack setup code would trash AX). The external
- code simply pushes the registers on the stack, establishes DS
- addressability and calls FPMATH (see below). }
-
- function FPMATH(Fn : word; DXReg : word; AXReg: word) : word; far;
- { __FPMATH has 13 functions. Some (such as 0, 2 and 3) are already handled
- by the Borland emulator. Some MS DLLs/EXEs call function 11 to determine
- if an NPU is present, so this function is supported. The others MIGHT need
- to be implemented. Function 4 was easy to reverse engineer, so it's
- included here. }
- begin
- FPMATH := 0; { assume success }
- case fn of
- 0: ; { Initialize }
- 1: ;
- 2: ; { Cleanup }
- 3: ; { set Exception Vector }
- 4: begin
- asm
- and AXReg,$FF3C
- fldcw AXReg
- end;
- FPMATH := AXReg;
- end;
- 5: ;
- 6: ;
- 7: ;
- 8: ;
- 9: ;
- 10: ;
- 11: FPMath := Test8087;
- 12: ;
- else
- FPMATH := $FFFF; { invalid function }
- end;
- end;
-
- function __WIN87EMINFO(var InfoBuff; BuffLen : word) : wordbool; export;
- var
- IB : array[0..5] of word absolute InfoBuff;
- begin
- __WIN87EMINFO := true;
- if BuffLen = 12 then
- begin
- IB[0] := $0600; { I'm not sure what this value signifies ?? }
- IB[1] := SaveDataSize;
- IB[2] := SSeg;
- IB[3] := CSeg;
- IB[4] := word(Test8087);
- IB[5] := 0;
- __Win87EMInfo := false;
- end;
- end;
-
- function __WIN87EMRESTORE(var SaveBuff; BuffLen : word): wordbool; export;
- begin
- __Win87EMRestore := true; { assume failure }
- if BuffLen <> SaveDataSize then exit;
- asm
- mov dl,Test8087
- push ds
- lds si,SaveBuff
- or dl,dl
- jz @1
- db $9B { wait }
- db $DD,$24 { frstor [si] }
- @1:
- add si,npuDataSize
- push ss
- pop es
- mov di,0
- mov cx,emDataSize
- rep movsb
- pop ds
- end;
- __Win87EMRestore := false;
- end;
-
- function __WIN87EMSAVE(var SaveBuff; BuffLen : word) : wordbool; export;
- begin
- __Win87EMSave := true; { assume failure }
- if BuffLen <> SaveDataSize then exit;
- asm
- les di,SaveBuff;
- cmp Test8087,0
- jz @1
- db $9B { wait }
- db $26,$DD,$35 { fsave es:[di] }
- @1:
- add di,npuDataSize
- push ds
- mov ax,ss
- mov ds,ax
- xor ax,ax
- mov cx,emDataSize
- rep movsb
- pop ds
- end;
- __Win87EMSave := false;
- end;
-
-
- exports
- __FPMATH index 1
- ,__WIN87EMINFO index 3
- ,__WIN87EMRESTORE index 4
- ,__WIN87EMSAVE index 5
- ;
-
-
- begin
- if GetWinFlags and wf_DPMI = 0 then
- begin
- MessageBox(0,NotWindowsErr,LibName,MB_OK);
- halt;
- end;
- if not EmulatorWillInstall then
- begin
- MessageBox(0,EmulatorError,LibName,MB_OK);
- RunError(207);
- end;
- InstallIntVect;
- { A DLL doesn't link in the FP library, so Test8087 must be initialized
- "manually" }
- if GetWinFlags and wf_80x87 <> 0 then
- Test8087 := 1;
- end.