You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2666 lines
68 KiB
2666 lines
68 KiB
comment $
|
|
|
|
cmacros - assembly macros for interfacing to hlls
|
|
|
|
(C)Copyright Microsoft Corp. 1984-1988
|
|
|
|
$
|
|
|
|
;; Revision History
|
|
;;
|
|
;; 1.00 05/03/84 Initial Release
|
|
;;
|
|
;; 1.01 05/06/84 Greg Whitten
|
|
;; Added defgrp and changed cMerge to Microsoft C
|
|
;; Added copyright message and changed to 1.01
|
|
;; Changes should have no affect on working programs
|
|
;;
|
|
;; 1.02 07/10/84 Steve Wood
|
|
;; Added labelx macros
|
|
;;
|
|
;; 1.03 07/14/84 Greg Whitten
|
|
;; Added defines for ?pu, ?adj, ?lblpu
|
|
;; (removes undefined errors)
|
|
;; Changes should have no affect on working programs
|
|
;;
|
|
;; 1.04 07/18/84 Greg Whitten
|
|
;; Added local control from PL/M or C conventions
|
|
;; except for cCall macro
|
|
;;
|
|
;; 1.05 08/06/84 Steve Wood
|
|
;; Made ?PLM and ?WIN be the defaults
|
|
;;
|
|
;; 1.06 01/02/85 Steve Wood
|
|
;; Changed createSeg and defgrp to automatically
|
|
;; define the ln_assumes macro and the lnoffset
|
|
;; and lnbase equates for each logical segment
|
|
;; name.
|
|
;;
|
|
;; 1.07 02/19/85 Walt Moore
|
|
;; Added farptr macro for defining a far pointer
|
|
;; to be used in a cCall. Folded regptr into
|
|
;; farptr. Space compaction in macros. Changed
|
|
;; ?pp to be smaller. Moved ?ia out of ?al1 into
|
|
;; ?aloc. Merged cProc and ?pd into one macro.
|
|
;; Changed some %outs to use the error macro so
|
|
;; an error would be generated. Added makeframe
|
|
;; and parmR to cProc. Changed error to also put
|
|
;; the error message in the listing.
|
|
;; Deleted the smashes macro.
|
|
;;
|
|
;; 1.08 03/18/85 Steve Wood
|
|
;; Added NODATA support.
|
|
;;
|
|
;; 1.09 03/27/85 Steve Wood
|
|
;; Added ?definitions
|
|
;;
|
|
;; 2.00 04/01/85 Steve Wood
|
|
;; April fools
|
|
;;
|
|
;; 2.01 06/17/85 Steve Wood
|
|
;; Changed NODATA to always generate POP DS
|
|
;; for return address patching
|
|
;;
|
|
;; 2.02 02/11/86 Steve Wood
|
|
;; Added ATOMIC keyword to cProc macro
|
|
;; Changed far epilog to use LEA SP,BP-2
|
|
;; Changed error macro to ??error to avoid
|
|
;; conflict
|
|
;;
|
|
;; 2.03 03/06/86 Steve Wood
|
|
;; Fixed bug with ATOMIC and locals in far proc
|
|
;; Added DOS5 switch to disable INC/DEC BP
|
|
;; instructions in special far prologs/epilogs
|
|
;;
|
|
;; 2.04 08/07/86 Scott Randell
|
|
;; Fixed bug with ATOMIC and ?TF
|
|
;; (was doing unnecessary MOV SP,BP)
|
|
;; Added pcode profile ?PROFILE
|
|
;;
|
|
;; 2.05 08/12/86 Walt Moore
|
|
;; Changed _TEXT alignment to word.
|
|
;; Added/corrected some comments.
|
|
;; Removed redundant init of ?pc in cProc
|
|
;; Made ATOMIC require NODATA
|
|
;; Moved definition of non-underscored 'C' label
|
|
;; from the cProc to the cBegin macro
|
|
;; Minor clean-up of code
|
|
;;
|
|
;; 2.06 09/11/86 Walt Moore
|
|
;; Added private stack checking
|
|
;; Put local control for PL/M or C into cCall
|
|
;;
|
|
;;
|
|
;; 2.07 09/19/86 Steve Wood
|
|
;; Added ?SMALL, ?MEDIUM, etc. symbols
|
|
;; Added forceframe keyword to cProc macro.
|
|
;; Interpret ?TF for all epilogs.
|
|
;;
|
|
;; 3.xx.a 02/26/87 Massive rework. Documentation coming.
|
|
;;
|
|
;;
|
|
;; Assembly macros for interfacing to C
|
|
;;
|
|
;; User setable conditional assembly flags used within the cmacros
|
|
;;
|
|
;; Memory model flags. Set only one of the following. memS is the
|
|
;; default. The symbols with ? are for defining from the command line
|
|
;; and the memx symbols are numeric symbols that can be set in your source
|
|
;; file prior to including this file.
|
|
;;
|
|
;; ?SMALL memS - small model
|
|
;; ?MEDIUM memM - medium model
|
|
;; ?LARGE memL - large model
|
|
;; ?COMPACT memC - compact model
|
|
;; ?HUGE memH - huge model
|
|
;;
|
|
;; ?DF Define flag. If this flag is 0, then defines default segment
|
|
;; and group definitions based on the compiler flag. If this
|
|
;; flag is 1, then does not define any segments or groups.
|
|
;;
|
|
;; ?TF Tight flag. If this flag is 0, then use longer epilog
|
|
;; sequence that safely cleans up a stack frame. If this flag is
|
|
;; 1, then use more efficient epilog that assumes the stack is
|
|
;; valid (SP)
|
|
;;
|
|
;; ?WIN Windows flag. Enables generation of special prolog/epilog
|
|
;; for far procedures. Default value is 1 (Windows).
|
|
;;
|
|
;; DOS5 If defined, then special far prolog/epilog seqeuences will not
|
|
;; include the INC/DEC BP instructions.
|
|
;;
|
|
;; PMODE Protect mode flag. Enables use of ENTER/LEAVE, disables
|
|
;; INC/DEC BP instructions, sets .286p.
|
|
;;
|
|
;; ?386regs Enables use of eax,ebx,ecx,esi,edi,fs,gs as registers
|
|
;; in cProc et al. Use of al,ah,bl,bh,cl,ch,dl,dh,ss,bp,sp,ss,cs
|
|
;; no longer allowed.
|
|
;;
|
|
;; ?PLM Calling convention flag. If this flag is 0, then the
|
|
;; calling convention used is that of C. If this flag
|
|
;; is 1, then the PL/M calling convention is used.
|
|
;; The default value is 1. The PL/M calling convention
|
|
;; is used by pascal, fortran, basic, and cobol.
|
|
;;
|
|
;; In the C calling convention, arguments are passed
|
|
;; in reverse order; arg0 is the last pushed, argn is the
|
|
;; first pushed. also, it is the callers responsibility
|
|
;; to remove the arguments from the stack upon a return
|
|
;; from a call.
|
|
;;
|
|
;; In the PL/M calling comvention, arguments are passed
|
|
;; as encountered; arg0 is the first pushed, argn is the
|
|
;; last pushed. also, it is the called procedure's
|
|
;; responsibility to remove parameters from the stack
|
|
;; before returning (using the RET n instruction)
|
|
;;
|
|
;; ?NODATA If defined, then no data segment or DGROUP is defined and
|
|
;; the special prolog/epilog sequences will not contain the
|
|
;; code needed to setup DS.
|
|
;;
|
|
;; ?CHKSTK If defined, then prolog sequences for cProcs with local
|
|
;; parameters will call the CHKSTK procedure to allocate
|
|
;; the stack space.
|
|
;;
|
|
;; ?CHKSTKPROC If defined, then this macro will be invoked to
|
|
;; perform the stack checking, otherwise the
|
|
;; standard stack checking procedure will be
|
|
;; performed. ?CHKSTKPROC must be declared
|
|
;; before the cmacros are included in the source
|
|
;; else the standard chkstk routine will be declared
|
|
;; as an external symbol.
|
|
;;
|
|
;; On entry to the user's stack checking procedure,
|
|
;; the frame has been setup except for allocating
|
|
;; local variable space and saving autosave registers.
|
|
;;
|
|
;; The user supplied macro is passed as an argument
|
|
;; the number of byte of stack space requested.
|
|
;;
|
|
;; ?PROFILE If defined then all far cBegin entries will have StartNMeas,
|
|
;; and all cEnd will have StopNMeas calls, StartNMeas and
|
|
;; StopNMeas will be defined as externfp
|
|
;;
|
|
;; ?NOPARMR If defined, then the "parmR" macro will not be defined.
|
|
;;
|
|
;; ?NOGLOBAL If defined, then the "globalx" macros will not be defined.
|
|
;;
|
|
;; ?NOSTATIC If defined, then the "staticx" macros will not be defined.
|
|
;;
|
|
;; ?NOEXTERN If defined, then the "externx" macros will not be defined.
|
|
;;
|
|
;; ?NOLABEL If defined, then the "labelx" macros will not be defined.
|
|
;;
|
|
;; ?NODEF If defined, then the "defx" macros will not be defined.
|
|
;;
|
|
;; ?NOPTR If defined, then "farptr & regptr" will not be defined.
|
|
;;
|
|
;; ?QUIET If defined, then only error messages will be issued to
|
|
;; the console. If undefined, then certain cmacro text will
|
|
;; be generated to the console.
|
|
|
|
|
|
.xcref ;;Get rid of a lot of symbols
|
|
|
|
|
|
|
|
; ??_out - output given message to the console unless ?QUIET has
|
|
; been specified.
|
|
;
|
|
; usage:
|
|
; ??_out <t>
|
|
;
|
|
; where:
|
|
; <t> is the message to output
|
|
|
|
.xcref ??_out
|
|
??_out macro t
|
|
ifndef ?QUIET
|
|
%out 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 symbol if not defined
|
|
; if blank, then 0 will be used
|
|
; onmsg text to display if symbol is non-zero
|
|
; offmsg test to be displayed if symbol is zero
|
|
|
|
|
|
outif macro name,defval,onmsg,offmsg
|
|
ifndef name
|
|
ifb <defval>
|
|
name=0
|
|
else
|
|
name=defval
|
|
endif
|
|
endif
|
|
if name
|
|
name=1
|
|
ifnb <onmsg>
|
|
??_out <! onmsg>
|
|
endif
|
|
else
|
|
ifnb <offmsg>
|
|
??_out <! offmsg>
|
|
endif
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
; ??error - output msg and generate an assembly time error
|
|
;
|
|
; usage:
|
|
; ??error <t>
|
|
; where:
|
|
; t is the text to be output
|
|
|
|
|
|
.xcref ??error
|
|
??error macro msg
|
|
e r r o r ----- msg ;;to console
|
|
.err ;;for good measure, force this also
|
|
endm
|
|
|
|
|
|
.xcref ASMpass
|
|
.xcref memS,memM,memL,memC,memH,memMOD,sizec,sized
|
|
|
|
if1 ;;Only on pass 1
|
|
ASMpass=1
|
|
ifdef ?SMALL ;;inform user what is going on
|
|
memS=1
|
|
endif
|
|
ifdef ?MEDIUM
|
|
memM=1
|
|
endif
|
|
ifdef ?COMPACT
|
|
memC=1
|
|
endif
|
|
ifdef ?LARGE
|
|
memL=1
|
|
endif
|
|
ifdef ?HUGE
|
|
memH=1
|
|
endif
|
|
|
|
??_out <cMacros Version 5.20 - Copyright (c) Microsoft Corp. 1984-1988>
|
|
outif memS,0,<Small model>
|
|
outif memM,0,<Medium model>
|
|
outif memL,0,<Large model>
|
|
outif memC,0,<Compact 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
|
|
else
|
|
??error <more than 1 memory model selected>
|
|
endif
|
|
endif
|
|
|
|
sizec= memM + memL + memH ; large code
|
|
sized= memL + memC + (memH*2) ; large data (2 if huge)
|
|
|
|
outif ?DF,0,<No segments or groups will be defined>
|
|
outif ?TF,0,<Epilog sequences assume valid SP>
|
|
outif ?WIN,1,<Windows support>
|
|
|
|
ifdef PMODE
|
|
?pmd=1
|
|
??_out <! 286 protect mode>
|
|
else
|
|
?pmd=0
|
|
endif
|
|
|
|
ifdef ?386regs
|
|
if ?386regs
|
|
??_out <! 386 registers enabled>
|
|
endif
|
|
else
|
|
?386regs=0
|
|
endif
|
|
|
|
if ?WIN eq 1
|
|
outif ?PLM,1,<>
|
|
else
|
|
outif ?PLM,1,<Pascal calling convention>
|
|
endif
|
|
|
|
ifndef ?NODATA
|
|
?nodata1=0
|
|
else
|
|
?nodata1=1
|
|
??_out <! NODATA module>
|
|
endif
|
|
|
|
ifndef ?CHKSTK
|
|
?chkstk1=0
|
|
else
|
|
?chkstk1=1
|
|
ifdef ?CHKSTKPROC
|
|
??_out <! Private stack checking enabled>
|
|
else
|
|
??_out <! Stack checking enabled>
|
|
endif
|
|
endif
|
|
|
|
ifndef DOS5
|
|
?DOS5=0
|
|
else
|
|
?DOS5=1
|
|
??_out <! DOS5 module>
|
|
endif
|
|
|
|
ifdef ?PROFILE
|
|
??_out <! Native profiling enabled>
|
|
endif
|
|
else
|
|
ASMpass=2
|
|
endif
|
|
|
|
ifdef ?pmd
|
|
.286p
|
|
endif
|
|
|
|
;; Initialize all symbols used in the macros. Theses symbols will not be
|
|
;; included in any cross reference listing.
|
|
|
|
if ?386regs
|
|
.xcref ?n,?ax,?eax,?bx,?ebx
|
|
.xcref ?cx,?ecx,?dx,?edx
|
|
.xcref ?si,?esi,?di,?edi,?es,?ds,?fs
|
|
.xcref ?gs
|
|
else
|
|
.xcref ?n,?ax,?ah,?al,?bx,?bh
|
|
.xcref ?bl,?cx,?ch,?cl,?dx,?dh
|
|
.xcref ?dl,?si,?di,?es,?ds,?bp
|
|
.xcref ?sp,?ss,?cs
|
|
endif
|
|
.xcref ?rsl,?cpd,?argl,?argc,?ba
|
|
.xcref ?acb,???,?po
|
|
.xcref ?pas,?pc
|
|
|
|
.xcref uconcat,mpush,mpop
|
|
.xcref ?ri,?pp,?pp1,?al1
|
|
.xcref ?ad,?ap,?atal,?dd,?dd1,?dd2
|
|
.xcref ?pg,?pg1,?aloc,?cs1,?cs2
|
|
.xcref ?DF,?TF,?ff,?PLM,?WIN,?ia,?pu,?adj
|
|
.xcref ?uf,?rp,?nx,?nd,?nodata1,?chkstk1,?DOS5,?pmd,?lds,?exp
|
|
.xcref ?wfp,arg,cCall,cProc,assumes,?cs3,?cs2,?cs1
|
|
.xcref defgrp,addseg,createSeg
|
|
.xcref save,outif,errnz,errn$,errnz1
|
|
.xcref ?PLMPrevParm,?gcc
|
|
.xcref ?cCall1,?pcc
|
|
|
|
|
|
;; conditionals set by the macros
|
|
;;
|
|
;; ?pc Procedure class. If this is set to 1, then the procedure
|
|
;; is a far procedure, else it is a near procedure.
|
|
;;
|
|
;; ?ia Interface adjustment count for far procedures. The
|
|
;; interface adjustment defines the number of bytes of
|
|
;; storage allocated between BP and the first frame variable
|
|
;; allocated on the stack.
|
|
;;
|
|
;; Normally zero, it will be adjusted for both far windows
|
|
;; procedures and by register parameters.
|
|
;;
|
|
;; ?cpd Current procedure defined. This is set to a non-zero
|
|
;; value if a procedure is being defined (i.e a cProc has
|
|
;; been encountered, and cBegin has not).
|
|
;;
|
|
;; ?ba Begin active. This is set to a non-zero value if a
|
|
;; cBegin is active (i.e. a cBegin has been encountered,
|
|
;; and cEnd has not).
|
|
;;
|
|
;; ?wfp Windows far procedure. Set if a windows far procedure
|
|
;;
|
|
;; ?lds LOADDS keyword - Preserve DS and set DS to _DATA
|
|
;;
|
|
;; ?exp EXPORTED keyword - mov ax,ds/nop as first inst, preserve DS
|
|
;;
|
|
;; Other variables that are defined once so that the .xcref command
|
|
;; doesn't get too upset if they show up missing!
|
|
|
|
?rsl = 0 ;;0 = no register to save
|
|
?cpd = 0 ;;<> 0 if in a procedure definition
|
|
?argl = 0 ;;length of arguments pushed on stack
|
|
?argc = 0 ;;# of arguments so far
|
|
?ba = 0 ;;<>0 if in a procedure (xbegin)
|
|
?acb = 0 ;;number of arguments to a call
|
|
??? = 0 ;;byte count of local storage
|
|
?po = 0 ;;byte count of parameters
|
|
?pas = 0 ;;autosave value for procedure
|
|
?pc = 0 ;;class of a procedure (near/far)
|
|
?ia = 0 ;;no adjustment
|
|
?pu = 0 ;;public flag for some macros
|
|
?adj = 0 ;;initial define for .xcref
|
|
?rp = 0 ;;count of register parameters
|
|
?uf = 0 ;;user's frame code specified
|
|
?nd = 0 ;;NODATA keyword specified
|
|
?nx = 0 ;;ATOMIC keyword specified
|
|
?wfp = 0 ;;window far procedure
|
|
?lds = 0 ;;LOADDS keyword
|
|
?exp = 0 ;;EXPORTED keyword
|
|
?ff = 0 ;;forceframe keyword specified
|
|
?dd2 = 0 ;;used for globalx and staticx
|
|
?cCall1 = 0 ;;used for cCalls
|
|
?pcc = 0 ;;procedure calling convention
|
|
?PLMPrevParm = 0 ;;Used in parameter processing
|
|
|
|
.xcref ?casen
|
|
if1 ;;only define ?casen on pass 1
|
|
?casen = 0 ;;case sensitive assembly if <> 0
|
|
endif
|
|
|
|
if ?386regs
|
|
?n = 0000000000000000b
|
|
?ax = 0000000000000001b
|
|
?eax = 0000000000000010b
|
|
?bx = 0000000000000100b
|
|
?ebx = 0000000000001000b
|
|
?cx = 0000000000010000b
|
|
?ecx = 0000000000100000b
|
|
?dx = 0000000001000000b
|
|
?edx = 0000000010000000b
|
|
?si = 0000000100000000b
|
|
?esi = 0000001000000000b
|
|
?di = 0000010000000000b
|
|
?edi = 0000100000000000b
|
|
?ds = 0001000000000000b
|
|
?es = 0010000000000000b
|
|
?fs = 0100000000000000b
|
|
?gs = 1000000000000000b
|
|
else
|
|
?n = 0000000000000000b ;;register none
|
|
?ax = 0000000000000011b ;;register ax
|
|
?ah = 0000000000000001b ;;register ah
|
|
?al = 0000000000000010b ;;register al
|
|
?bx = 0000000000001100b ;;register bx
|
|
?bh = 0000000000000100b ;;register bh
|
|
?bl = 0000000000001000b ;;register bl
|
|
?cx = 0000000000110000b ;;register cx
|
|
?ch = 0000000000010000b ;;register ch
|
|
?cl = 0000000000100000b ;;register cl
|
|
?dx = 0000000011000000b ;;register dx
|
|
?dh = 0000000001000000b ;;register dh
|
|
?dl = 0000000010000000b ;;register dl
|
|
?si = 0000000100000000b ;;register si
|
|
?di = 0000001000000000b ;;register di
|
|
?es = 0000010000000000b ;;register es
|
|
?ds = 0000100000000000b ;;register ds
|
|
?bp = 0001000000000000b ;;register bp
|
|
?sp = 0010000000000000b ;;register sp
|
|
?ss = 0100000000000000b ;;register ss
|
|
?cs = 1000000000000000b ;;register cs
|
|
endif
|
|
.cref
|
|
|
|
|
|
|
|
;; uconcat - unconditionally generate a statement from a field
|
|
;; of given parameters
|
|
;;
|
|
;; usage:
|
|
;; uconcat a,b,c,d,e,f,g
|
|
;;
|
|
;; where:
|
|
;; a,b are concatenated for field 1
|
|
;; c,d are concatenated for field 2
|
|
;; e,f,g are concatenated for field 3
|
|
|
|
uconcat macro a,b,c,d,e,f,g
|
|
a&b c&d e&f&g
|
|
endm
|
|
|
|
|
|
|
|
;; mpush pushes multiple registers onto the stack according to
|
|
;; a register specification.
|
|
;;
|
|
;; format:
|
|
;; mpush r
|
|
;;
|
|
;; where:
|
|
;; r is a numeric expression returned from ?ri
|
|
;; or any other valid register expression
|
|
|
|
if ?386regs
|
|
|
|
mpush macro r
|
|
irp x,<ax,eax,bx,ebx,cx,ecx,dx,edx,si,esi,di,edi,ds,es,fs,gs>
|
|
if (r and ?&&x)
|
|
push x ;@
|
|
endif
|
|
endm
|
|
endm
|
|
|
|
else
|
|
|
|
mpush macro r
|
|
irp x,<ax,bx,cx,dx,si,di,es,ds,bp,sp,ss,cs>
|
|
if (r and ?&&x)
|
|
push x ;@
|
|
endif
|
|
endm
|
|
endm
|
|
|
|
endif
|
|
|
|
;; mpop pops multiple registers from the stack according to
|
|
;; a register specification.
|
|
;;
|
|
;; format:
|
|
;; mpop r
|
|
;;
|
|
;; where:
|
|
;; r is a numeric expression returned from ?ri
|
|
;; or any other valid register expression
|
|
|
|
if ?386regs
|
|
|
|
mpop macro r
|
|
irp x,<gs,fs,es,ds,edi,di,esi,si,edx,dx,ecx,cx,ebx,bx,eax,ax>
|
|
if (r and ?&&x)
|
|
pop x ;@
|
|
endif
|
|
endm
|
|
endm
|
|
|
|
else
|
|
|
|
mpop macro r
|
|
irp x,<cs,ss,sp,bp,ds,es,di,si,dx,cx,bx,ax>
|
|
if (r and ?&&x)
|
|
pop x ;@
|
|
endif
|
|
endm
|
|
endm
|
|
|
|
endif
|
|
|
|
;; save - flag that the indicated registers are to be saved/restored
|
|
;;
|
|
;; A flag is created which indicates which registers are to be saved
|
|
;; when the cCall macro is invoked, and then restored after the call.
|
|
;;
|
|
;; usage:
|
|
;; save <r>
|
|
;;
|
|
;; where r is the list of registers to save, which may be:
|
|
;;
|
|
;; register saves
|
|
;; AX AX
|
|
;; AH AX
|
|
;; AL AX
|
|
;; BX BX
|
|
;; BH BX
|
|
;; BL BX
|
|
;; CX CX
|
|
;; CH CX
|
|
;; CL CX
|
|
;; DX DX
|
|
;; DH DX
|
|
;; DL DX
|
|
;; SI SI
|
|
;; DI DI
|
|
;; ES ES
|
|
;; DS DS
|
|
;; BP BP
|
|
;;
|
|
;; none nothing
|
|
;;
|
|
;; the macro generates a value for the variable ?rsl
|
|
|
|
save macro r
|
|
?rsl=0 ;;initialize save list
|
|
?ri ?rsl,<r> ;;generate magic number
|
|
endm
|
|
|
|
;; ?ri - or register indexes to variable
|
|
;;
|
|
;; ?ri is a macro that examines the passed argument list and computes
|
|
;; a register index variable.
|
|
;;
|
|
;; The values ORed with the variable are:
|
|
;;
|
|
;; ?n equ 0000000000000000b;
|
|
;; ?AX equ 0000000000000011b;
|
|
;; ?AH equ 0000000000000001b;
|
|
;; ?AL equ 0000000000000010b;
|
|
;; ?BX equ 0000000000001100b;
|
|
;; ?BH equ 0000000000000100b;
|
|
;; ?BL equ 0000000000001000b;
|
|
;; ?CX equ 0000000000110000b;
|
|
;; ?CH equ 0000000000010000b;
|
|
;; ?CL equ 0000000000100000b;
|
|
;; ?DX equ 0000000011000000b;
|
|
;; ?DH equ 0000000001000000b;
|
|
;; ?DL equ 0000000010000000b;
|
|
;; ?SI equ 0000000100000000b;
|
|
;; ?DI equ 0000001000000000b;
|
|
;; ?ES equ 0000010000000000b;
|
|
;; ?DS equ 0000100000000000b;
|
|
;; ?BP equ 0001000000000000b;
|
|
;; ?SP equ 0010000000000000b;
|
|
;; ?SS equ 0100000000000000b;
|
|
;; ?CS equ 1000000000000000b;
|
|
;; usage:
|
|
;; ?ri n,<r>
|
|
;; where:
|
|
;; n is the variable to contain the new index value
|
|
;; r is the register list
|
|
|
|
?ri macro n,r
|
|
irp x,<r>
|
|
.ERRNDEF ?&&x ;; yell if register isn't defined
|
|
n=n or ?&&x
|
|
endm
|
|
endm
|
|
|
|
|
|
|
|
;; parmx - generate reference to parameter(s) on the stack
|
|
;;
|
|
;; An equate is generated for addressing a paramter(s)
|
|
;; on the stack for the current procedural frame.
|
|
;;
|
|
;; An error message is generated if there isn't a current frame.
|
|
;;
|
|
;; 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 parameter(s).
|
|
;;
|
|
;; Bytes are considered to be two bytes long for alignment.
|
|
;;
|
|
;; The parmd form of the macro generates three equates:
|
|
;;
|
|
;; name - for accessing the parameter as a double word
|
|
;; off_name - for accessing the offset (lsw) of the parameter
|
|
;; seg_name - for accessing the segment (msw) of the parameter
|
|
|
|
.xcref
|
|
.xcref parmB,parmW,parmD,parmQ,parmT,parmCP,parmDP
|
|
.cref
|
|
|
|
parmB macro n
|
|
?pp <n>,<byte>,2,1
|
|
endm
|
|
|
|
parmW macro n
|
|
?pp <n>,<word>,2,2
|
|
endm
|
|
|
|
parmD macro n
|
|
ife ?PLM ;;if to assemble for C
|
|
irp x,<n>
|
|
?pp <&&x>,<dword>,0,4
|
|
?pp <off_&&x>,<word>,2,2
|
|
?pp <seg_&&x>,<word>,2,2
|
|
endm
|
|
else ;;if to assemble for PL/M
|
|
irp x,<n>
|
|
?pp <seg_&&x>,<word>,2,2
|
|
?pp <off_&&x>,<word>,2,2
|
|
?pp <&&x>,<dword>,0,4
|
|
endm
|
|
endif
|
|
endm
|
|
|
|
parmQ macro n
|
|
?pp <n>,<qword>,8,8
|
|
endm
|
|
|
|
parmT macro n
|
|
?pp <n>,<tbyte>,10,10
|
|
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
|
|
|
|
|
|
|
|
;; ?pp is the generalized parameter definition macro
|
|
;;
|
|
;; usage:
|
|
;; ?pp m,t,l,s
|
|
;;
|
|
;; where:
|
|
;; n is the name(s) of the parameters
|
|
;; t is the type (word, dword)
|
|
;; l is the length to update parameter byte count by
|
|
;; s is the internal typing size
|
|
|
|
|
|
?pp macro n,t,l,s ;;process parameter
|
|
if ?cpd ;;must be in a procedure definition
|
|
.xcref
|
|
irp x,<n>
|
|
.xcref ?t&&x ;;don't want this in xref
|
|
?t&&x=s ;;save size info
|
|
ife ?PLM ;;if C calling convention
|
|
?pp1 x,<t>,,,%(?po+?adj)
|
|
?po=?po+l ;;update parameter offset
|
|
else ;;else assemble for PL/M
|
|
?PLMPrevParm=?PLMPrevParm+1 ;;Show next parameter
|
|
?po=?po+l ;;update parameter offset
|
|
?pp1 x,<t>,%?po,%?adj,,%?PLMPrevParm,%(?PLMPrevParm-1)
|
|
endif
|
|
endm
|
|
.cref
|
|
else
|
|
??error <parm(s) "&n" declared outside proc def>
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
;; ?pp1 is the macro that generates the text equate for the
|
|
;; parameter. Two options exist, one for the C calling
|
|
;; convention where the last parameter was the first pushed onto
|
|
;; the stack ('C' convention), and one for the PL/M calling
|
|
;; convention where the first parameter was the first
|
|
;; pushed (also the same as ms-pascal).
|
|
;;
|
|
;; The text generated will be of one of two forms:
|
|
;;
|
|
;; name equ (type ptr [bp+(adj+offset)]) for C
|
|
;; or
|
|
;; name equ (type ptr [bp+adj+?po-offset]) for PL/M
|
|
;;
|
|
;;
|
|
;; For C, since parameters are pushed first last, the offset
|
|
;; plus the adjust will point to the correct parameter.
|
|
;;
|
|
;; For PL/M, since parameters are pushed first first, the offset
|
|
;; of a parameter is much more complicated. A known portion of
|
|
;; the offset can be computed when the text equate is generated.
|
|
;;
|
|
;; What is known is the number of garbage bytes between BP and
|
|
;; the nearest parameter (in this case the last parameter), and
|
|
;; also how many bytes of parameters have preceeded this parameter.
|
|
;;
|
|
;; What is unknown is how many total bytes of parameters there will
|
|
;; be, which affects all the generated text equates since the offset
|
|
;; from bp must be determined at some point.
|
|
;;
|
|
;; Well, the offset from BP can be computed with one variable if
|
|
;; the following is remembered:
|
|
;;
|
|
;; the offset of any parameter from the first parameter is always
|
|
;; the current parameter offset (?po).
|
|
;;
|
|
;; With this in mind, you just have to figure out where the first
|
|
;; parameter is, which is:
|
|
;;
|
|
;; bp + garbage adjustment + distance to first parameter
|
|
;; or
|
|
;; bp + ?adj + ?po
|
|
;;
|
|
;; This implies that any parameter can be defined as:
|
|
;;
|
|
;; bp + ?adj + ?po -%?po
|
|
;;
|
|
;; Make any sense?
|
|
;;
|
|
;; For PL/M, a chain of self-purging macros will be generated
|
|
;; which will pass the evaluated ?po to any previous incarnation
|
|
;; of the macro. This will allow the text equate to be generated
|
|
;; with the actual offset instead of the symbolic ?po.
|
|
;;
|
|
;;
|
|
;; usage:
|
|
;; ?pp1 n,t,o,a,b,cpc,ppc
|
|
;;
|
|
;; where:
|
|
;; n is the name to be given the equate
|
|
;; t is the type (byte, word, dword)
|
|
;; o is the offset from the first parameter
|
|
;; a is the adjustment
|
|
;; b is the adjustment plus the offset from the first parameter
|
|
;; cpc is the number of parameters so far
|
|
;; ppc is cpc - 1
|
|
|
|
|
|
?pp1 macro n,t,o,a,b,cpc,ppc
|
|
ife ?PLM ;;if to generate for C
|
|
n equ (t ptr [bp+b])
|
|
else ;;else generate for PL/M
|
|
.xcref
|
|
.xcref ?PLMParm&cpc
|
|
.cref
|
|
?PLMParm&cpc ¯o po
|
|
uconcat <n>,,<equ>,,<(t ptr [bp+>,%(a+po-o),<])>
|
|
?PLMParm&ppc po
|
|
purge ?PLMParm&cpc
|
|
&endm
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
;; 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
|
|
if ?pmd
|
|
parmR macro n,r,r2
|
|
??error <Sorry: ParmR can't be used with PMODE=1>
|
|
endm
|
|
else
|
|
.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
|
|
|
|
|
|
|
|
;; localx - generate reference to a local variable on the stack
|
|
;;
|
|
;; An equate is generated for addressing a local variable
|
|
;; on the stack for the current procedural frame.
|
|
;;
|
|
;; usage:
|
|
;; localx n
|
|
;; where:
|
|
;; x is the type b=byte, w=word, d=dword, v=variable size
|
|
;; n is the name(s) to be given the variable(s).
|
|
;;
|
|
;; Bytes are considered to be two bytes long for alignment reasons
|
|
;;
|
|
;; The locald form of the macro generates three equates:
|
|
;;
|
|
;; name - for accessing the variable as a double word
|
|
;; off_name - for accessing the offset (lsw) of the variable
|
|
;; seg_name - for accessing the segment (msw) of the variable
|
|
|
|
|
|
.xcref
|
|
.xcref localB,localW,localD,localQ,localT,localCP,localDP,localV
|
|
.cref
|
|
|
|
localB macro n
|
|
?aloc <n>,<byte ptr>,1,1,0 ;; no alignment
|
|
endm
|
|
|
|
localW macro n
|
|
?aloc <n>,<word ptr>,2,2,1 ;; word aligned
|
|
endm
|
|
|
|
localD macro n
|
|
irp x,<n>
|
|
?aloc <seg_&&x>,<word ptr>,2,2,1 ;; word aligned
|
|
?aloc <off_&&x>,<word ptr>,2,2,1 ;; word aligned
|
|
?aloc <&&x>,<dword ptr>,0,4,1 ;; word aligned
|
|
endm
|
|
endm
|
|
|
|
localQ macro n
|
|
?aloc <n>,<qword ptr>,8,8,1 ;; word aligned
|
|
endm
|
|
|
|
localT macro n
|
|
?aloc <n>,<tbyte ptr>,10,10,1 ;; word aligned
|
|
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 n,a
|
|
?aloc <n>,,%(a),0,1 ;; word aligned
|
|
endm
|
|
|
|
|
|
;; ?aloc is the macro that actually allocates local storage.
|
|
;; it is only invoked by the localx macros.
|
|
;;
|
|
;; usage:
|
|
;; ?aloc n,t,l,s,a
|
|
;; where:
|
|
;; n is a list of names of local variable of the
|
|
;; given type.
|
|
;; t is the text string for the given variable
|
|
;; and is one of:
|
|
;; word ptr
|
|
;; dword ptr
|
|
;; byte ptr
|
|
;; or alternatively left blank for variable size
|
|
;; allocations (no implicit type).
|
|
;; l is the size of the variable in bytes
|
|
;; s is the internal type flag (size), and is one of:
|
|
;; word - 2
|
|
;; dword - 4
|
|
;; byte - 1
|
|
;; variable - 0
|
|
;; a is a flag indicating that word alignment is to be
|
|
;; forced for this type of item.
|
|
;;
|
|
;; NOTE: It is assumed that the stack is already aligned on a word
|
|
;; boundary when the cProc is invoked. The macros will guarantee
|
|
;; to allocate an even number of bytes on the stack to maintain
|
|
;; word alignment.
|
|
|
|
|
|
?aloc macro n,t,l,s,a
|
|
if ?cpd ;;must be in a proc def
|
|
.xcref
|
|
irp x,<n> ;;generate symbol equates
|
|
???=???+l ;;update length of locals
|
|
if a ;;if align, then force word alignment
|
|
???=((??? + 1) and 0fffeh)
|
|
endif
|
|
?al1 x,<t>,%(???+?ia) ;;?ia will always be valid (0 or 2)
|
|
.xcref ?t&&x
|
|
?t&&x=s ;;save size info
|
|
endm
|
|
.cref
|
|
else
|
|
??error <locals "&n" declared outside procedure def>
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
;; ?al1 - allocate local, continued.
|
|
;;
|
|
;; ?al1 actually generates the text equate for the local variable.
|
|
;; The form of the text equate generated is more or less:
|
|
;;
|
|
;; name equ (type ptr [bp-?ia-nn])
|
|
;; or
|
|
;; name equ ([bp-?ia-nn])
|
|
;;
|
|
;; where:
|
|
;; ?ia is defined to be either zero, or is defined to be
|
|
;; the number of bytes between the saved BP and the first
|
|
;; local. ?ia is only applicable if the current cProc is
|
|
;; a windows far procedure or if parmRs have been
|
|
;; encountered. If not, the ?ia will be zero. since ?ia
|
|
;; is determinable prior to invoking this macro, it will be
|
|
;; added into the offset ("nn") passed to this macro
|
|
;;
|
|
;; usage:
|
|
;; ?al1 n,t,o
|
|
;; where:
|
|
;; n is the name for the text equate
|
|
;; t is the type of the equate
|
|
;; o is the offset of the equate
|
|
|
|
|
|
?al1 macro n,t,o
|
|
n equ (t [bp-o])
|
|
endm
|
|
|
|
|
|
;; ?gcc - get calling convention
|
|
;;
|
|
;; ?gcv sets the given symbol to the calling convention
|
|
;; to be used.
|
|
;;
|
|
;; usage:
|
|
;; ?gcc s,i,cc
|
|
;;
|
|
;; where:
|
|
;; s is the symbol to return the convention in
|
|
;; s = 0 if 'C' calling convention
|
|
;; s = 1 if PL/M (PASCAL) calling convention
|
|
;; i is the initial value for s
|
|
;; cc is the calling convention override, and may be one of
|
|
;; C use 'C' convention
|
|
;; PLM use PL/M calling convention
|
|
;; PASCAL use PL/M calling convention
|
|
|
|
?gcc macro s,i,cc
|
|
s = i ;;Set default calling convention
|
|
ifnb <cc>
|
|
ifidn <cc>,<C> ;;If overriding default
|
|
s=0 ;; 'C' calling convention
|
|
endif
|
|
ifidn <cc>,<PLM>
|
|
s=1 ;; PL/M calling convention
|
|
endif
|
|
ifidn <cc>,<PASCAL>
|
|
s=1 ;; PL/M calling convention
|
|
endif
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
ifndef ?NOGLOBAL
|
|
.xcref
|
|
.xcref globalB,globalW,globalD,globalQ,globalT,globalCP,globalDP
|
|
.cref
|
|
|
|
;; globalx - define global data of type x
|
|
;;
|
|
;; usage:
|
|
;; globalx n,i,s,c
|
|
;; where:
|
|
;; x is the type of the variable b=byte, w=word, d=dword
|
|
;; q=quad word, t=tenbytes, cp=code pointer, dp=data pointer
|
|
;; n is the name to be given the variable.
|
|
;; i is the initial value of the variable.
|
|
;; s is the duplication factor
|
|
;; c is the convention, C for C, PLM or PASCAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
;;
|
|
;; The D form will generate two extra equates of the form off_n and seg_n.
|
|
|
|
globalB macro n,i,s,c
|
|
?ad <n>,1
|
|
?dd n,1,<byte>,<db>,<i>,<s>,<c>
|
|
endm
|
|
|
|
globalW macro n,i,s,c
|
|
?ad <n>,2
|
|
?dd n,1,<word>,<dw>,<i>,<s>,<c>
|
|
endm
|
|
|
|
globalD macro n,i,s,c
|
|
?ad <n>,4
|
|
?dd n,1,<dword>,<dd>,<i>,<s>,<c>
|
|
off_&n equ n
|
|
seg_&n equ n[2]
|
|
endm
|
|
|
|
globalQ macro n,i,s,c
|
|
?ad <n>,8
|
|
?dd n,1,<qword>,<dq>,<i>,<s>,<c>
|
|
endm
|
|
|
|
globalT macro n,i,s,c
|
|
?ad <n>,10
|
|
?dd n,1,<tbyte>,<dt>,<i>,<s>,<c>
|
|
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
|
|
|
|
endif
|
|
|
|
|
|
ifndef ?NOSTATIC
|
|
.xcref
|
|
.xcref staticB,staticW,staticD,staticQ,staticT,staticCP,staticDP
|
|
.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=tenbytes, cp=code pointer, dp=data pointer
|
|
;; n is the name to be given the variable.
|
|
;; i is the initial value of the variable.
|
|
;; s is the duplication factor
|
|
;;
|
|
;; statics do not generate an underscored version of the symbol
|
|
;; since they are intended to be internal symbols. If they are
|
|
;; required to be public, use globlax.
|
|
|
|
|
|
staticB macro n,i,s
|
|
?ad <n>,1
|
|
?dd n,0,<byte>,<db>,<i>,<s>,<PLM> ;;PLM to keep from generating _
|
|
endm
|
|
|
|
staticW macro n,i,s
|
|
?ad <n>,2
|
|
?dd n,0,<word>,<dw>,<i>,<s>,<PLM>
|
|
endm
|
|
|
|
staticD macro n,i,s
|
|
?ad <n>,4
|
|
?dd n,0,<dword>,<dd>,<i>,<s>,<PLM>
|
|
endm
|
|
|
|
staticQ macro n,i,s
|
|
?ad <n>,8
|
|
?dd n,0,<qword>,<dq>,<i>,<s>,<PLM>
|
|
endm
|
|
|
|
staticT macro n,i,s
|
|
?ad <n>,10
|
|
?dd n,0,<tbyte>,<dt>,<i>,<s>,<PLM>
|
|
endm
|
|
|
|
if sizec
|
|
staticCP macro n,i,s
|
|
staticD n,<i>,<s>
|
|
endm
|
|
else
|
|
staticCP macro n,i,s
|
|
staticW n,<i>,<s>
|
|
endm
|
|
endif
|
|
|
|
if sized
|
|
staticDP macro n,i,s
|
|
staticD n,<i>,<s>
|
|
endm
|
|
else
|
|
staticDP macro n,i,s
|
|
staticW n,<i>,<s>
|
|
endm
|
|
endif
|
|
endif
|
|
|
|
|
|
|
|
;; ?dd is the generalized data definition macro.
|
|
;;
|
|
;; format:
|
|
;; ?dd n,p,t,d,i,s,c
|
|
;; where:
|
|
;; n is the name of the procedure
|
|
;; p is the public flag
|
|
;; t is the assembler type (byte, word, dword)
|
|
;; d is the assembler directive (db,dw or dd)
|
|
;; i is the initial value
|
|
;; s is a duplication factor
|
|
;; c is the convention, C for C, PLM or PSACAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
|
|
|
|
?dd macro n,p,t,d,i,s,c
|
|
?gcc ?dd2,%?PLM,<c> ;;Set calling convention
|
|
ife ?dd2 ;;If 'C'
|
|
n label t
|
|
?dd1 _&n,p,<d>,<i>,<s> ;;Microsoft C uses leading underscores
|
|
else
|
|
?dd1 n,p,<d>,<i>,<s> ;;If PL/M
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
;; ?dd1 is the generalized data definition macro.
|
|
;;
|
|
;; format:
|
|
;; ?dd1 n,p,d,i,s
|
|
;; where:
|
|
;; n is the name of the procedure
|
|
;; p is the public flag
|
|
;; d is the assembler directive (db,dw or dd)
|
|
;; i is the initial value
|
|
;; s is a duplication factor
|
|
|
|
|
|
?dd1 macro n,p,d,i,s
|
|
if p
|
|
public n
|
|
endif
|
|
ifb <s>
|
|
n d i
|
|
else
|
|
ifb <i>
|
|
n d s dup (?)
|
|
else
|
|
n d s dup (i)
|
|
endif
|
|
endif
|
|
endm
|
|
|
|
|
|
|
|
ifndef ?NOEXTERN
|
|
.xcref
|
|
.xcref ?ex1,?ex2,externB,externW,externD,externQ,externT
|
|
.xcref externNP,externFP,externP,externCP,externDP,externA
|
|
.cref
|
|
?ex2 = 0
|
|
|
|
;; 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=quad word, t=tenbytes, cp=code pointer
|
|
;; dp=data pointer, a=absolute
|
|
;; n is a list of names to define
|
|
;; c is the convention, C for C, PLM or PSACAL forPL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
|
|
externA macro n,c ;;40h is reserved for whatever will
|
|
?ex1 <n>,40h,<abs>,<c>,<> ;; be done in the future for ASB
|
|
endm ;; externals
|
|
|
|
externB macro n,c
|
|
?ex1 <n>,1,<byte>,<c>,<>
|
|
endm
|
|
|
|
externW macro n,c
|
|
?ex1 <n>,2,<word>,<c>,<>
|
|
endm
|
|
|
|
externD macro n,c
|
|
?ex1 <n>,4,<dword>,<c>,<>
|
|
endm
|
|
|
|
externQ macro n,c
|
|
?ex1 <n>,8,<qword>,<c>,<>
|
|
endm
|
|
|
|
externT macro n,c
|
|
?ex1 <n>,10,<tbyte>,<c>,<>
|
|
endm
|
|
|
|
externNP macro n,c
|
|
?ex1 <n>,2,<near>,<c>,<cc>
|
|
endm
|
|
|
|
externFP macro n,c
|
|
?ex1 <n>,4,<far>,<c>,<cc>
|
|
endm
|
|
|
|
if sizec
|
|
externP macro n,c
|
|
?ex1 <n>,4,<far>,<c>,<cc>
|
|
endm
|
|
else
|
|
externP macro n,c
|
|
?ex1 <n>,2,<near>,<c>,<cc>
|
|
endm
|
|
endif
|
|
|
|
if sizec
|
|
externCP macro n,c
|
|
?ex1 <n>,4,<dword>,<c>,<>
|
|
endm
|
|
else
|
|
externCP macro n,c
|
|
?ex1 <n>,2,<word>,<c>,<>
|
|
endm
|
|
endif
|
|
|
|
if sized
|
|
externDP macro n,c
|
|
?ex1 <n>,4,<dword>,<c>,<>
|
|
endm
|
|
else
|
|
externDP macro n,c
|
|
?ex1 <n>,2,<word>,<c>,<>
|
|
endm
|
|
endif
|
|
|
|
|
|
|
|
;; ?ex1 is the generalized external definition macro
|
|
;;
|
|
;; format:
|
|
;; ?ex1 n,s,d,c,scv
|
|
;; where:
|
|
;; n is are the names of the externals
|
|
;; s is the size in bytes (used for typing)
|
|
;; d is the type
|
|
;; c is the convention, C for C, PLM or PSACAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
;; scv save calling convention. If this field is "cc", then
|
|
;; the calling convention will be saved in a ?CCn equ.
|
|
|
|
?ex1 macro n,s,d,c,scv
|
|
?gcc ?ex2,%?PLM,<c>
|
|
irp x,<n>
|
|
.xcref
|
|
.xcref ?t&&x
|
|
.cref
|
|
?t&&x=s ;;save size info
|
|
ife ?ex2
|
|
extrn _&&x:&d
|
|
x equ _&&x
|
|
else
|
|
extrn x:&d
|
|
endif
|
|
ifidn <scv>,<cc> ;;save calling convention (C or PL/M)
|
|
.xcref ;; if NP, FP, or P
|
|
.xcref ?CC&&x
|
|
.cref
|
|
?CC&&x=?ex2
|
|
endif
|
|
endm
|
|
endm
|
|
endif
|
|
|
|
|
|
|
|
ifndef ?NOLABEL
|
|
.xcref
|
|
.xcref ?lb1,?lblpu,?lb2
|
|
.xcref labelB,labelW,labelD,labelQ,labelT
|
|
.xcref labelNP,labelFP,labelP,labelCP,labelDP
|
|
.cref
|
|
?lblpu = 0
|
|
?lb2 = 0
|
|
|
|
;; labelx - define label of data type x
|
|
;;
|
|
;; usage:
|
|
;; labelx n,c
|
|
;; where:
|
|
;; x is the type of the variable b=byte, w=word, d=dword
|
|
;; q=quad word, t=tenbytes, cp=code pointer, dp=data pointer
|
|
;; n is a list of names to define, the first of which can
|
|
;; be the keyword public
|
|
;; c is the convention, C for C, PLM or PSACAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
|
|
labelB macro n,c
|
|
?lb1 <n>,1,<byte>,<c>
|
|
endm
|
|
|
|
labelW macro n,c
|
|
?lb1 <n>,2,<word>,<c>
|
|
endm
|
|
|
|
labelD macro n,c
|
|
?lb1 <n>,4,<dword>,<c>
|
|
endm
|
|
|
|
labelQ macro n,c
|
|
?lb1 <n>,8,<qword>,<c>
|
|
endm
|
|
|
|
labelT macro n,c
|
|
?lb1 <n>,10,<tbyte>,<c>
|
|
endm
|
|
|
|
labelNP macro n,c
|
|
?lb1 <n>,2,<near>,<c>
|
|
endm
|
|
|
|
labelFP macro n,c
|
|
?lb1 <n>,4,<far>,<c>
|
|
endm
|
|
|
|
if sizec
|
|
labelP macro n,c
|
|
?lb1 <n>,4,<far>,<c>
|
|
endm
|
|
else
|
|
labelP macro n,c
|
|
?lb1 <n>,2,<near>,<c>
|
|
endm
|
|
endif
|
|
|
|
if sizec
|
|
labelCP macro n,c
|
|
?lb1 <n>,4,<dword>,<c>
|
|
endm
|
|
else
|
|
labelCP macro n,c
|
|
?lb1 <n>,2,<word>,<c>
|
|
endm
|
|
endif
|
|
|
|
if sized
|
|
labelDP macro n,c
|
|
?lb1 <n>,4,<dword>,<c>
|
|
endm
|
|
else
|
|
labelDP macro n,c
|
|
?lb1 <n>,2,<word>,<c>
|
|
endm
|
|
endif
|
|
|
|
|
|
;; ?lb1 is the generalized label definition macro
|
|
;;
|
|
;; format:
|
|
;; ?lb1 n,s,d
|
|
;; where:
|
|
;; n are the names of the labels
|
|
;; s is the size in bytes (used for typing)
|
|
;; d is the type
|
|
;; c is the convention, C for C, PLM or PSACAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
|
|
?lb1 macro n,s,d,c
|
|
?gcc ?lb2,%?PLM,<c>
|
|
?lblpu=0
|
|
irp x,<n>
|
|
ifidn <x>,<PUBLIC>
|
|
?lblpu=1
|
|
else
|
|
.xcref
|
|
.xcref ?t&&x
|
|
.cref
|
|
?t&&x=s ;;save size info
|
|
ife ?lb2 ;;If C
|
|
if ?lblpu
|
|
public _&&x
|
|
endif
|
|
_&&x label &d
|
|
x equ _&&x
|
|
else ;;If PL/M
|
|
if ?lblpu
|
|
public x
|
|
endif
|
|
x label &d
|
|
endif
|
|
endif
|
|
endm
|
|
endm
|
|
endif
|
|
|
|
|
|
|
|
ifndef ?NODEF
|
|
|
|
;; defx - inform macros that name is of type x
|
|
;;
|
|
;; The given name(s) is flaged to be of the given type. This macro
|
|
;; is intended for giving types to variables that were not generated
|
|
;; by the macros (i.e., static storage). There must be a type definition
|
|
;; for all parameters in a call list.
|
|
;;
|
|
;; usage:
|
|
;; defx n
|
|
;; where:
|
|
;; x is the type of the variable b=byte, w=word, d=dword
|
|
;; n is the name(s) to be given the variable(s).
|
|
;;
|
|
;; Bytes are considered to be two bytes long for alignment reasons
|
|
|
|
.xcref
|
|
.xcref defB,defW,defD,defQ,defT,defCP,defDP
|
|
.cref
|
|
|
|
defB macro n
|
|
?ad <n>,1
|
|
endm
|
|
|
|
defW macro n
|
|
?ad <n>,2
|
|
endm
|
|
|
|
defD macro n
|
|
?ad <n>,4
|
|
endm
|
|
|
|
defQ macro n
|
|
?ad <n>,8
|
|
endm
|
|
|
|
defT macro n
|
|
?ad <n>,10
|
|
endm
|
|
|
|
if sizec
|
|
defCP macro n
|
|
defD <n>
|
|
endm
|
|
else
|
|
defCP macro n
|
|
defW <n>
|
|
endm
|
|
endif
|
|
|
|
if sized
|
|
defDP macro n
|
|
defD <n>
|
|
endm
|
|
else
|
|
defDP macro n
|
|
defW <n>
|
|
endm
|
|
endif
|
|
endif
|
|
|
|
|
|
|
|
; ?ad is the macro which creates a definition for the given
|
|
; symbol
|
|
;
|
|
; usage:
|
|
; ?ad <n>,s
|
|
; where:
|
|
; n is a list of names to define
|
|
; s is the size info (1,2,4,8,10)
|
|
|
|
|
|
?ad macro n,s
|
|
irp x,<n>
|
|
.xcref
|
|
.xcref ?t&&x
|
|
.cref
|
|
?t&&x=s ;;save size info
|
|
endm
|
|
endm
|
|
|
|
|
|
|
|
ifndef ?NOPTR
|
|
.xcref
|
|
.xcref regPtr,farPtr
|
|
.cref
|
|
|
|
;; 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
|
|
.xcref
|
|
.xcref ?t&n
|
|
.cref
|
|
n ¯o
|
|
push s ;@
|
|
push o ;@
|
|
&endm
|
|
?t&n=80h
|
|
endm
|
|
endif
|
|
|
|
|
|
|
|
;; arg - declare argument
|
|
;;
|
|
;; The given argument(s) is added to the argument list structure
|
|
;;
|
|
;; format:
|
|
;; arg a
|
|
;;
|
|
;; where:
|
|
;; a is any valid argument to push.
|
|
;;
|
|
;; If any element in arglist has not been defined or isn't a 16-bit
|
|
;; register, then a complete specification must have been given in a
|
|
;; text equate and a defx also given (if not, you'll pay the penalty!)
|
|
|
|
|
|
arg macro a
|
|
irp x,<a>
|
|
?argc=?argc+1 ;;increment the arg count
|
|
?atal <x>,%?argc ;;generate argument
|
|
endm
|
|
endm
|
|
|
|
|
|
|
|
;; ?atal (add to argument list) generates a macro that will cause
|
|
;; the given argument to be processed when invoked. It is used by
|
|
;; the arg macro only.
|
|
|
|
|
|
?atal macro n,i
|
|
.xcref
|
|
.xcref ?ali&i
|
|
.cref
|
|
?ali&i ¯o
|
|
?ap n
|
|
&endm
|
|
endm
|
|
|
|
|
|
|
|
;; ?ap - process arguments and place onto stack
|
|
;;
|
|
;; The given argument is processed (type checking) and place on
|
|
;; the stack for a pending call. There must be a type definition
|
|
;; for all arguments (except words). This can be done by using
|
|
;; text equates and the defx macro.
|
|
;;
|
|
;; format:
|
|
;; ?ap n
|
|
;; where:
|
|
;; n is the name of the argument to be pushed
|
|
;;
|
|
;; The variable ?argl is updated by the length of the arguments
|
|
;; pushed so that the stack can be cleaned up after the call.
|
|
|
|
|
|
?ap macro n
|
|
?argl=?argl+2 ;;assume one word is pushed
|
|
ifdef ?t&n
|
|
ife ?t&n-1 ;;byte type
|
|
push word ptr (n) ;@
|
|
exitm
|
|
endif
|
|
|
|
ife ?t&n-2 ;;word type
|
|
push n ;@
|
|
exitm
|
|
endif
|
|
|
|
ife ?t&n-4 ;;dword type
|
|
push word ptr (n)[2] ;@
|
|
push word ptr (n) ;@
|
|
?argl=?argl+2
|
|
exitm
|
|
endif
|
|
|
|
ife ?t&n-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
|
|
|
|
if ?t&n and 80h ;;far pointer type
|
|
n
|
|
?argl=?argl+2
|
|
exitm
|
|
endif
|
|
|
|
ife ?t&n ;;variable storage
|
|
push word ptr (n) ;@
|
|
exitm
|
|
endif
|
|
endif
|
|
|
|
push n ;;unknown or register or immediate ;@
|
|
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 conventing 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 arguments to be pushed (optional, may be
|
|
;; specified with the "arg" macro.
|
|
;; c is the convention, C for C, PLM or PSACAL for PL/M.
|
|
;; The default (?PLM flag) will be used if not specified.
|
|
|
|
|
|
cCall macro n,a,c
|
|
ifnb <a> ;;add any arguments to list
|
|
arg <a>
|
|
endif
|
|
mpush %?rsl ;;save registers (if any)
|
|
|
|
ifdef ?CC&n ;;if calling convention has been
|
|
?cCall1=?CC&n ;; defined for target, use it
|
|
else ;;else use the default
|
|
?cCall1=?PLM
|
|
endif
|
|
|
|
ifnb <c> ;;If possible override, check it out
|
|
?gcc ?cCall1,%?cCall1,<c>
|
|
endif
|
|
|
|
?argl=0 ;;init argument length
|
|
ife ?cCall1 ;;if C calling convention
|
|
?acb=?argc ;;initialize for looping
|
|
else
|
|
?acb=1 ;;initialize for looping
|
|
endif
|
|
|
|
rept ?argc ;;push arguments and purge macros
|
|
uconcat <?ali>,%?acb
|
|
uconcat <purge>,,<?ali>,%?acb
|
|
ife ?cCall1 ;;if C calling convention
|
|
?acb=?acb-1
|
|
else
|
|
?acb=?acb+1
|
|
endif
|
|
endm
|
|
call n ;;call the procedure ;@
|
|
if ((?cCall1 eq 0) and (?argl ne 0)) ;;If C calling convention and arguments
|
|
add sp,?argl ;; then remove them ;@
|
|
endif
|
|
mpop %?rsl ;;pop all specified regs
|
|
?rsl=0 ;;invalidate save list
|
|
?argc= 0 ;; " arguments
|
|
?argl= 0
|
|
endm
|
|
|
|
|
|
|
|
|
|
;; cProc - define a 'c' procedure
|
|
;;
|
|
;; cProc is the procedure definition for procedures.
|
|
;;
|
|
;; format:
|
|
;; cProc n,cf,a
|
|
;; where:
|
|
;; n is the name of the procedure
|
|
;;
|
|
;; cf controls certain definitions, and may be:
|
|
;; NEAR proc is to be a near label
|
|
;; FAR proc is to be a far label
|
|
;; PUBLIC proc is to be defined as public
|
|
;; SMALL call makeframe procedure
|
|
;; NODATA dont create prolog code to setup DS
|
|
;; ATOMIC don't link stack if not needed
|
|
;; NODATA must be specified for ATOMIC
|
|
;; FORCEFRAME Force generation of a frame
|
|
;; NOTE: FORCEFRAME no longer supported
|
|
;; C proc is to be a C procedure
|
|
;; PLM proc is to be a PL/M procedure
|
|
;; PASCAL proc is to be a PL/M procedure
|
|
;; WIN proc is to be a windows procedure
|
|
;; NONWIN proc isn't to be a windows procedure
|
|
;; LOADDS Preserve & set DS to _DATA
|
|
;; EXPORTED mov ax,ds, nop at start of routine
|
|
;;
|
|
;; a is a list of registers that are to be saved whenever
|
|
;; the procedure is invoked.
|
|
;;
|
|
;; makeframe procedure: If small is specified, then
|
|
;; the "makeframe procedure" is invoked instead of
|
|
;; generating normal prologues/epilogues
|
|
;;
|
|
;; A call is performed to the makeframe procedure. The
|
|
;; call is followed by two bytes. the first byte is the
|
|
;; number of locals to allocate for the frame, the second
|
|
;; is the number of bytes of parameters. The makeframe
|
|
;; procedure will in turn call the cProc routine at the
|
|
;; address following the data bytes. When the cProc is
|
|
;; finished, it will do a near return to the makeframe
|
|
;; procedure to clean up the frame and exit.
|
|
;;
|
|
;; Note that register parameters and makeframe are
|
|
;; incompatible and cannot be used together.
|
|
;;
|
|
;; The makeframe procedure will save SI, DI, and also
|
|
;; DS if a far procedure. These registers will be
|
|
;; removed from the autosave list if specified.
|
|
|
|
|
|
cProc macro n,cf,a
|
|
if ?cpd
|
|
?utpe ;;generate unterminated proc error
|
|
endif
|
|
|
|
?cpd=1 ;;a procdef is active now
|
|
???=0 ;;no locals are defined yet
|
|
?argc=0 ;;no arguments are defined
|
|
?ba=0 ;;not in a procedure
|
|
?po=0 ;;initial parameter offset
|
|
?pu=0 ;;initial public setting
|
|
?ia=0 ;;no special prolog/epilog
|
|
?adj=4 ;;parameter adjustment (near ret+bp)
|
|
?rp=0 ;;no register parameters
|
|
?uf=0 ;;don't use makeframe
|
|
?wfp=?WIN ;;default far procedure (win or not)
|
|
?ff=0 ;;don't force frame setup
|
|
?pas=0 ;;process register save list
|
|
?pcc=?PLM ;;calling convention (C or PL/M)
|
|
?lds=0 ;;no LOADDS
|
|
?exp=0 ;;not EXPORTED
|
|
|
|
ifnb <a> ;;register save list
|
|
?ri ?pas,<a>
|
|
endif
|
|
|
|
?pc=sizec ;;default size
|
|
?nd=?nodata1 ;;default NODATA flag
|
|
?nx=0 ;;default is not ATOMIC
|
|
irp x,<cf>
|
|
ifidn <x>,<FAR> ;;if far,
|
|
?pc=1 ;; set far flag
|
|
endif
|
|
ifidn <x>,<NEAR> ;;if near,
|
|
?pc=0 ;; set near flag
|
|
endif
|
|
ifidn <x>,<PUBLIC> ;;if public,
|
|
?pu=1 ;; set public flag
|
|
endif
|
|
ifidn <x>,<SMALL> ;;if small
|
|
?uf=1 ;; set small flag
|
|
endif
|
|
ifidn <x>,<DATA> ;;if data
|
|
?nd=0 ;; reset NODATA flag
|
|
endif
|
|
ifidn <x>,<NODATA> ;;if NODATA
|
|
?nd=1 ;; set NODATA flag
|
|
endif
|
|
ifidn <x>,<ATOMIC> ;;if ATOMIC
|
|
?nx=1 ;; set ATOMIC flag
|
|
endif
|
|
ifidn <x>,<C> ;;if to force C calling convention
|
|
?pcc=0 ;; set flag
|
|
endif
|
|
ifidn <x>,<PLM> ;;if to force PLM calling convention
|
|
?pcc=1 ;; set flag
|
|
endif
|
|
ifidn <x>,<PASCAL> ;;if to force PLM calling convention
|
|
?pcc=1 ;; set flag
|
|
endif
|
|
ifidn <x>,<WIN> ;;if to force a Window's frame
|
|
?wfp=1 ;; set flag
|
|
endif
|
|
ifidn <x>,<NONWIN> ;;if not to be a Window's frame
|
|
?wfp=0 ;; set flag
|
|
endif
|
|
ifidn <x>,<LOADDS> ;; Preserve & setup DS
|
|
?lds=1
|
|
endif
|
|
ifidn <x>,<EXPORTED>
|
|
?exp=1
|
|
endif
|
|
endm
|
|
|
|
if ?pcc ;;If PLM
|
|
?PLMPrevParm=0 ;; show no previous parameter
|
|
.xcref
|
|
.xcref ?PLMParm0
|
|
.cref
|
|
?PLMParm0 ¯o ;;Null macro to terminate
|
|
purge ?PLMParm0
|
|
&endm
|
|
endif
|
|
|
|
.xcref
|
|
.xcref ?CC&n
|
|
.cref
|
|
?CC&n=?pcc ;;Save procedure type
|
|
|
|
if (?nx eq 1) and (?nd eq 0) ;;ATOMIC requires NODATA
|
|
?nx = 0 ;;clear the ATOMIC keyword
|
|
??error <ATOMIC specified without NODATA - ATOMIC ignored>
|
|
endif
|
|
|
|
if ?pc ;;if a far procedure
|
|
if ?wfp+?exp+?lds ;;if windows, EXPORTED, or LOADDS
|
|
ife ?nx ;;and not ATOMIC
|
|
ife ?pmd ;;If PMODE, no adjustment needed
|
|
?ia=2 ;; adjust locals for saved ds
|
|
endif
|
|
?pas = ?pas and (not ?ds) ;;no need for extra save
|
|
endif
|
|
endif
|
|
?adj=?adj+2 ;;far, make parameter adjustment
|
|
else
|
|
?wfp=0 ;;not a far windows procedure
|
|
endif
|
|
|
|
ife ?386regs
|
|
?pas = ?pas and (not (?sp+?cs+?ss)) ;;make no sense to save these
|
|
endif
|
|
|
|
if ?uf ;;don't save these if user frame
|
|
if ?386regs
|
|
?pas = ?pas and (not (?si+?di))
|
|
else
|
|
?pas = ?pas and (not (?bp+?si+?di))
|
|
endif
|
|
endif
|
|
|
|
ife ?pcc
|
|
?pg <_&n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
|
|
else
|
|
?pg <n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
|
|
endif
|
|
endm
|
|
|
|
|
|
;; ?pg - generate begin and nested macros for current procedure
|
|
;;
|
|
;; format:
|
|
;; ?pg n,p,c,a,w,nnu,cc
|
|
;; where:
|
|
;; n is the name of the procedure
|
|
;; p is the public flag
|
|
;; c is the class definition for the procedure
|
|
;; a is an enumerated list of registers to save
|
|
;; at entry and restore at exit
|
|
;; w true if a far windows procedure
|
|
;; nnu procedure name without any underscore
|
|
;; cc calling convention (C or PL/M)
|
|
;;
|
|
;;
|
|
;; local stack allocation will be forced to an even byte count to
|
|
;; maintain stack word alignment.
|
|
|
|
;;
|
|
;;
|
|
|
|
|
|
?pg macro n,p,c,a,w,nnu,cc
|
|
.xcref
|
|
if ?uf ;;if user frame
|
|
if ?nd
|
|
??error <NODATA encountered in &n - user frame ignored>
|
|
?uf=0
|
|
endif
|
|
endif
|
|
|
|
.xcref cBegin
|
|
cBegin ¯o g ;;generate cBegin macro
|
|
.xcref
|
|
if cc ;;Finish definition of parameters
|
|
uconcat <?PLMParm>,%?PLMPrevParm,%?po
|
|
endif
|
|
|
|
if ?uf ;;if user frame
|
|
if ?rp
|
|
??error <parmR encountered in &n - user frame ignored>
|
|
?uf=0
|
|
endif
|
|
endif
|
|
?pg1 <n>,c,a,%?po,w,%?uf,%?nd,%?rp,cc ;;generate cEnd macro
|
|
?cpd=0 ;;terminate current proc def
|
|
?argc=0 ;;no arguments are defined yet
|
|
?ba=1 ;;have reached a begin
|
|
???=(???+1) and 0fffeh ;;word align local storage
|
|
|
|
if p ;;If to be public
|
|
public n
|
|
endif
|
|
|
|
ife c ;;declare procedure type
|
|
n proc near
|
|
else
|
|
n proc far
|
|
endif
|
|
|
|
ife cc ;;if 'C' calling convention
|
|
nnu equ n ;; generate label without underscore
|
|
endif
|
|
|
|
ifidn <g>,<nogen> ;;if nogen specified, shouldn't have
|
|
if ???+?po+a+?rp ;; parms, locals, or saved regs
|
|
??_out <cBegin - possible invalid use of nogen>
|
|
endif
|
|
else ;;must generate a frame
|
|
if ?uf ;;if user frame code specified
|
|
?mf c,%???,%?po ;; call user's makeframe
|
|
mpush a ;; save specified registers
|
|
else
|
|
|
|
if w+?exp+?lds ;;if a far windows procedure
|
|
|
|
if ?pmd ;;if protect mode:
|
|
ife ?nd ;; if not NODATA
|
|
if ?lds ;; and LOADDS
|
|
mov ax,_DATA ;; ax = _DATA
|
|
else ;; else if EXPORTED
|
|
if ?exp
|
|
mov ax,ds ;; ax = DS
|
|
nop ;;
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ife ?nx ;;!ATOMIC:
|
|
if ???+?po ;; or there are no locals or params
|
|
if ?chkstk1 ;; if stack checking,
|
|
push bp ;; we can't use ENTER
|
|
mov bp,sp
|
|
else
|
|
if ??? ;; If there are locals, use ENTER
|
|
enter ???,0
|
|
else
|
|
push bp ;; otherwise it's smaller/faster
|
|
mov bp,sp ;; to use standard sequence
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
push ds ;; preserve DS
|
|
|
|
if ?lds+?exp ;; if LOADDS or EXPORTED
|
|
mov ds,ax ;; set up new DS
|
|
endif
|
|
else ;;ATOMIC:
|
|
if ?ff+???+?po+?rp ;;if any locals or parameters
|
|
push bp ;; then must set frame pointer ;@
|
|
mov bp,sp ;; to be able to access them ;@
|
|
endif
|
|
endif
|
|
|
|
else ;; !?pmd
|
|
|
|
ife ?nd ;;if not NODATA,
|
|
mov ax,ds ;; then set AX = currentds, and ;@
|
|
nop ;; leave room for MOV AX,_DATA ;@
|
|
endif
|
|
ife ?nx ;;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 ;@
|
|
push ds ;@
|
|
else ;;ATOMIC procedure
|
|
if ?ff+???+?po+?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 ?nd ;;if not NODATA, then AX should
|
|
mov ds,ax ;; have the ds to use ;@
|
|
endif
|
|
|
|
endif ;; !?pmd
|
|
|
|
else ;;not windows. use standard prolog
|
|
|
|
if ?pmd ;;protect mode:
|
|
|
|
if ?exp
|
|
mov ax,ds ;; If EXPORTED, generate patchable
|
|
nop ;; instruction.
|
|
else
|
|
if ?lds ;; If LOADDS, set up AX = _DATA
|
|
mov ax,_DATA
|
|
endif
|
|
endif
|
|
|
|
if ?ff+???+?po+?rp ;; If frame needed...
|
|
if ?chkstk1 ;; if stack checking,
|
|
push bp ;; we can't use ENTER
|
|
mov bp,sp
|
|
else
|
|
if ??? ;; If there are locals, use ENTER
|
|
enter ???,0
|
|
else
|
|
push bp ;; otherwise it's smaller/faster
|
|
mov bp,sp ;; to use standard sequence
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
if ?exp+?lds ;;if EXPORTED or LOADDS
|
|
push ds ;; preserve DS
|
|
mov ds,ax ;; and set up new one.
|
|
endif
|
|
|
|
else ;; ! ?pmd
|
|
if ?ff+???+?po+?rp ;;if any locals or parameters
|
|
push bp ;; then must set frame pointer ;@
|
|
mov bp,sp ;; to be able to access them ;@
|
|
endif
|
|
endif ;; ! ?pmd
|
|
|
|
endif ;; ! w
|
|
|
|
if ?rp ;;if parmR's, push them before
|
|
?uf=0 ;; allocating locals and saving
|
|
rept ?rp ;; the autosave registers
|
|
uconcat mpush,,?rp,%?uf
|
|
?uf=?uf+1
|
|
endm
|
|
endif
|
|
|
|
if ??? ;;if locals to allocate
|
|
if ?chkstk1 ;;if stack checking enabled
|
|
ifdef ?CHKSTKPROC ;;if user supplied stack checking
|
|
?CHKSTKPROC %??? ;; invoke it with bytes requested
|
|
else
|
|
mov ax,??? ;;invoke default stack checking ;@
|
|
ife cc
|
|
call _chkstk ;@
|
|
else
|
|
call chkstk ;@
|
|
endif
|
|
endif
|
|
else ;;no stack checking
|
|
ife ?pmd ;;no need if in pmode (we used enter)
|
|
sub sp,??? ;; allocate any local storage ;@
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
mpush a ;;save autosave registers
|
|
endif
|
|
|
|
ifdef ?PROFILE ;;if profiling enabled
|
|
if c ;; and a far procedure
|
|
call StartNMeas ;; invoke profile start procedure ;@
|
|
endif
|
|
endif
|
|
|
|
endif
|
|
|
|
.cref
|
|
purge cBegin ;;remove the macro
|
|
&endm ;;end of cBegin macro
|
|
|
|
.xcref ?utpe
|
|
?utpe ¯o
|
|
??error <unterminated procedure definition: "&n">
|
|
&endm
|
|
.cref
|
|
endm ;;end of ?pg macro
|
|
|
|
|
|
|
|
|
|
;; ?pg1 - generate end macro for current procedure
|
|
;;
|
|
;; format:
|
|
;; ?pg1 n,c,a,o,w,f,d,r,cc
|
|
;; where:
|
|
;; n is the name of the procedure
|
|
;; c is the class definition for the procedure
|
|
;; a is an enumerated list of registers to save
|
|
;; at entry and restore at exit
|
|
;; o is the number of bytes of paramteres to remove at exit
|
|
;; w true if a far windows procedure
|
|
;; f is 1 if to use the user's makeframe procedure
|
|
;; d is 1 if NODATA procedure
|
|
;; r number of register parameters
|
|
;; cc calling convention (C or PL/M)
|
|
|
|
|
|
?pg1 macro n,c,a,o,w,f,d,r,cc
|
|
.xcref
|
|
.xcref cEnd
|
|
cEnd ¯o g ;;start of cEnd macro
|
|
.xcref
|
|
?ba=0 ;;no longer in a procedure
|
|
|
|
ifidn <g>,<nogen> ;;validate nogen usage
|
|
if o+a+r
|
|
??_out <cEnd - possible invalid use of nogen>
|
|
endif
|
|
else ;;must generate an epilog
|
|
ifdef ?PROFILE ;;if profiling enabled
|
|
if c ;; and a far procedure
|
|
call StopNMeas ;; invoke profile stop procedure
|
|
endif ;; (doesn't trash DX:AX)
|
|
endif
|
|
|
|
mpop a ;;restore autosaved registers
|
|
if f ;;if to use the "makeframe" procedure
|
|
db 0c3h ;; near return to user's makeframe @
|
|
else
|
|
if w+?exp+?lds ;;if far win proc, use special epilog
|
|
if ?pmd
|
|
ife ?nx
|
|
pop ds ;; if not ATOMIC, restore DS
|
|
endif
|
|
|
|
ife ?nx ;; if not ATOMIC and no locals
|
|
if ?chkstk1+???+?po
|
|
leave
|
|
endif
|
|
else ;;ATOMIC:
|
|
if ?ff+???+?po+?rp ;;if any parameters
|
|
leave ;; fix up BP & exit
|
|
endif
|
|
endif
|
|
|
|
else ;; !?pmd
|
|
ife ?nx ;;if not ATOMIC, bp was pushed
|
|
if (?TF eq 0) or (???+?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 @
|
|
pop bp ;; @
|
|
ife ?DOS5 ;;if not DOS5, bp was
|
|
dec bp ;; incremented to mark far frame @
|
|
endif
|
|
else ;;ATOMIC frame was set up
|
|
if (?TF eq 0) or (???+?rp) ;;if cannot assume valid sp
|
|
mov sp,bp ;; or locals or parmR's, get valid SP @
|
|
endif
|
|
if ???+?po+?rp
|
|
pop bp ;@
|
|
endif
|
|
endif
|
|
endif ;; !?pmd
|
|
else ;;non-windows standard epilog
|
|
if ?pmd
|
|
if ?ff+???+?po+?rp ;;if any parameters
|
|
leave
|
|
endif
|
|
else
|
|
if ?ff+???+?po+?rp ;;if any parameters
|
|
if (?TF eq 0) or (???+?rp) ;;if cannot assume valid SP
|
|
mov sp,bp ;; or locals or parmR's, get valid SP;@
|
|
endif
|
|
pop bp ;@
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ife cc ;;if C calling convention
|
|
ret ;; return ;@
|
|
else ;;else
|
|
ret o ;; return and remove paramteres ;@
|
|
endif
|
|
endif
|
|
endif
|
|
n endp ;;end of process
|
|
.cref
|
|
purge cEnd ;;remove the macro
|
|
&endm
|
|
.cref
|
|
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
|
|
|
|
|
|
|
|
; createSeg is a macro that sets up a segment definition and
|
|
; a logical name for that segment. The logical name can be
|
|
; used to enter the segment, but it cannot be used for anything
|
|
; 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 this segment
|
|
|
|
|
|
createSeg macro n,ln,a,co,cl,grp
|
|
ifnb <grp>
|
|
addseg grp,n
|
|
else
|
|
ln&OFFSET equ offset n:
|
|
ln&BASE equ n
|
|
?cs3 <ln>,<n>
|
|
endif
|
|
ifnb <cl>
|
|
n segment a co '&cl'
|
|
else
|
|
n segment a co
|
|
endif
|
|
n ends
|
|
?cs1 <ln>,<n>
|
|
endm
|
|
|
|
|
|
addseg macro grp,seg
|
|
.xcref
|
|
.xcref grp&_def
|
|
.cref
|
|
ifndef grp&_def
|
|
grp&_def=0
|
|
endif
|
|
if grp&_def ne ASMpass
|
|
.xcref
|
|
.xcref grp&_add
|
|
.cref
|
|
grp&_add ¯o s
|
|
grp&_in <seg>,s
|
|
&endm
|
|
.xcref
|
|
.xcref grp&_in
|
|
.cref
|
|
grp&_in ¯o sl,s
|
|
ifb <s>
|
|
grp group sl
|
|
else
|
|
grp&_add ¯o ns
|
|
grp&_in <sl,s>,ns
|
|
&endm
|
|
endif
|
|
&endm
|
|
grp&_def=ASMpass
|
|
else
|
|
grp&_add seg
|
|
endif
|
|
endm
|
|
|
|
|
|
defgrp macro grp,ln
|
|
addseg grp
|
|
ifnb <ln>
|
|
irp x,<ln>
|
|
?cs3 <&x>,<grp>
|
|
x&&OFFSET equ offset grp:
|
|
x&&BASE equ grp
|
|
endm
|
|
endif
|
|
endm
|
|
|
|
|
|
?cs1 macro ln,n
|
|
.xcref
|
|
.xcref ln&_sbegin
|
|
.cref
|
|
ln&_sbegin ¯o
|
|
.xcref
|
|
.xcref ?mf
|
|
.cref
|
|
?mf &¯o c,l,p ;;when sBegin is invoked, generate
|
|
if c ;; the makeframe macro
|
|
extrn n&_FARFRAME:near ;; make frame for far procedures ;@
|
|
call n&_FARFRAME ;@
|
|
else
|
|
extrn n&_NEARFRAME:near ;; make frame for near procedures ;@
|
|
call n&_NEARFRAME ;@
|
|
endif
|
|
db l shr 1 ;;define number of locals ;@
|
|
db p shr 1 ;;define number of parameters ;@
|
|
&&endm
|
|
?cs2 <ln>,<n>
|
|
n segment
|
|
&endm
|
|
endm
|
|
|
|
|
|
?cs2 macro ln,n
|
|
.xcref
|
|
.xcref sEnd
|
|
.cref
|
|
sEnd ¯o
|
|
n ends
|
|
purge ?mf ;;delete the makeframe macro
|
|
purge sEnd
|
|
&endm
|
|
endm
|
|
|
|
|
|
?cs3 macro ln,n
|
|
.xcref
|
|
.xcref ln&_assumes
|
|
.cref
|
|
ln&_assumes ¯o s
|
|
assume s:&n
|
|
&endm
|
|
endm
|
|
|
|
|
|
|
|
; sBegin is the macro that opens up the definition of a segment.
|
|
; The segment must have already been defined with the createSeg
|
|
; macro.
|
|
;
|
|
; usage:
|
|
; sBegin ln
|
|
;
|
|
; where:
|
|
; ln is the logical name given to the segment when
|
|
; it was declared.
|
|
|
|
.xcref
|
|
.xcref sBegin
|
|
.cref
|
|
sBegin macro ln
|
|
ln&_sbegin
|
|
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 name,logname,align,combine,class,group
|
|
|
|
createSeg _TEXT,Code,word,public,CODE
|
|
ife ?nodata1
|
|
createSeg _DATA,Data,word,public,DATA,DGROUP
|
|
defgrp DGROUP,Data
|
|
endif
|
|
|
|
if ?chkstk1
|
|
ifndef ?CHKSTKPROC
|
|
externp <chkstk>
|
|
endif
|
|
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
|
|
if2
|
|
if x ;;if expression is non-zero,
|
|
errnz1 <x>,%(x)
|
|
endif
|
|
endif
|
|
endm
|
|
|
|
errnz1 macro x1,x2
|
|
= *errnz* x1 = x2
|
|
.err
|
|
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
|
|
|
|
|
|
|
|
;; If profile has been specified, declare the profile routines
|
|
;; to be external and far. It would be best if this could be done
|
|
;; when the call is actually made, but then the fix-up would be
|
|
;; generated as segment-relative.
|
|
|
|
|
|
ifdef ?PROFILE
|
|
externFP <StartNMeas,StopNMeas>
|
|
endif
|