关于8086实模式'来电'

时间:2013-12-29 08:13:59

标签: assembly x86-16

我正在学习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字节偏移量。装配时没有任何警告。联。

4 个答案:

答案 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 sector

以下是最终的boot.bin数据快照。