1613 lines
35 KiB
Plaintext
1613 lines
35 KiB
Plaintext
|
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 <nogen>
|
||
|
;
|
||
|
; 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 <t>
|
||
|
; where:
|
||
|
; <t> 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 <onmsg>
|
||
|
??_out <! onmsg>
|
||
|
endif
|
||
|
else
|
||
|
ifnb <offmsg>
|
||
|
??_out <! offmsg>
|
||
|
endif
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
|
||
|
; ??error - output message and generate an assembly time error
|
||
|
;
|
||
|
; usage:
|
||
|
; ??error <t>
|
||
|
; 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 <cMacros Version 6.00 - 1/1/91>
|
||
|
??_out <Copyright (C) Microsoft Corp. 1990-1991. All rights reserved.>
|
||
|
|
||
|
;
|
||
|
; 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,<Small Model>
|
||
|
outif memM,0,<Medium Model>
|
||
|
outif memC,0,<Compact Model>
|
||
|
outif memL,0,<Large Model>
|
||
|
outif memH,0,<Huge Model>
|
||
|
|
||
|
memMOD = memS + memM + memL + memC + memH
|
||
|
if memMOD ne 1
|
||
|
if memMOD eq 0
|
||
|
memS=1 ; Assume small model
|
||
|
outif memS,0,<Small Model>
|
||
|
else
|
||
|
??error <must have only 1 memory model selected>
|
||
|
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,<No segments or groups will be defined>
|
||
|
outif ?DFDATA,0,<No data segments will be defined>
|
||
|
outif ?DFCODE,0,<No code segments will be defined>
|
||
|
outif ?TF,0,<Epilogue sequences will assume valid SP>
|
||
|
outif ?WIN,1,<Windows support>
|
||
|
outif ?COW,0,<Character Windows support>
|
||
|
outif ?PLM,1,<PL/M calling convention>
|
||
|
outif ?NOATOMIC,0,<ATOMIC calling convention>
|
||
|
outif ?NODATA,0,<NODATA module>
|
||
|
|
||
|
ifdef ?CHKSTK
|
||
|
ifdef ?CHKSTKPROC
|
||
|
??_out <! Private stack checking enabled>
|
||
|
else
|
||
|
??_out <! Stack checking enabled>
|
||
|
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 <arg1&arg2>
|
||
|
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 <LOCAL arg1>
|
||
|
??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 <CMACROS.INC: Cannot have more than 20 arguements to a procedure>
|
||
|
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,<userparms>
|
||
|
ifdef ??CM_Paste(??_cproc_, x)
|
||
|
??CM_Paste(??_cproc_, x)
|
||
|
else
|
||
|
??error <e r r o r - unknown keyword x>
|
||
|
.err
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
;;
|
||
|
;; Now start some error checking
|
||
|
;;
|
||
|
|
||
|
if (??CM_Atomic eq 1) and (??CM_NoData eq 0) ;;ATOMIC requires NODATA
|
||
|
??error <ATOMIC specified without NODATA - ATOMIC ignored>
|
||
|
??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 <NODATA encountered in &n - user frame ignored>
|
||
|
??CM_UserDoesFrame=0
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
if ??CM_UserDoesFrame ;;if user frame
|
||
|
if ?rp ;;If register parameters
|
||
|
??error <parmR encountered in &n - user frame ignored>
|
||
|
??CM_UserDoesFrame=0
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
?cpd=0 ;;terminate current proc def
|
||
|
|
||
|
ifidn <g>,<nogen> ;;if nogen, then cannot have locals
|
||
|
if cbLocals + cbParms + ?rp ;; saved regs, or parmRs
|
||
|
??_out <cBegin - possibly invalid use of nogen>
|
||
|
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 <g>,<nogen> ;;if nogen, then cannot have parmRs
|
||
|
if a+r+lc ;; locals, or saved registers
|
||
|
??_out <cEnd - possibly invalid use of nogen>
|
||
|
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 <CMACROS.INC: Cannot nest procedure definitions>
|
||
|
endif
|
||
|
;
|
||
|
; Setup some state variables to start the procedure definition
|
||
|
;
|
||
|
??CM_state = 1 ;; Set state variable to seen cProc
|
||
|
??CM_ProcName EQU <pname>
|
||
|
??CM_ProcAutoSave EQU <autoSave>
|
||
|
??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 <NEAR>
|
||
|
else
|
||
|
dist equ <FAR>
|
||
|
endif
|
||
|
|
||
|
vis equ <> ;; Set the default visibility
|
||
|
|
||
|
for arg,<attribs> ;; Look at the attribute list
|
||
|
ifidn <arg>, <FAR>
|
||
|
dist equ <FAR>
|
||
|
elseifidn <arg>,<NEAR>
|
||
|
dist equ <NEAR>
|
||
|
elseifidn <arg>,<PUBLIC>
|
||
|
vis equ <PUBLIC>
|
||
|
elseifidn <arg>,<PRIVATE>
|
||
|
vis equ <PRIVATE>
|
||
|
elseifidn <arg>,<LOCAL> ;; Ignore -- only for CRT
|
||
|
elseifidn <arg>,<PASCAL>
|
||
|
??CM_langType equ <PASCAL>
|
||
|
elseifidn <arg>,<C>
|
||
|
??CM_langType equ <C>
|
||
|
elseifidn <arg>,<WIN>
|
||
|
??CM_WinFarProc=1
|
||
|
elseifidn <arg>,<NOWIN>
|
||
|
??CM_WinFarProc=0
|
||
|
elseifidn <arg>,<NODATA>
|
||
|
??CM_NoData=1
|
||
|
elseifidn <arg>,<ATOMIC>
|
||
|
??CM_Atomic=1
|
||
|
else
|
||
|
% .err <CMACROS.INC: cProc -- Unknown arguement '&arg'>
|
||
|
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 <CMACROS.INC: cBegin must follow a cProc>
|
||
|
endif
|
||
|
??CM_nogen = 0
|
||
|
ifnb <pname>
|
||
|
ifidn <pname>,<nogen>
|
||
|
??CM_nogen = 1
|
||
|
elseifdif ??CM_ProcName, <pname>
|
||
|
% echo <cBegin name (&pname) must match name on preceding cProc (&??CM_ProcName>
|
||
|
endif
|
||
|
endif
|
||
|
??CM_state = 2 ;; Seen a cBegin
|
||
|
% setDefLangType ??CM_langType
|
||
|
macroarg EQU <>
|
||
|
ifnb ??CM_ProcAutoSave
|
||
|
??uses CATSTR <uses >, ??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,<C>
|
||
|
% ??CM_Paste(_, %??CM_ProcName) textequ <??CM_ProcName>
|
||
|
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 <cEnd must follow a cProc>
|
||
|
endif
|
||
|
|
||
|
??CM_nogen = 0
|
||
|
|
||
|
ifnb <pname>
|
||
|
ifidn <pname>,<nogen>
|
||
|
??CM_nogen = 1
|
||
|
elseifdif ??CM_ProcName, <pname>
|
||
|
% echo <cEnd name (&pname) must match preceeding cProc name (&??CM_ProcName)>
|
||
|
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 <cRet must follow a cProc>
|
||
|
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 <class>
|
||
|
segName segment aalign combine '&class'
|
||
|
else
|
||
|
segName segment aalign combine
|
||
|
endif
|
||
|
segName ends
|
||
|
ifnb <grp>
|
||
|
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 <name>
|
||
|
ifdifi <name>,<logName>
|
||
|
% echo <sEnd name does not match sBegin logName>
|
||
|
endif
|
||
|
endif
|
||
|
segName ends
|
||
|
purge sEnd
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
ifnb <grp>
|
||
|
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 <overLangType>
|
||
|
ifidn <overLangType>,<C>
|
||
|
defLangType textequ <C>
|
||
|
elseifidn <overLangType>,<PASCAL>
|
||
|
defLangType textequ <PASCAL>
|
||
|
elseifidn <overLangType>,<PLM>
|
||
|
defLangType textequ <PASCAL>
|
||
|
else
|
||
|
% .err <Illegal Language Type specified 'overLangType'>
|
||
|
endif
|
||
|
else ; !nb <overLangType>
|
||
|
if ?PLM EQ 1
|
||
|
defLangType textequ <PASCAL>
|
||
|
elseif ?PLM EQ 0
|
||
|
defLangType textequ <C>
|
||
|
else
|
||
|
.err <Illegal value for ?PLM>
|
||
|
endif
|
||
|
endif ; nb <overLangType>
|
||
|
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 <repCount>
|
||
|
name db repCount dup (initVal)
|
||
|
else
|
||
|
name db initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
staticW macro name:req, initVal:=<?>, repCount
|
||
|
ifnb <repCount>
|
||
|
name dw repCount dup (initVal)
|
||
|
else
|
||
|
name dw initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
staticD macro name:req, initVal:=<?>, repCount
|
||
|
ifnb <repCount>
|
||
|
name dd repCount dup (initVal)
|
||
|
else
|
||
|
name dd initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
staticI macro name:req, initVal:=<?>, repCount
|
||
|
ifnb <repCount>
|
||
|
name asmI repCount dup (initVal)
|
||
|
else
|
||
|
name asmI initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
staticQ macro name:req, initVal:=<?>, repCount
|
||
|
ifnb <repCount>
|
||
|
name dq repCount dup (initVal)
|
||
|
else
|
||
|
name dq initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
staticT macro name:req, initVal:=<?>, repCount
|
||
|
ifnb <repCount>
|
||
|
name dt repCount dup (initVal)
|
||
|
else
|
||
|
name dt initVal
|
||
|
endif
|
||
|
endm
|
||
|
|
||
|
if sizec
|
||
|
staticCP macro name:req, i, s
|
||
|
staticD name,<i>,<s>
|
||
|
endm
|
||
|
else
|
||
|
staticCP macro name:req, i, s
|
||
|
staticW name,<i>,<s>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
staticDP macro name:req, i, s
|
||
|
staticD name,<i>,<s>
|
||
|
endm
|
||
|
else
|
||
|
staticDP macro name:req, i, s
|
||
|
staticW name,<i>,<s>
|
||
|
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, <initVal>, repCount, DW
|
||
|
endm
|
||
|
|
||
|
globalD macro name:req, initVal:=<?>, repCount, langType
|
||
|
??CM_gbl1 name, langType, <initVal>, 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 <n>,<i>,<s>,<c>
|
||
|
endm
|
||
|
else
|
||
|
globalCP macro n,i,s,c
|
||
|
globalW <n>,<i>,<s>,<c>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
globalDP macro n,i,s,c
|
||
|
globalD <n>,<i>,<s>,<c>
|
||
|
endm
|
||
|
else
|
||
|
globalDP macro n,i,s,c
|
||
|
globalW <n>,<i>,<s>,<c>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
|
||
|
??CM_gbl1 macro name, langType, initVal, repCount:=<1>, kind
|
||
|
setDefLangType langType
|
||
|
ifidn defLangType,<C>
|
||
|
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 <names>, langtype, ABS
|
||
|
endm
|
||
|
|
||
|
externB macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, BYTE
|
||
|
endm
|
||
|
|
||
|
externW macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, WORD
|
||
|
endm
|
||
|
|
||
|
externD macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, DWORD
|
||
|
endm
|
||
|
|
||
|
externQ macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, QWORD
|
||
|
endm
|
||
|
|
||
|
externT macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, TBYTE
|
||
|
endm
|
||
|
|
||
|
externNP macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, NEAR
|
||
|
endm
|
||
|
|
||
|
externFP macro names:req, langtype
|
||
|
??CM_ex1 <names>, langtype, FAR
|
||
|
endm
|
||
|
|
||
|
if sizec
|
||
|
externP macro n,c
|
||
|
externFP <n>,c
|
||
|
endm
|
||
|
externCP macro n,c
|
||
|
externD <n>,c
|
||
|
endm
|
||
|
else
|
||
|
externP macro n,c
|
||
|
externNP <n>,c
|
||
|
endm
|
||
|
externCP macro n,c
|
||
|
externW <n>,c
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
externDP macro n,c
|
||
|
externD <n>,c
|
||
|
endm
|
||
|
else
|
||
|
externDP macro n,c
|
||
|
externW <n>,c
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
??CM_ex1 macro names, langType, kind
|
||
|
setDefLangType langType
|
||
|
for name,<names>
|
||
|
ifidn defLangType,<C>
|
||
|
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 <names>, langType, BYTE
|
||
|
endm
|
||
|
|
||
|
labelW macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, WORD
|
||
|
endm
|
||
|
|
||
|
labelD macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, DWORD
|
||
|
endm
|
||
|
|
||
|
labelQ macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, QWORD
|
||
|
endm
|
||
|
|
||
|
labelT macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, TBYTE
|
||
|
endm
|
||
|
|
||
|
labelNP macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, NEAR
|
||
|
endm
|
||
|
|
||
|
labelFP macro names:req,langType
|
||
|
??CM_lb1 <names>, langType, FAR
|
||
|
endm
|
||
|
|
||
|
if sizec
|
||
|
labelP macro n,c
|
||
|
labelFP <n>,c
|
||
|
endm
|
||
|
labelCP macro n,c
|
||
|
labelD <n>,c
|
||
|
endm
|
||
|
else
|
||
|
labelP macro n,c
|
||
|
labelNP <n>,c
|
||
|
endm
|
||
|
labelCP macro n,c
|
||
|
labelW <n>,c
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
labelDP macro n,c
|
||
|
labelD <n>,c
|
||
|
endm
|
||
|
else
|
||
|
labelDP macro n,c
|
||
|
labelW <n>,c
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
??CM_lb1 macro names:req, langType, kind
|
||
|
setDefLangType langType
|
||
|
?pu = 0
|
||
|
for name,<names>
|
||
|
ifidn <name>,<PUBLIC>
|
||
|
?pu =1
|
||
|
else
|
||
|
ifidn defLangType,<C>
|
||
|
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,<defB, defW, defD, defQ, defT, defCP, defDP>
|
||
|
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,<word ptr 3[si]>)
|
||
|
|
||
|
|
||
|
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 <args>
|
||
|
ifnb ??CM_ArgList
|
||
|
??CM_ArgList textequ ??CM_ArgList, <,>, <args>
|
||
|
else
|
||
|
??CM_ArgList textequ <args>
|
||
|
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,<name>
|
||
|
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,<list>
|
||
|
newlist catstr <x>, <,>, 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,<a>,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 <args> ;; add any passed in arguments
|
||
|
arg <args> ;; to the list of arguments
|
||
|
endif ;; for this procedure
|
||
|
|
||
|
ifnb ??CM_RegSaveList ;; If there are any resgisters
|
||
|
% for reg,<??CM_RegSaveList> ;; to be saved across the call
|
||
|
push reg ;; save then on the stack
|
||
|
endm ;;
|
||
|
endif ;;
|
||
|
|
||
|
ifnb <langType> ;; 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 <C> ;;
|
||
|
elseif i EQ 4 ;; Pascal Lang Type
|
||
|
defLangType textequ <PASCAL> ;;
|
||
|
else ;; Unknown lang type
|
||
|
.err <Unknown lang type specfied for '&name'> ;;
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
ifidn defLangType,<C> ;; If using C calling convention
|
||
|
??CM_ArgList textequ @reverse(%??CM_ArgList) ;; then reverse the arguements
|
||
|
endif ;;
|
||
|
|
||
|
?argl=0 ;; Clear arguement byte count
|
||
|
|
||
|
% for arg,<??CM_ArgList> ;; Push the procedures arguements
|
||
|
?ap <arg> ;; onto the stack. ?ap takes
|
||
|
endm ;; care of sizing arguements
|
||
|
|
||
|
call name ;; Do the actual call
|
||
|
|
||
|
ifidn defLangType,<C> ;; 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,<??CM_RegSaveList> ;; 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 <r>
|
||
|
;;
|
||
|
;; 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 <r>
|
||
|
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,<names>
|
||
|
??CM_addParm <&name:BYTE>
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
parmW macro names:req
|
||
|
for name,<names>
|
||
|
??CM_addParm <&name:WORD>
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
parmD macro names:req
|
||
|
for name,<names>
|
||
|
??CM_addParm <&name:DWORD>
|
||
|
??CM_Paste(off_, name) textequ <word ptr name[0]>
|
||
|
??CM_Paste(seg_, name) textequ <word ptr name[2]>
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
parmQ macro names:req
|
||
|
for name,<names>
|
||
|
??CM_addParm <&name:QWORD>
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
parmT macro names:req
|
||
|
for name,<names>
|
||
|
??CM_addParm <&name:TBYTE>
|
||
|
endm
|
||
|
endm
|
||
|
|
||
|
if sizec
|
||
|
parmCP macro n
|
||
|
parmD <n>
|
||
|
endm
|
||
|
else
|
||
|
parmCP macro n
|
||
|
parmW <n>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
parmDP macro n
|
||
|
parmD <n>
|
||
|
endm
|
||
|
else
|
||
|
parmDP macro n
|
||
|
parmW <n>
|
||
|
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 <r2> ;;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 <invalid parmR encountered: &n,&r>
|
||
|
exitm
|
||
|
endif
|
||
|
|
||
|
n equ (word ptr [bp-o]) ;;assume a word register
|
||
|
?t&n=2 ;;show a word to cmacros
|
||
|
irp x,<bh,ch,dh,bl,cl,dl,ah,al>
|
||
|
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 <word ptr name[0]>
|
||
|
seg_&name textequ <word ptr name[2]>
|
||
|
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 <n>
|
||
|
endm
|
||
|
else
|
||
|
localCP macro n
|
||
|
localW <n>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
if sized
|
||
|
localDP macro n
|
||
|
localD <n>
|
||
|
endm
|
||
|
else
|
||
|
localDP macro n
|
||
|
localW <n>
|
||
|
endm
|
||
|
endif
|
||
|
|
||
|
localV macro name,a
|
||
|
local t1
|
||
|
t1 catstr <name>, < [>, %a, <]:BYTE>
|
||
|
% ??CM_addLocal <t1>
|
||
|
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 <offset $ - offset label> ;error if not at "label"
|
||
|
; errnz <eofflag and 00000001b> ;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 <offset $ - offset l x>
|
||
|
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
|