1

I'm converting some assembly code to C to be able to use it with the current compiler environment I have to work with.

I've reached 2 operations I don't know how to translate to C. Anyone know how to do it?

In both, offset is an unsigned 32-bit integer and shift is a signed integer value. C_FLAG is a bool.

OP1:

__asm {
    __asm mov ecx, shift
    __asm ror offset, cl
}

OP2:

__asm {
    __asm bt dword ptr C_FLAG, 0
    __asm rcr offset, 1
}

Thank you very much for your expertise.

P.S.: I'm not the original developer, nor I have seen many x86 assembly code...

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Are you asking what these operations do? Or are you asking how to express them in C? Or both? – David Thornley May 03 '10 at 20:24
  • Kind of both, now I know the first one is a rotate right operation; but I have no clue on what the second one does, nor how to convert it to C code. – Juan Antonio May 03 '10 at 20:33
  • You're right, the first one is a rotate right operation. The second one seems to be implementing ARM RRX (rotate right with extend / rotate right extended) in x86 assembly. You can see both explained here: http://www.mitchellwebdesign.com/arm/lecture4/lecture4-3-4.html – Xandy May 03 '10 at 22:24
  • 1
    The second one is Rotate-through-Carry-Right. – caf May 04 '10 at 00:45

5 Answers5

2

rotate right discussion: http://www.osix.net/modules/article/?id=320 (web.archive)

pictorial descriptions: http://www.penguin.cz/~literakl/intel/r.html

Hope this helps

Jan
  • 2,178
  • 3
  • 14
  • 26
Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • Thank you, the rotate right discussion served very well for the first operation. – Juan Antonio May 03 '10 at 20:34
  • [Best practices for circular shift (rotate) operations in C++](https://stackoverflow.com/q/776508) covers normal rotate, not through carry (or rotating in a bit from another integer in this case, since BT sets CF) – Peter Cordes Mar 22 '22 at 19:20
0

For bit shifting, as per your first example, use the << operator. In the C language there is no wrap-around for shifts, often termed as rotates. You would have to implement the operation yourself:

unsigned char carry;
carry = byte & 0x80; // Save the Most Significant bit for 8-bit byte.
byte <<= 1;  // Left shift by one, insert a 0 as the least significant bit.
byte |= carry;  // Put the rotated bit back into the byte.

Some processors also have a rotate through carry operation which will rotate the carry value in the next shift. This assumes that the carry be a global variable.

To test bits in the C language, you will use the & (binary AND operator) and maybe the ~ operator (negate). To test the most significant bit in an 8-bit byte:

   if (byte & 0x80)
   {
      // bit is a 1
   }
   else
   {
      // bit is a 0
   }

With all that said, you will have to find out why the carry flag (C_FLAG) is used and design a different system around it. Generally, the carry bit is invalid outside of the assembly language function that it is used in. Some tightly coupled assembly language functions may violate this rule. In that case, rewrite the assembly language rather than debugging it. Redesign the whole program!

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
0

for the second op

__asm bt dword ptr C_FLAG, 0

sets the carry bit flag to C_FLAG (so 1 or 0)

__asm rcr offset, 1

is Rotate Carry Left which is a 33 bit rotate using the carry flag as the 33rd bit. (and the results put the 33rd bit in the carry flag. Which (I think) is the same as

offset =  offset << 1 + C_FLAG ? 1:0

(unless you care about the carry flag later)

EDIT - for some reason I was reading rcr as rcl. So more like

offset = offset >> 1 | (C_FLAG ? 1<<31 : 0)
aspo
  • 146
  • 4
  • `1UL<<31` is better (`1<<31` gives undefined behaviour on systems where `int` can't represent that number, whereas that is within the guaranteed range of `unsigned long`). – caf May 04 '10 at 00:49
0

Although it's more or less covered, I'd do it this way:

OP 1: It's a rotate right operation over offset, shift places. In C it could be something like this:

offset = (offset >> shift) | (offset << (WORD_LENGTH - shift);

You could get the word length with sizeof(void *) * 8 for example.


OP 2: I think this op is implementing the RRX operation in x86 assembly. It's another type of rotate right operation where the carry flag is used to provide a 33 bit quantity to be shifted. In C it could be something like this:

offset = (C_FLAG << 31) | (offset >> 1);

Where C_FLAG is the carry flag, you should find more about what that bool value is really being used for in the code you have.

Xandy
  • 1,369
  • 1
  • 12
  • 12
0

Assuming that the flags are not used to hold persistent state (which is a reasonable assumption), OP1 is equivalent to:

/* Precondition: `*offset` is in range 0..0xFFFFFFFF */
void inline op1(unsigned long *offset, int shift)
{
    shift = ((unsigned)shift) % 32;

    if (shift)
        *offset = ((*offset >> shift) | (*offset << (32 - shift))) & 0xFFFFFFFFUL;
}

and OP2 is equivalent to:

/* Precondition: `*offset` is in range 0..0xFFFFFFFF */
void inline op2(unsigned long *offset, unsigned long C_FLAG)
{
    *offset = (*offset >> 1) | ((C_FLAG & 1) << 31);
}

(On systems with 32 bit long, the precondition is automatically satisfied).

caf
  • 233,326
  • 40
  • 323
  • 462
  • Thank you very much, after some reading I think this one is the right answer. What I don't really know is why the bt operation in the 2nd op, if it is a rotate through carry right wont rcr be enough? – Juan Antonio May 05 '10 at 00:18
  • 1
    @Juan Antonio: `BT` is what actually *sets* the carry flag - in this case, it sets the carry flag to bit 0 of `C_FLAG`, which is a 32 bit memory operand. – caf May 05 '10 at 00:30
  • Thank you for the explanation, the carry flag might had the result from a previous operation. – Juan Antonio May 05 '10 at 00:44
  • `uint32_t` would be more appropriate than `unsigned long`; on non-Windows x86-64 C implementations, `unsigned long` is a 64-bit type (an x86 qword not a dword). Also, `ror` is well-defined for a count of 0, but this isn't. `32- (shift%32)` is 32. See [Best practices for circular shift (rotate) operations in C++](https://stackoverflow.com/q/776508) for C that's UB-free for any count but can still compile to a single `ror` instruction. – Peter Cordes Mar 22 '22 at 21:59
  • @PeterCordes: The masking operation in the code is to ensure it works correctly even where `unsigned long` is wider than 32 bits. The test for non-zero `shift` is specifically to avoid the UB when shift is zero. – caf Mar 24 '22 at 02:30
  • Oh, I missed the `if`. Masking the count makes it branchless, but still very clunky. https://godbolt.org/z/Wrz1MrPcM shows that, and also that using [Best practices for circular shift (rotate) operations in C++](https://stackoverflow.com/q/776508) on a `uint32_t` temporary that you load / store to an `unsigned long*` will let the compiler use load / `ror eax, cl` / store even if you insist on keeping your value to be rotated in the bottom of a wider type. – Peter Cordes Mar 24 '22 at 02:45
  • You could use `uint_least32_t` with your fixed 32-bit counts to make it 100% portable even to systems that don't have a 32-bit type, but still let it compile efficiently for systems that do. https://godbolt.org/z/afP1of9TE compiles to `ror dword ptr [rdi], cl`. IMO, `uint_least32_t` is a *much* better type to model an x86 dword than `unsigned long`, if you don't want to assume that `unsigned int` is at least 32 bits wide. – Peter Cordes Mar 24 '22 at 02:51