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

Replying to a message of Darryl Gregorash to All:
 DG> means the way the LDS instruction is used here is
 DG> inefficient or else the LODS instruction should not be
 DG> used, but I make no attempt right now to improve things in
 DG> this regard. I'll take a closer look at this later.
Well, this actually turns out to be much easier than I thought.. the LODS 
instruction is not the best instruction to use except on a 8086/88 (and 
perhaps also a 286, where MOV reg, mem and LODS require the same number of 
clocks) because it is far slower than a MOV on the 386 and up. This is 
particularly true for the 486 and Pentium.
Here I use base-index addressing in a MOV instruction.
BUF_PTR    DD
BUF_HEAD   DB  0
BUF_TAIL   DB  0
LEN EQU 
BUFFER  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 BUF_HEAD, BUF_TAIL        ; check if buffer empty
je   empty                    ; do not read character if buffer empty
push ds                       ; preserve DS:BX+SI
push si
push bx
lds bx, BUF_PTR               ; load far pointer to BUFFER in DS:BX
mov si, BUF_HEAD              ; and ensure DS:BX+SI points to next character 
to read
mov al,[bx][si]               ; read the character into AL and increment SI
; incrementing the value in BUF_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, LEN    
jb   no-wrap                  ; if SI >= LEN, we must wrap to the head of the
                              ; physical buffer
sub si, LEN                   ; perform the wrap by subtraction
mov BUF_HEAD, si              ; store incremented value of queue head
no-wrap:
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. Structurally, it is 
much the same as the read function, except that here, BUF_TAIL must be 
incremented prior to checking for a buffer full condition.
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.
push si
mov si, BUF_TAIL
inc si
cmp si, LEN                   ; check for wrap condition si >= LEN
jb no_wrap2
sub si, LEN
no_wrap2:
cmp BUF_HEAD, si              ; check if buffer was already full
je   full                     ; do not write character if buffer full
push ds                       ; preserve DS:BX
push bx
lds bx, BUF_PTR               ; load far pointer to BUFFER in DS:BX
mov [bx][si], al              ; write character in AL to the queue
pop bx
pop ds
pop si
clc 
retf
full:
pop si
stc 
retf
write_char endp
Note that this function 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 BUF_HEAD, 
si" and "je full" instructions; remove the label "full"; remove the 3 
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™.