;    File              : $PCMIF.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log: $
;    PCMIF.A86 1.21 93/09/09 22:36:50
;    Int 21/59 uses error stack 
;    PCMIF.A86 1.20 93/08/27 18:56:30 
;    Int 25/26 32 bit sector detection
;    PCMIF.A86 1.19 93/07/22 19:30:54 
;    chnage int 25/26 support for NDD 
;    PCMIF.A86 1.18 93/07/20 22:47:54 
;    Even fewer checks on int 25/26 
;    PCMIF.A86 1.14 93/05/06 19:28:31
;    Move int 23/28 support to CIO.
;    PCMIF.A86 1.13 93/05/05 23:31:0
;    int 2A/84 is now only generated on input-and-wait functions
;    PCMIF.A86 1.12 93/03/25 15:06:48
;    tweak int21 entry
;    ENDLOG
;
; 24 Aug 87 The CARRY flag is now preserved for all DOS functions below
;           038h Get/Set Country Code.
; 04 Sep 87 Display the Interrupt Number and Registers when an illegal
;           software interrupt is executed by an application. This
;           is disabled by DBG OFF.
; 09 Sep 87 DDIO interface changed to support a Double Word sector
;           number.
; 05 Oct 87 Critical Error abort routine now uses the correct 
;           terminate code.
; 14 Oct 87 INT2F responds like a Network Redirector
; 28 Oct 87 Preserve the state of the INDOS_INTERNAL flag during multiple
;           calls through DOS_ENTRY
; 29 Oct 87 DS now points at the IBM PC ROS during an INT1B and INT1B
;           moved into IBMROS.
;  7 Nov 87 Removal of development flags
;  5 Jan 88 Terminate CP/M applications making DOS Calls in Concurrent DOS
; 26 Feb 88 Terminate DOS applications making CP/M calls in DOSPLUS
; 26 Apr 88 INT25/26 error codes only translated for Concurrent
; 18 May 88 Prevent corruption of the EXIT_CODE by INT23 and INT24
;           when the application does not return.
; 23 May 88 Prevent termination during CONFIG.SYS processing in DOSPLUS
; 26 May 88 Force INDOS and ERROR flags to ZERO on terminate
;  1 Jun 88 Modify INT28 to execute delays and work with SideKick.
; 15 Jul 88 Support the IDLECNT field in the Process Descriptor and
;           CCB BUSY bit.
; 07 Sep 88 PolyTron PolyWindows make DOS functions calls whenever
;           CS < SS. Therefore during the INT21 Entry and Exit code
;           interrupts must be disabled untill the stack swap occurs
;           even though the INDOS_FLAG is non-zero.
; 24 Oct 88 File Lock/Unlock is now treated as an INACTIVE function.
;  4 Nov 88 Correct the INT25/26 Error Translation Code
; 21 Nov 88 Make INt25/26 Error Translation even better.
; 29 Nov 88 Command Line Editor Insert on by default.
; 21 Dec 88 IJs IDLE detection improvement only DELAY if someone else is
;           ready to run.
; 11 Jan 89 Inc/Dec INTERNAL_FLAG to support DOS_ENTRY reentrancy.
; 30 Jan 89 Inc/Dec INDOS_FLAG to INT25/26 Direct Disk I/O (CHKDSK/PRINT)
; 19 Feb 89 Check the SHARE_FLAG for SHARING status in DR DOS
;  9 Mar 89 Save/Restore PSP_USERSP/SS round int28's (SKPLUS/LINK/CTRL-C)
; 18 Apr 89 ij	Maintain the INDOS_FLAG correctly while processing INT 24
; 18 Apr 89 Only take over INTs 5B and 5C for Debug Systems
; 22 May 89 Setup DS before testing the state of the Share Flag.
; 25 May 89 Support INT28_FLAG for functions 01 to 0C inclusive
; 31 May 89 Move break_sp into DOS internal data area
;  1 Jun 89 Save PSP_USERSS and SP during a critival Error (INT 24)
; 05 Jun 89 Return NOT present for ASSIGN command and TRUE state for APPEND
; 29 Jun 89 Save/restore retry_sp & retry_off around INT 24
; 10 Jul 89 INT27 corrected to handle memory size of XXX1 (INFORM)
; 31 Jul 89 Move stacks to DATA.PCM (CDROM support under way...)
; 11 Aug 89 Create INT2F.PCM to support real INT 2F functions
;  6 Sep 89 INT2A/84 keyboard busy loop added to do_int28
; 23 Oct 89 MON_PERM removed from func5D/func5E/func5F (pain with MSNET..)
;  9 Nov 89 Int 5B & 5C no longer taken over in DRDOS Debug versions
;           (Conflicts with PC-Net)
; 15 Jan 90 pcmode_swapin/pcmode_swapout added for paged isr support
; 25/Jan/90 Support Idle Data Area
; 25/Jan/90 Add forth stack to support Japanese FEP and PTHOT.
; 29/Jan/90 keep int21 CS/IP/FLAGS on local stack
; 13/Feb/90 Added func63.
; 20/Feb/90 CDOS checks caller is DOS process and aborts if not.
; 22/Feb/90 Int25/26 checks disk label for passwords
;           Also swaps to normal stack like others DOS's
;  7 Mar 90 Convert to register preserved function calls
; 16 Mar 90 Int25/26 checks out for Leopard Beta 2
;  4 Jun 90 Int21/25&35 don't swap stacks-ANSI.SYS Framework DOS window bug
;  7 Jun 90 Print Rite fix moves to init.pcm
; 29 Jun 90 CDOS idle detection uses timer info
;  9 Aug 90 Int 8 tick count packed in P_CMOD
; 19 Sep 90 load current_psp before checking PSP_PIDF (thanks Datapac)
;  4 Oct 90 improved control break handling
; 11 Oct 90 dev_map now supported to set LPTn/COMn mapping
;  1 Nov 90 default Int 24 handler returns "Fail"
; 19 feb 91 do_int28 calls read_time_and_date (BIOS CLOCK needs a call
;           every day)
; 14 jun 91 copy user regs to local copy after int24 for pcshell
;  8 aug 91 SI preserved on Int25/26 for SCAN 7.7

	include	pcmode.equ
	include	fdos.def
	include	vectors.def
	include	i:msdos.equ
	include	i:mserror.equ
	include	i:psp.def
	include	i:fdos.equ

PCM_CODE	CSEG	BYTE

	extrn	pcmode_dseg:word
	extrn	break_check:near		; Control-C Check 
	extrn	error_exit:near
	extrn	fdos_nocrit:near
	extrn	get_ddsc:near

eject
;
;	This entry point is used when a CALLF PSP:0005 has been executed
;	Here the function number is passed in CL and not AH and only
;	functions 0 to 24 inclusive can be executed.
;
;		Entry Stack	->	INT 21 Stack
;
; SP + 04	Return Offset		Current Flags
; SP + 02	 PSP Segment		 PSP Segment
; SP + 00	   000Ah		Return Offset
;
	Public	call5_entry
call5_entry:
	pop	ax		; Remove 000Ah return Offset
	pushf			; Save the Flags
	push bp	! mov bp,sp	; Get Stack Frame Pointer
	mov	ax,02[bp]	; Get the FLAGS
	xchg	ax,06[bp]	; Swap with the return offset
	mov	02[bp],ax	; and then save the return offset
	pop	bp		; Restore the BP register
	mov	ah,cl		; Make it look like an INT 21
	cmp	ah,024h		; Check for a valid function for this
	jbe	int21_entry	; entry technique if not return
illegal_iret:
	mov	al,0
	iret

int21_e01:
	mov	ds,word ptr .INT31_SEGMENT
	jmps	int21_e02

eject
;	++++++++++++++++++++++++++
;	Int 20 - Program Terminate
;	++++++++++++++++++++++++++
;
	Public	int20_entry
int20_entry:
    mov ah,00h          
;	jmps	int21_entry		; and jump to the standard entry point

eject
;	+++++++++++++++++++++++++
;	Int 21 - Function Request
;	+++++++++++++++++++++++++
;
	Public	int21_entry
int21_entry:
	cmp	ah,pcmode_ftl		; is it in range ?
	 ja	illegal_iret		;  no, return BEFORE enabling ints
	cld				; Clear the direction flag
	PUSH$DOS			; Save User Registers
	mov	ds,pcmode_dseg		; get CS relative Data Segment
	test	pcmode_dseg,0FFFFh	; if Data Segment is zero then get
	 jz	int21_e01		; the Data segment address from
int21_e02:				; the segment portion of INT 31

;
;	The following routines execute on the users stack without
;	modifing the INDOS_FLAG etc. These routines should only read
;	or update BASIC system variables.
;
	cmp	ah,33h			; Func 33h - Control Break
	 je	int21_e10
	cmp	ah,50h			; Func 50h - Set PSP
	 jb	int21_e20
	cmp	ah,51h			; Func 51h - Get PSP
	 jbe	int21_e10
	cmp	ah,62h			; Func 62h - Get PSP
	 jne	int21_e20
int21_e10:
	mov	bp,sp			; Calculate the Stack Frame address
	call	internal_func		; "jump" to appropriate routine
	jmp	int21_exit

int21_e20:
	inc	indos_flag		; Increment the INDOS Flag

	mov	int21AX,ax		; save function number

if DOS5
	cmp	WindowsHandleCheck,26h	; is windows active ?
	 jne	int21_e30
	mov	ax,LocalMachineID	; get local machine ID (zero unless
	mov	machine_id,ax		;  we are multi-tasking)
else
    callf   win386_local_machine    
	cmp	machine_id,0		; did it nobble the machine ID
	 jnz	int21_e30		; if so leave it alone

	mov	ax,LocalMachineID	; get local machine ID (zero unless
	mov	machine_id,ax		;  we are multi-tasking)
endif
int21_e30:
	mov	ax,current_psp
	mov	owning_psp,ax
	mov	es,ax			; Get the PSP

	mov	ax,sp
	mov	PSP_USERSP,ax		; Save the SS:SP pointer to 
	mov	PSP_USERSS,ss		; the register image ready for any
					; Critical errors that might occur

	xchg	ax,int21regs_off	; point to callers registers
	mov	prev_int21regs_off,ax	; while saving current
	mov	ax,ss			;  pointer to cater for
	xchg	ax,int21regs_seg	;  re-entrancy
	mov	prev_int21regs_seg,ax

	xor	ax,ax
	mov	remote_call,ax		; indicate we are a local call
	mov	int28_flag,al		; Do NOT Generate INT 28s

	mov	ax,ds
	mov	ss,ax			; swap initially to the error
	mov	sp,offset error_stack	;  stack until we know better
	mov	ax,int21AX		; reload AX

	sti
	cmp	ah,59h			; Func 59h - Return Extended Error
	 je	int21_e50		;  use the error stack
	cmp	ah,0Ch			; are we a character function
	 ja	int21_e40		;  in range 01-0C ?
	cmp	ah,00h
	 je	int21_e40
	cmp	error_flag,0		; Use the "ERROR" Stack for the above
	 jnz	int21_e50		;  functions 01-0C if error_flag set
	mov	sp,offset indos_stack	; else use the "INDOS" stack for these
	mov	int28_flag,TRUE		;  functions and generate INT 28s
	jmps	int21_e50

int21_e40:
	mov	error_flag,0		; clear the error flag in case someone
					; hasn't returned from an Int 24
	push	ax			; save function on stack
	mov	ah,82h			; magic number in AH
	int	2ah			; call server hook
	pop	ax			; recover function number

	mov	sp,offset normal_stack	; switch to the NORMAL stack
	test	break_flag,0FFh		; is the Break Flag ON
	 jz	int21_e50		;  NO - So continue
	call	break_check		; Handle the Control-C
int21_e50:
if IDLE_DETECT
	test	idle_flags,IDLE_DISABLE	; don't break the pipeline unless
	 jz	int21_idle		;  IDLE checking enabled
int21_e60:
endif
	call	int21_func		; Execute the function
	cli				; Stop anybody interfering
	les	bp,int21regs_ptr	; point to user stack
	mov	es:reg_AL[bp],al	; always return AL
	mov	ax,prev_int21regs_off
	mov	int21regs_off,ax
	mov	ax,prev_int21regs_seg
	mov	int21regs_seg,ax
	mov	ax,es
	mov	ss,ax			; back to users stack
	mov	sp,bp
	dec	indos_flag		; Decrement the INDOS_FLAG
;	jmp	int21_exit

	Public	int21_exit
int21_exit:
	POP$DOS				; Restore Registers
	iret

eject
if IDLE_DETECT
; Only called if Idle detection is enabled
; AH,DL as on Int 21 Entry
; Decide if function is active/inactive

int21_idle:
;----------
if IDLE_DETECT
	mov	bx,int28_reload		; reset the INT28 delay counter
	mov	int28_delay,bx		;  with the Reload value
endif
	cmp ah,5Ch ! je int21_inactive	; Treat Lock/Unlock as inactive some
					; applications poll locked record.
	cmp ah,44h ! je int21_inactive	; IO Control Treated as Inactive
	cmp ah,2Ch ! ja int21_active	; > Get Current Time all active
		     je int21_inactive	; Get Current Time inactive
	cmp ah,2Ah ! je int21_inactive	; Get Current Date inactive
	cmp ah,0Bh ! je int21_inactive	; Console Status
	cmp ah,0Ch ! je int21_inactive	; Flush and Invoke Treated as Inactive
	cmp ah,19h ! je int21_inactive	; Get Current Disk
	cmp ah,06h ! jne int21_active	; Treat RAW_IO Status as Inactive
	cmp dl,0FFh ! je int21_inactive	;

int21_active:				; Active function Executed
	or	idle_flags,IDLE_DOSFUNC	; set DOSFUNC flag for BIOS
	call	active			; remember we were active
	jmps	int21_e60		; continue execution

int21_inactive:
	call	inactive		; Process this INACTIVE function
	jmps	int21_e60


	Public	inactive
inactive:
	push es ! push ax

	dec	active_cnt		; Decrement the count
	jnz	inactive_10		; Return if Non-Zero

	mov	ax,idle_max		; Get the default count value
	mov	active_cnt,ax		; and reset the internal count

	test	idle_flags,IDLE_DISABLE	; Has Idle Checking been enabled
	 jnz	inactive_10		; Skip if NO.
	mov	ax,PROC_IDLE		; Process is IDLE
	callf	idle_vec		; Call the IDLE Handler

inactive_10:
	pop ax ! pop es
	ret
;
;	This routine will reset the active count for functions which
;	are treated as INACTIVE but which have active sub-functions.
;
	Public	active
active:
	push	ax
	mov	ax,idle_max		; Get the default count value
	mov	active_cnt,ax		; and reset the internal count
	pop	ax
	ret
endif
;
;
;	This function is invoked for functions number above the last 
;	supported function number. It forces AL to zero and returns.
;	Just that and nothing more.
; 
ms_zero_AL:
	xor	ax,ax			; AL = 0 for return
	ret

eject
;	DOS_ENTRY is used to call DOS functions internally.
;	eg. Func4B (exec) calls MS_X_OPEN, MS_X_READ, MS_X_CLOSE etc.
;	It is the responsibilty of the caller to make sure that no side
;	effects exist if this entry point is used.
;	eg. critical error handling
;
;
	Public	dos_entry
dos_entry:
	clc
	cld
	pushf				; look like Int21 registers
	pushf ! pushf			; fake CS/IP positions
	push ds ! push es		; Save registers on the USER stack
	push bp				; no Stack Swap is executed and DS
	push di ! push si		; and ES are swapped.
	push dx ! push cx
	push bx ! push ax
	mov	bp,sp			; Initialise Stack Frame

	call	get_dseg		; Get our Data Area

	inc	internal_flag

	push	fdos_data+0*WORD	; save fdos pblk so we can
	push	fdos_data+1*WORD	;  be re-entrant(ish)
	push	fdos_data+2*WORD
	push	fdos_data+3*WORD
	push	fdos_data+4*WORD
	push	fdos_data+5*WORD
	push	fdos_data+6*WORD
	
	push	int21regs_off
	push	int21regs_seg

	mov	int21regs_off,bp
	mov	int21regs_seg,ss

	call	internal_func		; Execute the function
	mov	reg_AL[bp],al		; always return AL to caller

	pop	int21regs_seg
	pop	int21regs_off		; restore previous pointer user REGS

	pop	fdos_data+6*WORD
	pop	fdos_data+5*WORD
	pop	fdos_data+4*WORD
	pop	fdos_data+3*WORD
	pop	fdos_data+2*WORD
	pop	fdos_data+1*WORD
	pop	fdos_data+0*WORD	; restore fdos_pb for nested calls

	dec	internal_flag

	pop ax ! pop bx			; Update the registers then
	pop cx ! pop dx			; set the flags and return
	pop si ! pop di			; to the user
	pop bp
	pop es ! pop ds
	popf				; discard dos_IP
	popf				;  and dos_CS
	popf				; get result
	 jnc	dos_entry10
	neg	ax			; return using our negative error
	stc				; conventions
dos_entry10:
	ret

	Public	int21_func

int21_func:
;----------
; On Entry:
;	AX, CX, DX, SI, DI as per Int 21
;	BX = ??
;	BP = ??
;	DS = pcmode data
;	ES = ??
; On Exit:
;	(to client function)
;	All general purpose registers as per Int 21 entry
;	ES = dos_DS
;

	xor	bx,bx			; BH = 0
	mov	bl,ah			; BX = function number
	shl	bx,1			; make it a word offset
	push	pcmode_ft[bx]		; save address of Function
	les	bp,int21regs_ptr
	mov	bx,es:reg_BX[bp]	; reload from dos_BX,dos_BP,and dos_DS
	les	bp,es:dword ptr reg_BP[bp]
	ret


internal_func:
;-------------
; On Entry:
;	All registers as per Int 21 EXCEPT
;	DS = pcmode data
;	BP = dos_REGS stack frame
; On Exit:
;	(to client function)
;	ES = dos_DS
;
	mov	al,ah			; function number in AL
	cbw				; AH = 0
	xchg	ax,bx			; get subfunction in BX
	shl	bx,1			; make offset in the internal table
	push	pcmode_ft[bx]		; save address of Function
	xchg	ax,bx			; restore BX
	mov	ax,reg_AX[bp]		; recover function number
	mov	es,reg_DS[bp]		; ES = callers DS
	ret				; "jump" to handler

eject
;	INT25 and INT26 direct disk I/O interface
;
;Standard DOS 1.xx - 3.30 INT25/26 Interface
;===========================================
;
;	entry:	al = drive number
;		ds = DMA segment
;		bx = DMA offset
;		cx = number of sectors
;		dx = beginning relative sector
;
;
;Enhanced DOS 3.31 INT25/26 Interface
;====================================
;
;	If CX == 0FFFFh then the application is using the enhanced
;	INT25/INT26 interface which allows access to more than 64K
;	sectors.
;
;	entry:	al = drive number
;		bx = Parameter block Offset
;		ds = Parameter block Segment
;
;	Parameter Block Format
;DS:BX ->	DD	Starting Sector No.
;		DW	Number of Sectors
;		DD	Transfer Address
;
;
;	exit:	C flag = 0 if successful
;		       = 1 if unsuccessful
;		ax = error code(if CF = 1)
;		  ah physical error
;		  al logical error
;		Users orginal flags left on stack
;
;
eject
DDIO_INT13	equ     0
DDIO_READ_OP	equ     1
DDIO_WRITE_OP	equ     2

;	++++++++++++++++++++++++++++
;	Int 26 - Absolute Disk Write
;	++++++++++++++++++++++++++++
;
	Public	int26_entry
int26_entry:
	mov 	ah,DDIO_WRITE_OP	; This is a WRITE operation	
	jmps	int26_10


;	+++++++++++++++++++++++++++
;	Int 25 - Absolute Disk Read
;	+++++++++++++++++++++++++++
;
	Public	int25_entry
int25_entry:
	mov 	ah,DDIO_READ_OP		; This is a READ operation	
int26_10:				; Common Direct Disk I/O code
	cld
	push ds ! push es
	push	dx			; save DX for FLASHCARD
	push ds ! pop es		; ES = callers DS
	call	get_dseg		; Get PCMODE Data Segment
	inc	indos_flag		; Update the INDOS_FLAG
	mov	normal_stack+2,ss	; save users SS:SP
	mov	normal_stack,sp
	cli
	push ds ! pop ss		; use normal stack when in here
	mov	sp,offset normal_stack
	sti
	inc	cx			; CX = FFFF indicates this is
     jz int26_30       
; CHECK FOR PARITIONS > 32 MBytes...
	dec	cx			; CX restored
	push	es
	push	ax
	push	bx
	push	dx
	call	get_ddsc		; ES:BX -> DDSC_
	mov	di,0201h		; assume bad drive
	 jc	int26_20
	mov	di,0207h		; assume large media, and this error
if 0
; This code works out the total number of sectors on a drive
	mov	ax,es:DDSC_NCLSTRS[bx]	; get last cluster #
	dec	ax			;  make it # data clusters
	xor	dx,dx
	mov	dl,es:DDSC_CLMSK[bx]	; get sec/cluster -1
	inc	dx			; DX = sec/cluster
	mul	dx			; DX:AX = # data sectors
	add	ax,es:DDSC_DATADDR[bx]	; add in address of 1st data sector
	adc	dx,0
else
	mov	ax,es:DDSC_NCLSTRS[bx]	; get last cluster #
	xor	dx,dx
	mov	dl,es:DDSC_CLMSK[bx]	; get sec/cluster -1
	inc	dx			; DX = sec/cluster
	mul	dx			; DX:AX is vaguely the # sectors
	test	dx,dx			; close enough for bill
endif
	stc				; assume an error
	 jnz	int26_20
	xor	di,di			; DI = zero, no error
int26_20:
	pop	dx
	pop	bx
	pop	ax
	pop	es
	 jnc	int26_40
	xchg	ax,di			; AX = error code
	jmps	int26_60		; return it
int26_30:	
	mov	dx,es:word ptr 0[bx]	; Get Starting Sector Low
	mov	di,es:word ptr 2[bx]	; Get Starting Sector High
	mov	cx,es:word ptr 4[bx]	; Get No. of sectors to transfer
	les	bx,es:dword ptr 6[bx]	; Tranfer Address Offset
int26_40:
	mov	FD_DDIO_DRV_OP,ax	; save drive and operation
	mov	FD_DDIO_NSECTORS,cx	; No. of Sectors
	mov	FD_DDIO_STARTLOW,dx	; Starting Sector No.
	mov	FD_DDIO_STARTHIGH,di	; High Word of Sector Number
	mov 	FD_DDIO_DMAOFF,bx	; DMA Offset
	mov	FD_DDIO_DMASEG,es	; DMA Segment

	mov	FD_FUNC,FD_DDIO
	call	fdos_nocrit		; let the FDOS do the work
	neg	ax			; AX is DOS extended error
	 jz	int26_exit
	sub	al,-ED_PROTECT		; get AL to Int24 error format
	cmp	al,-(ED_GENFAIL-ED_PROTECT)
     jbe    int26_50       
	mov	al,-(ED_GENFAIL-ED_PROTECT)
int26_50:				; no, make it general failure
	mov	ah,al			; save error in AH
	mov	bx,offset int13_error
	xlat	int13_error		; convert error to int13 format
	xchg	al,ah			; get errors in correct registers
int26_60:
	stc				; Set the Carry Flag when an error
int26_exit:				; Occurs and return to the calling app.
	cli
	mov	ss,normal_stack+2	; back to user stack
	mov	sp,normal_stack
	sti
	dec	indos_flag		; Update the INDOS_FLAG
	sti
	pop	dx
	pop es ! pop ds			; restore callers registers
	retf				; leave flags on stack

int13_error	db	03h,02h,80h,01h,10h,02h,40h,02h,04h,02h,02h,02h,02h

eject
;	++++++++++++++++++++++++++++++++++++
;	Int 27 - Terminate but Stay Resident
;	++++++++++++++++++++++++++++++++++++
;
	Public	int27_entry
int27_entry:
	mov	ax,3100h	; Convert this to a DOS 'Terminate and 
	add	dx,15		; Stay Resident' function by converting the
	rcr	dx,1		; memory size in bytes to paragraphs.
	shr	dx,1		; On entry DX == memsize + 1 bytes therefore
	shr	dx,1		; round upto a paragraph boundary by adding
	shr	dx,1		; 15 then divide by 16
	jmp	int21_entry

eject
;
;	DO_INT24:
;	    On Entry:-
;			AH	Set for INT 24
;			AL	Drive Number (0 = A:)
;			DI	Error Code
;			ES:SI	Device Header Control Block
;
;	    On Exit:-
;			AL	Error Response Retry/Ignore/Fail
;
;	INT 24 Critical Error:-
;	On Entry:-	AH/7	0 = Disk Device
;			AH/5	0 = IGNORE is an Invalid Response
;			AH/4	0 = RETRY in an Invalid Response
;			AH/3	0 = FAIL is an Invalid Response
;			AH/2-1	00= DOS Area
;				01= File Allocation Table
;				10= Directory
;				11= Data
;			AH/0	0 = Read, 1 = Write
;
;			AL	1 Retry the Operation
;			BP:SI	Device Header Control Block
;			DI	High Byte Undefined, Low Byte Error Code 
;
;	    On Exit:-	AL	0 = IGNORE Error
;				1 = RETRY the Operation
;				2 = TERMINATE using INT 23
;				3 = FAIL the current DOS function
;
	Public	do_int24
do_int24:
	cmp	error_flag,0		; Skip the critical error routine
	jz	di24_05			; if the handler is active
	mov	al,ERR_FAIL		; Then return the FAIL condition
	ret				; to the calling routine
di24_05:
	push ax ! push bp		; Save our Base Pointer and then the
	cli				; Disable Interupts

	mov	bp,es			; BP:SI points to dev header
	mov	es,current_psp		; Get the current PSP and USER Stack

	push	PSP_USERSS		; Save the Users Real SS and SP
	push	PSP_USERSP		; on the internal Stack
	push	retry_sp		; also the retry info
	push	retry_off
	push	remote_call
	push	machine_id

	mov	critical_sp,sp		; Internal Stack Pointer Offset

	inc	error_flag		; Entering Critical Error Handler
	dec	indos_flag		; I may be gone some time....
					; (an application error handler need
					; never return so tidy up first)

	mov	ss,PSP_USERSS		; Switch to the Users Stack
	mov	sp,PSP_USERSP

	int	24h			; Call the Critical Error Handler

	cld
	cli				; paranioa.....
	call	get_dseg		; Reload DS just in case someone at
					; A-T or Lotus cannot read

	push ds ! pop ss		; Swap back to the Internal stack
	mov	sp,critical_sp		; and process the returned info.

	pop	machine_id
	pop	remote_call
	pop	retry_off		; restore retry info
	pop	retry_sp
	mov	es,current_psp		; Restore the Users original SS and
	pop	PSP_USERSP		; SP registers from the Stack
	pop	PSP_USERSS

	pop bp ! pop bx			; Restore BP and original AX
	sti

	mov	error_flag,0
    inc indos_flag  

	cmp al,ERR_IGNORE ! jnz di24_10	; Check for IGNORE and force
	test bh,OK_IGNORE ! jnz di24_10 ; to become a FAIL if its an 
	mov	al,ERR_FAIL		; invalid response.

di24_10:
	cmp al,ERR_RETRY ! jnz di24_20	; Check for RETRY and force
	test bh,OK_RETRY ! jnz di24_20	; to become a FAIL if its an
	mov	al,ERR_FAIL		; invalid response.	

di24_20:
	cmp al,ERR_FAIL ! jnz di24_30	; Check for FAIL and force
	test bh,OK_FAIL ! jnz di24_30	; to become a ABORT if its an
	mov	al,ERR_ABORT		; invalid response.	

di24_30:
	cmp	al,ERR_ABORT		; Do not return if the ABORT option
	jz	di24_abort		; has been selected but execute
					; INT 23 directly
	cmp	al,ERR_FAIL		; All invalid reponses are converted
	ja	di24_abort		; to ABORT
di24_40:
	ret

di24_abort:				; Abort this Process
	mov	ax,current_psp		; check not root application because
	mov	es,ax			; it must not be terminated so force
	cmp	ax,PSP_PARENT		; Is this the root Process
	mov	al,ERR_FAIL		; convert the error to FAIL
	 je	di24_40			; if not we terminate
	mov	exit_type,TERM_ERROR	; Set the correct exit Type
	mov	ax,04C00h		; and return code.
	mov	int21AX,ax
	jmp	func4C			; Then terminate

eject
;
;	Get the PCMODE Emulator data Segment from the PD
;
	Public	get_dseg
get_dseg:
	mov	ds,pcmode_dseg		; get CS relative Data Segment
	test	pcmode_dseg,0FFFFh	; If Data Segment is zero then get
	jz	get_d10			; the Data segment address from
	ret				; the segment portion of INT 31
get_d10:
	mov	ds,word ptr .INT31_SEGMENT
	ret


;
;INVALID_FUNCTION is called when any unsupported function has been executed
;
	Public	invalid_function
invalid_function:
	mov	ax,ED_FUNCTION		; Mark as Invalid Function
	jmp	error_exit		; and Exit


eject
	Public	reload_registers
reload_registers:
;----------------
; This routine is called to reload the registers we expect to have correct
; at the start of a PCMODE function.
	push	ds
	lds	bp,int21regs_ptr
	mov	ax,ds:reg_AX[bp]
	mov	bx,ds:reg_BX[bp]
	mov	cx,ds:reg_CX[bp]
	mov	dx,ds:reg_DX[bp]
	mov	si,ds:reg_SI[bp]
	mov	di,ds:reg_DI[bp]
	les	bp,ds:dword ptr reg_BP[bp]
	pop	ds
	ret

PCMODE_DATA	DSEG	WORD

	extrn	retry_sp:word
	extrn	retry_off:word

	extrn	break_flag:byte
	extrn	current_psp:word
	extrn	current_dsk:byte
	extrn	dma_offset:word
	extrn	dma_segment:word
	extrn	error_flag:byte
	extrn	error_stack:word
	extrn	exit_type:byte
	extrn	int21regs_ptr:dword
	extrn	int21regs_off:word
	extrn	int21regs_seg:word
	extrn	prev_int21regs_ptr:dword
	extrn	prev_int21regs_off:word
	extrn	prev_int21regs_seg:word
	extrn	indos_flag:byte
	extrn	indos_stack:word
	extrn	LocalMachineID:word
	extrn	machine_id:word
	extrn	int21AX:word
	extrn	normal_stack:word
	extrn	owning_psp:word
	extrn	remote_call:word
if DOS5
	extrn	WindowsHandleCheck:byte
else
	extrn	win386_local_machine:dword
endif

if IDLE_DETECT
	extrn	active_cnt:word
	extrn	idle_max:word
	extrn	idle_flags:word
	extrn	idle_vec:dword
	extrn	int28_delay:word
	extrn	int28_reload:word
endif
	
	extrn	critical_sp:word
	extrn	internal_flag:byte
	extrn	int28_flag:byte

PCM_CODE	CSEG	BYTE
;
;	The following Function tables are forced onto a word boundary
;	because of the word alignment of the PCMODE_RODATE segment.
;	Only word based Read Only data is held in this segment.
;
	extrn	func00:near, func01:near, func02:near, func03:near
	extrn	func04:near, func05:near, func06:near, func07:near
	extrn	func08:near, func09:near, func0A:near, func0B:near
	extrn	func0C:near, func0D:near, func0E:near, func0F:near
	extrn	func10:near, func11:near, func12:near, func13:near
	extrn	func14:near, func15:near, func16:near, func17:near
	extrn	func19:near, func1A:near, func1B:near, func1C:near
	extrn	func1F:near, func21:near, func22:near, func23:near
	extrn	func24:near, func25:near, func26:near, func27:near
	extrn	func28:near, func29:near, func2A:near, func2B:near
	extrn	func2C:near, func2D:near, func2E:near, func2F:near
	extrn	func30:near, func31:near, func32:near, func33:near
	extrn	func34:near, func35:near, func36:near, func37:near
	extrn	func38:near, func39:near, func3A:near, func3B:near
	extrn	func3C:near, func3D:near, func3E:near, func3F:near
	extrn	func40:near, func41:near, func42:near, func43:near
	extrn	func44:near, func45:near, func46:near, func47:near
	extrn	func48:near, func49:near, func4A:near, func4B:near
	extrn	func4C:near, func4D:near, func4E:near, func4F:near
	extrn	func50:near, func51:near, func52:near, func53:near
	extrn	func54:near, func55:near, func56:near, func57:near
	extrn	func58:near, func59:near, func5A:near, func5B:near
	extrn	func5C:near, func5D:near, func5E:near, func5F:near
	extrn	func60:near, func62:near, func63:near, func65:near
	extrn	func66:near, func67:near, func68:near, func69:near
	extrn	func6C:near

PCM_RODATA	CSEG	WORD		
	Public	pcmode_ft, pcmode_ftl
pcmode_ft	rw	0
	dw	func00			; (00) Terminate Program
	dw	func01			; (01) Read Keyboard and Echo
	dw	func02			; (02) Display Character
	dw	func03			; (03) Auxilary Input
	dw	func04			; (04) Auxilary Output
	dw	func05			; (05) Print Character
	dw	func06			; (06) Direct Console I/O
	dw	func07			; (07) Direct Console Input
	dw	func08			; (08) Read Keyboard
	dw	func09			; (09) Display String
	dw	func0A			; (0A) Buffered Keyboard Input
	dw	func0B			; (0B) Check Keyboard Status
	dw	func0C			; (0C) Flush Buffer, Read Keyboard
	dw	func0D			; (0D) Reset Disk
	dw	func0E			; (0E) Select Disk
	dw	func0F			; (0F) Open File
	dw	func10			; (10) Close File
	dw	func11			; (11) Search for First
	dw	func12			; (12) Search for Next
	dw	func13			; (13) Delete File
	dw	func14			; (14) Sequential Read
	dw	func15			; (15) Sequential Write
	dw	func16			; (16) Create File
	dw	func17			; (17) Rename File
	dw	ms_zero_AL		; (18) Unused DOS function (AL = 0)
	dw	func19			; (19) Current Disk
	dw	func1A			; (1A) Set Disk Transfer Address
	dw	func1B			; (1B) *Get Default Drive Data
	dw	func1C 			; (1C) *Get Drive Data
	dw	ms_zero_AL		; (1D) Unused DOS function (AL = 0)
	dw	ms_zero_AL		; (1E) Unused DOS function (AL = 0)
	dw	func1F 			; (1F) Get Default DPB
	dw	ms_zero_AL		; (20) Unused DOS function (AL = 0)
	dw	func21			; (21) Random Read
	dw	func22			; (22) Random Write
	dw	func23			; (23) File Size
	dw	func24			; (24) Set Relative Record
	dw	func25			; (25) Set Interrupt Vector
	dw	func26			; (26) Duplicate PSP
	dw	func27			; (27) Random Block Read
	dw	func28			; (28) Random Block Write
	dw	func29			; (29) Parse File Name
	dw	func2A			; (2A) Get Date
	dw	func2B			; (2B) Set Date
	dw	func2C			; (2C) Get Time
	dw	func2D			; (2D) Set Time
	dw	func2E			; (2E) Set/Reset Verify Flag
	dw	func2F			; (2F) Get Disk Transfer Address
	dw	func30			; (30) Get Version Number
	dw	func31 			; (31) Keep Process
	dw	func32			; (32) Get DPB
	dw	func33			; (33) CONTROL-C Check
	dw	func34			; (34) Get the Indos Flag
	dw	func35			; (35) Get Interrupt Vector
	dw	func36			; (36) Get Disk Free Space
	dw	func37			; (37) Get/Set Switch Character
	dw	func38			; (38) Return Country Dependant Info
	dw	func39			; (39) Create Sub-directory
	dw	func3A			; (3A) Remove Sub-directory
	dw	func3B			; (3B) Change Sub-directory
	dw	func3C			; (3C) Create a File
	dw	func3D			; (3D) Open a File Handle
	dw	func3E			; (3E) Close a File Handle
	dw	func3F			; (3F) Read from a File/Device
	dw	func40			; (40) Write to a File/Device
	dw	func41			; (41) Delete a Directory Entry
	dw	func42			; (42) Move a File Pointer
	dw	func43			; (43) Change Attributes
	dw	func44			; (44) I/O Control
	dw	func45			; (45) Duplicate File Handle
	dw	func46			; (46) Force a Duplicate File Handle
	dw	func47			; (47) Return Text of Current Directory
	dw	func48			; (48) Allocate Memory
	dw	func49			; (49) Free Allocated Memory
	dw	func4A			; (4A) Modify Allocated Memory
	dw	func4B			; (4B) Load and Execute Program
	dw	func4C			; (4C) Terminate a Process
	dw	func4D			; (4D) Get Return Code
	dw	func4E			; (4E) Find Matching File
	dw	func4F			; (4F) Find Next Matching File
	dw	func50			; (50) Set Current PSP
	dw	func51			; (51) Get Current PSP
	dw	func52			; (52) *Get In Vars
	dw	func53			; (53) *Build DPB from BPB
	dw	func54			; (54) Return Verify State
	dw	func55			; (55) Create a New PSP
	dw	func56			; (56) Move a Directory Entry
	dw	func57			; (57) Get/Set File Date and Time
	dw	func58			; (58) Memory Allocation Strategy
	dw	func59			; (59) Get Extended Error
	dw	func5A			; (5A) Create Temporary File
	dw	func5B			; (5B) Create New File
	dw	func5C			; (5C) File Lock Control
	dw	func5D			; (5D) Internal DOS Function
	dw	func5E			; (5E) Control Local Machine Data
	dw	func5F			; (5F) Get Network Assignments
	dw	func60			; (60) Perform Name Processing
	dw	ms_zero_AL		; (61) ?? Parse Path (AL = 0)
	dw	func62			; (62) Get Current PSP
	dw	func63			; (63) Get Lead Byte Table
	dw	invalid_function	; (64) *Saves AL and Returns
	dw	func65			; (65) Get Extended Country Information
	dw	func66			; (66) Get/Set Global Code Page
	dw	func67			; (67) Set Handle Count
	dw	func68			; (68) Commit File
	dw	func69			; (69) Get Serial number
	dw	func68			; (6A) Commit File (again)
	dw	invalid_function	; (6B) Unknown DOS 4
	dw	func6C			; (6C) Extended Open/Create
pcmode_ftl	equ	(offset $ - offset pcmode_ft)/2
	;**************************
	;* Do Not Move This Entry *
	;**************************
	dw	ms_zero_AL		; Illegal Function Handler

	end
