[net.sources] BISHOW -- CP/M file scroll utility

emigh@ecsvax.UUCP (09/23/83)

  The following is the CP/M program for bidirectional file scroll.  It now
allows complete control over which columns are to be displayed.  I am told that
it is quite useful in searching through DBASEII files.  To extract, strip
this header off and run it through your shell.
  I am sorry if this is a duplication, we had news problems some weeks ago,
so this is a resubmission.
--------------------------------------------------------------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting bishow17.asm'
sed 's/^X//' <<'//go.sysin dd *' >bishow17.asm
;	title	'BISHOW v1.07 - buffered bidirectional file scroll utility'
;
; Ver 1.07, 1 Aug 83, Ted H. Emigh  ...!mcnc!ecsvax!emigh
;	- added screen width and height specification
;		(BISHOW file.nam [width [lines]])
;	- added windowing capability.  Helpful in looking at files
;	 	wider than 80 columns (see notes below)
;
; Ver 1.06, 2 Jul 83, Chuck Forsberg
;	- added commands for more, mince, vi familiarity. Bad cmd gives help
;
; Ver 1.051, 26 June 83, Dick Mead
;	- added "?" for help on commands.
;
; Ver 1.05, 31 May 83, Bruce Ratoff
;	- added 'N' (next line) and 'P' (previous line) cmds
;	- decreased buffer from 8k to 4k (8k takes too long)
;
; Ver 1.04, 15 May 83, Keith Petersen, W8SDZ
;	- fixed bug which caused display past end-of-file
;		and added bugus eof in case none at file end
;	- added strip of high-order bit in line count routine
;	- added exit clear of any left-over keyboard character
;
; Ver 1.03, 11 May 83, Keith Petersen, W8SDZ
;	- fixed to allow assembly with ASM.COM
;	- fixed screen clear bug when crossing read boundries
;	- added strip for high-order bit in character before
;		printing (needed for WordStar files)
;	- improved stack routines
;	- fixed bug in console input routine
;	- removed Z80 dependant code (now works on 8080 too)
;
; Ver 1.02, 06 May 83, Lucien Pan, Toronto, Canada
; 	- fixed some minor bugs
; 	- returns to ccp w/o warm boot
; 	- filters form-feeds (useful in .PRN files)
; 	- scrolls foward/backwards by same number of lines
; 	- disable/enable cursor during scroll for H-19
;
; Ver 1.01, 30 Mar 83 - added BDOS function  6.  W.F.Mcgee
;
; Ver 1.00, 23 Aug 82 Phil Cary, 748 Kenilworth Parkway, Baton
; Rouge, LA  70808
;
; BISHOW is a  buffered,  bidirectional  version  of  SHOW.ASM
; which first appeared in Interface Age, November, 1981.  That
; program  could  only  scroll  forward  in  a  file, and read
; sectors from disk one at a time as they  were  sent  to  the
; console.   I  used SHOW frequently to take a quick look at a
; file without loading a  big  text  editor,  and  to  examine
; another  file  with the RUN command while in Wordstar.  TYPE
; does not work since it is not a file that Wordstar can  load
; and run.
;
; It was annoying when I went past the point I was looking for
; in a file with SHOW, and could not go backwards.  Thus, this
; bidirectional  version  which  uses  random access reads. In
; addition, buffering was added so that  the  number  of  disk
; reads  would  be  reduced,  and  moving  back and forth in a
; moderate sized file would be speeded up.  There is  a  trade
; off between the size of the buffer and the length of time it
; takes to refill the buffer which should be set to the user's
; preference.
;
; There are several customizing items in this program.  One is
; the equate "maxsec" which sets the buffer size.   Another is
; the  string  in the subroutine "clrscr" just after  the  org
; statement.   This should be changed to erase the screen  and
; home  the cursor for the user's terminal.   The program,  as
; written,  requires a terminal with an erase screen and  home
; cursor  function.   Some  terminals  do not allow  the  80th
; column  to be filled without going to the  next  line.   For
; this reason, the screen width ("maxchr") initially is set to
; 79.   The screen sizes can be changed using the "S" (screen)
; command.  The parameters that can be changed are the maximum
; column  displayed ("maxchr"),  the minimum column  displayed
; (allowing  you  to "window" the output),  and the number  of
; lines ("scroln").   A zero for the maximum column  displayed
; will  give an  unlimited screen width.   The maximum  column
; displayed  and  the number of lines can be set when  calling
; BISHOW,  e.g.,  "BISHOW file.nam 79 24" will give 79 columns
; and 24 lines,  and "BISHOW file.nam 79" will give 79 columns
; with the default number of lines.  The last customizing item
; is the "short" equate.   If this is chosen, the multiplicity
; of command forms is not allowed (see the beginning to change
; the  commands  used),  and certain messages  are  shortened.
; This will allow BISHOW to fit into a 1K area.  If "short" is
; false, the program is slightly over 1K.  Finally, direct I/O
; to the console is used to avoid echoing the commands to  the
; console as the CP/M write console function does.
;
; Just  a  small contribution to the public domain software as
; partial payment for the many fine and  educational  programs
; the system has given me.  Phil Cary.
;
;Define version number for help message
vers	equ	1
revs	equ	07
;
false	equ	0
true	equ	not false
;
;	Operational equates
;
maxsec	equ	32		;number of sectors in buffer
scroln	equ	24		;number of lines per scroll
maxchr	equ	79		;number of characters per line
fulbuf	set	dskbuf+(maxsec*128)	;need to know end of buffer
;
heath	equ	false		;assemble for H-19 terminal
short	equ	true		;set to true if you want the short version
				;which is less than 1K
;
base	equ	0		;standard zero base CP/M
;
;	BDOS functions
;
conout	equ	2		;console write
conio	equ	6		;direct console I/O
rdcon	equ	10		;read console buffer
open	equ	15		;open file
close	equ	16		;close file
readr	equ	33		;read file random access
stdma	equ	26		;set dma address
;
;	Page zero equates
;
wboot	equ	base		;warm boot entry point
bdos	equ	wboot+5		;BDOS entry point
fcb	equ	wboot+5ch	;default fcb drive number
cmdtail	equ	wboot+80h	;location of command tail
fcbfn	equ	fcb+1		;start of filename
fcbft	equ	fcb+9		;start of filetype
fcbex	equ	fcb+12		;current extent number
fcbcrr	equ	fcb+33		;current record number, random access
tpa	equ	wboot+100h	;transient program area
;
;	ASCII equates
;
endmsg	equ	0		;null
bell	equ	7		;bell
tab	equ	9		;tab
lf	equ	0ah		;line feed
cr	equ	0dh		;carriage return
eof	equ	1ah		;end of file
esc	equ	1bh		;escape
space	equ	20h		;space
;
; Equates for the short version
;
; Any invalid command gives help
; in addition to quit, ^C ends the program
; Be sure to change the help2 printout
;
nxtpag	equ	space		;next page
back	equ	'B'		;scroll backward
next	equ	cr		;next line
prev	equ	'-'		;previous line
screen	equ	'S'		;set screen parameters
quit	equ	'Q'		;exit bishow
first	equ	'1'		;first page
;
	org	tpa
;
	jmp	start		;skip over next subroutine
;
clrscr:	if	not heath
	call	cdisp
	db	esc,'+',endmsg	;put your screen clear string here
	endif
;
	if	heath
	call	cdisp		;command to erase screen and home cursor
	db	esc,'E',endmsg	;__for H/Z-19 terminal. change as required
	endif
;
wait:	mvi	b,0		;waste time (may or may not be necessary)
;
wait1:	xthl			;good time gobbeler!
	xthl
	dcr	b
	jnz	wait1
	ret			;return from clrscr
;
help2:
if not short
	call	cdisp
	db	cr,lf,'Commands:',cr,lf
	db	'^F,F,^V,sp=next page, ^B,B=back page',cr,lf
	db	'CR,+,N=next line, '
	db	'-,P=back line, 1=1st line, ^C,Q=exit',cr,lf
	db	'S=set screen parameters',cr,lf,endmsg
	jmp	getcmd
endif
if short
	call	cdisp
	db	cr,lf,'sp=next page, B=back page',cr,lf
	db	'CR=next line, '
	db	'-=back line, 1=1st line, Q=exit',cr,lf
	db	'S=set screen',cr,lf,endmsg
	jmp	getcmd
endif
;
start:	lxi	h,0		;get ccp's stack
	dad	sp
	shld	stack		;save old stack for later
	lxi	sp,stack	;set new stack
	lxi	h,cmdtail	;point to command tail
	mov	b,m		;get number of char in tail
	inx	h		;point to first character
	inr	b
eatsp:	mov	a,m		;get character if there is one
	inx	h
	dcr	b		;b=number of characters left
	jz	openf		;no more characters
	cpi	space		;ignore spaces
	jz	eatsp
filnam:	mov	a,m		;get characters in file name
	inx	h
	dcr	b		;b=number of characters left
	jz	openf		;only file name in tail
	cpi	space		;wait for next space
	jnz	filnam
	lxi	d,chrmax	;point to chr/line
	call	getnbr		;get number of characters/line
	jc	help		;invalid number
	lxi	d,linmax	;point to number of lines
	cnz	getnbr		;call only if characters still in
				;__command tail
	jc	help		;invalid number
;
openf:	call	opnfil		;open file in default fcb
;
wrtfwd:	xra	a		;get a 0
	sta	lincnt		;store in line count
	sta	chrcnt		;store in character count
	sta	fcbex		;zero current extent
	sta	fcbcrr		;zero current record
	sta	fcbcrr+1	;__both bytes
	sta	fcbcrr+2	;__and the overflow
	call	clrscr		;erase the screen
;
wrtfw0:	call	filbuf		;fill the disk buffer
	lxi	h,dskbuf	;point to beginning of buffer
;
wrtfw1:	mov	a,m		;get a character
	cpi	eof		;see if eof
	jz	getcmd		;yes, wait for command
	inx	h		;bump pointer
	ani	7fh		;strip high bit
	cpi	'L'-40h		;filter form-feeds
	jz	filter		;__commonly found in .PRN files
	call	co1		;put it on console
	cpi	cr		;see if end of line
	jz	fwdcnt		;yes, adjust line count
;
wrtfw2:	lxi	d,fulbuf	;get end of buffer address
	mov	a,d		;compare high
	cmp	h		;__order bytes
	jnz	wrtfw1		;if not equal, continue
	mov	a,e		;else compare low
	cmp	l		;__order bytes
	jz	wrtfw0		;refill buffer and start over
	jmp	wrtfw1		;else, continue with next character
;
help:	call	cdisp
	db	'BISHOW version '
	db	(vers mod 10)+'0','.'
	db	revs/10+'0',(revs mod 10)+'0',cr,lf
	db	'Usage: d:bishow d:fn.ft [cols [lines]]',cr,lf
	db	endmsg
	jmp	exit1
;
filter:	push	psw		;save character
	mvi	a,'^'		;print '^' in front
	call	co1		;__of control character
	pop	psw		;restore character
	adi	40h		;mask into displayable char
	call	co1		;display filtered control char
;
fwdcnt:	lda	lincnt		;get number of lines displayed
	inr	a		;bump it
	sta	lincnt		;__and store it
	mov	d,a		;save lincnt
	lda	linmax		;get max number of line
	cmp	d		;compare with line count
	jnz	wrtfw2		;if not there, continue, else get command
	xra	a		;zero the
	sta	lincnt		;__line count
;
getcmd:	push	h
	push	d
	push	b
;
	if	heath
	call	cdisp
	db	esc,'x5',endmsg	;disable cursor
	endif
;
getcm1:	mvi	c,conio		;direct console I/O
	mvi	e,0ffh		;__set up for input
	call	bdos
	ora	a		;loop till char avail
	jz	getcm1
	pop	b
	pop	d
	pop	h
	cpi	'a'		;change command to
	jc	getcm2		;__upper case
	cpi	'z'+1
	jnc	getcm2
	xri	20h		;is lower case, make upper case
getcm2:
if not short
	cpi	'1'		; 1 means goto 1st line
	jz	wrtfwd
	cpi	'?'		;help request
	jz	help2
	cpi	' '		;more use space
	jz	wrtfw1
	cpi	'F'		;scroll forward?
	jz	wrtfw1		;br if yes
	cpi	'F'-40h		;vi uses ^F
	jz	wrtfw1
	cpi	'V'-40h		;mince uses ^V
	jz	wrtfw1
	cpi	'B'		;scroll backward?
	jz	wrtbak
	cpi	'B'-40h		;vi uses ^B
	jz	wrtbak
	cpi	'N'		;scroll next line?
	jz	wrtnxt
	cpi	'+'
	jz	wrtnxt
	cpi	cr		;scroll next line?
	jz	wrtnxt
	cpi	lf
	jz	wrtnxt
	cpi	'P'		;scroll prev line?
	jz	wrtprv
	cpi	'-'
	jz	wrtprv
	cpi	'C'-40h		;must be exit
	jz	exit		;return control to CCP if yes or
	cpi	'Q'		;more use q for quit
	jz	exit
	cpi	'S'		;set screen parameters
	jz	setscr
	jmp	help2		;else give a hint
endif
if short
	cpi	first		; 1 means goto 1st line
	jz	wrtfwd
	cpi	nxtpag		;scroll forward?
	jz	wrtfw1		;br if yes
	cpi	back		;scroll backward?
	jz	wrtbak
	cpi	next		;scroll next line?
	jz	wrtnxt
	cpi	prev		;scroll prev line?
	jz	wrtprv
	cpi	'C'-40h		;must be exit
	jz	exit		;return control to CCP if yes or
	cpi	quit		;alternative quit
	jz	exit
	cpi	screen		;set screen parameters
	jz	setscr
	jmp	help2		;else give a hint
endif
;
wrtnxt:	lda	linmax
	dcr	a		;fool wrtfw1 to only write one line
	sta	lincnt
	jmp	wrtfw1
;
wrtprv:	lda	linmax		;back up one screen + 1 line
	inr	a
	jmp	wrtbk0
;
wrtbak:	lda	linmax		;get screen line count
	add	a		;__multiply by 2
wrtbk0:	inr	a		;__and add 1
	sta	lincnt		;__to backup to previous page
	call	clrscr		;clear the screen
;
wrtbk1:	lxi	d,dskbuf	;get address of buffer start
	mov	a,d		;compare high
	cmp	h		;__order bytes
	jnz	wrtbk2		;continue if not equal
	mov	a,e		;else, compare low
	cmp	l		;__order bytes
	jnz	wrtbk2		;continue if not equal
	jmp	filbak		;__and go write it
;
wrtbk2:	mov	a,m		;get a character
	ani	7fh		;strip high bit
	dcx	h		;decrement buffer
	cpi	cr		;see if end of line
	jz	bakcnt		;__or form-feed
	cpi	'L'-40h		;__and adjust line count if so
	jnz	wrtbk1		;else, loop if not
;
bakcnt:	lda	lincnt		;else, get number of lines to move back
	dcr	a		;__and decrement it
	sta	lincnt		;__store it
	jnz	wrtbk1		;__and loop if not there
	inx	h		;else bump pointer
	inx	h		;__to account for dcx
	jmp	wrtfw1		;and go write a screen
;
filbak:	lxi	d,maxsec	;get the buffer size
	lhld	seccnt		;__and number of sectors last read
	dad	d		;add them
	xchg			;__and put them in DE
	lda	fcbcrr		;subtract low order byte
	sub	e		;__from current record count
	sta	fcbcrr		;__and store in current record count
	lda	fcbcrr+1	;same with high order byte
	sbb	d		;__but with borrow
	jm	wrtfwd		;if beyond beginning of file, start over
	sta	fcbcrr+1	;else, store high order byte
	call	filbuf		;fill the buffer
	lxi	h,fulbuf	;__and point to end of buffer
	call	clrscr		;clear the screen
	jmp	wrtbk2		;continue moving back in file
;
filbuf:	lxi	d,dskbuf	;load start of disk buffer
	mvi	b,maxsec	;number of sectors to resd
	lxi	h,0		;zero out the
	shld	seccnt		;__number of sectors in buffer
;
filbu1:	push	h		;save all
	push	d		;__registers from
	push	b		;__BDOS clobber
	mvi 	c,stdma		;set dma to
	call	bdos		;__disk buffer
	lxi	d,fcb		;set up to read
	mvi	c,readr		;__a record
	call	bdos		;do it
	ora	a		;read OK?
	lhld	fcbcrr		;get current record number
	inx	h		;__bump it
	shld	fcbcrr		;__and save it
	lhld	seccnt		;get sectors in buffer
	inx	h		;bump it
	shld	seccnt		;store it
	pop	b
	pop	d
	pop	h
	jnz	rderr		;no, last sector read
	dcr	b		;decrement it
	rz			;if done return
	lxi	h,128		;else, add 128 to
	dad	d		;__dma address
	xchg			;put it in de
	jmp	filbu1		;read another sector
;
;We only get here if end of file
;
rderr:	mvi	a,eof		;get bogus eof
	stax	d		;save at buffer end in case no eof in file
	xra	a		;get a zero to direct to start of buffer
	ret			;__on ret
;
opnfil:	lda	fcbfn		;point to first letter of filename
	cpi	' '		;anything there?
	jz	help		;no, give help message
	lxi	d,fcb		;file name in default fcb
	mvi	c,open		;set up to open
	call	bdos		;do it
	inr	a		;open OK?
	rnz			;yes
	call	cdisp		;else, give error msg and quit
	db	'file not found ',endmsg
	jmp	exit1		;leave msg on screen on exit
;
getnbr:	mov	a,m		;get first digit
	inx	h
	dcr	b		;b=number of characters left in buffer
	rz			;no digit
	cpi	space
	jz	getnbr		;wait until next non-space
	push	d		;save location to save number
	mvi	d,0		;initialize number
gnum1:	sui	30h		;change ASCII to number
	jc	invnum		;not a number
	cpi	10
	cmc
	jc	invnum		;not a number
	push	psw		;save number
	mov	a,d		;multiply old number by 10
	add	a		;*2
	add	a		;*4
	add	d		;*5
	add	a		;*10
	mov	d,a
	pop	psw		;restore new digit
	add	d
	mov	d,a
	mov	a,m		;get next digit
	inx	h
	dcr	b
	jz	endnum		;end of number
	cpi	space
	jnz	gnum1
;
endnum:	mov	a,d		;save number
	dcr	a		;correct number for co routines
	xchg			;set to restore save location
	pop	h		;restore save location
	mov	m,a		;save digit
	xchg			;put buffer location where it belongs
	mov	a,b		;see if any characters left in buffer
	ora	a		;zero if no characters in buffer
	ret
;
invnum:	pop	d		;correct stack
	ret
;
asciin:	mvi	b,100		;divide by 100, then change
	call	divide		;__digit to ASCII and store at hl
	mvi	b,10		;divide by 10, then change
	call	divide		;__digit to ASCII and store at hl+1
	mvi	b,30h		;change ones place number
	add	b		;__to ASCII and store at hl+2
	mov	m,a
;
if not (short and heath)
	dcx	h		;delete leading zeroes
	dcx	h		;__in the number
	mvi	c,space		;__and replace with spaces
	mov	a,b
	cmp	m		;check first digit for '0'
	jnz	cdisp		;not zero
	mov	m,c		;replace with space
	inx	h
	cmp	m		;check second digit for '0'
	jnz	cdisp		;not zero
	mov	m,c		;replace with space
endif
;
cdisp:	xthl			;exchange top of stack and HL
;
cdis1:	mov	a,m		;HL now pointing to db message
	ora	a		;see if 0 at end of message
	inx	h
	jz	cdis2		;yes, restore stack and return
	call	co		;no, print the character
	jmp	cdis1		;__and loop
;
cdis2:	xthl			;get return address on top of stack
	ret			;__and return
;
co:	push	b		;Save the registers
	push	d		;__from bdos
	push	h		;__clobber
co2:	push	psw
co4:	mov	e,a		;set up character
	mvi	c,conout	;__to send to console
	call	bdos		;do it
co3:	pop	psw
co5:	pop	h		;restore
	pop	d		;__the registers
	pop	b
	ret
;
co1:	push	b		;Save the registers
	push	d
	push	h
	push	psw
	lxi	h,chrcnt	;Get address of character count
	cpi	cr		;see if end of line
	jz	endlin		;update line information
	cpi	lf		;ignore linefeed
	jz	co4
	lda	chrmax		;get maximum characters per line
	cmp	m		;see if too many char
	jc	co3		;don't print character
	pop	psw		;__and print character
	cpi	tab		;fix chrcnt for tabs
	jz	tabfix
	inr	m		;increment character count
	mov	e,a
	lda	chrmin		;see if up to minimum display yet
	cmp	m
	mov	a,e		;restore character
	jnc	co5		;do not display character
	jmp	co2		;finally, display character
;
endlin:	mvi	m,0		;reset character count
	jmp	co4		;print cr
;
tabfix:	mvi	a,08h		;fix chrcnt for tabs
	add	m		;increment to next 8-count
	ani	0f8h		;make into multiple of 8
tab2:	sub	m		;get number of space to go
	mov	b,a
tab1:	mvi	a,space		;expand tab
	call	co1
	dcr	b
	jnz	tab1		;still more spaces
	jmp	co5		;exit routine
;
setscr:	push	h
	push	d
	push	b
getmax:	lda	chrmax		;get maximum number of characters
	inr	a
	lxi	h,huns
	call	asciin		;put ASCII number in message
if not short
	db	cr,lf,'Maximum Column: '
endif
if short
	db	cr,lf,'Max Column: '
endif
huns:	db	30h
	db	30h
	db	30h
	db	' New? ',endmsg
	call	getinp		;read input from console
	lxi	d,chrmax	;set new chrmax in memory
	call	getnbr
	jc	getmax		;if error, repeat last message
getmin:	lda	chrmin		;get minimum character display
	inr	a
	lxi	h,hun
	call	asciin		;put ASCII number in message
if not short
	db	lf,'Minimum Column: '
endif
if short
	db	lf,'Min Column: '
endif
hun:	db	30h
	db	30h
	db	30h
	db	' New? ',endmsg
	call	getinp		;read input from console
	lxi	d,chrmin	;set new chrmax in memory
	call	getnbr
	jc	getmin		;if error, repeat last message
getlin:	lda	linmax		;get number of lines per display
	lxi	h,hund
	call	asciin		;put ASCII number in message
	db	lf,'Lines Displayed: '
hund:	db	30h
	db	30h
	db	30h
	db	' New? ',endmsg
	call	getinp		;read input from console
	lxi	d,linmax	;set new chrmax in memory
	call	getnbr
	jc	getlin		;if error, repeat last message
	pop	b
	pop	d
	pop	h
;
wrtsam:	lda	linmax		;write the same screen
	jmp	wrtbk0
;
;
getinp:	mvi	a,5		;get set to read new maximum column
	sta	cmdtail
	mvi	c,rdcon
	lxi	d,cmdtail
	call	bdos		;get maximum column
	lxi	h,cmdtail+1	;now, change to ASCII
	mov	b,m
	inx	h
	inr	b
	ret			;return with input in buffer
;
divide:	mvi	c,'0'-1		;extract dividend of a div c
div1:	inr	c		;__and store in location pointed
	sub	b		;__to by hl
	jnc	div1
	add	b
	mov	m,c		;save ASCII digit
	inx	h		;bump pointer to next location
	ret
;
exit:	mvi	e,0ffh		;clear console of characters
	mvi	c,conio		;direct console I/O
	call	bdos		;__get any waiting characters
	call 	clrscr		;clear the screen
;
	if	heath
	call	cdisp		;re-enable cursor
	db	esc,'y5',endmsg
	endif
;
	lxi	d,fcb		;close file
	mvi	c,close		;--in case this is MP/M
	call	bdos
;
exit1:	lhld	stack		;get old stack
	sphl
	ret			;return to CCP
;
;	Memory allocation
;
seccnt:	dw	0		;number of sectors read into buffer
linmax:	db	scroln		;number of to write lines on console
chrmax:	db	maxchr-1	;number of characters to display per line
chrmin:	db	0		;character to start displaying on line
chrcnt:	ds	1		;character number in line
lincnt:	ds	1		;line number on write or move back in buffer
	ds	60		;stack area
stack:	ds	2		;old stack saved here
dskbuf:	equ	$		;disk buffer area above the program
;
	end	tpa
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 bishow17.asm
	/bin/echo -n '	'; /bin/ls -ld bishow17.asm
fi