实模式程序集中指向 .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数据地址)在程序段内。
我对实模式寻址一定有一些基本的误解。我正在尝试设置一个函数以在实模式下通过 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数据地址)在程序段内。