0F 03 /r LSL r16,r/m16 Load: r16 ← segment limit, selector r/m16.
0F 03 /r LSL r32,r/m32 Load: r32 ← segment limit, selector r/m32).
Now the Intel documentation isn't very clear on this point, but I get
the impression that an assembler does *not* allow either syntax to be
used, but rather lets you specify the 16-bit version in a 16-bit
segment and the 32-bit version in a 32-bit segment (I'm guessing here,
but that's what the documentation seems to imply). Obviously, the
opcodes are exactly the same, so I'm assuming this is how the CPU
differentiates them.
The main reason I'm asking is because MASM only allows the 32-bit
syntax whereas FASM allows only the 16-bit syntax. I suspect that this
is a defect in FASM, but I'm wanting to verify this before deciding
what to do with HLA's code generation algorithm.
Thanks,
Randy Hyde
The AMD doc is somewhat clearer, but says something rather different.
It says that the size of the segment specified in the 16 bit second
parameter is stored in the first parameter, in either 64, 32 or 16 bit
format, truncated as necessary. I'd say that that both address and
operand size overrides (and REX's) are valid, and do the obvious thing.
Of course the last time I coded an LSL was in straight 16 bit code...
The documentation says:
When the operand size is 32 bits, the 32-bit byte limit is stored in the
destination operand. When the operand size is 16 bits, a valid 32-bit
limit is computed; however, the upper 16 bits are truncated and only
the lower 16 bits are loaded into the destination operand.
Therfore I use:
seg16
00000000: 0f 03 c2 ldsl.w r0,r1
00000003: 66 0f 03 c2 ldsl.l r0,r1
seg32
00000007: 66 0f 03 c2 ldsl.w r0,r1
0000000b: 0f 03 c2 ldsl.l r0,r1
I notice, however, that you've encoded a $66 size prefix into the seg32
section. The Intel documentation doesn't mention this. And this is
exactly why I was asking the question -- there is no way to
differentiate the two based on the opcode. Either the differentiation
is done by the segment size (only), or the Intel documentation is
wrong. That's what I'm trying to figure out.
> > The documentation says:
> >
> > When the operand size is 32 bits, the 32-bit byte limit is stored in the
> > destination operand. When the operand size is 16 bits, a valid 32-bit
> > limit is computed; however, the upper 16 bits are truncated and only
> > the lower 16 bits are loaded into the destination operand.
> >
> >
> > Therfore I use:
> > seg16
> > 00000000: 0f 03 c2 ldsl.w r0,r1
> > 00000003: 66 0f 03 c2 ldsl.l r0,r1
> >
> > seg32
> > 00000007: 66 0f 03 c2 ldsl.w r0,r1
> > 0000000b: 0f 03 c2 ldsl.l r0,r1
>
> I notice, however, that you've encoded a $66 size prefix into the seg32
> section. The Intel documentation doesn't mention this.
_ Prefixes -- one or more bytes preceding an instruction that modify the
operation of the instruction. The following types of prefixes can be
used by applications programs:
2. Address size -- switches between 32-bit and 16-bit address
generation.
3. Operand size -- switches between 32-bit and 16-bit operands.
> And this is
> exactly why I was asking the question -- there is no way to
> differentiate the two based on the opcode. Either the differentiation
> is done by the segment size (only), or the Intel documentation is
> wrong. That's what I'm trying to figure out.
I don't understand what you want to say. I don't see any difference
in ldsl and for example the add instruction. If you want a 32 bit
operation in a 16 bit segment or a 16 bit operation in a 32 bit
segment, then you have to prefix the instruction by 66.
seg16
00000000: 0f 03 c2 ldsl.w r0,r1
00000003: 66 0f 03 c2 ldsl.l r0,r1
00000007: 01 c2 add.w r0,r1
00000009: 66 01 c2 add.l r0,r1
seg32
0000000c: 66 0f 03 c2 ldsl.w r0,r1
00000010: 0f 03 c2 ldsl.l r0,r1
00000013: 66 01 c2 add.w r0,r1
00000016: 01 c2 add.l r0,r1
And for an address size switch you have to use 67:
seg16
00000000: 0f 03 07 ldsl.w r0,(r3.w)
00000003: 67 0f 03 03 ldsl.w r0,(r3.l)
00000007: 66 0f 03 07 ldsl.l r0,(r3.w)
0000000b: 67 66 0f 03 03 ldsl.l r0,(r3.l)
seg32
00000010: 67 66 0f 03 07 ldsl.w r0,(r3.w)
00000015: 66 0f 03 03 ldsl.w r0,(r3.l)
00000019: 67 0f 03 07 ldsl.l r0,(r3.w)
0000001d: 0f 03 03 ldsl.l r0,(r3.l)
Sorry, but it seems my assembler has got an Intel virus and
now also mixes up source and destination (never used this
instruction) but at least the disassembler does it the correct
way.
> seg16
>
> 00000000: 0f 03 07 ldsl.w r0,(r3.w)
> 00000003: 67 0f 03 03 ldsl.w r0,(r3.l)
> 00000007: 66 0f 03 07 ldsl.l r0,(r3.w)
> 0000000b: 67 66 0f 03 03 ldsl.l r0,(r3.l)
00000100 ldsl.w (r3.w),r0 (0f 03 07)
00000103 ldsl.w (r3),r0 (67 0f 03 03)
00000107 ldsl.l (r3.w),r0 (66 0f 03 07)
0000010b ldsl.l (r3),r0 (67 66 0f 03 03)
:Either the differentiation
:is done by the segment size (only), or the Intel documentation is
:wrong.
False dichotomy.
The default operand-size and address-size attributes are determined by
the D bit in the code segment descriptor, but may be modified for any
given instruction by use of the appropriate instruction prefixes. This
rule is so basic to the architecture that it would be massively redundant
to repeat it the individual instruction descriptions.
-- Chuck
I don't understand either. That's why I'm asking. The Intel
documentation isn't clear about this. Maybe I'm just expecting the
documentation to be a little more clear about the differences between
the opcodes in 32-bit mode.
Well, I guess the problem that I am having is that MASM refuses to
compile the 16-bit version in 32-bit mode and FASM refuses to compile
the 32-bit version in 32-bit mode, but both generate exactly the same
opcode sequence (one with 32-bit operands, one with 16-bit operands).
Clearly, both assemblers are broken (though FASM moreso than MASM on
this point).
One could argue that MASM's error is the least obtrusive as as 16-bit
LSL is probably useless for all practical purposes in 32-bit mode.
Clearly FASM's error is a defect that ought to be corrected.
:Clearly FASM's error is a defect that ought to be corrected.
Not so clear, actually.
In testing with FASM under Linux, I got the following results:
0F 03 C3 lsl eax,bx ;16-bit selector in 16-bit register
0F 03 03 lsl eax,[ebx];32-bit register -> 16-bit selector
67 0F 03 07 lsl eax,[bx] ;16-bit register -> 16-bit selector
<error> lsl eax,ebx ;16-bit selector in 32-bit register
Note that the assembled code is correct in all cases, although a
disassembler might be show case 1 as lsl eax,ebx.
Presumably, FASM's refusal to accept the last case is because a selector
is always 16 bits.
-- Chuck
That makes sense. Nasm does it differently... in the reg, reg forms, the
size of the registers must match - "lsl eax, bx" reports an "invalid
combination of opcode and operands".
Seems to me that an assembler ought to accept *either* "lsl eax, ebx" or
"lsl eax, bx", emitting the same opcode - no override - for either case.
Since, as you say, a selector is always 16 bits, it's the same
operation. Much like "mov ds, ax" vs "mov ds, eax" (in 32-bit code) -
some assemblers (perhaps only Masm, at this point) require the latter
syntax to avoid emitting the override... but it's the same instruction
in either case - the override (if any) is ignored...
Pity Nasm and Fasm didn't do it the same...
Best,
Frank
You missed,
LSL ax, bx
Which also produces 0F 03 C3.
And then there's MASM, which only accepts "lsl eax, ebx" (of the
register forms) and also produces 0F 03 C3 for the result.
The Intel documentation says the following:
0F 03 /r LSL r16, r16/m16 Valid Valid Load: r16 ← segment limit,
selector r16/m16.
0F 03 /r LSL r32, r32/m161 Valid Valid Load: r32 ← segment limit,
selector r32/m16.
Which suggests that (reg16, reg16) and (reg32, reg32) are both legal
forms. Though you could make a logical argument for a (reg32, reg16)
form, the Intel documentation says nothing about this.
That's why I'm asking for clarification of the Intel documentation.
Clearly, someone is doing this wrong here (and probably both
assemblers).
Cheers,
Randy Hyde
Right. Using a 16-bit destination isn't going to be that helpful, even
in a 16-bit segment - the actual limit is going to be 32-bits (all bets
are off on 64-bit machines - I have no clue). Maybe a truncated limit
would be some use - mostly, I think we're going to want the whole 32-bit
result... so we'd want the override in 16-bit code, usually.
The source is always 16-bits - Nasm makes us *say* "lsl eax, ebx", but
only bx is used (experimentation shows that garbage in the high word of
ebx makes no difference). I haven't done an experiment on the reg, mem
form, but surely it only reads 16-bits, regardless of prefix (???
assumption is the mother of fuckup, I suppose...).
Honestly, I'd say that Fasm gets it right - "lsl eax, bx". Nasm insists
on "lsl eax, ebx", which *looks* like properly matched operands, but is
misleading, since src is always 16 bits... Ideally, I'd (LuxAsm?) accept
either way of "saying" the source, much as you'd accept either "mov ds,
ax" or "mov ds, eax" and generate the same sequence (no override).
I guess Randy's original question (?) was whether "lsl ax, bx" is valid
(in 32-bit code... do we care about 16-bit?). Apparently, yes, but
returns a "truncated" limit in ebx - upper word untouched. Not too
useful, but apparently valid...
Ideal syntax would be "lsl ax, bx", not "lsl (bx, ax);"
, of course! :)
Best,
Frank
>From the Intel Documentation:
REX.W + 0F 03 /r LSL r64, r32/m161 Valid Valid Load: r64 ← segment
limit,
selector r32/m16
IOW, you're getting a 64-bit limit.
> Maybe a truncated limit
> would be some use - mostly, I think we're going to want the whole 32-bit
> result... so we'd want the override in 16-bit code, usually.
AFAICT, the 16-bit version in 32-bit mode is useless. This is probably
why MASM doesn't support it at all.
>
> The source is always 16-bits - Nasm makes us *say* "lsl eax, ebx", but
> only bx is used (experimentation shows that garbage in the high word of
> ebx makes no difference). I haven't done an experiment on the reg, mem
> form, but surely it only reads 16-bits, regardless of prefix (???
> assumption is the mother of fuckup, I suppose...).
>From the Intel documentation:
0F 03 /r LSL r16, r16/m16 Valid Valid Load: r16 ← segment limit,
selector r16/m16.
0F 03 /r LSL r32, r32/m16[1] Valid Valid Load: r32 ← segment limit,
selector r32/m16.
[1] For all loads (regardless of destination sizing), only bits 16-0
are used. Other bits are ignored.
IOW, memory operands are always 16 bits. Even with the register is 32
bits, only 16 bits are read, according to Intel. So yes, "lsl eax, bx"
would seem to make more sense, but the Intel syntax calls for "lsl eax,
ebx". So in that respect NASM is right. We could easily argue that
Intel is wrong (they are on many other mnemonics and instruction
syntaxes); but that's a different issue.
>
> Honestly, I'd say that Fasm gets it right - "lsl eax, bx". Nasm insists
> on "lsl eax, ebx", which *looks* like properly matched operands, but is
> misleading, since src is always 16 bits...
NASM didn't get it wrong, Intel did. NASM is just doing what Intel
called for.
> Ideally, I'd (LuxAsm?) accept
> either way of "saying" the source, much as you'd accept either "mov ds,
> ax" or "mov ds, eax" and generate the same sequence (no override).
>
> I guess Randy's original question (?) was whether "lsl ax, bx" is valid
> (in 32-bit code... do we care about 16-bit?). Apparently, yes, but
> returns a "truncated" limit in ebx - upper word untouched. Not too
> useful, but apparently valid...
Actually, my question was about the prefix bytes. A 16-bit LSL is
definitely valid, though the results won't make much sense.
Cheers,
Randy Hyde
:You missed,
: LSL ax, bx
Here is an expanded list:
0F 03 C3 lsl eax,bx
0F 03 03 lsl eax,[ebx]
67 0F 03 07 lsl eax,[bx]
<error> lsl eax,ebx
0F 03 05 00 00 00 00 lsl eax,[selector16]
<error> lsl eax,[selector32]
66 0F 03 C3 lsl ax,bx
66 0F 03 03 lsl ax,[ebx]
67 66 0F 03 07 lsl ax,[bx]
<error> lsl ax,ebx
66 0F 03 05 00 00 00 00 lsl ax,[selector16]
<error> lsl ax,[selector32]
Please note that FASM is consistent in rejecting any attempt to specify a
32-bit selector value, which is reasonable, since selectors are always 16
bits.
:Clearly, someone is doing this wrong here (and probably both
:assemblers).
It's not a question of right or wrong, since both assemblers produce
correct code, but rather of programming style. You chose not to use strict
Intel syntax in HLA, so why does it bother you that NASM and FASM share
this trait?
-- Chuck
Contrary to Intel's semantics.
>
> :Clearly, someone is doing this wrong here (and probably both
> :assemblers).
>
> It's not a question of right or wrong, since both assemblers produce
> correct code, but rather of programming style. You chose not to use strict
> Intel syntax in HLA, so why does it bother you that NASM and FASM share
> this trait?
Actually, it's MASM and FASM, not NASM that differ from Intel's syntax.
NASM works just the way Intel says it should. As for HLA, it provides
extensions, but I've generally supported the operands exactly the way
Intel suggests that they should be supported. For example, the LSL
instruction accepts two 32-bit registers. I am going to, however, drop
support for the 16-bit register form as that's a 16-bit-ism that has
absolutely no use in flat-model 32-bit programs (then again, one could
argue that there is little use for the LSL instruction in flat model
code, but that's a different argument).
Cheers,
Randy Hyde
:Contrary to Intel's semantics.
"Still, a man hears what he wants to hear
And disregards the rest."
[Simon and Garfunkel]
Okay... that's "as expected"... surprisingly, in 64-bit :)
>>Maybe a truncated limit
>>would be some use - mostly, I think we're going to want the whole 32-bit
>>result... so we'd want the override in 16-bit code, usually.
>
>
> AFAICT, the 16-bit version in 32-bit mode is useless. This is probably
> why MASM doesn't support it at all.
Not very useful in 16-bit mode either - the limit is still 32 bits.
But... not the assemblers job to decide what's "useful", IMHO. If the
instruction is "valid", a "good" assembler ought to generate it, if
asked. (hopefully *only* when asked :)
....
> NASM didn't get it wrong, Intel did. NASM is just doing what Intel
> called for.
You've got a cc on this, I think. For the rest of us:
<https://sourceforge.net/tracker/index.php?func=detail&aid=1490407&group_id=6208&atid=106208>
Nasm64developer has submitted a patch to "correct" Nasm's behavior. With
this patch applied, "lsl eax, bx" assembles correctly, but "lsl eax,
ebx" is an error... breaking existing code... if any...
I'm still inclined to think that accepting both would be ideal (I'll
take a look to see if I can figure out how to do that, having been given
a clue...)
I'm not sure what Fasm or HLA2 do with:
mov ds, ax
(in 32-bit code) It *looks* like it needs an override, but the override
has no effect. The Intel Manual (as quoted to me, as usual) says "most
assemblers" require "mov ds, eax" to avoid generating the override. Masm
generates the override on "mov ds, ax", Tasm, Gas, and now Nasm, do not.
Maybe they should spell it "MoSt" :)
This seems to me like a similar situation. Nasm64developer observes that
the same situation exists on "lar"...
Best,
Frank
> Well, I guess the problem that I am having is that MASM refuses to
> compile the 16-bit version in 32-bit mode and FASM refuses to compile
> the 32-bit version in 32-bit mode, but both generate exactly the same
> opcode sequence (one with 32-bit operands, one with 16-bit operands).
> Clearly, both assemblers are broken (though FASM moreso than MASM on
> this point).
Doesn't look to me that MASM refuses to compile the 16 bit version in 32
bit mode (nor the 32 bit version in the 16 bit mode, BTW): it generates
the mode override prefix in a rather predictable way, doesn't it?
00000000 InitCode32 SEGMENT BYTE PUBLIC USE32 'INITCODE'
00000000 66| 50 PUSH AX
00000002 50 PUSH EAX
00000003 0F 03 C3 LSL EAX,EBX
00000006 66| 0F 03 C3 LSL AX,BX
0000000A 66| 0F 03 C7 LSL AX,DI
0000000E 66| 0F 03 C8 LSL CX,AX
00000012 0F 03 C8 LSL ECX,EAX
; But it doesn't want reg size mixes.
LSL EAX,BX
..\Int13h.asm(1351) : error A2022: instruction operands must be the same
size
LSL AX,EBX
..\Int13h.asm(1352) : error A2022: instruction operands must be the same
size
002B InitCode32 ENDS
Microsoft (R) Macro Assembler Version 7.00.9466 05/29/06 15:32:11
Page 32 - 1
0000 InitCode16 SEGMENT BYTE PUBLIC USE16 'INITCODE'
0000 50 PUSH AX
0001 66| 50 PUSH EAX
0003 66| 0F 03 C3 LSL EAX,EBX
0007 0F 03 C3 LSL AX,BX
000A 0F 03 C7 LSL AX,DI
000D 0F 03 C8 LSL CX,AX
0010 66| 0F 03 C8 LSL ECX,EAX
LSL EAX,BX
; But it doesn't want reg size mixes.
..\Int13h.asm(1366) : error A2022: instruction operands must be the same
size
LSL AX,EBX
..\Int13h.asm(1367) : error A2022: instruction operands must be the same
size
002A InitCode16 ENDS