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 等,基本就是在用这个比较结果做分支判断。



没有回复内容