Reputation: 1847
I read some code of bootloaders for x86. The first part of these bootloaders is usually written using gas. They use ".code16" to tell gas generate 16bit code.
However, I found that 32bit code is executable in REAL MODE. Why do they use 16bit code? Is the ".code16" necessary?
Upvotes: 3
Views: 2175
Reputation: 9
When the first Intel processors were made they all used 16 bit code, 32 bit wasn't used. When the 386 processor was produced as 32 bits, Intel specified that it's default start state would be 16 bits for backward compatibility with the older processors. The OS or Bootloader developer could then use 16 bit instructions to access Protected (32 bit) Mode. Intel seem to like their backward compatibility and this has stuck on all x86 processors to date so they can retain compatibility with older software.
Upvotes: 0
Reputation: 113
Intel:
Instruction prefixes can be used to override the default operand size and address size of a code segment. These prefixes can be used in real-address mode as well as in protected mode and virtual-8086 mode. An operand-size or address-size prefix only changes the size for the duration of the instruction.
The following two instruction prefixes allow mixing of 32-bit and 16-bit operations within one segment: •The operand-size prefix (66H) •The address-size prefix (67H)
These prefixes reverse the default size selected by the D flag in the code-segment descriptor. For example, the processor can interpret the (MOV mem, reg) instruction in any of four ways: •In a 32-bit code segment: —Moves 32 bits from a 32-bit register to memory using a 32-bit effective address. —If preceded by an operand-size prefix, moves 16 bits from a 16-bit register to memory using a 32-bit effective address. —If preceded by an address-size prefix, moves 32 bits from a 32-bit register to memory using a 16-bit effective address. —If preceded by both an address-size prefix and an operand-size prefix, moves 16 bits from a 16-bit register to memory using a 16-bit effective address.
•In a 16-bit code segment: —Moves 16 bits from a 16-bit register to memory using a 16-bit effective address. —If preceded by an operand-size prefix, moves 32 bits from a 32-bit register to memory using a 16-bit effective address. —If preceded by an address-size prefix, moves 16 bits from a 16-bit register to memory using a 32-bit effective address. —If preceded by both an address-size prefix and an operand-size prefix, moves 32 bits from a 32-bit register to memory using a 32-bit effective address.
The previous examples show that any instruction can generate any combination of operand size and address size regardless of whether the instruction is in a 16- or 32-bit segment. The choice of the 16- or 32-bit default for a code segment is normally based on the following criteria: •Performance — Always use 32-bit code segments when possible. They run much faster than 16-bit code segments on P6 family processors, and somewhat faster on earlier IA-32 processors. •The operating system the code segment will be running on — If the operating system is a 16-bit operating system, it may not support 32-bit program modules. •Mode of operation — If the code segment is being designed to run in real-address mode, virtual-8086 mode, or SMM, it must be a 16-bit code segment. •Backward compatibility to earlier IA-32 processors — If a code segment must be able to run on an Intel 8086 or Intel 286 processor, it must be a 16-bit code segment.
The D flag in a code-segment descriptor determines the default operand-size and address-size for the instructions of a code segment. (In real-address mode and virtual-8086 mode, which do not use segment descriptors, the default is 16 bits.) A code segment with its D flag set is a 32-bit segment; a code segment with its D flag clear is a 16-bit segment.
Executable code segment. The flag is called the D flag and it indicates the default length for effective addresses and operands referenced by instructions in the segment. If the flag is set, 32-bit addresses and 32-bit or 8-bit operands are assumed; if it is clear, 16-bit addresses and 16-bit or 8-bit operands are assumed. The instruction prefix 66H can be used to select an operand size other than the default, and the prefix 67H can be used select an address size other than the default.
The 32-bit operand prefix can be used in real-address mode programs to execute the 32-bit forms of instructions. This prefix also allows real-address mode programs to use the processor’s 32-bit general-purpose registers. The 32-bit address prefix can be used in real-address mode programs, allowing 32-bit offsets.
The IA-32 processors beginning with the Intel386 processor can generate 32-bit offsets using an address override prefix; however, in real-address mode, the value of a 32-bit offset may not exceed FFFFH without causing an exception.
Assembler Usage: If a code segment that is going to run in real-address mode is defined, it must be set to a USE 16 attribute. If a 32-bit operand is used in an instruction in this code segment (for example, MOV EAX, EBX), the assembler automatically generates an operand prefix for the instruction that forces the processor to execute a 32-bit operation, even though its default code-segment attribute is 16-bit.
The 32-bit operand prefix allows a real-address mode program to use the 32-bit general-purpose registers (EAX, EBX, ECX, EDX, ESP, EBP, ESI, and EDI).
When moving data in 32-bit mode between a segment register and a 32-bit general-purpose register, the Pentium Pro processor does not require the use of a 16-bit operand size prefix; however, some assemblers do require this prefix. The processor assumes that the 16 least-significant bits of the general-purpose register are the destination or source operand. When moving a value from a segment selector to a 32-bit register, the processor fills the two high-order bytes of the register with zeros.
Upvotes: 1
Reputation: 58762
You can use 32 bit code but you are nevertheless bound by real mode segmentation and BIOS services. .code16
is however necessary because the cpu is running in 16 bit mode, so to use 32 bit instructions you need to use operand and address-size override prefixes. The directive instructs the assembler to add those for you.
If you write mov edx, eax
without .code16
, the assembler will generate machine code 89 C2
. However when the cpu is in 16 bit mode, it will interpret that as mov dx, ax
. If you really want to operate on 32 bits, you need a 66
prefix, that is 66 89 C2
. Using .code16
the assembler will do just that. The 66
prefix basically switches to the "other" mode, that is it will use 32 bit when in 16 bit mode and vica versa. This also means if you write mov dx, ax
in 32 bit code, you will get 66 89 C2
, which when run in 16 bit mode is mov edx, eax
. So you need to tell the assembler what mode your code is intended to run under.
TL;DR: .code16
ensures that the assembler generates code which when executed by the cpu in 16 bit mode will do what you intended.
Upvotes: 4