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.
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 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.
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.
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.
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.
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.
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.
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 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.
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.
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.
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.
getInst$ bl, Color, Master ;Fetch color from object pointed to by Master setInst Color, bl, Self ;Copy it to Self's color
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.
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.
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]
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
[LISTING TWO]
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
[LISTING THREE]
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
[LISTING FOUR]
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
[LISTING FIVE]
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
[LISTING SIX]
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