Search code examples
assemblyx86-64nasmcpu-registers

How to specify the register size (b/w/d) in a macro


I would like to learn a way of specifying the size of a general-register argument.

for instance:

%macro lalala 1
  mov word [buff], %1w
%endmacro

it works only for r8-r15, but I would like to use this macro for them all rax-rdx,rdi,rsi,r8-r15.

How should I go about it? Is there a built-in mapping?


Solution

  • I put together two NASM preprocessor macros which combined allow you to take any register name (of any size) as input, along with a size, and convert that into the appropriate register name of the given size. The altreg register names that I mentioned (documented in the NASM manual here) are not used by these macros at all. Compatibility for inputting r0 to r7 could be added without much effort though. Here are the full macros:

        %imacro regindexdef 2.nolist
    %push
     %define _%1 256
     %assign %$size 0
     %assign %$exit 0
     %rep 5
      %ifn %$exit
       %if %$size == 0
        %define %$regnames "ah  ch  dh  bh  "
       %elif %$size == 1
        %define %$regnames "al  cl  dl  bl  spl bpl sil dil r8b r9b r10br11br12br13br14br15b"
       %elif %$size == 2
        %define %$regnames "ax  cx  dx  bx  sp  bp  si  di  r8w r9w r10wr11wr12wr13wr14wr15w"
       %elif %$size == 4
        %define %$regnames "eax ecx edx ebx esp ebp esi edi r8d r9d r10dr11dr12dr13dr14dr15d"
       %elif %$size == 8
        %define %$regnames "rax rcx rdx rbx rsp rbp rsi rdi r8  r9  r10 r11 r12 r13 r14 r15 "
       %endif
       %assign %$index 0
       %rep 16
        %ifn %$exit
         %substr %$reg %$regnames %$index * 4 + 1, 4
         %deftok %$reg %$reg
         %ifnempty %$reg
          %ifidni %$reg, %2
           %assign _%1 %$index
           %assign %$exit 1
           %exitrep
          %endif
         %endif
        %endif
        %assign %$index %$index + 1
       %endrep
       %if %$exit
        %exitrep
       %endif
       %assign %$size !%$size + %$size * 2
      %endif
     %endrep
     %ifn %$exit
      %error Invalid register name: %2
     %endif
    %pop
        %endmacro
    
    
        %imacro regsizedef 3.nolist
    %push
     %define _%1 invalidregister
     %assign %$size 0
     %assign %$highbyte 0
     %ifidni %2, byte
      %assign %$size 1
     %elifidni %2, highbyte
      %assign %$size 1
      %assign %$highbyte 1
     %elifidni %2, word
      %assign %$size 2
     %elifidni %2, dword
      %assign %$size 4
     %elifidni %2, qword
      %assign %$size 8
     %else
      %assign %$size %2
     %endif
     %if %$size == 0
      %error Invalid register size: %2
     %else
      %ifnnum %3
       %error Invalid register number: %3
      %elif (%3) >= 16
       %error Invalid register number: %3
      %elif (%3) < 0
       %error Invalid register number: %3
      %else
       %if %$size == 1 && %$highbyte
        %define %$regnames "ah  ch  dh  bh  "
       %elif %$size == 1
        %define %$regnames "al  cl  dl  bl  spl bpl sil dil r8b r9b r10br11br12br13br14br15b"
       %elif %$size == 2
        %define %$regnames "ax  cx  dx  bx  sp  bp  si  di  r8w r9w r10wr11wr12wr13wr14wr15w"
       %elif %$size == 4
        %define %$regnames "eax ecx edx ebx esp ebp esi edi r8d r9d r10dr11dr12dr13dr14dr15d"
       %elif %$size == 8
        %define %$regnames "rax rcx rdx rbx rsp rbp rsi rdi r8  r9  r10 r11 r12 r13 r14 r15 "
       %else
        %define %$regnames ""
       %endif
       %substr %$reg %$regnames (%3) * 4 + 1, 4
       %deftok %$reg %$reg
       %ifempty %$reg
        %error Invalid register type selected
       %else
        %xdefine _%1 %$reg
       %endif
      %endif
     %endif
    %pop
        %endmacro
    

    Here are a few valid examples of using the macros:

        bits 64
    
    regsizedef REG, 2, 0
        mov _REG, 1234h
        ; mov to ax
    regsizedef REG, 4, 0
        mov _REG, 12345678h
        ; mov to eax
    regsizedef REG, qword, 0
    %warning REG=>>_REG<<
    ; rax
    regsizedef REG, highbyte, 2
    %warning REG=>>_REG<<
    ; dh
    
    regindexdef INDEX, rdi
    regsizedef REG, word, _INDEX
        mov _REG, 1234h
        ; mov to di
    
    regindexdef INDEX, cx
    regsizedef REG, qword, _INDEX
    %warning REG=>>_REG<< INDEX=>>_INDEX<<
    ; reg rcx, index 1
    

    I also uploaded a file including the macros, the valid test cases, and a few invalid test cases: https://ulukai.org/ecm/20201207.txt (needs lmacros from https://hg.ulukai.org/ecm/lmacros/)

    Here's how to use the macros with your example:

    %macro lalala 1
    regindexdef INDEX, %1
    regsizedef REG, word, _INDEX
      mov word [buff], _REG
    %endmacro