home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: SysTools / SysTools.zip / pci040vk.zip / newdelay.pas < prev    next >
Pascal/Delphi Source File  |  1996-02-23  |  14KB  |  407 lines

  1. UNIT NewDelay;
  2.  
  3. { Improved Delay routine
  4.   For TP 6.0 and TP/BP 7.00/7.01 (real and protected mode).
  5.  
  6.   Version 1.00, released 29-09-97
  7.  
  8.   (Note: The code itself has NOT changed since version 0.96!
  9.          I just thought it was about time to finish beta stage and make a
  10.          version 1.00)
  11.  
  12.   Please send any BUG REPORTS, COMMENTS and other FEEDBACK to me:
  13.   heckenb(at)mi.uni-erlangen.de or fjf(at)gmx.de
  14.  
  15.   Especially the accuracy probably still is to be improved a little. If
  16.   someone wants to do that (using a high-precision timer - NOT the Bios system
  17.   timer, or the GetTime procedure), please send me the results.
  18.  
  19.   The code is uncommented (as you will see yourself...), and I admit, some
  20.   parts of it are probably not so easy to understand.
  21.   If someone urgently needs some comments, I might write some.
  22.  
  23.  Copyright:
  24.   Partially based on the Delay routine in the unit Crt,
  25.    Copyright (C) 1988,91 Borland International.
  26.   Any changes made are copyright 1996 by Frank Heckenbach; free for
  27.   non-commercial use, only for people not affiliated with Microsoft in any way.
  28.   You may change the code below for your own use (e.g. remove the "IFDEF"ed
  29.   parts you don't need), but don't give away modified versions of it. If you
  30.   think you made an improvement, please contact me at the address shown above.
  31.   You may not remove or modify these comments from the beginning up to "***"!
  32.  
  33.  Disclaimer:
  34.   This code is provided "as is", and comes WITHOUT ANY WARRANTY, expressed or
  35.   implied; without even the implied warranty of fitness for a particular
  36.   purpose. I shall not be liable for any damages, whether direct, indirect,
  37.   special, incidental or consequential arising from the use of the software or
  38.   from the inability to use the software or from a failure of the software to
  39.   operate in a manner desired by the user.
  40.   In short: Use this code totally at your own risk!
  41.  
  42.  Features:
  43.  
  44.   * DelayCnt changed from Word to Longint to avoid overflow on a 200+ MHz CPU.
  45.  
  46.   * No init code necessary (unlike Crt) - at least for BP 7.0, if you don't
  47.     use runtime patching (see below). Delay is initialized when first called.
  48.     This saves about 1/18s and some bytes in programs that don't use Delay.
  49.     Thus, the first delay will be a bit inaccurate (+/- 1/36.4 s).
  50.     You can call Delay(0) at the start of the program to initialize Delay
  51.     and avoid this inaccuracy.
  52.  
  53.   * Tries to avoid busy waiting by giving up timeslices via INT $2F,$1680 when
  54.     running on a 80386 or above in a multitasking environment (as Windoze
  55.     or Linux). E.g., in Linux, the CPU usage during a Delay dropped from
  56.     about 90% with the old Delay code to under 0.1% with this replacement.
  57.  
  58.     In such an environment, of course, the delay may get inaccurate,
  59.     especially longer than intended (with the old code as well as with
  60.     this replacement).
  61.  
  62.     Credits to Walter Koch (walterk@ddorf.rhein-ruhr.de) for pointing me to
  63.     this interrupt function.
  64.  
  65.   * Can patch Crt at runtime and prevent a runtime error caused by Crt's init
  66.     code on a fast CPU.
  67.  
  68.   * Tested under NWDOS 7.0, Windoze 3.1 and the Linux DosEmu by the author.
  69.  
  70.     Dan Dumbrill <73170.1423@CompuServe.COM> says:
  71.     "... on a PPro200 with Windows 95 ...  the code seems to
  72.      work fine with both protected and real modes."
  73.  
  74.     Michael Hermann <hermann@fmi.uni-passau.de> says:
  75.     "I tried the fix, and it works also under OS/2 and Win95."
  76.  
  77.     Further feedback about the behaviour under OS/2 and Win95 is wanted.
  78.  
  79.  History:
  80.   0.90 06-10-96 First release
  81.   0.91 20-10-96 Minor improvement in Delay
  82.   0.92 16-11-96 TP 6.0 compatibility added
  83.   0.93 18-11-96 Crt patching added
  84.   0.94 27-11-96 Added comments confirming OS/2 and Win95 compatibility
  85.   0.95 00-00-00 Skipped this version number! :-)
  86.   0.96 21-01-97 Added comments about using Make to modify Crt
  87.   1.00 29-09-97 Added comments about Linux (DosEmu) compatibility
  88.                 Officially ended beta stage
  89.  
  90.  Bug (possibly):
  91.   Ralf Brown's interrupt list says about INT $2F,$1680:
  92.   'When called very often without intermediate screen output under WIN 3+,
  93.    the VM will go into an idle-state and will not receive the next slice
  94.    before 8 seconds. This time can be changed in SYSTEM.INI through
  95.    "IdleVMWakeUpTime=<seconds>". Setting to zero results in a long wait.'
  96.   However, I could not see this effect, so my routine does nothing to prevent
  97.   this problem. If you encounter this problem, please contact me (address:
  98.   see above).
  99.  
  100.  ***
  101.  
  102.  Using this unit together with Crt
  103.  Choose one of the three solutions described below:
  104.  
  105.  FIRST solution
  106.  + Easiest
  107.  - Requires changing all your units and programs
  108.  - Does not fix the 200 MHz problem
  109.  
  110.   Use NewDelay AFTER Crt in the uses clause ("Uses ...,Crt,NewDelay,...") of
  111.   the main program and of ALL units that use Crt, otherwise Crt's Delay
  112.   routine will be used instead of the new one.
  113.   On a 200+ MHz CPU, Crt's init code related to Delay will produce a runtime
  114.   error. Using this unit in this way won't help against that.
  115.  
  116.  SECOND solution
  117.  + "Clean" solution
  118.  + No programs or other units have to be modified
  119.  + Other units don't even have to be recompiled
  120.  - only for BP 7.0
  121.  - Needs RTL source
  122.  - Most work
  123.  
  124.   Modify Crt and rebuild your RTL. (Even if you are not afraid of the 200 MHz
  125.   problem, it might be a good idea to change Crt, if you have the RTL source.)
  126.   This is done as follows (Note: since Crt is copyrighted by Borland, I cannot
  127.   distribute a modified version of it, nor will you be allowed to give away
  128.   your modified version.):
  129.  
  130.   Preparations:
  131.   * Read all of the following steps before you start. If there's anything you
  132.     don't understand, don't start!
  133.   * Of course you will make BACKUPS of any files you change during the
  134.     following process (BEFORE you change anything)!
  135.   * You must have BP7.0 with the RTL sources. If you only have TP 6.0 or
  136.     TP 7.0, you can't use this solution.
  137.   * Did I mention BACKUPS already?
  138.   * You should have a bit more than basic experience working with BP, or have
  139.     someone experienced to assist you in case of unexpected problems.
  140.   * If you lose some important data without having made BACKUPS, you'll get
  141.     some problems - so make BACKUPS NOW!
  142.  
  143.   Main part:
  144.   * Remove all delay related parts from CRT.ASM (in the RTL\CRT directory).
  145.     (Search for the string "delay" in the file, and keep your eyes open!
  146.      Note: In the procedure "Initialize" it's the part from the line
  147.      "MOV ES,Seg0040" to the line "MOV DelayCnt,AX", inclusively.)
  148.   * Insert the implementation part of this unit - up to, but not including the
  149.     line with "$IFDEF PATCHCRT" - into the implementation of CRT.PAS (same
  150.     directory), and remove the line "procedure Delay; external;" from it.
  151.     Don't change anything in the interface part of CRT.PAS.
  152.   * Instead of the next two or three steps, you can change into the RTL
  153.     directory and call "make \bp\rtl\bin\tpu\crt.tpu" and
  154.     "make \bp\rtl\bin\tpu\crt.tpp" or simply "make", respectively.
  155.     However, this may not work if your directories aren't set up exactly as
  156.     the makefile expects them to.
  157.   * Assemble CRT.ASM (with -d_DPMI_ to CRT.OBP for protected mode, and without
  158.     this switch to CRT.OBJ for real mode) with TASM.
  159.   * Compile CRT.PAS to CRT.TPU and CRT.TPP with BPC.
  160.   * Update CRT.TPU and CRT.TPP in TURBO.TPL and TPP.TPL, respectively, with
  161.     TPUMOVER (or, alternatively: remove them from TURBO/TPP.TPL and include
  162.     the path to either CRT.PAS or CRT.TP? into your unit directories, and in
  163.     the former case also the path to CRT.OB? into your object directories).
  164.   * After modifying Crt this way, you don't have to use NewDelay in your
  165.     programs, of course.
  166.  
  167.  THIRD solution
  168.  + Easy
  169.  + No RTL source needed
  170.  + Only the program - no other units - has to be modified and recompiled
  171.  - Kind of a workaround
  172.  - Not for protected mode
  173.  
  174.   This method patches Crt at runtime, i.e. the code in the Crt unit is
  175.   modified whenever a program compiled with this unit is started. However,
  176.   Crt's Delay procedure is not really fixed, just "redirected" to this unit's
  177.   Delay procedure. Therefore, two versions of Delay will exist in the
  178.   executable file, making it bigger than actually necessary.
  179.   Additionally, an interrupt handler is installed to trap the "division by
  180.   zero" error caused by Crt's init code. This works only for real mode.
  181.   However, Windoze does not have Crt at all (and WinCrt does not have Delay),
  182.   and protected mode is only available with BP 7.0 which comes with the RTL
  183.   source, so you can use the second solution in this case. It should be
  184.   obvious that installing a (temporary) interrupt handler is also not a very
  185.   "clean" solution, and makes the executable bigger than necessary, but anyway
  186.   it works.
  187.  
  188.   How to do it:
  189.   * Define the symbol PATCHCRT in this unit, i.e. remove the # in the
  190.     following line:
  191.     }
  192.  
  193.     {$DEFINE PATCHCRT}
  194.  
  195.     {
  196.   * Use NewDelay IMMEDIATELY BEFORE Crt, and before any units that use Crt in
  197.     the uses clause of the main program ("Uses NewDelay,Crt...")
  198.   * Insert the following line at the start of the main program:
  199.     PatchCrt(Crt.Delay);
  200. }
  201.  
  202. {$IFDEF WINDOWS}
  203. This unit is not for Windoze!
  204. {$ENDIF}
  205.  
  206. INTERFACE
  207.  
  208. {$IFDEF PATCHCRT}
  209. USES {$IFDEF WINDOWS}WinProcs{$ELSE}Dos{$ENDIF};
  210.  
  211. TYPE TCrtDelay=PROCEDURE(ms:Word);
  212. PROCEDURE PatchCrt(CrtDelay:TCrtDelay);
  213. {$ENDIF}
  214.  
  215. {$IFDEF VER60}
  216. CONST
  217.   Seg0040:Word=$40;
  218.   Test8086:Byte=0; {Will be set to 2 if processor is 80386 or above}
  219. {$ENDIF}
  220.  
  221. PROCEDURE Delay(ms:Word);
  222.  
  223. IMPLEMENTATION
  224.  
  225. CONST TimeSlice=100; {Threshold (in ms), above which Delay tries to give up
  226.                       time slices. Can be changed.}
  227.  
  228. PROCEDURE DelayLoop; NEAR; ASSEMBLER; {Internal!}
  229. ASM
  230. @1:SUB  AX,1
  231.    SBB  DX,0
  232.    JC   @2
  233.    CMP  BL,ES:[DI]
  234.    JE   @1
  235. @2:
  236. END;
  237.  
  238. PROCEDURE Delay(ms:Word); ASSEMBLER;
  239. TYPE LongRec=RECORD Lo,Hi:Word END;
  240. CONST DelayCnt:Longint=0; {0 means unitialized}
  241. CONST op32=$66; {Prefix for 32bit operations}
  242. ASM
  243.    MOV  ES,Seg0040
  244.    MOV  CX,ms
  245.    MOV  SI,$6C
  246.    MOV  AX,DelayCnt.LongRec.Lo
  247.    OR   AX,DelayCnt.LongRec.Hi
  248.    JNE  @2
  249.    MOV  DI,SI
  250.    MOV  BL,ES:[DI]
  251. @1:CMP  BL,ES:[DI]
  252.    JE   @1
  253.    MOV  BL,ES:[DI]
  254.    MOV  AX,-28
  255.    CWD
  256.    CALL DelayLoop
  257.    NOT  AX
  258.    NOT  DX
  259.    MOV  BX,AX
  260.    MOV  AX,DX
  261.    XOR  DX,DX
  262.    MOV  CX,55
  263.    DIV  CX
  264.    MOV  DelayCnt.LongRec.Hi,AX
  265.    MOV  AX,BX
  266.    DIV  CX
  267.    MOV  DelayCnt.LongRec.Lo,AX
  268.    MOV  CX,ms
  269.    SUB  CX,83
  270.    JBE  @x
  271. @2:JCXZ @x
  272.    XOR  DI,DI
  273.    MOV  BL,ES:[DI]
  274.    CMP  Test8086,2
  275.    JNB  @4
  276. @3:XOR  SI,SI
  277. @4:MOV  BH,ES:[SI]
  278. @5:MOV  AX,DelayCnt.LongRec.Lo
  279.    MOV  DX,DelayCnt.LongRec.Hi
  280.    CALL DelayLoop
  281.    CMP  BH,ES:[SI]
  282.    JNE  @7
  283. @6:LOOP @5
  284.    JMP  @x
  285. @7:CMP  CX,TimeSlice
  286.    JB   @6
  287.    DB   op32;MOV DX,ES:[SI]
  288. @8:MOV  AX,$1680
  289.    INT  $2F
  290.    OR   AL,AL
  291.    JNZ  @3
  292.    DB   op32;MOV AX,DX
  293.    DB   op32;MOV DX,ES:[SI]
  294.    DB   op32;SUB AX,DX
  295.    JBE  @9
  296.    DB   op32;MOV AX,DX
  297.    JMP  @a
  298. @9:DB   op32;NEG AX
  299. @a:DB   op32;CMP AX,$4A7;DW 0 {CMP EAX,$10000 DIV 55}
  300.    JA   @x
  301.    PUSH DX
  302.    PUSH CX
  303.    MOV  CX,55
  304.    MUL  CX
  305.    POP  CX
  306.    POP  DX
  307.    SUB  CX,AX
  308.    JBE  @x
  309.    CMP  CX,TimeSlice
  310.    JNB  @8
  311.    JMP  @3
  312. @x:
  313. END;
  314.  
  315. {$IFDEF PATCHCRT}
  316. PROCEDURE Patch(OldProc,NewProc:Pointer);
  317. {General patch procedure.
  318.  Patch writes a far jump to NewProc at the beginning of OldProc, thus
  319.  directing all calls to OldProc to NewProc.
  320.  OldProc and NewProc must both be pointers to far procedures/functions with
  321.  the same number, order and type of parameters and the same return type (if
  322.  functions). If they are different, no immediate error is generated, but most
  323.  likely the program will crash when OldProc is called.
  324.  Should also work with procedures/functions in overlaid units.}
  325. TYPE
  326.   TFarJmp=RECORD
  327.     OpCode:Byte;
  328.     Operand:Pointer
  329.   END;
  330. BEGIN
  331.   {Get a writeable pointer to OldProc}
  332.   {$IFDEF DPMI}
  333.   OldProc:=Ptr(Seg(OldProc^)+SelectorInc,Ofs(OldProc^));
  334.   {$ENDIF}
  335.   {$IFDEF WINDOWS}
  336.   OldProc:=Ptr(AllocCStoDSAlias(Seg(OldProc^)),Ofs(OldProc^));
  337.   {$ENDIF}
  338.   WITH TFarJmp(OldProc^) DO
  339.     BEGIN
  340.       OpCode:=$EA; {JMP FAR PTR}
  341.       Operand:=NewProc
  342.     END;
  343.   {$IFDEF WINDOWS}
  344.   FreeSelector(Seg(OldProc^))
  345.   {$ENDIF}
  346. END;
  347.  
  348. {$IFDEF MSDOS}
  349. CONST OldInt0P:Pointer=NIL;
  350. VAR OldInt0:PROCEDURE(Flags:Word) ABSOLUTE OldInt0P;
  351. {"CONST OldInt0:PROCEDURE(Flags:Word)=NIL" is not possible in TP 6.0!}
  352. {$ENDIF}
  353.  
  354. PROCEDURE PatchCrt(CrtDelay:TCrtDelay);
  355. BEGIN
  356.   Patch(@CrtDelay,@Delay);
  357.   {$IFDEF MSDOS}
  358.   IF @OldInt0<>NIL THEN SetIntVec(0,@OldInt0) {No init bug has occurred!}
  359.   {$ENDIF}
  360. END;
  361.  
  362. {$IFDEF MSDOS}
  363.  
  364. PROCEDURE NewInt0(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP:Word); INTERRUPT;
  365. BEGIN
  366.   IF MemW[CS:IP]=$F1F7 {DIV CX}
  367.      {Not a foolproof check, but should be sufficient, since NewDelay should
  368.       be used IMMEDIATELY before Crt}
  369.     THEN
  370.       BEGIN
  371.         Writeln('Crt init bug trapped!');
  372.         SetIntVec(0,@OldInt0);
  373.         @OldInt0:=NIL;
  374.         DX:=CX-1
  375.       END
  376.     ELSE OldInt0(Flags)
  377. END;
  378.  
  379. BEGIN
  380.   GetIntVec(0,@OldInt0);
  381.   SetIntVec(0,@NewInt0);
  382. {$ENDIF}
  383. {$ENDIF}
  384.  
  385. {$IFDEF VER60}
  386. BEGIN
  387. ASM {Check for 80386}
  388.    PUSHF
  389.    POP  AX
  390.    OR   AH,$F0
  391.    PUSH AX
  392.    POPF
  393.    PUSHF
  394.    POP  AX
  395.    AND  AH,$F0
  396.    JE   @1
  397.    MOV  Test8086,2
  398. @1:
  399. END
  400. {$IFDEF PATCHCRT}
  401. {$IFDEF MSDOS}
  402. END
  403. {$ENDIF}
  404. {$ENDIF}
  405. {$ENDIF}
  406. END.
  407.