I teach C ++ and examining one project I stumbled upon the fact that the author implemented some of the functions (core) on MASM, and I know very little about MASM, and I really didn’t find anything on the Internet on this topic. Can anyone translate these 3 functions in C ++, with comments like that and how? Then I'll figure it out myself.

The code itself on MASM ( source code , header file ):

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 

And in C ++, they are written as:

 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 

I will be grateful.

  • What is your ultimate goal? Understand how it works or write an analog? - insolor
  • In short: ALLOCX allocates memory for the base unit, FREEX frees memory, ALLOCN allocates memory for N blocks, NORMX normalizes the mantissa (see Normal and normalized forms ). - insolor
  • @insolor, and how the function works, and how to write its analogue in C ++, I just want a minimal dependency on the platform to use it as a library, and a strong dependency of the assembler spoils everything - complex

2 answers 2

The basic structure (described in the header file 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]; }; 

This is essentially a header and reserved fields. The size of this structure is (6 + 12)*4 = 72 bytes, which is equal to HED + 4 байта мантиссы + RES . The structure and additional data block is ALLOCX with the ALLOCX function:

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

The function allocates memory of the desired size, initializes the header, returns a pointer to the mantissa (the m field inside the structure).

The meaning is something like this:

 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 } 

This is not a word-for-word translation of assembler code, this is how it works.

In the NORMX procedure, NORMX contrary, a pointer to a mantissa is taken. If you translate into C, then in an amicable way you need to calculate from this pointer the pointer to the beginning of the structure, and continue to work through the field names, and not through offsets, as in the assembler version.

I would alter the procedures altogether so that they work through the pointer to the beginning of the structure, and not the pointer to the mantissa. This is generally a rather strange decision, the benefit of such a decision is not very clear.

Below is an added subscript of the norm procedure (it could be wrong somewhere, you need to test):

 #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; } 
  • "I would alter the procedures altogether so that they work through the pointer to the beginning of the structure, not the pointer to the mantissa." I would not mind looking at how you would implement it;) Well, although I will try it myself. And about the strangeness, the author wrote this program during the student days, and not a single code from banal crutches. Well, thanks for the help, although your code will have to be finished a bit - complex
  • @complex, added translation of the NORMX procedure. What will be different option with a pointer to the beginning of the structure instead of a pointer to the mantissa - you will not need to calculate the pointer to the beginning of the structure, as in the above code. - insolor
  • and what about trim (Pint x)? Not quite delving into what this function does - complex
  • @complex, I have the following assumption: the mantissa is actually not only a field m , but also an array of numbers after it. The actual length of the mantissa is stored in the len field. The trim seems to decrease the value of len if the mantissa is actually shorter than this value. - insolor
  • Thank you for your help, then I think I will figure it out if there are any other ideas about this issue - write, I will always be happy - complex

Pint is probably int *; ALLOCX then does this:

 #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; } 

I think the rest can be understood as well