Transition to the Unreal mode, processor failure

I am writing a simple bootloader. Its main task is to load the kernel and switch the processor to unreal mode. My problem is when I turn on Unreal mode, the processor crashes. Here is my code (Some code is used from MikeOS). I am using NASM.

BITS 16 jmp short bootloader_start ; Jump past disk description section nop ; Pad out before disk description ; ------------------------------------------------------------------ ; Disk description table, to make it a valid floppy ; Note: some of these values are hard-coded in the source! ; Values are those used by IBM for 1.44 MB, 3.5" diskette OEMLabel db "16DOSRUN" ; Disk label BytesPerSector dw 512 ; Bytes per sector SectorsPerCluster db 1 ; Sectors per cluster ReservedForBoot dw 1 ; Reserved sectors for boot record NumberOfFats db 2 ; Number of copies of the FAT RootDirEntries dw 224 ; Number of entries in root dir ; (224 * 32 = 7168 = 14 sectors to read) LogicalSectors dw 2880 ; Number of logical sectors MediumByte db 0F0h ; Medium descriptor byte SectorsPerFat dw 9 ; Sectors per FAT SectorsPerTrack dw 18 ; Sectors per track (36/cylinder) Sides dw 2 ; Number of sides/heads HiddenSectors dd 0 ; Number of hidden sectors LargeSectors dd 0 ; Number of LBA sectors DriveNo dw 0 ; Drive No: 0 Signature db 41 ; Drive signature: 41 for floppy VolumeID dd 00000000h ; Volume ID: any number VolumeLabel db "16DOS "; Volume Label: any 11 chars FileSystem db "FAT12 " ; File system type: don't change! ; ------------------------------------------------------------------ ; Main bootloader 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 push ds ; save real mode lgdt [gdtinfo] ; load gdt register mov eax, cr0 ; switch to pmode by or al,1 ; set pmode bit mov cr0, eax jmp $+2 ; tell 386/486 to not crash mov bx, 0x08 ; select descriptor 1 mov ds, bx ; 8h = 1000b and al,0xFE ; back to realmode mov cr0, eax ; by toggling bit again pop ds ; get back old segment sti ;***********END***********; 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 ; NOTE: A few early BIOSes are reported to improperly set DL cmp dl, 0 je no_change mov [bootdev], dl ; Save boot device number mov ah, 8 ; Get drive parameters int 13h jc fatal_disk_error and cx, 3Fh ; Maximum sector number mov [SectorsPerTrack], cx ; Sector numbers start at 1 movzx dx, dh ; Maximum head number add dx, 1 ; Head numbers start at 0 - add 1 for total mov [Sides], dx no_change: mov eax, 0 ; Needed for some older BIOSes ; First, we need to load the root directory from the disk. Technical details: ; Start of root = ReservedForBoot + NumberOfFats * SectorsPerFat = logical 19 ; Number of root = RootDirEntries * 32 bytes/entry / 512 bytes/sector = 14 ; Start of user data = (start of root) + (number of root) = logical 33 floppy_ok: ; Ready to read first block of data mov ax, 19 ; Root dir starts at logical sector 19 call l2hts mov si, buffer ; Set ES:BX to point to our buffer (see end of code) mov bx, ds mov es, bx mov bx, si mov ah, 2 ; Params for int 13h: read floppy sectors mov al, 14 ; And read 14 of them pusha ; Prepare to enter loop read_root_dir: popa ; In case registers are altered by int 13h pusha stc ; A few BIOSes do not set properly on error int 13h ; Read sectors using BIOS jnc search_dir ; If read went OK, skip ahead call reset_floppy ; Otherwise, reset floppy controller and try again jnc read_root_dir ; Floppy reset OK? search_dir: popa mov ax, ds ; Root dir is now in [buffer] mov es, ax ; Set DI to this info mov di, buffer mov cx, word [RootDirEntries] ; Search all (224) entries mov ax, 0 ; Searching at offset 0 next_root_entry: xchg cx, dx ; We use CX in the inner loop... mov si, kern_filename ; Start searching for kernel filename mov cx, 11 rep cmpsb je found_file_to_load ; Pointer DI will be at offset 11 add ax, 32 ; Bump searched entries by 1 (32 bytes per entry) mov di, buffer ; Point to next entry add di, ax xchg dx, cx ; Get the original CX back loop next_root_entry mov si, file_not_found ; If kernel is not found, bail out call print_string found_file_to_load: ; Fetch cluster and load FAT into RAM mov ax, word [es:di+0Fh] ; Offset 11 + 15 = 26, contains 1st cluster mov word [cluster], ax mov ax, 1 ; Sector 1 = first sector of first FAT call l2hts mov di, buffer ; ES:BX points to our buffer mov bx, di mov ah, 2 ; int 13h params: read (FAT) sectors mov al, 9 ; All 9 sectors of 1st FAT pusha ; Prepare to enter loop read_fat: popa ; In case registers are altered by int 13h pusha stc int 13h ; Read sectors using the BIOS jnc read_fat_ok ; If read went OK, skip ahead call reset_floppy ; Otherwise, reset floppy controller and try again jnc read_fat ; Floppy reset OK? ; ****************************************************************** fatal_disk_error: ; ****************************************************************** mov si, disk_error read_fat_ok: popa mov ax, 2000h ; Segment where we'll load the kernel mov es, ax mov bx, 0 mov ah, 2 ; int 13h floppy read params mov al, 1 push ax ; Save in case we (or int calls) lose it ; Now we must load the FAT from the disk. Here how we find out where it starts: ; FAT cluster 0 = media descriptor = 0F0h ; FAT cluster 1 = filler cluster = 0FFh ; Cluster start = ((cluster number) - 2) * SectorsPerCluster + (start of user) ; = (cluster number) + 31 load_file_sector: mov ax, word [cluster] ; Convert sector to logical add ax, 31 call l2hts ; Make appropriate params for int 13h mov ax, 2000h ; Set buffer past what we've already read mov es, ax mov bx, word [pointer] pop ax ; Save in case we (or int calls) lose it push ax stc int 13h jnc calculate_next_cluster ; If there no error... call reset_floppy ; Otherwise, reset floppy and retry jmp load_file_sector ; In the FAT, cluster values are stored in 12 bits, so we have to ; do a bit of maths to work out whether we're dealing with a byte ; and 4 bits of the next byte -- or the last 4 bits of one byte ; and then the subsequent byte! calculate_next_cluster: mov ax, [cluster] mov dx, 0 mov bx, 3 mul bx mov bx, 2 div bx ; DX = [cluster] mod 2 mov si, buffer add si, ax ; AX = word in FAT for the 12 bit entry mov ax, word [ds:si] or dx, dx ; If DX = 0 [cluster] is even; if DX = 1 then it odd jz even ; If [cluster] is even, drop last 4 bits of word ; with next cluster; if odd, drop first 4 bits odd: shr ax, 4 ; Shift out first 4 bits (they belong to another entry) jmp short next_cluster_cont even: and ax, 0FFFh ; Mask out final 4 bits next_cluster_cont: mov word [cluster], ax ; Store cluster cmp ax, 0FF8h ; FF8h = end of file marker in FAT12 jae end add word [pointer], 512 ; Increase buffer pointer 1 sector length jmp load_file_sector end: ; We've got the file to load! pop ax ; Clean up the stack (AX was pushed earlier) mov dl, byte [bootdev] ; Provide kernel with boot device info jmp 2000h:0000h ; Jump to entry point of loaded kernel! ; ------------------------------------------------------------------ ; BOOTLOADER SUBROUTINES print_string: ; Output string in SI to screen pusha mov ah, 0Eh ; int 10h teletype function .repeat: lodsb ; Get char from string cmp al, 0 je .done ; If char is zero, end of string int 10h ; Otherwise, print it jmp short .repeat .done: popa ret reset_floppy: ; IN: [bootdev] = boot device; OUT: carry set on error push ax push dx mov ax, 0 mov dl, byte [bootdev] stc int 13h pop dx pop ax ret l2hts: ; Calculate head, track and sector settings for int 13h ; IN: logical sector in AX, OUT: correct registers for int 13h push bx push ax mov bx, ax ; Save logical sector mov dx, 0 ; First the sector div word [SectorsPerTrack] add dl, 01h ; Physical sectors start at 1 mov cl, dl ; Sectors belong in CL for int 13h mov ax, bx mov dx, 0 ; Now calculate the head div word [SectorsPerTrack] mov dx, 0 div word [Sides] mov dh, dl ; Head/side mov ch, al ; Track pop ax pop bx mov dl, byte [bootdev] ; Set correct device ret ; ------------------------------------------------------------------ ; STRINGS AND VARIABLES kern_filename db "KERNEL SYS" ; MikeOS kernel filename disk_error db "Error.", 0 file_not_found db "Error.", 0 bootdev db 0 ; Boot device number cluster dw 0 ; Cluster of the file we want to load pointer dw 0 ; Pointer into Buffer, for loading kernel 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: ; ------------------------------------------------------------------ ; END OF BOOT SECTOR AND BUFFER START times 510-($-$$) db 0 ; Pad remainder of boot sector with zeros dw 0AA55h ; Boot signature (DO NOT CHANGE!) buffer: ; Disk buffer begins (8k after this, stack starts) ; ================================================================== 

So how to fix this code? If switching to "unreal" mode is impossible in my case, how can I access the whole memory (4GiB will last), in real mode? I have included A20 in the kernel code. After a few years: it turned out that SmallerC had support for switching to unreal mode, so the whole assembly was not really needed, and I could just write it in C.

+5
source share
1 answer

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 ; ------------------------------------------------------------------ ; Disk description table, to make it a valid floppy ; Note: some of these values are hard-coded in the source! ; Values are those used by IBM for 1.44 MB, 3.5" diskette OEMLabel db "16DOSRUN" ; Disk label BytesPerSector dw 512 ; Bytes per sector SectorsPerCluster db 1 ; Sectors per cluster ReservedForBoot dw 1 ; Reserved sectors for boot record NumberOfFats db 2 ; Number of copies of the FAT RootDirEntries dw 224 ; Number of entries in root dir ; (224 * 32 = 7168 = 14 sectors to read) LogicalSectors dw 2880 ; Number of logical sectors MediumByte db 0F0h ; Medium descriptor byte SectorsPerFat dw 9 ; Sectors per FAT SectorsPerTrack dw 18 ; Sectors per track (36/cylinder) Sides dw 2 ; Number of sides/heads HiddenSectors dd 0 ; Number of hidden sectors LargeSectors dd 0 ; Number of LBA sectors DriveNo dw 0 ; Drive No: 0 Signature db 41 ; Drive signature: 41 for floppy VolumeID dd 00000000h ; Volume ID: any number VolumeLabel db "16DOS "; Volume Label: any 11 chars FileSystem db "FAT12 " ; File system type: don't change! ; ------------------------------------------------------------------ ; Main bootloader code bootloader_start: ; Modify all segment setup code to assume an ORG of 0x0000 mov ax, 07C0h ; Set up 4K of stack space above buffer mov ds, ax ; Set DS segment to where we're loaded add ax, 544 ; 8k buffer = 512 paragraphs + 32 paragraphs (loader) cli ; Disable interrupts while changing stack mov ss, ax mov sp, 4096 ; Enter unreal mode ; Keep interrupts off while we switch to real mode push ds ; Switch to real mode detroys DS. We need to save it lgdt [gdtinfo] ; load gdt register mov eax, cr0 ; switch to pmode by or al,1 ; set pmode bit mov cr0, eax jmp $+2 ; Clear the instruction pre-fetch queue ; Set DS=ES=FS=GS to descriptor with 4gb limit 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 and al,0xFE ; back to realmode mov cr0, eax ; by toggling bit again sti ; enable interrupts pop ds ; Retsore DS to original value ;***********END OF UNREAL MODE SWITCH ***********; ; NOTE: A few early BIOSes are reported to improperly set DL cmp dl, 0 je no_change mov [bootdev], dl ; Save boot device number mov ah, 8 ; Get drive parameters int 13h jc fatal_disk_error and cx, 3Fh ; Maximum sector number mov [SectorsPerTrack], cx ; Sector numbers start at 1 movzx dx, dh ; Maximum head number add dx, 1 ; Head numbers start at 0 - add 1 for total mov [Sides], dx no_change: mov eax, 0 ; Needed for some older BIOSes ; First, we need to load the root directory from the disk. Technical details: ; Start of root = ReservedForBoot + NumberOfFats * SectorsPerFat = logical 19 ; Number of root = RootDirEntries * 32 bytes/entry / 512 bytes/sector = 14 ; Start of user data = (start of root) + (number of root) = logical 33 floppy_ok: ; Ready to read first block of data mov ax, 19 ; Root dir starts at logical sector 19 call l2hts mov si, buffer ; Set ES:BX to point to our buffer (see end of code) mov bx, ds mov es, bx mov bx, si mov ah, 2 ; Params for int 13h: read floppy sectors mov al, 14 ; And read 14 of them pusha ; Prepare to enter loop read_root_dir: popa ; In case registers are altered by int 13h pusha stc ; A few BIOSes do not set properly on error int 13h ; Read sectors using BIOS jnc search_dir ; If read went OK, skip ahead call reset_floppy ; Otherwise, reset floppy controller and try again jnc read_root_dir ; Floppy reset OK? search_dir: popa mov ax, ds ; Root dir is now in [buffer] mov es, ax ; Set DI to this info mov di, buffer mov cx, word [RootDirEntries] ; Search all (224) entries mov ax, 0 ; Searching at offset 0 next_root_entry: xchg cx, dx ; We use CX in the inner loop... mov si, kern_filename ; Start searching for kernel filename mov cx, 11 rep cmpsb je found_file_to_load ; Pointer DI will be at offset 11 add ax, 32 ; Bump searched entries by 1 (32 bytes per entry) mov di, buffer ; Point to next entry add di, ax xchg dx, cx ; Get the original CX back loop next_root_entry mov si, file_not_found ; If kernel is not found, bail out call print_string found_file_to_load: ; Fetch cluster and load FAT into RAM mov ax, word [es:di+0Fh] ; Offset 11 + 15 = 26, contains 1st cluster mov word [cluster], ax mov ax, 1 ; Sector 1 = first sector of first FAT call l2hts mov di, buffer ; ES:BX points to our buffer mov bx, di mov ah, 2 ; int 13h params: read (FAT) sectors mov al, 9 ; All 9 sectors of 1st FAT pusha ; Prepare to enter loop read_fat: popa ; In case registers are altered by int 13h pusha stc int 13h ; Read sectors using the BIOS jnc read_fat_ok ; If read went OK, skip ahead call reset_floppy ; Otherwise, reset floppy controller and try again jnc read_fat ; Floppy reset OK? ; ****************************************************************** fatal_disk_error: ; ****************************************************************** mov si, disk_error read_fat_ok: popa mov ax, 2000h ; Segment where we'll load the kernel mov es, ax mov bx, 0 mov ah, 2 ; int 13h floppy read params mov al, 1 push ax ; Save in case we (or int calls) lose it ; Now we must load the FAT from the disk. Here how we find out where it starts: ; FAT cluster 0 = media descriptor = 0F0h ; FAT cluster 1 = filler cluster = 0FFh ; Cluster start = ((cluster number) - 2) * SectorsPerCluster + (start of user) ; = (cluster number) + 31 load_file_sector: mov ax, word [cluster] ; Convert sector to logical add ax, 31 call l2hts ; Make appropriate params for int 13h mov ax, 2000h ; Set buffer past what we've already read mov es, ax mov bx, word [pointer] pop ax ; Save in case we (or int calls) lose it push ax stc int 13h jnc calculate_next_cluster ; If there no error... call reset_floppy ; Otherwise, reset floppy and retry jmp load_file_sector ; In the FAT, cluster values are stored in 12 bits, so we have to ; do a bit of maths to work out whether we're dealing with a byte ; and 4 bits of the next byte -- or the last 4 bits of one byte ; and then the subsequent byte! calculate_next_cluster: mov ax, [cluster] mov dx, 0 mov bx, 3 mul bx mov bx, 2 div bx ; DX = [cluster] mod 2 mov si, buffer add si, ax ; AX = word in FAT for the 12 bit entry mov ax, word [ds:si] or dx, dx ; If DX = 0 [cluster] is even; if DX = 1 then it odd jz even ; If [cluster] is even, drop last 4 bits of word ; with next cluster; if odd, drop first 4 bits odd: shr ax, 4 ; Shift out first 4 bits (they belong to another entry) jmp short next_cluster_cont even: and ax, 0FFFh ; Mask out final 4 bits next_cluster_cont: mov word [cluster], ax ; Store cluster cmp ax, 0FF8h ; FF8h = end of file marker in FAT12 jae end add word [pointer], 512 ; Increase buffer pointer 1 sector length jmp load_file_sector end: ; We've got the file to load! pop ax ; Clean up the stack (AX was pushed earlier) mov dl, byte [bootdev] ; Provide kernel with boot device info jmp 2000h:0000h ; Jump to entry point of loaded kernel! ; ------------------------------------------------------------------ ; BOOTLOADER SUBROUTINES print_string: ; Output string in SI to screen pusha mov ah, 0Eh ; int 10h teletype function .repeat: lodsb ; Get char from string cmp al, 0 je .done ; If char is zero, end of string int 10h ; Otherwise, print it jmp short .repeat .done: popa ret reset_floppy: ; IN: [bootdev] = boot device; OUT: carry set on error push ax push dx mov ax, 0 mov dl, byte [bootdev] stc int 13h pop dx pop ax ret l2hts: ; Calculate head, track and sector settings for int 13h ; IN: logical sector in AX, OUT: correct registers for int 13h push bx push ax mov bx, ax ; Save logical sector mov dx, 0 ; First the sector div word [SectorsPerTrack] add dl, 01h ; Physical sectors start at 1 mov cl, dl ; Sectors belong in CL for int 13h mov ax, bx mov dx, 0 ; Now calculate the head div word [SectorsPerTrack] mov dx, 0 div word [Sides] mov dh, dl ; Head/side mov ch, al ; Track pop ax pop bx mov dl, byte [bootdev] ; Set correct device ret ; ------------------------------------------------------------------ ; STRINGS AND VARIABLES kern_filename db "KERNEL SYS" ; MikeOS kernel filename disk_error db "Error.", 0 file_not_found db "Error.", 0 bootdev db 0 ; Boot device number cluster dw 0 ; Cluster of the file we want to load pointer dw 0 ; Pointer into Buffer, for loading kernel gdtinfo: dw gdt_end - gdt - 1 ;last byte in table dd gdt+0x7c00 ;start of table gdt dd 0,0 ; entry 0 is always unused flatdesc db 0xff, 0xff, 0, 0, 0, 10010010b, 11001111b, 0 gdt_end: ; ------------------------------------------------------------------ ; END OF BOOT SECTOR AND BUFFER START times 510-($-$$) db 0 ; Pad remainder of boot sector with zeros dw 0AA55h ; Boot signature (DO NOT CHANGE!) buffer: ; Disk buffer begins (8k after this, stack starts) ; ================================================================== 

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:

enter image description here

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.
+3
source

Source: https://habr.com/ru/post/1258725/


All Articles