这两种形式的内联汇编程序在C中有什么区别?

时间:2010-01-18 15:22:53

标签: c assembly dos inline-assembly

背景:我的任务是编写一个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函数是否为我处理了一些事情,我在第二个示例中没有使用自己的汇编代码处理自己?

提前感谢您的帮助。

6 个答案:

答案 0 :(得分:9)

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

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

int86是一种让调用DOS中断更容易的方法。这在旧的Borland Turbo C编译器套件和微软中非常受欢迎,我在谈论Win 3.1出现之前的旧编译器。

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

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

您可以在Ralph Brown的中断列表here上找到广泛的BIOS功能。此外,HelpPC v2.1也可以提供帮助,找到here

答案 1 :(得分:1)

第一种形式更具可读性,也适用于某些事物; - )

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

答案 2 :(得分:1)

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

如果您要编写 lot 像素,并且开始严重打击速度问题,可能会有一种更直接(并且更不安全但可能有价值)的方式直接写入像素存储器中。

答案 3 :(得分:1)

两个代码段都完成了同样的事情。第一个的一大优点是,当您切换编译器时,您仍有可能仍然可以使用它。而且你没有踩到'C'编译器的代码生成器用于其他目的的寄存器。你绝对忘记在你的asm片段中处理的事情。

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

那不是内联汇编,它是C.非常低级别的C,使用一个函数来引起中断,但仍然是C。

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

  

请注意,与__dpmi_int不同   功能,请求通过   int86和类似的函数是   经过特殊加工制作而成   适合调用实模式   来自保护模式的中断   程式。例如,如果特定的话   例程在BX,int86中获取指针   期望你放一个(保护模式)   EBX中的指针。因此,int86   应该对每个人都有具体的支持   中断和函数你调用它   办法。目前,它只支持一个   所有可用中断的子集和   功能[...]