NASM - The Netwide Assembler

Related Projects => NASMX => Topic started by: Laocoon on October 18, 2013, 12:08:32 AM

Title: movzx 32 / 64 bit compatibility
Post by: Laocoon on October 18, 2013, 12:08:32 AM
From what I have understood, there is no such thing as "movzx rax, eax"
"and rax, 0FFFFFFFFh" Should work, So should "and eax, 0FFFFFFFFh" (silly as it may be). So when writing code that is 32 or 64 bit compatible, "and __AX, 0FFFFFFFFh"

How about adding a macro to nasmx.inc that will overwrite "and" and check to make sure that if %2 is 0FFFFFFFFh and __BITS__ is set to 32bit nothing is written and the code is optimized. However, if we are in 64bit mode, it will let you "and rax, 0FFFFFFFFh."


My excuse to not write the macro: I've worked with nasm for just the afternoon in between customers at work. Slowly getting some knowledge of the behind the scene's stuff, but some of it (nasmx macro's) is a bit more than I'm interested in learning at the moment. I wanted to switch to win64asm and masm wasn't going to cut it.
Title: Re: movzx 32 / 64 bit compatibility
Post by: Bryant Keller on October 18, 2013, 03:28:33 AM
Such a feature is up to Robb if he wants to implement it, but early on it was decided that overloading opcodes would only cause headaches and confusion for new users. For example; these were some I found useful early on.

Code: [Select]
%ifndef __EXTENDED_OPCODES
%define __EXTENDED_OPCODES

; Undocumented Opcodes
%imacro BHT 0
; Branch Hint: Branch Taken
DB 0x2e
%endmacro

%imacro BHNT 0
; Branch Hint: Branch Not Taken
DB 0x3e
%endmacro


%imacro SALC 0
; Set AL if Carry Flag
DB 0xd6
%endmacro

; Custom Opcodes
%imacro msm 2
push %2
pop %1
%endmacro

%imacro mrm 2-3 eax
mov %3, %2
mov %1, %3
%endmacro

%endif

Now imagine if a user tried to look up documentation on the "MRM" or "MSM" opcodes? What if a new user was reading your code and noticed "movzx rax, eax" and tried to do that without NASMX. Things like this always ended up being better to let the user create.

Note: This is also why NASMX started using INVOKE instead of the early NASM32 convention of overloading "CALL". ;)
Title: Re: movzx 32 / 64 bit compatibility
Post by: Laocoon on October 18, 2013, 04:02:35 PM
I can understand that in an all or nothing situation.

In the overloaded "AND" opcode, it would just not put anything in 32bit mode if all bits are set in the second argument. Someone copying the code would have messy code trying to compile in 32bit, but it would work fine in and out of nasm. Following the rule of just not overloading opcodes is probably better from a maintainers standpoint.

Code: [Select]
%imacro AND 2
    %if %2 == 0xFFFFFFFF
        %ifidn __BITS__,64
            and %1, %2
        %endif
    %else
        and %1, %2
    %endif
%endmacro

That look like the proper way to detect if compiling 64bit (assuming nasmx.inc is included first)?


EDIT:
On second thought, don't add this in to nasmx.inc it may break sources relying on (the un-optimized) "and e*x, 0xFFFFFFFF" to set register flags... I wouldn't use that, but I'm sure someone, somewhere has/will...

EDIT2:
Is there an easy way to test the register size of an argument? I'd like to actually just overload movzx instead of and, but I have to determine if %1 is a 64bit register instead of a legitimate movzx eax, ax or whatever.
Title: Re: movzx 32 / 64 bit compatibility
Post by: Bryant Keller on October 18, 2013, 06:16:29 PM
EDIT2:
Is there an easy way to test the register size of an argument?

Not really. You can do it by individually testing the parameter against every possible register if you want 100% accuracy (that's a bunch of %ifidni) or if you think you can trust the caller to pass an actual register, you might get by with something like this.

Code: (regsize.inc) [Select]
%ifndef __REGISTER_SIZE_MACRO
%define __REGISTER_SIZE_MACRO

%define sizeof_lreg 0 ;; at this point, we haven't ran regsize

;; Note: This macro doesn't do much to validate if you passed a valid
;; register. That will need to be done by the caller.
%imacro regsize 1
%push  _regsize_

;; Okay, we're going to put our return value in here.
%undef sizeof_lreg

;; Change the parameter into a string
%defstr %$regName %1

;; Get the parameter's length
%strlen %$regLength %$regName

%if %$regLength > 3
;; Not a Register
%define sizeof_lreg 0
%elif %$regLength < 2
;; Not a Register
%define sizeof_lreg 0
%elif %$regLength = 3
;; Big Register

%substr %$prefix %$regName 1

%ifidni %$prefix,'r'
;; Register is 64-bits
%define sizeof_lreg 8
%else
;; Register is 32-bits
%define sizeof_lreg 4
%endif
%elif %$regLength = 2
;; Small Register

%substr %$suffix %$regName 2

%ifidni %$suffix,'h'
;; Register is 8-bits
%define sizeof_lreg 1
%elifidni %$suffix,'l'
;; Register is 8-bits
%define sizeof_lreg 1
%else
;; Register is 16-bits
%define sizeof_lreg 2
%endif
%endif

%pop
%endmacro

%endif

Which would be used like this:

Code: (main.asm) [Select]
BITS 32

%include "regsize.inc"

SECTION .text

EXTERN printf
GLOBAL main

main: enter 0, 0

regsize eax
push dword sizeof_lreg
push szFormat
call printf
add esp, 8

regsize rax
push dword sizeof_lreg
push szFormat
call printf
add esp, 8

;; Note that although this correctly returns 0 when the non-register is passed, if
;; we had passed something like `PL' or `REM' the primitive parser would treat them
;; as 1 byte and 8 byte registers.
regsize main
push dword sizeof_lreg
push szFormat
call printf
add esp, 8

xor eax, eax
leave
ret

SECTION .rodata

szFormat: db "%d", 13, 10, 0


Now, if you're using NASMX, there is also a 3rd option. Use the sizeof macro-directive.

Code: [Select]
push dword sizeof(rax)
push szFormat
call printf
add esp, 8

This would work because NASMX creates a series of {REG}_size definitions which are used as part of the advanced type system in NASMX.INC. Just to note, SIZEOF doesn't work outside of NASMX, so be careful of that. ;)
Title: Re: movzx 32 / 64 bit compatibility
Post by: Laocoon on October 18, 2013, 06:49:02 PM
I guess I should have been more specific:

In writing the macro to overload movzx to allow anything from movzx ax,al to movzx rax,al or movzx rax,eax ect for all the registers. I'm guessing you have to test for every register. and any 64bit registers, mov the smaller amount into a size matched register of %1 then and %1 with 0xFF size of register %2. Probably going to turn into a much larger macro than I intended..
Title: Re: movzx 32 / 64 bit compatibility
Post by: Bryant Keller on October 19, 2013, 12:39:40 AM
I'm not sure what part of my post is tripping you up. Maybe this will help you get started:

Code: [Select]
%include "nasmx.inc" ; We are using NASMX's type extension.

%imacro _movzx 2
%if %{1}_size = 8
%if %{2}_size = 4
; MOVZX r64, r32
%elif %{2}_size = 2
; MOVZX r64, r16
%elif %{2}_size = 1
; MOVZX r64, r8
%else
; ???
%endif
%elif %{1}_size = 4
%if %{2}_size = 2
; MOVZX r32, r16
%elif %{2}_size = 1
; MOVZX r32, r8
%else
; ???
%endif
%elif %{1}_size = 2
%if %{2}_size = 1
; MOVZX r16, r8
%else
; ???
%endif
%else
; ???
%endif
%endmacro
Title: Re: movzx 32 / 64 bit compatibility
Post by: Laocoon on October 20, 2013, 10:23:41 PM
PERFECT! That's exactly what I was looking for! :D