;; --80x86-- MACRO LIBRARY
; (c) François-René "Faré" Rideau Dang-Vu Bân 1989,1994

IF 0 ; COMMENT @@@

To Do:
* Change IF semantics (add a THEN) so the IF appears before the condition
* Add support for the ELSEIF construct (can be done only after the other)
* Add support for AND, OR constructs, (and perhaps even @BRA and @KET for
 parentheses).
* Create EXITSTK and CONTINUESTK parallel to the structure stack for EXIT
 and CONTINUE statements (with condition and levels statements).
* Support CASE OF ENDOF ENDCASE
* Support different type of arguments...
* (extension to the assembler itself) replace all instructions so that we
 trace the use of registers, save those needed, allocate registers needed...
 we obtain an "optimizer".

======================== This Library contains: ==========================
* DEBUG-like shortcuts for WORD PTR,BYTE PTR,OFFSET,SHORT,JUMP SHORT.

* Instruction set enhancement macros:
-ldea		est un lea rapide en mode direct
-clr		efface un registre ou une case mémoire
-Setf		positionne SF,ZF,PF sur un registre, efface CF et OF
-Max		qui affecte à %1 le plus grand de %2 et %3
-Min		qui affecte à %1 le plus petit de %2 et %3

* Macro useful for writing other macros
-IsReg		rend le nombre Answer non nul ssi le paramètre est un registre

* DOS/BIOS calling macros
-fction		initializes ax to call DOS or BIOS functions and subfunctions
-functn		initializes any register in the same way.
-MsDos		calls DOS INT 21h with given function

* Macros for structured programming (Yes ! In assembly !)
- Stack macros:			StkLvl,@PUSH,@POP
- String manipulation macros:	EnlevePremierCaractere,PremierCaractere
- Reverse-conditional jumps:	@NCC,@JN
- Conditional structures:	@IF ... @THEN ... (@ELSE ...) @ENDIF
- structures de boucle:		@REPEAT ... @UNTIL
- structures de boucle:		@WHILE ... @DO ... @WEND
- sortie de structure(s):	@END


* Writing Libraries as Include files:
- defining a macro defining a proc:	LibProc

* Usual data structure:
- ListeChaineeProche

* Console I/O aid:
- Printing a Character:		def_Putc, PUTC
- Printing a string:		def_Puts
- literal unnamed strings:	lsa, PUTS
- Printing hex numbers:		def_prof_PrHex
- 
NOTES:
+ uses _codeseg and _dataseg for user parametrability
+ assumes al for a parameter, calling Putc to echo a character. Have Putc
 callable, or EQU'ed to whatever you want. Same about Getc
+ assumes Putc saves all registers but ax

* Conditionally defined Debugging Aid:
- Echoing routine message:	__
- Echoing variable values:	@by@ @wo@ @dwo@ @_@
- Echoing register values:	@_@ @__@
- Ending Prematurely:		End
NOTES:
+ DEBUG must be defined before Including macro.inc, and non-zero for these
 to work.
+ if you don't understand why your long program does not compile properly,
 try to END the compilation, to see symbol values at some stage of the
 process.

* Macros to export data in GOOSE format:

ENDIF ; @@@ END OF COMMENT @@@

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SHORT CUTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BY	equ	byte ptr
WO	equ	word ptr
DWO	equ	dword ptr
PWO	equ	pword ptr
FWO	equ	fword ptr
ofs	equ	offset
SH	equ	short

Macro	jmps	a,b,c,d,e,f
	jmp	short a b c d e f
EndM	jmps

Macro	calls	a,b,c,d,e,f
	db	66h,0E8h
	dw	offset (a b c d e f)-$-2
EndM	calls

;;;;;;;;;;;;;;;;;;;;;;;; INSTRUCTION SET ENHANCEMENTS ;;;;;;;;;;;;;;;;;;;;;;;;

Macro	ldea	reg,lbl		;lea sur 3 octets au lieu de 4
	mov     &reg,offset lbl
EndM	ldea
Macro	clr	op		;efface un registre, ou une case mémoire
	IsReg	op
	if	Answer
	  xor	op,op
	else
	  mov	op,0
	endif
EndM	clr
Macro	Setf	op		;positionne ZF,PF,SF sur reg
	IsReg	op
	if	Answer
	or	op,op
	else
	test	op,-1
	endif
EndM	Setf
Macro	FlushInstr
	local	fi
	jmp short fi
	fi:
EndM	FlushInstr
; Pushm & Popm stolen from:
;   LOADLIN v1.4 (C) 1994 Hans Lermen (lermen@elserv.ffm.fgan.de)
Macro	Pushm	r1,r2,r3,r4,r5,r6,r7,r8,rx
	irp	parm,<&r1,&r2,&r3,&r4,&r5,&r6,&r7,&r8,&rx>
	  ifb <parm>
            exitm
          elseifidni <parm>,<ad>
	    pushad
	  elseifidni <parm>,<a>
	    pusha
	  else
	    push parm
	  endif
	endm
EndM	Pushm
Macro	Popm	r1,r2,r3,r4,r5,r6,r7,r8,rx
	irp	parm,<&rx,&r8,&r7,&r6,&r5,&r4,&r3,&r2,&r1>
	  ifidni <parm>,<ad>
	    popad
	  elseifidni <parm>,<a>
	    popa
	  elseifnb <parm>
	    pop parm
	  endif
	endm
EndM	Popm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MACRO WRITING AID ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Macro	Max	dest,orig1,orig2
	if	orig1 gt orig2
	dest  =	orig1
	else
	dest  =	orig2
	endif
EndM

Macro	Min	dest,orig1,orig2
	if	orig2 gt orig1
	dest  =	orig1
	else
	dest  =	orig2
	endif
EndM

Macro	IsReg	param
  Answer = 0
  irp	extens,<>,<e>
    irpc	genreg,<abcd>
      irpc	suffix,<hlx>
	ifidni	extens&genreg&suffix,param
	  Answer = 1
	endif
      endm
    endm
    irp	indexreg,<si>,<di>,<bp>,<sp>
      ifidni	extens&indexreg,param
	Answer = 1
      endif
    endm
  endm
EndM

Macro	AlignP	paraf,offset,fill
	ifb <fill>
	fill equ 90h
	endif
	shift =	($-0&offset) mod paraf
	if	shift
	db	(paraf-shift) dup(90h)	;NOP's
	endif
EndM	AlignP
;;; PS:(!) This was written for MASM 1.0;
;;; most of AlignP functionality already exists in MASM 5.0 under the name
;;; ALIGN (also original name for AlignP).
;Purge AlignP

;;;;;;;;;;;;;;;;;;;;;;;;;;; BIOS/DOS CALL MACROS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Macro	MsDos	func,subfc
	fction	func,subfc
	int	21h
	EndM
Macro	Bios	func,subfc
	fction	func,subfc
	int	10h
	EndM
Macro	fction	fnctn,sbfnc
	functn	a,fnctn,sbfnc
	EndM
Macro	functn	genreg,funct,sbfct
	ifnb	<&funct>
	ifnb	<&sbfct>
	mov	genreg&x,funct*100h+sbfct
	else
	mov	genreg&H,funct
	endif
	else
	ifnb	<&sbfct>
	mov	genreg&L,sbfct
	endif
	endif
	EndM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; USEFUL CONSTANTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ASCII codes
LF	=	0Ah
CR	=	0Dh
Escape	=	1Bh
; Keyboard scancodes
K_Home	=	47h
K_Up	=	48h
K_PgUp	=	49h
K_Lft	=	4Bh
K_Rgt	=	4Dh
K_EndC	=	4Fh
K_Dn	=	50h
K_PgDn	=	51h
K_InsC	=	52h
K_Del	=	53h


;;;;;;;;;;;;;;;; STRUCTURED PROGRAMMING: MACROPROCESSING STACKS ;;;;;;;;;;;;;;;
STRUCSTKPtr = 0

Macro	StkLvl	Var,Stk,Lvl
   Var	CATSTR	<Stk>,%(Lvl)
EndM	StkLvl
Macro	@PUSH	Stk,Val
   LOCAL	StkPtr,StkLvl
   StkPtr	CATSTR	<Stk>,<Ptr>
   % StkPtr	= StkPtr + 1
   StkLvl	CATSTR	<Stk>,%(StkPtr)
   % StkLvl	EQU     Val
EndM
Macro	@POP	Dest,Stk
   LOCAL	StkPtr,StkLvl,Val
   StkPtr	CATSTR	<Stk>,<Ptr>
   ERRIFE       (&StkPtr)
   StkLvl	CATSTR	<Stk>,%(StkPtr)
   Val		EQU	<Dest>
   % Val	EQU	% StkLvl
   % StkPtr	= StkPtr - 1
EndM

;;;;;;;;;;  Programmation structurée : manipulation de chaines de caractères

Macro   EnlevePremierCaractere      STR0,DEST
   LOCAL  COUNT
   COUNT = 0
   DEST EQU <>
   IRPC C,STR0
   IF COUNT
   DEST CATSTR DEST,<C>
   ENDIF
   COUNT=1
   ENDM
ENDM
;;; Was found equivalent to % DEST SUBSTR <STR0>,2

Macro   PremierCaractere      STR0,DEST
   IRPC C,STR0
   DEST EQU C
   EXITM
   ENDM
ENDM
;;; Was found equivalent to % DEST SUBSTR <STR0>,1,1

;;;;;;;;;;  Programmation structurée : négation de code condition

Macro	@NCC	cc,NCC
  LOCAL Found,CC0
  Found = 0
  PremierCaractere   cc,CC0
  % IFidni    <CC0>,<p>
    % IFidni  <cc>,<po>
      &NCC EQU <pe>
      Found=1
    % ELSE
      % IFidni  <cc>,<pe>
	&NCC EQU <po>
	Found=1
      % ENDIF
    % ENDIF
  % ENDIF
  IF Found
    EXITM
  ENDIF
  % IFidni    <CC0>,<n>
  EnlevePremierCaractere cc,NCC
  % ELSE
  % NCC EQU <n&cc>
  % ENDIF
EndM


Macro   @JN     cc,Lbl,a1,a2,a3,a4,a5,a6
	LOCAL   NCC,JNCC
	@NCC    cc,NCC
	JNCC    CATSTR  <j>,NCC
	% JNCC    Lbl a1 a2 a3 a4 a5 a6
ENDM

;;;;;;;;;;  Programmation structurée : IF ... ( ELSE ...) ENDIF

IFNUM = 0
@EXIT@IF	EQU  @ELSE
@EXIT@ELSE	EQU  @ELSE

Macro   @IF     cc
	IFNUM = IFNUM + 1
	@PUSH   STRUCSTK,    %(IFNUM)
	@PUSH	STRUCSTK,    cc
	@PUSH	STRUCSTK,    <@IF>
ENDM

Macro   @THEN
	Local	IFNUM,ELSENUM,IFLBL,StrucName,cc
	@POP	StrucName,STRUCSTK
	IFIDN	StrucName,<@IF>
	  @POP	cc,STRUCSTK
	  @POP	IFNUM,STRUCSTK
	  STRUCSTKPtr = STRUCSTKPtr + 1	;;; Rempiler IFNUM
	  @PUSH	STRUCSTK,<@THEN>
	  LBL	CatStr       <@ELSE>,%(IFNUM)
	  % @JN   cc, LBL ;; jmp short ?
	ELSEIFIDN StrucName,<@ELSEIF>
	ELSE
	  ERR @THEN DOES NOT MATCH @IF,@ELSE, OR @ELSEIF
	ENDIF
ENDM

Macro	@ELSE
	Local	IFNUM,ENDIFLBL,ELSELBL,StrucName
	@POP	StrucName,STRUCSTK
	ERRIFDIF	StrucName,<@THEN>
	@POP	IFNUM,STRUCSTK
	STRUCSTKPtr = STRUCSTKPtr + 1	;;; Rempiler ifnum
	@PUSH	STRUCSTK,<@ELSE>
	ELSELBL   CATSTR <@ELSE>,%(IFNUM)
	ENDIFLBL  CATSTR <@ENDIF>,%(IFNUM)
	% jmp	short ENDIFLBL
	% ELSELBL :
ENDM

Macro	@ELSEIF	cc
	Local	IFNUM,ELSENUM,ENDIFLBL,ELSELBL,StrucName
	@POP	StrucName,STRUCSTK
	ERRIFDIF	StrucName,<@THEN>
	@POP	IFNUM,STRUCSTK
	STRUCSTKPtr = STRUCSTKPtr + 1	;;; Rempiler ifnum
	@PUSH	STRUCSTK,<@ELSE>
	ELSELBL   CATSTR <@ELSE>,%(IFNUM)
	ENDIFLBL  CATSTR <@ENDIF>,%(IFNUM)
	% jmp	short ENDIFLBL
	% ELSELBL :
ENDM

Macro	@ENDIF	jmp, a,b,c,d,e,f,g,h,i,j,k,l	;;; End label may be back...
	Local	IFNUM,LBL,StrucName,ok
	@POP	STRUCname,STRUCSTK
	@POP	IFNUM,STRUCSTK
	IFIDN	STRUCname,<@THEN>
	  LBL   CATSTR <@ELSE>,IFNUM
	  % LBL :
	  ok EQU YES
	ELSEIFIDN STRUCname,<@ELSE>
	  LBL   CATSTR <@ENDIF>,IFNUM
	  % LBL :
	ELSEIFIDN STRUCname,<@ELSEIF>
	  ERR @ELSEIF NOT SUPPORTED YET.
	ELSE
	  ERR @ENDIF DOES NOT MATCH THEN, ELSE OR ELSEIF.
	ENDIF
ENDM

;;;;;;;;;;  Programmation structurée : REPEAT ... UNTIL
REPEATNUM = 0
@EXIT@REPEAT	EQU  @UNTIL

Macro   @REPEAT
	Local	LBL
	@PUSH	STRUCSTK,    %(REPEATNUM)
	@PUSH	STRUCSTK,    <@REPEAT>
	LBL	CatStr       <@REPEAT>,%(REPEATNUM)
	% LBL :
	REPEAT = REPEATNUM + 1
ENDM

Macro	@UNTIL	cc
	Local	REPEATNUM,LBL,StrucName
	@POP	StrucName,STRUCSTK
	ERRIFDIF	StrucName,<@REPEAT>
	@POP	REPEATNUM,STRUCSTK
	LBL	CATSTR <@REPEAT>,%(REPEATNUM)
	@JN	cc,< short LBL >
	LBL	CATSTR <@UNTIL>,%(REPEATNUM)
	% LBL :
ENDM

;;;;;;;;;;  Programmation structurée : WHILE ... DO ... WEND
WHILENUM = 0
@EXIT@WHILE	EQU  @WEND
@EXIT@DO	EQU  @WEND

Macro	@WHILE	cc
	Local	LBL
	@PUSH	STRUCSTK,    %(WHILENUM)
	@PUSH	STRUCSTK,    cc
	@PUSH	STRUCSTK,    <@WHILE>
	LBL	CatStr       <@WHILE>,%(WHILENUM)
	% LBL :
	WHILENUM = WHILENUM + 1
ENDM	@WHILE

Macro	@DO
	Local	WHILENUM,LBL,StrucName,cc
	@POP	StrucName,STRUCSTK
	ERRIFDIF	StrucName,<@WHILE>
	@POP	cc,STRUCSTK
	@POP	WHILENUM,STRUCSTK
	@PUSH	STRUCSTK,%(WHILENUM)
	@PUSH	STRUCSTK,<@DO>
	LBL	CATSTR <@WEND>,%(WHILENUM)
	% @JN	cc,short LBL
ENDM

Macro	@WEND
	Local	WHILENUM,LBL,StrucName
	@POP	StrucName,STRUCSTK
	ERRIFDIF	StrucName,<@DO>
	@POP	WHILENUM,STRUCSTK
	LBL	CATSTR <@WHILE>,%(WHILENUM)
	% jmp	LBL
	LBL	CATSTR <@WEND>,%(WHILENUM)
	% LBL :
ENDM

;;;;;;;;;;  Programmation structurée : FOR ... NEXT (STEP)
IFDEF __FOR__

FORNUM = 0
@EXIT@FOR	EQU  @XFOR
@EXIT@LOOP	EQU  @XFOR

Macro	@FOR	I,Egal,I0,UpOrDownTo,I1,signed,NoPreTest
	Local	LBL,XLBL,cc
	% mov	I,I0
    ;;; trouver la condition
	% ifidni <UpOrDownTo>,<TO>
	 ifdef signed
	  cc EQU le
	 else
	  cc EQU be
	 endif
	% else
	 % errifdifi <UpOrDownTo>,<DOWNTO>
	  ifdef signed
	   cc EQU ge
	  else
	   cc EQU ae
	   % ifidni <I>,<CX>
	    if	I1 EQ 1
	      @PUSH	STRUCSTK,%(FORNUM)
	      @PUSH	STRUCSTK,<@LOOP>
	      ifndef	NoPreTest
		jcxz	XLBL
	      endif
	    endif
	   % endif
	  endif
	% endif
    ;;; Définitions
	LBL	CatStr	<@FOR>,%(FORNUM)
	XLBL	CatStr	<@XFOR>,%(FORNUM)
	@PUSH	STRUCSTK,I1
	@PUSH	STRUCSTK,I
	@PUSH	STRUCSTK,cc
	@PUSH	STRUCSTK,%(FORNUM)
	@PUSH	STRUCSTK,<@FOR>
    ;;; faire une boucle while !!!
	ifndef	NoPreTest
	  % cmp	I,I1
	  % @JN	cc,short XLBL
	endif
	% LBL :
	FORNUM = FORNUM + 1
ENDM

Macro	@NEXT
	Local	FORNUM,LBL,XLBL,cc,StructName,I1,I,I2
	@POP	StructName,STRUCSTK
	@POP	FORNUM,STRUCSTK
	LBL	CATSTR <@FOR>,%(FORNUM)
	XLBL	CATSTR <@XFOR>,%(FORNUM)
   ;;; Vérifier qu'il y a bien une boucle FOR
	% ifdif <StructName>,<@FOR>
	% errifdif <StructName>,<@LOOP>
	 Loop	LBL
	 % XLBL :
	 ExitM
	% endif
	@POP	cc,STRUCSTK
	@POP	I,STRUCSTK
	@POP	I1,STRUCSTK
	% ifidn	<cc>,<le>
	dec	I
	I2 = I1-1
	% endif
	% ifidn	<cc>,<be>
	dec	I
	I2 = I1-1
	% endif
	% ifidn	<cc>,<ge>
	inc	I
	I2 = I1+1
	% endif
	% ifidn	<cc>,<ae>
	inc	I
	I2 = I1+1
	% endif
	if i2
	% cmp	I,I1
	% jz	short LBL
	endif
	% XLBL :
EndM
ENDIF

;;;;;;;;;;  Programmation structurée : EXIT
Macro	@EXIT	n,cc
	Local	REPET,NUM,LBL,StructName,Val
	% ifb	<cc>
	cc equ mp
	% endif
	NUM = STRUCSTKPtr
	Val EQU <StructName>
	% ifdef n
	% rept	n-1
	StkLvl	LBL,STRUCSTK,NUM
	% Val EQU LBL
	% ifdif <StructName>,<@FOR>
	NUM = NUM - 3
	% endif
	NUM = NUM - 2
	% endm
	% endif
	% LBL	CATSTR <@EXIT>,<StructName>
	LBL	CATSTR LBL,%(NUM)
	% j&cc	short LBL
EndM


;;;;;;;;;;;;;;;;;;;;;; Writing Libraries as Include files ;;;;;;;;;;;;;;;;;;;

; This is object managing at source code level. Reduces the number of
; object files (one source for any model), allows inlining, etc.


; To Do: use the STRUCSTK for this
; use markers to see if a particular procedure was already defined

; defining a macro defining a proc
Macro LibProc ProcName,MacroParams,Locals
	local MacroName
	MacroName CATSTR <def_>,ProcName
	% Macro <MacroName> <MacroParams>
	% local <Locals>
	% ifdifi <distance>,<inline>
	%   proc TestCPU distance
	% endif
EndM  LibProc

Macro EndLib whattoend
	% ifdifi <distance>,<inline>
	%    RET
    	%  EndP  TestCPU
	% endif
	% EndM <whattoend>
EndM  EndLib





;;;;;;;;;;;;;;;;;;;; Structures classiques de donnees ;;;;;;;;;;;;;;;;;;;;;;;

Struc	ListeChaineeProche
   Precedent	dd	?
   Suivant	dd	?
EndS

;;;;;;;;;;;;;;;;;;;;;;;; Exportation de donnees ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Macro	Export	Identifier , Type , Name
  Local StringAddress, EName
  % ifnb <Name>
      EName equ Name
  % else
      EName equ Identifier
  % endif
  Segment HeaderStrings
    StringAddress:
    %	db	!'<Name>!' , 0
  EndS
  Segment HeaderBinary
	dd	StringAddress, ID, Type
  EndS
EndM


;;;;;;;;;;;;;;;;;;;;;;;;;;;; Changing Segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Macro	Seg_Data
   ifdef @Model
	dataseg
   else
	Segment _Data_seg
   endif
EndM	Seg_Data
Macro	End_Data
   ifdef @Model
	codeseg	;???
   else
	EndS _Data_seg
   endif
EndM	End_Data
Macro	_Data_is dseg
   _Data_seg equ dseg
EndM	_Data_is

Macro	Seg_UData
   ifdef @Model
	udataseg
   else
	Segment _UData_seg
	;if1
   endif
EndM	Seg_UData
Macro	End_UData
   ifdef @Model
	codeseg	;?
   else
	;endif
	EndS _UData_seg
   endif
EndM	End_UData
Macro	_UData_is dseg
   _UData_seg equ dseg
EndM	_UData_is

Macro	Seg_Code
   ifdef @Model
	codeseg
   else
	Segment _Code_seg
   endif
EndM	Seg_Code
Macro	End_Code
   ifdef @Model
	dataseg
   else
	EndS _Code_seg
   endif
EndM	End_Code
Macro	_Code_is cseg
   _Code_seg equ cseg
EndM	_Code_is

;;;;;;;;;;;;;;;;;;;;;;;;; Going around the linker ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Macro	AddN	s1,s2,n,dest
 dest	=	s1
 	rept	n
 dest	=	dest + s2
 	endm
EndM	Add


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CONSOLE I/O ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Macro	PUTC	char
	ifnb	<char>
	  mov	al,char
	endif
	call	Putc
EndM	PUTC
Macro	GETC	mesg,savereg
	ifnb	<mesg>
	  ifnb	<savereg>
	    pusha
	  endif
	  PUTS	<mesg>
	  ifnb	<savereg>
	    popa
	  endif
	endif
	call	cGetc
EndM	GETC

Macro	lsa	dest,Message,terminator	; lsa means "Load String Address"
	local	msg,term
	ifnb <terminator>
	  term equ terminator
	else
	  term equ 0
	endif
	  Seg_Data
	   msg	db	Message
	     ifdifi <term>,<none>
		db	term
	     endif
	  End_Data
	  mov dest, offset msg
EndM	lsa

Macro	PUTS	Message
	ifidn <Message>,<CRLF>
	  call PrCRLF
	  exitm
	endif
	ifnb	<Message>
          lsa  si,<Message>
	endif
	call Puts
EndM	PUTS
Macro @PrByte mes1,val,mes2
	pusha
	ifdifi <val>,<al>
	  mov al,val
	endif
	ifnb <mes1>
	  push ax
	  PUTS <mes1>
	  pop ax
	endif
	call PutHexByte
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrByte
Macro @PrWord mes1,val,mes2
	pusha
	ifnb <mes1>
	  push val
	  PUTS <mes1>
	  pop ax
	elseifdifi <val>,<ax>
	  mov ax,val
	endif
	call PutHexWord
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrWord
Macro @PrAddr mes1,seg,ofs,mes2
	pusha
	push ofs
	push seg
	ifnb <mes1>
	  PUTS <mes1>
	endif
	pop es
	pop di
	call PutAddr
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrAddr
Macro @PrDword mes1,val,mes2
	pusha
	ifnb <mes1>
	  push val
	  PUTS <mes1>
	  pop eax
	elseifdifi <val>,<eax>
	  mov eax,val
	endif
	call PutHexDword
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrDword
Macro @PrZone mes1,seg,addr,len,mes2
	pusha
	ifdifi <seg>,<es>
	  push es
	endif
	ifnb <mes1>
	  Pushm <seg>,<addr>,<len>
	  PUTS <mes1>
	  Popm es,di,cx
	else
	  ifdifi <seg>,<es>
	    push seg
	    pop es
	  endif
	  ifdifi <addr>,<di>
	    mov di,addr
	  endif
	  ifdifi <len>,<cx>
	    mov cx,len
	  endif
	endif
	call PutZone
	ifdifi <seg>,<es>
	  pop es
	endif
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrZone
Macro @PrWZone mes1,seg,addr,len,mes2
	pusha
	ifdifi <seg>,<es>
	  push es
	endif
	ifnb <mes1>
	  Pushm <seg>,<addr>,<len>
	  PUTS <mes1>
	  Popm es,di,cx
	else
	  ifdifi <seg>,<es>
	    push seg
	    pop es
	  endif
	  ifdifi <addr>,<di>
	    mov di,addr
	  endif
	  ifdifi <len>,<cx>
	    mov cx,len
	  endif
	endif
	call PutWZone
	ifdifi <seg>,<es>
	  pop es
	endif
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrWZone
Macro @PrDZone mes1,seg,addr,len,mes2
	pusha
	ifdifi <seg>,<es>
	  push es
	endif
	ifnb <mes1>
	  Pushm <seg>,<addr>,<len>
	  PUTS <mes1>
	  Popm es,di,cx
	else
	  ifdifi <seg>,<es>
	    push seg
	    pop es
	  endif
	  ifdifi <addr>,<di>
	    mov di,addr
	  endif
	  ifdifi <len>,<cx>
	    mov cx,len
	  endif
	endif
	call PutDZone
	ifdifi <seg>,<es>
	  pop es
	endif
	ifnb <mes2>
	  PUTS <mes2>
	endif
	popa
EndM @PrDZone


;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DEBUGGING AID ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DEBUG=0

Macro %%OUT a,b,c,d,e,f,g,h,i,j,k,l
  MASM
  %OUT a b c d e f g h i j k l ;
  IDEAL
EndM

Macro @OUT@ a,b,c,d,e,f,g,h,i,j,k,l
  IF DEBUG
    %%OUT a b c d e f g h i j k l
  ENDIF
EndM
; Macro @__ ; switch in Debug Mode
; EndM
; Macro __@
; EndM
 Macro __ Message
  IF DEBUG
    pusha
    PUTS <Message>
    popa
  ENDIF
 EndM __
 Macro @_@ mesg,stop
  IF DEBUG
    ifnb <mesg>
      pusha
      PUTS <mesg>
      popa
    endif
    call DumpRegs
    ifnb <stop>
      pusha
      GETC
      popa
    endif
  ENDIF
 EndM @_@
 Macro @__@ mesg,stop
  IF DEBUG
    ifnb <mesg>
      pusha
      PUTS <mesg>
      popa
    endif
    call DumpERegs
    ifnb <stop>
      pusha
      GETC
      popa
    endif
  ENDIF
 EndM @__@
 Macro @by@ m1,v,m2
  IF DEBUG
   @PrByte <m1>,<v>,<m2>
  ENDIF
 EndM @by@
 Macro @wo@ m1,v,m2
  IF DEBUG
   @PrWord <m1>,<v>,<m2>
  ENDIF
 EndM @wo@
 Macro @addr@ m1,s,o,m2
  IF DEBUG
   @PrAddr <m1>,<s>,<o>,<m2>
  ENDIF
 EndM @addr@
 Macro @dwo@ m1,v,m2
  IF DEBUG
   @PrDword <m1>,<v>,<m2>
  ENDIF
 EndM @dwo@
 Macro @Zone@ m1,s,a,l,m2
  IF DEBUG
   @PrZone <m1>,<s>,<a>,<l>,<m2>
  ENDIF
 EndM @Zone@
 Macro @WZone@ m1,s,a,l,m2
  IF DEBUG
   @PrWZone <m1>,<s>,<a>,<l>,<m2>
  ENDIF
 EndM @WZone@
 Macro @DZone@ m1,s,a,l,m2
  IF DEBUG
   @PrDZone <m1>,<s>,<a>,<l>,<m2>
  ENDIF
 EndM @DZone@
; Macro @__
;   MASM ; could not return to IDEAL mode :-(
;   COMMENT __@
;   ;if 0 ; Once was if 0, but __@ was never expanded as endif !!!
; EndM @__
; Macro __@
;   ; endif  ;; Would never get expanded !
;   ;% endif
;   IDEAL
; EndM __@

;%%OUT End of MACRO.INC !

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Heavy debugging ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Macro STEP n
IF __DEBUG__
	P386
	push n
	pop gs
ENDIF
EndM  STEP
Macro STOP base,x,y,c,a
IF __DEBUG__
	P386
	mov [word base 2*x+2*80*y],c+256*a
ENDIF
EndM  STOP
Macro SST mesg
IF __DEBUG__
	GETC <mesg>
ENDIF
EndM  STOP

CRLF	equ	<13,10>
