home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
ddkx86v5.zip
/
DDKX86
/
SRC
/
DEV
/
ATCOM
/
ATQUEUE.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-04-14
|
18KB
|
510 lines
;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT Copyright (C) 1995 IBM Corporation
;
; The following IBM OS/2 WARP source code is provided to you solely for
; the purpose of assisting you in your development of OS/2 WARP device
; drivers. You may use this code in accordance with the IBM License
; Agreement provided in the IBM Device Driver Source Kit for OS/2. This
; Copyright statement may not be removed.;
;*****************************************************************************/
; SCCSID = @(#)atqueue.asm 6.3 91/04/22
; ***************************************************************************
; *
; *
; *
; ***************************************************************************
PAGE ,132
.286p
TITLE com01.sys - Asynchronous Communication Device Driver
NAME com01
; Bryan Diehl
; David Gilman
;*** atqueue.asm
;
; The device driver's internal buffers are maintained as circular
; queues. This file contains the routines necessary to access and
; maintain these queues. There are routines to transfer data to
; and from user space, as well as routines used by the interrupt
; handlers. These routines have been specialized to convert physical
; to virtual addresses and to recognize when flow control states
; need to be changed.
;
; The routines that exist in this file are:
;
; + ReadQueueByte - read a single byte from the output queue
; + WriteQueueByte - write a single byte to the input queue
; + ReadQueue - read a specified number of bytes into user space
; + WriteQueue - write a specified number of bytes from user space
; + CopyQueue - support routine for ReadQueue and WriteQueue
;
; Modification History
;
; DJG 01//16/87 Re-written to use the circular buffer
; code lifted from dos\pipe.asm
;
; YN 05/25/89 MVDM Support - @VDM
;
; ACW 04/16/91 @PVW Added perfview counters/timers
;
; WDM 04/21/94 82548 - pvwxport.inc now included in atcom.inc
;
.xlist
; 82548 include pvwxport.inc ;@PVW
include devhlp.inc
include devsym.inc
include basemaca.inc
include realmac.inc
.list
include atcom.inc
extrn DisableRemoteTX:near
extrn EnableRemoteTX:near
EXTRN DevHlp:DWORD ; DevHlp entry point
CSEG SEGMENT
ASSUME cs:CSEG
;** ReadQueueByte - read a single byte from the qout IO_Queue
;
; ReadQueueByte removes one character from the output queue
; and returns it to the caller in (al).
;
; ENTRY (ds:si) -> ComInfo
;
; EXIT if 'C' clear
; (al) = byte
; ioq_count is decremented by one
; else
; queue is empty
;
; USES bx
Procedure ReadQueueByte,HYBRID
ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING
ChkComInfoPtr
mov bx,[si].ci_qout.ioq_out
cmp bx,[si].ci_qout.ioq_in
stc ; assume the queue is empty
je rqbx ; queue is empty (out == in)
mov al,[bx] ; (al) = byte
dec [si].ci_qout.ioq_count ; adjust count
jnz rqb10 ; didn't take last char
or [si].ci_event,EV_TX_EMPTY ; show that txq emptied
rqb10: inc bx ; adjust out pointer
cmp [si].ci_qout.ioq_end,bx ; clears carry (end >= bx)
jne rqb20 ; did not hit end of queue
; Hit the end of the queue, wrap out pointer (out == base).
mov bx,[si].ci_qout.ioq_base ; (bx) = base of queue
rqb20: mov [si].ci_qout.ioq_out,bx ; reset the output pointer
rqbx: ret
EndProc ReadQueueByte
;** WriteQueueByte - write a single byte to the qin IO_Queue
;
; WriteQueueByte takes a single character in (al) and puts
; it into the input queue.
;
; If a Last Close is in progress, nothing is done.
;
; The read timeout countdown is reset.
; The byte is put into the queue. However if the queue
; is actually full the pointers are not adjusted. This can be
; done since a 'full' queue has one empty slot (used to differentiate
; between full and empty).
;
; ENTRY (ds:si) -> ComInfo
; (al) = byte
;
; EXIT if Last Close in progress
; return
; reset read timeout countdown
; if queue is not full
; byte enqued
; ioq_count is incremented by one
; if queue is above highwater mark
; DisableRemoteTX
; else queue is full
; if ErrChar Replacement enabled
; last character in queue is replaced with
; the error replacement character
;
; USES ax bx dx
Procedure WriteQueueByte,HYBRID
ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING
ChkComInfoPtr
test [si].ci_flagx,FX_LAST_CLOSE
jnz wqbx ; last close in progress, drop character
; reset timeout anytime a character is put into the receive queue
mov bx,[si].ci_r_to_start ; get start value for timeout
mov [si].ci_r_to,bx ; reset timeout countdown
mov bx,[si].ci_qin.ioq_in
mov [bx],al ; put byte in queue
inc bx ; adjust in pointer
cmp bx,[si].ci_qin.ioq_end
jne wqb1 ; did not wrap
mov bx,[si].ci_qin.ioq_base ; wrapped
wqb1: cmp bx,[si].ci_qin.ioq_out
je wqb2 ; (in + 1) == out
; queue was already full
mov [si].ci_qin.ioq_in,bx ; update in pointer
inc [si].ci_qin.ioq_count ; adjust count
or [si].ci_event,EV_RX_CHAR ; flag rx character
; If standard port, we must handle flow control: if the receive queue is
; above the high water mark, we must disable the remote transmitter.
cmp [si].ci_qin.ioq_count,RX_HIGH_HS
jbe wqbx ; queue is below high water mark for HS
call DisableRemoteTX ; may send XOFF and/or drop HW HS lines
wqbx: ret
; If the queue is full then we may have to show the user that there
; was a queue overrun. That is, we may have to perform error character
; replacement.
wqb2: or [si].ci_comerr,CE_SW_OVERRUN ; flag rx queue overrun
ifdef PERFVIEW
pvw_SW_Overrun ;@PVW increment perfview cntr
endif
or [si].ci_event,EV_ERR ; flag rx queue overrun
test [si].ci_dcb_flags2,F2_ERR_CHAR
jz wqbx ; error replacement not on
mov bx,[si].ci_qin.ioq_in ; (bx) = in pointer
cmp bx,[si].ci_qin.ioq_base
jne wqb3 ; we didn't wrap when placing
; the byte in the queue
mov bx,[si].ci_qin.ioq_end ; 'unwrap' back to end
wqb3: dec bx ; (bx) -> last byte in queue
mov al,[si].ci_dcb_ErrChar
mov [bx],al ; replace it with the error
; character
jmp SHORT wqbx
EndProc WriteQueueByte
;** ReadQueue - read from the qin IO_Queue and write to user memory
;
; ReadQueue will transfer no more than (cx) bytes from the input
; queue to user memory. It performs the necessary address conversion
; as well as recognition of when flow control needs to be changed.
;
; ENTRY (ds:si) -> ComInfo
; (es:di) -> Read Request packet
; (cx) = byte count
;
; EXIT (bx) = number actually moved
; (cx) = residual byte count
;
; NOTE On exit (bx) + (cx) = (cx) on input.
;
; USES ax bx dx
Procedure ReadQueue,HYBRID
ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING
ChkComInfoPtr
ChkRPPtr
; Get destination of transfer.
SaveReg <es,di>
; mov ax,es:[di].IoPData._hi
; mov bx,es:[di].IoPData._lo ; (ax:bx)= physical xfer address
; mov dx,(0100h OR DevHlp_PhysToVirt)
; DevHelp
; jnc rdq00
;
; ComErr <ReadQueue : PhysToVirt invalid address>
les di,es:[di].IoPData ; (es:di)-> virtual xfer address
rdq00: SaveReg <cx>
mov bx,cx ; (bx) = byte count
rdq0: mov cx,[si].ci_qin.ioq_in
sub cx,[si].ci_qin.ioq_out
jz rdq5 ; buffer empty, go return
jnc rdq2 ; can transfer up to in ptr
mov cx,[si].ci_qin.ioq_end
sub cx,[si].ci_qin.ioq_out ; can only xfer to end of buffer
rdq2: cmp cx,bx
jbe rdq3 ; xfer len smaller than byte count
mov cx,bx ; don't xfer more than byte count
rdq3: push si ; (tos) = ComInfo
mov si,[si].ci_qin.ioq_out ; (si) = offset part of buffer ptr
; (ds:si) -> source of data
; (es:di) -> dest for copy
; (cx) = byte count for this copy
sub bx,cx ; (bx) = bytes remaining after copy
call CopyQueue ; (ds:si) is adjusted
; (es:di) is adjusted
; (cx) = zero
mov cx,si ; (cx) = offset of new out ptr
pop si ; (ds:si) -> ComInfo
ChkComInfoPtr
cmp cx,[si].ci_qin.ioq_end
jb rdq4 ; didn't hit end of buffer
mov cx,[si].ci_qin.ioq_base ; hit end, pointer wraps around
rdq4: mov [si].ci_qin.ioq_out,cx ; save the new pointer
or bx,bx
jnz rdq0 ; more bytes to xfer
; Release virtual address
rdq5:
; mov dl,DevHlp_UnPhysToVirt
; DevHelp
RestoreReg <cx> ; (cx) = requested transfer
RestoreReg <di,es> ; (es:di) -> request packet
ASSUME es:NOTHING
ChkRPPtr
sub cx,bx ; (cx) = actual transfer
add es:[di].IOpData._lo,cx ; adjust physical address pointer
; adc es:[di].IOpData._hi,0
sub [si].ci_qin.ioq_count,cx; adjust ioq_count
xchg cx,bx ; (cx) = residual byte count
; (bx) = actual transfer
mov [si].ci_r_to_move,cx ; update number left to move
; If port is STANDARD, may have to flow on remote transmitter.
rdq90: cmp [si].ci_qin.ioq_count,RX_LOW_HS
jae rdqx ; not below low water mark for HS
SaveReg <cx>
call EnableRemoteTX ; below low water, enable remote TXer
RestoreReg <cx> ; (cx) = residual byte count
rdqx: ret
EndProc ReadQueue
;** WriteQueue - read from user memory and write to the qout IO_Queue
;
; WriteQueue will transfer no more than (cx) bytes from user memory
; into the output queue. It performs the necessary address conversion.
;
; ENTRY (ds:si) -> ComInfo
; (es:di) -> Write Request packet
; (cx) = byte count
;
; EXIT (bx) = number actually moved
; (cx) = residual byte count
;
; NOTE On exit (bx) + (cx) = (cx) on input.
;
; USES ax dx si
Procedure WriteQueue,HYBRID
ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING
ChkComInfoPtr
ChkRPPtr
SaveReg <si>
SaveReg <es,di>
; Get physical address of xfer source
; mov ax,es:[di].IoPData._hi
; mov bx,es:[di].IoPData._lo ; (ax:bx) = physical addr of user data
;
; push ds
; pop es
; ASSUME es:DSEG
; xchg si,di
;
; mov dx,(0000 OR DevHlp_PhysToVirt)
; DevHelp
; jnc wrq00
;
; ComErr <WriteQueue : PhysToVirt invalid address>
push si
push ds
lds si,es:[di].IoPData ; (ds:si) -> virtual xfer source
pop es
pop di ; (es:di) -> ComInfo
ASSUME es:DSEG
wrq00: SaveReg <cx>
mov bx,cx ; (bx) = byte count
; calculate length of transfer
; there are two cases:
; 1. in ptr is < out ptr
; in this case, we can only put (out - in) - 1 bytes in
; 2. in ptr >= out ptr
; In this case, we can only xfer to end of buffer.
; If in == out == base, we must leave last byte
; empty.
wrq0: mov cx,es:[di].ci_qout.ioq_in
mov dx,es:[di].ci_qout.ioq_out
sub cx,dx ; (cx) = -(out - in)
jb wrq1 ; in < out, can only go to (out - 1)
add cx,dx ; (cx) = ioq_in
sub cx,es:[di].ci_qout.ioq_end
cmp dx,es:[di].ci_qout.ioq_base
jne wrq2
wrq1: inc cx ; 'subtract' from negated count
wrq2: neg cx ; (cx) = transfer count
jcxz wrq5 ; no room, return
cmp cx,bx
jbe wrq3 ; xfer len smaller than request
mov cx,bx ; don't xfer more than requested
; (es:di) -> ComInfo
; (cx) = byte count for this xfer
; (ds:si) -> source
wrq3: push di
mov di,es:[di].ci_qout.ioq_in ; (es:di) -> dest
; (ds:si) -> source
; (cx) = byte count for copy
sub bx,cx ; (bx) = bytes remaining
; after copy
call CopyQueue ; (ds:si) is adjusted
; (es:di) is adjusted
; (cx) = zero
mov cx,di ; (cx) = offset part of
; new out ptr
pop di ; (es:di) -> ComInfo
cmp cx,es:[di].ci_qout.ioq_end
jb wrq4 ; didn't hit end of buffer
mov cx,es:[di].ci_qout.ioq_base ; hit end, handle wrap around
wrq4: mov es:[di].ci_qout.ioq_in,cx ; and save the new pointer
or bx,bx
jnz wrq0 ; more bytes to xfer
; Release virtual address
wrq5: mov ax,es
mov ds,ax ; (ds) -> DD data
ASSUME ds:DSEG
; mov dl,DevHlp_UnPhysToVirt
; DevHelp
RestoreReg <cx> ; (cx) = requested transfer
sub cx,bx ; (cx) = actual transfer
add es:[di].ci_qout.ioq_count,cx ; adjust ioq_count
RestoreReg <di,es> ; (es:di) -> request packet
RestoreReg <si> ; (ds:si) -> ComInfo
ChkComInfoPtr
ChkRPPtr
add es:[di].IOpData._lo,cx ; adjust physical address pointer
; adc es:[di].IOpData._hi,0
xchg cx,bx ; (cx) = byte count
; (bx) = actual transfer
mov [si].ci_w_to_move,cx ; update number left to move
ret
EndProc WriteQueue
;** CopyQueue - move contents of memory
;
; We copy bytes from one place to another, optimizing for
; word alignment (word aligned moves are faster).
;
; ENTRY (ds:si) -> source
; (es:di) -> destination
; (cx) = byte count
;
; EXIT (ds:si) -> one past last byte copied in source
; (es:di) -> one past last byte copied in dest
; (cx) = 0
;
; NOTE Move is assumed to be non-overlapping
; Length is assumed to be non-zero
;
; USES si di cx flags
Procedure CopyQueue,NEAR
ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING
jcxz cpqer
test si,1
jz cpq1 ; source is even
test di,1
jz cpq1 ; dest is even
movsb ; both are odd, copy one byte to make both even
dec cx ; and adjust the count
jz cpq2 ; it was only one byte
cpq1: shr cx,1 ; word count, carry set if count was odd
rep movsw ; mov cx words (doesn't jam carry flag)
jnc cpq2 ; count was even, we're done
movsb
cpq2: ret
cpqer:
ComErr <CopyQueue : count is zero.>
EndProc CopyQueue
CSEG ENDS
END