mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 16:04:20 +00:00
1069 lines
29 KiB
Plaintext
1069 lines
29 KiB
Plaintext
; 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
|