home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1993 #2
/
Image.iso
/
os2
/
gtak212b.zip
/
SOURCE.ZIP
/
OS2-ST01
/
dosmac.inc
< prev
next >
Wrap
Text File
|
1989-06-06
|
32KB
|
1,679 lines
;
; copyright (C) Microsoft, 1988
;
;
FALSE equ 0
TRUE equ not FALSE
AsmVars macro varlist, value
irp var,<varlist>
AsmVar var, value
endm
endm
AsmVar macro var, value
ifndef var
ifb <value>
var = FALSE
else
var = <value>
endif
endif
endm
; Check if all (or most) the checking flags are to be enabled.
ifdef ALLSTRICT
AsmVar MISCSTRICT, TRUE
AsmVar VMSTRICT, TRUE
AsmVar DEVSTRICT, TRUE
AsmVar VOLPTRCHK, TRUE
AsmVar INTERRPRT, TRUE
AsmVar DHSTRICT, TRUE
AsmVar SELSTRICT, TRUE
AsmVar FSSTRICT, TRUE
endif
;* Values for ?farcode, set by FARCODE macro to indicate which
; code segment is being assembled.
FC_DosCode EQU 0 ; DosCode
FC_InitCode EQU 1 ; DosInitCode
FC_HighCode EQU 2 ; DosHighCode
FC_High2Code EQU 3 ; DosHigh2Code
FC_Ring3Code EQU 4 ; DosRing3Code
FC_InitRMCode EQU 5 ; DosInitRMCode
SUBTTL BREAK a listing into pages and give new subtitles
PAGE
BREAK MACRO subtitle
SUBTTL subtitle
PAGE
ENDM
.xcref BREAK
;
; 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 code address in a word
;
CSOFFSET EQU OFFSET DosCode:
short_addr MACRO name
IFDIF <name>,<?>
.xcref
IF2
IFNDEF name
EXTRN name:NEAR
ENDIF
ENDIF
.cref
DW CSOFFSET 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 Procedure:
; Parameters:
; name: procedure_name
; distance: near,far,hybrid,faronly
; scope: local,farlocal,nearlocal
; treg: scratch register
; If non-blank, this register is used to optimizize far calls
; to hybrid procedures, at the expense of near calls.
; By default, near calls are the more efficient of the two.
; abase: the number of bytes to skip for calculating the ArgVar
; offsets. The default (if this is blank) depends on the
; "distance" variable. "NEAR" is 4 and "FAR" is 6.
.xcref ?frame
.xcref ?aframe
.xcref ?abase
.xcref ?stackdepth
.xcref ?initstack
?frame = 0 ; initial
?aframe = 0 ; initial
?abase = 0 ; initial
?stackdepth = 0 ; initial stack size
?initstack = 0 ; initial stack size
?local = 0 ; local proc flag
?near = 0 ; near proc flag
?faronly = 0 ; faronly proc flag
?oldfaronly = 0 ; outer scope faronly proc flag
?depth = 0 ; procedure nesting level
Procedure macro name,distance,scope,treg,abase
if2
if ?depth gt 1
%out Nesting too deep in Procedure name
.err
endif
endif
?depth = ?depth + 1
?oldfaronly = ?faronly ;; save previous value
?frame = 0
?aframe = 0
?faronly = 0 ;; default to near/hybrid
?near = 1 ;; default to near
?initstack = ?stackdepth ;; beginning of procedure
?local = 0 ;; default to public
ifnb <scope>
?local = 1 ;; local if non-blank scope
if2
?x = 0
irp x,<LOCAL,local,farlocal,nearlocal>
ifidn <x>,<scope>
?x = 1
endif
endm
ife ?x
%out Bad scope_arg scope in Procedure name
;;.err
endif
endif
endif
ifnb <distance> ;; if nonblank, is hybrid, near, faronly or far
if2
?x = 0
irp x,<HYBRID,hybrid,NEAR,near,FAR,far,FARONLY,faronly>
ifidn <x>,<distance>
?x = 1
endif
endm
ife ?x
%out Bad distance_arg distance in Procedure name
;;.err
endif
endif
irp x,<HYBRID,hybrid>
?local = 0
ifidn <scope>,<local>
?local = 1 ;; local if scope is 'local'
endif
ifidn <scope>,<LOCAL>
?local = 1 ;; local if scope is 'LOCAL'
endif
ifidn <distance>,<x>
ifb <treg>
ifidn <scope>,<nearlocal>
GENHYBRID name ;; generate far public thunk
?local = 1 ;; but near label is local
else
GENHYBRID name,<scope> ;; generate far thunk
endif
else
ifidn <scope>,<farlocal>
Entry name ;; generate near public label
?local = 1 ;; but far label is local
else
Entry name,,<scope>
endif
pop treg
push cs
push treg
?faronly = 1 ;; pretend to be faronly
?near = 0 ;; not near
endif
endif
endm
irp x,<FAR,far>
ifidn <distance>,<x>
?near = 0 ;; is far
endif
endm
irp x,<FARONLY,faronly>
ifidn <distance>,<x>
?faronly = 1 ;; is faronly
?near = 0 ;; not near
endif
endm
endif
if ?faronly
if ?local
GENPUBLIC hy_&name,<local>,code
else
GENPUBLIC hy_&name,,code
endif
else
if ?local
GENPUBLIC name,<local>,code
else
GENPUBLIC name,,code
endif
endif
if ?near
?abase = 2+2 ;; remember the pushed BP
name proc near ;; is near or hybrid
else
?abase = 4+2 ;; remember the pushed BP
if ?faronly
hy_&name proc far ;; is faronly
else
name proc distance ;; else must be far
endif
endif
ifnb <abase> ;; if abase is not blank, then use it
?abase = abase
endif
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
if ?faronly
hy_&name endp ;; is faronly
else
name endp ;; else near, far or hybrid
endif
ife ?depth
if2
%out EndProc without matching Procedure name
.err
endif
else
?depth = ?depth - 1
?faronly = ?oldfaronly ;; restore previous value
endif
ifdef KILLASSUMES
assume ds:nothing, es:nothing, ss:nothing
endif
endm
.xcref EndProc
;** GENPUBLIC - generate a public symbol in all cases, but first
; pervert the name if it really is a local symbol. For symbolic
; debugger support only.
?lcnt = 0
GENPUBLIC macro name,local,code ;; generate a public or local symbol
ifb <local> ;; if local symbol not requested
public name
else
ifdef NOLOCAL ;; if all symbols should be public
public name
else
?GENLOCAL %?lcnt,name,<code> ;; else croft a local (pseudo public)
?lcnt = ?lcnt + 1
endif
endif
endm
?GENLOCAL macro cnt,name,code ;; generate a public local symbol
public l&cnt&_&name
ifb <code>
l&cnt&_&name label byte ;; allow masm PUBDEF DS association
else
l&cnt&_&name: ;; avoid masm PUBDEF DS association
endif
endm
;** EXTRNFAR - generate an EXTRN for the hybrid symbol if it
; has not yet been defined, and the skip argument is blank.
EXTRNFAR macro name,skip
ifb <skip>
ifndef hy_&name
extrn hy_&name:far
endif
endif
endm
; generate a far pointer to a hybrid procedure
DDFAR macro name
dd hy_&name
endm
; move the offset of a hybrid procedure into a register or memory
MOVFAROFFSET macro target,name,group
ifb <group>
mov target,offset cs:hy_&name
else
mov target,offset group:hy_&name
endif
endm
; generate an intersegment call or jmp to a hybrid procedure
CALLFAR macro name
call far ptr hy_&name
endm
JMPFAR macro name
jmp far ptr hy_&name
endm
; generate a hybrid procedure linkage procedure
GENHYBRID macro name,lcl
local farproc
farproc proc far
ret
GENPUBLIC hy_&name,<lcl> ;; generate a public or local symbol
hy_&name label far
push offset cs:farproc ;; near-code must immediately follow
farproc endp
endm
; generate a near/far/hybrid entry into a procedure
Entry macro name,distance,scope
local a
a = 0 ;; assume near label
ifnb <scope>
if2
?x = 0
irp x,<local,LOCAL>
ifidn <x>,<scope>
?x = 1
endif
endm
ife ?x
%out Bad scope_arg scope in Entry name
;;.err
endif
endif
endif
ifnb <distance>
if2
?x = 0
irp x,<faronly,hybrid,HYBRID>
ifidn <x>,<distance>
?x = 1
endif
endm
ife ?x
%out Bad distance_arg distance in Entry name
;;.err
endif
endif
ifidn <distance>,<faronly> ;; was it far only?
a = 1 ;; then generate far label
else ;; else must be hybrid
jmp short name ;; previous callers skip hybrid linkage
GENHYBRID name,<scope>
endif
endif
if a ;; generate far label?
GENPUBLIC hy_&name,<scope> ;; generate a public or local symbol
hy_&name:
else ;; else generate near label
GENPUBLIC name,<scope> ;; generate a public or local symbol
name:
endif
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_AL
.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>
Dos3Mac MACRO name
IF2
%out Warning - DOS 3 macro name called
ENDIF
ENDM
SegContext macro srl,gr,noassume,base,grpnam
IFB <gr> ;; push and pop if no general register
IRP sr,<srl> ;; for each register
PUSH base ;; push base
POP sr ;; pop into the register
IFB <noassume> ;; if ASSUME not inhibited
ASSUME sr:grpnam
ENDIF
ENDM
ELSE ;; else if we have a helper register
MOV gr,base ;; load helper with base
IRP sr,<srl> ;; for each register in list
MOV sr,gr ;; load the register from the helper
IFB <noassume> ;; if ASSUME not inhibited
ASSUME sr:grpnam
ENDIF
ENDM
ENDIF
endm
.xcref SegContext
DosContext macro srl,gr,noassume
LOCAL i
IFNB <gr> ;; if intermediate general register
mov gr,DosGroup
IRP sr,<srl>
mov sr,gr
ENDM
ELSE
IRP sr,<srl> ;; for sr = *first* segreg in list
i = 0
IRP r,<srl> ;; for r = each segreg in list
i = i + 1
IF i EQ 1 ;; if r is first in list
push DosGroup ;; do push immediate
ELSE ;; else r is second (or later) in list
push sr ;; and sr contains desired value
ENDIF
pop r
ENDM
EXITM ;; quit after sr = first in list
ENDM
ENDIF
IFB <noassume>
IRP sr,<srl>
ASSUME sr:DosGroup
ENDM
ENDIF
endm
.xcref DosContext
DosHighContext macro srl,gr,noassume
SegContext <srl>,gr,noassume,DosHighData,DosHighData
endm
.xcref DosHighContext
TaskContext macro srl,gr,noassume
SegContext <srl>,gr,noassume,SS,TaskArea
endm
.xcref TaskContext
DosInitContext macro srl,gr,noassume
SegContext <srl>,gr,noassume,DosInitData,DosInitData
endm
.xcref DosInitContext
BREAK <SaveReg/RestoreReg - save or restore a set of registers>
SaveReg MACRO reglist ;; push those registers
IRP reg,<reglist>
?stackdepth = ?stackdepth + 1
PUSH reg
ENDM
ENDM
.xcref SaveReg
RestoreReg MACRO reglist ;; pop those registers
IRP reg,<reglist>
?stackdepth = ?stackdepth - 1
POP reg
ENDM
ENDM
.xcref RestoreReg
BREAK <Critical section macros>
ifdef INTERRPRT
IntErrF = TRUE
endif
AsmVars <Debug,IntErrF,ShareF,Redirector>
BREAK <DOSAssume - validate assumes>
DOSAssume Macro reg,reglist,message
local a,b
IFIDN <reg>,<CS>
$temp = 1
ELSE
IFIDN <reg>,<SS>
$temp = 0
ELSE
IFIDN <reg>,<DOS>
IF Redirector
$temp = 8
ELSE
$temp = 1
ENDIF
ELSE
%out ***** Invalid DOS register reg in DOSAssume *****
ENDIF
ENDIF
ENDIF
IF Debug
IRP r,<reglist>
IFIDN <r>,<DS>
$temp = $temp OR 2
ELSE
IFIDN <r>,<ES>
$temp = $temp OR 4
ELSE
%out ***** Invalid register r in DOSAssume *****
ENDIF
ENDIF
ENDM
PUSH AX
MOV AX,$temp
PUSH AX
IF ShareF OR Redirector
MOV AX,OFFSET b
ELSE
MOV AX,OFFSET DosGroup:b
ENDIF
PUSH AX
Invoke SegCheck
POP AX
IF ShareF OR Redirector
JMP SHORT a
b DB message,0
a:
ELSE
Table segment
b DB message,0
Table ends
ENDIF
ENDIF
IRP r,<reglist>
IF $temp AND 1
IF Redirector
ASSUME r:NETWRK
ELSE
ASSUME r:DosGroup
ENDIF
ELSE
IF $temp AND 8
ASSUME r:DosGroup
ELSE
ASSUME r:TaskArea
ENDIF
ENDIF
ENDM
ENDM
BREAK <ASSERT - make assertions about registers>
;* Assert - sanity checks (contolled by DEBUG switch)
;
; kind: one of Z, NZ, ISBUF, ISCURBUF, ISSFT, ISVPB (case is
; significant)
; objs: register pair which should point to given structure
; for IS???; an expression for Z or NZ
; message: message printed if assertion fails; should identify
; the calling routine
IF Debug
Assert MACRO kind, objs, message
LOCAL a,b
IFIDN <kind>,<Z>
CMP objs,0
JZ a
DBPRT <>,<>,<message>
a:
ELSE
IFIDN <kind>,<NZ>
CMP objs,0
JNZ a
DBPRT <>,<>,<message>
a:
ELSE
IFIDN <kind>,<ISDPB>
%out ISDPB referenced... Needs to be examined (someday) and changed to ISVPB
EXITM
ENDIF
PUSH AX
IRP obj,<objs>
PUSH obj
ENDM
IF ShareF
MOV AX,OFFSET b
ELSE
MOV AX,OFFSET DosGroup:b
ENDIF
PUSH AX
IFIDN <kind>,<ISBUF>
Invoke BUFCheck
ENDIF
IFIDN <kind>,<ISCURBUF>
Invoke CURBUFCheck
ENDIF
IFIDN <kind>,<ISSFT>
Invoke SFTCheck
ENDIF
IFIDN <kind>,<ISVPB>
Invoke xVPBCheck
ENDIF
POP AX
IF ShareF
JMP SHORT a
b DB message,0
a:
ELSE
Table segment
b DB message,0
Table ends
ENDIF
ENDIF
ENDIF
ENDM
ELSE
Assert Macro
ENDM
ENDIF
BREAK <CallInstall - hook to installable pieces>
CallInstall MACRO name,save,restore
IFNB <save>
SaveReg <save>
ENDIF
push name ;; index of routine to call
call Redir_Dispatcher ;; go dispatch to redirector
ifnb <restore>
RestoreReg <restore>
endif
ENDM
; The Redir_Dispatcher routine is common low code and will
; merely perform an indirect call through the redirector-supplied
; address
BREAK <CallPCache - hook to installable PathCacher, distinct from redir.>
CallPCache MACRO name
push name
call PathCache_Dispatcher
endm
; The PathCache_Dispatcher is a jump-stub, which must be called far
; because it may had off to the PathCacher, which resides in a separate
; segment when installed. When the PathCacher is not installed, all
; that happens is a clear-carry and a far ret.
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
;* DLJMP - generate debug long jump
;
; If Debug is defined then generate long jump, else a short one.
;
; DLJMP <label>
DLJMP macro lbl
IF Debug
jump lbl
ELSE
jmp short lbl
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
BREAK <NEW MACROS FOR 4.0>
ReferTask MACRO sym,len
TaskArea segment
IFIDN <len>,<WORD>
EXTRN &sym:WORD
ELSE
IFIDN <len>,<DWORD>
EXTRN &sym:DWORD
ELSE
EXTRN &sym:BYTE
ENDIF
ENDIF
TaskArea ENDS
ENDM
ReferInitGlobal MACRO sym,len
InitData segment
IFIDN <len>,<WORD>
EXTRN &sym:WORD
ELSE
IFIDN <len>,<DWORD>
EXTRN &sym:DWORD
ELSE
EXTRN &sym:BYTE
ENDIF
ENDIF
InitData ENDS
ENDM
ReferGlobal MACRO sym,len
CONSTANTS SEGMENT
IFIDN <len>,<WORD>
EXTRN &sym:WORD
ELSE
IFIDN <len>,<DWORD>
EXTRN &sym:DWORD
ELSE
IFIDN <len>,<QWORD>
EXTRN &sym:QWORD
ELSE
EXTRN &sym:BYTE
ENDIF
ENDIF
ENDIF
CONSTANTS ENDS
ENDM
ReferHighGlobal MACRO sym,symtype
HIGHDATA SEGMENT
EXTRN &sym:&symtype
HIGHDATA ENDS
ENDM
?farcode = FC_DosCode ; default to near code
;** NEARCODE/FARCODE macros - used to control model of calls
; used in macros (InternalError, DBPRT, PMONLY, etc.).
; These macros should be invoked just *prior* to every code
; segment declaration, and OUTSIDE of all segments.
NEARCODE macro
ifdef FSSTRICT
EXTRNFAR AssertNoBufProc
endif
?farcode = FC_DosCode ; near code segment
assume cs:DosCode
endm
FARCODE macro segname,NoIntErr,NoProtReal,NoDPRINTF
EXTRNFAR IntErr,<NoIntErr>
EXTRNFAR IntErrNull,<NoIntErr>
ifdef MODECHECK
EXTRNFAR TaskOnly,<NoProtReal>
EXTRNFAR ProtTaskOnly,<NoProtReal>
EXTRNFAR ProtOnly,<NoProtReal>
EXTRNFAR RealTaskOnly,<NoProtReal>
EXTRNFAR RealOnly,<NoProtReal>
EXTRNFAR RealProcTaskOnly,<NoProtReal>
endif
if Debug
EXTRNFAR DPRINTF,<NoDPRINTF>
EXTRNFAR dbtest,<NoDPRINTF>
endif
ifidn <segname>,<INIT>
assume cs:DosInitCode
?farcode = FC_InitCode ; Indicate which far code segment
else
ifidn <segname>,<HIGH>
assume cs:DosHighCode
?farcode = FC_HighCode ; Indicate which far code segment
else
ifidn <segname>,<HIGH2>
assume cs:DosHigh2Code
?farcode = FC_High2Code ; Indicate which far code segment
else
ifidn <segname>,<RING3>
assume cs:DosRing3Code
?farcode = FC_Ring3Code ; Indicate which far code segment
else
ifidn <segname>,<INITRM>
assume cs:DosInitRMCode
?farcode = FC_InitRMCode ; Indicate which far code segment
else
if2
%out FARCODE: bad segment name: segname
.ERR
endif
endif
endif
endif
endif
endif
ifdef FSSTRICT
if ?farcode ne FC_High2Code
EXTRNFAR AssertNoBufProc
endif
endif
endm
;** GENCALL - generate a call of the appropriate model
GENCALL macro target
if ?farcode
CALLFAR target
else
invoke target
endif
endm
;** Internal error detected in system
; Also used by the bios!!
ifndef DataOffset ;; if not defined by bios
DataOffset equ offset DosGroup: ;; use DosGroup
endif
InternalError macro arg,lab
local a
if IntErrF or Debug
push DataOffset a
GENCALL IntErr
else
GENCALL IntErrNull ;; no string passed
endif
ifdef ?iecnt ;; if ModName macro was invoked
IntErrLabel %?iecnt,lab ;; generate a public label
?iecnt = ?iecnt + 1 ;; and bump sequential label number
endif
if IntErrF or Debug
IntErrMsg segment
a db '&arg',0dh,0ah,'$'
IntErrMsg ends
endif
ENDM
ModName macro mn
?iecnt = 0
IntErrLabel macro n,lab
ifnb <lab>
public ie_&mn&&n&&_&&lab
ie_&mn&&n&&_&&lab:
else
public ie_&mn&&n
ie_&mn&&n:
endif
endm
endm
BREAK <Stack frame manipulators>
LocalVar macro name,length,lowname,highname,pad
local a
?pad = 0
?nopad = 0
ifidn <pad>,<PAD>
?pad = 1
endif
ifidn <pad>,<NOPAD>
?nopad = 1
endif
ifidn <length>,<BP> ;; makes a variable point to the old BP
name EQU (WORD PTR [BP])
else
ifidn <length>,<BYTE>
?frame = ?frame + 1
a = ?frame
name EQU (BYTE PTR [BP-a])
else
ifidn <length>,<DBYTE>
if ?frame and 1
?frame = ?frame + 1
endif
?frame = ?frame + 2
a = ?frame
name EQU (WORD PTR [BP-a])
ifb <lowname>
name&l EQU (BYTE PTR [BP-a])
else
lowname EQU (BYTE PTR [BP-a])
endif
ifb <highname>
name&h EQU (BYTE PTR [BP-a+1])
else
highname EQU (BYTE PTR [BP-a+1])
endif
else
ifidn <length>,<WORD>
ife ?nopad
if ?frame and 1
?frame = ?frame + 1
endif
endif
?frame = ?frame + 2
a = ?frame
name EQU (WORD PTR [BP-a])
else
ifidn <length>,<DWORD>
if ?frame and 1
?frame = ?frame + 1
endif
?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
if ?pad
if ?frame and 1
?frame = ?frame + 1
endif
endif
a = ?frame
name EQU (BYTE PTR [BP-a])
endif ;; DWORD
endif ;; WORD
endif ;; DBYTE
endif ;; BYTE
endif ;; BP
endm
EnterProc macro varlist
local a
if ?frame and 1
?frame = ?frame + 1 ;; make final frame even
endif
if ?frame
ifb <varlist> ;; if no LocalVars initialized
enter ?frame,0 ;; all done
else
push bp ;; else set up bp frame
mov bp,sp
a = ?frame
irp var,<varlist> ;; and push each value
if2
ife a
%out EnterProc: too many args
.err
endif
endif
push var
a = a - 2
endm
if a ;; if any left over, adjust sp
if a gt 4
lea sp,[bp-?frame] ;; takes 3 bytes
else
push ax
if a eq 4
push ax ;; if a==4, takes 2 bytes
endif
endif
endif
endif
else
push bp
mov bp,sp
endif
endm
LeaveProc macro
leave
endm
ArgVar macro name,length,lowname,highname
local a
a = ?aframe + ?abase
ifidn <length>,<BYTE>
?aframe = ?aframe + 1
name EQU (BYTE PTR [BP+a])
else
ifidn <length>,<DBYTE>
?aframe = ?aframe + 2
name EQU (WORD PTR [BP+a])
ifb <lowname>
name&l EQU (BYTE PTR [BP+a])
else
lowname EQU (BYTE PTR [BP+a])
endif
ifb <highname>
name&h EQU (BYTE PTR [BP+a+1])
else
highname EQU (BYTE PTR [BP+a+1])
endif
else
ifidn <length>,<WORD>
?aframe = ?aframe + 2
name EQU (WORD PTR [BP+a])
else
ifidn <length>,<DWORD>
?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
?aframe = ?aframe + length
name EQU (BYTE PTR [BP+a])
endif ;; DWORD
endif ;; WORD
endif ;; DBYTE
endif ;; BYTE
endm
BREAK <ERRNZ - Conditional Error>
;** ERRNZ - generate assembly error if arg != 0
;
ERRNZ MACRO expr
.errnz expr
ENDM
;* POPFF - special version of pop flags for i286
;
; This avoids the pitfalls of the 286 POPF instruction
; which will sometimes allow an interrupt to come in
; even if IF is off in both the old and new flags.
POPFF macro
local a
push cs
if ?farcode
push offset cs:a
iret
a:
else
invoke PopFlags
endif
endm
;** POPAF - pop arithmetic flags into flags register. Avoids the
; popf interrupt exposure bug and is more efficient than
; the POPFF macro. To be used ONLY when interrupt flag
; state was not modified and does not need to be restored.
; Requires a scratch register parameter, preferably AX.
;
; If reg is AX:
; pop ax
; xchg ah,al
; sahf
; Else
; pop reg
; xchg ax,reg
; xchg ah,al
; sahf
; xchg reg,ax
POPAF macro reg
local ?useax
?useax = 0
irp x,<ax,AX,Ax,aX>
ifidn <x>,<reg>
?useax = 1
endif
endm
pop reg
ife ?useax
xchg ax,reg
endif
mov ah,al
sahf
ife ?useax
mov ax,reg
endif
endm
;** RMONLY - verify that the processor is in real mode
RMONLY macro task
RMPM Real,<task>
endm
;** PMONLY - verify that the processor is in protected mode
PMONLY macro task
RMPM Prot,<task>
endm
;** DUALMODE - document that the processor may be in either mode
DUALMODE macro task
RMPM <>,<task>
endm
;** RMPROCONLY - verify that the currently running process is the
; real mode process. Presumed to be at task time.
RMPROCONLY macro
RMPM RealProc,TASKTIME
endm
;** RMPM - RMONLY/PMONLY support
RMPM macro rmpm,task
ifdef MODECHECK
ifnb <task>
if2
ifdif <task>,<TASKTIME>
%out RMONLY/PMONLY/DUALMODE/RMPROCONLY: bad macro arg: task
.ERR
endif
endif
ifb <rmpm>
GENCALL TaskOnly
else
GENCALL rmpm&TaskOnly
endif
else
ifnb <rmpm>
GENCALL rmpm&Only
endif
endif
endif
endm
;* callgate - allow ring3 code in doscode call api
;
; Kernel entry looks just like a normal api call
callgate MACRO gate
ifndef GDT_&gate
extrn GDT_&gate:BYTE
endif
db 9ah ; far ret
dw 0,GSEL GDT_&gate
ENDM
;** IODelay - delay long enough for the bus to catch up to the cpu
IODelay macro
jmp short $+2
endm
;** BEGINNOBLOCK/ENDNOBLOCK - begin/end kernel critical section.
; Debugging aid only, ProcBlocks and switching tasks shouldn't take
; place when the variable is non-zero, else an internal error will
; be generated.
ifdef MISCSTRICT
BEGINNOBLOCK macro segreg
ifndef NoProcBlock
ReferGlobal NoProcBlock,BYTE
endif
push ax
pushf
ifb <segreg>
push ds
DosContext ds,,noassume
inc ds:[NoProcBlock] ;; increment counter
pop ds
else
inc segreg:[NoProcBlock] ;; increment counter
endif
POPAF ax
pop ax
endm
ENDNOBLOCK macro segreg
ifndef NoProcBlock
ReferGlobal NoProcBlock,BYTE
endif
push ax
pushf
ifb <segreg>
push ds
DosContext ds,,noassume
dec ds:[NoProcBlock] ;; decrement counter
pop ds
else
dec segreg:[NoProcBlock] ;; decrement counter
endif
POPAF ax
pop ax
endm
else
BEGINNOBLOCK macro
endm
ENDNOBLOCK macro
endm
endif
;;*** macro to clear FS GS regs in a 386 processor
ClearFSGS macro
local a,b
a label near
;; at init_time, if the CPU is a 80286, the byte immediately below will
;; be replaced with the instruction following this macro (which must be
;; an IRET or RETF, else Internal Error will be invoked).
jmp short b
dw 0 ;; to be part of "mov ax,0" instr
;; at init-time, if the CPU is a 80386, "jmp short b" above will
;; be replaced by the following:
;; "push ax" instruction
;; opcode for "mov ax,immed data" ; not using 'xor ax,ax', we need
;; ; to preserve the flags
db 8eh,0e0h ;; mov fs,ax
db 8eh,0e8h ;; mov gs,ax
pop ax
b label near
if ((b EQ a) OR ((b-a) GT 127))
%out **** 386 specific code: invalid code size ****
.err
endif
CodeInfoTbl SEGMENT
dd a ;; save the address to the code
dw b - a ;; save the size of the code
CodeInfoTbl ENDS
endm ;; end of macro ClearFSGS
;** DefTaskData - define resizable task control data structures
;
; This macro is used to declare a task control data
; structure that can be resized after the maximum
; number of threads allowed in the system has been
; determined
;
; INPUT name Name of pointer to structure
; width Width of an element in structure
; length Number of elements in structure
; value Initial value for each element
DefTaskData macro name,width,length,value
local l
CONSTANTS segment
public name
name dw offset DosGroup:l
CONSTANTS ends
LASTDATA segment
dw offset DosGroup:name ;; point to pointer to this array
ifidn <width>,<WORD>
db 2 ;; give the width of the item
l dw length dup (value) ;; reserve space for the item
else
ifidn <width>,<BYTE>
db 1
l db length dup (value)
else
ifidn <width>,<DWORD>
db 4
l dd length dup (value)
else
db width
l db length dup (width dup (value))
endif
endif
endif
LASTDATA ends
endm
;* AssertCLD - Check the state of the direction flag
;
; Entry: none
; Exit: none
; Uses: <reg>, Flags
;
; Causes an internal error if the direction flag is set
;
AssertCLD macro proc, reg
local acld
ifdef MISCSTRICT
ifnb <reg>
pushf
pop reg
test reg,f_Direction ;; make sure mi.inc is included
else
push ax
pushf
pop ax
test ax,f_Direction ;; make sure mi.inc is included
pop ax
endif
jz acld ;; jmp if direction flag is clear
InternalError <&proc&: direction flag set>
acld:
endif
endm
;*** AssertNoBuf - Assert that current thread isn't holding a buffer
;
; AssertNoBuf is used to detect certain states that often lead to
; deadlock. In particular, we'll often deadlock if we call the
; swapper while holding a buffer. We can call the swapper either
; via a call to the memory manager, or via taking a not-present
; fault touching a swappable segment. AssertNoBuf should be placed
; anywhere we might do these things to help catch accidentally doing
; them with a buffer locked.
;
; AssertNoBuf is turned on by the FSSTRICT flag.
;
; AssertNoBuf is by no means fool-proof, but should detect the
; majority of cases where we have a buffer locked.
;
; ENTRY (SS) = TaskArea
; EXIT None if OK, InternalError if error found
; USES None, including flags
AssertNoBuf macro
ifdef FSSTRICT
if ?farcode EQ FC_High2Code ;; in same segment, near call
invoke AssertNoBufProc
else ;; Generate FAR call
callfar AssertNoBufProc
endif
endif
endm