我正在学习i8086实模式编程。
在第一个.S程序集cdoe文件中:
.intel_syntax noprefix
/* _a20en defined in another .S file */
.section .text
.code16
.extern _a20en
/* code entry is 0x7c00, defined in BIOS */
entry:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov gs, ax
mov sp, 0x7c00
mov ax, 0xb800
mov fs, ax
cld
call _a20en
call _a20en
/* send string '8086' to video ram */
mov byte ptr fs : [0], '8'
mov byte ptr fs : [1], 0x01
mov byte ptr fs : [2], '0'
mov byte ptr fs : [3], 0x01
mov byte ptr fs : [4], '8'
mov byte ptr fs : [5], 0x01
mov byte ptr fs : [6], '6'
mov byte ptr fs : [7], 0x01
jmp . /* stop here */
第二个.S汇编代码文件:
.intel_syntax noprefix
.section .text
.code16
.global _a20en
_a20en:
enter 2, 0
push ax
in al, 0x92
or al, 0x02
out 0x92, al
pop ax
leave
ret
组装后,连接并运行。我在屏幕上看不到字符串'8086'。在bochs中调试之后,我发现问题出在2'调用_a20en'指令中。如果我将2行更改为:
lea bx, _a20en
call bx
call bx
代码工作正常。 如果只使用一个'call _a20en'指令,代码也能正常工作。
真正的问题是在'call _a20en'指令中执行。推ip后,然后跳转到'jmp'。指令,死循环。真正的目标跳转地址是_a20en,远离'jmp'2个字节。指令。
我不知道如何生成2字节偏移量。装配时没有任何警告。联。
答案 0 :(得分:2)
这里的记录是扇区图像的反汇编版本。我想我也可以用另一篇帖子把这个帖子垃圾邮件。
00007C00 31C0 xor ax,ax
00007C02 8ED8 mov ds,ax
00007C04 8EC0 mov es,ax
00007C06 8ED0 mov ss,ax
00007C08 8EE8 mov gs,ax
00007C0A BC007C mov sp,0x7c00
00007C0D B800B8 mov ax,0xb800
00007C10 8EE0 mov fs,ax
00007C12 FC cld
00007C13 E83400 call word 0x7c4a
00007C16 E83100 call word 0x7c4a
00007C19 64C606000038 mov byte [fs:0x0],0x38
00007C1F 64C606010001 mov byte [fs:0x1],0x1
00007C25 64C606020030 mov byte [fs:0x2],0x30
00007C2B 64C606030001 mov byte [fs:0x3],0x1
00007C31 64C606040038 mov byte [fs:0x4],0x38
00007C37 64C606050001 mov byte [fs:0x5],0x1
00007C3D 64C606060036 mov byte [fs:0x6],0x36
00007C43 64C606070001 mov byte [fs:0x7],0x1
00007C49 EBFE jmp short 0x7c49
00007C4B 90 nop
00007C4C C8020000 enter 0x2,0x0
00007C50 50 push ax
00007C51 E492 in al,0x92
00007C53 0C02 or al,0x2
00007C55 E692 out 0x92,al
00007C57 58 pop ax
00007C58 C9 leave
00007C59 C3 ret
00007C5A 90 nop
00007C5B 90 nop
请注意,(相对!)调用会缩短两个字节。我只能假设这是链接器脚本的一个问题,但说实话,我不太了解黑魔法的特定领域,告诉你什么。
至于变通方法,我怀疑如果将图像链接为单个目标文件,这将正常工作。那也应该为你摆脱32位对齐。
(在https://sites.google.com/site/doynax/gary_sector.zip完成二进制和反汇编)
答案 1 :(得分:1)
g++ -c -o xx.o xx.S
ld -T boot.lds
The file boot.lds :
OUTPUT_ARCH(i8086)
ENTRY(_start)
MEMORY
{
ROM (rx) : ORIGIN = 0x7c00, LENGTH = 0x1f0
RAM (rwx) : ORIGIN = 0x7df0, LENGTH = 0x10
}
/* the 0x16 bytes RAM seg is for pading 0xaa55 */
SECTIONS
{
/* may overide by cmd 'ld -Ttext 0x...' */
. = ORIGIN(ROM);
_start = .;
.text :
{
boot.o (.text)
lib.o (.text)
*(.rodata)
} > ROM
.data :
{
*(.data)
} > RAM
_end = .;
}
答案 2 :(得分:1)
使用GNU工具链(ld.exe)时出现问题我也在自己的程序中观察到:
GNU链接器(MinGW变体“ld.exe”)并不总是(甚至从不)正确解析与PC相关的16位重定位。
因此你不应该对其他目标文件使用(近)“call”和(near)“jmp”指令,但是你必须使用“lea”和“call cx”(或“call”)来解决这个问题。 BX“)。
为什么程序只使用一个“call _a20en”指令?
答案很简单:链接器在目标文件之间插入“NOP”指令(取决于文件长度)。当您只使用一个“call _a20en”指令时,文件长度足够短,已插入两个NOP。当然你不能依赖这个!
答案 3 :(得分:-2)
以下是最终的boot.bin数据快照。