VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

I know that during JCXZ/JECXZ, the address size selects between the ECX and CX registers.

But what happens with the address size on Jcc instructions? I'd assume nothing on rel8. But what happens with the 0F Jcc instructions? Does it affect the immediate size(imm16 vs imm32)? Or does it do nothing?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 13, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

Operand size (0x66) will switch between ECX and CX, and 32-bit or 16-bit immediate for 0F Jcc branches). It does nothing for rel8 Jcc.
Address size (0x67) will switch between EIP and IP for the destination address if taken, including for rel8 Jcc.

Reply 2 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

So, according to the documentation, operand size (0x66) has effect on the opcodes themselves(e.g. 0F80 being 0x0F80XXXX vs 0x0F80XXXXXXXX), so just changing the immediate operand size.

And with those 0F Jcc (0F80-0F8F) switch between EIP and IP depending on the effective address size(determined by the D-bit in CS combined with the 0x67 prefix being present or not)?

So, in 32-bit programs(D-bit set), with 32-bit operand size not being overridden:
0F8012345678 with a 67h prefix will jump to EIP+12345678, masking the EIP result to 16-bits. But the masking to 16-bits won't happen when the 67h prefix isn't used?
And if you don't specify the 67h prefix, it won't mask to 16-bits?

What happens with D-bit set, opcode 660F801234 being executed. So 1234 is added to EIP. Is EIP masked to 16-bits after that or not?

Oddly enough, the 80386 documentation says that the instructions are only depending on the operand size. It says nothing about any address size? So will a 16-bit immediate 0F80(16-bit operand size, but 32-bit address size due to D-bit being set in the CS descriptor) truncate EIP, after adding the immediate to EIP, to 16-bits? Or will the resulting EIP not be truncated in that case?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 13, by Stenzek

User metadata
Rank Newbie
Rank
Newbie

Pentium manual says the relative jumps are EIP <= EIP + rel8/rel16/rel32, masked with FFFFh if operand size is 16-bit.

Indirect jumps are EIP <= r/m16 or 32 (so zero-extended?), then masked with FFFFh if operand-size is 16-bit. As the address referred to by the pointer will be the same size, AFAICT the mask here is redundant.

FWIW, using the address size instead of operand size results in OS/2 crashing on boot in my emulator.

Reply 4 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

Good point. I will have to try OS/2 2.0 again. Tried it a long time ago, before many of the 80386 bugfixes. Maybe it'll install and/or run somewhat now, if at least with the 80286 bugfixes. Perhaps 80386 will run, but it might crash with the remaining bugs I can't seem to find(same as Linux crashing on such a thing)?

Edit: The installation disk boots, but I oddly enough see the CPU speed percentage that's enabled in the settings jump from ~28% to 0% at periodic intervals. Perhaps it's doing some weird kind of test?

Edit: Having inserted disk 1 and pressed the enter key, I get a black screen after the blue screen with information, floppy drive activity and a cursor at the bottom left corner of the screen?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 5 of 13, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

So, according to the documentation, operand size (0x66) has effect on the opcodes themselves(e.g. 0F80 being 0x0F80XXXX vs 0x0F80XXXXXXXX), so just changing the immediate operand size.

Yes.

superfury wrote:

And with those 0F Jcc (0F80-0F8F) switch between EIP and IP depending on the effective address size(determined by the D-bit in CS combined with the 0x67 prefix being present or not)?

Yes, and 70-7F are affected similarly. In 32-bit code, the 0x67 prefix present causes IP to be used as the target address.
In 16-bit, EIP will be used which can allow branching beyond 0xFFFF in unreal mode (otherwise it will crash).

superfury wrote:

0F8012345678 with a 67h prefix will jump to EIP+12345678, masking the EIP result to 16-bits. But the masking to 16-bits won't happen when the 67h prefix isn't used?
And if you don't specify the 67h prefix, it won't mask to 16-bits?

That's correct.

superfury wrote:

What happens with D-bit set, opcode 660F801234 being executed. So 1234 is added to EIP. Is EIP masked to 16-bits after that or not?

No. Only if 0x67 is present.

superfury wrote:

Oddly enough, the 80386 documentation says that the instructions are only depending on the operand size. It says nothing about any address size? So will a 16-bit immediate 0F80(16-bit operand size, but 32-bit address size due to D-bit being set in the CS descriptor) truncate EIP, after adding the immediate to EIP, to 16-bits? Or will the resulting EIP not be truncated in that case?

No truncation in that case, just a smaller style of branching within a 32-bit space.

Reply 6 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

So, with the 0F80-0F8F and 70-7F opcodes the Address size determines to use (E)IP and truncate it, while the operand size determines the immediate size(imm16 vs imm32 when used)?

What about the other jmp/call instructions that have an immediate offset? Do they use the address size as well for truncation of (E)IP after adding the immediate, just like the 0F80-0F8F and 70-7F instructions?

So opcodes E8, E9, EB? Do they behave like that depending on the Address size as well?

Oddly enough, the 80386 documentation states the opposite: https://css.csail.mit.edu/6.858/2010/readings/i386/Jcc.htm

IF condition
THEN
EIP := EIP + SignExtend(rel8/16/32);
IF OperandSize = 16
THEN EIP := EIP AND 0000FFFFH;
FI;
FI;

So 'OperandSize = 16' can mean either 'OperandSize = 16' for most opcodes, but 'AddressSize = 16' for the Jcc opcodes?

So, looking at those instructions, it's actual undocumented behaviour the CPU uses the Address Size attribute(seeing as it's based on CS.D and prefix 0x67(Address size override)) for wrapping EIP to 16-bits, while the documentation says the EIP wrapping(it using IP with compatible effect) is based on the Operand size being 16-bits?

Edit: Bochs source code also seems to imply that the Address size is unused with those instructions: https://sourceforge.net/p/bochs/code/HEAD/tre … /ctrl_xfer16.cc ?
Look at row 38, where it writes a 16-bit IP to EIP(truncating it). Said function is called at line 294 and onwards. So that means the Operand size(which this module implies) affects the resulting EIP directly and no address size attribute is used in these instructions?

Edit: Opcode EB might not have said problem, because it's always using a imm8 immediate value. But E8 and E9 do seem to have this potential problem? Do they truncate EIP when the operand size is 16-bits? Or do they not truncate when the address size is 32-bits?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 7 of 13, by Stenzek

User metadata
Rank Newbie
Rank
Newbie

I was using OS/2 Warp 3 as a test case, not 2.0. Makes a good test case for protected mode stuff, as it seems to heavily use mixed 16/32-bit code, call gates, etc.

The Am486 manual agrees with the Intel manuals, in which EIP <= EIP + SignExtend(operand) AND FFFFh, if the *operand-size* is 16-bit. It seems like this might be worth testing on real hardware, particularly whether EIP is masked *before* the addition for an effective 16-bit address size. The manuals don't make any mention of this.

Reply 8 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

Any knowledge on the JMP/CALL opcodes E8(CALL imm16), E9(JMP rel16) and EB(JMP imm8)? How does the address size prefix affect those? Do they have the same behaviour as the conditional Jcc opcodes? So do they also use the address size prefix combined with CS.D for determining IP vs EIP and wrapping?

So what effect does 66E8XXXX, 66E9XXXX and 66EBXX have on 32-bit software(CS.D set) with EIP>=0x10000?
And the same with 67 instead of 66 prefix?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 9 of 13, by Stenzek

User metadata
Rank Newbie
Rank
Newbie

As far as I understand, the "effective operand size" controls how the instruction behaves, so CS.D=0, no prefix = 16-bit, CS.D=0, 66 prefix = 32-bit, CS.D=1, no prefix = 32-bit, CS.D=0, 66 prefix = 16-bit.

For E8/E9, the effective operand size controls the size of the immediate, which is sign-extended and masked if 16-bit. It also controls whether a word or dword is pushed for calls. This is what is in the Intel manuals, and how I have it implemented in my emulator.

The effective address size will change how the modrm bits are interpreted for the effective address in indirect branches. For direct branches (immediate or relative), shouldn't have any effect as far as I can tell.

Reply 10 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

So the Jcc conditional jumps are an exception to the rule, since they use 16-bit EIP masking(AND with 0xFFFF) and masking only when the address size is 16-bits, instead of the operand size(that is what peterferrie seems to imply, as I understand what he's saying)? Also, that behaviour is undocumented behaviour(if peterferrie is right about that)? All other opcodes just behave as documented, masking to 16-bits depending on the operand size only?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 13, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

Gosh. Indeed I have it entirely backwards.
0x66 is the override that affects all transfers of control: 9A/E0/E1/E2/E3/E8/E9/EA/EB/7x/0F 8x.
There are no exceptions, except that it's 0x66 and not 0x67. That was my mistake.

400000: 66 EB 00 will jump to 0x0003.

Reply 13 of 13, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just tried OS/2 warp 3 (Blue Spine CD), extracted the DISK0 to DISK3 .DSK images from the DISKIMGS\OS2\35 folder, mounted DISK0.DSK into UniPCemu(afaik there aren't many emulators for the PC that support said format) as a read-only disk, then reversed the BIOS ROM from the Plop BIOS(for booting the CD-ROM, which isn't actually bootable as I've found out), pressed A in the XT-IDE BIOS to boot the floppy disk, after which it displays four white block characters(the filled block, with all pixels set to foreground color, character code 0xDB), followed by a space and the text "OS/2".

I see it throwing a #UD for opcode 0FA*(The 80486-only CMPXCHG) after which I see it faulting a #UD on opcode FFFFh? So something clearly went wrong there?

The last floppy read was from location 0xD6800.

I see it's still in Real mode, so it hasn't entered protected mode yet, as far as I can see.

Edit: Now making a log file with everything running until the FFFFh opcode...

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io