Looking through 3 versions of the references, I can't seem to find a clear description of the semantics that covers all forms of the instruction. Vol. 2B says that "the operand-size attribute of the current code segment determines the amount the stack pointer is decremented (2, 4, or 8 bytes)." Yet, the description of pushing immediate values states, "If the source operand is an immediate and its size is less than the address size of the stack, a sign-extended value is pushed on the stack."
Does this imply that the address-size attributeof the stack segment cannot be larger than the operand-size attribute of the code segment?
The computation of the operand sizes for segment registers other than FS and GS seems a bit ambiguous as well. I have heard that there are quite a few errors in the documentation of PUSH, but have never verified this, nor have I found the corrections anywhere.
Working on a code analyzer, so I now need to hammer this out. Does anyone know of a complete, consistent, and correct description of PUSH?
Thanks for any help.
Perhaps you were expecting the elements that influence the behavior differences between different flavors of PUSH should be intuitive and do not have branchy behavior.
Basically, the operand size of a given instruction (on a per opcode basis, vs. per mnemonic) can be opcode-specific. However, the organization of the instruction reference pages are either per-mnemonic or grouping several mnemonic with the same general processing capabilities. It is quite often that the operand size of a given instruction is associated with the default osize attribute of the current code segment but there are several nuances that deviates from that generality and making things a bit complicated.
If you look at the operation section, you'll see the somewhat complicated if/else structure illustrating the mode-specific behavior with a common pattern, the size granularity of data that are push/pop'ed either follow the default or at a different size via the use of Osize override prefix 66H. So in 64-bit segment, you have 8 byte (default) vs. 2 byte; in 32-bit segment you have 4 bytes (default) vs. 2 byte; in 16-bit segment, you have 2 byte (default) vs. 4 byte.
The immediate flavor of PUSH adds a small twist (the operand size of the instruction can be different from the number bytes that are encoded as a immediate) to these behavior so that PUSH and POP can work together with either 64-bit, 32-bit, or 16-bit segments.Since the number of bytes being encoded in a PUSH instruction can be smaller than the data size that gets push on the stack, Sign-extension is applied to make up the difference is size.