【问题标题】:About 8086 real mode 'call'关于 8086 实模式“通话”
【发布时间】:2013-12-29 08:13:59
【问题描述】:

我正在学习 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 'call _a20en' 指令中。如果我将 2 行更改为:

lea bx, _a20en
call bx
call bx

代码运行良好。 如果只使用一条“call _a20en”指令,代码也可以正常工作。

真正的问题是在'call _a20en'指令执行中。 push ip 后,跳转到'jmp'。指令,死循环。真正的目的地跳转地址是_a20en,距离'jmp' 2 个字节。说明。

我不知道 2 字节偏移量是如何生成的。组装和链接时没有警告。

【问题讨论】:

  • 您应该将此代码范围缩小到仅用于演示问题所需的代码。
  • 您使用的是哪个汇编程序?另外,您使用哪些命令行调用来组装和链接?
  • Mingw32,gnu 工具链,windows 版本。 gcc 4.7.1,作为 2.22
  • 您似乎没有在该内联链接器脚本中指定 0x7C00 原点。请记住,BIOS 会跳转到 0000:7C00,而不是 07C0:0000。我本来希望相对工作和绝对 lea 失败,所以也许我错了
  • 哦,您能否发布生成的二进制图像,以便我们分解它们并仔细检查编码?

标签: assembly x86-16


【解决方案1】:

这里记录的是扇区映像的反汇编版本。我想我不妨用另一篇帖子向这个帖子发送垃圾邮件。

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完成二进制和反汇编)

【讨论】:

  • 我完全同意你的看法。我应该对链接器进行一些研究。
【解决方案2】:

我在自己的程序中也观察到使用 GNU 工具链 (ld.exe) 存在问题:

GNU 链接器(MinGW 变体“ld.exe”)并不总是(甚至从不)正确解析 PC 相关的 16 位重定位。

因此,您不应对其他目标文件使用 (near) "call" 和 (near) "jmp" 指令,但您必须使用 "lea" 和 "call cx" (或 "call bx")。

为什么程序只使用一条“call _a20en”指令?

答案很简单:链接器在目标文件之间插入“NOP”指令(取决于文件长度)。当您只使用一条“call _a20en”指令时,文件长度足够短,以至于插入了两个 NOP。当然不能依赖这个!

【讨论】:

  • 是的,我想是的。我已经在 Kubuntu 12.10 32 位中测试了相同的代码。只需将工具链更改为 linux 版本。然后组装和链接并运行,一切顺利!!!
  • @MartinRosenau 在我读到这篇文章之前,我以为我疯了;我不明白 2 字节错误来自哪里!如果您还没有提交错误报告,则应该提交。
【解决方案3】:
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 = .;
}

【讨论】:

  • 最后一步:objcopy -O binary -S boot.elf boot.bin
【解决方案4】:

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

【讨论】:

  • 抱歉,我懒得手动输入或使用 OCR 软件摆弄。您可以改为以纯文本形式发布十六进制转储吗?这样我们就可以直接用 xxd -r 恢复文件
  • evernote.com - 将为您将该图像转换为文本:)。但完全同意谁会在此处发布类似的屏幕截图该死的
  • 我发现这个目标代码存在一些问题。首先是 TASM 汇编为 33 的第一个字节,但这里是 31。是因为它可能。下一个问题是@0013。字节序列 E83400 应该跳转到 0016+0034 = 004a - 但这是一个 FE (不管是什么......) - 它应该跳转到 004b 或 004c (因为 @004b 是一个 NOP)现在也许我是有点过时,但我认为,列表在这里会非常有用。
  • 谢谢。最新的答案是您需要的文本。
猜你喜欢
  • 1970-01-01
  • 2014-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多