Search code examples
windowsassemblyx86-64masmmsr

Converting rdpmc_reference_cycles to 64-bit in MASM


I've been using the following code in assembly language to read performance-monitoring counters using the RDPMC instruction:

rdpmc_reference_cycles proc
    mov ecx, 1h
    shl ecx, 30
    add ecx, 2
    xor eax, eax
    xor edx, edx
    rdpmc
    ret
rdpmc_reference_cycles endp

This works fine for a 32-bit environment, but now I'm transitioning to a 64-bit system and I'm having trouble adapting the code. After the rdpmc instruction, the 32-bit value in eax contains the lower half of the result, and the 32-bit value in edx contains the upper half.

I need to combine these two 32-bit values to produce a 64-bit result. However, I'm not sure how to achieve this in MASM. I've tried a few approaches, but they didn't work as expected. Could anyone guide me on how to modify this code so that it produces a 64-bit result?


Solution

  • The RDPMC instruction only depends on the ECX register for an input (in the 64-bit environment the high dword of RCX is ignored). And the result is returned in EDX:EAX with the high dwords of RAX and RDX reset to zero.

    Your code

    mov ecx, 1h
    shl ecx, 30
    add ecx, 2
    xor eax, eax
    xor edx, edx
    rdpmc
    

    is convoluted because

    • it uses 3 instructions to load ECX instead of the one mov ecx, 40000002h (equivalent to mov ecx, (1<<30) + 2).
    • it clears both RAX and RDX for no good reason

    So to read the counter and deliver the 64-bit result in the single RAX register, you can do:

    mov   ecx, (1<<30) + 2
    rdpmc                   ; -> EDX:EAX
    shl   rdx, 32
    or    rax, rdx
    

    The shl rdx, 32 shifts the contents of EDX into the high dword of RDX.
    The or rax, rdx combines both dwords in RAX.
    It is important to note that this or could only work well because the shl made the low dword of RDX zero, and because the rdpmc made the high dword of RAX zero.