Перевод функции на MASM в C++

169
25 февраля 2019, 09:00

Учу C++ и разбирая один проект наткнулся на то что автор часть функций (основных) реализовал на MASM, а очень плохо разбираюсь в MASM, и в интернете ничего толком не нашёл по этой теме. Можете кто-нибудь перевести эти 3 функции в C++, с комментариями что да как? Дальше я уже сам разберусь.

Сам код на MASM (исходник, заголовочный файл):

RES equ 52
HED equ 16
;normalizace [edi] - mantisa nebude zaинnat ani konиit na nulu
;zmмnн esi,edi
norm    proc
    cmp dword ptr [edi-12],0
    jbe @@ret    ;zlomek nebo nula
    call    trim
    mov ecx,[edi-12]
    test    ecx,ecx
    jz  @@ret   ;trim vэsledek vynulovalo
    xor edx,edx
    cmp [edi],edx
    jnz @@ret
    mov eax,edi
@@lp:   add eax,4
    dec dword ptr [edi-4]
    jo  @@nul
    dec ecx
    jz  @@nul
    cmp [eax],edx
    jz  @@lp
@@e:    mov esi,eax
    mov [edi-12],ecx
    cld
    rep movsd
@@ret:  ret
@@nul:  mov dword ptr [edi-12],0
    ret
norm    endp
@NORMX@4:   mov eax,ecx
@normx  proc    uses esi edi
    mov edi,eax
    call    norm
    ret
@normx  endp

;alokuje инslo s mantisou dйlky eax
@ALLOCX@4:  mov eax,ecx
@allocx proc
    push    eax
    lea eax,[eax*4+HED+RES] ;vиetnм hlaviиky a rezervovanйho mнsta
    push    eax
    call    Alloc ; вызывает функцию из C++ - void *Alloc(int size) { return operator new(size); }
    pop edx
    pop ecx
    test    eax,eax
    jz  @@ret
    add eax,HED
    mov [eax-16],ecx
    mov dword ptr [eax-12],0    ;inicializuj na nulu
    mov dword ptr [eax-4],1
    mov dword ptr [eax-8],0
@@ret:  ret
@allocx endp
ALLOCN:
ALLOCNX proc
    mov eax,[esp+8]
    lea eax,[eax*4+HED+RES] ;vиetnм hlaviиky a rezervovanйho mнsta
    push    eax
    mul dword ptr [esp+8]
    push    eax
    call    Alloc
    pop edx
    pop ecx
    test    eax,eax
    jz  @@ret
    lea eax,[eax+edx+HED]
@@lp:   
    sub eax,ecx
    mov edx,[esp+8]
    mov [eax-16],edx
    mov dword ptr [eax-12],0    ;inicializuj na nulu
    mov dword ptr [eax-4],1
    mov dword ptr [eax-8],0
    mov edx,[esp+4]
    mov edx,[esp+4*edx+8]
    mov [edx],eax
    dec dword ptr [esp+4]
    jnz @@lp
@@ret:  ret
ALLOCNX endp

А в C++ они записаны как:

Pint __fastcall ALLOCX(Tint len);                              // Allocate number (len is in Tint units), initialize to zero, return pointer to mantissa
Pint __cdecl ALLOCN(int n, Tint len, ...);          // Allocate n numbers, vararg are variables for pointers
void __fastcall NORMX(Pint x);                                 // Normalize mantissa

Буду признателен.

Answer 1

Базовая структура (описана в заголовочном файле arith.h):

struct Numx {
    Tint
     alen, //allocated data length (in Tint units)
     len,  //used data length (in Tint units), 0=zero, -1=variable, -2=fraction, -5=range, -12=matrix
     sgn,  //sign 0=plus,1=minus
     exp,  //exponent (how many Tints has integer part)
     m,   //mantissa, (most significant digits are at the beginning)
     d,
     reserved[12];
};

Это по сути заголовок и зарезервированные поля. Размер этой структуры (6 + 12)*4 = 72 байта, что равно HED + 4 байта мантиссы + RES. Выделяется структура и дополнительный блок данных функцией ALLOCX:

Pint __fastcall ALLOCX(Tint len);
// Allocate number (len is in Tint units), initialize to zero, return pointer to mantissa

Функция выделяет память нужного размера, делает инициализацию заголовка, возвращает указатель на мантиссу (поле m внутри структуры).

Смысл примерно такой:

Pint ALLOCX(Tint len) {
    Numx * numx = alloc(sizeof(Numx) + sizeof(Tint)*(len-1));
    numx->alen = len; // [eax-16]
    numx->len = 0; // [eax-12]
    numx->exp = 1; // [eax-4]
    numx->sgn = 0; // [eax-8]
    return &(numx->m); // eax
}

Это не подстрочный перевод ассемблерного кода, это именно как оно работает.

В процедуре NORMX наоборот принимается указатель на мантиссу. Если переводить на Си, то по-хорошему нужно из этого указателя вычислить указатель на начало структуры, и дальше работать через имена полей, а не через смещения, как в ассемблерном варианте.

Я бы вообще переделал процедуры, чтобы они работали через указатель на начало структуры, а не указатель на мантиссу. Это вообще довольно странное решение, выгода такого решения не очень понятна.

Ниже добавил подстрочный перевод процедуры norm (мог где-то ошибиться, нужно тестировать):

#ifndef BYTE
typedef char BYTE;
#endif
// mantisa nebude začínat ani končit na nulu // Мантисса не будет начинаться или заканчиваться на ноль
void NORMX(Pint x) {
    // Костыль ниже нужен, чтобы из указателя на мантиссу получить указатель на начало структуры
    struct Numx * numx = 0;
    size_t HED = (BYTE*)&(numx->m) - (BYTE*)numx;  // Для 32-битного кода получится 16
    numx = (Numx*)((BYTE*)x - HED);
    // cmp dword ptr [edi-12],0  ; len
    // jbe @@ret    ;zlomek nebo nula
    if(numx->len <= 0) return;  // zlomek nebo nula // дробь или ноль
    // call    trim  // uříznutí koncových nul [edi] // обрезать конечный нуль [edi]
    trim(x);
    // mov ecx,[edi-12] ; len
    // test    ecx,ecx
    // jz  @@ret   ;trim výsledek vynulovalo
    Tint len = numx->len;
    if(len == 0) return; // trim výsledek na nulu // обрезать результат до нуля
    // xor edx,edx  // edx = 0
    // cmp [edi],edx
    // jnz @@ret
    if(numx->m != 0) return;
    // mov eax,edi
    Pint p = &(numx->m);
    do {
        // @@lp:   add eax,4
        p++;
        // dec [dword edi-4]
        // jo @@nul
        Tint new_exp = numx->exp - 1;
        if(new_exp > numx->exp) { // переполнение
            numx->len = 0;
            return;
        }
        numx->exp = new_exp;
        // dec ecx
        // jz  @@nul
        len--;
        if(len == 0) {
            numx->len = 0;
            return;
        }
        // cmp [eax],edx
        // jz  @@lp
    } while(*p == 0);
    // @@e:    mov esi,eax
    // mov [edi-12],ecx ; len
    numx->len = len;
    // cld
    // rep movsd  ; [dword edi] = [dword esi], edi++, esi++, ecx+=4
    for(Pint edi=&(numx->m); len>0; len--, edi++, p++)
        *edi = *p;
}
Answer 2

Pint - это видимо int *; ALLOCX тогда делает вот, что:

#define HED 4
#define RES 13
Pint ALLOCX(Tint len)
{
    int *iptr = new int[len + HED + RES];
    if(!iptr)
        return 0;
    iptr += HED;
    iptr[-4] = len;
    iptr[-3] = 0;
    iptr[-1] = 1;
    iptr[-2] = 0;
    return iptr;
}

остальное я думаю можно понять и так

READ ALSO
Вызов метода одного потока из другого

Вызов метода одного потока из другого

У меня есть функция которая вызывается отдельным потоком и поток отделяется:

136
Проверка соседних ячеек массива в игре крестики-нолики

Проверка соседних ячеек массива в игре крестики-нолики

Задание: необходимо реализовать ИИ при игре в крестики-нолики на джавеСамый первый шаг рандомный выбор ячейки и проверка соседних ячеек...

132
Как форматировать массив

Как форматировать массив

Выводит эти значения, но RUB,PLN,JPY,DKK и так дальше, не форматированы

134
зачем проверять if(fos!=null)

зачем проверять if(fos!=null)

Учу андроид разработкуИ уведел такую штуку:

191