Author Topic: Asmc and Visual Studio  (Read 6142 times)

jj2007

  • Member
  • *****
  • Posts: 11880
  • Assembler is fun ;-)
    • MasmBasic
Re: Asmc and Visual Studio
« Reply #45 on: January 14, 2022, 12:07:14 PM »
SIZE was also a reserved word

Even TYPE is a reserved word in MASM. Now I need a special command line option to assemble sources with AsmC.

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #46 on: January 15, 2022, 01:04:06 AM »
Quote
No change in VS2010 with asmc v2.33.31.
I still get that strange formatted types and no string is displayed in the popup.
So I migrated back to asmc v2.32(.0). Here is all working except constants debug info.

I tested this using VS2015 and it works fine on my end. The object file from both versions would be helpful here.

I added some of the macros to the header files. The min/max macros covers integer and imm values. The assumption is signed compare.
Code: [Select]
ifndef max
define max_imm_dword   <max_sdword_sdword>
define max_imm_sdword  <max_sdword_sdword>
define max_dword_imm   <max_sdword_imm>
define max_dword_dword <max_sdword_sdword>
max_imm_imm macro a,b
if (a) GT (b)
exitm<a>
endif
    exitm<b>
    endm
max_sdword_imm proto watcall a:sdword, b:abs {
    .ifs a < b
mov a,b
    .endif
    }
max_sdword_sdword proto watcall a:sdword, b:abs {
    cmp   a,b
    cmovl a,b
    }
max macro a, b
%   exitm<typeid(max_, a)typeid(_, b)(a, b)>
    endm
endif

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #47 on: January 15, 2022, 01:26:17 AM »
You may add float and double as well.

max_real4_real4 proto fastcall a:real4, b:abs { maxss a,b }
max_real8_real8 proto fastcall a:real8, b:abs { maxsd a,b }
...
  local f1:real4, f2:real4
  local d1:real8, d2:real8

    movss f1,max(f1, f2)
    movsd d1,max(d1, d2)
...
        movd    xmm0, dword ptr [rbp-4H]
        maxss   xmm0, dword ptr [rbp-8H]
        movss   dword ptr [rbp-4H], xmm0
        movq    xmm0, qword ptr [rbp-10H]
        maxsd   xmm0, qword ptr [rbp-18H]
        movsd   qword ptr [rbp-10H], xmm0

Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #48 on: January 15, 2022, 01:30:22 AM »
> So this will be /Zi 64-bit?

Yes, exactly. ;o)

> Okay, so lets fix'm then. They are defined in windef.inc and minwindef.inc

OK then, but I must confess that I'm a bad programmer and with assembly macros it's getting worse. ASM macros are still a book with seven seals to me.

However, I've posted my "attempts" to implement the macros in asm a few pages back in this thread, and if a skilled person could optimize it, that would be great.
I'm pretty sure you can shorten the macros to one line (or less).  :bgrin:

I had a look at your implementation of min/max and it's as elegant as simple. But it always uses signed comparism and from my findings the c compiler differntiates here.

The documentation says:
The max macro compares two values and returns the larger one. The data type can be any numeric data type, signed or unsigned. The data type of the arguments and the return value is the same.

I tried to reflect this in my attempts of implementation.

At first some global helper macros would be useful.

This, for example, is retrieving the accumulateor register depending on the size of it's argument.
(I'm also bad in denominating or naming things, so feel free to give it a matching denomination)

Code: [Select]
;// Helper routine to get the accumulator register matching the size of "a"
get_accumulator macro a:REQ
if     (SIZEOF (TYPEOF (a))) EQ 1
exitm<al>
elseif (SIZEOF (TYPEOF (a))) EQ 2
exitm<ax>
elseif (SIZEOF (TYPEOF (a))) EQ 4
exitm<eax>
elseif (SIZEOF (TYPEOF (a))) EQ 8
exitm<rax>
endif
endm

At next a helper routine to determine if a macro argument is a signed or unsigned type/value.

Code: [Select]
;// Helper routine to check if a macro argument represents a signed value
is_macro_arg_signed macro a:REQ
if ((OPATTR (a)) and 00000100b) ;// argument is immediate value
ifidni @SubStr(<a>, 1, 1),<-> ;// check for leading sign
exitm<1>
endif
endif
;// check arguments for signed/unsigned type
if ((OPATTR (a)) and 0x10) ;// argument is a register
if (@SizeStr(<a>) ge 10)
ifidni @SubStr(<a>, 1, 8),<INT_ ptr> ;// check for type prefix
exitm<1>
endif
ifidni @SubStr(<a>, 1, 8),<long ptr>
exitm<1>
endif
ifidni @SubStr(<a>, 1, 10),<sdword ptr>
exitm<1>
else
ifidni @SubStr(<a>, 1, 9),<sword ptr>
exitm<1>
else
ifidni @SubStr(<a>, 1, 9),<sbyte  ptr>
exitm<1>
endif
endif
endif
endif
endif
if ((OPATTR (a)) and 0x02) ;// argument is a memory variable or location
% ifidni <typeid(?, a)>,<?sqword>
exitm<1>
endif
% ifidni <typeid(?, a)>,<?sdword>
exitm<1>
endif
% ifidni <typeid(?, a)>,<?sword>
exitm<1>
endif
% ifidni <typeid(?, a)>,<?sbyte>
exitm<1>
endif
endif
exitm<0> ;// unsigned
endm

Quiet complicated code, but it works for me.  :bgrin:
Maybe this all can be covered by typeid(?, a) ???
The typeid() is a very powerful thing and gave me the ability to determine if a mem var is signed or unsigned.

The helper routines can also be used with MAKELONG, etc..

And finally here's the max macro:

Code: [Select]
;///////////////////////
max macro a:REQ,b:REQ
local a_ishigher,reg,arg_a,arg_b
arg_a equ <a>
arg_b equ <b>
reg equ <> ;// macro max(a,b) from include WinDef.inc
if ((OPATTR (a)) and 0x04) and ((OPATTR (b)) and 0x04) ;;// first and second argument are immediate values (compile time expansion)
if a gt b
exitm <a>
else
exitm <b>
endif
elseif ((OPATTR (a)) and 0x04) ;// argument "b" is an immediate value
reg equ get_accumulator(b) ;// get register depending on size of "b" ???
else
reg equ get_accumulator(a) ;// get register depending on size of "a" (assuming "a" is a mem var)
endif

ifidni arg_a, arg_b ;// this comparism is erroneous, e.g. it will not detect max(sdword ptr edx, edx).
% echo @CatStr(%@FileName, <.asm (>, %@Line, <)>,< : "Warning: comparism of same registers in macro max()!">)
endif

if ((OPATTR (b)) and 0x10) ;// argument "b" is a register
ifidni reg, <b> ;// if "b" is the accumulator register,
exitm max(b,a) ;// "return" max with swapped arguments.
endif
endif

if ((OPATTR (a)) and 0x10) ;// argument "a" is a register
ifdifi reg, <a> ;// don't load accumulator into itself
mov  reg, a
endif
else
mov  reg, a
endif

cmp reg, b ;// finally, do the comparism ...

;// check arguments for signed/unsigned type and choose the right instruction.
if ((is_macro_arg_signed(a) eq 1) or (is_macro_arg_signed(b) eq 1))
jge a_ishigher ;// signed comparison
else
jae a_ishigher ;// unsigned comparison
endif
mov reg, b
a_ishigher:
exitm reg
endm
endif

Also very complicated and far away from beeing perfect but I hope you understand what I'm trying to achieve here.
I guess the macro doesn't catch all possible constellations but for most cases it gives me quiet efficient code.

For example, the following code line of nested min/max ...
mov  [rbx].CBDOCKITEM.rc.left, min(max(min([rbx].CBDOCKITEM.rc.left, [r14].CBDOCKITEM.rc.right), [rbx].CBDOCKITEM.rcOld.left), LONG ptr ecx)


...is expanded to:
1     mov  eax, [rbx].CBDOCKITEM.rc.left
1     cmp eax, [r14].CBDOCKITEM.rc.right      ;// macro min([rbx].CBDOCKITEM.rc.left,[r14].CBDOCKITEM.rc.right) from include stdlib.inc
1     jnge ??0019   ;// signed comparison
1     mov eax, [r14].CBDOCKITEM.rc.right
1     cmp eax, [rbx].CBDOCKITEM.rcOld.left   ;// macro max(eax,[rbx].CBDOCKITEM.rcOld.left) from include stdlib.inc
1     jge ??001E   ;// signed comparison
1     mov eax, [rbx].CBDOCKITEM.rcOld.left
1     cmp eax, LONG ptr ecx            ;// macro min(eax,LONG ptr ecx) from include stdlib.inc
1     jnge ??0023   ;// signed comparison
1     mov eax, LONG ptr ecx
      mov  [rbx].CBDOCKITEM.rc.left, eax


To be continued ...  :arrow_down:

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #49 on: January 15, 2022, 03:32:17 AM »
So, a full implementation then  :biggrin:

The typeid(sdword ptr reg) failed as it didn't consider registers to be signed. This is fixed in v2.33.34.

Unless the second operand is IMM the CMOVx instruction is used here. I guess you could also flip the operands and do reverse compare there to skip the branching.

Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #50 on: January 15, 2022, 08:14:45 AM »
Here's my old implementation of MAKEWORD, MAKELONG, etc. ...

WinDef.inc
Code: [Select]
MAKEWORD macro a:REQ,b:REQ
if ((OPATTR (a)) and 00000100b) and ((OPATTR (b)) and 00000100b) ;;// first and second argument are constants
exitm <( ( ( ( a )  and  0ffh )  or  ( ( b )  and  0ffh )  shl  8 ) ) >
elseif ((OPATTR (a)) and 00000100b) ;;// first argument is a constant
mov  eax, a ;// macro MAKEWORD(a,b)
if (SIZEOF (TYPEOF (b))) EQ 1
mov  cl, b
elseif (SIZEOF (TYPEOF (b))) EQ 2
mov  cx, b
elseif (SIZEOF (TYPEOF (b))) EQ 4
mov  ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 8
mov  rcx, b
endif
and  eax, 0FFh
and  ecx, 0FFh
shl  ecx, 8
or   eax, ecx
exitm <ax>
elseif ((OPATTR (b)) and 00000100b) ;;// second argument is a constant
mov  ecx, b ;// macro MAKEWORD(a,b)
if (SIZEOF (TYPEOF (a))) EQ 1
mov  al, a
elseif (SIZEOF (TYPEOF (a))) EQ 2
mov  ax, a
elseif (SIZEOF (TYPEOF (a))) EQ 4
mov  eax, a
elseif (SIZEOF (TYPEOF (a))) EQ 8
mov  rax, a
endif
and  eax, 0FFh
and  ecx, 0FFh
shl  ecx, 8
or   eax, ecx
exitm <ax>
else
if (SIZEOF (TYPEOF (a))) EQ 1
mov  al, a
elseif (SIZEOF (TYPEOF (a))) EQ 2
mov  ax, a
elseif (SIZEOF (TYPEOF (a))) EQ 4
mov  eax, a
elseif (SIZEOF (TYPEOF (a))) EQ 8
mov  rax, a
endif
if (SIZEOF (TYPEOF (b))) EQ 1
mov  cl, b
elseif (SIZEOF (TYPEOF (b))) EQ 2
mov  cx, b
elseif (SIZEOF (TYPEOF (b))) EQ 4
mov  ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 8
mov  rcx, b
endif
and eax, 0FFh ;// macro MAKEWORD(a,b)
and ecx, 0FFh
shl ecx, 8
or  eax, ecx
exitm <ax>
endif
endm

MAKELONG macro a:REQ,b:REQ
if ((OPATTR (a)) and 00000100b) and ((OPATTR (b)) and 00000100b) ;;// first and second argument are constants
exitm <( ( ( ( a )  and  0ffffh )  or  ( ( b )  and  0ffffh )  shl  16 ) ) >
elseif ((OPATTR (a)) and 00000100b) ;;// first argument is a constant
mov  eax, a ;// macro MAKELONG(a,b)
if (SIZEOF (TYPEOF (b))) EQ 1
mov  cl, b
elseif (SIZEOF (TYPEOF (b))) EQ 2
mov  cx, b
elseif (SIZEOF (TYPEOF (b))) EQ 4
mov  ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 8
mov  rcx, b
endif
and  eax, 0FFFFh
and  ecx, 0FFFFh
shl  ecx, 16
or   eax, ecx
exitm <eax>
elseif ((OPATTR (b)) and 00000100b) ;;// second argument is a constant
mov  ecx, b ;// macro MAKELONG(a,b)
if (SIZEOF (TYPEOF (a))) EQ 1
mov  al, a
elseif (SIZEOF (TYPEOF (a))) EQ 2
mov  ax, a
elseif (SIZEOF (TYPEOF (a))) EQ 4
mov  eax, a
elseif (SIZEOF (TYPEOF (a))) EQ 8
mov  rax, a
endif
and  eax, 0FFFFh
and  ecx, 0FFFFh
shl  ecx, 16
or   eax, ecx
exitm <eax>
else
if (SIZEOF (TYPEOF (a))) EQ 1
mov  al, a
elseif (SIZEOF (TYPEOF (a))) EQ 2
mov  ax, a
elseif (SIZEOF (TYPEOF (a))) EQ 4
mov  eax, a
elseif (SIZEOF (TYPEOF (a))) EQ 8
mov  rax, a
endif
if (SIZEOF (TYPEOF (b))) EQ 1
mov  cl, b
elseif (SIZEOF (TYPEOF (b))) EQ 2
mov  cx, b
elseif (SIZEOF (TYPEOF (b))) EQ 4
mov  ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 8
mov  rcx, b
endif
and  eax, 0FFFFh ;// macro MAKELONG(a,b)
and  ecx, 0FFFFh
shl  ecx, 16
or   eax, ecx
exitm <eax>
endif
endm

LOWORD macro l:REQ
if (OPATTR (l)) and 00000100b ;;// argument is constant
exitm <( ( ( ( l ) )  and  0ffffh ) ) >
else
if (OPATTR (l)) and 00010000b ;;// argument is register
if (SIZEOF (TYPEOF (l))) EQ 4
ifdifi <l>, <eax>
mov  eax, l ;// macro LOWORD(l)
endif
elseif (SIZEOF (TYPEOF (l))) EQ 8
ifdifi <l>, <rax>
mov  rax, l ;// macro LOWORD(l)
endif
else
.err <Wrong argument size in macro LOWORD.>
endif
else
if (SIZEOF (TYPEOF (l))) EQ 4
mov  eax, l ;// macro LOWORD(l)
elseif (SIZEOF (TYPEOF (l))) EQ 8
mov  rax, l ;// macro LOWORD(l)
else
.err <Wrong argument size in macro LOWORD.>
endif
endif
and  eax, 0FFFFh ;// macro LOWORD(l)
exitm <ax>
endif
endm

HIWORD macro l:REQ
if (OPATTR (l)) and 00000100b ;;// argument is constant
exitm <( ( ( ( ( l ) )  shr  16 )  and  0ffffh ) ) >
else
if (OPATTR (l)) and 00010000b ;;// argument is register
if (SIZEOF (TYPEOF (l))) EQ 4
ifdifi <l>, <eax>
mov  eax, l ;// macro HIWORD(l)
endif
elseif (SIZEOF (TYPEOF (l))) EQ 8
ifdifi <l>, <rax>
mov  rax, l ;// macro HIWORD(l)
endif
else
.err <Wrong argument size in macro HIWORD.>
endif
else
if (SIZEOF (TYPEOF (l))) EQ 4
mov  eax, l ;// macro HIWORD(l)
elseif (SIZEOF (TYPEOF (l))) EQ 8
mov  rax, l ;// macro HIWORD(l)
else
.err <Wrong argument size in macro HIWORD.>
endif
endif
shr  eax, 16 ;// macro HIWORD(l)
and  eax, 0FFFFh ;// macro HIWORD(l)
exitm <ax>
endif
endm

LOBYTE macro w:REQ
if (OPATTR (w)) and 00000100b ;;// argument is constant
exitm <( ( ( ( w ) )  and  0ffh ) ) >
else
if (OPATTR (w)) and 00010000b ;;// argument is register
if (SIZEOF (TYPEOF (w))) EQ 2
ifdifi <w>, <ax>
mov  ax, w ;// macro LOBYTE(w)
endif
elseif (SIZEOF (TYPEOF (w))) EQ 4
ifdifi <w>, <eax>
mov  eax, w ;// macro LOBYTE(w)
endif
elseif (SIZEOF (TYPEOF (w))) EQ 8
ifdifi <w>, <rax>
mov  rax, w ;// macro LOBYTE(w)
endif
else
.err <Wrong argument size in macro LOBYTE.>
endif
else
if (SIZEOF (TYPEOF (w))) EQ 2
mov  ax, w ;// macro LOBYTE(w)
elseif (SIZEOF (TYPEOF (w))) EQ 4
mov  eax, w ;// macro LOBYTE(w)
elseif (SIZEOF (TYPEOF (w))) EQ 8
mov  rax, w ;// macro LOBYTE(w)
else
.err <Wrong argument size in macro LOBYTE.>
endif
endif
and  ax, 0FFh ;// macro LOBYTE(w)
exitm <al>
endif
endm

HIBYTE macro w:REQ
if (OPATTR (w)) and 00000100b ;;// argument is constant
exitm <( ( ( ( ( w ) )  shr  8 )  and  0ffh ) ) >
else
if (OPATTR (w)) and 00010000b ;;// argument is register
if (SIZEOF (TYPEOF (w))) EQ 2
ifdifi <w>, <ax>
mov  ax, w ;// macro HIBYTE(w)
endif
elseif (SIZEOF (TYPEOF (w))) EQ 4
ifdifi <w>, <eax>
mov  eax, w ;// macro HIBYTE(w)
endif
elseif (SIZEOF (TYPEOF (w))) EQ 8
ifdifi <w>, <rax>
mov  rax, w ;// macro HIBYTE(w)
endif
else
.err <Wrong argument size in macro HIBYTE.>
endif
else
if (SIZEOF (TYPEOF (w))) EQ 2
mov  ax, w ;// macro HIBYTE(w)
elseif (SIZEOF (TYPEOF (w))) EQ 4
mov  eax, w ;// macro HIBYTE(w)
elseif (SIZEOF (TYPEOF (w))) EQ 8
mov  rax, w ;// macro HIBYTE(w)
else
.err <Wrong argument size in macro HIBYTE.>
endif
endif
shr  ax, 8 ;// macro HIBYTE(w)
and  ax, 0FFh ;// macro HIBYTE(w)
exitm <al>
endif
endm


Unfortunately I have no quick idea to shorten the macros above. My first thought about a general helper routine does not work here.
So, maybe a special helper routine for these ones ?


WindowsX.inc
Code: [Select]
GET_X_LPARAM macro lp
movsx  eax, LOWORD ( lp ) ;// Not really happy with it, because of "movsx  eax, ax"
exitm <eax >
endm
GET_Y_LPARAM macro lp
movsx  eax, HIWORD ( lp ) ;// Not really happy with it, because of "movsx  eax, ax"
exitm <eax >
endm

WinUser.inc (not sure if POINTSTOPOINT works)
Code: [Select]
POINTSTOPOINT macro pt,pts
movsx  eax, pts.POINTS.x
mov  pt.POINT.x, eax
movsx  eax, pts.POINTS.y
mov  pt.POINT.y, eax
endm
POINTTOPOINTS macro pt
exitm <( MAKELONG ( ( pt.POINT.x ) , ( pt.POINT.y ) ) ) >
endm
MAKEWPARAM macro l,h
ifdef _WIN64
if ((OPATTR (l)) and 00000100b) and ((OPATTR (h)) and 00000100b) ;; first and second argument are constants
mov r8, MAKELONG ( l , h )
else
movsxd r8, MAKELONG ( l , h )
endif
exitm <r8>
else
exitm <( MAKELONG ( l , h ) ) >
endif
endm
MAKELPARAM macro l,h
ifdef _WIN64
if ((OPATTR (l)) and 00000100b) and ((OPATTR (h)) and 00000100b) ;; first and second argument are constants
mov r9, MAKELONG ( l , h )
else
movsxd r9, MAKELONG ( l , h )
endif
exitm <r9>
else
exitm <( MAKELONG ( l , h ) ) >
endif
endm
MAKELRESULT macro l,h
ifdef _WIN64
if ((OPATTR (l)) and 00000100b) and ((OPATTR (h)) and 00000100b) ;; first and second argument are constants
mov rax, MAKELONG ( l , h )
else
movsxd rax, MAKELONG ( l , h )
endif
exitm <rax>
else
exitm <( MAKELONG ( l , h ) ) >
endif
endm
endif ;/* !NOMSG */


To be continued ...  :arrow_down:

Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #51 on: January 15, 2022, 09:16:09 AM »
For the 32bit version of MAKEWPARAM() and MAKELPARAM() it would be necessary to return different registers. In the macros above there is a "eax"-trap.

SendMessage (hwnd, WM_ANYMSG, MAKEWPARAM(l.h), MAKELPARAM(l.h))

This would not work in 32bit.

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #52 on: January 15, 2022, 11:32:13 AM »
I made some extensions to macros so they should handle bytes, words, dwords and qword in addition to floats.

However, it's difficult to get a clean syntax here without using registers. Using a rule-based implementation also have the advantage of producing error messages if registers are overwritten and other things. So DX will be trashed here but there will be no branching.

This means it will fail on existing code where DX is used.

    mov  edx, min(first, last)
    mov  ecx, max(first, last)
    mov  first, edx
    mov  last,  ecx
    add  ecx,   1
...
    mov  ecx, max(first, last)
    mov  edx, min(first, edx)
    mov  first, edx
    mov  last,  ecx
    add  ecx,   1

Generated code:

   4:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
   7:   3b 45 68                cmp    eax,DWORD PTR [rbp+0x68]
   a:   73 03                   jae    0xf
   c:   8b 45 68                mov    eax,DWORD PTR [rbp+0x68]
   f:   8b c8                   mov    ecx,eax
  11:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
  14:   3b c2                   cmp    eax,edx
  16:   72 02                   jb     0x1a
  18:   8b c2                   mov    eax,edx
  1a:   8b d0                   mov    edx,eax
  1c:   89 55 60                mov    DWORD PTR [rbp+0x60],edx
  1f:   89 4d 68                mov    DWORD PTR [rbp+0x68],ecx
  22:   83 c1 01                add    ecx,0x1
...
   4:   8b 55 68                mov    edx,DWORD PTR [rbp+0x68]
   7:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
   a:   3b c2                   cmp    eax,edx
   c:   0f 42 c2                cmovb  eax,edx
   f:   8b c8                   mov    ecx,eax
  11:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
  14:   3b c2                   cmp    eax,edx
  16:   0f 47 c2                cmova  eax,edx
  19:   8b d0                   mov    edx,eax
  1b:   89 55 60                mov    DWORD PTR [rbp+0x60],edx
  1e:   89 4d 68                mov    DWORD PTR [rbp+0x68],ecx
  21:   83 c1 01                add    ecx,0x1

There was some problems with serialization with typeid(function_call(...)).

    max(min(first, edx), ebx)
...
   4:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
   7:   3b c2                   cmp    eax,edx
   9:   72 02                   jb     0xd
   b:   8b c2                   mov    eax,edx
   d:   3b c3                   cmp    eax,ebx
   f:   73 02                   jae    0x13
  11:   8b c3                   mov    eax,ebx
  13:   8b f8                   mov    edi,eax
...
   4:   8b 45 60                mov    eax,DWORD PTR [rbp+0x60]
   7:   3b c2                   cmp    eax,edx
   9:   0f 47 c2                cmova  eax,edx
   c:   8b d3                   mov    edx,ebx
   e:   3b c2                   cmp    eax,edx
  10:   0f 42 c2                cmovb  eax,edx
  13:   8b f8                   mov    edi,eax

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #53 on: January 15, 2022, 01:02:15 PM »
It's possible to add a return value to the macro.

POINTSTOPOINT proto fastcall pt:ptr POINT, pts:ptr {
   movsx eax,dx
   mov   [pt].POINT.x,eax
   sar   edx,16
   mov   [pt].POINT.y,edx
   }
POINTTOPOINTS proto fastcall pt:ptr POINT {
   mov   eax,[pt].POINT.y
   shl   eax,16
   or    eax,[pt].POINT.x
   }

MAKEWPARAM macro l, h, reg:=<eax>
   MAKELONG(l, h, reg)
   retm<reg>
   endm
MAKELPARAM macro l, h, reg:=<eax>
   MAKELONG(l, h, reg)
   retm<reg>
   endm
MAKELRESULT macro l,h
   exitm<MAKELONG(l, h)>
   endm
...
    SendMessage (rbx, 10, MAKEWPARAM(h,l,r8d), MAKELPARAM(h,l,r9d))
...
  1c:   8b 55 10                mov    edx,DWORD PTR [rbp+0x10]
  1f:   8b 45 18                mov    eax,DWORD PTR [rbp+0x18]
  22:   44 8b c0                mov    r8d,eax
  25:   41 c1 e0 10             shl    r8d,0x10
  29:   41 0f ac d0 10          shrd   r8d,edx,0x10
  2e:   8b 55 10                mov    edx,DWORD PTR [rbp+0x10]
  31:   8b 45 18                mov    eax,DWORD PTR [rbp+0x18]
  34:   44 8b c8                mov    r9d,eax
  37:   41 c1 e1 10             shl    r9d,0x10
  3b:   41 0f ac d1 10          shrd   r9d,edx,0x10
  40:   ba 0a 00 00 00          mov    edx,0xa
  45:   48 8b cb                mov    rcx,rbx
  48:   e8 b3 ff ff ff          call   SendMessageA


Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #54 on: January 15, 2022, 05:27:00 PM »
WinGDI.inc
Code: [Select]
;/* Quaternary raster codes */
MAKEROP4 macro fore,back
if ((OPATTR (fore)) and 00000100b) and ((OPATTR (back)) and 00000100b) ;; first and second argument are constants
exitm <( ( ( ( back )  shl  8 )  and  0FF000000h )  or  ( fore ) ) >
else
if (SIZEOF (TYPEOF (back))) EQ 1
mov al, back
elseif (SIZEOF (TYPEOF (back))) EQ 2
mov ax, back
elseif (SIZEOF (TYPEOF (back))) EQ 4
mov eax, back
elseif (SIZEOF (TYPEOF (back))) EQ 8
mov rax, back
endif
shl eax, 8
and eax, 0FF000000h
or  eax, fore
exitm <eax>
endif
endm

...

;/* Macros to retrieve CMYK values from a COLORREF */
GetKValue macro cmyk
mov eax, cmyk ;// macro GetKValue(cmyk)
exitm <al>
endm
GetYValue macro cmyk
mov eax, cmyk ;// macro GetYValue(cmyk)
shr eax, 8
exitm <al>
endm
GetMValue macro cmyk
mov eax, cmyk ;// macro GetMValue(cmyk)
shr eax, 16
exitm <al>
endm
GetCValue macro cmyk
mov eax, cmyk ;// macro GetCValue(cmyk)
shr eax, 24
exitm <al>
endm
CMYK macro c,m,y,k
if ((OPATTR (c)) and 00000100b) and ((OPATTR (m)) and 00000100b) and ((OPATTR (y)) and 00000100b) and ((OPATTR (k)) and 00000100b) ;; all arguments are constants
exitm <( ( ( ( ( k )  or  ( ( ( y ) )  shl  8 ) )  or  ( ( ( m ) )  shl  16 ) )  or  ( ( ( c ) )  shl  24 ) ) ) >
else
mov eax, y ;// macro CMYK(c,m,y,k)
shl eax, 8
or  eax, k
mov ecx, m
shl ecx, 16
or  eax, ecx
mov ecx, c
shl ecx, 24
or  eax, ecx
exitm <eax>
endif
endm

...

RGB macro r:REQ,g:REQ,b:REQ
if ((OPATTR (r)) and 00000100b) and ((OPATTR (g)) and 00000100b) and ((OPATTR (b)) and 00000100b) ; all arguments are constants
exitm <(((r) or ((g) shl 8)) or ((b) shl 16))>
else
; Copy r into ECX
if (OPATTR (r)) and 00010000b ; argument is register
if (SIZEOF (TYPEOF (r))) EQ 1
ifdifi <r>, <cl>
movzx ecx, r ;-- macro RGB(r,g,b)
endif
elseif (SIZEOF (TYPEOF (r))) EQ 2
ifdifi <r>, <cx>
movzx ecx, r ;-- macro RGB(r,g,b)
endif
elseif (SIZEOF (TYPEOF (r))) EQ 4
ifdifi <r>, <ecx>
mov ecx, r ;-- macro RGB(r,g,b)
endif
elseif (SIZEOF (TYPEOF (r))) EQ 8
ifdifi <r>, <rcx>
mov rcx, r ;-- macro RGB(r,g,b)
endif
endif
else
if (SIZEOF (TYPEOF (r))) EQ 1
movzx ecx, r ;-- macro RGB(r,g,b)
elseif (SIZEOF (TYPEOF (r))) EQ 2
movzx ecx, r ;-- macro RGB(r,g,b)
elseif (SIZEOF (TYPEOF (r))) EQ 4
mov ecx, r ;-- macro RGB(r,g,b)
elseif (SIZEOF (TYPEOF (r))) EQ 8
mov rcx, r ;-- macro RGB(r,g,b)
endif
endif
; Copy g into EAX
if (OPATTR (g)) and 00010000b ; argument is register
if (SIZEOF (TYPEOF (g))) EQ 1
ifdifi <g>, <al>
movzx eax, g
endif
elseif (SIZEOF (TYPEOF (g))) EQ 2
ifdifi <g>, <ax>
movzx eax, g
endif
elseif (SIZEOF (TYPEOF (g))) EQ 4
ifdifi <g>, <eax>
mov eax, g
endif
elseif (SIZEOF (TYPEOF (g))) EQ 8
ifdifi <g>, <rax>
mov rax, g
endif
endif
else
if (SIZEOF (TYPEOF (g))) EQ 1
movzx eax, g
elseif (SIZEOF (TYPEOF (g))) EQ 2
movzx eax, g
elseif (SIZEOF (TYPEOF (g))) EQ 4
mov eax, g
elseif (SIZEOF (TYPEOF (g))) EQ 8
mov rax, g
endif
endif
shl eax, 8 ; Shift EAX eight bits to the left side
or  eax, ecx ; and now ORing EAX with ECX.
; Copy b into ECX
if (OPATTR (b)) and 00010000b ; argument is register
if (SIZEOF (TYPEOF (b))) EQ 1
ifdifi <b>, <cl>
movzx ecx, b
endif
elseif (SIZEOF (TYPEOF (b))) EQ 2
ifdifi <b>, <cx>
movzx ecx, b
endif
elseif (SIZEOF (TYPEOF (b))) EQ 4
ifdifi <b>, <ecx>
mov ecx, b
endif
elseif (SIZEOF (TYPEOF (b))) EQ 8
ifdifi <b>, <rcx>
mov rcx, b
endif
endif
else
if (SIZEOF (TYPEOF (b))) EQ 1
movzx ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 2
movzx ecx, b
elseif (SIZEOF (TYPEOF (b))) EQ 4
mov ecx, b ; Copy b into ECX
elseif (SIZEOF (TYPEOF (b))) EQ 8
mov rcx, b
endif
endif
shl ecx, 16 ; Shift ECX 16 bits to the left side,
or  eax, ecx ; and OR EAX with ECX together.
exitm <eax>
endif
endm
PALETTERGB macro r,g,b
if ((OPATTR (r)) and 00000100b) and ((OPATTR (g)) and 00000100b) and ((OPATTR (b)) and 00000100b) ; all arguments are constants
exitm <( 02000000h  or  RGB ( r , g , b ) ) >
else
or RGB ( r , g , b ), 02000000h
exitm <eax>
endif
endm
PALETTEINDEX macro i
if ((OPATTR (i)) and 00000100b) ;  argument is a constants
exitm <( ( 01000000h  or  ( i ) ) ) >
else
if (OPATTR (i)) and 00010000b ; argument is register
ifdifi <i>, <eax>
mov eax, i ; Copy r into EAX
endif
else
mov eax, i ; Copy r into EAX
endif
or eax, 01000000h
exitm <eax>
endif
endm
;* palette entry flags *
;* palette index used for animation *
PC_RESERVED EQU 01h
;* palette index is explicit to device *
PC_EXPLICIT EQU 02h
;* do not match color to system palette *
PC_NOCOLLAPSE EQU 04h
GetRValue macro rgb
exitm < LOBYTE ( rgb ) >
endm
GetGValue macro rgb
if ((OPATTR (rgb)) and 00000100b) ;  argument is a constant
exitm <( LOBYTE ( ( ( rgb ) )  shr  8 ) ) >
else
if (OPATTR (rgb)) and 00010000b ; argument is register
ifdifi <rgb>, <eax>
mov eax, rgb ;-- macro GetGValue(rgb)
endif
else
mov eax, rgb ;-- macro GetGValue(rgb)
endif
shr eax, 8
exitm <LOBYTE ( <eax> )>
endif
endm
GetBValue macro rgb
if ((OPATTR (rgb)) and 00000100b) ;  argument is a constant
exitm <( LOBYTE ( ( rgb )  shr  16 ) ) >
else
if (OPATTR (rgb)) and 00010000b ; argument is register
ifdifi <rgb>, <eax>
mov eax, rgb ;-- macro GetGValue(rgb)
endif
else
mov eax, rgb ;-- macro GetGValue(rgb)
endif
shr eax, 16
exitm <LOBYTE ( <eax> )>
endif
endm

Don't know if I did more. Maybe MAKEPOINTS ...
Maybe this is the last episode ...  :arrow_up:

Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #55 on: January 15, 2022, 06:02:51 PM »
MAKEWPARAM macro l, h, reg:=<eax>
MAKELPARAM macro l, h, reg:=<eax>


For _WIN64 they should return r8, resp. r9 by default.

And MAKELRESULT should return rax in this case ...

Very nice progress.  :thumbsup:

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #56 on: January 16, 2022, 07:15:19 AM »
Some more macro fun.

This inline the function call to retrieve the registers.

load_ax_dx macro a, b, t1, t2, r1, r2

  local func

    func proto watcall :t1, :t2 {
        r1 equ <_1>
        r2 equ <_2>
        }
    func(a, b)
    exitm<>
    endm

cmp_ax_dx macro a, b, x

  local rb,rb,ta,tb,signed

    ta textequ typeid(a)
    tb textequ typeid(b)

    ifidn ta,<imm>
     ifidn tb,<imm>
      if (a) &x&t (b)
        exitm<a>
      else
        exitm<b>
      endif
     else
        ta textequ tb
     endif
    elseifidn tb,<imm>
        tb textequ ta
    endif

    ifidn ta,<ptr>
        ta textequ tb
    elseifidn tb,<ptr>
        tb textequ ta
    endif
    ifidn ta,<near>
        ta textequ tb
    elseifidn tb,<near>
        tb textequ ta
    endif

    load_ax_dx(a,b,ta,tb,ra,rb)
    cmp ra,rb

    if ( typeof(ra) lt 4 )
        ra textequ <eax>
        rb textequ <edx>
    endif

    ifidn @SubStr(%ta, 1, 1),<s>
        signed = 1
    elseifidn @SubStr(%tb, 1, 1),<s>
        signed = 1
    else
        signed = 0
    endif
    if signed
     ifidn <x>,<g>
        cmovl ra,rb
     else
        cmovg ra,rb
     endif
    else
     ifidn <x>,<g>
        cmovb ra,rb
     else
        cmova ra,rb
     endif
    endif
    retm<ra>
    endm

max macro a, b
%   retm<cmp_ax_dx(a, b, g)>
    endm
min macro a, b
%   retm<cmp_ax_dx(a, b, l)>
    endm

The loading of the registers will extend byte and words to 32-bit, and as the CMOV instruction do not handle bytes this will be the return, but the compare value will be input size. Signed compare is done if one or both arguments are signed. Also note that RETM do not return an unused value.

    mov ebx,max(sw, ax)
    min(al, sb)
...
   4:   0f b7 d0                movzx  edx,ax
   7:   0f bf 45 38             movsx  eax,WORD PTR [rbp+0x38]
   b:   66 3b c2                cmp    ax,dx
   e:   0f 4c c2                cmovl  eax,edx
  11:   8b d8                   mov    ebx,eax
  13:   0f be 55 28             movsx  edx,BYTE PTR [rbp+0x28]
  17:   0f b6 c0                movzx  eax,al
  1a:   3a c2                   cmp    al,dl
  1c:   0f 4f c2                cmovg  eax,edx

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #57 on: January 17, 2022, 12:32:37 AM »
I updated most of the macros but they need testing.

As for return values and HRESULT they should be signed integer values, so maybe MAKELRESULT() should be 32-bit?

SUCCEEDED proto watcall hr:sdword {
   retm<(sdword ptr eax !>= 0)>
   }
FAILED proto watcall hr:sdword {
   retm<(sdword ptr eax !< 0)>
   }

    .if (SUCCEEDED(Package.get_IsFramework(&isFramework)))

        wcout << "IsFramework: " << isFramework << endl
    .endif
   
    .return 0 .ifd InitWindow( hInstance, nCmdShow ) != S_OK

Greenhorn

  • Member
  • ***
  • Posts: 258
Re: Asmc and Visual Studio
« Reply #58 on: January 17, 2022, 02:32:03 AM »
Great !  :thumbsup:

I will test it as soon as possible.

> As for return values and HRESULT they should be signed integer values, so maybe MAKELRESULT() should be 32-bit?

The C macro is defined as:
#define MAKELRESULT(l, h)     ((LRESULT)(DWORD)MAKELONG(l, h))

It is type-casted to LRESULT which is defined as LONG_PTR which again is QWORD in 64bit and DWORD in 32bit.

Same applies to MAKEWPARAM and MAKELPARAM:
#define MAKEWPARAM(l, h)      ((WPARAM)(DWORD)MAKELONG(l, h))
#define MAKELPARAM(l, h)      ((LPARAM)(DWORD)MAKELONG(l, h))
...
typedef LONG_PTR WPARAM;
typedef LONG_PTR LPARAM;


So, for MAKEWPARAM and MAKELPARAM the C compiler will do: movsxd  r8, eax, respectively movsxd  r9, eax in 64bit if the macro is put into a function parameter.
SendMessage (hwnd, WM_..., MAKEWPARAM(l,h), MAKELPARAM(l,h))
> Creates a value for use as a wParam parameter in a message. The macro concatenates the specified values.
> Creates a value for use as an lParam parameter in a message. The macro concatenates the specified values.

For MAKELRESULT the compiler does a type-casting like movsxd  rax, eax since this macro is designed to create a return value, e.g.:
.return MAKELRESULT(l,h)
> Creates a value for use as a return value from a window procedure. The macro concatenates the specified values.

This is how these macros are intended to be used. For anything else MAKELONG is intended to be used, which returns a LONG (eax).

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Asmc and Visual Studio
« Reply #59 on: January 17, 2022, 02:34:09 AM »
Small update.

This failed:

ifdef _WIN64
MAKEWPARAM macro l, h, reg:=<r8d>
else
MAKEWPARAM macro l, h, reg:=<eax>
endif
   MAKELONG(l, h, reg)
   retm<reg>
   endm
...
ifdef _WIN64
MAKEWPARAM macro l, h, reg:=<r8d>
   MAKELONG(l, h, reg)
   retm<reg>
   endm
else
MAKEWPARAM macro l, h, reg:=<eax>
   MAKELONG(l, h, reg)
   retm<reg>
   endm
endif

Using AH in 64-bit will fail for R8++

ifdef _WIN64
   ifdif <c>,<al>
   mov   al,c
   endif
   shl   eax,8
   mov   al,m
   shl   eax,8
   mov   al,y
   shl   eax,8
else
   mov   ah,c
   mov   al,m
   shl   eax,16
   mov   ah,y
endif
   mov   al,k

This will also fail using AL or AH as argument..