Porting mC/OS to the x86 Protected Mode

Jean Gareau

This page describes the port of mC/OS to a protected mode, 32-bit, flat memory model of the 80386, 80486, Pentium and Pentium II CPUs.

mC/OS is a portable, ROMable, preemptive, real-time, multitasking kernel for microprocessors. Originally written in C and x86 (real mode) assembly languages, it has been ported to a variety of microprocessors.


The port is based on ix86s.c and ix86s_a.asm, a x86 small memory model port provided on the standard mC/OS distribution. Although a large memory model port is also provided, the small memory model closely resembles to the current port, as no segments are required. But the small memory implementation of mC/OS has some real mode dependencies:

These assumptions do not hold in protected mode since DOS only runs in real mode. Consequently the following steps, described in the following sections, are required to complete the port:



Loading the application

The application cannot be loaded by DOS (since there is no DOS) and requires its own loader.

Initializing the hardware

The hardware must be initialized and brought to a known and stable state. Interrupts – particularly the clock interrupt – must be fully processed.

Converting to 32-bit, flat memory model.

Converting 16-bit, real mode code to 32-bit, flat code.

Building the application

Compiling, linking and preparing a bootable disk.

Let’s recall that mC/OS is implemented as a library, or a set of function calls, bundled with the target application. These steps are consequently application-centered.

Loading the Application

The application can be loaded in various ways:

  1. It can be loaded from DOS. But switching into protected mode and fully bypassing DOS and the BIOS may create some inconsistencies inside DOS, preventing a normal return to it. Thus, the PC would have to be rebooted in order to get back to DOS. Also, the application would have to be built as a DOS application, requiring a 16-bit compiler.
  2. It can be loaded from a floppy disk upon boot-up. By providing a bootstrap loader, the application would be the first thing loaded and would be in full control. The PC also has to be rebooted back to DOS (or Windows) if required.
  3. It can be burned into PROM, into a stand-alone system.

Since I did not have a 16-bit compiler (I am using Microsoft Visual C++ 5.0) and I already had a bootstrap loader at hand, I decided to choose method #2. I also used a second PC to test the application, in order not to have to reboot my workstation after each test.

The bootstrap loader (BootSctr.img) can easily be installed on the very first sector of a floppy disk (e.g. the boot sector) by using the DEBUG program, distributed with DOS and Windows. The DEBUG’s write command has the following format:

-w offset disk track sector_count

By executing the following commands:

C:\MyTask>debug bootstrap.img

-w 0100 0 0 1


DEBUG loads the file in memory at offset 0100h (the segment address is irrelevant) and copies 512 bytes from offset 100h (where the file is) onto drive A, on track 0, for 1 sector (512 bytes).

When loaded, this bootstrap loader:

  1. Loads the first 64k of the floppy disk (the first file in fact) at the physical address 1000h (we will see why this address is important later).
  2. Disables the interrupts.
  3. Jumps at 1000h to start executing the application.

Initializing the Hardware

mC/OS is normally initialized within the application, by calling OsInit(). This assumes that the application has already been loaded and started. But in our case, the protected mode must first be activated in order to start the application in a 32-bit, flat memory model mode. Thus, a special application entry point is required, which must be run before main(). Such an entry point is normally provided with a compiler and is operating system-dependent. In our case, since there is no underlying operating system, the entry point must perform itself any initialization tasks required to run the application. Luckily, there are only a few things to care about.

The entry point of the application is coded in Entry.asm. With the interrupts still disabled, the following actions are performed:

The file also contains the system tables required by the processor in protected mode:






Unused (set to 0)



Code segment, with the following attributes:

  • code segment
  • executable
  • read-only
  • base address of 0
  • limit of 4GB (FFFFFh, with the G bit set)
  • D-bit set (to run 32-bit code by default)



Data and Stack segment, with the following attributes:

  • data segment
  • writable
  • base address of 0
  • limit of 4GB (FFFFFh, with the G bit set)
  • B-bit set (to execute 32-bit stack instructions by default)
  • Interrupt Descriptor Table (IDT). Only 64 entries are reserved, among them 16 for mC/OS and the application (the number can be increased as needed).





    CPU interrupts and exceptions



    Hardware Interrupt Request Lines (IRQs)



    Available for mC/OS and the application




    The rest of the hardware initialization is performed in the application. Once in main(), a call is done to OsCpuInit(), in Ix86p.c, in order to perform the following:

    Back in main(), OsInit() is then called to initialize mC/OS. Two tasks are then created by calling OsTaskCreate() twice. Finally, multi-tasking is started by executing OSStart(), which never returns.

    Note that the entire initialization takes place with the interrupts disabled; they will be re-enabled when the first task is executed.

    Converting 16-Bit, Real Mode Code to a 32-Bit, Flat Memory Model

    Additionally, minor changes have been done in the include files:

    Building the Application

    A test application has been developed for Visual C++ 5.0 as an example of 80386 protected mode programming.


    1. Labrosse, Jean J., "mC/OS The Real-Time Kernel", Fourth Printing, R&D Publications, 1992

    mC/OS is a trademark of Jean J. Labrosse.