intVal3 TBYTE 1234 -- 无效的 TBYTE 变量声明没有被汇编程序注意到
intVal3 TBYTE 1234 -- invalid TBYTE variable declaration gone unnoticed by assembler
我目前正在按照 Kip Irvine 的 "assembly language x86 programming" 书学习汇编编程。
作者在书中说
MASM uses the TBYTE directive to declare packed BCD variables.
Constant initializers must be in hexadecimal because the assembler
does not automatically translate decimal initializers to BCD. The
following two examples demonstrate both valid and invalid ways of
representing decimal -1234:
intVal TBYTE 80000000000000001234h ; valid
intVal TBYTE -1234 ; invalid
The reason the second example is invalid
is that MASM encodes the constant as a binary integer rather than a
packed BCD integer.
我了解 MASM 汇编程序无法将十进制整数转换为 BCD。但是我想出了以下编译得很好的代码(请注意 intVal3 TBYTE 1234
应该是无效的,但它的编译就像有效代码一样)
.386
.MODEL FLAT, STDCALL
.STACK 4096
ExitProcess PROTO, dwExitCode: DWORD
.DATA
intVal1 TBYTE 800000000000001234h
intVal2 TBYTE -1234h
intVal3 TBYTE -1234 ; compiled despite being invalid
.CODE
main PROC
invoke ExitProcess, 0
main ENDP
END main
为什么汇编器没有注意到无效代码?这是不是汇编程序检测不到的错误,需要程序员提高警惕?
===============编辑 1 =================
我已经按照@PaulH 的建议检查了列表文件,这里是截图
根据列表文件中的结果和@PaulH 所说的判断,我得出以下结论(虽然不确定完全正确):
TBYTE 类型的变量将解释简单地将参数的二进制值(无论是 80000000000000001234h、-1234h 还是 -1234)存储到变量中。因为 TBYTE 类型的变量被假定为 BCD 整数,因此完全由程序员来确保 TBYTE 类型的变量使用正确。
TBYTE
类型的存在理由是具有与 x87 FPU 的内部寄存器宽度相同的东西,这意味着它可以用于 spill 将其中一个寄存器的内容写入内存 而不会丢失任何精度 .
通常,当您在内存中保存浮点值时,您将其表示为单精度(32 位;DWORD
)或双精度(64 位;QWORD
) 值。这很好,只是它失去了精度。如果你想在计算过程中溢出一个临时的中间值,那么你通常不能通过截断该值来失去精度,因为这会影响最终结果。
名称TBYTE
仅表示此类型的值有 10 个字节宽——与 is used internally for floating-point values on the x87 的宽度相同。 (默认情况下,至少假设您没有降低 FPU 的精度。)
因此,TBYTE
与二进制编码的十进制 (BCD) 本质上没有任何关系。我不知道 Kip Irvine 在说什么。您当然可以将 BCD 值存储在 TBYTE
中,但您也可以将较小的 BCD 值存储在 QWORD
或 DWORD
中。顾名思义,BCD 只是一种允许您以二进制形式存储十进制数字的编码。
原因
intVal3 TBYTE -1234
compiles assembles 是因为,对于汇编程序 (MASM),您所做的只是声明一个初始化为 -1234
的 10 字节值。它隐式地扩展了 -1234
以填充 10 个字节,导致值 0xFFFFFFFFFFFFFFFFFB2E
,如您在十六进制转储中所见。 -1234h
的情况相同,只是 h
表示该值被解释为十六进制,而不是十进制。
请注意,这与您这样做时发生的事情基本相同
myValue QWORD -1234
因为汇编程序将 -1234
扩展为 8 个字节长。
正如 Ped7g 在评论中所说,使用汇编语言编程时首先要记住的是:
In the end, it doesn't matter how you specify the memory content in the source, … the code which operates upon that memory defines its "meaning" (type).
汇编程序只存储字节。使用 TBYTE
,它存储其中的 10 个。使用 QWORD
,它存储其中的 8 个。使用 DWORD
,它存储其中的 4 个。你明白了。您的代码如何 解释 这些字节取决于您,因为您必须编写该代码。
Peter Cordes 指出(见评论)x87 FPU 确实有设计用于加载和存储 BCD 值的指令:FBLD
and FBSTP
. These can be used as a slow way to turn a binary integer into decimal digits.
这两条指令都将 m80bcd
值作为它们唯一的操作数,这是一个 80 位 BCD 值,其长度与 TYBTE
相同。因此,可能 Kip Irvine 正在谈论 TBYTE
值的这种用途。
但是,我不认为 MASM 会隐式地将 TBYTE
初始值设定项转换为 BCD 格式,因为当您使用 [=12] 时,这 非常 不方便=] 来存储扩展精度浮点值,如上所述。使用 MASM 或任何其他汇编程序,您仍然需要自己将分配给 TBYTE
的值适当地表示为 BCD 或浮点数,无论您想要哪个。
无论如何,既然您已经听说过 FBLD
和 FBSTP
,您就可以再次忘记它们了。我不认为它们曾经很常用,而且现在肯定没有任何用处。即使在较旧的 CPU 上,如最初的奔腾 (P5) 和奔腾 II (P6),这些指令也需要大约 150 个时钟周期。在较新的 CPU 上,它们变得更慢(Skylake 的吞吐量为每 266 个周期 1 FBSTP
)。因此,即使您确实 想要使用 80 位 BCD 值,您最好还是自己写出必要的指令。 (如果您需要帮助,请提出一个新问题。)
我目前正在按照 Kip Irvine 的 "assembly language x86 programming" 书学习汇编编程。
作者在书中说
MASM uses the TBYTE directive to declare packed BCD variables. Constant initializers must be in hexadecimal because the assembler does not automatically translate decimal initializers to BCD. The following two examples demonstrate both valid and invalid ways of representing decimal -1234:
intVal TBYTE 80000000000000001234h ; valid intVal TBYTE -1234 ; invalid
The reason the second example is invalid is that MASM encodes the constant as a binary integer rather than a packed BCD integer.
我了解 MASM 汇编程序无法将十进制整数转换为 BCD。但是我想出了以下编译得很好的代码(请注意 intVal3 TBYTE 1234
应该是无效的,但它的编译就像有效代码一样)
.386
.MODEL FLAT, STDCALL
.STACK 4096
ExitProcess PROTO, dwExitCode: DWORD
.DATA
intVal1 TBYTE 800000000000001234h
intVal2 TBYTE -1234h
intVal3 TBYTE -1234 ; compiled despite being invalid
.CODE
main PROC
invoke ExitProcess, 0
main ENDP
END main
为什么汇编器没有注意到无效代码?这是不是汇编程序检测不到的错误,需要程序员提高警惕?
===============编辑 1 =================
我已经按照@PaulH 的建议检查了列表文件,这里是截图
根据列表文件中的结果和@PaulH 所说的判断,我得出以下结论(虽然不确定完全正确):
TBYTE 类型的变量将解释简单地将参数的二进制值(无论是 80000000000000001234h、-1234h 还是 -1234)存储到变量中。因为 TBYTE 类型的变量被假定为 BCD 整数,因此完全由程序员来确保 TBYTE 类型的变量使用正确。
TBYTE
类型的存在理由是具有与 x87 FPU 的内部寄存器宽度相同的东西,这意味着它可以用于 spill 将其中一个寄存器的内容写入内存 而不会丢失任何精度 .
通常,当您在内存中保存浮点值时,您将其表示为单精度(32 位;DWORD
)或双精度(64 位;QWORD
) 值。这很好,只是它失去了精度。如果你想在计算过程中溢出一个临时的中间值,那么你通常不能通过截断该值来失去精度,因为这会影响最终结果。
名称TBYTE
仅表示此类型的值有 10 个字节宽——与 is used internally for floating-point values on the x87 的宽度相同。 (默认情况下,至少假设您没有降低 FPU 的精度。)
因此,TBYTE
与二进制编码的十进制 (BCD) 本质上没有任何关系。我不知道 Kip Irvine 在说什么。您当然可以将 BCD 值存储在 TBYTE
中,但您也可以将较小的 BCD 值存储在 QWORD
或 DWORD
中。顾名思义,BCD 只是一种允许您以二进制形式存储十进制数字的编码。
原因
intVal3 TBYTE -1234
compiles assembles 是因为,对于汇编程序 (MASM),您所做的只是声明一个初始化为 -1234
的 10 字节值。它隐式地扩展了 -1234
以填充 10 个字节,导致值 0xFFFFFFFFFFFFFFFFFB2E
,如您在十六进制转储中所见。 -1234h
的情况相同,只是 h
表示该值被解释为十六进制,而不是十进制。
请注意,这与您这样做时发生的事情基本相同
myValue QWORD -1234
因为汇编程序将 -1234
扩展为 8 个字节长。
正如 Ped7g 在评论中所说,使用汇编语言编程时首先要记住的是:
In the end, it doesn't matter how you specify the memory content in the source, … the code which operates upon that memory defines its "meaning" (type).
汇编程序只存储字节。使用 TBYTE
,它存储其中的 10 个。使用 QWORD
,它存储其中的 8 个。使用 DWORD
,它存储其中的 4 个。你明白了。您的代码如何 解释 这些字节取决于您,因为您必须编写该代码。
Peter Cordes 指出(见评论)x87 FPU 确实有设计用于加载和存储 BCD 值的指令:FBLD
and FBSTP
. These can be used as a slow way to turn a binary integer into decimal digits.
这两条指令都将 m80bcd
值作为它们唯一的操作数,这是一个 80 位 BCD 值,其长度与 TYBTE
相同。因此,可能 Kip Irvine 正在谈论 TBYTE
值的这种用途。
但是,我不认为 MASM 会隐式地将 TBYTE
初始值设定项转换为 BCD 格式,因为当您使用 [=12] 时,这 非常 不方便=] 来存储扩展精度浮点值,如上所述。使用 MASM 或任何其他汇编程序,您仍然需要自己将分配给 TBYTE
的值适当地表示为 BCD 或浮点数,无论您想要哪个。
无论如何,既然您已经听说过 FBLD
和 FBSTP
,您就可以再次忘记它们了。我不认为它们曾经很常用,而且现在肯定没有任何用处。即使在较旧的 CPU 上,如最初的奔腾 (P5) 和奔腾 II (P6),这些指令也需要大约 150 个时钟周期。在较新的 CPU 上,它们变得更慢(Skylake 的吞吐量为每 266 个周期 1 FBSTP
)。因此,即使您确实 想要使用 80 位 BCD 值,您最好还是自己写出必要的指令。 (如果您需要帮助,请提出一个新问题。)