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

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #270 on: May 07, 2020, 12:14:35 AM »
Construction of classes may now be done directly providing there's a proto type defined:

    Rect(0, 0, 400, 200)

This may be assigned or used as argument to a function call.

Some restrictions added to this feature as it may create some problems for data declarations. When used from a template, code will be generated with a standard function return (RETM/EXITM ignored), or as a inline function by a direct call (Rect_Rect(...)), or simply as a macro.

Rect macro _1, _2, _3, _4

  local rc

   .new rc:RECT

    mov rc.left,    _1
    mov rc.top,     _2
    mov rc.right,   _3
    mov rc.bottom,  _4

    exitm<rc>
    endm

This creates a new instance for each call and can thus be used multiple times in a function call:

    Print(&Rect(1,2,3,4), &Rect(5,6,7,8))

        mov     dword ptr [rbp-10H], 1
        mov     dword ptr [rbp-0CH], 2
        mov     dword ptr [rbp-8H], 3
        mov     dword ptr [rbp-4H], 4
        mov     dword ptr [rbp-20H], 5
        mov     dword ptr [rbp-1CH], 6
        mov     dword ptr [rbp-18H], 7
        mov     dword ptr [rbp-14H], 8
        lea     rdx, [rbp-20H]       
        lea     rcx, [rbp-10H]       
        call    Print                 

From a template (same output):

.template Rect

    left    int_t ?
    top     int_t ?
    right   int_t ?
    bottom  int_t ?

    .operator Rect :abs, :abs, :abs, :abs {

      local rc

       .new rc:RECT

        mov rc.left,    _1
        mov rc.top,     _2
        mov rc.right,   _3
        mov rc.bottom,  _4

        exitm<rc>
        }
    .ends
    Print(&Rect_Rect(0, 1,2,3,4), &Rect_Rect(0, 5,6,7,8))

However, the operator is a inline function so it should return RAX here and the construction will also fail applied this way:

        lea rax,rc
        exitm<>
...
    Print(Rect(1,2,3,4), Rect(5,6,7,8))

        xor     ecx, ecx 
        mov     dword ptr [rbp], 1
        mov     dword ptr [rbp+4H], 2
        mov     dword ptr [rbp+8H], 3
        mov     dword ptr [rbp+0CH], 4
        lea     rax, [rbp]           
        xor     ecx, ecx             
        mov     dword ptr [rbp], 5   
        mov     dword ptr [rbp+4H], 6
        mov     dword ptr [rbp+8H], 7
        mov     dword ptr [rbp+0CH], 8
        lea     rax, [rbp]           
        mov     rdx, rax             
        mov     rcx, rax             
        call    Print                 


nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #271 on: May 07, 2020, 01:31:26 AM »
Added some more classes to the GDI+ headers. Most of these classes are relatively small so a static declaration may be the simplest way to initialize the class if possible. However, functions are often dedicated to initialize instances of a class and return a pointer so the constructor should handle both cases.

Many of these classes has multiple constructors so a default constructor is used in that case.
Code: [Select]
    .operator Brush {

       .new brush:ptr Brush

        mov rax,this
        .repeat
            .if rax == NULL
                .break .if !GdipAlloc(Brush)
                mov [rax].Brush.memState,Ok
            .else
                mov [rax].Brush.memState,NotImplemented
            .endif
            mov [rax].Brush.nativeBrush,NULL
            mov [rax].Brush.lastResult,NotImplemented
        .until 1
        retm<rax>
        }

A low-case name of the class is used by members to preserve the pointer (no push/pop allowed), otherwise locals are added as needed.
Code: [Select]
    .operator Release {

        mov brush,this

        GdipDeleteBrush([this].Brush.nativeBrush)

        mov this,brush
        .if [this].Brush.memState != NotImplemented

            GdipFree(this)
        .endif
        exitm<>
        }

Arguments resides in registers and RSP points to the call stack but absolute values may survive an API call.

Code: [Select]
    .operator HatchBrush :abs, :abs, :abs {

        .if Brush()

            mov brush,rax
            GdipCreateHatchBrush(_1, _2, _3, &[rax].Brush.nativeBrush)
            brush.SetStatus(eax)
            mov rax,this
        .endif
        retm <rax>
        }

The classes are tested in the ./gdiplus/class directory but only as far as to see if they assemble. Some of the Image functions are tested and I tried a few of the Draw functions.
Code: [Select]
include windows.inc
include winres.inc
include gdiplus.inc
include tchar.inc

IDM_INSERT      equ 1
IDM_DRAW        equ 2
IDM_EXIT        equ 3
IDR_MAINMENU    equ 30

    .data
    MenuId SINT 0

    .code

Insert proc hdc:HDC

    .new g:Graphics()

    .if g.FromHDC2(hdc, rax) == Ok

        .new i:ptr Image()

        i.FromFile("..\\image.png", FALSE)
        g.DrawImage(i, 10, 10)
        i.Release()
        g.Release()
    .endif
    ret

Insert endp

Draw proc hdc:HDC

    .new g:ptr Graphics()

    .if g.FromHDC2(hdc, rax) == Ok

        .new p:ptr Pen()
        .new b:ptr HatchBrush(HatchStyleCross, Goldenrod, Blue)

        p.CreatePen(DarkTurquoise, 3.0)
        g.FillRectangleI(b, 250, 50, 40, 30)
        g.DrawRectangleI(p, 250, 50, 80, 30)
        b.Release()

        p.NewPen(Red, 2.0)
        g.DrawLineI(p, 20, 10, 200, 100)
        g.DrawBezierI(p, 200, 100, 300, 80, 180, 150, 150, 10)

        p.Release()
        g.Release()
    .endif
    ret

Draw endp

WndProc proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM

  local ps:PAINTSTRUCT

    .switch edx

    .case WM_PAINT

        BeginPaint(rcx, &ps)

        .switch pascal MenuId
        .case IDM_INSERT
            Insert(ps.hdc)
        .case IDM_DRAW
            Draw(ps.hdc)
        .endsw

        EndPaint(hWnd, &ps)
        .endc

      .case WM_COMMAND
        .if !r9
            .switch r8w
            .case IDM_INSERT
                mov MenuId,IDM_INSERT
                InvalidateRect(rcx, NULL, TRUE)
                .endc
            .case IDM_DRAW
                mov MenuId,IDM_DRAW
                InvalidateRect(rcx, NULL, TRUE)
                .endc
            .case IDM_EXIT
                DestroyWindow(rcx)
                .endc
            .endsw
        .endif
        .endc

    .case WM_DESTROY
        PostQuitMessage(0)
        .endc

    .default
        .return DefWindowProc(rcx, edx, r8, r9)
    .endsw
    xor eax,eax
    ret

WndProc endp

_tWinMain proc hInstance:HINSTANCE, hPrevInstance:HINSTANCE, lpCmdLine:LPTSTR, nShowCmd:SINT

  local wc:WNDCLASSEX, msg:MSG, hwnd:HANDLE

    xor eax,eax
    mov wc.cbSize,          WNDCLASSEX
    mov wc.style,           CS_HREDRAW or CS_VREDRAW
    mov wc.cbClsExtra,      eax
    mov wc.cbWndExtra,      eax
    mov wc.hbrBackground,   COLOR_WINDOW+1
    mov wc.lpszMenuName,    IDR_MAINMENU
    mov wc.hInstance,       hInstance
    mov wc.lpfnWndProc,     &WndProc
    mov wc.lpszClassName,   &@CStr("GdiPlus")
    mov wc.hIcon,           LoadIcon(0, IDI_APPLICATION)
    mov wc.hIconSm,         rax
    mov wc.hCursor,         LoadCursor(0, IDC_ARROW)

    .ifd RegisterClassEx(&wc)

        .if CreateWindowEx(0, "GdiPlus", "GdiPlus::GdiPlus()", WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, 0)

            mov hwnd,rax

            ;; Initialize GDI+.
            .new gdiplus:ptr GdiPlus()

            ShowWindow(hwnd, SW_SHOWNORMAL)
            UpdateWindow(hwnd)

            .while GetMessage(&msg,0,0,0)
                TranslateMessage(&msg)
                DispatchMessage(&msg)
            .endw
            gdiplus.Release()
            mov rax,msg.wParam
        .endif
    .endif
    ret

_tWinMain endp

RCBEGIN
    RCTYPES 1
    RCENTRY RT_MENU
    RCENUMN 1
    RCENUMX IDR_MAINMENU
    RCLANGX LANGID_US
    MENUBEGIN
     MENUNAME "&File", MF_END
      MENUITEM IDM_INSERT, "&Insert"
      MENUITEM IDM_DRAW, "&Draw"
      SEPARATOR
      MENUITEM IDM_EXIT, "&Exit", MF_END
    MENUEND
RCEND

    end _tstart

Another problem with this concept is internal calls within the same call stack. This works for register arguments but "fails" with memory arguments.
Code: [Select]
    .operator DrawLine2 :ptr Pen, :ptr PointF, :ptr PointF {
        exitm<[this].Graphics.DrawLine(_1, [_2].PointF.X, [_2].PointF.Y, [_3].PointF.X, [_3].PointF.Y)>
        }
...
00000799  8B442428          *1   mov eax, [rsp + 5 * 8]
0000079D  89442428          *1   mov [rsp+40], eax
Well, it adds some useless code as it is now but should be possible to fix.

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #272 on: May 08, 2020, 05:17:53 AM »
Added a small fix to the floating point conversion in Asmc. In a proc only registers was tested for size and locals was guessed when immediate values was used.

    local float:real4
    local double:real8

    mov float,  4.0 ; the guess here was real8..
    mov double, 8.0

This is fixed in v2.31.27

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #273 on: May 27, 2020, 09:05:28 AM »
Added some fixup to the WATCALL calling convention debated here.

The keyword WATCALL may now be used in the same way as C or STDCALL. The calling convention is testing on the CRT math functions defined in crtl.inc. Direct calls to these are added and some inlined.

alldiv   proto watcall :qword, :qword
aulldiv  proto watcall :qword, :qword
allmul   proto watcall :qword, :qword
allrem proto watcall :qword, :qword {
    call    _I8D
    mov     eax,ebx
    mov     edx,ecx
    }
aullrem proto watcall :qword, :qword {
    call    _U8D
    mov     eax,ebx
    mov     edx,ecx
    }
allshr proto watcall :qword, :dword {
    call    _I8RS
    }
aullshr proto watcall :qword, :dword {
    call    _U8RS
    }
allshl proto watcall :qword, :dword {
    call    _U8LS
    }

INVOKE may now be used here. The one used by the Watcom compiler are declared as C functions but uses register calls.

include stdio.inc
include crtl.inc

return equ <.return>

    .code

main proc

   printf( "alldiv(10, 5): %lld\n", edx::alldiv(10, 5) )
   printf( "aulldiv(9, 5): %lld\n", edx::aulldiv(9, 5) )
   printf( "allmul(10, 5): %lld\n", edx::allmul(10, 5) )
   printf( "allrem(10, 5): %d\n", allrem(10, 5) )
   printf( "aullrem(9, 5): %d\n", aullrem(9, 5) )
   printf( "allshr(10, 2): %d\n", allshr(10, 2) )
   printf( "aullshr(9, 2): %d\n", aullshr(9, 2) )
   printf( "allshl(10, 2): %d\n", allshl(10, 2) )

   return( 0 )

main endp

    end

        mov     ebx, 5
        mov     ecx, 0
        mov     eax, 10
        mov     edx, 0
        call    alldiv_
        push    edx
        push    eax
        push    offset _DS0000
        call    _printf
        add     esp, 12
        mov     ebx, 5
        mov     ecx, 0
        mov     eax, 9
        mov     edx, 0
        call    aulldiv_
        push    edx
        push    eax
        push    offset _DS0001
        call    _printf

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #274 on: May 28, 2020, 09:03:08 PM »
Added some optimizations to WATCALL and a few bug fixes for FASTCALL and WATCALL.

In 32-bit FASTCALL the stack return was wrongly calculated. This apply to most variations using QWORD sized arguments.

    .486
    .model flat, fastcall
    .code

f88 proc a:qword, b:qword
    ret ; 8 --> 16
f88 endp

f48 proc a:dword, b:qword
    ret ; 4 --> 8
f48 endp

f84 proc a:qword, b:dword
    ret ; 4 --> 8
f84 endp

In WATCALL double register pair failed.

    .486
    .model flat, watcall

w28 proto :qword, :qword
w38 proto :qword, :qword, :qword

    .code

    invoke w28, edx::eax, ecx::ebx
    invoke w38, edx::eax, ecx::ebx, esi::edi

    end

The correct result should be:

        call    w28_
        push    esi
        push    edi
        call    w38_ 
        add     esp, 8

In JWASM using -zf1 and .model flat, fastcall

        push    ecx
        mov     ebx, ebx
        mov     ecx, ebx
        push    edx
        mov     eax, eax
        mov     edx, eax
        call    w28_
        push    esi
        push    edi
        push    ecx
        mov     ebx, ebx
        mov     ecx, ebx
        push    edx
        mov     eax, eax
        mov     edx, eax
        call    w38_
        add     esp, 8

In addition to the mov with same source and destination issue mov reg,0 is replaced with xor. It's also updated for inline functions similar to 64-bit FASTCALL so it can be used more or less in the same way. So there are now three FASTCALL conventions in 32 and 64-bit that works independently:

convention register vector  count
fastcallecx edx2
vectorcallecx edx0..56
watcalleax edx ebx ecx4
64-bit conventions
fastcallrcx rdx r8 r90..34
vectorcallrcx rdx r8 r90..56
syscallrdi rsi rdx rcx r8 r90..714