mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 07:54:28 +00:00
524 lines
15 KiB
Plaintext
524 lines
15 KiB
Plaintext
title 'F_DOS Character device I/O'
|
|
; File : $CDEVIO.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: $
|
|
; CDEVIO.A86 1.18 94/12/21 11:16:29
|
|
; Changed dev_dup to refuse from duplicating remote devices rather
|
|
; than refuse from duplicating FCBs
|
|
; Made open_dev return IFN rather than XFN for FCB opens
|
|
; Made open_dev put the current time/date into DHNDL_TIME/DHNDL_DATE
|
|
; CDEVIO.A86 1.16 94/10/07 09:00:18
|
|
; Added patch 004 as source fix. Changed request header length for character
|
|
; device to 16h. Used RH4_CDEV_LEN instead of RH4_LEN.
|
|
; CDEVIO.A86 1.15 93/12/10 00:09:17
|
|
; Move non-inherited bit to correct place in file handle
|
|
; CDEVIO.A86 1.14 93/11/26 16:17:14
|
|
; Update char_error so ES:SI -> device driver header itself;
|
|
; ENDLOG
|
|
|
|
eject ! include i:psp.def
|
|
eject ! include i:modfunc.def
|
|
eject ! include i:fdos.equ
|
|
eject ! include i:mserror.equ
|
|
eject ! include i:doshndl.def
|
|
eject ! include i:driver.equ
|
|
eject ! include rh.equ
|
|
|
|
BDOS_DATA dseg
|
|
|
|
extrn fdos_buf:byte ; caveat: in PCMODE data segment
|
|
extrn fdos_pb:word
|
|
extrn fdos_ret:word
|
|
|
|
;
|
|
; Critical Error responses from the default INT 24 handler and
|
|
; the DO_INT24 routine.
|
|
;
|
|
ERR_IGNORE equ 0 ; Ignore Error
|
|
ERR_RETRY equ 1 ; Retry the Operation
|
|
ERR_ABORT equ 2 ; Terminate the Process
|
|
ERR_FAIL equ 3 ; Fail Function
|
|
|
|
BDOS_CODE cseg
|
|
|
|
public open_dev
|
|
public close_dev
|
|
public dup_dev
|
|
public read_dev
|
|
public write_dev
|
|
public first_dev
|
|
public ioc6_dev
|
|
public ioc7_dev
|
|
|
|
extrn cooked_write:near ; write to cooked console
|
|
extrn read_line:near ; read edited line
|
|
extrn break_check:near ; look for CTRL C
|
|
extrn char_error:near ; generate Int 24
|
|
|
|
extrn current_dsk2al:near ; get default drive in AL
|
|
extrn device_driver:near
|
|
extrn alloc_dhndl:near ; find free DHNDL_ structure
|
|
extrn alloc_xfn:near
|
|
extrn les_di_dmaptr:near
|
|
extrn release_handle:near
|
|
extrn get_xftptr:near
|
|
extrn timestamp_dhndl:near
|
|
|
|
|
|
; Character device functions
|
|
; ==========================
|
|
|
|
open_dev:
|
|
;--------
|
|
; Entry: ES:BX -> character device header
|
|
|
|
; Note: We own the MXdisk here, fdos_pb -> parameter data
|
|
|
|
push es ! push bx ; save device driver address
|
|
call alloc_xfn ; DI = XFN
|
|
push di ; save XFN
|
|
call alloc_dhndl ; ES:BX -> DHNDL, AX = IFN
|
|
pop dx ; DX = XFN
|
|
xchg ax,si ; SI = IFN
|
|
xor ax,ax
|
|
lea di,DHNDL_DATRB[bx] ; point at start of area to fill
|
|
mov cx,DHNDL_UID-DHNDL_DATRB
|
|
rep stosb
|
|
mov es:DHNDL_SHARE[bx],ax ; also zap share word
|
|
pop es:DHNDL_DEVOFF[bx]
|
|
pop es:DHNDL_DEVSEG[bx] ; save device driver address
|
|
|
|
push si ! push dx ; save IFN/XFN
|
|
|
|
mov es:DHNDL_COUNT[bx],1
|
|
push ds
|
|
mov ax,fdos_pb+6 ; AX = open mode
|
|
mov cx,DHAT_DEV+DHAT_CLEAN+DHAT_TIMEOK
|
|
lds si,es:DHNDL_DEVPTR[bx] ; DS:SI -> device driver
|
|
or cl,ds:byte ptr DH_ATTRIB[si]
|
|
; get attrib from device driver
|
|
test al,DHM_LOCAL ; is it private ?
|
|
jz open_dev10
|
|
and al,not DHM_LOCAL ; clear inherit bit
|
|
or ch,DHAT_LOCAL/256 ; rememmber it's local
|
|
open_dev10:
|
|
mov es:DHNDL_MODE[bx],ax
|
|
mov es:DHNDL_WATTR[bx],cx
|
|
lea si,DH_NAME[si] ; copy the device name
|
|
lea di,DHNDL_NAME[bx] ; from the device header
|
|
mov cx,8/WORD
|
|
rep movsw
|
|
mov al,' '
|
|
mov cl,3 ; space the file extension
|
|
rep stosb
|
|
pop ds
|
|
|
|
pop dx ! pop ax ; AX = IFN, DX = XFN
|
|
|
|
push es
|
|
call get_xftptr ; ES:DI -> PSP_XFTPTR for current_psp
|
|
jc open_dev20 ; no PSP, skip updating XFN
|
|
add di,dx ; ES:DI -> entry in PSP
|
|
stosb ; update entry in PSP
|
|
xchg ax,dx
|
|
open_dev20:
|
|
pop es
|
|
|
|
mov fdos_ret,ax ; save XFN (IFN for FCB open) to return
|
|
call timestamp_dhndl
|
|
or es:DHNDL_ATTR[bx],DHAT_READY
|
|
; jmp open_dup_dev
|
|
|
|
open_dup_dev:
|
|
;------------
|
|
; On Entry:
|
|
; ES:BX = DHNDL_
|
|
; On Exit:
|
|
; None
|
|
;
|
|
mov al,CMD_DEVICE_OPEN
|
|
; jmp open_close_dev ; call device open routine
|
|
|
|
open_close_dev:
|
|
;--------------
|
|
; entry: ES:BX = DHNDL_
|
|
; AL = cmd_type (CMD_DEVICE_OPEN/CMD_DEVICE_CLOSE)
|
|
;
|
|
push ds ! push es
|
|
push bx ! push si
|
|
lds si,es:DHNDL_DEVPTR[bx] ; DS:SI -> device driver
|
|
test ds:DH_ATTRIB[si],DA_REMOVE
|
|
jz ocdev1 ; does the device support OPEN/CLOSE/RM
|
|
sub sp,RH13_LEN-2*word ; make space on stack for RH_
|
|
push ax ; RH_CMD = AL
|
|
mov ax,RH13_LEN
|
|
push ax ; RH_LEN = RH13_LEN
|
|
push ss ! pop es
|
|
mov bx,sp ; ES:BX -> RH_
|
|
call device_driver ; call the device driver
|
|
add sp,RH13_LEN ; recover stack space
|
|
ocdev1:
|
|
pop si ! pop bx
|
|
pop es ! pop ds
|
|
ret
|
|
|
|
dup_dev:
|
|
;-------
|
|
; On Entry:
|
|
; ES:BX -> DHNDL_
|
|
; On Exit:
|
|
; None
|
|
; AX trashed
|
|
;
|
|
mov ax,es:DHNDL_WATTR[bx]
|
|
test al,DHAT_DEV
|
|
jz dup_dev10 ; skip if disk file
|
|
test ax,DHAT_REMOTE ; or remote
|
|
jz open_dup_dev
|
|
dup_dev10:
|
|
ret
|
|
|
|
|
|
close_dev: ; close character device handle
|
|
;---------
|
|
; entry: FDOS_PB+2 = user file handle
|
|
; ES:BX = file handle
|
|
; NOTE: This is called with the MXdisk owned
|
|
|
|
mov al,CMD_DEVICE_CLOSE
|
|
call open_close_dev ; call device close routine
|
|
call release_handle ; release the XFN
|
|
dec es:DHNDL_COUNT[bx] ; one less XFN refers to this IFN
|
|
ret
|
|
|
|
|
|
write_dev: ; write to character device handle
|
|
;---------
|
|
; entry: ES:BX -> DHNDL_ structure
|
|
;
|
|
|
|
mov cl,CMD_OUTPUT ; OUTPUT driver function
|
|
or es:DHNDL_ATTR[bx],DHAT_READY
|
|
mov al,es:DHNDL_ATTR[bx] ; get file info
|
|
and al,DHAT_BIN+DHAT_NUL+DHAT_CIN+DHAT_COT
|
|
cmp al,DHAT_CIN+DHAT_COT ; is it cooked console?
|
|
jne inst_io ; no, device driver i/o
|
|
mov si,2[bp] ; SI -> parameter block
|
|
mov cx,ds:8[si] ; CX = string length
|
|
jcxz write_dev20 ; exit if nothing to write
|
|
les di,ds:4[si] ; ES:DI -> string to print
|
|
mov al,'Z'-40h ; we have to stop at ^Z
|
|
repne scasb ; scan for ^Z character
|
|
jne write_dev10 ; skip if ^Z not found
|
|
inc cx ; include ^Z in count of chars to skip
|
|
sub ds:8[si],cx ; subtract from total count
|
|
write_dev10:
|
|
mov bx,ds:2[si] ; BX = handle number
|
|
mov cx,ds:8[si] ; CX = string length
|
|
mov si,ds:4[si] ; ES:SI -> string to print
|
|
call cooked_write ; write w/ tab expansion & ^C checking
|
|
write_dev20:
|
|
sub bx,bx ; no errors
|
|
ret ; return the result
|
|
|
|
|
|
|
|
read_dev: ; read to character device handle
|
|
;--------
|
|
; entry: ES:BX -> DHNDL_ structure
|
|
|
|
mov al,es:DHNDL_ATTR[bx] ; get the file info
|
|
mov ah,al ; save ioctl info
|
|
and al,DHAT_BIN+DHAT_CIN+DHAT_COT
|
|
cmp al,DHAT_CIN+DHAT_COT ; is it cooked console?
|
|
jne rddev1 ; skip if binary or not console
|
|
jmp read_con ; read from console handle
|
|
; return the result
|
|
rddev1:
|
|
test ah,DHAT_READY ; previous EOF ?
|
|
jnz rddev2 ; yes we return now
|
|
mov di,2[bp] ; DI -> parameter block
|
|
mov ds:word ptr 8[di],0 ; zero bytes xfered
|
|
ret
|
|
rddev2:
|
|
mov cl,CMD_INPUT
|
|
inst_io:
|
|
; ES:BX = DHNDL_, CL = command
|
|
sub sp,RH4_CDEV_LEN ; make RH_ on stack
|
|
mov di,bx ; save address DHNDL_ in DI
|
|
mov bx,sp ; SS:BX -> request header
|
|
|
|
mov ds:RH_CMD[bx],cl
|
|
mov ds:RH_LEN[bx],RH4_CDEV_LEN
|
|
mov ds:RH_STATUS[bx],0 ; status OK in case of zero chars
|
|
mov si,2[bp] ; DS:SI -> parameter block
|
|
lea si,4[si] ; point at buffer offset
|
|
lodsw ; get buffer offset
|
|
; Normalising the address has been unnecessary so far
|
|
; push ax
|
|
; and ax,15 ; normalise the address
|
|
; pop cx
|
|
; shr cx,1 ! shr cx,1
|
|
; shr cx,1 ! shr cx,1
|
|
mov ds:RH4_BUFOFF[bx],ax ; set buffer offset in request header
|
|
lodsw ; get buffer segment
|
|
; add ax,cx ; add in normalised offset/16
|
|
mov ds:RH4_BUFSEG[bx],ax ; get buffer segment in request header
|
|
lodsw ; get byte count
|
|
xchg ax,cx ; byte count in CX
|
|
|
|
; Parameter block created on stack at SS:BX and initialised for xfer
|
|
; ES:DI -> DHNDL_, CX = total number of bytes to xfer
|
|
|
|
inst_io20:
|
|
mov ds:RH4_COUNT[bx],cx ; try and do this many
|
|
test es:DHNDL_ATTR[di],DHAT_BIN+DHAT_NUL
|
|
; are we in binary mode ?
|
|
jcxz inst_io30 ; return on zero length xfer
|
|
jnz inst_io25 ; binary, skip calling PCMODE
|
|
mov ds:RH4_COUNT[bx],1 ; do one char at a time
|
|
call break_check ; call the break check routine
|
|
cmp ds:RH_CMD[bx],CMD_OUTPUT ; which way are we going
|
|
jne inst_io25
|
|
call inst_io_getchar ; AL = 1st char in the buffer
|
|
cmp al,1Ah ; EOF - don't send it or anything after
|
|
je inst_io30 ; and exit without xfering any
|
|
inst_io25:
|
|
push ds ! push es ! push di ! push cx
|
|
lds si,es:DHNDL_DEVPTR[di] ; DS:SI -> device driver
|
|
push ss ! pop es ; ES:BX -> RH_
|
|
call device_driver ; execute the command
|
|
pop cx ! pop di ! pop es ! pop ds
|
|
jns inst_io_continue ; if no errors carry on
|
|
push es
|
|
les si,es:DHNDL_DEVPTR[di] ; ES:SI -> device driver
|
|
call char_error ; this will handle the Int 24
|
|
pop es
|
|
cmp al,ERR_RETRY ; what should we do ?
|
|
je inst_io20 ; retry the operation
|
|
ja inst_io30 ; fail - return error
|
|
mov ds:RH_STATUS[bx],RHS_DONE
|
|
jmps inst_io_ignore ; ignore - fiddle status and
|
|
inst_io_continue: ; say we did it all
|
|
mov dx,ds:RH4_COUNT[bx] ; how many did we xfer ?
|
|
test dx,dx ; if we haven't done any
|
|
jz inst_io30 ; we are stuck so exit now
|
|
inst_io_ignore:
|
|
call inst_io_getchar ; AL = 1st char in the buffer
|
|
add ds:RH4_BUFOFF[bx],dx ; it may not enough so adjust offset
|
|
sub cx,dx ; and number still to do
|
|
cmp ds:RH_CMD[bx],CMD_INPUT ; which way are we going - if input
|
|
jne inst_io20 ; we need to check for CR/EOF
|
|
test es:DHNDL_ATTR[di],DHAT_BIN+DHAT_NUL
|
|
jnz inst_io30 ; if BIN then exit now
|
|
cmp al,13 ; is it a CR character ?
|
|
je inst_io30 ; yes, we stop now
|
|
cmp al,1Ah ; is it the EOF character ?
|
|
jne inst_io20 ; yes, we aren't ready
|
|
and es:DHNDL_ATTR[di],not DHAT_READY
|
|
inst_io30:
|
|
mov di,2[bp] ; DI -> parameter block
|
|
sub ds:8[di],cx ; subtract # not xfered from byte count
|
|
|
|
mov ax,ds:RH_STATUS[bx] ; get result for later
|
|
sub bx,bx ; assume no errors
|
|
test ax,ax ; test error bit (8000h)
|
|
jns rddev_no_err ; skip if ERROR set
|
|
mov bl,al ; AL is error code
|
|
neg bx ; make it negative code
|
|
add bx,ED_PROTECT ; normalize for extended errors
|
|
rddev_no_err:
|
|
add sp,RH4_CDEV_LEN ; free up RH_ on stack
|
|
ret ; return BX
|
|
|
|
inst_io_getchar:
|
|
push ds
|
|
lds si,ds:RH4_BUFFER[bx] ; point to the users buffer
|
|
lodsb ; get 1st char in the buffer
|
|
pop ds
|
|
ret
|
|
|
|
|
|
read_con: ; handle read from cooked console
|
|
;--------
|
|
; entry: AH = DHNDL_ATTR
|
|
; ES:BX -> DHNDL_
|
|
; 2[BP] -> F_DOS parameter block
|
|
; exit: BX = return value
|
|
|
|
xor cx,cx ; assume we've already had EOF
|
|
test ah,DHAT_READY ; now see if we have
|
|
jnz con_dev_not_eof
|
|
jmp con_dev_exit ; yes, just return zero chars read
|
|
con_dev_not_eof:
|
|
push es
|
|
push bx ; save DHNDL_
|
|
con_dev_loop:
|
|
mov bx,word ptr fdos_buf ; get # of bytes already used
|
|
xor ax,ax
|
|
xchg al,bh ; get # bytes in the buffer
|
|
inc ax ! inc ax ; also count the CR/LF
|
|
sub ax,bx ; have we any bytes left in the buffer?
|
|
ja con_dev_cont ; yes, return them
|
|
; no, we need a fresh input line
|
|
mov fdos_buf,128 ; read up to 128 characters
|
|
mov si,2[bp] ; SI -> parameter block
|
|
mov bx,ds:2[si] ; BX = input handle
|
|
push ds ! pop es
|
|
mov dx,offset fdos_buf ; ES:DX -> console buffer
|
|
mov cx,bx ; output to same handle as input
|
|
push bx
|
|
push bp
|
|
call read_line ; read edited line
|
|
pop bp
|
|
mov bl,fdos_buf+1 ; # byte we have read
|
|
mov bh,0 ; BX = # of characters read
|
|
mov word ptr fdos_buf+2[bx],0A0Dh; append carriage return/line feed
|
|
mov fdos_buf,0 ; start reading at beginning
|
|
lea si,fdos_buf+3[bx] ; Echo the LF character to the
|
|
pop bx ; Same Output handle
|
|
mov cx,1 ; Only One Character
|
|
call cooked_write
|
|
jmps con_dev_loop
|
|
|
|
con_dev_cont: ; BX = buffer offset
|
|
mov di,2[bp] ; DI -> parameter block
|
|
mov cx,ds:8[di] ; CX = # of bytes to read
|
|
cmp cx,ax ; reading more than available?
|
|
jbe con_dev_ok ; no, read as much as you want
|
|
mov cx,ax ; else take all that there is
|
|
con_dev_ok:
|
|
add fdos_buf,cl ; update buffer index for next time
|
|
les di,ds:4[di] ; ES:DI -> buffer to read into
|
|
lea si,fdos_buf+2[bx] ; DS:SI -> function 10 buffer
|
|
push cx ; save count
|
|
rep movsb ; read all the data
|
|
pop cx ; restore count
|
|
mov al,1Ah ; now we look for EOF mark...
|
|
push ds ! pop es
|
|
lea di,fdos_buf+2[bx] ; ES:DI -> function 10 buffer
|
|
mov si,cx ; keep count safe
|
|
repne scasb
|
|
xchg cx,si ; restore count
|
|
pop bx ; recover DHNDL_
|
|
pop es
|
|
jne con_dev_exit ; if no EOF, skip to exit
|
|
sub cx,si ; subtract any after EOF mark
|
|
dec cx ; and the EOF mark itself
|
|
and es:DHNDL_ATTR[bx],not DHAT_READY
|
|
con_dev_exit:
|
|
mov di,2[bp] ; DI -> parameter block
|
|
mov ds:8[di],cx ; set # of characters read
|
|
sub bx,bx ; good return code
|
|
ret
|
|
|
|
|
|
|
|
|
|
eject
|
|
|
|
first_dev: ; F_DOS FIRST call on device performed
|
|
;--------- ; Called with MXDisk
|
|
; On Entry:
|
|
; ES:BX -> device header
|
|
; On Exit:
|
|
; dma_buffer initialised with device name
|
|
;
|
|
mov dx,es ; DX:BX -> device header
|
|
call les_di_dmaptr ; ES:DI -> DMA buffer
|
|
mov al,0FFh ; invalidate search state for NEXT
|
|
mov cx,21
|
|
rep stosb
|
|
mov al,40h ; mark it as a device
|
|
stosb
|
|
sub ax,ax
|
|
mov cx,4
|
|
rep stosw ; zero time, date, file size
|
|
|
|
lea si,10[bx]
|
|
push ds
|
|
mov ds,dx ; DS:SI -> name in device header
|
|
mov cx,4
|
|
rep movsw ; copy device name across
|
|
pop ds
|
|
|
|
mov cx,8
|
|
frstdev1: ; scan off trailing spaces
|
|
cmp es:byte ptr 0-1[di],' '
|
|
jne frstdev2
|
|
dec di
|
|
loop frstdev1
|
|
frstdev2:
|
|
mov al,0
|
|
stosb ; add a trailing NUL
|
|
ret
|
|
|
|
|
|
eject
|
|
|
|
ioc6_dev: ; IOCTL(6) - input status for device
|
|
;--------
|
|
; entry: ES:BX -> DHNDL_
|
|
|
|
mov al,CMD_INPUT_NOWAIT
|
|
jmps ioc67d ; call the device driver
|
|
|
|
|
|
ioc7_dev: ; IOCTL(7) - output status for device
|
|
;--------
|
|
; entry: ES:BX -> DHNDL_
|
|
|
|
mov al,CMD_OUTPUT_STATUS ; OUTPUT STATUS
|
|
ioc67d: ; common code for I/O STATUS
|
|
push ds
|
|
sub sp,RH5_LEN-2*word ; allocate request header on stack
|
|
push ax ; RH_CMD = AL
|
|
mov ax,RH5_LEN
|
|
push ax ; RH_LEN = RH5_LEN
|
|
lds si,es:DHNDL_DEVPTR[bx] ; DS:SI -> device driver
|
|
push ss ! pop es
|
|
mov bx,sp ; ES:BX -> RH_
|
|
mov es:RH5_CHAR[bx],0 ; zero the char
|
|
call device_driver ; do the CALLF's to the device driver
|
|
xor dl,dl ; assume not ready
|
|
mov dh,es:RH5_CHAR[bx] ; possible peeked character
|
|
add sp,RH5_LEN ; recover stack space
|
|
pop ds
|
|
test ax,RHS_ERROR+RHS_BUSY ; test if BUSY bit set in status
|
|
jnz ioc67d_ret ; device not ready if error or busy
|
|
dec dl ; return ready DL = FF
|
|
ioc67d_ret:
|
|
mov si,2[bp] ; SI -> user's parameter block
|
|
mov ds:6[si],dx ; update returned status
|
|
sub bx,bx ; no error occurred
|
|
ret ; for now
|
|
|
|
end ; of CDEVIO.A86
|