;BACKUP1/ASM - Backup utility module
	SUBTTL	'<Backup initialization>'
;
SMALL	EQU	0
FMT	EQU	0
LF	EQU	10
CR	EQU	13
LOCK	EQU	60H
TKCAP	EQU	0CCH
PSWD	EQU	0CEH
DAT	EQU	0D8H
AUTO	EQU	0E0H
FCNT1	EQU	1111H
FCNT2	EQU	1555H
PASSWORD	EQU	42E0H
;
*GET	SVCMAC:3		;SVC Macro equivalents
*GET	COPYCOM:3		;Copyright message
;
	ORG	2600H
;
	IF	@MOD2
BOOTST$	DB	03H
	ENDIF
	IF	@MOD4
BOOTST$	DB	9DH		;Boot step rate ptr
	ENDIF
;
;	Data area
;
FTFLG$	DB	0
SPCFLD$	DC	11,' '
MFLG$	DB	0
NEWPRM$	DW	0
OLDPRM$	DW	0
MODPRM$	DW	0
QPARM$	DW	0
BUFFER$	DW	0
FCB1$	DS	32
FCB2$	DS	32
FCB3$	DS	32
LILBUF$	EQU	FCB3$
DATFLD$	DS	8
FMPAKD$	DS	2
TOPAKD$	DS	2
CLSFLG$	DS	1
;
	IF	@MOD2
;
	SUBTTL	'<BACKUP Module - #4/2>'
;
*GET	BACKUP42
	ENDIF
;
;
;	Normal exit - no errors
;
EXIT1	LD	HL,BUCAO$	;"Backup complete...
	PUSH	HL		;Save msg ptr
	CALL	EXIT5		;Ck if prompt for sys disk
	POP	HL
	@@DSPLY
	JR	EXIT
;
;	Error exit
;
DIRERR	LD	A,17		;Init "Dir read error
	DB	1		;Ignore next inst
EXIT2	LD	A,20H		;Init illegal drive #
EXIT3	PUSH	AF
	LD	C,CR		;Terminate pending line
	@@DSP
	CALL	EXIT5		;Get system disk if needed
	POP	AF
	LD	L,A		;Error code to HL
	LD	H,0
	OR	0C0H		;Set short,return
	LD	C,A		;Error to C
	@@ERROR			;  for error dsply
	JR	ERREXIT
;
;	Abort exit
;
BREAK	EQU	$
ABRTBU	LD	HL,ABRTBU$	;"Backup aborted
EXIT4	PUSH	HL		;Save msg ptr
	CALL	EXIT5		;Get system disk if needed
	POP	HL
	@@LOGOT			;Display the message
	LD	HL,-1		;Set error return code
ERREXIT	LD	(RETCOD),HL
EXIT	EQU	$
SPSAV	LD	SP,$-$		;P/u the stack pointer
	LD	HL,0		;Set the return code
RETCOD	EQU	$-2
	@@CKBRKC		;Check and clear break
	@@EXIT			;Can't return from BACKUP
;
;	Get system disk if needed & zero memory used
;
EXIT5	EQU	$
XPARM$	LD	DE,0		;P/u prompt zero drive
	INC	E		;Ck for entry
	JR	NZ,EXIT5A
	XOR	A
	LD	(SXORD+1),A
	CALL	SYSDRV$		;Force prompt for SYSTEM
	JR	EXIT5B
EXIT5A	LD	A,(SXORD+1)	;  else if not entered,
	OR	A		;  ck if source & dest
	CALL	Z,NDSYS$	;  are same - we may need
EXIT5B	LD	DE,(BUFFER$)	;  a prompt
	LD	A,D		;Ck if we did a backup
	OR	E
	RET	Z		;Ret if buf adr never set
	LD	HL,0		;  else calculate how
	LD	B,L		;  many bytes in RAM
	@@HIGH$			;  to zero
	XOR	A
	SBC	HL,DE		;Get length to zero
	LD	B,H
	LD	C,L		;  into BC
	LD	H,D		;Pt HL to start of buffer
	LD	L,E
	INC	DE
	LD	(HL),0		;Init 1st to zero
	DEC	BC		;  & propogate it
	LDIR
	RET
;
;	Prompt for system disk
;
NDSYS$	LD	A,(SRCDRV$+1)	;On exit, if S=D <> 0
	OR	A		;  then no need to prompt
	RET	NZ
SYSDRV$	LD	A,0		;P/u drive 0 indicator
	OR	20H		;Set bit 5 for sys test
	PUSH	HL
	LD	HL,PMTSYS$	;"insert system...
	PUSH	AF		;Save drive #
	LD	A,(CURDSK+1)	;Save cur disk
	LD	(TDSK),A
	POP	AF
	CALL	CURDSK		;get wanted disk
	LD	A,(CURDSK+1)
	AND	7		;Mask all but drive
	LD	C,A		;Drive # to C
	@@GTDCT			;Get the DCT this drive
	LD	A,(IY+4)	;Get ckdrv bit
	PUSH	AF
	AND	7FH		;Mask ckdrv, force on
	LD	(IY+4),A
	PUSH	IY
	@@CKDRV
	POP	IY		;Get back DCT
	POP	BC		;Old info to B
	PUSH	AF		;Save ready status
	LD	A,B		;Old ckdrv status
	AND	80H
	OR	(IY+4)
	LD	(IY+4),A	;Restore ckdrv status
	POP	AF		;Get ready status
	POP	HL		;Set for return
	RET	Z		;Back if disk in
	LD	A,0
TDSK	EQU	$-1
	LD	(CURDSK+1),A	;Reset for proper disk
	JR	SYSDRV$		;Redo
;
;	Prompt source disk
;
SRCDRV$	LD	A,0		;Source drive
	OR	80H		;Set bit 7 on source
	PUSH	HL
	LD	HL,PMTSRC$	;"Insert source
	CALL	CURDSK		;Prompt for source if needed
	POP	HL
	RET
;
;	Prompt source disk if needed to swap
;
PMTSRC	LD	A,(CURDSK+1)	;P/u current drive
	BIT	7,A		;Is source the one?
	JR	NZ,SRCDRV$	;Jump if it is
	CALL	SRCDRV$		;  else prompt for it
	LD	A,(SXORD+1)
	OR	A
	RET	NZ		;Ret if source <> dest
	CALL	RESTOR		;Restore to cyl 0
	PUSH	BC
	PUSH	DE		;Save registers
	PUSH	HL
	LD	HL,BUF3$	;Use this for I/O buffer
	LD	DE,0		;Read the BOOT
	CALL	RDSEC
	POP	HL
	POP	DE		;Restore the registers
	POP	BC
	JP	NZ,EXIT3	;Quit on read error
	LD	A,(BUF3$)	;P/u 1st byte of BOOT
	OR	A		;If source, s/b 0
	JR	NZ,PSRC3	;Jump if not this disk
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	D,(IY+9)	;P/u dir cyl
	LD	E,0		;Pt to GAT sector
	LD	HL,BUF3$
	CALL	RDSEC		;Read the GAT
	CP	6
	JP	NZ,DIRERR
	LD	HL,BUF1$+PSWD	;Ck for match with orig
	LD	DE,BUF3$+PSWD	;  source disk
	LD	B,10		;Set match count
PSRC1	LD	A,(DE)
	CP	(HL)
	JR	NZ,DIFSRC	;Wrong disk if no match
	INC	DE		;Bump pointers
	INC	HL
	DJNZ	PSRC1		;Loop for 10 compares
	POP	HL		;Was a match,
	POP	DE		;  restore and return
	POP	BC
	RET
DIFSRC	@@DSPLY	DIFSRC$		;"wake up...
	POP	HL		;Clean the stack
	POP	DE
	POP	BC
PSRC3	XOR	A		;Show not current disk
	LD	(CURDSK+1),A
	JR	PMTSRC		;Loop to re-prompt
;
;	Destination disk selection
;
DSTDRV$	LD	A,0		;Dest drive
	OR	40H		;Set dest diskette code
	PUSH	HL
	LD	HL,PMTDST$	;"insert dest...
	CALL	CURDSK
	POP	HL
	RET
;
;	Prompt destination if needed
;
PMTDST	LD	A,(CURDSK+1)	;P/u current disk/drive &
	BIT	6,A		;  ck if destination disk
	JR	NZ,DSTDRV$	;Jump if it is
	CALL	DSTDRV$		;  else request swap
	LD	A,(SXORD+1)
	OR	A
	RET	NZ		;Ret if source <> dest
	CALL	RESTOR		;  else restore to cyl 0
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,BUF3$	;Use this for I/O buffer
	LD	DE,0		;Pt to BOOT sector
	CALL	RDSEC		;  & read the BOOT
	POP	HL
	POP	DE
	POP	BC
	JP	NZ,EXIT3	;Quit on read error
	LD	A,(BUF3$)	;P/u 1st byte of BOOT
	CP	76H		;Dest s/b a HALT
PMTDST1	RET	Z
	PUSH	HL
	PUSH	DE
	@@DSPLY	DIFDST$		;"not same dest...
	POP	DE
	POP	HL
	XOR	A
	LD	(CURDSK+1),A	;Show no current diskette
	JR	PMTDST		;  and prompt again
;
;	Force a prompt of the target disk
;
FRCPMT	LD	A,C		;P/u target drive
	LD	(CURDSK+1),A	; with code bit
	JR	FLASH
;
;	Routine to check if flashing prompt is needed
;
CURDSK	CP	0		;P/u current disk
	JR	Z,FLSH6		;Match with wanted disk?
	LD	(CURDSK+1),A	;No, update current
SXORD	LD	A,0FFH		;0=src & dst drive same
	OR	A
	JR	NZ,FLSH6	;Jump if source <> dest
;
;	Routine to flash the prompt
;
FLASH	PUSH	BC
	PUSH	DE
	PUSH	HL
	@@FLAGS			;IY => flag table base
	LD	C,CR		;Write a new line
	@@DSP
	LD	C,15		;Cursor off
	@@DSP
FLASH0
	@@CKBRKC		;Check and clear break
	CALL	RESKFLG		;Reset Pause,Enter,Break
	LD	BC,16893	;Delay for 1/4 sec
	@@PAUSE
	LD	A,(IY+'K'-'A')
	AND	4!1		;Wait for no ENTER!BRK
	JR	NZ,FLASH0
	CALL	RESKFLG		;Reset in case BREAK
FLS1	@@DSPLY			;Display the message
	LD	BC,FCNT2
	CALL	FLS2		;Blink start
	LD	C,29		;Cursor to BOL
	@@DSP
	LD	C,1EH		;Cursor erase to EOL
	@@DSP
	LD	BC,FCNT1	;Wait delay count
	CALL	FLS2		;Wait & ck Enter or Break
	JR	FLS1		;Loop until Enter
FLS2
	@@CKBRKC		;Check for break
	JP	NZ,BREAK	;  and abort if so
	LD	A,(IY+'K'-'A')	;P/u KFLAG settings
	BIT	2,A		;Enter pressed?
	JR	NZ,FLS4		;Go if so
	DEC	BC		;Count down
	LD	A,B
	OR	C
	JR	NZ,FLS2		;  and loop if more time
	RET
FLS4	POP	AF		;Pop return address
FLS5	@@KBD			;Clear type ahead buffer
	JR	Z,FLS5		;Loop til no key down
	LD	C,0DH		;Dsply a new line
	@@DSP
	LD	C,14		;Cursor on
	@@DSP
	CALL	RESKFLG		;Reset Break,Enter,Pause
	POP	HL
	POP	DE		;Restore registers
	POP	BC
FLSH6	LD	A,(CURDSK+1)	;P/u drive #
	AND	7		;Strip off code bits
	LD	C,A		;Drive # to C to
	@@GTDCT			;  get DCT vector
	IF	@MOD4
	CALL	RSELCT		;Get drive status in A
	ENDIF
	IF	@MOD2
	CALL	SELECT
	ENDIF
	RLCA
	RLCA
	RET
RESKFLG	LD	A,(IY+'K'-'A')	;Reset 3-bit field
	AND	0F8H
	LD	(IY+'K'-'A'),A
	RET
;
;	Drive disk I/O call setups
;
TSTDRV	PUSH	BC
	XOR	A		;Test for drive
	JR	DIO1
SELECT	PUSH	BC
	LD	A,1		;Select new drive
	JR	DIO1
RESTOR	PUSH	BC
	LD	A,4		;Restore
	JR	DIO1
RSELCT	PUSH	BC
	LD	A,7		;Reselect
	JR	DIO1
WRSEC	PUSH	BC
	LD	A,13		;Write sector
	JR	DIO1
WRSYS	PUSH	BC
	LD	A,14		;Write protected
	JR	DIO1
RDSEC	PUSH	BC
	LD	A,9		;Read sector
	JR	DIO1
;
	IF	@MOD2
FMTCYL	PUSH	BC		;Save
	LD	A,15		;I/O command
	JR	DIO1		;Continue
	ENDIF
;
VERSEC	PUSH	BC
	LD	A,10		;Verify sector
DIO1	ADD	A,40		;Adjust for SVC
	LD	B,A		;Save tempy
	LD	A,(CURDSK+1)	;Get drive number
	AND	7		;Strip diskette type bit
	LD	C,A		;Load up drive register
	LD	A,B		;Get back SVC #
	IF	@MOD4
	DI			;Interrupts off
	ENDIF
	RST	40
	IF	@MOD4
	EI			;Interrupts on
	ENDIF
	POP	BC
	RET
;
;	Check for correct disk
;
CKSWDD	PUSH	DE		;Save DE,BC
	PUSH	BC
	LD	A,(SRCDRV$+1)	;Get drive
	LD	HL,CURDSK+1
	LD	C,(HL)		;Get current drive
	LD	(HL),A		;Make curdsk our dsk
	LD	HL,(BUFFER$)	;I/O buffer
	LD	DE,2		;Trk 0, sect 2
PROTSEC	EQU	$-2
	CALL	RDSEC		;Read SIS sector
	JR	NZ,EX2		;Quit on read error
	LD	L,0C6H		;Set buffer posn
	LD	A,$-$		;Get original id byte
SVCTR	EQU	$-1
	CP	(HL)		;Is it the same disk?
	JR	NZ,EX1		;NZ=error exit
	INC	A
	JR	Z,EX1
	DEC	A		;If id byte 0,
	JR	Z,EX1		; no modifying needed
	DEC	A		; else dec remaining
	JR	NZ,$+3		;If now 0, make FFH
	DEC	A
	LD	(HL),A		;Store the new id
	IF	@MOD2
	LD	L,0		;Reset buffer
	ENDIF
	IF	@MOD4
	LD	L,D		;Reset buffer
	ENDIF
	CALL	WRSEC		;Put it back, ck error later
EX1	LD	A,C
	LD	(CURDSK+1),A	;Restor orig drv #
	POP	BC
	POP	DE
EX2	LD	HL,CANTBU$	;Go if was write error
	RET	Z
	JP	EXIT4
;
;
;	Message area
;
DSTWP$	DB	LF,'Destination disk is write '
	DB	'protected',CR
OLD2NEW$	DB	'Can''t move SYS files from 6.2'
	DB	' or earlier versions to 6.3.x',CR
BADMPW$	DB	'Invalid master password',CR
PMTSYS$	DB	29,30,'Insert SYSTEM disk  <ENTER>',3
PMTSRC$	DB	29,30,'Insert SOURCE disk  <ENTER>',3
PMTDST$	DB	29,30,'Insert DESTINATION disk  '
	DB	'<ENTER>',3
DIFSRC$	DB	29,30,'* A L E R T *  That',27H
	DB	's not the same source disk ',CR
DIFDST$	DB	29,30,'* A L E R T *  That',27H
	DB	's not the same destination disk ',CR
CCMOD$	DB	'Source disk is write protected; '
	DB	'MOD flags not updated',CR
BUCAO$	DB	LF,'Backup complete',CR
ABRTBU$	DB	LF,'Command aborted',14,CR
CANTBU$	DB	'Can''t Backup - source disk write protected',LF
PROT$	DB	'Disk contains protected files ',CR
NEWDT	DB	0
DVTEST1	DB	0
DVTEST2	DB	0
BUCORE$	DEFL	$
	ORG	$<-8+1<+8
BUF1$	DS	256
BUF2$	DS	256
BUF3$	DS	256
	PAGE	OFF
;
;
;	Backup entry point
;
;
BACKUP
	@@CKBRKC
	JR	Z,BACKUPA	;Go ahead if no break
	LD	HL,-1		; else abort
	RET
;
BACKUPA	LD	(SPSAV+1),SP	;Save current SP
	PUSH	HL		;Save cmdbuf
	@@BREAK	0		;Remove any BREAK vector
	@@DSPLY	HELLO$		;Welcome
	@@FLAGS			;IY => flag table
	PUSH	IY
	POP	DE
	LD	HL,'Y'-'A'	;Get year type locn
	ADD	HL,DE
	LD	(YFLAG1),HL	;Save for ckdrv
	CALL	RESKFLG		;Reset KFLAG bits
	BIT	1,(IY+'C'-'A')	;Check on CMNDR active
	LD	HL,LDOS$
	JP	NZ,EXIT4	;  and exit if so
	POP	HL
BCK1	LD	A,(HL)		;Bypass cmdline spaces
	INC	HL
	CP	' '
	JR	Z,BCK1
;
;	Scan for source partial spec
;
	LD	DE,SPCFLD$	;Pt to filespec field
	LD	B,8		;Init for file name
	CP	'-'		;Exclude matches?
	JR	NZ,BCK2		;If '-', set flag
	LD	(MFLG$),A
	LD	A,(HL)		;Get next char
	INC	HL
BCK2	CALL	PRSPEC		;Parse possible filename
	CP	'.'		;Valid ext. char
	JR	Z,BCK2A
	CP	'/'		;File ext?
	JR	NZ,BCK3
BCK2A	LD	DE,SPCFLD$+8	;Reposn buffer ptr
	LD	B,3		;Init for 3 chars
	LD	A,(HL)
	INC	HL		;Bypass the /
	CALL	PRSPEC		;Parse extension
;
;	Determine source & destination drives
;
BCK3	CP	':'		;Drive number coming?
	JR	Z,BCK4		;Go if so
	CP	'('+1		;If legal, is some term.
	JR	C,BCK3A		;Go if ok
	LD	A,19		;"Illegal file name
	JP	EXIT3		;Quit
BCK3A	DEC	HL		;Save possible parms
	PUSH	HL
	@@DSPLY	SRCNUM$		;No drives enter, so
	LD	HL,LILBUF$	;  prompt for them
	LD	BC,1<8		;1 char response
	@@KEYIN
	JP	C,ABRTBU	;Quit on Break
	LD	A,(HL)		;Get response. Restore
	POP	HL		;  command buffer. Ignore
	DB	0DAH		;  next 2 inst with JP C,
BCK4	LD	A,(HL)		;P/u source drive #
	INC	HL		;Bump to separator
	SUB	'0'		;Adj to binary
	CP	8		;Error if not in
	JP	NC,EXIT2	;  the range <0-7>
	LD	(SRCDRV$+1),A	;Stuff source drive
BCK5	LD	A,(HL)		;P/u char or separator
	INC	HL		;Bump ptr
	CP	':'		;Find dest drive?
	JR	Z,BCK6		;Get drive # if :
	CP	30H		; let prepositions thru
	JR	NC,BCK5
	CP	20H		;Or a space separator
	JR	Z,BCK5
	DEC	HL		;Save possible parms
	PUSH	HL
	@@DSPLY	DSTNUM$		;Prompt for dest drive
	LD	HL,LILBUF$	;Use for keyin buffer
	LD	BC,1<8		;1 char only
	@@KEYIN
	JP	C,ABRTBU	;Quit on Break
	LD	A,(HL)		;Get response. Restore
	POP	HL		;  buffer. Ignore next 2
	DB	0DAH		;  inst with JP C,nn
BCK6	LD	A,(HL)		;P/u dest drive #
	INC	HL		;Bump line ptr
	SUB	'0'		;Adjust to binary
	CP	8		;Error if not in the
	JP	NC,EXIT2	;  range <0-7>
	LD	(DSTDRV$+1),A	;Stuff dest drive
;
	LD	DE,PRMTBL$	;P/u parm table ptr
	PUSH	DE		;Also in IX to check
	POP	IX		;  responses
	@@PARAM			;Get parms if any
	LD	HL,PRMERR$	;Init "parm error
	JR	NZ,$EX4		;Quit on parm error
	LD	A,(IX+DATRSP)	;Date can only be STR
	AND	VAL!SW		;This must be string
$EX4	JP	NZ,EXIT4	;Quit if not
;
;	Check on Source = Destination
;
	LD	A,(SRCDRV$+1)	;P/u source drive
	LD	HL,DSTDRV$+1
	XOR	(HL)		;Match against dest
	LD	(SXORD+1),A	;0 if S=D, <>0 if S<>D
	JR	NZ,DATPRM	;Bypass if source <> dest
	@@FLAGS			;Else test if <DO> proc
	BIT	5,(IY+'S'-'A')
	LD	HL,NOINDO$	;"can't do single...
	JP	NZ,EXIT4	;Abort if from <DO>
;
;	Check on date entries
;
DATPRM	LD	HL,0		;P/u date="from-to"
	LD	A,H
	OR	L
	JR	Z,CKCLAS	;Bypass if not entered
	LD	A,(HL)		;Check for "-to"
	CP	'-'
	JR	Z,CKTO		;Go if no From used
	LD	A,80H		;Set From bit
	LD	(FTFLG$),A	;Note From entered
	CALL	PAKDAT		;Pack the date entry
	LD	(FMPAKD$),BC	;Save From packed date
	LD	A,(HL)		;Ck if more in date parm
	CP	'"'		;End of string?
	JR	Z,FRCDAT	;Go if so
	CP	'-'		;Check for "-to"
	JR	NZ,CKCLAS	;Done if not
CKTO	INC	HL		;Bypass the '-'
	LD	A,(HL)		;Ck for end of parm
	CP	'"'
	JR	Z,CKCLAS	;Go if done
	CALL	PAKDAT		;Pack To date
FRCDAT	LD	A,(FTFLG$)	;P/u From/To flag and
	OR	1		;  set To bit
	LD	(FTFLG$),A
	LD	(TOPAKD$),BC	;Save To packed date
;
;	Check on parms to force CLASS backup
;
CKCLAS	LD	B,0		;Init class flag
SYSPRM	LD	DE,0		;SYS parm used?
	LD	A,D
	OR	E
	JR	Z,INVPRM	;Go if not
	SET	6,B		;Set 6 if SYS
INVPRM	LD	DE,0		;INV parm used?
	LD	A,D
	OR	E
	JR	Z,CKCLA1	;Go if not
	SET	3,B		;Set 3 if INV
CKCLA1	LD	A,B
	LD	(CLSFLG$),A	;Store by class flag
	LD	A,(SPCFLD$)	;Get 1st char of possible
	SUB	' '		;  file name
	LD	B,A		;Save test result and
	LD	A,(SPCFLD$+8)	;  check if extension used
	SUB	' '		;Ck for ext
	OR	B		;A <> 0 if partspec
	LD	B,A		;Hold in reg B
;
;	Merge all "CLASS" parms together
;
	LD	A,(IX+SYSRSP)	;System files
	OR	(IX+INVRSP)	;Invisible files
	OR	(IX+MODRSP)	;Mod flag files
	OR	(IX+NEWRSP)	;Files not on dest
	OR	(IX+OLDRSP)	;Files on dest
	OR	(IX+QRSP)	;Query forces by class
	LD	C,A		;Hold value
	AND	VAL!STR		;Above parms only SWITCH
	LD	HL,PRMERR$	;Init "parm error
	JP	NZ,EXIT4	;Quit if not switches only
	OR	C
	OR	B		;Merge with partspec
	OR	(IX+DATRSP)	;D=" mm/dd/yy-mm/dd/yy"
;
;	Advise backup by class if any class parameter
;
	LD	(CLSTST+1),A	;Set for all flags
	JR	Z,GETDAT	;Z=may be mirror image
	@@LOGOT	CLASS$		;  else log by class msg
;
;	Recover today's date
;
GETDAT	LD	HL,DATFLD$	;Date storage buffer
	@@DATE			;Get date
	LD	A,(DE)		;Check if date in system
	OR	A
	JR	NZ,GETGM	;Go if it is
	LD	HL,NODAT$
	@@LOGOT			;Show "no date" if none
GETGM	PUSH	DE		;Save date$
	PUSH	HL		;  and date buffer
	LD	DE,RES$		;See if SYS modules resident
	@@GTMOD			;  in case needed later
	JR	NZ,GETDAT1	;Skip if none res'ed
	LD	(RESLOC+1),DE	;Store the module loc
;
;	Get SYS2 loaded for password hash
;
GETDAT1	POP	HL
	POP	DE
	CALL	GETSYS2		;Get sys2 and move date
;
;	Check on (X) parm for source/dest swap
;
	LD	A,(SRCDRV$+1)	;If source is not 0,
	OR	A		; then let PMTSRC handle
	JR	NZ,SRCDFT
	OR	80H		;Set to SRC code
	LD	C,A		;Save if needed
	LD	A,(XPARM$+1)	;Source is drive 0,
	INC	A		; if (X), then swap
	PUSH	AF
	LD	HL,PMTSRC$
	CALL	Z,FRCPMT	;Force prompt on (X)
	POP	AF
SRCDFT	CALL	NZ,SRCDRV$	;Prompt for source
	CALL	RESTOR		;Get set to see if a
	CALL	CKDRV		; source disk mounted
	JR	Z,GOTSRC	;Z=ok
	PUSH	AF
	LD	HL,PMTSRC$	;Else prompt "Insert...
	CALL	FRCPMT
	POP	AF
	JR	SRCDFT		; and then check again
;
;	Get source disk attributes
;
GOTSRC	LD	A,(IY+3)	;P/u 5" or 8" from
	AND	20H		;  DCT+3, bit 5
	LD	(TST5_8+1),A	;  and save for later
	CALL	TSTDRV		;Ck for active DCT
	JP	NZ,EXIT3	;  and quit if not
	LD	HL,BUF3$	;Disk buffer
;
	IF	@MOD2
	CALL	GETPSEC		;Get prot sector
	JP	NZ,EXIT3	;Go on error
	CP	6		;Directory?
	JP	NZ,DIRERR	;Nope, go!
	ENDIF
	LD	DE,0		;Set to track/sector 0/0
	CALL	RDSEC		;Read boot
	JP	NZ,EXIT3	;Quit on read error
	LD	A,(BUF3$+2)	;P/u dir track
	LD	(IY+9),A	;  & stuff in table
	IF	@MOD2
	LD	DE,(PROTSEC)	;Get info sector
	ENDIF
	IF	@MOD4
	INC	E		;Point to SYSINFO sector
	INC	E
	ENDIF
	LD	H,BUF1$<-8	;Use this disk buffer
	CALL	RDSEC		;Read the info sector
	JP	NZ,EXIT3	;Quit on read error
	LD	A,(BUF1$+0C6H)	;Get & save id byte
	LD	(SVCTR),A
	INC	A
	JR	Z,CKGAT
;
;	Check write protect status
;
	DEC	A		;Need to check?
	JR	Z,CKGAT		;Go if not
	CALL	RESTOR		;Start the drive
	CALL	RSELCT		;Ck if WP
	OR	(IY+3)		;Merge in soft WP
	RLCA			;Push WP to CF
	JR	NC,CKGAT	;Bypass if not WP
CANTBU	LD	HL,CANTBU$
	JP	EXIT4
;
CKGAT	LD	D,(IY+9)	;Directory track,
	LD	E,0		;  sector 0
	LD	HL,BUF1$
	CALL	RDSEC		;Read GAT
	CP	6		;Ensure directory cyl
	JP	NZ,DIRERR	;Quit on any other error
	LD	A,(BUF1$+0CDH)	;GAT type byte
	AND	8		;Mask all but date type
	LD	(DVTEST1),A	;Save for date dsply
	LD	(DVTEST2),A	;Save for unpack routine
	LD	(SRCTYP),A	;Save for cp to dest disk
	CALL	TSTMPW		;Get password if needed
;
;	Check if destination formatted & not protected
;
	LD	A,(DSTDRV$+1)	;If dest is not 0,
	OR	A		; then let DSTDRV handle
	JR	NZ,DSTDFT
	OR	40H		;Set DST code
	LD	C,A		;Save if needed
	LD	A,(XPARM$+1)	;Dest is drive 0
	INC	A		;If (X), then swap
	PUSH	AF
	LD	HL,PMTDST$
	CALL	Z,FRCPMT	;Force prompt on (X)
	POP	AF
DSTDFT	CALL	NZ,DSTDRV$	;Get dest drive
	CALL	RESTOR		;Restore destination
	CALL	RSELCT		;Test it
	JR	NZ,PMTDD	;Might be signal from
				;HD driver
	RLCA
	OR	(IY+3)		;Merge in soft WP
	BIT	7,A		; Check on WP status
	LD	HL,DSTWP$	;Dest write prot...
	JP	NZ,EXIT4	;Jp if write protected
	CALL	CKDRV		;Ck for disk, read GAT
	JR	NZ,PMTDD	;Redo if not there
	LD	A,(CKDRBUF+0CDH);GAT type byte
	AND	8		;Mask all but date type
	RRCA
	LD	L,A		;Save result
	LD	A,$-$
SRCTYP	EQU	$-1		;Source type
	OR	L		;Merge the two
	LD	(NEWDT),A	;If result=4, old to new
	CP	4		;Is old to new?
	JR	NZ,SRCTYP1	;Go if not old to new
	LD	A,(SYSPRM+1)	;If sys move, cant be
	OR	A		;Old to new
	LD	HL,OLD2NEW$
	JP	NZ,EXIT4
SRCTYP1	BIT	3,(IY+3)	;Hard drive?
	JP	NZ,RECON	;Go if hard
	JR	GOTDST		;  else continue tests
;
PMTDD	PUSH	AF		;Kludge a force of
	LD	HL,PMTDST$
	CALL	FRCPMT
	POP	AF
	JR	DSTDFT		;  the destination prompt
;
;	Check 5" vs 8" for forced reconstruction
;
GOTDST	LD	A,(IY+3)
	AND	20H		;See if 5/8 mismatch
TST5_8	XOR	0		;P/u source size
	JP	NZ,RECON	;Go if different
	LD	DE,0
	CALL	VERSEC		;Verify boot sector readable
	JR	Z,CKDST		;Jump if ok
;
;	Destination not formatted, abort
;
	LD	HL,NOFMT$	;Init "Not formatted
	JP	EXIT4		;Display and abort
;
;	Check destination attributes
;
CKDST	LD	HL,BUF3$
	LD	DE,0		;SET for track/sector 0/0
	CALL	RDSEC		;Read dest boot
	JP	NZ,EXIT3
	LD	A,(BUF3$+2)	;P/u its dir track
	LD	D,A		;Set up in D
	LD	HL,BUF2$
	LD	E,L		;  and 0 in E
	CALL	RDSEC		;Read dest GAT
	CP	6		;Ensure a dir cyl
	JP	NZ,DIRERR	;Quit on any other error
	LD	HL,(BUF1$+TKCAP)	;P/u source capacity
	LD	DE,(BUF2$+TKCAP)	;P/u dest capacity
	LD	A,(SVCTR)	;If id byte was FF
	INC	A
	JR	Z,SHOPROT	; then force recon
	DEC	A		;If id was not 0
	JR	NZ,TSTCAP	;  then test sizes
	BIT	4,H		;If types differ
	JR	Z,TSTCAP	;  force reconstruct
;
SHOPROT	@@LOGOT	PROT$		;Show reconstruct invoked
	JR	RECON		;Skip next tests
;
TSTCAP	LD	A,H		;Den/sides match?
	XOR	D		;Force Reconstruct if
	AND	60H		;  density & sides
	JR	NZ,RECON	;  differ
	LD	A,L		;Test # of cyls
	SUB	E
	JR	Z,BYCLAS	;Jump if same
;
;	Cylinder count differs - question Mirror
;
	LD	A,(CLSTST+1)	;But don't question if
	OR	A		; Class parms already
	JP	NZ,CLSTST	; entered
	CALL	MIRROR		;Attempt mirror?
BYCLAS	JP	Z,CLSTST	;Jump if mirror to be tried
RECON	@@LOGOT	RECON$		;"backup re-con...
	JP	MVBYCLS		;Go do file backup
;
;	Different # of tracks - Prompt for mirror
;
MIRROR	@@DSPLY	MIRROR$		;"Attempt mirror...
	LD	HL,LILBUF$+1	;Keyin buffer
QM1	LD	BC,3<8		;3 chars max
	@@KEYIN
	JP	C,ABRTBU	;Quit on break
	LD	A,(HL)
	RES	5,A		;Convert to UC
	CP	'Y'		;Ret Z if Yes
	RET
;
;	Get & check Disk master password
;
TSTMPW	LD	HL,(BUF1$+PSWD)	;P/u src MPW
	LD	DE,PASSWORD	;If "PASSWORD",
	XOR	A		;  don't prompt
	SBC	HL,DE
	RET	Z
	LD	DE,$-$		;P/u User entry
MPWPRM	EQU	$-2
	LD	HL,PMTMPW$	;Init "Enter MPW
	CALL	GETMPW		;Get the user's response
	EX	DE,HL
	LD	HL,(BUF1$+PSWD)
	XOR	A
	SBC	HL,DE		;Entry match?
	RET	Z		;Ret if MPW match
	LD	HL,BADMPW$	;  else init "bad MPW...
	JP	EXIT4		;Don't do the backup
;
;	Routine to parse partial filespecs & cvrt to UC
;
PRSPEC	CP	'*'
	JR	NZ,PS4
	LD	A,'$'		;Wild card char
PS5	LD	(DE),A		;Store it
	DJNZ	PS5
	LD	A,(HL)		;P/u terminator
	INC	HL
	RET
PS4	CP	'$'		;Wild character?
	JR	Z,PS1		;Always a match
	CP	'A'		;Filename entered?
	JR	NC,PS1
	CP	'9'+1		;Ck on 0-9
	RET	NC
	CP	'0'
	RET	C
PS1	CP	'a'		;Cvrt to UC if needed
	JR	C,$+4
	RES	5,A		;Convert to upper case
	LD	(DE),A		;Save in partspec buffer
	INC	DE		;Bump buffer
	LD	A,(HL)		;Get next char and
	INC	HL		;  bump string ptr
	DJNZ	PRSPEC
	RET
;
;	Pack user date string
;
PAKDAT	LD	A,(HL)
	LD	C,'/'		;Init separator
	CALL	PARSDAT		;Parse entry
	JR	NZ,BADFMT	;Jump on format error
	EX	DE,HL
	LD	A,(LILBUF$)	;Is year a leap year?
	AND	3
	LD	HL,MAXDAYS+1	;Set Feb to have 29 days
	JR	NZ,$+3		; if so
	INC	(HL)
	LD	A,(LILBUF$+2)	;P/u month
	DEC	A		;Range check
	CP	12
	JR	NC,BADFMT	;Go if 0 or >12
	DEC	HL		;Point to Jan entry
	ADD	A,L		;Index the month
	LD	L,A
	LD	A,H
	ADC	A,0
	LD	H,A
	LD	A,(LILBUF$+1)	;P/u day entry
	DEC	A		;Reduce for test (0->FF)
	CP	(HL)
	JR	NC,BADFMT	;Go if too large (or 0)
	LD	HL,LILBUF$+2	;Pt to month
	LD	B,(HL)
	LD	C,0
	SRL	B		;Split month between
	RR	C		;  B and C
	DEC	HL
	LD	A,(HL)		;Get the day
	RLCA			;Move to bits 2-6
	RLCA
	OR	C		;Merge w/month
	LD	C,A
	DEC	HL		;Pt to year
	LD	A,(HL)
	SUB	80		;Make only offset
	JR	NC,GDATE	;Ok if not < 1980
	XOR	A		;  else use 1980
GDATE	RLCA			;Move to bits 3-7
	RLCA
	RLCA
	OR	B		; & merge with month
	LD	B,A
	EX	DE,HL
	RET
BADFMT	LD	HL,BADFMT$
	JP	EXIT4
;
;	Routine to parse DATE/TIME entry
;
PARSDAT	LD	DE,LILBUF$+2	;Point to buf end
	LD	B,3		;Process 3 fields
PRSD1	PUSH	DE		;Save pointer
	CALL	PRSD2		;Get a digit pair
	POP	DE		;Recover pointer
	RET	NZ		;Ret if bad digit pair
	LD	(DE),A		; else stuff the value
	DEC	DE		;Backup the pointer
	DEC	B		;Loop countdown
	RET	Z
	LD	A,(HL)		;Ck for valid separator
	INC	HL		;Bump pointer
	CP	C		;Separator char required
	JR	Z,PRSD1		;Loop if match
	RET			;Else ret bad (NZ)
;
;	Routine to parse a digit pair
;
PRSD2	CALL	PRS4		;Get a digit
	JR	NC,PRSD3	;Jump if bad digit
	LD	E,A		;Multiply by ten
	RLCA
	RLCA
	ADD	A,E
	RLCA
	LD	E,A
	CALL	PRS4		;Get another digit
	JR	NC,PRSD3	;Jump on bad digit
	ADD	A,E		;Accumulate new digit
	LD	E,A		;Save 2-digit value
	XOR	A		;Clear flags
	LD	A,E		;Xfer field value
	RET
PRSD3	OR	A		;Set NZ
	RET
PRS4	LD	A,(HL)		;P/u a digit &
	INC	HL		;Convert to binary
	SUB	30H
	CP	10		;Set CF if good
	RET
;
;	Save PC for later use
;
CORE$	DEFL	$<-8+1<8	;Set to next page
	ORG	BACKUP		;Set for MIRROR exec
MIRBU	EQU	CORE$
	LORG	MIRBU		;Load origin
;
	SUBTTL	'<Mirror Image Backup>'
	PAGE	OFF
;
*GET	BACKUP2:3
;
	DC	64,0		;PATCH space
;
MIRSIZ	EQU	$<-8+1<8-BACKUP
;
;
;	Adjust PC & load address for CLASS
;
	ORG	BACKUP
;
CLSBU	EQU	CORE$+MIRSIZ
	LORG	CLSBU
;
	SUBTTL	'<Backup By Class>'
	PAGE	OFF
;
*GET	BACKUP3:3
;
	SUBTTL	'<Backup Misc. routines>'
	PAGE	OFF
;
CLSSIZ	EQU	$-BACKUP
;
;	Establish PC for rest of BACKUP initialization
;
	ORG	CORE$+MIRSIZ+CLSSIZ
	LORG	$		;No offset here
;
;	Shift in Mirror or By-file module
;
CLSTST	LD	A,0		;Non-zero if any option
	OR	A
	JP	NZ,MVBYCLS	;Bypass if special
	LD	HL,MIRBU	;Move in standard code
	LD	DE,BACKUP
	LD	BC,MIRSIZ
	LDIR
	JR	SETBFR
;
MVBYCLS	LD	A,(SXORD+1)	;Restrict by class
	OR	A		; if a single drive
	JR	NZ,MVBYC1
	LD	HL,CLS1DB$	;Can't by class on 1 drv
MOVNOT	@@DSPLY			;Display the error
	JP	ABRTBU		;  and abort the backup
;
MVBYC1	LD	A,(XPARM$+1)	;By class backup requires
	OR	A		; either non (X) or residency
	JR	Z,MVBYC2	; of SYS 2, 3, 10, and 12
RESLOC	LD	DE,$-$		;Store location (RES$)
	LD	A,E
	OR	D		;Check if there
	LD	HL,RESREQ$	;Init "Must be resident
	JR	Z,MOVNOT	;Error if not in use
	PUSH	DE		;OK, it's in use,
	POP	IX		;  are all modules
	LD	A,(IX+2*2+5)	;  present and accounted
	OR	A		;SYS2 resident?
	JR	Z,MOVNOT
	LD	A,(IX+3*2+5)	;Is SYS3 resident?
	OR	A
	JR	Z,MOVNOT
	LD	A,(IX+10*2+5)	;Is SYS10 resident?
	OR	A
	JR	Z,MOVNOT
	LD	A,(IX+12*2+5)	;Is SYS12 resident?
	OR	A
	JP	Z,MOVNOT
MVBYC2	LD	HL,CLSBU	;Move in special code
	LD	DE,BACKUP
	LD	BC,CLSSIZ
	LDIR
SETBFR	DEC	DE		;Set the buffer
	INC	D		;  one page above the code
	LD	E,0
	LD	(BUFFER$),DE	;  and save starting posn
	JP	BACKUP
;
;	Routine to get password
;
GETMPW	CALL	GMPW1
	LD	A,0E4H		;Get SYS2 for hash
	RST	28H
;
GETSYS2	LD	A,84H		;Load SYS2, no function
	RST	28H
;
GMPW1	LD	A,D		;Pswd entered as parm?
	OR	E
	JR	Z,GMPW3		;Prompt if not
	LD	HL,BUF3$
	PUSH	HL
	LD	B,8
GMPW2	LD	A,(DE)		;P/u pswd character
	CP	CR		;At end of line?
	JR	Z,GMPW4		;Space out if yes
	CP	','		;Comma separator?
	JR	Z,GMPW4
	CP	'"'		;Closing quote?
	JR	Z,GMPW4
	INC	DE
	LD	(HL),A		;Xfer the character
	INC	HL
	DJNZ	GMPW2
	JR	GMPW5
;
;	Not entered as parm, grab from keyboard
;
GMPW3	@@DSPLY			;Display request
	LD	BC,8<8		;Max 8 chars input
	LD	HL,BUF3$	;Point to buffer
	PUSH	HL
	@@KEYIN			;Grab password
	JP	C,ABRTBU	;Abort on BREAK
	EX	DE,HL		;Buf start to DE
	LD	H,0		;Buf length to HL
	LD	L,B
	ADD	HL,DE		;Pt to 1st unused pos
	LD	A,8		;Calculate spaces needed
	SUB	B
	JR	Z,GMPW5		;Don't put any if 8 input
	LD	B,A		;Set space counter
GMPW4	LD	(HL),' '
	INC	HL
	DJNZ	GMPW4
GMPW5	POP	HL		;Rcvr pointer to buf
	PUSH	HL
	LD	B,8		;Loop thru field
GMPW6	LD	A,(HL)
	CP	'a'
	JR	C,GMPW7
	CP	'z'+1
	JR	NC,GMPW7
	RES	5,(HL)		;Lc -> UC
GMPW7	INC	HL
	DJNZ	GMPW6
	POP	DE		;Rcvr pointer to start
	RET
;
;	Check a drive for availability
;
CKDRV
	LD	A,(CURDSK+1)	;P/u drive spec
	LD	C,A		;Place in C
	LD	A,(IY+0)	;P/u drive vector
	CP	0C3H		;Ck for enabled
	JP	NZ,CKDR5	;Bypass if disabled
	PUSH	HL
	PUSH	DE
	LD	A,(IY+6)	;Make sure the current
	CP	(IY+5)		;  cylinder count is in range
	JP	NC,CKDRV1	;Go if in range
	CALL	RESTOR		;Restore drive
	JP	NZ,CKDR7A	;Go if error
;
CKDRV1	LD	D,(IY+5)	;P/u current track
	LD	E,0		;Set for sector 0
	@@SEEK			;Set track info to FDC
	JP	NZ,CKDR7A	;Go if error
	CALL	RSELCT		;Wait until not busy
	JR	NZ,CKDR7A	;Not there - ret NZ
	BIT	3,(IY+3)	;If hard drive, bypass
	JR	NZ,CKDR2B	;  GAT data update
	BIT	4,(IY+4)	;If "ALIEN" by pass
	JR	NZ,CKDR2B	;  test of index pulses
	IF	@MOD4
	LD	A,09		;Set MSB of count down
	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
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
	LD	D,(IY+9)
	LD	HL,CKDRBUF	;Point to HIT buffer
	LD	E,L		;Sector 0 for GAT
	@@RDSSC			;Read the GAT
	JR	NZ,CKDR7	;Jump on error
;
;	Update YFLAG$ for year type
;
	LD	DE,(CKDRBUF+0CCH)	;Gat type byte
	LD	A,C		;Drive # to A
	RLCA			;Rotate it into posn
	RLCA			;For SET x,(hl)
	RLCA			; opcode
	OR	0C4H		;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,$-$		;Pt to year type flag
YFLAG1	EQU	$-2		;YFLAG$ locn
	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
	ADD	A,A		;Write prot to carry flg
;
CKDR4	EQU	$
	EI
	POP	DE
	POP	HL
CKDR5	RET
INDEX	LD	A,H		;Count down tries
	OR	L
	JR	Z,CKDR7		;Error if counted out
	DEC	HL		;Dec the count
	CALL	RSELCT		;Check for index pulse
	BIT	1,A		;Test index
	RET			;Back with condition
CKDR7	POP	AF
CKDR7A	LD	A,8		;Set Device not avail
	OR	A		;Set NZ ret
	JR	CKDR4		;Leave
;
;	Data area
;
PRMTBL$
VAL	EQU	80H
SW	EQU	40H
STR	EQU	20H
SGL	EQU	10H
	DB	'S'!80H
	DB	SW!STR!3,'MPW',0
MPWRSP	EQU	$-PRMTBL$-1
	DW	MPWPRM
	DB	SW!STR!SGL!3,'SYS',0
SYSRSP	EQU	$-PRMTBL$-1
	DW	SYSPRM+1
	DB	SW!SGL!3,'INV',0
INVRSP	EQU	$-PRMTBL$-1
	DW	INVPRM+1
	DB	SW!SGL!3,'MOD',0
MODRSP	EQU	$-PRMTBL$-1
	DW	MODPRM$
	DB	SW!SGL!5,'QUERY',0
QRSP	EQU	$-PRMTBL$-1
	DW	QPARM$
	DB	SW!1,'X',0
XRSP	EQU	$-PRMTBL$-1
	DW	XPARM$+1
	DB	STR!SGL!4,'DATE',0
DATRSP	EQU	$-PRMTBL$-1
	DW	DATPRM+1
	DB	SW!SGL!3,'NEW',0
NEWRSP	EQU	$-PRMTBL$-1
	DW	NEWPRM$
	DB	SW!SGL!3,'OLD',0
OLDRSP	EQU	$-PRMTBL$-1
	DW	OLDPRM$
	NOP
;
NOINDO$	DB	'Single drive backup invalid during'
	DB	' <DO> processing',CR
NOFMT$	DB	'Destination disk not formatted'
	DB	' - Backup aborted',CR
HELLO$	DB	'BACKUP'
*GET	CLIENT:3
LDOS$	DB	'Command executes only from DOS Ready',CR
PRMERR$	DB	'Parameter error',CR
SRCNUM$	DB	'Source drive number ?        ',3
DSTNUM$	DB	'Destination drive number ?   ',3
NODAT$	DB	'No date established',CR
CLASS$	DB	'Backup by class invoked',CR
CLS1DB$	DB	LF,'Single drive BACKUP invalid by files',CR
	IF	.NOT.SMALL
RES$	DB	'SYSRES'	;Terminate with LF
RESREQ$	DB	LF,'This backup requires residency '
	DB	'of SYS''s: 2, 3, 10 & 12.',CR
	ENDIF
	IF	SMALL
RESREQ$	DB	'Backup by class requires the us'
	DB	'e of a SYSTEM diskette!       ',CR
	ENDIF
RECON$	DB	'Backup-reconstruct invoked',CR
MIRROR$	DB	'Cylinder count differs - '
	DB	'Attempt mirror-image backup ? ',3
PMTMPW$	DB	'Master password ?      ',3
MAXDAYS	DB	31,28,31,30,31,30,31,31,30,31,30,31
BADFMT$	DB	'Bad date format',CR
CKDRBUF	EQU	$<-8+1<8
	DS	256
LAST	EQU	$
