C(DOS)中的嵌入式汇编 - 非法指令

时间:2011-12-03 10:31:39

标签: c assembly dos inline-assembly

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

#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寄存器的内容。我不能继续这个计划。有什么建议吗?

2 个答案:

答案 0 :(得分:8)

由于多种原因,您正在做的事情不正确:

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

答案 1 :(得分:1)

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}
}

此函数设置了一个未正确销毁的堆栈帧。