|
|
; ; Microsoft Confidential ; Copyright (C) Microsoft Corporation 1991 ; All Rights Reserved. ;
;** Macro definitions for MSDOS. ; ; Revision history: ; M019 DB 10/26/90 - Added Cmp32 macro.
TRUE EQU 0FFFFh FALSE EQU 0
SUBTTL BREAK a listing into pages and give new subtitles PAGE BREAK MACRO subtitle SUBTTL subtitle PAGE ENDM .xcref break
BREAK <ASMVAR - handle assembly variables once and for all>
AsmVars Macro varlist IRP var,<varlist> AsmVar var ENDM ENDM
AsmVar Macro var IFNDEF var var = FALSE ENDIF ENDM
BREAK <I_NEED: declare a variable external, if necessary, and allocate a size>
; ; declare a variable external and allocate a size ; AsmVar InstalledData I_NEED MACRO sym,len IF NOT InstalledData DOSDATA SEGMENT WORD PUBLIC 'DATA' IFIDN <len>,<WORD> EXTRN &sym:WORD ELSE IFIDN <len>,<DWORD> EXTRN &sym:DWORD ELSE EXTRN &sym:BYTE ENDIF ENDIF DOSDATA ENDS ENDIF ENDM .xcref I_need
; call a procedure that may be external. The call will be short.
invoke MACRO name .xcref IF2 IFNDEF name EXTRN name:NEAR ENDIF ENDIF .cref CALL name ENDM .xcref invoke
PAGE ; ; jump to a label that may be external. The jump will be near. ; transfer MACRO name .xcref IF2 IFNDEF name EXTRN name:NEAR ENDIF ENDIF .cref JUMP name ENDM .xcref transfer
; ; get a short address in a word ; short_addr MACRO name IFDIF <name>,<?> .xcref IF2 IFNDEF name EXTRN name:NEAR ENDIF ENDIF .cref DW OFFSET DOSCODE:name ELSE DW ? ENDIF ENDM .xcref short_addr
; ; get a long address in a dword ; long_addr MACRO name .xcref IF2 IFNDEF name EXTRN name:NEAR ENDIF ENDIF .cref DD name ENDM .xcref long_addr
; ; declare a PROC near or far but PUBLIC nonetheless ; .xcref ?frame .xcref ?aframe .xcref ?stackdepth .xcref ?initstack ?frame = 0 ; initial ?aframe = 0 ; initial ?stackdepth = 0 ; initial stack size ?initstack = 0 ; initial stack size
procedure MACRO name,distance ?frame = 0 ?aframe = 2 ;; remember the pushed BP PUBLIC name name PROC distance ASSUME DS:nothing,ES:nothing ?initstack = ?stackdepth ;; beginning of procedure ENDM .xcref procedure
; end a procedure and check that stack depth is preserved
EndProc MACRO name, chk IFDIF <chk>,<NoCheck> ;; check the stack size IF2 IF ?initstack NE ?stackdepth ;; is it different? %OUT ***** Possible stack size error in name ***** ENDIF ENDIF ENDIF name ENDP ENDM .xcref endproc PAGE ; ; define a data item to be public and of an appropriate size/type ;
I_AM MACRO name,size,init ;; declare the object public PUBLIC name ;; declare the type of the object IFIDN <size>,<WORD> name LABEL WORD I_AM_SIZE = 1 I_AM_LEN = 2 ELSE IFIDN <size>,<DWORD> name LABEL DWORD I_AM_SIZE = 2 I_AM_LEN = 2 ELSE IFIDN <size>,<BYTE> name LABEL BYTE I_AM_SIZE = 1 I_AM_LEN = 1 ELSE name LABEL BYTE I_AM_SIZE = size I_AM_LEN = 1 ENDIF ENDIF ENDIF ;; if no initialize then allocate blank storage IFB <init> DB I_AM_SIZE*I_AM_LEN DUP (?) ELSE IF NOT InstalledData IRP itm,<init> IF I_AM_LEN EQ 1 DB itm ELSE DW itm ENDIF I_AM_SIZE = I_AM_SIZE - 1 ENDM IF I_AM_SIZE NE 0 %out ***** initialization of name not complete ***** ENDIF ELSE DB I_AM_SIZE*I_AM_LEN DUP (?) ENDIF ENDIF ENDM .xcref I_AM .xcref I_AM_SIZE .xcref I_AM_LEN I_AM_SIZE = 0 I_AM_LEN = 0
PAGE
; ; define an entry in a procedure ; entry macro name PUBLIC name name: endm .xcref entry
BREAK <ERROR - store an error code then jump to a label>
error macro code .xcref MOV AL,code transfer SYS_RET_ERR .cref ENDM .xcref error
BREAK <JUMP - real jump that links up shortwise> ; ; given a label <lbl> either 2 byte jump to another label <lbl>_J ; if it is near enough or 3 byte jump to <lbl> ;
jump macro lbl local a .xcref
ifndef lbl&_J ;; is this the first invocation a: JMP lbl ELSE IF (lbl&_J GE $) OR ($-lbl&_J GT 126) a: JMP lbl ;; is the jump too far away? ELSE a: JMP lbl&_J ;; do the short one... ENDIF ENDIF lbl&_j = a .cref endm .xcref jump
BREAK <RETURN - return from a function>
return macro x local a .xcref a: RET ret_l = a .cref endm .xcref return
BREAK <CONDRET - conditional return>
condret macro cc,ncc local a .xcref .xcref a .cref ifdef ret_l ;; if ret_l is defined if (($ - ret_l) le 126) and ($ gt ret_l) ;; if ret_l is near enough then a: j&cc ret_l ;; a: j<CC> to ret_l ret_&cc = a ;; define ret_<CC> to be a: exitm endif endif ifdef ret_&cc ;; if ret_<CC> defined if (($ - ret_&cc) le 126) and ($ gt ret_&cc) ;; if ret_<CC> is near enough a: j&cc ret_&cc ;; a: j<CC> to ret_<CC> ret_&cc = a ;; define ret_<CC> to be a: exitm endif endif j&ncc a ;; j<NCC> a: return ;; return a: ;; a: ret_&cc = ret_l ;; define ret_<CC> to be ret_l endm .xcref condret
BREAK <RETZ - return if zero, links up shortwise if necessary>
retz macro condret z,nz endm .xcref retz
BREAK <RETNZ - return if not zero, links up shortwise if necessary>
retnz macro condret nz,z endm .xcref retnz
BREAK <RETC - return if carry set, links up shortwise if necessary>
retc macro condret c,nc endm .xcref retc
BREAK <RETNC - return if not carry, links up shortwise if necessary>
retnc macro condret nc,c endm .xcref retnc
BREAK <CONTEXT - set the DOS context to a particular register>
context macro r PUSH SS POP r ASSUME r:DOSDATA endm .xcref context
BREAK <SaveReg - save a set of registers>
SaveReg MACRO reglist ;; push those registers IRP reg,<reglist> ?stackdepth = ?stackdepth + 1 PUSH reg ENDM ENDM .xcref SaveReg
Save MACRO arglist ;; push those arguments IRP arg,<arglist> ?stackdepth = ?stackdepth + 1 PUSH arg ENDM ENDM .xcref Save
BREAK <RestoreReg - unsave some registers>
RestoreReg MACRO reglist ;; pop those registers IRP reg,<reglist> ?stackdepth = ?stackdepth - 1 POP reg ENDM ENDM .xcref RestoreReg
Restore MACRO arglist ;; pop those arguments IRP arg,<arglist> ?stackdepth = ?stackdepth - 1 POP arg ENDM ENDM .xcref Restore
BREAK <Critical section macros>
EnterCrit MACRO section ; Invoke E§ion ENDM
LeaveCrit MACRO section ; Invoke L§ion ENDM
Break <message - display a message>
AsmVars <ShareF,Cargs,Redirector>
if debug fmt MACRO typ,lev,fmts,args local a,b,c PUSHF IFNB <typ> TEST BugTyp,typ JZ c CMP BugLev,lev JB c ENDIF PUSH AX PUSH BP MOV BP,SP If (not sharef) and (not redirector) DOSDATA segment a db fmts,0 DOSDATA ends MOV AX,OFFSET DOSDATA:a else jmp short b a db fmts,0 if sharef b: mov ax,offset share:a else b: mov ax,offset netwrk:a endif endif PUSH AX cargs = 2 IRP item,<args> IFIDN <AX>,<item> push [bp+2] ; MOV AX,[BP+2] ELSE ; MOV AX,item push item ENDIF ; PUSH AX cargs = cargs + 2 ENDM invoke PFMT ADD SP,Cargs POP BP POP AX c: POPF ENDM else fmt macro endm endif
Break <DOSAssume - validate assumes>
AsmVar Debug,$temp
;** DOSAssume - Check that a register addresses DOSSEG ; ; DOSAssume reglist, message
IF debug DOSAssume Macro reglist,message local a,b $temp = 0 IRP r,<reglist> IFIDN <r>,<DS> $temp = $temp OR 2 ELSE IFIDN <r>,<ES> $temp = $temp OR 4 ELSE IFIDN <r>,<SS> $temp = $temp OR 1 ELSE %out ***** Invalid register reg in DOSAssume ***** ENDIF ENDIF ENDIF ENDM Invoke SegCheck jmp short a db $temp db message,0 a:
IRP r,<reglist> ASSUME r:DOSDATA ENDM ENDM
ELSE
DOSAssume Macro reglist,message IRP r,<reglist> ASSUME r:DOSDATA ENDM ENDM ENDIF
BREAK <ASSERT - make assertions about registers>
IF DEBUG Assert MACRO kind, objs, message LOCAL a,b IFIDN <kind>,<Z> CMP objs,0 JZ a fmt <>,<>,<message> a: ELSE IFIDN <kind>,<NZ> CMP objs,0 JNZ a fmt <>,<>,<message> a: ELSE PUSH AX IRP obj,<objs> PUSH obj ENDM IF SHAREF MOV AX,OFFSET b ELSE MOV AX,OFFSET DOSDATA:b ENDIF PUSH AX IFIDN <kind>,<ISBUF> Invoke BUFCheck ENDIF IFIDN <kind>,<ISSFT> Invoke SFTCheck ENDIF IFIDN <kind>,<ISDPB> Invoke DPBCheck ENDIF POP AX IF SHAREF JMP SHORT a b DB Message,0 a: ELSE DOSDATA segment b db Message,0 DOSDATA ends ENDIF ENDIF ENDIF ENDM ELSE Assert Macro ENDM ENDIF
Break <CallInstall - hook to installable pieces>
CallInstall MACRO name,mpx,fn,save,restore IF Installed IFNB <save> SaveReg <save> ENDIF MOV AX,(mpx SHL 8) + fn INT 2Fh IFNB <restore> RestoreReg <restore> ENDIF ELSE Invoke name ENDIF ENDM
Break <Stack frame manipulators>
localvar macro name,length local a ifidn <length>,<BYTE> ?frame = ?frame + 1 a = ?frame name EQU BYTE PTR [BP-a] else ifidn <length>,<WORD> ?frame = ?frame + 2 a = ?frame name EQU WORD PTR [BP-a] else ifidn <length>,<DWORD> ?frame = ?frame + 4 a = ?frame name EQU DWORD PTR [BP-a] name&l EQU WORD PTR [BP-a] name&h EQU WORD PTR [BP-a+2] else ?frame = ?frame + length a = ?frame name EQU BYTE PTR [BP-a] endif endif endif endm
enter macro push bp mov bp,sp sub sp,?frame endm
leave macro mov sp,bp pop bp endm
Argvar macro name,length local a ifidn <length>,<BYTE> a = ?aframe ?aframe = ?aframe + 1 name EQU BYTE PTR [BP+a] else ifidn <length>,<WORD> a = ?aframe ?aframe = ?aframe + 2 name EQU WORD PTR [BP+a] else ifidn <length>,<DWORD> a = ?aframe ?aframe = ?aframe + 4 name EQU DWORD PTR [BP+a] name&l EQU WORD PTR [BP+a] name&h EQU WORD PTR [BP+a+2] else a = ?aframe ?aframe = ?aframe + length name EQU BYTE PTR [BP+a] endif endif endif endm
save_world macro
push es invoke save_user_world
endm
restore_world macro
invoke restore_user_world pop es
endm
; ; This macro gets the DOS data segment value and puts it in the specified ; segment register. This can only be used in the DOSCODE segment. ;
getdseg macro r
mov r, cs:[DosDseg] assume r:dosdata endm
; ; This macro does the necessary extrns to allow use of the getdseg macro. ; allow_getdseg macro
ifdef ROMDOS extrn BioDataSeg:word bdata segment at 70H extrn DosDataSg:word bdata ends else extrn DosDseg:word endif
endm
BREAK <LJcc - Long Conditional Jumps>
LJE macro l LJ JE JNE l endm
LJNE macro l LJ jne JE l endm
LJZ macro l LJE l endm
LJNZ macro l LJNE l endm
LJC macro l LJ jc JNC l endm
LJNC macro l LJ jnc JC l endm
LJA macro l LJ ja JNA l endm
LJNA macro l LJ jna JA l endm
LJB macro l LJ jb JNB l endm
LJNB macro l LJ jnb JB l endm
LJS macro l LJ js JNS l endm
LJNS macro l LJ jns JS l endm
LJAE macro l LJ jae JNAE l endm
LJBE macro l LJ jbe JNBE l endm
LJL macro l LJ jl JNL l endm
LJG macro l LJ jg JNG l endm
LJLE macro l LJ jle JNLE l endm
DLJE macro l DLJ JE JNE l endm
DLJNE macro l DLJ jne JE l endm
DLJZ macro l DLJE l endm
DLJNZ macro l DLJNE l endm
DLJC macro l DLJ jc JNC l endm
DLJNC macro l DLJ jnc JC l endm
DLJA macro l DLJ ja JNA l endm
DLJNA macro l DLJ jna JA l endm
DLJB macro l DLJ jb JNB l endm
DLJNB macro l DLJ jnb JB l endm
DLJS macro l DLJ js JNS l endm
DLJNS macro l DLJ jns JS l endm
DLJAE macro l DLJ jae JNAE l endm
DLJBE macro l DLJ jbe JNBE l endm
DLJG macro l DLJ jg JNG l endm
DLJL macro l DLJ jl JNL l endm
DLJLE macro l DLJ jle JNLE l endm
;* LJ - generate long conditional jump ; ; if target preceeds us and is in range just use a short jump ; else use a long jump ; ; LJ <direct jmp>,<skip jmp>,<label>
LJ MACRO dirop,idirop,l local a IF ((.TYPE l) XOR 20h) AND 0A0h idirop a ;; not defined or is external jmp l a: ELSE ;; is local definied IF (($-l) LT 124) AND ($ GT l) dirop l ;; is before and within range ELSE idirop a ;; is out of range or forward (pass 2) jmp l a: ENDIF ENDIF ENDM
;* DLJ - generate debug long conditional jump ; ; If DEBUG is defined then we generate a long jump, else a short ; one. ; ; DLJ <direct jmp>,<skip jmp>,<label>
DLJ MACRO dirop,idirop,l local a IF DEBUG idirop a jmp l a: ELSE dirop l ENDIF ENDM
.xcref LJE, LJNE, LJZ, LJNZ, LJC, LJNC, LJA, LJNA .xcref LJB, LJNB, LJS, LJNS, LJAE, LJBE, LJG, LJL, LJLE .xcref DLJE, DLJNE, DLJZ, DLJNZ, DLJC, DLJNC, DLJA, DLJNA .xcref DLJB, DLJNB, DLJS, DLJNS, DLJAE, DLJBE, DLJG, DLJL, DLJLE .xcref LJ,DLJ
;** SHORT offset macro ; ; expands to SHORT if non debug, to nul if debug. ; ; this allows us to code ; ; jmp SHRT foobar ; ; and get a long form if debugging is turned on (because the extra ; debug code puts the target out of range)
if DEBUG SHRT EQU <> else SHRT EQU SHORT endif
;** FALLTHRU - Verifies Fallthrough Validity
FALLTHRU MACRO labl ; BUGBUG - restore align when we make code segment word aligned ; align 2 ; don't have errnz fail due to alignment IF2 ; of following label .errnz labl-$ ENDIF ENDM
;** INTTEST - Generate an INT 3 for testing
INTTEST MACRO if DEBUG int 3 endif ENDM
;** DPUBLIC - Make a public symbol for debugging
DPUBLIC MACRO arg if DEBUG public arg endif ENDM
;* Debug Traps ; ; These are removed as the code is exercised
TRAP macro ; Like INTTEST but is normally left in during int 3 ; debugging; indicates "should not occur" ENDM
TRAPC macro local l jnc short l int 3 l: ENDM
TRAPNC macro local l jc short l int 3 l: ENDM
TRAPA macro local l jna short l int 3 l: ENDM
TRAPNA macro local l ja short l int 3 l: ENDM
TRAPZ macro local l jnz short l int 3 l: ENDM
TRAPNZ macro local l jz short l int 3 l: ENDM
BREAK <Structure Field Macros>
;** Follows - Verify that a field follows another ; ; FOLLOWS field1, field2 [, size] ; ; This macro generates an error if "field1" doesn't immeidately ; follow "field2". If "size" is specified then an error is generated ; if Field1 is not of the proper size.
FOLLOWS macro field1, field2, fldsiz .errnz field1 - size field2 - field2 IFNB <fldsiz> .errnz size field1 - fldsiz ENDIF ENDM
;** LAST - Verify that a field is the last field in a structure ; ; LAST fieldname, structname ; ; Generates an error if fieldname is not last in structname.
LAST macro fldnam, strunam .errnz size strunam - fldnam - size fldnam endm
;** TESTB - Use Byte form for Word TESTS, when possible ; ; TESTB is used in place of 16-bit TEST instructions. It substitutes ; a smaller 8-bit test when possible.
TESTB macro targ,mask,thirdarg local mask2,delta
ifnb <thirdarg> .err mask must be enclosed in brackets endif
ifidn <targ>,<[bx]> test targ,mask exitm endif ifidn <targ>,<[si]> test targ,mask exitm endif ifidn <targ>,<[di]> ; don't process these operands specially test targ,mask exitm endif ifidn <targ>,<[BX]> test targ,mask exitm endif ifidn <targ>,<[SI]> test targ,mask exitm endif ifidn <targ>,<[DI]> test targ,mask exitm endif ifidn <targ>,<SI> test targ,mask exitm endif ifidn <targ>,<DI> test targ,mask exitm endif ifidn <targ>,<BP> test targ,mask exitm endif delta = 0 mask2 = mask
if mask2 AND 0ff00h ; have a mask bit in the high half if mask2 AND 0ffh test targ,mask exitm endif mask2 = mask2 SHR 8 delta = 1 endif
ifidn <targ>,<AX> if delta test targ,mask else test AL,mask2 endif exitm endif ifidn <targ>,<BX> if delta test BH,mask2 else test BL,mask2 endif exitm endif ifidn <targ>,<CX> if delta test CH,mask2 else test CL,mask2 endif exitm endif ifidn <targ>,<DX> if delta test DH,mask2 else test DL,mask2 endif exitm endif ifidn <targ>,<ax> if delta test targ,mask else test AL,mask2 endif exitm endif ifidn <targ>,<bx> if delta test BH,mask2 else test BL,mask2 endif exitm endif ifidn <targ>,<cx> if delta test CH,mask2 else test CL,mask2 endif exitm endif ifidn <targ>,<dx> if delta test DH,mask2 else test DL,mask2 endif exitm endif
test byte ptr targ+delta,mask2 endm
; ; Some old versions of the 80286 have a bug in the chip. The popf ; instruction will enable interrupts. Therefore in a section of code with ; interrupts disabled and you need a popf instruction use the 'popff' ; macro instead. ;
POPFF macro jmp $+3 iret push cs call $-2 endm
Break <Cmp32 - 32-bit compare> ;---------------------------------------------------------------------------- ; ; Macro Name : Cmp32 ; ; Inputs: ; msw1 -- 1st operand, most significant word; MUST BE REGISTER. ; lsw1 -- 1st operand, least significant word; MUST BE REGISTER. ; msw2 -- 2nd operand, most significant word. ; lsw2 -- 2nd operand, least significant word. ; Function: ; Compare 2 32-bit operands. Implemented as a macro. ; Outputs: ; CF = 1 if 1st operand < 2nd operand ; = 0 if 1st operand >= 2nd operand ; ZF = 1 if 1st operand == 2nd operand ; = 0 if 1st operand <> 2nd operand ;----------------------------------------------------------------------------- ;M019: Created.
Cmp32 MACRO msw1,lsw1,msw2,lsw2 LOCAL cmp32x
cmp msw1,msw2 jne cmp32x cmp lsw1,lsw2 cmp32x: ENDM
Break <HRDSVC - SVC call where hard error is possible> ;---------------------------------------------------------------------------- ; ; Macro Name : HRDSVC ; ; Inputs: ; iSVC -- SVC index ; Function: ; Make a DEM SVC. If hard error happens handle it. ; Outputs: ; CF = 1 if operation failed ; = 0 if operation successful ;-----------------------------------------------------------------------------
HRDSVC MACRO iSVC LOCAL hs_nerr,hs_fail,hs_retry
SVC iSVC jnc hs_nerr
; Check if hard err to be handled . If no harderr then it ; will come back without effecting any reg or carry flags. ; Else it will do an int24. If user chooses retry it ; will retry the SVC call. On fail or abort it will ; come back with usual dos style setup. ; If user chose "Abort" it wont come back
invoke TestHrdErr hs_nerr: ENDM
|