16位的 .com C 程序在实模式操作系统下

4

我一直在开发一个实模式的操作系统,使用汇编语言编写并使用NASM编译成平面的.bin可执行文件。
我希望能够使用C语言编写部分操作系统代码,因此编写了一个实验性质的程序(ctest.c),想要访问一个字符串并打印第一个字符:

void test();

int main() { test(); return 0; }

char msg [] = "Hello World!";

void test() {
    _asm
    {
        mov si, word ptr [msg]
        mov al, [si]
        mov ah, 0eh
        int 10h
    }
    for(;;);
}

我使用 Open Watcom v1.9 编译了这个程序,命令为 wcl ctest.c -lr -l=COM。这将创建一个名为ctest.com的文件。 我使用 NASM 汇编语言编写了内核,并将这个程序加载到0x2010:0x0000,将 DS 和 ES 设置为0x2000:0x0000,然后跳转到0x2010:0x0000。 我以同样的方式调用 NASM 汇编并使用nasm -f bin test.asm -o test.com进行编译。
当我测试操作系统时(使用Bochs),它成功加载了ctest.com,但是打印出了一个与msg[]无关的无意义字符。
有没有人对此有任何建议? 我认为字符串只是在错误的位置初始化。 我想保持这个操作系统为16位。
谢谢!


2
一个 .com 内存映像的前 128 字节包含操作系统数据,类似于 CP/M。DOS 依赖它。接下来的 128 字节包含命令行。执行从 0x100 开始。 - Hans Passant
2个回答

4
您正在使用错误的地址。
您可以在0x2000:0x0100处加载并跳转到0x2000:0x0100(不要忘记在此之前设置DS=ES=SS=0x2000和SP=0),或者您可以在0x2000:0x0000处加载(相当于0x1FF0:0x0100,因为0x2000 * 0x10 + 0x0000 = 0x1FF0 * 0x10 + 0x0100 = 0x20000 =实模式下的物理内存地址),并跳转到0x1FF0:0x0100(不要忘记在此之前设置DS=ES=SS=0x1FF0和SP=0)。
这样做的原因是编译的x86代码通常不具有位置独立性,如果您移动它,必须调整代码内部的一些数据偏移量。显然,您没有进行这些调整。在简单情况下,没有什么需要调整的,因此您可以通过错误的地址得逞。 编辑
实际上,这里还有更多问题:
  1. mov si, word ptr [msg] 必须更改为 lea si, byte ptr [msg],因为您不想将si加载为字符串内部的内容,而是要将其加载为字符串的地址。
  2. 启动代码链接到您的程序中的OW受DOS的依赖,并调用DOS函数,而在启动程序时没有DOS函数。请参见如何解决此问题here

1

在MS-DOS下,COM程序被加载到偏移量0x100。我猜Open Watcom也是基于这个假设。建议将COM程序加载到0x2010:0x0100,看看效果如何。


我刚刚尝试了一下,但是没有成功。顺便说一下,我正在使用int 13h ah 2来从虚拟软盘中读取扇区。 - user1112148

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接