TIP: Click on subject to list as thread! ANSI
echo: 80xxx
to: ALL
from: DARRYL GREGORASH
date: 1998-01-20 16:16:00
subject: circular buffer

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)

SOURCE: echomail via exec-pc

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.