;	SCCSID = @(#)dosmac.asm	1.1 85/04/10
;	SCCSID = @(#)dosmac.asm	1.1 85/04/10
;
; Macro file for MSDOS.
;

%ifndef __DOSMAC_MAC__
 %assign __DOSMAC_MAC__ 1

TRUE    EQU 0FFFFh
FALSE   EQU 0


; NASM original macros (comment for fixmem.pl)

;SUBTTL BREAK a listing into pages and give new subtitles
;PAGE
%imacro BREAK 0-1+.nolist
	;SUBTTL  subtitle
	;PAGE
%endmacro
;.xcref  break

BREAK   <ASMVAR - handle assembly variables once and for all>

%if 0
AsmVars Macro   varlist
IRP     var,<varlist>
AsmVar  var
ENDM
ENDM

AsmVar  Macro   var
IFNDEF  var
var = FALSE
ENDIF
ENDM
%endif

BREAK <I_NEED: declare a variable external, if necessary, and allocate a size>

;
; declare a variable external and allocate a size
;
;AsmVar  InstalledData
%ifndef InstalledData
 %define InstalledData 0
%endif

%imacro I_NEED 1-2.nolist
%ifn InstalledData
	[section DATA]
    %IFIDNi   %2, WORD
	EXTRN   %1:WORD
    %ELSE
	%IFIDNi   %2, DWORD
	EXTRN   %1:DWORD
	%ELSE
	EXTRN   %1:BYTE
	%ENDIF
    %ENDIF
	__SECT__
%ENDIF
%endmacro
	;.xcref I_need

;
; call a procedure that may be external.  The call will be short.
;
%imacro invoke 1.nolist
;.xcref
	EXTRN   %1:NEAR
;.cref
	CALL    %1
%endmacro
;.xcref  invoke

;PAGE
;
; jump to a label that may be external.  The jump will be near.
;
%imacro transfer 1.nolist
;.xcref
	EXTRN   %1:NEAR
;.cref
	JUMP    %1
%endmacro
;.xcref  transfer

;
; get a short address in a word
;
%imacro short_addr 1.nolist
    %ifnidn %1, ?
	EXTRN   %1:NEAR
	DW  OFFSET %1
    %ELSE
	DW  ?
    %ENDIF
%endmacro
;.xcref  short_addr

;
; get a long address in a dword
;
%imacro long_addr 1.nolist
	EXTRN   %1:NEAR
	DD  %1
%endmacro
;.xcref  long_addr

;
; declare a PROC near or far but PUBLIC nonetheless
;
;.xcref ?frame
;.xcref ?aframe
;.xcref ?stackdepth
;.xcref ?initstack
%assign ?frame           0                     ; initial
%assign ?aframe          0                     ; initial
%assign ?stackdepth      0                     ; initial stack size
%assign ?initstack       0                     ; initial stack size

%imacro procedure 1-2.nolist
	%assign ?frame     0
	%assign ?aframe    2                   ;; remember the pushed BP
	%warning proc %1...
%1    PROC    %2
	PUBLIC  %1
	%assign ?initstack     ?stackdepth     ;; beginning of procedure
%endmacro
;.xcref  procedure

;
; end a procedure and check that stack depth is preserved
;
%imacro EndProc 1-2.nolist
	%ifnidni   %2, NoCheck        ;; check the stack size
	%if ?initstack != ?stackdepth    ;; is it different?
	%warning    ***** Possible stack size error in %1 *****
	%endif
	%endif
%1    ENDP
%endmacro
;.xcref  endproc
;PAGE
;
; define a data item to be public and of an appropriate size/type
;

%imacro stripangles 2.nolist
%defstr %%param %2
%rep 16
 %substr %%opening %%param 1
 %ifidn %%opening, '<'
  %substr %%param %%param 2,-1
 %endif
%endrep
%rep 16
 %strlen %%length %%param
 %substr %%closing %%param %%length
 %ifidn %%closing, '>'
  %substr %%param %%param 1,-2
 %endif
%endrep
%deftok %%token %%param
%1 %%token
%endmacro

%imacro I_AM 2-*.nolist
;I_AM    MACRO   name,size,init
%xdefine %%name %1
;; declare the type of the object
    %IFIDNi %2, WORD
%1    LABEL WORD
	%assign I_AM_SIZE      1
	%assign I_AM_LEN       2
    %ELSE
	%IFIDNi %2, DWORD
%1    LABEL DWORD
	%assign I_AM_SIZE      2
	%assign I_AM_LEN       2
	%ELSE
	    %IFIDNi %2, BYTE
%1    LABEL BYTE
	%assign I_AM_SIZE      1
	%assign I_AM_LEN       1
	    %ELSE
%1    LABEL BYTE
%undef I_AM_SIZE
	stripangles %assign I_AM_SIZE, %2
	%assign I_AM_LEN       1
	    %ENDIF
	%ENDIF
    %ENDIF
;; declare the object public
	PUBLIC  %%name
;; if no initialize then allocate blank storage
    %IF %0 == 2
	DB  I_AM_SIZE*I_AM_LEN DUP (?)
    %ELSE
%ifn InstalledData
 %rotate 2
 %rep %0 - 2
	    %IF I_AM_LEN == 1
		stripangles db, %1
	    %ELSE
		stripangles dw, %1
	    %ENDIF
	    %assign I_AM_SIZE I_AM_SIZE - 1
	    %rotate 1
 %endrep
	%IF I_AM_SIZE != 0
	    %error    ***** initialization of %%name not complete *****
	%ENDIF
%ELSE
	DB  I_AM_SIZE*I_AM_LEN DUP (?)
%ENDIF
    %ENDIF
%endmacro

%imacro I_AM_NOBITS 2-*.nolist
;I_AM    MACRO   name,size,init
%xdefine %%name %1
;; declare the type of the object
    %IFIDNi %2, WORD
%1    LABEL WORD
	%assign I_AM_SIZE      1
	%assign I_AM_LEN       2
    %ELSE
	%IFIDNi %2, DWORD
%1    LABEL DWORD
	%assign I_AM_SIZE      2
	%assign I_AM_LEN       2
	%ELSE
	    %IFIDNi %2, BYTE
%1    LABEL BYTE
	%assign I_AM_SIZE      1
	%assign I_AM_LEN       1
	    %ELSE
%1    LABEL BYTE
%undef I_AM_SIZE
	stripangles %assign I_AM_SIZE, %2
	%assign I_AM_LEN       1
	    %ENDIF
	%ENDIF
    %ENDIF
;; declare the object public
	PUBLIC  %%name
;; if no initialize then allocate blank storage
    %IF %0 == 2
	resb  I_AM_SIZE*I_AM_LEN
    %ELSE
%ifn InstalledData
 %rotate 2
 %rep %0 - 2
	    %IF I_AM_LEN == 1
		resb 1
	    %ELSE
		resw 1
	    %ENDIF
	    %assign I_AM_SIZE I_AM_SIZE - 1
	    %rotate 1
 %endrep
	%IF I_AM_SIZE != 0
	    %error    ***** initialization of %%name not complete *****
	%ENDIF
%ELSE
	resb  I_AM_SIZE*I_AM_LEN
%ENDIF
    %ENDIF
%endmacro

;.xcref  I_AM
;.xcref  I_AM_SIZE
;.xcref  I_AM_LEN
%assign I_AM_SIZE 0
%assign I_AM_LEN  0

;PAGE

;
; define an entry in a procedure
;
%imacro entry 1-2.nolist
%1:
	PUBLIC  %1
%endmacro
;.xcref  entry

BREAK <ERROR - store an error code then jump to a label>

%imacro error 1.nolist
;.xcref
	MOV AL, %1
	transfer    SYS_RET_ERR
;.cref
%endmacro
;.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>
;

%imacro jump 1.nolist
;.xcref

    %ifndef %1_J                       ;; is this the first invocation
%%a:      JMP %1
    %ELSE
	%IF (%1_J >= $) || ($-%1_J > 126)
%%a:      JMP %1                         ;; is the jump too far away?
	%ELSE
%%a:      JMP %1_J                      ;; do the short one...
	%ENDIF
    %ENDIF
    %ixdefine %1_j %%a
;.cref
%endmacro
;.xcref  jump

BREAK <RETURN - return from a function>

%imacro return 0.nolist
%%a:
	RET
%xdefine ret_l %%a
%endmacro
;.xcref  return

BREAK <CONDRET - conditional return>

%imacro condret 2.nolist
%assign %%exit 0
    %ifdef   ret_l                       ;; if ret_l is defined
	%if (($ - ret_l) <= 126) && ($ > ret_l)
					;;     if ret_l is near enough then
	    %%a:  j%1    ret_l           ;;         a: j<CC> to ret_l
	    %xdefine ret_%1 %%a                 ;;         define ret_<CC> to be a:
	    %assign %%exit 1
	%endif
    %endif
    %ifn %%exit
    %ifdef   ret_%1                     ;; if ret_<CC> defined
	%if (($ - ret_%1) <= 126) && ($ > ret_%1)
					;;     if ret_<CC> is near enough
	    %%a:  j%1    ret_%1         ;;         a: j<CC> to ret_<CC>
	    %xdefine ret_%1 %%a                 ;;         define ret_<CC> to be a:
	    %assign %%exit 1
	%endif
    %endif
    %endif
    %ifn %%exit
    j%2   %%a                           ;; j<NCC> a:
    return                              ;; return
    %%a:                                  ;; a:
    %xdefine ret_%1 ret_l                     ;; define ret_<CC> to be ret_l
    %endif
%endmacro
;.xcref  condret

BREAK <RETZ - return if zero, links up shortwise if necessary>

%imacro retz 0.nolist
    condret z,nz
%endmacro
;.xcref  retz

BREAK <RETNZ - return if not zero, links up shortwise if necessary>

%imacro retnz 0.nolist
    condret nz,z
%endmacro
;.xcref  retnz

BREAK <RETC - return if carry set, links up shortwise if necessary>

%imacro retc 0.nolist
    condret c,nc
%endmacro
;.xcref  retc

BREAK <RETNC - return if not carry, links up shortwise if necessary>

%imacro retnc 0.nolist
    condret nc,c
%endmacro
;.xcref  retnc

BREAK <CONTEXT - set the DOS context to a particular register>

%imacro context 1.nolist
	PUSH    SS
	stripangles POP, %1
	; ASSUME  %1:DOSGROUP
%endmacro
;.xcref  context

BREAK <SaveReg - save a set of registers>

%imacro SaveReg 0-*.nolist                 ;; push those registers
 %rep %0
	%assign ?stackdepth ?stackdepth + 1
	stripangles push, %1
	%rotate 1
 %endrep
%endmacro
;.xcref  SaveReg

BREAK <RestoreReg - unsave some registers>

%imacro RestoreReg 0-*.nolist             ;; pop those registers
 %rep %0
	%assign ?stackdepth ?stackdepth - 1
	stripangles pop, %1
	%rotate 1
 %endrep
%endmacro
;.xcref  RestoreReg

BREAK <Critical section macros>

%imacro EnterCrit 1.nolist
	Invoke  E%1
%endmacro

%imacro LeaveCrit 1.nolist
	Invoke  L%1
%endmacro

Break   <message - display a message>

;AsmVars <ShareF,Cargs,Redirector>
%ifndef ShareF
 %idefine ShareF 0
%endif
%ifndef Cargs
 %idefine Cargs 0
%endif
%ifndef Redirector
 %idefine Redirector 0
%endif
%ifndef debug
 %idefine debug 0
%endif

%if debug
%imacro fmt 3-*.nolist
;fmt     MACRO   typ,lev,fmts,args
;local   a,b,c
	PUSHF
%IFNempty %1
	TEST    word [BugTyp],%1
	JZ      %%c
	CMP     word [BugLev],%2
	JB      %%c
%ENDIF
	PUSH    AX
	PUSH    BP
	MOV     BP,SP
%If (! sharef) && (! redirector)
	[section Table]
%%a:       db      %3,0
	__SECT__
	MOV     AX,OFFSET %%a wrt DOSGROUP
%else
	jmp     short %%b
%%a:       db      %3,0
%if sharef
%%b:      mov     ax,offset %%a wrt share
%else
%%b:      mov     ax,offset %%a wrt netwrk
%endif
%endif
	PUSH    AX
%iassign cargs 2
%rotate 3
%rep %0 - 3
 %ifidni ax, %1
	MOV     AX,[BP+2]
 %ELSE
	MOV     AX, %1
 %ENDIF
	PUSH    AX
 %iassign cargs cargs + 2
 %rotate 1
%endrep
	invoke  PFMT
	ADD     SP, Cargs
	POP     BP
	POP     AX
%%c:
	POPF
%endmacro
%else
%imacro fmt 3-*.nolist
%endmacro
%endif

Break   <DOSAssume - validate assumes>

;AsmVar  Debug,$temp

%imacro detectstripangles 4.nolist
%defstr %%param %4
%assign ?%2 0
%assign ?%3 0
%rep 16
 %substr %%opening %%param 1
 %ifidn %%opening, '<'
  %substr %%param %%param 2,-1
  %assign ?%2 ?%2 + 1
 %endif
%endrep
%rep 16
 %strlen %%length %%param
 %substr %%closing %%param %%length
 %ifidn %%closing, '>'
  %substr %%param %%param 1,-2
  %assign ?%3 ?%3 + 1
 %endif
%endrep
%deftok %%token %%param
%define ?%1 %%token
%endmacro


%IF debug
%imacro DOSAssume 3-*.nolist
;DOSAssume   Macro   reg,reglist,message
 %ifidni %1, CS
  %assign %%temp 1
 %else
  %ifidni %1, SS
   %assign %%temp 0
  %else
   %error ***** Invalid DOS register %1 in DOSAssume *****
  %endif
 %endif
 %rotate 1
 %assign %%level 0
 %assign %%amount %0 - 1
 %rep 16
  detectstripangles %%token, %%opening, %%closing, %1
  %assign %%level %%level + ? %+ %%opening
  %assign %%level %%level - ? %+ %%closing
  %ifidni ? %+ %%token, DS
   %assign %%temp %%temp | 2
  %else
   %ifidni ? %+ %%token, ES
    %assign %%temp %%temp | 4
   %else
    %error ***** Invalid register reg in DOSAssume *****
   %endif
  %endif
  %assign %%amount %%amount - 1
  %rotate 1
  %if %%level <= 0
   %exitrep
  %endif
 %endrep

	PUSH    AX
	MOV     AX, %%temp
	PUSH    AX
%IF SHAREF
	MOV     AX,OFFSET %%a
%ELSE
	MOV     AX,OFFSET %%a wrt DOSGroup
%ENDIF
	PUSH    AX
	Invoke  SegCheck
	POP     AX
%IFN SHAREF
	[section Table]
%ELSE
	JMP     SHORT %%b
%ENDIF
%%a:
 %rep %%amount
        DB      %1
  %rotate 1
 %endrep
	db 0
%IFN SHAREF
	__SECT__
%ELSE
%%b:
%ENDIF
;IRP r,<reglist>
;    ASSUME  r:DOSGroup
;ENDM
%endmacro
%ELSE
%imacro DOSAssume 3-*.nolist
;DOSAssume   Macro   reg,reglist,message
;IRP r,<reglist>
;    ASSUME  r:DOSGroup
;ENDM
%endmacro
%ENDIF

BREAK   <ASSERT - make assertions about registers>

%if 0
;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 DOSGroup: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
Table   segment
b   db  Message,0
Table   ends
	    ENDIF
	ENDIF
    ENDIF
ENDM
%ELSE
%imacro Assert 0-*.nolist
%endmacro
%ENDIF

%ifndef Installed
 %idefine Installed 1
%endif

Break   <CallInstall - hook to installable pieces>

%imacro CallInstall 3-*.nolist
;CallInstall MACRO   name,mpx,fn,save,restore
%define %%name %1
%assign %%mpx %2
%assign %%fn %3
%IF Installed
 %rotate 3
 %assign %%level 0
 %assign %%amountsaved 0
 %rep %0 - 3
  %ifnempty %1
   detectstripangles %%token, %%opening, %%closing, %1
   %assign %%level %%level + ? %+ %%opening
   %assign %%level %%level - ? %+ %%closing
   SaveReg ? %+ %%token
  %endif
  %assign %%amountsaved %%amountsaved + 1
  %rotate 1
  %if %%level <= 0
   %exitrep
  %endif
 %endrep
	MOV     AX,(%%mpx << 8) + %%fn
	INT     2Fh
 %assign %%level 0
 %assign %%amountrestored 0
 %rep %0 - 3 - %%amountsaved
  %ifnempty %1
   detectstripangles %%token, %%opening, %%closing, %1
   %assign %%level %%level + ? %+ %%opening
   %assign %%level %%level - ? %+ %%closing
   RestoreReg ? %+ %%token
  %endif
  %assign %%amountrestored %%amountrestored + 1
  %rotate 1
  %if %%level <= 0
   %exitrep
  %endif
 %endrep
 %if %%level > 0 || %0 != 3 + %%amountsaved + %%amountrestored
  %error Wrong amount saved or restored
 %endif
%ELSE
	Invoke  %%name
%ENDIF
%endmacro

Break   <Stack frame manipulators>

%imacro localvar 2.nolist
%ifidni %2, BYTE
 %assign ?frame ?frame + 1
 %assign %%a ?frame
 labelsize %1, byte, bp - %%a
%else
 %ifidni %2, WORD
  %assign ?frame ?frame + 2
  %assign %%a ?frame
  labelsize %1, word, bp - %%a
 %else
  %ifidni %2, DWORD
   %assign ?frame ?frame + 4
   %assign %%a ?frame
   labelsize %1 %+ l, word, bp - %%a
   labelsize %1 %+ h, word, bp - %%a + 2
   labelsize %1, dword, bp - %%a
  %else
   %assign ?frame ?frame + %2
   %assign %%a ?frame
   labelsize %1, byte, bp - %%a
  %endif
 %endif
%endif
%endmacro

%imacro enter 0.nolist
	push    bp
	mov     bp,sp
	sub     sp,?frame
%endmacro

%imacro leave 0.nolist
	mov     sp,bp
	pop     bp
%endmacro


%imacro argvar 2.nolist
%ifidni %2, BYTE
 %assign %%a ?aframe
 %assign ?aframe ?aframe + 1
 labelsize %1, byte, bp + %%a
%else
 %ifidni %2, WORD
  %assign %%a ?aframe
  %assign ?aframe ?aframe + 2
  labelsize %1, word, bp + %%a
 %else
  %ifidni %2, DWORD
   %assign %%a ?aframe
   %assign ?aframe ?aframe + 4
   labelsize %1 %+ l, word, bp + %%a
   labelsize %1 %+ h, word, bp + %%a + 2
   labelsize %1, dword, bp + %%a
  %else
   %assign %%a ?aframe
   %assign ?aframe ?aframe + %2
   labelsize %1, byte, bp + %%a
  %endif
 %endif
%endif
%endmacro

BREAK   <errnz - generate compilation errors>

%imacro errnz 1.nolist
detectstripangles %%token, %%opening, %%closing, %1
%if ? %+ %%token
    %error %1 <> 0
%endif
%endmacro

%endif
