Одна из "проблем", нарисовавшихся первоначально при написании ImgUtil, а затем и DisAsm-11 - в силу сложной структуры .TSK - необходимость переноса определений всяких констант, смещений и т.п., которые были сделаны через макросы в макробиблиотеках, выглядевшие примерно так:
Код:
	.MACRO	LBLDF$	L,B
	.ASECT
.=0
R$LNAM:'L'.BLKW	2
R$LSA:'L' .BLKW	1
R$LHGV:'L'.BLKW	1
R$LMXV:'L'.BLKW	1
R$LLDZ:'L'.BLKW	1
R$LMXZ:'L'.BLKW	1
R$LOFF:'L'.BLKW	1
R$LWND:'L'.BLKW	1
R$LSEG:'L'.BLKW	1
R$LFLG:'L'.BLKW	1
R$LDAT:'L'.BLKW	3
R$LSIZ:'L'.BLKW	0
LD$ACC='B'100000
LD$RSV='B'040000
LD$CLS='B'020000
LD$SMV='B'010000
LD$SCL='B'000200
LD$AMK='B'000060
LD$RES='B'000040
LD$SUP='B'000010
LD$REL='B'000004
LD$TYP='B'000002
....
Первоначально всё это дело переносилось руками 1) в описание констант-смещений, а затем 2) создавались описания классов для удобства программирования, после чего это выглядело примерно так:
Код:
  [Flags]
  public enum LD_FLG: UInt16
  {
      LD_TYP =     2  //      2 Shared region type (1 - common, 1 - library)
    , LD_REL =     4  //      4 Position-independent code (PIC) flag (1 - PIC)
    , LD_SUP =     8  //     10 Supervisor mode library (1 - yes)
    , LD_RES =    32  //     40 Library has memory-resident overlays
    , LD_CLS =  8192  //  20000 Library is part of a cluster
    , LD_RSV = 16384  //  40000 APR was reserved
    , LD_ACC = 32768  // 100000 Access intention (1 - rw, 0 - ro)
  }

...

  public static class RLBDF
  {
    public static readonly UInt16 R_LNAM =  0;                    // 4 Library/common name
    public static readonly UInt16 R_LSA  = (ushort)(R_LNAM + 4);  // 2 Base address of library
    public static readonly UInt16 R_LHGV = (ushort)(R_LSA  + 2);  // 2 Highest address in first library window
    public static readonly UInt16 R_LMXV = (ushort)(R_LHGV + 2);  // 2 Highest address in library
    public static readonly UInt16 R_LLDZ = (ushort)(R_LMXV + 2);  // 2 Library load size (64 byte blocks)
    public static readonly UInt16 R_LMXZ = (ushort)(R_LLDZ + 2);  // 2 Library max size (64 byte blocks)
    public static readonly UInt16 R_LOFF = (ushort)(R_LMXZ + 2);  // 2 Library offset into region
    public static readonly UInt16 R_LWND = (ushort)(R_LOFF + 2);  // 2 Number of library window blocks
    public static readonly UInt16 R_LSEG = (ushort)(R_LWND + 2);  // 2 Size of library segment desciptors
    public static readonly UInt16 R_LFLG = (ushort)(R_LSEG + 2);  // 2 Library flag word
    public static readonly UInt16 R_LDAT = (ushort)(R_LFLG + 2);  // 2+2+2 Library creation date - year, month, day
    public static readonly UInt16 R_LSIZ = (ushort)(R_LDAT + 6);  // length
  }
и этот процесс был как долгим в принципе, так и чреват ошибками. Была уже мысль написать некий скрипт, который на основе макроса создавал бы похожие описания, но..

Мне как то не нравилась сама эта идея (да и написание скрипта - тоже не быстро и не сразу под всякие варианты), потому как она не решала две другие проблемы - короткие имена членов класса и преобразование "сырых" данных, прочитанных из файла (по сути - массив байт) в экземпляры класса.

Да, имена членов класса можно после написания программы можно переименовать (и VS поможет найти и поправить старые имена по всему проекту), но после этого терялась связь с первоначальными именами - если только не переносить их - руками - во что-нибудь типа комментариев.

Плюс - под каждый класс надо было писать конструктор, типа такого:
Код:
    public SharedRegion(byte[] data)
    {
      Name                      = data.GetDWord(RLBDF.R_LNAM).Radix50().Trim();
      BaseAddress               = data.GetWord(RLBDF.R_LSA);
      HighetFirstWinAddress     = data.GetWord(RLBDF.R_LHGV);
      HighestVirtualAddress     = data.GetWord(RLBDF.R_LMXV);
      LoadSizeIn64bBlocks       = data.GetWord(RLBDF.R_LLDZ);
      MaximumSizeIn64bBlocks    = data.GetWord(RLBDF.R_LMXZ);
      OffsetIntoPartition       = data.GetWord(RLBDF.R_LOFF);
      WindowBlocksCount         = data.GetWord(RLBDF.R_LWND);
      SizeOfSegmentDescriptors  = data.GetWord(RLBDF.R_LSEG);
      Flags                     = (LD_FLG)data.GetWord(RLBDF.R_LFLG);
      CreationDate              = new RSXDate(data.GetTWord(RLBDF.R_LDAT));
    }
который принимает на вход массив байт и парсит его по полям класса. Ещё одна совершенно "ручная" и нудная задача.

И вот сегодня, почесав почесуемое, решил я использовать возможности .NET (однажды такое уже было, но несколько в другой области). А именно - аттрибуты и рефлексию Примерно через три часа программирования - остался только один класс, который выглядит примерно так:

Код:
  public class SharedRegion
  {
    [Map("R$LNAM: .BLKW 2", PDP11Type.Radix50)] public string  Name;                      // Shared region name
    [Map("R$LSA:  .BLKW 1")]                    public UInt16  BaseAddress;               // Base virtual address
    [Map("R$LHGV: .BLKW 1")]                    public UInt16  HighetFirstWinAddress;     // Highest address in first library window
    [Map("R$LMXV: .BLKW 1")]                    public UInt16  HighestVirtualAddress;     // Highest address in library
    [Map("R$LLDZ: .BLKW 1")]                    public UInt16  LoadSizeIn64bBlocks;       // Library load size (64 byte blocks)
    [Map("R$LMXZ: .BLKW 1")]                    public UInt16  MaximumSizeIn64bBlocks;    // Library max size (64 byte blocks)
    [Map("R$LOFF: .BLKW 1")]                    public UInt16  OffsetIntoPartition;       // Library offset into region
    [Map("R$LWND: .BLKW 1")]                    public UInt16  WindowBlocksCount;         // Number of library window blocks
    [Map("R$LSEG: .BLKW 1")]                    public UInt16  SizeOfSegmentDescriptors;  // Size of library overlay segment descriptor
    [Map("R$LFLG: .BLKW 1")]                    public LD_FLG  Flags;                     // Library flag word
            public bool IsLibrary                 => ((Flags & LD_FLG.LD_TYP) != 0);      //      2 Shared region type (1 - common, 1 - library)
            public bool IsPositionIndependentCode => ((Flags & LD_FLG.LD_REL) != 0);      //      4 Position-independent code (PIC) flag (1 - PIC)
            public bool IsSupervisorModeLibrary   => ((Flags & LD_FLG.LD_SUP) != 0);      //     10 Supervisor mode library (1 - yes)
            public bool HasMemoryResidentOverlays => ((Flags & LD_FLG.LD_RES) != 0);      //     40 Library has memory-resident overlays
            public bool IsClusterPart             => ((Flags & LD_FLG.LD_CLS) != 0);      //  20000 Library is part of a cluster
            public bool IsAPRReserved             => ((Flags & LD_FLG.LD_RSV) != 0);      //  40000 APR was reserved
            public bool IsRWAccessIntention       => ((Flags & LD_FLG.LD_ACC) != 0);      // 100000 Access intention (1 - rw, 0 - ro)

    [Map("R$LDAT: .BLKW 3")]                    public RSXDate CreationDate;              // Library creation date

    [Map("R$LSIZ:")]                            public static UInt16 Length;              // Block length
а его конструктора - так:

Код:
    public SharedRegion(byte[] data)
    {
      this.Mapper(data);
    }
И вот этот метод Mapper - он общий для любого класса, описание которое построено таким же образом. И большую часть такого описания класса можно сделать с помощью макросов в FAR-е на основе исходного макроса. Останется только добавить название полей в класс для C# и нарисовать простейшие конструкторы. Всё. Длина поля, как можно видеть, берётся из макроса, а смещение в блоке байт высчитывается на основении описаний в методе Mapper. Плюс этот метод может осуществлять и некоторые преобразование - скажем, поле Name ("R$LNAM") хранится как два слова в формате Radix50 - но в объект класса C# будет перенесено как строка.

Пока это больше концептуальная идея (есть ещё нюансы, над которыми надо подумать - скажем, пока не нравится, как выглядят и как используются описания флагов), но технически её УЖЕ можно задействовать. Одну описанную пару классов я переделал, надо переделать вторую, а вот описание третьего блока данных уже будет делаться сразу по такой схеме - а значит - конструктор можно будет уже не писать

Ну и как в очередной раз буду развлекаться в ImgUtil - тоже начну переделывать по такой схеме