; Gr Services, assembler source,
; Copr. 1994,1996 Matthias K”ppe

	; SegToLinear creates a linear video address out of a
	; pseudo segment, using Granularity and MapFlags.
	;
	; in:	ax	pseudo segment
	; out:	dx:ax	linear video address

Def_SegToLinear	MACRO
SegToLinear	PROC NEAR
		cmp	MapFlags, 0
		jz	SHORT @@1
		and	ah, 0fh			;; compatibility mode
@@1:		mov	cl, Granularity
		mov	dx, ax
		shl	ax, cl
		dec	cl
		xor	cl, 0fh
		shr	dx, cl
		retn
SegToLinear	ENDP
		ENDM

	; GetPageSeg returns the pseudo segment of the specified
	; screen page, or of the current page.
	;
	; in:	bx      page
	; out:	ax	pseudo segment

Def_GetPageSeg	MACRO
GetPageSeg	PROC NEAR
		or	bx, bx
		mov     ax, Page0Seg
		jz      SHORT @@1
		dec	bx
		mov     ax, Page1Seg
		jz      SHORT @@1
		mov     ax, ActiveSeg
@@1:		retn
GetPageSeg	ENDP
		ENDM

;
; Procedure definitions for video memory mapping support ####################
;

Def_Mapping	MACRO

	;; Map to linear address proc
	;;
	;; In:  dx:ax	linear video address
	;;	ds	the data segment
	;; Out: dx:ax	memory address
	;;	registers saved

MapLinear	PROC NEAR
		push	es bx cx si di		;; calling an unnice proc
		push	ax
		mov	cl, MapGranRight
		shr	ax, cl
		mov	cl, MapGranLeft
		shl	dx, cl
		or	dx, ax
	cli
		cmp	dx, WindowNum
		je	SHORT @@1

		mov	WindowNum, dx		;; store addr in gran units
		mov	bx, 0			;; Do mapping / Window A
		call	MapAddrProc
@@1:
	sti
		pop	ax			;; get offset
		and	ax, MapGranMask
		mov	dx, WORD PTR WindowAddr[2]
		pop	di si cx bx es
		retn
MapLinear	ENDP

	;; Map to next window
	;;
	;; Out:	dx	new window number

NextWindow	PROC NEAR
		mov	dx, WindowNum
		add	dx, GransPerWindow
		jmp	SHORT CallMapAddr
NextWindow	ENDP

	;; Map to previous window
	;;
	;; Out:	dx	new window number

PrevWindow	PROC NEAR
		mov	dx, WindowNum
		sub	dx, GransPerWindow
PrevWindow	ENDP

	;; Map to a window number
	;;
	;; In:	dx	new window number
	;; Out:	dx	new window number

CallMapAddr	PROC NEAR
		push	es ax bx cx dx si di
	cli
		mov	bx, 0
		mov	WindowNum, dx
		call	MapAddrProc
	sti
		pop	di si dx cx bx ax es
		retn
CallMapAddr	ENDP

ENDM    ; Def_Mapping #######################################################

;
; Procedure definitions for 32-bit video memory mapping support #############
;

Def_Mapping32	MACRO

	;; Map to linear address proc, 32-bit version
	;;
	;; In:  eax	linear video address
	;;	ds	the data segment
	;; Out: dx:eax	memory address
	;;	edx	high word destroyed

MapLinear32	PROC NEAR
		push	ecx
		mov	cl, MapGran32
		xor	dx, dx
		shld	edx, eax, cl
	cli
		cmp	dx, WindowNum
		je	SHORT @@1

		mov	WindowNum, dx		;; store addr in gran units
		mov	bx, 0			;; Do mapping / Window A
		push	es ebx eax esi edi
		call	MapAddrProc
		pop	edi esi eax ebx es
@@1:
	sti
		and	eax, MapGranMask32
		mov	dx, WORD PTR WindowAddr[2]
		pop	ecx
		retn
MapLinear32	ENDP

	;; Map to next window
	;;
	;; Out:	dx	new window number

NextWindow32	PROC NEAR
		mov	dx, WindowNum
		add	dx, GransPerWindow
		jmp	SHORT CallMapAddr32
NextWindow32	ENDP

	;; Map to previous window
	;;
	;; Out:	dx	new window number

PrevWindow32	PROC NEAR
		mov	dx, WindowNum
		sub	dx, GransPerWindow
PrevWindow32	ENDP

	;; Map to a window number
	;;
	;; In:	dx	new window number
	;; Out:	dx	new window number

CallMapAddr32	PROC NEAR
		push	es eax ebx ecx edx esi edi
	cli
		mov	bx, 0
		mov	WindowNum, dx
		call	MapAddrProc
	sti
		pop	edi esi edx ecx ebx eax es
		retn
CallMapAddr32	ENDP

ENDM    ; Def_Mapping32 #####################################################

;
; Procedure definitions for mapping syncronization support
;

Def_MapSync	MACRO  ;; ###################################################

	;; Get current window number
	;;
	;; In:	none
	;; Out: dx	current window number

GetWindowNum	PROC NEAR
		push	es ax bx cx si di
		mov	bx, 0100h
		call	MapAddrProc
		pop	di si cx bx ax es
		retn
GetWindowNum	ENDP

	; Sets Gr's WindowNum variable to the right value.
	;
	; In:	ds	set to DSeg, or use the arguments below
	;
	; Arg:	DSeg	the main data segment, if to be set
	;	DestSeg	the current data segment, if to be reset

SyncMapping	MACRO	DSeg, DestSeg
	IFNB <DSeg>
		mov	ds, DSeg		;; on proc's frame
	ENDIF
		push	dx
		call	GetWindowNum
		mov	WindowNum, dx
		pop	dx
	IFNB <DestSeg>
		mov	ds, DestSeg		;; on proc's frame
	ENDIF
		ENDM

ENDM

;
; Useful macros for mapping support, 16-bit versions
;

	; MapNext maps to the next window if necessary
	;
	; In:	flags	set by a small add operation (< WindowSize)
	;
	; Arg:	Reg	the offset value (to be modified)
	;	WinSize	WindowSize
	;	SetDS	non-empty if DS is to be set

MapNext		MACRO	Reg, WinSize, SetDS
		LOCAL	@@0, @@1
		jna	SHORT @@1	;; CF = 1 or ZF = 1 (word wrap around)
		cmp	WinSize, 0
		jz	SHORT @@0
		cmp	Reg, WinSize
		jb	SHORT @@0
@@1:		push	dx
	IFNB <SetDS>
		push	ds
		mov	dx, SEG DATA
		mov	ds, dx
	ENDIF
		call	NextWindow
		and	Reg, OffsetMask
	IFNB <SetDS>
		pop	ds
	ENDIF
		pop	dx
@@0:		ENDM

	; MapPrev maps to the previous window if necessary
	;
	; In:	flags	set by a small subtract operation (< WindowSize)
	;
	; Arg:	Reg	the offset value (to be modified)
	;	WinSize	WindowSize (a register or value in DSeg, or SSeg)
	;	SetDS	non-empty if DS is to be set

MapPrev		MACRO	Reg, WinSize, SetDS
		LOCAL	@@0
		jnb	SHORT @@0		;; no word wrap around
		push	dx
	IFNB <SetDS>
		push	ds
		mov	dx, SEG DATA
		mov	ds, dx
	ENDIF
		call	PrevWindow
		and	Reg, OffsetMask
	IFNB <SetDS>
		pop	ds
	ENDIF
		pop	dx
@@0:		ENDM

;
; Useful macros for mapping support, 32-bit versions
;
	; MapNext maps to the next window if necessary
	;
	; In:	flags	set by a small add operation (< WindowSize)
	;
	; Arg:	Reg	the 32-bit offset value (to be modified)
	;	WinSize	WindowSize32
	;	SetDS	non-empty if DS is to be set

MapNext32	MACRO	Reg, WinSize, SetDS
		LOCAL	@@0, @@1

	PUSHSTATE
	MASM

		cmp	Reg, WinSize
		jb	SHORT @@0

		push	dx
	IFNB <SetDS>
		push	ds
		mov	dx, SEG DATA
		mov	ds, dx
	ENDIF
		call	NextWindow32
		and	Reg, OffsetMask32
	IFNB <SetDS>
		pop	ds
	ENDIF
		pop	dx
@@0:
	POPSTATE

		ENDM

	; MapPrev maps to the previous window if necessary
	;
	; In:	flags	set by a small subtract operation (< WindowSize)
	;
	; Arg:	Reg	the 32-bit offset value (to be modified)
	;	WinSize	WindowSize32 (a register or value in DSeg, or SSeg)
	;	SetDS	non-empty if DS is to be set

MapPrev32	MACRO	Reg, WinSize, SetDS
		LOCAL	@@0

	PUSHSTATE
	MASM

		jnb	SHORT @@0		;; no word wrap around

		push	dx
	IFNB <SetDS>
		push	ds
		mov	dx, SEG DATA
		mov	ds, dx
	ENDIF
		call	PrevWindow32
		and	Reg, OffsetMask32
	IFNB <SetDS>
		pop	ds
	ENDIF
		pop	dx
@@0:

	POPSTATE

		ENDM

	; Pushes the adaptor memory mapping to the stack.
	;
	; In:	ds	set to DSeg, or use the arguments below
	;
	; Arg:	DSeg	the main data segment, if to be set
	;	DestSeg	the current data segment, if to be reset

PushMapping	MACRO	DSeg, DestSeg
	IFNB <DSeg>
		mov	ds, DSeg		;; on proc's frame
	ENDIF
		push	WindowNum
	IFNB <DestSeg>
		mov	ds, DestSeg		;; on proc's frame
	ENDIF
		ENDM

	; Pops the adaptor memory mapping from the stack.
	;
	; In:	ds	set to DSeg, or use the arguments below
	;
	; Arg:	DSeg	the main data segment, if to be set
	;	DestSeg	the current data segment, if to be reset
	;	Destroy	A register that can be destroyed, or empty

PopMapping	MACRO	DSeg, DestSeg, Destroy
		LOCAL	@@0, @@1
	IFNB <DSeg>
		mov	ds, DSeg		;; on proc's frame
	ENDIF
	IFB <Destroy>
		push	bp
		mov	bp, sp
		xchg	dx, [bp+2]
		cmp	dx, WindowNum
		jz	SHORT @@0
		call	CallMapAddr
@@0:		pop	bp dx
	ELSE
		pop	Destroy
	  IFDIFI <dx>, <Destroy>
		xchg	dx, Destroy
	  ENDIF
		cmp	dx, WindowNum
		jz	SHORT @@1
		call	CallMapAddr
@@1:	  IFDIFI <dx>, <Destroy>
		xchg	dx, Destroy
	  ENDIF
	ENDIF
	IFNB <DestSeg>
		mov	ds, DestSeg		;; on proc's frame
	ENDIF
		ENDM

	; Performs a REP XXXX operation, optimized by WORD or DWORD
	; transfers if allowed. No mapping provided. Non-REPable operations
	; supported by REP emulation.
	;
	; in:	ds:si	source address
	; 	es:di	destination address
	;	cx	byte count
	;
	; out:	ds:si	counted forward (by Command)
	;	es:di	counted forward (by Command)
	;	cx	possibly zeroed
	;
	; arg:	Reg	the reg (si/di) to be aligned
	;	Size	empty or <BYTE> if only byte transfer,
	;		<WORD> or <DWORD> if these transfers allowed.
	;	Cmd	<MOVS> or the command to be executed
	;	Cond	repetition condition, e.g. <NE> for REPNE, or empty
	;		only valid for BYTE transfer
	;	Align	this string is appended to alignment command macros
	;		(movsb->movsba)

DoRepNoMap	MACRO	Reg, Size, Cmd, Cond, Align
		LOCAL	@@0, @@1, @@2, @@3, @@4, @@5, @@6, CanRep

	PUSHSTATE
	MASM


	IFIDNI <Cmd>, <MOVS>
		CanRep = 1
	ELSEIFIDNI <Cmd>, <STOS>
		CanRep = 1
	ELSEIFIDNI <Cmd>, <CMPS>
		CanRep = 1
	ELSEIFIDNI <Cmd>, <SCAS>
		CanRep = 1
	ELSE
		CanRep = 0
	ENDIF

	IFIDNI <Size>, <DWORD>
		push	dx
		jcxz	SHORT @@0
	    IFNB <Reg>
		test	Reg, 1
		jz	SHORT @@1

		&Cmd&b&Align
		dec	cx
		jz	@@0
@@1:
		test	Reg, 2
		jz	SHORT @@2

		dec	cx
		jz	SHORT @@6

		&Cmd&w&Align
		dec	cx
@@2:
	    ENDIF
		mov	dx, cx
		shr	cx, 2
	    IF CanRep
		rep &Cmd&d
	    ELSE
		jz	SHORT @@5
@@4:		&Cmd&d
		dec	cx
		jnz	@@4
@@5:
	    ENDIF
		test	dx, 2
		jz	SHORT @@3
		&Cmd&w
@@3:		test	dx, 1
		jz	SHORT @@0
@@6:
		&Cmd&b
@@0:
		pop	dx
	ELSEIFIDNI <Size>, <WORD>
	    IFNB <Reg>
		test	Reg, 1
		jz	SHORT @@1
		jcxz	SHORT @@0
		&Cmd&b&Align
		dec	cx
@@1:
	    ENDIF
		shr	cx, 1
	    IF CanRep
		rep &Cmd&w
		jnc	SHORT @@0
		&Cmd&b
@@0:
	    ELSE
		jz	SHORT @@5
		pushf
@@4:		&Cmd&w
		dec	cx
		jnz	@@4
		popf
@@5:		jnc	SHORT @@0
		&Cmd&b
@@0:
	    ENDIF
	ELSE
	    IF CanRep
		rep&Cond& &Cmd&b
	    ELSE
		or	cx, cx
		jz	SHORT @@5
@@4:		&Cmd&b
	      IFNB <Cond>
		j&Cond&	SHORT @@1
		pushf
		dec	cx
		popf
		jmp     SHORT @@5
	      ENDIF
@@1:
		dec	cx
		jnz	@@4
@@5:
	    ENDIF
	ENDIF

	POPSTATE
		ENDM

	; Performs a REP MOVSB operation, optimized by WORD or DWORD
	; transfers if allowed. No mapping provided.
	;
	; in:	ds:si	source address
	; 	es:di	destination address
	;	cx	byte count
	;
	; out:	ds:si	counted forward
	;	es:di	counted forward
	;	cx	zeroed
	;
	; arg:	Reg	the reg (si/di) to be aligned
	;	Size	empty or <BYTE> if only byte transfer,
	;		<WORD> or <DWORD> if these transfers allowed.

DoRepMovsbNoMap	MACRO	Reg, Size

	DoRepNoMap <Reg>, <Size>, MOVS

		ENDM

	; Performs a REP XXXX operation, mapping once to the next window
	; if needed, optimized by WORD or DWORD transfers if allowed.
	;
	; in:	ds:si	source address
	; 	es:di	destination address
	;	cx	byte count
	;
	; out:	ds:si	counted forward
	;	es:di	counted forward
	;	cx	possibly zeroed
	;	ax	undefined
	;
	; arg:	Index	the index register addressing the video memory
	;		can be (e)bx, (e)dx, (e)bp, (e)si, (e)di.
	;		If given 32-bit Index, all registers must be 32 bit
	;		(WinSize, Source, Dest, Count).
	;	WinSize	WindowSize (register or value in SSeg).
	;	Dir	empty if forward, non-empty if backward move
	;	SetDS	non-empty if not DS eq SEG DATA
	;	Size	empty or <BYTE> if only byte transfer,
	;		<WORD> or <DWORD> if these transfers allowed.
	;	Cmd	<MOVS> or the command to be executed
	;	Cond	repetition condition, e.g. <NE> for REPNE, or empty
	;		only valid for BYTE transfer
	;	Destroy	the reg which is to be destroyed (default is ax)
	;		16-bit reg required
	;	eDestroy the 32-bit version of Destroy
	;	Align	this string is appended to alignment command macros
	;		(movsb->movsba)

DoRepDest	MACRO	Index, WinSize, Dir, SetDS, Size, Cmd, Cond, \
			Destroy, eDestroy, Align
		LOCAL	@@0, @@1, @@2, @@3

	PUSHSTATE
	MASM


	IFIDNI <Index>, <EBX>
		Is32 = 1
	ELSEIFIDNI <Index>, <EDX>
		Is32 = 1
	ELSEIFIDNI <Index>, <EBP>
		Is32 = 1
	ELSEIFIDNI <Index>, <ESI>
		Is32 = 1
	ELSEIFIDNI <Index>, <EDI>
		Is32 = 1
	ELSE
		Is32 = 0
	ENDIF

	IF Is32
		mov	eDestroy, Index
		add	eDestroy, ecx
		sub	eDestroy, WinSize
		jb	SHORT @@2
	ELSE
		mov	Destroy, Index
		add	Destroy, cx
		jc      SHORT @@1		;; word wrap-around
		cmp	WinSize, 0
		jz	SHORT @@2
		sub	Destroy, WinSize
		jb	SHORT @@2
@@1:
	ENDIF   	;; now we may use 16-bit values throughout...

		push	Destroy			;; store count of second part
		sub	cx, Destroy
		DoRepNoMap <Index>, <Size>, <Cmd>, <Cond>, <Align> ;; first part
	IFNB <Cond>
		j&Cond&	SHORT @@3
		pop	Destroy
		add	cx, Destroy
		jmp	SHORT @@0
@@3:
	ENDIF
		pop	cx
	IFNB <SetDS>
		push	ds
		push	dx
		mov	dx, SEG DATA
		mov	ds, dx
	ENDIF
	IF Is32
	  IFB <Dir>
		call	NextWindow32
	  ELSE
		call	PrevWindow32
	  ENDIF
		and	Index, OffsetMask32
	ELSE
	  IFB <Dir>
		call	NextWindow
	  ELSE
		call	PrevWindow
	  ENDIF
		and	Index, OffsetMask
	ENDIF
	IFNB <SetDS>
		pop	dx
		pop	ds
	ENDIF
@@2:		DoRepNoMap <Index>, <Size>, <Cmd>, <Cond>, <Align> ;; whole / 2nd part
@@0:

	POPSTATE
		ENDM


DoRep	MACRO	Index, WinSize, Dir, SetDS, Size, Cmd, Cond

	DoRepDest Index, WinSize, Dir, SetDS, Size, Cmd, Cond, ax, eax

ENDM

	; Performs a REP MOVSB operation, mapping once to the next window
	; if needed, optimized by WORD or DWORD transfers if allowed.
	;
	; in:	ds:si	source address
	; 	es:di	destination address
	;	cx	byte count
	;
	; out:	ds:si	counted forward
	;	es:di	counted forward
	;	ax	undefined
	;
	; arg:	Index	the index register addressing the video memory
	;	WinSize	WindowSize (register or value in SSeg)
	;	Dir	empty if forward, non-empty if backward move
	;	SetDS	non-empty if not DS eq SEG DATA
	;	Size	empty or <BYTE> if only byte transfer,
	;		<WORD> or <DWORD> if these transfers allowed.

DoRepMovsb	MACRO	Index, WinSize, Dir, SetDS, Size
		push	cx

	DoRep <Index>, <WinSize>, <Dir>, <SetDS>, <Size>, MOVS

		pop	cx
		ENDM
