
;===========================================================================
;
; 	TITLE Low Memory Stub for DOS when DOS runs in HMA
;
;	Microsoft Confidential
;	Copyright (C) Microsoft Corporation 1991
;	All Rights Reserved.
;
;	Revision History:
;
;	M003 - MS PASCAL 3.2 support. Please see under tag M003 in dossym.inc
;	       7/30/90
;
;	M006 - print A20 Hardware error using int 10.
;
;	M020 - Fix for Rational Bug - see exepatch.asm for details
;
;	M011 - check for wrap rather than do an XMS query 
;	       A20 after int 23,24 and 28
;
;	M012 - Rearranged stuff to make Share build with msdata
;
;	M023 - Added variable UmbSave1 for preserving umb_head arena across 
;	       win /3 session for win ver < 3.1
;
;       M032 - Correct length of Exec data block in Win 2.x instance table.
;
;	M034 - add a new variable i20to28count to be used by MSDOS.SYS
;		(msinit.asm). IF POWER is built into BIOS, then this var
;		should be 8 (which means, DOS will not patch INT 28 to an
;		IRET); otherwise, this should be 9.
;
;	M00x - Added a variable for storing the ROM Exclude list ptr
;
;============================================================================


DOSDATA    SEGMENT WORD PUBLIC 'DATA'
	   assume cs:DOSDATA


;----------------------------------------------------------------------------
;
;	P U B L I C S
;
;----------------------------------------------------------------------------

	PUBLIC	DOSINTTABLE

	public	ldivov
	public	lquit
	public	lcommand
	public	labsdrd
	public	labsdwrt
	public	lStay_resident
	public	lint2f
	public	lcall_entry
	public	lirett


	public	i0patch
	public	i20patch
	public	i21patch
	public	i25patch
	public	i26patch
	public	i27patch
	public	i2fpatch
	public	cpmpatch



;----------------------------------------------------------------------------
;
; 	D A T A
;
;----------------------------------------------------------------------------

	EVEN
DOSINTTABLE	LABEL	DWORD
	
	DW	OFFSET DOSCODE:DIVOV 		, 0
	DW	OFFSET DOSCODE:QUIT 		, 0
	DW	OFFSET DOSCODE:COMMAND		, 0
	DW	OFFSET DOSCODE:ABSDRD		, 0
	DW	OFFSET DOSCODE:ABSDWRT		, 0
	DW	OFFSET DOSCODE:Stay_resident	, 0
	DW	OFFSET DOSCODE:INT2F		, 0
	DW	OFFSET DOSCODE:CALL_ENTRY	, 0
	DW	OFFSET DOSCODE:IRETT		, 0

	SS_Save	DW	?		; save user's stack segment
	SP_Save	DW	?		; save user's stack offset



;-------------------------------------------------------------------------
;
; LOW MEM STUB:
;
; The low mem stub contains the entry points into DOS for all interrupts 
; handled by DOS. This stub is installed if the user specifies that the 
; DOS load in HIMEM. Each entry point does this.
;
;
; 	1. if jmp to 8 has been patched out
;	   2. if A20 OFF
;	      3. Enable A20
;	   4. else 
;	      5. just go to dos entry
;	   6. endif
;	7. else
;	   8. just go to dos entry
;	9. endif
;
;
;--------------------------------------------------------------------------
	assume	cs:dosdata

;--------------------------------------------------------------------------
;
; DIVIDE BY 0 handler
;
;--------------------------------------------------------------------------

ldivov:
	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i0patch:
	jmp	short divov_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

divov_cont:
	jmp	dword ptr DOSINTTABLE	; jmp to DOS

;------------------------------------------------------------------------
;
; INT 20 Handler
;
; Here we do not have to set up the stack to return here as the abort call
; will return to the address after the int 21 ah=4b call. This would be the
; common exit point if A20 had been OFF (for TOGGLE DOS) and the A20 line
; will be restored then.
;
;-------------------------------------------------------------------------

lquit:
	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i20patch:
	jmp	short quit_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	
quit_cont:
	jmp	dword ptr DOSINTTABLE+4	; jump to DOS

;--------------------------------------------------------------------------
;
; INT 21 Handler
;
;--------------------------------------------------------------------------

lcommand:

	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i21patch:
	jmp	short command_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

command_cont:
	jmp	dword ptr DOSINTTABLE+8	; jmp to DOS

;------------------------------------------------------------------------
;
; INT 25 
;
;----------------------------------------------------------------------------

labsdrd:
	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i25patch:
	jmp	short absdrd_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

absdrd_cont:
	jmp	dword ptr DOSINTTABLE+12; jmp to DOS

;-------------------------------------------------------------------------
;
; INT 26
;
;-----------------------------------------------------------------------

labsdwrt:

	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i26patch:
	jmp	short absdwrt_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

absdwrt_cont:
	jmp	dword ptr DOSINTTABLE+16; jmp to DOS

;------------------------------------------------------------------------
;
; INT 27
;
;-----------------------------------------------------------------------

lStay_resident:

	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i27patch:
	jmp	short sr_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

sr_cont:
	jmp	dword ptr DOSINTTABLE+20; jmp to DOS

;-----------------------------------------------------------------------------
;
; INT 2f
;
;-------------------------------------------------------------------------

lint2f:

	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
i2fpatch:
	jmp	short int2f_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

int2f_cont:
	jmp	dword ptr DOSINTTABLE+24; jmp to DOS

;-----------------------------------------------------------------------------
;
; CPM entry
;
;------------------------------------------------------------------------

lcall_entry:

	;
	; The following jump, skipping the XMS calls will be patched to 
	; NOPS by SEG_REINIT if DOS successfully loads high. This jump is 
	; needed because the stub is installed even before the XMS driver
	; is loaded if the user specifies dos=high in the config.sys
	;
cpmpatch:
	jmp	short callentry_cont	

	call	EnsureA20ON		; we must turn on A20 if OFF	

callentry_cont:
	jmp	dword ptr DOSINTTABLE+28; jmp to DOS


;--------------------------------------------------------------------------

lirett:	iret

;---------------------------------------------------------------------------
;
; LowIntXX:
;
; Interrupts from DOS that pass control to a user program must be done from
; low memory, as the user program may change the state of the A20 line or
; they may require that the A20 line be OFF. The following piece of code is
; far call'd from the following places in DOS:
;
;	1. msctrlc.asm where dos issues an int 23h (ctrlc)
;	2. msctrlc.asm where dos issues an int 24h (critical error)
;	3. msctrlc.asm where dos issues an int 28h (idle int)
;
; The int 23 and int 24 handlers may decide to do a far return instead of an
; IRET ane leave the flags on the stack. Therefore we save the return address
; before doing the ints and then do a far junp back into DOS.
;
;---------------------------------------------------------------------------


public	DosRetAddr23, DosRetAddr24
public	LowInt23, LowInt24, LowInt28

DosRetAddr23	DD	?
DosRetAddr24	DD	?
DosRetAddr28	DD	?

	; 
	; Execute int 23h from low memory
	;

LowInt23:
					; save the return address that is on
					; the stack
	pop	word ptr cs:[DosRetAddr23]
	pop	word ptr cs:[DosRetAddr23+2]

	int	23h			; ctrl C
					; turn on A20 it has been turned OFF
					; by int 28/23/24 handler.

	call	EnsureA20ON		; M011: we must turn on A20 if OFF 

	jmp	dword ptr DosRetAddr23	; jump back to DOS



	; 
	; Execute int 24h from low memory
	;

LowInt24:
					; save the return address that is on
					; the stack
	pop	word ptr cs:[DosRetAddr24]
	pop	word ptr cs:[DosRetAddr24+2]

	int	24h			; crit error
					; turn on A20 it has been turned OFF
					; by int 28/23/24 handler.

	call	EnsureA20ON		; M011: we must turn on A20 if OFF	

	jmp	dword ptr DosRetAddr24	; jump back to DOS


	; 
	; Execute int 23h from low memory
	;

LowInt28:

	int	28h			; idle int
					; turn on A20 it has been turned OFF
					; by int 28/23/24 handler.

	call	EnsureA20ON		; M011: we must turn on A20 if OFF	

	retf

;-------------------------------------------------------------------------
;
; int 21 ah=4b (exec) call will jump to the following label before xferring
; control to the exec'd program. We turn of A20 inorder to allow programs
; that have been packed by the faulty exepack utility to unpack correctly.
; This is so because exepac'd programs rely on address wrap.
;
;------------------------------------------------------------------------- 

public	disa20_xfer
disa20_xfer:
	call	XMMDisableA20		; disable A20

	;
	; Look at msproc.asm at label exec_go for understanding the following:
	;

	; DS:SI points to entry point
	; AX:DI points to initial stack
	; DX has PDB pointer
	; BX has initial AX value


	cli
	mov	BYTE PTR InDos,0	; SS Override

	ASSUME	SS:NOTHING

	mov	SS,AX			; set up user's stack
	mov	SP,DI			; and SP
	sti
	push	DS			; fake long call to entry
	push	SI
	mov	ES,DX			; set up proper seg registers
	mov	DS,DX
	mov	AX,BX			; set up proper AX
	retf



;-------------------------------------------------------------------------
;
; M003:
;
; If an int 21 ah=25 call is made immediately after an exec call, DOS will
; come here, turn A20 OFF restore user stack and registers before returning 
; to user. This is done in dos\msdisp.asm. This has been done to support 
; programs complied with MS PASCAL 3.2. See under TAG M003 in DOSSYM.INC for 
; more info.	
;
; Also at this point DS is DOSDATA. So we can assume DS DOSDATA. Note that 
; SS is also DOS stack. It is important that we do the XMS call on DOS's 
; stack to avoid additional stack overhead for the user.
;
; -------------------------------------------------------------------------
public	disa20_iret
disa20_iret:

	assume	ds:DOSDATA

	call	XMMDisableA20
	dec	InDos
	mov	SS,User_SS		; restore user stack
	mov	SP,User_SP
	mov	BP,SP
	mov	BYTE PTR [BP.User_AX],AL
	mov	AX,Nsp
	mov	User_SP,AX
	mov	AX,Nss
	mov	User_SS,AX

	pop	AX			; restore user regs
	pop	BX
	pop	CX
	pop	DX
	pop	SI
	pop	DI
	pop	BP
	pop	DS
	pop	ES
	iret

	assume	ds:NOTHING

;*****************************************************************************
;***	XMMDisableA20 - switch 20th address line			      
;									      
;	This routine is used to disable the 20th address line in 	      
;	the system using XMM calls.					      
;									      
;	ENTRY	none		;ds = _DATA				      
;	EXIT	A20 line disabled					      
;	USES	NOTHING					      
;									      
;*****************************************************************************


XMMDisableA20	proc	near

	push	bx
	push	ax
	mov	ah, XMM_LOCAL_DISABLE_A20
	call	cs:[XMMcontrol]
	pop	ax
	pop	bx
	ret

XMMDisableA20	endp


; The entry point in the BIOS XMS driver is defined here.
public	XMMcontrol
	XMMcontrol	DD	?


;---------------------------------------------------------------------------
;
;***	EnsureA20ON - Ensures that A20 is ON
;									      
;	This routine is used to query the A20 state in		 	      
;	the system using XMM calls.					      
;									      
;	ENTRY: none		
;
;	EXIT : A20 will be ON
;		
;									      
; 	USES : NONE								      
;									      
;---------------------------------------------------------------------------


LowMemory   label   dword		; Set equal to 0000:0080
	dw	00080h
	dw	00000h

HighMemory  label   dword
	dw	00090h			; Set equal to FFFF:0090
	dw	0FFFFh

EnsureA20ON	proc	near

	pushf
	push    ds
	push	es
	push	cx
	push	si
	push	di

	lds	si,cs:LowMemory     	; Compare the 4 words at 0000:0080
	les	di,cs:HighMemory    	; with the 4 at FFFF:0090
	mov	cx,4
	cld
	repe    cmpsw

	jz	EA20_OFF

EA20_RET:

	pop	di
	pop	si
	pop	cx
	pop	es
	pop	ds
	popf
	ret

EA20_OFF:

	;
	; We are going to do the XMS call on the DOS's AuxStack. NOTE: ints
	; are disabled at this point.
	;

	push	bx
	push	ax

	mov	ax, ss			; save user's stack pointer
	mov	cs:[SS_Save],ax
	mov	cs:[SP_Save],sp
	mov	ax, cs
	mov	ss, ax
	mov	sp, OFFSET DOSDATA:AuxStack
					; ss:sp -> DOSDATA:AuxStack
	
	mov	ah, XMM_LOCAL_ENABLE_A20
	call	cs:[XMMcontrol]
	or	ax, ax
	jz	XMMerror		; AX = 0 fatal error


	mov	ax, cs:[SS_Save]	; restore user stack
	mov	ss, ax
	mov	sp, cs:[SP_Save]

	pop	ax
	pop	bx

	jmp	short EA20_RET


EnsureA20ON	endp


XMMerror:				; M006 - Start

	mov	ah, 0fh			; get video mode
	int	10h
	cmp	al, 7			; Q: are we an MDA
	je	XMMcont			; Y: do not change mode
	xor	ah, ah			; set video mode
	mov	al, 02h			; 80 X 25 text
	int	10h
XMMcont:
	mov	ah, 05h			; set display page
	xor	al, al			; page 0
	int	10h
	
	mov	si, offset XMMERRMSG
	push	cs
	pop	ds
	cld				; clear direction flag

XMMprnt:
	lodsb
	cmp	al, '$'			; indicates end of XMMERRMSG
	jz	XMMStall		; function 0eh	
	mov	ah, 14
	mov	bx, 7
	int	10h
	jmp	short XMMprnt
	
XMMStall:
	sti				; allow the user to warm boot
	jmp	XMMStall		; M006 - End



;
;This has been put in for WIN386 2.XX support. The format of the instance 
;table was different for this. Segments will be patched in at init time.
;
public  OldInstanceJunk
OldInstanceJunk	dw	70h	;segment of BIOS
		dw	0	;indicate stacks in SYSINIT area
		dw	6	;5 instance items

		dw	0,offset dosdata:contpos, 2
		dw	0,offset dosdata:bcon, 4
		dw	0,offset dosdata:carpos,106h
		dw	0,offset dosdata:charco, 1
		dw	0,offset dosdata:exec_init_sp, 34               ;M032
		dw	070h,offset BData:altah, 1	 ; altah byte in bios




; M021-
;
; DosHasHMA - This flag is set by seg_reinit when the DOS actually
; 	takes control of the HMA.  When running, this word is a reliable
;	indicator that the DOS is actually using HMA.  You can't just use
;	CS, because ROMDOS uses HMA with CS < F000.

public	DosHasHMA
	DosHasHMA		db	0

public	fixexepatch, RationalPatchPtr
	fixexepatch		dw	?		; M012
	RationalPatchPtr 	dw	?		; M012

; End M021


;
; M020 Begin
;
		public	RatBugCode
RatBugCode	proc	far
		push	cx
		mov	cx, word ptr ds:[10h]
		loop	$
		pop	cx
		ret
RatBugCode	endp
;
; M020 End
;

	
public	UmbSave1			; M023
UmbSave1	db	0bh dup (?)	; M023

public	Mark3
	Mark3	label byte

IF2
	IF ((OFFSET MARK3) GT (OFFSET COUNTRY_CDPG) )
		%OUT !DATA CORRUPTION!MARK3 OFFSET TOO BIG. RE-ORGANIZE DATA.
	ENDIF
ENDIF

;############################################################################
;
; ** HACK FOR DOS 4.0 REDIR **
;
; The dos 4.X redir requires that country_cdpg is at offset 0122ah. Any new
; data variable that is to be added to DOSDATA must go in between Mark3
; COUNTRY_CDPG if it can. 
;
; MARK3 SHOULD NOT BE > 122AH 
;
; As of 9/6/90, this area is FULL!
;
;############################################################################
 
	ORG	0122ah

; The following table is used for DOS 3.3
;DOS country and code page information is defined here for DOS 3.3.
;The initial value for ccDosCountry is 1 (USA).
;The initial value for ccDosCodepage is 850.
;
;
		   PUBLIC  UCASE_TAB,FILE_UCASE_TAB,DBCS_TAB
		   PUBLIC  FILE_CHAR_TAB,COLLATE_TAB

PUBLIC	COUNTRY_CDPG

;
; country and code page infomation
;
COUNTRY_CDPG  label  byte

	 db   0,0,0,0,0,0,0,0	      ; reserved words
	 db   '\COUNTRY.SYS',0        ; path name of country.sys
	 db   51 dup (?)
; ------------------------------------------------<MSKK01>----------------------
ifdef	DBCS
  ifdef	  JAPAN
	 dw   932		      ; system code page id (JAPAN)
  endif
  ifdef	  TAIWAN
	 dw   938		      ; system code page id (TAIWAN)
  endif
  ifdef   KOREA
         dw   934                     ; system code page id (KOREA IBM)
  endif
else
	 dw   437		      ; system code page id
endif
; ------------------------------------------------<MSKK01>----------------------
	 dw   6 		      ; number of entries
	 db   SetUcase		      ; Ucase type
	 dw   OFFSET DOSDATA:UCASE_TAB    ;pointer to upper case table
	 dw   0 			   ; segment of poiter
	 db   SetUcaseFile	      ; Ucase file char type
	 dw   OFFSET DOSDATA:FILE_UCASE_TAB	;pointer to file upper case table
	 dw   0 			   ; segment of poiter
	 db   SetFileList	      ; valid file chars type
	 dw   OFFSET DOSDATA:FILE_CHAR_TAB   ;pointer to valid file char tab
	 dw   0 			   ; segment of poiter
	 db   SetCollate	      ; collate type
	 dw   OFFSET DOSDATA:COLLATE_TAB  ;pointer to collate table
	 dw   0 			   ; segment of poiter
	 db   SetDBCS		      ;AN000; DBCS Ev			  2/12/KK
	 dw   OFFSET DOSDATA:DBCS_TAB ;AN000;;pointer to DBCS Ev table   2/12/KK
	 dw   0 		       ;AN000; segment of poiter	 2/12/KK
	 db   SetCountryInfo	      ; country info type
	 dw   NEW_COUNTRY_SIZE	      ; extended country info size
; ------------------------------------------------<MSKK01>----------------------
ifdef	DBCS
  ifdef	  JAPAN
	 dw   81 		      ; <MSKK01> JAPAN country id
	 dw   932		      ; <MSKK01> JAPAN system code page id
	 dw   2 		      ; <MSKK01> date format (YMD)
	 db   '\',0,0,0,0             ; <MSKK01> currency symbol (YEN)
	 db   ',',0                   ; thousand separator
	 db   '.',0                   ; decimal separator
	 db   '-',0                   ; date separator
	 db   ':',0                   ; time separator
	 db   0 		      ; currency format flag
	 db   0 		      ; <MSKK01> # of disgit in currency
	 db   1 		      ; <MSKK01> time format (HR24)
	 dw   OFFSET DOSDATA:MAP_CASE	;mono case routine entry point
	 dw   0 			; segment of entry point
	 db   ',',0                    ; data list separator
	 dw   0,0,0,0,0 	       ; reserved
  endif
  ifdef	  TAIWAN
	 dw   88		      ; TAIWAN country id
	 dw   938		      ; TAIWAN system code page id
	 dw   0 		      ; date format (MDY)
	 db   'N','T','$',0,0	      ; currency symbol
	 db   ',',0                   ; thousand separator
	 db   '.',0                   ; decimal separator
	 db   '-',0                   ; date separator
	 db   ':',0                   ; time separator
	 db   0 		      ; currency format flag
	 db   2 		      ; # of disgit in currency
	 db   1 		      ; time format (HR24)
	 dw   OFFSET DOSDATA:MAP_CASE	;mono case routine entry point
	 dw   0 			; segment of entry point
	 db   ',',0                    ; data list separator
	 dw   0,0,0,0,0 	       ; reserved
  endif
  ifdef   KOREA
         dw   82                      ; <MSCH> KOREA country id
         dw   934                     ; <MSCH> KOREA system code page id
         dw   2                       ; <MSCH> date format (YMD)
         db   '\',0,0,0,0             ; <MSCH> currency symbol (WON)
	 db   ',',0                   ; thousand separator
	 db   '.',0                   ; decimal separator
	 db   '-',0                   ; date separator
	 db   ':',0                   ; time separator
	 db   0 		      ; currency format flag
         db   0                       ; <MSCH> # of disgit in currency
         db   1                       ; <MSCH> time format (HR24)
	 dw   OFFSET DOSDATA:MAP_CASE	;mono case routine entry point
	 dw   0 			; segment of entry point
	 db   ',',0                    ; data list separator
	 dw   0,0,0,0,0 	       ; reserved
  endif
else
	 dw   1 		      ; USA country id
	 dw   437		      ; USA system code page id
	 dw   0 		      ; date format
	 db   '$',0,0,0,0             ; currency symbol
	 db   ',',0                   ; thousand separator
	 db   '.',0                   ; decimal separator
	 db   '-',0		      ; date separator
	 db   ':',0                   ; time separator
	 db   0 		      ; currency format flag
	 db   2 		      ; # of disgit in currency
	 db   0 		      ; time format
	 dw   OFFSET DOSDATA:MAP_CASE	;mono case routine entry point
	 dw   0 			; segment of entry point
	 db   ',',0                    ; data list separator
	 dw   0,0,0,0,0 	       ; reserved
endif
; ------------------------------------------------<MSKK01>----------------------

; M034 BEGIN
IFDEF	ROMDOS

        public	i20To28Count		; M034


IFDEF	POWER			; if POWER Driver is part of BIOS
i20To28Count	dw	8	; then dont' patch I28 to an IRET
ELSE
i20To28Count	dw	9	; else patch I28 to an IRET
ENDIF

ENDIF
; M034 END

; M035 BEGIN

IFDEF	ROMEXEC
	public	RomExclRegPtr
RomExclRegPtr	dd	0
ENDIF ; ROMEXEC

; M035 END

include	msdos.cl2			; XMMERRMSG

DOSDATA	ends

