; READ SNAPSHOT FROM MS-DOS 720KB DISKS ON SPECTRUM +3
; DRIVE B: *MUST* BE A 3.5" DRIVE
; PART ONE THE *SNAPSHOT LOADER*
; THIS PART RESIDES IN THE SCREEN AND WILL LOAD THE FIRST 8 CLUSTERS 
; TO RAM PAGE 1, THIS IS DUE TO THE SYSTEM-VARIABLES AT #5B00...
;
; THANKS TO RICHARD SWANN FOR THE +D VERSION
; +3DOS VERSION BY THOMAS KJAER
; EMAIL TAKJAER@DAIMI.AAU.DK
;
	ORG	16384
PART2	EQU	#004C
PART3	EQU	#0017
;
PT2ST	EQU	60000	; START OF PART 2 (IN PAGE 01)
;
FAT	EQU	#8000	; WHERE TO STORE THE MS-DOS FAT TABLE
MAIN	LD	SP,FAT-1	; SAFE FOR SP AT FIRST PLACE
	CALL	DOSIN
	CALL	#0100	; DOS INIT
; FIRST FREE RAM PAGE 1
	LD	D,32	; START OF CACHE
	LD	E,64	; NUMBER OF CACHE SECTORS
	LD	H,96	; FIRST SECTOR FOR RAMDISK
	LD	L,32	; LENGTH OF RAMDISK IN SECTORS
	CALL	#013F	; DOS SET 1346
	CALL	DOSOUT	; SWITCH OUT THE DOS ENVIROMENT
; THEN MOVE PART2+3 TO PAGE 1
	CALL	RAM01
	LD	HL,#4800	; PART2+3
	LD	DE,PT2ST
	LD	BC,PART2+PART3	; MOVE PART2+3 TO RAM 01
	LDIR
	CALL	DOSOUT
; HERE THE ACTION STARTS!!! :)
	LD	HL,(#5C76)	; SEED, HL=START CLUSTER
	CALL	CHAIN	; FIND THE CLUSTER CHAIN OF THE FILE
	LD	A,#01	; FIRST 15 CLUSTERS MUST BE LOADED IN PAGE 01
	LD	(TMPPAG),A	
	LD	IX,#C000	; WHICH WILL BE PAGED AT C000..
	LD	HL,CHNPTR	; POINT TO FIRST CLUSTER NUMBER
	LD	B,#08	; READ FIRST 8 CLUSTERS
; READ FIRST 8 CLUSTERS IN PAGE 01
MAIN1	PUSH	BC
	CALL	RDCLST	
	POP	BC
	DJNZ	MAIN1
; SHOW AS MUCH OF THE SCREEN AS POSSIBLE
	PUSH	HL
	CALL	RAM01	; PAGE IN RAM 01
	LD	HL,#C400	; GET MOST OF SCREEN
	LD	DE,#43E5	; N.B. THIS PART MUST NOT BE LONGER THAN #3E5
	LD	BC,#171B	; LENGTH
	LDIR
	LD	A,(#C01A)	; GET BORDER
	OUT	(#FE),A	; AND SET IT RIGHT
	CALL	DOSOUT	; GET RAM 0 AGAIN
	POP	HL
; LOAD REST OF SNAPSHOT
	LD	SP,#5000	; SAFE PLACE FOR SP. D_FILE :)
	XOR	A
	LD	(TMPPAG),A	; USE PAGE 0 AT C000...
	LD	IX,24549	; WHERE TO LOAD REST OF CLUSTERS
	LD	B,#29	; 41 CLUSTERS LEFT
LOOP	PUSH	BC
	CALL	RDCLST	; READ CLUSTER INTO MEMORY
	POP	BC
	DJNZ	LOOP	; JUMP BACK, UNTIL ALL LOADED
	CALL	RAM01	; PAGE IN RAM 01 AND TURN OFF DISK-MOTOR
	DI	; PREVENT DATA CORRUPTION
	JP	PT2ST	; JUMP TO LAST CODE MOVER, PART2
TMPPAG	DEFB	0	; WHICH PAGE AT C000.. DURING LOAD
;
; READ THE CLUSTER POINTED TO BY HL, INTO MEMORY AT ADDRESS IX
; ON ENTRY IX=DESTINATION ADDRESS OF CLUSTER
;          HL=POINTER TO CLUSTER NUMBER
; ON EXIT  IX=DEST.ADDR+1024
;          HL=POINTER TO NEXT CLUSTER NUMBER
RDCLST	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	INC	HL	; DE=CLUSTER
	PUSH	HL	; SAVE UPDATED POINTER TO CLUSTER CHAIN
	DEC	DE
	DEC	DE	; CLUSTER=CLUSTER-2
	SLA	E
	RL	D	; CLUSTER=(CLUSTER-2)*2 (2 SEC/CLUST)
	EX	DE,HL	; HL=REL.SECTOR NUMBER
	LD	DE,#0105	; DATA STARTS AT T/S=1/5
RDCLNX	LD	A,H
	OR	L
	JR	Z,RDCLEN	; IF WE ARE FINISH, THEN JUMP TO END
	DEC	HL	; UPDATE REL.SECTOR
	INC	E	; UPDATE PHYSICAL SECTOR
	LD	A,E
	CP	#09	; SHIFTING TRACK?
	JR	NZ,RDCLNX	; NO, JUMP BACK
	INC	D	; UPDATE PHYSICAL TRACK
	LD	E,#00	; RESET PHYSICAL SECTOR
	JR	RDCLNX	; AND JUMP BACK TO CONTINUE CALCULATION
; NOW DE=TRACK/SECTOR FOR FIRST SECTOR OF CLUSTER
; A CLUSTER CONTAINS 2 SECTORS
RDCLEN	PUSH	IX	; SAVE DESTINATION ADDRESS   
	PUSH	DE	; SAVE TRACK/SECTOR
	LD	A,(TMPPAG)	; GET PAGE
	LD	B,A
	LD	A,#01	; DRIVE B:
	CALL	SEC_RD	; READ THE SECTOR
	POP	DE	; GET CURRENT TRACK/SECTOR
	INC	E	; INCREASE SECTOR
	LD	A,E
	CP	#09	; DO WE HAVE TO SHIFT TRACK?
	JR	NZ,RDNXCL	; NO, SO JUST JUMP AND READ THE NEXT SECTOR
; INCREASE TRACK
	LD	E,#00	; RESET PHYSICAL SECTOR 
	INC	D	; UPDATE PHYSICAL TRACK
; READ THE SECOND SECTOR IN CLUSTER
RDNXCL	POP	IX	; GET DESTINATION ADDRESS  
	DEFB	#DD
	INC	H	; INC IXh
	DEFB	#DD
	INC	H	; INC IXh
	PUSH	IX	; SAVE NEW DESTINATION
	LD	A,(TMPPAG)
	LD	B,A	; B=PAGE
	LD	A,#01	; DRIVE B:
	CALL	SEC_RD	; READ SECTOR
	POP	IX
	DEFB	#DD
	INC	H	; INC IXh
	DEFB	#DD
	INC	H	; INC IXh
	POP	HL	; GET BACK THE POINTER TO THE CHAIN LIST
	RET	; RETURN TO CALLER
;
; CHAIN FIND THE CHAIN FOR THE FILE'S CLUSTERS (31 IN ALL) STARTING FROM HL
; FIRST LOAD THE FAT TABLE AT #8000, THE FAT IS 3 SECTORS STARTING AT
; TRACK/SECTOR=0/1
CHAIN	PUSH	HL	; SAVE START CLUSTER
	LD	IX,FAT
	LD	DE,#0001	; TRACK/SECTOR=0/1
	LD	A,#01	; USE DRIVE B:
	LD	B,#00	; AND RAM0 (NORMAL BASIC PAGE)
	CALL	SEC_RD	; READ THE FIRST SECTOR
;
	LD	IX,FAT+#0200
	LD	DE,#0002	; TRACK/SECTOR=0/1
	LD	A,#01	; USE DRIVE B:
	LD	B,#00	; AND RAM0 (NORMAL BASIC PAGE)
	CALL	SEC_RD	; READ THE SECOND SECTOR
;
	LD	IX,FAT+#0400
	LD	DE,#0003	; TRACK/SECTOR=0/1
	LD	A,#01	; USE DRIVE B:
	LD	B,#00	; AND RAM0 (NORMAL BASIC PAGE)
	CALL	SEC_RD	; READ THE THIRD SECTOR
;
	LD	IX,CHNPTR	; POINTER TO LIST OF CLUSTERS
	POP	HL	; RETRIEVE START CLUSTER
	LD	B,#31	; 49 CLUSTERS=50176 BYTES
CHNLP	LD	(IX+#00),L
	LD	(IX+#01),H	; STORE CURRENT CLUSTER
	INC	IX
	INC	IX	; UPDATE POINTER TO CHAIN LIST
	CALL	NXTCHN	; AND FIND THE NEXT CLUSTER FROM HL
	LD	A,H
	AND	#0F
	LD	H,A
	DJNZ	CHNLP	; FIND ALL 49
	RET
;
; FINDS THE NEXT CLUSTER FROM THE CURRENT IN HL
NXTCHN	PUSH	IX	; SAVE THE LIST-POINTER  
	LD	IX,FAT
	SRL	H
	RR	L	; DIVIDE CLUSTER BY 2
	PUSH	AF	; AND SAVE THE ARITY
NXTMLT	INC	IX
	INC	IX
	INC	IX
	DEC	HL
	LD	A,H
	OR	L
	JR	NZ,NXTMLT
	POP	AF	; IX=CLUSTER*1.5+FAT
	JR	C,NXTODD	; JUMP IF THE CLUSTER WAS ODD

; THE CLUSTER WAS EVEN, SO JUST KEEP LOW ORDER 12 BITS OF ENTRY
NXTEVN	LD	A,(IX+#01)	
	AND	#0F
	LD	H,A
	LD	L,(IX+#00)
	JR	NXTEND
; THE CLUSTER WAS ODD
NXTODD	LD	A,(IX+#02)
	SRL	A
	SRL	A
	SRL	A
	SRL	A
	LD	H,A
	LD	A,(IX+#02)
	SLA	A
	SLA	A
	SLA	A
	SLA	A
	PUSH	AF
	LD	A,(IX+#01)
	AND	#F0
	SRL	A
	SRL	A
	SRL	A
	SRL	A
	LD	L,A
	POP	AF
	ADD	A,L
	LD	L,A
NXTEND	POP	IX	; RESTORE POINTER TO LIST
	RET

; SECTOR READ
; 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.
;
SEC_RD	JR	SECRUN	; JUMP TO MAIN FOR SECTOR READ
;
; 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
SECRUN	LD	C,A
	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,(DRIVE)	; GET DRIVE
	ADD	A,"A"           ; EITHER 'A' OR 'B'
	CALL	#0151	; DOS REF XDPB
	LD	(XDPB),IX	; SAVE ADDR. OF PARAMETRE BLOCK
	LD	A,(DRIVE)
	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
	PUSH	AF
	PUSH	BC
	CALL	#0199	; TURN OFF MOTOR
	CALL	DOSOUT	; PAGE OUT THE +3DOS ENVIROMENT
; NOW TEST IF SPACE PRESS, AND IF SO DO A 'REAL' RESET
	LD	A,#7F
	IN	A,(#FE)
	RRA
	JR	NC,RESET	; JUMP TO RESET IF SPACE PRESSED
	POP	BC
	POP	AF
	RET	; AND RETURN TO CALLER
; THIS RESETS THE SPECTRUM LIKE WHEN THE SWITCH IS PLUGGED IN
RESET	DI
	XOR	A	; USE ROM 0
	LD	BC,PORTM
	OUT	(C),A
	LD	BC,PORT6
	OUT	(C),A	; AND SELECT IT 
	RST	0	; JUMP TO COLD BOOT
;
; VARIABLES FOR MEMORY PAGEING
BANK6	EQU	#5B67
PORT6	EQU	#1FFD
BANKM	EQU	#5B5C
PORTM	EQU	#7FFD
; 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
; PAGE IN RAM PAGE 1 AT C000..  AND TURN OFF DISC MOTOR
; ALL REGISTERS PRESERVED
RAM01	DI
	PUSH	BC
	PUSH	AF
	LD	A,(BANK6)
	RES	3,A	
	LD	(BANK6),A
	LD	BC,PORT6
	OUT	(C),A	; TURN OFF MOTOR
	LD	A,(BANKM)
	AND	#F8
	OR	#01	; RAM PAGE 1
	LD	(BANKM),A	; STORE COMBINATION
	LD	BC,PORTM
	OUT	(C),A
	POP	AF
	POP	BC
	EI
	RET
; MISC VARS AND BUFFERS
CHNPTR	DEFS	49*2	; 49 CLUSTERS IN CHAIN
END
LEN	EQU	END-MAIN

