我有一些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 扇区?
2条答案
按热度按时间rkttyhzu1#
你的代码超过了512字节,这部分代码没有被加载到RAM中,所以实际上它跳转到了一个未初始化的内存地址。你要么加载下一个扇区(在跳转/调用之前),要么像这样:
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,你就能找到答案。