;LBATTRIB/ASM - ATTRIB Command
	TITLE	<ATTRIB - LS-DOS 6.3>
;
CR	EQU	13
BLNKMPW	EQU	4296H
PASSWORD	EQU	42E0H
;
*GET	SVCMAC:3		;SVC Macro equivalents
;
	ORG	2400H
;
ATTRIB
	LD	(SAVESP+1),SP	;Save stack pointer
	CALL	ATTRIB1		;Call attrib code
	LD	HL,0		;Set no error
	JR	SAVESP		;P/u stack & return
;
;	I/O Error Handling
;
IOERR	CP	63		;Extended error?
	JR	Z,EXTERR
	LD	L,A		;Error # to HL
	LD	H,0
	OR	0C0H		;Abbrev & return
	LD	C,A
	@@ERROR
	JR	SAVESP		;P/u Stack & RETurn
;
;	Internal Error Message Handling
;
ABORT	LD	HL,ABORT$	;"Command aborted
	DB	0DDH
SPCREQ	LD	HL,SPCREQ$	;"File spec required
	DB	0DDH
NOTDUN	LD	HL,NOTDUN$	;"Specifications req.
	DB	0DDH
ATBERR	LD	HL,ATBERR$	;"Attr. specification error
EXTERR	@@LOGOT
	LD	HL,-1		;Set abort error
SAVESP	LD	SP,$-$		;Reload stack pointer
	@@CKBRKC		;Clean up <BREAK>
	RET
;
;	ATTRIB1 - Set Attributes of a file/disk
;
ATTRIB1
	LD	DE,FCB		;Check filespec or drive #
	@@FSPEC
	JP	NZ,PROT		;Assume drive # if error
	LD	A,(DE)		;Cannot be a device
	CP	'*'
	JP	Z,SPCREQ
	@@FLAGS			;Get flag table pointer
	PUSH	HL		;Save cmdline ptr
	LD	HL,BUFFER	;Use local buffer
	LD	B,L		;Open the file
	SET	0,(IY+'S'-'A')	;Don't set file open bit
	@@OPEN
	POP	HL		;Rcvr cmdline ptr
	JP	NZ,IOERR	;Jump if non-existant
	LD	A,(FCB+1)	;P/u protection
	AND	7		;Mask other bits
	LD	A,25H		;Init for access denied
	JP	NZ,IOERR	;Jump if no can do
	XOR	A
	LD	(PRMCOD+1),A	;Init prot to 0
;
;	Convert command line to upper case
;
	PUSH	HL		;Save cmdline ptr
ATT0	LD	A,(HL)		;  & cvrt lc to UC
	CP	CR
	JR	Z,ATT02		;Done if CR
	CP	3		;  ETX
	JR	Z,ATT02
	CP	'a'		;Not lc?
	JR	C,ATT01
	CP	'z'+1
	JR	NC,ATT01
	RES	5,(HL)		;<a-z> to <A-Z>
ATT01	INC	HL		;Bump to next char
	JR	ATT0		;Loop
;
ATT02	POP	HL		;Rcvr orig cmdline ptr
;
;	Scan command line for parameters
;
ATT1	LD	A,(HL)		;Scan for start of parm
	CP	'('		;There yet?
	JR	Z,ATT2		;Jump if so
	CP	' '		;Ignore spaces
	JR	NZ,ATT3		;Assume parm on dif char
	INC	HL
	JR	ATT1
ATT2	INC	HL		;Bump past '('
	LD	A,(HL)
ATT3	CP	'I'		;Ck for INV
	JR	Z,DOINV
	CP	'V'		;Ck for VIS
	JR	Z,DOVIS
	CP	'O'		;Ck for OWNER
	JP	Z,DOOWN
	CP	'P'		;Ck for PROT
	JP	NZ,ATBERR	;Err if none of the above
;
;	Process PROT=parm
;
	CALL	PRSFLD		;Parse field
	JP	Z,ATBERR	;Error if end of line
	PUSH	HL		;Save ptr to next char
	LD	B,8		;Init for 8 prots
	LD	DE,(PSWDBUF)	;P/u 1st 2 chars
	LD	HL,PROTS$	;Pt to various prots
DOPR01	LD	A,(HL)		;P/u 1st prot char
	INC	HL		;Bump pointer
	CP	E		;Does 1st match?
	CALL	Z,DOPR02	;Check 2nd if 1st OK
	INC	HL		;Bump to next
	DJNZ	DOPR01		;Loop for all 8
	POP	HL		;Stack integrity
	JP	ATBERR		;Abort if no match
;
;	Check 2nd prot= char for match
;
DOPR02	LD	A,(HL)		;P/u 2nd table char
	CP	D		;Match user's entry?
	RET	NZ		;Go back if not
	POP	AF		;Pop the ret addr
	LD	A,B		;Calculate which prot was
	DEC	A		;  entered by the user
	JR	Z,DOPR03	;Jump on PROT=FU
	CP	5		;REname, REad, REmove?
	JR	NZ,DOPR03	;Go if none of the above
	LD	A,(PSWDBUF+2)	;P/u user's 3rd char
	CP	'N'		;Was it 'N'?
	LD	A,2		;Init for REname
	JR	Z,DOPR03	;Go if REName
	DEC	A		;Else init to REMove
	CP	'M'		;  & test entry
	JR	Z,DOPR03
	LD	A,5		;  else assume REAd
DOPR03	LD	(PROTLVL+1),A	;Stuff protection level
	POP	HL		;Rcvr INBUF$ pointer
	LD	B,1		;Init to show PROT given
DOPR04	LD	A,(HL)		;P/u next parm
	CP	'"'		;Closing quote on last?
	JR	NZ,DOPR05	;Go if something else
	INC	HL		;Ignore closing quote
DOPR05	LD	A,(PRMCOD+1)	;P/u parm test bits
	OR	B		;Merge PROT entered
	LD	(PRMCOD+1),A	;Restuff parm test bits
	LD	A,(HL)		;P/u next char
	CP	CR		;End of line?
	JR	Z,$+4		;Go on end-of-line
	CP	')'		;End of parms?
	JP	Z,UPDDIR	;Go on end of parms
	CP	','		;More parms?
	JR	Z,ATT2		;Loop on more parms
	JP	ATBERR		;Exit on wrong char
;
;	Process INV parm
;
DOINV	CALL	PRSFLD		;Parse parm
	JP	NZ,ATBERR	;Go on parm error
	LD	A,(IVCOD+1)	;Set bit 3 to indicate
	OR	8		;  that INV given
	LD	(IVCOD+1),A
	LD	B,8		;Show vis/inv done
	JR	DOPR05		;Merge w/prev. parms
;
;	Process VIS parm
;
DOVIS	CALL	PRSFLD		;Parse parm
	JP	NZ,ATBERR	;Quit on parm error
	LD	A,(IVCOD+1)	;Strip bit 3
	AND	0F7H
	LD	(IVCOD+1),A
	LD	B,8		;Show vis/inv done
	JR	DOPR05		;Merge w/prev. parms
;
;	Process OWNER parm
;
DOOWN	CALL	PRSFLD		;Parse parm
	JP	Z,ATBERR	;Quit on parm error
	PUSH	HL		;Save cmdline ptr
	LD	DE,PSWDBUF
	CALL	DOHASH		;Hash the password
	LD	(HASHBUF),HL
	POP	HL
	LD	B,4		;Show OWNER done
	JR	DOPR04		;Merge w/prev. parms
;
;	Transfer the field, 1st char alpha
;
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		;Quit if < 0
	CP	'9'+1
	JR	C,XSPEC11	;Go if numeric
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			;Go home
;
;	Parse rest of parm (ignore until separator)
;
PRSFLD	INC	HL
	LD	A,(HL)		;Get next char
	CP	CR		;Ret on end of line
	RET	Z
	CP	')'		;Ret on closing paren
	RET	Z
	CP	','		;Ret on separator
	RET	Z
	CP	'='		;Assignment operator?
	JR	NZ,PRSFLD	;Loop if not
	INC	HL
	LD	A,(HL)
	CP	'"'		;Is quote there?
	JR	NZ,$+3
	INC	HL		;Bypass the quote
	LD	DE,PSWDBUF
	LD	B,8
	PUSH	DE
	PUSH	BC
	LD	A,' '		;Space out the buffer
PRSF01	LD	(DE),A
	INC	DE
	DJNZ	PRSF01
	POP	BC
	POP	DE
	CALL	XSPEC8A		;Transfer the spec
	DEC	HL
	OR	1		;Show got a parm
	RET
;
;	Routine updates file's directory data
;
UPDDIR	LD	BC,(FCB+6)	;P/u drive & DEC
	@@DIRRD			;Read its directory
	JP	NZ,IOERR	;Quit on read error
	LD	A,(HL)		;P/u attributes byte
PRMCOD	LD	D,$-$		;P/u parm test bits
	BIT	0,D		;Was Prot entered?
	JR	Z,UPDIR1	;Jump if not
	AND	0F8H		;Rmv prot level
PROTLVL	OR	0		;Merge new prot level
UPDIR1	BIT	3,D		;Was Inv or Vis entered?
	JR	Z,UPDIR2	;Bypass if not
	AND	0F7H		;Remove any vis/inv
IVCOD	OR	0		;Merge new inv/vis
UPDIR2	LD	(HL),A		;  & update dir rec
	LD	A,L		;Pt to owner pswd
	ADD	A,16
	LD	L,A
	BIT	2,D		;Was OWN parm entered?
	JR	Z,UPDIR4	;Bypass if not
	LD	A,(HASHBUF)	;Xfer new hashed pswd
	LD	(HL),A		;  into the directory
	LD	A,(HASHBUF+1)	;  OWNER psw position
	INC	HL
	LD	(HL),A
UPDIR4	@@DIRWR			;Write dir back to disk
	JP	NZ,IOERR	;Abort on write error
	RET			;Done - return
;
;	Change attributes of entire disk
;
PROT	LD	C,0		;Init for drive 0
	DEC	HL		;Backup to separator
	LD	A,(HL)		;Ck for drive entered
	CP	':'		;Colon indicator?
	JR	NZ,PROT01	;Bypass if not
	INC	HL		;Point to drive #
;
;	Is the drivespec legal (0-7) ?
;
	LD	A,(HL)		;P/u drive
	SUB	'0'		;Cvrt to binary
	JR	C,ILLDRVN	;Less than 0, illegal
	CP	7+1		;Greater than 7 ?
	CCF
	LD	C,A
ILLDRVN	LD	A,32		;Init "Illegal Drive #
	JP	C,IOERR
;
;	Drive # is legal - Check it out
;
	@@CKDRV			;Do a check drive
	LD	A,32		;Init "Illegal drive
	JP	NZ,IOERR	;Go if bad
;
	INC	HL		;Bump line pointer
PROT01	LD	A,C
	LD	(TSTMPW+1),A	;Stuff drive for later
	@@GTDCT			;Get DCT -> IY
	LD	DE,PRMTBL$	;Get parms
	@@PARAM
	JP	NZ,IOERR	;Quit on parm error
	LD	A,(PPARM+1)	;Make sure a parm
	LD	HL,LPARM+1	;  was entered
	OR	(HL)
	LD	HL,UPARM+1
	OR	(HL)
	LD	HL,NPARM+1
	OR	(HL)
	JP	Z,NOTDUN	;Quit if none entered
	CALL	TSTMPW		;Test master password
	JP	NZ,IOERR	;Quit on error
NPARM	LD	DE,0		;P/U Name parm
	LD	A,D
	OR	E
	JR	Z,PPARM		;Jump if name not entered
	LD	HL,PACKNM$	;Get name into buf1
	CALL	GMPW1
	JP	NZ,IOERR	;Quit on error
	LD	HL,BUFFER	;HL = new name
	LD	DE,GATBUF+0D0H	;Where new name goes
	PUSH	DE
	PUSH	HL
	LD	BC,8		;Name len
	LDIR			;Xfer new name
	POP	HL
	LD	DE,2		;Trk 0, sect 2
	LD	A,(TSTMPW+1)	;P/u drive
	LD	C,A
	@@RDSEC			;Read SIS
	POP	HL		;Get Name pointer
	JP	NZ,IOERR	;Quit if read error
	PUSH	BC		;Save drive
	LD	DE,BUFFER+10H	;Point to where name goes
	LD	BC,8
	LDIR			;Xfer new name to SIS
	POP	BC		;Recover drive
	LD	DE,2
	LD	HL,BUFFER
	@@WRSEC			;Write new SIS
	JP	NZ,IOERR	;Quit on write error
PPARM	LD	DE,0		;Was PW parm used?
	LD	A,D
	OR	E
	JR	Z,PROT02	;Jump if PW not entered
	LD	HL,NEWMPW$	;Buffer for new MPW
	CALL	GETMPW		;Input the new one
	JP	NZ,IOERR	;Quit if bad PW
	LD	(GATBUF+0CEH),HL	;Stuff PW
PROT02	LD	D,(IY+9)	;Dir cl => reg D
	LD	A,(TSTMPW+1)	;P/u drive
	LD	C,A
	CALL	GATWR		;Write sector 0 from buf
	JP	NZ,IOERR	;Jump on write error
	LD	HL,(GATBUF+0CEH)	;P/u pack MPW
;
;	Check on Lock or Unlock
;
LPARM	LD	BC,0		;Lock parm used?
	LD	A,B
	OR	C
	LD	DE,BLNKMPW	;P/u blank MPW for test
	JR	NZ,PROT03	;Jump if LOCK entered
UPARM	LD	BC,0		;Unlock parm used?
	LD	A,B
	OR	C
	RET	Z		;Neither LOCK or UNLOCK
	EX	DE,HL		;Switch New & Test MPW
;
;	Lock to pack MPW or unlock pswds to blanks
;
PROT03	LD	(REVMPW+1),HL	;Stuff New MPW
	LD	(THISPW+1),DE	;Stuff test MPW
	LD	A,(TSTMPW+1)	;P/u drive #
	LD	C,A
	LD	D,(IY+9)	;Get dir cyl => reg D
	LD	E,1		;Point to HIT
	LD	HL,HITBUF	;Point to HIT buffer
	@@RDSSC			;Read it into buffer
	JP	NZ,IOERR	;Quit on read error
PROT04	LD	A,(HL)		;P/u a DEC
	OR	A
	JR	Z,PROT09	;Loop on spare
	LD	B,L		;Put DEC in reg B
	PUSH	BC		;  & save it in stack
	LD	A,L		;Ck if this DEC points
	AND	0E0H		;  to same dir sector as
	LD	L,A		;  the previous DEC
	XOR	B
PROT05	CP	0FFH		;1st time, no DEC
	JR	Z,PROT06	;Jump if the same
	LD	(PROT05+1),A	;Save it for testing
	@@DIRRD			;Read this dir sector
	JP	NZ,IOERR	;Quit on read error
	LD	A,H		;Set hi-order SBUFF$
	LD	(PROT06+1),A
PROT06	LD	H,$-$		;Point to buf hi-order
	LD	A,(HL)		;P/u type code
	AND	0F8H		;Remove protection
	CP	10H		;Jump if INV, SYS, FXDE
	JR	NZ,PROT08
	LD	A,L		;Point to password fields
	ADD	A,16
	LD	L,A
	PUSH	DE		;Save reg DE
PROT07	PUSH	HL		;Save pointer to OWNER
	LD	E,(HL)		;P/u owner MPW
	INC	HL
	LD	D,(HL)
THISPW	LD	HL,$-$		;P/u test MPW & see
	XOR	A		;  if this one matches
	SBC	HL,DE
	POP	HL		;Restore ptr to OWN
	JR	NZ,PROT07B	;Don't change if diff
REVMPW	LD	DE,$-$		;  else p/u new MPW
	LD	(HL),E		;  & insert it
	INC	L
	LD	(HL),D
PROT07B	POP	DE		;Restore reg DE
PROT08	POP	BC		;Recover DEC
	LD	H,HITBUF<-8	;Point to HIT hi-order
	LD	L,B		;Stuff HIT lo-order
PROT09	LD	A,L		;Point to next entry
	ADD	A,32		;  for this dir sector
	LD	L,A
	JR	NC,PROT04	;Jump if still in same
	LD	A,(PROT05+1)	;P/u current DEC
	XOR	L
	JR	NZ,PROT10	;Jump if different
	PUSH	HL
	@@DIRWR			;Write out this sector
	POP	HL
	JP	NZ,IOERR	;Quit on write error
PROT10	LD	A,L		;Advance to the next
	INC	L		;  directory sector
	CP	1FH		;At end of disk?
	JR	NZ,PROT04	;Loop if not
	RET			;  else go home
;
;	Routine to test master password for match
;
TSTMPW	LD	C,$-$		;Init to drive requested
	CALL	GATRD		;Read GAT into GATBUF
	RET	NZ		;Back on error
	LD	HL,(GATBUF+0CEH)
	LD	DE,PASSWORD	;Password=PASSWORD?
	XOR	A
	SBC	HL,DE
	RET	Z		;Back if PASSWORD
;
;	MPW is not "PASSWORD" - check entry match
;
MPARM	LD	DE,0		;P/u MPW string addr
	LD	HL,CURMPW$	;Init prompt
	CALL	GETMPW		;Hash parm or entry
	RET	NZ		;Back on bad PW
	EX	DE,HL		;Xfer hashed MPW to DE
	LD	HL,(GATBUF+0CEH)	;Grab pack MPW &
	XOR	A		;  check if user entered
	SBC	HL,DE		;  the pack MPW
	LD	HL,BADMPW$	;Init error pointer
	LD	A,63		;Set extended error
	RET			;Z or NZ
;
;	Enter SYS2 & hash the password
;
GETMPW	CALL	GMPW1		;Get MPW into buffer
	JR	Z,DOHASH
	CP	63		;Extended error?
	RET	NZ
	LD	HL,BADMPW$	;Switch error message
	OR	A		;  to password error
	RET
DOHASH	LD	A,0E4H		;Hash password (DE) to HL
	RST	28H		;Ret to what called
;
;	Routine places a password field into buffer
;
GMPW1	LD	A,D		;Test if user entered MPW
	OR	E
	JR	Z,GMPW3		;Prompt if not
	INC	A		;  or if no operand
	JR	Z,GMPW3
;
;	Place entered password into buffer
;
	LD	HL,BUFFER	;Point to buffer
	PUSH	HL
	LD	B,8		;Init for 8 chars
GMPW2	LD	A,(DE)		;P/u a char
	CP	CR		;End of line?
	JR	Z,GMPW4
	CP	','		;Comma separator?
	JR	Z,GMPW4
	CP	'"'		;Closing quote?
	JR	Z,GMPW4
	INC	DE		;Bump input pointer
	LD	(HL),A		;Transfer character
	INC	HL		;Bump output pointer
	DJNZ	GMPW2		;Loop until done
	JR	CKMPW
;
;	MPW not entered - Prompt & fetch
;
GMPW3	CALL	CKINDO		;Can't prompt in <DO>
	RET	NZ
	@@DSPLY			;Display prompt
	RET	NZ
	LD	BC,8<8		;Init for 8 chars
	LD	HL,BUFFER	;Point to buffer
	PUSH	HL
	@@KEYIN			;Get parm input
	JP	C,ABORT		;Quit on Break
	EX	DE,HL		;Start pointer to reg DE
	LD	H,0		;Calculate trailing
	LD	L,B		;  spaces needed for MPW
	ADD	HL,DE
	LD	A,8
	SUB	B
	JR	Z,CKMPW		;Go if 8 chars entered
	LD	B,A		;Set loop count
GMPW4	LD	(HL),' '	;  and fill to end
	INC	HL		;  with spaces
	DJNZ	GMPW4
;
;	Convert (SP) through (SP)+7 to upper case
;
CKMPW	POP	HL		;Get buffer start
	PUSH	HL
	LD	B,8		;Init loop 8 chars
	LD	A,(HL)		;P/u 1st char
	JR	CKMPW2		;  & check <A-Z>
CKMPW1	INC	HL
	LD	A,(HL)
	CP	' '		;Got to a space?
	JR	Z,CKMPW7
	CP	'0'		;Less than '0' is error
	JR	C,INVNAM
	CP	'9'+1		;<0-9> is okay for 2-n
	JR	C,CKMPW3
CKMPW2	CP	'A'		;Less than "A" is error
	JR	C,INVNAM
	CP	'Z'+1		;<A-Z> is okay
	JR	C,CKMPW3
	CP	'a'		;Ck convert to upper
	JR	C,INVNAM
	CP	'z'+1
	JR	NC,INVNAM
	RES	5,(HL)
CKMPW3	DJNZ	CKMPW1		;Loop if more
CKMPW4	POP	DE		;Point to buffer start
	XOR	A		;Set Z = good
	RET
;
CKMPW5	INC	HL
	CP	(HL)		;No imbedded spaces
	JR	NZ,INVNAM
CKMPW7	DJNZ	CKMPW5		;A space found, now
	JR	CKMPW4		;  must be all spaces
INVNAM	LD	HL,BADNAM$	;Pt to error string
	LD	A,63		;Init extended error
	OR	A		;Set NZ
	POP	DE
	RET
;
;	Read or write the granule allocation table
;
GATRD	DB	0F6H		;Set NZ for test
GATWR	XOR	A		;Set Z for test
	PUSH	DE
	PUSH	HL
	PUSH	AF
	PUSH	IY
	@@GTDCT			;DCT to reg IY
	LD	D,(IY+9)	;Get dir track
	POP	IY
	LD	HL,GATBUF
	LD	E,L		;Set to sector 0
	POP	AF
	JR	Z,GATRW1	;Go if GAT write
	@@RDSSC
	LD	A,14H		;Init "GAT read error
	JR	GATRW3
GATRW1	@@WRSSC
	JR	NZ,GATRW2	;Skip verify if error
	@@VRSEC			;Verify the write
GATRW2	CP	6		;Error 6 expected
	LD	A,15H		;Init "GAT write error
GATRW3	POP	HL
	POP	DE
	RET
;
CKINDO	PUSH	IY
	@@FLAGS
	BIT	5,(IY+'S'-'A')	;Ck on DO in effect
	POP	IY
	RET	Z		;Go back if not in DO
	LD	HL,NOINDO$	;  else set error code
	LD	A,63		;Set extended error
	RET			;  & back with NZ
;
;	Messages
;
SPCREQ$	DB	'File spec required',CR
NOTDUN$	DB	'Specifications Required',CR
ATBERR$	DB	'Attribute specification error',CR
PROTS$	DB	'NOEXREUPWRRNRMFU'
NOINDO$	DB	'Invalid command during DO processing',CR
PACKNM$	DB	'New disk pack name ?   ',3
NEWMPW$	DB	'New master password ?  ',3
CURMPW$	DB	'Master password ?      ',3
BADMPW$	DB	'Invalid master password',CR
BADNAM$	DB	'Invalid disk name',CR
ABORT$	DB	'Command aborted',CR
;
PRMTBL$	DB	80H
;
VAL	EQU	80H
SW	EQU	40H
STR	EQU	20H
SGL	EQU	10H
;
	DB	SW!STR!2,'PW',0
	DW	PPARM+1
	DB	SW!SGL!4,'LOCK',0
	DW	LPARM+1
	DB	SW!SGL!6,'UNLOCK',0
	DW	UPARM+1
	DB	SW!STR!SGL!4,'NAME',0
	DW	NPARM+1
	DB	SW!STR!SGL!3,'MPW',0
	DW	MPARM+1
	NOP
;
PSWDBUF	DS	8		;Password buffer
HASHBUF	DS	4		;Owner & user hashes
FCB	DS	32
;
	ORG	$<-8+1<+8
BUFFER	DS	256
GATBUF	DS	256
HITBUF	DS	256
;
	END	ATTRIB
