Replying to a message of Darryl Gregorash to All:
%^^(*@Q*$*^$#*Q$)Q&$...
The previous functions turn out to be inefficient, because of the lack of a
variable to contain the number of elements in the queue.. without such a
variable, the queue can hold at most LEN - 1 elements before the "full"
condition is triggered. In addition, using such a variable obviously makes
detecting the full and empty conditions much easier.
Here are the improved, and preferred, versions. Note that it is now possible
to perform both reads and writes by incrementing the head or tail pointer
after the read/write operation.
PUBLIC Q_PTR, Q_HEAD, Q_TAIL, Q_NUM
Q_PTR DD
Q_HEAD DB 0
Q_TAIL DB 0
Q_NUM DB 0
Q_LEN EQU
QUEUE DB LEN DUP 0
read_char proc far
; uses EAX
; returns: carry clear with character read returned in AL, if no error
carry flag set, if queue empty when function called
cmp Q_NUM, 0 ; check if buffer empty
jbe empty ; do not read character if buffer empty
push ds ; preserve DS:BX+SI
push si
push bx
lds bx, Q_PTR ; load far pointer to QUEUE in DS:BX
mov si, Q_HEAD ; and ensure DS:BX+SI points to next character
to read
mov al,[bx][si] ; read the character into AL and decrement
_NUM
dec Q_NUM
; Now determine the new value of Q_HEAD.
; Incrementing the value of Q_HEAD is done in SI, because it is always faster
; to perform algebraic operations on registers than directly on memory
operands
inc si
cmp si, Q_LEN
jb no_wrap ; if SI >= Q_LEN, we must wrap to the head of
the
; physical buffer
sub si, Q_LEN ; perform the wrap by subtraction
no_wrap:
mov Q_HEAD, si ; store incremented value of queue head
pop bx ; restore registers
pop si
pop ds
clc
retf
empty:
stc
retf
read_char endp
Here is the function to write a character to the queue. .
write_char proc far
; called with character to be written to queue in AL
; returns: carry not set - character successfully written to queue
; carry set - queue was full when function called.
cmp Q_NUM, LEN ; check for queue full condition
jae full
push ds ; preserve DS:BX+SI
push si
push bx
lds bx, Q_PTR ; load far pointer to QUEUE in DS:BX
mov si, Q_TAIL ; and ensure DS:BX+SI points to next empty slot
mov [bx][si], al ; write the character into the queue
; and increment SI and Q_NUM
inc Q_NUM
; Now determine the new value of Q_TAIL. Again it is faster to perform the
algebra
; using si.
inc si
cmp si, Q_LEN ; check for wrap condition si >= Q_LEN
jb no_wrap2
sub si, Q_LEN ; wrap the pointer
no_wrap2:
mov Q_TAIL, si
pop bx ; restore registers
pop si
pop ds
clc
retf
full:
stc
retf
write_char endp
Note that this function still does not do exactly what Benjamin McGee
requested, ie. writes to the queue are not possible if the queue is full.
Anyone really wishing to emulate a UART should do the following: remove the
"cmp Q_HEAD, si" and "jae full" instructions; remove the label "full"; remove
the 2 instructions which follow that label; and finally, remove the
now-redundant CLC instruction.
--- FleetStreet 1.21 NR
---------------
* Origin: BIG BANG Burger Bar: Regina SK Canada (1:140/86)
|