;SYS2/ASM - LS-DOS 6.3
	TITLE	<SYS2 - LS-DOS 6.3>
;
; This SYS module performs the following functions:
; . OPENs an existing File or Device
; . INITs a new File
; . Checks availability of a specific drive
; . Hashes an 11-byte field (file name & ext)
; . Hashes an 8-byte field (password)
; . Renames a filespec/devspec
; . Gets the address of a device control block
;
CR	EQU	13
*LIST	OFF			;Get SYS0/EQU
*GET	SYS0/EQU:2
*LIST	ON
*GET	COPYCOM:3		;Copyright message
;
	ORG	1E00H
;
SYS2	AND	70H		;Strip all but entry
	RET	Z		;Back on zero entry
	CP	10H		;Check for OPEN
	JP	Z,OPEN
	CP	20H		;Check for INIT
	JP	Z,INIT
	CP	70H		;Check for rename
	JP	Z,RENAME
	CP	30H		;Get a DCB?
	JR	Z,GTDCB
	CP	40H		;Drive availability?
	JR	Z,CKDRV
	CP	60H		;Check password hash
	JR	Z,HASHPSWD
;
;	Routine to hash a file name
;
HASHNAME
	LD	B,11		;Init for 11 chars
	XOR	A		;Clear for start
HNAME1	XOR	(HL)		;Modulo 2 addition
	INC	HL		;Bump to next character
	RLCA			;Rotate bit structure
	DJNZ	HNAME1		;  & loop for field len
	OR	A		;Do not permit a zero
	JR	NZ,HNAME2	;  hash code
	INC	A
HNAME2	LD	(FILEHASH),A	;Stuff code for later
	RET
;
;	Hash a password
;
HASHPSWD
	LD	HL,7		;Hashing will be from
	ADD	HL,DE		;  right to left so
	EX	DE,HL		;  point to lo-order
	LD	HL,-1		;Init shift reg to 1's
	LD	B,8		;Init for 8-char string
HPSWD1	LD	A,(DE)		;P/u the next byte
	PUSH	DE		;  & save the pointer
	LD	D,A
	LD	E,H
	LD	A,L		;Modulo 2 add bits 0-2
	AND	7		;  to bits 4-6 of the
	RRCA			;  16-bit shift register
	RRCA
	RRCA
	XOR	L
	LD	L,A		;Shift shift-register
	LD	H,0		;  left by 4-bits to
	ADD	HL,HL		;  isolate bits 4-7
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	XOR	H		;Mod 2 add SR bits 4-7
	XOR	D		;Mod 2 add new byte
	LD	D,A		;Save tempy for hi-order
	LD	A,L
	ADD	HL,HL
	XOR	H
	XOR	E
	LD	E,A
	EX	DE,HL		;SR result to HL
	POP	DE		;P/u pointer to string
	DEC	DE		;  & point to next byte
	DJNZ	HPSWD1		;Loop for field length
	XOR	A
	RET
;
;	Routine to locate a Device Control Block
;
GETDCB	LD	E,(IX+1)	;P/u the 2-character
	LD	D,(IX+2)	;  device name
GTDCB	LD	HL,KIDCB$	;Point to 1st DCB
DEV1	PUSH	HL
	LD	A,L		;Point to device
	ADD	A,6		;  name field
	LD	L,A
	LD	A,(HL)		;P/u 1st char of name
	INC	L		;Point to 2nd char
	CP	E		;Compare 1st for match
	JR	NZ,DEV2		;No match? then loop
	LD	A,(HL)		;1st matches, does 2nd?
	CP	D
	JR	NZ,DEV2		;Loop if no match
	POP	HL		;Get start of DCB
	RET
DEV2	POP	AF		;Pop last DCB start
	INC	L		;Inc to start of next DCB
	JR	NZ,DEV1		;Bypass if not at end
;
;	Device not found in tables
;
	LD	A,8		;"device not available"
	OR	A
	RET
;
;	Check a drive for availability
;
CKDRV	PUSH	IY		;We use IY in disk I/O
	CALL	@GTDCT		;Get driver routine addr
	LD	A,(IY+0)	;P/u drive vector
	CP	0C3H		;Ck for enabled
	JP	NZ,CKDR5	;Bypass if disabled
	PUSH	HL
	PUSH	DE
	BIT	3,(IY+3)	;Test for HARD drive
	JR	NZ,CKDRV1A	;If so bypass range check
	LD	A,(IY+6)	;Make sure the current
	CP	(IY+5)		;  cylinder is in range
	JR	NC,CKDRV1	;Go if in range
	CALL	@RSTOR		;Restore drive
	JP	NZ,CKDR7A	;Go if error
;
CKDRV1	LD	D,(IY+5)	;P/u current track
	LD	E,0		;Set for sector 0
	CALL	@SEEK		;Set track info to FDC
	JP	NZ,CKDR7A	;Go if error
CKDRV1A	CALL	@RSLCT		;Wait until not busy
	JP	NZ,CKDR7A	;Not there - ret NZ
	BIT	3,(IY+3)	;If hard drive, bypass
	JR	NZ,CKDR2B	;  index pulse
	BIT	4,(IY+4)	;If "ALIEN" by pass
	JR	NZ,CKDR2B	;  test of index pulses
	IF	@MOD4
	LD	A,(FDDINT$)	;Check 'SMOOTH' state
	OR	A
	LD	A,09		;Set MSB of count down
	JR	Z,INTRON	;Go if not SMOOTH
	SRL	A		;Divide the count by two
	DI
	ENDIF
	IF	@MOD2
	LD	A,20
	ENDIF
INTRON	LD	(CDCNT+1),A	;Store in 'LD H' instruction
	LD	HL,0020H	;Set up count (short)
;
;	Test for diskette in drive & rotating
;
CKDR1	CALL	INDEX		;Test index pulse
	JR	NZ,CKDR1	;Jump on index
	BIT	7,(IY+4)	;Check CKDRV inhibit bit
	JR	NZ,CKDR2B	;If on skip index test
CDCNT	LD	H,00H		;CKDRV counter (long)
				;Count set from above
CKDR2	CALL	INDEX		;Test index pulse
	JR	Z,CKDR2		;Jump on no index
	IF	@MOD4
	EI			;OK for INTs now
	ENDIF
	LD	HL,0020H	;Index off wait (short)
CKDR2A	CALL	INDEX
	JR	NZ,CKDR2A	;Jump on index
;
;	Diskette is rotating
;
CKDR2B	PUSH	AF		;Save FDC status
	CALL	@DIRCYL		;Get directory track in D
	LD	HL,SBUFF$	;Point to HIT buffer
	LD	E,L		;Sector 0 for GAT
	CALL	@RDSSC		;Read the GAT
	JR	NZ,CKDR7	;Jump on error
;
;	Update YFLAG$ for year type
;
	LD	DE,(SBUFF$+0CCH)	;Gat type byte
	LD	A,C		;Drive # to A
	RLCA			;Rotate it into posn
	RLCA			;For SET x,(hl)
	RLCA			; opcode
	OR	0C6H		;For SET opcode
	BIT	3,D		;Is disk new type?
	JR	NZ,FIXBIT	;Go if so
	XOR	40H		;Else make RES opcode
FIXBIT	LD	(BIT1),A	;Save opcode
	LD	HL,YFLAG$	;Pt to year type flag
	DB	0CBH		;Either SET or RES
BIT1	DB	0
	BIT	3,(IY+3)	;If rigid drive,
	JR	NZ,CKDR3	; bypass the rest
	LD	A,22H		;Add offset
	ADD	A,E
	LD	(IY+6),A	;Max track # to DCT
	RES	5,(IY+4)	;Set to side 0
	BIT	5,D		;Test double sided
	JR	Z,CKDR3		;Jump if only single
	SET	5,(IY+4)	;Set for side 2
CKDR3	POP	AF		;Recover FDC status
CKDR3A	RLCA			;Shift write prot to 7
	OR	(IY+3)		;Merge Soft WP bit
	AND	80H		;Strip all but 7
	LD	(OPNCB9+1),A	;Save WP status for OPNCB
	ADD	A,A		;Write prot to carry flg
;
CKDR4	EQU	$
	EI
	POP	DE
	POP	HL
CKDR5	POP	IY
	RET
INDEX	LD	A,H
	OR	L
	JR	Z,CKDR7
	DEC	HL
	CALL	@RSLCT		;Check for index pulse
	BIT	1,A		;Test index
	RET
CKDR7	POP	AF
;
CKDR7A	OR	A		;Set NZ ret
	JR	CKDR4		;  and exit
;
;	OPEN a device
;	Device Control Blocks are from X'0208' - X'02FF'
;
DEVOPEN	CALL	GETDCB		;Find the DCB named
	RET	NZ		;  in the IX pointer
;
;	Found the needed Device Control Block
;
DEV4	LD	B,H		;Xfer dcb vector to BC
	LD	C,L
	PUSH	IX		;User DCB to HL
	POP	HL
	LD	(HL),10H	;Show routed
	INC	HL
	LD	(HL),C		;Stuff dcb vector
	INC	HL
	LD	(HL),B
	INC	HL
	XOR	A		;Zero next 3 bytes
	LD	(HL),A
	INC	HL
	LD	(HL),A
	INC	HL
	LD	(HL),A
	INC	HL
	LD	(HL),E		;Stuff dcb name
	INC	HL
	LD	(HL),D
	RET
;
;
;	OPEN a file
;	 HL <= the address of a 256-byte buffer
;	 DE <= the address of a 32-byte FCB
;	  B <= the logical record length (LREC)
;
OPEN	CALL	LNKFCB@		;Set up link to dcb
OPEN1	LD	A,(SFLAG$)	;Stuff current sysflag
	LD	(OPEN14+1),A	;  to check later then
	AND	0F8H		;  remove bits 0, 1 & 2
	LD	(SFLAG$),A
	LD	A,(IX+0)
	CP	'*'		;If name starts with '*',
	JR	Z,DEVOPEN	;  it is a device spec
	LD	A,B		;P/u Lrl requested
	LD	(LREC$),A
	LD	(OPNCB4+1),HL	;Stuff disk I/O buffer
	PUSH	IX		;Transfer the filespec
	POP	HL		;  into the system
	CALL	XFRSPEC		;  buffer area
	RET	NZ		;Return if bad name
	LD	HL,NAME$EXT	;Point to name/ext field
	CALL	HASHNAME	;  & hash it (11 chars)
	LD	DE,PSWDBUF	;Point to the password
	CALL	HASHPSWD	;  & hash it
	LD	(PW$HASH1),HL	;Stuff owner pswd
	LD	(PW$HASH2),HL	;Stuff user pswd
OPEN2	LD	A,0		;P/u drive <FF-07>
	LD	C,A
	INC	A		;Jump if :d entered
	JR	NZ,OPEN3
	LD	C,A
OPEN3	CALL	CKDRV		;Drive available?
	JR	NZ,OPEN6	;Jump if not
	CALL	@HITRD		;Get hash index table
	RET	NZ		;Return if read error
;
;	Compare hashed filename/ext with each entry
;	in the HIT to see if file is on this drive
;
OPEN4	LD	A,(HL)		;Bypass HIT entry if
	OR	A		;  unused
	JR	Z,OPEN5
	PUSH	HL		;Not vacant
	LD	HL,FILEHASH	;Point to DEC
	CP	(HL)		;Compare with HIT entry
	POP	HL
	JR	Z,OPEN9		;Jump if a match else
OPEN5	INC	L		;  bump to next entry
	JR	NZ,OPEN4	;Loop until 256 bytes
;
;	File not on this drive
;
OPEN6	CALL	TESTDRV		;Bump drive if we can
	JR	C,OPEN3		;Loop if another to test
OPEN7	LD	A,24		;File not found error
	OR	A
	RET
TESTDRV	LD	A,(OPEN2+1)	;If drive still X'FF',
	INC	A		;  then advance to next
	OR	A		;Reset Carry for ret w/o
	RET	NZ		;  affecting Z/NZ result
	INC	C		;Bump drive counter
	LD	A,C
	CP	8		;Loop end, 8 max
	RET
;
;	Although the HIT entry matched, the filename/ext
;	did not (due to a collision). Continue to scan
;	the rest of the hash index table.
;
OPEN8	POP	BC		;Remove ret address and
	POP	HL		;  excess registers
	POP	BC
	CALL	@HITRD		;Re-read the hit
	POP	HL
	RET	NZ		;Go on i/o error
	JR	OPEN5
;
;	The hashed name matches, read the directory
;
OPEN9	PUSH	HL
	PUSH	BC
	LD	B,L		;Set up the DEC
	CALL	@DIRRD
	JR	Z,OPEN10	;Jump if no error
	POP	BC		;  else pop returns
	POP	HL
	RET			;  & exit
;
;	Verify that directory entry is this file
;
OPEN10	PUSH	HL
	PUSH	BC		;Save drive (reg C)
;
;	If bit 7 is set, it denotes an extended
;	directory entry which does not include
;	the filename. Go to next HIT entry if set
;
	BIT	7,(HL)		;Test for FXDE
	JR	NZ,OPEN8	;Jump if extended
	BIT	4,(HL)		;If DIR record spare,
	JR	Z,OPEN8		;  continue to search
	LD	A,5		;Point to filename/ext
	ADD	A,L		;  field in directory
	LD	L,A
	LD	DE,NAME$EXT	;Point to entered name
	LD	B,11		;Init to check 11 chars
OPEN11	LD	A,(DE)		;Verify a match
	CP	(HL)		;  or no match
	JR	NZ,OPEN8	;Go to next HIT entry
	INC	HL		;  if no match; else bump
	INC	DE		;  pointers & loop
	DJNZ	OPEN11
	POP	BC		;Matches! get drive #
	LD	A,C		;  & stuff it
	LD	(OPEN2+1),A
	POP	HL
	POP	AF
	POP	AF
	PUSH	BC		;Save DEC and drive
	PUSH	HL		;Save ptr to dir record
	LD	A,(HL)		;P/u 1st byte of dir rec
	LD	(DIR$INIT),A	;Stuff it
	AND	7		;Strip all but protection
	LD	C,A
	LD	B,0
	LD	A,16		;Point to update password
	ADD	A,L
	LD	L,A
	LD	DE,(PW$HASH2)	;P/u password hash
	LD	A,(HL)		;P/u owner pswd lo order
	INC	HL
	PUSH	HL
	LD	H,(HL)		;P/u owner pswd hi order
	LD	L,A
	LD	A,(NFLAG$)	;P/u NFLAG$
	BIT	7,A		;Check network active bit
	JR	Z,USEPWD
	LD	D,H
	LD	E,L
USEPWD	XOR	A		;Compare password entry
	SBC	HL,DE		;  with owner password
	POP	HL
WASMAT	JR	Z,OPEN16	;Grant access if match
	LD	A,C		;Recover protection
	CP	7		;Abort if "no access"
	JR	NZ,OPEN13	;Have access if not "no"
;
;	File is password protected - abort
;
OPEN12	POP	HL
	POP	BC
	LD	A,25		;"file access denied"
	OR	A
	RET
;
;	Check if prot is exec only
;
OPEN13	LD	A,C
	CP	6		;Check for EXEC ONLY
	JR	NZ,OPEN16	;Jump if not
OPEN14	LD	B,0		;P/u SFLAG$ entry state
	BIT	2,B		;Did RUN request open?
	JR	Z,OPEN15	;Bypass if NOT from RUN
	LD	HL,SFLAG$
	SET	1,(HL)		;Show RUN & EXEC file
	LD	A,5		;Set read access for now
OPEN15	LD	HL,SET@EXEC	;Set RST vector to turn
	LD	(HL),0C9H	;  off DEBUG
OPEN16	LD	(OPNCB1+1),A	;Stuff access level
	POP	HL		;Ptr to direc record
	POP	BC		;P/u DEC and drive
;
;	Routine to open up the fcb from the directory
;	HL => directory record in SBUFF$
;	BC => DEC and drive used for directory read/write
;	IX => pointer to File Control Block
;
OPNCB	PUSH	IY		;Save IY
	PUSH	HL		;Transfer direc record
	POP	IY		;  ptr to IY
	PUSH	BC		;Save DEC and drive
	CALL	OPNCB0		;Create the opened FCB
	POP	BC
	LD	HL,OPEN14+1	;If from LOAD, don't do
	BIT	0,(HL)		;  any further checks
	JR	Z,OPNEX1
	XOR	A
OPNEX	POP	IY
	RET
OPNEX1	BIT	5,(IY+1)	;If file already open
	JR	Z,OPNCB8	;  then set read-only
	POP	IY		;  & return "file open...
OPNEX2	LD	A,(IX+1)	;P/u current attributes
	AND	0F8H		;Mask off current prot
	OR	5		;  & replace with READ
	LD	(IX+1),A	;Reset access to READ
	LD	A,41		;Set "file already open"
	RET
;
;	If access level is > read, set file open flag in
;	the directory & note close authority in the FCB.
;
OPNCB8	LD	A,(IX+1)	;P/u FCB access level
	AND	7		;Mask off other junk
	CP	5		;Ck READ, EXEC, NONE
	JR	NC,OPNCB10	;Go if one of the above
OPNCB9	LD	A,0		;P/u CKDRV status
	RLCA			;Was drive write prot?
	JR	C,FRCREAD	;CF = WP
	SET	5,(IY+1)	;Set file open in direc
	LD	A,(NFLAG$)	;P/u Nflag
	BIT	0,A		;Check for function ON
	CALL	NZ,@DIRWR	;Write the directory
	JR	NZ,OPNEX
	SET	6,(IX+0)	;Set close authority
;
;	Ck if passed LRL matches directory
;
OPNCB10	LD	A,(IX+9)	;P/u LRL from FCB &
	CP	(IY+4)		;  compare with directory
	LD	A,42		;Init "LRL open fault
	JR	OPNEX
;
;	Disk write protected - Change access to READ
;
FRCREAD	CALL	OPNEX2		;Change access to READ
	JR	OPNCB10
;
;	This routine creates the open file control block
;
OPNCB0	EX	DE,HL
	PUSH	IX		;Transfer fcb pointer
	POP	HL
	LD	A,(DE)		;Get DIR+0
	AND	20H		;Keep "PDS" bit & show
	OR	80H		;  FCB as open
	LD	(HL),A		;Shove into FCB+0
	INC	HL
	LD	A,(LREC$)	;P/u lrl
	OR	A		;Test for 0 (256)
OPNCB1	LD	A,0		;Now start byte 2 with
	JR	Z,OPNCB2	;  that set by "OPEN16"
	OR	80H		;Show sector or byte I/O
OPNCB2	OR	20H		;Show buffer is empty
;
;	Set bit 3 if filespec ended in an
;	exclamation point. This causes the
;	directory to be updated on every
;	file write where the EOF is extended
;
OPNCB3	OR	0
	LD	(HL),A		;Init FCB+1
	INC	HL
	XOR	A
	LD	(HL),A		;Init FCB+2 with 0
	INC	HL
	PUSH	DE		;Put address of disk I/O
OPNCB4	LD	DE,0		;  buf into FCB+3 & FCB+4
	LD	(HL),E
	INC	HL
	LD	(HL),D
	INC	HL
	POP	DE		;FCB+5 with 0 for
	LD	(HL),A		;  lo order next
	INC	HL
	LD	(HL),C		;FCB+6 with drive
	INC	HL
	LD	(HL),B		;FCB+7 with DEC
	INC	HL
	INC	DE		;Point to DIR EOF byte
	INC	DE
	INC	DE
	LD	A,(DE)		;P/u DIR lo order EOF
	LD	(HL),A		;  & stuff into FCB+8
	INC	HL
	INC	DE
	LD	A,(LREC$)	;P/u lrl & stuff
	LD	(HL),A		;  into FCB+9
	INC	HL
	XOR	A
	LD	(HL),A		;Init FCB+10 & FCB+11
	INC	HL		;  with zero for NRN
	LD	(HL),A
	INC	HL
	SET	4,E		;Point to file EOF
	LD	BC,2		;Move ERN
	EX	DE,HL
	LDIR			;  and zero BC reg
	EX	DE,HL
	LD	A,5		;Max 5 extents
	PUSH	AF
OPNCB5	LD	A,(DE)		;Move starting track
	LD	(HL),A
	INC	HL
	INC	DE
	LD	A,(DE)		;Move grans & offset
	LD	(HL),A
	INC	HL
	AND	1FH		;Strip out grans
	INC	A		;Bump for zero offset
;
;	Add reg A to reg pair BC
;
	ADD	A,C		;Add previous count
	LD	C,A		;Update C
	JR	NC,$+3		;Go if no carry to B
	INC	B
	POP	AF		;Recover counter
	DEC	A		;Decrement loop
	RET	Z		;Done if moved in 5
	PUSH	AF
	INC	DE
	LD	A,(DE)		;Test for end of extents
	CP	0FEH		;Extent in use?
	JR	NC,OPNCB6	;Jump if not
	LD	(HL),C		;Stuff # of cumulative
	INC	HL		;  grans to this
	LD	(HL),B		;  allocation into FCB
	INC	HL
	JR	OPNCB5		;Loop for next
;
;	Unused extents - put X'FFFF' in remaining fields
;
OPNCB6	POP	AF		;Recover counter
	RLCA			;Make times 4 and
	RLCA			;  fill remaining
	LD	B,A		;  extent bytes with
OPNCB7	LD	(HL),0FFH	;  0FFH
	INC	HL
	DJNZ	OPNCB7
	RET
;
;	INIT a file
;	 HL => the address of a 256-byte buffer
;	 DE => the address of a 32-byte FCB
;	  B => the logical record length (LREC)
;
INIT	CALL	LNKFCB@		;Link to FCB
	LD	(OPNCB1+1),A	;Start FCB+1 with 0
	LD	A,10H		;Init for no prot on
	LD	(INIT0),A	;  a new file
	PUSH	HL
	LD	HL,SFLAG$	;Reset called by RUN bit
	RES	2,(HL)
	POP	HL
	CALL	OPEN1		;Can we "OPEN" the file?
	RET	Z		;Return if file existing
	CP	24		;Return if error not
	RET	NZ		;  "file not found"
	LD	A,10H		;Set dir rec to show
INIT0	EQU	$-1
	LD	(DIR$INIT),A	;  assigned
	LD	A,(OPEN2+1)	;P/u the drive entry
	LD	C,A
	INC	A		;Jump if a drive entry
	PUSH	AF
	JR	NZ,INIT1	;  was made
	LD	C,A
INIT1	POP	AF		;Stack integrity
	CALL	CKDRV		;Is this drive available?
	JR	NZ,INIT2	;Jump if not
	JR	C,INIT2		;  or if write protected
	CALL	@HITRD		;Read hash index table
	RET	NZ		;Return if read error
	CALL	SPRHIT		;Locate spare entry
	JR	Z,INIT4		;Jump if space
	XOR	A		;Set status of CKDRV=Z
INIT2	PUSH	AF		;Save last CKDRV status
	CALL	TESTDRV
	JR	C,INIT1		;Loop if not at end
	LD	A,(OPEN2+1)	;If drivespec not entered
	INC	A		;  then "directory full
	JR	NZ,INIT2A
	POP	AF		;Stack integrity
	JR	ERR26
INIT2A	POP	AF		;  else if no drive then
	JR	NZ,ERR32	;  "illegal drive...
	JR	C,ERR15		;  else if write protect
ERR26	LD	A,26		;  "directory space full"
	DB	1
ERR15	LD	A,15		;"write protect...
	DB	1
ERR32	LD	A,32		;"Illegal drive...
	OR	A
	RET
;
;	Found a spare HIT entry position
;
INIT4	LD	B,L		;Save DEC
	LD	A,(FILEHASH)	;P/u filespec hash
	LD	(HL),A		;  & store in HIT
	CALL	@HITWR		;Write updated HIT
	CALL	Z,@DIRRD	;Read that dir record
	RET	NZ		;Return if read error
	PUSH	HL
	PUSH	BC
	EX	DE,HL
	LD	BC,5		;Move 1st 5 bytes into
	LD	HL,DIR$INIT	;  directory record
	LDIR
	LD	C,17		;Move filename & password
	LD	HL,NAME$EXT	;  info into directory
	LDIR
	EX	DE,HL
	LD	B,10		;Put X'FFFF' into 5 ext's
INIT5	CALL	OPNCB7		;4 for the ext's & 1 for
	POP	BC		;  staarting info
	CALL	@DIRWR		;Write updated directory
	POP	HL
	RET	NZ		;Return if write error
	CALL	OPNCB		;  else open the fcb
	SCF			;Indicate new file
	RET
;
;	Xfer the file spec to system buffer area
;
XFRSPEC	LD	B,19
	LD	DE,PSWDBUF
	LD	A,20H		;Blank out the filename
XSPEC1	LD	(DE),A		;  field in system buffer
	INC	DE
	DJNZ	XSPEC1
	LD	A,0FFH		;Set drive to X'FF' for
	LD	(OPEN2+1),A	;  checking user entry
	LD	E,NAME$EXT&0FFH	;Xfer file name
	IF	@BLD631
	CALL	XSPEC8B		;<631>
	ELSE
	CALL	XSPEC8
	ENDIF
	LD	C,A
	LD	A,B
	SUB	8		;Any valid chars found?
	JR	NZ,XSPEC3	;Jump if valid name
;
;	Filename was invalid format
;
	OR	19		;"illegal file name"
	RET
;
;	Continue to check file spec
;
XSPEC3	LD	A,C
	CP	'/'		;Ext entered?
	LD	E,FILE$EXT&0FFH
	LD	B,3
	CALL	Z,XSPEC8A	;Xfer the ext
	CP	'.'		;Password entered?
	LD	E,PSWDBUF&0FFH
	IF	@BLD631
	CALL	Z,XSPEC8	;<631>Xfer the password
	ELSE
	JR	NZ,XSPEC3A
	LD	A,17H		;If a password, set init
	LD	(INIT0),A	; to show "no" prot level
	CALL	XSPEC8		;Xfer the password
	ENDIF
XSPEC3A	CP	':'		;Drive entered?
	JR	NZ,XSPEC6
	LD	A,(HL)		;P/u drive #
	SUB	'0'		;Convert to binary
	LD	(OPEN2+1),A	;Stuff drive #
	AND	0F8H		;Must be <0-7>
	LD	A,32		;"illegal drive #"
	RET	NZ		;Return error if out
	INC	HL		;  of range
	LD	A,(HL)		;Does filespec end in
XSPEC6	SUB	21H		;  exclamation point?
	LD	A,8		;Init to set bit 3 of
	JR	Z,XSPEC7	;  FCB+1 & jump if "!"
	XOR	A		;  else reset if not
XSPEC7	LD	(OPNCB3+1),A
	RET
;
;
;
XSPEC8
	IF	@BLD631
	LD	A,17H		;If a password, set init
	LD	(INIT0),A	; to show "no" prot level
XSPEC8B
	ENDIF
	LD	B,8
XSPEC8A	LD	A,(HL)		;P/u a filespec character
	INC	HL		;  & 1st test for A-Z
	JR	XSPEC10
XSPEC9	LD	A,(HL)		;P/u a filespec character
	INC	HL		;Advance to next one
	CP	'0'		;Check for 0-9
	RET	C
	CP	'9'+1
	JR	C,XSPEC11
XSPEC10	CP	'A'		;Check for A-Z
	RET	C
	CP	'Z'+1
	RET	NC
XSPEC11	LD	(DE),A		;Character is valid
	INC	DE		;Advance to next one
	DJNZ	XSPEC9		;  & loop
	LD	A,(HL)		;P/u following character
	INC	HL
	RET
;
;	Routine to find a spare HIT entry
;
SPRHIT	LD	A,28H		;Start scan here
	CALL	@SCNHIT		;Call to SYSRES
	RET
;
;	Routine to rename a filespec/devspec
;
REN0	LD	A,18H
	LD	(WASMAT),A
	OR	A		;Denote "file not in dir
	RET
RENAME	CALL	LNKFCB@		;Save regs & link to IX
	LD	A,(IX+0)	;If a device, use the
	SUB	'*'		;  "device" routine
	JR	Z,RENDEV
	CP	'R'!80H-'*'	;Special open condition?
	JR	Z,REN0		;Go if so
	PUSH	HL		;Save new pointer
	LD	HL,SFLAG$	;Set don't test flags
	SET	0,(HL)
	CALL	OPEN1		;Open the "old" spec
	POP	HL
	RET	NZ		;Exit on error
	LD	A,(IX+1)	;Make sure user has
	AND	7		;  permission to rename
	CP	3
	JR	C,REN1
	LD	A,25H		;"Illegal access...
	OR	A
	RET
;
;	User has access to rename - locate drivespec
;
REN1	PUSH	HL		;Save start
REN2	LD	A,(HL)		;P/u char of new spec
	INC	HL
	CP	CR
	JR	Z,REN3		;Go on ENTER
	CP	3
	JR	Z,REN3		;Go on ETX
	CP	':'
	JR	NZ,REN2		;Loop on colon
REN3	DEC	HL		;Backup to where the
	LD	(HL),':'	;  colon should go
	INC	HL		;  & force the drivespec
	LD	A,(IX+6)	;  to the same as "old"
	LD	C,A		;Keep drive spec in C
	AND	7
	ADD	A,'0'		;Make it an ASCII digit
	LD	(HL),A
	INC	HL
	LD	(HL),CR
	LD	B,(IX+7)	;Get DEC
	POP	IX		;Put "new" FCB into IX
	PUSH	BC		;  & save DEC & drive
	LD	HL,SFLAG$	;Set don't test flags
	SET	0,(HL)
	CALL	OPEN1		;Open the "new" spec
	POP	BC
	JR	NZ,REN4		;Should error here
REN3A	LD	A,19		;  or else return
	OR	A		;  if "new" is existing
	RET			;  & we opened it
REN4	CP	24		;If not "file not found"
	RET	NZ		;  then is error
	CALL	@DIRRD		;Read "old's" directory
	RET	NZ
	PUSH	BC		;Save drive spec
	LD	D,H		;Xfer buffer hi
	LD	A,L
	ADD	A,5		;Pt to filename field
	LD	E,A		;Set buffer lo
	LD	HL,NAME$EXT	;Point to where the
	LD	BC,11		;  new name is stored
	LDIR			;Move in new name
	POP	BC
	CALL	@DIRWR		;Rewrite the directory
	CALL	Z,@HITRD	;Read the HIT
	RET	NZ
	LD	D,H		;Set the buffer hi
	LD	E,B		;Set the exact HIT lo
	LD	HL,NAME$EXT	;This doesn't change C
	CALL	HASHNAME	;Hash the new name
	LD	(DE),A		;Stuff code into HIT
	JP	@HITWR		;Rewrite & exit
;
;	Routine to rename a device
;
RENDEV	PUSH	HL		;Save new pointer
	CALL	GETDCB		;Locate old in tables
	POP	IX		;Recover pointer to "new"
	RET	NZ		;Back if not in tables
	LD	A,L
	CP	DCBKL$		;Ck if protected device
	LD	A,40		;"Protected system device
	RET	C
	LD	A,(IX+0)	;"new" must be a device
	CP	'*'
	JR	NZ,REN3A	;"illegal file name...
	PUSH	HL		;Save address of "old"
	CALL	GETDCB		;Ck if "new" is unused
	POP	HL		;Rcvr address of "old"
	JR	Z,REN3A
	LD	BC,6		;Point to name field
	ADD	HL,BC		;  of "old" device
	LD	(HL),E		;Stuff new name into
	INC	HL		;  device control block
	LD	(HL),D
	XOR	A		;Set Z-flag
	RET
;
;	Parameter storage area
;
	IF	@BLD631
	ELSE
FILEHASH	DS	1
	ENDIF
PSWDBUF		DS	8
NAME$EXT	DS	8
FILE$EXT	DS	3
PW$HASH1	DS	2
PW$HASH2	DS	2
	DW	0		;ERN init
DIR$INIT	DB	0,0,0,0
	IF	@BLD631
LAST	EQU	$
LREC$	DS	1			;<631>
FILEHASH	DS	1		;<631>
	ELSE
LREC$	DS	1
LAST	EQU	$
	ENDIF
	IFGT	$,DIRBUF$
	ERR	'Module too big'
	ENDIF
	ORG	MAXCOR$-2
	DW	LAST-SYS2	;Overlay length
;
	END	SYS2
