neg
NEG
是 x86 汇编语言中的一个指令,全称是 Negate(取反),作用是对一个操作数进行 二进制求反后加 1 的操作(也就是取补码),等价于 0 - 操作数
,即:
NEG dest ≡ dest = 0 - dest
✅ 功能描述
它把一个数变成它的 负值,无论这个数是正的还是负的。
例如:
mov al, 5 ; AL = 00000101b = 0x05
neg al ; AL = 11111011b = 0xFB = -5 (以补码形式)
rtcAnsiValueBstr
将某个变量转换为 BSTR(Unicode 字符串)格式的 ANSI 编码形式字符串
如果传入的是单个字符,rtcAnsiValueBstr
不是返回它的 ASCII 值,而是返回一个只包含这个字符的字符串,这个字符串内部的字节当然就是这个字符的 ASCII 编码。
🔍 名称拆解理解
rtc
:runtime call(VB/VBA 的运行时支持函数)Ansi
:表示 ANSI 字符编码格式(而非 Unicode)Value
:表示是将值(变量)转换Bstr
:VB 的字符串类型,全称是 Basic String(BSTR),是一种以双字节 Unicode 编码为基础的 COM 字符串类型
__vbaR8Str
__vbaR8Str
是 VB6(Visual Basic 6.0)运行时库中的内部函数
✅ 函数功能
将 BSTR 字符串转换为 Double(R8)类型的浮点数。
🔍 分解命名含义
R8
→ 表示 IEEE-754 的 8 字节浮点数(double
)Str
→ 表示字符串(BSTR)- 所以
__vbaR8Str
就是:
BSTR → double(字符串转浮点数)
🔄 其他相关函数对照
函数名 |
功能 |
|
|
|
|
|
|
|
|
fld 指令
fld ds:flt_401008
是一条 x86 浮点指令,意思是:
从内存地址 ds:flt_401008
加载一个浮点数(通常是单精度或双精度)到 x87 浮点栈的顶部(即 st(0)
寄存器中)。
🔍 逐项解释:
组件 |
含义 |
|
浮点加载指令(Floating-point LoaD),将内存中的浮点数压入 x87 浮点栈(最多8个元素) |
|
数据段寄存器,一般是默认段前缀,可省略 |
|
是一个标签,通常代表某个内存地址处的浮点常量(例如 |
🧠 举个具体例子:
.data
flt_401008 dq 10.0
那么:
fld ds:flt_401008
就相当于把这个 10.0
的 double
常量加载进 x87 浮点栈顶部。
🧪 加载后效果(浮点栈):
st(0) = 10.0
🧩 附加说明:
fld
可以加载不同精度的浮点数:
-
fld dword ptr [...]
→ 加载float
(4字节)fld qword ptr [...]
→ 加载double
(8字节)fld tbyte ptr [...]
→ 加载long double
(10字节)
adj_fdiv_m32(x)
adj_fdiv_m32(0x40A00000);
是汇编里的宏或伪代码,表示调用 浮点除法辅助函数,用于支持一些老的、精度或兼容性特殊的浮点除法操作。
🧠 0x40A00000
是什么?
这个是一个 32 位浮点数的原始内存表示(IEEE-754格式),我们来看它是什么实际数值:
0x40A00000 → float → 5.0
你可以在 C 中验证一下:
float* f = (float*)0x40A00000;
// f 的值是 5.0
🔍 代码解释
__asm { fld ds:flt_401008 } // 将 10.0 压入浮点栈 st(0)
adj_fdiv_m32(0x40A00000); // 让 st(0) = st(0) / 5.0 → 即 10.0 / 5.0 = 2.0
也就是
fld 10.0 ; st(0) = 10.0
fdiv 5.0 ; st(0) = 10.0 / 5.0 = 2.0
faddp st(1), st
把浮点栈 st(0)
和 st(1)
相加,结果存到 st(1)
中,然后弹出st(0)
faddp
是 fadd
+ pop
的组合指令,常用于边加边清栈的场景
📌 举个例子
假设
fld 2.0 ; st(0) = 2.0
fld 3.0 ; st(0) = 3.0, st(1) = 2.0
然后执行:
faddp st(1), st
那么执行过程为:
st(1) = 2.0 + 3.0 = 5.0
- 弹出
st(0)
(原来的 3.0)
最后栈变为:
st(0) = 5.0
fadd
和 faddp
是经典的 FPU 指令对,非常容易混淆。我们来一张对比表+实例彻底说明清楚。
✅ fadd 和 faddp 核心区别一览表:
指令 |
含义 |
是否弹栈? |
结果保存位置 |
|
浮点相加 |
❌ 不弹栈 |
默认在 |
|
浮点相加并弹出(p=pop) |
✅ 弹出 |
保存到指定的 |
✅ 用法对比(默认参数)
; === fadd 示例 ===
fld 2.0 ; st(0) = 2.0
fld 3.0 ; st(0) = 3.0, st(1) = 2.0
fadd st(1), st ; st(1) = 2.0 + 3.0 = 5.0,st(0) = 3.0(不变)
结果:
st(0) = 3.0
st(1) = 5.0
; === faddp 示例 ===
fld 2.0 ; st(0) = 2.0
fld 3.0 ; st(0) = 3.0, st(1) = 2.0
faddp st(1), st ; st(1) = 2.0 + 3.0 = 5.0,弹出 st(0)
结果:
st(0) = 5.0 ← 原来的 st(1)
✅ 类比帮助记忆
fadd
是加法,不清栈,适合临时计算faddp
是加法 + 清栈,适合循环或连续处理(类似累加器)
🧠 补充:常见等价形式
faddp st(1), st
; 等价于:
fadd st(1), st(0)
fstp st(0)
✅ 总结一张图
操作 |
栈前状态 |
操作 |
栈后状态 |
|
st(0)=A, st(1)=B |
B = B + A |
st(0)=A, st(1)=B+A |
|
st(0)=A, st(1)=B |
B = B + A |
st(0)=B+A |
fdiv
vs fdivr
指令 |
表达式 |
意义 |
|
|
正常除法 |
|
|
逆序除法(r = reverse) |
fdivr st(0), qword ptr ss:[ebp-0E4]
是:
st(0) = [ebp-0E4] / st(0)
即:
st(0) = 123456.0 / 1600363.0
🧮 实际数值计算:
123456 ÷ 1600363 ≈ 0.0771...
所以:
st(0) ≈ 0.0771
✅ 总结解释:
组成 |
含义 |
|
逆序浮点除法(memory ÷ register) |
|
是目标寄存器,保存最终结果 |
|
是内存地址中的 double 值(123456) |
原始值 |
|
执行结果 |
|
改成 fdiv
,就是反过来:
fdiv st(0), qword ptr [ebp-0E4] ; st(0) = st(0) / [mem]
→ 1600363.0 ÷ 123456.0 ≈ 12.97
你给的这条汇编指令:
fcomp ds:dbl_401028
其中 dbl_401028
是一个内存中的 double
类型值(8 字节浮点数),其内容是 1.0。
fcomp
fcomp
是浮点比较指令,含义是:
将浮点栈顶 st(0)
与指定数值(这里是内存中的 1.0
)进行比较,并弹出 st(0)
。
这一句相当于:
compare st(0), 1.0
pop st(0)
✅ FPU 比较行为
FPU 比较不是通过设置 EFLAGS 来判断,而是设置 FPU 状态字(SW) 的标志位。
常用组合是:
fcomp ds:dbl_401028 ; 比较 st(0) 与 1.0,并弹出 st(0)
fnstsw ax ; 把 FPU 状态字存入 AX
sahf ; 把 AH 的标志位放入 EFLAGS
这样就可以通过 jz
, jb
, ja
等指令判断结果。
🔍 比较结果会设置哪些位?
C0
,C2
,C3
是 FPU 状态字中的关键位,用来反映比较结果:
比较结果 |
C3 |
C2 |
C0 |
|
0 |
0 |
0 |
|
0 |
0 |
1 |
|
1 |
0 |
0 |
|
1 |
1 |
1 |
📌 举个例子
fld qword ptr [ebp-10] ; 假设 = 1.5 → st(0) = 1.5
fcomp ds:dbl_401028 ; 与 1.0 比较,弹出 st(0)
fnstsw ax
sahf
ja isGreater ; 如果 > 1.0,跳转
总结
fcomp ds:dbl_401028
的含义是:
将 FPU 栈顶的值 st(0)
与内存地址 dbl_401028
中的值(即 1.0
)进行浮点比较,比较后弹出 st(0)
,并设置 FPU 状态字中的标志位(C0/C2/C3)用于判断大小关系。
如后续有看到 fnstsw ax
、sahf
、jp
、ja
等,基本就是在用这个比较结果做分支判断。
没有回复内容