MikeOS comes with a bootloader that accepts a 0x07c0 segment and an offset of 0x0000 (0x07c0: 0x0000). Part of the offset is also the starting point ( ORG value in NASM). To 20-bit segment: addressing offset : segment 0x07c0 and offset 0x0000 is the physical address 0x07c00 (0x07c0 <4 + 0x0000 = 0x07c00), where the boot loader is expected to be in memory.
It looks like when you used MikeOS, you spliced ββin some unreal mode code from the OSDev Wiki , which assumes the starting point is based on the segment: offset address 0x0000: 0x7c00. This also represents the physical address 0x07c00 (0x0000 <4 + 0x7c00 = 0x7c00). In this case, you will need an ORG 0x7c00 in NASM code.
When building with NASM using the -f bin option (which is the default if no output format is specified): if you do not specify the ORG directive, the default is ORG 0x0000 .
You will need to use one or the other, not one or the other. Since most of the MikeOS bootloader code relies on the 0x07c0 segment and offset 0x0000, itβs easier to change the code to look like the one originally used by the MikeOS bootloader. Following code
bootloader_start: xor ax, ax ; make it zero mov ds, ax ; DS=0 mov ss, ax ; stack starts at seg 0 mov sp, 0x9c00 ; 2000h past code start, ; making the stack 7.5k in size ;***********HERE I TRY TO SWITCH INTO "UNREAL" MODE***********; cli ; no interrupts
Can be changed to:
bootloader_start: ; Modify all segment setup code to assume an ORG of 0x0000 mov ax, 07C0h ; Set data segment to where we're loaded mov ds, ax add ax, 544 ; 8k buffer = 512 paragraphs + 32 paragraphs (loader) cli ; Disable interrupts while changing stack and entering ; protected mode, turn them on after when in unreal mode mov ss, ax mov sp, 4096
You can then remove all of this duplicate code that appears after you finish setting up Unreal mode. These lines need to be eliminated:
mov ax, 07C0h ; Set up 4K of stack space above buffer add ax, 544 ; 8k buffer = 512 paragraphs + 32 paragraphs (loader) cli ; Disable interrupts while changing stack mov ss, ax mov sp, 4096 sti ; Restore interrupts mov ax, 07C0h ; Set data segment to where we're loaded mov ds, ax
Usually, Unreal mode sets all data registers to a flat memory model. Instead of just updating the DS to point to the 4gb flat selector, you can also set the DS / ES / FS / GS registers. Change the code:
mov bx, 0x08 ; select descriptor 1 mov ds, bx ; 8h = 1000b mov es, bx ; 8h = 1000b mov fs, bx ; 8h = 1000b mov gs, bx ; 8h = 1000b
Once this is done, one change to the gdtinfo structure should be made. You put it in the bootloader:
gdtinfo: dw gdt_end - gdt - 1 ;last byte in table dd gdt ;start of table gdt dd 0,0 ; entry 0 is always unused flatdesc db 0xff, 0xff, 0, 0, 0, 10010010b, 11001111b, 0 gdt_end:
Now the problem is that we are using the 0x07c0 segment, and the GDT base now refers to the offset 0x0000 (not 0x7c00). The base address in the gdtinfo structure, which we will load into the GDT register , is a linear address (and not a segment: offset address). In real mode, the linear address and the physical address are one and the same. To make gdt into a linear address, add 0x7c00 to gdt . We modify the line:
dd gdt ;start of table
So now he reads:
dd gdt+0x7c00 ;start of table
Full code with changes
The revised version of the file could be:
BITS 16 jmp short bootloader_start ; Jump past disk description section nop ; Pad out before disk description ;
Bounty
In generosity you say this:
I would like the bonus winner to explain to me why this code is wrong, and help me fix it (so that it works by going into aka flat real mode and loading the kernel file with the name KERNEL.SYS and executing it. The kernel will use interrupts, so the aint protected mode option.)
Protected mode supports software and hardware interrupts. I believe that you want to say that your kernel will use BIOS interrupts. BIOS interrupts are not available during protected mode unless you create a VM86 task or return to real mode.
There may be kernels of real / unreal mode, but they will have limitations with the lack of virtual memory, memory protection and swap mechanisms that are available in protected mode.
Switching to unreal mode requires a temporary entry into protected mode; DS / ES / GS / FS setup using a selector that points to a 16-bit data descriptor with a 4gb limit (not 64k); then disables protected mode. During the switch's switch to protected mode, interrupts must be disabled because there is no established protected mode interrupt vector.
KERNEL.SYS
The MikeOS bootloader requires the KERNEL.SYS file to be placed in the root directory of a disk image formatted as FAT12. I suppose you know how to do this. The method for this is different from Windows and Linux and is beyond the scope of this answer. The kernel.asm sample, which checks if unreal mode is turned on and works, looks like this:
bits 16 ; MikeOS bootloader loads our code at 0x2000:0x0000 so we need org of 0x0000 ; for the kernel code to work properly. org 0x0000 kernel_start: ; Set DS, ES, FS, GS to 0x0000. In Unreal mode these segment registers ; are not limited to 64kb. We can address full 4gb of memory xor ax, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax ; This code will not work in normal real mode. We emit instructions ; that use 0xb8000 as an offset. This offset is >= 65536 and normally ; isn't addressable directly in real mode. This should display MDP white ; on purple to upper left of screen. 0xb8000 is pointer to first cell ; of the text mode video display ; ; In Real AND Unreal mode on a 386 you are allowed to use 32-bit registers ; and memory operands. Real mode is limited to an offset that computes ; to a value below 64kb (65536) unlike unreal mode with a 4gb limit mov edi, 0xb8000 mov word [edi], 0x57<<8 | 'M'; mov word [edi+2], 0x57<<8 | 'D'; mov word [edi+4], 0x57<<8 | 'P'; cli .endloop: hlt jmp .endloop
It can be built in KERNEL.SYS with:
nasm -f bin kernel.asm -o KERNEL.SYS
When a disk image with this bootloader and the KERNEL.SYS file is generated and launched in QEMU (Bochs will be similar), the result will look something like this:

If the processor is not in Unreal mode, the characters written in the upper left corner are not displayed or the hardware / emulator may reach some other type of undefined state.
Other observations and information
- It is not really necessary to place the switch in Unreal mode in the MikeOS bootloader. You can leave the MikeOS bootloader as is and move the switch in Unreal mode to KERNEL.SYS.
- If you intend to access data in memory above 1MiB in any memory area with an odd megabyte number (0x100000-0x1fffff, 0x300000-0x3fffff, ...), you also need to make sure that the A20 gate is turned on.
- The Unreal mode code that you used in your bootloader sets data segments with a 4gb limit. It does not install CS in the same way. This version of Unreal mode is called Big Unreal Mode .
- Handling or calling interrupts (e.g. BIOS interrupts) in Big Unreal mode is the same as in real mode
- If you want to create an interrupt handler, its placement is limited by a small memory area between 0x0000: 0x0000 and 0xFFFF: 0xFFFF. All code in the code segment has the same limitations as in Real mode.
- A comment from @RossRidge that says that you cannot enable interrupts or use BIOS calls in unreal mode, since they can change the value in DS, is not true for Big Unreal mode.
- It is possible to use a 4gb restriction on a code segment, however code above 64kb cannot be reliably used, since interrupts only save CS: IP, not CS: EIP. EIP can be any value between 0 and 4gb, but nothing beyond the first 64kb cannot be performed reliably unless you disable interrupts when you run such code. This is pretty restrictive and why this mode is rarely used. This mode is often called the huge unreal mode .
- @AlexeyFrunze has a number of bootloaders that support kernel booting and supports Unreal mode. Alex also developed the Small C Compiler , which can be used to create code that can be run from its loaders and supports code generation for Unreal mode.