;    File              : $CIO.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: $
;    CIO.A86 1.20 94/12/01 10:05:21
;    Made cooked_write and is_device aware of FCB writes;    
;    CIO.A86 1.19 94/06/28 12:21:07
;    Fix last_key_ext bug
;    CIO.A86 1.18 94/05/12 14:06:22
;    The routine cooked_status now sets a flag last_key_ext if the keycode is 0.
;    On entry, it checks this flag to see if the last one was 0, and if so does
;    not do the checks for the control keys. In this way, Alt-R and Alt-Q can
;    be distinguished from Ctrl-S and Ctrl-P.
;    CIO.A86 1.17 93/12/21 17:58:15
;    Preserve BX round clock read
;    Update char_error so DS:SI -> device driver header itself
;    CIO.A86 1.10 93/05/06 19:28:03
;    Move int 23/28 support to CIO.
;    Read the clock in idle_dev, not int 28 loop.
;    CIO.A86 1.9 93/05/05 23:30:44
;    int 2A/84 is now only generated on input-and-wait functions
;    CIO.A86 1.8 93/03/25 15:05:56
;    tweak console block output
;    ENDLOG
;
;	This module contains all the Character I/O functions used by PCMODE
;
; 12 Nov 87 Disable Control-Break when the Console Output mode is RAW
; 24 Feb 88 Display Control characters correctly. ie "^X"
; 23 May 88 Support ^S to Pause screen output.
; 25 May 88 Support Control-P for Cooked_Write and remove Kanji Character
;           check.
; 26 May 88 Check for CTLC on CON_DEV when character input is redirected.
;           Correctly detect EOF on redirected input.
; 17 Aug 88 Call PRN device with Open/Close on ^P
; 30 Aug 88 Jump to correct exit when Open/Close is not supported by a 
;           device driver for ^P.
; 14 Sep 88 Break checking should only be carried out when the INDOS_FLAG
;           is 1. (Novell and Cntrl-C).
; 03 Sep 88 Return the character output by INT21/04,05,06 in AL.
; 10 Nov 88 Preserve ES when calling any Device Driver (Revalation DEVDRVR)
; 15 Dec 88 Check STDERR for Control-C if it is a Device.
; 15 Mar 89 Check for CTLC during Cooked Write.
; 16 Mar 89 Explicitly allow INT 28 during char reads (SmartNotes bug)
; 25 Apr 89 Execute break_check after getting the console status INT21/0B
;  2 May 89 Save Device driver STRAT and INT address's on the stack
; 10 May 89 Now check keyboard more often during cooked write
; 25 May 89 Move INT28 flag to PCMIF.PCM
;  6 Sep 89 Enter/Exit critical region round device request
; 26 Oct 89 saving some bytes again...
; 25 Jan 90 Insert IDLE Detection Code
; 29 Jan 90 Int 2A critical section support added to device_callf
;  7 Mar 90 Convert to register preserved function calls
; 27 Mar 90 cooked_write checks STD_OUT for ctl-s ctl-p etc (STD_IN may
;           have been redirected)
; 29 Mar 90 preserve BX round device_callf (3+Share CLOCK$ bug)
; 12 Jun 90 get_doshndl parameter BX not AX
; 15 Oct 90 Added support for Return Interim Character flag (see PSP_RIC).
; 26 Oct 90 handle PSP=0 (ie. FCB device I/O)
;  1 mar 91 break_check now goes to con_device, not STDERR
; 17 jun 91 ij	fix to cooked_out to avoid status checks if STDOUT redirected

	include	pcmode.equ
	include i:driver.equ
	include	i:reqhdr.equ
	include	i:msdos.equ
	include	i:fdos.equ
	include	i:psp.def
	include	i:mserror.equ
	include	i:char.def
	include	i:redir.equ
	include	i:doshndl.def

CIO_CTLP	equ	0000$0001b	; Printer Echo State
CIO_HANDLE	equ	0000$0010b	; use handle rather than Int 29
CIO_RAW		equ	0000$0100b	; no "cooked_status" checks

CHECK_EVERY	equ	80		; check keyboard every "n" characters

PCM_CODE	CSEG	BYTE
	extrn	char_error:near
	extrn	device_driver:near
	extrn	dos_entry:near
	extrn	get_dseg:near
	extrn	ifn2dhndl:near
	extrn	int21_entry:near
	extrn	int21_func:near
	extrn	read_line:near
	extrn	ReadTimeAndDate:near
	extrn	reload_registers:near

;	*****************************
;	***    DOS Function 01    ***
;	***  Keybd Input W/Echo   ***
;	*****************************
;
	Public	func01
func01:
;
; Entry:
;	AH  ==	01h
; Exit:
;	AL  ==	char
;
	call	func08			; Read 1 character from Standard Input
					; and check for Control-C
	xchg	ax,dx			; echo using common code

;	*****************************
;	***    DOS Function 02    ***
;	***     Display Output    ***
;	*****************************
;
	Public	func02
func02:
;
; Entry:
;	AH  ==	02h
;	DL  ==	char to display
;
	push	dx			; char on stack
	push ss ! pop es
	mov	si,sp			; ES:DX -> character
	mov	cx,1
	call	stdout_cooked_write	; write character
	pop	ax			; recover char
	ret

;	*****************************
;	***    DOS Function 03    ***
;	***    Auxiliary Input    ***
;	*****************************
;
	Public	func03
func03:
;
; Entry:
;	AH  ==	03h
; Exit:
;	AL  ==	Char
;
	mov	bx,STDAUX		; Read 1 character from Standard AUX
f03_10:
	jmp	raw_read

;	*****************************
;	***    DOS Function 04    ***
;	***    Auxiliary Output   ***
;	*****************************
;
	Public	func04
func04:
;
; Entry:
;	AH  ==	04h
;	DL  ==	Character to output
;
	mov	bx,STDAUX		; write the character passed in DL
	jmps	f456common		;  to the STDAUX Handle

;	*****************************
;	***    DOS Function 05    ***
;	***    Printer Output     ***
;	*****************************
;
	Public	func05
func05:
;
; Entry:
;	AH  ==	05h
;	DL  ==	character to output to printer
;
	mov	bx,STDPRN		; write the character passed in DL
;	jmp	f456common		;  to the STDPRN Handle

f456common:
	xchg	ax,dx			; character in AL
;	jmp	hndl_write

hndl_write:
;----------
; On Entry:
;	AL = character to write
;	BX = handle
; On Exit:
;	AL preserved
;
	call	is_device		; Does this handle refer to a device
	 jc	hndl_w10
	test	es:DH_ATTRIB[si],DA_SPECIAL
	 jz	hndl_w10		; Fast Console Output Using Int 29?
	int	29h			; This device supports FAST console
	ret				;  output so write this using Int29

hndl_w10:
	push	ax			; character on stack
	mov	dx,sp			; SS:DX -> char
	mov	cx,1			; do a single character
	 jc	hndl_w20		; was it a file ?
	call	device_write		; send to device driver
	jmps	hndl_w30
hndl_w20:
	push ss ! pop es		; ES:DX -> character
	mov	ah,MS_X_WRITE		; otherwise call the FDOS to do all
	call	dos_entry		;  the hard work
hndl_w30:
	pop	ax
	ret


;	*****************************
;	***    DOS Function 06    ***
;	***   Direct Console I/O  ***
;	*****************************
;
	Public	func06
func06:
;
; Entry:
;	AH  ==	06h
;	DL  ==	0FFh or Output char
; Exit:
;	AL  ==	Input char, if DL was 0FFh on input
;
	mov	bx,STDOUT			; Assume output DL to console
	cmp	dl,0FFH				; or is it input ?
	 jne	f456common
	dec	bx
;	mov	bx,STDIN			; is there a character ready
	call	char_check			;  to be input
	 jz	func07
	mov	ax,RHS_IC			; set AL=0 and also set ZF on
	jmps	funcICexit			;  exit as incomplete char


;	*****************************
;	***    DOS Function 07    ***
;	***   Raw Input w/o echo  ***
;	*****************************
;
	Public	func07
func07:
;
; Entry:
;	AH  ==	07h
; Exit:
;	AL  ==	character
;
	mov	bx,STDIN
	call	raw_read			; extra status call made
	jmps	funcICexit			; set incomplete char
	
;	*****************************
;	***    DOS Function 08    ***
;	***    Input w/o echo     ***
;	*****************************
;
	Public	func08
func08:
;
; Entry:
;	AH  ==	08h
; Exit:
;	AL  ==	character
;
	mov	bx,STDIN		; Read 1 character from Standard Input
	call	cooked_read
funcICexit:
; exit point for incomplete character support
; On Entry:
;	AL = character
;	AH = request header status (RHS_IC as on return from device driver)
; On Exit:
;	AL = character
;	dos_FLAGS ZF set if incomplete character
;
	les	di,int21regs_ptr		; point to callers registers
	and	es:reg_FLAGS[di],not ZERO_FLAG	; clear ZF
	test	ah,RHS_IC/256			; is it an incomplete char ?
	 jz	funcIC10			;  no - exit
	or	es:reg_FLAGS[di],ZERO_FLAG	;  yes - set ZF
funcIC10:
	ret

;	*****************************
;	***    DOS Function 09    ***
;	***     Print String      ***
;	*****************************
;
	Public	func09
func09:
;
; Entry:
;	AH  ==	09h
;    DS:DX  ==	address of character string
;

	mov	al,'$'			; it's terminated with a '$'
	mov	di,dx			; locate the end of the string 
	mov	cx,0FFFFh		; and calculate its length
	repnz	scasb
	not	cx
	dec	cx			; CX is the character count
	mov	si,dx
	call	stdout_cooked_write	; ES:SI -> character buffer
    mov al,'$'          
    ret


;	*****************************
;	***    DOS Function 0A    ***
;	***      Read String      ***
;	*****************************
;
	Public	func0A
func0A:
;
; Entry:
;	AH  ==	0Ah
;    DS:DX  ==	pointer to input buffer
;
	mov	bx,STDIN		; Read the editted line from STDIN
	mov	cx,STDOUT		; and display the results on STDOUT
	jmp	read_line		; Read the Line

;	*****************************
;	***    DOS Function 0B    ***
;	***    Console Status     ***
;	*****************************
;
	Public	func0B
func0B:
;
; Entry:
;	AH  ==	0Bh
; Exit:
;	AL  ==	0FFh if char available
;	    ==	 00h otherwise
;
	mov	bx,STDIN
	call	cooked_status		; Get the current handle status
	mov 	al,0FFh			; Assume that the handle is ready
	 jz	f0B_exit		; and return 0FFh in AL
	mov	al,00			; Not Ready
f0B_exit:
	jmps	funcICexit		; exit thru incomplete char support



;	*****************************
;	***    DOS Function 0C    ***
;	***    Flush and Execute  ***
;	*****************************
;
	Public	func0C
func0C:
;
; Entry:
;	AH  ==	0Ch
;	AL  ==  function to execute:  1,6,7,8 or A
; Exit:
;	AL = 0 if function in AL is invalid
;
	push	ax			; save sub-function
	mov	bx,STDIN		; Is this Standard Input Handle a
	call	is_device		;  file or device. Do not flush the 
	 jc	f0C_20			;  buffer contents for a FILE
f0C_10:
	call	hndl_instat		; check if any characters are left
	 jnz	f0C_20			;  and quit when buffer empty
	call	raw_read		; read the character
	jmps	f0C_10			; loop till the buffer is empty

f0C_20:
	pop	ax
	cmp al,01h ! je	al_ok		; is legal for this command
	cmp al,0ah ! je	al_ok
	cmp al,06h ! jb	al_nogo
	cmp al,08h ! ja	al_nogo

al_ok:					; Valid function so now execute
	call	reload_registers	; all register reloaded as per entry
	mov	ah,al			; Get the requested sub-function in AH
	jmp	int21_func		; execute the function

al_nogo:				; Illegal command to execute
	xor	ax,ax			; from this function so return error
	ret

eject
;
;	BREAK_CHECK checks for a CNTRL-C and is called by functions 01h to 
;	0Ch. Or by the entry code if the break flag is non zero.
;
	Public	break_check
break_check:
	cmp	indos_flag,01		; Skip the check if we are
	 jnz	break_c15		; already in the emulator
	push	ax
	push	es
	push	si
	les	si,con_device
	call	device_instat		; get the input status
	pop	si
	pop	es
	 jnz	break_c10		; No Character Ready
	cmp	al,CTLC			; Is the character a Control-C
	 jz	break_c20		; Yes
break_c10:
	pop	ax
break_c15:
	ret

break_c20:				; The User has Typed Control-C so flush
	mov	bx,0FFFFh		;  input buffer (FFFF=con_device)
	call	char_get
go_int23:
	push cs ! pop es		; ES:DX -> Character Buffer
	mov	si,offset cntrl_c_msg	; Message Offset
	mov	cx,length cntrl_c_msg	; Message Length
	call	stdout_cooked_write	; write the ^C String to console
;
;	Prepare to execute an Interrupt 23 (Break Check) and process
;	the return values. If the called routine returns with an IRET
;	or with a RETF and the carry flag reset continue the function
;	otherwise Abort.
;
	mov	es,current_psp		; Get the Entry SS and SP
	mov	ax,PSP_USERSP		; Get the Users Stack Pointer
	add	ax,18 - 2		; Compensate for the User Registers
	mov	break_sp,ax		; and save for RETF check
	cli
	dec	indos_flag		; Exit the PCDOS emulator
	mov	ss,PSP_USERSS		; Switch to the Users Stack
	mov	sp,PSP_USERSP		; and Restore the registers

	POP$DOS				; Update the registers then
					; set the flags and return
					; to the user
	clc				; Default to continue function
	int	23h			; Call the Break Handler
	cli				; Check the Flag State
	 jnc	do23_10			; If CARRY then Abort this process
	call	get_dseg		; Get our data segment
	mov	exit_type,TERM_BREAK	; Force EXIT_TYPE to TERM_BREAK
	mov	ax,4C00h		; "Good-Bye Cruel World" 
    jmps    do23_20
    do23_10:
	push	ds			; Otherwise restart the aborted func
	call	get_dseg
	cmp	sp,break_sp
	pop	ds			; Restore the the USER DS correct
	 jz	do23_30			; Did we Use a RETF or Not
do23_20:
	add	sp,2			; Yes so correct the stack pointer
do23_30:				; and restart the aborted function
	jmp	int21_entry		; re-start the function call



eject
;
; cooked_status is called on input or output and looks for live keys ^C,^P,^S.
; If any of these are found they are dealt with.
; If ^P is encountered it is swallowed.
; If ^C is encountered we always do an Int23.
; If ^S is pressed we swallow it, and the next character (checking for ^C, but
; not for ^P), then say a character is ready.
; Note that this can lead to status calls (func0B) hanging inside the OS,
; or the return of ^S characters from input calls (func01), but this is
; intentional.
;

cooked_status:
;-------------
; check input
; On Entry:
;	BX = handle to check
; On Exit:
;	ZF set if character available
;	AL = character
;	AH = RHS_IC
;
	call	break_check		; check for a ^C on console
	call	char_check		; is there a character ready
	 jnz	cooked_s50		;  no so keep scanning

	cmp	last_key_ext,0		; was last char an zero ?
	mov	last_key_ext,0		; (clear flag for next time)
	 jne	cooked_s40		; skip ^P,^S,^C checks if so
	
	cmp	al,CTLP			; has the user typed ^P
	 jne	cooked_s10		;  flush the buffer and
	xor	cio_state,CIO_CTLP	;  toggle ^P flag
	call	char_get		;  flush the character from buffer
	call	open_or_close_prn	;  open/close printer device
	test	ax,ax			; ZF clear, ie. no char available
	jmps	cooked_s50

cooked_s10:
	cmp	al,CTLC
	 jnz	cooked_s30		; has the user typed ^C
	call	char_get		; so get the RAW character
cooked_s20:
	jmp	go_int23		; and terminate the function

cooked_s30:
	cmp	al,CTLS			; pause if the user has typed
	 jnz	cooked_s40		;  a ^S
	call	char_get		; remove ^S and resume when
	call	raw_read_wait		; the next character is typed
	cmp	al,CTLC
	 je	cooked_s20		; has the user typed ^C
cooked_s40:
	cmp	al,0
	 jne	cooked_s45
	mov	last_key_ext,1
cooked_s45:
	cmp	ax,ax			; ZF set, ie. char available
cooked_s50:
	ret
	
eject
;
;	The COOKED, CMDLINE and RAW Read functions are basically the same
;	except in their treatment of 'live' characters ^C,^P, and ^S.
;	COOKED will look for and act upon all three live characters.
;	CMDLINE will look for and act upon ^C and ^P, but ^S will be returned
;	so we can use it as a line editing key.
;	RAW will not check for any live keys.
;
	public	cmdline_read, raw_read	; for CMDLINE.PCM

cmdline_read_wait:			; Waiting for a device to become
	call	idle_dev		; ready. So call IDLE routines to
					; put the processor to sleep.
cmdline_read:
	call	break_check		; check for a ^C on console
	call	char_check		; is there a character ready
	 jnz	cmdline_read_wait	;  no so keep scanning
	cmp	al,CTLS			; if the user has typed ^S
	 jne	cooked_read		;  we have to do a raw read
;	jmps	raw_read		;  else we do a cooked read

raw_read_wait:				; Waiting for a device to become
	call	idle_dev		; ready. So call IDLE routines to
					; put the processor to sleep.
raw_read:
	call	char_check		; Is there a character Ready
	 jnz	raw_read_wait		; loop until character available
	jmps	char_get

cooked_read_wait:			; Waiting for a device to become
	call	idle_dev		; ready. So call IDLE routines to
					; put the processor to sleep.
cooked_read:
	call	break_check		; check for a ^C on console
	call	cooked_status		; check for a ^S,^P,^C on handle BX
	 jnz	cooked_read_wait	; wait until char is available
;	jmps	char_get		;  else get the character

char_get:
	push es ! push ax		; Input one character and
	mov	dx,sp			;  return it in AL
	call	is_device		; Does this handle refer to a device
	mov	cx,1
	 jc	char_get30		; if it's a device then
	call	device_read		;  use device_read
char_get20:
	pop ax ! pop es
	ret

char_get30:
; We are redirected, so call to the FDOS to get a character
	push ss ! pop es		; EX:DX -> character to read
	mov	ah,MS_X_READ		; call the FDOS to do all
	call	dos_entry		;  the hard work
	jmps	char_get20

eject

stdout_cooked_write:
	mov	bx,STDOUT		; output to the console device
;	jmp	cooked_write

;
;	The COOKED_WRITE routine will expand TABS etc in the string
;	passed passed by the calling routine. 
;
;	On Entry:
;		ES:SI		Buffer Address
;		CX		Character Count
;		BX		Output Handle
;	On Exit:
;		AL = last char written
;
	Public cooked_write
cooked_write:
	push	es
	push	bx
	mov	ah,cio_state		; get CIO_CTLP status
	or	ah,CIO_RAW+CIO_HANDLE	; assume we will want raw handle output
	mov	al,bl
	test	byte ptr remote_call+1,DHM_FCB/100h
	 jnz	cook_w03
	mov	es,current_psp		; get our PSP
	cmp	bx,PSP_XFNMAX		; range check our handle
	 jae	cook_w05
	les	di,PSP_XFTPTR
	mov	al,es:byte ptr [bx+di]	; AL = Internal File Handle
cook_w03:
	call	ifn2dhndl		; ES:BX -> DHNDL_
	 jc	cook_w05		; skip if bad handle
	mov	dx,es:DHNDL_WATTR[bx]	; get handle attributes
	and	dx,DHAT_DEV+DHAT_CIN+DHAT_COT+DHAT_BIN+DHAT_REMOTE
	cmp	dx,DHAT_DEV+DHAT_CIN+DHAT_COT+DHAT_BIN
	 je	cook_w04		; accept binary console device
	cmp	dx,DHAT_DEV+DHAT_CIN+DHAT_COT
	 jne	cook_w05		; skip if not cooked console device
	and	ah,not CIO_RAW		; we want cooked output
cook_w04:
	les	bx,es:DHNDL_DEVPTR[bx]	; its the console - but is it FAST ?
	test	es:DH_ATTRIB[bx],DA_SPECIAL
	 jz	cook_w05		; skip if not
	and	ah,not CIO_HANDLE	; don't use handle functions
cook_w05:
	pop	bx
	pop	es
     jcxz   cook_w80        
cook_w10:
	lods	es:al			; Read the next character
	cmp al,DEL ! je cook_w60	; DEL is a NON Printing Character
	cmp al,' ' ! jae cook_w50	; Space and Above are Normal
	cmp al,LF  ! je cook_w60	; Just print LineFeeds
	cmp al,ESC ! je cook_w60	; Just print Escape
	cmp al,BELL! je cook_w60	; Just ring the Bell
	cmp al,CR  ! jne cook_w20	; CR zeros the column number
	mov	column,0
	mov	char_count,1		; check for ^S etc NOW
	jmps	cook_w60
cook_w20:
	cmp al,CTLH ! jne cook_w30	; BackSpace decrements the
	dec	column			; column count by one
	jmps	cook_w60
cook_w30:
	cmp al,TAB ! jne cook_w60	; is it a TAB ?
cook_w40:
	mov	al,' '			;  spaces
	call	cooked_out		; output a space char
	inc	column
	test	column,7		; are we at a TAB stop yet ?
	 jnz	cook_w40
	jmps	cook_w70
cook_w50:
	inc	column			; Update the column count and
cook_w60:
	call	cooked_out		;  output the character
cook_w70:
	loop	cook_w10		; onto the next character
cook_w80:
	ret

cooked_out:
; On Entry:
;	AH = handle status
;	AL = character
;	BX = handle
; On Exit:
;	AX, BX, CX, ES:SI preserved
;
	dec	char_count		; time to check keyboard input ?
	 jz	cooked_o10		;  no, skip status check
	test	ah,CIO_HANDLE+CIO_CTLP	; is it complicated ?
	 jnz	cooked_o10
	int	29h			; This device supports FAST console
	ret

cooked_o10:
	push	es
	push	ax
	push	cx
	push	si
	call	hndl_write		; display the character
	test	ah,CIO_CTLP		; Check for Printer Echo
	 jz	cooked_o20		; Off so No Echo
	push	bx			; Save Output Handle
	mov	bx,STDPRN		; and output the same data to the
	call	hndl_write		; to the Printer Handle
	pop	bx
cooked_o20:
	test	ah,CIO_RAW		; is it a cooked console ?
	 jnz	cooked_o30		; skip check if not
	call	cooked_status		; look for keyboard input
	mov	char_count,CHECK_EVERY	; look again in a while
cooked_o30:
	pop	si
	pop	cx
	pop	ax
	pop	es
	ret

eject
;	IDLE_DEV is called when the PCMODE is waiting for a character.
;	This routine must determine if the request is for a device or not
;	and call the IDLE interface for device requests to the system can be
;	put to sleep until a character is ready.
;
;	On Entry:-	BX Handle Number
;	
idle_dev:
	push	bx			; preserve handle
	mov	ax,8400h
	int	2ah			; Server hook for idle
	dec	clock_count
	 jnz	idle_dev10		; Zero if NO skip delay and execute
	call	ReadTimeAndDate		; for PC BIOS's who must read every day
idle_dev10:
if IDLE_DETECT
	test	idle_flags,IDLE_DISABLE	; Has Idle Checking been enabled
	 jnz	idle_dev40		; Skip if NO
	push es ! push si
	call	is_device		; The requested handle a file or device
	 jc	idle_dev30		; File Access skip IDLE
	mov	ax,PROC_KEYIN		; Assume this is the REAL Console
	test	es:DH_ATTRIB[si],DA_ISCIN; Test Attribute Bits
	 jnz	idle_dev20		; Yes this is Default Console Device
	mov	ax,PROC_DEVIN		; Input from Another Device
idle_dev20:
	callf	idle_vec		; Call the IDLE Handler
idle_dev30:
	pop si ! pop es
idle_dev40:
endif
	pop	bx			; recover handle
	ret

eject
;	The following routine reads CX bytes from the device whose address 
;	is held in the DWORD pointer passed by DS:SI. A Request Header 
;	is built on the stack and the command is executed.
;
;	On Entry:
;		ES:SI		DWORD Pointer to Device Header
;		SS:DX		Buffer Address
;		CX		Character Count
;
;	On Exit:
;		AX		Request Header Status
;		Zero		No Error
;
	Public	device_read
device_read:
	mov	al,CMD_INPUT		; we want input
	jmps	device_common		; now use common code

eject
;	The following routine writes CX bytes to the device whose address 
;	is held in the DWORD pointer passed by DS:SI. A Request Header 
;	is built on the stack and the command is executed.
;
;	On Entry:
;		ES:SI		DWORD Pointer to Device Header
;		SS:DX		Buffer Address
;		CX		Character Count
;
;	On Exit:
;		AX		Request Header Status
;		Zero		No Error
;
	Public	device_write
device_write:
	mov	al,CMD_OUTPUT		; we want output
device_common:
	push	bx
	sub	sp,RH4_LEN		; reserve space on the stack
	mov	bx,sp			; request header offset
	mov	ss:RH_LEN,RH4_LEN	; request header length
	mov	ss:RH4_BUFOFF,dx	; buffer offset
	mov	ss:RH4_BUFSEG,ss	; buffer segment
device_common10:
	mov	ss:RH4_COUNT,cx		; character count
	call	device_req		; execute command
	 jns	device_common20		; if no errors return to the caller
	sub	cx,ss:RH4_COUNT		; CX = chars remaining
	push	ax			; save the error code
	call	char_error		; ask int 24 what to do
	cmp	al,ERR_RETRY		; should we retry the operation ?
	pop	ax			; recover the error code
	 ja	device_common20		; Fail/Abort return error
	mov	al,ss:RH_CMD		; reload the command
	 je	device_common10		; Retry, re-issue the device request
	mov	ax,RHS_DONE		; Ignore, pretend no errors
device_common20:
	add	sp,RH4_LEN		; restore the stack to its normal
	test	ax,RHS_ERROR		;  state and return the status.	
	pop	bx
	ret


char_check:
; On Entry:
;	BX = handle to check
; On Exit:
;	ZF set if character ready
;	AL = character (if device handle)
;	AH = RIC status
;
	push	bx			; Save the current handle status
if IDLE_DETECT
	test	idle_flags,IDLE_DISABLE	; Has Idle Checking been enabled
	 jnz	char_check10		; Skip if NO
	dec	int28_delay		; Has the INT28 Loop count reached
	 jnz	char_check10		; Zero if NO skip delay and execute
	mov	ax,int28_reload		; INT28. Otherwise DELAY/DISPATCH
	mov	int28_delay,ax
	mov	ax,PROC_INT28		; Process is IDLE
	callf	idle_vec		; Call the IDLE Handler
char_check10:
endif
	cmp	indos_flag,1		; Only execute an INT 28
	 jnz	char_check20		; when the INDOS flag is 1
	cmp	int28_flag,TRUE		; Only generate INT 28s for the
	 jnz	char_check20		; selected functions
	
	push	remote_call
	push	machine_id
	mov	es,current_psp		; Get the PSP
	push	PSP_USERSP		; Save the SS:SP pointer to 
	push	PSP_USERSS		; the register image

if IDLE_DETECT				; Set IDLE_INT28 so $IDLE$ knows
	or	idle_flags,IDLE_INT28	; that we are nolonger inside DOS
endif
	int	28h			; Execute an INT 28 for SideKick and
					; the PRINT utility. INDOS flag is 1

if IDLE_DETECT				; Reset IDLE_INT28 so $IDLE$ knows
	and	idle_flags,not IDLE_INT28; that we are back DOS
endif
	mov	int28_flag,TRUE		; Restore INT28_FLAG
	mov	es,current_psp		; Get the PSP
	pop	PSP_USERSS		; Restore the SS:SP pointer to 
	pop	PSP_USERSP		; the register image
	pop	machine_id
	pop	remote_call
char_check20:
	pop	bx
;	jmp	hndl_instat		; Check Input Status. ZERO == Ready

;
;
hndl_instat:
	call	is_device		; Does this handle refer to a device
	 jnc	device_instat
	mov	ax,(MS_X_IOCTL shl 8)+6	; Get the file status
	call	dos_entry		; for the specified handle
	cmp	al,0FFh			; and return ZERO until the EOF
	ret

;	The following routine executes the Non Destructive Input
;	command to the device whose address passed in ES:SI.
;
;	On Entry:
;		ES:SI		DWORD Pointer to Device Header
;
;	On Exit:
;		Zero		Character Ready
;		AH		Top Byte Request Header Status
;		AL		Next Character if ZERO
;

device_instat:
	push	bx
	sub	sp,RH5_LEN		; Reserve Space on the Stack
	mov	bx,sp			; Request Header Offset
	mov	ss:RH_LEN,RH5_LEN	; Set Request Header Length
	mov	al,CMD_INPUT_NOWAIT	; Command Number
	call	device_req		; Execute the Command
	mov	al,ss:RH5_CHAR		; Assume a character is ready
	add	sp,RH5_LEN		; Restore the Stack to its normal
	test	ax,RHS_BUSY		; state and return the status.	
	pop	bx			; Zero if a Character is ready
	ret

;	The following routine handles the low level device interface to
;	the character device drivers. All the generic Request Header 
;	initialization is carried out here.
;
;	On Entry:
;		AL		Command
;		ES:SI		Device Header
;		SS:BX		Current Request Header
;
;	On Exit:
;		AX		Request Header Status
;

device_req:
;----------
	mov	ss:RH_CMD,al		; save the command
	push	ds
	push	es
	push es ! pop ds		; DS:SI -> device driver
	mov	es,ss:current_psp	; es = current PSP
	mov	al,es:PSP_RIC		; al = Return Interim Character flag
	mov	ss:RH4_RIC,al		; Return Interim Char flag
	push ss ! pop es		; ES:BX -> RH_
	call	device_driver
	pop	es
	pop	ds
	ret

eject
;
;	IS_DEVICE checks the internal handle structures to determine
;	if the handle referenced in BX is a file or device. Invalid
;	handles all map to the default console device.
;
; On Entry:
;	BX	Handle Number
;
; On Exit:
;	CY set if handle is for a file
;	CY clear if handle is for device at ES:SI
;
is_device:
	push	ax
	push	bx			; Convert the Standard Handle number
	mov	ax,bx			; get XFN in AL
;	mov	cx,current_psp		; into an internal handle number
;	 jcxz	is_dev10		; no PSP, we have IFN already
;	mov	es,cx
	test	byte ptr remote_call+1,DHM_FCB/100h; if FCB initiated access
	 jnz	is_dev10		; we have IFN already
	mov	es,current_psp
	cmp	bx,es:PSP_XFNMAX	; Check if the handle is in range for
	 jae	is_dev_bad		; this PSP.
	les	si,es:PSP_XFTPTR
	mov	al,es:byte ptr [bx+si]	; AL = Internal File Handle
is_dev10:
	call	ifn2dhndl		; ES:BX -> DHNDL_
	 jc	is_dev_bad
	mov	ax,es:DHNDL_WATTR[bx]	; get file attributes
	and	ax,DHAT_REMOTE+DHAT_DEV
	cmp	ax,DHAT_DEV		; is it a local device ?
	stc				; assume it's a file
	 jne	is_dev30
	les	si,es:DHNDL_DEVPTR[bx]	; its a device
is_dev20:
	clc
is_dev30:
	pop	bx
	pop	ax
	ret

is_dev_bad:
	les	si,con_device		; bad handles map to console
	jmps	is_dev20

open_or_close_prn:
;-----------------
; called when CIO_CTLP toggled - call prn device with Open or Close as appropriate
;
	push ds ! push ax ! push bx
	mov	ax,CTLP
	push	ax			; ^P on stack
	mov	cx,current_psp		; look in PSP
	 jcxz	oc_prn30		; no PSP, forget it
	mov	es,cx
	cmp	bx,PSP_XFNMAX		; Check if the handle is in range for
	 jae	oc_prn30		; this PSP. 
	les	si,es:PSP_XFTPTR	; for the internal handle number
	mov	al,es:byte ptr STDPRN[si]
	cmp	al,0FFh			; AL = Internal File Handle
	 je 	oc_prn30		;  skip if invalid Handle Number
	call	ifn2dhndl		; ES:BX -> doshndl
	 jc	oc_prn30
	test	es:DHNDL_WATTR[bx],DHAT_NETPRN
	 jz	oc_prn10
	mov	ax,I2F_CTLP		; turn on the network printer
	int	2fh			; with a magic INT 2F call
	 jnc	oc_prn10
	and	cio_state,not CIO_CTLP	; make sure Printer Echo is off
	mov	ax,I2F_CTLP_ERR
	int	2fh
	jmps	oc_prn30
oc_prn10:
	mov	ax,es:DHNDL_WATTR[bx]	; get file attributes
	and	ax,DHAT_REMOTE+DHAT_DEV
	cmp	ax,DHAT_DEV		; is it a local device ?
	 jne	oc_prn30
	mov	al,CMD_DEVICE_OPEN	; assume we've just opened
	test	cio_state,CIO_CTLP	; Check for Printer Echo
	 jnz	oc_prn20		; yes, skip next bit
	mov	al,CMD_DEVICE_CLOSE	; no, we must close
oc_prn20:
	les	si,es:DHNDL_DEVPTR[bx]	; get the device driver address
	test	es:DH_ATTRIB[si],DA_REMOVE
	 jz	oc_prn30		; no, skip call if not supported
	sub	sp,RH13_LEN		; Reserve Space on the Stack
	mov	bx,sp			; and point to it
	mov	ss:RH_LEN,RH13_LEN	; Set Request Header Length
	mov	ss:RH_CMD,al		; Command Number
	call	device_driver		; issue the command
	add	sp,RH13_LEN		; Restore the Stack to its normal
oc_prn30:
	pop	ax			; discard ^P from stack
	pop bx ! pop ax ! pop ds
	ret
	
	
PCM_RODATA	CSEG	WORD

cntrl_c_msg	db	'^C', CR, LF	; Control-Break Message

GLOBAL_DATA	dseg

clock_count	db	0

PCMODE_DATA	DSEG	WORD

	extrn	break_sp:word		; For Control-Break handler
	extrn	char_count:byte
	extrn	cio_state:byte		; Character I/O State
	extrn	column:byte		; Console Cursor Location
	extrn	con_device:dword	; Current Console Device
	extrn	current_psp:word	; Current PSP Address
	extrn	exit_type:byte
	extrn	last_key_ext:byte
	extrn	indos_flag:byte		; INDOS Count
	extrn	int21regs_ptr:dword	; pointer to callers registers
	extrn	machine_id:word
	extrn	remote_call:word
if IDLE_DETECT
	extrn	idle_flags:word		; IDLE State Flags
	extrn	idle_vec:dword		; IDLE routine Vector
	extrn	int28_delay:word
	extrn	int28_reload:word
	extrn	int28_flag:byte
endif

	end
