; OO Testcase 2
; Build with:
;..\uasm64 -win64 -c -Zp8 -Zi -Zd -Zf oo1.asm
;d:\vs2015\vc\bin\link /subsystem:console /machine:x64 /debug /Libpath:"%WINSDK%\v7.1\Lib\x64" oo2.obj
.X64
OPTION CASEMAP:NONE
OPTION WIN64:7 ; 11/15 for RSP and 1-7 for RBP.
OPTION STACKBASE:RBP ; RSP or RBP are supported options for the stackbase.
OPTION LITERALS:ON
OPTION VTABLE:OFF ; [ON/OFF] dictates whether c-style method invocations use the vtable (slower but method pointers can be modified) or the (faster but fixed) direct invocation.
.nolist
.nocref
WIN32_LEAN_AND_MEAN equ 1
_WIN64 equ 1
include \wininc\Include\windows.inc
.list
.cref
includelib <kernel32.lib>
includelib <user32.lib>
NormalProc PROTO (real4) a:real4
;=====================================================================================================================================================
;
; CLASS Definition inclusion guard.
;
;=====================================================================================================================================================
IFNDEF _CLASS_PERSON_
_CLASS_PERSON_ EQU 1
OINTERFACE Entity
CVIRTUAL PrintName, <VOIDARG>
CVIRTUAL SetName, <VOIDARG>
ENDMETHODS
ENDOINTERFACE
;---------------------------------------------------------------------------------------------------------------
; Define our Person class with 2 instance methods, 1 static method and 3 member fields.
;---------------------------------------------------------------------------------------------------------------
CLASS Person
CMETHOD PrintName
CMETHOD SetName
CMETHOD Calc
CMETHOD Calc2
CMETHOD DoAdd
CSTATIC IsHuman
ENDMETHODS
pName dq 0
age db 0
human db 0
handle dq 0
ENDCLASS
; A Pointer to Object type.
pPerson TYPEDEF PTR Person
;---------------------------------------------------------------------------------------------------------------
; Constructor
; Can take optional arguments.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, Init, <VOIDARG>, <USES rbx r10>, age:BYTE, consoleHandle:QWORD, ptrName:PTR
LOCAL isAlive:DWORD
; Internally the METHOD forms a traditional procedure, so anything that you can do in a PROC you can do in a method.
; Store console output handle.
mov rax,consoleHandle
mov [rcx].handle,rax
; Store pointer to passed in name. (r9 can be used directly)
; -> The proc arguments conform to calling convention: rcx=thisPtr, dl=age, r8=consoleHandle, r9=ptrName
mov [rcx].pName,r9
; On entry into any method RCX is a pointer to the instance
; and the correct reference type is assumed.
mov [rcx].human, 1 ; Hence this is possible.
mov (Person PTR [rcx]).human, 1 ; Alternative forms of reference.
mov [rcx].Person.human, 1 ; " "
mov isAlive,0
mov al,age
mov [rcx].age,al
.if( age < 100 )
mov isAlive,1
.endif
; The constructor MUST return it's this pointer in RAX.
mov rax,thisPtr
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Destructor
; Takes no arguments.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, Destroy, <VOIDARG>, <>
mov [rcx].age,0
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Set person name.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, SetName, <VOIDARG>, <USES rbx>, pNameStr:PTR
mov rax,pNameStr
mov [rcx].pName,rax
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Print the persons name to the console.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, PrintName, <VOIDARG>, <USES rbx>
LOCAL bWritten:DWORD
mov rsi,thisPtr
assume rsi:PTR Person
mov rax,lstrlen([rcx].pName)
__imp_WriteConsoleA( [rsi].handle, [rsi].pName, eax, ADDR bWritten, NULL )
assume rsi:NOTHING
; calls to other instance methods with pointer syntax.
; -> Because the METHOD declaration sets the prototype, you cannot forward reference methods.
thisPtr->SetName("New name")
mov rcx,thisPtr
mov rax,[rcx].handle
mov rbx,[rcx].pName
invoke WriteConsole, rax, rbx, 8, ADDR bWritten, NULL
; calls to other instance methods with pointer syntax using a register.
lea rcx,thisPtr
[rcx].Person->SetName("New name")
mov rcx,thisPtr
mov rax,[rcx].handle
mov rbx,[rcx].pName
invoke WriteConsole, rax, rbx, 8, ADDR bWritten, NULL
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Perform a float calculation, return a dword.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, Calc, <real4>, <USES rbx>, aValue:REAL4
vmovss xmm0,aValue
vaddss xmm0,xmm0,FP4(1.0)
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Perform a calculation on 2 vectors via vectorcall abi.
;---------------------------------------------------------------------------------------------------------------
option stackbase:rsp ; vectorcall currently only supports rsp stackbases.
option win64:11
VECMETHOD Person, Calc2, <VOIDARG>, <USES rbx>, aVec:XMMWORD, bVec:XMMWORD
ret
ENDMETHOD
option stackbase:rbp
option win64:7
;---------------------------------------------------------------------------------------------------------------
; Perform a calculation on two arguments.
;---------------------------------------------------------------------------------------------------------------
METHOD Person, DoAdd, <dword>, <USES rbx>, aValue:REAL4, bValue:REAL4
vmovss xmm0,aValue
vmovss xmm1,bValue
vaddss xmm0,xmm0,xmm1
vcvttss2si eax,xmm0
ret
ENDMETHOD
;---------------------------------------------------------------------------------------------------------------
; Static method to check if a person is a human.
;---------------------------------------------------------------------------------------------------------------
STATICMETHOD Person, IsHuman, <qword>, <USES rbx>, somebody:PTR Person
mov rax,somebody
mov al,(Person PTR [rax]).human
ret
ENDMETHOD
ENDIF
;=====================================================================================================================================================
; END OF PERSON CLASS
;=====================================================================================================================================================
.DATA
hOutput dq 0
person1 pPerson 0 ; A global variable to hold a reference to a Person type.
avgAge REAL4 0 ; Integer to real type promotion. (You can use an integer initializer instead of N.n format).
.CODE
; -> Using a FRAME PROC as the entry point for both Console and Windows applications is advised to ensure correct stack startup.
MainCRTStartup PROC FRAME
LOCAL person2:PTR Person
; A local variable to hold a reference to a Person type. (Note you can also use _DECLARE if the Object name includes <>).
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov hOutput,rax
; Create two Person instances.
mov person1, _NEW(Person, 30, hOutput, CSTR("Jane Doe "))
mov person2, _NEW(Person, 40, hOutput, "Peter Smith ") ; Use string literals.
_INVOKE Person, PrintName, person1 ; Direct call (Type, Method, arguments)
_VINVOKE person2, Person, PrintName ; Vtable call (instance, Type, Method)
_INVOKE Person, SetName, person1, CSTR("Michael Smith ")
_INVOKE Person, PrintName, person1
_INVOKE Person, Calc, person1, 1.0
_VINVOKE person2, Person, Calc, 1.0
_INVOKE Person, Calc2, person2, xmm4, xmm5 ; Vectorcall based method.
; Use return type information.
.if( _V(person1, Person, Calc, 1.0) == FP4(2.0) )
xor eax,eax
.endif
mov rax, _STATIC(Person, IsHuman, person1) ; Default return type from _STATIC is 64bit integer.
; Method invocation via register pointer.
lea rsi, person1
_INVOKE Person, Calc, [rsi], 1.0
_VINVOKE [rsi], Person, Calc, 1.0
.if( _V([rsi], Person, Calc, 1.0) == FP4(2.0) )
xor eax,eax
.endif
; Method invocation using c-style calls.
albl: person1->Calc(1.0)
person2->Calc(2.0)
.if( person1->Calc(1.0) == FP4(2.0) )
xor eax,eax
.endif
; Using a register as an object pointer with c-style calls requires the type to specified after the pointer.
; The register must be doubley-indirect, in that it points to the pointer to the object, which allows for rapid iteration through lists of object pointers.
lea rsi,person1
[rsi].Person->Calc(1.0)
.if( [rsi].Person->Calc(1.0) == FP4(2.0) )
xor eax,eax
.endif
xor rax,rax
lea rsi,person2
[rsi+rax].Person->Calc(1.0)
xor rax,rax
.if( [rsi+rax].Person->Calc(1.0) == FP4(2.0) )
xor eax,eax
.endif
person1->Calc( person2->Calc(1.0) ) ; Pass the typed result of a method call to another method.
NormalProc( person1->Calc(1.0) ) ; Pass the typed result of a method call to a normal procedure.
person1->Calc( NormalProc(1.0) ) ; Pass the typed result of a normal procedure to a method call.
person2->DoAdd( 1.0, NormalProc(2.0) )
; Delete the objects.
_DELETE(person1)
_DELETE(person2)
ret
MainCRTStartup ENDP
NormalProc PROC (real4) FRAME a:real4
LOADSS xmm0,1.0
ret
NormalProc ENDP
END MainCRTStartup