Assembler в Pascal и в C#

111
19 июня 2021, 13:30

Есть программа на паскале, написанная изначально не мной. Перевожу ее на C#. В одной из части программы идет весьма своеобразный расчет контрольной суммы файлов. Контрольная сумма хранится в базе в формате bigint и функция расчета контрольной суммы выглядит так:

function GetCheckSum(FileName: string): DWORD;
var
  F: file of DWORD;
  P: Pointer;
  Fsize: DWORD;
  Buffer: array[0..500] of DWORD;
begin
  FileMode := 0;
  AssignFile(F, FileName);
  Reset(F);
  Seek(F, FileSize(F) div 2);
  Fsize := FileSize(F) - 1 - FilePos(F);
  if Fsize > 500 then
    Fsize := 500;
  BlockRead(F, Buffer, Fsize);
  Close(F);
  P := @Buffer;
  asm
        XOR     eax, eax
        XOR     ecx, ecx
        mov     edi, P
    @again:
        add     eax, [edi + 4 * ecx]
        inc     ecx
        cmp     ecx, Fsize
        jl      @again
        mov     @result, eax
  end;
end;

Подскажите, что происходит в Ассемблере и как бы могла выглядеть данная конструкция в C#?

Воспользовался следующим:

private long GetCheckSum(string FilePath)
{
    byte[] FBytes = File.ReadAllBytes(FilePath);
    int FSize = FBytes.Length / 2;
    int FToRead = FSize + 500 > FBytes.Length ? FBytes.Length : FSize + 500;
    long FSum = 0;
    for (int i = FSize; i < FToRead; i++)
    {
        FSum += FBytes[i];
    }
    return FSum;
}

На мой взгляд получилось идентично. Поправьте, если я не прав.

Answer 1

Просто сумма 32-разрядных беззнаковых чисел (по модулю 2^32, если возникнет переполнение).

UInt32 buffer[500];    
sum = 0;
for (i = 0; i < fsize; i++)
  sum += buffer[i];

Файл читается как бинарный, суммирование осуществляется для чисел из второй половины файла (благодаря Seek), их количество не превышает 500. Т.о. для размера файла 8000 байт (2000 чисел) прочитаны будут числа с номерами от 1000 по 1499. Для размера файла 800 байт (200 чисел) прочитаны будут числа с номерами от 100 по 199.

Answer 2

Пояснения на c-подобном псевдокоде

    XOR     eax, eax // DWORD eax = 0; // здесь будет храниться результат
    XOR     ecx, ecx // int ecx = 0; // будет работать как индекс
    mov     edi, P // DWORD * edi = buffer;
 @again:
    add     eax, [edi + 4 * ecx] // eax += edi[ecx]; // На ассемблере *4 потому что размер элемента 4 байта
    inc     ecx // ecx++;
    // if(ecx<Fsize) goto again;
    cmp     ecx, Fsize  
    jl      @again
    mov     @result, eax // result = eax;
Answer 3

Сделал так:

private long GetCheckSum(string FilePath)
{
    byte[] FBytes = File.ReadAllBytes(FilePath);
    int FSize = FBytes.Length / 2;
    int FToRead = FSize + 500 * 4 > FBytes.Length ? FBytes.Length : FSize + 500 * 4;
    long FSum = 0;
    for (int i = FSize; i < FToRead; i += 4)
    {
        FSum += BitConverter.ToUInt32(FBytes, i);
    }
    return FSum;
}

Вроде как, возвращает то, что надо. BitConverter.ToUInt32 конвертирует 4 байта в массиве с указанной позиции в 32-битное беззнаковое число. Сама функция GetCheckSum читает последовательность байтов из файла. Узнает где середина файла и начиная с нее перебегает через максимум 2000 элементов (если столько есть) с шагом 4. То есть по факту максимум 500 шагов. С помощью функции BitConverter.ToUInt32 получает сумму беззнаковых чисел и возвращает ее.

READ ALSO
Как открыть cmd.exe от системного пути?

Как открыть cmd.exe от системного пути?

Всем привет! Нужно, чтобы cmdexe открывался по пути "{Системный диск}\Windows\System32"

104
MySql запрос (ругаеться на 2 Left Join)

MySql запрос (ругаеться на 2 Left Join)

Не выходит исправить ошибку 2 errors were found during analysis

119