改为"虚幻"模式,处理器崩溃

时间:2016-10-24 16:58:32

标签: c assembly x86 bootloader

我正在编写一个简单的引导程序。它的主要任务是加载内核,并将处理器切换到虚幻模式。我的问题是当我打开虚幻模式时,处理器崩溃了。这是我的代码(一些代码来自MikeOS)。我使用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's 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's 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's 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)


; ==================================================================

那么如何修复这段代码呢?如果切换到"虚幻"在我的情况下模式是不可能的,我怎样才能在实模式下访问整个内存(4GiB会持续)?我在内核代码中打开了A20。 几年后:发现SmallerC支持进入虚幻模式,所以所有的程序集实际上并不需要,我只能用C语言编写。

1 个答案:

答案 0 :(得分:3)

MikeOS附带一个引导加载程序,它假设一段0x07c0,偏移量为0x0000(0x07c0:0x0000)。偏移部分也是 NASM 中的原点(ORG值)。在20-bit segment:offset addressing中:0x07c0的一段和0x0000的偏移量是物理地址0x07c00(0x07c0<< 4 + 0x0000 = 0x07c00),这是引导加载程序应该在内存中的位置。

当您使用MikeOS时,您拼接了某些unreal mode code from OSDev Wiki,假设原点基于段:偏移地址0x0000:0x7c00。这也表示物理地址0x07c00(0x0000 <&lt;&lt; 4 + 0x7c00 = 0x7c00)。在这种情况下, NASM 代码中需要ORG 0x7c00。

使用-f bin选项与 NASM 组合时(如果未指定输出格式,则为默认选项):如果您未指定{{ 1}}指令默认为ORG

您需要使用其中一个,而不是两个。由于大多数MikeOS引导加载程序代码依赖于0x07c0的一段和0x0000的偏移量,因此更容易将代码更改为类似于最初使用的MikeOS引导加载程序。以下代码

ORG 0x0000

可以更改为:

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

然后,您可以删除设置完虚幻模式后出现的所有重复代码。需要消除这些线:

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

通常,虚幻模式将所有数据寄存器设置为平面内存模型。您可以设置 DS / ES / FS / GS 寄存器,而不仅仅是将 DS 更新为指向扁平4gb选择器。修改代码:

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

完成此操作后,需要对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 结构进行一次更改。您将其放入引导程序:

gdtinfo

现在的问题是我们正在使用一段0x07c0,而GDT的基数现在相对于0x0000(不是0x7c00)的偏移量。我们加载到GDT registergdtinfo: 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: 结构中的基址是线性地址(不是段:偏移地址)。在实模式中,线性地址和物理地址是相同的。要将gdtinfo转换为线性地址,我们将0x7c00添加到gdt。我们修改了这一行:

gdt

现在它写着:

    dd gdt                 ;start of table

包含更改的完整代码

您文件的修订版可能是:

    dd gdt+0x7c00          ;start of table

赏金

在赏金中,你这样说:

  

我希望赏金获胜者向我解释为什么这样的代码是错误的并帮助我修复它(使它工作,通过切换到虚幻模式也称为平实模式并加载名为KERNEL.SYS的内核文件并执行它。内核将使用中断,所以保护模式aint选项。)

保护模式支持软件和硬件中断。我相信你的意思是你的内核将使用BIOS中断。除非您创建VM86任务或切换回实模式,否则在受保护模式下运行时BIOS中断不可用。

可以制作真实/虚幻模式内核,但是由于缺少虚拟内存,内存保护和在保护模式下可用的分页机制,它们会受到限制。

进入虚幻模式需要暂时进入保护模式;使用选择器设置 DS / ES / GS / FS ,该选择器指向具有4gb限制(而不是64k)的16位数据描述符;然后关闭保护模式。在切换到保护模式期间,必须禁用中断,因为没有设置保护模式中断向量。

KERNEL.SYS

MikeOS引导加载程序需要将名为KERNEL.SYS的文件放入格式为FAT12的磁盘映像的根目录中。我会假设你知道如何做到这一点。执行此操作的方法在Windows和Linux之间是不同的,并且超出了本答案的范围。一个示例 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's 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's 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's 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) ; ================================================================== ,用于测试是否启用了虚幻模式并且工作如下:

kernel.asm

可以使用以下内容汇总到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

KERNEL.SYS

当生成具有此引导加载程序和KERNEL.SYS文件的磁盘映像并在QEMU中运行(Bochs将类似)时,输出将类似于:

enter image description here

如果处理器未处于虚幻模式,则不会出现写在左上角的字符,或者硬件/仿真器可能会达到某种其他类型的未定义状态。

其他观察和信息

  • 实际上并不需要在MikeOS引导加载程序中将开关置于虚幻模式。您可以将MikeOS引导程序原样保留,并将切换到Unreal模式,转换为KERNEL.SYS。
  • 如果您打算在任何奇数编号的mebibyte内存区域(0x100000-0x1fffff,0x300000-0x3fffff,...)中访问1MiB以上的内存中的数据,那么您还需要确保A20 gate已启用。
  • 您在引导加载程序中使用的虚幻模式代码会设置限制为4gb的数据段。它没有以相同的方式设置CS。此版本的虚幻模式称为 Big Unreal Mode
  • 在Big Unreal模式下处理或调用中断(即BIOS中断)与在实模式下相同
  • 如果要创建中断处理程序,其位置仅限于0x0000:0x0000和0xFFFF:0xFFFF之间的低内存区域。代码段中的所有代码都具有与实模式相同的限制。
  • @RossRidge的评论说这意味着您无法在虚幻模式下启用中断或使用BIOS调用,因为他们可以更改DS中的值对于Big Unreal模式不正确
  • 可以在代码段上使用4gb限制,但是可以可靠地使用64kb以上的代码,因为中断只保存CS:IP而不是CS:EIP。 EIP可以是介于0和4gb之间的任何值,但除非在运行此类代码时禁用中断,否则无法在第一个64kb之外执行任何操作。这是非常严格的限制,为什么很少使用这种模式。此模式通常称为Huge Unreal mode
  • @AlexeyFrunze有一个number of bootloaders,支持加载内核并支持虚幻模式。 Alex还开发了Smaller C Compiler,可以用来生成可以从他的引导加载程序启动的代码,并支持为虚幻模式生成代码。
相关问题