将多种数据类型的数字输出到 MessageBox 会造成混乱
Outputing numbers in several data types to MessageBox creates a mess
我正在尝试以 MB 逐行输出从 byte
到 real10
的几乎所有数据类型的小数(整数和浮点数),但由于某种原因几乎 none显示正确(下图),但大部分都坏了,有人可以解释为什么会这样吗?我的错误是什么?
.386
.model flat, stdcall
option casemap: none
include \masm32\include\masm32rt.inc
.data
titletext db 'Title',0
frmt db 'A (Byte) = %d',10
db '-A (Byte) = %d',10
db 'A (Word) = %d',10
db 'B (Word) = %d',10
db '-A (Word) = %d',10
db '-B (Word) = %d',10
db 'A (ShortInt) = %d',10
db 'B (ShortInt) = %d',10
db 'C (ShortInt) = %d',10
db '-A (ShortInt) = %d',10
db '-B (ShortInt) = %d',10
db '-C (ShortInt) = %d',10
db 'A (LongInt) = %d',10
db 'B (LongInt) = %d',10
db 'C (LongInt) = %d',10
db '-A (LongInt) = %d',10
db '-B (LongInt) = %d',10
db '-C (LongInt) = %d',10
db 'D (Single) = %g',10
db '-D (Single) = %g',10
db 'E (Double) = %g',10
db '-E (Double) = %g',10
db 'F (Extended) = %g',10
db '-F (Extended) = %g',0
buff db 1024 dup (?)
Abyte db 6
nAbyte db -6
Aword dw 6
Bword dw 603
nAword dw -6
nBword dw -603
Ashort dd 6
Bshort dd 603
Cshort dd 6032000
nAshort dd -6
nBshort dd -603
nCshort dd -6032000
Along dq 6
Blong dq 603
Clong dq 6032000
nAlong dq -6
nBlong dq -603
nClong dq -6032000
Dsingle real4 0.001
nDsingle real4 -0.001
Edouble real8 0.074
nEdouble real8 -0.074
Fextended real10 735.430
nFextended real10 -735.430
.code
start:
invoke crt_sprintf, addr buff, addr frmt,
Abyte, nAbyte,
Aword, Bword, nAword, nBword,
Ashort, Bshort, Cshort, nAshort,nBshort, nCshort,
Along, Blong, Clong, nAlong, nBlong, nClong,
Dsingle, nDsingle,
Edouble, nEdouble,
Fextended, nFextended
invoke MessageBox, 0, addr buff, addr titletext, MB_OK
invoke ExitProcess, 0
end start
当前结果:
您的格式字符串与您要invoke
传递的内容不匹配。
您正在对不升级为 int
的窄参数使用 %d
转换。 %hhd
和 %hd
分别是 int8_t
(带符号的整数)和 int16_t
(short
) 的转换。 ISO C 没有指定 short = int16_t,但 32 位 Windows.
就是这种情况
无论如何,如果 MASM 的 invoke
没有神奇地为您进行符号扩展或零扩展,那么用于传递那些窄整数的那些 4 字节堆栈槽的高字节可能包含大量垃圾。
此外,short
通常是 16 位字节,但您使用的是 dd
。在这种情况下这无关紧要,因为 32 位代码始终至少使用 32 位堆栈槽来传递参数。
要自己进一步调试: 查看调用指令或宏实际上如何汇编为真实的机器指令。即反汇编生成的代码,看看它到底在做什么。 (或者使用带有反汇编视图的调试器来单步执行)。此外,在每个步骤之后 call
之前,使用调试器查看堆栈内存,以查看您的参数是否被正确传递。
如果您不确定您需要的 asm 指令的正确序列是什么,请查看 C 或 C++ 编译器的编译器输出,寻找使用您想要的相同类型的 C 全局变量调用 sprintf 的等效代码。 (例如,在 https://godbolt.org/ 上,它有 32 位 x86 MSVC 可用。它的调用约定和类型宽度应该是 crt_sprintf
所期望的,您正在通过 MASM32 进行链接。)
此外,浮点数都被破坏了,因为您忘记将 C 默认的 float 提升应用到 double,这总是发生在 args 到 a 的 ...
部分可变函数。 所以你的 4 字节浮点数稍后会倾斜所有内容。
同样,您有一些 dq
64 位整数,但您仍然告诉 sprintf 它们是 %d
(int)。所以你实际上得到了 dq
的高半部分和低半部分被两个单独的 %d
转换读取,导致错误的转换读取错误的字节。
I've changed all byte
, word
and dword
to signed types and it worked well, but I am having trouble with qword
and real4
. I changed %d
for %ld
for qword
, but it still puts 0 to the next entry, shifting all numbers after it.
在 32 位 ABI 中,long
仍然是 32 位类型。您需要 long long
和 转换为 %lld
为 dq
。
And about floats, as I understood from the link to the other thread you posted - it is impossible to print out a float, I need to convert it to different type somehow?
没错。您根本不能将 invoke
与 real4
一起用于类似 printf 的函数,这些函数通过 C 可变参数函数的 ...
部分获取其 arg。
查看编译器生成的等效 C 代码。
在调用之前,您必须将 real4
转换为 double
并手动推送它们。 (按从右到左的顺序调用推送,最后一个参数先推送,所以你可以进行这些推送然后使用 invoke
我猜?)
还有 (请参阅该答案的评论),而不是 10 字节 x87 类型。所以可能没有办法让 sprintf 处理一个 tbyte,你也必须将它转换为 double
。或者,除非您找到可以处理它的 C 库,否则不要费心使用 real10
。
(哦,原来是你问的,还是老问题。)
我正在尝试以 MB 逐行输出从 byte
到 real10
的几乎所有数据类型的小数(整数和浮点数),但由于某种原因几乎 none显示正确(下图),但大部分都坏了,有人可以解释为什么会这样吗?我的错误是什么?
.386
.model flat, stdcall
option casemap: none
include \masm32\include\masm32rt.inc
.data
titletext db 'Title',0
frmt db 'A (Byte) = %d',10
db '-A (Byte) = %d',10
db 'A (Word) = %d',10
db 'B (Word) = %d',10
db '-A (Word) = %d',10
db '-B (Word) = %d',10
db 'A (ShortInt) = %d',10
db 'B (ShortInt) = %d',10
db 'C (ShortInt) = %d',10
db '-A (ShortInt) = %d',10
db '-B (ShortInt) = %d',10
db '-C (ShortInt) = %d',10
db 'A (LongInt) = %d',10
db 'B (LongInt) = %d',10
db 'C (LongInt) = %d',10
db '-A (LongInt) = %d',10
db '-B (LongInt) = %d',10
db '-C (LongInt) = %d',10
db 'D (Single) = %g',10
db '-D (Single) = %g',10
db 'E (Double) = %g',10
db '-E (Double) = %g',10
db 'F (Extended) = %g',10
db '-F (Extended) = %g',0
buff db 1024 dup (?)
Abyte db 6
nAbyte db -6
Aword dw 6
Bword dw 603
nAword dw -6
nBword dw -603
Ashort dd 6
Bshort dd 603
Cshort dd 6032000
nAshort dd -6
nBshort dd -603
nCshort dd -6032000
Along dq 6
Blong dq 603
Clong dq 6032000
nAlong dq -6
nBlong dq -603
nClong dq -6032000
Dsingle real4 0.001
nDsingle real4 -0.001
Edouble real8 0.074
nEdouble real8 -0.074
Fextended real10 735.430
nFextended real10 -735.430
.code
start:
invoke crt_sprintf, addr buff, addr frmt,
Abyte, nAbyte,
Aword, Bword, nAword, nBword,
Ashort, Bshort, Cshort, nAshort,nBshort, nCshort,
Along, Blong, Clong, nAlong, nBlong, nClong,
Dsingle, nDsingle,
Edouble, nEdouble,
Fextended, nFextended
invoke MessageBox, 0, addr buff, addr titletext, MB_OK
invoke ExitProcess, 0
end start
当前结果:
您的格式字符串与您要invoke
传递的内容不匹配。
您正在对不升级为 int
的窄参数使用 %d
转换。 %hhd
和 %hd
分别是 int8_t
(带符号的整数)和 int16_t
(short
) 的转换。 ISO C 没有指定 short = int16_t,但 32 位 Windows.
无论如何,如果 MASM 的 invoke
没有神奇地为您进行符号扩展或零扩展,那么用于传递那些窄整数的那些 4 字节堆栈槽的高字节可能包含大量垃圾。
此外,short
通常是 16 位字节,但您使用的是 dd
。在这种情况下这无关紧要,因为 32 位代码始终至少使用 32 位堆栈槽来传递参数。
要自己进一步调试: 查看调用指令或宏实际上如何汇编为真实的机器指令。即反汇编生成的代码,看看它到底在做什么。 (或者使用带有反汇编视图的调试器来单步执行)。此外,在每个步骤之后 call
之前,使用调试器查看堆栈内存,以查看您的参数是否被正确传递。
如果您不确定您需要的 asm 指令的正确序列是什么,请查看 C 或 C++ 编译器的编译器输出,寻找使用您想要的相同类型的 C 全局变量调用 sprintf 的等效代码。 (例如,在 https://godbolt.org/ 上,它有 32 位 x86 MSVC 可用。它的调用约定和类型宽度应该是 crt_sprintf
所期望的,您正在通过 MASM32 进行链接。)
此外,浮点数都被破坏了,因为您忘记将 C 默认的 float 提升应用到 double,这总是发生在 args 到 a 的 ...
部分可变函数。
同样,您有一些 dq
64 位整数,但您仍然告诉 sprintf 它们是 %d
(int)。所以你实际上得到了 dq
的高半部分和低半部分被两个单独的 %d
转换读取,导致错误的转换读取错误的字节。
I've changed all
byte
,word
anddword
to signed types and it worked well, but I am having trouble withqword
andreal4
. I changed%d
for%ld
forqword
, but it still puts 0 to the next entry, shifting all numbers after it.
在 32 位 ABI 中,long
仍然是 32 位类型。您需要 long long
和 转换为 %lld
为 dq
。
And about floats, as I understood from the link to the other thread you posted - it is impossible to print out a float, I need to convert it to different type somehow?
没错。您根本不能将 invoke
与 real4
一起用于类似 printf 的函数,这些函数通过 C 可变参数函数的 ...
部分获取其 arg。
查看编译器生成的等效 C 代码。
在调用之前,您必须将 real4
转换为 double
并手动推送它们。 (按从右到左的顺序调用推送,最后一个参数先推送,所以你可以进行这些推送然后使用 invoke
我猜?)
还有 double
。或者,除非您找到可以处理它的 C 库,否则不要费心使用 real10
。
(哦,原来是你问的,还是老问题。)