How to program in 16 bit protected mode with more than 64kb of data?

622 Views Asked by At

I want to write some code for the 16 bit protected mode, specifically a simple operating system with some programs. I know this sounds silly and it probably is, but I'm interested in understanding how to write programs under these constraints.

I'd like to know what kinds of conventions have been employed in the various operating systems working in 16 bit protected mode (e.g. OS/2 and Win 3.1). What ABI did they use? How are far pointers passed around? Were there multiple ABIs for different code models?

To clarify, I know what far pointers are and how they are used on the API level. What I'd like to know is how this works on assembly level. Are segments for far pointers passed on the stack? Are there any special conventions?

2

There are 2 best solutions below

9
On BEST ANSWER

Most 16-bit protected mode APIs took far pointers as parameters. A far pointer is a 32-bit value containing both a 16-bit offset (low order word) and a 16-bit selector (high order word, selectors refer to segments). It's passed by value by putting it on the stack like any other parameter. Generally these pointers could only refer a region of memory up to 65536 bytes in size, but different far pointers could refer to different regions of memory allowing more than 64K of memory to be used.

For example in the 16-bit Windows API (Win16) the function GetClientRect had the following documented interface:

void GetClientRect(hwnd, lprc)

HWND hwnd;    /* handle of window */
RECT FAR* lprc;   /* address of structure for rectangle   */

The symbol FAR was a macro that expanded to far keyword when using the 16-bit Windows API. Today this API function is documented as taking an LPRECT parameter, which is meant to be read as being "long (far) pointer to RECT". The symbol LPRECT is defined as a typedef of RECT * in the 32-bit and 64-bit Windows APIs. It would be a typedef of RECT far * when using the 16-bit API if that was still supported.

There weren't separate APIs for different memory models (small, medium, compact, large), because of the use of the far keyword on pointers (and the function itself) the API was accessible from all memory models. The compiler would see that it took a far pointer and promote any near (16-bit) pointers as necessary.

1
On

As far as I remember from old days that AT systems with 80286 architectures were introduced, the memory consisted of 64 KB segments which is the max address that can be addressed using a 16-bit address register (real mode). Near and far jumps could happen within or outside of a segment respectively when coding was done in assembly. When a far jump happens, in assembly, you should first of all give the segment that the jump will happen, then the local address within the segment.

In protected mode, a descriptor table could be used to extend the addressing range of the segments which could increase the amount of memory that could be utilized on a machine.

Multi-tasking and also Terminate and Stay Resident (TSR) programs could be implemented using appropriate BIOS and OS interrupts if the code was implemented to be reentrant.