	title	'Non-Resident Portion of Banked BDOS'
	cseg

;***************************************************************
;***************************************************************
;**                                                           **
;**   B a s i c    D i s k   O p e r a t i n g   S y s t e m  **
;**                                                           **
;**         N o n - R e s i d e n t   P o r t i o n           **
;**                B a n k e d   B D O S                      **
;**                                                           **
;***************************************************************
;***************************************************************

;/*
;  Copyright (C) 1978,1979,1980,1981,1982
;  Digital Research
;  P.O. Box 579
;  Pacific Grove, CA 93950
;
;  Version 2.1 Patch Release
;    12 Apr 82 by David K. Brown
;*/

on	equ	0ffffh
off	equ	00000h

MPM	EQU	ON
MPMENV	EQU	ON
BNKBDOS	EQU	ON

ctlc	equ	03h	;control-c
cr	equ	0dh	;carriage return
lf	equ	0ah	;line feed
;
;	BDOS call equates
dskinit		equ	13
dskslct		equ	14
dsksetDMA	equ	26
setusercode	equ	32
;
;	Process Descriptor Offsets
pname		equ	6
console		equ	14
memseg		equ	15
thread		equ	18
disksetDMA	equ	20
diskselect	equ	22
diskparams	equ	23
MULT$CNT$OFF	EQU	50
PD$CNT$OFF	EQU	51
;
;	System Data Page Offsets
userstackflag	equ	3
brkvctr		equ	48
usrstkregion	equ	80
;
;
;	MP/M Data Page Offsets
rlros		equ	5
thrdrt		equ	17
msegtbl		equ	150
;
;**************************************************
;*                                                *
;**************************************************
;
	org	0000h
base	equ	$

	jmp	bnkbdose
	JMP	BNKBDOSINIT
	JMP	REMOVE$FILES
XDMAAD:	DW	$-$
SYSDAT:	DW	$-$
BUFFA:	DW	$-$

bnkbdosinit:
	pop h! push h! inx h! inx h! inx h
	lxi d,extjmptbl! mvi c,24! xchg
	CALL MOVE

	;INITIALIZE OPEN FILE AND LOCKED RECORD LIST
	
	LHLD SYSDAT! MVI L,187
	MOV A,M! STA LOCK$MAX! INX H
	MOV A,M! STA OPEN$MAX
	INX H! MOV E,M! INX H! MOV D,M! INX H
	PUSH D
	MOV E,M! INX H! MOV D,M! XCHG! SHLD FREE$ROOT
	POP D! DCX D! LXI B,10
ILIST:
	PUSH D
	MOV D,H! MOV E,L! DAD B! XCHG
	MOV M,E! INX H! MOV M,D! XCHG
	POP D! DCX D! MOV A,D! ORA E! JNZ ILIST
	MOV M,A! DCX H! MOV M,A! RET

extjmptbl:

reboot:	jmp	$-$
rlr:	jmp	$-$
rlradr:	jmp	$-$
dsplynm: jmp	$-$
xprint:	jmp	$-$
xcrlf:	jmp	$-$
conoutx: jmp	$-$
getmemseg: jmp	$-$
;
;************************************************
;*                                              *
;************************************************
;

bnkbdose:

	push d! push b

	LXI H,DELETED$FILES
	DI
	MOV A,M! ORA A! MVI M,0
	EI
	CNZ DELETE$FILES

	;perform the necessary BDOS parameter initialization
	;disk set DMA
	call rlr! SHLD PDADDR
	lxi b,disksetDMA! dad b
	mov e,m! inx h! mov d,m! push h! xchg! shld dmaad
	mov b,h! mov c,l! call setdmaf

	;disk select
	pop h! inx h
	mov a,m! rrc! rrc! rrc! rrc! ani 0fh! STA SELDSK

	;set user code
	mov a,m! ani 0fh! STA USRCODE

	;copy local disk params
	inx h! push h! lxi d,dcnt
	XCHG! MVI C,2! CALL MOVE
	LXI H,SEARCHL! LDAX D! MOV M,A
	LXI H,MULT$CNT$OFF-DISKPARAMS-2! DAD D
	MOV A,M! STA MULT$CNT
	INX H! MOV A,M! ANI 0F0H! CMP M! JZ BNKBDOS1
	ADI 10H! MOV M,A
BNKBDOS1:
	STA PDCNT

	;perform requested BDOS function
	pop h! pop b! pop d! push h
	call bdose

	;save results
	pop d! push h ; save results in HL

	;copy disk params
	lxi h,dcnt
	XCHG! MVI C,2! CALL MOVE
	LXI D,SEARCHL! LDAX D! MOV M,A

	;return to non-banked bdos, will restore calling bank
	; and release disk mutual exclusion message
	pop h! ret


bdose:
	xchg! shld info! xchg ;save parameter
	mov a,e! sta linfo ;linfo = low(info) - don't equ
	lxi h,0! shld aret ;return value defaults to 0000
	SHLD RESEL ; RESEL,FCBDSK = 0
	SHLD COMP$FCB$CKS ; COMP$FCB$CKS,SEARCH$USER0 = 0
	SHLD MAKE$XFCB ; MAKE$XFCB,FIND$XFCB = 0
	DAD SP! SHLD ENTSP ; SAVE STACK POSITION
	lxi h,goback! push h ;fake return address

	;compute disk function code address
	mov a,c! STA FX! mov c,e! mov b,d
	CPI NFUNCS+12! JNC HIGH$FXS
	LXI H,DISKF-24! JMP BDOS$JMP
HIGH$FXS:
	LXI H,XDISKF-200
BDOS$JMP:
	mov e,a! mvi d,0 ;DE=func, HL=.diskf-24
	dad d! dad d! mov e,m! inx h! mov d,m ;DE=functab(func)
	LHLD INFO	;INFO IN DE FOR LATER EXCHANGE
	xchg! pchl ;dispatched
;
;	dispatch table for disk functions
diskf:
	dw	func12,func13,func14,func15
	dw	func16,func17,func18,func19
	dw	func20,func21,func22,func23
	dw	func24,func25,func26,func27
	dw	func28,func29,func30,func31
	dw	func32,func33,func34,func35
	dw	func36,func37,func38,func39
	DW	FUNC40,FUNC41,FUNC42,FUNC43
	DW	FUNC44,FUNC45,FUNC46,FUNC47
	DW	FUNC48,FUNC49,FUNC50
nfuncs	equ	($-diskf)/2
XDISKF:
	DW	FUNC100,FUNC101,FUNC102,FUNC103
	DW	FUNC104,FUNC105,FUNC106,FUNC107

PERERR:	DW	PERSUB		;PERMANENT ERROR SUBROUTINE
RODERR:	DW	RODSUB		;RO DISK ERROR SUBROUTINE
ROFERR:	DW	ROFSUB		;RO FILE ERROR SUBROUTINE
SELERR:	DW	SELSUB		;SELECT ERROR SUBROUTINE

;
;	error subroutines
PERSUB:
	;report permanent error
	lxi h,permsg! jmp report$err ;to report the error
;
SELSUB:
	;report select error
	lxi h,selmsg! jmp report$err ;wait console before boot
;
RODSUB:
	;report write to read/only disk
	lxi h,rodmsg! jmp report$err ;wait console
;
ROFSUB:
	;report read/only file
	lxi h,rofmsg ;drop through to wait for console
;
report$err:
	;report error to console, message address in HL
	push h ;stack msg address
	;set D=console #
	call rlr! lxi d,console! dad d! mov d,m
	call xcrlf ;new line
	LDA SELDSK! adi 'A'! sta dskerr ;current disk name
	lxi b,dskmsg! call xprint ;the error message
	pop b! call xprint ;error mssage tail

	lda fx! mvi b,30h
	lxi h,pr$fx1
	cpi 100! jc rpt$err1
	mvi m,31h! inx h! sui 100
rpt$err1:
	sui 10! jc rpt$err2
	inr b! jmp rpt$err1
rpt$err2:
	mov m,b! inx h! adi 3ah! mov m,a
	inx h! mvi m,20h
	lxi h,pr$fcb! mvi m,0
	lda resel! ora a! jz rpt$err3
	mvi m,20h! push d
	lhld info! inx h! xchg! lxi h,pr$fcb1
	mvi c,8! call move! mvi m,'.'! inx h
	mvi c,3! call move! pop d
rpt$err3:
	call xcrlf
	lxi b,pr$fx! call xprint

	CALL GET$MEM$SEG
	ORA A! JZ RTN$PHY$ERRS
	INR A! JZ RTN$PHY$ERRS
	LXI D,PNAME+5! CALL TEST$ERROR$MODE1! JNZ RTN$PHY$ERRS
	jmp reboot ;terminate process
	;ret

xerr$list:
	dw	xe3,xe4,xe5,xe6,xe7,xe8,xe9,xe10,xe11

xe3:	db	'File Opened in Read/Only Mode',0
xe4:	db	0
xe5:	db	'File Currently Open',0
xe6:	db	'Close Checksum Error',0
xe7:	db	'Password Error',0
xe8:	db	'File Already Exists',0
xe9:	db	'Illegal ? in FCB',0
xe10:	db	'Open File Limit Exceeded',0
xe11:	db	'No Room in System Lock List',0

pr$fx: 	db	'Bdos Function: '
pr$fx1:	db	'   '
pr$fcb:	db	'  File: '
pr$fcb1:ds	12
	db	0
 
fx:	ds	1


;
;*****************************************************************
;*****************************************************************
;
;	error messages
IF MPM
dskmsg:	db	'Bdos Err On '
dskerr:	db	' : ',0	
permsg:	db	'Bad Sector',0
selmsg:	db	'Select',0
rofmsg:	db	'File '
rodmsg:	db	'R/O',0
ELSE
dskmsg:	db	'Bdos Err On '
dskerr:	db	' : $'	;filled in by errflg
permsg:	db	'Bad Sector$'
selmsg:	db	'Select$'
rofmsg:	db	'File '
rodmsg:	db	'R/O$'
XERROR:	DB	'X/Err '
XERR$ID:DB	' $'
ENDIF
;
IF BNKBDOS
setlret1:
	mvi a,1
sta$ret:
	sta	aret
func$ret:
	ret
entsp:	ds	2
ENDIF
;
;*****************************************************************
;*****************************************************************
;
;	common values shared between bdosi and bdos
usrcode:db	0	;current user number
info:	ds	2	;information address
aret:	ds	2	;address value to return
lret	equ	aret	;low(aret)
;
;*****************************************************************
;*****************************************************************
;**                                                             **
;**   B a s i c    D i s k   O p e r a t i n g   S y s t e m    **
;**                                                             **
*****************************************************************
;*****************************************************************
;
dvers	equ	30h	;version 3.0
;	module addresses
;
;	literal constants
true	equ	0ffh	;constant true
false	equ	000h	;constant false
enddir	equ	0ffffh	;end of directory
byte	equ	1	;number of bytes for "byte" type
word	equ	2	;number of bytes for "word" type
;
;	fixed addresses in low memory
tfcb	equ	005ch	;default fcb location
tbuff	equ	0080h	;default buffer location
;
;	fixed addresses referenced in bios module are
;	pererr (0009), selerr (000c), roderr (000f)
;
;	error message handlers
;
;
rod$error:						;
	;report read/only disk error
	MVI B,2
 	JMP GOERR				;
;
rof$error:						;
	;report read/only file error			;
	MVI B,3
 	JMP GOERR	
;
sel$error:
	;report select error
	MVI B,4
;
goerr:
	;HL = .errorhandler, call subroutine
	MOV H,B! MVI L,0FFH! SHLD ARET
IF MPM
	call test$error$mode! jnz rtn$phy$errs
ELSE
	LDA ERROR$MODE! ORA A! JNZ RTN$PHY$ERRS
ENDIF
	MOV A,B! LXI H,PERERR-2! JMP BDOS$JMP
;
RTN$PHY$ERRS:
;
IF MPM
	lda lock$shell! ora a! jnz lock$perr
ENDIF
	LDA RETURN$FFFF! ORA A! JNZ GOBACK0
	JMP GOBACK
;
IF MPM
test$error$mode:
	lxi d,pname+4
test$error$mode1:
	call rlr! dad d
	mov a,m! ani 80h! ret
ENDIF
;
BDE$E$BDE$M$HL:
	MOV A,E! SUB L! MOV E,A ;
	MOV A,D! SBB H! MOV D,A ;
	RNC! DCR B! RET
;
BDE$E$BDE$P$HL:
	MOV A,E! ADD L! MOV E,A ;
	MOV A,D! ADC H! MOV D,A ;
	RNC! INR B! RET
;
SHL3BV:
	INR C
SHL3BV1:
	DCR C! RZ
	DAD H! ADC A! JMP SHL3BV1
;
;*********************************************
;	routines used by shell and lock/unlock
;*********************************************
incr$rr:
	call get$rra
	inr m! rnz
	inx h! inr m! rnz
	inx h! inr m! ret
;
save$rr:
	call save$rr2! xchg
save$rr1:
	mvi c,3! jmp move ;ret
save$rr2:
	call get$rra! lxi d,save$ranr! ret
;
reset$rr:
	call save$rr2! jmp save$rr1 ;ret
;
compare:
	ldax d! cmp m! rnz
	inx h! inx d! dcr c! rz
	jmp compare
;****************************************************
;
;
;	local subroutines for bios interface
;
move:
	;move data length of length C from source DE to
	;destination given by HL
	inr c ;in case it is zero
	move0:
		dcr c! rz ;more to move
		ldax d! mov m,a ;one byte moved
		inx d! inx h ;to next byte
		jmp move0
;
selectdisk:
	;select the disk drive given by curdsk, and fill
	;the base addresses curtrka - alloca, then fill
	;the values of the disk parameter block
	MVI A,0FFH! STA CURDSK ; IN CASE SELECT FAILS
	LDA SELDSK! mov c,a ;current disk# to c
	;lsb of e = 0 if not yet logged - in
	call seldskf ;HL filled by call
	;HL = 0000 if error, otherwise disk headers
	mov a,h! ora l! rz ;return with 0000 in HL and z flag
		;disk header block address in hl
		mov e,m! inx h! mov d,m! inx h ;DE=.tran
		shld cdrmaxa! inx h! inx h ;.cdrmax
		shld curtrka! inx h! inx h ;HL=.currec
		shld curreca! inx h! inx h ;HL=.buffa
		INX H! SHLD DRVLBLA! INX H
		;DE still contains .tran
		xchg! shld tranv ;.tran vector
		LXI H,DPBADDR ;DE= source for move, HL=dest
		mvi c,addlist! call move ;addlist filled
		;now fill the disk parameter block
		lhld dpbaddr! xchg ;DE is source
		lxi h,sectpt ;HL is destination
		mvi c,dpblist! call move ;data filled
		;now set single/double map mode
		lhld maxall ;largest allocation number
		mov a,h ;00 indicates < 255
		lxi h,single! mvi m,true ;assume a=00
		ora a! jz retselect
		;high order of maxall not zero, use double dm
		mvi m,false
	retselect:
	LDA SELDSK! STA CURDSK
IF NOT MPM
	LHLD BUFFA! MOV A,H! ORA L! RNZ
	LHLD DRVLBLA! MOV D,M! DCX H! MOV E,M
	XCHG! SHLD BUFFA
ENDIF
	INR A! RET ;select disk function ok
;
home:
	;move to home position, then offset to start of dir
	call homef ;move to track 00, sector 00 reference
	;lxi h,offset ;mov c,m ;inx h ;mov b,m ;call settrkf	;
	;first directory position selected
	xra a ;constant zero to accumulator
	lhld curtrka! mov m,a! inx h! mov m,a ;curtrk=0000
	lhld curreca! mov m,a! inx h! mov m,a ;currec=0000
	INX H! MOV M,A ;CURREC HIGH BYTE=00
	LXI H,0! SHLD DBLK ; DBLK = 0000
	;curtrk, currec both set to 0000
	ret
;
PASS$ARECORD:
	LXI H,ARECORD
	MOV E,M! INX H! MOV D,M! INX H! MOV B,M
	RET
;
rdbuff:
	;read buffer and check condition
	CALL PASS$ARECORD
	call readf ;current drive, track, sector, dma
	jmp diocomp	;check for i/o errors
;
wrbuff:
	;write buffer and check condition
	;write type (wrtype) is in register C
	;wrtype = 0 => normal write operation
	;wrtype = 1 => directory write operation
	;wrtype = 2 => start of new block
	CALL PASS$ARECORD
IF MPM
	lda rem$drv! ora c! mov c,a
ENDIF
	call writef ;current drive, track, sector, dma
diocomp:	;check for disk errors
	ora a! rz			;
	MOV B,A
	CPI 3! JC GOERR
	MVI B,1! JMP GOERR
;
seekdir:
	;seek the record containing the current dir entry
;
	LXI D,0FFFFH ; MASK = FFFF
	LHLD DBLK! MOV A,H! ORA L! JZ SEEKDIR1
	LDA BLKMSK! MOV E,A! XRA A! MOV D,A ; MASK = BLKMSK
	LDA BLKSHF! MOV C,A! XRA A ;
	CALL SHL3BV ; AHL = SHL(DBLK,BLKSHF)
SEEKDIR1:
	PUSH H! PUSH A ; SAVE AHL
;
	lhld dcnt ;directory counter to HL
	mvi c,dskshf! call hlrotr ;value to HL
;
;	ARECORD = SHL(DBLK,BLKSHF) + SHR(DCNT,DSKSHF) & MASK
;
	MOV A,L! ANA E! MOV L,A ; DCNT = DCNT & MASK
	MOV A,H! ANA D! MOV H,A
	POP B! POP D! CALL BDE$E$BDE$P$HL ;
	LXI H,ARECORD! MOV M,E! INX H! MOV M,D! INX H! MOV M,B
;
	;  jmp seek				;
	;ret
;
;
seek:
	;seek the track given by arecord (actual record)
;
	LHLD CURTRKA! MOV C,M! INX H! MOV B,M ; BC = CURTRK
	PUSH B ; S0 = CURTRK 
	LHLD CURRECA! MOV E,M! INX H! MOV D,M
	INX H! MOV B,M ; BDE = CURREC
	LHLD ARECORD! LDA ARECORD+2! MOV C,A ; CHL = ARECORD
SEEK0:
	MOV A,L! SUB E! MOV A,H! SBB D! MOV A,C! SBB B
	PUSH H ; SAVE LOW(ARECORD)
	JNC SEEK1 ; IF ARECORD >= CURREC THEN GO TO SEEK1
	LHLD SECTPT! CALL BDE$E$BDE$M$HL ; CURREC = CURREC - SECTPT
	POP H! XTHL! DCX H! XTHL ; CURTRK = CURTRK - 1
	JMP SEEK0
SEEK1:
	LHLD SECTPT! CALL BDE$E$BDE$P$HL ; CURREC = CURREC + SECTPT
	POP H ; RESTORE LOW(ARECORD)
	MOV A,L! SUB E! MOV A,H! SBB D! MOV A,C! SBB B
	JC SEEK2 ; IF ARECORD < CURREC THEN GO TO SEEK2
	XTHL! INX H! XTHL ; CURTRK = CURTRK + 1
	PUSH H ; SAVE LOW (ARECORD)
	JMP SEEK1
SEEK2:
	XTHL! PUSH H ; HL,S0 = CURTRK, S1 = LOW(ARECORD)
	LHLD SECTPT! CALL BDE$E$BDE$M$HL ; CURREC = CURREC - SECTPT
	POP H! PUSH D! PUSH B! PUSH H ; HL,S0 = CURTRK, 
	; S1 = HIGH(ARECORD,CURREC), S2 = LOW(CURREC), 
	; S3 = LOW(ARECORD)
	XCHG! LHLD OFFSET! DAD D! MOV B,H! MOV C,L
	CALL SETTRKF ; CALL BIOS SETTRK ROUTINE
	; STORE CURTRK
	POP D! LHLD CURTRKA! MOV M,E! INX H! MOV M,D
	; STORE CURREC
	POP B! POP D!
	LHLD CURRECA! MOV M,E! INX H! MOV M,D
	INX H! MOV M,B ; CURREC = BDE
	POP B ; BC = LOW(ARECORD), DE = LOW(CURREC)
	MOV A,C! SUB E! MOV C,A ; BC = BC - DE
	MOV A,B! SBB D! MOV B,A ;
;
	lhld tranv! xchg ;BC=sector#, DE=.tran
	call sectran ;HL = tran(sector)
	mov c,l! mov b,h ;BC = tran(sector)
	jmp setsecf ;sector selected
	;ret
;
;	file control block (fcb) constants
empty	equ	0e5h	;empty directory entry
lstrec	equ	127	;last record# on extent
recsiz	equ	128	;record size
fcblen	equ	32	;file control block size
dirrec	equ	recsiz/fcblen	;directory FCBS / record
dskshf	equ	2	;log2(dirrec)
dskmsk	equ	dirrec-1
fcbshf	equ	5	;log2(fcblen)
;
extnum	equ	12	;extent number field
maxext	equ	31	;largest extent number
ubytes	equ	13	;unfilled bytes field
modnum	equ	14	;data module number
;
MAXMOD	equ	64	;LARGEST MODULE NUMBER
;
fwfmsk	equ	80h	;file write flag is high order modnum
namlen	equ	15	;name length
reccnt	equ	15	;record count field
dskmap	equ	16	;disk map field
lstfcb	equ	fcblen-1
nxtrec	equ	fcblen
ranrec	equ	nxtrec+1;random record field (2 bytes)
;
;	reserved file indicators
rofile	equ	9	;high order of first type char
invis	equ	10	;invisible file in dir command
;
;	utility functions for file access
;
dm$position:
	;compute disk map position for vrecord to HL
	lxi h,blkshf! mov c,m ;shift count to C
	lda vrecord ;current virtual record to A
	dmpos0:
		ora a! rar! dcr c! jnz dmpos0
	;A = shr(vrecord,blkshf) = vrecord/2**(sect/block)
	mov b,a ;save it for later addition
	mvi a,8! sub m ;8-blkshf to accumulator
	mov c,a ;extent shift count in register c
	lda extval ;extent value ani extmsk
	dmpos1:
		;blkshf = 3,4,5,6,7, C=5,4,3,2,1
		;shift is 4,3,2,1,0
		dcr c! jz dmpos2
		ora a! ral! jmp dmpos1
	dmpos2:
	;arrive here with A = shl(ext and extmsk,7-blkshf)
	add b ;add the previous shr(vrecord,blkshf) value
	;A is one of the following values, depending upon alloc
	;bks blkshf
	;1k   3     v/8 + extval * 16
	;2k   4     v/16+ extval * 8
	;4k   5     v/32+ extval * 4
	;8k   6     v/64+ extval * 2
	;16k  7     v/128+extval * 1
	ret ;with dm$position in A
;
GETDMA:
	LHLD INFO! LXI D,DSKMAP! DAD D! RET
;
getdm:
	;return disk map value from position given by BC
	CALL GETDMA
	dad b ;index by a single byte value
	lda single ;single byte/map entry?
	ora a! jz getdmd ;get disk map single byte
		mov l,m! mvi h,0! ret ;with HL=00bb
	getdmd:
		dad b ;HL=.fcb(dm+i*2)
		;double precision value returned
		mov e,m! inx h! mov d,m! xchg! ret
;
index:
	;compute disk block number from current fcb
	call dm$position ;0...15 in register A
	mov c,a! mvi b,0! call getdm ;value to HL
	shld arecord! ret
;
allocated:
	;called following index to see if block allocated
	lhld arecord! mov a,l! ora h! ret
;
atran:
	;compute actual record address, assuming index called
;
;	ARECORD = SHL(ARECORD,BLKSHF)
;
	LDA BLKSHF! MOV C,A
	LHLD ARECORD! XRA A! CALL SHL3BV
	SHLD ARECORD! STA ARECORD+2 ; 
;
	SHLD ARECORD1 ; SAVE LOW(ARECORD)
;
;	ARECORD = ARECORD OR (VRECORD AND BLKMSK)
;
	LDA BLKMSK! MOV C,A! LDA VRECORD! ANA C
	LXI H,ARECORD! ORA M! MOV M,A! RET
;
GET$ATTS:
	;GET VOLATILE ATTRIBUTES STARTING AT F'5
	;INFO LOCATES FCB
	LHLD INFO
	LXI D,5! DAD D ; HL = .FCB(F'5)
	MVI E,0111$1111B! MVI C,4
GET$ATTS$LOOP:
	MOV A,M! PUSH A
	RAL! MOV A,D! ADC A! MOV D,A
	POP A! ANA E! MOV M,A
	INX H! DCR C! JNZ GET$ATTS$LOOP
	MOV A,D! ADD A! ADD A! ADD A! ADD A! RET
;
GET$S1:
	;GET CURRENT S1 FIELD TO A
	CALL GETEXTA! INX H! MOV A,M! RET ;
;
GET$RRA:
	;GET CURRENT RAN REC FIELD ADDRESS TO HL
	LHLD INFO! LXI D,RANREC! DAD D ;HL=.FCB(RANREC)
	RET
;
getexta:
	;get current extent field address to HL
	lhld info! lxi d,extnum! dad d ;HL=.fcb(extnum)
	ret
;
GETRCNTA:
	;GET RECCNT ADDRESS TO HL
	LHLD INFO! LXI D,RECCNT! DAD D! RET
;
getfcba:
	;compute reccnt and nxtrec addresses for get/setfcb
	CALL GETRCNTA! xchg ;DE=.fcb(reccnt)
	lxi h,(nxtrec-reccnt)! dad d ;HL=.fcb(nxtrec) 
	ret
;
getfcb:
	;set variables from currently addressed fcb
	call getfcba ;addresses in DE, HL
	mov a,m! sta vrecord ;vrecord=fcb(nxtrec)
	xchg! mov a,m! sta rcount ;rcount=fcb(reccnt)
	call getexta ;HL=.fcb(extnum)
	lda extmsk ;extent mask to a
	ana m ;fcb(extnum) and extmsk
	sta extval
	ret
;
setfcb:
	;place values back into current fcb
	call getfcba ;addresses to DE, HL
	lda seqio
	cpi 02! jnz setfcb1! xra a	;check ranfill	
setfcb1:
 	mov c,a ;=1 if sequential i/o
	lda vrecord! add c! mov m,a ;fcb(nxtrec)=vrecord+seqio
	xchg! lda rcount! mov m,a ;fcb(reccnt)=rcount
	ret
;
hlrotr:
	;hl rotate right by amount C
	inr c ;in case zero
	hlrotr0: dcr c! rz ;return when zero
		mov a,h! ora a! rar! mov h,a ;high byte
		mov a,l! rar! mov l,a ;low byte
		jmp hlrotr0
;
;
compute$cs:
	;compute checksum for current directory buffer
	mvi c,recsiz ;size of directory buffer
	lhld buffa ;current directory buffer
	xra a ;clear checksum value
	computecs0:
		add m! inx h! dcr c ;cs=cs+buff(recsiz-C)
		jnz computecs0
	ret ;with checksum in A
;
CHKSUM$FCB: ; COMPUTE CHECKSUM FOR FCB
	; ADD 1ST 12 BYTES OF FCB + CURDSK + 
	;     HIGH$EXT + XFCB$READ$ONLY + BBH
IF MPM
	lxi h,pdcnt! mov a,m
	inx h! add m ; Add high$ext
ELSE
	LXI H,HIGH$EXT! MOV A,M
ENDIF
	INX H! ADD M ; ADD XFCB$READ$ONLY
	INX H! ADD M ; ADD CURDSK
	ADI 0BBH ; ADD 0BBH TO BIAS CHECKSUM
	LHLD INFO! MVI C,12! CALL COMPUTECS0
	; SKIP EXTNUM
	INX H
	; ADD FCB(S1)
	ADD M! INX H
	; SKIP MODNUM
	INX H
	; SKIP FCB(RECCNT)
	; ADD DISK MAP
	INX H! MVI C,16! CALL COMPUTECS0
	ORA A! RET ; Z FLAG SET IF CHECKSUM VALID
;
SET$CHKSUM$FCB:
	CALL CHKSUM$FCB! RZ
	MOV B,A! CALL GETS1
	CMA! ADD B! CMA
	MOV M,A! RET
;
RESET$CHKSUM$FCB:
	XRA A! STA COMP$FCB$CKS
	CALL CHKSUM$FCB! RNZ
	CALL GET$S1! INR M! RET
;
CHEK$FCB:
	LDA HIGH$EXT
	; IF EXT & 0110$0000B = 0110$0000B THEN
	; SET FCB(0) TO 0 (USER 0)
	CPI 0110$0000B! JNZ CHEK$FCB1
	LHLD INFO! XRA A! MOV M,A ; FCB(0) = 0
CHEK$FCB1:
	JMP CHKSUM$FCB ;RET
;
CHECK$FCB:
IF MPM
	xra a! sta check$fcb4
check$fcb1:
ENDIF
	CALL CHEK$FCB! RZ
CHECK$FCB2:
;
IF MPM
	ani 0fh! jnz check$fcb3
	lda pdcnt! ora a! jz check$fcb3
	call set$sdcnt! sta dont$close
	call close1
	lxi h,lret! inr m! jz check$fcb3
	mvi m,0! call pack$sdcnt! mvi b,5
	call search$olist! rz
check$fcb3:
;
ENDIF
	POP H ; DISCARD RETURN ADDRESS
IF MPM
check$fcb4:
	nop
ENDIF
	MVI A,10! JMP STA$RET
;
SET$FCB$CKS$FLAG:
	MVI A,0FFH! STA COMP$FCB$CKS! RET
;
hlrotl:
 	;rotate the mask in HL by amount in C
 	inr c ;may be zero
 	hlrotl0: dcr c! rz ;return if zero
 		dad h! jmp hlrotl0
;
;
set$cdisk:
	;set a "1" value in curdsk position of BC
	LDA SELDSK
SET$CDISK1:
	push b ;save input parameter
	mov c,a ;ready parameter for shift
	lxi h,1 ;number to shift
	call hlrotl ;HL = mask to integrate
	pop b ;original mask
	mov a,c! ora l! mov l,a
	mov a,b! ora h! mov h,a ;HL = mask or rol(1,curdsk)
	ret
;
nowrite:
	;return true if dir checksum difference occurred
	lhld rodsk! 
;
TEST$VECTOR:
	LDA SELDSK
TEST$VECTOR1:
	mov c,a! call hlrotr
	mov a,l! ani 1b! ret ;non zero if curdsk bit on
;
set$ro:
	;set current disk to read only
	lxi h,rodsk! mov c,m! inx h! mov b,m
	call set$cdisk ;sets bit to 1
	shld rodsk
	;high water mark in directory goes to max
	lhld dirmax! inx h! xchg ;DE = directory max	
	lhld cdrmaxa ;HL = .cdrmax
	mov m,e! inx h! mov m,d ;cdrmax = dirmax
	ret
;
check$rodir:
	;check current directory element for read/only status
	call getdptra ;address of element
;
check$rofile:
	;check current buff(dptr) or fcb(0) for r/o status
	CALL RO$TEST
	rnc ;return if not set
	jmp rof$error ;exit to read only disk message
;
RO$TEST:
	LXI D,ROFILE! DAD D
	MOV A,M! RAL! RET ;CARRY SET IF R/O
;
;
check$write:
	;check for write protected disk
	call nowrite! rz ;ok to write if not rodsk
	jmp rod$error ;read only disk error
;
getdptra:
	;compute the address of a directory element at
	;positon dptr in the buffer
	lhld buffa! lda dptr			;
addh:
	;HL = HL + A
	add l! mov l,a! rnc
	;overflow to H
	inr h! ret
;
;
getmodnum:
	;compute the address of the module number 
	;bring module number to accumulator
	;(high order bit is fwf (file write flag)
	lhld info! lxi d,modnum! dad d ;HL=.fcb(modnum)
	mov a,m! ret ;A=fcb(modnum)
;
clrmodnum:
	;clear the module number field for user open/make
	call getmodnum! mvi m,0 ;fcb(modnum)=0
	ret
;
CLR$EXT:
	;FCB EXT = FCB EXT & 1FH
	CALL GETEXTA! MOV A,M! ANI 0001$1111B! MOV M,A!
	RET
;
setfwf:
	call getmodnum ;HL=.fcb(modnum), A=fcb(modnum)
	;set fwf (file write flag) to "1"
	ori fwfmsk! mov m,a ;fcb(modnum)=fcb(modnum) or 80h
	;also returns non zero in accumulator
	ret
;
;
compcdr:
	;return cy if cdrmax > dcnt
	lhld dcnt! xchg ;DE = directory counter
	lhld cdrmaxa ;HL=.cdrmax
	mov a,e! sub m ;low(dcnt) - low(cdrmax)
	inx h ;HL = .cdrmax+1
	mov a,d! sbb m ;hig(dcnt) - hig(cdrmax)
	;condition dcnt - cdrmax  produces cy if cdrmax>dcnt
	ret
;
setcdr:
	;if not (cdrmax > dcnt) then cdrmax = dcnt+1
	call compcdr
	rc ;return if cdrmax > dcnt
	;otherwise, HL = .cdrmax+1, DE = dcnt
	inx d! mov m,d! dcx h! mov m,e
	ret
;
subdh:
	;compute HL = DE - HL
	mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a
	ret
;
newchecksum:
	mvi c,true ;drop through to compute new checksum
checksum:
	;compute current checksum record and update the
	;directory element if C=true, or check for = if not
	;drec < chksiz?
	LHLD ARECORD! xchg! lhld chksiz
	MOV A,H! ANI 7FH! MOV H,A ; MASK OFF PERMANENT DRIVE BIT
	call subdh ;DE-HL
	rnc ;skip checksum if past checksum vector size
		;drec < chksiz, so continue
		push b ;save init flag
		call compute$cs ;check sum value to A
		lhld checka ;address of check sum vector
		xchg
		LHLD ARECORD
		dad d ;HL = .check(drec)
		pop b ;recall true=0ffh or false=00 to C
		inr c ;0ffh produces zero flag
		jz initial$cs
IF MPM
		inr c! jz test$dir$cs
ENDIF
			;not initializing, compare
			cmp m ;compute$cs=check(drec)?
			rz ;no message if ok
			;checksum error, are we beyond
			;the end of the disk?
			call compcdr
			rnc ;no message if so
IF MPM
			call nowrite! cz flush$file0
ENDIF
			jmp set$ro ;read/only disk set
IF MPM
		test$dir$cs:
			cmp m! jnz flush$files
			ret
ENDIF
		initial$cs:
			;initializing the checksum
			mov m,a! ret
;
;
wrdir:
	;write the current directory entry, set checksum
	CALL CHECK$WRITE
	call newchecksum ;initialize entry
	call setdir ;directory dma
	mvi c,1 ;indicates a write directory operation
	call wrbuff ;write the buffer
        jmp setdata ;to data dma address
	;ret
;
rd$dir:
	;read a directory entry into the directory buffer
	call setdir ;directory dma
	call rdbuff ;directory record loaded
        ; jmp setdata to data dma address    
	;ret
;
setdata:
	;set data dma address
	lxi h,dmaad! jmp setdma ;to complete the call
;
setdir:
	;set directory dma address
	lxi h,buffa  ;jmp setdma to complete call     
;
setdma:
	;HL=.dma address to set (i.e., buffa or dmaad)
	mov c,m! inx h! mov b,m ;parameter ready
	jmp setdmaf
;
;
IF NOT MPM
dir$to$user:
	;copy the directory entry to the user buffer
	;after call to search or searchn by user code
	lhld buffa! xchg ;source is directory buffer
	lhld dmaad ;destination is user dma address
	mvi c,recsiz ;copy entire record
	jmp move
	;ret
ENDIF
;
MAKE$FCB$INV: ; FLAG FCB AS INVALID
	; RESET FCB WRITE FLAG
	CALL SETFWF
	; SET 1ST TWO BYTES OF DISKMAP TO FFH
	INX H! INX H! MVI A,0FFH! MOV M,A! INX H! MOV M,A
	RET
;
CHK$INV$FCB: ; CHECK FOR INVALID FCB
	CALL GETDMA! JMP TEST$FFFF
;
TST$INV$FCB: ; TEST FOR INVALID FCB
	CALL CHK$INV$FCB! RNZ
	POP H! MVI A,9! JMP STA$RET! ; LRET = 9
;
end$of$dir:
	;return zero flag if at end of directory, non zero
	;if not at end (end of dir if dcnt = 0ffffh)
	lxi h,dcnt
TEST$FFFF:
	mov a,m ;may be 0ffh
	inx h! cmp m ;low(dcnt) = high(dcnt)?
	rnz ;non zero returned if different
	;high and low the same, = 0ffh?
	inr a ;0ffh becomes 00 if so
	ret
;
set$end$dir:
	;set dcnt to the end of the directory
	lxi h,enddir! shld dcnt! ret
;
read$dir:
	;read next directory entry, with C=true if initializing
	lhld dirmax! xchg ;in preparation for subtract
	lhld dcnt! inx h! shld dcnt ;dcnt=dcnt+1
	;continue while dirmax >= dcnt (dirmax-dcnt no cy)
	call subdh ;DE-HL
	jnc read$dir0
		;yes, set dcnt to end of directory
		jmp set$end$dir			;
;		ret				;
	read$dir0:
		;not at end of directory, seek next element
		;initialization flag is in C
		lda dcnt! ani dskmsk ;low(dcnt) and dskmsk
		mvi b,fcbshf ;to multiply by fcb size
		read$dir1:
			add a! dcr b! jnz read$dir1
		;A = (low(dcnt) and dskmsk) shl fcbshf
		sta dptr ;ready for next dir operation
		ora a! rnz ;return if not a new record
		push b ;save initialization flag C
		call seek$dir ;seek proper record
		call rd$dir ;read the directory record
		pop b ;recall initialization flag
		jmp checksum ;checksum the directory elt
		;ret
;
;
getallocbit:
	;given allocation vector position BC, return with byte
	;containing BC shifted so that the least significant
	;bit is in the low order accumulator position.  HL is
	;the address of the byte for possible replacement in
	;memory upon return, and D contains the number of shifts
	;required to place the returned value back into position
	mov a,c! ani 111b! inr a! mov e,a! mov d,a
	;d and e both contain the number of bit positions to shift
;
	MOV H,B! MOV L,C! MVI C,3 ; BC = BC SHR 3
	CALL HLROTR ; HLROTR DOES NOT TOUCH D AND E
	MOV B,H! MOV C,L
;
	lhld alloca ;base address of allocation vector
	dad b! mov a,m ;byte to A, hl = .alloc(BC shr 3)
	;now move the bit to the low order position of A
	rotl: rlc! dcr e! jnz rotl! ret
;
;
setallocbit:
	;BC is the bit position of ALLOC to set or reset.  The
	;value of the bit is in register E.
	push d! call getallocbit ;shifted val A, count in D
	ani 1111$1110b ;mask low bit to zero (may be set)
	pop b! ora c ;low bit of C is masked into A
;	jmp rotr ;to rotate back into proper position	
	;ret
rotr:
	;byte value from ALLOC is in register A, with shift count
	;in register C (to place bit back into position), and
	;target ALLOC position in registers HL, rotate and replace
	rrc! dcr d! jnz rotr ;back into position
	mov m,a ;back to ALLOC
	ret
;
scandm:
	;scan the disk map addressed by dptr for non-zero
	;entries, the allocation vector entry corresponding
	;to a non-zero entry is set to the value of C (0,1)
	call getdptra ;HL = buffa + dptr
	;HL addresses the beginning of the directory entry
	lxi d,dskmap! dad d ;hl now addresses the disk map
	push b ;save the 0/1 bit to set
	mvi c,fcblen-dskmap+1 ;size of single byte disk map + 1
	scandm0:
		;loop once for each disk map entry
		pop d ;recall bit parity
		dcr c! rz ;all done scanning?
		;no, get next entry for scan
		push d ;replace bit parity
		lda single! ora a! jz scandm1
			;single byte scan operation
			push b ;save counter
			push h ;save map address
			mov c,m! mvi b,0 ;BC=block#
			jmp scandm2
		scandm1:
			;double byte scan operation
			dcr c ;count for double byte
			push b ;save counter
			mov c,m! inx h! mov b,m ;BC=block#
			push h ;save map address
		scandm2:
			;arrive here with BC=block#, E=0/1
			mov a,c! ora b ;skip if = 0000
			jz scanm3
				lhld maxall	;check invalid index
			mov a,l! sub c! mov a,h! sbb b	;maxall - block#
			cnc set$alloc$bit			;
			;bit set to 0/1
		scanm3:						;
			pop h! inx h ;to next bit position
			pop b ;recall counter
			jmp scandm0 ;for another item
;
GET$NALBS: ; GET # OF ALLOCATION VECTOR BYTES
	LHLD MAXALL! MVI C,3
	;number of bytes in alloc vector is (maxall/8)+1
	CALL HLROTR! INX H! RET
;
IF MPM
test$dir:
	call home
	call set$end$dir
test$dir1:
	mvi c,0feh! call read$dir
	lda flushed! ora a! rnz
	call end$of$dir! rz
	jmp test$dir1
;
ENDIF
initialize:
	;initialize the current disk
	;lret = false ;set to true if $ file exists
	;compute the length of the allocation vector - 2
IF MPM
	lhld tlog! call test$vector! jz initialize1
	lhld tlog! call remove$drive! shld tlog
	xra a! sta flushed

; **** PATCH 2.1
; Always reinitialize if no open files exist on drive

	CALL PATCH10

; Removed code:
;	call test$dir

	rz
initialize1:
ENDIF
	CALL GET$NALBS ; get # of allocation vector bytes
	mov b,h! mov c,l ;count down BC til zero
	lhld alloca ;base of allocation vector
	;fill the allocation vector with zeros
	initial0:
		mvi m,0! inx h ;alloc(i)=0
		dcx b ;count length down
		mov a,b! ora c! jnz initial0
	;
	LHLD DRVLBLA! MOV M,A ; ZERO OUT DRIVE DESC BYTE
	;
	;set the reserved space for the directory
	lhld dirblk! xchg
	lhld alloca ;HL=.alloc()
	mov m,e! inx h! mov m,d ;sets reserved directory blks
	;allocation vector initialized, home disk
	call home
        ;cdrmax = 3 (scans at least one directory record)
	lhld cdrmaxa! mvi m,3! inx h! mvi m,0
	;cdrmax = 0000
	call set$end$dir ;dcnt = enddir
	;read directory entries and check for allocated storage
	initial2:
		mvi c,true! call read$dir
		call end$of$dir! rz ;return if end of directory
		;not end of directory, valid entry?
		call getdptra ;HL = buffa + dptr
		mvi a,empty! cmp m
		jz initial2 ;go get another item
		;
		MVI A,20H! CMP M! JZ DRV$LBL
		MVI A,70H! ANA M! JNZ INITIAL3
		;
		;now scan the disk map for allocated blocks
		mvi c,1 ;set to allocated
		call scandm
	INITIAL3:
		call setcdr ;set cdrmax to dcnt
		jmp initial2 ;for another entry
;
DRV$LBL:
		LXI D,EXTNUM! DAD D! MOV A,M
		LHLD DRVLBLA! MOV M,A! JMP INITIAL3
;
;
copy$dirloc:
	;copy directory location to lret following
	;delete, rename, ... ops
	lda dirloc! jmp sta$ret				;
;	ret						;
;
compext:
	;compare extent# in A with that in C, return nonzero
	;if they do not match
	push b ;save C's original value
	push psw! lda extmsk! cma! mov b,a
	;B has negated form of extent mask
	mov a,c! ana b! mov c,a ;low bits removed from C
	pop psw! ana b ;low bits removed from A
	sub c! ani maxext ;set flags
	pop b ;restore original values
	ret
;
GET$DIR$EXT:
	;COMPUTE DIRECTORY EXTENT FROM FCB
	;SCAN FCB DISK MAP BACKWARDS
	CALL GETFCBA ; HL = .FCB(VRECORD)
	MVI C,16! MOV B,C! INR C! PUSH B
	; B=DSKMAP POS (REL TO 0)
GET$DE0:
	POP B ;
	DCR C ;
	XRA A ; COMPARE TO ZERO
GET$DE1:
	DCX H! DCR B; DECR DSKMAP POSITION
	CMP M! JNZ GET$DE2 ; FCB(DSKMAP(B)) ~= 0
	DCR C! JNZ GET$DE1
	;ALL BLOCKS = 0 IN FCB DISK MAP
GET$DE2:
	LDA SINGLE! ORA A! MOV A,B ;
	JNZ GET$DE3
	RAR ; NOT SINGLE, DIVIDE BLK IDX BY 2
GET$DE3:
	PUSH B! PUSH H ; SAVE DSKMAP POSITION & COUNT
	MOV L,A! MVI H,0 ; HL = NON-ZERO BLK IDX
	; COMPUTE EXT OFFSET FROM LAST NON-ZERO
	; BLOCK INDEX BY SHIFTING BLK IDX RIGHT
	; 7 - BLKSHF
	LDA BLKSHF! MOV D,A! MVI A,7! SUB D ;
	MOV C,A! CALL HLROTR! MOV B,L ;
	; B = EXT OFFSET
	LDA EXTMSK! CMP B! POP H! JC GET$DE0
	; VERIFY COMPUTED EXTENT OFFSET <= EXTMSK
	CALL GETEXTA! MOV C,M
	CMA! ANI MAXEXT! ANA C! ORA B
	;DIR EXT = (FCB EXT & (~ EXTMSK) & MAXEXT) | EXT OFFSET
	POP B ; RESTORE STACK
	RET ; A = DIRECTORY EXTENT
;
SEARCHI:
	;SEARCH INITIALIZATION
	lhld info! shld searcha ;searcha = info
SEARCHI1:
	mvi a,0ffh! sta dirloc ;changed if actually found
	lxi h,searchl! mov m,c ;searchl = C
	RET
;
search:
	;search for directory element of length C at info
	CALL SEARCHI;
SEARCH1: ; ENTRY POINT USED BY RENAME
	call set$end$dir ;dcnt = enddir
	call home ;to start at the beginning
	;(drop through to searchn)			;
;
searchn:
	;search for the next directory element, assuming
	;a previous call on search which sets searcha and
	;searchl
;
IF MPM
	lxi h,user0$pass! xra a! cmp m! mov m,a! cnz swap
ELSE
	XRA A! STA USER0$PASS ;
ENDIF
;
	mvi c,false! call read$dir ;read next dir element
	call end$of$dir! jz search$fin ;skip to end if so
		;not end of directory, scan for match
		lhld searcha! xchg ;DE=beginning of user fcb
		ldax d ;first character
		cpi empty ;keep scanning if empty
		jz searchnext
		;not empty, may be end of logical directory
		push d ;save search address
		call compcdr ;past logical end?
		pop d ;recall address
		jnc search$fin ;artificial stop
searchnext:
		call getdptra ;HL = buffa+dptr
		lda searchl! mov c,a ;length of search to c
		mvi b,0 ;b counts up, c counts down
		;
		MOV A,M! CPI EMPTY! CZ SAVE$DCNT$POS1 
		XRA A! STA SAVE$XFCB
		MOV A,M! ANI 1110$1111B! CMP M! JZ SEARCH$LOOP
		XCHG! CMP M! XCHG! JNZ SEARCH$LOOP
		LDA FIND$XFCB! ORA A! JZ SEARCH$N
		STA SAVE$XFCB! JMP SEARCHOK
		;
		searchloop:
			mov a,c! ora a! jz endsearch
			ldax d! cpi '?'! jz searchok ;? IN USER FCB
			;scan next character if not ubytes
			mov a,b! cpi ubytes! jz searchok
			;not the ubytes field, extent field?
			cpi extnum ;may be extent field
			jz searchext ;skip to search extent
			CPI MODNUM! LDAX D! CZ SEARCHMOD
			sub m! ani 7fh ;mask-out flags/extent modulus
			JNZ SEARCHNM ;skip if not matched
			jmp searchok ;matched character
		searchext:
			LDAX D
			;attempt an extent # match
			push b ;save counters
			;
IF MPM
			push h
 			lhld sdcnt
 			inr h! jnz dont$save
 			lhld dcnt! shld sdcnt
 			lhld dblk! shld sdblk
 		dont$save:
			pop h
ENDIF
			mov c,m ;directory character to c
			call compext ;compare user/dir char
			;
			MOV B,A
			LDA USER0PASS! INR A! JZ SAVE$DCNT$POS2
			; DISABLE SEARCH OF USER 0 IF ANY FCB
			; IS FOUND UNDER THE CURRENT USER #
			XRA A! STA SEARCH$USER0
			DCR A! STA FCB$EXISTS
			MOV A,B
			;
			pop b ;recall counters
			ORA A ; SET FLAG
			jnz searchn ;skip if no match
		searchok:
			;current character matches
			inx d! inx h! inr b! dcr c
			jmp searchloop
		endsearch:
			;entire name matches, return dir position
			LDA SAVE$XFCB! ORA A! JZ ENDSEARCH1
			LDA XDCNT+1! CPI 0FEH! CZ SAVE$DCNT$POS0
			JMP SEARCHN
		ENDSEARCH1:
			STA DIRLOC ; DIRLOC = 0
			lda dcnt! ani dskmsk! sta lret
			;lret = low(dcnt) and 11b
			;SUCCESSFUL SEARCH -
			;RETURN WITH ZERO FLAG RESET
			MOV B,A! INR B! RET
		SEARCHMOD:
			ANI 3FH! RET ; MASK OFF HIGH 2 BITS
		search$fin:
			;end of directory, or empty name
			;
			CALL SAVE$DCNT$POS1 ;
			;
			;SET DCNT = 0FFFFH
			call set$end$dir ;may be artifical end
		LRET$EQ$FF:
			;UNSUCCESSFUL SEARCH -
			;RETURN WITH ZERO FLAG SET
			;LRET,LOW(ARET) = 0FFH
			mvi a,255! MOV B,A! INR B! jmp sta$ret		;
			;
		SEARCHNM: ; SEARCH NO MATCH ROUTINE
			MOV A,B! ORA A! JNZ SEARCHN ; FCB(0)?
			MOV A,M! ORA A! JNZ SEARCHN ; DIR FCB(0)=0?
			LDA SEARCH$USER0! ORA A! JZ SEARCHN
			STA USER0$PASS
			;
IF MPM
			call swap
ENDIF
			;
			JMP SEARCHOK
;
IF MPM
swap: ; swap dcnt,sdblk with sdcnt0,sdblk0
	push h! push d! push b
	lxi d,sdcnt! lxi h,sdcnt0
	mvi b,4
swap1:
	ldax d! mov c,a! mov a,m
	stax d! mov m,c
	inx h! inx d! dcr b! jnz swap1
	pop b! pop d! pop h! 
	ret
ENDIF
;
SAVE$DCNT$POS2:
	; SAVE DIRECTORY POSITION OF MATCHING FCB
	; UNDER USER 0 WITH MATCHING EXTENT # & MODNUM = 0
	; A = 0 ON ENTRY
	ORA B! POP B! LXI B,SEARCHN! PUSH B! RNZ
	; CALL IF USER0PASS = 0FFH &
	;         DIR FCB(EXTNUM) = FCB(EXTNUM)
	;         DIR FCB(MODNUM) = 0
SAVE$DCNT$POS0:
	CALL SAVE$DCNT$POS ; RETURN TO SEARCHN
SAVE$DCNT$POS1:
	; SAVE DIRECTORY POSITION OF FIRST EMPTY FCB
	; OR THE END OF THE DIRECTORY
	PUSH H! 
	LHLD XDCNT! INR H! JNZ SAVE$DCNT$POS$RET ; RETURN IF H ~= 0FFH
SAVE$DCNT$POS:
	LHLD DCNT! SHLD XDCNT ;
	LHLD DBLK! SHLD XDBLK ;
SAVE$DCNT$POS$RET:
	POP H! RET
;
INIT$XFCB$SEARCH:
	MVI A,0FFH
INIT$XFCB$SEARCH1:
	STA FIND$XFCB! MVI A,0FEH! STA XDCNT+1! RET

;
DOES$XFCB$EXIST:
	LDA XDCNT+1! CPI 0FEH! RZ
	CALL SET$DCNT$DBLK
	XRA A! CALL INIT$XFCB$SEARCH1
	LHLD SEARCHA! MOV A,M! ORI 10H! MOV M,A
	MVI C,EXTNUM! CALL SEARCHI1! JMP SEARCHN 
;
delete:
	;delete the currently addressed file
	call check$write ;write protected?
	CALL GET$ATTS! STA ATTRIBUTES
	CALL CHK$PWS! PUSH A
	CALL INIT$XFCB$SEARCH
	MVI C,EXTNUM! CALL SEARCH ;SEARCH THROUGH FILE TYPE
	POP B! JZ DELETE2
	MOV A,B! ORA A! JNZ CHK$PW1
	LDA ATTRIBUTES! RAL! JC DELETE015
	LHLD INFO! CALL CHK$WILD! JZ DELETE015
IF MPM
	call tst$olist
ENDIF
	CALL CHECK$RODIR! JMP DELETE0
;
	DELETE01:
		;LOOP WHILE DIRECTORY MATCHES
		JZ DELETE02
	DELETE015:
IF MPM
		call tst$olist
ENDIF
		CALL CHECK$RODIR
		CALL SEARCHN! JMP DELETE01
	DELETE02:
;
	LDA ATTRIBUTES! RAL! JC DELETE2

	mvi c,extnum! call search ;search through file type
	delete0:
		;loop while directory matches
		JZ DELETE2
	DELETE1:
		;set each non zero disk map entry to 0
		;in the allocation vector
IF MPM
		call chk$olist
ENDIF
		call getdptra ;HL=.buff(dptr)
		MOV A,M! ANI 10H
		mvi m,empty
		mvi c,0! CZ SCANDM ;alloc elts set to 0
		call wrdir ;write the directory
		call searchn ;to next element
		jmp delete0 ;for another record
	DELETE2:
		CALL DOES$XFCB$EXIST! JNZ DELETE1! JMP COPY$DIR$LOC
;
get$block:
	;given allocation vector position BC, find the zero bit
	;closest to this position by searching left and right.
	;if found, set the bit to one and return the bit position
	;in hl.  if not found (i.e., we pass 0 on the left, or
	;maxall on the right), return 0000 in hl
	mov d,b! mov e,c ;copy of starting position to de
	lefttst:
		mov a,c! ora b! jz righttst ;skip if left=0000
		;left not at position zero, bit zero?
		dcx b! push d! push b ;left,right pushed
		call getallocbit
		rar! jnc retblock ;return block number if zero
		;bit is one, so try the right
		pop b! pop d ;left, right restored
	righttst:
		lhld maxall ;value of maximum allocation#
		mov a,e! sub l! mov a,d! sbb h ;right=maxall?
		jnc retblock0 ;return block 0000 if so
		inx d! push b! push d ;left, right pushed
		mov b,d! mov c,e ;ready right for call
		call getallocbit
		rar! jnc retblock ;return block number if zero
		pop d! pop b ;restore left and right pointers
		jmp lefttst ;for another attempt
	retblock:
		ral! inr a ;bit back into position and set to 1
		;d contains the number of shifts required to reposition
		call rotr ;move bit back to position and store
		pop h! pop d ;HL returned value, DE discarded
		ret
	retblock0:
		;cannot find an available bit, return 0000
		mov a,c				;
		ora b! jnz lefttst	;also at beginning    
		lxi h,0000h! ret
;
copy$dir:
	;copy fcb information starting at C for E bytes
	;into the currently addressed directory entry
	MVI D,80H
COPY$DIR0:
	CALL COPY$DIR2
	INR C
COPY$DIR1:
	DCR C! JZ SEEK$COPY
	MOV A,M! ANA B! PUSH B
	MOV B,A! LDAX D! ANI 7FH! ORA B! MOV M,A
	POP B! INX H! INX D! JMP COPY$DIR1
COPY$DIR2:
	push d ;save length for later
	mvi b,0 ;double index to BC
	lhld info ;HL = source for data
	dad b! xchg ;DE=.fcb(C), source for copy
	call getdptra ;HL=.buff(dptr), destination
	pop b ;DE=source, HL=dest, C=length
	RET
seek$copy:
	;enter from close to seek and copy current element
	call seek$dir ;to the directory element
	jmp wrdir ;write the directory element
	;ret
;
CHECK$WILD:
	;CHECK FOR ? IN FILE NAME OR TYPE
	LHLD INFO
CHECK$WILD0:	; ENTRY POINT USED BY RENAME
	CALL CHK$WILD! RNZ
	MVI A,9! POP H ; DISCARD RETURN ADDRESS
	JMP SET$ARET
;
CHK$WILD:
	MVI B,03FH! MVI C,11
CHK$WILD1:
	INX H! MOV A,B! SUB M! ANA B! RZ
	DCR C! JNZ CHK$WILD1! ORA A! RET
;
COPY$USER$NO:
	LHLD INFO! MOV A,M! LXI B,DSKMAP
	DAD B! MOV M,A! RET
;
rename:
	;rename the file described by the first half of
	;the currently addressed file control block. the
	;new name is contained in the last half of the
	;currently addressed file conrol block.  the file
	;name and type are changed, but the reel number
	;is ignored.  the user number is identical
	;
	;VERIFIES NEW FILE NAME DOES NOT EXIST
	;ALSO VERIFIES THAT NO WILD CHARS EXIST
	;IN EITHER FILENAME

; **** PATCH 2.1
; Patch to get attributes as well as check for r/o disk to
; prepare for test of interface attribute F5' if file to be
; renamed has an extended lock.

	CALL PATCH15

; Replaced code:
;	call check$write

	;VERIFY NO WILD CHARS EXIST IN 1ST FILENAME
	CALL CHECK$WILD
	CALL CHK$PASSWORD
;
	CALL INIT$XFCB$SEARCH
;
	;COPY USER NUMBER TO 2ND FILENAME
	CALL COPY$USER$NO
	;VERIFY NEW FILENAME DOES NOT ALREADY EXIST
	SHLD SEARCHA
;
	;VERIFY NO WILD CHARS EXIST IN 2ND FILENAME
	CALL CHECK$WILD0
;
	MVI C,EXTNUM! CALL SEARCHI1! CALL SEARCH1
	JNZ FILE$EXISTS ; NEW FILENAME EXISTS
	CALL DOES$XFCB$EXIST! CNZ DELETE1
;
	CALL COPY$USER$NO
	CALL INIT$XFCB$SEARCH
	;search up to the extent field
	mvi c,extnum! call search
	rz
	CALL CHECK$RODIR ;MAY BE READ-ONLY FILE
IF MPM
	call chk$olist
ENDIF
	;copy position 0
	rename0:
		;not end of directory, rename next element
		mvi c,dskmap! mvi e,extnum! call copy$dir
		;element renamed, move to next
		call searchn
		JNZ RENAME0
	RENAME1:
		CALL DOES$XFCB$EXIST! JZ COPY$DIR$LOC
		CALL COPY$USER$NO! JMP RENAME0
;
indicators:
	;set file indicators for current fcb

; **** PATCH 2.1
; Save attributes to prepare for test of interface attribute F5'
; in case interface attribute has an extended lock.

	CALL PATCH16

; Replaced code:
;	CALL GET$ATTS

	mvi c,extnum! call search ;through file type
	RZ
IF MPM
	call chk$olist
ENDIF
	indic0:
		;not end of directory, continue to change
		mvi c,0! mvi e,extnum ;copy name
		CALL COPY$DIR2! CALL MOVE! CALL SEEK$COPY
		call searchn
		JZ COPY$DIR$LOC
		jmp indic0
;
open:
	;search for the directory entry, copy to fcb
	mvi c,namlen! call search
OPEN1:
	rz ;return with lret=255 if end
	;not end of directory, copy fcb information
open$copy:
	;(referenced below to copy fcb info)
	CALL SETFWF! MOV E,A! PUSH H! DCX H! DCX H
	MOV D,M! PUSH D ; SAVE EXTENT# & MODULE# WITH FCB WRITE FLAG SET
	call getdptra! xchg ;HL = .buff(dptr)
	lhld info ;HL=.fcb(0)
	mvi c,nxtrec ;length of move operation
	push d ; save .buff(dptr)
	call move ;from .buff(dptr) to .fcb(0)
	;note that entire fcb is copied, including indicators
	pop d! lxi h,extnum! dad d ;HL=.buff(dptr+extnum)
	mov c,m ;C = directory extent number
	; RESTORE MODULE # AND EXTENT #
	POP D! POP H! MOV M,E! DCX H! DCX H! MOV M,D
	;HL = .user extent#, C = dir extent#
	;above move set fcb(reccnt) to dir(reccnt)
	;if fcb ext < dir ext then fcb(reccnt) = fcb(reccnt) | 128
	;if fcb ext = dir ext then fcb(reccnt) = fcb(reccnt)
	;if fcb ext > dir ext then fcb(reccnt) = 0
	;
SET$RC: ; HL=.FCB(EXT), C=DIREXT
		MVI B,0
		XCHG! LXI H,(RECCNT-EXTNUM)! DAD D
		LDAX D! CMP C

; ***** MP/M 2.1
; Set record count to 128 if record count = 0 and the directory
; extent (as determined by allocated blocks) is not equal to zero.

		JZ PATCH00

; Replaced code
;		JZ SET$RC2

		MVI A,0! JNC SET$RC1
		MVI A,128! MOV B,M
	SET$RC1:
		MOV M,A
	SET$RC2:
		MOV A,B! STA ACTUAL$RC
		RET 
;
mergezero:
	;HL = .fcb1(i), DE = .fcb2(i),
	;if fcb1(i) = 0 then fcb1(i) := fcb2(i)
	mov a,m! inx h! ora m! dcx h! rnz ;return if = 0000
	ldax d! mov m,a! inx d! inx h ;low byte copied
	ldax d! mov m,a! dcx d! dcx h ;back to input form
	ret
;
RESTORE$RC:
;	HL = .FCB(EXTNUM)
;	IF ACTUAL$RC ~= 0 THEN RCOUNT = ACTUAL$RC
	PUSH H
	LDA ACTUAL$RC! ORA A! JZ RESTORE$RC1
	LXI D,(RECCNT-EXTNUM)! DAD D
	MOV M,A
RESTORE$RC1:
	POP H! RET
;
close:
	;locate the directory element and re-write it
	xra a! sta lret ;
IF MPM
	sta dont$close
ENDIF
	call nowrite! rnz ;skip close if r/o disk
	;check file write flag - 0 indicates written
	call getmodnum ;fcb(modnum) in A
	ani fwfmsk! rnz ;return if bit remains set
CLOSE1:
	CALL SET$FCB$CKS$FLAG

; **** PATCH 2.1

	CALL GET$DIR$EXT! MOV C,A
	MOV B,M! PUSH B
	CALL CLOSE$FCB
	CALL GET$DIR$EXT! POP B
	CMP B! JNZ CLOSE2
	CMP C! RZ
CLOSE2:
	MOV M,B! MOV C,A! JMP SET$RC

patch00:
	call set$rc2 ; actual$rc = 0
	mov a,m! ora a! rnz ; rc ~= 0
	call get$dir$ext! cmp m! rc ; dir$ext < ext
	mov a,c! ora a! rz ; no blocks in fcb
	lda fx! jmp patch000

CLOSE$FCB:
	CALL RESTORE$RC
	MOV A,B! CMP C! MOV M,C! CNZ SET$RC
	XRA A! STA ACTUAL$RC! NOP

; **** Replaced code:

;	CALL GETEXTA  MOV A,M  STA SAVE$EXT
;	CALL CLOSE$FCB
;	CALL GETDPTRA  LXI D,EXTNUM  DAD D  MOV C,M
;	CALL GETEXTA  LDA SAVE$EXT  MOV M,A
;	JMP SET$RC

;CLOSE$FCB:
;	CALL GET$DIR$EXT ; HL = .FCB(EXT)
;	MOV B,M  MOV M,A  INR A  SUB B  JNZ CLOSE3
;	ORA C  JZ CLOSE3
;	PUSH H  LXI D,(RECCNT-EXTNUM)  DAD D
;	MOV A,M  ORA A  JNZ CLOSE2
;	MVI M,80H
;CLOSE2:
;	POP H
;	CALL RESTORE$RC ; RESTORE FCB(RECCNT)

	mvi c,namlen! call search ;locate file
	rz ;return if not found
	;merge the disk map at info with that at buff(dptr)
	lxi b,dskmap! call getdptra
	dad b! xchg ;DE is .buff(dptr+16)
	lhld info! dad b ;DE=.buff(dptr+16), HL=.fcb(16)
	mvi c,(fcblen-dskmap) ;length of single byte dm
	merge0:
		lda single! ora a! jz merged ;skip to double
		;this is a single byte map
		;if fcb(i) = 0 then fcb(i) = buff(i)
		;if buff(i) = 0 then buff(i) = fcb(i)
		;if fcb(i) <> buff(i) then error
		mov a,m! ora a! ldax d! jnz fcbnzero
			;fcb(i) = 0
			mov m,a ;fcb(i) = buff(i)
		fcbnzero:
		ora a! jnz buffnzero
			;buff(i) = 0
			mov a,m! stax d ;buff(i)=fcb(i)
		buffnzero:
		cmp m! jnz mergerr ;fcb(i) = buff(i)?
		jmp dmset ;if merge ok
	merged:
		;this is a double byte merge operation
		call mergezero ;buff = fcb if buff 0000
		xchg! call mergezero! xchg ;fcb = buff if fcb 0000
		;they should be identical at this point
		ldax d! cmp m! jnz mergerr ;low same?
		inx d! inx h ;to high byte
		ldax d! cmp m! jnz mergerr ;high same?
		;merge operation ok for this pair
		dcr c ;extra count for double byte
	dmset:
		inx d! inx h ;to next byte position
		dcr c! jnz merge0 ;for more
		;end of disk map merge, check record count
		;DE = .buff(dptr)+32, HL = .fcb(32)
		lxi b,-(fcblen-extnum)! dad b! xchg! dad b
		;DE = .fcb(extnum), HL = .buff(dptr+extnum)
		ldax d ;current user extent number
		;if fcb(ext) >= buff(fcb) then
		;buff(ext) := fcb(ext), buff(rec) := fcb(rec)
		cmp m! JNC DMSET0
		XCHG ; UPDATE FCB IN MEMORY FROM DIR FCB
DMSET0:

IF MPM
		push a
ENDIF

		;fcb extent number >= dir extent number
		mov m,a ;buff(ext) = fcb(ext)
		;update directory record count field
		lxi b,(reccnt-extnum)! dad b! xchg! dad b
		;DE=.buff(reccnt), HL=.fcb(reccnt)
IF MPM
		;under mp/m reccnt may have been extended
		;by another process - if extents match, set
		;both reccnt fields to the higher value
		pop a

; **** PATCH 2.1
; This patch only updates fcb(rc) if in unlocked mode.

		JMP PATCH0000
DMSET05:

; Replaced Code:
;		jnz dmset1

		ldax d! cmp m! jc dmset1
		mov m,a ; fcb(reccnt)=buff(reccnt)
dmset1:
ENDIF
		mov a,m! stax d ;buff(reccnt)=fcb(reccnt)
endmerge:
IF MPM
		lda dont$close! ora a! rnz
ENDIF
		;SET T3' OFF INDICATING FILE UPDATE
		CALL GETDPTRA! LXI D,11! DAD D
		MOV A,M! ANI 7FH! MOV M,A
		CALL SETFWF
		jmp seek$copy ;ok to "wrdir" here - 1.4 compat
	;		ret				;
	mergerr:
		;elements did not merge correctly
		CALL MAKE$FCB$INV
		lxi h,lret! dcr m ;=255 non zero flag set
	ret
;
SET$XDCNT:
	LXI H,0FFFFH! SHLD XDCNT! RET
;
SET$DCNT$DBLK:
	LHLD XDCNT! MVI A,1111$1100B! ANA L
	MOV L,A! DCX H! SHLD DCNT ;
	LHLD XDBLK! SHLD DBLK ;
	RET
;
IF MPM
sdcnt$eq$xdcnt:
	lxi h,sdcnt! lxi d,xdcnt! mvi c,4
	jmp move
ENDIF
;
make:
	;create a new file by creating a directory entry
	;then opening the file
;
	LXI H,XDCNT! CALL TEST$FFFF! JZ LRET$EQ$FF
	CALL SET$DCNT$DBLK
;
	call check$write ;may be write protected
	lhld info! push h ; save fcb address, look for e5
	lxi h,efcb! shld info ; info = .empty
	mvi c,1! 
;
	CALL SEARCHI! CALL SEARCHN
;
	;zero flag set if no space
	pop h ; recall info address
	shld info ;in case we return here
	rz ;return with error condition 255 if not found
;
	; RETURN EARLY IF MAKING AN XFCB
	LDA MAKE$XFCB! ORA A! RNZ
	;clear the remainder of the fcb
	;CLEAR S1 BYTE
	LXI D,13! DAD D! MOV M,D! INX H
	;CLEAR AND SAVE FILE WRITE FLAG OF MODNUM
	MOV A,M! PUSH A! PUSH H! ANI 3FH! MOV M,A! INX H
	mvi c,fcblen-namlen ;number of bytes to fill
	make0:
		MOV M,D! inx h! dcr c! jnz make0
	call setcdr ;may have extended the directory
	;now copy entry to the directory
	MVI C,0! LXI D,FCBLEN! CALL COPY$DIR0
	;AND RESTORE THE FILE WRITE FLAG
	POP H! POP A! MOV M,A
	;and set the FCB write flag to "1"
	XRA A! STA ACTUAL$RC
	jmp setfwf
;
open$reel:
	;close the current extent, and open the next one
	;if possible.  RMF is true if in read mode
	CALL GETMODNUM! STA SAVE$MOD
	CALL GETEXTA
	MOV A,M! MOV C,A ;
	INR C! CALL COMPEXT ;
	JZ OPEN$REEL3
	PUSH H! PUSH B
	CALL CLOSE
	POP B! POP H
	LDA LRET! INR A! RZ
	MVI A,MAXEXT! ANA C! MOV M,A; INCR EXTENT FIELD
	JNZ OPEN$REEL0 ; JMP IF IN SAME MODULE
;
	open$mod:
		;extent number overflow, go to next module
		INX H! INX H ;HL=.fcb(modnum)
		inr m ;fcb(modnum)=++1
		;module number incremented, check for overflow
;
		mov a,m! ANI 3FH! ;mask high order bits
;
		JZ OPEN$R$ERR ;cannot overflow to zero
		;otherwise, ok to continue with new module
	open$reel0:
		CALL SET$XDCNT ; RESET XDCNT FOR MAKE
IF MPM
		call set$sdcnt
ENDIF
		mvi c,namlen! call search ;next extent found?
		jnz open$reel1
			;end of file encountered
			lda rmf! inr a ;0ffh becomes 00 if read
			jz open$r$err ;sets lret = 1
			;try to extend the current file
			call make
			;cannot be end of directory
			;call end$of$dir
			jz open$r$err ;with lret = 1
IF MPM
			call fix$olist$item
ENDIF
			CALL SET$FCB$CKS$FLAG
			jmp open$reel2
		open$reel1:
			;not end of file, open
			CALL SET$FCB$CKS$FLAG
			call open$copy
		open$reel2:
			call getfcb ;set parameters
			xra a! STA VRECORD! jmp sta$ret ;lret = 0	;
;			ret ;with lret = 0
	open$r$err:
		CALL GETMODNUM! LDA SAVE$MOD! MOV M,A

; **** PATCH 2.1 
; This patch restores the FCB extent field to its value
; at entry to openreel.

		dcx h! dcx h! mov a,m! dcr a! ani 1fh

; Replaced code:
;		CALL GETEXTA  LDA SAVE$EXT

		MOV M,A! CALL SET$FCB$CKS$FLAG
		;cannot move to next extent of this file
		call setlret1 ;lret = 1
		jmp setfwf ;ensure that it will not be closed
		;ret
	OPEN$REEL3:
		MOV M,C ; INCREMENT EXTENT FIELD
		CALL GET$DIR$EXT! MOV C,A
		CALL RESTORE$RC
		CALL SET$RC! JMP OPEN$REEL2
;
seqdiskread:
	;sequential disk read operation
	mvi a,1! sta seqio
	;drop through to diskread
;
diskread:	;(may enter from seqdiskread)
	CALL TST$INV$FCB ; CHECK FOR VALID FCB
	mvi a,true! sta rmf ;read mode flag = true (open$reel)
IF MPM
	sta dont$close
ENDIF
	;read the next record from the current fcb
	call getfcb ;sets parameters for the read
DISKREAD0:
	lda vrecord! lxi h,rcount! cmp m ;vrecord-rcount
	;skip if rcount > vrecord
	jc recordok
IF MPM
		call test$disk$fcb! jnz diskread0
		lda vrecord
ENDIF
		;not enough records in the extent
		;record count must be 128 to continue
		cpi 128 ;vrecord = 128?
		JNZ SETLRET1 ;skip if vrecord<>128
		call open$reel ;go to next extent if so
		; xra a  sta vrecord ;vrecord=00 (DONE BY OPEN$REEL)
		;now check for open ok
		lda lret! ora a! JNZ SETLRET1 ;stop at eof
	recordok:
		;arrive with fcb addressing a record to read
		call index
		;error 2 if reading unwritten data
		;(returns 1 to be compatible with 1.4)
		call allocated ;arecord=0000?
IF MPM
		jnz recordok1
		call test$disk$fcb! jnz diskread0
ENDIF
		JZ SETLRET1
	RECORDOK1:
		;record has been allocated, read it
		call atran ;arecord now a disk address
		call seek ;to proper track,sector
		call rdbuff ;to dma address
		jmp setfcb ;replace parameter	

IF MPM
;
test$unlocked:
	lda high$ext! ani 80h! ret
;
test$disk$fcb:
	call test$unlocked! rz 
	lda dont$close! ora a! rz
	call close1
test$disk$fcb1:
	pop d
	lxi h,lret! inr m! mvi a,11! jz sta$ret
	mvi m,0
	push d
	call getrcnta! mov a,m! sta rcount ; reset rcount
	xra a! sta dont$close
	inr a! ret
ENDIF
;
RESET$FWF:
	call getmodnum ;HL=.fcb(modnum), A=fcb(modnum)
	;reset the file write flag to mark as written fcb
	ani (not fwfmsk) and 0ffh ;bit reset
	mov m,a ;fcb(modnum) = fcb(modnum) and 7fh
	RET
;
seqdiskwrite:
	;sequential disk write
	mvi a,1! sta seqio
	;drop through to diskwrite
;
diskwrite:	;(may enter here from seqdiskwrite above)
	mvi a,false! sta rmf ;read mode flag
	;write record to currently selected file
	call check$write ;in case write protected
;
	LDA XFCB$READ$ONLY! ORA A
	MVI A,3! JNZ SET$ARET
;
	LDA HIGH$EXT! ANI 0100$0000B
	; Z FLAG RESET IF R/O MODE
	MVI A,3! JNZ SET$ARET
;
	lhld info ;HL = .fcb(0)
	call check$rofile ;may be a read-only file
;
	CALL TST$INV$FCB ; TEST FOR INVALID FCB
	call getfcb ;to set local parameters
	lda vrecord! cpi lstrec+1 ;vrecord-128
	JC DISKWRITE0
	CALL OPEN$REEL ;VRECORD = 128, TRY TO OPEN NEXT EXTENT
	LDA LRET! ORA A! RNZ ; NO AVAILABLE FCB
DISK$WRITE0:
;
IF MPM
	mvi a,0ffh! sta dont$close
disk$write1:
ENDIF
	;can write the next record, so continue
	call index
	call allocated
IF MPM
	jz diskwrite2
	;if file is unlocked, verify record is not locked
	;record has to be allocated to be locked
	call test$unlocked! jz not$unlocked
	call atran! mov c,a
	lda mult$cnt! mov b,a! push b
	call test$lock! pop b
	mov a,c! mvi c,0! push b
	jmp diskwr10
not$unlocked:
	inr a
ENDIF
	mvi c,0 ;marked as normal write operation for wrbuff
	jnz diskwr1
IF MPM
	diskwrite2:
		call test$disk$fcb! jnz diskwrite1
ENDIF
		;not allocated
		;the argument to getblock is the starting
		;position for the disk search, and should be
		;the last allocated block for this file, or
		;the value 0 if no space has been allocated
		call dm$position
		sta dminx ;save for later
		lxi b,0000h ;may use block zero
		ora a! jz nopblock ;skip if no previous block
			;previous block exists at A
			mov c,a! dcx b ;previous block # in BC
			call getdm ;previous block # to HL
			mov b,h! mov c,l ;BC=prev block#
		nopblock:
			;BC = 0000, or previous block #
			call get$block ;block # to HL
		;arrive here with block# or zero
		mov a,l! ora h! jnz blockok
			;cannot find a block to allocate
			mvi a,2! jmp sta$ret 	;lret=2	
		blockok:
		CALL SET$FCB$CKS$FLAG
		;allocated block number is in HL
		shld arecord
		xchg ;block number to DE
		lhld info! lxi b,dskmap! dad b ;HL=.fcb(dskmap)
		lda single! ora a ;set flags for single byte dm
		lda dminx ;recall dm index
		jz allocwd ;skip if allocating word
			;allocating a byte value
			call addh! mov m,e ;single byte alloc
			jmp diskwru ;to continue
		allocwd:
		;allocate a word value
			mov c,a! mvi b,0 ;double(dminx)
			dad b! dad b ;HL=.fcb(dminx*2)
			mov m,e! inx h! mov m,d ;double wd
		diskwru:
		;disk write to previously unallocated block
		mvi c,2 ;marked as unallocated write
	diskwr1:
	;continue the write operation of no allocation error
	;C = 0 if normal write, 2 if to prev unalloc block
	;lda lret  ora a  rnz ;stop if non zero returned value
	;ABOVE LINE REMOVED - LRET CANNOT BE NON-ZERO
	push b ;save write flag
	call atran ;arecord set
	diskwr10:
;
		MOV D,A ; SAVE LOW BYTE OF ARECORD RETURNED
			; IN REGISTER A FROM ATRAN
;
		lda seqio! dcr a! dcr a! jnz diskwr11   ;
		pop b! push b! mov a,c! dcr a! dcr a	;
		jnz diskwr11		;old allocation  
;
		PUSH D ; SAVE LOW BYTE OF ARECORD ON STACK
;
		lhld buffa! mov d,a	;zero buffa & fill 
	fill0:  mov m,a! inx h! inr d! jp fill0		;
		call setdir! lhld arecord1		;
		mvi c,2					;
	fill1:  shld arecord! push b! call seek! pop b  ;
		call wrbuff	;write fill record	;
		lhld arecord!	;restore last record     
		mvi c,0		;change  allocate flag   
		lda blkmsk! mov b,a! ana l! cmp b!inx h	;
		jnz fill1	;cont until cluster is zeroed
;
		POP A! STA ARECORD ; RESTORE ARECORD LOW BYTE
;
		call setdata ; restore dma
	diskwr11:					;
	call seek ;to proper file position
	pop b ;
;
	LDA VRECORD! MOV B,A ; LOAD AND SAVE VRECORD
;
	push b ;restore/save write flag (C=2 if new block)
;
	LXI H,BLKMSK! ANA M! JZ WRITE ;
	MVI C,0 ; SET C TO ZERO IF VRECORD DOES NOT LOCATE
;		  FIRST RECORD OF NEW BLOCK
WRITE:
;
	call wrbuff ;written to disk
	pop b ;C = 2 if a new block was allocated, 0 if not
	;increment record count if rcount<=vrecord
	MOV A,B! lxi h,rcount! cmp m ;vrecord-rcount
	jc diskwr2
		;rcount <= vrecord
		mov m,a! inr m ;rcount = vrecord+1
IF	MPM
		call test$unlocked! jz write1
	
		;for unlocked files 
		;  rcount = rcount & (~ blkmsk) + blkmsk + 1

		lda blkmsk! mov b,a! inr b! cma! mov c,a
		mov a,m! dcr a! ana c! add b! mov m,a
	write1:
ENDIF
		mvi c,2 ;mark as record count incremented
	diskwr2:
	;A has vrecord, C=2 if new block or new record#
	dcr c! dcr c! jnz noupdate
		CALL RESET$FWF
IF MPM
		call test$unlocked! jz noupdate
		lda rcount! call getrcnta! mov m,a
		call close
		call test$disk$fcb1
ENDIF

noupdate:
;
	;SET FILE WRITE FLAG IF RESET
	CALL GETMODNUM! ANI 0100$0000B! JNZ DISK$WRITE3
	MOV A,M! ORI 0100$0000B! MOV M,A
	;RESET FCB FILE WRITE FLAG TO ENSURE T3' GETS
	;RESET BY THE CLOSE FUNCTION
	CALL RESET$FWF
DISK$WRITE3:
	jmp setfcb ;replace parameters
	;ret
;
rseek:   
	;random access seek operation, C=0ffh if read mode
	;fcb is assumed to address an active file control block
	;(modnum has been set to 1100$0000b if previous bad seek)
	xra a! sta seqio ;marked as random access operation
rseek1:
	push b ;save r/w flag
	lhld info! xchg ;DE will hold base of fcb
		lxi h,ranrec! dad d ;HL=.fcb(ranrec)
		mov a,m! ani 7fh! push psw ;record number
		mov a,m! ral ;cy=lsb of extent#
		inx h! mov a,m! ral! ani 11111b ;A=ext#
		mov c,a ;C holds extent number, record stacked
;
		MOV A,M! ANI 1111$0000B! INX H! ORA M ;
		RRC! RRC! RRC! RRC! MOV B,A ; 
		; B HOLDS MODULE #
;
		; CHECK HIGH BYTE OF RAN REC <= 3
		MOV A,M
		ANI 1111$1100B! POP H! MVI L,6! MOV A,H ;
;
		;produce error 6, seek past physical eod
		jnz seekerr
		;otherwise, high byte = 0, A = sought record
		lxi h,nxtrec! dad d ;HL = .fcb(nxtrec)
		mov m,a ;sought rec# stored away
	;arrive here with B=mod#, C=ext#, DE=.fcb, rec stored
	;the r/w flag is still stacked.  compare fcb values
	;
		;CHECK MODULE # FIRST
		PUSH D! CALL CHK$INV$FCB! POP D! JZ RANCLOSE
		lxi h,modnum! dad d! mov a,b ;B=seek mod#
		;could be overflow at eof, producing module#
		;of 90H or 10H, so compare all but fwf
		sub m! ani 3fh! JNZ RANCLOSE ;same?
		;MODULE MATCHES, CHECK EXTENT
		lxi h,extnum! dad d
		MOV A,M! CMP C! JZ SEEKOK1 ;EXTENTS EQUAL
		CALL COMPEXT! JNZ RANCLOSE
		;EXTENT IS IN SAME DIRECTORY FCB

; **** PATCH 2.1
; This patch intercepts attempts to read beyond written extents
; in the current dir fcb and returns error code 1 if so.

 		call patch0
 		mov m,c! mov c,a
		
; Removed code:
; 		MOV M,C ; FCB(EXT) = C
; 		CALL GET$DIR$EXT
; 		MOV C,A

		; HL=.FCB(EXT),C=DIR EXT
		CALL RESTORE$RC
		CALL SET$RC
		JMP SEEKOK1
	ranclose:
		push b! push d ;save seek mod#,ext#, .fcb
		call close ;current extent closed
		pop d! pop b ;recall parameters and fill
		mvi l,3 ;cannot close error #3
		lda lret! inr a
; **** PATCH 2.1
; No longer invalidate fcb.

		jz seekerr

; Removed code:
;		jz badseek

		CALL SET$XDCNT ; RESET XDCNT FOR MAKE
IF MPM
		call set$sdcnt
ENDIF
		lxi h,extnum! dad d
; **** PATCH 2.1
; This patch keeps the fcb valid while returning
; the proper error code.

		push h! mov d,m
		mov m,c ;fcb(extnum)=ext#
		INX H! INX H! MOV A,M
		mov e,a! push d
		ANI 040H! ORA B! MOV M,A
		;fcb(modnum)=mod#
		call open ;is the file present?
		lda lret! inr a! jnz seekok ;open successful?
		;cannot open the file, read mode?
		pop d! pop h! pop b ;r/w flag to c (=0ffh if read)
		push b! push h! push d ;everyone expects this item stacked
		mvi l,4 ;seek to unwritten extent #4
		inr c ;becomes 00 if read operation
		jz badseek ;skip to error if read operation
		;write operation, make new extent
		call make
		jmp patch01

; Removed code:
;		mov m,c ;fcb(extnum)=ext#
;		INX H  INX H  MOV A,M  ANI 040H  ORA B  MOV M,A
;		;fcb(modnum)=mod#
;		call open ;is the file present?
;		lda lret  inr a  jnz seekok ;open successful?
;		;cannot open the file, read mode?
;		pop b ;r/w flag to c (=0ffh if read)
;		push b ;everyone expects this item stacked
;		mvi l,4 ;seek to unwritten extent #4
;		inr c ;becomes 00 if read operation
;		jz badseek ;skip to error if read operation
;		;write operation, make new extent
;		call make
;		mvi l,5 ;cannot create new extent #5
;		jz badseek ;no dir space
IF MPM
;		call fix$olist$item
ENDIF
;		;file make operation successful
;	seekok:
;		CALL SET$FCB$CKS$FLAG

	SEEKOK1:
		pop b ;discard r/w flag
		xra a! jmp sta$ret 	;with zero set	
	badseek:
		;fcb no longer contains a valid fcb, mark
		;with 1100$000b in modnum field so that it
		;appears as overflow with file write flag set

; **** PATCH 2.1
; No longer invalidate fcb.

		pop d! xthl
		mov m,d! inx h! inx h! mov m,e
		pop h! nop

; Removed code:
;		push h ;save error flag
;		CALL SET$FCB$CKS$FLAG
;		CALL MAKE$FCB$INV ; FLAG FCB AS INVALID
;		pop h ;and drop through

	seekerr:
		pop b ;discard r/w flag
		mov a,l! sta lret ;lret=#, nonzero
		;setfwf returns non-zero accumulator for err
		RET ; FOLLOWING LINE REMOVED
;		jmp setfwf ;flag set, so subsequent close ok
		;ret
;
randiskread:
	;random disk read operation
	mvi c,true ;marked as read operation
	call rseek
	cz diskread ;if seek successful
	ret
;
randiskwrite:
	;random disk write operation
	mvi c,false ;marked as write operation
	call rseek
	cz diskwrite ;if seek successful
	ret
;
compute$rr:
	;compute random record position for getfilesize/setrandom
	xchg! dad d
	;DE=.buf(dptr) or .fcb(0), HL = .f(nxtrec/reccnt)
	mov c,m! mvi b,0 ;BC = 0000 0000 ?rrr rrrr
	lxi h,extnum! dad d! mov a,m! rrc! ani 80h ;A=e000 0000
	add c! mov c,a! mvi a,0! adc b! mov b,a
	;BC = 0000 000? errrr rrrr
	mov a,m! rrc! ani 0fh! add b! mov b,a
	;BC = 000? eeee errrr rrrr
	lxi h,modnum! dad d! mov a,m ;A=XXmm mmmm
	add a! add a! add a! add a ;cy=m A=mmmm 0000
;
	ORA A! ADD B! MOV B,A! PUSH PSW ; SAVE CARRY
	MOV A,M! RAR! RAR! RAR! RAR! ANI 0000$0011B ; A=0000 00mm
	MOV L,A! POP PSW! MVI A,0! ADC L ; ADD CARRY
	RET ;
;
getfilesize:
	;compute logical file size for current fcb
	;zero the receiving ranrec field
	CALL GET$RRA! push h ;save position
	mov m,d! inx h! mov m,d! inx h! mov m,d;=00 00 00
	MVI C,EXTNUM! CALL SEARCH
	getsize:
		jz setsize
		;current fcb addressed by dptr
		call getdptra! lxi d,reccnt ;ready for compute size
		call compute$rr
		;A=0000 00mm BC = mmmm eeee errr rrrr
		;compare with memory, larger?
		pop h! push h ;recall, replace .fcb(ranrec)
		mov e,a ;save cy
		mov a,c! sub m! inx h ;ls byte
		mov a,b! sbb m! inx h ;middle byte
		mov a,e! sbb m ;carry if .fcb(ranrec) > directory
		jc getnextsize ;for another try
		;fcb is less or equal, fill from directory
		mov m,e! dcx h! mov m,b! dcx h! mov m,c
	getnextsize:
		call searchn
		MVI A,0! STA ARET
		jmp getsize
	setsize:
	pop h ;discard .fcb(ranrec)
	ret
;
setrandom:
	;set random record from the current file control block
	XCHG! lxi d,nxtrec ;ready params for computesize
	call compute$rr ;DE=info, A=0000 00mm, BC=mmmm eeee errr rrrr
	lxi h,ranrec! dad d ;HL = .fcb(ranrec)
	mov m,c! inx h! mov m,b! inx h! mov m,a ;to ranrec
	ret
;
TMPSELECT:
	LXI H,SELDSK! MOV A,M! PUSH A! MOV M,E
	CALL CURSELECT! POP A! STA SELDSK! RET
;
XCURSELECT:
	MVI A,0FFH! STA RETURN$FFFF 
	CALL CURSELECT
	XRA A! STA RETURN$FFFF! RET
;
CURSELECT:
	LDA SELDSK! 
	LXI H,CURDSK! CMP M! JZ TEST$FF$DSK
	;SKIP IF SELDSK = CURDSK, FALL INTO SELECT
;
select:
	;select disk info for subsequent input or output ops
	lhld dlog! CALL TEST$VECTOR
	MOV E,A! PUSH D ;SEND TO SELDSK, SAVE FOR TEST BELOW
	call selectdisk! pop h ;recall dlog vector
	cz sel$error ;returns true if select ok
	;is the disk logged in?
	mov a,l! ORA A
IF MPM
	jz select1
	lhld rlog! call test$vector
	sta rem$drv! ret ; set rem$drv & return
select1:
ELSE
	rnz ;return if bit is set
ENDIF
	;disk not logged in, set bit and initialize
	lhld dlog! mov c,l! mov b,h ;call ready
	call set$cdisk! shld dlog ;dlog=set$cdisk(dlog)
IF MPM
	lxi h,chksiz+1! mov a,m! ral! mvi a,0! jc select2
	lhld rlog! mov c,l! mov b,h
	call set$cdisk! shld rlog ;rlog=set$cdisk(rlog)
	mvi a,1
select2:
	sta rem$drv
ENDIF
	jmp initialize
	;ret
;
TEST$FF$DSK:
	INR A! JZ SEL$ERROR
	RET
;
SET$SELDSK:
	LDA LINFO! STA SELDSK! RET
;
RESELECTX:
	XRA A! STA HIGH$EXT! JMP RESELECT1
reselect:
	;check current fcb to see if reselection necessary
	MVI A,80H! MOV B,A! DCR A! MOV C,A
	LHLD INFO! LXI D,7! XCHG! DAD D
	MOV A,M! ANA B! STA XFCB$READ$ONLY
	MOV A,M! ANA C! MOV M,A
	INX H! LXI D,4
	MOV A,M! ANA C! CMP M! MOV M,A! MVI A,60H! JNZ RESELECT0
	DAD D! MVI A,0E0H! ANA M
RESELECT0:
	STA HIGH$EXT! CALL CLR$EXT
	CALL GETRCNTA! MOV A,M! ANA B! JZ RESELECT1
	MOV A,M! ANA C! MOV M,B
RESELECT1:
	STA ACTUAL$RC
	mvi a,true! sta resel ;mark possible reselect
	lhld info! mov a,m ;drive select code
	ani 1$1111b ;non zero is auto drive select
	dcr a ;drive code normalized to 0..30, or 255
	sta linfo ;save drive code
	CPI 0FFH! JZ NOSELECT
		;auto select function, save SELDSK
		LDA SELDSK! sta olddsk ;olddsk=SELDSK
		mov a,m! sta fcbdsk ;save drive code
		ani 1110$0000b! mov m,a ;preserve hi bits
		CALL SET$SELDSK
	noselect:
		CALL CURSELECT
		;set user code
		lda usrcode ;0...31
		lhld info! ora m! mov m,a
		ret
;
CHK$PASSWORD:
	CALL GET$DIR$MODE! ANI 80H! JNZ CHK$PW! RET
;
GET$DIR$MODE:
	LHLD DRVLBLA! MOV A,M! RET
;
CHK$PW:		; CHECK PASSWORD
	CALL GET$XFCB! RZ ; A = XFCB OPTIONS
	DCR A! CNZ CMP$PW! RZ
CHK$PW1:	; PASSWORD ERROR
	POP H! MVI A,7! JMP SET$ARET
;
CMP$PW:		; COMPARE PASSWORDS
	INX H! MOV B,M
	MOV A,B! ORA A! JNZ CMP$PW2
	MOV D,H! MOV E,L! INX H! INX H
	MVI C,9
CMP$PW1:
	INX H! MOV A,M! DCR C! RZ
	ORA A! JZ CMP$PW1
	CPI 20H! JZ CMP$PW1
	XCHG
CMP$PW2:
	LXI D,(23-UBYTES)! DAD D! XCHG
	LHLD XDMAAD! MVI C,8
CMP$PW3:
	LDAX D! XRA B! CMP M! JNZ CMP$PW4
	DCX D! INX H! DCR C! JNZ CMP$PW3
	RET
CMP$PW4:
	DCX D! DCR C! JNZ CMP$PW4
	INX D
IF MPM
	call get$df$pwa! inr a! jnz cmp$pw5
	inr a! ret
cmp$pw5:
ELSE
	LXI H,DF$PASSWORD
ENDIF
	MVI C,8! JMP COMPARE
;
IF MPM
get$df$pwa:		;A = FF => no df pwa
	call rlr! lxi b,console

; **** PATCH 2.1
; This patch corrects an error associated with accessing and
; setting the default password when the current list device 
; was non-zero.  The list device number resides in the high 
; order nibble of the console field in the process descriptor.
; In addition, the default password for consoles 14 and 15 have been
; removed to increase the patch area.

	jmp patch00000

; Replaced code:
;	dad b  mov a,m
;	cpi 16  mvi a,0ffh  rnc  mov a,m
;	add a  add a  add a
;	mvi h,0  mov l,a  lxi b,dfpassword
;	dad b
;	ret

ENDIF

; PATCH 2.1
; This patch is part of the ranclose patch.
; stuck here because it fits

patch01:
		mvi l,5 ;cannot create new extent #5
		jz badseek ;no dir space
IF MPM
		call fix$olist$item
ENDIF
		;file make operation successful
	seekok:
		pop b! pop b
		CALL SET$FCB$CKS$FLAG
		jmp seekok1

;
SET$PW:		; SET PASSWORD IN XFCB
	PUSH H ; SAVE .XFCB(EX) 
	LXI B,8 ; B = 0, C = 8
	LXI D,(23-EXTNUM)! DAD D
	XCHG! LHLD XDMAAD
SET$PW0:
	XRA A! PUSH A
SET$PW1:
	MOV A,M! STAX D! ORA A! JZ SET$PW2
	CPI 20H! JZ SET$PW2
	INX SP! INX SP! PUSH A
SET$PW2:
	ADD B! MOV B,A
	DCX D! INX H! DCR C! JNZ SET$PW1
	POP A! ORA B! POP H! JNZ SET$PW3
	MOV M,A ; ZERO XFCB(EX) - NO PASSWORD
SET$PW3:
	INX D! MVI C,8
SET$PW4:
	LDAX D! XRA B! STAX D! INX D! DCR C! JNZ SET$PW4
	INX H! RET
;
GET$XFCB:
	MVI B,0
GET$XFCB0:
	LHLD INFO! MOV A,M! PUSH A
	ORI 010H! MOV M,A! MVI C,EXTNUM
	XRA A! CMP B! JNZ GET$XFCB00
	CALL SEARCH! JMP GET$XFCB01
GET$XFCB00:
	CALL SEARCHN
GET$XFCB01:
	MVI A,0! STA LRET
	LHLD INFO! POP B! MOV M,B! RZ
GET$XFCB1:
	CALL GETDPTRA! XCHG
	LXI H,EXTNUM! DAD D! MOV A,M! ANI 0E0H! ORI 1
	RET
;
ADJUST$DMAAD:
	PUSH H! LHLD XDMAAD! DAD D
	SHLD XDMAAD! POP H! RET
;
INIT$XFCB:
	CALL SETCDR ;MAY HAVE EXTENDED THE DIRECTORY
	LXI B,1014H ; B=10H, C=20
INIT$XFCB0:
	;B = FCB(0) LOGICAL OR MASK
	;C = ZERO COUNT
	PUSH B
	CALL GETDPTRA! XCHG! LHLD INFO! XCHG
	;ZERO EXTNUM AND MODNUM
	LDAX D! ORA B! MOV M,A! INX D! INX H
	MVI C,11! CALL MOVE! POP B! INR C
INIT$XFCB1:
	DCR C! RZ
	MVI M,0! INX H! JMP INIT$XFCB1
;
CHK$XFCB$PASSWORD:
	CALL GET$XFCB1! DCR A
CHK$XFCB$PASSWORD1:
	PUSH H! CNZ CMP$PW! POP H! RZ
	JMP CHK$PW1
;
CHK$PWS:
	CALL GET$DIR$MODE! ANI 80H! RZ
	CALL GET$XFCB
CHK$PWS1:
	RZ! DCR A
	CNZ CMP$PW! RNZ
	MVI B,1! CALL GET$XFCB0! JMP CHK$PWS1
;
STAMP1:
	LXI B,24! JMP STAMP3
STAMP2:
	LXI B,28!
STAMP3:
;
IF MPM
	push b!
	call get$stamp$add! xchg
	pop b
ELSE
	LXI D,STAMP
ENDIF
	CALL GETDPTRA! DAD B
	MVI C,4! JMP MOVE ;RET
;
IF MPM
;
pack$sdcnt:
;
;packed$dcnt = dblk(low 15 bits) || dcnt(low 9 bits)
;
;	if sdblk = 0 then dblk = shr(sdcnt,blkshf+2)
;		     else dblk = sdblk
;	dcnt = sdcnt & (blkmsk || '11'b)
;
;	packed$dcnt format (24 bits)
;
;	12345678 12345678 12345678
;	23456789 .......1 ........ sdcnt (low 9 bits)
;	........ 9abcdef. 12345678 sdblk (low 15 bits)
;
	lhld sdblk! mov a,h! ora l! jnz pack$sdcnt1
	lda blkshf! adi 2! mov c,a! lhld sdcnt
	call hlrotr
pack$sdcnt1:
	dad h! xchg! lxi h,sdcnt! mvi b,1
	lda blkmsk! ral! ora b! ral! ora b
	ana m! sta packed$dcnt
	lda blkshf! cpi 7! jnz pack$sdcnt2
	inx h! mov a,m! ana b! jz pack$sdcnt2
	mov a,e! ora b! mov e,a
pack$sdcnt2:
	xchg! shld packed$dcnt+1
	ret
;
;
;olist element = link(2) || atts(1) || dcnt(3) || 
;		 pdaddr(2) || opncnt(2)
;
;	link = 0 -> end of list
;
;	atts - 80 - open in locked mode
;	       40 - open in unlocked mode
;	       20 - open in read/only mode
;	       10 - deleted item
;	       0n - drive code (0-f)
;
;	dcnt = packed sdcnt+sdblk
;	pdaddr = process descriptor addr
;	opncnt = # of open calls - # of close calls
;		 olist item freed by close when opncnt = 0
;
;llist element = link(2) || drive(1) || arecord(3) || 
;	         pdaddr(2) || .olist$item(2)
;
;	link = 0 -> end of list
;
;	drive - 0n - drive code (0-f)
;
;	arecord = record number of locked record
;	pdaddr = process descriptor addr
;	.olist$item = address of file's olist item
;
search$olist:
	lxi h,open$root! jmp srch$list0
search$llist:
	lxi h,lock$root! jmp srch$list0
searchn$list:
	lhld cur$pos
srch$list0:
	shld prv$pos
;
;search$olist, search$llist, searchn$list conventions
;
;	B = 0 -> return next item
;	B = 1 -> search for matching drive
; 	B = 3 -> search for matching dcnt
;	B = 5 -> search for matching dcnt + pdaddr
;	if found then z flag is set
;	              prv$pos -> previous list element
;		      cur$pos -> found list element
;		      HL -> found list element
;	else prv$pos -> list element to insert after
;
;	olist and llist are maintained in drive order
;
srch$list1:
	mov e,m! inx h! mov d,m! xchg
	mov a,l! ora h! jz srch$list3
	xra a! cmp b! jz srch$list6
	inx h! inx h!
	lxi d,curdsk! mov a,m! ani 0fh! mov c,a
	ldax d! sub c! jnz srch$list4
	mov a,b! dcr a! jz srch$list5
	mov c,b! push h
	inx d! inx h! call compare
	pop h! jz srch$list5
srch$list2:
	dcx h! dcx h
	shld prv$pos! jmp srch$list1
srch$list3:
	inr a! ret
srch$list4:
	jnc srch$list2
srch$list5:
	dcx h! dcx h
srch$list6:
	shld cur$pos! ret
;
delete$item: ; HL -> item to be deleted
	di
	push d! push h
	mov e,m! inx h! mov d,m
	lhld prv$pos! shld cur$pos
	;prv$pos.link = delete$item.link
	mov m,e! inx h! mov m,d
	;
	lhld free$root! xchg
	;free$root = .delete$item
	pop h! shld free$root
	;delete$item.link = previous free$root
	mov m,e! inx h! mov m,d
	pop d! ei! ret
;
create$item: ; HL -> new item if successful
	     ; Z flag set if no free items
	lhld free$root! mov a,l! ora h! rz
	push d! push h! shld cur$pos
	mov e,m! inx h! mov d,m
	;free$root = free$root.link
	xchg! shld free$root
	;
	lhld prv$pos
	mov e,m! inx h! mov d,m
	pop h
	;create$item.link = prv$pos.link
	mov m,e! inx h! mov m,d! dcx h
	xchg! lhld prv$pos
	;prv$pos.link = .create$item
	mov m,e! inx h! mov m,d! xchg
	pop d! ret
;
set$olist$item:
	; A = attributes
	; HL = olist entry address
	inx h! inx h
	mov b,a! lxi d,curdsk! ldax d! ora b
	mov m,a! inx h! inx d
	mvi c,5! call move
	xra a! mov m,a! inx h! mov m,a! ret
;
set$sdcnt:
 	mvi a,0ffh! sta sdcnt+1! ret
;
tst$olist:
	mvi a,0c9h! sta chk$olist05! jmp chk$olist0
chk$olist:
	xra a! sta chk$olist05
chk$olist0:
	lxi d,dcnt! lxi h,sdcnt! mvi c,4! call move
	call pack$sdcnt! mvi b,3! call search$olist! rnz

; **** PATCH 2.1
; Part of following patch

	NOP 

; Replaced code:
; 	pop d ; pop return address

	inx h! inx h

; **** PATCH 2.1
; If in compatibility mode F1', R/O mode is default open mode;
; otherwise default is locked mode.  Branch to openx06
; if the open mode is not the default mode.

	CALL GET$CMP$MODE! CALL PATCH3

; Replaced code:
;	mov a,m  ani 80h  jz openx06

	dcx h! dcx h

; **** PATCH 2.1
; Verify another process does not have the file open
; in case the default open mode is r/o as a result of
; comp att F1' = 1.  Patch13 sets DE to the lock list item
; addr and HL to the lock list item opencnt field addr
; for PATCH14.  The XTHL following CALL COMPARE$PDS
; restores HL and puts the address of the opencnt field
; on the stack.

	PUSH H
	CALL COMPARE$PDS! XTHL! JNZ OPENX06
	CALL PATCH13

; Replaced code:
;	push d  push h
;	call compare$pds  pop h  pop d  jnz openx06
;	push d

chk$olist05:
	nop ; tst$olist changes this instr to ret

; **** PATCH 2.1
; This patch tests to see if the lock list item is
; extended and if the interface attribute requesting
; that the lock continue to be extended is set.  If so,
; control is returned to the routine calling chk$olist.
; Otherwise, the lock item is deleted.  If the lock item
; is not an extended lock, the process's PDCNT is advanced.

	CALL PATCH14

; Replaced code:
;	call delete$item

	lda pdcnt
chk$olist1:
	adi 16! jz chk$olist1
	sta pdcnt

	push a! call rlr
	lxi b,pdcnt$off! dad b! pop a
	mov m,a

	ret
;
remove$files:	;BC = pdaddr
	lhld cur$pos! push h
	lhld prv$pos! push h
	mov d,b! mov e,c! lxi h,open$root! shld cur$pos
remove$file1:
	mvi b,0! push d! call searchn$list! pop d! jnz remove$file2
	lxi b,6! call tst$tbl$lmt! jnz remove$file1
	inx h! inx h! mov a,m! ori 10h! mov m,a
	sta deleted$files
	jmp remove$file1
remove$file2:
	pop h! shld prv$pos
	pop h! shld cur$pos
	ret
;
delete$files:
	lxi h,open$root! shld cur$pos
delete$file1:
	mvi b,0! call search$nlist! rnz
	inx h! inx h! mov a,m! ani 10h! jz delete$file1
	dcx h! dcx h! call remove$locks! call delete$item
	jmp delete$file1
;
flush$files:
	lxi h,flushed! mov a,m! ora a! rnz
	inr m
flush$file0:
	lxi h,open$root! shld cur$pos
flush$file1:
	mvi b,1! call searchn$list! rnz
	push h! call remove$locks! call delete$item! pop h
	lxi d,6! dad d! mov e,m! inx h! mov d,m
	lxi h,pdcnt$off! dad d! mov a,m! ani 1! jnz flush$file1
	mov a,m! ori 1! mov m,a
	lhld pdaddr! mvi c,2! call compare! jnz flush$file1
	lda pdcnt! adi 10h! sta pdcnt! jmp flush$file1
;
free$files:
	; free$mode = 1 - remove curdsk files for process
	;	      0 - remove all files for process
	lhld pdaddr! xchg! lxi h,open$root! shld curpos
free$files1:
	lda free$mode! mov b,a
	push d! call searchn$list! pop d! rnz
	lxi b,6! call tst$tbl$lmt! jnz free$files1
	push h! inx h! inx h! inx h
	call test$ffff! jnz free$files2
	call test$ffff! jz free$files3
free$files2:
	mvi a,0ffh! sta incr$pdcnt
free$files3:
	pop h! call remove$locks! call delete$item
	jmp free$files1
;
remove$locks:
	shld file$id
	inx h! inx h! mov a,m! ani 40h! jz remove$lock3
	push d! lhld prv$pos! push h
	lhld file$id! xchg! lxi h,lock$root! shld cur$pos
remove$lock1:
	mvi b,0! push d! call searchn$list! pop d
	jnz remove$lock2
	lxi b,8! call tst$tbl$lmt! jnz remove$lock1
	call delete$item
	jmp remove$lock1
remove$lock2:
	pop h! shld prv$pos! pop d
remove$lock3:
	lhld file$id! ret
;
tst$tbl$lmt:
	push h! dad b
	mov a,m! inx h! mov h,m
	sub e! jnz tst$tbl$lmt1
	mov a,h! sub d
tst$tbl$lmt1:
	pop h! ret
;
create$olist$item:
	mvi b,1! call search$olist
	di
	call create$item! lda attributes! call set$olist$item
	ei
	ret
;
count$opens:
	xra a! sta open$cnt
	lhld pdaddr! xchg! lxi h,open$root! shld curpos
count$open1:
	mvi b,0! push d! call searchn$list! pop d! jnz count$open2
	lxi b,6! call tst$tbl$lmt! jnz count$open1
	lda open$cnt! inr a! sta open$cnt
	jmp count$open1
count$open2:
	lxi h,open$max! lda open$cnt! ret
;
count$locks:
	xra a! sta lock$cnt
	xchg! lxi h,lock$root! shld cur$pos
count$lock1:
	mvi b,0! push d! call searchn$list! pop d! rnz
	lxi b,8! call tst$tbl$lmt! jnz count$lock1
	lda lock$cnt! inr a! sta lock$cnt
	jmp count$lock1
;
check$free:
	lda mult$cnt! mov e,a
	mvi d,0! lxi h,free$root! shld cur$pos
check$free1:
	mvi b,0! push d! call searchn$list! pop d! jnz check$free2
	inr d! mov a,d! sub e! jc check$free1
	ret
check$free2:
	pop h! mvi a,14! jmp sta$ret
;
lock:				;record lock and unlock 
	call reselect! call check$fcb
	call test$unlocked
	rz ; file not opened in unlocked mode
	lhld xdmaad! mov e,m! inx h! mov d,m
	xchg! inx h! inx h
	mov a,m! mov b,a! lda curdsk! sub b
	ani 0fh! jnz lock8 ; invalid file id
	mov a,b! ani 40h! jz lock8 ; invalid file id
	dcx h! dcx h! shld file$id
	lda lock$unlock! inr a! jnz lock1 ; jmp if unlock
	call count$locks
	lda lock$cnt! mov b,a
	lda mult$cnt! add b! mov b,a
	lda lock$max! cmp b
	mvi a,12! jc sta$ret ; too many locks by this process
	call check$free
lock1:
	call save$rr! lxi h,lock9! push h! lda mult$cnt
lock2:
	push a! call get$lock$add
	lda lock$unlock! inr a! jnz lock3
	call test$lock
lock3:
	pop a! dcr a! jz lock4
	call incr$rr! jmp lock2
lock4:
	call reset$rr! lda mult$cnt
lock5:
	push a! call get$lock$add
	lda lock$unlock! inr a! jnz lock6
	call set$lock! jmp lock7
lock6:
	call free$lock
lock7:
	pop a! dcr a! rz
	call incr$rr! jmp lock5
lock8:
	mvi a,13! jmp sta$ret ; invalid file id
lock9:
	call reset$rr! ret
;
get$lock$add:
	lxi h,0! dad sp! shld lock$sp

; **** PATCH 2.1
; This patch sets Reg. C to FFh
; for call to RSEEK

	lxi h,lock$shell
	mvi c,0ffh ! mov m,c ! push h
	call rseek
	pop h ! inr m

; Replaced code:
;	mvi a,0ffh  sta lock$shell
;	call rseek
;	xra a  sta lock$shell

	call getfcb
	lhld aret! mov a,l! ora a! jnz lock$err

; **** PATCH 2.1
; Verify that block has not been allocated by another
; process before returning error code 1 (reading unwritten data)

	CALL PATCH11! CALL PATCH12! JZ LOCK$ERR
	CALL PATCH11! JMP LOCK$ERR

; Replaced code:
;	call index  call allocated  
;	lxi h,1  jz lock$err
;	call atran

	ret
;
lock$perr:
	xra a! sta lock$shell
	xchg! lhld lock$sp! sphl! xchg
lock$err:
	pop d ; discard return address
	pop b ; B = mult$cnt-# recs processed
	lda mult$cnt! sub b
	add a! add a! add a! add a
	ora h! mov h,a! mov b,a
	shld aret! ret
;
test$lock:
	call move$arecord
	mvi b,3! call search$llist! rnz
	call compare$pds! rz
	lxi h,8! jmp lock$err
;
set$lock:
	call move$arecord
	mvi b,1! call search$llist
	di
	call create$item
	xra a! call set$olist$item
	xchg! lhld file$id! xchg
	mov m,d! dcx h! mov m,e
	ei! ret
;
free$lock:
	call move$arecord
	mvi b,5! call search$llist! rnz
free$lock0:
	call delete$item
	mvi b,5! call searchn$list! rnz
	jmp free$lock0
;
compare$pds:
	lxi d,6! dad d

; **** PATCH 2.1
; Changed for chk$olist patch

	NOP! LXI D,PDADDR

; Replaced code:
;	xchg lxi h,pdaddr

	mvi c,2! jmp compare
	;ret
;
move$arecord:
	lxi d,arecord! lxi h,packed$dcnt
	mvi c,3! jmp move ;ret
;
fix$olist$item:
	lxi d,xdcnt! lxi h,sdcnt
	;is xdblk,xdcnt < sdblk,sdcnt
	mvi c,4! ora a!
fix$ol1:
	ldax d! sbb m! inx h! inx d! dcr c! jnz fix$ol1
	rnc
	;yes - update olist entry
	call swap! call sdcnt$eq$xdcnt
	lxi h,open$root! shld cur$pos
	;find file's olist entry
fix$ol2:
	call swap! call pack$sdcnt! call swap
	mvi b,3! call searchn$list! rnz
	;update olist entry with new dcnt value
	push h! call pack$sdcnt! pop h
	inx h! inx h! inx h! lxi d,packed$dcnt
	mvi c,3! call move! jmp fix$ol2
;
hl$eq$hl$and$de:
	mov a,l! ana e! mov l,a
	mov a,h! ana d! mov h,a
	ret
;
remove$drive:
	xchg! lda curdsk! mov c,a! lxi h,1
	call hlrotl
	mov a,l! cma! ana e! mov e,a
	mov a,h! cma! ana d! mov d,a
	xchg! ret
;
diskreset:
	lxi h,0! shld ntlog
	xra a! sta set$ro$flag
	lhld info
intrnldiskreset:
	xchg! lhld open$root! mov a,h! ora l! rz
	xchg! lda curdsk! push a! mvi b,0
dskrst1:
	mov a,l! rar! jc dskrst3
dskrst2:
	mvi c,1! call hlrotr! inr b
	mov a,h! ora l! jnz dskrst1
	pop a! sta curdsk
	lhld ntlog! xchg! lhld tlog
	mov a,l! ora e! mov l,a
	mov a,h! ora d! mov h,a! shld tlog

; **** PATCH 2.1
; Patch to return with Z flag reset in all cases

	INR B

; Replaced code:
;	inr a
	
	ret
dskrst3:
	push b! push h! mov a,b

; **** PATCH 2.1
; compute shiftr(tlog,curdsk) and push result on stack

	CALL PATCH8

; Replaced code:
;	sta curdsk

	lhld rlog! call test$vector1! push a
	lhld rodsk! lda curdsk! call test$vector1! mov b,a
	pop h! lda set$ro$flag! ora b! ora h

; **** PATCH 2.1
; ORA with shiftr(tlog,curdsk) value pushed on stack
; and store result in check$disk

	CALL PATCH9

; Replaced code:
;	sta check$disk

	lxi h,open$root! shld cur$pos
dskrst4:
	mvi b,1! call searchn$list! jnz dskrst6
	lda check$disk! ora a! jz dskrst5
	push h! call compare$pds! jz dskrst45
	pop h! xra a! xchg! jmp dskrst6
dskrst45:
	lhld ntlog! mov b,h! mov c,l
	lda curdsk! call set$cdisk1! shld ntlog
	pop h! jmp dskrst4
dskrst5:
	lhld info! call remove$drive! shld info
	ori 1
dskrst6:
	pop h! pop b! jnz dskrst2

	;error - olist item exists for another process
	;for removable drive to be reset
	pop a! sta curdsk! mov a,b! adi 41h ; A = ASCII drive
	lxi h,6! dad d! mov c,m! inx h! mov b,m ; BC = pdaddr
	push psw! call test$error$mode! pop d! jnz dskrst7
	mov a,d

	push b! push psw
	call rlr! lxi d,console! dad d! mov d,m ;D = console #
	lxi b,deniedmsg! call xprint
	pop psw! mov c,a! call conoutx
	mvi c,':'! call conoutx
	lxi b,cnsmsg! call xprint
	pop h! push h! lxi b,console! dad b
	mov a,m! adi '0'! mov c,a! call conoutx
	lxi b,progmsg! call xprint
	pop h! call dsplynm

dskrst7:
	pop h ;remove return addr from diskreset
	lxi h,0ffffh! shld aret ;flag the error
	ret
;
deniedmsg:
	db cr,lf,'Disk reset denied, Drive ',0
cnsmsg:
	db ' Console ',0
progmsg:
	db ' Program ',0
;
;
ENDIF
;
;	individual function handlers
func12:
	;return version number
IF MPM
	lxi h,0100h+dvers! jmp sthl$ret
ELSE
	mvi a,dvers! jmp sta$ret ;lret = dvers (high = 00)
ENDIF
;	ret ;jmp goback
;
func13:
IF MPM

; **** PATCH 2.1
; Test all drives, not just logged in drives, because
; some drives may be in TLOG state.

	LXI H,0FFFFH

; Replaced code:
;	lhld dlog

	shld info
	call diskreset! jz reset$all
	call reset$37
	jmp func13$cont
reset$all:
ENDIF
	;reset disk system - initialize to disk 0
	lxi h,0! shld rodsk! shld dlog
IF MPM
	shld rlog! shld tlog
func13$cont:
ENDIF
	xra a! STA SELDSK ;note that usrcode remains unchanged
	DCR A! STA CURDSK 
IF MPM
	xra a! call getmemseg ;A = mem seg tbl index
	ora a! rz
	inr a! rz
	call rlradr! lxi b,msegtbl-rlros! dad b
	add a! add a! mov e,a! mvi d,0! dad d
	mov h,m! mvi l,80h
	jmp intrnlsetDMA
ELSE
	lxi h,tbuff! shld dmaad ;dmaad = tbuff
        JMP SETDATA ;to data dma address
ENDIF
	;ret ;jmp goback
;
func14:	
	;select disk info
	CALL SET$SELDSK ; SELDSK = LINFO
IF MPM
	call curselect
	call rlr! lxi b,diskselect! dad b
	mov a,m! ani 0fh! rrc! rrc! rrc! rrc
	mov b,a! lda seldsk! ora b! rrc! rrc! rrc! rrc
	mov m,a! ret
ELSE
	JMP CURSELECT
ENDIF
	;ret ;jmp goback
;
func15:
	;open file
	call clrmodnum ;clear the module number
	call reselect
IF MPM
	xra a! sta make$flag
	call set$sdcnt
	lxi h,open$file! push h
	mvi a,0c9h! sta check$fcb4
	call check$fcb1
	pop h! lda high$ext! cpi 060h! jnz open$file
	call home! call set$end$dir
	jmp open$user$zero
open$file:
	lhld info! lda usrcode! mov m,a
	call set$sdcnt
ENDIF
	CALL RESET$CHKSUM$FCB ; SET INVALID CHECK SUM
	CALL CHECK$WILD ; CHECK FOR WILD CHARS IN FCB
	CALL GET$ATTS! ANI 1100$0000B ; A = ATTRIBUTES
IF MPM
 	cpi 1100$0000b! jnz att$ok
ENDIF
	ANI 0100$0000B ; MASK OFF UNLOCK MODE 
ATT$OK:
	STA HIGH$EXT
IF MPM
	mov b,a! ora a! rar! jnz att$set
	mvi a,80h
att$set:
	sta attributes! mov a,b
ENDIF
	ANI 80H! JNZ CALL$OPEN
	LDA USRCODE! ORA A! JZ CALL$OPEN 
	MVI A,0FEH! STA XDCNT+1! INR A! STA SEARCH$USER0
IF MPM
	sta sdcnt0+1
ENDIF
;
CALL$OPEN:
	CALL OPEN! CALL OPENX ; RETURNS IF UNSUCCESSFUL, A = 0
	LXI H,SEARCH$USER0! CMP M! RZ
	MOV M,A! LDA XDCNT+1! CPI 0FEH! RZ 
;
;	FILE EXISTS UNDER USER 0
;
IF MPM
	call swap
ENDIF
	CALL SET$DCNT$DBLK
	MVI A,0110$0000B! STA HIGH$EXT
OPEN$USER$ZERO:
	; SET FCB USER # TO ZERO
	LHLD INFO! MVI M,0
	MVI C,NAMLEN! CALL SEARCHI! CALL SEARCHN
	CALL OPEN1 ; ATTEMPT REOPEN UNDER USER ZERO
	CALL OPENX ; OPENX RETURNS ONLY IF UNSUCCESSFUL
	RET
OPENX:
	CALL END$OF$DIR! RZ
	; OPEN SUCCESSFUL
	POP H ; DISCARD RETURN ADDRESS
	; WAS FILE OPENED UNDER USER 0 AFTER UNSUCCESSFUL
	; ATTEMPT TO OPEN UNDER USER N
IF MPM
	LDA HIGH$EXT! CPI 060H! JZ OPENX00 ;YES
	; WAS FILE OPENED IN LOCKED MODE?
	ORA A! JNZ OPENX0 ; NO
	; DOES USER = ZERO?
	LHLD INFO! ORA M! JNZ OPENX0 ; NO
	; DOES FILE HAVE READ/ONLY ATTRIBUTE SET?
	CALL ROTEST! JNC OPENX0 ; NO
	; DOES FILE HAVE SYSTEM ATTRIBUTE SET?
	INX H! MOV A,M! RAL! JNC OPENX0 ; NO

	; FORCE OPEN MODE TO READ/ONLY MODE AND SET USER 0 FLAG
	; IF FILE OPENED IN LOCKED MODE, USER = 0, AND
	; FILE HAS READ/ONLY AND SYSTEM ATTRIBUTES SET

OPENX00:
ELSE
	LDA HIGH_EXT! CPI 060H! JNZ OPENX0 ; NO
ENDIF
	; IS FILE UNDER USER 0 A SYSTEM FILE ?
	MVI A,20H! STA ATTRIBUTES
	LHLD INFO! LXI D,10! DAD D
	MOV A,M! ANI 80H! JNZ OPENX0 ; YES - OPEN SUCCESSFUL
	; OPEN FAILS
	STA HIGH$EXT! JMP LRET$EQ$FF
OPENX0:
	CALL RESET$CHKSUM$FCB
	;
	;DOES DIR LBL EXIST?
IF MPM
	call get$dir$mode! ora a! jz openx1
ELSE
	CALL GET$DIR$MODE! ORA A! JZ SET$FCB$CKS$FLAG
ENDIF
	;YES - IS FILE PASSWORD PROTECTED FOR OPEN?
	PUSH A! CALL GET$XFCB! POP B
	ANI 0C0H! JZ OPENX1
	;YES - HAVE PASSWORDS BEEN ENABLES BY DIR LBL?
	MOV C,A! MOV A,B! ANI 80H! JZ OPENX1
	;YES - CHECK PASSWORD
	PUSH B! CALL CMP$PW! POP B! JZ OPENX1
	MOV A,C! ANI 40H! CZ CHK$PW1
	MVI A,080H! STA XFCB$READ$ONLY
OPENX1:
;
IF MPM

; **** PATCH 2.1 
; Set default open mode to R/O if in compatibility mode and
; if compatibility attribute F1' is set.

	CALL PATCH1

; Replaced code:
;	call pack$sdcnt

	;is this file currently open?
	mvi b,3! call search$olist! jz openx04
openx01:
	;no - is olist full?
	lhld free$root! mov a,l! ora h! jnz openx03
	;yes - error
openx02:
	mvi a,11! jmp set$aret
openx03:
	;has process exceeded open file maximum?
	call count$opens! sub m! jc openx035
	;yes - error
openx034:
	mvi a,10! jmp set$aret
openx035:
	;create new olist element
	call create$olist$item
	jmp openx08
openx04:
	;do file attributes match?
	inx h! inx h
	lda attributes! ora m! cmp m! jnz openx06
	;yes - is open mode locked?
	ani 80h! jnz openx07
	;no - has this file been opened by this process?
	lhld prv$pos! shld cur$pos
	mvi b,5! call searchn$list! jnz openx01
openx05:
	;yes - increment open file count
	lxi d,8! dad d! inr m! jnz openx08
	;count overflow
	inx h! inr m! jmp openx08
openx06:
	;error - file opened by another process in imcompatible mode
	mvi a,5! jmp set$aret
openx07:
	;does this olist item belong to this process?
	dcx h! dcx h! push h
	call compare$pds
	pop h! jnz openx06 ; no - error
	jmp openx05 ; yes
openx08:; open ok
	;was file opened in unlocked mode?
	lda attributes! ani 40h! jz openx09 ; no
	;yes - return .olist$item in ranrec field of fcb
	call get$rra
	lxi d,cur$pos! mvi c,2! call move
openx09:
;
	call set$fcb$cks$flag
	lda make$flag! ora a! rnz
	call get$dir$mode
ELSE
	CALL SET$FCB$CKS$FLAG! MOV A,B
ENDIF
	;IS FILE ACCESS STAMPING ENABLED BY DIR LBL?
	ANI 40H! RZ
	CALL ENDOFDIR! RZ
	;YES - IS FILE READ/WRITE?
	CALL NOWRITE! RNZ ; CANNOT STAMP IF DISK READ ONLY
	;YES - UPDATE FCB ACCESS STAMP
OPENX2:
	CALL STAMP1
	JMP SEEK$COPY
;
func16:
	;close file
	call reselect
	CALL GET$ATTS! STA ATTRIBUTES
IF MPM
	lxi h,close00! push h
	mvi a,0c9h! sta check$fcb4
	call check$fcb1
	pop h
close000:
	call set$sdcnt
	call getmodnum! ani 80h! jnz close01
	call close! jmp close02
close00:
	mvi a,6

; **** PATCH 2.1
; if comp att F3 = 0 then report close checksum error
; otherwise allow c1ose to proceed by jumping to close000

	jmp patch5

; Removed code:
;	jmp set$aret


close01:
	mvi a,0ffh! sta dont$close! call close1
close02:
ELSE
	CALL CHEK$FCB! MVI A,6! JNZ SET$ARET
	CALL CLOSE
ENDIF
	LDA LRET! INR A! RZ
	CALL FUNC48 ; FLUSH BUFFERS
	LDA ATTRIBUTES! ANI 1000$0000B! RNZ
IF MPM
	call pack$sdcnt
	;find olist item for this process & file
	mvi b,5! call search$olist! jnz close03
	;is open count = 0ffffh

; **** PATCH 2.1
; Default to partial close if comp att F2' = 1.
; If permanent close, dont delete lock list item
; if interface attribute F6' = 1.

	CALL GET$CMP$MODE! RAL! RAL
	;yes - remove file's olist entry
	JC CLOSE03
	PUSH H! LXI D,8! DAD D
	MOV C,M! INX H! MOV B,M! DCX B
	MOV M,B! DCX H! MOV M,C! POP D
	INR B! JNZ CLOSE03
	CALL PATCH4! CALL DELETE$ITEM

; Replaced code:
;       push h  lxi d,8  dad d
;       mov a,m  sui 1  mov m,a  inx h
;       mov a,m  sui 0  mov m,a  dcx h
;       call test$ffff  pop h  jnz close03
;       shld file$id  call delete$item
;       call reset$chksum$fcb

	;if unlocked file, remove file's locktbl entries
	call test$unlocked! jz close03
	lhld file$id! call remove$locks
close03:
ENDIF
	;
	;HAS FILE BEEN WRITTEN TO SINCE IT WAS OPENED?
	CALL GETMODNUM! ANI 40H! RZ
	;YES - IS FILE UPDATE STAMPING ENABLED BY DIR LBL?
	CALL GET$DIR$MODE! ANI 20H! RZ
	;YES - DOES XFCB EXIST FOR FILE?
	CALL GET$XFCB! RZ
	;YES - UPDATE XFCB UPDATE STAMP
	CALL STAMP2
	JMP SEEK$COPY
	;ret ;jmp goback
;
func17:
	;search for first occurrence of a file
	xchg! XRA A
CSEARCH:
	PUSH A
	mov a,m! cpi '?'! JNZ CSEARCH1 ; NO RESELECT IF ?
	CALL CURSELECT! MVI C,0! JMP CSEARCH3
CSEARCH1:
	CALL GETEXTA! MOV A,M! CPI '?'! JZ CSEARCH2
	CALL CLR$EXT! CALL CLRMODNUM
CSEARCH2:
	CALL RESELECTX
	MVI C,NAMLEN
CSEARCH3:
	POP A
IF NOT MPM
	LXI H,DIR$TO$USER
	PUSH H
ENDIF
	JNZ SEARCHN
	JMP SEARCH
	;ret ;jmp goback
;
func18:
	;search for next occurrence of a file name
IF MPM
	xchg! shld searcha
ELSE
	lhld searcha! shld info
ENDIF
;
	ORI 1! JMP CSEARCH
	;ret ;jmp goback
;
func19:
	;delete a file
	call reselectx
	jmp delete
	;ret ;jmp goback
;
func20:
	;read a file
	call reselect

; **** PATCH 2.1
; if comp att F4 = 1 then dont call check$fcb

	CALL PATCH6

; replaced code:
;	call check$fcb

	jmp seqdiskread				;
	 ;jmp goback
;
func21:
	;write a file
	call reselect

; **** PATCH 2.1
; if comp att F4 = 1 then dont call check$fcb

	CALL PATCH6

; replaced code:
;	call check$fcb

	jmp seqdiskwrite
	 ;jmp goback
;
func22:
	;make a file
	CALL GET$ATTS! STA ATTRIBUTES
	XRA A! STA FCB$EXISTS
	CALL CLR$EXT
	call clrmodnum ; FCB MOD = 0
	call reselect
	CALL RESET$CHKSUM$FCB
	CALL CHECK$WILD
	CALL SET$XDCNT ; RESET XDCNT FOR MAKE
IF MPM
	call set$sdcnt
ENDIF
	CALL OPEN ; VERIFY FILE DOES NOT ALREADY EXIST
	CALL RESET$CHKSUM$FCB
	CALL END$OF$DIR! JNZ FILE$EXISTS ; 
IF MPM
	lda attributes! ani 80h! rrc! jnz makex00
	mvi a,80h
makex00:

; **** PATCH 2.1
; Set the default open mode to R/O if in compatibility mode
; and attribute F1' is set.

	CALL PATCH2

; Replaced code:
;	sta make$flag

	lda sdcnt+1! inr a! jz makex01
	call pack$sdcnt
	mvi b,3! call search$olist! jz make$x02
makex01:
	lhld free$root! mov a,l! ora h! jz openx02
	jmp makex03
makex02:
	inx h! inx h
	lda makeflag! ana m! jz openx06
	dcx h! dcx h! call compare$pds! jz makex03
	lda makeflag! ral! jc openx06
makex03:
ENDIF
	;DO ANY FCBS FOR FILE EXIST IN THE DIRECTORY?
	LDA FCB$EXISTS! ORA A! JZ MAKEX04
	;YES - DOES DIR LBL REQUIRE PASSWORDS?
	CALL GET$DIR$MODE! ANI 80H! JZ MAKEX04
	;YES - DOES XFCB EXIST WITH MODE 1 OR 2 PASSWORD?
	CALL GET$XFCB! ANI 0C0H! JZ MAKEX04
	;YES - CHECK PASSWORD
	CALL CHK$XFCB$PASSWORD1
MAKEX04:
;
	CALL MAKE
	CALL RESET$CHKSUM$FCB
	CALL END$OF$DIR! RZ ; RETURN IF MAKE UNSUCCESSFUL
	;
	;DOES DIR LBL EXIST?
	CALL GET$DIR$MODE! ORA A! JZ MAKE4
	;YES - DOES XFCB ALREADY EXIST FOR FILE
	CALL GET$XFCB! JNZ MAKE000
	;NO - DOES DIR LBL INVOKE AUTO XFCB CREATE
	CALL GET$DIR$MODE! ANI 10H! JZ MAKE4
	MVI A,0FFH! STA MAKE$XFCB! CALL MAKE! JNZ MAKE00
	MVI C,NAMLEN! CALL SEARCH	
	CALL DELETE0! JMP LRET$EQ$FF
MAKE000:
	;YES - IS THIS FIRST FCB FOR FILE?
	LDA FCB$EXISTS! ORA A! JNZ MAKE4
MAKE00:
	CALL INIT$XFCB 
MAKE1:
	CALL SET$FCB$CKS$FLAG
	;DID USER SET PASSWORD ATTRIBUTE
	LDA ATTRIBUTES! ANI 40H! JZ MAKE3
	XCHG! LHLD XDMAAD! LXI B,8! DAD B! XCHG
	LDAX D! ANI 0E0H! JNZ MAKE2
	MVI A,80H
MAKE2:
	PUSH A! CALL GETXFCB1! POP A! MOV M,A
	CALL SET$PW! MOV M,B
MAKE3:
	CALL OPENX2
MAKE4:
IF MPM
	lda make$flag! sta attributes
	ani 40h! ral! sta high$ext
	lda sdcnt+1! inr a! jnz makexx02
	call sdcnt$eq$xdcnt! call pack$sdcnt
	jmp openx03
makexx02:
	call fix$olist$item! jmp openx1
ENDIF
	JMP SET$FCB$CKS$FLAG
	;ret ;jmp goback
FILE$EXISTS:
	MVI A,8! 
SET$ARET:
	MOV C,A! STA ARET+1! CALL LRET$EQ$FF
IF MPM
	call test$error$mode! jnz goback
ELSE
	LDA ERROR$MODE! INR A! RZ
	MVI A,40H! ADD C! STA XERR$ID
ENDIF
IF MPM
	mov a,c! sui 3
	mov l,a! mvi h,0! dad h
	lxi d,xerr$list! dad d
	mov e,m! inx h! mov d,m
	xchg! jmp report$err
ELSE
	LXI H,XERROR! JMP REPORT$ERR
ENDIF
;
func23:
	;rename a file
	call reselectx
	jmp rename
	;ret ;jmp goback
;
func24:
	;return the login vector
	lhld dlog! jmp sthl$ret			;
;	ret ;jmp goback
;
func25:
	;return selected disk number
	LDA SELDSK! jmp sta$ret			;
;	ret ;jmp goback
;
func26:
IF MPM
	;save DMA address in process descriptor
	lhld info
intrnlsetDMA:
	xchg
	call rlr! lxi b,disksetDMA! dad b
	mov m,e! inx h! mov m,d
ENDIF
	;set the subsequent dma address to info
	xchg! shld dmaad ;dmaad = info
        jmp setdata ;to data dma address
;
func27:
	;return the login vector address
	CALL XCURSELECT
	lhld alloca! jmp sthl$ret
;	ret ;jmp goback
;
IF MPM
func28:
	;write protect current disk
	;first check for open files on disk
	mvi a,0ffh! sta set$ro$flag
	lda seldsk! mov c,a! lxi h,0001h
	call hlrotl! call intrnldiskreset
	jmp set$ro
ELSE
func28:	equ	set$ro				;
	;write protect current disk
	;ret ;jmp goback
ENDIF
;
func29:
	;return r/o bit vector
	lhld rodsk! jmp sthl$ret		;
;	ret ;jmp goback
;
func30:
	;set file indicators
	CALL CHECK$WRITE
	CALL CHECK$WILD
	call reselectx
	CALL CHK$PASSWORD
	call indicators
	jmp copy$dirloc ;lret=dirloc
	;ret ;jmp goback
;
func31:
	;return address of disk parameter block
	CALL XCURSELECT
	lhld dpbaddr
sthl$ret:
 	shld aret
	ret ;jmp goback
;
func32:
	;set user code
        lda linfo! cpi 0ffh! jnz setusrcode
		;interrogate user code instead
		lda usrcode! jmp sta$ret ;lret=usrcode	
;		ret ;jmp goback
	setusrcode:
		ANI 0FH! sta usrcode
IF MPM
		push a
		call rlr! lxi b,diskselect! dad b
		pop b
		mov a,m! ani 0f0h! ora b! mov m,a
ENDIF
		ret ;jmp goback
;
func33:
	;random disk read operation
	call reselect

; **** PATCH 2.1
; if comp att F4 = 1 then dont call check$fcb

	CALL PATCH6

; replaced code:
;	call check$fcb

	jmp randiskread ;to perform the disk read
	;ret ;jmp goback
;
func34:
	;random disk write operation
	call reselect

; **** PATCH 2.1
; if comp att F4 = 1 then dont call check$fcb

	CALL PATCH6

; replaced code:
;	call check$fcb

	jmp randiskwrite ;to perform the disk write
	;ret ;jmp goback
;
func35:
	;return file size (0-65536)
	call reselect
	CALL CHECK$WILD
	jmp getfilesize
	;ret ;jmp goback
;
func36:	equ setrandom
	;set random record
	;ret ;jmp goback
func37:
;
IF MPM
	call diskreset
reset$37:
	lhld info
ELSE
	XCHG
ENDIF
	mov a,l! cma! mov e,a! mov a,h! cma
	lhld dlog! ana h! mov d,a! mov a,l! ana e
	mov e,a! lhld rodsk! xchg! shld dlog
IF MPM
	push h! call hl$eq$hl$and$de
ELSE
	mov a,l! ana e! mov l,a
	mov a,h! ana d! mov h,a
ENDIF
	shld rodsk! 
;
IF MPM
	pop h! xchg! lhld rlog! call hl$eq$hl$and$de! shld rlog
ENDIF
	LHLD DLOG! LDA CURDSK! CALL TEST$VECTOR1
	RNZ ; RETURN IF CURDSK NOT RESET
	MVI A,0FFH! STA CURDSK
	; FORCE SELECT CALL IN NEXT CURSELECT
	RET
IF MPM
;
func38:				; access drive
	lxi h,packed$dcnt! mvi a,0ffh
	mov m,a! inx h! mov m,a! inx h! mov m,a
	xra a! xchg! lxi b,16
acc$drv0:
	dad h! adc b! dcr c! jnz acc$drv0
	ora a! rz
	sta mult$cnt! dcr a! push a
	call acc$drv02
	pop a! jmp openx02 ; insufficient free lock list items
acc$drv02:
	call check$free! pop h ; discard return addr, free space exists
	call count$opens! pop b! add b! jc openx034
	sub m! jnc openx034 ; openmax exceeded
	lhld info! lda curdsk! push a! mvi a,16
acc$drv1:
	dcr a! dad h! jc acc$drv2
acc$drv15:
	ora a! jnz acc$drv1
	pop a! sta curdsk! ret
acc$drv2:
	push a! push h! sta curdsk
	call create$olist$item
	pop h! pop a! jmp acc$drv15
;
func39:				; free drive
	lhld open$root! mov a,h! ora l! rz
	xra a! sta incr$pdcnt! inr a! sta free$mode
	lhld info! mov a,h! cmp l! jnz free$drv1
	inr a! jnz free$drv1
	sta free$mode! call free$files! jmp free$drv3
free$drv1:
	lda curdsk! push a! mvi a,16
free$drv2:
	dcr a! dad h! jc free$drv4
free$drv25:
	ora a! jnz free$drv2
	pop a! sta curdsk
free$drv3:
	lda incr$pdcnt! ora a! rz
	lda pdcnt! jmp chk$olist1
free$drv4:
	push a! push h! sta curdsk
	call free$files
	pop h! pop a! jmp free$drv25
ELSE
func38 	equ	func$ret
func39	equ	func$ret
ENDIF
;
func40:
	;random disk write with zero fill of unallocated block
	call reselect

; **** PATCH 2.1
; if comp att F4 = 1 then dont call check$fcb

	CALL PATCH6

; replaced code:
;	call check$fcb

	mvi a,2! sta seqio
	mvi	c,false
	call	rseek1
	cz	diskwrite	;if seek successful
	ret
;
func41	equ	func$ret	;test & write
IF MPM
func42:				;record lock
	mvi a,0ffh! sta lock$unlock! jmp lock
func43:				;record unlock
	xra a! sta lock$unlock! jmp lock
ELSE
func42	equ	func$ret	;record lock
func43	equ	func$ret	;record unlock
ENDIF
;
func44:				;set multi-sector count
	mov a,e! ora a! jz lret$eq$ff
	cpi 17! jnc lret$eq$ff
	sta mult$cnt
IF MPM
	mov d,a
	call rlr! lxi b,mult$cnt$off! dad b
	mov m,d
ENDIF
	ret
;
func45:				;set BDOS error mode
IF MPM
	call rlr! lxi b,pname+4! dad b
	call set$pflag
	mov m,a! inx h
	call set$pflag
	mov m,a! ret
;
set$pflag:
	mov a,m! ani 7fh! inr e! rnz
	ori 80h! ret
ELSE
	mov a,e! sta error$mode
ENDIF
	ret
;
func46:				;get free space
	;perform temporary select of specified drive
	call tmpselect
	lhld alloca! xchg ; DE = alloc vector addr
	call get$nalbs ; get # alloc blocks
	; HL = # of allocation vector bytes
	; count # of true bits in allocation vector
	lxi b,0 ; BC = true bit accumulator
gsp1:	ldax d
gsp2:	ora a! jz gsp4
gsp3:	rar! jnc gsp3
	inx b! jmp gsp2
gsp4:	inx d! dcx h
	mov a,l! ora h! jnz gsp1
	;HL = 0 when allocation vector processed
	;compute maxall + 1 - BC
	lhld maxall! inx h
	mov a,l! sub c! mov l,a
	mov a,h! sbb b! mov h,a
	;HL = # of available blocks on drive
	lda blkshf! mov c,a! xra a
	call shl3bv
	;AHL = # of available sectors on drive
	;store AHL in beginning of current DMA
	xchg! lhld xdmaad! mov m,e! inx h
	mov m,d! inx h! mov m,a
	ret
;
IF MPM
func47	equ	func$ret
ELSE
func47: 			;chain to program
	;save user# and seldsk in location 0004h
	lda usrcode
	add a! add a! add a! add a! mov b,a
	lda seldsk! ora b! sta base+0004h
	mvi a,0ffh! sta chain$flg! jmp REBOOTX
ENDIF
;
func48:				;flush buffers
	lxi h,diocomp! push h
	lxi b,0ffffh! call setdmaf
	pop h! jmp setdata
;
IF MPM
func49	equ	func$ret
ELSE
func49:				;ccp chain query
	lxi h,chain$flg! mov a,m! ora a! rz
	mvi m,0! jmp lret$eq$ff
ENDIF
;
IF MPM
func50	equ	func$ret
ELSE
func50:				;direct bios call
	;DE -> function (1 byte)
	;      BC value (2 bytes)
	;      DE value (2 bytes)
	xchg
	mov a,m! inx h
	mov c,m! inx h! mov b,m! inx h
	mov e,m! inx h! mov d,m
	mov l,a! add a! add l

	lxi h,bios

	add l! mov l,a!
	mvi a,0! adc h! mov h,a! pchl
ENDIF
;
func100:			;set directory label
	;DE -> .fcb
	;      drive location
	;      name & type fields user's discretion
	;      extent field definition
	;      bit 1 (80h): enable passwords on drive
	;      bit 2 (40h): enable file access 	
	;      bit 3 (20h): enable file update stamping
	;      bit 8 (01h): assign new password to dir lbl
	call reselectx
	call check$write
	;does dir lbl exist on drive?
	lhld info! mvi m,20h! mvi c,1
	call set$xdcnt! call search! jnz sdl1
	;no - make one
	mvi a,0ffh! sta make$xfcb
	call make! rz ;no dir space
	call init$xfcb! call stamp1 ; create date & time stamp
sdl1:
	;update date & time stamp
	call stamp2
	;verify password - new dir lbl falls through
	call getxfcb1! call chk$xfcb$password1
	lxi b,0! call init$xfcb0
	;set dir lbl dta in extent field
	ldax d! ori 1h! mov m,a
	;low bit of dir lbl data set to indicate dir lbl exists
	;update drive's dir lbl vector element
	push h! lhld drvlbla! mov m,a! pop h
sdl2:
	;assign new password to dir lbl or xfcb?
	ldax d! ani 1! jz seek$copy
	;yes - new password field is in 2nd 8 bytes of dma
	lxi d,8! call adjust$dmaad
	call set$pw! mov m,b
	lxi d,-8! call adjust$dmaad
	jmp seek$copy
;
func101:			;return directory label data
	;perform temporary select of specified drive
	call tmpselect
	call get$dir$mode! jmp sta$ret
;
func102:			;read file xfcb
	call reselectx
	call check$wild
	;does xfcb exist for file?
	call getxfcb! mvi a,0ffh! jz sta$ret
	;yes - copy xfcb to user's fcb
	lhld info! mvi c,nxtrec! jmp move ;ret
;
func103:			;write or update file xfcb
	call reselectx
	call check$wild
	;does xfcb exist for file?
	call set$xdcnt
	call getxfcb! jnz wxfcb1
	;no - does dir lbl exist in directory?
	call get$dir$mode
	ora a! mvi a,0ffh! jz sta$ret
	;yes - does file exist in directory?
	sta make$xfcb
	mvi c,extnum! call search! rz
	;yes - attempt to make xfcb for file
	call make! rz ; no dir space
	;initialize xfcb
	call init$xfcb 
wxfcb1:
	;verify password - new xfcb falls through
	call chk$xfcb$password
	;set xfcb options data
	push h! call getexta! pop d! xchg
	mov a,m! ora a! jnz wxfcb2
	ldax d! ani 1! jz seek$copy
wxfcb2:
	ldax d! ani 0e0h! jnz wxfcb3
	mvi a,80h
wxfcb3:
	mov m,a! jmp sdl2
;
func104:			;set current date and time
IF MPM
	call get$stamp$add
	call copy$stamp
	mvi m,0! ret
ELSE
	lxi h,stamp
	jmp copy$stamp
ENDIF
;
func105:			;get current date and time
IF MPM
	call get$stamp$add
ELSE
	lxi h,stamp
ENDIF
	xchg
copy$stamp:
	mvi c,4! jmp move ;ret
;
IF MPM
get$stamp$add:
	call rlradr! lxi b,-5! dad b
	ret
ENDIF
;
func106:			;set default password
IF MPM
	call get$df$pwa! inr a! rz
	lxi b,7! dad b
ELSE
	LXI H,DF$PASSWORD+7
ENDIF
	xchg! lxi b,8! push h
	jmp set$pw0
;
func107:			;return serial number
IF MPM
	lhld sysdat! mvi l,181
ELSE
	lxi h,serial
ENDIF
	xchg! mvi c,6! jmp move
;
GOBACK0:
	XRA A! STA RETURN$FFFF
	LXI H,0FFFFH! SHLD ARET
goback:
	;arrive here at end of processing to return to user
	lda resel! ora a! jz retmon
		;reselection may have taken place
		LDA COMP$FCB$CKS! ORA A! CNZ SET$CHKSUM$FCB
		LDA XFCB$READ$ONLY! ORA A! JZ GOBACK05
		LHLD INFO! LXI D,7! DAD D
		ORA M! MOV M,A
	GOBACK05:
		CALL GETEXTA! LDA HIGH$EXT! CPI 60H! JNZ GOBACK1
		LXI D,-4! DAD D! MVI A,80H
	GOBACK1:
		ORA M! MOV M,A
		LDA ACTUAL$RC! ORA A! JZ GOBACK2
		CALL GETRCNTA! ORA M! MOV M,A
	GOBACK2:
		lhld info! mvi m,0 ;fcb(0)=0
		lda fcbdsk! ora a! jz retmon
		;restore disk number
		mov m,a ;fcb(0)=fcbdsk
		lda olddsk! STA SELDSK
;
;	return from the disk monitor
retmon:
	lhld entsp! sphl
	lhld aret! mov a,l! mov b,h! ret
;
;
;	data areas
;
;	initialized data
efcb:	db	empty	;0e5=available dir entry
rodsk:	dw	0	;read only disk vector
dlog:	dw	0	;logged-in disks
IF MPM
rlog:	dw	0	;removeable logged-in disks
tlog:	dw	0	;removeable disk test login vector
ntlog:	dw	0	;new tlog vector
rem$drv: db	0	;curdsk removable drive switch
			;0 = permanent drive, 1 = removable drive
ENDIF
IF NOT BNKBDOS
xdmaad	equ	$
ENDIF
dmaad:	dw	tbuff	;initial dma address
IF NOT MPM
buffa:	ds	word	;pointer to directory dma address
ENDIF
;
;	curtrka - alloca are set upon disk select
;	(data must be adjacent, do not insert variables)
;	(address of translate vector, not used)
cdrmaxa:ds	word	;pointer to cur dir max value
curtrka:ds	word	;current track address
curreca:ds	word	;current record address
DRVLBLA:DS	WORD	;CURRENT DRIVE LABEL BYTE ADDRESS
dpbaddr:ds	word	;current disk parameter block address
checka:	ds	word	;current checksum vector address
alloca:	ds	word	;current allocation vector address
addlist	equ	$-DPBADDR	;address list size
;
;	sectpt - offset obtained from disk parm block at dpbaddr
;	(data must be adjacent, do not insert variables)
sectpt:	ds	word	;sectors per track
blkshf:	ds	byte	;block shift factor
blkmsk:	ds	byte	;block mask
extmsk:	ds	byte	;extent mask
maxall:	ds	word	;maximum allocation number
dirmax:	ds	word	;largest directory number
dirblk:	ds	word	;reserved allocation bits for directory
chksiz:	ds	word	;size of checksum vector
offset:	ds	word	;offset tracks at beginning
dpblist	equ	$-sectpt	;size of area
;
;	local variables
RETURN$FFFF:	DS	BYTE	;SEL ERR FLAG FOR FXS 27 & 31
tranv:	ds	word	;address of translate vector
lock$unlock:
make$flag:
rmf:	ds	byte	;read mode flag for open$reel
incr$pdcnt:
dirloc:	ds	byte	;directory flag in rename, etc.
fcb$exists:
	ds	byte	;Make FCB exists flag
free$mode:
seqio:	ds	byte	;1 if sequential i/o
linfo:	ds	byte	;low(info)
dminx:	ds	byte	;local for diskwrite
searchl:ds	byte	;search length
searcha:ds	word	;search address
ACTUAL$RC:
	DS	BYTE	;DIRECTORY EXT RECORD COUNT
SAVE$XFCB:
	DS	BYTE	;SEARCH XFCB SAVE FLAG
single:	ds	byte	;set true if single byte allocation map
SELDSK: DS	BYTE	;CURRENTLY SELECTED DISK 
olddsk:	ds	byte	;disk on entry to bdos
rcount:	ds	byte	;record count in current fcb
extval:	ds	byte	;extent number and extmsk
SAVE$MOD:
	DS	BYTE	;OPEN$REEL MODULE SAVE FIELD
SAVE$EXT:
	DS	BYTE	;OPEN$REEL EXTENT SAVE FIELD
vrecord:ds	BYTE	;current virtual record
arecord:ds	word	;current actual record
	DS	BYTE
;			SHELL FCB SAVE AREA
SAVE$RANR:	DB	0,0,0	;RANDOM RECORD SAVE AREA
arecord1:	ds	word	;current actual block# * blkmsk
IF NOT MPM
MULT$CNT:	DB	1	;MULTI-SECTOR COUNT
ERROR$MODE 	DB	0	;BDOS ERROR MODE
ENDIF
ATTRIBUTES:	DB	0	;MAKE ATTRIBUTE HOLD AREA
IF NOT MPM
CHAIN$FLG:	DB	0	;CHAIN FLAG
STAMP:		DB	0FFH,0FFH,0FFH,0FFH
ENDIF
;
;******** Following variable order critical *****************
IF MPM
mult$cnt:	db	1	;multi-sector count
pdcnt:		db	0	;process descriptor count
ENDIF
HIGH$EXT:	DB	0	;FCB HIGH EXT BITS
XFCB$READ$ONLY:	DB	0	;XFCB READ ONLY FLAG
CURDSK:		DB	0	;CURRENT DISK
IF MPM
;				MP/M VARIABLES	*
packed$dcnt:	db	0,0,0			;
pdaddr:		dw	0			;
;************************************************************
cur$pos:	dw	0			;
prv$pos:	dw	0			;
sdcnt:		dw	0   	 		;
sdblk:		dw	0   			;
sdcnt0:		dw	0			;
sdblk0:		dw	0			;
dont$close:	db	0			;
open$cnt:			;MP/M temp variaable for open
lock$cnt:	dw	0	;MP/M temp variable for lock
file$id:	dw	0	;MP/M temp variable for lock
deleted$files:	db	0
lock$shell:	db	0
lock$sp:	dw	0
set$ro$flag:	db	0
check$disk:	db	0
flushed:	db	0
fcb$cks$valid:  db	0
;				MP/M VARIABLES  *
ENDIF
;
;	local variables for directory access
dptr:	ds	byte	;directory pointer 0,1,2,3
XDCNT:  DS	WORD	;EMPTY DIRECTORY DCNT
XDBLK:  DS	WORD	;EMPTY DIRECTORY BLOCK
dcnt:	ds	word	;directory counter 0,1,...,dirmax
DBLK:	DS	WORD	;DIRECTORY BLOCK INDEX
USER0$PASS:
	DS	BYTE	;SEARCH USER 0 PASS FLAG
;
;	local variables initialized by bdos at entry
;
resel:		ds	byte	;reselection flag
fcbdsk:		ds	byte	;disk named in fcb
COMP$FCB$CKS:	DS	BYTE	;COMPUTE FCB CHECKSUM FLAG
SEARCH$USER0:	DS	BYTE	;SEARCH USER 0 FOR FILE (OPEN)
MAKE$XFCB:	DB	BYTE	;MAKE & SEARCH XFCB FLAG
FIND$XFCB:	DB	BYTE	;SEARCH FIND XFCB FLAG
;
DF$PASSWORD:	DW	0,0,0,0
IF MPM
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
		dw	0,0,0,0
ENDIF
;
IF MPM
	ds	192
last:
	org	(((last-base)+255) AND 0ff00h) - 279

	;	BNKBDOS patch area

patch00000:
	dad b! mov a,m! ani 0fh
	cpi 9! jnc lret$eq$ff! mov a,m
	add a! add a! add a
	mvi h,0! mov l,a! lxi b,dfpassword
	dad b
	ret

patch0000:
	jnz dmset1
	call test$unlocked! jz dmset1
	jmp dmset05

patch000:
	cpi 15! rz
	call getrcnta! mvi m,128! ret ; rc = 128

patch0:
 	push b! call get$dir$ext! pop b! cmp c! rnc ; dir$ext > ext
	call test$unlocked! rnz
 	pop d! xthl! mov b,l! inr b! xthl! push d! rnz ; write operation
 	inr b! pop d! pop d! jmp set$lret1 ; reading beyond written extent

get$cmp$mode:			; return PD compatibility 
	push	h		; attribute in A
	lhld 	pdaddr
	lxi	d,1dh
	dad	d
	mov 	a,m
	pop	h
	ret

patch1:
	call	pack$sdcnt
	lxi 	h,attributes	; is open mode default?
	mov	a,m
	ral
	rnc			; no
	call	get$cmp$mode	; is comp att F1 = 1?
	ral
	rnc			; no
	mvi	m,20h		; set open mode to r/o
	ret

patch2:
	mov	b,a		; is open mode default?
	ral			
	jnc	patch21		; no
	call	get$cmp$mode	; is comp att F1 = 1
	ral
	jnc	patch$21	; no
	mvi	b,20h		; set open mode to r/o
patch21:
	mov	a,b
	sta	make$flag
	ret

patch3:
	ral			; is comp att F1 = 1
	mov	a,m
	jnc	patch31		; no
	ral
	ral
patch31:
	ral
	jnc	openx06		; jump if not default open mode
	ret

patch4:		
	mvi	m,0ffh		; mark lock list item as closed
	push	d
	call	reset$chksum$fcb
	pop	h
	shld	file$id		; save addr of lock list item
	lda	high$ext	; is open mode default?
	ora	a
	rnz			; no - delete lock list item
	lda	attributes	; has extended lock been requested?
	ral	
	ral
	rnc			; no - delete lock list item
	pop	h		; dont delete item
	jmp	close03

patch5:
	mov	b,a
	call 	get$cmp$mode	; does comp att F3 = 1?
	ani	20h
	mov	a,b
	jz	set$aret	; no - report close checksum error
	jmp	close000

patch6:
	call	get$cmp$mode	; if comp att F4 = 1 then
	ani	10h		; dont check FCB checksum
	jz	check$fcb
	ret

patch8:
	sta	curdsk		; push shiftr(tlog,curdsk) on stack
	lhld	tlog
	call	test$vector1
	pop	h
	push	a
	lda	curdsk
	pchl

patch9:
	pop	h		; checkdisk includes tlog
	pop	d
	ora	d
	sta	check$disk
	pchl

patch10:
	mvi	b,1		; verify no open files exist on curdsk
	call	search$olist
	jz	test$dir	; open files exist
	ret

patch11:
	call	index		; has block been allocated for this record?
	call	allocated
	lxi	h,1
	rz			; no
	pop	h		; yes - pop return address
	jmp	atran		; return to get$lock$add caller

patch12:
	dcr	a		; go to directory to verify block
	sta	dont$close	; has not been allocated to the file
	call	test$disk$fcb	; by another process
	lxi	h,1
	ret

patch13:
	mvi	b,3		; is file opened by another process?
	call	search$nlist
	jz	openx06		; yes - error
	mvi	b,3		; redo search in case delete is required
	call	search$olist
	xchg			; DE = address of lock list item
	pop	h
	xthl			; HL = address of open cnt field
	ret			; in lock list item

patch14:
	pop	b		; pop return address
	inx	h		; advance HL to opencnt high byte
	lda	attributes
	ana	m
	rm			; return if F5' = 1 and opencnt+1 = FF
	inr	m		; is file lock an extended lock?
	xchg
	jz	delete$item	; yes - delete lock item but dont
				; advance PDCNT for process
	push	b
	jmp	delete$item	; delete and advance PDCNT

patch15:
	call	check$write	; check for r/o disk
patch16:
	call	getatts		; get interface atts f5' - F8'
	sta	attributes	; save
	ret

free$root:	dw	$-$			;
open$root:	dw	0			;
lock$root:	dw	0			;
lock$max:	db	0			;
open$max:	db	0			;
;
;	bios access table
bios	equ	$		;base of the bios jump table
bootf	equ	bios		;cold boot function
wbootf	equ	bootf+3		;warm boot function
constf	equ	wbootf+3	;console status function
coninf	equ	constf+3	;console input function
conoutf	equ	coninf+3	;console output function
listf	equ	conoutf+3	;list output function
punchf	equ	listf+3		;punch output function
readerf	equ	punchf+3	;reader input function
homef	equ	readerf+3	;disk home function
seldskf	equ	homef+3		;select disk function
settrkf	equ	seldskf+3	;set track function
setsecf	equ	settrkf+3	;set sector function
setdmaf	equ	setsecf+3	;set dma function
readf	equ	setdmaf+3	;read disk function
writef	equ	readf+3		;write disk function
liststf	equ	writef+3	;list status function
sectran	equ	liststf+3	;sector translate
;
ENDIF
	end
 