首页 > 解决方案 > x86 实模式函数调用未执行

问题描述

我有一些 x86 实模式汇编代码,其行为与预期不完全一致。我相信这个问题与错误计算的 jmp/call 偏移有关,但我可能弄错了。

这是汇编语言代码:

[org 0x7c00]

mov ah, 0x0e

mov al, 'h'
int 0x10

mov al, 'e'
int 0x10

mov al, 'l'
int 0x10

mov al, 'l'
int 0x10

mov al, 'o'
int 0x10

mov al, '!'
;int 0x10
call print_char

;loop:
;    jmp loop

mov si, mystring
call print_string

jmp $


; fill to 512 bytes
times 510 - ($ - $$) db 0
dw 0xAA55

; the address is stored in si
print_string:
    pusha
    ; load character from si
    mov al, [si]
    cmp al, 0x00
    jz print_string_end
    call print_char ; print the char using the print_char function
    inc si ; increment the string printing index si
print_string_end:
    popa
    ret

; print function: print a single character
; the character is stored in al
print_char:
    pusha
    mov ah, 0x0e
    int 0x16
    popa            ; don't know what registers int 0x16 modifies
    ret

mystring:
db "loading operating system",0x00

这是反汇编:objdump -D -b binary -m i8086 -M intel bootsector.bin

bootsector.bin:     file format binary


Disassembly of section .data:

00000000 <.data>:
   0:   b4 0e                   mov    ah,0xe
   2:   b0 68                   mov    al,0x68
   4:   cd 10                   int    0x10
   6:   b0 65                   mov    al,0x65
   8:   cd 10                   int    0x10
   a:   b0 6c                   mov    al,0x6c
   c:   cd 10                   int    0x10
   e:   b0 6c                   mov    al,0x6c
  10:   cd 10                   int    0x10
  12:   b0 6f                   mov    al,0x6f
  14:   cd 10                   int    0x10
  16:   b0 21                   mov    al,0x21
  18:   e8 f2 01                call   0x20d
  1b:   be 14 7e                mov    si,0x7e14
  1e:   e8 df 01                call   0x200
  21:   eb fe                   jmp    0x21
    ...
 1fb:   00 00                   add    BYTE PTR [bx+si],al
 1fd:   00 55 aa                add    BYTE PTR [di-0x56],dl
 200:   60                      pusha  
 201:   8a 04                   mov    al,BYTE PTR [si]
 203:   3c 00                   cmp    al,0x0
 205:   74 04                   je     0x20b
 207:   e8 03 00                call   0x20d
 20a:   46                      inc    si
 20b:   61                      popa   
 20c:   c3                      ret    
 20d:   60                      pusha  
 20e:   b4 0e                   mov    ah,0xe
 210:   cd 16                   int    0x16
 212:   61                      popa   
 213:   c3                      ret    
 214:   6c                      ins    BYTE PTR es:[di],dx
 215:   6f                      outs   dx,WORD PTR ds:[si]
 216:   61                      popa   
 217:   64 69 6e 67 20 6f       imul   bp,WORD PTR fs:[bp+0x67],0x6f20
 21d:   70 65                   jo     0x284
 21f:   72 61                   jb     0x282
 221:   74 69                   je     0x28c
 223:   6e                      outs   dx,BYTE PTR ds:[si]
 224:   67 20 73 79             and    BYTE PTR [ebx+0x79],dh
 228:   73 74                   jae    0x29e
 22a:   65 6d                   gs ins WORD PTR es:[di],dx
    ...

该文件是用nasm bootsector.asm -f bin -o bootsector.bin

网上1e有说明call 0x200。除非我误解,否则这会将当前(指令指针 + 1)推入堆栈,并跳转以执行 offset 处的代码0x200。这是内存中原点下方的某个位置,即0x7c00,因此它似乎是与函数print_char所在的地址不同的地址。

至少我认为这是正在发生的事情,但我可能完全错了,因为我是新手。

另外-也许我不允许将超过 512 字节的文件作为引导扇区?

标签: assemblyx86real-modeintel-syntax

解决方案


(这可能会帮助您记住,英特尔汇编代码可以为一个汇编助记符使用多个操作码。因此需要注意几种不同版本的“调用”)

在偏移量 +1e 处反汇编的“调用 0x200”被编码为 e8 df 01,CPU 将作为对下一条指令 +01df 的相对调用执行。

因为反汇编默认从偏移量 +0 开始,反汇编为 21+1df (=0200),或十进制的 512。请记住, print_string 是在 512 字节引导扇区之后立即组装的,因此相加。

如果您的代码在 0000:7c00 加载,则相对调用将转到 0000:7e00,这是正确计算的,但正如其他人所说,代码不会在那里,因为它不会被 BIOS 加载,它只会加载第一个部门。

我前段时间做了一个引导扇区,我的建议是 a) 很容易耗尽空间,所以使用紧凑的代码 b) 不要假设 CS:IP 以外的任何东西都指向你的代码。如果您依赖 DS、ES、SS,您可能会发现它们由不同的 BIOS 和模拟器设置不同,因此请尝试靠近顶部的“mov ax,cs”和“mov ds,ax”等,以确保安全。

您的代码使用“mov al,[si]”来加载字符串数据。si 默认与 ds 配对,因此它从 ds:[si] 加载。因此,您的意外输出可能是因为 ds:[si] 指向错误的数据。如果您设置了 Bochs,您将能够找到答案。


推荐阅读