;       SCCSID = @(#)disp.asm   1.1 85/04/10
;       SCCSID = @(#)disp.asm   1.1 85/04/10
;
; Dispatcher code
;

[list -]
;.xcref
%include "lmacros2.mac"
%include "dosseg.nas"
%include "dossym.mac"
%include "entrysw.mac"
;.cref
[list +]

;AsmVars <Kanji, Debug>
%ifndef Kanji
 %assign Kanji 0
%endif
%ifndef Debug
 %assign Debug 0
%endif

BREAK   <Copyright notice and version>
section DOSCODECODE


        I_need  CurrentPDB,WORD
        I_need  CntCFlag,BYTE
        I_need  User_SS,WORD
        I_need  User_SP,WORD
        I_need  NSS,WORD
        I_need  NSP,WORD
        I_need  SaveDS,WORD
        I_need  SaveBX,WORD
        I_need  INDOS,BYTE
        I_need  User_ID,WORD
        I_need  Proc_ID,WORD
        I_need  AuxStack,BYTE
        I_need  IOSTACK,BYTE
        I_need  DSKSTACK,BYTE
        I_need  fsharing,BYTE
        I_need  NoSetDir,BYTE
        I_need  FailERR,BYTE
        I_need  Errormode,BYTE
        I_need  restore_tmp,WORD
        I_need  WPERR,BYTE
        I_need  Dispatch,WORD
        I_need  ConSwap,BYTE
        I_need  User_In_AX,WORD
        I_need  EXTERR_LOCUS,BYTE
        I_need  IdleInt,BYTE
        I_need  Printer_Flag,BYTE
        I_need  CPSWFLAG,BYTE              ;AN000;
        I_need  CPSWSAVE,BYTE              ;AN000;
        I_need  DISK_FULL,BYTE             ;AN000;
        I_need  InterCon,BYTE              ;AN000;
        I_need  BOOTDRIVE,BYTE             ;AN000;
        I_need  EXTOPEN_ON,BYTE            ;AN000;
        I_need  DOS34_FLAG,WORD            ;AN000;
%ifndef BUF2
        I_need  ACT_PAGE,WORD              ;AN000;
%endif
	i_need	MSVERS,WORD
	I_need	doslocation3306, byte

        %IFN IBM
        I_need  OEM_HANDLER,DWORD
        %ENDIF

%ifndef BUF2
	%IF	BUFFERFLAG
	I_am	SETVECTFLAG,BYTE,<0>
	i_need	BUF_EMS_SEG_CNT,WORD	     ; DOS 4.00 EMS seg count		;AN000;
	i_need	BUF_EMS_MODE,BYTE	     ; DOS 4.00 EMS mode 		;AN000;
	i_am	BUF_EMS_MAP_USER,12,<0,0,0,0,0,0,0,0,0,0,0,0>
	%ENDIF
%endif


BREAK   <System call entry points and dispatcher>

versionstring:
	%include "../../version.inc"
	db 0


BREAK <$Set_CTRL_C_Trapping -- En/Disable ^C check in dispatcher>

; Inputs:
;	ds => DOSDATA
;	ss:sp -> original ds, original ip, original cs, original fl
;       AL = 0 read ^C status
;       AL = 1 Set ^C status, DL = 0/1 for ^C off/on
;       AL = 2 Set ^C status to contents of DL.  Output is old state.
;       AL = 3 Get CPSW state to DL        DOS 3.4
;       AL = 4 Set CPSW state from DL      DOS 3.4
;       AL = 5 get DOS boot drive
; Function:
;       Enable disable ^C checking in dispatcher
; Outputs:
;       If AL = 0 then DL = 0/1 for ^C off/on

        procedure   D_SET_CTRL_C_TRAPPING,NEAR
 ASSUME DS:DOSGROUP, ES:NOTHING, SS:NOTHING
	cmp al, 6
	jne .nottrueversion
	xor dx, dx
	mov dh, [doslocation3306]
	mov bx, [MSVERS]
	pop ds
 assume ds:nothing
	iret

.nottrueversion:
 assume ds:DOSGROUP
	cmp al, 0FFh				; function 33FFh ?
	jne .notversionstring			; no -->
	mov ax, offset versionstring		; access with cs
	mov dx, cs				; yes, return dx:ax -> ASCIZ
	pop ds
 assume ds:nothing
	iret					; (CF preserved)

.notversionstring:
 assume ds:DOSGROUP
        test    AL,AL
        JNZ     Check1
        MOV     DL,[CntCFlag]
        pop ds
 assume ds:nothing
        IRET
Check1:
 assume ds:DOSGROUP
        CMP     AL,2
        JA      CPSWchk                        ;AN000;
        JZ      SetAndRet
        PUSH    DX
        AND     DL,01h
CNTCFLAG equ CntCFlag	; NASM port label
        MOV     [CNTCFLAG],DL
        POP     DX
        pop ds
 assume ds:nothing
        IRET
SetAndRet:
 assume ds:DOSGROUP
        AND     DL,01h
        XCHG    [CntCFlag],DL
        pop ds
 assume ds:nothing
        IRET
BadVal:
 assume ds:DOSGROUP
        MOV     AL,0FFH
doscode_popds_iret:
        pop ds
 assume ds:nothing
        IRET
;; DOS 4.00 File Tagging

CPSWchk:                                ;AN000;
 assume ds:DOSGROUP
;       PUSH    AX                      ;AN000;;FT.
;       MOV     AL,[CPSWSAVE]           ;AN000;;FT. DOS 3.4
;       MOV     [CPSWFLAG],AL           ;AN000;;FT. DOS 3.4 in case ABORT
;       POP     AX                      ;AN000;;FT.
        CMP     AL,3                    ;AN000;;FT get CPSW state ?
        ; JNZ     CPSWset                 ;AN000;;FT. no
        je doscode_popds_iret
;       MOV     DL,CPSWFLAG             ;AN000;;FT. return CPSW state
	; pop ds
        ; IRET                            ;AN000;
CPSWset:                                ;AN000;
        CMP     AL,4                     ;AN000;;FT. set CPSW state ?
        ; JA      QueryDOSboot             ;AN000;;FT. check query dos boot drive
        jbe doscode_popds_iret
;       PUSH    AX                       ;AN000;;FT.
;       CallInstall NLSInstall,NLSFUNC,0 ;AN000;;FT. NLSFUNC installed ?
;       CMP     AL,0FFH                  ;AN000;;FT.
;       POP     AX                       ;AN000;;FT.
;       JNZ     BadVal                   ;AN000;;FT. not loaded, therefore ignore
;;;;    AND     DL,01H                   ;AN000;;FT. only 0 or 1
;;;;    MOV     [CPSWFLAG],DL            ;AN000;;FT. set the flag
;;;;    MOV     [CPSWSAVE],DL            ;AN000;;FT. save one copy
	; pop ds
        ; IRET                             ;AN000;;FT.
QueryDOSboot:                            ;AN000;
 assume ds:DOSGROUP
        CMP     AL,5                ;AN000;MS.
        JA      BadVal              ;AN000;MS.
        MOV     DL,[BOOTDRIVE]      ;AN000;;MS. put boot drive in DL
        pop ds
 assume ds:nothing
        IRET                        ;AN000;;MS.


;; DOS 4.00 File Tagging

EndProc D_SET_CTRL_C_TRAPPING

BREAK <$Get_current_PDB -- Set/Get PDB value>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;            C  A  V  E  A  T     P  R  O  G  R  A  M  M  E  R             ;
;                                                                          ;
; The following two routines are dispatched to directly with ints disabled
; immediately after the int 21h entry.  no DIS state is set.
;
; $Set_current_PDB takes BX and sets it to be the current process
;   *** THIS FUNCTION CALL IS SUBJECT TO CHANGE!!! ***
;
        procedure   D_SET_CURRENT_PDB,NEAR
        ASSUME  DS:DOSGROUP,ES:NOTHING,SS:NOTHING
        MOV     [CurrentPDB],BX
	pop ds
 assume ds:nothing
        IRET
EndProc D_SET_CURRENT_PDB

;
; $get_current_PDB returns in BX the current process
;   *** THIS FUNCTION CALL IS SUBJECT TO CHANGE!!! ***
;
procedure   D_GET_CURRENT_PDB,NEAR
        ASSUME  DS:DOSGROUP,ES:NOTHING,SS:NOTHING
        MOV     BX,[CurrentPDB]
        pop ds
 assume ds:nothing
        IRET
EndProc D_GET_CURRENT_PDB
;            C  A  V  E  A  T     P  R  O  G  R  A  M  M  E  R             ;
;                                                                          ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

;
; Sets the Printer Flag to whatever is in AL.
; NOTE: THIS PROCEDURE IS SUBJECT TO CHANGE!!!
;
Procedure D_Set_Printer_Flag
ASSUME DS:DOSGROUP, ES:NOTHING, SS:NOTHING
        mov     [Printer_Flag],al
        pop ds
 assume ds:nothing
        IRET
EndProc D_Set_Printer_Flag


getint:
	invoke get_esbx_ivt_from_al
	les bx, [es:bx]
	iret

setint:
	push es
	push bx
	invoke get_esbx_ivt_from_al
	cli
	mov word [es:bx], dx
	mov word [es:bx + 2], ds
	pop bx
	pop es
	iret


        procedure   SYSTEM_CALL,NEAR

%if 0
;
; The Quit entry point is where all INT 20h's come from.  These are old- style
; exit system calls.  The CS of the caller indicates which Process is dying.
; The error code is presumed to be 0.  We simulate an ABORT system call.
;
        entry   QUIT
        MOV     AH,0
        JMP     SHORT SAVREGS
%endif

;
; The system call in AH is out of the range that we know how to handle.  We
; arbitrarily set the contents of AL to 0 and IRET.  Note that we CANNOT set
; the carry flag to indicate an error as this may break some programs
; compatability.
;
BADCALL:
        MOV     AL,0
        entry   IRETT
        IRET
;
; An alternative method of entering the system is to perform a CALL 5 in the
; program segment prefix with the contents of CL indicating what system call
; the user would like.  A subset of the possible system calls is allowed here;
; only the CPM-compatible calls may get dispatched.
;

		; Call DOS interface for CP/M compatibility, "CALL 5"
relocated call5
        entry   CALL_ENTRY              ; System call entry point and dispatcher

; The CALL 5 interface isn't just a far call to this address.
; Rather it's a near call (inside the program's PSP) to address 5, which
; then calls far to this address. Doing a retf here will screw everything
; up and return into the PSP data instead of into the actual caller code.
	pushf
	push bp
	mov bp, sp
		; [ bp ] = bp
		; [ bp + 2 ] = flags (that we just pushed)
		; [ bp + 4 ] = call far's ip (will point into PSP so it's garbage)
		; [ bp + 6 ] = call far's cs
		; [ bp + 8 ] = "CALL 5"'s ip

	xchg ax, word [ byte bp + 2]	; ax		= flags
					; [ bp + 2 ]	= original ax
	xchg ax, word [ byte bp + 8]	; [ bp + 8 ]	= flags
					; ax		= "CALL 5"'s ip
	xchg ax, word [ byte bp + 4]	; [ bp + 4 ]	= "CALL 5"'s ip
					; clobbers ax (call far's ip)
	pop bp
	pop ax				; ax = original ax

		; [ sp ] = "CALL 5"'s IP
		; [ sp + 2 ] = call far's cs
		; [ sp + 4 ] = flags
		;  Surprisingly this really looks like the stack for an interrupt!
	cli				; Real interrupt entries do the same


	cmp cl, 24h		; CP/M function number
				; hardcoded ms_table.nas table size
	ja .nocpmfunc		;  is it above 24h? Really no CP/M function -->
				;  (I found this behaviour while debugging MS-DOS)

	mov ah,cl		; CP/M compatibility interface uses cl as function
SavRegs equ SAVREGS	; NASM port label
        JMP     SHORT SavRegs
				; Pass it through to our normal Int21 entry

.nocpmfunc: equ BADCALL
	; xor al,al		; Report "Unused" function
	; iret			;  (don't need to care about CY/NC)
; RxDOS 7.20N:	The above, correct, code requires some space you may want to save.
;		A short solution for all this CP/M crap is to place "xor al,al",
;		"retn" at address 5 of the PSP. This will report an error to all CP/M
;		CALL 5s and it's even shorter than the code used by RxDOS <= 7.20N


;
; This is the normal INT 21 entry point.  We first perform a quick test to see
; if we need to perform expensive DOS-entry functions.  Certain system calls
; are done without interrupts being enabled.
;

%ifn _RELOCATEDOSCODE
entry i20
	xor ax, ax
%endif
relocated i21
entry   COMMAND                         ; Interrupt call entry point (INT 21H)

        %IFN IBM
SET_OEM_HANDLER equ Set_Oem_Handler	; NASM port equate
        CMP     AH,SET_OEM_HANDLER
        JB      NOTOEM
        JMP     D_SET_OEM_HANDLER
NOTOEM:
        %ENDIF

        CMP     AH, 6Ch			; hardcoded ms_table.nas table size
BadCall equ BADCALL	; NASM port label
        JA      BadCall

;
; The following set of calls are issued by the server at *arbitrary* times
; and, therefore, must be executed on the user's entry stack and executed with
; interrupts off.
; lDOS: Set up a minimal stack frame consisting of the original ds,
;  so that we can access DOSDATA with ds.
;
SAVREGS:

extern doscode_getdosdata

	cmp ah, Get_Interrupt_Vector
	je getint
	cmp ah, Set_Interrupt_Vector
	je setint

	push ds					; now ds on stack
	 push ax
	call doscode_getdosdata
	mov ds, ax				; => DOSDATA
 assume ds:DOSGROUP
	 pop ax

GET_CURRENT_PDB equ Get_Current_PDB	; NASM port equate
        CMP     AH,GET_CURRENT_PDB
        JZ      D_GET_CURRENT_PDB
        CMP     AH,GetCurrentPSP
        JZ      D_GET_CURRENT_PDB
SET_CURRENT_PDB equ Set_Current_PDB	; NASM port equate
        CMP     AH,SET_CURRENT_PDB
        JZ      D_SET_CURRENT_PDB
        CMP     AH,Set_CTRL_C_Trapping
        JNZ     chkprt
D_Set_CTRL_C_Trapping equ D_SET_CTRL_C_TRAPPING
        JMP     D_Set_CTRL_C_Trapping
chkprt:
SET_PRINTER_FLAG equ Set_Printer_Flag	; NASM port equate
        CMP     AH,SET_PRINTER_FLAG
        JZ      D_Set_Printer_Flag
        pop ds					; restore ds before save_world
 assume ds:nothing
;
; Preserve all user's registers on their own stack.
;
        CALL    save_world
	call doscode_getdosdata			; ax => DOSDATA
	push ds					; stack original ds
	mov ds, ax				; => DOSDATA
        ASSUME  DS:DOSGROUP
        INC     byte [INDOS]                 ; Flag that we're in the DOS
        pop word [SaveDS]			; = original ds
        MOV     [SaveBX],BX
        XOR     AX,AX
USER_ID equ User_ID	; NASM port label
        MOV     [USER_ID],AX
        MOV     AX,[CurrentPDB]           ; current process
PROC_ID equ Proc_ID	; NASM port label
        MOV     [PROC_ID],AX            ; Assume local machine for the moment
;
; Provide one level of reentrancy for INT 24 recallability.
;
user_SP equ User_SP	; NASM port label
        MOV     AX,[user_SP]
        MOV     [NSP],AX
user_SS equ User_SS	; NASM port label
        MOV     AX,[user_SS]
        MOV     [NSS],AX
        POP     AX
        PUSH    AX
        MOV     [user_SP],SP
        MOV     [user_SS],SS
;
; save user stack in his area for later returns (possibly from EXEC)
;
fSharing equ fsharing	; NASM port label
        MOV     byte [fSharing],0              ; allow redirection

	mov bx, ds
        MOV     DS,[CurrentPDB]
ASSUME  DS:NOTHING
        MOV     WORD PTR [PDB_User_stack],SP
        MOV     WORD PTR [PDB_User_stack+2],SS

        ; MOV     BX,CS                   ; no holes here.
        MOV     SS,BX
 assume nocheck, ss:DOSGROUP

entry   REDISP
AUXSTACK equ AuxStack	; NASM port label
        MOV     SP,OFFSET AUXSTACK wrt DOSGROUP ; Enough stack for interrupts
ASSUME  SS:DOSGROUP
        STI                             ; stack is in our space now...
 %IF  DBCS                                       ;AN000;
        MOV     BH, BYTE PTR [PDB_InterCon]  ;AN000;; get interim mode  2/13/KK
        MOV     [SS:InterCon], BH               ;AN000;; 2/13/KK
 %ENDIF                                          ;AN000;
        push ss
        pop ds
        DOSAssume   CS,<DS>,"MSCODE/ReDisp"
;; DOS 3.4 INIT
;       MOV     BL,[CPSWSAVE]           ;AN000;;FT. DOS 3.4
;       MOV     [CPSWFLAG],BL           ;AN000;;FT. DOS 3.4 in case ABORT
        MOV     byte [DISK_FULL],0           ;AN000;;MS. no disk full
        MOV     byte [EXTOPEN_ON],0          ;AN000;;EO. clear extended open flag
        MOV     word [DOS34_FLAG],0          ;AN000;;MS. clear common flag
%ifndef BUF2
        MOV     word [ACT_PAGE],-1           ;BN000;BL;AN000;;LB. invalidate active page
%endif
;; DOS 4.00 INIT
        XOR     BH,BH
CONSWAP equ ConSwap	; NASM port label
        MOV     [CONSWAP],BH            ; random clean up of possibly mis-set flags
IDLEINT equ IdleInt	; NASM port label
        MOV     byte [IDLEINT],1             ; presume that we can issue INT 28
        MOV     BYTE PTR [NoSetDir],BH  ; set directories on search
FAILERR equ FailERR	; NASM port label
        MOV     BYTE PTR [FAILERR],BH   ; FAIL not in progress
        MOV     BL,AH
        SHL     BX,1                    ; 2 bytes per call in table
        CLD
;
; Since the DOS maintains mucho state information across system calls, we
; must be very careful about which stack we use.
;
; First, all abort operations must be on the disk stack.  THis is due to the
; fact that we may be hitting the disk (close operations, flushing) and may
; need to report an INT 24.
;
        OR      AH,AH
        JZ      DSKROUT                 ; ABORT
;
; Second, PRINT and PSPRINT and the server issue GetExtendedError calls at
; INT 28 and INT 24 time.  This call MUST, therefore, use the AUXSTACK.
;
        CMP     AH,GetExtendedError
        JZ      DISPCALL
;
; Old 1-12 system calls may be either on the IOSTACK (normal operation) or
; on the AUXSTACK (at INT 24 time).
;
        CMP     AH,12
        JA      DSKROUT
ERRORMODE equ Errormode	; NASM port label
        CMP     byte [ERRORMODE],0           ; Are we in an INT 24?
        JNZ     DISPCALL                ; Stay on AUXSTACK if INT 24.
        MOV     SP,OFFSET IOSTACK wrt DOSGROUP
        JMP     SHORT DISPCALL
;
; We are on a system call that is classified as "the rest".  We place
; ourselves onto the DSKSTACK and away we go.  We know at this point:
;
;   o   An INT 24 cannot be in progress.  Therefore we reset errormode and
;       wperr
;   o   That there can be no critical sections in effect.  We signal the
;       server to remove all the resources.
;
DSKROUT:
USER_IN_AX equ User_In_AX	; NASM port label
        MOV     [USER_IN_AX],AX         ; Remember what user is doing
        MOV     byte [EXTERR_LOCUS],errLOC_Unk ; Default
        MOV     byte [ERRORMODE],0           ; Cannot make non 1-12 calls in
        MOV     byte [WPERR],-1              ; error mode, so good place to make
;
; Release all resource information
;
        PUSH    AX
        MOV     AH,82h
        INT     int_IBM
        POP     AX

;
; Since we are going to be running on the DSKStack and since INT 28 people
; will use the DSKStack, we must turn OFF the generation of INT 28's.
;
        MOV     byte [IdleInt],0
        MOV     SP,OFFSET DSKSTACK wrt DOSGROUP
        TEST    byte [CNTCFLAG],-1
        JZ      DISPCALL                ; Extra ^C checking is disabled
        PUSH    AX
        invoke  DSKSTATCHK
        POP     AX
DISPCALL:
        MOV     BX,[CS:Dispatch + BX]
        XCHG    BX,[SaveBX]
        MOV     DS,[SaveDS]
 ASSUME DS:NOTHING

%ifndef BUF2
%IF	BUFFERFLAG
	mov	byte [ss:SETVECTFLAG], 0
	cmp	ah, 25h
	jne	saveuser
	cmp	ah, 35h
	jne	saveuser
	mov	byte [ss:SETVECTFLAG], 1
saveuser:
	invoke	SAVE_USER_MAP		    ;AN000;LB.	save EMS map
%ENDIF
%endif

        CALL    near [ss:SaveBX]

%ifndef BUF2
%IF	BUFFERFLAG
      invoke  RESTORE_USER_MAP            ;AN000;LB.  retsore EMS map
%ENDIF
%endif

entry doscode_leavedos
 assume ss:DOSGROUP, ds:nothing, es:nothing
;ASSUME SS:NOTHING			; User routines may misbehave
        CLI
        push ss
        pop ds				; ds => DOSDATA, overwritten soon
 assume ds:DOSGROUP
        MOV     BP, [user_SP]		; -> user stack
        MOV     SS, [user_SS]
        MOV     SP, bp			; restore user stack
 assume ss:nothing
        MOV     BYTE PTR [BP + user_AX],AL
        MOV     AX,[NSP]
        MOV     [user_SP],AX
        MOV     AX,[NSS]
        MOV     [user_SS],AX
        DEC     byte [INDOS]
        CALL    restore_world		; restore all registers
 assume ds:nothing, es:nothing
        IRET
EndProc SYSTEM_CALL

section DOSDATACODE	; in DOSDATA
extern dosdata_to_doscode
	entry   LEAVEDOS
 assume ss:DOSGROUP, ds:nothing, es:nothing
	push word [cs:dosdata_to_doscode]
	mov bp, doscode_leavedos
	push bp
	retf

section DOSCODECODE

entry servernoop
	xor al, al
	retn


;
; restore_world restores all registers ('cept SS:SP, CS:IP, flags) from
; the stack prior to giving the user control
;
        procedure   restore_world,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

	lframe -2
; have: es, ds, ..., ax, ip
; want: ip, ds, ..., ax, es
	lpar word, in_es_out_ip
	lpar word, ds
	lpar word, bp
	lpar word, di
	lpar word, si
	lpar word, dx
	lpar word, cx
	lpar word, bx
	lpar word, ax
	lpar word, in_ip
	lemit off
	lenter
	mov bp, sp			; replacement code
	mov ax, [bp + ?in_ip]		; ax = ip
	xchg ax, [bp + ?in_es_out_ip]	; ?out_ip = ip, ax = ?in_es
	lleave ctx
	mov es, ax			; es = ?in_es
 assume es:nothing
	pop ax				; discard ?in_ip
	pop ax
	pop bx
	pop cx
	pop dx
	pop si
	pop di
	pop bp
	pop ds
 assume ds:nothing
	retn
EndProc restore_world

;
; save_world saves complete registers on the stack
;
        procedure   save_world,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
	lframe 0
	lpar word, in_ip_out_es
	lpar word, ds
	 push ds
	lenter				; bp
	lvar word, di
	 push di
	lvar word, si
	 push si
	lvar word, dx
	 push dx
	lvar word, cx
	 push cx
	lvar word, bx
	 push bx
	lvar word, ax
	 push ax
	lvar word, in_ax_out_ip
	 push ax			; ?in_ax = ax
	mov ax, es			; = es
	xchg ax, word [bp + ?in_ip_out_es]	; ?out_es = es, ax = ip
	xchg ax, word [bp + ?in_ax_out_ip]	; ?out_ip = ip, ax = ?in_ax
	mov bp, [bp + ?frame_bp]	; restore bp
	lleave ctx			; drop context without code
	retn
EndProc save_world

%ifndef BUF2
%IF BUFFERFLAG

Break	<SAVE_USER_MAP - save map >							;AN000;
; Inputs:									;AN000;
;	none									;AN000;
; Function:									;AN000;
;	save map								;AN000;
; Outputs:									;AN000;
;	none									;AN000;
; No other registers altered							;AN000;
										;AN000;
Procedure   SAVE_USER_MAP,NEAR							 ;AN000;
	ASSUME	DS:NOTHING,ES:NOTHING						;AN000;
										;AN000;
	CMP	byte [ss:BUF_EMS_MODE],-1	  ;LB. EMS support			;AN000;
	JZ	No_user_save 		  ;LB. no				;AN000;
	CMP	byte [ss:SETVECTFLAG], 1
	jz	No_user_save
;	MOV	[ACT_PAGE],-1		  ;LB. invalidate active page		;AN000;
;	MOV	WORD PTR [LASTBUFFER],-1  ;LB.	and last buffer pointer 	;AN000;
	PUSH	AX			  ;LB. save regs			;AN000;
	PUSH	DS			  ;LB. save regs			;AN000;
	PUSH	ES			  ;LB.					;AN000;
	PUSH	SI			  ;LB.					;AN000;
	PUSH	DI			  ;LB.					;AN000;
	MOV	SI,OFFSET BUF_EMS_SEG_CNT wrt DOSGROUP     ;LB.			;AN000;
	MOV	DI,OFFSET BUF_EMS_MAP_USER wrt DOSGROUP     ;LB.			;AN000;

	PUSH	ss
	POP	ES
	PUSH	ss			  ;LB. ds:si -> ems seg cnt 		;AN000;
	POP	DS			  ;LB.					;AN000;

	MOV	AX,4F00H		  ;LB. save map 			;AN000;
	EnterCrit  critDisk		  ;LB. enter critical section		;AN000;
	INT	67H			  ;LB.					;AN000;
	LeaveCrit  critDisk		  ;LB. leave critical section		;AN000;
	POP	DI			  ;LB.					;AN000;
	POP	SI			  ;LB. restore regs			;AN000;
	POP	ES			  ;LB.					;AN000;
	POP	DS			  ;LB.					;AN000;
	POP	AX			  ;LB. restore				;AN000;
No_user_save:									 ;AN000;
	return									;AN000;
EndProc SAVE_USER_MAP								 ;AN000;
										;AN000;

Break	<RESTORE_USER_MAP- retore map >						;AN000;
; Inputs:									;AN000;
;	none									;AN000;
; Function:									;AN000;
;	restore_map								;AN000;
; Outputs:									;AN000;
;	none									;AN000;
; No other registers altered							;AN000;
										;AN000;
Procedure   RESTORE_USER_MAP,NEAR							 ;AN000;
	ASSUME	DS:NOTHING,ES:NOTHING						;AN000;
	
	CMP	byte [ss:BUF_EMS_MODE],-1	  ;LB. EMS support			;AN000;
	JZ	No_user_restore		  ;LB. no				;AN000;
	CMP	byte [ss:SETVECTFLAG], 1
	jz	No_user_restore
	PUSH	AX			  ;LB. save regs			;AN000;
	PUSH	DS			  ;LB. save regs			;AN000;
	PUSH	SI			  ;LB.					;AN000;
	MOV	SI,OFFSET BUF_EMS_MAP_USER wrt DOSGROUP     ;LB.			;AN000;

	PUSH	ss
	POP	DS

	MOV	AX,4F01H		  ;LB. restore map			;AN000;
	EnterCrit  critDisk		  ;LB. enter critical section		;AN000;
	INT	67H			  ;LB.					;AN000;
	LeaveCrit  critDisk		  ;LB. leave critical section		;AN000;
	POP	SI			  ;LB. restore regs			;AN000;
	POP	DS			  ;LB.					;AN000;
	POP	AX			  ;LB.					;AN000;
No_user_restore:									 ;AN000;
	return									;AN000;
EndProc RESTORE_USER_MAP

%ENDIF
%endif

;
; get_user_stack returns the user's stack (and hence registers) in DS:SI
;
        procedure   get_user_stack,NEAR
 assume ds:nothing, es:nothing, ss:nothing
        xchg ax, si
        call doscode_getdosdata
        mov ds, ax
 assume ds:DOSGROUP
        xchg ax, si
        LDS     SI,[user_SP]
        return
EndProc get_user_stack

        %IFN IBM
BREAK <Set_OEM_Handler -- Set OEM sys call address and handle OEM Calls

D_SET_OEM_HANDLER:
ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

; Inputs:
;       User registers, User Stack, INTS disabled
;       If CALL F8, DS:DX is new handler address
; Function:
;       Process OEM INT 21 extensions
; Outputs:
;       Jumps to OEM_HANDLER if appropriate

        JNE     DO_OEM_FUNC             ; If above F8 try to jump to handler
invalid instruction fixme
; ldos: need segment override
        MOV     WORD PTR [OEM_HANDLER],DX   ; Set Handler
        MOV     WORD PTR [OEM_HANDLER+2],DS
        IRET                            ; Quick return, Have altered no registers

DO_OEM_FUNC:
invalid instruction fixme
; ldos: need segment override
        CMP     WORD PTR [OEM_HANDLER],-1
        JNZ     OEM_JMP
        JMP     BADCALL                 ; Handler not initialized

OEM_JMP:
invalid instruction fixme
; ldos: need segment override
        JMP     [OEM_HANDLER]
        %ENDIF
