0

I am using the YASM assembler.

If I have a variable declared as such

segment .bss
 number resb 100

and I perform a logical right shift like so

shr byte [number], 8

and if for example 123 is stored in there so that the memory looks like such 0x333231 then I expect the result to be 0x3332 but the result is instead 0x333200. This problem does not occur if I have the data stored in a register, could anyone explain to me why this occurs and how to fix it (I would like to use memory and not a register).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
NullPointer7
  • 101
  • 7
  • 2
    You used `byte` operand-size, so you're only operating on the low byte, shifting out all 8 bits. You'd get the same result if you did `shr al, 8`, leaving the high 7 bytes of RAX unaffected. – Peter Cordes Sep 20 '21 at 21:30
  • 4
    Note that one `shr` instruction can affect at most 8 bytes (`qword`) of the 100 you reserved with `resb 100`; you'll need extended-precision stuff (e.g. `shrd`) to deal with that space as a single 100-byte integer. Or maybe you intend to treat that space as an array of 25 `dword` elements? IDK. – Peter Cordes Sep 20 '21 at 21:32
  • 1
    Related, but coming at the question from the other angle (with an operation that wouldn't affect the upper bytes if wider, like an increment that doesn't carry): [Why do we need to disambiguate when adding an immediate value to a value at a memory address](https://stackoverflow.com/q/47445362) – Peter Cordes Sep 20 '21 at 21:51

1 Answers1

2

for example 123 is stored in there so that the memory looks like such 0x333231

Seeing the value 0x333231, I dare assume that the memory at number holds digits in ASCII representation.

31 32 33 00 00 00 ... 00

A shift right by 8 bits would therefore shift out the lowest digit. You don't need the shr instruction to do that. Just copy the memory:

mov  edi, number
lea  esi, [edi+1]
mov  ecx, 99
cld
rep movsb
mov  [edi], cl           ; CL=0

The above code does to the whole 100-byte buffer, what below code can do for the first 4 bytes only.

mov  eax, [number]
shr  eax, 8
mov  [number], eax

or

shr  dword [number], 8

If we consider the 100-byte number as a bitstring, we can shift its contents down by counts other than 8:

    mov  ebx, number
    mov  eax, [ebx]
More:
    mov  edx, [ebx+4]
    shrd eax, edx,  4        ; Shift count [0-31]
    mov  [ebx], eax
    add  ebx, 4
    mov  eax, edx
    cmp  ebx, number+96
    jb   More
    shr  eax,  4             ; Shift count [0-31]
    mov  [ebx], eax
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • My guess based on YASM with a `section .bss` was that this was at least 32-bit code, and there seems to be a confusion between byte and dword, so again 32-bit code where that's the natural size would make sense. Not that you can't use `rep movsb` in 32-bit mode, but I was surprised to see 16-bit addresses. – Peter Cordes Sep 20 '21 at 23:02
  • Also, `mov cl,4`? You're already using 32-bit registers and `shrd` (386 or newer), and immediate shifts were new in 186. Variable-count `shrd` is slower than immediate on current mainstream Intel CPUs. (Also `shr`, but less bad.) I guess you're illustrating that this is a variable input. – Peter Cordes Sep 20 '21 at 23:02
  • I'd also mention `shr dword [number], 8` as being equivalent to the load / shift / store sequence. – Peter Cordes Sep 20 '21 at 23:04
  • @PeterCordes I was going to treat this as 32-bit code, but I guess old habits don't die. In my first draft of the answer, I used the immediate shift count but I felt it didn't stand out enough. Now editing... – Sep Roland Sep 20 '21 at 23:21