Quick! Can you see the bug in this code?
getTsc PROC NEAR USES EAX EDX
DB 0Fh, 31h
mov WORD PTR es:[ebx][ecx], ax
add ecx, 2
ret
getTsc ENDP
Ok, here's a hint. The following is the actual code generated:
0000006E getTsc PROC NEAR USES EAX EDX
0000006E 0F 31 DB 0Fh, 31h
00000070 50 * push eax
00000071 52 * push edx
00000072 66| 26: 89 04 19 mov WORD PTR es:[ebx][ecx], ax
00000077 83 C1 02 add ecx, 2
ret
0000007A 5A * pop edx
0000007B 58 * pop eax
0000007C C3 * ret 00000h
0000007D getTsc ENDP
If you look closely, you'll see an extra push eax/push edx and an extra pop edx/pop eax inserted by the assembler. Why? Because of the USES EAX EDX on the PROC line. This tells the assembler that EAX and EDX should be preserved, so the assembler adds the PUSH instructions before the first instruction and the POPs before each RET in the function.
So what happened? Well, it turns out that MASM doesn't consider the DB 00Fh/DB 031h as "real" instructions and thus doesn't trigger the generation of the PUSHes. You can validate this by changing the code to:
getTsc PROC NEAR USES EAX EDX
rdtsc
mov WORD PTR es:[ebx][ecx], ax
add ecx, 2
ret
getTsc ENDP
This will get you the following code:
0000006E getTsc PROC NEAR USES EAX EDX
0000006E 50 * push eax
0000006F 52 * push edx
00000070 0F 31 rdtsc
00000072 66| 26: 89 04 19 mov WORD PTR es:[ebx][ecx], ax
00000077 83 C1 02 add ecx, 2
ret
0000007A 5A * pop edx
0000007B 58 * pop eax
0000007C C3 * ret 00000h
0000007D getTsc ENDP
Now it generates correctly. Why? Because RDTSC is an opcode. This kicks MASM's prologue generation code into gear. The worst problem I've seen with this forced me to add a NOP and ORG $-1 in the code, but this was with tool-generated code. Haven't found any Microsoft reports on this behavior, but it's pretty consistent.
This is the first in a series of MASM quirks and bugs. Thanks to Savitha for bringing up this topic.
UPDATE #1: Well, this certainly generated a flurry of e-mails inside of Phoenix. Here's an example of the strange behavior when you put DB's inside of MACROS, sent in by Naonobu:
TEST_MACRO MACRO siidSig
DB 68h ;PUSH imm16
DW siidSig
TEST_MACRO ENDM
testProc PROC USES EAX EDX
TEST_MACRO 1234h
TEST EAX, EAX
RET
testProc ENDP
This code fails for the same reason, which makes it difficult to use macros for new CPU opcodes
Meanwhile, Kelly points out that MASM's default behavior makes it difficult to use PUSHF/POPF because it inserts SUB [R|E]SP, xxxx in the prologue code.Tim


