在NASM的 x86汇编中,bits指令为什么在我编写用于unreal模式的代码时不起作用?

编程语言 2026-07-12

我是x86汇编语言的新手。我听说BIOS有一种叫做“大模式”的技术,可以执行或访问超出64KB的 BIOS代码和数据,远远超出BIOS的 0-1MB地址空间。因此我想写一段x86汇编代码来模拟这个过程。(编辑者注:这被称为 unreal mode,就像代码块中的注释所称的那样。)

我的做法如下:简要切换到保护模式,将一个基址为0、界限为4GB的数据段描述符加载到某个段寄存器中(如DS、ES、FS、GS),然后立即返回实模式。该段寄存器的描述符缓存应在保护模式下保留设置(基址、界限和属性)。随后,即使在实模式下,使用该段寄存器访问内存时,也能寻址整整4GB的空间,而不再受64KB的限制。

代码如下:

; The Unreal Mode testing program 
; For testing in the Bochs emulator 
; NASM Syntax

org 0x7c00                  ; BIOS loading address

[BITS 16]
start:
    ; Set the segment register
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00

    ; Display message indicating entering real mode
    mov si, msg_real
    call print_string


    mov si, msg_try_high
    call print_string

    xor eax, eax
    mov ax, 0x1000          
    mov ds, ax
    mov bx, 0               
    mov al, [ds:bx]         

    mov si, msg_done
    call print_string

    mov ax, cs          ;
    mov ds, ax 
    ; Enter the main mode
    mov si, msg_unreal
    call print_string

    call enter_unreal       ; Switch to Unreal Mode

    ; Now it is in the main mode and you can access 4GB of storage space. 
    ; Set a test mark at the 2MB position.
    mov eax, 0x200000       ; 2MB address
    mov byte [ds:eax], 0x55 ; Write in the test value

    ;Then read and display from the 2MB position.
    mov eax, 0x200000
    mov bl, [ds:eax]

    ; displaying results
    mov si, msg_success
    call print_string

    ; Read and display the written value
    mov si, msg_value
    call print_string
    mov al, bl
    call print_hex_byte

    ; loop
    jmp $

; Enter the main mode function
enter_unreal:
    cli

    ; Keep the original DS
    push ds

    ; Load GDT
    lgdt [gdt_ptr]

    ; Enter protection mode
    mov eax, cr0
    or al, 1
    mov cr0, eax

    ; Switch to the refresh pipeline
    jmp 0x08:protect_mode

[BITS 32]
protect_mode:
    ; Loading 4GB data segment
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; Return to real mode
    mov eax, cr0
    and al, 0xFE
    mov cr0, eax

    ; Return to the real mode
    jmp 0:real_mode_back

;The declare below have no effect ,why?
[BITS 16]
real_mode_back:
    ; Restore SS and SP
    xor ax, ax
    mov ss, ax
    mov sp, 0x7c00

    ; Restore DS to 0 (but retain the 4GB attribute of the cache)
    pop ds                  ; The original DS = 0

    sti
    ret

;Print string function
print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

; Print hexadecimal bytes
print_hex_byte:
    push ax
    mov ah, al
    shr al, 4
    call print_hex_digit
    mov al, ah
    and al, 0x0F
    call print_hex_digit
    pop ax
    ret

print_hex_digit:
    add al, '0'
    cmp al, '9'
    jle .print
    add al, 7
.print:
    mov ah, 0x0E
    int 0x10
    ret

; data segent
msg_real    db '1. In Real Mode (16-bit)', 13, 10, 0
msg_try_high db '2. Trying to access >1MB memory...', 13, 10, 0
msg_done    db '   (This will wrap around in real mode)', 13, 10, 0
msg_unreal  db '3. Entering Unreal Mode...', 13, 10, 0
msg_success db '4. Unreal Mode active!', 13, 10, 0
msg_value   db '   Value at 2MB: 0x', 0

; GDT

align 8
gdt:
    ;NULL descriptor
    dw 0, 0, 0, 0
    ; Code segment descriptor (Base address 0, Maximum length 4GB)
    dw 0xFFFF, 0, 0x9A00, 0xCF
    ; Data segment descriptor (base address 0, limit 4GB)
    dw 0xFFFF, 0, 0x9200, 0xCF

gdt_ptr:
    dw $ - gdt - 1
    dd gdt

; Fill up to 510 bytes and end with the boot sector flag.
times 510-($-$$) db 0
dw 0xAA55

然后我使用 nasm -f bin bigmode.asm -o bigmode.bin 来编译它。接着使用Bochs的 bximage创建一个软盘映像,并使用

dd if=bigmode.bin of=bigmode.img bs=512 count=1 conv=notrunc

来加载代码。然后在Bochs中运行它。然而,发生了一些奇怪的事情:

当我用调试器在代码后查看时,显然进入了保护模式,它出现在第85行:

(0) [0x000000007c75] 0008:0000000000007c75 (unk. ctxt): mov ax, 0x0010            ; 66b81000
<bochs:5> s
Next at t=14103073
(0) [0x000000007c79] 0008:0000000000007c79 (unk. ctxt): mov ds, ax                ; 8ed8
<bochs:6> s
Next at t=14103074
(0) [0x000000007c7b] 0008:0000000000007c7b (unk. ctxt): mov es, ax                ; 8ec0
<bochs:7> s
Next at t=14103075
(0) [0x000000007c7d] 0008:0000000000007c7d (unk. ctxt): mov fs, ax                ; 8ee0
<bochs:8> s
Next at t=14103076
(0) [0x000000007c7f] 0008:0000000000007c7f (unk. ctxt): mov gs, ax                ; 8ee8
<bochs:9> s
Next at t=14103077
(0) [0x000000007c81] 0008:0000000000007c81 (unk. ctxt): mov eax, cr0              ; 0f20c0
<bochs:10> q
00014103077i[      ] dbg: Quit
00014103077i[CPU0  ] CPU is in protected mode (active)
00014103077i[CPU0  ] CS.mode = 32 bit
00014103077i[CPU0  ] SS.mode = 16 bit
00014103077i[CPU0  ] EFER   = 0x00000000
00014103077i[CPU0  ] | EAX=60000010  EBX=00000000  ECX=00090000  EDX=00000000
00014103077i[CPU0  ] | ESP=00007bfc  EBP=00000000  ESI=000e7d4c  EDI=0000ffac
00014103077i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
00014103077i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00014103077i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00014103077i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103077i[CPU0  ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00014103077i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103077i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103077i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103077i[CPU0  ] | EIP=00007c81 (00007c81)
00014103077i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00014103077i[CPU0  ] | CR3=0x00000000 CR4=0x00000000

但继续执行时,CPU应该切换到实模式,但我发现它仍在运行32位代码,导致错误

(0) [0x000000007c81] 0008:0000000000007c81 (unk. ctxt): mov eax, cr0              ; 0f20c0
<bochs:3> s
Next at t=14103078
(0) [0x000000007c84] 0008:0000000000007c84 (unk. ctxt): and al, 0xfe              ; 24fe
<bochs:4> s
Next at t=14103079
(0) [0x000000007c86] 0008:0000000000007c86 (unk. ctxt): mov cr0, eax              ; 0f22c0
<bochs:5> s
Next at t=14103080
(0) [0x000000007c89] 0008:7c89 (unk. ctxt): jmpf 0x0000:00007c90      ; ea907c00000000
<bochs:6> s
Next at t=14103081
(0) [0x000000007c90] 0000:7c90 (unk. ctxt): xor eax, eax              ; 31c0
<bochs:7> s
Next at t=14103082
(0) [0x000000007c92] 0000:7c92 (unk. ctxt): mov ss, ax                ; 8ed0
<bochs:8> s
Next at t=14103083
(0) [0x000000007c94] 0000:7c94 (unk. ctxt): mov esp, 0xfb1f7c00       ; bc007c1ffb
<bochs:9> s
Next at t=14103084
(0) [0x000000007c99] 0000:7c99 (unk. ctxt): ret                       ; c3
<bochs:10> s
Next at t=14103085
bx_dbg_read_linear: physical memory read error (phy=0x00008ec031fa, lin=0x000000008ec031fa)
<bochs:11> q
00014103085i[      ] dbg: Quit
00014103085i[CPU0  ] CPU is in real mode (active)
00014103085i[CPU0  ] CS.mode = 32 bit
00014103085i[CPU0  ] SS.mode = 16 bit
00014103085i[CPU0  ] EFER   = 0x00000000
00014103085i[CPU0  ] | EAX=00000000  EBX=00000000  ECX=00090000  EDX=00000000
00014103085i[CPU0  ] | ESP=fb1f7c04  EBP=00000000  ESI=000e7d4c  EDI=0000ffac
00014103085i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
00014103085i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00014103085i[CPU0  ] |  CS:0000( 0001| 0|  0) 00000000 ffffffff 1 1
00014103085i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103085i[CPU0  ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00014103085i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103085i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103085i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00014103085i[CPU0  ] | EIP=8ec031fa (8ec031fa)
00014103085i[CPU0  ] | CR0=0x60000010 CR2=0x00000000
00014103085i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
bx_dbg_read_linear: physical memory read error (phy=0x00008ec031fa, lin=0x000000008ec031fa)

这相当奇怪。我在第100行添加了

[BITS 16]

——为什么它仍然在执行32位代码?

我甚至使用 objdump 对bin文件进行反汇编。

objdump -D -b binary -m i386 -M intel bigmode.bin

结果如下:

70:   ea 75 7c 08 00 66 b8    jmp    0xb866:0x87c75                                                                     77:   10 00                   adc    BYTE PTR [eax],al                                                                  79:   8e d8                   mov    ds,eax                                                                             7b:   8e c0                   mov    es,eax                                                                             7d:   8e e0                   mov    fs,eax                                                                             7f:   8e e8                   mov    gs,eax                                                                             81:   0f 20 c0                mov    eax,cr0                                                                            84:   24 fe                   and    al,0xfe                                                                            86:   0f 22 c0                mov    cr0,eax                                                                            89:   ea 90 7c 00 00 00 00    jmp    0x0:0x7c90                                                                         90:   31 c0                   xor    eax,eax                                                                            92:   8e d0                   mov    ss,eax                                                                             94:   bc 00 7c 1f fb          mov    esp,0xfb1f7c00                                                                     99:   c3                      ret                                                                                       9a:   ac                      lods   al,BYTE PTR ds:[esi]                                                               9b:   08 c0                   or     al,al                                                                              9d:   74 06                   je     0xa5                                                                               9f:   b4 0e                   mov    ah,0xe                                                                             a1:   cd 10                   int    0x10                                                                               a3:   eb f5                   jmp    0x9a                                                                               a5:   c3                      ret                                                                                       a6:   50                      push   eax                                                                                a7:   88 c4                   mov    ah,al                                                                              a9:   c0 e8 04                shr    al,0x4                                                                             ac:   e8 09 00 88 e0          call   0xe08800ba                                                                         b1:   24 0f                   and    al,0xf                                                                             b3:   e8 02 00 58 c3          call   0xc35800ba                                                                         b8:   04 30                   add    al,0x30                                                                            ba:   3c 39                   cmp    al,0x39                                                                            bc:   7e 02                   jle    0xc0                                                                               be:   04 07                   add    al,0x7                                                                             c0:   b4 0e                   mov    ah,0xe                                                                             c2:   cd 10                   int    0x10                                                                               c4:   c3                      ret                                                                                       c5:   31 2e                   xor    DWORD PTR [esi],ebp                                                                c7:   20 49 6e                and    BYTE PTR [ecx+0x6e],cl                                                             ca:   20 52 65                and    BYTE PTR [edx+0x65],dl                                                             cd:   61                      popa                                                                                      ce:   6c                      ins    BYTE PTR es:[edi],dx                                                               cf:   20 4d 6f                and    BYTE PTR [ebp+0x6f],cl                                                             d2:   64 65 20 28             fs and BYTE PTR gs:[eax],ch                                                               d6:   31 36                   xor    DWORD PTR [esi],esi                                                                d8:   2d 62 69 74 29          sub    eax,0x29746962                                                                     dd:   0d 0a 00 32 2e          or     eax,0x2e32000a                                                                     e2:   20 54 72 79             and    BYTE PTR [edx+esi*2+0x79],dl                                                       e6:   69 6e 67 20 74 6f 20    imul   ebp,DWORD PTR [esi+0x67],0x206f7420                                                ed:   61                      popa                                                                                      ee:   63 63 65                arpl   WORD PTR [ebx+0x65],sp                                                             f1:   73 73                   jae    0x166                                                                              f3:   20 3e                   and    BYTE PTR [esi],bh                                                                  f5:   31 4d 42                xor    DWORD PTR [ebp+0x42],ecx                                                           f8:   20 6d 65                and    BYTE PTR [ebp+0x65],ch                                                             fb:   6d                      ins    DWORD PTR es:[edi],dx                                                              fc:   6f                      outs   dx,DWORD PTR ds:[esi]                                                              fd:   72 79                   jb     0x178                                                                              ff:   2e 2e 2e 0d 0a 00 20    cs cs cs or eax,0x2020000a                                                               106:   20                                                                                                               107:   20 28                   and    BYTE PTR [eax],ch                                                                 109:   54                      push   esp                                                                               10a:   68 69 73 20 77          push   0x77207369                                                                        10f:   69 6c 6c 20 77 72 61    imul   ebp,DWORD PTR [esp+ebp*2+0x20],0x70617277                                         116:   70                                                                                                               117:   20 61 72                and    BYTE PTR [ecx+0x72],ah                                                            11a:   6f                      outs   dx,DWORD PTR ds:[esi]                                                             11b:   75 6e                   jne    0x18b                                                                             11d:   64 20 69 6e             and    BYTE PTR fs:[ecx+0x6e],ch                                                         121:   20 72 65                and    BYTE PTR [edx+0x65],dh                                                            124:   61                      popa                                                                                     125:   6c                      ins    BYTE PTR es:[edi],dx                                                              126:   20 6d 6f                and    BYTE PTR [ebp+0x6f],ch                                                            129:   64 65 29 0d 0a 00 33    fs sub DWORD PTR gs:0x2e33000a,ecx                                                       130:   2e                                                                                                               131:   20 45 6e                and    BYTE PTR [ebp+0x6e],al                                                            134:   74 65                   je     0x19b                                                                             136:   72 69                   jb     0x1a1                                                                             138:   6e                      outs   dx,BYTE PTR ds:[esi]                                                              139:   67 20 55 6e             and    BYTE PTR [di+0x6e],dl                                                             13d:   72 65                   jb     0x1a4                                                                             13f:   61                      popa                                                                                     140:   6c                      ins    BYTE PTR es:[edi],dx                                                              141:   20 4d 6f                and    BYTE PTR [ebp+0x6f],cl                                                            144:   64 65 2e 2e 2e 0d 0a    fs gs cs cs cs or eax,0x2e34000a                                                         14b:   00 34 2e                                                                                                         14e:   20 55 6e                and    BYTE PTR [ebp+0x6e],dl                                                            151:   72 65                   jb     0x1b8                                                                             153:   61                      popa                                                                                     154:   6c                      ins    BYTE PTR es:[edi],dx                                                              155:   20 4d 6f                and    BYTE PTR [ebp+0x6f],cl                                                            158:   64 65 20 61 63          fs and BYTE PTR gs:[ecx+0x63],ah                                                         15d:   74 69                   je     0x1c8                                                                             15f:   76 65                   jbe    0x1c6                                                                             161:   21 0d 0a 00 20 20       and    DWORD PTR ds:0x2020000a,ecx                                                       167:   20 56 61                and    BYTE PTR [esi+0x61],dl                                                            16a:   6c                      ins    BYTE PTR es:[edi],dx                                                              16b:   75 65                   jne    0x1d2                                                                             16d:   20 61 74                and    BYTE PTR [ecx+0x74],ah                                                            170:   20 32                   and    BYTE PTR [edx],dh                                                                 172:   4d                      dec    ebp                                                                               173:   42                      inc    edx                                                                               174:   3a 20                   cmp    ah,BYTE PTR [eax]                                                                 176:   30 78 00                xor    BYTE PTR [eax+0x0],bh                                                             179:   90                      nop                                                                                      17a:   90                      nop                                                                                      17b:   90                      nop                                                                                      17c:   90                      nop                                                                                      17d:   90                      nop                                                                                      17e:   90                      nop                                                                                      17f:   90                      nop                                                                                             ...                                                                                                              188:   ff                      (bad)                                                                                    189:   ff 00                   inc    DWORD PTR [eax]                                                                   18b:   00 00                   add    BYTE PTR [eax],al                                                                 18d:   9a cf 00 ff ff 00 00    call   0x0:0xffff00cf                                                                    194:   00 92 cf 00 17 00       add    BYTE PTR [edx+0x1700cf],dl                                                        19a:   80 7d 00 00             cmp    BYTE PTR [ebp+0x0],0x0                                                                   ...                                                                                                              1fe:   55                      push   ebp                                                                               1ff:   aa                      stos   BYTE PTR es:[edi],al

它也指出了问题

[BITS 16]

不起作用。

我不知道为什么?需要添加一些代码才能让它工作吗?

我的nasm版本是2.15.05,我的操作系统是WSL的 Ubuntu 22.04。我的Bochs版本是2.6.8。

解决方案

但当它继续执行时,CPU应该切换到实模式,但我发现它仍在运行32位代码,这导致错误

确实如此。有意思的是,它确实切换到了实模式,但仍在运行32位代码。相关日志:

00014103085i[CPU0  ] CPU is in real mode (active)
00014103085i[CPU0  ] CS.mode = 32 bit
00014103085i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00014103085i[CPU0  ] |  CS:0000( 0001| 0|  0) 00000000 ffffffff 1 1

所以这就是原因——CS描述符缓存仍然保持D=1(这是决定何时启用16位还是32位模式的标志)。Unreal模式之所以可行,是因为进入实模式前设置的界限保持不变,并且在重新载入段寄存器时从不被覆盖。问题在于,实模式中不仅基址会被保留,其他一切也会被保留,其中就包括DB标志。这就是为什么任何从32位保护模式切换到16位实模式的代码在执行前必须经过16位 PM(例如参见https://retrocomputing.stackexchange.com/questions/32242/is-it-possible-to-switch-from-32-bit-protected-mode-to-real-mode-without-going-t)。

所以你需要确保在返回到(非)实模式之前,你的代码是在16位保护模式下运行。要么再添加一个16位的CS选择子,要么完全放弃32位(毕竟你并不真的需要它,你只需要短暂地切换到保护模式以重新加载描述符缓存,这在16位就可以完成)。

这很奇怪。我在第100行添加了 [BITS 16]。为什么它仍然在执行32位代码?

因为这不是CPU指令,而是汇编器指令。它并不会切换CPU模式!你的程序控制CPU及其模式,而 BITS 告诉汇编器程序中哪一部分应该在哪种模式下运行(以便它能输出CPU能理解的机器码)。是否能始终让描述所谓CPU模式的指令与实际运行时的CPU模式相匹配,取决于你自己。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章