From: dmurdoch@mast.queensu.ca (Duncan Murdoch)
Subject: Tiny TSR contest entry - 160 bytes.
Organization: Queen's University
Date: Thu, 27 Aug 1992 15:25:06 GMT

In article <2a964930@ralf> Ralf.Brown@B.GP.CS.CMU.EDU writes:
>The TSR I was thinking of swaps two pairs of keys on the keyboard in 53
>bytes of resident code.  You need to keep the first 64 bytes of the PSP
>(though you can clobber the first ten), so the TSR actually takes 128
>bytes when loaded low, 64 when loaded into a UMB.

The quick little Turbo Pascal program below swaps Esc and Backspace, and
X and Z, just to drive you a little crazy.  It takes 160 bytes when 
resident, according to TSRCOM's MAPMEM.  I'd guess it would take 96 bytes in 
a UMB, but I don't know how to put it there.

{$S-,R-}        { Don't want stack or range checking }
program swapkeys;

{ A tiny TSR to swap the ESC and BackSpace keys, and the Z and X keys. }

uses dos,
     opinline, { The "op" units are part of the Object Professional library }
     opdos,
     opstring,
     opint,
     optsr;

const
  Esc       = $01;
  Backspace = $0E;
  XKey      = $2D;
  ZKey      = $2C;

  OrigAddr  = ptr($1234,$5678);      { This will be replaced with the
                                       original INT 15 address }

procedure Int15ISR(bp:word); interrupt;
var
  regs : intregisters absolute bp;
begin
  with regs do
  begin
    if ah = $4f then
      case al of
        Esc:       al := Backspace;
        Backspace: al := Esc;
        Zkey:      al := XKey;
        Xkey:      al := ZKey;
      end;
    JumpToOldIsr(OrigAddr);
  end;
end;

procedure EndMarker;
begin end;

procedure FreeSeg(seg:word);
{ I'm not sure why Turbopower doesn't supply this routine.  Maybe I just 
  missed it. }
var
  regs : registers;
begin
  with regs do
  begin
    ah := $49;
    es := seg;
  end;
  MsDos(Regs);
end;

const
  ISRhandle = 20;    { Used by the ISR management routines in OPINT }

var
  p,q : pointer;     { Used for some memory fiddling }
  signature : word;
  position : word;

  paras : word;
  israddress : pointer;
  thisenv : envrec;
begin
  RestoreAllVectors;       { OPint and OPTSR install several handlers; we 
                             don't want those }

  { Release the environment block }

  CurrentEnv(thisenv);
  Freeseg(thisenv.envseg);

  { Copy our ISR down into the PSP.  Don't overwrite the first 4 paras. }
  { Note that TP generates 10 bytes we don't need at the end of our
    ISR }
  paras := (ofs(endmarker)+15-10) div 16;
  ISRaddress := ptr(Prefixseg+4,0);
  move(@Int15ISR^, ISRaddress^ , 16*paras);

  if not initvector($15,
                     ISRHandle,
                     ISRaddress) then
  begin
    writeln('InitVector failed! Aborting.');
    halt(99);
  end;

  { Now put the old ISR address into our service routine. }

  signature := $5678;
  p := ptr(seg(ISRaddress^),search(ISRaddress^,16*paras, signature, 2));
  q := IsrArray[ISRHandle].OrigAddr;
  Move(q,p^,2);

  signature := $1234;  
  p := ptr(seg(p^),search(ISRaddress^,16*paras, signature, 2));  
  q := pointer(longint(q) shr 16);   { Shift the segment to the offset }
  Move(q,p^,2);

  paras := seg(ISRaddress^) - prefixseg          { the PSP }
           + paras;                              { plus our code }
  { Have to trick StayRes to let us release so much memory }
  heapptr := ptr(pred(prefixseg+paras),0);
  StayRes(Paras, 0);
  writeln('StayRes failed!  We''re not resident.');
end.