Pass Data between 32-bit and 16-bit Code (OS/2)

If a structure will be referenced in both 32-bit and 16-bit code and contains bit-fields or members of type int or enum, you may have to rewrite the structure to ensure that all members align properly.

int
To ensure all integers map the same way, change your integer declarations to use short for 2-byte integers and long for 4-byte integers.
long double
To use your IBM C/2 long double under IBM C and C++ Compilers you must declare it as double.

To use Microsoft C Version 6.0 long double data under IBM C and C++ Compilers, you must recompile it on the Microsoft C Version 6.0 compiler as double first, and place into into a struct to pad out to 128 bits. Although IBM C and C++ Compilers and Microsoft C Version 6.0 both store long double as 80 bit real, IBM C and C++ Compilers stores it in a 16 byte (128 bit) field.

enum
Use the /Su2 compiler option to force enum variables to be 2 bytes in size. This will make them compatible with 16-bit code.

The size of type enum differs between compilers. For example, the IBM C/2 makes all enum types 2 bytes, while IBM C and C++ Compilers defines the size as 1, 2, or 4 bytes, depending on the range of values the enumeration contains.

You can use the /Su compiler option to force IBM C and C++ Compilers to make the enum type 1,2, or 4 bytes, or to use the SAA rules that make all enum types the size of the smallest integral type that can contain all variables.

bit fields
IBM C and C++ Compilers and 16-bit compilers use entirely different algorithms for packing bit fields. In general, if the bit fields are packed "tightly", they will be compatible. "Tightly" means that there is no padding introduced by the compiler. IBM C/2 and Microsoft C Version 6.0 use the type specifier for the bit field member to determine the size of the bit field area. IBM C and C++ Compilers allocates the minimum number of bytes possible.
    struct s1 {            /* Compatible */    (1)
     int a : 4;
     int b : 7;
     int c : 5;
   };
 
   struct s2 {            /* Not compatible */ (2)
     int z : 9;
     int y : 12;
   };
 

(1) Because the type of the bit field members is int, Microsoft C Version 6.0 will allocate 16 bits' (sizeof(int)) of space in the struct to store the bit field.

There are 16 bits of bit fields declared in the declaration of s1, so the bit fields are packed tightly.

IBM C and C++ Compilers allocates 2 bytes for the bit field, and is compatible with Microsoft C Version 6.0 as they lay out the bit field in precisely the same way.

(2) There is not enough room in the 16-bit int to store the first two members of s2, so Microsoft C Version 6.0 introduces 7 bits of padding to made the member z fill out an entire int. It introduces an additional 4 bits of padding after y to fill up the last byte. This bit field requires 4 bytes of storage using Microsoft C Version 6.0, but only 3 bytes using IBM C and C++ Compilers. Therefore, the padding introduced by the compiler makes the bit fields incompatible.

Compilers using 16-bit format align these types on 1-byte boundaries.

IBM C and C++ Compilers aligns members of type int, long, float, double, long double, and pointer on 4-byte boundaries.

structure layout
Use the /Sp1 compiler option or #pragma pack(1) on structure declarations that will be passed to or from 16-bit code.
segmented pointers
If the pointer is passed to the function as a member of an aggregate or an array, you must qualify it with _Seg16. The _Seg16 keyword tells the compiler to store the pointer as a segmented pointer and not as a flat pointer used in 32-bit code. Segmented means a 16-bit segment selector and a 16-bit offset. The _Seg16 keyword is also required if you are using two or more levels of indirection (for example, a pointer to a pointer).

If the pointer is passed directly as a parameter, the compiler automatically converts it to a 16-bit pointer and the _Seg16 keyword is not required. For example, the declaration

void _Far16 _cdecl foo(char *);

is equivalent to

void _Far16 _cdecl foo(char * _Seg16);

It is good programming style to explicitly declare pointer parameters in 16-bit function prototypes as being _Seg16.

If your pointers are used primarily as parameters to 16-bit functions and are not used extensively in your 32-bit code, it may be better to declare them with _Seg16.

Use the _Seg16 qualifier only when necessary. Because of the conversions that are performed whenever a _Seg16 pointer is used in 32-bit code, unnecessary use of segmented pointers can cause a noticeable degradation in application performance.



Call Between 32-bit and 16-bit Code


Declare Segmented Pointers