7

背景:我的任务是为Unitech HT630编写一个数据收集程序,它运行一个专有的 DOS 操作系统,可以运行为 16 位 MS DOS 编译的可执行文件,尽管有一些限制。我正在使用 Digital Mars C/C++ 编译器,它似乎工作得很好。

对于某些事情,我可以使用标准 C 库,但在单元屏幕上绘图等其他事情需要汇编代码。设备文档中给出的汇编示例与我被教导在 C/C++ 中使用内联汇编代码的方式不同。作为参考,BYTE在下面的示例中是类型unsigned char.

我得到的示例代码示例:

#include <dos.h>

/* Set the state of a pixel */
void LCD_setpixel(BYTE x, BYTE y, BYTE status) {
  if(status > 1 || x > 63 || y > 127) {
    /* out of range, return */
    return;
  }
  /* good data, set the pixel */
  union REGS regs;
  regs.h.ah = 0x41;
  regs.h.al = status;
  regs.h.dh = x;
  regs.h.dl = y;
  int86(0x10, &regs, &regs);
}

我总是被教导如何使用内联汇编:

/* Set the state of a pixel */
void LCD_setpixel(BYTE x, BYTE y, BYTE status) {
  if(status > 1 || x > 63 || y > 127) {
    /* out of range, return */
    return;
  }
  /* good data, set the pixel */
  asm {
    mov AH, 41H
    mov AL, status
    mov DH, x
    mov DL, y
    int 10H
  }
}

两种形式似乎都有效,到目前为止我还没有遇到任何一种方法的问题。对于 DOS 编程,一种形式是否比另一种形式更好?在第二个示例中,该函数是否int86为我处理了一些我没有在自己的汇编代码中处理的东西?

预先感谢您的任何帮助。

4

6 回答 6

9

当您使用int86函数调用时,这是一个 C 运行时库调用,它设置寄存器并发出 DOSint中断函数。这两种方法实际上是相同的,但有一个例外,当您使用内联汇编程序时,代码实际上是在编译和链接时嵌入到目标代码中的。

内联汇编会被认为更快,因为您没有调用 C 运行时库为您调用 DOS 中断所涉及的开销。int86您有责任确保在使用内联汇编时有足够的堆栈空间,而 C 运行时库在调用函数之前设置寄存器时负责分配堆栈空间。

int86是一种更容易调用 DOS 中断的方法。这在旧的 Borland Turbo C 编译器套件和 Microsoft 中非常流行,我说的是 Win 3.1 出现之前的旧编译器。

说到负责视频输出的中断 0x10,如果我没记错的话,当时,一些 BIOS 破坏了bp寄存器,解决方法是这样做:

__asm{
   push bp;
}
/* set up the registers */
int86(0x10, &regs, &regs);
__asm{
   pop bp;
}

您可以在 Ralph Brown 的中断列表中找到广泛的BIOS 功能。HelpPC v2.1 也可能有所帮助,在此处找到。

于 2010-01-18T16:03:06.043 回答
1

第一种形式更具可读性,这也很重要;-)

如果您想知道 int86 是否在背后做某事,只需编译您的程序并检查生成的汇编代码

于 2010-01-18T15:30:55.347 回答
1

通过调用 int86,您的代码将保留在 C 中。无论哪种方式,它都是通过执行系统中断来写入像素。

如果您有很多像素要写入,并且您开始严重遇到速度问题,那么可能有一种更直接(并且不太安全但可能值得)的方式直接写入像素内存。

于 2010-01-18T15:43:12.560 回答
1

两个代码片段都完成了同样的事情。第一个的最大优势是,当您切换编译器时,您仍有可能使用它。并且您不要踩在“C”编译器的代码生成器用于其他目的的寄存器上。你肯定会忘记在你的 asm 代码段中处理的事情。

于 2010-01-18T16:42:59.520 回答
1

您应该查看编译器手册以找出谁负责在内联汇编部分之后恢复寄存器值。由于您的变量已分配给寄存器,因此值的意外更改可能会导致难以发现的错误。int86(0x10, ®s, ®s); 保存寄存器并在执行软件中断后恢复它们。

一些编译器接受指令来定义一个clobber列表(应该保存和恢复的寄存器)。通常,汇编程序部分应该保存将通过 push 更改的寄存器和标志,并由编译器或您自己使用 pop 恢复它们。因此,应该首选第一个示例。

于 2010-01-18T17:00:47.563 回答
0

那不是内联汇编,而是C。非常低级的C,使用函数引起中断,但仍然是C。

此页面有一些文档(对于 DJGPP 编译器,您的可能会有所不同),包括用于表示寄存器的结构。它还指出:

请注意,与 __dpmi_int 函数不同,通过 int86 和类似函数的请求经过特殊处理,以使其适合从保护模式程序调用实模式中断。例如,如果一个特定的例程在 BX 中使用一个指针,int86 期望您在 EBX 中放置一个(保护模式)指针。因此,int86 应该对您以这种方式调用的每个中断和函数都有特定的支持。目前,它仅支持所有可用中断和功能的一个子集 [...]

于 2010-01-18T15:29:04.200 回答