Author Topic: Asmc source and binaries  (Read 37484 times)

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #240 on: December 27, 2019, 02:10:37 AM »
Added a fix for the delayed expansion of macro error in v2.31.05.

Regress:

    .x64
    .model flat, fastcall
    .code

_HRESULT_TYPEDEF_ macro val
    exitm<val>
    endm

E_OUTOFMEMORY EQU _HRESULT_TYPEDEF_ ( 8007000Eh )

    .code

    .if edx
        nop
    .elseif eax == E_OUTOFMEMORY
        nop
    .endif

    .while eax == E_OUTOFMEMORY
        nop
    .endw

    end

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #241 on: January 05, 2020, 01:46:27 AM »
More integration of PROC and MACRO.

The same functionality used by the CLASS directive which enables constructors and members to be declared as inline (macro) functions are now also available for regular PROTO definition of functions.

In HLL both of these are allowed to be declared in the same header:

_CRTIMP int __cdecl isalnum (int __c);
...
#define isalnum(c)  (_ctype[(unsigned char)(c) + 1] & (_DIGIT | _UPPER | _LOWER))

This is now also allowed in Asmc. If a PROTO is defined before a MACRO the latter will be stored as a sub-symbol of the PROTO definition. This means that the MACRO call will be handled by INVOKE. The parameters to the MACRO will then be exclusively registers sized up according to the PROTO definition.

foo proto :word, :byte, :real4

foo macro  a, b, c ; cx, dl, xmm2
    mov ax,a
    mov al,b
    exitm<movss xmm0,c>
    endm

So the second definition of foo will override the first and a third declaration (a PROC with the same name) will override the MACRO.

main proc

    foo(1,2,3.0)
    ret

main endp

   0:   48 83 ec 28             sub    rsp,0x28
   4:   b8 00 00 40 40          mov    eax,0x40400000
   9:   66 0f 6e d0             movd   xmm2,eax
   d:   ba 02 00 00 00          mov    edx,0x2
  12:   b9 01 00 00 00          mov    ecx,0x1
  17:   66 8b c1                mov    ax,cx
  1a:   8a c2                   mov    al,dl
  1c:   f3 0f 10 c2             movss  xmm0,xmm2
  20:   48 83 c4 28             add    rsp,0x28
  24:   c3                      ret   

...
foo proc a:word, b:byte, c:real4
    ret
foo endp

main proc

    foo(1,2,3.0)
    ret

main endp

   0:   55                      push   rbp
   1:   48 8b ec                mov    rbp,rsp
   4:   48 83 ec 20             sub    rsp,0x20
   8:   c9                      leave 
   9:   c3                      ret   
   a:   48 83 ec 28             sub    rsp,0x28
   e:   b8 00 00 40 40          mov    eax,0x40400000
  13:   66 0f 6e d0             movd   xmm2,eax
  17:   ba 02 00 00 00          mov    edx,0x2
  1c:   b9 01 00 00 00          mov    ecx,0x1
  21:   e8 da ff ff ff          call   0x0
  26:   48 83 c4 28             add    rsp,0x28
  2a:   c3                      ret   

jj2007

  • Member
  • *****
  • Posts: 10088
  • Assembler is fun ;-)
    • MasmBasic
Re: Asmc source and binaries
« Reply #242 on: January 05, 2020, 05:10:47 AM »
Just downloaded the latest version to do my occasional tests:
- works fine with the RichMasm source (21k lines)
- chokes several times with error A2006: undefined symbol : type with the MasmBasic source

I used option /Zne with both. Version 18 April 2019 works fine with both sources. Are you really using the keyword "type" somewhere in a non-Masm compatible way?

One of the errors is related to the second line, with dest being MbCat$ defined in .data? as MbCat$ dd ?, and is2 logically being zero:
         % is2 INSTR <dest>, <.>
         if is2 and type(dest) eq BYTE

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #243 on: January 06, 2020, 07:35:12 AM »
I failed to reproduce this so I will assume you may omitted the /Zne (or /Znk) switch in the second case?

This works with both /Znk and /Zne

    .486
    .model flat, c

    t macro dest
        local q
        % q INSTR <dest>,<.>
        if q and type(dest) eq byte
            exitm<eax>
        endif
        exitm<edx>
        endm

    .data?
    x dd ?

    .code

    mov ebx,t(x)

    end

It will however produce an error without:

type.asm(18) : error A2006: undefined symbol : type

jj2007

  • Member
  • *****
  • Posts: 10088
  • Assembler is fun ;-)
    • MasmBasic
Re: Asmc source and binaries
« Reply #244 on: January 06, 2020, 09:42:29 AM »
Option /Zne used to solve the problem, now it doesn't. I also added /Znk, just for fun - no success. With option /Fl and .listall, I get a *.lst file with 616,865 lines, but there is no error A in the whole 36MB file. And no A2006 either (I used case-insensitive search).

It works fine with UAsm, ML 6.15 and ML 10.0
It chokes with ML 14.0, saying xEnd is a syntax error - that was apparently because I had used it as a local var in one proc but as a label in another proc. Which wasn't a problem for UAsm, AsmC, ML 6.15 and ML 10.0

I changed the var xEnd to xxEnd, and voilĂ , ML 14.0 is happy.
But ML 15.0 wasn't, it still choked on the remaining xEnd, so I suppose they misuse it as a keyword somewhere. Solved by calling it @xEnd - but the exe crashes, probably that "jump one byte too short" MASM bug :cool:

So it builds with ML 6.15, 10.0, 14.0, 15.0, UAsm and AsmC of April 2019 but not with the current AsmC. Oh well...

The good news is that the RichMasm editor and a number of simpler sources build just fine :thumbsup:

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #245 on: January 10, 2020, 02:42:23 AM »
Added some changes to the implementation above. It still a bit hackish but the general idea is to skip all the probing of parameters normally done in a macro.

The .NEW directive also allow stack allocation within a macro which extend the functionality even further.

SystemDirectory macro

    .new buffer[_MAX_PATH]:char_t

    GetSystemDirectory(&buffer, _MAX_PATH)
    lea rax,buffer
    retm<rax>
    endm

WindowsDirectory macro

    .new buffer[_MAX_PATH]:char_t

    GetWindowsDirectory(&buffer, _MAX_PATH)
    lea rax,buffer
    retm<rax>
    endm

This will reduce the use of static .data as done in HLL where objects normally are dynamically created. Using the same label (buffer) as done above will then share the same address.

main proc
   printf( "WindowsDirectory: %s\n", WindowsDirectory() )
   printf( "SystemDirectory:  %s\n", SystemDirectory() )
   return( 0 )
main endp

The RETURN directive is now hooked up to ENDP so it may be used as above and also in a macro. The DOT is still needed thought, so RETURN equ <.RETURN> here.

        push    rbp
        mov     rbp, rsp
        sub     rsp, 304                               
        mov     edx, 260                               
        lea     rcx, [rbp-104H]                         
        call    GetWindowsDirectoryA                   
        lea     rax, [rbp-104H]                         
        mov     rdx, rax                               
        lea     rcx, [DS0000]                           
        call    printf                                 
        mov     edx, 260                               
        lea     rcx, [rbp-104H]                         
        call    GetSystemDirectoryA                     
        lea     rax, [rbp-104H]                         
        mov     rdx, rax                               
        lea     rcx, [DS0001]                           
        call    printf                                 
        xor     eax, eax                               
        leave                                           
        ret                                             

Some of the ctype functions are implemented as inline which illustrate the use of RETM versus EXITM.

ctype_inline macro char, flags
    lea rax,_ctype
    mov al,[rax+rcx*2+2]
    and eax,flags
    retm<eax>
    endm

isupper macro char
    exitm<ctype_inline(char,_UPPER)>
    endm

The RETM directive skips the returned value if the macro is the first token which makes a function-macro more flexible or (more to the point) more equal to a PROC.

   isupper('A')
   return( 0 )

        sub     rsp, 40
        mov     ecx, 65
        lea     rax, [_ctype]
        mov     al, byte ptr [rax+rcx*2+2H]
        and     eax, 01H                   
        xor     eax, eax                   
        add     rsp, 40                   
        ret                               

   printf( "isupper('A'): %d\n", isupper('A') )
   return( 0 )

        sub     rsp, 40
        mov     ecx, 65
        lea     rax, [_ctype]
        mov     al, byte ptr [rax+rcx*2+2H]
        and     eax, 01H                   
        mov     rdx, rax                   
        lea     rcx, [DS0000]             
        call    printf                     
        xor     eax, eax                   
        add     rsp, 40                   
        ret                               


Vortex

  • Member
  • *****
  • Posts: 2102
Re: Asmc source and binaries
« Reply #246 on: February 05, 2020, 06:33:42 AM »
Hi nidud,

Today, I downloaded the latest version of Asmc.  Completing the build of the libraries after running asmc-2.31.cmd, I received the following error messages :

Code: [Select]
Missing: lib\amd64\uuid.lib
Missing: lib\amd64\quadmath.lib
Missing: lib\amd64\directxmath.lib

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #247 on: February 05, 2020, 07:01:48 AM »
Some notes about inline function and stack alignment.

This assumes Win64 ABI where RSP is always align16 and points to the first argument. Note that a PROTO/MACRO combination will create a stack frame regardless if the function is in-lined and thus extends the number of arguments beyond registers. However, the placeholders in the MACRO only sets available registers and the rest is random so memory locations needs special handling. The actual location is always [rsp+n*8].

foo proto :ptr, :ptr, :ptr, :ptr, :ptr, :ptr
foo macro a, b, c, d, e, f
    mov rax,a ; rcx
    mov rax,b ; rdx
    mov rax,c ; r8
    mov rax,d ; r9
    mov rax,[rsp+4*8]
    mov rax,[rsp+5*8]
    exitm<>
    endm

So basically the stack always point to a @ReservedStack sized aligned memory block of at least 32 byte. In order to allocate more stack the reserved call-stack has to be relocated:

    sub  rsp,16
    lea  rax,[rsp+@ReservedStack]
    ...

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #248 on: February 05, 2020, 07:11:14 AM »
Hi nidud,

Today, I downloaded the latest version of Asmc.  Completing the build of the libraries after running asmc-2.31.cmd, I received the following error messages :

Code: [Select]
Missing: lib\amd64\uuid.lib
Missing: lib\amd64\quadmath.lib
Missing: lib\amd64\directxmath.lib
This is just a notification that these libraries needs to be built if you plan on using them. Quadmath is included in LIBC but the other two may be needed to build some of the sample code.

Vortex

  • Member
  • *****
  • Posts: 2102
Re: Asmc source and binaries
« Reply #249 on: February 05, 2020, 07:19:09 AM »
Hi nidud,

Thanks for the info :thumbsup:

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #250 on: February 21, 2020, 07:54:41 AM »
However, the placeholders in the MACRO only sets available registers and the rest is random so memory locations needs special handling. The actual location is always [rsp+n*8].

foo proto :ptr, :ptr, :ptr, :ptr, :ptr, :ptr
foo macro a, b, c, d, e, f
    mov rax,a ; rcx
    mov rax,b ; rdx
    mov rax,c ; r8
    mov rax,d ; r9
    mov rax,[rsp+4*8]
    mov rax,[rsp+5*8]
    exitm<>
    endm
The memory address is added to the parameters so this is now fixed.

foo proto :ptr, :ptr, :ptr, :ptr, :ptr, :ptr
foo macro a, b, c, d, e, f
    mov rax,a ; rcx
    mov rax,b ; rdx
    mov rax,c ; r8
    mov rax,d ; r9
    mov rax,e ; [rsp+4*8]
    mov rax,f ; [rsp+5*8]
    exitm<>
    endm

In addition to this a new argument type is added for immediate values defined as :ABS. Invoke will skip this type and just add the param value directly.

foo(1,2,3,4,5,6)

    invoke:
        mov     qword ptr [rsp+28H], 6
        mov     qword ptr [rsp+20H], 5
        mov     r9d, 4               
        mov     r8d, 3               
        mov     edx, 2               
        mov     ecx, 1
    macro:
        mov     rax, rcx
        mov     rax, rdx                               
        mov     rax, r8                                 
        mov     rax, r9                                 
        mov     rax, qword ptr [rsp+20H]               
        mov     rax, qword ptr [rsp+28H]               

foo proto :ptr, :ptr, :ptr, :abs, :abs, :abs

    invoke:
        mov     r8d, 3
        mov     edx, 2
        mov     ecx, 1
    macro:
        mov     rax, rcx
        mov     rax, rdx
        mov     rax, r8
        mov     rax, 4
        mov     rax, 5
        mov     rax, 6

Greenhorn

  • Member
  • **
  • Posts: 121
Re: Asmc source and binaries
« Reply #251 on: February 23, 2020, 04:11:18 AM »
Hi nidud,

I've noticed a problem with nested macros/text equates on procedure calls.

A good example is SNDMSG from the windows header files.

ifdef _UNICODE
SendMessage      equ <SendMessageW>
else
SendMessage      equ <SendMessageA>
endif

ifndef SNDMSG
ifndef _MAC
SNDMSG equ <SendMessage>
else
SNDMSG equ <AfxSendMessage>
endif
endif


TabCtrl_InsertItem macro hwnd, iItem, pitem
   exitm<SNDMSG(hwnd, TCM_INSERTITEM, iItem, pitem)>
   endm


Using the TabCtrl_InsertItem or the SNDMSG macro will end up with error A2008: syntax error SendMessageW.
The macros are expanded to:
00000075                           SendMessageW, hwndTab, TCM_INSERTITEMW, 1, &tcitem


BTW, I found in the commctrl.inc a lot of inaccurate text equates which lacks of brackets. For example, TCITEM equ TCITEMW instead of  TCITEM equ <TCITEMW>.


Kind Regards
Greenhorn

Greenhorn

  • Member
  • **
  • Posts: 121
Re: Asmc source and binaries
« Reply #252 on: February 23, 2020, 06:33:27 AM »
This is a small "nice-to-have" feature request.

It would be nice if one could define several initializers and instructions in the .for directive.

For example:

.for (a = 0 : b = 10 : a < b : a++ : b--)
    ;...
.endf



I have also encountered a nice-to-have moment on the .if directive.

It would be nice if the directive could do simple instructions, for example:

.if ((a & 0xFC) == 0xF8)
.elseif ((a & 0xFE) == 0xFC)
.elseif ((a & 0xC0) == 0x80)
.elseif (...)
...
.endif


For short .if/.elseif branches it is no problem to do the calculations before the .if branches but for very large branches it would be very nice for readability and maintenance of the code if the directive could handle this.
Otherwise one would have to use locals for storing the calculations if there are no free registers left. Or one have to do it without the HLL directive.

However, as I said, it's just a nice-to-have, not a must-have. :smiley:

Regards
Greenhorn

Greenhorn

  • Member
  • **
  • Posts: 121
Re: Asmc source and binaries
« Reply #253 on: February 23, 2020, 09:20:55 AM »
Unfortunately I've been trapped again in issues with mov mem,procedure() and mov mem,&mem.

mov  g_mem, procedure() fails if g_mem is an external variable.

DATA struct
    a    QWORD  ?
    b    QWORD  ?
DATA ends

MYSTRUCT struct
    data    DATA  <>
MYSTRUCT ends

...

LOCAL ms: MYSTRUCT

lea  rbx, ms
mov  [rbx].MYSTRUCT.data.a, &[rbx].MYSTRUCT.data    ; This will fail

lea  rax, [rbx].MYSTRUCT.data
mov  [rbx].MYSTRUCT.data.a, rax    ; This works


Regards
Greenhorn

nidud

  • Member
  • *****
  • Posts: 1849
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #254 on: February 24, 2020, 01:01:56 AM »
The parser used for translating header files sort of evolved over time so newer files are more correctly translated. The commctrl.inc file is probably one of the first.

The result is assembled and errors corrected. Missing <> may end with data or code creation so this is tested but as for macros, there are around 6300 of them and only a few of these are tested so you still get a lot of these macros:

exitm<fn(hwnd, WM_GETDLGCODE, (lpmsg ? lpmsg->wParam : 0), (lpmsg))>

Quote
Using the TabCtrl_InsertItem or the SNDMSG macro will end up with error A2008: syntax error SendMessageW.
The macros are expanded to:
00000075                           SendMessageW, hwndTab, TCM_INSERTITEMW, 1, &tcitem

So from this using asmc64 -ws

include windows.inc
include commctrl.inc

    .code

WndProc proc hwnd:HWND, uiMsg:UINT, wParam:WPARAM, lParam:LPARAM

    TabCtrl_InsertItem(hwnd, 1, lParam)
    TabCtrl_InsertItem(hwnd, 1, &lParam)
    ret

WndProc endp

    end

I get this:

    * mov r9, lParam
    * mov r8d, 1
    * mov edx, TCM_INSERTITEM
    * mov rcx, hwnd
    * call SendMessageW
    * lea r9, lParam
    * mov r8d, 1
    * mov edx, TCM_INSERTITEM
    * mov rcx, hwnd
    * call SendMessageW

Quote
BTW, I found in the commctrl.inc a lot of inaccurate text equates which lacks of brackets. For example, TCITEM equ TCITEMW instead of  TCITEM equ <TCITEMW>.

Thanks. There are a few of those but they only get fixed as they pop up.

Quote
This is a small "nice-to-have" feature request.

It would be nice if one could define several initializers and instructions in the .for directive.

For example:

.for (a = 0 : b = 10 : a < b : a++ : b--)
    ;...
.endf

That already works but you have to use a comma to separate assignments.

    .for (a = 0, b = 10 : a < b : a++, b--)
        ;...
    .endf

This also works:

    .for (a = strlen(s1), b = strlen(s2) : a < b :,
          a += strlen(s2), b -= strlen(s1) )
        ;...
    .endf

Quote
I have also encountered a nice-to-have moment on the .if directive.

It would be nice if the directive could do simple instructions, for example:

.if ((a & 0xFC) == 0xF8)
.elseif ((a & 0xFE) == 0xFC)
.elseif ((a & 0xC0) == 0x80)
.elseif (...)
...
.endif

Yes, or direct assignment: .if (a = strlen(p)) > 2
It's possible to do similar things in a .for loop but not indirectly. I guess it would be possible to do but it demand some extended parsing to achieve that.

Quote
Code: [Select]
mov  [rbx].MYSTRUCT.data.a, &[rbx].MYSTRUCT.data    ; This will fail

Confirmed.

    works: mov [rcx],&ms
    fails: mov [rcx],&[rcx]

I look into that.