Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

An Object-Oriented Assembly Language Macro Library


MAR92: AN OBJECT-ORIENTED ASSEMBLY LANGUAGE MACRO LIBRARY

Donald is a programmer for Digital Alchemy Inc., a start-up firm specializing in process control and communications software. His experience includes systems and applications software development for Asymetrix Corp., MITRE Corp., Computer Sciences Corp., and the Kingdom of Saudi Arabia. He has been involved in object-oriented programming for the past four years and can be reached at Digital Alchemy, P.O. Box 254801, Sacramento, CA 95865, fax: 916-481-6467.


Object -oriented programming techniques allow you to develop reusable, maintainable code by providing mechanisms for inheritance, data abstraction, and encapsulation--features which C++, CLOS, and Smalltalk programmers currently enjoy. But 80x86 assembly language programmers can also use object-oriented programming techniques. For example, I've used the techniques described in this article to develop an object-oriented assembly language macro library that provides windows, pop-up menus, mouse support, horizontal and vertical scroll bars, sound support, and the like.

In object-oriented systems, programs are organized as a collection of objects related to one another through inheritance. An object is the embodiment of a local state. It contains a description some data and procedures that operate upon it. A message is the generic name for a set of procedures or methods.

An object may use methods from other objects through inheritance. For the purpose of this discussion, objects that inherit methods from others are derived objects, and those that do not are base objects. An ancestor is any object that bestows methods. In this object-oriented programming scheme, ancestral objects may contribute up to two methods per message.

A combined method is the runtime version of a message. Methods are assembled into a combined method in a well defined manner based on an object's ancestor list. However, only objects directly sent as messages may have combined methods.

Method combining is done by first finding all of an object's ancestors, and then collecting methods from each ancestor grouped by message. Object collection starts with an object, its ancestors, their ancestors, and so on recursively. Once this depth-first search is completed, duplicate objects are removed to prevent duplication of effort. This object collection becomes the basis of a search for method addresses grouped under the given message name.

In this scheme, there may be up to three methods per message per object. This makes the combining of methods nontrivial. One of the many ways to combine methods is through daemon combination, which specifies how these three element sets of methods may be ordered to form a combined method.

Method Combining

The three methods that make up an object's local message are known as Before, Primary, and After. Before methods execute "before" and After methods execute "after" the Primary. When a combined method is assembled, only the local (that is, uninherited) Primary method is included. In other words, ancestors can contribute only Before and After methods to a combined method.

Combined methods are created only for objects which receive directly sent messages. Methods received indirectly through inheritance will not have combined methods. This optimizes message passing so that runtime searching is not required to resolve a message pass into its associated set of methods. Combined methods make message passing a simple matter of fetching and executing method addresses.

The daemon combination scheme specifies methods combination in the following manner: the local Before, ancestral Befores (in-depth first order), the local Primary, ancestral Afters (in reverse order of Befores), and the local After method.

To reiterate, combined method construction involves two steps. First, objects found in a depth-first search of an object's ancestor list are placed onto a list with duplicates removed. Second, this list is used to create an ordered list of methods based on the daemon combination scheme.

Message Passing

Message passing becomes possible after combined methods are constructed. Consequently, when an object is sent a message, it responds by executing the corresponding combined method. This involves locating a pointer to a combined method table and then fetching and executing the address in that table.

An object is known to ancestral objects through the object variable Self and to other objects by name. Self provides a means for anonymous message passing and access to instance data. This makes code generalization possible by providing a way to easily share code and data.

Message arguments are passed by way of the stack to all methods in a combined method. Therefore, methods must be stack neutral--they must not increase or decrease the stack depth. If a method is to return a value on the stack, space must be allocated prior to the message pass.

Example 1 demonstrates message passing with the send macro. send takes an object name, a message name, and an optional message argument list. In the first example, Self is assigned to Screen, the constant DoubleBdr is pushed onto the stack, and the combined Screen-Refresh method is called. Upon return, DoubleBdr is removed from the stack.

Example 1: Message passing using the send macro

  send Screen, Refresh, DoubleBdr     ;Send Screen a Refresh msg
  send Self, Read                     ;Send Self a Read msg
  send Self, <WORD PTR[bx]>           ;Send Self msg pointed to by BX
                                       register

Listing One (page 84) is the source code for the send macro. It pushes arguments onto the stack, moves an object address into a register, moves the message number into a register, calls the sendMsg procedure, and pops message arguments off the stack.

sendMsg assigns Self, searching the object's message list for a matching message number. If a match isn't found, it returns. Otherwise, it gets a pointer to the combined method table, and selects a method count. Using the method count as a loop counter, it then fetches and executes method addresses located in the method table.

Object Definition

Using Microsoft MASM 5.1 conventions, source files are comprised of code and data segments. For our purposes, the code segment will contain methods and procedures, while the data segment holds object ad message definitions along with other data.

Example 2 shows the use of the defObj macro for object definition. defObj takes an object name, an ancestor list, an instance variable list, and a message list. The object name may be any valid variable name. The ancestor list may be empty, as in the case of base objects, or may contain the names of objects to inherit from, as in the case of derived objects. The instance variable list may be empty, or may contain three element entries of instance variable name, size, and initial value. The last argument, the message list, contains the names of messages which the object will respond to.

Example 2: Using the defObj macro for object definition

  defObj Window,\                 ;Define Window object
         <>,\                     ;As a base object
         <>,\                     ;With no inst vars
         <Refresh>                ;Responds to Refresh msg

  defObj Border,\                 ;Define Border object
         <>,\                     ;As a base object
         <>,\                     ;With no inst vars
         <Refresh>                ;Responds to Refresh msg

  defObj Screen,\                 ;Define Screen object
         <Window, Border>,\       ;As a derived object
         <Row1, 1, 1,\            ;With these inst vars
         Col1, 1, 0,\
         Row2, 1, 23,\
         Col2, 1, 79,\
         Color, 1, 34h,
         BdrColor, 1, 30h,\
         MemSeg, 2, Nil>,\
        <Refresh>                 ;Responds to Refresh msg

The Screen object inherits some of its methods from Window and Border. Object and message names are public symbols, and are the only data visible to nonancestral objects. Ancestors, however, have access to all instance data through the object variable Self.

Listing Two page 84) shows the source code for the defObj macro. It assembles ancestor, instance variable, and message tables in memory. The _Object structure is used by defObj to assemble pointers to these tables.

Message Definition

A message describes a set of operations on some data. To associate operations to a message name, the defMsg macro is used. Example 3 demonstrates how you can use defMsg to define messages. defMsg takes an object name, message name, and a method list. The method list may contain up to three method names representing the Before, Primary and After methods.

Example 3: Using defMsg to define messages

  defMsg  Window,\                       ;Define for Window object
          Refresh,\                      ;The Refresh msg
          <clrWin,,>                     ;To clear window

  defMsg  Border,\                       ;Define for Border object
          Refresh,\                      ;The Refresh msg
          <,,drawBdr>                    ;To draw border

  defMsg  Screen,\                       ;Define for Screen object
          Refresh,\                      ;The Refresh msg
          <, drawBackDrop, drawLabel>    ;To draw back drop and label

Many objects respond to the same message, but each will use a different set of methods. Recall that this set may contain local and inherited methods. Thus the combined Screen-Refresh method contains: clrWin, drawBackDrop, drawBdr, and drawLabel.

Listing Three (page 85) shows source code for the defMsg macro. Using the _Message structure, it assembles three entries containing a method address, or null pointer. In turn, this table is pointed to by the concatenated object-message name (that is, ScreenRefresh). This name is used to locate local methods for method combining at initialization time.

Example 4 shows how you might generalize window labeling by creating a Label object to handle the specifics. Label must be declared a Screen ancestor after Border. drawLabel is declared under Label's Refresh message as an After method. This produces the same combined method as before, but affords a greater degree of modularity that makes your code easier to enhance and maintain.

Example 4: Generalizing window labeling by creating a Label object to handle the specifics

  defMsg Label,\                     ;Define for Label object
         Refresh,\                   ;The Refresh msg
         <, ,drawLabel>              ;To draw label

  defObj label,\                     ;Define Label object
         <>,\                        ;As a base object
         <>,\                        ;With no inst vars
         <Refresh>                   ;Responds to Refresh msg

  defMsg Screen,\                    ;Define for Screen object
         Refresh,\                   ;The Refresh msg
         <,drawBackDrop,>            ;To draw back drop

  defObj Screen,\                    ;Define Screen object
         <Window, Border, Label>,\   ;As a derived object
         <Row1, 1, 1,\               ;With these inst vars
         Col1, 1, 0,\
         Row2, 1, 23,\
         Col2, 1, 79,\
         Color, 1, 34h,\
         BdrColor, 1, 30h,\
         MemSeg, 2, Nil>,\
        <Refresh>                    ;Responds to Refresh msg

Ancestor lists determine who contributes code, and in what order they contribute it. By changing the order of objects on an ancestor list, you alter an object's behavior. For example, if Screen's ancestor list was changed to Window, Label, Border, the combined Screen-Refresh message would instead become clrWin, drawBackDrop, drawLabel, and drawBdr. Consequently, the label would have been drawn prior to the border, thus overwriting it.

Object Initialization

Object initialization is a runtime activity invoked with the initObj macro. It transforms an object's ancestor list into a table where duplicates have been removed. It also creates combined methods for each of an object's declared messages.

If an object is not initialized, combined methods will not be created for it. This is desirable for objects such as Window, Border, and Label which are never directly sent messages, but which receive them only through the inheritance mechanism.

Example 5 shows how you initialize an object. Initialization order is significant. An object must be initialized before its ancestors so that method pointer information can be accessed before being overwritten.

Example 5: Initializing an object

  initObj Screen   ;Combine methods for Screen

Listing Four(page 85) is the source code for the initObj macro. It moves an object address into a register, and calls the initObject procedure. initObject performs a depth-first search of the ancestor list to assemble a temporary table of ancestor pointers, then builds combined method tables for each declared message. initObject then replaces the local method list pointers in the message table (assembled by defMsg) with pointers to combined methods.

Using Instance Data

Instance variable values may be retrieved with the getInst macro, and changed with the setInst macro. Example 6 demonstrates usage of these macros. getInst takes a destination register, an instance variable name, and an optional object name. setInst takes an instance variable name, a source register, an optional object name, and an optional variable size. The optional object name specifies the source of the instance data. If not provided, it is assumed that the SI register already contains the address of the source object. This would be the case after one use of the getInst or setInst macro that included an object name. Listing Five (page 86) is the source code for this macro.

Example 6: Retrieving instance variable values using the getInst macro

  getInst bl, Color, Screen   ;Fetch Screen color
  setInst BdrColor, bl        ;Copy it to BdrColor
  setInst Color, bl, Self     ;And Self's color

getInst assembles instructions to place the object address in a register, and based on the register size, moves instructions to copy data from the variable to the register. setInst assembles code to place the object address into a register, and move instructions to copy data from the register to memory. If the move is from memory to memory then the optional size argument must also be provided.

The getInst$ macro allows source object specification through an instance variable instead of by name or by the object variable Self. getInst$ and setInst$ work the same as getInst and setInst except the specified instance variable points to the source object. It is assumed that Self points to the object supplying this instance data.

Example 7 and Listing Six (page 87) show how you might use these macros. Master is one of Self's instance variables and points to some object. This allows the Color instance variable of any object pointed to by Master to be accessed. Local object variables just provide another mechanism for code generalization.

Example 7: Using the getInst$ and setinst$ macros

  getInst$ bl, Color, Master  ;Fetch color from object pointed to by Master
  setInst Color, bl, Self     ;Copy it to Self's color

An Example

As stated, the code presented in this article is part of a larger assembly language macro library implemented using object-oriented programming schemes. The program, supporting object-oriented concepts such as multiple inheritance, was developed using Microsoft's MASM 5.1 and provides windows, pop-up menus, mouse support, horizontal and vertical scroll bars, sound support, and the like. Because of space constraints, the complete source code for this example is available electronically.

Limitations

The use of object-oriented programming techniques with assembly language allows for the development of highly modular code. Thus, reusability and ease of maintenance of assembly code improves. However, taking advantage of these features requires some trade-offs.

Object initialization must be done prior to message passing. This slows program start-up, and adds a move and call instruction for every initialized object. To overcome this limitation, you could add code to write your initialized program to an executable file, possibly as a final step before software delivery. Once initialization is done, however, message passing becomes very efficient.

Another trade-off arises with message look-up. When a message is passed to an object, its message table is searched to locate a pointer to a combined method. Some search code optimizations could be made. For example, the test for null pointers could be removed, but program corruption may occur when an undeclared message is passed to an object. However, the most significant optimization you can make is through intelligent object class design, which suggests that you make complete use of inheritance, Before and After methods, and generic objects.

Although no formal comparisons with other object-oriented programming languages have been done, practical experience with this system has shown it to be robust. In addition, programming productivity increases were very noticeable after the system was learned.

References

Barstow, David R. et al. Interactive Programming Environments. New York, N.Y.: McGraw-Hill, 1984.

Bobrow, David G., et al. Common LISP Object System Specification. X3J13 Document 88-002R.

Cannon, Howard I. Flavors: A Non-Hierarchical Approach to Object-Oriented Programming. Unpublished paper, 1983.

Cox, Brad J. Object-Oriented Programming: An Evolutionary Approach. Reading, Mass.: Addison-Wesley, 1986.

Dorfman, Len. Object-Oriented Assembly Language. Blue Ridge Summit, Penn.: Windcrest, 1990.

Duncan, Ray. Advanced MS-DOS. Redmond, Wash.: Microsoft Press, 1986.

Hyde, Randall L. "Object-Oriented Programming in Assembly Language." Dr. Dobb's Journal (March 1990).

Moon, David. "Object-Oriented Programming with Flavors." Proceedings of OOPSLA '86.

Toutonghi, Michael. "21st Century Assembler." Computer Language (June, 1990).

Wegner, P., ed. The Object-Oriented Classification Paradigm: Research Directions in Object-Oriented Programming. Cambridge, Mass.: MIT Press, 1987.

Wyatt, Allen. Using Assembly Language. Carmel, Ind.: Que Corp., 1987.



_AN OBJECT-ORIENTED ASSEMBLY LANGUAGE MACRO LIBRARY_
by Donald J. McSwain



[LISTING ONE]
<a name="0088_0015">

Macro File: objects.mac

COMMENT % ===============================================================
Sets up stack, SI with object pointer, DX with message number, and calls
sendMsg procedure.
Passed: Obj - Name of receiving object; Msg - Message number
=========================================================================%
send    MACRO       Obj,Msg,ArgList
        pushArgs    ArgList             ;Push arguments onto stack
        IFIDN       <Obj>,<Self>        ;If object is Self
        mov         si,Wptr[Self]       ;Get object ptr from it
        ELSE
        IFDIF       <Obj>,<si>          ;If object ptr not in SI
        lea         si,Obj              ;Load SI with ptr to object
        ENDIF
        ENDIF

        IFDIF       <Msg>,<dx>          ;If msg number not in DX
        mov         dx,Msg              ;Put it in DX
        ENDIF

        call        sendMsg             ;Send message
        IFNB        <ArgList>           ;If arguments
        X       =   0                   ;Init stack depth counter
        IRP         Arg,<ArgList>       ;For every arg on stack
        X       =   X+2                 ;Increment depth counter
        ENDM
        add         sp,X                ;Reset stack pointer
        ENDIF
        ENDM

COMMENT % ===============================================================
Pushes up to ten arguments onto the stack.
=========================================================================%
pushArgs    MACRO      A0,A1,A2,A3,A4,A5,A6,A7,A8,A9
    IFB                <A0>                     ;If no more arguments
    EXITM                                       ;Exit macro
    ENDIF

    IFIDN              <A0>,<ax>                ;If arg in AX
    push               ax                       ;Push AX
    ELSE
    IFIDN              <A0>,<bx>                ;If arg in BX
    push               bx                       ;Push BX
    ELSE
    IFIDN              <A0>,<cx>                ;If arg in CX
    push               cx                       ;Push CX
    ELSE
    IFIDN              <A0>,<dx>                ;If arg in DX
    push               dx                       ;Push DX
    ELSE
    mov                bx,A0                    ;Else move into BX
    push               bx                       ;Push BX
    ENDIF
    ENDIF
    ENDIF
    ENDIF
    pushArgs           A1,A2,A3,A4,A5,A6,A7,A8,A9
    ENDM

COMMENT %  =============================================================
Finds the specified message for specified object.
Passed: Msg - Message number; Obj - Addr ptr to object structure
Passes: si - Pointer to combined method pointer
=========================================================================%
findMsg MACRO   Obj,Msg,Lbl
        LOCAL   fdmg1,fdmg2
        IFDIF       <Obj>,<si>      ;If object ptr is not in SI
        mov         si,Obj          ;Put it there
        ENDIF
        mov         di,Wptr[si].Instances
                                    ;Addr of msg tbl end
        mov         si,Wptr[si].Messages
                                    ;Addr of msg tbl beginning
fdmg1: lodsb                        ;Fetch msg number
       eq           al,Msg,fdmg2    ;Exit if message is found
       add          si,2            ;Else point to next message
       cmp          si,di           ;More messages?
       jb           fdmg1           ;If so continue search

       IFNB         <Lbl>           ;If label provided
       jmp          Lbl             ;Jump to it upon failure
       ENDIF
fdmg2:
       ENDM

Source File: objects.asm
    PUBLIC  sendMsg
COMMENT % ===================================================================
Sends the specified object the given message. This causes the execution of
the combined message for the object.
Passed: dx - Message number; si - Combined method ptr
=============================================================================%
sendMsg PROC    NEAR
        findMsg          si,dl,smg2      ;Search for message
        mov              si,Wptr[si]     ;Get method addr
        mov              cx,Wptr[si]     ;Get method count
smg1:   add              si,2            ;Point to method
        pushData         <cx,si>         ;Save loop cnt, addr ptr
        call             Wptr[si]        ;Execute method
        popData          <si,cx>         ;Restore addr ptr, loop cnt
        loop             smg1            ;Loop
smg2:   ret
sendMsg ENDP





<a name="0088_0016">
<a name="0088_0017">
[LISTING TWO]
<a name="0088_0017">


Include File: objects.inc

COMMENT % ==================================================================
Data structure used to hold pointers to an object's ancestors, messages, and
instance variables.
===========================================================================%
_Object STRUC
    Objects     DW  Nil
    Messages    DW  Nil
    Instances   DW  Nil
_Object ENDS

Macro File: objects.mac

COMMENT % ===================================================================
Defines an object.
Passed: Obj - Object name; Objs - Ancestor list; Instances - Instance
variable list; Messages - Message list
=============================================================================%
defObj  MACRO   Obj,Objs,Instances,Messages
        LOCAL   ObjTbl,MsgTbl,InstTbl

        ObjTbl      LABEL   WORD
        objsDef     Obj,<Objs>

        MsgTbl      LABEL   WORD
        msgsDef     Obj,<Messages>
        InstTbl     LABEL   WORD
        instDef     <Instances>
        ALIGN   2
        PUBLIC  Obj
        Obj _Object     <ObjTbl,MsgTbl,InstTbl>
        ENDM

COMMENT % ===================================================================
Defines objects.
Passed: Obj - Object name; Objs - Ancestor list
=============================================================================%
objsDef MACRO   Obj,Objs
        DW      Obj
        IRP     Obj,<Objs>
        DW      Obj
        ENDM
        ENDM

COMMENT % ====================================================================
Defines messages.
Passed: Obj - Object name; Messages - Message list
=============================================================================%
msgsDef MACRO   Obj,Messages
        IRP     Msg,<Messages>
        DB      Msg         ;Msg# identifies msg
        IFNDEF  Obj&&Msg
        DW      Nil         ;Obj has no local methods
        ELSE
        DW      Obj&&Msg        ;Obj has local methods
        ENDIF
        ENDM
        ENDM

COMMENT % ===================================================================
Defines instances variables.
Passed: Instances - Instance variable list
=============================================================================%
instDef MACRO   Instances
        X       =      0
        Y       =      0
        IRP            Inst,<Instances>
        defInst        Inst,%X,%Y
        ENDM
        ENDM

COMMENT % ====================================================================
Defines an instance variable.
Passed: Inst - Instance variable name; Cnt - Instance variable field number;
Size - Size of instance variable
=============================================================================%
defInst MACRO   Inst,Cnt,Size
        IFIDN       <Cnt>,<0>
        X       =   X+1
        ELSE
        IFIDN       <Cnt>,<1>
        X       =   X+1
        Y       =   Inst
        ELSE
        X       =   0
        defVar      Size,Inst
        ENDIF
        ENDIF
        ENDM

COMMENT % ====================================================================
Defines a data item.
Passed: Size - Size of data in bytes; Value - Value of data item
=============================================================================%
defVar  MACRO   Size,Value
        IFIDN       <Size>,<1>
        DB          Value
        ELSE
        IFIDN       <Size>,<2>
        DW          Value
        ELSE
        IFIDN       <Size>,<4>
        DD          Value
        ELSE
        IFIDN       <Size>,<8>
        DQ          Value
        ELSE
        IFIDN       <Size>,<10>
        DT          Value
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDM





<a name="0088_0018">
<a name="0088_0019">
[LISTING THREE]
<a name="0088_0019">

Include File: objects.inc

COMMENT % ==================================================================
Data structure used to hold pointers to a message's Before, Primary, and
After methods.
===========================================================================%
_Message    STRUC
    Before      DW  Nil
    Primary     DW  Nil
    After       DW  Nil
_Message    ENDS

Macro File: objects.mac

COMMENT % ====================================================================
Defines a message.
Passed: Obj - Object name; Msg - Message name; Methods - Method list
=============================================================================%
defMsg  MACRO   Obj,Msg,Methods
        ALIGN   2
        Obj&Msg _Message    <Methods>
        ENDM





<a name="0088_001a">
<a name="0088_001b">
[LISTING FOUR]
<a name="0088_001b">

Macro File: objects.mac

COMMENT % ====================================================================
Sets us SI to point to object, and calls initObject procedure.
=============================================================================%
initObj     MACRO   Obj
        lea         si,Obj          ;Pass object ptr
        call        initObject      ;Find all ancestors
        ENDM

Source File: objects.asm

    PUBLIC  initObject
COMMENT % ====================================================================
Initializes an object by flattening its inheritance lattice to create
combined methods for its messages.
Passed: si - Addr ptr to object structure
=============================================================================%
initObject  PROC    NEAR
          lea       di,Buffer           ;Get buffer addr
          call      findAncestors       ;Find/Save all ancestors
          call      evalMsgs            ;Evaluate messages
          ret
initObject  ENDP

COMMENT % ====================================================================
Finds all of an object's ancestors and saves them for use by the message
evaluator.
Passed: bx - Addr ptr to message table (end of object table); di - Addr ptr
to temporary object table; si - Addr ptr to object structure
=============================================================================%
findAncestors   PROC    NEAR
        pushData        <bx,si>                  ;Save obj ptr
        mov             bx,Wptr[si].Messages     ;Get addr ptr to msg tbl
        mov             si,Wptr[si].Objects      ;Get addr of object tbl
        movsw                                    ;Move obj ptr
fas1:   eq              bx,si,fas2               ;Exit if end of tbl
        push            si
        mov             si,Wptr[si]              ;Get next object
        call            findAncestors            ;Find others
        pop             si
        add             si,2
        jmp             fas1                     ;More in tbl - Loop
fas2:   mov             Wptr[di],Nil             ;Mark end of list
        popData         <si,bx>                  ;Restore obj ptr
        ret
findAncestors   ENDP

COMMENT % ====================================================================
Creates combined methods for all of an object's messages.
Passed: si - Addr ptr to object structure
=============================================================================%
evalMsgs    PROC        NEAR
        mov             bx,Wptr[si].Messages    ;Get addr of message tbl
        mov             cx,Wptr[si].Instances   ;Get addr of instance tbl
ems1:   mov             dl,Bptr[bx]             ;Get msg number
        xor             dh,dh
        call            combineMethods          ;Combine methods
        add             bx,3                    ;Point to next tbl entry
        neq             bx,cx,ems1              ;More in tbl? - loop
        ret
evalMsgs    ENDP

COMMENT % ====================================================================
Combines methods for all included objects.
Passed: dx - Message number; si - Addr ptr to object structure
=============================================================================%
combineMethods  PROC    NEAR
       push             bx
       mov              ?Compiled,Nil           ;Clear compiled flag
       mov              bx,Wptr[CompileStart]   ;Get start of combined mthd
       mov              Wptr[CompilePtr],bx ;Init location ptr
       mov              di,Nil                  ;Zero count word
       call             saveMethodAddr          ;Save value
       call             saveBefores             ;Save Before methods
       mov              bx,Primary              ;Select Primary method type
       lea              di,Buffer               ;Get addr of tmp object tbl
       mov              di,Wptr[di]             ;Get tbl entry
       call             saveMethod              ;Save method
       call             saveAfters              ;Save After methods
       null             ?Compiled,cms1          ;Nothing compiled? - Exit
       call             updatePtrs              ;Update message, location ptrs
cms1:  pop              bx
       ret
combineMethods  ENDP

COMMENT % ====================================================================
Updates the message and location pointers.
Passed: dx - Message number; si - Addr ptr to object structure
=============================================================================%
updatePtrs  PROC        NEAR
       push             si
       findMsg          si,dl                   ;Find message
       mov              di,Wptr[CompileStart]   ;Get ptr to combined method
       mov              Wptr[si],di             ;Change message ptr

       mov              bx,Wptr[CompilePtr]     ;Get current compile location
       mov              Wptr[CompileStart],bx   ;Reset start of combined mthd
       pop              si
       ret
updatePtrs  ENDP

COMMENT % ====================================================================
Save the Before method type for the specified object.
Passed: dx - Message number
=============================================================================%
saveBefores PROC    NEAR
        push        si
        mov         bx,Before       ;Select Before method type
        lea         si,Buffer       ;Get addr of tmp object tbl
        mov         di,Wptr[si]     ;Get tbl entry
sbs1:   call        saveMethod      ;Save method
        add         si,2            ;Point to next tbl entry
        mov         di,Wptr[si]     ;Get next tbl entry
        identity    di,sbs1         ;More in table? - loop
        pop         si
        ret
saveBefores ENDP

COMMENT % ===================================================================
Save the After method type for the specified object.
Passed: dx - Message number
=============================================================================%
saveAfters  PROC    NEAR
        pushData    <cx,si>
        mov         bx,After        ;Select After method type
        lea         si,Buffer       ;Get addr of tmp object tbl
        mov         cx,si           ;Save addr of object tbl
sas1:   mov         ax,Wptr[si]     ;Get tbl entry
        null        ax,sas2         ;Null? - End of tbl, exit
        add         si,2            ;Point to next tbl entry
        jmp         sas1            ;Loop
sas2:   sub         si,2            ;Point to previous tbl entry
        mov         di,Wptr[si]     ;Get next tbl entry
        call        saveMethod      ;Save method
        neq         si,cx,sas2
        popData     <si,cx>
        ret
saveAfters  ENDP

COMMENT % ====================================================================
Save the specified method for specified object.
Passed: bx-Method type; di-Addr ptr to object structure; dx-Message number
=============================================================================%
saveMethod  PROC    NEAR
        pushData    <bx,di,si>
        findMsg     di,dl,svm3      ;Find message
        mov         di,Wptr[si]     ;Get method tbl addr ptr
        null        di,svm3         ;Exit if no local methods
        mov         di,Wptr[di+bx]  ;Get method addr ptr
        null        di,svm3         ;Exit if no message
        mov         bx,Wptr[CompileStart]   ;Get start of combined mthd
svm1:   eq          bx,Wptr[CompilePtr],svm2
        eq          di,Wptr[bx],svm3    ;Exit if duplicate method
        add         bx,2            ;Point to next addr
        jmp         svm1            ;Check next addr
svm2:   call        saveMethodAddr  ;Save method addr
svm3:   popData     <si,di,bx>
        ret
saveMethod  ENDP

COMMENT % ====================================================================
Save value at current compile location, and increments location pointer.
Passed: di - Value to store
=============================================================================%
saveMethodAddr  PROC    NEAR
    mov                 ?Compiled,1         ;Set compiled flag
    mov                 bx,Wptr[CompilePtr] ;Get ptr to combined mthd end
    mov                 Wptr[bx],di         ;Save value
    add                 bx,2                ;Point to next location
    mov                 Wptr[CompilePtr],bx ;Reset location ptr
    mov                 bx,Wptr[CompileStart]   ;Get ptr mthd count
    mov                 di,Wptr[bx]         ;Get mthd count
    inc                 di                  ;Increments mthd count
    mov                 Wptr[bx],di         ;Save value
    ret
saveMethodAddr  ENDP





<a name="0088_001c">
<a name="0088_001d">
[LISTING FIVE]
<a name="0088_001d">

Macro File: objects.mac

COMMENT % ====================================================================
Gets an object's instance variable.
Passed: Dest- Destination register; Var - Instance variable name;
Obj - Source object
=============================================================================%
getInst MACRO   Dest,Var,Obj
        IFNB            <Obj>
        IFIDN           <Obj>,<Self>
        mov             si,WORD PTR[Self]
        mov             si,WORD PTR[si].Instances
        ELSE
        IFIDN           <si>,<Obj>
        mov             si,WORD PTR[si].Instances
        ELSE
        mov             si,Obj&.Instances
        ENDIF
        ENDIF
        ENDIF
        mov             Dest,[si+Var]
        ENDM

COMMENT % ====================================================================
Sets an object's instance variable.
Passed: Var - Instance variable name; Source - Source register; Obj - Source
object; Size - Size of data
=============================================================================%
setInst       MACRO       Var,Source,Obj,Size
              IFNB        <Obj>
              IFIDN       <Obj>,<Self>
              mov         si,WORD PTR[Self]
              mov         si,WORD PTR[si].Instances
              ELSE
              mov         si,Obj&.Instances
              ENDIF
              ENDIF
              setInst_     Var,Source,Size
              ENDM

COMMENT % ====================================================================
Assembles move instruction based on source register.
=============================================================================%
setInst_      MACRO        Var,Source,Size
        IFIDN              <Source>,<al>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<ah>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<bl>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<bh>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<cl>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<ch>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<dl>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<dh>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<ax>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<bx>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<cx>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<dx>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<di>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Source>,<si>
        mov                WORD PTR[si+Var],Source
        ELSE
        IFIDN              <Size>,<1>
        mov                BYTE PTR[si+Var],Source
        ELSE
        IFIDN              <Size>,<2>
        mov                WORD PTR[si+Var],Source
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDIF
        ENDM





<a name="0088_001e">
<a name="0088_001f">
[LISTING SIX]
<a name="0088_001f">

Macro File: objects.mac

COMMENT % ====================================================================
Gets an object's instance variable, but object is pointed to by one of
Self's instance variables.
Passed: Dest- Destination register; Var - Instance variable name;
Obj - Source object instance variable
=============================================================================%
getInst$    MACRO   Dest,Var,Obj
    mov             si,WORD PTR[Self]
    mov             si,WORD PTR[si].Instances
    mov             si,[si+Obj]
    mov             si,WORD PTR[si].Instances
    mov             Dest,[si+Var]
    ENDM

COMMENT % ====================================================================
Sets an object's instance variable, but object is pointed to by one of
Self's instance variables.
Passed: Var - Instance variable name; Source - Source register; Obj - Source
object instance variable; Size - Size of data
=============================================================================%
setInst$    MACRO   Var,Source,Obj,Size
    mov             si,WORD PTR[Self]
    mov             si,WORD PTR[si].Instances
    mov             si,[si+Obj]
    mov             si,WORD PTR[si].Instances
    setInst_        Var,Source,Size
    ENDM




[Example 1]

send Screen,Refresh,DoubleBdr  ;Send Screen a Refresh msg
send Self,Read                 ;Send Self a Read msg
send Self,<WORD PTR[bx]>       ;Send Self msg pointed to by
                               ;  BX register


[Example 2]

defObj Window,\     ;Define Window object
       <>,\         ;As a base object
       <>,\         ;With no inst vars
       <Refresh>    ;Responds to Refresh msg

defObj Border,\     ;Define Border object
       <>,\         ;As a base object
       <>,\         ;With no inst vars
       <Refresh>    ;Responds to Refresh msg

defObj Screen,\     ;Define Screen object
       <Window,Border>,\  ;As a derived object
       <Row1,1,1,\  ;With these inst vars
       Col1,1,0,\
       Row2,1,23,\
       Col2,1,79,\
       Color,1,34h,\
       BdrColor,1,30h,\
       MemSeg,2,Nil>,\
       <Refresh>     ;Responds to Refresh msg



[Example 3]

defMsg Window,\     ;Define for Window object
       Refresh,\    ;The Refresh msg
       <clrWin,,>   ;To clear window

defMsg Border,\     ;Define for Border object
       Refresh,\    ;The Refresh msg
       <,,drawBdr>  ;To draw border

defMsg Screen,\     ;Define for Screen object
       Refresh,\    ;The Refresh msg
       <,drawBackDrop,drawLabel>
                    ;To draw back drop and label


[Example 4]

defMsg Label,\        ;Define for Label object
       Refresh,\      ;The Refresh msg
       <,,drawLabel>  ;To draw label

defObj label,\        ;Define Label object
       <>,\           ;As a base object
       <>,\           ;With no inst vars
       <Refresh>      ;Responds to Refresh msg

defMsg Screen,\        ;Define for Screen object
       Refresh,\       ;The Refresh msg
       <,drawBackDrop,> ;To draw back drop

defObj Screen,\        ;Define Screen object
       <Window,Border,Label>,\
                       ;As a derived object
       <Row1,1,1,\     ;With these inst vars
       Col1,1,0,\
       Row2,1,23,\
       Col2,1,79,\
       Color,1,34h,\
       BdrColor,1,30h,\
       MemSeg,2,Nil>,\
       <Refresh>         ;Responds to Refresh msg




[Example 5]

initObj Screen   ;Combine methods for Screen


[Example 6]

getInst bl,Color,Screen  ;Fetch Screen color
setInst BdrColor,bl      ;Copy it to BdrColor
setInst Color,bl,Self    ;And Self's color



[Example 7]

getInst$ bl,Color,Master  ;Fetch color from object
                          ; pointed to by Master
setInst  Color,bl,Self    ;Copy it to Self's color


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.