实模式程序集中指向 .text 的指针

Pointer to .text in real mode assembly

我对实模式寻址一定有一些基本的误解。我正在尝试设置一个函数以在实模式下通过 BIOS 中断打印文本。我正在使用在 DOSBox 下执行的 .com 文件测试代码。 .text 部分结束于 0x1000(.com 文件中的 0x0F00)。所以假设我想打印该文本的第一个字母。

xor ebx, ebx
mov ecx, 1
mov ah, 10
mov al, ds:[0x1000]
int 0x10

可行,并打印出 'H',因为我没有想象力。但是我不希望它只打印出相同的字母。我想传入一个指针,并且我想在打印更多文本时增加该指针。在这个阶段,我很高兴只从寄存器中读取偏移量。所以我做了以下更改。

mov edx, 0x1000
mov al, ds:[edx]

并且没有打印任何字符。我试过使用 esi 和 edi 寄存器,结果相同。使用 lea edx,byte ptr [0x1000] 产生相同的结果。更糟糕的是,尝试使用 16 位等效项(dx、si、di)会导致程序挂起。我已经尝试查看 .com 文件中的机器代码,但我找不到任何明显的错误。

我正在使用自定义链接器脚本和 objcopy 调用使用 gcc 编译代码以生成 .com 文件。没有链接库,目标架构是 386.

如有任何帮助,我们将不胜感激。

编辑:完整列表。

directio.s

.intel_syntax
.global _printChar

_printChar:
    push ebp;
    mov ebp, esp;

    xor edx, edx;
    xor ebx, ebx;
    xor eax, eax;
    mov ecx, 1;

    mov ah, 10;
    mov edx, 0x1000;
    mov al, ds:[edx];
    int 0x10;

    mov esp, ebp
    pop ebp;
    ret;

dirTest.c

asm
(
    ".code16gcc;\n" \
    "call _dosmain;\n" \
    "mov ah, 0x4C;\n" \
    "int 0x21;\n"
);

#include "directio.h"

int dosmain(void)
{
    printChar("Hello World!");
    return 0;
}

com_mingw.ld

SECTIONS
{
    . = 0x0100;
    .text :
    {
        *(.text);
    }
    .data :
    {
        *(.data);
        *(.bss);
        *(.rodata);
    }
    _heap = ALIGN(4);
}

所有这些都使用以下命令行编译。

gcc -std=gnu99 -Os -nostdlib -m32 -masm=intel -march=i386 -ffreestanding -o dirTest.com -Wl,--nmagic,--script=com_mingw.ld dirTest.c directio.s

其次是

objcopy dirTest.com -O binary

_printChar 应该是 16 位函数,所以不要 assemble 它是 32 位函数。在.s文件的顶部添加一个.code16gcc并将32位寄存器更改为16位:

.code16gcc
.intel_syntax
.global _printChar

_printChar:
    push bp;
    mov bp, sp

    xor dx, dx
    xor bx, bx
    xor ax, ax
    mov cx, 1

    mov ah, 10
    mov dx, 0x1000
    mov al, ds:[dx]     ; ERROR! See comments.
    int 0x10

    mov sp, bp
    pop bp
    ret

现在,它应该(希望)起作用。

自从我上次以 .com 图像格式编写任何实模式代码以来,肯定是大约三十年前,但这里有一些观察结果:

  • 编写.com格式代码时定义多个部分是不常见的。该格式只允许一个,最大初始图像大小为64Kb;按照惯例,这可能包含 逻辑 部分,通常称为 "CODE" 和 "DATA",或 "TEXT" 和 "DATA",但它们 必须分组为一个物理段;禁止 "STACK" class 的任何显式片段。
  • MS-DOS 是 16 位操作系统,因此您必须 16 位寄存器(或其 8 位子寄存器)编写代码。
  • 16 位处理器不允许您使用除 BX、SI、DI 和 BP 之外的任何寄存器作为间接内存寻址的基址寄存器;您不能为此目的使用 DX、(像您一样)、CX 或 AX; (您可能可以使用 SP,但您通常不理会它,将其保留用作堆栈指针。
  • 当操作系统加载 .com 格式的进程映像时,它首先分配一个环境块,然后是它后面的程序段。然后用管理数据填充程序段的前 256 个字节(创建通常称为程序段前缀或 PSP 的内容),然后立即加载 .com 图像,从地址 0x100 开始,(您提到的 0x1000 只是程序段内的任意偏移量,(大概)在该偏移量处定义了您的数据,但它没有什么神圣不可侵犯的——程序代码可以很容易地出现在那里。
  • 一旦图像被加载,操作系统将所有四个段寄存器设置为程序段的开始,并设置 CS:IP(通过执行 FAR JMP,或可能的 FAR CALL),以在适当的入口地址开始执行您的进程,该地址 always 在偏移量 0x100 处( 以任何方式相关到你的0x1000数据地址)在程序段内。