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)
|