Array and Struct Constants in MASM



Hi All,
One feature I added early on in HLA was the ability to create
compile-time array and structure constants. This has proven extremely
useful for a wide variety of sophisticated macros.

Though MASM doesn't support the notion of compile-time composite-type
constants that you can easily manipulate, there are a couple of hacks
you can to do *simulate* such things. I've been wanting to port some
more of the HLA Standard Library header code to MASM so MASM users
could better use the HLA Standard Library, but I really needed
compile-time arrays and structures to pull this off. So I created the
following set of macros to help me with this task and I'm passing these
macros along to those of you who are interested in creating some
sophisticated macros with MASM.
Enjoy!
Randy Hyde

; Randy Hyde's macro package to support array and struct constants in
MASM

.686p
.mmx
.xmm
.model flat, syscall


; Some utility macros:
;
;
; makeName -
; Creates an identifier by concatenating the second numeric operand
converted to
; string form to the end of the name passed as the first operand,
with "$$" between
; them (just to help make the name unique).
;
; Syntax (used as a macro function):
;
; makeName( someName, someValue )
;
; e.g.,
;
; makeName( i, %(5+1)) = 0
;
; produces
;
; i$$6 = 0

makeName macro theName,theSuffix
exitm <theName&$$&theSuffix>
endm

; makeText -
; converts a list of operands to a single text object

makeText macro mtxt:VARARG
exitm <mtxt>
endm

catNames macro leftName:REQ, rightName:REQ, suffix
ifb <suffix>
exitm @catstr( %leftName, <$$>, %rightName)
else
exitm @catstr( %leftName, <$$>, %rightName, <$$>, suffix )
endif
endm

; printint-
; prints operand as an integer value with preceding message
; (actually, prints any constant value, not just integers)
;
; Mainly used for debugging purposes.

printint macro msg,integer
%echo msg integer
endm


; @ArgCount -
; counts the number of macro arguments supplied to it.
; (straight out of the MASM manual.)

@ArgCount macro arglist:VARARG
local count
count = 0
for arg, <arglist>
count = count + 1 ;; Count the arguments
endm
exitm %count
endm


; @extract -
;
; Extracts an item from a multi-element text equate.
; Given a list like <5,4,3,2,1,0>, an invocation of
; the form @extract( 2, theList) returns <3> (items
; in the list are numbered starting at position zero).

@extract macro whichField,theStruct
local i
local result
local count
local returnResult

count = whichField
if whichField GT @ArgCount(theStruct)
echo "Bad field number specified in @extract macro"
count = 1
endif

count = count - 1
i = 0
for result,<theStruct>
if i eq count
returnResult textequ %result
endif
i = i + 1
endm
exitm %returnResult
endm



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; dclArrayConst-
;
; Declares a compile-time constant array.
;
; This macro generations the following symbols:
;
; Elements of the array:
;
; <aryName>$$0..<aryName>$$n, where arySize = n+1
;
; Size of the array (# of elements)
;
; <aryName>$$size
;
; This macro also creates an "accessor" macro that you can use to
; access elements of the array and dump the elements of the constant
; array to memory.
;
;
; Example of usage:
;
; ; Array Declaration:
;
; dclArrayConst myArray, 16
;
;
; ; Accessing individual elements of the array:
;
; myArray(5) textequ %(myArray(4) + 2)
;
;
; ; Using the array size:
;
; mov ecx, myArray(<size>)
;
;
; ; How to dump the constant array data to an actual run-time array.
; ; The following emits each element of the array to the program
; ; using the type specified as the second parameter, e.g.,
; ; myArray(<emit>, byte) emits all of the values in the constant
; ; array as byte values.
;
; myArray(<emit>,type)


dclArrayConst macro aryName, arySize
local i
local elementName
local names
local origName

; Convert the actual argument name to string form so we
; can use it without it being expanded on the spot:

origname textequ <aryName>

; Create a set of text equates, one per array element,
; to hold this constant array's values. Each name takes
; the form << aryName$$n >> where n=0..(arySize-1)

i = 0
while i lt arySize
makeName( aryName, %i ) textequ <0>
i = i + 1
endm

; Create a constant we can use to determine the # of array
elements:

&aryName&$$size = arySize

; Create an accesor macro for this constant array.
; The main way this macro is used is to access constant array
elements,
; e.g.,
;
; aryName(n)
;
; Which returns the nth (zero-based) element of the array. In this
form,
; the macro simply replaces itself with a string of the form
<<aryName$$n>>
; which references one of the above-defined text equates.
;
; A second form of this macro allows you to dump the array's values
to
; a sequence of memory variables. This invocation takes the
following
; form:
;
; aryName( <emit>, type )
;
; where "type" is one of the standard MASM data types, e.g., byte
or word.
; This invocation emits a sequence of data definition directives,
the
; operands of each containing that particular array element's
value.

ifdef aryName
purge aryName
endif
aryName macro aryIndex:req, elementType

ifidni <aryIndex>,<emit>

ifnb <elementType>

i = 0
while i lt arySize
elementType makeName( <%origName>, %i )
i = i + 1
endm

else

echo Expected type specification as second argument
.err
exitm <>
endif

else

ifnb <elementType>

echo Unexpected second operand in constant array access
.err

endif
exitm makeName(%&aryName&,aryIndex )

endif

endm

endm








;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; dclStructConst-
;
; Declares a structure type for use by compile-time constants and
; run-time objects.
;
; Syntax:
;
; dclStructConst structName, <list of fields>
;
; <list of fields> takes the following form:
;
; fieldName:type, fieldname:type, ...
;
; This macro creates a MASM struct declaration and also creates
; a set of symbols that this macro package can use to manipulate
; struct constants.
;
; ********************************************************************
; Important limitation!
; You cannot have arrays or other structured types as field types.
; Maybe with a lot of work, this limitation could be overcome. But the
; current macro package doesn't support this. If you really want to
; use such facilities, consider switching to a more powerful assembler
; (specifically, HLA).
; ********************************************************************
;
;
; Produces:
;

dclStructConst macro structName, fields:VARARG
local eachField
local curFieldName
local curType
local curValue
local posn
local fieldCnt
local fieldList
local origStructName
local sizeOfStruct
local i

origStructName textequ <structName>
fieldCnt = 0;
for eachfield, <fields>

fieldCnt = fieldCnt + 1

endm


; Define a constant specifying the field count:

structName&$$size = fieldCnt

; Create two arrays to hold the field names and the field types.

dclArrayConst &structName&$$names, fieldCnt
dclArrayConst &structName&$$types, fieldCnt

; Generate a list of text equates for each of the fields in this
struct:

i = 0
for eachfield, <fields>

; Separate the field name and the type name in each field
declaration.
; E.g., converts "a:byte" into "a" and "byte".

posn = @instr( 1, <eachfield>, <:> )
curFieldName textequ <@substr(<eachfield>, 1, posn-1)>
curType substr <eachfield>, posn+1

; Initialize the array elements that let us access the
; field names and types later on:

structName&$$names(%i) substr <eachfield>, 1, posn-1
structName&$$types(%i) substr <eachfield>, posn+1

; Define the structure constants

catNames( origStructName, curFieldName ) textequ %i
catNames( origStructName, curFieldName, <index> ) = i

i = i + 1

endm

; Emit an accessor macro that we can use to access fields of this
record:

ifdef structName
purge structName
endif
sizeOfStruct = i
structName macro fieldName:req, typename

; Check for a special declaration of the form:
;
; structName( <dcl>, uniqueName )
;
; And generate a MASM structure declaration for uniqueName that
; is compatible with this struct constant:

ifidni <fieldName>,<dcl>

ifb <typename>

echo Error, expected type declaration name for
declaration
.err

else

; Here's where we create an appropriate STRUCT for
; the user, based on the fields of our constant.

typename struct

i = 0
while i lt sizeOfStruct

; Generate the field name, by looking at the
; ..$$names$$n array elements:

curFieldName catstr
origStructName,<$$names$$>,%i

; Generate the type name, by looking at the
; ..$$types$$n array elements:

curType catstr origStructName,<$$types$$>,%i

;Okay, emit the actual field for the STRUCT
here

curFieldName curType ?
i = i + 1

endm

typename ends
endif

else

; None of the following options allow a second operand:

ifnb <typename>

echo Error, unexpected second argument
.err
exitm <>

endif

; Check for the special <emit> option, which emits
; the struct constant as data to the run-time code:

ifidni <fieldname>,<emit>

i = 0
while i lt sizeOfStruct

; Generate the name of the field, that we can use
; to expand into the field's value later on:

curFieldName catstr origStructName,<$$names$$>,%i

; Get the type of this field:

curType catstr origStructName,<$$types$$>,%i

; Emit a data declaration of the specified type
; and expand the field's name into the
corresponding
; constant value for use by the assembler:

curType @catstr( <%origStructName>, <$$>,
<%curFieldName>)
i = i + 1

endm


; If none of the above, then assume it's a field access:

else

exitm makeName(%&origStructName&,fieldName )

endif;

endif
endm


endm



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Stack operations
;
; constPush-
; Pushes some value onto the stack.

constPush macro value:req, stk:req
stk catstr( <%value>, <:>, <stk> )
endm

; constPop-
; Removes the item from the top of stack

constPop macro stk:req
local result
local posn
posn = @instr( 1, stk, <:> )
ife posn

echo Error, empty stack in pop operation
.err
result textequ <>

else

result substr stk, 1, posn-1
stk substr stk, posn+1

endif
exit <result>
endm

; constTOS-
; Returns item on the top of stack without popping it.

constTOS macro stk:req
local result
local posn
posn = @instr( 1, stk, <:> )
ife posn

echo Error, empty stack in constTOS operation
.err
result textequ <>

else

result substr stk, 1, posn-1

endif
exit <result>
endm





;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Sorting an array (simple bubblesort, feel free to improve this):
;
; Invocation:
;
; sort Array, size, comparison_macro
;
; The comparison macro must be a MASM function macro that
; takes two operands (a "left" value and a "right" value).
; These arguments will be two elements from the array and
; the macro must compare the left value against the right
; value and return true if the left value is greater than
; the right value.

sort macro arrayToSort,arraySize,elmntGT
local i
local bnd
local didswap
local temp
local left, right

bnd = arraySize - 1
didswap = 1
while didswap eq 1

didswap = 0
i = 0
while i LT bnd

left textequ %arrayToSort(%i)
right textequ %arrayToSort(%(i+1))
if elmntGT( %left, %right )

temp textequ %left
arrayToSort(%i) textequ %right
arrayToSort(%(i+1)) textequ %temp
didswap = 1

endif
i = i + 1

endm
bnd = bnd - 1

endm

endm



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Quick demo of the above macros:

.data

dclStructConst myStruct, a:byte, d:word, g:dword

; Initialize each of the fields of the above struct constant:

myStruct(a) textequ <'a'>
myStruct(d) textequ <1234h>
myStruct(g) textequ <67890123h>

; Create a MASM data type (mystruct_t) that is equivalent to the
myStruct
; struct type:

myStruct(<dcl>,mystruct_t)



; Declare a couple of constant arrays:

dclArrayConst constArray, 8
dclArrayConst sortedArray, 8


; Initialize all the elements of this array:

i = 0
while i lt constArray(size)
constArray(%i) textequ %(constArray(size)-i)
i = i + 1
endm


; Make a copy of the array and sort the copy:

i = 0
while i LT constArray(size)
sortedArray(%i) textequ constArray(%i)
i = i + 1
endm

; A comparison macro used to compare a simple integer array:

myCmp macro left, right
exitm <left GT right>
endm

sort <sortedArray>,sortedArray(size),myCMP






; Create memory variables for all the objects manipulated here:


memArray equ this byte
constArray(emit, byte)

sorted equ this word
sortedArray(emit, word )

mys equ this mystruct_t
myStruct(emit)




.code
Main proc near32

; The following all load immediate constants into the register:

mov al, myStruct(a)
mov ax, myStruct(d)
mov eax, myStruct(g)
mov al, constArray(0)
mov al, constArray(1)
mov al, sortedArray(0)
mov al, sortedArray(1)

; The following all load memory locations into the register:

mov eax, mys.g
mov ax, sorted[ebx*2]
mov al, memArray[ebx*1]



Main endp

end Main

.



Relevant Pages

  • Re: Help end the testing tedium please
    ... The new macro is crashing at: ... The filename and sheet name look fine. ... two arguments needed for "Consolidate". ... the file really were test1 thru test60, would the array statement need ...
    (microsoft.public.excel.programming)
  • Re: Is it possible to run a macro in tandem with, or right after,
    ... macro, or by having one macro call several others that each perform one piece ... 'sub procedure can access the rowHgts array from this ... Dim rowRng As Range ... Dim outputRow As Long ...
    (microsoft.public.excel.programming)
  • Re: Difference between C and advanced C
    ... static and type qualifiers in parameter array declarators ... trailing comma allowed in enum declaration ... additional predefined macro names ... additional strftime conversion specifiers ...
    (comp.lang.c)
  • Re: Code wont execute Main Macro Calls - non breaking spaces
    ... I am going to attach my first array which is the same as you have ... So each macro will execute one after the other. ... separated each find string and each replace string for the sake of ...
    (microsoft.public.word.vba.general)
  • Re: hardware time stamping with optional structs in data area
    ... have 2 bits defined so you only need an array of 4 entries. ... How can I get some code executed during the initialization of the IP ... macro definitions, ... struct A { ...
    (Linux-Kernel)