flat assembler
Message board for the users of flat assembler.

Index > Projects and Ideas > OOP extension - request for comments.

Goto page Previous  1, 2, 3  Next
Author
Thread Post new topic Reply to topic
m3ntal



Joined: 08 Dec 2013
Posts: 296
m3ntal 22 May 2014, 13:01
Quote:
or this? is much more readable and informative
Which is clearer?
Code:
DrawImage esi, x, y        ; this? 
exec esi, Draw:Image, x, y ; or this?    
All you did was add the weird exec prefix and put : between Object:Member.
Quote:
"exec" is not a "prefix" it is an "opcode"
Really? Never heard of the 'exec' opcode. It is a HL syntax no different than s->m or a[i] or f(1,2,3).
Post 22 May 2014, 13:01
View user's profile Send private message Reply with quote
m3ntal



Joined: 08 Dec 2013
Posts: 296
m3ntal 22 May 2014, 13:55
Note: Syntaxes like this can be automated:
Code:
create TDog 
mov    [Dog1], eax    
... may be replaced with something like:
Code:
create [Dog1]=new TDog

; in create macro (where p=[])

match [m]== =new type, p {
 create type
 mov [m], eax
}    
Practical use with auto-[] and "constructor":
Code:
create John=new User 'John'
destroy fresh.lib    
-> may also be matched for get/set with =.
Post 22 May 2014, 13:55
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 22 May 2014, 14:07
m3ntal wrote:
Note: Syntaxes like this can be automated:
Code:
create TDog 
mov    [Dog1], eax    
... may be replaced with something like:
Code:
create [Dog1]=new TDog    
Hehe, and we move ever closer to writing our code just like the big boys do in C and F#.

The problem with custom macros is the documentation. They might, or might not, do weird things with memory and/or registers that is hidden from view. Will macro XYZ corrupt eax or edx or some memory below esp? It is already bad enough with lods, div and loop having hidden registers, and now we have to contend with macros on top of that. It took me many years just to get comfortable with stdcall, so you can imagine how hard it is to cope with create and exec, or that newfangled call. Wink


Last edited by revolution on 22 May 2014, 15:46; edited 1 time in total
Post 22 May 2014, 14:07
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4353
Location: Now
edfed 22 May 2014, 14:34
Code:
DrawImage x,y,[content]
    


i don't get why a esi reference is needed.
Post 22 May 2014, 14:34
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 22 May 2014, 14:58
revolution wrote:
The problem with custom macros is the documentation. They might, or might not, do weird things with memory and/or registers that is hidden from view. Will macro XYZ corrupt eax or edx or some memory below esp?


I am fully agree with you here. I always tried to keep the macros as non-intrusive and predictable, as possible. And to write some documentation of course. Sometimes... Wink

_________________
Tox ID: 48C0321ADDB2FE5F644BB5E3D58B0D58C35E5BCBC81D7CD333633FEDF1047914A534256478D9
Post 22 May 2014, 14:58
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
m3ntal



Joined: 08 Dec 2013
Posts: 296
m3ntal 22 May 2014, 16:01
Quote:
i don't get why a esi reference is needed
Code:
DrawImage esi, x, y
call Image:Draw, x, y
int DrawImage(Image *this, int x, int y);    
Quote:
It took me many years just to get comfortable with stdcall
I thought the new call would make DOS programmers feel more comfortable about the transition to Windows. It was never a good idea to RE-specify conventions: stdcall/invoke/cinvoke/ccall/pinvoke/pcall/etc.
Post 22 May 2014, 16:01
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2909
Location: 0x77760000
typedef 22 May 2014, 16:15
m3ntal wrote:
Code:
create John=new User 'John'
destroy fresh.lib    

hahahah
Post 22 May 2014, 16:15
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 22 May 2014, 16:23
m3ntal wrote:
I thought the new call would make DOS programmers feel more comfortable about the transition to Windows.
Too much is hidden when things just happen magically. And when they go wrong it takes too much time to figure out what the hell is happening so as to avoid the macro bug.
m3ntal wrote:
It was never a good idea to RE-specify conventions: stdcall/invoke/cinvoke/ccall/pinvoke/pcall/etc.
Really? Never? Perhaps I could counter with "It is never a good idea to override native CPU instructions"? Oh, we've been here before, or is this just déjà vu?
Post 22 May 2014, 16:23
View user's profile Send private message Visit poster's website Reply with quote
m3ntal



Joined: 08 Dec 2013
Posts: 296
m3ntal 22 May 2014, 16:26
LOL. Check out this C code:
Code:
USER *john=NULL;
TRASH *fresh_lib=USELESS;

for (;;) {
 recommend_to_dummies(fresh_lib);
}    
Post 22 May 2014, 16:26
View user's profile Send private message Reply with quote
m3ntal



Joined: 08 Dec 2013
Posts: 296
m3ntal 22 May 2014, 16:46
Quote:
It is never a good idea to override native CPU instructions
1. New call extends/upgrades the meaning, does not override, redefine or degrade. 2. Then proc is not a good idea; it "overrides" the prologue/epilogue sequences and native instructions like push ebp, enter, leave. 3. The advantages of using call far outweigh any nitpicking. 4. 2 personal rules that I always follow: Never create a syntax that prevents writing pure ASM. Never create a syntax that is more difficult than ASM (Example: branch_if_greater).
Post 22 May 2014, 16:46
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 22 May 2014, 16:54
I have never liked proc either. A lot is hidden inside proc also. But at least it isn't named the same as a native instruction.
Post 22 May 2014, 16:54
View user's profile Send private message Visit poster's website Reply with quote
r22



Joined: 27 Dec 2004
Posts: 805
r22 22 May 2014, 17:51
PRAJ - Push Return-address And Jump, would be a superior name for CALL.

1) Easier to type
2) Means what it does
3) Can be shortened to PRJ and still retain meaning
4) Can alias with RPJ and RPAJ for when you're too lazy to type correctly

I win.
Post 22 May 2014, 17:51
View user's profile Send private message AIM Address Yahoo Messenger Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 23 May 2014, 01:13
No, it should be this:
Code:
lea esp,[esp-4] | mov [esp],eip | lea eip,[eip+offset]    
Post 23 May 2014, 01:13
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 23 May 2014, 04:50
Please, on topic. Some test cases and possible problems in the discussed library are welcome.
Post 23 May 2014, 04:50
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 28 May 2014, 13:38
After several days of work, I managed to write short reference manual of the OOP macro library. Please, read it and comment:
Code:
# Object oriented programming with FreshLib

## Objects definition

The objects are defined using macro pair "object" and "endobj".

The syntax of these macros is following:

        object OBJ_NAME [, PARENT_NAME]
          ; here object members are to be defined.
        endobj

The object can contain three types of members:

* Fields. They are simply some local labels with data definition, very similar to
the structure fields. The fields are defined the same way as the structure fields:

        object TAnimal
          .length dd ?
          .height dd ?
          .weight dd ?
        endobj

The fields are aimed to provide storage of private for the object data. Of course, it is
assembly language and the fields are not hidden from the user, but a good practice is to
not use them as an interface. (There are other members that are to be used as an interface).

* Methods. The methods are procedures that provide an object engine - the code that to
be executed on the object data. They are defined following way:


        object TAnimal
          .length dd ?
          .height dd ?
          .weight dd ?

          method .Jump, .height
        endobj


Here we defined a method TAnimal.Jump that will make our animal to jump. Smile
The methods can have any number of arguments. Besides the user defined arguments,
the method always contain one implicitly defined argument, named .self, containing
a pointer to the object itself.

The user should not define this argument, neither set it during the method invocation.
The object engine itself handle it. Of course the user can use this argument
on the method implementation code.

Once defined, the method needs to be implemented. Not implemented methods are
considered "abstract". They are allowed, but attempt to call them will generate exception.

The method implementation is similar to the procedures, but uses a macro "method" instead of
"proc". Also, no arguments need to be defined in the method implementation, because they are
already known from the object type definition:

        method TAnimal.Jump
        begin
                push    eax ecx

                mov     eax, [.self]
                mov     ecx, [eax+TAnimal.height]
                add     ecx, [.height]

                pop     ecx eax
                return
        endp

* Parameters. The parameters are, similar to the fields, data members. But unlike the fields,
they can contain a code, that to generate the data value. The definition of the parameter is
by the macro "param":

        param .ParamName, GET_METHOD, SET_METHOD

The GET_METHOD and SET_METHOD define how the value of the parameter is to be get or set by
the user. These arguments can have one of the following values:

* Name of a field - it means that the value of the parameter is to be get and/or set directly
from the object field member.

* Name of a method - instead of read or write the value of the parameter from the memory, some
object method is called.

The GET method should have none arguments (but there is always implicitly defined .self argument).
It should return the computed value in EAX register.

The SET method should have one argument for the value that have to be set in the parameter.
(and .self)

* NONE keyword - it means the parameter does not support reading or writing. This way, the
user can define read-only or write-only parameters. (And also, parameters that can't be neither
read nor write).


        object TAnimal
          .length dd ?
          .height dd ?
          .weight dd ?
          .speed  dd ?

          param  .Energy, .ComputeEnergy, NONE

          method .GetEnergy
          method .Jump, .height
        endobj


        method TAnimal.GetEnergy
        begin
                mov     ecx, [.self]
                mov     eax, [ecx+TAnimal.speed]
                imul    eax, eax
                imul    eax, [ecx+TAnimal.weight]
                sar     eax, 1                        ; E = m.v^2/2
                return
        endp


## Object inheritance

All objects can be inherited by other method definition. The child object contains all members of
the parent object and allows adding new members as well.

The methods of the child object can be inherited in the child object. The happens when the user
defines a method with the same name as already existing method:


        object TCat, TAnimal
          method .Jump, .height
        endobj


        method TCat.Jump
        begin
        ; code that makes the cat jump not as other animals.

                return
        endp

## Object use

Once defined, the object can be created and destroyed, a values can be set or get and
methods call. This functionality is provided by several macros. Notice that they are
made to have syntax very similar to CPU instructions and this way to not affect the "look&feel"
of the assembly language program.

### Creating object instance.

Once defined, the user can create any number of object instances using the macro "create":

        create TARGET, OBJECT_TYPE

This macro creates an object of type OBJECT_TYPE and put it into TARGET. TARGET can be
register or memory location

        create eax, TCat        ; put in EAX a pointer to the object instance.

        create [MyCat], TCat    ; put the pointer in [MyCat]

The macro create allocates memory for the instance, sets the proper pointer to the methods
table and (if defined) executes the method .Create of the object.

Create does not change any registers, unles one is set as TARGET.

### Destroying objects

If the object instance is no longer needed, it have to be destroyed by using "destroy" macro.

        destroy OBJECT

For example:

        destroy [MyCat]

If the object is descendent of TObject, the method .Destroy is to be called. TObject is some
special name that is aimed to be the root type for all objects in FreshLib. Hardcoding this
name is because "destroy" method can't know the type of the destroyed object and this way
can't check existing of the method .Destroy;

### Object method execution

The method of a object can be executed by using "exec" macro:

        exec OBJECT, TYPE:METHOD, [ARGUMENTS]

For example:

        exec [MyCat], TCat:Jump, 100

There is another macro "pexec" that acts exactly as "exec", but calls not the object
method, but the same method from the parent object type (of course if defined).

        pexec [MyCat], TCat:Jump, 100  ; it will call TAnimal.Jump, instead of
                                       ; the re-defined TCat.Jump


### Object parameter get and set:

The parameter getting or setting values is by the macros "get" and "set":

        get TARGET, OBJECT, TYPE:PARAMETER

        set OBJECT, TYPE:PARAMETER, NEW_VALUE

The TARGET and NEW_VALUE can be register or memory variable (as in mov instruction):

        get ecx, [MyCat], TAnimal:Energy

This line will compute the current energy (see the definition of .Energy above)
of [MyCat] and will return it in ECX register.

Notice, that the code, generated by this macro is optimal. For example if the parameter
is associated with a field and the object is passed by register, the whole line will generate
single "mov" instruction.

"get" and "set" macros will preserve all registers, except TARGET of course.


### Check object type

The user can check whether the object belongs to some type by using "istype" macro:

        istype OBJECT, TYPE

This macro returns ZF flag set if the object belongs to the type or ZF cleared if not.
It will not change any registers.

For example, see following code:

        ; object types definitions

        object TAnimal
        endobj

        object TCat, TAnimal
        endobj

        object TDog, TAnimal
        endobj

        ; instance creation

        create eax, TCat

        istype eax, TCat
        je     .yes_cat         ; ZF=1  It is a TCat

        int3

.yes_cat:
        istype eax, TAnimal
        je     .yes_animal      ; ZF=1  Also, it is TAnimal

        int3

.yes_animal:
        istype eax, TDog
        je     .yes_dog         ; ZF=0  but is it NOT TDog.

        int3                    ; will stop here

.yes_dog:


## Objects polymorphism.

Polymorphism is one of the main properties of the OOP. It is the ability of the object to
execute different code on the same method call, depending on its type.

In the context of our library, as long as the types inherit all the methods of its parents and
can redefine it, it means, that in the above example, TCat and TDog both have method .Jump,
inherited from TAnimal. (In this mean, they are both of type TAnimal), but the implementation of
TCat.Jump and TDog.Jump differs.

This way, we can use only one code to process both types.


        create  eax, TCat
        create  ebx, TDog

        exec    eax, TAnimal:Jump, 100
        exec    ebx, TAnimal:Jump, 100

You can see, that the same code will call in the first case TCat.Jump and in the second case TDog.Jump    

_________________
Tox ID: 48C0321ADDB2FE5F644BB5E3D58B0D58C35E5BCBC81D7CD333633FEDF1047914A534256478D9
Post 28 May 2014, 13:38
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 28 May 2014, 14:00
Can you remove the requirement for "begin". This was long ago removed from fasm's "proc" macro and replaced by "local" and "locals".

Which raises this suggestion: Show an example of using local variables in method bodies.
Post 28 May 2014, 14:00
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 28 May 2014, 14:20
revolution wrote:
Can you remove the requirement for "begin". This was long ago removed from fasm's "proc" macro and replaced by "local" and "locals".

Which raises this suggestion: Show an example of using local variables in method bodies.


The syntax with "begin" is needed, because FreshLib uses slightly different structure of the local variables frame. This structure is impossible to be made without "begin" macro. Or at least it is syntactically better. (FASM macros can't create such type of local variables).
Read more in the FreshLib reference manual.

In addition, the macro syntax used in FASM library, because of too heavy use of preprocessor symbols is not able to support some advanced features of Fresh IDE - for example code completion and cross reference functions.

There is nothing special in the local variables handling. Here is an example of local variables and arguments use in the method implementation. (and also, parameter set):

Code:
method MyObj.SetTop, .arg1
.rect RECT
begin
        mov    eax, [.arg1]
        mov    [.rect.top], eax

        set    [.self], MyObj:Top, eax
        return
endp    

_________________
Tox ID: 48C0321ADDB2FE5F644BB5E3D58B0D58C35E5BCBC81D7CD333633FEDF1047914A534256478D9
Post 28 May 2014, 14:20
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 28 May 2014, 14:46
I think "impossible" is too strong a word here. For example you could simply make a macro (called localc or clocals or something) instead of begin:
Code:
proc ...
  xor eax,eax
  clocals
    common_var1 dd ?
  endl
  mov ecx,[common_var1]
  clocals
    common_var2 dd ?
  endl
  mov edx,[common_var2]
;...    
BTW: omission of the leading dot in local variable names is also something that is visually more appealing.
Post 28 May 2014, 14:46
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 28 May 2014, 15:02
revolution wrote:
I think "impossible" is too strong a word here. For example you could simply make a macro (called localc or clocals or something) instead of begin:


I wrote also "Or at least it is syntactically better". Or you can argue, that two macros is better than one?

Quote:
BTW: omission of the leading dot in local variable names is also something that is visually more appealing.


No, it is not. Omission of the leading dot will make the local variables look the same way as global and this way will lower the code legibility.

_________________
Tox ID: 48C0321ADDB2FE5F644BB5E3D58B0D58C35E5BCBC81D7CD333633FEDF1047914A534256478D9
Post 28 May 2014, 15:02
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20449
Location: In your JS exploiting you and your system
revolution 28 May 2014, 15:07
"begin" only allows the common variables to be declared in one place. Whereas something like clocals/endl allows definitions in any, and multiple, places (or all at the top if so desired).

Do your macros allow for names without the dot, and give the user the choice to use them or not? If you force it then I'd suggest to make to more flexible for the user.
Post 28 May 2014, 15:07
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page Previous  1, 2, 3  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.