Use backtick code blocks for syntax highlighting
This commit is contained in:
parent
8ed9dea394
commit
f658bfbb40
203 changed files with 26546 additions and 25801 deletions
358
16-05.md
358
16-05.md
|
|
@ -36,181 +36,183 @@ Table: Table 16.2 The top four word-counting entries.
|
|||
|
||||
**LISTING 16.5 QSCAN3.ASM**
|
||||
|
||||
; QSCAN3.ASM
|
||||
; David Stafford
|
||||
|
||||
|
||||
COMMENT $
|
||||
|
||||
How it works
|
||||
——————
|
||||
The idea is to go through the buffer fetching each letter-pair (words
|
||||
rather than bytes). The carry flag indicates whether we are
|
||||
currently in a (text) word or not. The letter-pair fetched from the
|
||||
buffer is converted to a 16-bit address by shifting it left one bit
|
||||
(losing the high bit of the second character) and putting the carry
|
||||
flag in the low bit. The high bit of the count register is set to
|
||||
1. Then the count register is added to the byte found at the given
|
||||
address in a large (64K, naturally) table. The byte at the given
|
||||
address will contain a 1 in the high bit if the last character of the
|
||||
letter-pair is a word-letter (alphanumeric or apostrophe). This will
|
||||
set the carry flag since the high bit of the count register is also a
|
||||
1. The low bit of the byte found at the given address will be one if
|
||||
the second character of the previous letter-pair was a word-letter
|
||||
and the first character of this letter-pair is not a word-letter. It
|
||||
will also be 1 if the first character of this letter-pair is a
|
||||
word-letter but the second character is not. This process is
|
||||
repeated. Finally, the carry flag is saved to indicate the final
|
||||
in-a-word/not-in-a-word status. The count register is masked to
|
||||
remove the high bit and the count of words remains in the count
|
||||
register.
|
||||
Sound complicated? You're right! But it's fast!
|
||||
|
||||
The beauty of this method is that no jumps are required, the
|
||||
operations are fast, it requires only one table and the process can
|
||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
||||
jumping.
|
||||
|
||||
COMMEND $
|
||||
.model small
|
||||
.code
|
||||
|
||||
Test1 macro x,y ;9 or 10 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
or ax,si
|
||||
add al,[di]
|
||||
endm
|
||||
|
||||
Test2 macro x,y ;7 or 8 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
add ah,[di]
|
||||
endm
|
||||
|
||||
Scan = 128 ;scan 256 bytes at a time
|
||||
Buffer = 4 ;parms
|
||||
BufferLength = 6
|
||||
CharFlag = 8
|
||||
WordCount = 10
|
||||
|
||||
public _ScanBuffer
|
||||
_ScanBuffer proc near
|
||||
push bp
|
||||
mov bp,sp
|
||||
push si
|
||||
push di
|
||||
|
||||
xor cx,cx
|
||||
mov si,[bp+Buffer] ;si = text buffer
|
||||
mov ax,[bp+BufferLength] ;dx = length in bytes
|
||||
shr ax,1 ;dx = length in words
|
||||
jnz NormalBuf
|
||||
OneByteBuf:
|
||||
mov ax,seg WordTable
|
||||
mov es,ax
|
||||
|
||||
mov di,[bp+CharFlag]
|
||||
mov bh,[di] ;bh = old CharFlag
|
||||
mov bl,[si] ;bl = character
|
||||
add bh,‘A'-1 ;make bh into character
|
||||
add bx,bx ;prepare to index
|
||||
mov al,es:[bx]
|
||||
cbw ;get hi bit in ah (then bh)
|
||||
shr al,1 ;get low bit
|
||||
adc cx,cx ;cx = 0 or 1
|
||||
xchg ax,bx
|
||||
jmp CleanUp
|
||||
NormalBuf:
|
||||
push bp ;(1)
|
||||
pushf ;(2)
|
||||
|
||||
cwd ;dx = 0
|
||||
mov cl,Scan
|
||||
div cx
|
||||
or dx,dx ;remainder?
|
||||
jz StartAtTheTop ;nope, do the whole banana
|
||||
sub cx,dx
|
||||
sub si,cx ;adjust buf pointer
|
||||
sub si,cx
|
||||
inc ax ;adjust for partial read
|
||||
|
||||
StartAtTheTop: mov bx,dx ;get index for start...
|
||||
shl bx,1
|
||||
mov di,LoopEntry[bx] ;...address in di
|
||||
xchg dx,ax ;dx is the loop counter
|
||||
xor cx,cx ;total word count
|
||||
mov bx,[bp+CharFlag]
|
||||
mov bl,[bx] ;bl = old CharFlag
|
||||
mov bp,seg WordTable
|
||||
mov ds,bp
|
||||
mov bp,si ;scan buffer with bp
|
||||
mov si,8080h ;hi bits
|
||||
mov ax,si ;init local word counter
|
||||
shr bl,1 ;carry = old CharFlag
|
||||
jmp di
|
||||
|
||||
align 2
|
||||
Top: add bx,bx ;restore carry
|
||||
n = 0
|
||||
rept Scan/2
|
||||
Test1 %n,%n*2
|
||||
Test2 %n+1,%n*2+2
|
||||
n = n+2
|
||||
endm
|
||||
EndCount:
|
||||
sbb bx,bx ;save carry
|
||||
if Scan ge 128 ;because al+ah may equal 128!
|
||||
or ax,si
|
||||
add al,ah
|
||||
mov ah,0
|
||||
else
|
||||
add al,ah
|
||||
and ax,7fh ;mask
|
||||
endif
|
||||
add cx,ax ;update word count
|
||||
mov ax,si
|
||||
add bp,Scan*2
|
||||
dec dx ;any left?
|
||||
jng Quit
|
||||
jmp Top
|
||||
|
||||
Quit: popf ;(2) even or odd buffer?
|
||||
jnc ItsEven
|
||||
clc
|
||||
Test1 Odd,-1
|
||||
sbb bx,bx ;save carry
|
||||
shr ax,1
|
||||
adc cx,0
|
||||
ItsEven:
|
||||
push ss ;restore ds
|
||||
pop ds
|
||||
pop bp ;(1)
|
||||
CleanUp:
|
||||
mov si,[bp+WordCount]
|
||||
add [si],cx
|
||||
adc word ptr [si+2],0
|
||||
and bh,1 ;save only the carry flag
|
||||
mov si,[bp+CharFlag]
|
||||
mov [si],bh
|
||||
pop di
|
||||
pop si
|
||||
pop bp
|
||||
ret
|
||||
_ScanBuffer endp
|
||||
|
||||
.data
|
||||
Address macro X
|
||||
dw Addr&X
|
||||
endm
|
||||
|
||||
LoopEntry label word
|
||||
n = Scan
|
||||
REPT Scan
|
||||
Address %n MOD Scan
|
||||
n = n - 1
|
||||
ENDM
|
||||
|
||||
.fardata WordTable
|
||||
include qscan3.inc ;built by MAKETAB
|
||||
end
|
||||
```nasm
|
||||
; QSCAN3.ASM
|
||||
; David Stafford
|
||||
|
||||
|
||||
COMMENT $
|
||||
|
||||
How it works
|
||||
——————
|
||||
The idea is to go through the buffer fetching each letter-pair (words
|
||||
rather than bytes). The carry flag indicates whether we are
|
||||
currently in a (text) word or not. The letter-pair fetched from the
|
||||
buffer is converted to a 16-bit address by shifting it left one bit
|
||||
(losing the high bit of the second character) and putting the carry
|
||||
flag in the low bit. The high bit of the count register is set to
|
||||
1. Then the count register is added to the byte found at the given
|
||||
address in a large (64K, naturally) table. The byte at the given
|
||||
address will contain a 1 in the high bit if the last character of the
|
||||
letter-pair is a word-letter (alphanumeric or apostrophe). This will
|
||||
set the carry flag since the high bit of the count register is also a
|
||||
1. The low bit of the byte found at the given address will be one if
|
||||
the second character of the previous letter-pair was a word-letter
|
||||
and the first character of this letter-pair is not a word-letter. It
|
||||
will also be 1 if the first character of this letter-pair is a
|
||||
word-letter but the second character is not. This process is
|
||||
repeated. Finally, the carry flag is saved to indicate the final
|
||||
in-a-word/not-in-a-word status. The count register is masked to
|
||||
remove the high bit and the count of words remains in the count
|
||||
register.
|
||||
Sound complicated? You're right! But it's fast!
|
||||
|
||||
The beauty of this method is that no jumps are required, the
|
||||
operations are fast, it requires only one table and the process can
|
||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
||||
jumping.
|
||||
|
||||
COMMEND $
|
||||
.model small
|
||||
.code
|
||||
|
||||
Test1 macro x,y ;9 or 10 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
or ax,si
|
||||
add al,[di]
|
||||
endm
|
||||
|
||||
Test2 macro x,y ;7 or 8 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
add ah,[di]
|
||||
endm
|
||||
|
||||
Scan = 128 ;scan 256 bytes at a time
|
||||
Buffer = 4 ;parms
|
||||
BufferLength = 6
|
||||
CharFlag = 8
|
||||
WordCount = 10
|
||||
|
||||
public _ScanBuffer
|
||||
_ScanBuffer proc near
|
||||
push bp
|
||||
mov bp,sp
|
||||
push si
|
||||
push di
|
||||
|
||||
xor cx,cx
|
||||
mov si,[bp+Buffer] ;si = text buffer
|
||||
mov ax,[bp+BufferLength] ;dx = length in bytes
|
||||
shr ax,1 ;dx = length in words
|
||||
jnz NormalBuf
|
||||
OneByteBuf:
|
||||
mov ax,seg WordTable
|
||||
mov es,ax
|
||||
|
||||
mov di,[bp+CharFlag]
|
||||
mov bh,[di] ;bh = old CharFlag
|
||||
mov bl,[si] ;bl = character
|
||||
add bh,‘A'-1 ;make bh into character
|
||||
add bx,bx ;prepare to index
|
||||
mov al,es:[bx]
|
||||
cbw ;get hi bit in ah (then bh)
|
||||
shr al,1 ;get low bit
|
||||
adc cx,cx ;cx = 0 or 1
|
||||
xchg ax,bx
|
||||
jmp CleanUp
|
||||
NormalBuf:
|
||||
push bp ;(1)
|
||||
pushf ;(2)
|
||||
|
||||
cwd ;dx = 0
|
||||
mov cl,Scan
|
||||
div cx
|
||||
or dx,dx ;remainder?
|
||||
jz StartAtTheTop ;nope, do the whole banana
|
||||
sub cx,dx
|
||||
sub si,cx ;adjust buf pointer
|
||||
sub si,cx
|
||||
inc ax ;adjust for partial read
|
||||
|
||||
StartAtTheTop: mov bx,dx ;get index for start...
|
||||
shl bx,1
|
||||
mov di,LoopEntry[bx] ;...address in di
|
||||
xchg dx,ax ;dx is the loop counter
|
||||
xor cx,cx ;total word count
|
||||
mov bx,[bp+CharFlag]
|
||||
mov bl,[bx] ;bl = old CharFlag
|
||||
mov bp,seg WordTable
|
||||
mov ds,bp
|
||||
mov bp,si ;scan buffer with bp
|
||||
mov si,8080h ;hi bits
|
||||
mov ax,si ;init local word counter
|
||||
shr bl,1 ;carry = old CharFlag
|
||||
jmp di
|
||||
|
||||
align 2
|
||||
Top: add bx,bx ;restore carry
|
||||
n = 0
|
||||
rept Scan/2
|
||||
Test1 %n,%n*2
|
||||
Test2 %n+1,%n*2+2
|
||||
n = n+2
|
||||
endm
|
||||
EndCount:
|
||||
sbb bx,bx ;save carry
|
||||
if Scan ge 128 ;because al+ah may equal 128!
|
||||
or ax,si
|
||||
add al,ah
|
||||
mov ah,0
|
||||
else
|
||||
add al,ah
|
||||
and ax,7fh ;mask
|
||||
endif
|
||||
add cx,ax ;update word count
|
||||
mov ax,si
|
||||
add bp,Scan*2
|
||||
dec dx ;any left?
|
||||
jng Quit
|
||||
jmp Top
|
||||
|
||||
Quit: popf ;(2) even or odd buffer?
|
||||
jnc ItsEven
|
||||
clc
|
||||
Test1 Odd,-1
|
||||
sbb bx,bx ;save carry
|
||||
shr ax,1
|
||||
adc cx,0
|
||||
ItsEven:
|
||||
push ss ;restore ds
|
||||
pop ds
|
||||
pop bp ;(1)
|
||||
CleanUp:
|
||||
mov si,[bp+WordCount]
|
||||
add [si],cx
|
||||
adc word ptr [si+2],0
|
||||
and bh,1 ;save only the carry flag
|
||||
mov si,[bp+CharFlag]
|
||||
mov [si],bh
|
||||
pop di
|
||||
pop si
|
||||
pop bp
|
||||
ret
|
||||
_ScanBuffer endp
|
||||
|
||||
.data
|
||||
Address macro X
|
||||
dw Addr&X
|
||||
endm
|
||||
|
||||
LoopEntry label word
|
||||
n = Scan
|
||||
REPT Scan
|
||||
Address %n MOD Scan
|
||||
n = n - 1
|
||||
ENDM
|
||||
|
||||
.fardata WordTable
|
||||
include qscan3.inc ;built by MAKETAB
|
||||
end
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue