В итоге сделал так:

## Код:
```
MODULE TmpM3;

   IMPORT SYSTEM, Kernel;

   TYPE
      Msg* = POINTER TO ABSTRACT RECORD END;
      MsgHandler* = POINTER TO ABSTRACT RECORD END;
      Table* = POINTER TO ABSTRACT RECORD END;

   PROCEDURE (h: MsgHandler) Handle- (msg: Msg), NEW, ABSTRACT;

   PROCEDURE (t: Table) Lookup- (id: BYTE; OUT len: BYTE; OUT type: INTEGER; OUT ok: BOOLEAN), NEW, ABSTRACT;

   PROCEDURE DataReceived* (id: BYTE; IN a: ARRAY OF BYTE; len: BYTE; h: MsgHandler; table: Table);
      VAR type: INTEGER; len1: BYTE; ok: BOOLEAN;
         msg: Msg;
   BEGIN
      table.Lookup(id, len1, type, ok);
      IF ok & (len = len1) THEN
         Kernel.NewObj( msg, SYSTEM.VAL( Kernel.Type, type) );
         SYSTEM.MOVE( SYSTEM.ADR(a), SYSTEM.ADR(msg^), len );
         ASSERT(msg # NIL);
         h.Handle(msg)
      END
   END DataReceived;

END TmpM3.
```


## Код:
```
MODULE TmpM4;

   (* этот модуль должен создаваться автоматически *)

   IMPORT SYSTEM, TmpM3;

   TYPE
      MsgA* = POINTER TO MsgADesc;
      MsgADesc* = RECORD (TmpM3.Msg)
         a-, b-: INTEGER
      END;
      MsgB* = POINTER TO MsgBDesc;
      MsgBDesc* = RECORD (TmpM3.Msg)
         x-, y-, z-: SHORTREAL
      END;

      Table = POINTER TO RECORD (TmpM3.Table) END;

   VAR
      table-: Table;

   PROCEDURE (t: Table) Lookup (id: BYTE; OUT len: BYTE; OUT type: INTEGER; OUT ok: BOOLEAN);
   BEGIN
      CASE id OF 10H:
         len := SIZE(MsgADesc); type := SYSTEM.TYP(MsgADesc);
         ok := TRUE
      | 11H:
         len := SIZE(MsgBDesc); type := SYSTEM.TYP(MsgBDesc);
         ok := TRUE
      ELSE
         ok := FALSE
      END
   END Lookup;

BEGIN
   NEW(table)
END TmpM4.
```


## Код:
```
MODULE TmpM5;

   IMPORT TmpM3, TmpM4, StdLog;

   TYPE
      MsgHandler = POINTER TO RECORD (TmpM3.MsgHandler) END;

   PROCEDURE (h: MsgHandler) Handle (msg: TmpM3.Msg);
   BEGIN
      WITH msg: TmpM4.MsgA DO
         StdLog.String("A:"); StdLog.Int(msg.a); StdLog.Int(msg.b); StdLog.Ln 
      | msg: TmpM4.MsgB DO
         StdLog.String("B:");
            StdLog.Real(msg.x); StdLog.Real(msg.y); StdLog.Real(msg.z);
            StdLog.Ln
      END
   END Handle;

   PROCEDURE Test*;
      VAR h: MsgHandler;
         a: ARRAY 12 OF BYTE;
   BEGIN
      a[0] := 1; a[1] := 1; a[2] := 0; a[3] := 0;
      a[4] := 2; a[5] := 1; a[6] := 0; a[7] := 0;
      NEW(h);
      TmpM3.DataReceived(10H, a, 8, h, TmpM4.table)
   END Test;

END TmpM5.

!(Q)TmpM5.Test
```
