home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
turbopas
/
procparm.arc
/
PROCPARM.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-05-09
|
9KB
|
271 lines
{ ProcParm Version 2.1 86/05/09
Author: Mike Babulic Compuserve ID: 72307,314 FIDO: 134/1
3827 Charleswood Dr. N.W.
Calgary, Alberta,
CANADA
T2L 2C7
One of the weaknesses of TURBO Pascal is that procedures and functions
cannot be passed as parameters. The "ProcParm" files are meant to
overcome this limitation.
There are 6 files: 1) ProcParm.PAS - documentation with example program
2) ProcParm.INC - contains the call_ProcParm procedure
3) ProcParm.QK - Inline code to be used instead of
the call_ProcParm procedure.
Quicker execution, but larger programs.
4) ProcParm.BIN - the third (and best) alternative, an
externally assembled binary file.
- Fastest execution due to less stack
manipulation.
- Least code added to the program
(12 bytes!).
5) ProcParm.P - this is the "glue" that you $Include
in a program so it can use ProcParm.BIN.
6) ProcParm.ASM - the source code for ProcParm.BIN
The call_ProcParm procedure
---------------------------
When this procedure is called by a procedure or function, it will cause a
"short" jump to the procedure whose offset is the last parameter (of the
calling procedure). This is done by swapping the last parameter (a procedure
pointer) and the return address of the calling routine on the stack.
Using ProcParm, procedures can be passed as parameters to other procedures.
This is accomplished by defining a dummy procedure (or function) with exactly
the same arguments as the procedure to be passed EXCEPT that a final added
parameter of type Integer is also defined. When the calling procedure calls
the dummy procedure, it passes the offset of the procedure to be executed in
this last parameter (most often by using the Ofs function).
ProcParm will "return" to the routine specified by the last parameter of the
dummy and THE STACK WILL LOOK EXACTLY THE SAME AS IF THE "returned to"
PROCEDURE HAD BEEN CALLED BY THE PROCEDURE THAT CALLED THE DUMMY!
ProcParm.QK -- the Inline code
------------------------------
The ProcParm.QK file can be "included" in your dummy procedure instead of
calling the ProcParm procedure. This will speed up a program because several
fewer 8088 instructions will be executed to "jump" to the passed procedure.
However, be aware that the cost of this speed is a larger program!
The ProcPtr type will not be declared if you use ProcParm.QK, so use Integer
instead.
ProcParm.BIN and ProcParm.P
-----------------------------
The ProcParm.P file is "included" in your program to allow it to use the
contents of ProcParm.BIN file. As in the previous two methods, a dummy
procedure is created with the procedure to be "run" as the last parameter. This
is where the similarity ends.
As well as allowing you to call procedures and functions in the body of your
TURBO program (NEAR calls), ProcParm.P also allows you to call (FAR) procedures
and functions which lie OUTSIDE the body of the program. This program gives the
example of a procedure stored in a typed constant (FarDummy). Admittedly it is
a very simple procedure, just the 8086 instruction "RETF", but more complex
procedures could be read into an array variable (or into the heap?) and be run
just as easily.
You can also make a FAR call with an offset. This is useful if you have set
aside an area of storage for a group of procedures or functions. The method
used is quite a bit slower than the standard FAR call. If speed is important
you should save the address of each procedure in a variable, and use the
regular FAR call. I have included a function to make life easier for you.
One problem with the FAR calls that I haven't been able to solve yet is that
you can't do them from inside the TURBO environment. You have to create a
.COM file and execute that. If anyone comes up with a workable solution please
let me know.
}
{-------------- The procedures to be passed as Parameters --------------}
procedure first(a,b:integer);
begin
writeln(' FIRST ',a:3,b:3);
end;
procedure second(a,b:integer);
begin
writeln(' ',a:3,' SECOND',b:3);
end;
{-------------------- Use of the ProcParm procedure --------------------}
{$i procparm.inc} { Include the call_ProcParm procedure's definition}
procedure dummy(a,b:integer; p:integer {ProcPtr});
begin
call_ProcParm;
end;
procedure OneTwo(p:integer {ProcPtr});
begin
dummy(9,10,p);
end;
{----------------------- Use of ProcParm.QK -----------------------------}
procedure fasterDummy(a,b:integer; p:Integer {ProcPtr});
begin
{$I procparm.qk}
end;
procedure QuickStep(p:integer {ProcPtr});
begin
FasterDummy(20,30,p);
end;
{-------------------- Use of the EXTERNAL procedure --------------------}
{$i procparm.p} { Include the external procedure's definition}
procedure ExternalDummy(a,b:integer; p:ProcPtr); external ProcParm[near];
procedure Zoom(p:ProcPtr);
begin
ExternalDummy(49,50,p);
end;
{------------------------------- Timer -----------------------------------}
TYPE TimeType = record
case boolean of
False: (ffss,mmhh : integer);
True: (frac,sec,min,hour : byte)
end;
VAR oldTime, theTime : TimeType;
procedure Timer;
var REGS : record AX,BX,CX,DX,BP,SI,DI,DS,ES,FLAGS : integer end;
begin
OldTime := TheTime;
with REGS do begin
AX := $2C00;
MsDos(REGS);
with TheTime do begin
mmhh := CX;
ffss := DX;
end;
end;
end;
function TimeInSec(t:TimeType):real;
begin with t do begin
TimeInSec := frac/100.0 + sec + 60*(min + 60*hour);
end end;
function ElapsedTime: real;
{ returns elapsed time in seconds }
var hi,lo :real;
begin
ElapsedTime := TimeInSec(theTime)-TimeInSec(OldTime);
end;
{------------------------- Timing Procedures -----------------------------}
procedure theDummy;
begin
end;
Const farDummy : integer = $CBCB; { RETF ;RETurn Far }
procedure Speed1(p:ProcPtr);
begin
call_ProcParm;
end;
procedure Speed2(p:ProcPtr);
begin
{$i ProcParm.QK}
end;
procedure Speed3(p:ProcPtr); EXTERNAL ProcParm[NEAR];
procedure Speed4(p:FarProcPtr); EXTERNAL ProcParm[FAR];
procedure Speed5;
begin
OffsetProc(ADDR(farDummy),1);
end;
{-------------------------- Run the Examples -----------------------------}
const TimeLoop = MaxInt;
var i : integer;
s1,s2,s3,s4,s5 : real; { elapsed time from each timing loop }
begin
Writeln;
Writeln('Using call_ProcParm Routine ....');
OneTwo(Ofs(First));
OneTwo(Ofs(Second));
Writeln;
Writeln('Using Inline Code Dummy ....');
QuickStep(Ofs(First));
QuickStep(Ofs(Second));
Writeln;
Writeln('Using External Code Dummy ....');
Zoom(Ofs(First));
Zoom(Ofs(Second));
Writeln;
Writeln('Now running speed test');
Timer;
for i := 1 to TimeLoop do Speed1(Ofs(theDummy));
Timer;
s1 := elapsedTime;
Writeln(' call_ProcPtr = ',elapsedTime:3:2,' seconds');
Timer;
for i := 1 to TimeLoop do Speed2(Ofs(theDummy));
Timer;
s2 := elapsedTime;
Writeln(' Inline code = ',elapsedTime:3:2,' seconds');
Timer;
for i := 1 to TimeLoop do Speed3(Ofs(theDummy));
Timer;
s3 := elapsedTime;
Writeln(' External NEAR = ',elapsedTime:3:2,' seconds');
{ For some reason, the ProcParm[FAR] routine does not work properly
from within TURBO pascal itself. Thus, I have commented it out to
avoid any nasty surprises to whoever may run this program from inside TURBO.
To time the FAR routine: "uncomment" the following code and compile to a
.COM file, then execute the .COM file }
s4 := s1*20000; s5 := s1*20000;
{
Timer;
for i := 1 to TimeLoop do Speed4(ADDR(farDummy));
Timer;
s4 := elapsedTime;
Writeln(' External FAR = ',elapsedTime:3:2,' seconds');
Timer;
for i := 1 to TimeLoop do Speed5;
Timer;
s5 := elapsedTime;
Writeln(' Offset FAR = ',elapsedTime:3:2,' seconds');
}
writeln;
writeln('Relative Speeds : Call... : Inline : NEAR : FAR : FARofs :');
writeln(' :',s1/s1:8:3,' :',s1/s2:8:3,' :',s1/s3:8:3,
' :',s1/s4:8:3,' :',s1/s5:8:3,' :');
end.