代码之家  ›  专栏  ›  技术社区  ›  user123

为什么此汇编代码无法打印到VGA缓冲区

  •  0
  • user123  · 技术社区  · 4 年前

    我有以下汇编代码。我正在尝试创建一个小型引导加载程序,以进入32位保护模式。一旦进入保护模式,我需要打印到VGA文本模式视频存储器(0xb8000)进行测试。我的代码不起作用。我从网络上的各种资源中找到了代码,并了解到大多数代码都有类似的代码,可以正常工作,比如这个例子: Printing characters to screen ASM in protected mode 。我的代码不起作用:

    bits 16
    
    mov ah, 0x00  ;Set up video mode
    mov al, 0x03
    int 0x10
    
    gdt_start:
            dq 0x0
    gdt_code:
            dw 0xFFFF
            dw 0x0
            db 0x0
            db 10011010b
            db 11001111b
            db 0x0
    gdt_data:
            dw 0xFFFF
            dw 0x0
            db 0x0
            db 10010010b
            db 11001111b
            db 0x0  
    
    gdtr:
            dw 24
            dd gdt_start
    
    lgdt [gdtr]
    
    cli
    
    mov eax, cr0
    or al, 1
    mov cr0, eax
    
    jmp 0x08:protectedMode 
    
    bits 32
    
    protectedMode:
        mov ax, 0x10
        mov ds, ax
        mov es, ax 
        mov fs, ax
        mov gs, ax
        mov ss, ax
    
        mov word [0xb8000], 0x0769 
    
    times 510 - ($-$$) db 0
    dw 0xaa55
    

    我用以下代码编译代码:

    nasm -fbin boot.asm -oboot.bin
    

    并使用以下命令运行结果:

    qemu-system-x86_64 -fda boot.bin
    

    它什么都不做。

    当我使用以下代码进行反汇编时:

    ndisasm boot.bin
    

    它输出以下结果:

    enter image description here

    为什么指令在附加零之前

    mov dword [di], 0xb8000
    

    虽然它应该是

    mov word [0xb8000], 0x0769
    
    0 回复  |  直到 4 年前
        1
  •  3
  •   Peter Cordes Steve Bohrer    4 年前

    当你有这个数据块时:

    gdt_start:
            dq 0x0
    gdt_code:
            dw 0xFFFF
            dw 0x0
            db 0x0
            db 10011010b
            db 11001111b
            db 0x0
    gdt_data:
            dw 0xFFFF
            dw 0x0
            db 0x0
            db 10010010b
            db 11001111b
            db 0x0  
    
    gdtr:
            dw 24
            dd gdt_start
    

    它位于执行路径上。此数据将作为代码由处理器执行,作为之后的下一个指令 int 0x10 .将此位置移低,然后 mov word [0xb8000], 0x0769 .

    您还需要在执行该指令后添加一个无限循环,以防止执行落到后面的垃圾中(如果您把它放在GDT表中的话)。

    始终记住,组装是非常低级的。无论你在代码中坚持什么,无论是否是真正有意义的指令,如果处理器能够处理,都将被视为代码。它不会跳过数据,也不会在你写的最后一条指令后停止。


    至于指令反汇编错误的原因,反汇编程序不知道何时切换到32位模式。它只是一个反汇编程序,而不是模拟器,所以它看不到让CPU在32位模式下执行该部分的远jmp的效果。

    你可以在32位模式下反汇编整个东西,然后(在反汇编发生之前的一些混乱之后,重新与实际的指令边界同步)它反汇编成你想要的样子:

    ndisasm -b 32 boot.bin
    
    ...                 ;; some earlier mess of 16-bit code disassembled as 32
    0000003B  8ED8              mov ds,eax
    0000003D  8EC0              mov es,eax
    0000003F  8EE0              mov fs,eax
    00000041  8EE8              mov gs,eax
    00000043  8ED0              mov ss,eax
    00000045  66C70500800B0069  mov word [dword 0xb8000],0x769   ; correct disassembly
             -07
    0000004E  0000              add [eax],al
    00000050  0000              add [eax],al