Ive been researching how to define and implement a class correctly. The code below compiles yet crashes. Obviously I'm missing something here. I'm intrested in both compile time and run time objects creation. I've looked at the object.inc that was included with masm32. Impressive code, but I cant completely understand what taking place within that include. I'm looking for basic class objects, no inheritance required(yet). I'm hoping someone here can explain not just how but why also.

main PROTO
myclass_mysub PROTO

myclass STRUCT
    txt db 6 dup(0)
    cap db 6 dup(0)
    mysub dd offset myclass_mysub
myclass ENDS

.data
myinstance1 myclass<"Hello","World">
myinstance2 myclass<"ABCDE","FGHIJ">
; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

    .code

start:

    call main
    invoke ExitProcess,0

; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

main proc
    call myinstance2.mysub
    call myinstance1.mysub   
    ret
main endp

myclass_mysub PROC
    invoke MessageBox, NULL, myclass.txt, myclass.cap, MB_OK
    ret
myclass_mysub ENDP

; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

end start
Posted on 2006-06-27 21:43:59 by Re_Boot
Re_Boot,
    I don't know squat from classes and inheritances.  That is some C++ jargon, right?  Anyway your problem is that you don't supply the addresses of the text to MessageBox.  And you don't terminate the text with a zero.  No need to call "main" either, that is just an extra step.  And I have found out that it is easier to make a blank template with a STRUCT and apply it to something in the .DATA segment instead of trying to initialize STRUCTs.  Oh, and by the way, I never use PROCs and PROTOs.  Anyway the following code works.  Ask if you have any questions.  Ratch

myclass STRUCT
    txt db 7 dup (?)
    cap db 7 dup (?)
    mysub dd ?
myclass ENDS

.data
myinstance1 BYTE 'Hello ',0,'World ',0
            DWORD OFFSET myclass_mysub

myinstance2 BYTE 'ABCDE ',0,'FGHIJ ',0
            DWORD OFFSET myclass_mysub

; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

    .code

start:
    LEA EAX,myinstance1.myclass.cap
    LEA ECX,myinstance1.myclass.txt
    PUSH EAX
    PUSH ECX
    call myinstance2.myclass.mysub
    LEA EAX,myinstance2.myclass.cap
    LEA ECX,myinstance2.myclass.txt
    PUSH EAX
    PUSH ECX
    call myinstance2.myclass.mysub

    invoke ExitProcess,0

; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

myclass_mysub:
MYC STRUCT
return DWORD ?
cap    DWORD ?
txt    DWORD ?
MYC ENDS
    invoke MessageBox, NULL, , , MB_OK
    ret 2*DWORD

; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

end start
Posted on 2006-06-27 23:34:11 by Ratch
As someone who's worked with C++ and C#, and Java, and VB, i've got enough authority in this to say this:

I don't suggest you go out of your way to make "class" objects. They just get in the way of what you're trying to do. Stop thinking in a heavily OOP way, and start thinking about how to make your code clear, and readable. This stuf should come after you've gotten the ang of things, and want to start bringing OOP into the implemnetation of your program, but you really need to make everything xlear when you're starting.
Posted on 2006-06-27 23:54:33 by Bobbias
Rather than attempting to implement OOP in asm from the ground up, I suggest you take a look at ObjAsm32.
It's an OOP framework which is almost totally written in masm's macro language and is specifically for writing 'object classes' under masm.
With OA32 you can combine the best features of highlevel and lowlevel languages without sacrificing anything.
Your asm sourcecode becomes modular (and thus portable between projects), your development times are radically improved, and you can interface with code objects written in other languages.
I just love debunking the myths concerning modern asmcode.

Still, if you prefer to light your fires by rubbing sticks together, by all means continue :)


Posted on 2006-06-28 00:10:04 by Homer
Hey, comeon, if I'd been so ingrained by the OOP invironment that I felt the NEED for classes, I would have done this myself :P
Thank god JAVA wasn't my first language, lol.

In any case, I didn't know about OA32, so I couldn't have suggested it, but that sounds pretty awesome (and probably would have helped me with my pong, had I known about it). But happy hunting, in whatever style you prefer to code in.
Posted on 2006-06-28 00:25:23 by Bobbias
Thanks for all the responces. What I'm really intrested in is code generation for a compiler project I'm working on. I'm trying to terms with class objects and code generation.
Posted on 2006-06-28 05:51:58 by Re_Boot
TASM supports OOP. It is described in detail in the manual.
Posted on 2006-06-28 16:12:50 by ti_mo_n
OK.

Do you want to implement dynamic typing of objects as in Smalltalk or PHP ?

Or do you want to implement static typing of objects as in C++ and most other OOPLs ?
Posted on 2006-06-29 21:38:01 by tenkey
Good question, I'm surprised nobody thought to ask that before, since which one you chose greatly changes how you go about supporting them.
Posted on 2006-06-30 00:19:06 by Bobbias
OA32 handles both, and COM also :)

Posted on 2006-06-30 11:17:53 by Homer
I'll be using static typing. No GC, explicit alloc/dealloc. I'm using masm32 as the backend, my parser parses to masm compatible assembler code as my intermediate code. This makes testing what is generated easier. Eventually i hope to actually do the peformat and allow 64bit code. I guess my question besides "how is this done in assembler", is what is really taking place when a object is created, and methods and property's accessed.

Cheers, I have a 4 day weekend! ;)
Re_Boot
Posted on 2006-06-30 20:49:31 by Re_Boot
A compiler will strip away a lot of what is OO. The instruction sets of the most, if not all, of the current machines are non-OO.

You are left with data records (aka structures) and procedures (aka functions or subroutines).

=====

A table of procedures, known as a "dispatch table" (aka vtables or vmt's), is created for inheritance purposes. Each object contains a reference (pointer) to this table. The table contains the addresses of procedures (methods) introduced by the class definition, and all the procedures introduced by parent or base classes - except for the addresses of overridden procedures.

In single inheritance, this is fairly simple, as a base class dispatch table will be a prefix for the dispatch tables of derived classes.

=====

Similarly, in single inheritance, adding new object data (components or members) simply uses the data record layout of the base class as a prefix for the data record layout of each derived class.

=====

In single inheritance, calling a method just requires calling the associated procedure indirectly via the dispatch table. Code in a base class will only use the subset table. Overriding base class methods just requires modifying the subset table of the derived class. The compiler does this at compile time.

=====

There are two OO overheads - one is the use of dispatch tables to call procedures. The other is to add an object reference to the list procedure arguments, so that the same code can be used for any object of a given class.

The C++ language asks the programmer to distinguish between methods which are overridable (virtual), and those that are not (nonvirtual). A table entry is not needed for the latter, and such methods can be called directly.

Some compiler systems can do a "whole program" analysis, and discover which methods can use the lower overhead call by analyzing the class inheritance tree.
Posted on 2006-06-30 22:37:20 by tenkey
Minor point here: the fact that all computers are non OO is what makes me still weary of full out OOP despite living in an age where OOP is forcefed down everyone's throat. I think java is a good example of OOP gone wrong (your main program is.. a class?) Just look at how java handles formatting where controls go on a form.. It took me longer to code the GUI than the program for my teacher on an exam once..... and in most of the latter programs in that class, too.

In any case, I just needed to get that out there. (sorry bout going offtopic, I encourage anyone who's opinion differs to PM me or IM me if you want to further discuss this)
Posted on 2006-07-01 00:59:34 by Bobbias
I think that if you remove read-only dispatch tables, and move them to the structure of the object, you get rid of half the overhead of calling virtuals, and get my favourite type of OOP, that in asm doesn't even require macros :) :

class1 struct
  func1 dd ?
  func2 dd ?
  rarefunc dd ? ; a dispatcher like  WndProc(hwnd,msg,wparam,lparam)

  data1 dd ?
  data2 dd ?
class1 ends

; pObj1->func2(param)
mov eax,pObj1
push param
push eax
call dword ptr.class1.func2

; or simly
Scall .class1.func2,eax,param

But stuff like thousand-fold inheritance are not in my taste, it turned out.

This year I studied Java, and for coursework decided to make a game. Found my ways of dealing with the no-globals limitation, and ported half of a commercial game of mine (everything except save-states and advanced blending-effects) overnight. But the bugger there was about getting actual input from the keyboard. Installed 3 versions of the SDK, each of them - done via absolutely different approaches o_O. The "depreciated method used" warning was a critical error in one of the versions of the SDK, and the just-warned compiles wouldn't run on different machines with different VMs installed XD. This all just for basic key input.. "What happened to their boasted 'portability'", I raged :). But overall, actually Java isn't bad imho - just treat it as a script, and you would like it :) . (btw, I like the emulation scene, so it could shift my judgement)
Posted on 2006-07-03 16:12:47 by Ultrano
Well, I just don't like java's "Everything's a class, hell, a PROGRAM is a class!" attitude. Classes like that just give way too much overhead and can complicate things at times. I'm so annoyed at having to wrap numerous classes around eachother just for basic input. example: to get input form a socket, you need to get the InputStream, pipe that into an InputStreamReader, and pipe that into a BufferedReader (might be a simpler way, but this is from memory). Why can't I just call Read() from the damn InputStream :<
Posted on 2006-07-03 19:57:36 by Bobbias
That's fine and dandy for small and in-complex programs. When getting into massive amounts of data that you must alloc/initialize/free manually, OOP classes become like Macros... they are just there, tidying up the work for you.
Posted on 2006-07-03 22:47:14 by SpooK
Some great reply sand suggestions here. I appreciate everyones perspective. Its not that I want to write objects in assembler, but to learn how to structure code generation for a parser/compiler I've been working on. This is what I've come up with,

myclass_mysub  PROTO  :DWORD

myclass STRUCT
    txt db 7 dup(0)
    cap db 7 dup(0)
    mysub dd offset myclass_mysub
myclass ENDS

.data

myinstance1 myclass<"Hello","World">
myinstance2 myclass<"OOPing","Masm32">

.code

start:

    call main
    invoke ExitProcess,0

main proc
    lea eax, myinstance1
    push eax
    call myinstance1.mysub

    lea eax, myinstance2
    push eax
    call myinstance2.mysub

    ret
main endp

myclass_mysub PROC uses edi lpTHIS:DWORD
    mov edi,lpTHIS
    assume edi:ptr myclass
    invoke MessageBox, NULL, addr(myclass ptr).cap, addr(myclass ptr).txt, MB_OK
    assume edi:nothing
    ret
myclass_mysub ENDP

end start

Now, this code does work. But it opens up more questions than it answers. A couple things I dont like about the code why I needed to use "addr" ie: addr(myclass ptr).txt, since I passed the effective address? Also I'm not able to use invoke on the method call, appears I need to typedef it in some way.I also see how limited this is, for static objects it would work fine, dynamic would require a completely different approach. I guess dynamic will keep me preoccupied for awhile. :D
Posted on 2006-07-04 07:45:39 by Re_Boot

lea eax, myinstance2
    push eax
    call myinstance2.mysub

Use the Scall macro :) :

mov eax,offset myinstance2
Scall .myclass.mysub,eax


No, you won't have any trouble with dynamic objects, you just need constructor function:

myclass_myclass proc
mov eax,malloc(sizeof myclass)
mov .myclass.mysub,myclass_mysub
ret
myclass_myclass endp


Right here you don't need "lea eax,.myclass.txt", you can use eax/pThis directly - but later in your code bear in mind you used a shortcut for this :)


Posted on 2006-07-04 07:58:35 by Ultrano
If you are eventually going to create binary code, then I suggest getting rid of STRUCT and PROC in your generated code. You want to see the raw addressing form, for example, ; and the raw calling code (because you can CALL to an ordinary label). Another reason is to avoid fighting the MASM type system. You will also want to generate "internal" procedure labels to allow multiple procedures with the same name in the source code.

If you want to follow VC++ conventions, each object should have a pointer to a dispatch table as the very first field.
Then, to call a "virtual" function...

push lastarg
; ...
push firstarg
mov ecx,object_pointer
push ecx
mov eax,  ; get address of dispatch table (vtable) from object
call dword ptr ; use entry in dispatch table
add esp,xxxx  ; remove arguments


If the method is never overridden, you can delete the loading of the table address, and call the unique method directly.

If compatibility is not an issue, you are free to use any kind of argument passing convention. For example, you may want to push arguments in lexical order (reversed with respect to VC++ and Windows API) for ease of implementation. Or you may want to load a dedicated object pointer register just before calling a method - you can save code when the calling method and the called method are from the same object.
Posted on 2006-07-04 23:34:06 by tenkey