BLOG main image
LoofBackER's Blog
Jun - Sik , Yang Blog


Porting uC/OS-II to the x86 Protected Mode

Copyright (C) 2001-2006, Jean Louis Gareau. All rights reserved.
The material presented below is for educational purposes only.
To use this material for commercial purposes, please contact the author at

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

mC/OS-II 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. For more details about mC/OS-II, see the Reference section or visit

This port requires the following development tools:

  • Microsoft Macro Assembler?6.11 (MASM). Note: any other version (including 6.11d) may not work.
  • Microsoft Visual C++?6.0 (VC++).


The port ( can be downloaded for free, as long as it is not being used commercially.


The port is loosely based on a mix of the small & large memory models provided on the standard mC/OS-II distribution. The major differences are highlighted below:

  Large memory model implementation Protected mode implementation
Registers 32-bit real mode pointers (i.e. 16-bit segment registers and 16-bit offsets) 32-bit protected mode registers (i.e. no segment register, 32-bit offsets)
Initialization MS-DOS based. The system actually runs as an MS-DOS application and occasionally makes BIOS calls. Full autonomy. No Ms-DOS nor BIOS dependencies.
Clock Partially relies on MS-DOS clock handler Full implementation

The X86 ports provided on the standard mC/OS-II distribution have some real mode dependencies:

  • mC/OS-II is loaded and runs as a real-mode DOS application.
  • The underlying system is already initialized and is in a known state (thanks to DOS).
  • The implementation calls the default DOS clock interrupt handler upon completing its internal handling of that interrupt.

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:

Steps Description
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 a 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 uC/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++ 6.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-II is normally initialized within the application, by calling OsInit(). This assumes that the application has already been loaded and started. But in the current 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 protected mode is activated with a flat memory model that spans 4GB of physical memory.
  • All segment registers are initialized to default values in order to "forget" about them (hence obtaining a flat, non-segmented memory model).
  • A temporary 32-bit stack is set to address 8000h (an arbitrary address).
  • A call is done to main(), which is the application’s entry point.
  • In case main() returns, an infinite loop is executed. This is not really required, since main() is not expected to return.

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

  • Global Descriptor Table (GDT). Since all tasks are given full privileges (CPL 0) and share the same address space (e.g. the entire physical memory), only one code and data descriptors are required.
Entry Description
00h Unused (set to 0)
08h 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)
10h 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-II and the application (the number can be increased as needed).
Entry Description
00h-1Fh CPU interrupts and exceptions
20h-2Fh Hardware Interrupt Request Lines (IRQs)
30h-3F Available for mC/OS and the application
40h-FFh Unused
  • There is no Local Descriptor Table (LDT) in use.

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

  • Enable the address line 20, normally disabled for some real mode considerations. The line is enabled by sending a few commands to the Intel 8042 keyboard controller. See InitA20() for details (os_cpu_c.c)
  • Relocate the IRQ interrupts, since the overlap the CPU interrupts and exceptions (for instance, it is not possible to know if the interrupt 0 has been triggered by the clock or a division by zero). The IRQ are relocated in the range 20h-2Fh by sending a few commands to the Intel 8259 interrupt controllers. See InitPIC() for details (os_cpu_c.c)
  • The interrupt table is initialized by using SetIntVector() and SetIDTGate(). The 64 entries are set to point to a default interrupt handler (DefIntHandler(), in os_cpu_a.asm), which simply performs an interrupt return
  • The General Protection Fault (interrupt 13h), triggered by the CPU when a fault is detected (assigning invalid values to segment registers, executing an interrupt above 40h, etc.), is assigned a dump stack handler (DumpStackHandler(), in os_cpu_a.asm). This handler displays the address of the faulty instruction on the upper-left corner of the screen and enters an infinite loop. This is useful to identify the source of a problem.
  • The clock handler (OsTickISR(), in os_cpu_a.asm) is installed as the interrupt 20h handler.
  • The mC/OS-II context switch handler (OSCtxSw(), in os_cpu_a.asm), is installed as the interrupt uCOS handler (uCOS is defined as 0x30 in os_cpu.h).

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 are re-enabled when the first task is executed.

Converting to a 32-Bit, Flat Memory Model
  • A few typedefs and definitions are added in os_cpu.h:   
    • The format of an interrupt gate (IDT_GATE)
    • Interrupt gate types (IDTGATE_xxx)
    • The default code selector (CS_SELECTOR), required to initialize the interrupt gates
  • OSTaskStkInit() (in os_cpu_c.c) is updated to take into account:
    • The return address (which is TaskBucket()), should a task returns from its main function
    • The initial flag register. Note that the IF bit (interrupt enabled) must be set, otherwise interrupts will be disabled for that task
    • The code selector, which is always 08h (the second GDT entry)
    • 32-bit offset registers.
  • Converting the 16-bit real mode assembly functions (OSTickISR(), OSStartHighRdy(), OSCtxSw(),OSIntCtxSw(), all in os_cpu_a.asm), which consist of:
    • Removing segment registers
    • Using 32-bit offset registers (EAX instead of AX for instance)
    • Using 32-bit instructions (iretd instead of iret, for instance)
  • OSTickISR() (Ix86p_a.asm) makes no longer a call to the previous clock interrupt handler, but it sends an end-of-interrupt to the interrupt controllers (i8259). This would also be required for each hardware interrupt (IRQ) handler.
  • Some C library functions are added: inportb() and outportb(), implemented in assembly.

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

  • In os_cfg.h, the maximum number of tasks (OS_MAX_TASKS) is defined as 2 and the mC/OS interrupt (uCOS) is defined as 0x30.
Building the Application

The installation procedure is straight-forward:

  1. Load and unzip The distribution is as follows:
Subdirectory File Description
  BootSctr.img As already described.
  ExeToImg.exe As already described.
  Ucos-ii.obj The object file of mC/OS-II. The source of it is available from the book.
  Entry.asm The entry point, in assembly. This is where the protected mode is enabled, and is very similar to Example 2.
  Entry.lst The assembled listing of Entry.asm.
  Entry.obj The assembled application entry point.
  Makefile The Makefile to build Entry.obj.
  MyTask.dsw The Visual C++ (VC++) project file.
  MyTask.c Source file of the test application.
  Includes.h Os_cfg.h Ucos.h mC/OS-II and application header files.
  os_cpu_c.c os_cpu.h os_cpu_a.asm The 80x86 protected mode port of mC/OS-II
  Build.bat As described earlier.
  Debug.txt As described earlier.
  1. Build the project using Visual C++:
  • Go to the Dev sub-directory.
  • Double-click on the MyTask.dsw file to open the project in Visual C++ 6.0
  • Press F7 to build the application. The final image will be in Release\MyTask.img
  • Insert a formatted 1.44MB floppy disk in your drive A:
  • Double-click on (or execute) the file Build.bat to build a bootable floppy disk
  • Reboot with the diskette in drive A: in place, or transfer the diskette to another machine and reboot that machine


The next step would be to add paging to mC/OS-II. This step is significant since it will require many changes in the operating system itself. The objective is to have the ability to run separate tasks in their own address space, which could normally span 4GB on a x86. Paging also offers other advantages, such as isolating the kernel from faulty tasks, shared code and data, etc. Best of all, this port will be available on the 386 and up.


  1. Labrosse, Jean J., "MicroC/OS-II The Real-Time Kernel", R&D Publications, 2002

Micrium, mC/OS-II are trademarks of Jean J. Labrosse.

출처 :

1 ··· 45 46 47 48 49 50 51 52 53 ··· 107 


분류 전체보기 (107)
::::::Dairy::::: (5)
:::::what?::::: (1)
:::::Computer::::: (5)
:::::Idea::::: (2)
:::::Want::::: (1)


«   2019/02   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28