;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
;       SCCSID = @(#)osmaca.inc 6.4 91/10/18
;***    OSMACA.INC
;
;       Macro file for OS/2 kernel
;
;       SCCSID = @(#)osmaca.inc 6.4 91/10/18
;
;       This file provides the following macros:
;
;         AsmVars
;         AsmVar
;         EXTRNFAR
;         EXTRN16
;         EXTRN32
;         EXTRNF32
;         DDFAR
;         MOVFAROFFSET
;         CALLFAR
;         CALL16
;         JMPFAR
;         JMP16
;         CALLF32
;         CCALL32
;         PCALL32
;         CALL32
;         JMP32
;         ThunkFar32
;         CallInstall
;         SegContext
;         DosContext
;         GDTContext
;         TaskContext
;         DosInitContext
;         DosMTEContext
;         FlatContext
;         NullContext
;         ReferGlobal
;         ReferGDT
;         ReferTask
;         ReferHigh2
;         ReferInitGlobal
;         ReferFlat
;         FARCODE
;         InternalError
;         Panic
;         ModName
;         BeginNoBlock
;         EndNoBlock
;         AssertCLD
;         AssertNoBuf
;         DefTaskData
;         CallGate
;         StampRPL

; 12/15/93  RAC  76699  Make Perfview and Tracing Optional in system
; 1/4/94    RAC  77052  Add check for MMIO build to include RAS

;***    OLD usage equates

ifndef  BREAK
BREAK           equ     <Break>
endif

ifndef  CpuMode
CpuMode         equ     <CPUMode>
endif

;
; The RAS flag causes all RAS related code to be assembled as
; part of the DOS. All RAS related code should be surrounded
; by:
;       IF RAS
;         :
;       ras code
;         :
;       ENDIF   ; RAS
;

; If building Perfview and tracing kernal, turn on RAS, otherwise turn it off
; D 76699.  D 77052, adds check for MMIO build.
ifdef PERFVIEW
RAS     EQU     1
else
ifdef MMIOPH
RAS     EQU     1
else
RAS     EQU     0
endif   ; MMIOPH
endif   ; PERFVIEW

;***    AsmVars - initialize variables
;
;       Each variable in varlist is examined to see if it has already
;       been defined. If so, nothing happens. Otherwise it is
;       defined with the value "value".
;
;       ENTRY:  varlist = list of variables enclosed in < >
;               value   = a value to assign to all undefined variables.
;
;       EXIT:   undefined variables in varlist are set to "value."

AsmVars macro   varlist, value
    irp var,<varlist>
        AsmVar var, value
    endm
endm

;***    AsmVar - initialize a variable
;
;       The variable "var" is examined to see if it has already
;       been defined. If so, nothing happens. Otherwise it is
;       defined with the value "value."
;
;       ENTRY:  var   = a variable
;               value = value to assign to var
;
;       EXIT:   var is set to "value" if it wasn't previously defined.

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 DEMSTRICT,  TRUE
    AsmVar EM86STRICT, TRUE
    AsmVar VDHSTRICT,  TRUE
    AsmVar VDDSTRICT,  TRUE
    AsmVar VDMMSTRICT, TRUE
    AsmVar DEVSTRICT,  TRUE
    AsmVar FSSTRICT,   TRUE
    AsmVar PIPESTRICT, TRUE
    AsmVar INFOSTRICT, TRUE
    AsmVar INTERRPRT,  TRUE
    AsmVar KMSTRICT,   TRUE
    AsmVar LDRSTRICT,  TRUE
    AsmVar MISCSTRICT, TRUE
    AsmVar MODECHECK,  TRUE
    AsmVar PERFSTRICT, TRUE
    AsmVar PGSTRICT,   TRUE
    AsmVar SELSTRICT,  TRUE
    AsmVar SEMSTRICT,  TRUE
    AsmVar SMSTRICT,   TRUE
    AsmVar VMSTRICT,   TRUE
    AsmVar VOLPTRCHK,  TRUE
    AsmVar ALIGNCODE,  TRUE
    AsmVar DBGSTRICT,  TRUE
    AsmVar PTSTRICT,   TRUE
endif

ifdef INTERRPRT
        IntErrF = TRUE  ; for v86code only
else
        IntErrF = FALSE ; for v86code only
endif

AsmVars <Debug,ShareF>

?om1 = 0                ;; osmaca.inc scratch variable

;***    ?GenCall - generate a call to a hybrid procedure
;
;       It generates either far call or near call depending on the
;       last use of the FARCODE macro.
;
;       ENTRY:  target = name of hybrid procedure
;               targetseg = SN_targetseg of target segment
;       (global variables)
;               ?farcode = <targetseg> - generate appropriate type call
;
;       EXIT:   code generated to call the hybrid procedure
;
;       SEE ALSO: Procedure

?GenCall macro  target, targetseg
    if (?farcode eq targetseg)          ;; source and target in same segment?
        if2
            ifndef target
                extrn target:near
            endif
        endif
        call  target                    ;; near 16 or 32 bit call
    elseif (?cstype eq ?CS_32bit)       ;; source in 32 bit segment?
        CALL16 target                   ;; far 32 bit call
    elseif (targetseg eq SN_DosHigh32Code) ;; target in 32 bit segment?
        if2
            ifndef target
                extrn target:near
            endif
        endif
        CALL32 target                   ;; near 32 bit call (with thunk)
    else                                ;; else source & target in 16 bit segs
        CALLFAR target                  ;; far 16 bit call
    endif
endm


;***    EXTRNFAR - generate an extrn for the hybrid symbol.
;***    EXTRN16 - generate an extrn for the far16 symbol.
;***    EXTRN32 - generate an extrn for the near 32 bit symbol.
;***    EXTRNF32 - generate an extrn for the far32 symbol.
;       Skip it has already been defined or the skip argument is nonblank.
;
;       ENTRY   name = symbol
;               skip = blank - generate the extrn
;                    = non-blank - don't generate the extrn
;       EXIT    an extrn generated if skip is blank.
;       SEE ALSO Procedure

EXTRNFAR macro  name, skip
    if2
        ?GenExtrn %HYBPRE_, name,, far, skip
    endif
endm

EXTRN16 macro name, skip
    ?GenExtrn %F16PRE_, name,, far, skip
endm

EXTRN32 macro name, skip
    ?GenExtrn , name, fflat, near, skip
endm

EXTRNF32 macro name, skip
    .386p
    ?GenExtrn %F32PRE_, name, fflat, far, skip
    CpuMode reset
endm

?GenExtrn macro prefix, name, fflat, distance, skip
    ifb <skip>
        ifndef prefix&name
            ifnb <fflat>
                if (?cstype ne ?CS_32bit)
                    _TEXT segment
                endif
            endif
            .lall
            extrn prefix&name:distance
            .xall
            ifnb <fflat>
                if (?cstype ne ?CS_32bit)
                    _TEXT ends
                endif
            endif
        endif
    endif
endm


;***    DDFAR - generate far pointer to hybrid procedure
;***    DDOFF16 - generate 32 bit offset of far16 procedure
;***    DWOFF16 - generate 16 bit offset of far16 procedure
;
;       ENTRY:  name = name of hybrid/far16 procedure.
;                      Must be defined in the current file.
;
;       EXIT:   double word generated containing the far pointer (seg, off),
;               double word generated containing the 32 bit offset,
;               OR word generated containing the 16 bit offset.
;
;       SEE ALSO: Procedure

DDFAR   macro name
    CatPrefix <dw offset>, %HYBPRE_, <name>
    CatPrefix <dw seg>, %HYBPRE_, <name>
endm

DDOFF16   macro name
    CatPrefix <dd offset>, %F16PRE_, <name>
endm

DWOFF16   macro name
    CatPrefix <dw offset>, %F16PRE_, <name>
endm

;***    MOVFAROFFSET - move the offset of the far entry of a hybrid or
;                      a faronly procedure into a register or memory.
;
;       The offset is relative to the current group/segment cs is
;       assuming or relative to the supplied segment/group name.
;
;       ENTRY:  target = register or memory to receive the offset.
;               name   = hybrid/faronly procedure name
;               group  = blank - use whatever group cs is assuming
;                      = non-blank - use this as the group/segment to
;                                    calculate the offset.
;
;       EXIT:   "mov" instruction generated to move an offset into the
;               target.

MOVFAROFFSET macro target,name,group
    ifb <group>
        CatPrefix <mov target, offset cs:>, %HYBPRE_, <name>
    else
        CatPrefix <mov target, offset group:>, %HYBPRE_, <name>
    endif
endm

;***    CALLFAR - generate a far16 call to a faronly/hybrid procedure
;***    CALL16 - generate a far32 call to a far16 procedure
;***    JMPFAR - generate a far16 jump to a faronly/hybrid procedure
;***    JMP16 - generate a far32 jump to a far16 procedure
;
;       The procedure must already be defined in the current file
;       in a Procedure declaration or via EXTRNFAR or EXTRN16.
;
;       ENTRY   name = name of hybrid/faronly/far16 procedure
;       EXIT    generated a far call to the given procedure
;       SEE ALSO Procedure

CALLFAR macro   name                    ;; call far16 ptr HYBPRE_&name
    if2
        if ?cstype eq ?CS_32bit
            ProcError <CALLFAR name in 32 bit code>
            .err
        endif
        ifdef SN_DosInitR3Code
            if ?farcode eq SN_DosInitR3Code
                ProcError <CALLFAR name in ring 3 code>
                .err
            endif
        endif
    endif
    CatPrefix <call far ptr>, %HYBPRE_, <name>
endm

CALL16  macro   name                    ;; call far32 ptr F16PRE_&name
    if2
        if (?cstype ne ?CS_32bit)
            ProcError <CALL16 name in 16 bit code>
            .err
        endif
    endif
    assume cs:DOSHIGH32CODE             ;; disable assume cs:FLAT
    ;;  can't use a plain far call because masm turns it into a 16 bit far call
    ;;  call  far ptr %F16PRE_&name
    db  MI_LONG_CALL
    CatPrefix <df>, %F16PRE_, <name>
    ?ASSUMECS
endm

JMPFAR  macro   name
    if2
        if ?cstype eq ?CS_32bit
            ProcError <JMPFAR name in 32 bit code>
            .err
        endif
        ifdef SN_DosInitR3Code
            if ?farcode eq SN_DosInitR3Code
                ProcError <JMPFAR name in ring 3 code>
                .err
            endif
        endif
    endif
    CatPrefix <jmp far ptr>, %HYBPRE_, <name>
endm

JMP16   macro   name                    ;; jmp far ptr F16PRE_&name
    if2
        if (?cstype ne ?CS_32bit)
            ProcError <JMP16 name in 16 bit code>
            .err
        endif
    endif
    assume cs:DOSHIGH32CODE             ;; disable assume cs:FLAT
    ;; CatPrefix <jmp far ptr>, %F16PRE_, <name>
    ;; use explicit opcodes to avoid masm 5 trailing nop
        db      MI_OPERANDSIZE, MI_LONG_JMP
        CatPrefix <dw offset>, %F16PRE_, <name>
        CatPrefix <dw seg>, %F16PRE_, <name>
    ?ASSUMECS
endm

;***    CCALL32 - call 32-bit near "C" procedure from a 16-bit segment
;***    CALLF32 - call 32-bit far "C" procedure from a 16-bit segment
;***    PCALL32 - call 32-bit near "PASCAL" procedure from a 16-bit segment
;***    CALL32 - call 32-bit procedure from a 16-bit segment
;***    JMP32 - jump to 32-bit procedure from a 16-bit segment
;
;       This macro calls or jumps to a 32-bit procedure from a 16-bit
;       code segment.  If requested (CALL32 only), it sets DS and ES
;       to the flat ring 0 data selector before calling the function.
;
;       CCALL32 target, [<args>], [<segargs>]
;       CALLF32 target, [<args>], [<segargs>], [CCall modifiers]
;       PCALL32 target, [<args>], [<segargs>]
;       CALL32  target, [<segargs>], [calltype], [args], [CCall modifiers]
;
;       ENTRY   target  - name of function to call
;               args    - list of argument parameters
;               segargs - optional parameter
;                         nocheck: suppress verification of DS and ES
;                         nosegcheck: suppress verification thunk seg
;                         nofar32: suppress far32 call
;                         far32: use far32 call
;                         nocall: use far jmp thunk (preserve SP for call)
;                         noalignesp: suppress alignment of esp
;                         tgtseg: causes thunk to be placed in tgtseg
;       EXIT    As from called function
;       USES    EAX, ECX, EDX (optionally DS, ES), Flags

?thcnt = 0
?Call32Label macro modname, n, lab
;   public modname&n&lab   Defect 61299 workaround. Paul.
    modname&n&lab:
endm

CALLF32 macro   target, args, segargs, testarg
    CALL32 <target>,<far32,segargs>,,<args>,,<testarg>
endm

CCALL32 macro   target, args, segargs, modifiers, testarg
    CALL32 <target>,<segargs>,<CCall>,<args>,<modifiers>,<testarg>
endm

PCALL32 macro   target, args, segargs, testarg
    CALL32 <target>,<segargs>,<PCall>,<args>,,<testarg>
endm

?S_NOCHECK      equ     01h     ;; <nocheck> - suppress AssertDSESFlat
?S_NOSEGCHECK   equ     02h     ;; <nosegcheck> - suppress far32 address check
?S_NOFAR32      equ     04h     ;; <nofar32> - suppress far32 call
?S_FAR32        equ     08h     ;; <far32> - use far32 call
?S_NOCALL       equ     10h     ;; <nocall> - suppress near thunk call
?S_NOALIGNESP   equ     20h     ;; <noalignesp> - suppress align esp
?S_SEG          equ     40h     ;; <???> - specify thunk segment name
?thunkcodeseg   equ     <.err>

CALL32  macro   target, segargs, calltype, args, modifiers, testarg
local   label32, label16
    .386p
    ?sflags = 0                 ;; assert ds/es FLAT, resident thunk with retfd
    ?thunkcodeseg catstr ?ThunkSeg
    irp x,<segargs>                             ;; examine segargs
        ifnb <x>
            ifidn <x>,<nocheck>
                ?sflags = ?sflags or ?S_NOCHECK ;; don't assert ds,es FLAT
            elseifidn <x>,<nosegcheck>
                ?sflags = ?sflags or ?S_NOSEGCHECK;; don't assert thunk seg
            elseifidn <x>,<nofar32>
                ?sflags = ?sflags or ?S_NOFAR32 ;; don't call F32PRE_&target
            elseifidn <x>,<far32>
                ?sflags = ?sflags or ?S_FAR32   ;; use call F32PRE_&target
            elseifidn <x>,<nocall>
                ?sflags = ?sflags or ?S_NOCALL  ;; use jmp32/jmp16
            elseifidn <x>,<noalignesp>
                ?sflags = ?sflags or ?S_NOALIGNESP ;; don't align esp
            else
                ?sflags = ?sflags or ?S_SEG     ;; use specified segment
                ?thunkcodeseg catstr x
            endif
        endif
    endm
    irp x,<modifiers>                           ;; examine modifiers
        ifidn <x>,<PushP>
           if (?sflags and ?S_FAR32)
              ProcError <CCALL32 target: PushP conflicts with call>
           endif
           if ((?sflags and ?S_NOALIGNESP) eq 0)
               ?sflags = ?sflags or ?S_NOALIGNESP
           endif
           ?sflags = ?sflags or ?S_NOCALL       ;; don't use retfd
        endif
    endm
    .lall
    ?om1 = ?sflags                              ;; only for listing files
    .xall
    if2
        ifdef SN_DosInitR3Code
            if ?farcode eq SN_DosInitR3Code
                ProcError <CCALL32 target in ring 3 code>
                .err
            endif
        endif
        ?om1 = ?sflags and (?S_FAR32 or ?S_NOFAR32 or ?S_NOCALL)
        if (?om1 and (?om1 - 1))                ;; verify only one bit set
            ProcError <CCALL32 target: nofar32/far32/nocall conflict>
        endif
        if ((?sflags and ?S_FAR32) eq 0)
            ifb <calltype>
                ifnb <args>
                    ProcError <CALL32 target: use CCALL32/CALLF32 with parms>
                endif
                ifnb <modifiers>
                    ProcError <CALL32 target: use CCALL32/CALLF32 with CCall_modifiers>
                endif
            endif
        endif
        ifnb <testarg>
            ProcError <CCALL32 target: too many args: testarg>
        endif
    endif
    if ((?cstype eq ?CS_32bit) eq 0)
        if ((?sflags and (?S_FAR32 or ?S_NOFAR32 or ?S_NOCALL)) eq 0)
            ifb <args>
                ?sflags = ?sflags or ?S_FAR32
            endif
        endif
        if ((?sflags and ?S_FAR32) eq 0)        ;; thunk required?
%           ?Eval ?thunkcodeseg <segment>       ;; put thunk in 32 bit code
                assume cs:FLAT                  ;; change to flat cs
                ?Call32Label %@FileName, %?thcnt, target
                ?thcnt = ?thcnt + 1
                label32 label near
%           ?Eval ?thunkcodeseg <ends>
            ?ASSUMECS                           ;; back to original cs

            if (?sflags and ?S_NOCALL)
                JMP32  label32                  ;; generate a 32-bit far jump
                .386p
            else
                call  far ptr FLAT:label32      ;; generate a 32-bit far call
            endif

            label16 label far                   ;; and label for return jump
%           ?Eval ?thunkcodeseg <segment>       ;; put thunk in 32 bit code
                assume cs:FLAT                  ;; change to flat cs
        endif
    else
       ?sflags = ?sflags or ?S_NOALIGNESP ;; don't align esp
    endif
    if ((?sflags and ?S_NOALIGNESP) eq 0)       ;; align esp?
;       push    ebp
;       lea     ebp,4[esp]
;       and     esp,not 3
;       push    ebp
;       mov     ebp,ss:-4[ebp]
    endif
    if ((?cstype eq ?CS_32bit) eq 0)
        if ((?sflags and ?S_FAR32) eq 0)        ;; thunk required?
            ?ThunkStrict %?sflags,, label16,, target
        endif
    endif

    if (?sflags and ?S_FAR32)                   ;; 32-bit far global thunk?
        CCall <target>,<args>,<far32,modifiers> ;; make 32 bit near call
    elseifnb <calltype>
        calltype <target>,<args>,<modifiers>    ;; make 32 bit near call
    else
        call  target                            ;; make 32-bit near call
    endif
    if ((?sflags and ?S_NOALIGNESP) eq 0)       ;; was esp aligned?
;       pop     esp
    endif

    if ((?cstype eq ?CS_32bit) eq 0)
        if ((?sflags and ?S_FAR32) eq 0)
            if (?sflags and ?S_NOCALL)          ;; if should use jmp
                ;; jmp far ptr label16          ;; jmp far 16:16 back to caller
                ;; use explicit opcodes to avoid masm 5 trailing nop
                db      MI_OPERANDSIZE, MI_LONG_JMP
                dw      offset label16, seg label16
            else
                retf                            ;; retfd back to caller
            endif
%           ?Eval ?thunkcodeseg <ends>

            ?ASSUMECS                           ;; back to original cs
        endif
    endif
    CpuMode reset                               ;; back to original cpu mode
endm

JMP32   macro   name                            ;; jmp far ptr FLAT:name
    if2
        if (?cstype eq ?CS_32bit)
            ProcError <JMP32 name from 32 bit segment>
            .err
        endif
    endif
    .386p
    jmp   far ptr FLAT:name
    CpuMode reset                               ;; back to original cpu mode
endm

ThunkFar32 macro name, lcl, segname, fnocheck
    if2
        ifnb <fnocheck>
            ifdif <fnocheck>,<nocheck>
                %out <ThunkFar32: bad nocheck arg: fnocheck>
                .err
            endif
        endif
    endif
    FARCODE HIGH32
    segname segment
    if2
        ifndef name
            extrn name:near
        endif
    endif
    GenFar32 name, lcl, <usecall>, <tolabel>
    ifnb <fnocheck>
        ?ThunkStrict ?S_NOCHECK, %F32PRE_, name,, name
    else
        ?ThunkStrict 0, %F32PRE_, name,, name
    endif
    GenFar32 name, lcl, <usecall>, <afterlabel>
    segname ends
endm


?ThunkStrict macro sflags, srcpre, srclabel, tgtpre, tgtlabel
    ifdef MISCSTRICT
        if ((sflags and ?S_NOCHECK) eq 0)
            ?GenCall _AssertDSESFlat, ?farcode ;; force near call
        endif
        if ((sflags and ?S_NOSEGCHECK) eq 0)
            THUNKINIT_DATA segment
                dd  offset FLAT:srcpre&srclabel ;; source address
                dd  offset FLAT:tgtpre&tgtlabel ;; target address
            THUNKINIT_DATA ends
        endif
    endif
endm

;***    SaveAlignESP - Save and Align ESP
;
;       Save Current ESP and Align it
SaveAlignESP macro ereg
    ifnb    <ereg>
      mov   ereg,esp
      and   esp,not 3
      push  ereg
    else
      mov   eax,esp
      and   esp,not 3
      push  eax
    endif
endm

;***    SegContext - load segment register(s)
;
;       Make all given segment registers point to "base".
;
;       ENTRY:  srl      = segment registers list in < >
;               gr       = general register to speed operation
;                        = blank - use slow way of loading registers
;               noassume = blank - generate an assume sr:grpnam for
;                                  each segment register sr
;                        = non-blank - don't generate assumes.
;               base     = value to be loaded into segment register(s)
;               grpnam   = name of group/segment to assume
;
;       EXIT:   code generated to load segment registers. If "gr" is
;               supplied, its content is lost.
;
;       SEE ALSO: TaskContext, DosHighContext, DosInitContext.

?smov   macro a1, a2
    mov   a1,a2
endm

SegContext macro    srl,gr,noassume,base,grpnam
local i
    ifnb <gr>                           ;; if intermediate general register
        ?gr1 equ <gr>                   ;; assume 16 bit gr
        irp r,<ax,bx,cx,dx,si,di>
            ifidni <e&&r&&>,<gr>
                ?gr1 equ <r>            ;; oops, load selector into 16 bit gr
                exitm
            endif
        endm
        if (?cstype eq ?CS_32bit)
            ?gr2 catstr <e>,?gr1        ;; load sr from 32 bit gr
        else
            ?gr2 catstr <>,?gr1         ;; load sr from 16 bit gr
        endif
        ?smov   %?gr1,<base>            ;; load selector into 16 bit gr
        irp sr,<srl>
            ?smov   sr,%?gr2            ;; load sr from native size 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  base
                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>
            .lall
            assume sr:grpnam
            .xall
        endm
    endif
endm

;***    DosContext - make segment register(s) point to DOSGROUP
;***    DosHighContext - make segment register(s) point to DOSHIGHDATA
;***    GDTContext - make segment register(s) point to DOSGDTDATA
;***    TaskContext - make segment register(s) point to SS; SS must be TASKAREA
;***    DosInitContext - make segment register(s) point to DOSINITDATA
;***    DosMTEContext - make segment register(s) point to DOSMTE
;***    FlatContext - make segment register(s) point to FLAT descriptor
;***    NullContext - make segment register(s) point to nothing
;
;       Loads all given segment registers with the specified group.
;       This is often used to set up DS in order to use a global DOS variable.
;
;       ENTRY:  srl      = segment registers list in < >
;               gr       = general register to speed operation
;                        = blank - use slow way of loading registers
;               noassume = blank - generate an assume sr:DOSGROUP for
;                                  each segment register sr in "srl".
;                        = non-blank - don't generate assumes.
;       (global variables)
;               groups defined in dosseg.inc
;
;       EXIT:   code generated to load segment registers. If "gr" is
;               supplied, its content is lost.
;
;       SEE ALSO: SegContext

DosContext macro    srl,gr,noassume
        SegContext <srl>,gr,noassume,DOSGROUP,DOSGROUP
endm

DosHighContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,DOSHIGHDATA,DOSHIGHDATA
endm

GDTContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,DOSGDTDATA,DOSGDTDATA
endm

TaskContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,SS,TASKAREA
endm

DosInitContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,DOSINITDATA,DOSINITDATA
endm

DosMTEContext   macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,DOSMTE,DOSMTE
endm

FlatContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,<seg FLAT:DGROUP>,FLAT
endm

NullContext macro   srl,gr,noassume
        SegContext <srl>,gr,noassume,0,NOTHING
endm

;***    ReferGlobal - declare external symbol in DOSGROUP.
;***    ReferGDT - declare external symbol in the DOSGDTDATA.
;***    ReferTask - declare external symbol in TASKGROUP.
;***    ReferHigh2 - declare external symbol in DOSHIGH2CODE.
;***    ReferInitGlobal - declare external symbol in DOSINITDATA.
;***    ReferFlat - Declare an external symbol in FLAT group.
;
;       Declares a symbol as an external symbol in the appropriate segment.
;
;       ENTRY   sym = symbol name
;               symtype = valid masm type
;                       = blank implies byte
;                         BUGBUG: ReferTask allows obsolete symtype values
;       (global variables)
;               segments defined in dosseg.inc
;
;       EXIT    extrn generated for the symbol.

ReferGlobal macro sym, symtype, ifundef
    ?refer CONSTANTS, sym, symtype, ifundef
endm

ReferGDT macro sym, symtype, ifundef
    ?refer GDTSTART, sym, symtype, ifundef
endm

ReferTask macro sym, symtype, ifundef, nocheck
local a
    ifb <nocheck>
        ifdef   TKOPTDA
            %out ReferTask: not compatable with tkptda.inc
            .err
        endif
    endif

    ifnb <symtype>
        a = 0
        irp x, <83, 128, MAXMETANAME, MAXPATHLEN, PMPATHLEN, SRCHBUFSIZE>
            ifidn <symtype>, <x>        ; Is symtype a known obsolete value?
                a = 1
                exitm
            endif
        endm
    else
        a = 1
    endif
    if a
        ?refer TASKDATA, sym, byte, ifundef
    else
        ?refer TASKDATA, sym, symtype, ifundef
    endif
endm

ReferHigh2 macro sym, symtype, ifundef
    ?refer HIGH2CODE, sym, symtype, ifundef
endm


ReferInitGlobal macro sym, symtype, ifundef
    ?refer INITDATA, sym, symtype, ifundef
endm

ReferFlat macro sym, symtype, ifundef
    .386p
    ?refer _DATA, sym, symtype, ifundef
    CpuMode reset
endm

?refer macro segname, sym, symtype, ifundef
ifnb <ifundef>
    if2
        ifndef sym
            segname segment
            ifb <symtype>
                .lall
                extrn sym:byte
                .xall
            else
                .lall
                extrn sym:symtype
                .xall
            endif
            segname ends
        endif
    endif
else
    segname segment
    ifb <symtype>
        .lall
        extrn sym:byte
        .xall
    else
        .lall
        extrn sym:symtype
        .xall
    endif
    segname ends
endif
endm

ifndef  SN_DosCode
;       We need SN_DosCode to be defined to compare against ?farcode.
;       dosseg.inc may never be included after dosmac.inc, if used at all.
        SN_DosCode       equ    0       ; meaningless, non-redefinable value
endif
        ?farcode = SN_DosCode   ; default to near code

;***    FARCODE - Introduce high DOS code segment
;
;       Tell macros that generates hybrid procedure call's to generate
;       far call's. (E.g. InternalError, DBPRT, PMONLY) This remains
;       in effect until FARCODE is invoked again. This should be invoked
;       just prior to every code segment declaration, and outside of all
;       segments. If FARCODE is not invoked, the default is "FARCODE LOW".
;
;       ENTRY:  segname    = "LOW"    - select the DOSCODE group
;                          = "INIT"   - select the DOSCODE group
;                          = "INITR3" - select the DOSINITR3CODE group
;                          = "HIGH2"  - select the DOSHIGH2CODE group
;                          = "HIGH3"  - select the DOSHIGH3CODE group
;                          = "HIGH4"  - select the DOSHIGH4CODE group
;                          = "HIGH32" - select the DOSHIGH32CODE group
;               NoPanic    = blank - generate extrn's for the Panic
;                              and InternalError macro support routines,
;                              if they haven't been defined yet
;                          = non-blank - don't generate extrn's for
;                              IntErr and IntErrNull
;               NoProtReal = blank - generate extrn's for the mode
;                              checking routines (see RMONLY, PMONLY)
;                              if they haven't been defined yet and
;                              MODECHECK is defined.
;                          = non-blank - don't generate extrn's for
;                              the mode checking routines.
;               NoDPRINTF  = blank - generate extern's for DPRINTF and
;                              dbtest if they haven't been defined yet
;                              and Debug is non-zero.
;                          = non-blank - don't generate extrn's for
;                              DPRINTF and dbtest.
;       (global variables)
;               DOSCODE   = group defined in dosseg.inc
;               MODECHECK = flag to enable the mode checking routines
;               Debug     = flag to control the DPRINTF and dbtest
;                           routines.
;
;       EXIT:   an assume cs: generated with the proper group name
;               extrn's generated for debugging/mode-checking routines.
;       (global variables)
;               ?farcode = set to SN_xxx to remember which group was selected.
;               ?cstype = set to ?CS_16bit or ?CS_32bit, according to the
;                       D bit setting for the new CS

.386p
    assume fs:FLAT                      ;; to enable the FLAT keyword
    assume fs:nothing
CpuMode reset

?ThunkSeg       equ <THUNK_TEXT>
?PanicSuffix    equ <bad suffix>

FARCODE macro   segname, NoPanic, NoProtReal, NoDPRINTF, NoAssertNoBuf
    ?cstype = ?CS_16bit                 ;; default is 16-bit code
    ?farcode = SN_DosCode               ;; Indicate low code segment
    ?ASSUMECS macro
        assume cs:DOSCODE
    endm
    ?ThunkSeg equ <THUNK_TEXT>
    ?PanicSuffix equ <>
    ifidn <segname>,<LOW>
        ?PanicSuffix equ <LOW>
    elseifidn <segname>,<INIT>
        ?PanicSuffix equ <LOW>
        ?ThunkSeg equ <THUNKINIT_TEXT>
    elseifidn <segname>,<INITR3>
        ?PanicSuffix equ <INITR3>
        ?farcode = SN_DosInitR3Code     ;;  Indicate which far code segment
        ?ASSUMECS macro
            assume cs:DOSINITR3CODE
        endm
        ?ThunkSeg equ <no ring 3 thunks>
    elseifidn <segname>,<HIGH2>
        ?PanicSuffix equ <HIGH2>
        ?farcode = SN_DosHigh2Code      ;; Indicate which far code segment
        ?ASSUMECS macro
            assume cs:DOSHIGH2CODE
        endm
    elseifidn <segname>,<HIGH3>
        ?PanicSuffix equ <HIGH3>
        ?farcode = SN_DosHigh3Code      ;; Indicate which far code segment
        ?ASSUMECS macro
            assume cs:DOSHIGH3CODE
        endm
    elseifidn <segname>,<HIGH4>
        ?PanicSuffix equ <HIGH4>
        ?farcode = SN_DosHigh4Code      ;; Indicate which far code segment
        ?ASSUMECS macro
            assume cs:DOSHIGH4CODE
        endm
    elseifidn <segname>,<HIGH32>
        ?PanicSuffix equ <HIGH32>
        ?farcode = SN_DosHigh32Code     ;; Indicate which far code segment
        ?cstype = ?CS_32bit             ;; Mark as 32-bit code segment
        ?ASSUMECS macro
            assume cs:FLAT
        endm
    elseif2
        %out FARCODE: bad segment name: segname
        .err
    endif
    if1
        ifndef _TKSSBase
            ReferFlat _TKSSBase,dword
        endif
    endif
    ifdef INTERRPRT
        EXTRNFAR __panicsprintf,<NoPanic>
    endif
    ifdef MODECHECK
        EXTRN16 TaskOnly,<NoProtReal>
        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
    ifdef FSSTRICT
        if ?farcode ne SN_DosHigh2Code
            EXTRNFAR AssertNoBufProc,<NoAssertNoBuf>
        endif
    endif
    ?ASSUMECS
    .386p
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
    CpuMode reset
endm


;***    InternalError - panic when things go wrong
;
;       Calls Panic macro.
;
;       ENTRY   arg = error message to display before system halts
;       EXIT    Panic macro invoked.

InternalError   macro   arg
    if (?farcode eq SN_DosCode)         ;; if in tiled code, assume realmode
        Panic <'&arg&'>,,,realmode
    else
        Panic <'&arg&'>
    endif
endm

;***    Panic - print panic message and halt the system
;
;       This macro prints the specified panic message (with parameters) and
;       halts the system.
;
;       ENTRY   fmt             - message format string
;               parmlist        - parameter list
;               n               - source-file-unique Panic number
;               frealmode       - "realmode" flag
;       EXIT    *DOES NOT RETURN*

PANICBASE       equ     60000   ; start high to avoid source line conflict
?paniccnt = PANICBASE
?PF_TILED       equ     01h
?PF_FILE        equ     02h
?panicfile = 0
?panicmsgseg    equ     <bad seg>

Panic   macro   fmt,parmlist,n,frealmode
local fmtoff, cparms, rmode
    if2
        ifnb <frealmode>
            ifdif <frealmode>,<realmode>
                ProcError <Panic: bad realmode arg: frealmode>
                .err
            endif
        endif
    endif
    .386p
    ?panicfn equ <__panic>
    rmode = 0
    ifnb <frealmode>
        if (?farcode eq SN_DosCode)     ;; If in tiled code segment
            ?panicfn equ <__panicRM>
            rmode = 1
        endif
    endif
    ?om1 = 0
    ifdef INTERRPRT
        ifndef NOINTERRPRT
            ifnb <fmt>
                ?om1 = 1                ;; display filename and message text
            endif
        endif
    endif
    if (?om1 ne 0)
        ?panicfn equ <__panicfnmsg>
        ?panicmsgseg equ <PANICMSG>
        ifnb <frealmode>                        ;; don't use rmode flag!!

            ;; If this invocation requires __FILE__ in the tiled data segment,
            ;; then make sure the first Panic invocation put it there.

            if (rmode ne 0)
                if ((?panicfile and (?PF_FILE or ?PF_TILED)) eq ?PF_FILE)
                    if2
                        ProcError <first Panic must specify realmode>
                        .err
                    endif
                    exitm
                endif
                ?panicfn equ <__panicfnmsgRM>
            endif

            ;; Put the strings in the low (tiled) data segment if realmode was
            ;; specified in the tiled code segment (DOSCODE) OR if this is the
            ;; very first Panic invocation and the realmode flag was specified.
            ;; This allows a real mode Panic invocation to access the __FILE__
            ;; and fmtoff strings.  rmode is non-zero when InternalError was
            ;; invoked in DOSCODE.  ?panicfile is zero when this is the first
            ;; Panic invocation.

            if ((rmode ne 0) or (?panicfile eq 0))
                ?panicmsgseg equ <INTERRMSG>
                if (?panicfile eq 0)
                    ?panicfile = ?panicfile or ?PF_TILED
                endif
            endif
        endif
%       ?Eval ?panicmsgseg <segment>            ;; put string in proper segment
            if ((?panicfile and ?PF_FILE) eq 0)
                ifdef SCCSID
                    __FILE__  db  SCCSID, 0
                else
                    if2
%                       %out @FileName.asm: Panic: SCCSID undefined
                        .err
                    endif
%                   __FILE__  db  '&@FileName&.asm', 0
                endif
                ?panicfile = ?panicfile or ?PF_FILE
            endif
            fmtoff  db  fmt, 0          ;; Format string
%       ?Eval ?panicmsgseg <ends>

        cparms = 0
        irp x,<parmlist>
            cparms = cparms + 1         ;; Count arguments
            PUSHD  <x>                  ;; Push arguments
        endm
        if (rmode ne 0)
            push  offset DOSGROUP:fmtoff ;; Push format string address
        else
            PUSHD <offset FLAT:fmtoff>  ;; Push FLAT format string address
        endif
        if (cparms ne 0)                ;; sprintf into a buffer
            if2
                if (rmode ne 0)
                    ProcError <Panic: no parms in realmode>
                    .err
                endif
                ifdef SN_DosInitR3Code
                    if ?farcode eq SN_DosInitR3Code
                        ProcError <Panic: no parms in ring 3>
                        .err
                    endif
                endif
            endif
            if ?cstype eq ?CS_32bit
                ?GenCall __panicsprintf, SN_DosHigh32Code
            else
                CALLFAR __panicsprintf
            endif
            lea  esp,[esp + (cparms * 4)]  ;; clean up the stack
            ?panicfn equ <__panicfn>
        endif
        if (rmode ne 0)
            push  offset DOSGROUP:__FILE__ ;; Push filename address
        else
            PUSHD <offset FLAT:__FILE__>   ;; Push FLAT filename address
        endif
    endif
    CpuMode reset                       ;; reset cpu mode
    if (rmode eq 0)
        ?panicfn catstr ?panicfn,?PanicSuffix;; build panic function name
    endif
    ?GenCall %?panicfn, %?farcode       ;; call panic thunk near
    if (?ModNumber eq 0ffffh)
        ?ModNumber = 0
        if2
%           %out @FileName.asm: Panic: FNO_NUM undefined - use ModName macro
            .err
        endif
    endif
    dw  ?ModNumber                      ;; store file number
    ifnb <n>
        dw  n                           ;; store specified "line number"
    else
        dw  ?paniccnt                   ;; store computed "line number"
        ?paniccnt = ?paniccnt + 1
    endif
endm

;***    ModName - declare module name for debugging purpose
;
;       The module number is used for generating Panic file numbers.
;
;       ENTRY:  modname   = module name (ignored; see masm's @FileName feature)
;               modnumber = module number (used for Panic invocations)
;
;       EXIT:
;       (global variables)
;               ?ModNumber = file number

?ModNumber = 0ffffh

ModName macro   modname, modnumber
    ifnb <modnumber>
        ?ModNumber = modnumber
    endif
endm

;***    BeginNoBlock/EndNoBlock - begin/end kernel critical section.
;
;       Debugging aid only.  If ProcBlocks or task switches are attempted
;       between a BeginNoBlock/EndNoBlock pair, an internal error will result.

BeginNoBlock macro segreg
    ?NoBlock segreg,<inc>,<BeginNoBlock>
endm

EndNoBlock macro segreg
    ?NoBlock segreg,<dec>,<EndNoBlock>
endm

?NoBlock macro segreg,incdec,beginend
    ifdef MISCSTRICT
        ifndef _cTKNoBlock
            ReferGlobal _cTKNoBlock, byte
        endif
        pushf
        ifnb <segreg>

            ;; inc FLAT:[_cTKNoBlock] generates a FLAT-relative 16 bit
            ;; offset fixup, which the linker rejects.  inc DS:[_cTKNoBlock]
            ;; with DS assumed FLAT by the caller generates a FLAT-relative
            ;; 32 bit fixup, which works ok.

            if2
                ifidn <segreg>,<FLAT>
                    ProcError <beginend: masm will not allow segreg>
                    .err
                endif
            endif
            incdec segreg:[_cTKNoBlock]         ;; inc/dec counter
        elseif (?cstype eq ?CS_32bit)
            incdec [_cTKNoBlock]                ;; inc/dec counter
        else

        ;; The following doesn't work if the chosen segment register is
        ;; already assumed FLAT.  In such a case, masm generates a FLAT
        ;; relative reference with a non-FLAT segment register.  We use
        ;; FS here and disallow the construct in 32 bit code in a feeble
        ;; attempt to avoid using a segment register that is assumed FLAT.

            .386p
            push  fs
            DosContext fs,,noassume
            incdec fs:[_cTKNoBlock]             ;; inc/dec counter
            pop   fs
            CpuMode reset
        endif
        popf
    endif
endm

;***    TaskTime - enforce non-InterruptLevel execution context
;
;       Debugging aid only.  If running at InterruptLevel,
;       an internal error will be generated.

TaskTime macro
    ifdef MODECHECK
        ?GenCall TaskOnly, SN_DosCode
    endif
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
    ?GenCall AssertNoBufProc, SN_DosHigh2Code
endif
endm

;***    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
;               flatname        Name of flat pointer to structure

DefTaskData     macro   name,width,length,value,flatname
    local l

    CONSTANTS segment
            public name
        name  dw  offset DOSGROUP:l
    CONSTANTS ends

    .386p
    _DATA segment
    ifb <flatname>
            public fl_&name
        fl_&name  dd offset FLAT:l
    else
            public flatname
        flatname  dd offset FLAT:l
    endif
    _DATA ends

    LASTDATA segment
        dw  offset DOSGROUP:name        ;; point to pointer to this array
        ifb <flatname>
            dd  offset FLAT:fl_&name    ;; point to flat pointer to this array
        else
            dd  offset FLAT:flatname    ;; point to flat pointer to this array
        endif
        ifidni <width>,<word>
                db  2                   ;; give the width of the item
            l   dw  length dup (value)  ;; reserve space for the item
        elseifidni <width>,<byte>
                db  1
            l   db  length dup (value)
        elseifidni <width>,<dword>
                db  4
            l   dd  length dup (value)
        else
                db  width
            l   db  length dup (width dup (value))
        endif
    LASTDATA ends
    CpuMode reset
endm

;***    CallGate - call API from ring3 code
;
;       This is for system init process or for kernel exitlist or
;       signal dispatch code to get into kernel mode. Kernel entry
;       looks just like an API call.
;
;       ENTRY:  gate = name of api routine
;       (global variables)
;               GDT_&gate = GDT entry defined in api.asm, api2.asm
;
;       EXIT:   a call instruction generated to call the call gate.

CallGate macro  gate
ifndef GDT_&gate
        extrn   GDT_&gate:BYTE
endif
        db      MI_LONG_CALL            ; far ret
        dw      0,GSEL GDT_&gate        ; selector of call gate
endm

;***    StampRPL - Stamps the RPL of the selector parameter with the RPL
;                  of the SS of the caller.
;
;       Entry:  <sel> contains selector to be stamped (not base register)
;               <reg> contains a free index register (if it exists). It is
;               used to access the user Stack Segment, else the bp is used.
;               <curtcbreg> is a register that already contains [CurrTCB].
;
;       Exit:   Stamped selector in <sel> register
;
;       Uses:  <sel,reg>
;

StampRPL macro sel, reg, currtcbreg
        .errnb  <currtcbreg>
        ifb <reg>
                push    bp
                mov     bp,ss:[TKOPTDA].CurrTSD
                mov     bp,ss:[bp].TSDUserSS
                arpl    sel,bp
                pop     bp
        else
                mov     reg,ss:[TKOPTDA].CurrTSD
                mov     reg,ss:[reg].TSDUserSS
                arpl    sel,reg
        endif
endm

;***    JPathChar, JNPathChar
;***    TestPathChar
;
;       Conditional jumps based on whether AL is a path seperator character.
;
;       IMPORTANT NOTE: These macros do NOT account for / as a possible
;               path separator character.  Only \ is tested for.
;
;       JPathChar, JNPathChar - jump if AL contains a path char
;               (JPathChar) or if AL does not contain a path char
;               (JNPathChar).
;
;       TestPathChar - just set Z flag based on whether AL is path char.
;               (no jump is performed).
;
;       ENTRY: AL: character to test
;
;       EXIT :  ZR: character is a path character
;               NZ: character is not a path character
;
;       USES : AL
;


JPathChar MACRO target
        cmp     al,'\'
        je      target
ENDM

JNPathChar MACRO target
        cmp     al,'\'
        jne     target
ENDM

TestPathChar MACRO
        cmp     al,'\'
ENDM
