mov ah、word_variable 上的“无效指令操作数”以及在 16 位数字上使用 imul

2024-03-26

这是我想要实现的目标:a_x*b_x + a_y*b_y + a_z*b_z

我正在尝试在汇编中创建一个宏来执行上述计算。

我在用WORDs 代表我所有的号码。这是我的代码:

dotProduct   MACRO  A_X,A_Y,A_Z,B_X,B_Y,B_Z ;a.b (a dot b) = a_x*b_x + a_y*b_y + a_z*b_z
    mov ah, A_X
    mov al, B_X
    imul ax
    mov answer, ax
    mov ah, A_Y
    mov al, B_Y
    imul ax
    add answer, ax
    mov ah, A_Z
    mov al, B_Z
    imul ax
    mov answer, ax

    output answer

ENDM

answer BYTE 40 DUP (0)

但我收到以下错误:

Assembling: plane_line.asm
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(1): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(2): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(4): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(5): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(6): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(8): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(9): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(10): Macro Called From
  plane_line.asm(101): Main Line Code
plane_line.asm(101) : error A2070: invalid instruction operands
 crossProduct(12): Macro Called From
  plane_line.asm(101): Main Line Code

我相信这与我处理寄存器的方式有关。

我应该怎么做呢?


MOV 的两个操作数必须具有相同的大小。 AL 和 AH 是字节寄存器。

MASM 风格的汇编器从内存位置的大小推断DW您在符号名称后使用。这就是为什么它抱怨操作数大小不匹配(带有通用的无用错误消息,该消息也适用于许多其他问题)。

如果您确实想将 A_X 的第一个字节加载到 AL 中,则可以使用覆盖:mov al, BTYE PTR A_X.


但这不是您想要的,因为您实际上想要加载 16 位数字。两个 16 位数字的乘积最多可达 32 位(例如 0xffff^2 为 0xfffe0001)。因此,只进行 32 位数学计算可能是个好主意。

您也在使用imul错误地:imul ax sets DX:AX = AX * AX(在一对寄存器中生成 32 位结果)。要将 AH * AL 相乘并得到 AX 中的结果,您应该使用imul ah。请参阅IMUL 的 insn ref 手动输入 http://www.felixcloutier.com/x86/IMUL.html。另请参阅以下文档和指南的其他链接x86 /questions/tagged/x86标签维基。

IMUL 的双操作数形式更易于使用。它的工作原理与 ADD 完全相同,具有目的地和源,产生一个结果。 (它不会在任何地方存储全乘结果的高半部分,但这对于这个用例来说很好)。

要设置 32 位 IMUL,使用 MOVSX 进行符号扩展 http://www.felixcloutier.com/x86/MOVSX:MOVSXD.html从 DW 16 位内存位置到 32 位寄存器。

无论如何,这就是你应该做的:

movsx   eax, A_X       ; sign-extend A_X into a 32-bit register
movsx   ecx, B_X       ; Use a different register that's 
imul    eax, ecx       ; eax = A_X * B_X  (as a 32-bit signed integer)

movsx   edx, A_Y
movsx   ecx, B_Y
imul    edx, ecx       ; edx = A_Y * B_Y  (signed int)
add     eax, edx       ; add to the previous result in eax.

movsx   edx, A_Z
movsx   ecx, B_Z
imul    edx, ecx       ; edx = A_Z * B_Z  (signed int)
add     eax, edx       ; add to the previous result in eax

我不确定你的“输出”函数/宏应该如何工作,但将整数存储到字节数组中BYTE 40 DUP (0)似乎不太可能。你可以这样做mov dword ptr [answer], eax,但也许你应该output eax。或者如果output answer将 eax 转换为存储在的字符串answer,那么你不需要mov first.

我假设你的号码是signed从 16 位开始。这意味着如果所有输入都是,您的点积可能会溢出INT16_MIN http://en.cppreference.com/w/cpp/types/integer(即-32768 = 0x8000)。 0x8000^2 = 0x40000000,超过 INT32_MAX 的一半。因此 32 位 ADD 不太安全,但我认为您对此表示同意并且不想添加进位。


其他方式:我们可以使用 16 位 IMUL 指令,因此我们可以将其与内存操作数一起使用,而不必单独加载符号扩展。不过,如果您确实想要完整的 32 位结果,那么这会不太方便,因此我将仅使用低半部分进行说明。

mov    ax, A_X
imul   B_X         ; DX:AX  = ax * B_X
mov    cx, ax      ; save the low half of the result somewhere else so we can do another imul B_Y  and  add cx, ax

;or
mov    cx, A_X
imul   cx, B_X     ; result in cx

读到这里就停止了,剩下的内容对初学者来说没有用。

有趣的是:SSE4.1 有一个 SIMD 水平点积指令。

; Assuming A_X, A_Y, and A_Z are stored contiguously, and same for B_XYZ
pmovsxwd   xmm0, qword ptr [A_X]  ; also gets Y and Z, and a high element of garbage
pmovsxwd   xmm1, qword ptr [B_X]  ; sign-extend from 16-bit elements to 32
cvtdq2ps   xmm0, xmm0             ; convert in-place from signed int32 to float
cvtdq2ps   xmm1, xmm1

dpps       xmm0, xmm1,  0b01110001  ; top 4 bits: sum the first 3 elements, ignore the top one.  Low 4 bits: put the result only in the low element

cvtss2si   eax, xmm0              ; convert back to signed 32-bit integer
; eax = dot product = a_x*b_x + a_y*b_y + a_z*b_z.

这实际上可能比标量 imul 代码慢,特别是在每个时钟可以执行两次加载并具有快速整数乘法的 CPU 上(例如 Intel SnB 系列具有imul r32, r323 个周期的延迟,每个周期 1 个吞吐量)。标量版本具有大量指令级并行性:加载和乘法是独立的,只有组合结果的加法相互依赖。

DPPS 很慢(Skylake 上有 4 uops 和 13c 延迟,但每 1.5c 吞吐量仍然有 1 个)。


整数SIMD点积(仅需要SSE2):

;; SSE2
movq       xmm0, qword ptr [A_X]  ; also gets Y and Z, and a high element of garbage
pslldq     xmm0, 2                ; shift the unwanted garbage out into the next element.  [ 0 x y z   garbage 0 0 0 ]
movq       xmm1, qword ptr [B_X]  ; [ x y z garbage  0 0 0 0 ]
pslldq     xmm1, 2
;; The low 64 bits of xmm0 and xmm1 hold the xyz vectors, with a zero element

pmaddwd    xmm0, xmm1               ; vertical 16b*16b => 32b multiply,  and horizontal add of pairs.  [ 0*0+ax*bx   ay*by+az*bz   garbage  garbage ]

pshufd     xmm1, xmm0, 0b00010001   ; swap the low two 32-bit elements, so ay*by+az*bz is at the bottom of xmm1
paddd      xmm0, xmm1

movd       eax, xmm0

如果您可以保证 A_Z 和 B_Z 之后的 2 个字节为零,则可以省略PSLLDQ 字节移位指令 http://www.felixcloutier.com/x86/PSLLDQ.html.

如果您不必将一个垃圾字移出低 64 位,则可以在 MMX 寄存器中有效地执行此操作,而不需要 MOVQ 加载来将 64 位零扩展为 128 位寄存器。然后你可以用内存操作数来PMADDWD。但接下来您需要 EMMS。另外,MMX 已经过时了,并且Skylake 吞吐量较低 http://agner.org/optimize/ for pmaddwd mm, mmpmaddwd xmm,xmm(或 256b ymm)。

这里的所有内容都是最新 Intel 上的一个周期延迟,除了 PMADDWD 的 5 个周期。 (MOVD 是 2 个周期,但您可以直接存储到内存中。负载显然也有延迟,但它们来自固定地址,因此不存在输入依赖性。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mov ah、word_variable 上的“无效指令操作数”以及在 16 位数字上使用 imul 的相关文章

  • CMake 未添加 ASM_MASM 的预处理器定义

    在使用以下 cmake 生成的构建系统 Visual Studio 2017 Generator 中 test asm 源文件不是使用预处理器定义 MY MASM DEFINITION 进行编译的 cmake minimum require
  • 如何可移植地乘以 64 位操作数并获得 128 位结果?

    对于 x64 我可以使用这个 uint64 t hi lo hi lo 64bit x 64bit multiply of c 0 and b 0 asm mulq 3 n t d hi a lo a c 0 rm b 0 cc a 0 h
  • 切换 C++ 函数的调用堆栈

    这是我的上一个问题 https stackoverflow com questions 1371547 explanation of asm code关于切换 C 调用堆栈 但是 C 使用不同的调用约定 thiscall 并且可能需要一些不
  • 处理负整数的汇编 MASM

    我被指示用汇编语言编写一个程序 该程序将执行以下算术 A B C D A E 当没有负值发挥作用时 我成功地做到了这一点 但假设 A 5 B 4 C 3 D 2 和 E 1 这给了我们 5 4 3 2 5 1 或 6 这就是我需要帮助的地方
  • printf 内部如何工作? [复制]

    这个问题在这里已经有答案了 可能的重复 无需汇编的 C C 函数定义 https stackoverflow com questions 2442966 c c function definitions without assembly 你
  • 使用指针作为函数参数时的段前缀

    我有一个汇编 c 问题 我刚刚读到了有关段前缀的内容 例如 ds varX 等 前缀对于逻辑地址的计算很重要 我也读到 默认值是 ds 一旦您使用 ebp 寄存器来计算地址 就会使用 ss 对于代码 cs 是默认值 这一切都是有道理的 现在
  • 如何正确确定Intel处理器的-march和-mtune?

    我目前正在从源代码构建一个对我来说性能至关重要的软件 因此 我想对其进行优化 以便在我的特定 Intel CPU 上运行 构建过程要求我设置 march 和 mtune 标志 如果在我的处理器节点上我使用 gcc march native
  • MASM32 中令人困惑的括号

    我正在尝试掌握 MASM32 但对以下内容感到困惑 我认为括号用于间接 所以如果我有预定义的变量 data item dd 42 then mov ebx item 会将 item 的内容 即数字 42 放入 ebx 中并 mov ebx
  • 我应该使用哪些 gdb 命令来缩小标签“main”中出现分段错误的位置?

    这是我的汇编代码和我的主要子例程 这是我的宏和常量 text fmt string x t t ln x n sfmt string 10lf t 10lf n error string Error filename string inpu
  • 宏的问题

    HI 有人可以帮助我理解为什么 SQUARE x 的值为 49 吗 我正在使用 Visual C 6 0 define SQUARE X X X int main int argc char argv int y 5 printf d n
  • 用于读/写 XMM 和 YMM 寄存器的内联汇编代码?

    我有 2 个变量来模拟 X86 XMM 和 YMM 如下所示 uint64 t xmm value 2 uint64 t ymm value 4 现在我想使用内联汇编来读取和写入 XMM YMM 寄存器 如何编写GCC内联汇编来复制xmm
  • NOP 雪橇如何工作?

    我找不到回答这个问题的好来源 我知道 nop sled 是一种用于规避缓冲区溢出攻击中堆栈随机化的技术 但我无法理解它是如何工作的 有什么简单的例子可以说明这种方法 128 字节 nop sled 等术语是什么意思 有些攻击包括使程序跳转到
  • 这在 imacros javascript 中可能吗?尝试了很多方法都没有成功

    我已经处理了所有事情 但不幸的是它返回了 未定义 的数据 我尝试了许多不同的方法来从该网站获取数据 不成功 我在其他论坛上发布了这个问题 没有人能够解决这个问题来处理完整的数据提取 我需要像这样保存数据 91 228 53 28 8089
  • GCC/CLANG 与 MSVC 的预处理器之间有何差异?

    以下预处理器宏 通常的嫌疑人 测试空参数列表并计算参数数量 在 gcc clang 上运行时没有警告 但在 Microsoft VisualC 上失败 IS EMPTY returns nothing if the parameter li
  • 将预处理器宏添加到 xcode 6 中的目标

    也许这非常简单 但我找不到在 Xcode 6 中为目标定义预处理器宏的方法 我已经做了一个屏幕截图来显示它在 Xcode 中的位置 因为它更容易 选择项目文件 选择您想要的目标 转到构建设置 搜索 预处理器 添加用于调试 发布或两者的预处理
  • 如何禁用浮点单元(FPU)?

    我想在 x86 系统中禁用 FPU MMX SSE 指令 并且我将为设备不可用异常实现一个处理程序 我已经提到过控制寄存器 wiki 页面 http en wikipedia org wiki Control register 看来我必须在
  • arm-thumb指令集的blx指令如何支持4MB范围

    读自https www keil com support man docs armasm armasm dom1361289866046 htm https www keil com support man docs armasm arma
  • 无与伦比的括号宏怪异

    根据 C99 规则预处理以下 3 行的正确输出是什么 define y x x define x a y a x 1 x 2 顺便说一句 linux 下的 cpp 会产生错误消息 但我不明白为什么答案不简单 1 2 假设 cpp 是正确的而
  • gcc 如何知道内联汇编中使用的寄存器大小?

    我有内联汇编代码 define read msr index buf asm volatile rdmsr d buf 1 a buf 0 c index 使用该宏的代码 u32 buf 2 read msr 0x173 buf 我发现反汇
  • C99 中带有 0 个参数的可变参数宏

    我有一些调试代码 如下所示 define STRINGIFY x x define TOSTRING x STRINGIFY x define AT FILE TOSTRING LINE void my error const char l

随机推荐