home *** CD-ROM | disk | FTP | other *** search
- {
- BigTurbo - Turbo Pascal large Code model support
-
- **** FOR IBM PC And MSDOS machines only ****
- **** Requires Turbo Pascal version 3.X ****
-
- This file concatenates 8 files that should be separated
- after download. The 8 files are as follows:
-
- BIGTUR.DOC - describes the BigTurbo system
-
- MAIN1.INC - include files for extra segment control
- MAIN2.INC
- FAR1.INC
- FAR2.INC
-
- MAINEXAM.PAS - main code segment example
- FAREXAM.PAS - far code segment example
- EXAM.GLO - shared global data declarations for example
- }
-
- {****************************************************************************}
- {FOLLOWING IS BIGTUR.DOC}
-
- ********************************************************************
- * BigTurbo - copyright (c) 1985 Kim Kokkonen, TurboPower Software. *
- ********************************************************************
- * written 9/85. (408)-378-3672. Compuserve 72457,2131. *
- * This version is a prototype for a commercial product. *
- * It is hereby released to the public domain for personal, *
- * non-commercial use only. *
- * We will appreciate any feedback. *
- ********************************************************************
-
- The BigTurbo system provides control to set up an extra 64K code
- segment for Turbo Pascal programs. It provides an alternative to
- overlays and chaining with some advantages over either technique.
-
- The extra 64K code segment is written and compiled as a separate program
- from the main code segment. The main block of the separate segment is
- never used, unless you want to execute the program independently for
- debug purposes. BigTurbo allows you to call the global procedures in the
- extra code segment directly from the main program, or from procedures
- in the main program. It also allows procedures in the extra code segment
- to call each other and to call global procedures in the main code segment.
- The two code segments share a common data segment, heap and stack, and
- communication between the two segments is via global variables
- (parameters are not allowed at present).
-
- BigTurbo requires files MAIN1.INC and MAIN2.INC to be included and
- compiled with the main program, and the files FAR1.INC and FAR2.INC to
- be included and compiled with the code comprising the extra code segment.
- As described below, the programmer must make minor modifications to
- the include files MAIN2.INC and FAR2.INC to make them work in his/her
- program.
-
- The main program must have heap space reserved for the extra code
- segment when it is compiled to a .COM file (the extra code segment
- is loaded onto the heap of the main program). The extra code must be
- compiled to a .COM file prior to executing the main program. The
- code space, data space and heap/stack space options for the extra
- code segment are never accessed and are therefore arbitrary.
-
- Hereafter, we will call the extra code segment the "Far" code segment.
-
- Communication between the Main code segment and the Far code segment
- occurs only via global variables, similar to how Chain files are used.
- As a result the Far program should contain a set of global data definitions
- identical to those of the Main code segment. This is best managed by
- putting the global definitions in an include file, and including that
- file in both the Main and Far programs.
-
- Improvements of BigTurbo over using Chain files or Overlays are as follows:
-
- 1) The Far code segment is loaded into memory only once, and remains
- there until the program completes. You get a full extra 64K segment
- (less runtime library overhead, about 10K) to use.
-
- 2) Any global procedure in the Far code segment may be called directly
- either from the main code segment or from within the Far code
- segment.
-
- 3) Any global procedure in the Main code segment may be called directly
- either from the Far code segment or from within the main code
- segment (as usual).
-
- 4) Upon completion of a call to the Far code segment, control returns
- to the calling procedure. Calls in the other direction work the
- same way.
-
- 5) The Main and Far code share the same heap and data segment, which
- provides adequate means of communication between the two.
-
- 6) The restrictions on what can call what are fewer than for overlays.
-
- 7) You can do the equivalent of overlays by DISPOSing of the code
- on the heap and loading another file's worth of code there. Only
- one extra code segment is allowed at a time, although the techniques
- here can clearly be extended to more segments with some logistic
- headaches.
-
- 8) These techniques are not specific to any particular version of
- Turbo, although we have assumed version 3.X (of any flavor).
-
-
- Limitations of BigTurbo are as follows:
-
- 1) For now, calls across the code segment boundary may not use
- parameters, and must be made only to procedures (not functions).
-
- 2) The programmer must do a small amount of manual management to keep
- the required addressing tables set up (see SetupJumpTable below).
-
- 3) An extra copy of the Turbo runtime library is loaded into
- memory for the Far code segment. This costs about 10K bytes of
- RAM.
-
- 4) The Far code may not use global variables that are not used in the
- Main code segment, and vice versa. The order of the global variable
- definitions must be identical in each to guarantee proper addressing,
- just as with chain files.
-
- 5) The Far code should not use EXTERNAL procedures or INLINE code that
- enters and exits the global procedures in any way but the Turbo Pascal
- standard.
-
- 6) Stack checking is not currently implemented for Far calls. Such
- checking once implemented will be compiler version specific.
-
- 7) Far calls are somewhat slower than near calls (see the code in
- FarCallHandler and MainCallHandler to judge the overhead).
-
- 8) As with Turbo overlays, runtime errors will provide misleading
- addresses when they occur in the Far segment.
-
- 9) This hasn't been tested with overlays used being simultaneously, and
- we have a feeling it wouldn't work well.
-
- Other Comments:
-
- 1) The procedures in the Far code segment may contain subprocedures,
- local variables, and typed constants just as usual. Calls to the
- subprocedures may use parameters as usual. A Far procedure may
- call itself recursively if desired.
-
- 2) The main executable block of the Far program may be empty. It will
- never be accessed, unless you are using it to debug the Far procedures
- independently.
-
- 3) The Far code is loaded into a location on the heap of the Main program.
- You need to reserve this amount of memory when you compile the
- Main program.
-
- 4) Your program should use the {$V-} directive to avoid problems in
- string length type checking with these routines.
-
- 5) MAIN1.INC and MAIN2.INC add about 1800 bytes of code to the main
- segment. FAR1.INC and FAR2.INC add about 700 bytes of code to
- the far segment.
-
- *******************************************************
- * See the example files *
- * MAINEXAM.PAS *
- * FAREXAM.PAS *
- * EXAM.GLO *
- * for proper usage of the following include files *
- *******************************************************
-
- {END OF BIGTUR.DOC}
- {****************************************************************************}
- {FOLLOWING IS MAIN1.INC}
-
- CONST
- {set these values based on the size of the MAIN program}
- MaxNumProcs = 10; {maximum number of procedures available for Far calls}
- MaxProcNameLength = 20; {maximum name length of such procedures}
-
- TYPE
- ProcNameArray = ARRAY[1..MaxNumProcs] OF STRING[MaxProcNameLength];
- ProcOffsetArray = ARRAY[1..MaxNumProcs] OF Integer;
- BigTurboString = STRING[64];
- JumpRecord = RECORD
- offset, segment : Integer;
- END;
-
- CONST
- {these need to be adjusted based on the value in MaxNumProcs}
- {no literal values need be filled in here}
- pnames : ProcNameArray = ('', '', '', '', '', '', '', '', '', '');
- poffsets : ProcOffsetArray = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- VAR
- FarCodePtr : ^integer; {will hold pointer to where far code is stored}
- MainHand : JumpRecord; {holds pointer to MainCallHandler in this code segment}
- FarJumpSet : JumpRecord; {holds pointer to SetupJumpTable in Far code segment}
- FarHand : JumpRecord; {holds pointer to FarCallHandler in Far code segment}
-
- PROCEDURE MakeLongCall(FarProcName : BigTurboString);
- {-pass control to the call handler in the Far code}
- BEGIN
- INLINE(
- $8C/$D0/ {MOV AX,SS}
- $8E/$C0/ {MOV ES,AX}
- $8B/$F5/ {MOV SI,BP}
- $81/$C6/$04/$00/ {ADD SI,0004}
- $FF/$1E/FarHand {CALL FAR FarHand}
- );
- END; {MakeLongCall}
-
- {END OF MAIN1.INC}
- {****************************************************************************}
- {FOLLOWING IS MAIN2.INC}
-
- PROCEDURE SetupJumpTable;
- {-initialize the names and offsets of the near procedures}
- {
- ***************************************************
- * The programmer must maintain the lists below to *
- * include all global procedure names that will be *
- * called from the Far code segment. *
- ***************************************************
- }
- BEGIN
- {
- *** EXAMPLES and COMMENTS ********************************************
- * Order is unimportant, except that procedures which are called most *
- * often from far segment should be first in list. *
- * Case of the string is important. Must be same as string passed to *
- * MakeLongCall (avoids overhead time of uppercasing every call). *
- * Fill in your own procedure names below. *
- **********************************************************************
- }
- pnames[1] := 'mainproc1';
- poffsets[1] := Ofs(mainproc1);
- pnames[2] := 'mainproc2';
- poffsets[2] := Ofs(mainproc2);
- END; {SetupJumpTable}
-
- PROCEDURE MainCallHandler;
- {-pick up control from a far call and transfer to near procedure}
- VAR
- i : Integer;
- procofs : Integer;
- procname : BigTurboString;
- BEGIN
- {get procname from the es:si pointer passed in}
- INLINE(
- $31/$C9/ {XOR CX,CX}
- $26/ {ES: }
- $8A/$0C/ {MOV CL,[SI]}
- $FE/$C1/ {INC CL}
- $BF/procname/ {MOV DI,ofs(procname)}
- $FC/ {CLD }
- {10B:}
- $26/ {ES: }
- $AC/ {LODSB }
- $88/$03/ {MOV [BP+DI],AL}
- $47/ {INC DI}
- $E2/$F9 {LOOP 010B}
- );
- {match against the stored procnames}
- i := 0;
- REPEAT
- i := i+1;
- UNTIL (i > MaxNumProcs) OR (pnames[i] = procname);
- {error check}
- IF i > MaxNumProcs THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far procedure ', procname, ' not found....');
- Halt;
- END;
- {check for stack space, later}
- {assure stack aligned for parameter passing, later}
- {call the procedure}
- procofs := poffsets[i];
- INLINE(
- $C4/$46/< procofs/ {LES AX,procofs[BP]}
- $FF/$D0 {CALL AX}
- );
-
- {restore stack frame and FAR return}
- INLINE(
- $8B/$E5/ {mov sp,bp}
- $5D/ {pop bp}
- $CB {ret far}
- );
- END; {MainCallHandler}
-
- PROCEDURE LoadFarCode(FarComFileName : BigTurboString);
- {-load the FarComFile and set up the required addresses}
- CONST
- id1string : BigTurboString = 'FARCALLHANDLER FOLLOWS';
- id2string : BigTurboString = 'SETJUMPTABLE FOLLOWS';
- VAR
- f : FILE;
- len : Byte ABSOLUTE FarComFileName;
- i : Integer;
- size : Integer;
- FarCodeSeg : Integer;
- FarCodeOfs : Integer;
- ext : STRING[4];
- testlen : Byte;
- teststring : BigTurboString;
- BEGIN
-
- {assure it is a .COM file}
- ext := Copy(FarComFileName, len-3, 4);
- FOR i := 1 TO 4 DO ext[i] := UpCase(ext[i]);
- IF ext <> '.COM' THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far Code File must be a .COM file');
- Halt;
- END;
-
- {make sure it exists and open it}
- Assign(f, FarComFileName);
- {note we are using a block size of 1}
- {$I-} Reset(f, 1); {$I+}
- IF IOResult <> 0 THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far Code File not found....');
- Halt;
- END;
-
- {make sure there is something in it}
- size := FileSize(f);
- IF size = 0 THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far Code File is empty....');
- Halt;
- END;
-
- {make sure there is space for it}
- IF (size SHR 4) > (MaxAvail-32) THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far Code too large to load on heap....');
- Close(f);
- Halt;
- END;
-
- {allocate memory on heap}
- GetMem(FarCodePtr, size+512);
- {IMPORTANT: normalize seg and ofs so that we can make farcodeofs=$100}
- FarCodeSeg := Seg(FarCodePtr);
- FarCodeOfs := Ofs(FarCodePtr);
- WHILE FarCodeOfs > $100 DO BEGIN
- FarCodeOfs := FarCodeOfs-16;
- FarCodeSeg := FarCodeSeg+1;
- END;
- FarCodePtr := Ptr(FarCodeSeg, $100);
- FarCodeOfs := $100;
-
- {load code onto heap}
- BlockRead(f, farcodeptr^, size);
- Close(f);
-
- {store the pointers}
- FarJumpSet.segment := FarCodeSeg;
- FarHand.segment := FarCodeSeg;
- {store the local addresses}
- MainHand.offset := Ofs(MainCallHandler);
- MainHand.segment := CSeg;
-
- {search the far code for the idstrings identifying key offsets}
- {start at the top and work backwards, should be faster}
- i := size;
- testlen := Length(id1string)+1;
- REPEAT
- i := i-1;
- Move(Mem[FarCodeSeg:(FarCodeOfs+i)], teststring, testlen);
- UNTIL (i < 1) OR (teststring = id1string);
- IF i < 1 THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'ID string ', id1string, ' not found in Far code....');
- Halt;
- END;
- {store the adjusted offset for far calls}
- FarHand.offset := FarCodeOfs+i-7;
-
- testlen := Length(id2string)+1;
- REPEAT
- i := i-1;
- Move(Mem[FarCodeSeg:(FarCodeOfs+i)], teststring, testlen);
- UNTIL (i < 1) OR (teststring = id2string);
- IF i < 1 THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'ID string ', id2string, ' not found in Far code....');
- Halt;
- END;
- {store the adjusted offset for far calls}
- FarJumpSet.offset := FarCodeOfs+i-7;
-
- {set up the local jump table}
- SetupJumpTable;
-
- {set up the far jump table}
- INLINE(
- $FF/$1E/FarJumpSet {CALL FAR FarJumpSet}
- );
- {we are done}
- END; {LoadFarCode}
-
- {END OF MAIN2.INC}
- {****************************************************************************}
- {FOLLOWING IS FAR1.INC}
-
- CONST
- {set these values based on the size of the FAR program}
- {see comments in MAIN1.INC}
- MaxNumProcs = 10;
- MaxProcNameLength = 20;
- TYPE
- ProcNameArray = ARRAY[1..MaxNumProcs] OF STRING[MaxProcNameLength];
- ProcOffsetArray = ARRAY[1..MaxNumProcs] OF Integer;
- BigTurboString = STRING[64];
- JumpRecord = RECORD
- offset, segment : Integer;
- END;
- CONST
- {these need to be adjusted based on the value in MaxNumProcs}
- {no literal values need be filled in here}
- pnames : ProcNameArray = ('', '', '', '', '', '', '', '', '', '');
- poffsets : ProcOffsetArray = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- VAR
- FarCodePtr : ^integer; {will hold pointer to where far code is stored}
- MainHand : JumpRecord; {holds pointer to MainCallHandler in the other code segment}
- FarJumpSet : JumpRecord; {holds pointer to SetupJumpTable in this code segment}
- FarHand : JumpRecord; {holds pointer to FarCallHandler in this code segment}
-
- PROCEDURE MakeLongCall(FarProcName : BigTurboString);
- {-pass control to the call handler in the Far code}
- BEGIN
- INLINE(
- $8C/$D0/ {MOV AX,SS}
- $8E/$C0/ {MOV ES,AX}
- $8B/$F5/ {MOV SI,BP}
- $81/$C6/$04/$00/ {ADD SI,0004}
- $FF/$1E/MainHand {CALL FAR MainHand}
- );
- END; {MakeLongCall}
-
- {END OF FAR1.INC}
- {****************************************************************************}
- {FOLLOWING IS FAR2.INC}
-
- PROCEDURE SetupJumpTable;
- {-initialize the names and offsets of the near procedures}
- {
- ***************************************************
- * The programmer must maintain the lists below to *
- * include all global procedure names that may be *
- * called from the MAIN code segment. *
- * This procedure should never be called directly *
- * within its own program. *
- ***************************************************
- }
- CONST
- {following string must be here to allow address to be found from Main Code}
- idstring : BigTurboString = 'SETJUMPTABLE FOLLOWS';
- BEGIN
- {
- *** EXAMPLES and COMMENTS ********************************************
- * Order is unimportant, except that procedures which are called most *
- * often from far segment should be first in list. *
- * Case of the string is important. Must be same as string passed to *
- * MakeLongCall (avoids overhead time of uppercasing every call). *
- * Fill in your own procedure names below. *
- **********************************************************************
- }
- pnames[1] := 'farproc1';
- poffsets[1] := Ofs(farproc1);
- pnames[2] := 'farproc2';
- poffsets[2] := Ofs(farproc2);
-
- {restore stack and do a FAR return to the main segment}
- INLINE(
- $8B/$E5/ {mov sp,bp}
- $5D/ {pop bp}
- $CB {ret far}
- );
- END; {SetupJumpTable}
-
- PROCEDURE FarCallHandler;
- {-pick up control from a far call and transfer to near procedure}
- CONST
- {following string must be here to allow address to be found from Main Code}
- idstring : BigTurboString = 'FARCALLHANDLER FOLLOWS';
- VAR
- i : Integer;
- procofs : Integer;
- procname : BigTurboString;
- BEGIN
- {get procname from the es:si pointer passed in}
- INLINE(
- $31/$C9/ {XOR CX,CX}
- $26/ {ES: }
- $8A/$0C/ {MOV CL,[SI]}
- $FE/$C1/ {INC CL}
- $BF/procname/ {MOV DI,ofs(procname)}
- $FC/ {CLD }
- $26/ {ES: }
- $AC/ {LODSB }
- $88/$03/ {MOV [BP+DI],AL}
- $47/ {INC DI}
- $E2/$F9 {LOOP 010B}
- );
- {match against the stored procnames}
- i := 0;
- REPEAT
- i := i+1;
- UNTIL (i > MaxNumProcs) OR (pnames[i] = procname);
- {error check}
- IF i > MaxNumProcs THEN BEGIN
- WriteLn(Con);
- WriteLn(Con, 'Far procedure ', procname, ' not found....');
- Halt;
- END;
- {check for stack space, later}
- {call the procedure}
- procofs := poffsets[i];
- INLINE(
- $C4/$46/< procofs/ {LES AX,procofs[BP]}
- $FF/$D0 {CALL AX}
- );
-
- {restore stack and FAR return}
- INLINE(
- $8B/$E5/ {mov sp,bp}
- $5D/ {pop bp}
- $CB {ret far}
- );
- END; {FarCallHandler}
-
- {END OF FAR2.INC}
- {****************************************************************************}
- {FOLLOWING IS MAINEXAM.PAS}
-
- {$V-}
- PROGRAM mainexam;
- {-demonstrate BigTurbo techniques}
-
- {first BigTurbo include file - before anything else}
- {$I main1.inc}
-
- {all your global variables and declarations are in following file}
- {$I exam.glo}
-
- {your own procedures follow}
-
- PROCEDURE mainproc1;
- {-demonstrate getting to and from the extra segment}
- BEGIN
- MakeLongCall('farproc2');
- END; {mainproc1}
-
- PROCEDURE mainproc2;
- {-demonstrate getting to and from the extra segment}
- BEGIN
- WriteLn('got to mainproc2 from far segment');
- END; {mainproc1}
-
- {second BigTurbo include file - after all other procedures}
- {$I main2.inc}
-
- BEGIN
- {load far code}
- LoadFarCode('farexam.com');
-
- {your code follows}
- WriteLn('in main program');
- WriteLn('calling local procedure mainproc1');
- mainproc1;
- WriteLn('back from mainproc1');
-
- END.
-
- {END OF MAINEXAM.PAS}
- {****************************************************************************}
- {FOLLOWING IS FAREXAM.PAS}
-
- {$V-}
- PROGRAM farexam;
- {-demonstrate BigTurbo techniques}
- {this file holds code for the extra segment}
-
- {first BigTurbo include file - before everything else}
- {$I far1.inc}
-
- {all your global variables and declarations are in following file}
- {$I exam.glo}
-
- {your procedures follow}
-
- PROCEDURE farproc1;
- {-demonstrate a local call in the extra segment}
- BEGIN
- WriteLn('got to farproc1');
- WriteLn('calling mainproc2 in main code segment');
- MakeLongCall('mainproc2');
- WriteLn('back from mainproc2');
- END; {farproc1}
-
- PROCEDURE farproc2;
- {-just demonstrate getting here from main segment}
- BEGIN
- WriteLn('got to farproc2 from main code');
- WriteLn('calling local procedure farproc1 in far code');
- farproc1;
- WriteLn('returned from farproc1');
- END; {farproc2}
-
- {second BigTurbo include file - after all your procedures}
- {$I far2.inc}
-
- BEGIN
- {this block should normally be empty}
- {you can use it to directly call procedures in this program during debugging}
- END.
-
-
- {END OF FAREXAM.PAS}
- {****************************************************************************}
- {FOLLOWING IS EXAM.GLO}
-
- {global definitions and declarations go here}
- {example doesn't need any, so these are bogus}
- TYPE
- xxx = Integer;
- VAR
- i : xxx;
-
- {END OF EXAM.GLO}
- {****************************************************************************}
- ger;
- VAR
- i : xxx;
-
- {END OF EXAM.GLO}
- {***************************