Software Archive
Read-only legacy content
17061 Discussions

x86 opcodes / behavior

eidako
Beginner
814 Views
I've recently decided to explore the fun world of bootstrap loaders, protected mode, and opcode-level assembly for the x86 PC. Unfortunately I've hit a few roadblocks to further progress, and I'm having a hard time finding any of the answers. I apologize if this isn't the place to field such questions, but I thought it would be the best place to ask.

1) I've noticed that assemblers translate mnemonics such as

MOV AX, ES:[DI]

to

26h ( ES: )
8B05h (MOV AX, [DI])

I understand that the ES: opcode overrides the MOV opcode so that it references ES:[DI]. I'm assuming that in the absence of an override, DS:[DI] would be referenced. If this is the case, does opcode 3Eh (DS: ) serve any actual purpose? If the default segment isn't DS, what is?

2) I've worked exclusively in 16-bit real mode for a substantial amount of time, and as such, I am finding protected mode fairly alien. My understanding is that segment registers point to a block of memory called the GDT which describes, among other things, an actual flat memory location. So if I wanted ES to point to the VGA memory area, I would make a GDT entry which would reference A0000h and set ES to the GDT index. So far, so good...until I recall that my SVGA memory starts at E0000000h (based on Windows Hardware Manager). I only have 248mb of RAM, and if my math is correct, E0000000h is in excess of that. Is my understanding of how protected mode references memory flawed, or does the video adapter have its own memory outside of RAM?

3) On the other hand is unreal mode, which appeals to me due its similiarity to real mode. Get into protected mode, set up a 4GB page pointing to 0h in the GDT, point GS (or another sreg) to it, go back to real mode, and never modify GS again. The catch is that I'm uncertain what to do from this point.

The processor is in real mode, so instructions act on the 16-bit registers. If I understand correctly I would use GS as a normal segment such as GS:[DI], where DI would be an offset from GS. Only this gets me nowhere - DI is only 16 bits, so I would still be stuck with 64k of addressable space. In order to gain access to the full 4GB, would I force DI to 32 bit by prefixing instructions with 66h? Would the following opcode sequence be correct?

66h (operand size)
65h (GS: )
8B05h (MOV AX, [DI])

Or would I specify the segment first?

65h (GS: )
66h (operand size)
8B05h (MOV AX, [DI])

I'd be most greatful for help on any of these sticking points.

On a completely unrelated note: graphical emoticons on a technical forum are annoying. :) :) :)

Message Edited by Eidako on 02-23-2006 06:20 PM

0 Kudos
7 Replies
intel_software_netwo
814 Views

Hello,

Thank you for posting your question on the Intel Software Network forum. Engineering is investigating and a response will be posted as soon as possible.

Best regards,
Jim A

IntelSoftware NetworkSupport

http://www.intel.com/software

Contact us

0 Kudos
eidako
Beginner
814 Views
Update: I've located the answer to question two. It turns out, yes, the video adapter has its own memory (VRAM) seperate from conventional RAM, so the absurdly high base address is correct.

Thank you for researching this. I didn't expect an official reply, much less allocation of someone to look into the answer (hence the informal tone). I'm impressed a company as large as Intel would do such a thing - for what it's worth, you have my gratitude.
0 Kudos
Intel_Software_Netw1
814 Views

Hello,

This appears to be beyond the scope of our Intel Software Network Support forum communities. Something that would possibly help you in this case would be anx86 architecture book, such as Computer Architecture by Hennessy & Patterson

Developer resources for processors can also be found at http://developer.intel.com/products/processor/index.htm
Best regards,
Jim A
IntelSoftware NetworkSupport
http://www.intel.com/software
Contact us
0 Kudos
jim_dempsey
Beginner
814 Views

Let's see if I can finish this before the forum message applet cuts me off.

Since the 386 the IA32 processors have Real Mode, Protected Mode and a subset of Protected Mode called Virtual 86 Mode. V86 mode is not quite the same as Real Mode.

In Protected Mode when a segment register is loaded mappingdata is fetched from the descriptor table (GDT or LDT) and placed into a selector register (one for each of the segment registers). This data contains among other things a base address and a size (either multiple of bytes or multiple of 4KB pages or now multiple of 4MB super pages). Then after loading, whenever a segment register is used (implicitly or explicitly) for addressing the base is added to the specified address and a check for size is made (also r/w/e privledges too).

In V86 mode things are different. The O/S while in Protected Mode loads the segment registers and thus loads the selectors for each segment register thus initializing the selector base, size and protection information. Then a switch is made into V86 mode. While in V86 Mode an application can write to a segment register. However, while in V86 Mode the write to segment register does not reload the selector information from the GDT/LDT. Then in V86 Mode the contents of the referenced segment register *16 to be added to the address specified to form the effective address. *** There is one caviat. In V86 mode, regardless of the size restriction in the selector, the effective address prior to adding the shifted segment register is truncated to 16-bits (there is a status bitoption to specify truncation or generate an error).

In Real Mode (not V86 mode) there is no truncation of the address prior to addition of the shifted segment register. Thus, if you insert some startup code that temporarily enters protected mode, you can load the selector registers with Granularity Large (4KB count of pages for size), Default to 16-bit data access, and set the base and size to whatever you want (usualy 0 and all of available RAM). Then switch back to Real Mode. Now you have a mode that you can realy use. In this mode you can use 16-bit and 32-bit addressing. Most assemblers will insert the data address size override prefix if the indexing contains a 32-bit register

mov ax, word ptr es:[edi]

Since you have flat addressing you can base all the selectors at 0 and omit the segment register (placing 0 in DS). However, you can continue to use the segment registers if you prefer to.

If you program in DOS then you must be carefulnot to use anything that places you in V86 mode (EMM386). If you use only HIMEMthen you must monitor if HIMEM is being called elsewhere and if so reset the selectors as HIMEM will dink the Granularity Large bit.

You can have a lot of fun in Flat Mode DOS. Back in 1990 I had a product that made this relatively easy (BCCx32). No longer available.

Jim Dempsey

0 Kudos
eidako
Beginner
814 Views
Intel: thank you for the references - I think they may be able to help.

Jim: that helps quite a bit. What I meant by unreal mode in the third question is the flat real mode you wrote about in the last part. I'm hand coding instructions - manually writing binary opcodes - instead of using an assembler*, so I don't have the convenience of automatic 16/32 bit selection. The problem is the order of operations - segment, operand size, opcode or operand size, segment, opcode. Setting DS to a base of 0 would eliminate the need to specify the segment, but it seems like certain BIOS interrupts would cease functioning correctly.

As far as interference from DOS, this is being done from a custom bootstrap with nothing but BIOS in the background, so no worries there.

Thanks for the help. I didn't think anyone would still know about unreal/flat real mode.

* I could just use NASM, but I'm teaching myself about processor operation through self-torture :)

Edit: minutes after writing that: "Wait a minute - I can assemble it using NASM and check the opcodes to see how it's done!" Sometimes the most obvious answers are...the most obvious. I've got it from here - thanks again.

Message Edited by Eidako on 03-01-2006 01:48 AM

0 Kudos
jim_dempsey
Beginner
814 Views

The BIOS interface will clearly specify what is required to be placed into your segment registers prior to call. Also, sepcific registers when used for base addresshave a default segment register. Instruction fetch uses CS, ESP or EBP defaults to SS, destination strings (using EDI) default to ES, andall others default to DS. For 16-bit BIOS calls where argument is located below 64KB then leave 0. Else if argument below 640KB then fill required segment register with shifted value of argument address and then fixup offset. Then on return deal with return argument then reset DS or other segment register back to 0. Doing this in a BIOS interface module that you write will give you the most control. e.g. if argument in your code is above 640KB then copy to low memory intermediary buffer prior to BIOS call then return if necessary after the call.

You may need to hook some of the interrupt vectors to take care of some exception conditions. Such as to handle sleep mode transitions.

If you get stuck and need some help we can discuss this offline.

Jim Dempsey

0 Kudos
jim_dempsey
Beginner
814 Views
I forgot to mention. The order of preference for prefixes are:
1) Segment overide
2) Address size
3) Operand size
4) Repeat
Then:
Opcode
Register specifier
Address-mode specifier
SIB (scale, index, base)
Displacement
Immediate operand
Some of the above are optional and may not be present
Jim Dempsey
0 Kudos
Reply