assembly x86真实的模式函数调用未执行

vlurs2pr  于 2023-03-18  发布在  其他
关注(0)|答案(2)|浏览(163)

我有一些x86 realmode汇编代码,它的行为并不完全符合预期。我相信这个问题与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)推入堆栈,并跳转到执行偏移量为0x200的代码,这条指令位于内存中原点0x7c00的下面,所以看起来它的地址与函数print_char所在的地址不同。
至少我认为这是正在发生的事情,但我可能是完全错误的,因为我是新的。
此外-也许我不允许有一个超过512字节的文件作为 Boot 扇区?

rkttyhzu

rkttyhzu1#

你的代码超过了512字节,这部分代码没有被加载到RAM中,所以实际上它跳转到了一个未初始化的内存地址。你要么加载下一个扇区(在跳转/调用之前),要么像这样:

; maybe you should setup the stack some where here at the start
; ...

; ...
call func
; ...

; your hang instruction
jmp $

; the code below won't be reached except when you call it
; also you use ret so it returns. It will only be executed if you
; explicitly call it or jump to it (for jump returns don't work)
; also this part is before 0xaa55 so it is loaded in your memory.
func:
   ; ... stuff
   ret

; the padding
times 510 - ($ - $$) db 0
dw 0xaa55
suzh9iv8

suzh9iv82#

(It可能会帮助您记住,英特尔汇编代码可以为一个汇编助记符使用多个操作码。因此,“call”有几个不同的版本需要注意)
在偏移量+1e处反汇编的“call 0x200”被编码为e8 df 01,CPU将其作为对下一条指令+01df的相对调用来执行。
因为反汇编默认从偏移量+0开始,反汇编为21+1df(=0200),或者十进制的512。记住print_string是在512字节的 Boot 扇区之后立即汇编的,所以加起来就是512字节。
如果你的代码是在0000:7c00加载的,那么相对调用将转到0000:7e00,这是正确计算的,但正如其他人所说,代码将不在那里,因为它不会被BIOS加载,它只加载第一个扇区。
我以前做过一个 Boot 扇区,我的建议是a)它很容易耗尽空间,所以使用紧凑的代码b)不要假设除了CS:IP之外的任何东西指向你的代码。如果你依赖DS,ES,SS,你可能会发现它们被不同的BIOS和模拟器设置得不同,所以为了安全起见,尝试顶部附近的“mov ax,cs”和“mov ds,ax”等。
你的代码使用“mov al,[si]”来加载字符串数据。默认情况下,si和ds是成对的,所以它从ds:[si]加载。所以你的意外输出可能是因为ds:[si]指向了错误的数据。如果你设置好Bochs,你就能找到答案。

相关问题