Сообщение от
andrews
Самому все писать на asm-е (без использования биос-а или библиотек) очень непросто.
Смотря какие возможность заложены в ассемблер и смотря какая система команд.
Сообщение от
andrews
Код надо структурировать и комментировать
Да неужели? Проблема только в том, что существует большая группа программистов (не только на ретро-платформы), которым на это насрать - и им не поможет никакой вундерязык.
- - - Добавлено - - -
Сообщение от
Oleg N. Cher
Если бы надо было "ехать", то ехали бы
Кому надо ехать - они едут. На любом языке, который сочли подходящим. Что бы им не толдычили "адепты" "правильных" языков.
Пример из C# (причём тут, по сути, даже не возможности языка, а возможности реализации его окружения)
Код:
[Serializable]
[Macro(".CILHD")]
public class CILHeader
{
[Map(".CLCOD: .BLKB 1")] public byte Code; // HeaderCode
[Map(".CLLEN: .BLKB 1")] public byte Length; // HeaderLength
[Map(".CHTIM: .BLKW 2")] public DOS11Time Time;
[Map(".CHDAT: .BLKW 1")] public DOS11Date Date;
[Map(".CHBSZ: .BLKW 1")] public UInt16 BlockSize;
[Map(".CHIMC: .BLKW 1")] public UInt16 ImageCount;
[Map(".CHHSZ: .BLKW 1")] public UInt16 HeaderSize;
[Map(".CHFS0: .BLKW 1")] public UInt16 FileSize0;
[Map(".CHFS1: .BLKW 1")] public UInt16 FileSize1;
[Map(".CHFS2: .BLKW 1")] public UInt16 FileSize2;
[Map(".CHFS3: .BLKW 1")] public UInt16 FileSize3;
public SmartArray<byte> RAWData;
public COMD[] COMDs;
static CILHeader()
=> Helpers.StaticFieldsValueUpdater<CILHeader>();
public CILHeader(IArray<byte> data, int baseOffset = 0)
=> this.OneWayMapper(data, baseOffset);
}
[Serializable]
[Macro(".COMD")]
public class COMD
{
[Map(".CLCOD: .BLKB 1")] public byte Code; // COMDCode
[Map(".CLLEN: .BLKB 1")] public byte Length; // COMDLength
[Map(".CDLA: .BLKW 1")] public UInt16 ProgramLoadAddress; // Program load address
[Map(".CDPSZ: .BLKW 1")] public UInt16 ProgramSize; // Program size in bytes
[Map(".CDXFR: .BLKW 1")] public UInt16 XFRAddress; // Program transfer address
[Map(".CDODT: .BLKW 1")] public UInt16 ODTXFRAddress; // ODT(debugger) transfer address
[Map(".CDRBN: .BLKW 1")] public UInt16 RelativeBlockNumber; // Relative block# within the file
[Map(".CDPNM: .BLKW 2", PDP11Type.Radix50)] public string Name; // RAD50 program name
[Map(".CDIDN: .BLKW 2", PDP11Type.Radix50)] public string Ident; // RAD50 .IDENT of the program
[Map(".CDTIM: .BLKW 2")] public DOS11Time Time; // Program creation time, in ticks
[Map(".CDDAT: .BLKW 1")] public DOS11Date Date; // Program creation date
public SmartArray<byte> RAWData;
static COMD()
=> Helpers.StaticFieldsValueUpdater<COMD>();
public COMD(IArray<byte> data, int baseOffset = 0)
=> this.OneWayMapper(data, baseOffset);
}
На вход - массив (сырых) байт, на выходе - вполне себе объекты. Причём OneWayMapper-у фиолетово - что за объект (какого класса) надо создать - всё берётся из описания класса. Причём размеры конкретного поля могут быть не фиксированы - и браться из другого поля этого или родительского объекта.
И в этом вариант мне не надо писать парсер сырых байт под ещё один описанный класс объекта - всё делается ОДНОЙ строчкой
Код:
private static void CILImageDescription(SmartArray<byte> sarr)
{
cilHeader = new CILHeader(sarr);
И при таком подходе на цепляние к моему дизассемблеру ещё одного формата файла мне поднадобилось пол дня - на описание классов и на разборки с тем - как же это всё таки в байтах хранится - ибо документации - кот наплакал и некоторые вещи были определены опытным путём.
- - - Добавлено - - -
А, да, для возможности создавать образы, в которых была бы одна из ФС операционок с PDP-11, понадобился мне двунаправленный маппинг. Ок, пример. Тут несколько замороченней описание класса, но я пока не разобрался, как в run-time создать прокси-класс-наследник, поэтому пока так. Может, как руки дойдут - переделаю.
Код:
[Serializable]
[Macro(".DIEDF")] public class DirectoryEntry_S : BaseMappedClass // directory entry definition
{
[Map("E.STAT: .BLKW 1")] public F_ESTAT Status
{ get => (F_ESTAT)this.GetValue(nameof(Status)); set => this.SetValue(nameof(Status), value); } // entry status word
[Map("E.NAME: .BLKW 3", PDP11Type.Radix50)] public string FullName
{ get => (string)this.GetValue(nameof(FullName)); set => this.SetValue(nameof(FullName), value); } // RAD50 file name and extension
[Set(".=.-6")]
[Map("E.FNAM: .BLKW 2", PDP11Type.Radix50)] public string Name
{ get => (string)this.GetValue(nameof(Name)); set => this.SetValue(nameof(Name), value); } // RAD50 file name
[Map("E.FEXT: .BLKW 1", PDP11Type.Radix50)] public string Extension
{ get => (string)this.GetValue(nameof(Extension)); set => this.SetValue(nameof(Extension), value); } // RAD50 file extension
[Map("E.LENG: .BLKW 1")] public RT11BlockNum FileSize
{ get => (RT11BlockNum)this.GetValue(nameof(FileSize)); set => this.SetValue(nameof(FileSize), value); } // size of area described, in blocks
[Map("E.TIME: .BLKW 1")] public TPDP11Word Time
{ get => (TPDP11Word)this.GetValue(nameof(Time)); set => this.SetValue(nameof(Time), value); } // time of file closing(seconds/3 since midnight) <fut>
[Set(".=.-2")]
[Map("E.USED: .BLKW 1")] public TPDP11Word Used
{ get => (TPDP11Word)this.GetValue(nameof(Used)); set => this.SetValue(nameof(Used), value); } // <res> if not TENT
[Set(".=.-2")]
[Map("E.CHAN: .BLKB 1")] public byte Channel
{ get => (byte)this.GetValue(nameof(Channel)); set => this.SetValue(nameof(Channel), value); } // channel number for tentative file
[Map("E.JNUM: .BLKB 1")] public byte JobNum
{ get => (byte)this.GetValue(nameof(JobNum)); set => this.SetValue(nameof(JobNum), value); } // job number for tentative file
[Map("E.DATE: .BLKW 1")] public RT11Date Date
{ get => (RT11Date)this.GetValue(nameof(Date)); set => this.SetValue(nameof(Date), value); } // creation date
[Map("E.EXBT: .BLKB ?.DIHD/D.EXTR")] public IArray<byte> ExtraBytes
{ get => (IArray<byte>)this.GetValue(nameof(ExtraBytes)); set => this.SetValue(nameof(ExtraBytes), value); } // header extra bytes
//[Map("E.ELEN:")] public static readonly TPDP11Word Length; // basic entry len
static DirectoryEntry_S()
=> Helpers.StaticFieldsValueUpdater<DirectoryEntry_S>();
public DirectoryEntry_S(MappedToByteArray data, int baseOffset = 0)
: base(data, baseOffset)
{}
public DirectoryEntry_S()
=> this.TwoWayMapper();
}
...
DirectoryEntry_S temp = new()
{
Status = F_ESTAT.E_EMPTY
//, FullName = ""
, Name = "TEST"
, Extension = "TXT"
, FileSize = 0
, Time = 0
, Used = 0
, Channel = 0
, JobNum = 0
, Date = new(0)
};
В этом примере свойство ExtraBytes - массив переменной длины и его длина (если он вообще будет) берётся из другого поля родительского объекта. И при записе какого-либо поля объекта - моментальное изменение в байтовом массиве. Так что инициализация и наполнение ФС становится тривиальным делом.