1

我对实模式寻址一定有一些基本的误解。我正在尝试设置一个功能以在实模式下通过 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。

任何帮助将非常感激。

编辑:完整列表。

方向.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;

目录测试.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
4

2 回答 2

1

_printChar应该是 16 位的函数,所以不要把它组装成 32 位的。在 .s 文件的顶部添加 a.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

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

于 2015-10-05T07:43:46.850 回答
-1

距离我上次以图像格式编写任何实模式代码已经过去了大约 3 年.com,但这里有一些观察结果:

  • 在编写.com格式代码时,定义多个部分是不常见的。该格式只允许一个segment,最大初始图像大小为64Kb;通常,这可能包括逻辑部分,通常称为“CODE”和“DATA”,或“TEXT”和“DATA”,但它们必须组合成单个物理段;禁止任何“STACK”类的显式段。
  • 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)以开始执行您的进程适当的入口地址,它始终位于程序段内的偏移量 0x100(与您的 0x1000 数据地址没有任何关系)。
于 2015-10-05T09:54:17.837 回答