【问题标题】:Embedded assembly in C (DOS) - illegal instructionC (DOS) 中的嵌入式汇编 - 非法指令
【发布时间】:2011-12-03 10:31:39
【问题描述】:

我正在尝试重新编程指令向量表。这是我使用的代码:

#include <stdio.h>

int a=1;
void func();

void keyboard()
{
    printf("\n\nkeyboard!!!\n");
    a=0;
    asm{iret}
}

int main ()
{
    printf("starting...");
    func();
    return 0;
}

      int vectorcs = 0;
 int vectorip = 0;

void func()
{

    printf("\n*****\n");
    asm{
        cli
        mov ax,0
        mov es,ax
        mov bx,36
        mov ax,word ptr es:[bx]
        mov vectorip,ax
        push ax
         mov ax,word ptr es:[bx+2]
        mov vectorcs,ax
        push ax
        mov ax,cs
        mov word ptr es:[bx],offset keyboard
        mov es:[bx+2],ax
        sti
    }
    printf("\n%d %d\n",vectorip,vectorcs);

    while (a) {
    }
    asm {
        cli
        mov es,bx
        mov bx,36
        pop ax
        mov word ptr es:[bx+2],ax
    }
    asm{
        pop ax
        mov word ptr es:[bx],ax
        sti
    }
}

我正在使用 Turbo C++ 3.0 当我尝试运行此程序时,“16 位 MS-DOS 子系统:NTVDM CPU 遇到非法指令。”出现。然后显示 CS、OP 和 IP 寄存器的内容。我无法继续该程序。有什么建议吗?

【问题讨论】:

    标签: c assembly dos inline-assembly


    【解决方案1】:

    由于多种原因,您的做法不正确:

    1. 常规 C 函数不能安全地用作中断服务例程,因为它们不能正确保存、加载和恢复 CPU 寄存器。它们必须用interrupt 关键字声明。最后他们会为您提供iret
    2. 可以在程序中与中断例程异步更改的变量必须声明为 volatile,否则您可能会面临编译器错误优化对它们的访问的风险。
    3. 您的内联汇编代码可能损坏了 CPU 寄存器的内容。这段代码的一个问题是您的 asm 块与堆栈指针混淆。第一个块退出,堆栈上有几个额外的单词。这对于编译器来说可能是完全出乎意料的,并且可能会破坏您的程序。可能还有其他问题,但我不会检查编译器文档,哪些寄存器必须由内联汇编块保留。我会完全避免这样做,而是选择setvect() 函数。
    4. 从中断服务例程内部调用大多数标准库函数是自找麻烦,因为这些函数通常不是可重入的/线程安全的。他们可以为程序的其余部分以完全意想不到的方式修改一些全局变量或状态。从中断服务例程(您的 printf() 依赖于,顺便说一句)调用 DOS 服务函数也是如此。只有当 DOS 说没问题时,您才能调用它们。它是通过InDos 标志变量来实现的,但当InDos=0 时,并不是所有的都可以安全调用。

    this question 的回答中了解如何更改中断向量、定义中断服务例程并从中调用 DOS 函数,所有这些都使用 Turbo C。

    您可能还会发现this question 及其答案很有用。

    编辑

    如果没有 dos.h 的内联 asm 功能,您就是这样做的:

    #include <stdio.h>
    
    volatile int a = 1;
    void interrupt (*pOldInt9)(void);
    void func(void);
    
    void interrupt keyboard(void)
    {
        printf("\n\nkeyboard!!!\n");
    
        asm {
            in  al, 0x60
            in  al, 0x61
            mov ah, al
            or  al, 0x80
            out 0x61, al
            mov al, ah
            out 0x61, al
        }
    
        a = 0;
    
        asm {
            mov al, 0x20
            out 0x20, al
        }
    }
    
    int main(void)
    {
        printf("starting...");
        func();
        return 0;
    }
    
    void func(void)
    {
        printf("\n*****\n");
    
        asm {
            push    bx
            push    es
    
            mov     bx, 9 * 4
            mov     ax, 0
            mov     es, ax
    
            cli
    
            mov     ax, es:[bx]
            mov     word ptr pOldInt9, ax
            mov     word ptr es:[bx], offset keyboard
    
            mov     ax, es:[bx + 2]
            mov     word ptr pOldInt9[2], ax
            mov     es:[bx + 2], cs
    
            sti
    
            pop     es
            pop     bx
        }
    
        while (a) {}
    
        asm {
            push    bx
            push    es
    
            mov     bx, 9 * 4
            mov     ax, 0
            mov     es, ax
    
            cli
    
            mov     ax, word ptr pOldInt9
            mov     es:[bx], ax
    
            mov     ax, word ptr pOldInt9[2]
            mov     es:[bx + 2], ax
    
            sti
    
            pop     es
            pop     bx
        }
    }
    

    【讨论】:

    • 不使用dos.h怎么办?通过 getvect 和 setvect 我了解您的代码,您在代码段之后调用原始键盘功能。如果我不能使用 getvect-setvect,你有什么建议吗?
    • 查看我答案中的最后一个链接。有足够的 asm 代码让您开始使用中断处理程序。
    • 我又看到了,但我问的是内联汇编。我无法处理 C 或中断向量表中的函数,两天都出现同样的错误。我对函数使用了中断关键字,将 iret 添加到函数末尾(再次),但仍然是一样的。
    • 我很困惑。你对 dos.h 和非内联汇编有什么看法?有些事情您将无法使用内联汇编 (ISR) 完成。不过,你可以用它做 getvect()/setvect()。您有什么具体要求,还是只是想以某种方式做事?
    • 我有具体要求:) 比如我不能使用dos.h,我必须设置int。手动向量表。
    【解决方案2】:
    asm {
        cli
        mov es,bx
        mov bx,36
        pop ax
        mov word ptr es:[bx+2],ax
    }
    

    bx 在该代码之前包含什么?

    void keyboard()
    {
        printf("\n\nkeyboard!!!\n");
        a=0;
        asm{iret}
    }
    

    这个函数设置了一个你没有正确销毁的栈帧。

    【讨论】:

    • bx 应该是 0。我想使用内联汇编来处理标签,例如(假设有一个名为 LABEL1 的标签) mov eax, offset LABEL1 - 我不能使用它,汇编器将 LABEL1 视为符号并说它未定义。即使我尝试 jmp LABEL1 - 标签未定义。我不得不使用函数,它似乎不起作用......
    猜你喜欢
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    相关资源
    最近更新 更多