首页 > 解决方案 > 在 Turbo C++ 内联汇编中使用 32 位寄存器,如 EAX

问题描述

我用 Turbo C++ 写了一个宏

部分 C++ 程序(仅限宏)

/* @intvar address=0x8f79fff4 */

{

   -asm mov cx,08f7h
   _asm mov es,cx

   -asm mov ax,9000h
   -asm mov ds,ax

   -asm mov ax,0fff4h
   -asm mov si,ax

/* -asm mov cl,0fh */  

   -asm mov eax,es
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1
   -asm shl,eax,1

   -asm add eax,ds[si]

   -asm mov ah,9h
   -asm mov al,[eax]

   -asm mov bh,0h
   -asm mov bl,0h
   -asm mov cx,0h

   -asm int 10h
}

为什么我不能写0fh注册cl

标签: c++x86-16inline-assemblyturbo-c++

解决方案


我从来没有使用过_asmnor -asm(我认为-asm无效),而是使用这个:

asm{  

   // multi line 
   // assembly code
   // in here 
   };

这在旧的 turbo C++ 和 Pascal 中也可以正常工作......所以只需摆脱所有的-asmand ,只需在 asm 代码的开头和结尾_asm放置 single 即可。asm{}

但是你的代码有很多语法错误!!!

例如到底是什么:

shl,eax,1

它应该是:

shl eax,1

或者更好的是,您可以使用shl eax,16指令。立即移位是 186 中的新功能,使用 32 位寄存器的代码只能在 386 或更高版本上运行。或者使用 2x 16bitpush和 1x 32-bitpop移动es到更高的 16 位eax,作为将两个 16 位事物连接到 32 位寄存器中的一种方法。(不过,在现代 CPU 上相当慢。)

另一个问题是 Turbo C++ 编译器无法生成带有 32 位寄存器的代码,即使是在 16 位模式下(至少是我的)。 因此,您确实需要使用不同的编译器才能使所有这些成为可能。

在项目选项中,您可以检查 x386 和 x387 指令集,但这不适用于内联汇编...带有 32 位寄存器的行会抛出错误(未知eax或类似的东西)所以您需要重写代码以使用 16位寄存器。


另一个问题是您使用了错误的十六进制数字格式。

在 C/C++ 中,您需要使用0x0前缀而不是h后缀。所以例如9000h应该是0x09000!!!

另一个可能的问题是解决您使用

add eax,ds[si]

您在此处使用 16 位指针,但稍后使用 32 位[eax]指针。你确定那是你想要的吗?此外,您真的想要进行 32 位加载,而不是仅仅将新的低 16 位合并到 EAX 中mov ax, [si]吗?不同之处在于加法的进位是否传播到高位,或者该内存位置的双字的上半部分是否非零。

此外,DS 是默认段(BP 或 ESP 除外),那么为什么在此处明确指定 DS 而不是在 中[eax]?您无法避免使用 32 位偏移量的 DS 基数。格式也是这样的:

add ax,[ds:si]

或者:

add eax,dword[ds:si]

取决于你想做什么。编译器(TASM,NASM,...)之间的寻址格式不同asm,这很麻烦。

此外,在您尝试将 EAX 作为指针取消引用之前asm mov ah,9,修改 EAX 的第二个字节。如果您在 EAX中构造一个有效的偏移量(即地址的低位部分),那么以其他顺序执行这两条指令可能是有意义的。mov al,[eax]seg:off


最后但并非最不重要的一点是,您不保留注册值,这简直是个坏主意。与 MSVC 不同,在 MSVC 中,编译器会确定您的 asm 使用哪些寄存器并为您保存/恢复它们,push/pop必须在 asm 代码的开始/结束处添加语句,否则您可能会损坏 C/C++ 编译器生成的寄存器值您周围的代码块取决于。

并非所有寄存器都需要保留(除非您编写 ISR 代码),但应恢复段和索引寄存器。IIRC ax 保存 asm 函数的返回值(不确定是否也在 TC 中)。

(在 MSVC 中,您可以在 EAX 中保留一个值,然后让执行从非 void 函数的末尾(没有 a return),并且该函数的返回值是您在 EAX 中留下的值。当内联函数包含一个时,MSVC 实际上支持这一点asm{}块,但如果这在 TC 中有效,则可能只是在 asm 块之后不内联并且不弄乱 AX。)


也看看这个:

在第一个项目符号中,有使用 asm 的 TC++ 示例...

这是我刚刚在我的 TC 档案中找到的另一个示例(使用 x387):

long sin(float a,long b)
    {
    long alfa=long(a*1000);
    long far* ptr=(long far*)0xA000FA00;
    ptr[0]=alfa;
    ptr[1]=1000;
    ptr[2]=b;
    asm {
        fninit
        push    es
        push    di
        mov di,0xA000
        mov es,di
        mov di,0xFA00
        fild    dword[es:di]
        fild    dword[es:di+4]
        fdivp   st(1),st(0)
        db  0xD9,0xFE
        fild    dword[es:di+8]
        fmulp   st(1),st(0);
        fistp   dword[es:di]
        pop di
        pop es
        }
    b=ptr[0];
    return b;
    }

我从中推断出 TC 的寻址语法/格式,因为我根本不记得了。


推荐阅读