background image

Vol. 3B 21-3

MIXING 16-BIT AND 32-BIT CODE

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.

21.3 

SHARING DATA AMONG MIXED-SIZE CODE SEGMENTS

Data segments can be accessed from both 16-bit and 32-bit code segments. When a data segment that is larger 
than 64 KBytes is to be shared among 16- and 32-bit code segments, the data that is to be accessed from the 16-
bit code segments must be located within the first 64 KBytes of the data segment. The reason for this is that 16-
bit pointers by definition can only point to the first 64 KBytes of a segment. 
A stack that spans less than 64 KBytes can be shared by both 16- and 32-bit code segments. This class of stacks 
includes:

Stacks in expand-up segments with the G (granularity) and B (big) flags in the stack-segment descriptor clear.

Stacks in expand-down segments with the G and B flags clear.

Stacks in expand-up segments with the G flag set and the B flag clear and where the stack is contained 
completely within the lower 64 KBytes. (Offsets greater than FFFFH can be used for data, other than the stack, 
which is not shared.)

See Section 3.4.5, “Segment Descriptors,” for a description of the G and B flags and the expand-down stack type.
The B flag cannot, in general, be used to change the size of stack used by a 16-bit code segment. This flag controls 
the size of the stack pointer only for implicit stack references such as those caused by interrupts, exceptions, and 
the PUSH, POP, CALL, and RET instructions. It does not control explicit stack references, such as accesses to 
parameters or local variables. A 16-bit code segment can use a 32-bit stack only if the code is modified so that all 
explicit references to the stack are preceded by the 32-bit address-size prefix, causing those references to use 32-
bit addressing and explicit writes to the stack pointer are preceded by a 32-bit operand-size prefix.
In 32-bit, expand-down segments, all offsets may be greater than 64 KBytes; therefore, 16-bit code cannot use 
this kind of stack segment unless the code segment is modified to use 32-bit addressing.

21.4 

TRANSFERRING CONTROL AMONG MIXED-SIZE CODE SEGMENTS

There are three ways for a procedure in a 16-bit code segment to safely make a call to a 32-bit code segment:

Make the call through a 32-bit call gate.

Make a 16-bit call to a 32-bit interface procedure. The interface procedure then makes a 32-bit call to the 
intended destination.

Modify the 16-bit procedure, inserting an operand-size prefix before the call, to change it to a 32-bit call.

Likewise, there are three ways for procedure in a 32-bit code segment to safely make a call to a 16-bit code 
segment:

Make the call through a 16-bit call gate. Here, the EIP value at the CALL instruction cannot exceed FFFFH.

Make a 32-bit call to a 16-bit interface procedure. The interface procedure then makes a 16-bit call to the 
intended destination.

Modify the 32-bit procedure, inserting an operand-size prefix before the call, changing it to a 16-bit call. Be 
certain that the return offset does not exceed FFFFH.

These methods of transferring program control overcome the following architectural limitations imposed on calls 
between 16-bit and 32-bit code segments:

Pointers from 16-bit code segments (which by default can only be 16 bits) cannot be used to address data or 
code located beyond FFFFH in a 32-bit segment.

The operand-size attributes for a CALL and its companion RETURN instruction must be the same to maintain 
stack coherency. This is also true for implicit calls to interrupt and exception handlers and their companion IRET 
instructions.

A 32-bit parameters (particularly a pointer parameter) greater than FFFFH cannot be squeezed into a 16-bit 
parameter location on a stack.