COMMENT $ CMACROS - assembly macros for interfacing to HLL (C)Copyright Microsoft Cor. 1990 $ ; Revision History ; 6.0 Initial Release of cmacros for MASM 6.0 ; COMMENT $ ; Note: There are some differences between this version of CMACROS ; and previous releases. The most signification is the fact ; that locals and parameter names are now scoped to the body ; of a procedure. To help ease this problem we have introduced ; a new directive cRet. This means that the following transformation ; can occur on your source to deal with parameters no referenced ; in the body of a procedure ; ; cProc cProc ; locals,parms locals,parms ; cBegin cBegin ; .... ... ; cEnd cRet ; error code referencing error code referencing ; locals and parms locals and params ; cEnd ; ; The major reason for making locals and parameters scoped was ; the percieved benifit of error checking for defined labels in ; the procedure and the addition of codeView information on locals ; and parameters for functions. $ .xcref ; Get rid of alot of symbols ; ??_out - output the given message to the console unless ?QUIET ; has been specified ; ; usage: ; ??_out ; where: ; is the message to output ??_out macro t ifndef ?QUIET echo t endif endm ; outif - output msg if name is non-zero. If name is undefined, ; set name =0, else set name to the default value. ; ; usage: ; outif name, defval, onmsg, offmsg ; where: ; name name of symbol ; defval default value to give symobl if not defined. ; if blank, then 0 will be used ; onmsg text to display if symbol is non-zero ; offmsg text to display if symbol is zero ; outif macro name:req, defval:=<0>, onmsg, offmsg ifndef name name = defval endif if name name = 1 ifnb ??_out endif else ifnb ??_out endif endif endm ; ??error - output message and generate an assembly time error ; ; usage: ; ??error ; where: ; t is the text to output ; ??error macro msg echo e r r o r ------ msg ;; to console .err e r r o r ------ msg ;; forced error by assembler endm ??_out ??_out ; ; Determine the memory model for cmacros. Default to small if ; no other model has been specified. ; ifdef ?SMALL memS=1 endif ifdef ?MEDIUM memM=1 endif ifdef ?COMPACT memC=1 endif ifdef ?LARGE memL=1 endif ifdef ?HUGE memH=1 endif outif memS,0, outif memM,0, outif memC,0, outif memL,0, outif memH,0, memMOD = memS + memM + memL + memC + memH if memMOD ne 1 if memMOD eq 0 memS=1 ; Assume small model outif memS,0, else ??error endif endif sizec = memM + memL + memH ; Large Code models sizeC = sizec sized = memC + memL + memH ; Large Data models sizeD = sized ; ; Inform user of any other options selected ; outif ?DF,0, outif ?DFDATA,0, outif ?DFCODE,0, outif ?TF,0, outif ?WIN,1, outif ?COW,0, outif ?PLM,1, outif ?NOATOMIC,0, outif ?NODATA,0, ifdef ?CHKSTK ifdef ?CHKSTKPROC ??_out else ??_out endif else ?CHKSTK = 0 endif ifndef ?DOS5 ?DOS5 = 0 endif ; ; Setup some local variables to the Cmacros package ; ??CM_state = 0 ; 0 - inactive, 1-cProc, 2-cBegin, ??CM_RegSaveList textequ <> ; List of saved registers ??CM_ArgList textequ <> ; ; This function is used to paste together two text items to ; get a third text item. ; ??CM_Paste macro arg1:req, arg2:req exitm endm ; ; This function is used to create a text macro containning the ; n-th local to a fuction definition. ; ??CM_addLocal macro arg1:req ??CM_Paste(??CM_local, %??CM_localCount) textequ ??CM_localCount = ??CM_localCount + 1 endm ; ; This function is used to create a text macro containning the ; n-th parameter to a function definition ; ??CM_addParm macro arg1:req if ??CM_argCount EQ 20 .err endif ??CM_Paste(??CM_arg, %??CM_argCount) textequ <, arg1> ??CM_argCount = ??CM_argCount + 1 endm ; ; This macro creates the prologue code for a cmacro defined function. ; ; Prologue sequences ; cPrologue macro procname, flags, cbParms, cbLocals, reglist, userparms ?ba=0 ;;not in a procedure ?pu=0 ;;initial public setting ?ia=0 ;;no special prolog/epilog ?rp=0 ;;no register parameters ??CM_UserDoesFrame=0 ;;don't use makeframe ?ff=0 ;;don't force frame setup ?pas=0 ;;process register save list ?pcc=?PLM ;;calling convention (C or PL/M) ;; ;; Look at all of the user parameters and make appropriate decisions ;; for x, ifdef ??CM_Paste(??_cproc_, x) ??CM_Paste(??_cproc_, x) else ??error .err endif endm ;; ;; Now start some error checking ;; if (??CM_Atomic eq 1) and (??CM_NoData eq 0) ;;ATOMIC requires NODATA ??error ??CM_Atomic = 0 ;;clear the ATOMIC keyword endif if flags AND 020h ;;if a far procedure if ??CM_WinFarProc ;;if windows ife ??CM_Atomic ;;if not ATOMIC ife ?COW ;; COW dos not save DS ?ia=2 ;; adjust locals for saved ds ; ?pas = ?pas and (not ?ds) ;;no need for extra save endif endif endif else ??CM_WinFarProc=0 ;;not a far windows procedure endif ; ?pas = ?pas and (not (?sp+?cs+?ss)) ;;make no sense to save these if ??CM_UserDoesFrame ;;don't save these if user frame ; ?pas = ?pas and (not (?bp+?si+?di)) endif if ??CM_UserDoesFrame ;;if user frame if ??CM_NoData ??error ??CM_UserDoesFrame=0 endif endif if ??CM_UserDoesFrame ;;if user frame if ?rp ;;If register parameters ??error ??CM_UserDoesFrame=0 endif endif ?cpd=0 ;;terminate current proc def ifidn , ;;if nogen, then cannot have locals if cbLocals + cbParms + ?rp ;; saved regs, or parmRs ??_out endif else ;;else must generate a frame ;; ;; Allow the user to specify his own routine which is going to ;; do the frame set-up for this procedure ;; if ??CM_UserDoesFrame ;;if user frame code specified ?mf c,cbLocals,%?po ;; call user's makeframe for reg,reglist ;; save specified registers push reg endm else if ??CM_WinFarProc ;;if a far windows procedure ife ??CM_NoData ;;if not NODATA, mov ax,ds ;; then set AX = current ds, and ;@ nop ;; leave room for MOV AX,1234h ;@ endif ife ??CM_Atomic ;;if not ATOMIC, far frame must be set ife ?DOS5 ;;if not DOS5, then set far frame flag inc bp ;; by incrementing the old bp ;@ endif push bp ;@ mov bp,sp ;@ ife ?COW ;; save DS not needed for CW push ds ;@ endif else ;;ATOMIC procedure if ?ff+cbLocals+cbParms+?rp ;;if any locals or parameters push bp ;; then must set frame pointer ;@ mov bp,sp ;; to be able to access them ;@ endif endif ife ??CM_NoData ;;if not NODATA, then AX should mov ds,ax ;; have the ds to use ;@ endif else ;;not windows. use standard prolog if ?ff+cbLocals+cbParms+?rp ;;if any locals or parameters push bp ;; then must set frame pointer ;@ mov bp,sp ;; to be able to access them ;@ endif endif if ?rp ;;if parmR's, push them before ??CM_UserDoesFrame=0 ;; allocating locals and saving rept ?rp ;; the autosave registers uconcat mpush,,?rp,%??CM_UserDoesFrame ??CM_UserDoesFrame=??CM_UserDoesFrame+1 endm endif if cbLocals ;;if locals to allocate if ?CHKSTK ;;if stack checking enabled ifdef ?CHKSTKPROC ;;if user supplied stack checking ?CHKSTKPROC %cbLocals ;; invoke it with bytes requested else mov ax,cbLocals ;;invoke default stack checking ;@ ife cc call _chkstk ;@ else call chkstk ;@ endif endif else ;;no stack checking sub sp,cbLocals ;; allocate any local storage ;@ endif endif endif for reg,reglist ;;save autosave registers push reg endm endif ;; No Gen ifdef ?PROFILE ;;if profiling enabled if c ;; and a far procedure call StartNMeas ;; invoke profile start procedure ;@ endif endif exitm %(cbLocals+?ia) endm ; ; This macro will define the epilogue sequences for CMACROS ; functions. ; ; Epilog sequences cEpilog macro procname, flags, cbParms, cbLocals, reglist, userparms if ??CM_nogen ;; Nogen the cEnd -- exitm endif ?ba=0 ;;no longer in a procedure ifidn , ;;if nogen, then cannot have parmRs if a+r+lc ;; locals, or saved registers ??_out endif else ;;else must remove the frame ifdef ?PROFILE ;;if profiling enabled if flags AND 020H ;; and a far procedure call StopNMeas ;; invoke profile stop procedure endif ;; (doesn't trash DX:AX) endif ;; ?PROFILE for reg,reglist ;;restore autosaved registers pop reg endm if ??CM_UserDoesFrame ;;if to use the "makeframe" procedure db 0c3h ;; near return to user's makeframe @ else if ??CM_WinFarProc ;;if far win proc, use special epilog ife ??CM_Atomic ;;if not ATOMIC, bp was pushed ife ?COW ;; restore DS not needed for CW if (?TF eq 0) or (cbLocals+?rp) ;;if cannot assume valid sp lea sp,-2+[bp] ;; or locals or parmR's, get valid SP @ endif pop ds ;;restore saved ds and bp @ else if (?TF eq 0) or (cbLocals+?rp) ;;if cannot assume valid sp mov sp,bp endif endif pop BP ;; @ ife ?DOS5 ;;if not DOS5, bp was dec BP ;; incremented to mark far frame @ endif else ;;ATOMIC frame was set up if memS32 leave else if (?TF eq 0) or (cbLocals+?rp) ;;if cannot assume valid sp mov SP,BP ;; or locals or parmR's, get valid SP @ endif if cbLocals+cbParms+?rp pop BP ;@ endif endif endif else ;;non-windows standard epilog if ?ff+cbLocals+cbParms+?rp ;;if any parameters if (?TF eq 0) or (cbLocals+?rp) ;;if cannot assume valid SP mov SP,BP ;; or locals or parmR's, get valid SP;@ endif pop BP ;@ endif endif ;; Far Win Proc ife flags AND 010H ;;if Pascal calling convention ret cbParms ;; return and remove paramteres ;@ else ;;else ret ;; return ;@ endif endif ;; User makes frame endif ;; noGen endm ; ; cProc - This macro is used to define the start of a procedure in CMACROS. ; ; PARAMETERS: ; pname - The name of the procedure to be defined. This field is ; required to be present. ; attribs - This is a list of attributes which may be placed on the ; function being defined. ; autoSave - This is an optional list of registers which are to be ; saved and restored during the prologue/epilogue processing ; cProc macro pname:REQ, attribs, autoSave IF ??CM_state NE 0 ;; No nesting of functions allowed .err endif ; ; Setup some state variables to start the procedure definition ; ??CM_state = 1 ;; Set state variable to seen cProc ??CM_ProcName EQU ??CM_ProcAutoSave EQU ??CM_localCount = 0 ??CM_argCount = 0 ??CM_langType EQU <> ??CM_WinFarProc=?WIN ;;default far procedure (win or not) ??CM_NoData=?NODATA ;;default NODATA flag ??CM_Atomic=?NOATOMIC ;;default is not ATOMIC repeat 20 ;; Clear all parameter textmacros ??CM_Paste(??CM_arg, %??CM_argCount) textequ <> ??CM_argCount = ??CM_argCount + 1 endm ??CM_argCount = 0 ife sizec ;; Set the default distance dist equ else dist equ endif vis equ <> ;; Set the default visibility for arg, ;; Look at the attribute list ifidn , dist equ elseifidn , dist equ elseifidn , vis equ elseifidn , vis equ elseifidn , ;; Ignore -- only for CRT elseifidn , ??CM_langType equ elseifidn , ??CM_langType equ elseifidn , ??CM_WinFarProc=1 elseifidn , ??CM_WinFarProc=0 elseifidn , ??CM_NoData=1 elseifidn , ??CM_Atomic=1 else % .err endif endm option prologue:cPrologue ;; Change to our prologue sequence option epilogue:none ;; rets from here on are just rets endm ; cBegin macro pname local t2 IF ??CM_state NE 1 ;; Must follow cProc .err endif ??CM_nogen = 0 ifnb ifidn , ??CM_nogen = 1 elseifdif ??CM_ProcName, % echo endif endif ??CM_state = 2 ;; Seen a cBegin % setDefLangType ??CM_langType macroarg EQU <> ifnb ??CM_ProcAutoSave ??uses CATSTR , ??CM_ProcAutoSave t2 = @InStr(, %??uses, <,>) while t2 NE 0 ??uses CATSTR @SubStr(<%??uses>, 1, %t2-1), < >, @SubStr(<%??uses>, %t2+1) t2 = @InStr(, %??uses, <,>) endm else ??uses textequ <> endif ifidn defLangType, % ??CM_Paste(_, %??CM_ProcName) textequ endif ??CM_ProcName proc dist defLangType vis macroarg ??uses ??CM_arg0 ??CM_arg1 ??CM_arg2 ??CM_arg3 ??CM_arg4 ??CM_arg5 ??CM_arg6 ??CM_arg7 ??CM_arg8 ??CM_arg9 ??CM_arg10 ??CM_arg11 ??CM_arg12 ??CM_arg13 ??CM_arg14 ??CM_arg15 ??CM_arg16 ??CM_arg17 ??CM_arg18 ??CM_arg19 ??CM_ProcAutoSave EQU <> t2 = 0 repeat ??CM_localCount ??CM_Paste(??CM_local, %t2) t2 = t2 + 1 endm endm ; cEnd macro pname IF ??CM_state NE 2 ;; Must follow a cEnd .err endif ??CM_nogen = 0 ifnb ifidn , ??CM_nogen = 1 elseifdif ??CM_ProcName, % echo endif endif option epilogue:cEpilog ret option prologue:prologuedef option epilogue:epiloguedef ??CM_ProcName endp ??CM_state = 0 ;; not in a function endm ; ; cRet macro IF ??CM_state NE 2 ;; Must follow a cBegin .err endif option epilogue:cEpilog ret option epilogue:none endm ; ; createSeg is a macro that sets up a segment definition and ; a logical name for that segment. The logical name can ; be used to egner the segment, but it cannot be used for anyting ; else. ; ; usage: ; createSeg n, ln, a, co, cl, grp ; where: ; n is the physical name of the segment ; ln is the name it is to be invoked by ; a is the alignment, and is optional ; co is the combine type, and is optional ; cl is the class, and is optional ; grp is the name of the group that contains the segment ; createSeg macro segName, logName, aalign, combine, class, grp ifnb segName segment aalign combine '&class' else segName segment aalign combine endif segName ends ifnb grp GROUP segName logName&OFFSET equ offset grp: logName&BASE equ grp else logName&OFFSET equ offset segName: logName&BASE equ segName endif logName&_sbegin macro segName segment sEnd macro name ifnb ifdifi , % echo endif endif segName ends purge sEnd endm endm ifnb logName&_assumes macro s assume s:grp endm else logName&_assumes macro s assume s:segName endm endif endm sBegin macro name:req name&_sbegin endm ; assumes is a macro that will set up the assumes for a segment ; or group created with the createSeg macro. If the assumed ; value passed in isn't known, then a normal assume is made. ; ; usage: ; assumes s,g ; ; where: ; s is the register to make the assumption about ; g is the value to assume is in it assumes macro s,ln ifndef ln&_assumes assume s:ln else ln&_assumes s endif endm ; ; defGrp ; defGrp macro foo:vararg endm ; setDefLangType setDefLangType macro overLangType ifnb ifidn , defLangType textequ elseifidn , defLangType textequ elseifidn , defLangType textequ else % .err endif else ; !nb if ?PLM EQ 1 defLangType textequ elseif ?PLM EQ 0 defLangType textequ else .err endif endif ; nb endm ifndef ?NOSTATIC .xcref .xcref staticB, staticW, staticD, staticQ, staticT, staticCP, staticDP, staticI .cref ; staticX - define static data of type X ; ; usage: ; staticX n, i, s ; ; where: ; X is the type of the variable: b=byte, w=word, d=dword ; q=quad word, t=ten bytes, cp=code pointer, ; dp=data pointer, i=int ; n is the name of the given variable ; i is the initial value of the variable ; s is the duplication factor ; ; statics are always pascal symbols and non-public. If they are required ; to be public then globalX should be used. staticB macro name:req, initVal:=, repCount ifnb name db repCount dup (initVal) else name db initVal endif endm staticW macro name:req, initVal:=, repCount ifnb name dw repCount dup (initVal) else name dw initVal endif endm staticD macro name:req, initVal:=, repCount ifnb name dd repCount dup (initVal) else name dd initVal endif endm staticI macro name:req, initVal:=, repCount ifnb name asmI repCount dup (initVal) else name asmI initVal endif endm staticQ macro name:req, initVal:=, repCount ifnb name dq repCount dup (initVal) else name dq initVal endif endm staticT macro name:req, initVal:=, repCount ifnb name dt repCount dup (initVal) else name dt initVal endif endm if sizec staticCP macro name:req, i, s staticD name,, endm else staticCP macro name:req, i, s staticW name,, endm endif if sized staticDP macro name:req, i, s staticD name,, endm else staticDP macro name:req, i, s staticW name,, endm endif endif ; ?NOSTATIC globalB macro name:req, initVal:=, repCount, langType ??CM_gbl1 name, langType, initVal, repCount, DB endm globalW macro name:req, initVal:=, repCount, langType ??CM_gbl1 name, langType, , repCount, DW endm globalD macro name:req, initVal:=, repCount, langType ??CM_gbl1 name, langType, , repCount, DD endm globalQ macro name:req, initVal:=, repCount, langType ??CM_gbl1 name, langType, initVal, repCount, DQ endm globalT macro name:req, initVal:=, repCount, langType ??CM_gbl1 name, langType, initVal, repCount, DT endm if sizec globalCP macro n,i,s,c globalD ,,, endm else globalCP macro n,i,s,c globalW ,,, endm endif if sized globalDP macro n,i,s,c globalD ,,, endm else globalDP macro n,i,s,c globalW ,,, endm endif ??CM_gbl1 macro name, langType, initVal, repCount:=<1>, kind setDefLangType langType ifidn defLangType, public _&name name textequ <_&name> _&name kind repCount dup (initVal) else public name name kind repCount dup (initVal) endif endm ifndef ?NOEXTERN .xcref .xcref externB, externW, externD, externQ, externT .xcref externNP, externFP, externP, externCP, externDP, externA .cref ; externX - define external data of type X ; ; usage: ; externX n,c ; ; where: ; X is the type of the variable: b=byte, w=word, d=dword ; q=qword, t=tbyte, cp=code pointer, dp=data pointer ; a=absolute, i=int ; n is a list of names to be defined ; c is the lanague convention. C for C or PASCAL or PLM for ; pascal. The default (?PLM flag) will be used if not specified externA macro names:req, langtype ??CM_ex1 , langtype, ABS endm externB macro names:req, langtype ??CM_ex1 , langtype, BYTE endm externW macro names:req, langtype ??CM_ex1 , langtype, WORD endm externD macro names:req, langtype ??CM_ex1 , langtype, DWORD endm externQ macro names:req, langtype ??CM_ex1 , langtype, QWORD endm externT macro names:req, langtype ??CM_ex1 , langtype, TBYTE endm externNP macro names:req, langtype ??CM_ex1 , langtype, NEAR endm externFP macro names:req, langtype ??CM_ex1 , langtype, FAR endm if sizec externP macro n,c externFP ,c endm externCP macro n,c externD ,c endm else externP macro n,c externNP ,c endm externCP macro n,c externW ,c endm endif if sized externDP macro n,c externD ,c endm else externDP macro n,c externW ,c endm endif ??CM_ex1 macro names, langType, kind setDefLangType langType for name, ifidn defLangType, name textequ ??CM_Paste(_, name) extern ??CM_Paste(_, name):kind else extern defLangType name:kind endif endm endm endif ; ?NOEXTERN ifndef ?NOLABEL ; labelX - define label of data type X ; labelB macro names:req,langType ??CM_lb1 , langType, BYTE endm labelW macro names:req,langType ??CM_lb1 , langType, WORD endm labelD macro names:req,langType ??CM_lb1 , langType, DWORD endm labelQ macro names:req,langType ??CM_lb1 , langType, QWORD endm labelT macro names:req,langType ??CM_lb1 , langType, TBYTE endm labelNP macro names:req,langType ??CM_lb1 , langType, NEAR endm labelFP macro names:req,langType ??CM_lb1 , langType, FAR endm if sizec labelP macro n,c labelFP ,c endm labelCP macro n,c labelD ,c endm else labelP macro n,c labelNP ,c endm labelCP macro n,c labelW ,c endm endif if sized labelDP macro n,c labelD ,c endm else labelDP macro n,c labelW ,c endm endif ??CM_lb1 macro names:req, langType, kind setDefLangType langType ?pu = 0 for name, ifidn , ?pu =1 else ifidn defLangType, if ?pu public ??CM_Paste(_, name) endif name textequ ??CM_Paste(_, name) ??CM_Paste(_, name) label kind else if ?pu public name endif name label kind endif endif endm endm endif ; ?NOLABEL ifndef ?NODEF ; defX - inform the macros that name is of type X ; ; The given name(s) is flagged to be of the given type. ; ; This macro is no longer needed. ; for lbl, lbl macro names:req endm endm endif ; ?NODEF ifndef ?NOPTR ;; regPtr generates information allowing a 32-bit pointer currently ;; in a register to be pushed as a parameter to a subroutine using ;; the cCall macro. ;; ;; usage: ;; regptr n,s,o ;; where: ;; n is the name the argument will be known as ;; s is the register containing the segment portion ;; of the pointer ;; o is the register containing the offset portion ;; of the pointer ;; ;; 2/14/85 - made obsolete with farptr regPtr macro n,s,o farPtr n,s,o endm ;; farPtr generates information allowing a 32-bit pointer to be ;; pushed as a parameter to a subroutine using the cCall macro. ;; ;; usage: ;; farptr n,s,o ;; where: ;; n is the name the argument will be known as ;; s is the segment portion of the pointer ;; o is the offset portion of the pointer ;; ;; Note that any cast must have been made in the argument itself ;; (i.e. regptr ptr1,ds,) farPtr macro n,s,o n macro push s push o endm endm endif ; ?NOPTR ;; arg - declare arguements ;; ;; The given arguments(s) is added to the argument list structure ;; ;; format: ;; arg a ;; ;; where: ;; a is any valid arugment(s) to push ;; ;; If any element in arglist has not been defined or isn't 16-bit ;; register, then a compilete specification must have been given in ;; a text equate and a defx also given (if not, you'll pay the penalty!) ;; arg macro args ifnb ifnb ??CM_ArgList ??CM_ArgList textequ ??CM_ArgList, <,>, else ??CM_ArgList textequ endif endif endm ;; ?CM_IsIdent - is the arguement a legal identifier? ;; ;; ;; Need to enforce the following additional rules: ;; Digit may not be first character ;; Assorted other characters may be used ;; Dot may be the first character ?CM_IsIdent macro name:REQ LOCAL result result = 1 forc char, if ('A' LE '&char') AND ('&char' LE 'Z') goto next endif if ('a' LE '&char') AND ('&char' LE 'z') goto next endif if ('0' LE '&char') AND ('&char' LE '9') goto next endif result = 0 exitm :next endm exitm %result endm ;; @reverse ;; @reverse macro list LOCAL newlist newlist textequ <> for x, newlist catstr , <,>, newlist endm ifnb newlist newlist substr newlist, 1, @sizestr(%newlist)-1 endif exitm newlist endm ;; ?ap - process arguments and place onto stack ;; ;; The given argument is processed (type checking) and placed ;; on the stack for a pending call. There must be a type ;; definition for all arguments (except words). This can be ;; done using text equates and the defx macro. ;; ?ap macro n ?argl=?argl+2 ;; assume one word is pushed if 0 EQ (OPATTR n) if ?CM_IsIdent(n) ifdef n n exitm endif endif % .err <'&n' is not valid to push> exitm else i = (TYPE n) if i EQ 1 ;; byte type push word ptr(n) exitm endif if i EQ 2 ;; word type push n exitm endif if i EQ 4 ;; dword type push word ptr (n)[2] push word ptr (n) ?argl=?argl+2 exitm endif if i EQ 8 ;; qword type push word ptr (n)[6] push word ptr (n)[4] push word ptr (n)[2] push word ptr (n) ?argl=?argl+6 exitm endif push word ptr (n) ;; variable storage exitm endif endm ;; cCall - call a 'c' language procedure ;; ;; The given procedure is called with the given parameters. If the ;; calling convention is C, the arguments are pushed in reverse order, ;; and removed after the called procedure returns. If the calling ;; convention is PL/M, the arguments are pushed as they were encountered, ;; and the called procedure is assumed to have removed them ;; from the stack. ;; ;; The calling convention priority will be: ;; 1) that specified on the cCall if present, ;; 2) that defined by the target, ;; 3) the default (?PLM flag). ;; ;; format: ;; cCall n,,c ;; ;; where: ;; n is the name of the procedure to call ;; a are the arguments to be pushed (optional, may be specified ;; with the "arg" macro. ;; c is the calling convention, C for C, PLM or PASCAL for ;; PL/M. The default (?PLM flag) will be used if ;; not specified). ;; cCall macro name:req,args,langType ifnb ;; add any passed in arguments arg ;; to the list of arguments endif ;; for this procedure ifnb ??CM_RegSaveList ;; If there are any resgisters % for reg, ;; to be saved across the call push reg ;; save then on the stack endm ;; endif ;; ifnb ;; If a lang type was specified then setDefLangType langType ;; it overrides all common sense else i = ((OPATTR name) SHR 8) AND 7 ;; Get the lang type from the symbol if i EQ 0 ;; No lang type --- setDefLangType ;; Use the default lang type elseif i EQ 1 ;; C lang type defLangType textequ ;; elseif i EQ 4 ;; Pascal Lang Type defLangType textequ ;; else ;; Unknown lang type .err ;; endif endif ifidn defLangType, ;; If using C calling convention ??CM_ArgList textequ @reverse(%??CM_ArgList) ;; then reverse the arguements endif ;; ?argl=0 ;; Clear arguement byte count % for arg, ;; Push the procedures arguements ?ap ;; onto the stack. ?ap takes endm ;; care of sizing arguements call name ;; Do the actual call ifidn defLangType, ;; If this is a C proc and there if ?argl NE 0 ;; add sp,?argl ;; are parameters then pop them endif ;; off the stack endif ;; ifnb ??CM_RegSaveList ;; If there were any saved registers ??CM_RegSaveList textequ @reverse(%??CM_RegSaveList) ;; then pop them off the stack % for reg, ;; at this point. pop reg ;; endm ;; endif ;; ??CM_RegSaveList textequ <> ;; Clear out the global state ??CM_ArgList textequ <> ;; variable used by the cCall macro endm ;; save - flag that the indicated registers are to be saved/restored ;; on the next cCall invocation ;; ;; usage: ;; save ;; ;; where: ;; r is the list of registers to be saved. ;; ;; the macro generates a value for the variable ??CM_RegSaveList ;; save macro r ??CM_RegSaveList textequ endm ;; parmX - generate reference to parameter(s) on the stack ;; ;; usage: ;; parmX n ;; where: ;; x is the type of the argument(s) b=byte, w=word, d=dword ;; n is the name(s) to be given the parmeter(s). ;; ;; Byte are considered to be two bytes long for alignment. ;; ;; The parmd form of the macro generates two equates: ;; ;; off_name - for accessing the offset (lsw) of the parameter ;; seg_name - for accessing the segment (msw) of the parameter ;; parmB macro names:req for name, ??CM_addParm <&name:BYTE> endm endm parmW macro names:req for name, ??CM_addParm <&name:WORD> endm endm parmD macro names:req for name, ??CM_addParm <&name:DWORD> ??CM_Paste(off_, name) textequ ??CM_Paste(seg_, name) textequ endm endm parmQ macro names:req for name, ??CM_addParm <&name:QWORD> endm endm parmT macro names:req for name, ??CM_addParm <&name:TBYTE> endm endm if sizec parmCP macro n parmD endm else parmCP macro n parmW endm endif if sized parmDP macro n parmD endm else parmDP macro n parmW endm endif if 0 ;; parmR - register parameter ;; ;; parmR is the macro used for generating register parameters. ;; The space allocated for the register parameters will be ;; the ?ia (interface adjust) area which is between the old ;; BP and the first parameter. Normally this is empty (?ia=0), ;; or has the saved ds for a windows far procedure. ;; ;; Byte and dword register parameters will be allowed. ;; ;; usage: ;; parmR n,r,r2 ;; where: ;; n is the name of the parameter ;; r is the register it is in ;; r2 is the offset register if a dword ifndef ?NOPARMR .xcref .xcref ?pr,parmR .cref parmR macro n,r,r2 ?pr n,r,r2,%?rp,%(?ia+2) endm ;; ?pr - register parameter ;; ;; ?pr is the actual macro for generating the equates for ;; register parameters. ;; ;; usage: ;; parmR n,r,r2,i,o ;; where: ;; n is the name of the parameter ;; r is the register it is in ;; r2 is the offset register if a dword ;; i is the index of the ?rp to generate ;; o is the offset from bp where the parm will be ?pr macro n,r,r2,i,o .xcref ifnb ;;if a dword parameter parmR seg_&n,r ;;define segment equate parmR off_&n,r2 ;;define offset equate n equ (dword ptr [bp-o-2]) ;;define dword equate .xcref ?t&n ?t&n=4 ;;show a dword to cmacros else .xcref ?rp&i ?rp&i=0 ;;show no register(s) ifdef ?&r ;;define register if valid ?rp&i=?&r endif if ??? or (?cpd eq 0) or (?rp&i eq 0) ??error exitm endif n equ (word ptr [bp-o]) ;;assume a word register ?t&n=2 ;;show a word to cmacros irp x, if ?&&x eq ?&r ;;if really a byte register n equ (byte ptr [bp-o]) ;; then make it a byte ?t&n=1 ;;show a byte to cmacros exitm endif endm ?ia=?ia+2 ;;show this guy is out there ?rp=?rp+1 ;;show one more register parameter endif .cref endm endif endif localB macro name ??CM_addLocal ??CM_Paste(name, <:BYTE>) endm localW macro name ??CM_addLocal ??CM_Paste(name, <:WORD>) endm localD macro name ??CM_addLocal ??CM_Paste(name, <:DWORD>) off_&name textequ seg_&name textequ endm localQ macro name ??CM_addLocal ??CM_Paste(name, <:QWORD>) endm localT macro name ??CM_addLocal ??CM_Paste(name, <:TBYTE>) endm if sizec localCP macro n localD endm else localCP macro n localW endm endif if sized localDP macro n localD endm else localDP macro n localW endm endif localV macro name,a local t1 t1 catstr , < [>, %a, <]:BYTE> % ??CM_addLocal endm ife ?DF ;; ;; Define all segments that will be used. This will allow the ;; assume and groups to be set up at one given place, and also ;; allow quick changes to be made ;; createSeg _TEXT,Code,word,public,CODE ife ?NODATA createSeg _DATA,Data,word,public,DATA,DGROUP endif endif ; errnz exp - generate error message if expression isn't zero ; ; The errnz will generate an error message if the expression "exp" ; does not evaluate to zero. This macro is very useful for testing ; relationships between items, labels, and data that was coded into ; an application. ; ; errnz ;error if not at "label" ; errnz ;eofflag must be bit 0 ; ; For expressions involving more than one token, the angle brackets ; must be used. ; ; The macro is only evaluated on pass 2, so forward references may be ; used in the expression. errnz macro x ;;display error if expression is <>0 .errnz x endm ; errn$ label,exp - generate error message if label (exp) <> $ ; ; The errnz will generate an error message if the label and "exp" ; does not evaluate to the current value of the location counter. ; This macro is very useful for testing relationships between ; labels and the location counter that was coded into an application. ; ; examples: errn$ label ;error if not at "label" ; errn$ label,+3 ;error if not three bytes from "label" ; errn$ label,-3 ;error if not three bytes past "label" ; ; If no "exp" is given, it is the same as specifying 0 ; ; The macro is only evaluated on pass 2, so forward references may be ; used in the expression. errn$ macro l,x ;;error if <$-label1 (exp2)> <>0 errnz endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Extra macros for the c-runtime package ; ; Macro for calling another run-time-library function. ; Does a PUSH CS/CALL NEAR in compact/large models, except ; for QuickC. --PHG, 5-24-89 callcrt MACRO funcname ifdef _QC2 call funcname else if sizeC push cs call near ptr (funcname) else call funcname endif endif ENDM .cref ; Permit symbols to be listed again