🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: alt.msdos.programmer
Article #1456

Re: oop in pascal - help needed!

#1456
From: Michael Tippach
Date: Wed, 19 Aug 1998 00:00
230 lines
7578 bytes
In article <35DAD0FB.1E9CCA28@physik.tu-muenchen.de>, Christopher
Eltschka <celtschk@physik.tu-muenchen.de> writes
>Merkosh wrote:
>> 
>> Hi,
>> 
>> I want to program an object that allows an easy installation of a keyboard
>> interrupt handler. The whole thing should look something like this:
>> 
>> Type    PKeyboard       = ^TKeyboard;
>>         TKeyboard       = Object
>>                 Scan    : Byte;
>>                 Control : Byte;
>> 
>>                 Constructor Init(Scan, Control : Byte);
>>                 Destructor Done;                Virtual;
>>                 Procedure ActionProcedure;      Virtual;
>>         End;
>> 
>> Were Scan is the scancode of the key which has to be pressed to start the
>> procedure ActionProcedure and Control is the control key (shift, alt, etc)
>> which has to be pressed.
>> The following implementation is invalid:
>> 
>>         Procedure TKeyoard.ActionProcedure; Interrupt;
>>         Begin
>>         ...
>>         End;
>> 
>> If anybody has a CLUE how something like this can be implemented with
>> virtual mehtods or can explain why this is impossible, please contact me:
>> <merkosh@iwwn.com.na>
>
>The problem is that interrupt procedures are a very special thing,
>and methods are another very special thing. The speciality
>of interrupt procedures is that they save the complete register set;
>the pseudo arguments which you can give on the definition just
>reference the saved registers on stack.
>Methods OTOH have a hidden extra parameter, the self parameter.
>This parameter tells the method on which object it was invoked.
>Now, an interrupt is never invoked on an object, it's invoked
>on the system (one could, of course, view the system as an object
>with the interrupt table being the virtual method table).
>So there's no self parameter on an interrupt, and therefore an
>interrupt handler cannot be a method.
>
>However, there's one way you can get your effect anyway:
>Maintain a global pointer to the current Keyboard object,
>and let the interrupt procedure call your method on that
>object. Functions called from interrupt functions may be
>just normal functions (except for some limitations due to
>non-reentrancy of DOS), including methods.

There is a more generic way to solve this. The idea is that you
create some code in the private data area of your object and
make the interrupt vector point to that code. This code would
preload the argument list passed to the method to call with a
reference to self and it could also be used to save/restore
registers as necessary in an interrupt procedure.

I've written some code a while ago that does just this. Even
though it is Delphi 32 bit code, it should demonstrate the idea.


unit OOinterr;

{ 

   Allowes routing of hardware interrupts to object instance members.

   Usage Example:


type

   TMyEvenGreaterClass = class(MyGreatClass)

   private
      MyInterrupt: TInterrupt;
      procedure TheUltimateISR;register;

      ... blah blah blah

   
constructor TMyEvenGreaterClass.Create(MyArguments: TMyArguments);
begin

   ...

   MyInterrupt:=TInterrupt.Create(MyHardwareDevice.GetIRQ,self,@TheUltim
ateISR);

   ... 

end;

destructor TMyEvenGreaterClass.Destroy;
begin
   Nuke(SomeOtherStuff);
   MyInterrupt.Destroy;
end;

procedure TMyEvenGreaterClass.TheUltimateISR;register;
begin
   
   ... can access instance members now ...

   if NeedToChain then MyInterrupt.CallOldHandler else
MyInterrupt.Acknowledge;
end;

}

interface

type

   TClassInterruptGlueCode = packed record
      OpMovEAXImm: byte;
      ImmOperand: TObject;
      OpJmpNear: byte;
      Disp32: longint;
   end;

   TInterrupt = class(TObject)

      protected
         Wrapper: TClassInterruptGlueCode;
         OldHandler: pointer;
         IntNumber: byte;
         IntMask: byte;
         WasEnabled: boolean;
         PICPort: word;

      public
         procedure Acknowledge;
         procedure CallOldHandler; register;
         Constructor Create(IRQ: integer; Owner: TObject; Handler:
pointer);
         Destructor Destroy;

   end;

   { Usage example for a keyboard handler }

   TKeyboardInterrupt = class(TInterrupt)

      public
         Constructor Create(Owner: TObject; Handler: pointer);
         procedure Acknowledge;

   end;


implementation
uses dos;

Constructor TInterrupt.Create(IRQ: integer; Owner: TObject; Handler:
pointer);
begin
   if IRQ and 15 > 7 then begin
      IntNumber := (IRQ and 15) + $68;
      PICPort := $A0;
      IntMask := 1 shl (IRQ - 8);
   end else begin
      IntNumber := (IRQ and 15) + 8;
      PICPort := $20;
      IntMask := 1 shl IRQ;
   end;
   if IntNumber = 10 then begin
      IntNumber := $71;
      PICPort := $A0;
      IntMask := 2;
   end;
   WasEnabled := ((port[PICPort+1] and IntMask) = 0);

   { 

      Created code:

      mov  eax,Owner
      jmp  Handler

   }

   Wrapper.OpMovEAXImm := $B8;
   Wrapper.ImmOperand := Owner;
   Wrapper.OpJmpNear := $E9;
   Wrapper.Disp32 := longint(@Handler)-longint(@Wrapper.Disp32)+4;

   GetIntVec(IntNumber,OldHandler);
   SetIntVec(IntNumber,@Wrapper);
   if not WasEnabled then port[PICPort+1] := port[PICPort+1] and
not(IntMask);
   if IntNumber > 15 then port[$21] := port[$21] and $FB;
end;

Destructor TInterrupt.Destroy;
begin
   if not WasEnabled then port[PICPort+1] := port[PICPort+1] or IntMask;
   SetIntVec(IntNumber,OldHandler);
end;

procedure TInterrupt.Acknowledge;
begin
   if IntNumber > 15 then port[$A0]:=$20;
   port[$20]:=$20;
end;

procedure TInterrupt.CallOldHandler;register;assembler;
asm
   call dword ptr [eax][TInterrupt.OldHandler];
end;

Constructor TKeyboardInterrupt.Create(Owner: TOBject; Handler: pointer);
begin
   inherited Create(1, Owner, Handler);
end; 

procedure TKeyboardInterrupt.Acknowledge;
begin
   port[$61]:=port[$61] or $80;
   port[$61]:=port[$61] and $7F;
   inherited Acknowledge;
end;

end.

-- 
Michael Tippach
Technical Details
Message-ID: <HLPFRAALAz21Ews6@wuschel.demon.co.uk>
Path: arc1-ca.pugleaf.net!unknown!not-for-mail
References: <01bdca19$cb831300$640c1bc4@Merkosh.iwwn.com.na> <35DAD0FB.1E9CCA28@physik.tu-muenchen.de>

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f8a86,87a99d11cf64563f
X-Google-Attributes: gidf8a86,public
X-Google-Thread: f9e32,87a99d11cf64563f
X-Google-Attributes: gidf9e32,public
X-Google-Thread: 13ed84,87a99d11cf64563f
X-Google-Attributes: gid13ed84,public
X-Google-Thread: 111659,87a99d11cf64563f
X-Google-Attributes: gid111659,public
X-Google-Thread: 110467,87a99d11cf64563f
X-Google-Attributes: gid110467,public
X-Google-Thread: 10261c,87a99d11cf64563f
X-Google-Attributes: gid10261c,public
X-Google-Thread: 102bfa,87a99d11cf64563f
X-Google-Attributes: gid102bfa,public
X-Google-Thread: 10cc59,87a99d11cf64563f
X-Google-Attributes: gid10cc59,public
From: Michael Tippach <Spammer.Drop.Dead@wuschel.demon.co.uk>
Subject: Re: oop in pascal - help needed!
Date: 1998/08/19
Message-ID: <HLPFRAALAz21Ews6@wuschel.demon.co.uk>
X-Deja-AN: 382751777
Distribution: world
X-NNTP-Posting-Host: wuschel.demon.co.uk:212.228.176.171
References: <01bdca19$cb831300$640c1bc4@Merkosh.iwwn.com.na> <35DAD0FB.1E9CCA28@physik.tu-muenchen.de>
X-Complaints-To: abuse@demon.net
X-Trace: news.demon.co.uk 903558051 nnrp-02:18475 NO-IDENT wuschel.demon.co.uk:212.228.176.171
Organization: really bad, I mean it!
MIME-Version: 1.0
Newsgroups: alt.msdos.programmer,comp.lang.pascal,comp.lang.pascal.borland,comp.lang.pascal.misc,comp.msdos.programmer,fido.ger.pascal,fido.pascal_ger,fido.pascal-ger,fidonet.pascal