NASM is not designed to just output code for whatever you are targeting. It is intended to target a multitude of potential environments where SS and DS may not have the same base. If SS.base != DS.base then the segment chosen matters. The assembler doesn't know what environment it is running in. NASM will generally try to emit the instructions so that they follow any of the nuances of the CPU. The default segment is one of those.
NASM assumes an effective address (EA) is of the form disp[base+index*scale] (where some components can be omitted). `inc dword [eax+ebp]` is disp=0, base=eax, index=ebp, and scale=1. `inc dword [ebp+eax]` is disp=0, base=ebp, index=eax, and scale=1. The problem here is that if the CPU encounters a BASE with register ESP or EBP the Stack Segment is the default, not the Data Segment.
Volume 1 of the Intel SDM section 3.7.5 provides this rule:
The uses of general-purpose registers as base or index components are restricted in the following manner:
• The ESP register cannot be used as an index register.
• When the ESP or EBP register is used as the base, the SS segment is the default segment. In all other cases,
the DS segment is the default segment.
The
NASM documentation has documented this situation in Section 3.3:
NASM has a hinting mechanism which will cause [eax+ebx] and [ebx+eax] to generate different opcodes; this is occasionally useful because [esi+ebp] and [ebp+esi] have different default segment registers.
If EBP is the index then the default segment is DS. If EBP is the base then the default segment is SS. So to NASM it matters whether something may be the base or index in the effective address. Note: If you attempt to illegally use ESP as an index (with a scale of 1) NASM will swap and make ESP the base and the other register the index. An attempt to apply scale >1 to ESP is invalid and NASM will warn you.
You claimed that NASM has bugs. We don't know what the intentions of your own assembler are (unless you tell us) or if we actually knew the design of your OS which we don't. NASM producing a 3 byte instruction vs 4 byte is not about alignment in this case. It is about what default segment the CPU will use. When SS.base != DS.base then these are not equivalent from the CPU perspective:
Code: Select all
inc dword [eax+ebp]
inc dword [ebp+eax]
The first has EAX as the base so the default segment is DS. The second has EBP as the base so the default segment is SS. To get the first one to use SS as a segment like the second (rather than DS) you would have to explicitly specify SS with:
The result would be a 4 byte encoding of 0x36, 0xFF, 0x04, 0x28.
Your assembler has no documentation and there is nothing saying what its expectations are. Your assembler as is would not be useful for encoding in environments where SS.base and DS.base are not the same which is often the case in 16-bit real and 16-bit protected mode. It is a deficiency in your assembler that doesn't make it generic. That is not how NASM was designed, as it is trying to be environmentally agnostic.
If you think that NASM is generating less than efficient instruction encodings it is far more likely that NASM understands the nuances of the x86 instruction encodings better than you do - and what you perceive to be a bug is actually by design. Most of the time NASM bugs are not related to basic instruction encodings. I found a bug in the Macho64 object metadata and it was eventually fixed by NASM developers.
The STRICT keyword in NASM can sometimes be used to generate a specific instruction encoding that may not been the default.