home *** CD-ROM | disk | FTP | other *** search
- {LAST COMMAND
- written by Stephen R. Davis 2/28/85}
- {$c-}
- {$k-}
-
- {this program is a "stay resident" program which allows
- the operator to perform any of the last 10 commands
- which he had entered by entering an AltF10 to get a
- list of them, followed by a function key F1-F10
- to select which command to reenter. Command lines which
- begin with nonprintable keystrokes are not saved. This
- program was written as an example of a TurboPascal
- stay-resident "interrupt borrower" program}
-
- {declare our constants}
-
- const
- our_char = 113; {this is the scan code for AltF10}
- scan_offset = 58; {scan code of F1 - 1}
- first_row = 5; {window size and position}
- first_col = 5;
- numb_saved = 10; {number of command lines saved}
- windowwidth = 40;
- windowlength = 12; {='numb_saved' + 2}
-
- CR = $D; {ascii carriage return}
- ESCAPE = $1B; { " escape}
- DEL = $8; { " delete character}
- CntrlC = $3; { " control C}
- Unprintable = $0; {nonascii keys generate 0 char}
-
- user_int = $68; {place to put borrowed interrupt--
- may be changed to any available
- interrupt the user desires}
-
- kybrd_int = $16; {BIOS keyboard interrupt}
- prog_size = 16000; {approximate size of program --
- this much space is reserved
- upon exit}
-
- {here the global (static type) variables}
-
- type
- regtype = record
- ax,bx,cx,dx,bp,si,di,ds,es,flags:integer
- end;
- halfregtype = record
- al,ah,bl,bh,cl,ch,dl,dh:byte
- end;
-
- const {put 'regs' in the code segment
- by giving variables initial values}
- regs : regtype = (ax:0;bx:0;cx:0;dx:0;bp:0;si:0;di:0;
- ds:0;es:0;flags:0);
- feeding_char :boolean = FALSE;
- no_cr :boolean = FALSE;
- j :integer = 1;
- saveds :integer = 0;
-
- var
- savreg :regtype; {define a variable for the
- register structures}
- halfregs :halfregtype absolute regs; {and for the
- half registers}
-
- i :integer;
- trash_line :boolean;
- last_lines :array [0..numb_saved] of
- array[1..60] of integer;
- cursorpos :integer;
- {.pa}
- {include the window manipulation software}
- {$i window.pas}
- {.pa}
- {the following code prints out the previous n commands in
- the window previously opened up}
-
- procedure printchoices;
- var
- i,j : integer;
- outchar : byte;
-
- begin
- for i := 2 to numb_saved+1 do {loop thru the
- saved commands}
- begin
- GoToXY(2,i);
- {put up the function key}
- Write('F');Write(((i-1) mod 10):1);Write(')');
- j := 1;
- {now the saved command}
- while ((last_lines[i-1][j] and $FF) <> CR)
- and (j <> (windowwidth-5)) do
- begin
- Write(Chr(last_lines[i-1][j] and $FF));
- j := j + 1
- end
- end
- end;
- {.pa}
- {the following routine saves a keystroke in the
- command push down stack. If the key has some special
- meaning, this routine attempts to interpret it; e.g.
- 'del' deletes previous character, etc. It can only
- interpret so much, and even then it only knows
- COMMAND.COM's rules}
-
- procedure save_key;
- begin
- last_lines[0][j] := regs.ax;
- if (j < 60) and not trash_line then
- j := j + 1;
- case halfregs.al of {if that was a...}
- DEL: {...delete then...}
- if j > 2 then {...delete char}
- j := j - 2
- else
- j := 1;
-
- ESCAPE: {...escape then...}
- j := 1; {...delete the line}
-
- CntrlC: {...Cntrl C then...}
- j := 1; {...delete the line}
-
- Unprintable: {...non ascii characters...}
- if (regs.ax = 0) or {...if its BREAK...}
- (regs.ax = $3F00) then {...or F5 then...}
- j := 1 {...just clear the line; else...}
- else
- trash_line := TRUE; {...trash the remainder}
- {of the line to next CR}
-
- CR: {...if carriage return then...}
- begin
- if trash_line then {if trash line flag set...}
- j := 1; {...dont save the line...}
- if j > 2 then {..and dont save empty lines...}
- {...else push command on 'stack',...}
- for i := numb_saved downto 1 do
- last_lines[i] := last_lines[i-1];
- for i := 1 to 60 do {...clear last entry,...}
- last_lines[0][i] := $07 shl 8 + CR;
- j := 1 {...and reset pointer}
- end
- end;
-
- if j = 1 then {if the line becomes empty...}
- trash_line := FALSE {...then stop trashing the line}
- end;
- {.pa}
- {this code processes interrupts to the keyboard BIOS interrupt (16 hex)}
-
- procedure process_intr;
- begin;
- {$i savereg.pas} {save the input registers}
- if halfregs.ah = 0 then {if this is character request...}
- begin
- {if we were in the middle of
- spooling chars...}
- if feeding_char then
- begin {...fetch the next character from
- the command stack and return that}
- regs.ax := last_lines[i][j];
- j := j + 1;
- {if this was the last char...}
- if (halfregs.al = CR) or (j > 60) then
- begin
- feeding_char := false; {...turn spooling off}
- j := 1;
- if no_cr then
- regs.ax := $0;
- no_cr := false
- end
- end
- else
- begin {(we are not in the middle of spooling)}
- Intr (user_int, regs); {perform the BIOS call
- the caller asked for}
- {if this wasn't "our" char...}
- if halfregs.ah <> our_char then
- save_key {...save the keystroke...}
- else
- begin
- savreg.ax := $0300; {fetch the current...}
- savreg.bx := $0; {...cursor position}
- Intr($10,savreg);
- cursorpos := savreg.dx;
-
- openwindow; {open up the display window}
- printchoices; {now print the command stack}
- regs.ax := $0; {read a character...}
- Intr(user_int,regs);{...from the keyboard}
-
- {make F0 maps to 1, F1 to 2, etc.}
- i := halfregs.ah - scan_offset;
- if (i > 25) and (i < 37) then
- begin {shift function keys act like normal
- function keys except no return on end}
- i := i - 25;
- no_cr := true
- end;
- if (i > 0) and (i <= 10) then
- begin {if input was a function key
- give him the 1st char of his choice...}
- regs.ax := last_lines[i][1];
- if halfregs.al <> CR then
- begin {...and set the flag to begin feeding
- him the remainder of the command every
- time he asks for a char from the keyboard}
- feeding_char := true;
- j := 2
- end;
-
- end
- else {not function key -- just save it}
- save_key;
- closewindow; {put what was there back}
-
- savreg.ax := $0200; {replace cursor}
- savreg.bx := $0;
- savreg.dx := cursorpos;
- Intr($10,savreg)
- end
- end
- end
- else {he's not trying to read a char}
- if feeding_char then {if he's spooling chars...}
- {...clear the z-flag}
- regs.flags := regs.flags and $FFBF
- else
- Intr(user_int,regs);
-
- {$i restreg.pas} {restore the registers from 'reg'}
- inline($CA/$02/$00) {RETF 02 - return to caller}
- end;
- {.pa}
- {this section of code installs the interrupt
- routine and makes it a permanently
- resident interrupt borrower}
-
- {the following dos calls are used:
- sys 25- install interrupt address
- input al = int number, ds:dx = address to install
-
- sys 35- get interrupt address
- input al = int number
- output es:bx = address in interrupt
-
- int 27- terminate and stay resident
- input dx = size of resident program
- }
- begin {**main**}
- {initialize the variables which the interrupt
- service routine will use}
-
- for i := 0 to numb_saved do
- for j := 1 to 60 do
- last_lines[i][j] := $07 shl 8 + CR;
- j := 1; trash_line := FALSE;
- saveds := Dseg; {save the data segment locally}
-
- {now install the interrupt routine}
-
- savreg.ax := $35 shl 8 + user_int; {check to make sure
- int not already used}
- Intr($21,savreg);
- if savreg.es <> $00 then
- begin
- WriteLn ('Interrupt in use -- cant install LASTCOM');
- Intr($20,savreg)
- end
- else
- begin
- WriteLn ('Installing LASTCOMMAND --');
- WriteLn (' press AltF10 to select last command');
- {get the address that was there}
- savreg.ax := $35 shl 8 + kybrd_int;
- Intr($21,savreg);
- {put the address in the
- user interrupt}
- savreg.ax := $25 shl 8 + user_int;
- savreg.ds := savreg.es;
- savreg.dx := savreg.bx;
- Intr($21,savreg);
- {install interrupt system call}
- savreg.ax := $25 shl 8 + kybrd_int;
- savreg.ds := cseg;
- {put our routine address}
- savreg.dx := Ofs(process_intr);
- Intr ($21,savreg);
-
- {now terminate and stay resident}
-
- savreg.dx := prog_size;
- Intr ($27,sa