If you’re going to “pull yourself up by your own bootstraps”, you have to do it carefully…


After RESET, the 80386EX CPU is in a minimal, but known state – barely sufficient to start executing code. It is imperative that the code very carefully initialises each part of the system, without using a part that hasn’t been initialised yet!

Initial State


The CPU’s register state after RESET has been defined by Intel – for the important registers, anyway. The code needs to be organised to be in the correct place for the initial state to start executing code.

  • CR0 set to 0xxx_xxx0h
    The CPU is in Real, rather than Protected mode, with no Paging;
  • DX set to the current version, revision, and stepping of the CPU –
    This is for diagnostic purposes;
  • CS:EIP set to F000h:0000FFF0h
    The final 16 bytes of the address space;
  • DS, ES, FS, GS and SS set to 0000h
    The beginning of the address space;
  • IDTR set to 0000_0000h with a limit of 03FFh
    The Real Mode Interrupt Vector Table.

Note that the value for CS is F000h, which implies it starts accessing code near the top of the 1MiB address space. This is backward-compatible with the original 8086/8088. But the 80386 is a 32-bit address design; so does that mean that there always has to be ROM just below the 1MiB address point of every hardware design?

No: the Intel designers have been smart. “Behind” every segment register is an invisible base address register, and every time a segment register is loaded, a new base address is calculated:

  • In Real Mode, that calculation is simply “left shift segment register value by four”, making F000h indeed 000F_0000h.
  • In Protected Mode, the base address is fetched from the relevant Descriptor Table , allowing the system designer to specify the base address explicitly.

At boot time though, although the value in CS is F000h, its base address is actually initialised as FFFF_0000h! That means that the ROM can be at the very top of the 4GiB address space, leaving everything below that for RAM and other memory-addressed peripherals.

But that trick is only good until CS is loaded for the first time. That loads the base address register with a value below that pesky 1MiB boundary – unless you’re in Protected Mode!

Goal: get into Protected Mode as quickly as possible.


There are a number of peripherals on a computer. The most fundamental would be the ROM, since that holds the boot code. Next, of course, would be the RAM. But RAM chips, especially DRAM, may require initialisation before they can be used. Also, the system has to know, or find out, exactly how much RAM is available. Thus RAM is not available immediately after boot.


Peripherals are accessed on the x86 series by the CPU in one of two ways (or a combination): memory-addressable, as though they were memory; or I/O port addressable, for when the interface to the peripheral is very small.


Memory-addressable peripherals (e.g. ROM, RAM, often video, and perhaps Ethernet cards), all share the same memory address bus, so each needs a separate signal called “chip select” to tell them that the CPU is addressing them specifically. There is often hardware on the computer to provide these, specifically programmed to produce the “memory map” for the system.

For the 80386EX, though, there’s an on-chip Chip Select Unit (CSU), that can generate these chip select signals when the different peripherals are accessed. The beauty of this is less external “glue” circuitry is required; the problem is that their functionality is not available until after the CSU has been initialised.

Of course, the hardware needs the functionality of the CSU immediately after boot: well, at least the ROM does. To that end the CSU is minimally initialised too: all chip selects are disabled except for the “Upper Chip Select” (UCS), which is expected to be used to enable the ROM; and is configured at boot to encompass the entire memory address space. That’s right: all ROM, zero RAM.

Goal: get the CSU initialised as early as possible.

I/O Ports

Many of the on-chip peripherals of the 80386EX are addressed by I/O port; and where they match what was used on the original IBM PC, they have been specifically implemented to match them as closely as possible. This even goes to the address that they are accessed at, meaning that there’s not much that needs to be done to implement a standard IBM PC BIOS.

However, many of the 80386EX peripherals aren’t in the original IBM PC, so need to be outside of that space to be able to access them to avoid interfering with a “traditional” design. That means that to use the advanced peripherals, more setup is required. In particular, the CSU described above is an advanced peripheral, so before it can be accessed the whole advanced peripheral space needs to be activated.

Goal: Activate the 80386EX Advanced Peripheral set as early as possible.


  • No RAM, so no stack – so no PUSHes, POPs, CALLs, or INTs;
  • Don’t program CS – so no JMP FARs;
  • Need to enable RAM, so need to configure the Chip Select Unit, so need to enable the Advanced Peripherals.

First instructions

Tiny first steps

As described above, the first instructions cannot rely on much about the state of the computer; indeed, they’re expected to initialise the system step by step to allow it to be brought to full functionality.

Each step will thus be individually responsible for enabling its own little part of the system. Normally, each would be a subroutine; but that’s not possible since not even RAM is available for CALLs! Thus the initialisation sequence will be a string of individual routines that will simply JMP to the next routine in order.


The sequence of initialisation then will be as follows:

  • Boot
    (Start the boot process for each of the modules below)
  • Initialise (test) CPU
  • Initialise (test) FPU [*]
  • Initialise Advanced Peripheral I/O region
  • Initialise General Purpose I/O (GPIO) pins
    (Allow access to the four LITEs (LEDs) for debugging)
  • Initialise Chip Select Unit
    (Allow the different modules below to configure their own chip selects)
  • Initialise ROM
    (Set the Upper Chip Select to the correct size)
  • Initialise Static RAM chip [*]
    (Set the SRAM chip select to the desired location and size)
  • Initialise Serial IO (non-interrupt-driven)
    (This will allow more sophisticated debug diagnostic messages)
  • Initialise Dynamic RAM module
    (Set the DRAM chip select to the desired location and size)
    (Initialise the DRAM module’s configuration)
  • Initialise Real-Time Clock peripheral [*]
  • Initialise IDE peripheral [*]
    (Set chip select for LIO)
  • Initialise SDCard peripheral [*]
  • Initialise EuroCard Bus
    (Set chip selects for XMEM and XIO)
  • Initialise Video peripheral [*]
  • Initialise Keyboard peripheral [*]

    [*] If attached

Load Operating System

Once the above is done, the system will be fully ready – but not operational. To do that, we need to load the actual operating system.

Now, there is more than enough room on the ROM to have the OS there as well as the boot code, but the development of the OS will be far greater than the development of the boot ROM. And given that programming the boot ROM requires the EEPROM to be removed and reprogrammed every time, doing that for the OS too doesn’t bear thinking about.


So instead, a boot loader mechanism will need to be developed. Given that the development machine will be pretty much permanently connected to the 80386EX board via a serial cable, the easiest mechanism initially would be to implement something like an Intel HEX parser, which would put the values sent into the indicated addresses, and then jump to the indicated start location when the transfer finished.

Later, something like an SDCard or IDE interface loader could be implemented. Of course, that would mean that the boot ROM would need to initialise that peripheral too, and a primitive file system reader would need to be implemented to find the boot file…