	.title	'Set HiWater mark in directory'
	.sbttl	'beginning type stuff'
	.ident	hiwaterd
version		==	0
revision	==	0
mod		==     'f'
;
;	Last mod	17 May 83	tpl
;
;
;
;	Set hiwater mark on the drive at the end of the logical directory.
; Compute size of physical directory from DBP.
; Read in entire directory into TPA, using BIOS read/seltrack/selsector
; Look from beginning of directory for empty entries, and from the end
;    for used entries. Transfer used entries from the end to the empty
;    entries at the beginning. Stop when two pointers meet.
; Set HiDos hiwater mark (= E8 hex).
; Write directory back to drive with BIOS calls.
;
;
	.pabs
	.phex
	.loc	100h
	jmp	begin

;
; equates
;
WrmBoot		==	0
WrmBtAddr	==	1
BDOS		==	5
;
PRINT		==	9
GETDPB		==	31
GETcurDSK	==	25
;
;	ASCII equates
;
CR	==	13
LF	==	10
;
;
;	offsets into bios jump vector
;	 from warm boot jump at bios+3
;
SlDskOffset	==	24
StTrkOffset	==	27
StSecOffset	==	30
StDmaOffset	==	33
ReadOffset	==	36
WriteOffset	==	39
ScTrnOffset	==	45
;
;	Offsets into DPB
;
DPBdirEntries	==	7
DPBsysTracks	==	13
;
;
EMPTY	==	0E5h
HIWATER	==	0E8h
DIRBUFF	==	3000h
;
;
;	jumps into BIOS jump vector
;
SelDisk:	jmp	0000
SetTrack:	jmp	0000
SetSector:	jmp	0000
SetDma:		jmp	0000
Read:		jmp	0000
Write:		jmp	0000
SecTran:	jmp	0000
;
;
numDirRec:	.blkw	1	; Size of directory in
				;128 byte records
sectPerTrack:	.blkw	1	; Set to DPB(SPT) = 
				;number of 128 byte 
				;sectors per track.
systmTracks:	.blkw	1	; Number of system
				;tracks = DPB(OFF)
dpbAddress:	.blkw	1	; = current DPB address
dphAddress:	.blkw	1	; = current DPH address
curTrack:	.blkw	1	; current track
				;reading/writing.
curSector:	.blkw	1	;current 128byte sector
				;reading/writing.
topOfDir:	.blkw	1	; Set to address of end
				;of directory for end
				;of dir check in
				;UpToEmptyEntry.
topDownPointer:	.blkw	1	; Pointer used to look
				;for used entries in
				;dir from end to begin.
botmUpPointer:	.blkw	1	; Pointer used to look
				;for empty entries in
				;dir from begin to end.
;
	.byte	76h,76h,76h,76h,76h,76h,76h,76h
	.byte	76h,76h,76h,76h,76h,76h,76h,76h
	.byte	76h,76h,76h,76h,76h,76h,76h,76h
	.byte	76h,76h,76h,76h,76h,76h,76h,76h
stack:
;
	.page
	.sbttl	'messages'
okEndMsg:	.ascii	'...successful completion'[CR][LF]'$'



	.page
	.sbttl	'main code'
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++
;

begin:
	lxi	SP,stack	; Set up local stack
	
	call	SetBiosJumps	; Set up jumps into
				;BIOS jump vector
	call	Initialize	; Initialize dpb/dph
				;addresses, other stuff
	call	CompDirSize	; Compute size of
				;directory in 128 byte
				;records. Store result
				;in numDirRec
;;;;;;;	jrnz	ErrExit		; If not zero flag,
				;directory > 32K bytes.
				;Can't process, so exit
	call	ReadDirectory	; Read directory into
				;TPA.
	call	Compress	; Compress directory,
				;i.e. take out empty
				;entries.
	call	WriteDirectory	; Write directory back
				;to the drive.
	lxi	D,okEndMsg	; Tell user program
	mvi	C,PRINT		;ended without error.
	call	BDOS
	jmp	WrmBoot		; MUST WARM BOOT for
				;non shared drives.

	.page
	.sbttl	'subroutines'
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++++




;    Fill in addresses for jumps into bios jump table.
;  The offsets are from the bios warm boot address, at
;  bios+3.
;
SetBiosJumps:
	lded	WrmBtAddr
    ;DE = bios warm boot jump address

	lxi	H,ReadOffset
	dad	D
	shld	Read+1

	lxi	H,WriteOffset
	dad	D
	shld	Write+1

	lxi	H,StDmaOffset
	dad	D
	shld	SetDma+1

	lxi	H,StTrkOffset
	dad	D
	shld	SetTrack+1

	lxi	H,StSecOffset
	dad	D
	shld	SetSector+1

	lxi	H,SlDskOffset
	dad	D
	shld	SelDisk+1

	lxi	H,ScTrnOffset
	dad	D
	shld	SecTran+1

	ret
;
;------------------------------------------------------
;	Compute the size of the directory in 128 byte
;    records. Store the result in numDirRec
;
CompDirSize:
	lhld	dpbAddress
	lxi	D,DPBdirEntries
	dad	D
	mov	E,M
	inx	H
	mov	D,M	;DE = #dir entries allowed - 1
	inx	D	;DE = #dir entries allowed
    ; divide DE by 4 so shift left 2 times
	srlr	D
	rarr	E
	srlr	D
	rarr	E
    ; DE = maximum number of 128 byte records allowed
    ;in directory.
	sded	numDirRec
	xra	A		; Set zero flag for
				;dir size < 32k
	ret
;
;------------------------------------------------------
;
Initialize:
	mvi	C,GETDPB
	call	BDOS
	shld	dpbAddress
	lxi	D,DPBsysTracks
	dad	D
	mov	E,M
	inx	H
	mov	D,M
	sded	systmTracks

	mvi	C,GETcurDSK
	call	BDOS
	mov	C,A
	call	SelDisk	; Returns HL = DPH
	shld	dphAddress

    ; Get DPB variable sectors per track and store
	lhld	dpbAddress	; First entry = sectors
				;per track
	mov	E,M
	inx	H
	mov	D,M
	sded	sectPerTrack	; Store for later use

	ret
;
;------------------------------------------------------
;	Read the directory into TPA space, starting at 
;   3000h. Use bios calls. Max dir allowed = 32K bytes.
;
ReadDirectory:
    ; Compute starting track for the directory.
    ;First track after system offset tracks, if any.
	lbcd	systmTracks	; BC = number of system
				;tracks
    ; BC = first track after system tracks.
	sbcd	curTrack
	call	SetTrack
    ; Set sector.
    ; DPH address = address of sector translate table 
    ;address
	lhld	dphAddress
	mov	E,M
	inx	H
	mov	D,M		; DE = sector translate
				;table address.
	lxi	B,0	; Know using first (0th) sector
	sbcd	curSector
	call	SecTran
	mov	C,L
	mov	B,H
	call	SetSector


    ; Set DmaAddress to start at 3000h
	lxi	B,3000h
	push	B	; BC = current DMA address
	call	SetDma

    ; Now we read numDirRec 128byte records from drive.
	lded	numDirRec	; DE = counter
	push	D	; DE = current number records
			;left to read.
..readLoop:
	call	Read
	pop	D	; DE = current number records
			;left to write
	pop	B	; BC = current DMA address
	dcx	D
	mov	A,E
	ora	D
	jrz	..endReadLoop
    ; Set DMA address to next record. BC = BC + 128
	lxi	H,80h
	dad	B
	mov	C,L
	mov	B,H
	push	B
	push	D
	call	SetDma
    ; Set sector to next sector
	lhld	curSector
	inx	H
	lded	sectPerTrack
	ora	A		; Clear carry
	dsbc	D
	jrnz	..justIncrementSector
    ; Else sector goes to 0 and track gets incremented.
	lxi	H,0
	shld	curSector
	lbcd	curTrack
	inx	B
	sbcd	curTrack
	call	SetTrack
	jmpr	..nextSector
..justIncrementSector:
	lhld	curSector
	inx	H
	shld	curSector
..nextSector:
    ; DPH address = address of sector translate table 
    ;address
	lhld	dphAddress
	mov	E,M
	inx	H
	mov	D,M		; DE = sector translate
				;table address.
	lbcd	curSector
	call	SecTran
	mov	C,L
	mov	B,H
	call	SetSector
	jmpr	..readLoop
..endReadLoop:
	ret
;
;------------------------------------------------------
;
WriteDirectory:
    ; Compute starting track for the directory.
    ;First track after system offset tracks, if any.
	lbcd	systmTracks	; BC = number of
				;reserved system tracks
    ; BC = first track after system tracks.
	sbcd	curTrack
	call	SetTrack
    ; Set sector.
    ; DPH address = address of sector translate table 
    ;address
	lhld	dphAddress
	mov	E,M
	inx	H
	mov	D,M		; DE = sector translate
				;table address.
	lxi	B,0	; Know using first (0th) sector
	sbcd	curSector
	call	SecTran
	mov	C,L
	mov	B,H
	call	SetSector

    ; Set DmaAddress to start at 3000h
	lxi	B,3000h
	push	B	; BC = current DMA address
	call	SetDma

    ; Now we write numDirRec 128byte records from drive.
	lded	numDirRec	; DE = counter
	push	D	; DE = current number records
			;left to write.
..writeLoop:
	call	Write
	pop	D	; DE = current number records
			;left to write
	pop	B	; BC = current DMA address
	dcx	D
	mov	A,E
	ora	D
	jrz	..endWriteLoop
    ; Set DMA address to next record. BC = BC + 128
	lxi	H,80h
	dad	B
	mov	C,L
	mov	B,H
	push	B
	push	D
	call	SetDma
    ; Set sector to next sector
	lhld	curSector
	inx	H
	lded	sectPerTrack
	ora	A		; Clear carry
	dsbc	D
	jrnz	..justIncrementSector
    ; Else sector goes to 0 and track gets incremented.
	lxi	H,0
	shld	curSector
	lbcd	curTrack
	inx	B
	sbcd	curTrack
	call	SetTrack
	jmpr	..nextSector
..justIncrementSector:
	lhld	curSector
	inx	H
	shld	curSector
..nextSector:
    ; DPH address = address of sector translate table 
    ;address
	lhld	dphAddress
	mov	E,M
	inx	H
	mov	D,M		; DE = sector translate
				;table address.
	lbcd	curSector
	call	SecTran
	mov	C,L
	mov	B,H
	call	SetSector
	jmpr	..writeLoop
..endWriteLoop:
	ret
;
;------------------------------------------------------
;	Compress the copy of the directory in TPA at
;   3000h. Do this by searching from the top down for
;   used entries and replacing them with empty entries
;   found by looking from the bottom up. 
;   When the two pointers cross, set the highwater mark
;   (=E8hex).
;	If topDownPointer hits the beginning of the dir
;   then set the hiwater mark at the beginning. This is
;   the case with an empty directory.
;	If botmUpPointer hits the end of the directory
;   then the directory is full and no hiwater mark will
;   fit, so just return with no change.
;
Compress:
	lxi	D,DIRBUFF	; Start of TPA copy of
				;the directory = 3000h
				; = pointer to search
				;for E5's 
	sded	botmUpPointer	; pointer for searching
				;from the bottom up.
    ; Set pointer to highest available entry. Moves
    ;down from the top of the directory copy in TPA.
	lhld	numDirRec	; # records read in
	mvi	B,7
..multBy128:		; numDirRec*128 = #bytes in dir
	slar	L
	ralr	H
	djnz	..multBy128
	dad	D	; DE = DIRBUFF
    ; HL = address of end of directory in memory.
	shld	topOfDir	;Used in UpToEmptyEntry
				;for end of dir check.
	shld	topDownPointer	
			; topDownPointer - 32 = first
			;available entry in directory.
			; Moves down from top of dir.
..compressLoop:
	call	DownToUsedEntry	   ; If hit beginning
				;of directory, set
				;hiwater and return
				;with zero flag set.
				; Use topDownPointer.
	jrz	..doneCompressing

	call	UpToEmptyEntry    ; If hit end of dir
				;set zero flag and ret.
				; Use botmUpPointer.
	jrz	..doneCompressing

	call	PtrCrossCheck	; Check if pointers
				;have crossed. If so
				;set hiwater mark and
				;return with sero flag
				;set.
	jrz	..doneCompressing

    ;
    ; Move used entry pointed to by topDownPointer to
    ;empty space pointed to by botmUpPointer. Then set
    ;entry pointed to by topDownPointer to empty.
    ;
	call	MoveDownEntry
	jmpr	..compressLoop

..doneCompressing:
	ret
;
;------------------------------------------------------
;
DownToUsedEntry:
	lhld	topDownPointer
..downLoop:
	lxi	D,32	; Next entry is 32 bytes
	ora	A	;down. Clear carry.
	dsbc	D
	push	H	; Save pointer from dsbc.
	lxi	D,DIRBUFF-32	; want to check entry
				;at DIRBUFF
	ora	A	; Clear carry for subtract.
	dsbc	D
	pop	H	; Get pointer back.
	jrz	..dirBeginning

	mov	A,M
	cpi	EMPTY
	jrz	..downLoop
	cpi	HIWATER
	jrz	..downLoop

    ; If not empty or hiwater assume used entry.
	shld	topDownPointer
	ori	0FFh	; Return with zero flag NOT set
	ret

..dirBeginning:
	xra	A	; Return with zero flag SET.
	ret
;
;------------------------------------------------------
;
UpToEmptyEntry:
	lhld	botmUpPointer
..upLoop:
	push	H	; Save pointer from dsbc.

	lded	topOfDir
	ora	A	; Clear carry for dsbc.
	dsbc	D
	pop	H
	jrz	..endOfDir

	mov	A,M
	cpi	EMPTY
	jrz	..foundEmtpy
	cpi	HIWATER
	jrz	..foundEmtpy
	lxi	D,32
	dad	D		; Next entry 32 bytes
	shld	botmUpPointer	;forward.
	jmpr	..upLoop

..foundEmpty:
	ori	0FFh	; Return with zero flag NOT set
	ret

..endOfDir:
	xra	A	; Return with zero flag SET.
	ret
;
;------------------------------------------------------
;
PtrCrossCheck:
	lhld	topDownPointer
	lded	botmUpPointer
	ora	A		; Clear carry
	dsbc	D
	jm	..crossed
	ori	0FFh	; Return with zero flag NOT set
	ret
..crossed:
    ; Set hiwater and return with zero flag set.
	xchg		; HL = botmUpPointer
	mvi	M,HIWATER
	xra	A	; Return with zero flag SET.
	ret
;
;------------------------------------------------------
;	Move used entry at topDownPointer to the empty
;   space at botmUpPointer. Then mark the entry at
;   topDownPointer to empty.
MoveDownEntry:
	lhld	topDownPointer
	lded	botmUpPointer
	lxi	B,32
	ldir
	lhld	topDownEntry	; Reset HL from 'ldir'
	mvi	M,EMPTY
	ret
;
;------------------------------------------------------
;
;
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
;

	.end
