The MASM Forum

64 bit assembler => ASMC Development => Topic started by: nidud on March 28, 2018, 04:00:32 AM

Title: Object-oriented programming (OOP) in Asmc
Post by: nidud on March 28, 2018, 04:00:32 AM
This is based on the Component Object Model (COM) introduced by Microsoft in 1993. In order to access these objects some "unconventional" methods where added to the assembler. The way COM objects are accessed in this case are from the C implementation declared in the Microsoft header files. A typical class will then be like this:

typedef struct IShellFolderVtbl {
   BEGIN_INTERFACE
    HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IShellFolder * _This, REFIID riid, void **ppvObject);
    ...
   END_INTERFACE
    } IShellFolderVtbl;

interface IShellFolder {
   CONST_VTBL struct IShellFolderVtbl *lpVtbl;
    };

The assembler version:
Code: [Select]
IShellFolderVtbl struct
STDMETHOD QueryInterface, :REFIID, :ptr
...
IShellFolderVtbl ends
IShellFolder struct
lpVtbl PVOID ?
IShellFolder ends
LPIShellFolder typedef ptr IShellFolder

The objects are normally instantiated with a CreateInstance() function that takes the class as argument and we end up with a pointer or just AX. The call to the method is then something like this:

    mov rcx,rax   ; load args
    mov rdx,riid
    mov r8,ppvObject
    mov rax,[rcx] ; make the call
    call [rax].IShellFolderVtbl.QueryInterface

The assembler needs to know the type of the base class in order to use these unconventional methods.

    local s:IShellFolder
    s.QueryInterface(riid, ppvObject)
  * lea rcx,s

    local p:LPIShellFolder
    p.QueryInterface(riid, ppvObject)
  * mov rcx,p

    assume rbx:ptr IShellFolder
    [rbx].QueryInterface(riid, ppvObject)
  * mov rcx,rbx

If PROC is used inside a structure (normally an error) Asmc assumes this to be a pointer to a function. A structure member with a placeholder for strcmp/stricmp may then be defined as follow:

    Compare proc :LPSTR, :LPSTR

  * T$0001 typedef proto :LPSTR, :LPSTR
  * P$0001 typedef ptr T$0001
  * Compare P$0001 ?

The next target is a simplified class definition based on the above logic including static/external callbacks. The .CLASSDEF <name> [args] directive creates two structures and add the Vtbl pointer to the base.

.class IShellFolder

    QueryInterface      proc :REFIID, :ptr
    AddRef              proc
    Release             proc

    .ends

The first method closes the base and adds Vtbl to the name.

.class IShellFolder
  * LPISHELLFOLDER typedef ptr IShellFolder
  * LPISHELLFOLDERVtbl typedef ptr IShellFolderVtbl
  * IShellFolder::IShellFolder proto
  * IShellFolder struct
  * lpVtbl LPISHELLFOLDERVtbl ?
  * IShellFolder ends
  * IShellFolderVtbl struct 8
    QueryInterface      proc :REFIID, :ptr
  * T$000B typedef proto :ptr IShellFolder, :REFIID, :ptr
  * P$000B typedef ptr T$000B
  * QueryInterface P$000B ?
    ...
    .ends
  * IShellFolderVtbl ends

To define the Compare pointer as in the structure above locally in the base class the keyword LOCAL may be used. Locally defined functions are called directly without the _this argument omitted.

    Compare proc local :LPSTR, :LPSTR


Keywords like public could be consider later I guess, or any keyword not normally used after PROC but this in turn creates a half-baked OOP framework to exploit.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on March 29, 2018, 06:17:15 AM
Here's a class created on the stack. This one resets the stack on exit so the order in which they are deleted matters as oppose to delete everything in the end using LEAVE. Note that the start offset of the class (.base in this case) is off by 8 bytes hence the buffer is aligned 16.

Code: [Select]
;
; Build: asmc -pe -win64 $*.asm
;
include stdio.inc

    .code

.class XPush :UINT

    base    QWORD ?
    buffer  OWORD 8 dup(?)

    Release proc

    .ends

    assume rcx:LPXPUSH

    option win64:rsp nosave noauto

XPush::Release proc

    movaps xmm0,[rcx].buffer[0x00]
    movaps xmm1,[rcx].buffer[0x10]
    movaps xmm2,[rcx].buffer[0x20]
    movaps xmm3,[rcx].buffer[0x30]
    movaps xmm4,[rcx].buffer[0x40]
    movaps xmm5,[rcx].buffer[0x50]
    movaps xmm6,[rcx].buffer[0x60]
    movaps xmm7,[rcx].buffer[0x70]
    mov rax,[rsp]
    mov rsp,[rcx].base
    jmp rax

XPush::Release endp

XPush::XPush proc ReservedStack:UINT

    .repeat

        .if !rcx

            mov r8,rsp
            lea rax,[r8+rdx]        ; stack base
            lea rcx,[rdx+8*16+16]   ; alloc size
            sub rax,rcx
            and al,-16
            test [rax],al           ; probe..
            mov rcx,[r8]            ; return addr
            mov rsp,rax             ; new stack
            mov [rax],rcx
            and dl,-16              ; shadow space
            lea rcx,[rax+rdx+16]
            add r8,8
            mov [rcx].base,r8
        .endif

        movaps [rcx].buffer[0x00],xmm0
        movaps [rcx].buffer[0x10],xmm1
        movaps [rcx].buffer[0x20],xmm2
        movaps [rcx].buffer[0x30],xmm3
        movaps [rcx].buffer[0x40],xmm4
        movaps [rcx].buffer[0x50],xmm5
        movaps [rcx].buffer[0x60],xmm6
        movaps [rcx].buffer[0x70],xmm7

        lea rdx,[rax+0x80]
        mov [rcx],rdx
        lea rax,XPush@Release
        mov [rdx],rax
        mov rax,rcx
    .until 1
    ret

XPush::XPush endp

    option win64:rbp auto

main proc

  local p:LPXPUSH

    printf("rsp: %p\n", rsp)
    mov p,XPush::XPush( NULL, @ReservedStack )
    printf("rsp: %p\n", rsp)
    p.Release()
    printf("rsp: %p\n", rsp)
    ret

main endp

    end main
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on April 03, 2018, 10:44:18 PM
.COMDEF
 
Added a separate directive for COM without any types added. This is now used in the windows include files. In addition to this a default method (Release()) is added to .CLASS.

    Method1 proc local  ; static
    Method2 proc :ptr   ; first virtual function
  * Class ends
  * ClassVtbl struct
  * T$000B typedef proto :ptr Class
  * P$000B typedef ptr T$000B
  * Method2 P$000B ?

This didn't work (fixed): externdef Profile:ptr CProfile

This is indeed a good way of bundle up software where objects could be created on the fly or used as global pointers. Data may not be needed or accessed using methods so the public information may not be the same as in the local implementation.

Local definition

MAXPROFILES equ 32

PROFILE     STRUC
string      LPSTR ?
begin       dq ?
result      dq ?
PROFILE     ENDS

.class CProfile

    table PROFILE MAXPROFILES dup(<>)

    Release proc
    Start proc :UINT, :LPSTR
    Stop  proc :UINT
    Save  proc :UINT, :LPSTR
    Clear proc

    .ends

Global header

.class CProfile

    Release proc
    Start proc :dword, :ptr
    Stop  proc :dword
    Save  proc :dword, :ptr
    Clear proc

    .ends

externdef Profile:LPCPROFILE

Guess you may see this as protected or private members of the class. As for sheared members this may be done using pointers (as in COM) or inheritance for code reuse.

Two classes using the same methods:

.class CHeap BufferSize:UINT

    heap_base   PVOID ?
    heap_free   PVOID ?

    Release     proc
    Alloc       proc :UINT
    Free        proc :PVOID
    Realloc     proc :PVOID, :UINT
    Aligned     proc :UINT, :UINT
    NewString   proc :LPSTR
    Coreleft    proc

    .ends

.class CStack BufferSize:UINT, ReservedStack:UINT

    heap_base   PVOID ?
    heap_free   PVOID ?
    stack_ptr   SIZE_T ?

    Release     proc
    Alloc       proc :UINT
    Free        proc :PVOID
    Realloc     proc :PVOID, :UINT
    Aligned     proc :UINT, :UINT
    NewString   proc :LPSTR
    Coreleft    proc

    .ends

The linkage of these modules is based on usage so the instance of Profile is auto created and ready to go in the same way as the __argv table. The stack buffer (CStack) however needs to be created in a stack frame so this have to be dynamically created.

The heap functions may then be profiled as follow:
Code: [Select]
if 0 ; compare to LIBC.malloc()
include malloc.inc
else
malloc  equ <memory.Alloc>
free    equ <memory.Free>
endif

; global pointer

.class CStack :dword, :dword
    Release     proc
    Alloc       proc :dword
    Free        proc :ptr
    .ends

    .data
    memory  LPCSTACK 0
    .code

; create a one million byte stack buffer

main proc

  local stk ; create a standard stack frame

    mov memory, CStack::CStack(NULL, 1000000, @ReservedStack)
    heap_loop()
    memory.Release()

    Profile.Save(4, "CStack::CStack()")
    ret

main endp

; allocate some memory

heap_loop proc uses rsi rdi rbx r12

    Profile.Start(0, "main")

    .for (r12d=1000 : r12d : r12d-- )

        Profile.Start(1, "loop")

        .break .if !malloc( 1000 * size_t )
        mov rsi,rax

        Profile.Start(2, "malloc")
        .for ( rdi=rsi, ebx=0 : ebx < 1000 : ebx++ )

            malloc( 512 )
            stosq
        .endf
        Profile.Stop(2)

        Profile.Start(3, "free")
        .for ( rdi=rsi, ebx=0 : ebx < 1000 : ebx++ )

            lodsq
            free(rax)
        .endf
        Profile.Stop(3)

        free(rdi)
        Profile.Stop(1)
    .endf
    Profile.Stop(0)
    ret

heap_loop endp

In this case the .Save() command append result to a text file in this format:
Code: [Select]
CStack::CStack() - 2018-4-2 21:13:58:
 0:       52290 main
 1:       46500 loop
 2:        9524 malloc
 3:        8708 free
LIBC.malloc() - 2018-4-2 21:14:58:
 0:       71304 main
 1:       65418 loop
 2:       26041 malloc
 3:       16665 free

As for a more sophisticated implementation of OOP this I think will be rather clunky in assembler. It will also demand a lot of compiling in addition to all the bloat produced. The 64-bit code seems to clean up very nicely using this approach, having RCX pinned to the class. Making a habit of also returning RCX may simplify even further.

foo proc

  local p:LPCLASS

    [rcx].Method1()
    [rcx].Method2(rdx)
    [rcx].Release()
    ret

foo endp

Code produced:

   0: 55            push   rbp
   1: 48 8b ec      mov    rbp,rsp
   4: 48 83 ec 30   sub    rsp,0x30
   8: ff 51 10      call   QWORD PTR [rcx+0x10]
   b: 48 8b 01      mov    rax,QWORD PTR [rcx]
   e: ff 50 08      call   QWORD PTR [rax+0x8]
  11: 48 8b 01      mov    rax,QWORD PTR [rcx]
  14: ff 10         call   QWORD PTR [rax]
  16: c9            leave
  17: c3            ret

Title: Re: Object-oriented programming (OOP) in Asmc
Post by: Greenhorn on April 16, 2018, 06:02:10 AM
Hi nidud,

there seems to be a name conflict when creating methods with names matching from protos in other includes, e.g. windows header files.

For example, if you create a method named OpenFile or CreateFile the call to it will throw an error.

"class.inc"
Code: [Select]
include windows.inc ;// OpenFile declared in winbase.inc
include stdio.inc
include malloc.inc

.classdef Class :LPSTR

    string LPSTR ?

    Print proc
    OpenFile proc :LPTSTR ;// Class::OpenFile ( ) declaration

    .ends


"class.asm"
Code: [Select]
include class.inc

    .data
    virtual_table PVOID 0

    .code

Class::Print proc

    printf( "%s\n", [rcx].Class.string )
    ret

Class::Print endp

;/////////////////////////////////////////////////
;//  Class::OpenFile procedure/method definition
;//
Class::OpenFile proc szPath:LPTSTR

xor rax, rax
ret
Class::OpenFile endp

Class::Class proc String:LPSTR

    .repeat

        .if !rcx

            .break .if !malloc( sizeof(Class) )
            mov rcx,rax
            mov rdx,String
        .endif
        mov [rcx].Class.string,rdx
        mov rax,virtual_table
        mov [rcx].Class.lpVtbl,rax
        mov rax,rcx
    .until 1
    ret

Class::Class endp

Install proc private

    .if malloc( sizeof(ClassVtbl) )

        mov virtual_table,rax
        lea rcx,free
        mov [rax].ClassVtbl.Release,rcx
        lea rcx,Class@Print
        mov [rax].ClassVtbl.Print,rcx
    .endif
    ret
Install endp

.pragma init Install 50

    end

"test.asm"
Code: [Select]
include class.inc

    .code

main proc

  local p:LPCLASS, s:Class

    .if Class::Class( NULL, "String" )

        mov p,rax

        p.Print()
        p.OpenFile("test.inc") ;// error A2008: syntax error : p
        p.Release()
    .endif

    .if Class::Class( &s, "String2" )

        s.Print()
    .endif
    ret

main endp

    end

Output
Code: [Select]
test.asm(14) : error A2008: syntax error : p
This is because asmc assumes in the call function OpenFile from winbase.inc.


Regards
Greenhorn
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on April 16, 2018, 09:03:53 PM
there seems to be a name conflict when creating methods with names matching from protos in other includes, e.g. windows header files.

Yes. It follows the same logic as a struct members. I usually add an underscore in front of these names.

I guess it would be possible to add some exception to this but that will destroy the simplicity of it all. As of now the .COMDEF and .CLASSDEF directives only creates two structures and add members accordingly, so this will also work without these directives by simply declaring a COM object as done in C.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 25, 2018, 02:11:59 AM
Added a more portable implementation for class::member

Instead of using '@' to separate class from member functions an underscore is now used. In addition to this regular types may also be used where *this is declared directly as oppose to a pointer.

Some examples using types.

    sbyte::add proto :sbyte
    oword::add proto :oword
 * sbyte_add proto :sbyte, :sbyte
 * oword_add proto :oword, :oword


The assembler will in this case convert oword to pointers but pass bytes as value.

foo proc

  local c1:sbyte, c2:sbyte
  local o1:oword, o2:oword

    sbyte::add( c1, c2 )
    oword::add( o1, o2 )
    ret

foo endp
 20a:   55                      push   rbp
 20b:   48 8b ec                mov    rbp,rsp
 20e:   48 83 ec 50             sub    rsp,0x50
 212:   8a 4d ff                mov    cl,BYTE PTR [rbp-0x1]
 215:   8a 55 fe                mov    dl,BYTE PTR [rbp-0x2]
 218:   e8 e3 ff ff ff          call   0x200
 21d:   48 8d 4d e8             lea    rcx,[rbp-0x18]
 221:   48 8d 55 d8             lea    rdx,[rbp-0x28]
 225:   e8 e0 ff ff ff          call   0x20a
 22a:   c9                      leave
 22b:   c3                      ret


Using struct's and floats.

xword struc
    l dq ?
    h dq ?
xword ends

xword::add proto :xword
real16::add proto :real16

foo proc

  local x1:xword, x2:xword
  local r1:real16, r2:real16

    xword::add( x1, x2 )
    real16::add( r1, r2 )
    ret

foo endp
 214:   55                      push   rbp
 215:   48 8b ec                mov    rbp,rsp
 218:   48 83 ec 60             sub    rsp,0x60
 21c:   48 8d 4d f0             lea    rcx,[rbp-0x10]
 220:   48 8d 55 e0             lea    rdx,[rbp-0x20]
 224:   e8 e1 ff ff ff          call   0x20a
 229:   0f 28 45 d0             movaps xmm0,XMMWORD PTR [rbp-0x30]
 22d:   0f 28 4d c0             movaps xmm1,XMMWORD PTR [rbp-0x40]
 231:   e8 ca ff ff ff          call   0x200
 236:   c9                      leave
 237:   c3                      ret
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on August 09, 2018, 10:18:47 PM
Added some more samples to class directory (https://github.com/nidud/asmc/tree/master/source/test/class).

Using RCX as mention above cleans up very well. In the TConsole (https://github.com/nidud/asmc/blob/master/source/test/class/TConsole/test.asm) sample RCX is used for all calls to the class.
Title: Using System V methods in a class
Post by: nidud on August 29, 2018, 02:42:42 AM
The SYSCALL directive in 64-bit (in addition to the instruction) refers to the System V AMD64 calling convention. The argument passing in the AMD64 ABI uses six registers as oppose to four in the Windows ABI.

RDI RSI RDX RCX R8 R9 – syscall
        RCX RDX R8 R9 – fastcall


This is convenient for class members given RDI/RSI is preserved through Windows API calls if the class pointer resides in a register (RDI in this case). You also get additional two registers for arguments so this is a good mix. However, the :vararg logic differ in System V so this can not be used with the Windows ABI functions unless the function called is local.

A sample class using syscall methods. This from https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
Code: [Select]
.class InstructionSet syscall

    nIds            SINT ?
    nExIds          SINT ?
    vendor          LPSTR ?
    brand           LPSTR ?
    isIntel         SINT ?
    isAMD           SINT ?
    f_1_ECX         dd ?
    f_1_EDX         dd ?
    f_7_EBX         dd ?
    f_7_ECX         dd ?
    f_81_ECX        dd ?
    f_81_EDX        dd ?

    Release         proc
    GetVendor       proc
    GetBrand        proc
    GetSSE3         proc
    GetPCLMULQDQ    proc
    GetMONITOR      proc
    GetSSSE3        proc
    GetFMA          proc
    GetCMPXCHG16B   proc
    GetSSE41        proc
    GetSSE42        proc
    GetMOVBE        proc
    GetPOPCNT       proc
    GetAES          proc
    GetXSAVE        proc
    GetOSXSAVE      proc
    GetAVX          proc
    GetF16C         proc
    GetRDRAND       proc
    GetMSR          proc
    GetCX8          proc
    GetSEP          proc
    GetCMOV         proc
    GetCLFSH        proc
    GetMMX          proc
    GetFXSR         proc
    GetSSE          proc
    GetSSE2         proc
    GetFSGSBASE     proc
    GetBMI1         proc
    GetHLE          proc
    GetAVX2         proc
    GetBMI2         proc
    GetERMS         proc
    GetINVPCID      proc
    GetRTM          proc
    GetAVX512F      proc
    GetRDSEED       proc
    GetADX          proc
    GetAVX512PF     proc
    GetAVX512ER     proc
    GetAVX512CD     proc
    GetSHA          proc
    GetPREFETCHWT1  proc
    GetLAHF         proc
    GetLZCNT        proc
    GetABM          proc
    GetSSE4a        proc
    GetXOP          proc
    GetTBM          proc
    GetSYSCALL      proc
    GetMMXEXT       proc
    GetRDTSCP       proc
    Get3DNOWEXT     proc
    Get3DNOW        proc

    .ends
It's possible to override the calling convention for the member functions but the default constructor and the Release() function follows the class calling convention if set.

    Get3DNOW proc fastcall

As for the implementation of methods they need to be declared if the calling convention differ from the global default.
Code: [Select]
    .code

    option win64:noauto
    assume rdi:LPINSTRUCTIONSET

InstructionSet::GetVendor proc syscall
    mov rax,[rdi].vendor
    ret
InstructionSet::GetVendor endp

InstructionSet::GetBrand proc syscall
    mov rax,[rdi].brand
    ret
InstructionSet::GetBrand endp

__declget macro method, reg, bit
InstructionSet::Get&method& proc syscall
    mov eax,[rdi].reg
    and eax,1 shl bit
    ret
InstructionSet::Get&method& endp
    endm

__declge2 macro method, reg, bit, is
InstructionSet::Get&method& proc syscall
    bt [rdi].is,0
    sbb eax,eax
    and eax,[rdi].reg
    and eax,1 shl bit
    ret
InstructionSet::Get&method& endp
    endm

__declget SSE3,         f_1_ECX, 0
__declget PCLMULQDQ,    f_1_ECX, 1
__declget MONITOR,      f_1_ECX, 3
...

Given there's no inherent shadow space logic and thus no placeholder for arguments the names refer to the corresponding register:

    mov rax,[this].vendor

The default constructor and Release():
Code: [Select]
    option win64:rbp nosave

InstructionSet::InstructionSet proc syscall uses rbx

  local cpui[4]:SINT
  local vendor[0x20]:SBYTE
  local brand[0x40]:SBYTE
  local cpustring[512]:SBYTE

    .if malloc( InstructionSet + InstructionSetVtbl )

        assume rsi:ptr InstructionSet

        mov rsi,rax
        mov rdi,rax
        xor eax,eax
        mov ecx,InstructionSet
        rep stosb
        mov [rsi],rdi
        lea rax,InstructionSet_Release
        stosq
        for q,<Vendor,Brand,SSE3,PCLMULQDQ,MONITOR,SSSE3,FMA,CMPXCHG16B,SSE41,SSE42,MOVBE,POPCNT,\
            AES,XSAVE,OSXSAVE,AVX,F16C,RDRAND,MSR,CX8,SEP,CMOV,CLFSH,MMX,FXSR,SSE,SSE2,FSGSBASE,BMI1,\
            HLE,AVX2,BMI2,ERMS,INVPCID,RTM,AVX512F,RDSEED,ADX,AVX512PF,AVX512ER,AVX512CD,SHA,\
            PREFETCHWT1,LAHF,LZCNT,ABM,SSE4a,XOP,TBM,SYSCALL,MMXEXT,RDTSCP,3DNOWEXT,3DNOW>
            lea rax,InstructionSet_Get&q&
            stosq
            endm

        ;;
        ;; Calling __cpuid with 0x0 as the function_id argument
        ;; gets the number of the highest valid function ID.
        ;;
        mov [rsi].nIds,__cpuid(&cpui, 0)

        .for (rdi = &cpustring, ebx = 0: ebx <= [rsi].nIds: ++ebx)

            __cpuidex(&cpui, ebx, 0)
            stosd
            mov eax,cpui[4]
            stosd
            mov eax,ecx
            stosd
            mov eax,edx
            stosd
        .endf
        ;;
        ;; Capture vendor string
        ;;
        lea rdi,brand
        xor eax,eax
        mov ecx,sizeof(brand) + sizeof(vendor)
        rep stosb
        lea rax,vendor
        mov ecx,dword ptr cpustring[0x04]
        mov [rax],ecx
        mov ecx,dword ptr cpustring[0x0C]
        mov [rax+4],ecx
        mov ecx,dword ptr cpustring[0x08]
        mov [rax+8],ecx
        mov [rsi].vendor,_strdup(rax)

        .if ( !strcmp(rax, "GenuineIntel") )
            mov [rsi].isIntel,TRUE
        .elseif ( !strcmp([rsi].vendor, "AuthenticAMD") )
            mov [rsi].isAMD,TRUE
        .endif
        ;;
        ;; load bitset with flags for function 0x00000001
        ;;
        .if ([rsi].nIds >= 1)

            mov eax,dword ptr cpustring[0x10][0x8]
            mov edx,dword ptr cpustring[0x10][0xC]
            mov [rsi].f_1_ECX,eax
            mov [rsi].f_1_EDX,edx
        .endif
        ;;
        ;; load bitset with flags for function 0x00000007
        ;;
        .if ([rsi].nIds >= 7)

            mov eax,dword ptr cpustring[0x70][0x4]
            mov edx,dword ptr cpustring[0x70][0x8]
            mov [rsi].f_7_EBX,eax
            mov [rsi].f_7_ECX,edx
        .endif
        ;;
        ;; Calling __cpuid with 0x80000000 as the function_id argument
        ;; gets the number of the highest valid extended ID.
        ;;
        mov [rsi].nExIds,__cpuid(&cpui, 0x80000000)

        .fors (rdi = &cpustring, ebx = 0x80000000: ebx <= [rsi].nExIds: ++ebx)

            __cpuidex(&cpui, ebx, 0)
            stosd
            mov eax,cpui[4]
            stosd
            mov eax,ecx
            stosd
            mov eax,edx
            stosd
        .endf

        ;;
        ;; load bitset with flags for function 0x80000001
        ;;
        .if ([rsi].nExIds >= 0x80000001)

            mov eax,dword ptr cpustring[0x10][0x8]
            mov edx,dword ptr cpustring[0x10][0xC]
            mov [rsi].f_81_ECX,eax
            mov [rsi].f_81_EDX,edx
        .endif

        ;;
        ;; Interpret CPU brand string if reported
        ;;

        .if ([rsi].nExIds >= 0x80000004)

            mov rdx,rsi
            lea rdi,brand
            lea rsi,cpustring[0x20]
            mov ecx,(sizeof(cpui) * 3) / 8
            rep movsq
            mov rsi,rdx
            mov [rsi].brand,_strdup(&brand)
        .endif

        assume rsi:nothing
        mov rax,rsi
    .endif
    ret

InstructionSet::InstructionSet endp

InstructionSet::Release proc syscall
    free([rdi].vendor)
    free([rdi].brand)
    free(rdi)
    ret
InstructionSet::Release endp

Create the class and print out some information using RDI as pointer.
Code: [Select]
main proc

    mov rdi,InstructionSet::InstructionSet(NULL)
    support_message macro isa_feature, is_supported
        mov edx,' '
        .if is_supported
            mov edx,'x'
        .endif
        printf( "[%c] %s\n", edx, isa_feature )
        retm<>
        endm

    printf( "%s\n", [rdi].GetVendor() )
    printf( "%s\n", [rdi].GetBrand() )

    support_message("3DNOW",       [rdi].Get3DNOW())
    support_message("3DNOWEXT",    [rdi].Get3DNOWEXT())
    support_message("ABM",         [rdi].GetABM())
    support_message("ADX",         [rdi].GetADX())
    support_message("AES",         [rdi].GetAES())
    support_message("AVX",         [rdi].GetAVX())
    support_message("AVX2",        [rdi].GetAVX2())
    support_message("AVX512CD",    [rdi].GetAVX512CD())
    support_message("AVX512ER",    [rdi].GetAVX512ER())
    support_message("AVX512F",     [rdi].GetAVX512F())
    support_message("AVX512PF",    [rdi].GetAVX512PF())
    support_message("BMI1",        [rdi].GetBMI1())
    support_message("BMI2",        [rdi].GetBMI2())
    support_message("CLFSH",       [rdi].GetCLFSH())
    support_message("CMPXCHG16B",  [rdi].GetCMPXCHG16B())
    support_message("CX8",         [rdi].GetCX8())
    support_message("ERMS",        [rdi].GetERMS())
    support_message("F16C",        [rdi].GetF16C())
    support_message("FMA",         [rdi].GetFMA())
    support_message("FSGSBASE",    [rdi].GetFSGSBASE())
    support_message("FXSR",        [rdi].GetFXSR())
    support_message("HLE",         [rdi].GetHLE())
    support_message("INVPCID",     [rdi].GetINVPCID())
    support_message("LAHF",        [rdi].GetLAHF())
    support_message("LZCNT",       [rdi].GetLZCNT())
    support_message("MMX",         [rdi].GetMMX())
    support_message("MMXEXT",      [rdi].GetMMXEXT())
    support_message("MONITOR",     [rdi].GetMONITOR())
    support_message("MOVBE",       [rdi].GetMOVBE())
    support_message("MSR",         [rdi].GetMSR())
    support_message("OSXSAVE",     [rdi].GetOSXSAVE())
    support_message("PCLMULQDQ",   [rdi].GetPCLMULQDQ())
    support_message("POPCNT",      [rdi].GetPOPCNT())
    support_message("PREFETCHWT1", [rdi].GetPREFETCHWT1())
    support_message("RDRAND",      [rdi].GetRDRAND())
    support_message("RDSEED",      [rdi].GetRDSEED())
    support_message("RDTSCP",      [rdi].GetRDTSCP())
    support_message("RTM",         [rdi].GetRTM())
    support_message("SEP",         [rdi].GetSEP())
    support_message("SHA",         [rdi].GetSHA())
    support_message("SSE",         [rdi].GetSSE())
    support_message("SSE2",        [rdi].GetSSE2())
    support_message("SSE3",        [rdi].GetSSE3())
    support_message("SSE4.1",      [rdi].GetSSE41())
    support_message("SSE4.2",      [rdi].GetSSE42())
    support_message("SSE4a",       [rdi].GetSSE4a())
    support_message("SSSE3",       [rdi].GetSSSE3())
    support_message("SYSCALL",     [rdi].GetSYSCALL())
    support_message("TBM",         [rdi].GetTBM())
    support_message("XOP",         [rdi].GetXOP())
    support_message("XSAVE",       [rdi].GetXSAVE())
    [rdi].Release()
    ret

main endp

    end main
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on February 18, 2019, 10:46:51 PM
Hi nidud,

there seems to be a name conflict when creating methods with names matching from protos in other includes, e.g. windows header files.

For example, if you create a method named OpenFile or CreateFile the call to it will throw an error.

Made some updates to the oop parsing so this is now fixed.
Code: [Select]
bar proto

.class foo

    bar proc

    .ends

main proc

 local p:ptr foo

    p.bar()
    ret

main endp
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on March 29, 2019, 09:42:41 PM
A full sample (https://github.com/nidud/asmc/tree/master/source/test/wininc/IFileOperation) that demonstrates the use of COM form the Windows Software Development Kit (SDK)\...\Samples\WinUI\Shell\AppPlatform\FileOperations. The sample uses the strsafe.lib (https://github.com/nidud/asmc/tree/master/source/strsafe) and uuid.lib (https://github.com/nidud/asmc/tree/master/source/uuid) (updated).

Code: [Select]

STRICT_TYPED_ITEMIDS equ 1

include windows.inc      ;; Standard include
include Shellapi.inc     ;; Included for shell constants such as FO_* values
include shlobj.inc       ;; Required for necessary shell dependencies
include strsafe.inc      ;; Including StringCch* helpers

c_szSampleFileName      equ <"SampleFile">
c_szSampleFileNewname   equ <"NewName">
c_szSampleFileExt       equ <"txt">
c_szSampleSrcDir        equ <"FileOpSampleSource">
c_szSampleDstDir        equ <"FileOpSampleDestination">
c_cMaxFilesToCreate     equ 10

.code

CreateAndInitializeFileOperation proc riid:REFIID, ppv:ptr ptr

  local hr:HRESULT
  local pfo:ptr IFileOperation

    xor eax,eax
    mov [rdx],rax

    ;; Create the IFileOperation object

    mov hr,CoCreateInstance(&CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))

        ;; Set the operation flags.  Turn off  all UI
        ;; from being shown to the user during the
        ;; operation.  This includes error, confirmation
        ;; and progress dialogs.
        mov hr,pfo.SetOperationFlags(FOF_NO_UI)
        .if (SUCCEEDED(hr))

            mov hr,pfo.QueryInterface(riid, ppv)
        .endif
        pfo.Release();
    .endif
    mov eax,hr
    ret

CreateAndInitializeFileOperation endp

;;  Synopsis:  Create the source and destination folders for the sample
;;
;;  Arguments: psiSampleRoot  - Item of the parent folder where the sample folders will be created
;;             ppsiSampleSrc  - On success contains the source folder item to be used for sample operations
;;             ppsiSampleDst  - On success contains the destination folder item to be used for sample operations
;;
;;  Returns:   S_OK if successful
CreateSampleFolders proc psiSampleRoot:ptr IShellItem,
     ppsiSampleSrc:ptr ptr IShellItem, ppsiSampleDst:ptr ptr IShellItem

  local hr:HRESULT
  local pfo:ptr IFileOperation
  local psiSrc:ptr IShellItem
  local psiDst:ptr IShellItem

    xor eax,eax
    mov [rdx],rax
    mov [r8],rax
    mov psiSrc,rax
    mov psiDst,rax

    mov hr,CreateAndInitializeFileOperation(&IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))

        ;; Use the file operation to create a source and destination folder
        mov hr,pfo.NewItem(psiSampleRoot, FILE_ATTRIBUTE_DIRECTORY, c_szSampleSrcDir, NULL, NULL)
        .if (SUCCEEDED(hr))

            mov hr,pfo.NewItem(psiSampleRoot, FILE_ATTRIBUTE_DIRECTORY, c_szSampleDstDir, NULL, NULL)
            .if (SUCCEEDED(hr))

                mov hr,pfo.PerformOperations()
                .if (SUCCEEDED(hr))

                    ;; Now that the folders have been created, create items for them.  This is just an optimization so
                    ;; that the sample does not have to rebind to these items for each sample type.
                    mov hr,SHCreateItemFromRelativeName(psiSampleRoot, c_szSampleSrcDir, NULL, &IID_IShellItem, &psiSrc)
                    .if (SUCCEEDED(hr))

                        mov hr,SHCreateItemFromRelativeName(psiSampleRoot, c_szSampleDstDir, NULL, &IID_IShellItem, &psiDst)
                        .if (SUCCEEDED(hr))

                            mov rcx,ppsiSampleSrc
                            mov rax,psiSrc
                            mov [rcx],rax
                            mov rcx,ppsiSampleDst
                            mov rax,psiDst
                            mov [rcx],rax
                            ;; Caller takes ownership
                            xor eax,eax
                            mov psiSrc,rax
                            mov psiDst,rax
                        .endif
                    .endif
                    .if (psiSrc)

                        psiSrc.Release()
                    .endif
                    .if (psiDst)

                        psiDst.Release()
                    .endif
                .endif
            .endif
        .endif
    .endif
    mov eax,hr
    ret

CreateSampleFolders endp

;;  Synopsis:  Creates all of the files needed by this sample the requested known folder
;;
;;  Arguments: psiFolder  - Folder that will contain the sample files
;;
;;  Returns:   S_OK if successful
CreateSampleFiles proc uses rsi psiFolder:ptr IShellItem

  local hr:HRESULT
  local pfo:ptr IFileOperation
  local szSampleFileName[MAX_PATH]:WCHAR

    mov hr,CreateAndInitializeFileOperation(&IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))

        mov hr,StringCchPrintfW(&szSampleFileName, ARRAYSIZE(szSampleFileName), "%s.%s", c_szSampleFileName, c_szSampleFileExt)
        .if (SUCCEEDED(hr))

            ;; the file to be used for the single copy sample
            mov hr,pfo.NewItem(psiFolder, FILE_ATTRIBUTE_NORMAL, &szSampleFileName, NULL, NULL)
            ;; the files to be used for the multiple copy sample
            .for (esi = 0: SUCCEEDED(hr) && esi < c_cMaxFilesToCreate: esi++)

                mov hr,StringCchPrintfW(&szSampleFileName, ARRAYSIZE(szSampleFileName), "%s%u.%s", c_szSampleFileName, rsi, c_szSampleFileExt)
                .if (SUCCEEDED(hr))

                    mov hr,pfo.NewItem(psiFolder, FILE_ATTRIBUTE_NORMAL, &szSampleFileName, NULL, NULL)
                .endif
            .endf
            .if (SUCCEEDED(hr))

                mov hr,pfo.PerformOperations()
            .endif
        .endif
        pfo.Release()
    .endif
    mov eax,hr
    ret

CreateSampleFiles endp

;;  Synopsis:  Deletes the files/folders created by this sample
;;
;;  Arguments: psiSrc  - Source folder item
;;             psiDst  - Destination folder item
;;
;;  Returns:   S_OK if successful
DeleteSampleFiles proc psiSrc:ptr IShellItem, psiDst:ptr IShellItem

  local hr:HRESULT
  local pfo:ptr IFileOperation

    mov hr,CreateAndInitializeFileOperation(&IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))

        mov hr,pfo.DeleteItem(psiSrc, NULL)
        .if (SUCCEEDED(hr))

            mov hr,pfo.DeleteItem(psiDst, NULL)
            .if (SUCCEEDED(hr))

                mov hr,pfo.PerformOperations()
            .endif
        .endif
        pfo.Release()
    .endif
    mov eax,hr
    ret

DeleteSampleFiles endp

;;  Synopsis:  This example copies a single item from the sample source folder
;;             to the sample dest folder using a new item name.
;;
;;  Arguments: psiSrc  - Source folder item
;;             psiDst  - Destination folder item
;;
;;  Returns:   S_OK if successful
CopySingleFile proc psiSrc:ptr IShellItem, psiDst:ptr IShellItem

  local hr:HRESULT
  local pfo:ptr IFileOperation
  local szNewName[MAX_PATH]:WCHAR
  local szSampleFileName[MAX_PATH]:WCHAR
  local psiSrcFile:ptr IShellItem

    ;; Create the IFileOperation object

    mov hr,CreateAndInitializeFileOperation(&IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))

        mov hr,StringCchPrintfW(&szSampleFileName, ARRAYSIZE(szSampleFileName), "%s.%s", c_szSampleFileName, c_szSampleFileExt)
        .if (SUCCEEDED(hr))

            mov hr,SHCreateItemFromRelativeName(psiSrc, &szSampleFileName, NULL, &IID_IShellItem, &psiSrcFile)
            .if (SUCCEEDED(hr))

                mov hr,StringCchPrintfW(&szNewName, ARRAYSIZE(szNewName), "%s.%s", c_szSampleFileNewname, c_szSampleFileExt)
                .if (SUCCEEDED(hr))

                    mov hr,pfo.CopyItem(psiSrcFile, psiDst, &szNewName, NULL)
                    .if (SUCCEEDED(hr))

                        mov hr,pfo.PerformOperations()
                    .endif
                .endif
                psiSrcFile.Release()
            .endif
        .endif
        pfo.Release()
    .endif
    mov eax,hr
    ret

CopySingleFile endp

;;  Synopsis:  Creates an IShellItemArray containing the sample files to be used
;;             in the CopyMultipleFiles sample
;;
;;  Arguments: psiSrc  - Source folder item
;;
;;  Returns:   S_OK if successful
CreateShellItemArrayOfSampleFiles proc uses rsi rdi psiSrc:ptr IShellItem, riid:REFIID, ppv:ptr ptr

  local hr:HRESULT
  local psfSampleSrc:ptr IShellFolder
  local rgpidlChildren[c_cMaxFilesToCreate]:PITEMID_CHILD ;= {0};
  local szSampleFileName[MAX_PATH]:WCHAR
  local psia:ptr IShellItemArray

    xor eax,eax
    mov [r8],rax
    lea rdi,rgpidlChildren
    mov [rdi],rax

    mov hr,psiSrc.BindToHandler(NULL, &BHID_SFObject, &IID_IShellFolder, &psfSampleSrc)
    .if (SUCCEEDED(hr))

        .for (esi = 0: SUCCEEDED(hr) && esi < ARRAYSIZE(rgpidlChildren): esi++)

            mov hr,StringCchPrintfW(&szSampleFileName, ARRAYSIZE(szSampleFileName), "%s%u.%s", c_szSampleFileName, esi, c_szSampleFileExt)
            .if (SUCCEEDED(hr))

                mov hr,psfSampleSrc.ParseDisplayName(NULL, NULL, &szSampleFileName, NULL, &[rdi+rsi*8], NULL)
            .endif
        .endf
        .if (SUCCEEDED(hr))

            mov hr,SHCreateShellItemArray(NULL, psfSampleSrc, c_cMaxFilesToCreate, &rgpidlChildren, &psia)
            .if (SUCCEEDED(hr))

                mov hr,psia.QueryInterface(riid, ppv)
                psia.Release()
            .endif
        .endif
        .for (esi = 0: esi < ARRAYSIZE(rgpidlChildren): esi++)

            CoTaskMemFree([rdi+rsi*8])
        .endf
        psfSampleSrc.Release()
    .endif
    mov eax,hr
    ret

CreateShellItemArrayOfSampleFiles endp

;;  Synopsis:  This example creates multiple files under the specified folder
;;             path and copies them to the same directory with a new name.
;;
;;  Arguments: psiSrc  - Source folder item
;;             psiDst  - Destination folder item
;;
;;  Returns:   S_OK if successful
CopyMultipleFiles proc psiSrc:ptr IShellItem, psiDst:ptr IShellItem

  local hr:HRESULT
  local pfo:ptr IFileOperation
  local psiaSampleFiles:ptr IShellItemArray

    ;; Create the IFileOperation object
    mov hr,CreateAndInitializeFileOperation(&IID_IFileOperation, &pfo)
    .if (SUCCEEDED(hr))


        mov hr,CreateShellItemArrayOfSampleFiles(psiSrc, &IID_IShellItemArray, &psiaSampleFiles)
        .if (SUCCEEDED(hr))

            mov hr,pfo.CopyItems(psiaSampleFiles, psiDst)
            .if (SUCCEEDED(hr))

                mov hr,pfo.PerformOperations()
            .endif
            psiaSampleFiles.Release()
        .endif
        pfo.Release()
    .endif
    mov eax,hr
    ret

CopyMultipleFiles endp

wmain proc

  local hr:HRESULT
  local psiDocuments:ptr IShellItem
  local psiSampleSrc:ptr IShellItem
  local psiSampleDst:ptr IShellItem

    mov hr,CoInitializeEx(NULL, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE)
    .if (SUCCEEDED(hr))

        ;; Get the documents known folder.  This folder will be used to create subfolders
        ;; for the sample source and destination

        mov hr,SHCreateItemInKnownFolder(&FOLDERID_Documents, KF_FLAG_DEFAULT_PATH, NULL, &IID_IShellItem, &psiDocuments)
        .if (SUCCEEDED(hr))

            mov hr,CreateSampleFolders(psiDocuments, &psiSampleSrc, &psiSampleDst)
            .if (SUCCEEDED(hr))

                mov hr,CreateSampleFiles(psiSampleSrc)
                .if (SUCCEEDED(hr))

                    mov hr,CopySingleFile(psiSampleSrc, psiSampleDst)
                    .if (SUCCEEDED(hr))

                        mov hr,CopyMultipleFiles(psiSampleSrc, psiSampleDst)
                    .endif
                .endif
                DeleteSampleFiles(psiSampleSrc, psiSampleDst)

                psiSampleSrc.Release()
                psiSampleDst.Release()
            .endif
            psiDocuments.Release()
        .endif
        CoUninitialize()
    .endif
    xor eax,eax
    ret

wmain endp

    end
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on April 04, 2019, 02:06:26 AM
Added another sample (https://github.com/nidud/asmc/tree/master/source/test/wininc/Touch) form the (SDK)\...\Samples\Touch\MTGestures.

This is a typical .class sample from C++ using a windows touch screen interface. Mainly used to test some added math functions and WinInc updates. It basically just draw a rectangle which is resized and centred on a desktop but if you have the hardware (which i don't) you supposed to be able to pan, zoom, and rotate the object using the GestureEngine class.

Code: [Select]
        .case GID_ROTATE
            .switch (gi.dwFlags)

            .case GF_BEGIN
                mov [rsi]._dwArguments,0
                .endc

            .default
                movzx eax,gi.ptsLocation.x
                mov [rsi]._ptFirst.x,eax
                movzx eax,gi.ptsLocation.y
                mov [rsi]._ptFirst.y,eax
                ScreenToClient(hWnd, &[rsi]._ptFirst)
                mov eax,dword ptr gi.ullArguments
                cvtsi2sd xmm0,rax
                movsd xmm2,GID_ROTATE_ANGLE_FROM_ARGUMENT(xmm0)
                mov eax,[rsi]._dwArguments
                cvtsi2sd xmm0,rax
                subsd xmm2,GID_ROTATE_ANGLE_FROM_ARGUMENT(xmm0)
                [rsi].ProcessRotate(xmm2, [rsi]._ptFirst.x, [rsi]._ptFirst.y)
                InvalidateRect(hWnd, NULL, TRUE)
                mov [rsi]._dwArguments,LODWORD(gi.ullArguments)
                .endc
            .endsw
            .endc
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on April 22, 2019, 07:59:10 AM
Added a DLL COM sample (https://github.com/nidud/asmc/tree/master/source/test/comdll) using CoGetClassObject().

This needs to be manually installed (https://github.com/nidud/asmc/blob/master/source/test/comdll/install.asm) with a full path to the dll, and two new GUIDs (https://github.com/nidud/asmc/blob/master/source/test/comdll/locals.inc) needs to be generated using guidgen.exe. This is added to HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID. Ones installed the OS will load the DLL from this location and call DllGetClassObject() to get a pointer to a IClassFactory.

    .if !CoGetClassObject(&CLSID_IConfig, CLSCTX_INPROC_SERVER,
            0, &IID_IClassFactory, &class)

        .if !class.CreateInstance(0, &IID_IConfig, &config)

            config.create( "Version" )

The sample is modified from this article:
https://www.codeproject.com/Articles/13601/COM-in-plain-C

The class used is copied from the IConfig (https://github.com/nidud/asmc/tree/master/source/test/classdef/IConfig) sample. This is a .classdef with full data access which is stripped in a COM component. The user-definition is then smaller than the actual implementation.

.comdef IConfig
    QueryInterface  proc :REFIID, :ptr
    AddRef          proc
    Release         proc
    read            proc :string_t
    write           proc :string_t
    find            proc :string_t
    create          proc :string_t, :vararg
    getvalue        proc :string_t, :string_t
    delete          proc :string_t
    .ends
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on April 25, 2019, 06:44:42 AM
Added .NEW directive for .CLASS.

This assumes a default constructor named <class>::<class>() that allocate a new instance if NULL is passed or just sets the vtable if not.

Code: [Select]
.class C :byte
    atom    byte ?
    delete  proc
    get     proc
    set     proc :byte
    .ends

    .data
    vtable CVtbl { free, C_get, C_set }
    .code

    assume rcx:ptr C

C::C proc val:byte
    .repeat
        .if !rcx
            .break .if !malloc( sizeof(C) )
            mov rcx,rax
        .endif
        lea rax,vtable
        mov [rcx].lpVtbl,rax
        mov [rcx].atom,val
        mov rax,rcx
   .until 1
   ret
C::C endp

C::get proc
    movzx eax,[rcx].atom
    ret
C::get endp

C::set proc val:byte
    mov [rcx].atom,dl
    ret
C::set endp

The directive is similar to LOCAL but allows late allocation.

Code: [Select]
putval proc c:ptr C
   printf("Value: %c\n", c.get())
   ret
putval endp

main proc

    .new c:C('C')        ; C::C(&c, 1)
    putval(&c)
    .new p:ptr C('C')    ; mov p,C::C(NULL, 'C')
    putval(p)
    p.delete()

    xor eax,eax
    ret
main endp
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on December 04, 2019, 03:42:14 AM
Added : public class to .class and .comdef
Code: [Select]
.comdef IUnknown

    QueryInterface  proc :REFIID, :ptr
    AddRef          proc
    Release         proc
    .ends

.comdef IClassFactory : public IUnknown

    CreateInstance  proc :ptr, :REFIID, :ptr
    LockServer      proc :BOOL
    .ends

The stack levels are currently added by anonymous structures and not exploded. The access is direct but declaration in levels.
Code: [Select]
.class a : byte
    A   byte ?
    A   proc
.ends
.class b : public a
    B   byte ?
    B   proc
.ends
.class c : public b
    C   byte ?
    C   proc
.ends
.class d : public c
    D   byte ?
    D   proc
.ends

Access:
Code: [Select]
  local x:d

    mov x.A,a
    mov x.B,b
    mov x.C,c
    mov x.D,d

    x.A()
    x.B()
    x.C()
    x.D()
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on December 14, 2019, 11:40:38 PM
Applied the public directive to the include files and this cleans up very well, reducing the share size of it with up to 200K. As a result you end up with a lot of empty .comdefs:

.comdef IUnknown
    Release proc
    .ends
.comdef ID3D11DeviceChild : public IUnknown
    GetDevice proc :ptr
    .ends
.comdef ID3D11Resource : public ID3D11DeviceChild
    GetType proc
    .ends
.comdef ID3D11Buffer : public ID3D11Resource
    GetDesc proc :ptr
    .ends
.comdef ID3D11Texture2D : public ID3D11Buffer
    .ends

If the base class (ID3D11Texture2D in this case) was empty the Vtbl struct was not created so this is fixed in v2.31.02.

A somewhat experimental mechanism to add inline class members is added. This is an interesting combination of INVOKE and MACRO. The implementation use INVOKE to load the arguments (as normal) and if exist, Class_Method MACRO, this will be used instead. This already works for constructors using the .NEW directive and it's also possible to override a PROTO with a MACRO.

The restrictions for these macros is 64-bit and register arguments. The stack may be set/reset by INVOKE depending on flags used. Max 4 arguments (including *this) in FAST/VECTOR-CALL and 6 in SYSCALL.

.comdef IUnknown

    Release proc :qword, :qword
    .ends

; inline

IUnknown_IUnknown macro this
    exitm<>
    endm

IUnknown_Release macro this, a1, a2
    add a1,a2
    mov rax,this
    exitm<>
    endm

Note that the arguments are always 64-bit in size. First argument always used (RCX/RDI). The stack is aligned 16 and RSP points to the shadow space for the call.

    .if eax

        .new p:IUnknown()

        .if p.Release(1, [rdi])

            nop
        .endif
    .endif

The constructor just add the class as a LOCAL so no code is added there.

        test    eax, eax
        jz      ?_001   
        mov     r8, [rdi]       ; arg 3
        mov     edx, 1          ; arg 2
        lea     rcx, [rbp-8H]   ; arg 1
        add     rdx, r8         ; macro..
        mov     rax, rcx        ; macro..
        test    rax, rax         
        jz      ?_001   
        nop         
?_001:
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on December 17, 2019, 11:55:14 PM
Here's a inline version of the InstructionSet class. The whole class is now just a header so no external code needed. Note that the constructor is defined as a PROTO by the CLASS directive so UNDEF is needed there but the member functions will be resolved by INVOKE.
Code: [Select]
include intrin.inc

MAXCPUSTRING    equ 512

.class InstructionSet_Internal

    nIds        int_t ?
    nExIds      int_t ?
    isIntel     int_t ?
    isAMD       int_t ?
    union
     struct
      f_1_ECX   uint_t ?
      f_1_EDX   uint_t ?
     ends
     f_1_RAX    uint64_t ?
    ends
    union
     struct
      f_7_EBX   uint_t ?
      f_7_ECX   uint_t ?
     ends
     f_7_RAX    uint64_t ?
    ends
    union
     struct
      f_81_ECX  uint_t ?
      f_81_EDX  uint_t ?
     ends
     f_81_RAX   uint64_t ?
    ends

    .ends


.class InstructionSet : public InstructionSet_Internal

    GetVendor      proc ; getters
    GetBrand       proc
    GetSSE3        proc
    GetPCLMULQDQ   proc
    GetMONITOR     proc
    GetSSSE3       proc
    GetFMA         proc
    GetCMPXCHG16B  proc
    GetSSE41       proc
    GetSSE42       proc
    GetMOVBE       proc
    GetPOPCNT      proc
    GetAES         proc
    GetXSAVE       proc
    GetOSXSAVE     proc
    GetAVX         proc
    GetF16C        proc
    GetRDRAND      proc
    GetMSR         proc
    GetCX8         proc
    GetSEP         proc
    GetCMOV        proc
    GetCLFSH       proc
    GetMMX         proc
    GetFXSR        proc
    GetSSE         proc
    GetSSE2        proc
    GetFSGSBASE    proc
    GetBMI1        proc
    GetHLE         proc
    GetAVX2        proc
    GetBMI2        proc
    GetERMS        proc
    GetINVPCID     proc
    GetRTM         proc
    GetAVX512F     proc
    GetRDSEED      proc
    GetADX         proc
    GetAVX512PF    proc
    GetAVX512ER    proc
    GetAVX512CD    proc
    GetSHA         proc
    GetPREFETCHWT1 proc
    GetLAHF        proc
    GetLZCNT       proc
    GetABM         proc
    GetSSE4a       proc
    GetXOP         proc
    GetTBM         proc
    GetSYSCALL     proc
    GetMMXEXT      proc
    GetRDTSCP      proc
    Get3DNOWEXT    proc
    Get3DNOW       proc

    .ends

undef InstructionSet_InstructionSet ; flip the PROTO to a MACRO

InstructionSet_InstructionSet macro this ; .NEW cpu : InstructionSet()

  .new vendor[16]:char_t ; add to local stack
  .new brand[64]:char_t

    ifidn @SubStr(this,1,1),<&>
        lea rax,@SubStr(this,2)
    else
        mov rax,this
    endif
    lea     rdx,vendor

    ; new stack frame

    push    rsi
    push    rdi
    push    rbx
    push    rbp
    mov     rbp,rsp
    sub     rsp,MAXCPUSTRING+16+8
    mov     [rbp-24],rdx
    mov     rsi,rax
    assume  rsi: ptr InstructionSet

    ; get the number of the highest valid function ID.

    mov rdi,rsp
    xor r8d,r8d
    xor eax,eax
    xor ecx,ecx
    cpuid
    mov [rsi].nIds,eax

    .fors ( r9 = rdi: r8d <= [rsi].nIds: r8d++, r9 += 16 )

        mov eax,r8d
        xor ecx,ecx
        cpuid
        mov [r9+0x00],eax
        mov [r9+0x04],ebx
        mov [r9+0x08],ecx
        mov [r9+0x0C],edx
    .endf

    ; Capture vendor string

    mov rdx,[rbp-24]
    mov dword ptr [rdx+0x00],[rdi+0x04]
    mov dword ptr [rdx+0x04],[rdi+0x0C]
    mov dword ptr [rdx+0x08],[rdi+0x08]
    mov dword ptr [rdx+0x0C],0
    mov rax,[rdx]
    mov rdx,[rdx+8]
    mov rcx,"IeniuneG"
    mov rbx,"letn"
    .if ( rax == rcx && rdx == rbx )

        mov [rsi].isIntel,TRUE
    .else
        mov rcx,"itnehtuA"
        mov rbx,"DMAc"
        .if ( rax == rcx && rdx == rbx )

            mov [rsi].isAMD,TRUE
        .endif
    .endif

    ; load bitset with flags for function 0x00000001

    .if ( [rsi].nIds >= 1 )

        mov [rsi].f_1_RAX,[rdi+0x10][0x8]
    .endif

    ; load bitset with flags for function 0x00000007

    .if ( [rsi].nIds >= 7 )

        mov [rsi].f_7_RAX,[rdi+0x70][0x4]
    .endif

    ; get the number of the highest valid extended ID.

    mov r8d,0x80000000
    mov eax,r8d
    xor ecx,ecx
    cpuid
    mov [rsi].nExIds,eax

    .fors ( r9 = rdi: r8d <= [rsi].nExIds: r8d++, r9 += 16 )

        mov eax,r8d
        xor ecx,ecx
        cpuid
        mov [r9+0x00],eax
        mov [r9+0x04],ebx
        mov [r9+0x08],ecx
        mov [r9+0x0C],edx
    .endf

    ; load bitset with flags for function 0x80000001

    .if ( [rsi].nExIds >= 0x80000001 )

        mov [rsi].f_81_RAX,[rdi+0x10][0x8]
    .endif

    ; Interpret CPU brand string if reported

    .if ( [rsi].nExIds >= 0x80000004 )

        lea rsi,[rdi+0x20]
        mov rdi,[rbp-24]
        sub rdi,64
        mov ecx,3*16
        rep movsb
    .endif

    assume rsi: nothing

    leave
    pop rbx
    pop rdi
    pop rsi

    exitm<>
    endm

InstructionSet_GetVendor macro this
    lea rax,vendor
    exitm<>
    endm

InstructionSet_GetBrand macro this
    lea rax,brand
    exitm<>
    endm


InstructionSetInline macro name, reg, bit, condition
 InstructionSet_Get&name& macro this
 ifnb <condition>
     bt  [this].InstructionSet.condition,0
     sbb eax,eax
     and eax,[this].InstructionSet.reg
     and eax,1 shl bit
 else
     mov eax,[this].InstructionSet.reg
     and eax,1 shl bit
 endif
     exitm<>
     endm
    endm

InstructionSetInline SSE3,          f_1_ECX,     0
InstructionSetInline PCLMULQDQ,     f_1_ECX,     1
InstructionSetInline MONITOR,       f_1_ECX,     3
InstructionSetInline SSSE3,         f_1_ECX,     9
InstructionSetInline FMA,           f_1_ECX,    12
InstructionSetInline CMPXCHG16B,    f_1_ECX,    13
InstructionSetInline SSE41,         f_1_ECX,    19
InstructionSetInline SSE42,         f_1_ECX,    20
InstructionSetInline MOVBE,         f_1_ECX,    22
InstructionSetInline POPCNT,        f_1_ECX,    23
InstructionSetInline AES,           f_1_ECX,    25
InstructionSetInline XSAVE,         f_1_ECX,    26
InstructionSetInline OSXSAVE,       f_1_ECX,    27
InstructionSetInline AVX,           f_1_ECX,    28
InstructionSetInline F16C,          f_1_ECX,    29
InstructionSetInline RDRAND,        f_1_ECX,    30
InstructionSetInline MSR,           f_1_EDX,     5
InstructionSetInline CX8,           f_1_EDX,     8
InstructionSetInline SEP,           f_1_EDX,    11
InstructionSetInline CMOV,          f_1_EDX,    15
InstructionSetInline CLFSH,         f_1_EDX,    19
InstructionSetInline MMX,           f_1_EDX,    23
InstructionSetInline FXSR,          f_1_EDX,    24
InstructionSetInline SSE,           f_1_EDX,    25
InstructionSetInline SSE2,          f_1_EDX,    26
InstructionSetInline FSGSBASE,      f_7_EBX,     0
InstructionSetInline BMI1,          f_7_EBX,     3
InstructionSetInline HLE,           f_7_EBX,     4, isIntel
InstructionSetInline AVX2,          f_7_EBX,     5
InstructionSetInline BMI2,          f_7_EBX,     8
InstructionSetInline ERMS,          f_7_EBX,     9
InstructionSetInline INVPCID,       f_7_EBX,    10
InstructionSetInline RTM,           f_7_EBX,    11, isIntel
InstructionSetInline AVX512F,       f_7_EBX,    16
InstructionSetInline RDSEED,        f_7_EBX,    18
InstructionSetInline ADX,           f_7_EBX,    19
InstructionSetInline AVX512PF,      f_7_EBX,    26
InstructionSetInline AVX512ER,      f_7_EBX,    27
InstructionSetInline AVX512CD,      f_7_EBX,    28
InstructionSetInline SHA,           f_7_EBX,    29
InstructionSetInline PREFETCHWT1,   f_7_ECX,     0
InstructionSetInline LAHF,          f_81_ECX,    0
InstructionSetInline LZCNT,         f_81_ECX,    5, isIntel
InstructionSetInline ABM,           f_81_ECX,    5, isAMD
InstructionSetInline SSE4a,         f_81_ECX,    6, isAMD
InstructionSetInline XOP,           f_81_ECX,   11, isAMD
InstructionSetInline TBM,           f_81_ECX,   21, isAMD
InstructionSetInline SYSCALL,       f_81_EDX,   11, isIntel
InstructionSetInline MMXEXT,        f_81_EDX,   22, isAMD
InstructionSetInline RDTSCP,        f_81_EDX,   27, isIntel
InstructionSetInline 3DNOWEXT,      f_81_EDX,   30, isAMD
InstructionSetInline 3DNOW,         f_81_EDX,   31, isAMD

The main code will then be the same and the INLINE (macro) versus EXTERN (proto) issue is controlled by the header.
Code: [Select]
include stdio.inc
include tchar.inc

include InstructionSet.inc

    .data
    count int_t 0

    .code

support_message proc isa_feature:string_t, is_supported:int_t

    .if is_supported

        printf( "%-16s", isa_feature )
        inc count
    .endif

    .if count == 4

        mov count,0
        printf( "\n\t" )
    .endif
    ret

support_message endp

main proc

    .new cpu:InstructionSet()

    printf(
        "\n"
        "CPU Information\n"
        "\n"
        " Vendor: %s\n", cpu.GetVendor() )
    printf(
        " Brand:  %s\n"
        "\n"
        " Supported features:\n"
        "\n\t", cpu.GetBrand() )

    support_message("MMX",         cpu.GetMMX())
    support_message("SSE",         cpu.GetSSE())
    support_message("SSE2",        cpu.GetSSE2())
    support_message("SSE3",        cpu.GetSSE3())
    support_message("SSE4.1",      cpu.GetSSE41())
    support_message("SSE4.2",      cpu.GetSSE42())
    support_message("SSE4a",       cpu.GetSSE4a())
    support_message("SSSE3",       cpu.GetSSSE3())
    support_message("AVX",         cpu.GetAVX())
    support_message("AVX2",        cpu.GetAVX2())
    support_message("AVX512CD",    cpu.GetAVX512CD())
    support_message("AVX512ER",    cpu.GetAVX512ER())
    support_message("AVX512F",     cpu.GetAVX512F())
    support_message("AVX512PF",    cpu.GetAVX512PF())
    support_message("3DNOW",       cpu.Get3DNOW())
    support_message("3DNOWEXT",    cpu.Get3DNOWEXT())
    support_message("ABM",         cpu.GetABM())
    support_message("ADX",         cpu.GetADX())
    support_message("AES",         cpu.GetAES())
    support_message("BMI1",        cpu.GetBMI1())
    support_message("BMI2",        cpu.GetBMI2())
    support_message("CLFSH",       cpu.GetCLFSH())
    support_message("CMPXCHG16B",  cpu.GetCMPXCHG16B())
    support_message("CX8",         cpu.GetCX8())
    support_message("ERMS",        cpu.GetERMS())
    support_message("F16C",        cpu.GetF16C())
    support_message("FMA",         cpu.GetFMA())
    support_message("FSGSBASE",    cpu.GetFSGSBASE())
    support_message("FXSR",        cpu.GetFXSR())
    support_message("HLE",         cpu.GetHLE())
    support_message("INVPCID",     cpu.GetINVPCID())
    support_message("LAHF",        cpu.GetLAHF())
    support_message("LZCNT",       cpu.GetLZCNT())
    support_message("MMXEXT",      cpu.GetMMXEXT())
    support_message("MONITOR",     cpu.GetMONITOR())
    support_message("MOVBE",       cpu.GetMOVBE())
    support_message("MSR",         cpu.GetMSR())
    support_message("OSXSAVE",     cpu.GetOSXSAVE())
    support_message("PCLMULQDQ",   cpu.GetPCLMULQDQ())
    support_message("POPCNT",      cpu.GetPOPCNT())
    support_message("PREFETCHWT1", cpu.GetPREFETCHWT1())
    support_message("RDRAND",      cpu.GetRDRAND())
    support_message("RDSEED",      cpu.GetRDSEED())
    support_message("RDTSCP",      cpu.GetRDTSCP())
    support_message("RTM",         cpu.GetRTM())
    support_message("SEP",         cpu.GetSEP())
    support_message("SHA",         cpu.GetSHA())
    support_message("SYSCALL",     cpu.GetSYSCALL())
    support_message("TBM",         cpu.GetTBM())
    support_message("XOP",         cpu.GetXOP())
    support_message("XSAVE",       cpu.GetXSAVE())
    printf( "\n\n" )
    ret

main endp

    end _tstart

CPU Information

 Vendor: GenuineIntel
 Brand:  Intel(R) Core(TM) i5-6500T CPU @ 2.50GHz

 Supported features:

        MMX             SSE             SSE2            SSE3
        SSE4.1          SSE4.2          SSSE3           AVX
        AVX2            ADX             AES             BMI1
        BMI2            CLFSH           CMPXCHG16B      CX8
        ERMS            F16C            FMA             FSGSBASE
        FXSR            HLE             INVPCID         LAHF
        LZCNT           MONITOR         MOVBE           MSR
        OSXSAVE         PCLMULQDQ       POPCNT          RDRAND
        RDSEED          RDTSCP          RTM             SEP
        SYSCALL         XSAVE
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on December 29, 2019, 04:05:59 AM
Added a few extensions to the macro call and a .template directive. This is the same as CLASS and COMDEF but without any default constructor or V-table pointer.

The limits for macros is max registers in FASTCALL (4) and SYSCALL (14). Arguments are sized up according to the proto type.

.template F fastcall

    m_db db ?
    m_dw dw ?
    m_r4 real4 ?

    Init    proc :word, :byte, :real4
    .ends

.template S syscall

    m_db db ?
    m_dw dw ?
    m_dd dd ?
    m_dq dq ?
    m_r4 real4 ?

    Init    proc :word, :byte, :real4, :qword, :dword
    .ends

The arguments may then be used directly without knowing the actual value.

F_Init macro this, a, b, c          ; rcx, dx, r8b, xmm3
    assume this:ptr F
    mov [this].m_dw,a
    mov [this].m_db,b
    exitm<movss [this].m_r4,c>
    endm

S_Init macro this, a, b, c, d, e    ; rdi, si, dl, xmm0, rcx, r8d
    assume this:ptr S
    mov [this].m_dw,a
    mov [this].m_db,b
    movss [this].m_r4,c
    mov [this].m_dq,d
    exitm<mov [this].m_dd,e>
    endm

This is just regular types.

  local f:F, s:S

Arguments are parsed by the INVOKE directive.

    f.Init(s.m_dw,s.m_db,3.0)
    s.Init(f.m_dw,f.m_db,f.m_r4,4,5)

        mov     eax, 1077936128
        movd    xmm3, eax
        mov     r8b, byte ptr [rbp-20H]
        mov     dx, word ptr [rbp-1FH]
        lea     rcx, [rbp-9H]

        mov     r8d, 5
        mov     ecx, 4
        movd    xmm0, dword ptr [rbp-6H]
        mov     dl, byte ptr [rbp-9H]
        mov     si, word ptr [rbp-8H]
        lea     rdi, [rbp-20H]
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on February 21, 2020, 08:55:04 AM
Been playing with vector definitions and translated some of the header files:

- dvect.inc (https://github.com/nidud/asmc/blob/master/include/dvec.inc)

/*
 *  Definition of a C++ class interface to Intel(R) Pentium(R) 4 processor SSE2 intrinsics.
 *
 *  File name : dvec.h  class definitions
 *
 *  Concept: A C++ abstraction of Intel(R) Pentium(R) 4 processor SSE2
 *      designed to improve programmer productivity.  Speed and accuracy are
 *      sacrificed for utility.  Facilitates an easy transition to compiler
 *      intrinsics or assembly language.
 *
 */

- fvec.inc (https://github.com/nidud/asmc/blob/master/include/fvec.inc)

/*
 *  Definition of a C++ class interface to Streaming SIMD Extension intrinsics.
 *
 *
 *  File name : fvec.h  Fvec class definitions
 *
 *  Concept: A C++ abstraction of Streaming SIMD Extensions designed to improve
 *
 *  programmer productivity.  Speed and accuracy are sacrificed for utility.
 *
 *  Facilitates an easy transition to compiler intrinsics
 *
 *  or assembly language.
 *
 *  F32vec4:    4 packed single precision
 *              32-bit floating point numbers
*/

In addition a simple test file: dvec.asm (https://github.com/nidud/asmc/blob/master/source/test/dvec/test.asm)

To accomplish this a directive (.operator) is added and a new argument type (:ABS) for immediate values.

.OPERATOR [ name | OP ] [[ : args ]] [[ { ... } ]]

Arithmetic Operators

    Operator        Name  Description

    .operator +     radd  - Add
    .operator -     rsub  - Subtract
    .operator *     rmul  - Multiply
    .operator /     rdiv  - Divide
    .operator %     rmod  - Modulus
    .operator ++    rinc  - Increment
    .operator --    rdec  - Decrement

Bitwise Operators

    .operator &     rand  - Binary AND Operator
    .operator |     ror   - Binary OR Operator
    .operator ^     rxor  - Binary XOR Operator
    .operator ~     rnot  - Binary Ones Complement Operator
    .operator &~    randn - Binary AND NOT Operator
    .operator <<    rshl  - Binary Left Shift Operator
    .operator >>    rshr  - Binary Right Shift Operator

Assignment Operators

    .operator =     mequ  - Simple assignment operator
    .operator +=    madd  - Add AND assignment operator
    .operator -=    msub  - Subtract AND assignment operator
    .operator *=    mmul  - Multiply AND assignment operator
    .operator /=    mdiv  - Divide AND assignment operator
    .operator %=    mmod  - Modulus AND assignment operator
    .operator ~=    mnot  - Bitwise NOT assignment operator
    .operator <<=   mshl  - Left shift AND assignment operator
    .operator >>=   mshr  - Right shift AND assignment operator
    .operator &=    mand  - Bitwise AND assignment operator
    .operator &~=   mandn - Bitwise AND NOT assignment operator
    .operator ^=    mxor  - Bitwise exclusive OR and assignment operator
    .operator |=    mand  - Bitwise inclusive OR and assignment operator

The size of the three first parameters are added to the name.

    .operator = :qword, :dword { ; mequ84
        mov [this],_1
        retm<this>
        }

Inline functions are rendered as macros with fixed argument names. The name may hold a register, a memory location or immediate value depending on calling convention.

    class_mequ84 macro this, _1, _2
        mov [this],_1
        retm<this>
        endm

Immediate values must be defined as :ABS.

    .operator >> :abs { exitm<_mm_srli_epi64(xmm0, _1)> }

The classes are defined as templates and thus not a real class so the whole concept is a pure virtual construct. This means the size of the object is equal to the size of the vector used.

Example.

;; 1 element, a __m128i data type

.template M128
    vec __m128i <>

    .operator = :vec128_t {
        exitm<_mm_store_ps([this], _1)>
        }
    .operator __m128i {
        exitm<_mm_store_ps(xmm0, [this])>
        }
    .operator &= :vec128_t {
        _mm_and_si128(xmm0, _1)
        exitm<_mm_store_ps([this],xmm0)>
        }
    .operator |= :vec128_t {
        _mm_or_si128(xmm0, _1)
        exitm<_mm_store_ps([this],xmm0)>
        }
    .operator ^= :vec128_t {
        _mm_xor_si128(xmm0, _1)
        exitm<_mm_store_ps([this],xmm0)>
        }
    .operator & :vec128_t {
        exitm<_mm_and_si128(xmm0, _1)>
        }
    .operator | :vec128_t {
        exitm<_mm_or_si128(xmm0, _1)>
        }
    .operator ^ :vec128_t {
        exitm<_mm_xor_si128(xmm0, _1)>
        }
    .operator andnot :vec128_t {
        exitm<_mm_andnot_si128(xmm0, _1)>
        }
    .ends

Test case:

test_M128 proc v:ptr M128

    assume rcx:ptr M128

    [rcx].mequ16    (xmm1)
    [rcx].__m128i   ()
    [rcx].mand16    (xmm1)
    [rcx].mor16     (xmm1)
    [rcx].mxor16    (xmm1)
    [rcx].rand16    (xmm1)
    [rcx].ror16     (xmm1)
    [rcx].rxor16    (xmm1)
    [rcx].andnot    (xmm1)
    ret

test_M128 endp

Code produced:

        push    rbp                                     
        mov     rbp, rsp                               
        sub     rsp, 32                                 
        movaps  xmmword ptr [rcx], xmm1                 
        movaps  xmm0, xmmword ptr [rcx]                 
        pand    xmm0, xmm1                             
        movaps  xmmword ptr [rcx], xmm0                 
        por     xmm0, xmm1                             
        movaps  xmmword ptr [rcx], xmm0                 
        pxor    xmm0, xmm1                             
        movaps  xmmword ptr [rcx], xmm0                 
        pand    xmm0, xmm1                             
        por     xmm0, xmm1                             
        pxor    xmm0, xmm1                             
        pandn   xmm0, xmm1                             
        leave                                           
        ret                                             
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 08, 2020, 06:36:13 AM
A sample from the GDI+ classes.
Code: [Select]
include windows.inc
include gdiplus.inc
include tchar.inc

    .code

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

    .switch edx

    .case WM_PAINT

       .new ps:PAINTSTRUCT

        BeginPaint(rcx, &ps)

       .new G:Graphics()
        .if G.FromHDC2(ps.hdc, rax) == Ok

           .new P1:PointF(0.0, 0.0)
           .new P2:PointF(300.0, 300.0)
           .new B:LinearGradientBrush()

            B.Create(&P1, &P2, Red, Blue)
            G.FillRectangleI(&B, 0, 0, 300, 300)
            B.Release()
        .endif
        G.Release()
        EndPaint(hWnd, &ps)
        .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,    rax
    mov wc.hInstance,       hInstance
    mov wc.lpfnWndProc,     &WndProc
    mov wc.lpszClassName,   &@CStr("Gradient")
    mov wc.hIcon,           LoadIcon(0, IDI_APPLICATION)
    mov wc.hIconSm,         rax
    mov wc.hCursor,         LoadCursor(0, IDC_ARROW)

    .ifd RegisterClassEx(&wc)

        .if CreateWindowEx(0, "Gradient", "gdiplus.Graphics(Gradient)", 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

    end _tstart
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 08, 2020, 06:47:13 AM
Another one drawing a sphere.

source for the translated samples:
http://www.johnfindlay.plus.com/pellesc/GdiPlus/GdiPlus.html
Code: [Select]
include windows.inc
include gdiplus.inc
include tchar.inc

    .code

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

    .switch edx

    .case WM_PAINT

       .new ps:PAINTSTRUCT
       .new count:SINT
       .new FullTranslucent:ARGB
       .new g:Graphics()

        BeginPaint(hWnd, &ps)

        g.FromHDC(ps.hdc)

        ; Create a GraphicsPath object
       .new p:GraphicsPath()

        ; Add an ellipse to the path
        p.AddEllipse(200, 0, 200, 200)

        ; Create a path gradient based on the ellipse
       .new b:PathGradientBrush(&p)

        ; Set the middle color of the path
        b.SetCenterColor(ColorAlpha(Green, 180))

        ; Set the entire path boundary to Alpha Black using translucency
        mov count,1
        mov FullTranslucent,ColorAlpha(Black, 230)
        b.SetSurroundColors(&FullTranslucent, &count)

        ; Draw the ellipse, keeping the exact coords we defined for the path
        ; We use AntiAlias drawing mode.
        ; To get a better antialising we enlarge area (+2 and -4).
        g.SetSmoothingMode(SmoothingModeAntiAlias)
        g.FillEllipseI(&b, 200 + 2, 0 + 2, 200 - 4, 200 - 4)

        b.Release()
        p.Release()

        ; Second Sphere

       .new p:GraphicsPath()

        p.AddEllipse(200, 100, 150, 150)

       .new b:PathGradientBrush(&p)

        b.SetCenterColor(ColorAlpha(Yellow, 180))
        mov FullTranslucent,ColorAlpha(Red, 200)
        mov count,1
        b.SetSurroundColors(&FullTranslucent, &count)
        g.FillEllipseI(&b, 200 + 2, 100 + 2, 150 - 4, 150 - 4)

        b.Release()
        p.Release()
        g.Release()
        EndPaint(hWnd, &ps)
        .endc

    .case WM_DESTROY
        PostQuitMessage(0)
        .endc
    .case WM_CHAR
        .gotosw(WM_DESTROY) .if r8d == VK_ESCAPE
        .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,    rax
    mov wc.hInstance,       hInstance
    mov wc.lpfnWndProc,     &WndProc
    mov wc.lpszClassName,   &@CStr("Sphere")
    mov wc.hIcon,           LoadIcon(0, IDI_APPLICATION)
    mov wc.hIconSm,         rax
    mov wc.hCursor,         LoadCursor(0, IDC_ARROW)

    .ifd RegisterClassEx(&wc)

        .if CreateWindowEx(0, "Sphere", "gdiplus.Graphics(Sphere)", WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, 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

    end _tstart
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 13, 2020, 11:29:44 PM
Some changes added to construction of classes.

- Constructors must now be added to the class and arguments definition for the type is ignored.
- Constructors added inside a class will not be a member of the class but defined as a PROTO type.

Example:

.template template

    atom        db ?

    template    proc :ptr
    Release     proc

    .ends

    template_template proto :ptr template, :ptr

    template struct
    atom db ?
    template ends

    templateVtbl struct
    Release P$0001 ?
    templateVtbl ends

A class or comdef will add a pointer:

.class class : public template

    class proc :ptr

    .ends

    class_class proto :ptr class, :ptr

    class struct 8
    lpVtbl LPCLASSVtbl ?
    template <>
    class ends

    classVtbl struct
    templateVtbl <>
    classVtbl ends

But only if lpVtbl don't exist:

.class class1 : public class

    .operator class1 :ptr {
        exitm<class(_1)>
        }

    .ends

    class1_class1 proto :ptr class1, :ptr
    class1_class1 macro this, _1
    exitm<class(_1)>
    endm

    class1 struct 8
    class <>
    class1 ends

    class1Vtbl struct
    classVtbl <>
    class1Vtbl ends
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 23, 2020, 10:48:58 AM
Some new changes.

In the GDI+ classes (and others) you have duplicated names for constructors and other functions. The inline macros may now have unused arguments as normally is the case in macros in general. However, these arguments needs to be declared as :ABS for operators.

Default values may then be used, and input with the same arg-count but different size may share the same name. A test case for drawing a Path using a Pen with default values and AddLine for both float and integer values.

This sample draws a person: https://www.codemag.com/article/0305031

(https://www.codemag.com/Article/Image/0305031/EggerBasic_Figure3.bmp)

        Person.AddEllipse(23, 1, 14, 14)
        Person.AddLine(18, 16, 42, 16)
        Person.AddLine(50, 40, 44, 42)
        Person.AddLine(38, 25, 37, 42)
        Person.AddLine(45, 75, 37, 75)
        Person.AddLine(30, 50, 23, 75)
        Person.AddLine(16, 75, 23, 42)
        Person.AddLine(22, 25, 16, 42)
        Person.AddLine(10, 40, 18, 16)

The Pen constructor defaults to 1.0, and Path/Scale also have additional arguments.

        .new Person:GraphicsPath()
        .new p:Pen(Blue)

        g.ScaleTransform(4.0, 4.0)
        g.DrawPath(&p, &Person)
        g.ResetTransform()
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: jj2007 on May 23, 2020, 08:19:14 PM
Very nice, and thanks for the inspiration (http://masm32.com/board/index.php?topic=6483.msg93685#msg93685) :thumbsup:
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 26, 2020, 06:16:01 AM
Some new additions to the MACRO/PROC merger.

proc  :vararg
macro :vararg
...
    .operator :vararg {
        exitm<>
        }

This combination is difficult to reason with so the macro needs the actual arg-list here. Invoke will skip loading arguments when the vararg start so in this case only RCX will be loaded. The macro receives the list but skips the ADDR prefix of the class, so this assumes a reference to a static class.

    Pen proc :vararg
    Pen_Pen macro this, _1, _2:=<1.0>, _3:=<0>
        ifb <_1>
            this.Pen0()
        elseif typeof(_1) eq 2
            this.Pen1(_1, _2, _3, rcx)
        else
            this.Pen2(_1, _2)
        endif
        lea rax,this
        exitm<>
        endm

typeof() will now accept an ADDR prefix so typeof(_1) --> typeof(addr p) = 8.
Note that this above is not a pointer here but a reference to the actual class. This means you dont need to save it between calls. The list is passed as a regular macro list.

    echo this
    for arg,<_1>
        echo arg
        endm

The GDI+ sample (https://github.com/nidud/asmc/blob/master/source/test/gdiplus/Person/test.asm) above is now written as a test for this concept, using static objects, and it cleans up very well. The code size for the GDI part is 910, total of 1389 byte. So, easy to use with minimum overhead.

Very nice, and thanks for the inspiration (http://masm32.com/board/index.php?topic=6483.msg93685#msg93685) :thumbsup:

 :biggrin:

Nice to inspire so maybe consider using a modular library too?

The code should be less than 5K but is now above 50K.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 29, 2020, 04:04:14 AM
Some changes made to inline functions and templates.

An inline function will not generate any stack frame unless needed. This in case the procedure do not allocate any stack.

stack   proto :byte, :byte, :byte, :byte, :byte { exitm<> }
nostack proto :byte, :byte, :byte, :byte { exitm<> }

Operators added to the class structure follows a strict naming logic which is difficult to figure out by the assembler given you will need the proto type in order to do something meaningful in an expression.

A simplified template based on types will enable parsing of such expressions. The logic is to assign *this to the logical vector (AL to ZMM0) based on the type used.

    float typedef real4

.template float vectorcall

    .operator = :float {
        movss   this,_1
        retm    <this>
        }
    .operator + :float {
        addss   this,_1
        retm    <this>
        }
    .operator - :float {
        subss   this,_1
        retm    <this>
        }
    .operator / :float {
        divss   this,_1
        retm    <this>
        }
    .operator * :float {
        mulss   this,_1
        retm    <this>
        }
    .operator == :float {
        comiss  this,_1
        retm    <this>
        }
    .operator ++ { exitm<float::add(this, 1.0)> }
    .operator -- { exitm<float::sub(this, 1.0)> }
    .ends

There is no decoration here so the name is add.

    float :: add ( xmm0, 1.0 )

The operator call skips *this.

    float :: + ( 1.0 )

And the parsing is recursive.

    float :: = ( xmm1 ) + ( a ) / ( b ) * ( c ) == ( 3.0 )

        movss   xmm0, xmm1         
        movd    xmm1, dword ptr [a]
        addss   xmm0, xmm1         
        movd    xmm1, dword ptr [_b]
        divss   xmm0, xmm1         
        movd    xmm1, dword ptr [c]
        mulss   xmm0, xmm1     
        mov     eax, 1077936128
        movd    xmm1, eax
        comiss  xmm0, xmm1

Using absolute values (:ABS) instead of float:

        movss   xmm0, xmm1         
        addss   xmm0, dword ptr [a]
        divss   xmm0, dword ptr [_b]
        mulss   xmm0, dword ptr [c]
        mov     eax, 1077936128
        movd    xmm1, eax
        comiss  xmm0, xmm1


A small update.

The exitm<> is now automatically added if RETM or EXITM is not the last token and brackets are not needed for the recursive parsing unless there are more than one token.

Example

    ostream typedef ptr

.template ostream

    .operator << :ptr {}

    .ends
    cout equ <ostream::>

    cout << "string" << "string2" << "string3"

The vector type is located in the CPU and PTR adapts to push size. The argument array therefor starts with _1 and this* added to the end with a default value of the vector.

    ostream_shl proto :ptr
    ostream_shl macro _1, this:=<rax>
    exitm<>
    endm

So RCX will be the first argument here (_1) and this* the return type.

*    lea rcx, DS0000
*    lea rcx, DS0001
*    lea rcx, DS0002
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on May 30, 2020, 02:21:32 AM
So to continue with this construct the problem is the method selection based on arguments where multiple function share the same name. It's a difficult task, especially in assembler given registers may be used as input so you will at best be able to approximate based on count and size. Problems get solved in general by naming them so using types is the real solution here.

Types may be sorted internally by the assembler so I added a macro that names types as follows.

    typeid( [ type, ] expression )

Immediate values:

    imm_32          ?i32
    imm_64          ?i64
    imm_128         ?i128
    imm_float       ?flt

Registers:

    reg_8           ?r8
    reg_16
    reg_32
    reg_64
    reg_128
    reg_256
    reg_512

Basic types:

    mem_byte        ?byte
    mem_sbyte
    mem_word
    mem_sword
    mem_real2
    mem_dword
    mem_sdword
    mem_real4
    mem_fword
    mem_qword
    mem_sqword
    mem_real8
    mem_tbyte
    mem_real10
    mem_oword
    mem_real16
    mem_yword
    mem_zword
    mem_proc
    mem_near
    mem_far

Basic pointers:

    ptr_byte        ?pbyte
    ptr_sbyte
    ...
    ptr_far
    ptr_ptr
    ptr_void

So this will enable type-based selection.

    ostream typedef ptr
    cout    equ <ostream::>

.template ostream

    .operator ptr_sbyte :ptr sbyte {
        mov rax,_1
        }
    .operator ptr_word :ptr word {
        mov rax,_1
        }
    .operator << :abs {
        cout typeid(_1)(_1)
        }
    .ends

    cout << "Ascii string" << ( L"Unicode string" )

In the regression test (https://github.com/nidud/asmc/blob/master/source/asmc/regress/src/bin/2.31.38.asm) the basic types are enumerated. User types expand in the same way:

types proto :vararg {
    for arg,<this>
%       echo typeid(arg)
        endm
        }

  local rc:RECT

    types( rc, addr rc )

mem_RECT
ptr_RECT

EDIT:

Optional argument.

    .operator ostream?pRECT :ptr RECT {
        mov eax,[_1].RECT.top
        }
    .operator ostream?psbyte :ptr sbyte {
        mov rax,_1
        }
    .operator ostream?pword :ptr word {
        mov rax,_1
        }
    .operator << :abs {
        cout typeid(ostream, _1)(_1)
        }

This may extend: typeid(ostream, _1)typeid(?, _2)(_1, _2)
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 04, 2020, 12:05:16 AM
Did some more testing using the GDI+ classes.

Constructors:

    LinearGradientBrush(PointF*, PointF*, ARGB, ARGB)
    LinearGradientBrush(Point*, Point*, ARGB, ARGB)
    LinearGradientBrush(RectF*, ARGB, ARGB, LinearGradientMode)
    LinearGradientBrush(Rect*, ARGB, ARGB, LinearGradientMode)
    LinearGradientBrush(RectF*, ARGB, ARGB, REAL, BOOL isAngleScalable = FALSE)
    LinearGradientBrush(Rect*, ARGB, ARGB, REAL, BOOL isAngleScalable = FALSE)
...
   .new p:LinearGradientBrush(pPointF, pPointF, argb, argb)
   .new p:LinearGradientBrush(pPoint, pPoint, argb, argb)
   .new p:LinearGradientBrush(pRectF, argb, argb, 0)
   .new p:LinearGradientBrush(pRect, argb, argb, 0)
   .new p:LinearGradientBrush(pRectF, argb, argb, 0.0)
   .new p:LinearGradientBrush(pRectF, argb, argb, 0.0, 0)
   .new p:LinearGradientBrush(pRect, argb, argb, 0.0)
   .new p:LinearGradientBrush(pRect, argb, argb, 0.0, 0)

Functions:

    DrawLine(Pen*, REAL, REAL, REAL, REAL)
    DrawLine(Pen*, PointF*, PointF*)
    DrawLine(Pen*, INT, INT, INT, INT)
    DrawLine(Pen*, Point*, Point*)
...
    p.DrawLine(pPen, 0.0, 0.0, 0.0, 0.0)
    p.DrawLine(pPen, pPointF, pPointF)
    p.DrawLine(pPen, 0, 0, 0, 0)
    p.DrawLine(pPen, ebx, ebx, ebx, ebx)
    p.DrawLine(pPen, pPoint, pPoint)

Added some new Graphics samples (https://github.com/nidud/asmc/blob/master/source/test/gdiplus/Graphics/):

   .new g:Graphics(hdc)
   .new p:PointF(20.0, 20.0)
   .new b:SolidBrush(Green)
   .new f:Font(L"Arial", 16.0)

    g.DrawString(L"Sample Text", 11, &f, &p, NULL, &b)

    b.Release()
    f.Release()
    g.Release()

A simplified Bitmap sample:

    .data
    hBitmap HBITMAP 0

    .code

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

    .switch edx

    .case WM_CREATE

       .new gdiplus:GdiPlus()
       .new bitmap:Bitmap(L"image.png")

        bitmap.GetHBITMAP(0, &hBitmap)
        bitmap.Release()
        gdiplus.Release()
        .endc

Bitmap class constructors:

    Bitmap(WCHAR*, BOOL = FALSE)
    Bitmap(IStream*, BOOL = FALSE)
    Bitmap(INT, INT, INT, PixelFormat, BYTE*)
    Bitmap(INT, INT, PixelFormat = PixelFormat32bppARGB)
    Bitmap(INT, INT, Graphics*)
    Bitmap(IDirectDrawSurface7*)
    Bitmap(BITMAPINFO*, VOID*)
    Bitmap(HBITMAP, HPALETTE)
    Bitmap(HICON)
    Bitmap(HINSTANCE, WCHAR*)
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 05, 2020, 08:33:33 AM
The recursive parsing of types from the leftmost parameter is done with VARARG expansion so this (inline functions using VARARG) skips loading the first argument (this*) given it is hidden, to prevent reloading. The rest of the array is optional but should be virtual if possible until the final call.

Here the base is static and all TextureBrush() functions starts with the same argument (Image*) so this is loaded at the first level.

    .operator TextureBrush :ptr Image, :abs=<WrapModeTile>, :abs, :vararg {
        mov this.nativeBrush,NULL
        mov rcx,rdx
        ifnb <_3>
            this.typeid(TextureBrush, _2)(rdx, _2, _3, _4)
        else
            GdipCreateTexture([rcx].Image.nativeImage, _2, addr this.nativeBrush)
        endif
        mov this.lastResult,eax
        }

Here all the rest is loaded before the call.

    .operator TextureBrush?pRectF :abs, :ptr RectF, :ptr ImageAttributes, :vararg {
        xor edx,edx
        .if r9
            mov rdx,[r9].ImageAttributes.nativeImageAttr
        .endif
        GdipCreateTextureIA([rcx].Image.nativeImage, rdx,\
                            [r8].RectF.X,\
                            [r8].RectF.Y,\
                            [r8].RectF.Width,\
                            [r8].RectF.Height,\
                            addr this.nativeBrush)
        }

This continues without loading.

    .operator TextureBrush?i32 :abs, :abs, :abs, :vararg {
        this.typeid(TextureBrush?i32, _3)(_1, _2, _3, _4)
        }

Here the array (minus the first arg) is passed directly.

    .operator TextureBrush?i32?flt :abs, :vararg {
        GdipCreateTexture2([rcx].Image.nativeImage, _2, addr this.nativeBrush)
        }

In this case real4 and r128 could be solved by EQU, but these calls do not generate any code.

    .operator TextureBrush?i32?real4 :vararg {
        this.TextureBrush?i32?flt(_1)
        }
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 05, 2020, 09:40:10 AM
Most of the GDI+ classes are now somewhat functional and a few more working samples are made. There's a simple parsing test made for all these classes but live samples are needed to see how this plays out.

The Graphics test is done in WM_PAINT with a macro after BeginPaint().

OnPaint macro hdc
    .new g:Graphics(hdc)
    ...
    g.Release()

The size of these classes are rounded up to 16-byte and holds the native pointer and status value. The latter is 4 byte so a scratch value is added. This holds return values like float, BOOL, and other integer sized necessities.

.template Region

    nativeRegion    ptr_t ?
    lastResult      Status ?
    scratch         int_t ?
...
    .operator GetHRGN :ptr Graphics, :vararg {
        GdipGetRegionHRgn(this.nativeRegion, [rdx].Graphics.nativeGraphics, addr this.scratch)
        this.SetStatus()
        mov eax,this.scratch
        }

This is all stack work so the bloat factor is relatively low. Inserting an image will create the following code:

    .new i:Image(L"image.png")

    g.DrawImage(&i, 100.0, 100.0, 60.0, 50.0, 150.0, 48.0, UnitPixel)
    i.Release()
...
        mov     qword ptr [rbp-68H], 0   ; .new i:Image()
        lea     rdx, [DS0000]         
        mov     rcx, rdx             
        lea     rdx, [rbp-68H]       
        call    GdipLoadImageFromFile
        mov     dword ptr [rbp-60H], eax ; status

        lea     rdx, [rbp-68H]           ; g.DrawImage()
        test    rdx, rdx   
        jz      ?_002       
        mov     rdx, qword ptr [rdx]
?_002:  mov     dword ptr [rsp+40H], 2
        mov     dword ptr [rsp+38H], 1111490560
        mov     dword ptr [rsp+30H], 1125515264
        mov     dword ptr [rsp+28H], 1112014848
        mov     dword ptr [rsp+20H], 1114636288
        movd    xmm3, dword ptr [F0000]
        movd    xmm2, dword ptr [F0000]
        mov     rcx, qword ptr [rbp-58H]
        call    GdipDrawImagePointRect
        test    eax, eax                 ; update status on error..
        cmove   eax, dword ptr [rbp-50H]
        mov     dword ptr [rbp-50H], eax

        mov     rcx, qword ptr [rbp-68H] ; image.Release()
        call    GdipDisposeImage       

A Bitmap is derived Image class and holds more diverse input, so the DrawImage() function apparently also accept a bitmap as input:

    .new hIcon:HICON

    .if ExtractIcon(hWnd, @CatStr(<!">, @Environ(HOMEDRIVE),<!">) "\\Windows\\regedit.exe", 2)

        mov hIcon,rax

        .new b:Bitmap(hIcon)

        g.DrawImage(&b, 200.0, 100.0, 0.0, 0.0, 150.0, 48.0, UnitPixel)
        b.Release()
        DestroyIcon(hIcon)
    .endif

There are some 20 more test samples attached.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 06, 2020, 10:17:25 AM
Test case for the Metafile class.

https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-metafiles-about

Create a Metafile:
Code: [Select]
    .case WM_PAINT

        BeginPaint(rcx, &ps)

       .new m:Metafile(L"MyDiskFile.emf", ps.hdc)
       .new g:Graphics(&m)

        g.SetSmoothingMode(SmoothingModeAntiAlias)
        g.RotateTransform(30.0)

       .new p:GraphicsPath()

        p.AddEllipse(0, 0, 200, 100)

       .new r:Region(&p)

        g.SetClip(&r)

       .new b:Pen(Blue)

        g.DrawPath(&b, &p)

        .for ebx = 0: ebx <= 300: ebx += 10

            mov eax,300
            sub eax,ebx
            g.DrawLine(&b, 0, 0, eax, ebx)
        .endf

        g.Release()
        m.Release()
        EndPaint(hWnd, &ps)
View the file:
Code: [Select]
    .case WM_PAINT

        BeginPaint(rcx, &ps)

       .new g:Graphics(rax)
       .new i:Image(L"MyDiskFile.emf")

        g.DrawImage(&i, 10, 10)

        i.Release()
        g.Release()

        EndPaint(hWnd, &ps)
        .endc
Should look something like this (with black background):
(https://docs.microsoft.com/en-us/windows/win32/gdiplus/images/aboutgdip05-art00.png)
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 08, 2020, 04:14:13 AM
Test case for the Effect class.

https://docs.microsoft.com/en-us/windows/win32/api/gdipluseffects/nl-gdipluseffects-effect

The Effect class serves as a base class for eleven classes that you can use to apply effects and adjustments to bitmaps. The following classes descend from Effect.

    Blur
    Sharpen
    Tint
    RedEyeCorrection
    ColorMatrixEffect
    ColorLUT
    BrightnessContrast
    HueSaturationLightness
    ColorBalance
    Levels
    ColorCurve

Code: [Select]
    .case WM_PAINT

        BeginPaint(rcx, &ps)

       .new g:Graphics(rax)
       .new i:Image(L"Photograph.jpg")
       .new r:RectF
       .new m:Matrix(1.0, 0.0, 0.0, 1.0, 20.0, 280.0)
       .new P:ColorBalanceParams
       .new E:ColorBalance()

        mov r.X,0.0
        mov r.Y,0.0
        i.GetWidth()
        cvtsi2ss xmm0,eax
        movss r.Width,xmm0
        i.GetHeight()
        cvtsi2ss xmm0,eax
        movss r.Height,xmm0

        ;; Integer in the range -100 through 100 that specifies a change in the
        ;; amount of red in the image. If the value is 0, there is no change.
        mov P.cyanRed,-60
        ;; Integer in the range -100 through 100 that specifies a change in the
        ;; amount of green in the image. If the value is 0, there is no change.
        mov P.magentaGreen,40
        ;; Integer in the range -100 through 100 that specifies a change in the
        ;; amount of blue in the image. If the value is 0, there is no change.
        mov P.yellowBlue,-50

        E.SetParameters(&P)

        ;; Draw the image with no change.
        g.DrawImage(&i, 20.0, 20.0, r.Width, r.Height)

        ;; Draw the image with applied Effect
        g.DrawImage(&i, &r, &m, &E, NULL, UnitPixel)

        i.Release()
        g.Release()
        EndPaint(hWnd, &ps)
        .endc
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 10, 2020, 04:49:05 AM
The ImageAttributes class.

https://docs.microsoft.com/en-us/windows/win32/api/gdiplusimageattributes/nl-gdiplusimageattributes-imageattributes

An ImageAttributes object contains information about how bitmap and metafile colors are manipulated during rendering. An ImageAttributes object maintains several color-adjustment settings, including color-adjustment matrices, grayscale-adjustment matrices, gamma-correction values, color-map tables, and color-threshold values.

Code: [Select]

include windows.inc
include gdiplus.inc
include tchar.inc

    .code

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

  local ps:PAINTSTRUCT

    .switch edx

    .case WM_PAINT

        BeginPaint(rcx, &ps)

       .new g:Graphics(rax)

       ;; Create a palette that has four entries.

       .new p:ptr ColorPalette(4)
        mov rcx,rax
        [rcx].ColorPalette.SetPalette(0, Aqua)
        [rcx].ColorPalette.SetPalette(1, White)
        [rcx].ColorPalette.SetPalette(2, Red)
        [rcx].ColorPalette.SetPalette(3, Green)

        ;; Display the four palette colors with no adjustment.

       .new b:SolidBrush(Black)

        .for (esi = 0: esi < 4: ++esi)

            p.GetPalette(rsi)
            b.SetColor(eax)

            imul r8d,esi,30
            add  r8d,20

            g.FillRectangle(&b, r8d, 20, 20, 20)
        .endf

        ;; Create a remap table that converts green to blue.

       .new map:ColorMap
        mov map.oldColor,Green
        mov map.newColor,Blue

        ;; Create an ImageAttributes object, and set its bitmap remap table.

       .new imAtt:ImageAttributes()

        imAtt.SetRemapTable(1, &map, ColorAdjustTypeBitmap)

        ;; Adjust the palette.

        imAtt.GetAdjustedPalette(p, ColorAdjustTypeBitmap)

        ;; Display the four palette colors after the adjustment.

        .for (esi = 0: esi < 4: ++esi)

            p.GetPalette(rsi)
            b.SetColor(eax)

            imul r8d,esi,30
            add  r8d,20

            g.FillRectangle(&b, r8d, 50, 20, 20)
        .endf

        p.Release()
        g.Release()
        EndPaint(hWnd, &ps)
        .endc

    .case WM_DESTROY
        PostQuitMessage(0)
        .endc
    .case WM_CHAR
        .gotosw(WM_DESTROY) .if r8d == VK_ESCAPE
        .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_ACTIVEBORDER
    mov wc.lpszMenuName,    rax
    mov wc.hIcon,           rax
    mov wc.hIconSm,         rax
    mov wc.hInstance,       rcx
    mov wc.lpfnWndProc,     &WndProc
    mov wc.lpszClassName,   &@CStr("GetAdjustedPalette")
    mov wc.hCursor,         LoadCursor(0, IDC_ARROW)

    .ifd RegisterClassEx(&wc)

        .if CreateWindowEx(0, "GetAdjustedPalette", "GetAdjustedPalette", WS_OVERLAPPEDWINDOW,
                100, 80, 400, 160, NULL, NULL, hInstance, 0)

            mov hwnd,rax

           ;; Initialize GDI+.
           .new gdiplus: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

    end _tstart
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 14, 2020, 11:58:40 AM
New sample
WIC Viewer GDI+ (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/wic/wicviewergdiplus)

This code sample shows a Windows application using WIC to decode an image file and GDI+ to render the image to the screen. 

Files
WICViewerGDIPlus.inc (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicviewergdiplus/WicViewerGdiPlus.inc): Header file that declares application class DemoApp interface
WICViewerGDIPlus.asm (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicviewergdiplus/WicViewerGdiPlus.asm): Implementation of the application class interface

Building the Sample
To build the sample using the command prompt:
asmc64 -pe -ws -gui WICViewerGDIPlus.asm

Running the Sample
After the application is launched, load an image file through file open menu. Window resizing is supported.

Changes
The @CStr() macro now handles zero terminated strings. It also accept additional L"" on split strings but only the first one is valid.
Code: [Select]
DemoApp::LocateImageFile proc hWnd:HWND, pszFileName:LPWSTR, cchFileName:DWORD

    local ofn:OPENFILENAME

    mov word ptr [r8],0
    ZeroMemory(&ofn, sizeof(ofn))

    mov ofn.lStructSize,  sizeof(ofn)
    mov ofn.hwndOwner,    hWnd
    mov ofn.lpstrFilter,  &@CStr(
        L"All Image Files\0"               L"*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico\0"
        L"Windows Bitmap\0"                L"*.bmp;*.dib\0"
        L"High Definition Photo\0"         L"*.wdp;*.mdp;*.hdp\0"
        L"Graphics Interchange Format\0"   L"*.gif\0"
        L"Portable Network Graphics\0"     L"*.png\0"
        L"JPEG File Interchange Format\0"  L"*.jpg;*.jpeg\0"
        L"Tiff File\0"                     L"*.tif\0"
        L"Icon\0"                          L"*.ico\0"
        L"All Files\0"                     L"*.*\0" )
    mov ofn.lpstrFile,    pszFileName
    mov ofn.nMaxFile,     cchFileName
    mov ofn.lpstrTitle,   &@CStr(L"Open Image")
    mov ofn.Flags,        OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST

    ;; Display the Open dialog box.
    GetOpenFileName(&ofn)
    ret

DemoApp::LocateImageFile endp
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 15, 2020, 02:13:32 PM
WIC Animated Gif (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/wic/wicanimatedgif)

WIC added support for reading/writing GIF metadata which is essential for rendering Animated GIFs.

This code sample is a simple Windows application that demonstrates decoding various frames in an GIF file, reading appropriate metadata for each frame, composing frames, and rendering the animation with Direct2D.

Files
sample.gif: sample gif file
WICAnimatedGif.inc (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicanimatedgif/WICAnimatedGif.inc): Header file that declares application class DemoApp interface
WICAnimatedGif.asm (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicanimatedgif/WicAnimatedGif.asm): Implementation of the application class interface
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: LiaoMi on June 15, 2020, 08:34:23 PM
Hi nidud,

very cool examples, thanks for the work!  :thumbsup:
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 20, 2020, 03:33:39 AM
IAmsiStream interface sample (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/AmsiStream)

Demonstrates how to use the Antimalware Scan Interface to scan a stream.

The sample implements the
IAmsiStream interface (https://msdn.microsoft.com/en-us/library/windows/desktop/dn889589(v=vs.85).aspx) so that an antimalware provider can use it to scan the contents of a stream.

The sample demonstrates a stream where the data comes from a file
and a stream where the data comes from an in-memory buffer.

Files
AmsiStream.inc (https://github.com/nidud/asmc/blob/master/source/test/wininc/AmsiStream/AmsiStream.inc): Header file
AmsiStream.asm (https://github.com/nidud/asmc/blob/master/source/test/wininc/AmsiStream/AmsiStream.asm): Implementation

Note: this needs Windows 10
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 20, 2020, 03:53:28 AM
printf versus cout

Files
https://github.com/nidud/asmc/tree/master/source/test/class/cout

Using cout:
Code: [Select]
    cout << "Ascii string"        << endl
    cout << ( L"Unicode string" ) << endl
    cout << endl

    cout << "signed char    (-1): " << signed_char  << endl
    cout << "signed short   (-1): " << signed_short << endl
    cout << "signed int     (-1): " << signed_int   << endl
    cout << "signed int64   (-1): " << signed_int64 << endl
    cout << "unsigned char  (-1): " << bl           << endl
    cout << "unsigned short (-1): " << bx           << endl
    cout << "unsigned int   (-1): " << ebx          << endl
    cout << "unsigned int64 (-1): " << rbx          << endl
    cout << endl

Using printf:
Code: [Select]
    printf( "Ascii string\n" )
    wprintf( L"Unicode string\n\n" )

    printf( "signed char    (-1): %d\n", signed_char )
    printf( "signed short   (-1): %d\n", signed_short )
    printf( "signed int     (-1): %d\n", signed_int )
    printf( "signed int64   (-1): %d\n", signed_int64 )
    printf( "unsigned char  (-1): %u\n", bl )
    printf( "unsigned short (-1): %u\n", bx )
    printf( "unsigned int   (-1): %u\n", ebx )
    printf( "unsigned int64 (-1): %llu\n\n", rbx )


cout.exe   7680 byte
stdio.exe 26624 byte

Note that this only apply to static linking using the Asmc LIBC-64.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 20, 2020, 06:34:11 AM
Added assignment of struct members. This apply to structs with default constructors (templates) and works with the .new directive.

Examples using the RECT structure (https://github.com/nidud/asmc/blob/master/include/windef.inc#L257). This also allow direct assignment.

    RECT()
    RECT(1, 2, 3)
    RECT() : left   (100),
             top    (200),
             right  (640),
             bottom (480)

    foo( RECT(100, 200, 640, 480) )

   .new RECT()        : right(1)
   .new rc:RECT()     : right(2)
   .new rp:ptr RECT() : right(3)

    rp.Clear()
    rp.Init(1, 2)

Note that the way a new RECT is constructed here there will only be one instance created (named __rc) except from rc.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on June 21, 2020, 01:07:23 AM
Added a small bug-fix to the MacroLocals (https://github.com/nidud/asmc/commit/aee00fc9bea4e5c55ae3241560ad7c0d5f9b441d) counter. This is now reset between passes so the local label may now be used as a stable id.

Example

.class T

    x dd ?
    y dd ?

    .operator T :abs=<0>, :abs=<0>, :vararg {
      local n
       .new n:T
        mov n.x,_1
        mov n.y,_2
        lea rax,n
        }
    .ends

main proc

    T()
    T(1, 2)
    ret

main endp

Label names:

  ??0000 T rbp - 0010
  ??0001 T rbp - 0020

Code produced:

        push    rbp
        mov     rbp, rsp
        sub     rsp, 64
        mov     dword ptr [rbp-8H], 0
        mov     dword ptr [rbp-4H], 0
        lea     rax, [rbp-10H]
        mov     dword ptr [rbp-18H], 1
        mov     dword ptr [rbp-14H], 2
        lea     rax, [rbp-20H]
        leave
        ret
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on July 03, 2020, 08:55:33 AM
Assignment of members based on input.

The main reason for these constructs is to optimize code based on type. The typeid operator (https://en.cppreference.com/w/cpp/language/typeid) is in C++ part of the standard library header typeinfo.

The Asmc implementation is similar and used as type identification for macro arguments. It's a text macro and may be used for direct branching in a class or a regular macro. This simplify and also gives a more accurate type definition than TYPE and OPATTR that is often used for the same purpose.

The preprocessor also handles real math so this opens up more possibilities for doing the same as more advanced compilers do. This also means that more code is moved to the header files but less needed on the user end.

As an example the ARGB color constants (https://github.com/nidud/asmc/blob/master/include/d2d1helper.inc) are defined as a DWORD and expanded to a D3DCOLORVALUE (https://github.com/nidud/asmc/blob/master/include/d2dBaseTypes.inc#L8) struct of 4 float values. The input will then in most cases be immediate values:

    .new color:D3DCOLORVALUE(Black, 0.0)

The code for each float:

    mov      eax,_1
    and      eax,sc_redMask
    shr      eax,sc_redShift
    cvtsi2ss xmm0,eax
    divss    xmm0,255.0
    movss    [rcx].D3DCOLORVALUE.r,xmm0

Or the simplified version:

    %mov [rcx].D3DCOLORVALUE.r, @CatStr(%((_1 and sc_redMask) shr sc_redShift), <.0>) / 255.0
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on July 08, 2020, 01:52:35 AM
Inheritance (https://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm) of objects has been limited to data members and functions. The inline functions in the base was therefor not expanded in derived classes.

This is now added by simple equations of the function names if defined.

Code: [Select]

include iostream

;; Base class

.class Shape

  width   int_t ?
  height  int_t ?

    .operator setWidth :int_t {
        mov [this].Shape.width,_1
        }
    .operator setHeight :int_t {
        mov [this].Shape.height,_1
        }
    .ends

;; Derived class

.class Rectangle : public Shape

    .operator getArea {
        mov eax,[this].Shape.width
        mul [this].Shape.height
        }
    .ends

    .code

main proc

  local Rect:Rectangle

    Rect.setWidth(5)
    Rect.setHeight(7)

    ;; Print the area of the object.

    cout << "Total area: " << ( Rect.getArea() ) << endl

    exit(0)

main endp

    end main

Derived classes are added as anonymous structures for class and classVtbl.

*   RectangleVtbl struct
*   ShapeVtbl <>
*   Rectangle_setWidth  equ <Shape_setWidth>
*   Rectangle_setHeight equ <Shape_setHeight>
...
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: guga on August 08, 2020, 12:43:01 PM
New sample
WIC Viewer GDI+ (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/wic/wicviewergdiplus)

This code sample shows a Windows application using WIC to decode an image file and GDI+ to render the image to the screen. 

Files
WICViewerGDIPlus.inc (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicviewergdiplus/WicViewerGdiPlus.inc): Header file that declares application class DemoApp interface
WICViewerGDIPlus.asm (https://github.com/nidud/asmc/blob/master/source/test/wininc/wicviewergdiplus/WicViewerGdiPlus.asm): Implementation of the application class interface

Building the Sample
To build the sample using the command prompt:
asmc64 -pe -ws -gui WICViewerGDIPlus.asm

Running the Sample
After the application is launched, load an image file through file open menu. Window resizing is supported.

Changes
The @CStr() macro now handles zero terminated strings. It also accept additional L"" on split strings but only the first one is valid.
Code: [Select]
DemoApp::LocateImageFile proc hWnd:HWND, pszFileName:LPWSTR, cchFileName:DWORD

    local ofn:OPENFILENAME

    mov word ptr [r8],0
    ZeroMemory(&ofn, sizeof(ofn))

    mov ofn.lStructSize,  sizeof(ofn)
    mov ofn.hwndOwner,    hWnd
    mov ofn.lpstrFilter,  &@CStr(
        L"All Image Files\0"               L"*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico\0"
        L"Windows Bitmap\0"                L"*.bmp;*.dib\0"
        L"High Definition Photo\0"         L"*.wdp;*.mdp;*.hdp\0"
        L"Graphics Interchange Format\0"   L"*.gif\0"
        L"Portable Network Graphics\0"     L"*.png\0"
        L"JPEG File Interchange Format\0"  L"*.jpg;*.jpeg\0"
        L"Tiff File\0"                     L"*.tif\0"
        L"Icon\0"                          L"*.ico\0"
        L"All Files\0"                     L"*.*\0" )
    mov ofn.lpstrFile,    pszFileName
    mov ofn.nMaxFile,     cchFileName
    mov ofn.lpstrTitle,   &@CStr(L"Open Image")
    mov ofn.Flags,        OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST

    ;; Display the Open dialog box.
    GetOpenFileName(&ofn)
    ret

DemoApp::LocateImageFile endp

That´s really really good. It can open webp files as well. Can you try making it to 32 bits, nidud ? And also making it open heic files too ?
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on August 11, 2020, 05:44:48 AM
Can you try making it to 32 bits, nidud ?

The samples here are primarily written to test inline functions and this is not fully supported in 32 bit yet.

It is however a Windows classic sample so it should be possible to build a 32 bit version from the original source:
https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/wic/wicviewergdiplus

Quote
And also making it open heic files too ?

I assume you may have to download some support for that? In any case it should be possible to add this to the list and see what happens.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on October 02, 2020, 08:25:47 AM
Added two Direct2D samples from the Windows-classic samples.

  GDI/Direct2D Interoperability Sample (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/Direct2D/GdiInteropSample)
  Direct2D Hello World Sample (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/Direct2D/Direct2DHelloWorld)

These are similar to the WIC Animated Gif sample which also use Direct2D but they use the dwrite.inc (https://github.com/nidud/asmc/blob/master/include/dwrite.inc) header file as well. The interface in these headers are pure C++ so the translation may not always be correct for all methods, especially value returned by reference like the ID2D1RenderTarget::GetSize method:

  D2D1_SIZE_F GetSize();

The struct will be passed in RDX here (the class in RCX) so the C version and the COMDEF entry should be something like this:

  D2D1_SIZE_F *GetSize(D2D1_SIZE_F*);
  GetSize proc :ptr D2D1_SIZE_F

Note that the data used here is not static but rather allocated by the class which is passed in the lpParam to CreateWindowEx(). The module handle (hInstance) however is a static global label created by the ORG directive. This has to be the first entry in the code segment to work:
Code: [Select]
    .code

    org -0x1000
    __ImageBase label IMAGE_DOS_HEADER
    org 0

mainCRTStartup proc

The class pointer is picked up in WndProc:

Code: [Select]
    .if edx == WM_CREATE

        mov r8,[r9].CREATESTRUCT.lpCreateParams
        SetWindowLongPtrW(rcx, GWLP_USERDATA, PtrToUlong(r8))
        mov result,1

    .else

        mov pDemoApp,GetWindowLongPtrW(rcx, GWLP_USERDATA)

The class itself is allocated on the stack in WinMain().

Code: [Select]
wWinMain proc hInstance:HINSTANCE, hPrevInstance:HINSTANCE, lpCmdLine:LPWSTR, nCmdShow:SINT

  local vtable:DemoAppVtbl

    ;; Ignore the return value because we want to run the program even in the
    ;; unlikely event that HeapSetInformation fails.

    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0)

    CoInitialize(NULL)
    .if (SUCCEEDED(eax))

       .new app:DemoApp(&vtable)

        app.Initialize()

        .if (SUCCEEDED(eax))

            app.RunMessageLoop()
        .endif
        CoUninitialize()
    .endif

    .return 0

wWinMain endp

Files
Direct2DHelloWorld.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/Direct2DHelloWorld/Direct2DHelloWorld.inc)
Direct2DHelloWorld.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/Direct2DHelloWorld/Direct2DHelloWorld.asm)
GdiInteropSample.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GdiInteropSample/GdiInteropSample.inc)
GdiInteropSample.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GdiInteropSample/GdiInteropSample.asm)
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on October 04, 2020, 07:12:40 PM
Some movement.

  Direct2D Simple Path Animation Sample (https://docs.microsoft.com/en-us/samples/microsoft/windows-classic-samples/direct2dsimplepathanimation/)

  Demonstrates how to create a simple path animation with Direct2D.

Files
Animation.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/SimplePathAnimationSample/Animation.inc) - Defines animation helpers.
SimplePathAnimationSample.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/SimplePathAnimationSample/SimplePathAnimationSample.inc) - The header file for the DemoApp class.
SimplePathAnimationSample.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/SimplePathAnimationSample/SimplePathAnimationSample.asm) - Contains the application entry point and the implementation of the DemoApp class.
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on October 07, 2020, 04:18:06 AM
Simple Direct2D Application (https://docs.microsoft.com/en-us/samples/microsoft/windows-classic-samples/simple-direct-2d-application/)

  Draws shapes, text, and images with Direct2D.

This sample loads two images. One from disk and one from resource. However, the image is rather big (770K) so the app just download the image from the MS site instead. The first run will therefor take some time to load.

Files
SimpleDirect2dApplication.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/SimpleDirect2DApplication/SimpleDirect2dApplication.asm): Contains the application entry point and the implementation of the DemoApp class.
SimpleDirect2dApplication.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/SimpleDirect2DApplication/SimpleDirect2dApplication.inc): The header file for the DemoApp class.

Building the Sample

  To build the sample using the command prompt:

asmc64 -pe -ws -gui SimpleDirect2dApplication.asm
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on October 12, 2020, 11:36:30 PM
Direct2D Geometry Realization Sample (https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/Direct2D/GeometryRealizationSample)

This sample shows how to use opacity masks and A8 targets to enhance performance for anti-aliased geometries. It also shows how to use meshes to enhance performance for aliased geometries.

Files
GeometryRealization.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/GeometryRealization.asm): Implements the IGeometryRealization interface.
GeometryRealization.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/geometryrealization.inc): Defines the IGeometryRealization interface and related types.
GeometryRealizationSample.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/GeometryRealizationSample.asm): Implements the DemoApp class, which creates a window and demonstrates the IGeometryRealization interface.
GeometryRealizationSample.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/GeometryRealizationSample.inc): Defines the the DemoApp class.
RingBuffer.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/ringbuffer.inc): The header file for the RingBuffer class. RingBuffer works like a standard array, except that when it fills up, data at the beginning is overwritten.
stdafx.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/GeometryRealizationSample/stdafx.inc): Defines standard system include files, or project specific include files that are used frequently, but are changed infrequently.

Building the Sample
Generate PE binary file:
  asmc64 -pe -Cs -ws -gui GeometryRealizationSample.asm
Using LINKW:
  asmc64 -q -ws -Cs GeometryRealization.asm
  asmc64 -q -ws -Cs GeometryRealizationSample.asm
  linkw system gui_64W file GeometryRealizationSample, GeometryRealization

Running the Sample
This sample uses the following controls:

  Up Arrow: Increases the number of primitives rendered
  Down Arrow: Decreases the number of primitives rendered
  Spacebar: Pauses/resumes the animation.
  Mouse Wheel: Zooms in and out.
  'T' key: Toggles between hardware and software rendering.
  'R' key: Toggles between rendering geometry with and without realizations.
  'A' key: Toggles between rendering modes.
  'S' key: Toggles Draw Stroke (outlines).
Title: Re: Object-oriented programming (OOP) in Asmc
Post by: nidud on October 17, 2020, 04:43:51 AM
Direct2D List View Sample (https://docs.microsoft.com/en-us/samples/microsoft/windows-classic-samples/direct2dlistview/)

This sample shows how to create and use a bitmap atlas to create and animate a list of items. The list view loads the files and directories from the current directory with the icons. The directories and files in the list view can be sorted in the alphabetical and reverse alphabetical order.

Files
ListViewSample.asm (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/ListViewSample/ListViewSample.asm): Contains the application entry point and the implementation of the ListViewAppclass.
ListViewSample.inc (https://github.com/nidud/asmc/blob/master/source/test/Direct2D/ListViewSample/ListViewSample.inc): The header file for the ListViewApp class.

Running the Sample
Use the following keys to sort the list:

'A' key: sorts the list alphabetically.
'Z' Key: sorts the list in reverse alphabetical order.
'D' Key: sorts the list alphabetically with directories first.

Changes
Support for a second pointer added to INVOKE. This is added for accessing members of a class and static levels of functions in regular structures.

Example

.class ListViewApp

    m_pD2DFactory           LPID2D1Factory ?
    m_pWICFactory           LPIWICImagingFactory ?
    m_pDWriteFactory        LPIDWriteFactory ?
    m_pRT                   LPID2D1HwndRenderTarget ?
    m_pTextFormat           LPIDWriteTextFormat ?
    m_pBlackBrush           LPID2D1SolidColorBrush ?
    m_pBindContext          LPIBindCtx ?
    m_pBitmapAtlas          LPID2D1Bitmap ?

These pointers may now be accessed directly:

       .new dpiX:FLOAT
       .new dpiY:FLOAT

        this.m_pD2DFactory.GetDesktopDpi(&dpiX, &dpiY)
        this.m_pTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)

It adds a move to resolve the pointer using RAX but the arguments are sorted before the call so there is no risk of trashing registers.

   *    lea r8, dpiY
   *    lea rdx, dpiX
   *    mov rax, this
   *    mov rcx, [rax].ListViewApp.m_pD2DFactory
   *    mov rax, [rcx]
   *    call [rax].ID2D1FactoryVtbl.GetDesktopDpi