home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / turbopas / procparm.arc / PROCPARM.PAS < prev    next >
Pascal/Delphi Source File  |  1986-05-09  |  9KB  |  271 lines

  1.  {                                ProcParm               Version 2.1   86/05/09
  2.  
  3.  Author: Mike Babulic    Compuserve ID: 72307,314   FIDO: 134/1
  4.          3827 Charleswood Dr. N.W.
  5.          Calgary, Alberta,
  6.          CANADA
  7.          T2L 2C7
  8.  
  9.  One of the weaknesses of TURBO Pascal is that procedures and functions
  10.  cannot be passed as parameters. The "ProcParm" files are meant to
  11.  overcome this limitation.
  12.  
  13.  There are 6 files:  1) ProcParm.PAS  -  documentation with example program
  14.                      2) ProcParm.INC  -  contains the call_ProcParm procedure
  15.                      3) ProcParm.QK   -  Inline code to be used instead of
  16.                                          the call_ProcParm procedure.
  17.                                          Quicker execution, but larger programs.
  18.                      4) ProcParm.BIN  -  the third (and best) alternative, an
  19.                                          externally assembled binary file.
  20.                                          -  Fastest execution due to less stack
  21.                                             manipulation.
  22.                                          -  Least code added to the program
  23.                                             (12 bytes!).
  24.                      5) ProcParm.P    - this is the "glue" that you $Include
  25.                                         in a program so it can use ProcParm.BIN.
  26.                      6) ProcParm.ASM  - the source code for ProcParm.BIN
  27.  
  28.  The call_ProcParm procedure
  29.  ---------------------------
  30.  
  31.  When this procedure is called by a procedure or function, it will cause a
  32.  "short" jump to the procedure whose offset is the last parameter (of the
  33.  calling procedure). This is done by swapping the last parameter (a procedure
  34.  pointer) and the return address of the calling routine on the stack.
  35.  
  36.  Using ProcParm, procedures can be passed as parameters to other procedures.
  37.  This is accomplished by defining a dummy procedure (or function) with exactly
  38.  the same arguments as the procedure to be passed EXCEPT that a final added
  39.  parameter of type Integer is also defined. When the calling procedure calls
  40.  the dummy procedure, it passes the offset of the procedure to be executed in
  41.  this last parameter (most often by using the Ofs function).
  42.  
  43.  ProcParm will "return" to the routine specified by the last parameter of the
  44.  dummy and THE STACK WILL LOOK EXACTLY THE SAME AS IF THE "returned to"
  45.  PROCEDURE HAD BEEN CALLED BY THE PROCEDURE THAT CALLED THE DUMMY!
  46.  
  47.  ProcParm.QK -- the Inline code
  48.  ------------------------------
  49.  
  50.  The ProcParm.QK file can be "included" in your dummy procedure instead of
  51.  calling the ProcParm procedure. This will speed up a program because several
  52.  fewer 8088 instructions will be executed to "jump" to the passed procedure.
  53.  However, be aware that the cost of this speed is a larger program!
  54.  
  55.  The ProcPtr type will not be declared if you use ProcParm.QK, so use Integer
  56.  instead.
  57.  
  58. ProcParm.BIN  and  ProcParm.P
  59. -----------------------------
  60.  
  61. The ProcParm.P file is "included" in your program to allow it to use the
  62. contents of ProcParm.BIN file. As in the previous two methods, a dummy
  63. procedure is created with the procedure to be "run" as the last parameter. This
  64. is where the similarity ends.
  65.  
  66. As well as allowing you to call procedures and functions in the body of your
  67. TURBO program (NEAR calls), ProcParm.P also allows you to call (FAR) procedures
  68. and functions which lie OUTSIDE the body of the program. This program gives the
  69. example of a procedure stored in a typed constant (FarDummy). Admittedly it is
  70. a very simple procedure, just the 8086 instruction "RETF", but more complex
  71. procedures could be read into an array variable (or into the heap?) and be run
  72. just as easily.
  73.  
  74. You can also make a FAR call with an offset. This is useful if you have set
  75. aside an area of storage for a group of procedures or functions. The method
  76. used is quite a bit slower than the standard FAR call. If speed is important
  77. you should save the address of each procedure in a variable, and use the
  78. regular FAR call. I have included a function to make life easier for you.
  79.  
  80. One problem with the FAR calls that I haven't been able to solve yet is that
  81. you can't do them from inside the TURBO environment. You have to create a
  82. .COM file and execute that. If anyone comes up with a workable solution please
  83. let me know.
  84.  
  85. }
  86.  
  87. {-------------- The procedures to be passed as Parameters --------------}
  88.  
  89. procedure first(a,b:integer);
  90.   begin
  91.     writeln('     FIRST ',a:3,b:3);
  92.   end;
  93.  
  94. procedure second(a,b:integer);
  95.   begin
  96.     writeln('    ',a:3,' SECOND',b:3);
  97.   end;
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105. {-------------------- Use of the ProcParm procedure --------------------}
  106.  
  107. {$i procparm.inc}   { Include the call_ProcParm procedure's definition}
  108.  
  109. procedure dummy(a,b:integer; p:integer {ProcPtr});
  110.   begin
  111.     call_ProcParm;
  112.   end;
  113.  
  114. procedure OneTwo(p:integer {ProcPtr});
  115.   begin
  116.    dummy(9,10,p);
  117.   end;
  118.  
  119. {----------------------- Use of ProcParm.QK -----------------------------}
  120.  
  121. procedure fasterDummy(a,b:integer; p:Integer {ProcPtr});
  122.   begin
  123.     {$I procparm.qk}
  124.   end;
  125.  
  126. procedure QuickStep(p:integer {ProcPtr});
  127.   begin
  128.     FasterDummy(20,30,p);
  129.   end;
  130.  
  131. {-------------------- Use of the EXTERNAL procedure --------------------}
  132.  
  133. {$i procparm.p}     { Include the external procedure's definition}
  134.  
  135. procedure ExternalDummy(a,b:integer; p:ProcPtr); external ProcParm[near];
  136.  
  137. procedure Zoom(p:ProcPtr);
  138.   begin
  139.    ExternalDummy(49,50,p);
  140.   end;
  141.  
  142. {------------------------------- Timer -----------------------------------}
  143.  
  144. TYPE TimeType = record
  145.        case boolean of
  146.         False: (ffss,mmhh : integer);
  147.         True:  (frac,sec,min,hour : byte)
  148.        end;
  149.  
  150. VAR oldTime, theTime : TimeType;
  151.  
  152. procedure Timer;
  153.   var REGS : record AX,BX,CX,DX,BP,SI,DI,DS,ES,FLAGS : integer end;
  154.   begin
  155.     OldTime := TheTime;
  156.     with REGS do begin
  157.       AX := $2C00;
  158.       MsDos(REGS);
  159.       with TheTime do begin
  160.         mmhh := CX;
  161.         ffss := DX;
  162.       end;
  163.     end;
  164.   end;
  165.  
  166. function TimeInSec(t:TimeType):real;
  167.   begin with t do begin
  168.     TimeInSec := frac/100.0 + sec + 60*(min + 60*hour);
  169.   end end;
  170.  
  171. function ElapsedTime: real;
  172. { returns elapsed time in seconds }
  173.   var hi,lo :real;
  174.   begin
  175.     ElapsedTime := TimeInSec(theTime)-TimeInSec(OldTime);
  176.   end;
  177.  
  178. {------------------------- Timing Procedures -----------------------------}
  179.  
  180. procedure theDummy;
  181.   begin
  182.   end;
  183.  
  184. Const farDummy : integer = $CBCB;   { RETF  ;RETurn Far }
  185.  
  186. procedure Speed1(p:ProcPtr);
  187.   begin
  188.     call_ProcParm;
  189.   end;
  190.  
  191. procedure Speed2(p:ProcPtr);
  192.   begin
  193.     {$i ProcParm.QK}
  194.   end;
  195.  
  196. procedure Speed3(p:ProcPtr); EXTERNAL ProcParm[NEAR];
  197.  
  198. procedure Speed4(p:FarProcPtr); EXTERNAL ProcParm[FAR];
  199.  
  200. procedure Speed5;
  201.   begin
  202.     OffsetProc(ADDR(farDummy),1);
  203.   end;
  204.  
  205. {-------------------------- Run the Examples -----------------------------}
  206.  
  207. const TimeLoop = MaxInt;
  208.  
  209. var i : integer;
  210.     s1,s2,s3,s4,s5 : real;   { elapsed time from each timing loop }
  211.  
  212. begin
  213.  
  214. Writeln;
  215. Writeln('Using call_ProcParm Routine ....');
  216.   OneTwo(Ofs(First));
  217.   OneTwo(Ofs(Second));
  218.  
  219. Writeln;
  220. Writeln('Using Inline Code Dummy ....');
  221.   QuickStep(Ofs(First));
  222.   QuickStep(Ofs(Second));
  223.  
  224. Writeln;
  225. Writeln('Using External Code Dummy ....');
  226.   Zoom(Ofs(First));
  227.   Zoom(Ofs(Second));
  228.  
  229. Writeln;
  230. Writeln('Now running speed test');
  231.   Timer;
  232.     for i := 1 to TimeLoop do Speed1(Ofs(theDummy));
  233.   Timer;
  234.   s1 := elapsedTime;
  235. Writeln('  call_ProcPtr  = ',elapsedTime:3:2,' seconds');
  236.   Timer;
  237.     for i := 1 to TimeLoop do Speed2(Ofs(theDummy));
  238.   Timer;
  239.   s2 := elapsedTime;
  240. Writeln('  Inline code   = ',elapsedTime:3:2,' seconds');
  241.   Timer;
  242.     for i := 1 to TimeLoop do Speed3(Ofs(theDummy));
  243.   Timer;
  244.   s3 := elapsedTime;
  245. Writeln('  External NEAR = ',elapsedTime:3:2,' seconds');
  246.  
  247. { For some reason, the ProcParm[FAR] routine does not work properly
  248.   from within TURBO pascal itself. Thus, I have commented it out to
  249.   avoid any nasty surprises to whoever may run this program from inside TURBO.
  250.   To time the FAR routine: "uncomment" the following code and compile to a
  251.   .COM file, then execute the .COM file }
  252.                  s4 := s1*20000; s5 := s1*20000;
  253. {
  254.   Timer;
  255.     for i := 1 to TimeLoop do Speed4(ADDR(farDummy));
  256.   Timer;
  257.   s4 := elapsedTime;
  258. Writeln('  External FAR  = ',elapsedTime:3:2,' seconds');
  259.   Timer;
  260.     for i := 1 to TimeLoop do Speed5;
  261.   Timer;
  262.   s5 := elapsedTime;
  263. Writeln('  Offset   FAR  = ',elapsedTime:3:2,' seconds');
  264. }
  265.  
  266. writeln;
  267. writeln('Relative Speeds : Call... :  Inline :    NEAR :     FAR :  FARofs :');
  268. writeln('                :',s1/s1:8:3,' :',s1/s2:8:3,' :',s1/s3:8:3,
  269.                        ' :',s1/s4:8:3,' :',s1/s5:8:3,' :');
  270.  
  271. end.