;	title	'BISHOW v1.06 - buffered bidirectional file scroll utility'
;
; 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  two  customizing  items in this program.  One is
; the equate "maxsec" which sets the buffer size.   The  other
; is  the string in the subroutine "clrscr" just after the org
; statement.  This should be changed to the erase  screen  and
; home cursor sequence for the user's terminal. The program is
; presently  set up  for  an ADM-3A.  The program, as written,
; does require a 24 X 80 screen with an erase screen and  home
; cursor function.  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	60
;
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
fulbuf	set	dskbuf+(maxsec*128)	;need to know end of buffer
;
heath	equ	true		;assemble for H-19 terminal
base	equ	0		;standard zero base CP/M
;
;	BDOS functions

;
conout	equ	2		;console write
open	equ	15		;open 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
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
lf	equ	0ah		;line feed
cr	equ	0dh		;carriage return
eof	equ	1ah		;end of file
esc	equ	1bh		;escape
;
	org	tpa
;
	jmp	start		;skip over next subroutine
;
clrscr:	if	not heath
	call	cdisp
	db	7eh,1ch,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
wait:	mvi	b,0		;waste time (may or may not be necessary)
;
wait1:	xthl			;good time gobbeler!
	xthl
	dcr	b
	jnz	wait1
	endif
;
	ret			;return from clrscr
;
start:	lxi	h,0		;get ccp's stack

	dad	sp
	shld	stack		;save old stack for later
	lxi	sp,stack	;set new stack
	call	opnfil		;open file in default fcb
;
wrtfwd:	xra	a		;get a 0
	sta	lincnt		;store in line 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	co		;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, continuee Hc htt
Prufwrtd ptuuuuut,
uuuuuuuuuuuuuuuuuuuuuuuu
	 l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l Hleet
	call	co		;__of control character

	pop	psw		;restore status
	adi	40h		;mask into displayable char
	call	co		;display filtered control char
;
fwdcnt:	lda	lincnt		;get number of lines displayed
	inr	a		;bump it
	sta	lincnt		;__and store it
	xchg			;save the buffer pointer
	lxi	h,linmax	;point to max number of line for this pass
	cmp	m		;compare with line count
	xchg			;restore pointer
	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,6		;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	'1'		; 1 means goto 1st line
	jz	wrtfwd
	cpi	'?'		;help request
	jz	help2
	cpi	' '		; more uses space
	jz	wrtfw1
	cpi	'F'		;scroll forward?
	jz	wrtfw1		;br if yes
	cpi	'f'
	jz	wrtfw1		;br if yes
	cpi	06		;vi uses ^F
	jz	wrtfw1
	cpi	026Q		; mince uses ^V
	jz	wrtfw1
	cpi	'B'		;scroll backward?
	jz	wrtbak
	cpi	'b'
	jz	wrtbak
	cpi	02		;vi uses ^B
	jz	wrtbak
	cpi	'N'		;scroll next line?
	jz	wrtnxt
	cpi	'n'
	jz	wrtnxt
	cpi	'+'
	jz	wrtnxt
	cpi	cr		;scroll next line?
	jz	wrtnxt
	cpi	lf
	jz	wrtnxt
	cpi	'P'		;scroll prev line?
	jz	wrtprv
	cpi	'p'
	jz	wrtprv
	cpi	'-'
	jz	wrtprv
	cpi	'C'-40h		;must be exit
	jz	exit		;return control to CCP if yes or
	cpi	'q'
	jz	exit		;more uses q for quit
	cpi	'Q'
	jz	exit
	jmp	help2		;else give a hint

;
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
	cpi	0ffh		;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
;
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 ',cr,lf
	db	endmsg
	jmp	exit1
help2:	call	cdisp
	db	cr,lf,'Commands:',cr,lf
	db	'^F,F,^V,sp=forward page, ^B,B=back page',cr,lf
	db	'CR,+,N=next line, '
	db	'-,P=back line, 1=1st line, ^C,Q=exit',cr,lf,endmsg
	jmp	getcmd
;
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
	push	psw
	mov	e,a		;set up character
	mvi	c,conout	;__to send to console
	call	bdos		;do it
	pop	psw
	pop	h		;restore
	pop	d		;__the registers
	pop	b
	ret
;
exit:	mvi	c,11		;console status
	call	bdos		;--check for any waiting characters
	ora	a		;character waiting?
	mvi	c,1		;console input
	cnz	bdos		;if so, gobble it up
	call 	clrscr		;clear the screen
;
	if	heath
	call	cdisp		;re-enable cursor
	db	esc,'y5',endmsg
	endif
;
	lxi	d,fcb		;close file
	mvi	c,16		;--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
lincnt:	db	0		;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


f stack
	ret			;__and return
;
co:	pusht
	dcx	h		;decrement buffer
	cpi	cr		;see if end of line
	jz	bakcnt		;__or form-feed
	cpi	