Одна из "проблем", нарисовавшихся первоначально при написании ImgUtil, а затем и DisAsm-11 - в силу сложной структуры .TSK - необходимость переноса определений всяких констант, смещений и т.п., которые были сделаны через макросы в макробиблиотеках, выглядевшие примерно так:
Первоначально всё это дело переносилось руками 1) в описание констант-смещений, а затем 2) создавались описания классов для удобства программирования, после чего это выглядело примерно так:Код:.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 ....
и этот процесс был как долгим в принципе, так и чреват ошибками. Была уже мысль написать некий скрипт, который на основе макроса создавал бы похожие описания, но..Код:[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
И вот этот метод Mapper - он общий для любого класса, описание которое построено таким же образом. И большую часть такого описания класса можно сделать с помощью макросов в FAR-е на основе исходного макроса. Останется только добавить название полей в класс для C# и нарисовать простейшие конструкторы. Всё. Длина поля, как можно видеть, берётся из макроса, а смещение в блоке байт высчитывается на основении описаний в методе Mapper. Плюс этот метод может осуществлять и некоторые преобразование - скажем, поле Name ("R$LNAM") хранится как два слова в формате Radix50 - но в объект класса C# будет перенесено как строка.Код:public SharedRegion(byte[] data) { this.Mapper(data); }
Пока это больше концептуальная идея (есть ещё нюансы, над которыми надо подумать - скажем, пока не нравится, как выглядят и как используются описания флагов), но технически её УЖЕ можно задействовать. Одну описанную пару классов я переделал, надо переделать вторую, а вот описание третьего блока данных уже будет делаться сразу по такой схеме - а значит - конструктор можно будет уже не писать
Ну и как в очередной раз буду развлекаться в ImgUtil - тоже начну переделывать по такой схеме![]()




Примерно через три часа программирования - остался только один класс, который выглядит примерно так:
Ответить с цитированием