; PC .SNA VIEWER-MENU FOR THE ZX-SPECTRUM +3/+2A WITH A 3.5" DISK       
; CONNECTED AS THE B DRIVE.
; (C)-1994 BY THOMAS KJAER
; THE PROGRAM WILL DISPLAY ALL PROGRAMS ON THE 720KB DISK, WITH THE 
; FILENAME '*.SNA' , THEN A MENU-BAR CAN BE MOVED TO SELECT THE REQUIRED 
; PROGRAM, ON RETURN BC=FIRST CLUSTER FOR SELECTED FILE
; THIS PROGRAM SHOULD BE USED WITH THE PC .SNA READER FOR SPECTRUM +3
;
; ***************************************************************************
	ORG	26000
START	JP	GETDIR	; JUMP OVER DATA AREA
;
; MISC VARIABLES
;
CATBUF	EQU	#6D00	; BUFFER FOR .SNA FILE CATALOG, LENGTH=13*112 BYTES
DOSCAT	EQU	#7200	; BUFFER FOR MS-DOS DIRECTORY, LENGTH=7 SECTORS
TOPBAR	EQU	4	; Y-COORD. FOR TOP POSITION OF BAR 
BOTBAR	EQU	21	; Y-COORD. FOR BOTTOM POSITION OF BAR
GLOBCL	EQU	40	; GLOBAL ATTRIBUE VALUE, CYAN PAPER, BLACK INK
ENTLNG	EQU	13	; LENGTH OF EVERY ENTRY IN .SNA CATALOG
;
; DATA AREA
;
LASTNO	DEFB	0	; NUMBER OF THE LAST FILE
FILENO	DEFB	0	; NUMBER OF THE FILE HIGHLIGHTED
BARPOS	DEFB	0	; CURRENT BAR POSITION
SCRBIT	DEFB	0	; 0=NO SCREEN UPDATE
FSTPRT	DEFB	0	; FILENO TO PRINT
Y_COOR	DEFB	0	; ROW FOR PRINT POSITION
;
; MISC STRINGS
;
TITLE	DEFM	"PC .SNA LOADER FOR SPECTRUM +3" ; TITLE STRING
	DEFB	0
STATUS	DEFM	"Q=UP A=DOWN ENT=LOAD L=NEW DISK" ; STATUS STRING
	DEFB	0
NOFILM	DEFM	"NO FILES FOUND, INSERT NEW DISK AND PRESS ANY KEY"
	DEFB	0
VOLUME	DEFM	"DISK VOLUME: "
LABEL	DEFS	11	; SPACE FOR DISK-LABEL
	DEFB	0
;
; ***************************************************************************
; MAIN ENTERY, CALLED EVERY TIME A NEW DISK WILL BE LOGGED IN
; ***************************************************************************
GETDIR	CALL	RD_DIR	; GET THE MS-DOS DIRECTORY AREA TO DOSCAT
;
; NOW MOVE ALL .SNA FILES TO CATBUF
	XOR	A
	LD	(LASTNO),A	; RESET NUMBER OF FILES
	LD	B,112	; 112 ENTRIES IN DOS ROOT DIRECTORY
	LD	IX,DOSCAT	; MS-DOS DIRECTORY
	LD	DE,CATBUF	; WHERE TO PLACE THE NAMES
;
; EXAMINE EVERY ENTRY IN DIRECTORY       
; BUT FIRST CHECK IF ENTRY IS THE DISK-LABEL
;
NXTLOP	LD	A,(IX+#0B)	; GET ATTRIBUTE FOR ENTRY
	BIT	3,A	; IS IT THE DISK-LABEL?
	JR	Z,CHKNAM	; NO, SO CONTINUE TO EXAMINE THE ENTRY
;
; O.K WE GOT THE DISK-LABEL, MOVE IT SO IT CAN BE PRINTED LATER
;
	PUSH	BC
	PUSH	DE
	PUSH	IX
	POP	HL
	LD	DE,LABEL
	LD	BC,11
	LDIR
	POP	DE
	POP	BC
	JR	NXTENT
CHKNAM	LD	A,(IX)	; GET FIRST BYTE IN FILENAME
	CP	#00	; FILENAME MARKED AS NEVER USED?
	JR	Z,NXTENT	; YES, FIND NEXT ENTRY
	CP	#2E	; FILENAME MARKED AS SUB-DIRECTORY?
	JR	Z,NXTENT	; YES, FIND NEXT ENTRY
	CP	#E5	; FILENAME MARKED AS DELETED?
	JR	Z,NXTENT	; YES, FIND NEXT ENTRY
;
; CHECK THAT THE FILENAME HAS THE EXTENSTION .SNA
;
	LD	A,(IX+8)
	CP	"S"
	JR	NZ,NXTENT
	LD	A,(IX+9)
	CP	"N"
	JR	NZ,NXTENT
	LD	A,(IX+10)
	CP	"A"
	JR	NZ,NXTENT
;
; FILENAME O.K. SO MOVE IT TO THE CATBUF, AND UPDATE NUMBER OF FILES
;        
	LD	A,(LASTNO)
	INC	A
	LD	(LASTNO),A	; UPDATE NUMBER OF FILES
	PUSH	IX
	POP	HL	; HL=POINTER TO FILENAME, DE=WHERE TO PUT IT
	PUSH	BC
	LD	BC,#000B	; #0B=LENGTH OF FILENAME
	LDIR	; MOVE IT
	LD	A,(IX+26)	; GET STARTING CLUSTER FOR FILE
	LD	(DE),A
	INC	DE
	LD	A,(IX+27)	
	LD	(DE),A	
	INC	DE	; AND STORE IT
	POP	BC	; RESTORE THE COUNTER
;
; UPDATE IX TO POINT TO NEXT ENTRY IN DIRECTORY
;
NXTENT	PUSH	BC	
	LD	BC,#0020	; EVERY ENTRY IS 32 BYTES LONG
	ADD	IX,BC
	POP	BC
	DJNZ	NXTLOP
;
; CHECK IF ANY .SNA FILES WAS FOUND
;        
	LD	A,(LASTNO)	
	AND	A
	JP	Z,NOFILE ; GIVE MESSAGE IF NO FILES FOUND
;
; ***************************************************************************
; CLEAR SCREEN AND PRINT TITLE         
; ***************************************************************************
SCRINI	LD	A,GLOBCL	
	CALL	CLS	; CLEAR SCREEN AND SET COLOR
	AND	%00111000
	RRCA
	RRCA
	RRCA	; FETCH THE PAPER VALUE
	OUT	(#FE),A	; AND SET THE BORDER
	LD	BC,#0001
	CALL	LOCATE	; HL=ADDR FOR (0,1)
	LD	DE,TITLE
	CALL	PRINTM
	LD	BC,#1700
	CALL	LOCATE
	LD	DE,STATUS
	CALL	PRINTM
	LD	BC,#0200
	CALL	LOCATE
	LD	DE,VOLUME
	CALL	PRINTM
;
; ***************************************************************************
; INIT VARS FOR THE SCROLL-BAR
; ***************************************************************************
	LD	A,#01
	LD	(FILENO),A	; POINT TO FIRST FILE
	LD	A,TOPBAR	; BAR AT TOP POSITION
	LD	(BARPOS),A
	LD	A,#FF
	LD	(SCRBIT),A	; SIGNAL, SCREEN UPDATE
;
; ***************************************************************************
; THIS IS THE MAIN MENU SECTION
; LOOP
;  UPDATE SCREEN IF (SCRBIT)<>0
;  DRAW SCROLL-BAR, INVERSE WITH BRIGHT
;  READ KEY
;  REMOVE SCROLL-BAR
;  ACTION(KEY)
; END LOOP
; ***************************************************************************
MAIN	LD	A,(SCRBIT)
	OR	A
	JR	Z,NOUPDT	; IF (SCRBIT)=0, NO SCREEN UPDATE
	CALL	SHOWNX	; SHOW THE NEXT ENTRIES
NOUPDT	CALL	BAR
	HALT
	CALL	GETKEY
	PUSH	AF	; SAVE KEY VALUE
	HALT
	CALL	NOBAR	
	POP	AF	; GET KEY VALUE
	RES	5,A	; FORCE UPPERCASE
	CP	"A"
	JR	Z,DOWN
	CP	"Q"
	JR	Z,UP
	CP	13
	JR	Z,RETUR
	CP	"L"
	JP	Z,GETDIR
	JR	MAIN
;
; ***************************************************************************
; THIS ROUTINE MOVES THE SCROLL-BAR DOWN 
; ***************************************************************************
DOWN	LD	A,(LASTNO)
	LD	B,A
	LD	A,(FILENO)
	CP	B
	JP	Z,MAIN	; IF AT LAST FILE, NO ACTION
	INC	A	; ELSE INCREMENT FILENO
	LD	(FILENO),A
	LD	A,(BARPOS)
	CP	BOTBAR
	JR	Z,DOWN1	; IF NOT AT LAST ROW 
	INC	A	; THEN UPDATE BARPOS
	LD	(BARPOS),A	
	XOR	A
	LD	(SCRBIT),A	; AND SIGNAL NO SCREEN UPDATE
	JP	MAIN
DOWN1	LD	A,#FF
	LD	(SCRBIT),A	; SCREEN NEEDS UPDATE
	JR	MAIN	; WHEN BAR IS AT LAST ROW
;
; ***************************************************************************
; THIS ROUTINE MOVES THE SCROLL-BAR UP 
; ***************************************************************************
UP	LD	A,(FILENO)
	CP	1
	JR	Z,MAIN	; IF AT FIRST FILE, NO ACTION
	DEC	A	; ELSE DECREMENT FILENO
	LD	(FILENO),A
	LD	A,(BARPOS)
	CP	TOPBAR
	JR	Z,UP1	; IF NOT AT TOP POSITION
	DEC	A	; THEN UPDATE BARPOS
	LD	(BARPOS),A
	XOR	A
	LD	(SCRBIT),A	; AND SIGNAL NO SCREEN UPDATE
	JP	MAIN
UP1	LD	A,#FF
	LD	(SCRBIT),A	; SCREEN NEEDS UPDATE
	JR	MAIN
;
; ***************************************************************************
; RETURNS TO CALLER WITH STARTING CLUSTER IN BC FOR SELECTED FILE
; ***************************************************************************
RETUR	LD	A,(FILENO)	; FETCH NUMBER OF SELECTED FILE
	DEC	A
	LD	L,A
	LD	H,0
	LD	DE,ENTLNG
	CALL	#30A9	; HL=HL*DE
	LD	DE,CATBUF
	ADD	HL,DE	
	PUSH	HL	
	POP	IX	; HL=POINTER TO ENTRY IN THE CATALOG
	LD	C,(IX+11)
	LD	B,(IX+12)	; BC=STARTING CLUSTER NUMBER
	RET	; RETURN TO BASIC
;
; ***************************************************************************
; THESE ROUTINES HIGHLIGHTS OR UNHIGHLIGHTS
; THE FILENAME AT CURRENT POSITION
; ***************************************************************************
BAR	LD	A,(BARPOS)
	LD	B,A
	LD	C,0
	CALL	LOCATE
;
; CONSTRUCT THE INVERSE ATTRIBUTE
;
	LD	A,GLOBCL
	AND	%00111000	; MASK OUT THE PAPER
	RRCA
	RRCA
	RRCA	; TRANSFER IT TO INK
	LD	B,A
	LD	A,GLOBCL
	AND	%00000111	; MASK OUT INK
	RLCA
	RLCA
	RLCA	; TRANSFER IT TO PAPER
	SET	6,A	; SET THE BRIGHT BIT
	OR	B	; AND ADD THE INK VALUE
BARCOL	LD	B,32
BARNXT	LD	(DE),A
	INC	DE
	DJNZ	BARNXT
	RET
; ***************************************************************************
NOBAR	LD	A,(BARPOS)
	LD	B,A
	LD	C,0
	CALL	LOCATE
	LD	B,32
	LD	A,GLOBCL	; JUST FILL ATTRIBUTE WITH GLOBAL ATTRIBUTE
	JR	BARCOL
;
; ***************************************************************************
; NO FILES FOUND, WRITE MESSAGE AND WAIT FOR USER TO PRESS A KEY
; ***************************************************************************
NOFILE	LD	A,GLOBCL
	CALL	CLS	; CLEAR SCREEN
	LD	BC,#0000	
	CALL	LOCATE
	LD	DE,NOFILM
	CALL	PRINTM	; PRINT MESSAGE
	CALL	GETKEY	; AND WAIT FOR USER TO PRESS A KEY
	JP	GETDIR
;
; ***************************************************************************
; WAIT FOR USER TO PRESS A KEY, RETURN A=ASCII VALUE
; ***************************************************************************
GETKEY	HALT
	RST	#38
	LD	HL,#5C3B	; FLAGS
	BIT	5,(HL)	; NEW KEY PRESSED?
	LD	A,#FF
	JR	Z,GETKEY	; IF NO KEY PRESSED, SCAN AGAIN
	LD	A,(#5C08)	; LAST_K
	RES	5,(HL)	; SIGNAL, KEY READ
	RET
;
; ***************************************************************************
; SHOW NEXT (BOTBAR-TOPBAR)+1 ENTRIES
; ***************************************************************************
SHOWNX	XOR	A
	LD	(SCRBIT),A	; NO SCREEN UPDATE
	LD	A,(BARPOS)
	SUB	TOPBAR
	LD	B,A
	LD	A,(FILENO)
	SUB	B	; A=NUMBER OF FIRST FILE TO PRINT
	LD	(FSTPRT),A
	LD	A,TOPBAR
	LD	(Y_COOR),A
SHOWLP	LD	A,(Y_COOR)
	LD	B,A	; B=ROW TO PRINT ON
	LD	C,10	; COLUMN=12
	CALL	LOCATE	; HL=ADDR. IN D.F FOR (Y,X)=(B,C)
	LD	A,(FSTPRT)	; GET FILENO TO PRINT
	CALL	PRT_FN
	LD	A,(LASTNO)
	LD	B,A
	LD	A,(FSTPRT)
	CP	B
	RET	Z	; RETURN IF LAST NAME PRINTED
	INC	A	; UPDATE FILENO TO PRINT
	LD	(FSTPRT),A
	LD	A,(Y_COOR)
	CP	BOTBAR	
	RET	Z	; RETURN WHEN REACHED BOTTOM LINE
	INC	A
	LD	(Y_COOR),A	; UPDATE ROW TO PRINT ON
	JR	SHOWLP
;
; ***************************************************************************
; PRINT A ZERO END STRING STARTING AT DE
; AT LOCATION GIVEN BY HL=ADDR IN D.F
; ***************************************************************************
PRINTM	LD	A,(DE)
	AND	A
	RET	Z	; A=ZERO, THEN FINISH
	CALL	PRTCHR
	INC	HL
	INC	DE
	JR	PRINTM
;
; ***************************************************************************
; PRINT FILENAME
; ON ENTRY : HL=D_FILE ADR A=FILENO.
; ***************************************************************************
PRT_FN	PUSH	HL
	DEC	A
	LD	L,A
	LD	H,0
	LD	DE,ENTLNG	
	CALL	#30A9	; HL=HL*DE
	LD	BC,CATBUF
	ADD	HL,BC
	EX	DE,HL
	POP	HL
	LD	B,8
; NOW PRINT EVERY CHARACTER IN THE NAME, STARTING AT DE
NEXCFN	LD	A,(DE)
	CALL	PRTCHR
	INC	HL
	INC	DE
	DJNZ	NEXCFN
; THE DOT
	LD	A,"."
	CALL	PRTCHR
	INC	HL
; AND AT LAST THE EXTENSION
	LD	B,3	
NEXCFE	LD	A,(DE)
	CALL	PRTCHR
	INC	HL
	INC	DE
	DJNZ	NEXCFE
	RET
;
; ***************************************************************************
; PRINT CHAR IN A, AT LOCATION GIVEN BY HL=ADDR IN D.F
; PRESERVED: HL,BC,DE
; ***************************************************************************
PRTCHR	PUSH	HL
	PUSH	DE
	PUSH	BC
	EX	DE,HL
	LD	L,A
	LD	H,0
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	LD	BC,(23606)
	ADD	HL,BC
	LD	B,8
PRTNXT	LD	A,(HL)
	LD	(DE),A
	INC	D
	INC	HL
	DJNZ	PRTNXT
	POP	BC
	POP	DE
	POP	HL
	RET
;
; ***************************************************************************
; CLS
; ENTRY: A=SCREEN ATTRIBUTE
; EXIT : BC=0, DE=#5B00, HL=#5AFF
; PRESERVED: A
; ***************************************************************************
CLS	LD	HL,#4000
	LD	BC,#1800
	LD	(HL),L
	LD	D,H
	LD	E,1
	LDIR
	LD	(HL),A
	LD	BC,#02FF
	LDIR
	RET
;
; ***************************************************************************
; LOCATE
; ENTRY: B=LINE, C=COLUMN
; EXIT : HL=D.F. ADDR, DE=ATTR. ADDR
; PRESERVED: BC
; ***************************************************************************
LOCATE	LD	A,B
	AND	#18
	LD	H,A
	SET	6,H
	RRCA
	RRCA
	RRCA
	OR	#58
	LD	D,A
	LD	A,B
	AND	7
	RRCA
	RRCA
	RRCA
	ADD	A,C
	LD	L,A
	LD	E,A
	LD	A,(DE)
	RET
;
; ***************************************************************************
; ***************************************************************************
; THE NEXT ROUTINES ARE SPECIFIC FOR THE SPECTRUM +3/2A
; BUT CAN VERY EASY BE IMPLEMENTED FOR OTHER DISK SYSTEMS LIKE THE +D
; ***************************************************************************
; GET DIRECTORY AREA FROM MS-DOS 720KB DISK, STARTING AT T/S=0/7 (BASE 0) 
; ESSENTIAL, CALLED EVERY TIME NEW DISK HAS TO BE LOGGED IN 
;
; DIRECTORY AREA LENGTH = 7 SECTORS = 7*512 BYTES
; ***************************************************************************
RD_DIR	LD	HL,LABEL
	LD	DE,LABEL+1
	LD	BC,10
	LD	A,32
	LD	(HL),A
	LDIR	; PAD DISK-LABEL WITH SPACES (32dec)
; 
	LD	A,#01	; USE DRIVE B:
	LD	B,#00	; USE PAGE #00 AT #C000...
	LD	IX,DOSCAT	; WHERE TO PUT FIRST SECTOR OF MS-DOS DIRECTORY
	LD	DE,#0007	; DIRECTORY STARTS AT TRACK/SECTOR=0/7
	CALL	SEC_RD	; READ THE SECTOR
;
	LD	A,#01
	LD	B,#00
	LD	IX,DOSCAT+#0200
	LD	DE,#0008
	CALL	SEC_RD
;
	LD	IX,DOSCAT+#0400
	LD	A,#00	; COUNTER FOR SECTOR
RD_LOP	PUSH	AF
	LD	D,#01
	LD	E,A
	LD	B,#00
	LD	A,#01	; DRIVE        
	PUSH	IX
	CALL	SEC_RD
	POP	IX
	POP	AF
	LD	BC,#0200
	ADD	IX,BC
	INC	A
	CP	#05
	JR	NZ,RD_LOP	; GET UNTIL TRACK/SECTOR=1/4(LAST DIR-SECTOR)       
	RET	; AND RETURN TO CALLER
;
; ***************************************************************************
; NEXT COMES SOME DISK ACCESS AND MEMORY PAGEING ROUTINES USED BY THE
; +3/2A VERSION OF RD_DIR
; ***************************************************************************
; SECTOR READ FOR SPECTRUM +3/2A
; EQUAL TO THE +D HOOKCODE 68dec LOAD @...
; ENTRY: A=DRIVE (0/1=A/B)
;        B=RAM PAGE AT #C000..
;        DE=TRACK/SECTOR (BASE 0) !!! +D USE 1 AS BASE
;        IX=DESTINATION ADDRESS FOR SECTOR
; EXIT : IF O.K CARRY SET
;        ELSE CARRY RESET AND A=ERROR CODE FROM DOS.
; ***************************************************************************
;
; VARIABLES FOR SEC_RD
;
DRIVE	DEFS	1	; DRIVE
PAGEC0	DEFS	1	; PAGE AT #C000..
SECTOR	DEFS	1	; SECTOR
TRACK	DEFS	1	; TRACK
BUFFER	DEFS	2	; ADDR. OF BUFFER
XDPB	DEFS	2	; ADDR. OF XDPB
;
; ***************************************************************************
; MAIN FOR SECTOR READ
; ***************************************************************************
SEC_RD	LD	C,#01	; ALWAYS USE DRIVE 1 (B:)
	LD	(DRIVE),BC	; SAVE PAGE/DRIVE
	LD	(SECTOR),DE	; SAVE TRACK/SECTOR
	LD	(BUFFER),IX	; SAVE BUFFER ADDRESS
	CALL	DOSIN	; PAGE IN THE +3DOS ENVIROMENT
;
; SET FORMAT AND LOGIN DRIVE
;        
	LD	A,'B'	; USE DRIVE B:
	CALL	#0151	; DOS REF XDPB
	LD	(XDPB),IX	; SAVE ADDR. OF PARAMETRE BLOCK
;
; THE DRIVE IS B: AND WE HAVE TO FAKE THAT IT HAS BEEN SEEN, SINCE
; THE +3DOS WON'T RECOGNIZE THE MS-DOS FORMAT
;        
	LD	A,#03	; DISK TYPE=AMSTRAD PCW - DDDSDT (720Kb)
	CALL	#0178	; DD SEL FORMAT (FOR THIS DRIVE)
;
; READ THE SECTOR
;
SEC_DO	LD	BC,(DRIVE)	; BC=PAGE AT C000.../UNIT
	LD	DE,(SECTOR)	; DE=TRACK/SECTOR
	LD	HL,(BUFFER)	; BUFFER
	LD	IX,(XDPB)	; AND XDPB
	CALL	#0163	; DD READ SECTOR
	CALL	#0199	; TURN OFF MOTOR
	CALL	DOSOUT	; PAGE OUT THE +3DOS ENVIROMENT
	RET	; AND RETURN TO CALLER
;
; ***************************************************************************
; MISC VARIABLES FOR MEMORY PAGEING ON THE +3/2A
; ***************************************************************************
BANKM	EQU	23388
BANK67	EQU	23399
PORTM	EQU	#7FFD
PORT67	EQU	#1FFD
;
; ***************************************************************************
; DOSIN, PAGE THE ENVIROMENT FOR +3DOS
; ALL REGISTERS PRESERVED
; ***************************************************************************
DOSIN	DI	
	PUSH	BC
	PUSH	AF
	LD	BC,PORTM
	LD	A,(BANKM)
	OR	#07	; RAM #07
	RES	4,A	; ROM #02
	LD	(BANKM),A	; SAVE COMBINATION
	OUT	(C),A	; AND SWITCH
	POP	AF
	POP	BC
	EI
	RET
;
; ***************************************************************************
; DOSOUT, PAGEOUT THE +3DOS ENVIROMENT 
; ALL REGISTERS PRESERVED
; ***************************************************************************
DOSOUT	DI
	PUSH	BC
	PUSH	AF
	LD	BC,PORTM
	LD	A,(BANKM)
	AND	#F8	; RAM #00
	SET	4,A	; ROM #03
	LD	(BANKM),A	; SAVE COMBINATION
	OUT	(C),A	; AND SWITCH
	POP	AF
	POP	BC
	EI
	RET
;
***************************************************************************
END	DEFB	0
