{$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 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;
    windowwidth = 40;
    windowlength = 12;                 {=numb_saved + 2}
    CR = $D;
    user_int = $68;                    {place to put out interrupt}
    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                                  {pur 'regs' in the code segment}
    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 structures}
    halfregs       :halfregtype absolute regs; {and for the half registers}

    i              :integer;
    last_lines     :array [0..numb_saved] of array[1..60] of integer;
    cursorpos      :integer;
{.pa}
{include the window manipulation software}
{$i window.inc}
{.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);
      Write('F');Write(((i-1) mod 10):1);Write(')'); {put up the command number}
      j := 1;
      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}
{this code processes interrupts to the get_char BIOS interrupt}

procedure process_intr;
begin;
{$i regsave.inc}                       {save the input registers}
    if halfregs.ah = 0 then            {if this is character request...}
    begin
         if feeding_char then          {if we were in the middle of spooling chars...}
         begin
              regs.ax := last_lines[i][j]; {...fetch the next character...}
              j := j + 1;
              if (halfregs.al = CR) or (j > 60) then {...if this was the last character...}
              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}
              if halfregs.ah <> our_char then {if this wasn't "our" character...}
              begin
                   last_lines[0][j] := regs.ax; {...save the keystroke...}
                   if j < 60 then
                        j := j + 1;
                   if halfregs.al = CR then     {...if that was a cr...}
                   begin
                        j := 1;                 {...pop down to next entry...}
                        if Lo(last_lines[0][1]) <> 0 then {...if the first char was printable...}
                             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
                   end
              end
              else
              begin
                   savreg.ax := $0300;   {fetch the current cursor position}
                   savreg.bx := $0;
                   Intr($10,savreg);
                   cursorpos := savreg.dx;

                   openwindow;         {open up the display window}
                   printchoices;       {now print the last commands}
                   regs.ax := $0;      {read a character from the keyboard}
                   Intr(user_int,regs);

                   i := halfregs.ah - scan_offset;   {note that 0 maps to 1}
                   if (i > 25) and (i < 37) then
                   begin
                        i := i - 25;
                        no_cr := true
                   end;
                   if (i > 0) and (i <= 10) then
                   begin               {legal input}
                        regs.ax := last_lines[i][1]; {give him the first char}

                        if halfregs.al <> CR then
                        begin
                             feeding_char := true;
                             j := 2
                        end;

                   end;
                   closewindow;        {put what was there back}

                   savreg.ax := $0200; {reposition cursor}
                   savreg.bx := $0;
                   savreg.dx := cursorpos;
                   Intr($10,savreg)
              end
         end
    end
    else                               {he's not trying t read a character}
         if feeding_char then          {if he's spooling characters...}
              regs.flags := regs.flags and $FFBF {...clear the zero bit}
         else
              Intr(user_int,regs);

{$i regrstor.inc}                      {restore the registers from 'reg'}
    inline($CA/$02/$00)                {RETF 02}
end;
{.pa}
{this section of code installs the interrupt routine and makes it 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;
    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
         WriteLn ('Interrupt in use -- cant install LASTCOMMAND')
    else
    begin
         WriteLn ('Installing LASTCOMMAND -- press AltF10 to select last command');

         savreg.ax := $35 shl 8 + kybrd_int;{get the address that was there}
         Intr($21,savreg);

         savreg.ax := $25 shl 8 + user_int; {put the address in the user interrupt}
         savreg.ds := savreg.es;
         savreg.dx := savreg.bx;
         Intr($21,savreg);

         savreg.ax := $25 shl 8 + kybrd_int;{install interrupt system call}
         savreg.ds := cseg;
         savreg.dx := Ofs(process_intr);    {put our routine address}
         Intr ($21,savreg);

         {now terminate and stay resident}

         savreg.dx := prog_size;
         Intr ($27,savreg)
    end
end.