; ; 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