home *** CD-ROM | disk | FTP | other *** search
- {**************************************************************************
- *.............................. CHNLOD .................................*
- * *
- * A memory resident program that automatically loads any Turbo Pascal *
- * CHN file into memory and clones a copy of the runtime library for *
- * the program to use. This can save you a lot of disk space (12-16K *
- * bytes per program) compared to storing COM files for small utilities, *
- * and will reduce loading time whenever one of these utilities is used. *
- * *
- * You may also note that this program does much of the low level *
- * interfacing needed to rewrite CED in Turbo Pascal. *
- * *
- ***************************************************************************
- * *
- * USAGE: *
- * Install CHNLOD simply by typing CHNLOD <Enter>. It should be *
- * installed AFTER CED in order to allow use of CED synonyms in *
- * loading CHN files. *
- * *
- * Precede the name of the chain file with the "chainchar" defined *
- * below. By default, the chainchar is !. Thus if you have a chain *
- * file named MYPROG.CHN, you can call it from DOS with the following *
- * command line: *
- * *
- * !MYPROG <Enter> *
- * *
- * Command line parameters will be passed on to the program as usual. *
- * If the CHN file is not found in the current directory, the PATH will *
- * be searched to find it. The program name may be preceded by a drive *
- * and/or path name, for example as follows: *
- * *
- * !B:\BIN\MYPROG CommandLineParameters Redirection *
- * *
- * All available memory is allocated to the CHN file for its use. *
- * *
- ***************************************************************************
- * *
- * RESTRICTIONS: *
- * 1) The FCBs in the program segment prefix of the CHN program *
- * are not initialized. *
- * 2) The PATH that is searched for the file is the PATH that was *
- * in effect when LODCHN was made resident. PATH changes after *
- * this will not be available. Similarly, the environment passed *
- * to the CHN program will be a copy of the resident program's *
- * environment. The calling program name as provided by DOS 3.X *
- * will name LODCHN.COM and not the name of the chain file. *
- * 3) The technique used will not work in a BATCH file, but only from *
- * the DOS command line (similar to CED). *
- * 4) If the chainchar ! is entered in DOS DEBUG or EDLIN or any other *
- * program which reads via int 21 function 0AH, LODCHN will stupidly *
- * try to load the "named" CHN file. *
- * 5) LODCHN will work in concert with CED synonyms as long as *
- * LODCHN is loaded into memory AFTER CED is loaded. Thus, you *
- * could set up synonyms including the chainchar. *
- * 6) LODCHN must be compiled with the same version of Turbo Pascal *
- * that your CHN programs were compiled with. Otherwise, the cloned *
- * runtime library is incompatible with the program, and who knows *
- * what will happen. *
- * 7) Input and Output redirection are supported only in a simple *
- * fashion. Only the < and > operators are handled. Not handled *
- * are | and >>. See OpenStdIO if you want to add more. *
- * 8) The memory resident program eats about 23K bytes. *
- * *
- ***************************************************************************
- * *
- * Copyright (C) 1986, Kim Kokkonen, TurboPower Software. *
- * Released to the public domain for personal, non-commercial use only. *
- * Version 1.0 - 2/10/86 *
- * Version 1.1 - 2/15/86 *
- * smarter approach to memory allocation may work better on *
- * other systems. *
- * *
- ***************************************************************************
- * *
- * !!!! IMPORTANT !!!! *
- * Requires Turbo Pascal Version 3 to compile. *
- * Set Min/Max Heap to $100/$100 and compile to a COM file before running.*
- * Should run on any PC/MSDOS system using DOS 2.X or later. *
- * Developed and tested on a Compaq Deskpro 286 running Compaq DOS 3.0. *
- * *
- **************************************************************************}
-
- PROGRAM ChainLoader;
- {-memory-resident loader for Turbo Pascal CHN files}
-
- CONST
- version = '1.1';
-
- {character which denotes that a chain file is to be loaded}
- chainchar = '!';
-
- {number of bytes to reserve at top of memory for transient portion of COMMAND.COM}
- TransientCommand = 22000;
-
- TYPE
- address =
- RECORD
- offset, segment : Integer;
- END;
-
- registers =
- RECORD
- CASE Integer OF
- 1 : (AX, BX, CX, DX, BP, SI, DI, DS, ES, FLags : Integer);
- 2 : (AL, AH, BL, BH, CL, CH, DL, DH : Byte);
- END;
-
- LongString = STRING[128];
- PathString = STRING[64];
-
- {mask for keyboard buffer returned by DOS function $0A}
- KeyBuffer =
- RECORD
- maxlen : Byte;
- keys : LongString;
- END;
- KeyBufferPtr = ^KeyBuffer;
-
- {mask for program segment prefix}
- PSPblock =
- RECORD
- int20 : Integer;
- topofmem : Integer;
- reserved : Byte;
- funcdispatch : ARRAY[1..5] OF Byte;
- termofs : Integer;
- termseg : Integer;
- breakofs : Integer;
- breakseg : Integer;
- errorofs : Integer;
- errorseg : Integer;
- whoknows1 : ARRAY[$16..$2B] OF Byte;
- envseg : Integer;
- whoknows2 : ARRAY[$2E..$7F] OF Byte;
- comtail : LongString;
- END;
- PSPblockPtr = ^PSPblock;
-
- CONST
- {code segment variables initialized by the program}
- thisDS : Integer = 0;
- thisSS : Integer = 0;
- thisSP : Integer = 0;
- applDS : Integer = 0;
- applDX : Integer = 0;
- applSS : Integer = 0;
- applSP : Integer = 0;
- tempSP : Integer = 0;
- tempBP : Integer = 0;
- newSEG : Integer = 0;
-
- {previous value of int 21 stored here}
- OldVector : address = (offset : 0; segment : 0);
-
- {address of new program segment prefix stored here}
- newPSP : address = (offset : 0; segment : 0);
-
- VAR
- {mask for system interrupt vectors}
- Vector : ARRAY[0..$FF] OF address ABSOLUTE 0 : 0;
- Reg : registers;
- kPtr : KeyBufferPtr;
- thefile : PathString;
- iname, oname, command, tail : LongString;
- F : FILE;
- PSPptr : PSPblockPtr;
- FirstRunJump : Integer ABSOLUTE CSeg : $101;
- CurPSP, MemReq, MCBstart, RuntimeSize, fSize, OldSeg, GotParas : Integer;
-
- PROCEDURE CheckForPreviousInstallation;
- {-if previously installed then halt}
- CONST
- IDstring : PathString = 'TURBO CHN FILE LOADER';
- VAR
- tstring : PathString;
- BEGIN
- Move(Mem[Vector[$21].segment:(Vector[$21].offset+7)], tstring, Succ(Length(IDstring)));
- IF tstring = IDstring THEN BEGIN
- WriteLn('CHN Loader previously installed...');
- Halt(1);
- END;
- END {checkforpreviousinstallation} ;
-
- PROCEDURE SetTurboEnvironment;
- {-save salient features of the program for the interrupt handler}
- BEGIN
- {save the current memory control block as starting point for later searches}
- MCBstart := Pred(CSeg);
- {get the amount of memory required by this TSR}
- MemReq := MemW[MCBstart:3];
- {save DS, SS and SP for use by the interrupt handler}
- thisDS := DSeg;
- thisSS := SSeg;
- INLINE($2E/$89/$26/thisSP); {mov CS:thisSP,SP}
- END {SetTurboEnvironment} ;
-
- PROCEDURE ExitStayResident(MemReq : Integer);
- {-allocate memreq paragraphs and exit leaving program in memory}
- BEGIN
- Reg.AX := $3100; {exit with success code}
- Reg.DX := MemReq;
- MsDos(Reg);
- END {ExitStayResident} ;
-
- PROCEDURE ExecuteInterrupt;
- {-high level handler}
-
- PROCEDURE WriteString(s : LongString);
- {-write a string via DOS standard output service}
- BEGIN
- s := s+#13#10#36;
- Reg.AH := $9;
- Reg.DS := Seg(s);
- Reg.DX := Ofs(s[1]);
- MsDos(Reg);
- END {writestring} ;
-
- PROCEDURE ChnInComing;
- {-completed CHN application returns here}
- {-restore and return to handler }
- BEGIN
- INLINE(
- {restore Turbo's stack pointers}
- $FA/ {CLI }
- $2E/$8E/$16/thisSS/ {MOV SS,CS:thisSS}
- $2E/$8B/$26/tempSP/ {MOV SP,CS:tempSP}
- $2E/$8B/$2E/tempBP/ {MOV BP,CS:tempBP}
- $FB/ {STI }
-
- {restore Turbo's data segment}
- $2E/$A1/thisDS/ {MOV AX,CS:thisDS}
- $8E/$D8 {MOV DS,AX}
- );
- {return info already on stack causes proper return}
- END {incoming} ;
-
- PROCEDURE ChnOutGoing;
- {-save needed stuff and jump to the loaded application}
- BEGIN
- INLINE(
- {save Turbo's stack pointers}
- $2E/$89/$2E/tempBP/ {MOV CS:tempBP,BP}
- $2E/$89/$26/tempSP/ {MOV CS:tempSP,SP}
- { $83/$EC/$08/ SUB SP,8 - margin for error}
- $2E/$89/$26/thisSP/ {MOV CS:thisSP,SP}
-
- {set up the segment registers and stack for the new program}
- $2E/$A1/newSEG/ {MOV AX,CS:newSEG}
- $8E/$C0/ {MOV ES,AX}
- $8E/$D0/ {MOV SS,AX}
- $8E/$D8/ {MOV DS,AX}
- $8B/$26/$06/$00/ {MOV SP,[0006] - gets top of new segment}
- $31/$C0/ {XOR AX,AX}
- $50/ {PUSH AX}
- $2E/$FF/$2E/newPSP {JMP FAR CS:newPSP}
- );
- END {outgoing} ;
-
- FUNCTION ChainLoad(VAR keystr : LongString;
- chainchar : Char;
- VAR command, tail : LongString) : Boolean;
- {-return true if chainchar is found in a legal position in key string }
- {legal positions are always within the first space or tab delimited word}
- {the chain character may precede a drive or path name in the first word }
- {also return the command name and command tail }
- VAR
- i : Byte;
- BEGIN
- i := 0;
- REPEAT
- i := Succ(i);
- UNTIL (i > Length(keystr)) OR (keystr[i] IN [' ', ^I, ^M]);
- command := Copy(keystr, 1, Pred(i));
- tail := Copy(keystr, i, 255);
- i := Pos(chainchar, command);
- ChainLoad := (i <> 0);
- IF i <> 0 THEN
- {remove the chain character from the command}
- Delete(command, i, 1);
- END {chainload} ;
-
- FUNCTION ExistFile(VAR fname : PathString; VAR F : FILE) : Boolean;
- {-search for the file, return true and an opened file var if found}
- CONST
- MaxPathEntries = 15;
- TYPE
- PathString = STRING[64];
- PathArray = ARRAY[1..MaxPathEntries] OF PathString;
- VAR
- fullname : PathString;
- slashpos : Byte;
- pathnum : Integer;
- CONST
- pathes : PathArray =
- ('', '', '', '', '', '', '', '', '', '', '', '', '', '', '');
-
- PROCEDURE GetPath;
- {-access the DOS environment and return a parsed array of path strings}
- {only done once the first time existfile is called}
- VAR
- pathCnt, envseg, envOfs : Integer;
- teststring : STRING[5];
- endOfEnv : Boolean;
- c : Char;
- BEGIN {GetPath}
- {get the address of the environment}
- envseg := MemW[CSeg:$2C];
- envOfs := 0;
- {scan the environment to find the PATH entry, if any}
- teststring[0] := #5;
- REPEAT
- Move(Mem[envseg:envOfs], teststring[1], 5);
- endOfEnv := (Copy(teststring, 1, 2) = #0#0);
- envOfs := Succ(envOfs);
- UNTIL endOfEnv OR (teststring = 'PATH=');
- IF teststring = 'PATH=' THEN BEGIN
- {parse the pathnames}
- envOfs := envOfs+4;
- pathCnt := 1;
- REPEAT
- c := Chr(Mem[envseg:envOfs]);
- IF (c <> ';') AND (c <> #0) THEN
- pathes[pathCnt] := pathes[pathCnt]+c
- ELSE BEGIN
- {assure that all pathes end with \}
- IF pathes[pathCnt][Length(pathes[pathCnt])] <> '\' THEN
- pathes[pathCnt] := pathes[pathCnt]+'\';
- pathCnt := Succ(pathCnt);
- END;
- envOfs := Succ(envOfs);
- UNTIL (c = #0) OR (pathCnt > MaxPathEntries);
- END;
- END {GetPath} ;
-
- BEGIN {ExistFile}
- {assume success}
- ExistFile := True;
- {see if the file exists as named}
- Assign(F, fname);
- {$I-} Reset(F, 1) {$I+} ;
- IF IOResult = 0 THEN Exit;
-
- {see if fname is already a pathname or includes an explicit drive}
- slashpos := Pos('\', fname)+Pos(':', fname);
- IF slashpos <> 0 THEN BEGIN
- {cannot prepend path names, we have failed}
- ExistFile := False;
- Exit;
- END;
-
- {try the DOS environment PATH}
- IF pathes[1] = '' THEN GetPath;
-
- {try all the pathes in order}
- pathnum := 1;
- WHILE pathes[pathnum] <> '' DO BEGIN
- fullname := pathes[pathnum]+fname;
- Assign(F, fullname);
- {$I-} Reset(F, 1) {$I+} ;
- IF IOResult = 0 THEN Exit;
- pathnum := Succ(pathnum);
- END;
-
- {we didn't find file on PATH either}
- ExistFile := False;
-
- END {ExistFile} ;
-
- PROCEDURE getNewSeg(VAR newSEG, GotParas, OldPSP : Integer);
- {-return the next free segment of memory }
- {as well as its length and the current owner of the block }
- {the current owner will be COMMAND.COM }
- VAR
- mcb : Integer;
- id : Byte;
- BEGIN
- {start at the block assigned to the TSR}
- mcb := MCBstart;
- REPEAT
- id := Mem[mcb:0];
- newSEG := Succ(mcb);
- GotParas := MemW[mcb:3];
- OldPSP := MemW[mcb:1];
- mcb := Succ(mcb+GotParas);
- UNTIL id = $5A;
- END {getnewseg} ;
-
- PROCEDURE SetOwner(newSEG, OldPSP, OldLen : Integer);
- {-set the block at newseg to its owner, oldseg}
- VAR
- mcb : Integer;
- BEGIN
- {last block, in case CHN file deallocated memory}
- mcb := Pred(newSEG);
- Mem[mcb:0] := $5A;
- MemW[mcb:1] := OldPSP;
- MemW[mcb:3] := OldLen;
- END {setowner} ;
-
- PROCEDURE OpenStdIO(VAR tail : LongString);
- {-determine whether command tail specifies redirection }
- {open the standard handles 0 and 1 as needed }
- {They will be passed on to the application that is started up}
-
- PROCEDURE GetName(rchar : Char; VAR tail, name : LongString);
- {-parse the tail for a redirection file name}
- VAR
- ipos, spos, i : Byte;
- BEGIN
- name := '';
- ipos := Pos(rchar, tail);
- IF ipos <> 0 THEN BEGIN
-
- {find next non-blank character}
- i := ipos;
- REPEAT
- i := Succ(i);
- UNTIL (i > Length(tail)) OR NOT(tail[i] IN [' ', ^I]);
- IF i > Length(tail) THEN BEGIN
- WriteString('illegal redirection');
- Exit;
- END;
-
- {find next blank character}
- spos := i;
- REPEAT
- i := Succ(i);
- UNTIL (i > Length(tail)) OR (tail[i] IN [' ', ^I]);
-
- {copy out the device name and delete it from the command tail}
- name := Copy(tail, spos, i-spos);
- Delete(tail, ipos, i-ipos);
-
- END;
- END {getname} ;
-
- PROCEDURE OpenFile(handle : Integer; VAR name : LongString);
- {-open the redirected file and dup it onto the selected handle}
- VAR
- thandle : Integer;
- BEGIN
- {open the file in appropriate mode}
- name := name+#00;
- Reg.DS := Seg(name);
- Reg.DX := Ofs(name[1]);
- CASE handle OF
- 0 : Reg.AX := $3D00;
- 1 : BEGIN
- Reg.AH := $3C;
- Reg.CX := 0;
- END;
- END;
- MsDos(Reg);
- IF Odd(Reg.FLags) THEN BEGIN
- WriteString('illegal redirection');
- Exit;
- END;
- {now DUP its handle onto the appropriate device}
- thandle := Reg.AX;
- Reg.BX := thandle;
- Reg.CX := handle;
- Reg.AH := $46;
- MsDos(Reg);
- IF Odd(Reg.FLags) THEN BEGIN
- WriteString('illegal redirection');
- Exit;
- END;
- {now close the original handle}
- Reg.AH := $3E;
- Reg.BX := thandle;
- MsDos(Reg);
- IF Odd(Reg.FLags) THEN BEGIN
- WriteString('illegal redirection');
- Exit;
- END;
- END {openfile} ;
-
- BEGIN
- {WARNING: only simple < and > supported here}
- {input}
- GetName('<', tail, iname);
- IF iname <> '' THEN OpenFile(0, iname);
- {output}
- GetName('>', tail, oname);
- IF oname <> '' THEN OpenFile(1, oname);
- END {openstdio} ;
-
- PROCEDURE DOSsetTermAddress(segment, offset : Integer);
- {-set the new termination address}
- BEGIN
- Reg.AX := $2522;
- Reg.DS := segment;
- Reg.DX := offset;
- MsDos(Reg);
- END {DOSsettermaddress} ;
-
- PROCEDURE DOSmakePSP(newSEG : Integer);
- {-have DOS set up a new PSP}
- BEGIN
- Reg.AH := $26;
- Reg.DX := newSEG;
- MsDos(Reg);
- END {DOSmakePSP} ;
-
- PROCEDURE DOSgetPSP(VAR PSP : Integer);
- {-return the current PSP}
- BEGIN
- Reg.AH := $51;
- MsDos(Reg);
- PSP := Reg.BX;
- END; {DOSgetPSP}
-
- PROCEDURE DOSsetPSP(PSP : Integer);
- {-set the active PSP as specified}
- BEGIN
- Reg.AH := $50;
- Reg.BX := PSP;
- MsDos(Reg);
- END; {DOSsetPSP}
-
- FUNCTION Cardinal(i : Integer) : Real;
- {-return a real 0..65535}
- BEGIN
- Cardinal := 256.0*Hi(i)+Lo(i);
- END {cardinal} ;
-
- PROCEDURE NullKeyBuffer(VAR keys : LongString);
- {-return an empty key string to fool DOS}
- BEGIN
- keys[0] := Chr(0);
- keys[1] := ^M;
- END {nullkeybuffer} ;
-
- BEGIN
- {point a structure to the returned keyboard buffer}
- kPtr := Ptr(applDS, applDX);
-
- {see if anything there}
- IF (Length(kPtr^.keys) = 0) THEN Exit;
-
- {see if special chain loader character is present in command name}
- IF NOT(ChainLoad(kPtr^.keys, chainchar, command, tail)) THEN Exit;
-
- {display the <CR> at end of line}
- WriteString('');
-
- {store the currently active PSP}
- DOSgetPSP(CurPSP);
-
- {find memory for the CHN program}
- getNewSeg(newSEG, GotParas, OldSeg);
-
- {mark the new segment as belonging to the new program}
- {set the length so that we won't overwrite transient COMMAND.COM}
- SetOwner(newSEG, newSEG, GotParas-(TransientCommand SHR 4));
-
- {set the active PSP to the new program}
- DOSsetPSP(newSEG);
-
- {create a PSP for it}
- DOSmakePSP(newSEG);
-
- {find the file}
- thefile := command+'.CHN';
- IF NOT(ExistFile(thefile, F)) THEN BEGIN
- WriteString('CHN file not found');
- SetOwner(newSEG, OldSeg, GotParas);
- DOSsetPSP(CurPSP);
- NullKeyBuffer(kPtr^.keys);
- Exit;
- END;
- fSize := FileSize(F);
-
- {check for adequate memory to load}
- RuntimeSize := FirstRunJump+3;
- IF Cardinal(fSize+RuntimeSize) > 16.0*Cardinal(GotParas-(TransientCommand SHR 4)) THEN BEGIN
- Close(F);
- WriteString('Insufficient memory - program aborted');
- SetOwner(newSEG, OldSeg, GotParas);
- DOSsetPSP(CurPSP);
- NullKeyBuffer(kPtr^.keys);
- Exit;
- END;
-
- {open standard I/O devices, if any}
- OpenStdIO(tail);
-
- {initialize the rest of the program segment prefix}
- WITH newPSP DO BEGIN
- segment := newSEG;
- offset := $100;
- END;
- PSPptr := Ptr(newSEG, 0);
- WITH PSPptr^ DO BEGIN
- {pass in command tail}
- comtail := tail+^M;
- {set topofmem above the allocated block}
- topofmem := newSEG+GotParas-(TransientCommand SHR 4);
- {point environment to our copy - WARNING: may be out of date}
- envseg := MemW[CSeg:$2C];
- {set the termination and break addresses}
- termofs:=Ofs(ChnInComing);
- termseg:=cseg;
- breakofs:=Ofs(ChnInComing);
- breakseg:=cseg;
- END;
-
- {copy the runtime library above the PSP}
- Move(Mem[CSeg:$100], Mem[newSEG:$100], RuntimeSize);
-
- {read the file above the library}
- BlockRead(F, Mem[newSEG:RuntimeSize+$100], fSize);
- Close(F);
-
- {jump to the new program via Outgoing procedure}
- ChnOutGoing;
- {get back to here via ChnIncoming procedure - STACK MAGIC}
-
- {release the memory}
- SetOwner(newSEG, OldSeg, GotParas);
- {set back to original PSP}
- DOSsetPSP(CurPSP);
-
- {null out the key buffer}
- {so DOS command processor ignores what just happened}
- NullKeyBuffer(kPtr^.keys);
-
- END {executeinterrupt} ;
-
- PROCEDURE InterruptHandler;
- {-low level handler }
- {save and restore machine state of interrupted program }
- {transfer control to Pascal interrupt handler }
- CONST
- IDstring : PathString = 'TURBO CHN FILE LOADER';
- BEGIN
-
- INLINE(
-
- {compiler inserts the following}
- {note pushes are onto DOS stack}
- {PUSH BP}
- {MOV BP,SP}
- {PUSH BP}
-
- {see if we care about this call}
- $9C/ {PUSHF }
- $80/$FC/$0A/ {CMP AH,0A}
- $74/$09/ {JZ YesWeCare}
-
- {pass control on over to DOS}
- $9D/ {POPF }
- $8B/$E5/ {MOV SP,BP}
- $5D/ {POP BP}
- $2E/$FF/$2E/OldVector/ {JMP FAR CS:oldvector}
-
- {YesWeCare:}
- {let DOS (or CED) handle the keyboard function, but return control here}
- $9C/ {PUSHF }
- $2E/$FF/$1E/OldVector/ {CALL FAR CS:OldVector}
-
- {switch stacks}
- $FA/ {CLI }
- $2E/$8C/$16/applSS/ {MOV CS:applSS,SS}
- $2E/$89/$26/applSP/ {MOV CS:applSP,SP}
- $2E/$8E/$16/thisSS/ {MOV SS,CS:thisSS}
- $2E/$8B/$26/thisSP/ {MOV SP,CS:thisSP}
-
- {save all the registers}
- $50/ {PUSH AX}
- $53/ {PUSH BX}
- $51/ {PUSH CX}
- $52/ {PUSH DX}
- $56/ {PUSH SI}
- $57/ {PUSH DI}
- $1E/ {PUSH DS}
- $06/ {PUSH ES}
-
- {save the parameters to the function call in Turbo variables}
- $8C/$D8/ {MOV AX,DS}
- $2E/$A3/applDS/ {MOV CS:applDS,AX}
- $2E/$89/$16/applDX/ {MOV CS:applDX,DX}
-
- {switch to Turbo's data segment}
- $2E/$A1/thisDS/ {MOV AX,CS:thisDS}
- $8E/$D8/ {MOV DS,AX}
- $FB {STI }
- );
-
- {call Turbo routines here}
- ExecuteInterrupt;
-
- INLINE(
- {restore the registers}
- $07/ {POP ES}
- $1F/ {POP DS}
- $5F/ {POP DI}
- $5E/ {POP SI}
- $5A/ {POP DX}
- $59/ {POP CX}
- $5B/ {POP BX}
- $58/ {POP AX}
-
- {switch stacks again}
- $FA/ {CLI }
- $2E/$89/$26/thisSP/ {MOV CS:thisSP,SP}
- $2E/$8E/$16/applSS/ {MOV SS,CS:applSS}
- $2E/$8B/$26/applSP/ {MOV SP,CS:applSP}
-
- {restore application's stack from first pushes}
- $9D/ {POPF }
- $5D/ {POP BP}
- $8B/$E5/ {MOV SP,BP}
- $5D/ {POP BP}
-
- {and return}
- $FB/ {STI }
- $CF {IRET }
- );
-
- END {interrupthandler} ;
-
- BEGIN
-
- {see if installed previously}
- CheckForPreviousInstallation;
-
- {save information needed by the interrupt handler}
- SetTurboEnvironment;
-
- {save old vector $21}
- OldVector.offset := Vector[$21].offset;
- OldVector.segment := Vector[$21].segment;
-
- WriteLn('Installing Turbo Resident CHN file loader');
- WriteLn('....by TurboPower Software - Version ', version);
- WriteLn('Precede CHN file name with ', chainchar, ' to invoke loader');
-
- {store the new interrupt handler}
- INLINE($FA);
- Vector[$21].offset := Ofs(InterruptHandler);
- Vector[$21].segment := CSeg;
- INLINE($FB);
-
- {terminate and stay resident}
- ExitStayResident(MemReq);
-
- END.