;####################################################################
; Modul fuer IDE-Karte
;
; Entwickelt aus IDETOOL von Alfred Klichs 2022,;
; Paul's 8051 Code Library, Paul Stoffregen 2005
; https://www.pjrc.com/tech/8051/ide/
;
;
; Assembler 8085-Simulator-ID V4.93 Oshonsoft (Vladimir Soso)
; Editor 	Notepad++ TAB=6
;
;
; Stand:
;	18.02.2023	jetzt vollstaendig mit den IDE-Routinen
;	12.02.2023	Aufgeraeumt
;	24.01.2023	uebernommen aus FDC-Routinen vom 23.01.2023
;
;--------------------------------------------------------------------
;
; IO-Adressen fuer PIO 8255
;
PIO_IDE		EQU	058H		; PIO I/O Adresse
;
PIO_LSB		EQU	0 + PIO_IDE ; Port A
PIO_MSB		EQU	1 + PIO_IDE ; Port B
PIO_CTL		EQU	2 + PIO_IDE ; Port C
PIO_CFG		EQU	3 + PIO_IDE ; I/O modus
;
PIO_RDW		EQU	10010010B 	; Port A+B input, Port C output
PIO_WR		EQU	10000000B 	; Port A+B+C output
;
IDE_DRV		EQU	11100000B 	; select the device als LBA Master
			; x1x00000b LBA master
			; x1x10000b LBA slave
			; x0x00000b CHS master
			; x0x10000b CHS slave
;			; Kanal B als Ausgabe
;
;--------------------------------------------------------------------
;
; Variable fuer IDE Funktionen
;
IDE_A0		EQU	00000001B 	; A0 direct to IDE HDD
IDE_A1		EQU	00000010B 	; A1 dto.
IDE_A2		EQU	00000100B 	; A2 dto.
IDE_CS0		EQU	00001000B 	; CS0 to Inverter
IDE_CS1		EQU	00010000B 	; CS1 dto.
IDE_WR		EQU	00100000B 	; WR dto.
IDE_RD		EQU	01000000B 	; RD dto.
IDE_RST		EQU	10000000B 	; RST dto.
;
IDE_DATA		EQU	IDE_CS0
IDE_ERR		EQU	IDE_CS0 + IDE_A0
IDE_SEC_CNT		EQU	IDE_CS0 + IDE_A1
IDE_SECTOR		EQU	IDE_CS0 + IDE_A1 + IDE_A0
IDE_CYL_LSB		EQU	IDE_CS0 + IDE_A2
IDE_CYL_MSB		EQU	IDE_CS0 + IDE_A2 + IDE_A0
IDE_HEAD		EQU	IDE_CS0 + IDE_A2 + IDE_A1
IDE_COMMAND		EQU	IDE_CS0 + IDE_A2 + IDE_A1 + IDE_A0
IDE_STATUS		EQU	IDE_CS0 + IDE_A2 + IDE_A1 + IDE_A0
IDE_CONTROL		EQU	IDE_CS1 + IDE_A2 + IDE_A1
IDE_ASTATUS		EQU	IDE_CS1 + IDE_A2 + IDE_A1 + IDE_A0
;
IDE_CMD_RECAL	EQU	010H
IDE_CMD_READ	EQU	020H
IDE_CMD_WRITE	EQU	030H
IDE_CMD_INIT	EQU	091H
IDE_CMD_ID		EQU	0ECH
IDE_CMD_SPINDN	EQU	0E0H
IDE_CMD_SPINUP	EQU	0E1H
;
;===========================================================
;
; IDE-karte lesen/schreiben
;
; I:
;	datbuf	= aktuelle Bufferadresse
;	trknr		= laufende Spurnummer
;	secnr		= laufende Sektornummer
;
;	LBA	= Sektor
;	LBA+1	= Track lsb
;	LBA+2	= Track msb
;	LBA+3	= Head
;
; O:
;	Buffer 	= 1KByte Daten, werden dann Recordweise mit DMA ausgetauscht
;	(A)		= 0 für OK
;	(CY) 		= 1 Fehler lesen/schreiben
;
;--------------------------------------------------------------------
;
; initrw =	Buffer, Spur, Sektorregister vorbereiten
;
; CHS_LBA uebertraegt die CHS zu LBA
;
; Parameteruebergabe:
;
;	datbuf	= aktuelle Bufferadresse
;
;	LBA		= Sektor
;	LBA+1		= Track lsb
;	LBA+2		= Track msb
;	LBA+3		= Head

initrw:	xra  	a		; clear wrtflg -"Buffer ungültig"
		sta  	wrtflg	;
		di			; Interrupt sperren
		sta	lba+2
		sta	lba+3		; Head 0
		lxi	h,buffer	;
		shld	datbuf	;
		lhld	akttrk	;
		shld	trknr		;
		shld	lba+1		; Track lsb/msb
		lda	aktsec	;
		sta	secnr		;
		sta	lba		; Sektor
		lxi	h,buffer	;
		shld	datbuf	;
		ret			;
;
; get/put_cf =	Block von CF lesen/schreiben
;
get_ide:	call 	initrw	;
		call 	READ_SECTOR	;	;
		ana	a		;
		jnz	cf_err	; Fehler
		;
		call 	updsek	; 2.Sektor laden
		call	READ_SECTOR	;
		ana	a		;
		jnz	cf_err	; Fehler
		xra	a		;
		ret			; zurück zum Disk-Manager
		;
		;
put_ide:	call 	initrw	;
		call 	WRITE_SECTOR		;
		ana	a		;
		jnz	cf_err	; Fehler
		call 	updsek	;
		call	WRITE_SECTOR		;
		ana	a		;
		jnz	cf_err	; Fehler
		xra	a		;
		ret			; zurück zum Disk-Manager
;
;
; updsek/sp = Sektor und Spurnummern aktualisieren
;             und an CF-Karte senden
;
;	! maimal 32 Records pro Spur !
;
updsek:	lxi	h,buffer+512
		shld	datbuf
		lda	secnr		; Sektor holen
		cpi	16		; letzter Sektor spur
		jz	updsp		;
		inr	a		; nächster Sektor gleiche Spur
		sta	secnr		;
		sta	lba
		ret

updsp:	mvi	a,01h		; nächste Spur mit Sektor 1
		sta	secnr		;
		sta	lba
		lhld	trknr		;
		inx	h		;
		shld	trknr		;
		shld	lba+1		;
		ret
;
; Fehler (A)=1
;
cf_err:	mvi	a,01		;
		stc			;
		ret			; Zurück mit Fehler
;
;
;--------------------------------------------------------------------
;
ideinit:	call	IDE_HARD_RESET
		call	IDE_INIT
		jnz	nohd			; IDE Busy
		ret			;

nohd:		lxi	h,nomsg		; keine HD
		call	string		;
		ret

;
IDE_HARD_RESET:
		MVI	A, PIO_RDW		; PIO Read/Write Mode
		OUT	PIO_CFG
		MVI	A, IDE_RST		; hard reset drive
		OUT	PIO_CTL
		PUSH	B
		LXI	B,3000		; ca. 10 ms delay
WLOOP:
		DCX	B
		MOV	A,B
		ORA	C
		JNZ	WLOOP
		POP	B
		XRA	A			; no ide control lines asserted
		OUT	PIO_CTL
		RET
;
; HD Init
;
IDE_INIT:
		call	IDE_GET_ASTATUS	; Test ob Karte
		ana	a			; gesteckt ist
		jz	IDE_NOT_RDY		; nein --> Ende

		MVI	A,IDE_HEAD		;
		MVI	C,IDE_DRV		; select the device
		CALL	IDE_WR_8		;
		LXI	D,0010h		; wait for ready
IDE_RDY_BSY:
		MVI	B,10h
INILP:					; delay for slow drives
		DCR	B
		JNZ	INILP			; djnz $ Z80
		DCX	D
		MOV	A,D
		ORA	E
		JZ	IDE_NOT_RDY		; timeout
		MVI	A,IDE_STATUS
		CALL	IDE_RD_8
		MOV	A,C

		ANI	01000000B		; bit 6,a
						; Z80, if on then ready
		JZ	 IDE_RDY_BSY	; wait for READY bit to be set
		MOV	A,C

		ANI	10000000B		; bit 7,a
						; Z80, if on then busy
		JNZ	 IDE_RDY_BSY	; wait for BUSY bit to be clear
		RET

IDE_NOT_RDY:
		MVI	a,0FFh
		ORA	A
		STC				; timeout
		RET

;
;
; Wait for DRQ
;
IDE_WAIT_DRQ:
		MVI	A,0FFH
		MOV	B,A
		STA	DELAY_DRQ
MORE_DRQ:

		MVI	A,IDE_STATUS
		CALL	IDE_RD_8
		MOV	A,C
		ANI	10001000B
		CPI	00001000B
		JZ	DONE_DRQ
		DCR	B
		JNZ	MORE_DRQ
		LDA	DELAY_DRQ
		DCR	A
		JNZ	MORE_DRQ
		STA	DELAY_DRQ
		STC
		RET
DONE_DRQ:
		XRA	A
		RET

DELAY_DRQ:
		DB	0

;
; Wait for BUSY to be clear
;
IDE_WAIT_NOT_BUSY:
		MVI	A,0FFH
		MOV	B,A
		STA	DELAY_BUSY
MORE_BUSY:
		MVI	A,IDE_STATUS
		CALL	IDE_RD_8
		MOV	A,C
		ANI	11000000B
		XRI	01000000B
		JZ	NOT_BUSY
		DCR	B
		JNZ	MORE_BUSY
		LDA	DELAY_BUSY
		DCR	A
		STA	DELAY_BUSY
		JNZ	MORE_BUSY
		STC
		RET
NOT_BUSY:
		XRA	A
		RET

DELAY_BUSY:
		DB	0

;
; Get Active Status of the IDE Drive
;
IDE_GET_ASTATUS:
		MVI	A,IDE_ASTATUS
		CALL	IDE_RD_8
		MOV	A,C
		RET

;
; 8 bit read
;
IDE_RD_8:
		MOV	D,A			; save adress to D
		OUT	PIO_CTL		; drive address onto control lines
		ORI	IDE_RD		; set read pin on
		OUT	PIO_CTL
;		nop				; delay 2x nop
;		nop
		IN	PIO_LSB		; read lower byte
		MOV	C,A			; to C
		MOV	A,D			; reset read pin first
		OUT	PIO_CTL
		XRA	A			; reset all control pins
		OUT	PIO_CTL
		RET

;
; 8 bit write
;
IDE_WR_8:
		MOV	D,A			; save adress to D
						; Command, head, sector, cylinder etc. in C
		MVI	A, PIO_WR
		OUT	PIO_CFG		; PIO Write Mode
		MOV	A,C
		OUT	PIO_LSB		; Drive lower lines
		MOV	A,D			; Drive address onto control lines
		OUT	PIO_CTL
		ORI	IDE_WR		; set write pin on
		OUT	PIO_CTL
;		nop				; delay	2x nop
;		nop
		MOV	A,D
		OUT	PIO_CTL		; reset write pin first
		XRA	A
		OUT	PIO_CTL		; reset all control pins
		MVI	A,PIO_RDW
		OUT	PIO_CFG		; set PIO read/write mode
		RET

;
; Do the identify command and fill
; the buffer with the drive infos
;
DRIVE_ID:
		CALL	IDE_WAIT_NOT_BUSY
		MVI	A,IDE_HEAD
		MVI	C,IDE_DRV		; select the device
		CALL	IDE_WR_8
		CALL	IDE_WAIT_NOT_BUSY
		MVI	A,IDE_COMMAND
		MVI	C,IDE_CMD_ID	; 0ech
		CALL	IDE_WR_8		; issue the command
		CALL	IDE_WAIT_DRQ
		CALL	READ_DATA
		RET

;
; 16 bit read
;
READ_SECTOR:
						; hdread:
		CALL	IDE_WAIT_NOT_BUSY
		CALL	WR_LBA		; set sector
		MVI	A,IDE_COMMAND	; command register
		MVI	C,IDE_CMD_READ
		CALL	IDE_WR_8
		CALL	IDE_WAIT_DRQ
		CALL	READ_DATA
		CALL	IDE_WAIT_NOT_BUSY
		ANI	10001001B		; busy, DRQ or error
		RZ				; no
		JMP	GET_ERR

;
; 16 bit write
;
WRITE_SECTOR:
						; hdwrite:
		CALL	IDE_WAIT_NOT_BUSY
		CALL	WR_LBA		; set sector
		MVI	A,IDE_COMMAND	; command register
		MVI	C,IDE_CMD_WRITE
		CALL	IDE_WR_8
		CALL	IDE_WAIT_DRQ
		CALL	WRITE_DATA
		CALL	IDE_WAIT_NOT_BUSY
		ANI	10001001B		; busy, DRQ or error
		RZ				; no
						;
						; Return with errors if occurs
GET_ERR:
		MVI	A,IDE_ERR
		CALL	IDE_RD_8
		MOV	A,C
		ORA	A			; set Zero Flag
		RET

;
;
; Read one sector of 512 bytes
;




READ_DATA:	MVI	B,0			; 256 words, 512 bytes per sector
		lhld	datbuf		; target
READ_LP:
		MVI	A,IDE_DATA		; drive address onto control lines
		OUT	PIO_CTL		; and set read pin off (loop)
		ORI	IDE_RD
		OUT	PIO_CTL		; set read pin on
		IN	PIO_LSB		; read the lower byte
		MOV	M,A
		INX	H
		IN	PIO_MSB		; read the upper byte
		MOV	M,A
		INX	H
		DCR	B			; Z80 djnz read_lp
		JNZ	READ_LP
		XRA	A			; set all control pins of
		OUT	PIO_CTL		;
		shld	datbuf		; Zeiger sichern
		ret
;
; Write one sector of 512 bytes
;
WRITE_DATA:	MVI	B,0			; 256 words, 512 bytes per sector
		lhld	datbuf		; target			; source
		MVI	A, PIO_WR
		OUT	PIO_CFG		; PIO Write Mode
WRITE_LP:
		MOV	A,M
		OUT	PIO_LSB		; Drive lower lines with lsb
		INX	H
		MOV	A,M
		OUT	PIO_MSB		; Drive upper lines with msb
		MVI	A,IDE_DATA
		OUT	PIO_CTL		; Drive address onto control lines
		ORI	IDE_WR		; set write pin on
		OUT	PIO_CTL
		MVI	A,IDE_DATA
		OUT	PIO_CTL		; reset write pin
		INX	H
		DCR	B			; Z80 djnz write_lp
		JNZ	WRITE_LP
		XRA	A			; set all control pins of
		OUT	PIO_CTL
		MVI	A,PIO_RDW		; PIO Read/Write Mode
		OUT	PIO_CFG
		shld	datbuf		; Zeiger sichern
		RET
;
;
; LBA = 28bit 4+3x8
; If CHS mode head 8-bit (0-254), sektor 6-bit (1-63)
; cylinder/track 10-bit (0-1023) = 24-bit / 8,38GB max.
;
;
WR_LBA:	LDA	LBA+3			; 4/8-bit, CHS head 0-254
		ANI	0FH			; LBA 0-15
		ORI	IDE_DRV		; mode master/slave CHS/LBA
		MOV	C,A
		MVI	A,IDE_HEAD
		CALL	IDE_WR_8

		LDA	LBA+2			; 8-bit, CHS cyl msb
		MOV	C,A
		MVI	A,IDE_CYL_MSB
		CALL	IDE_WR_8

		LDA	LBA+1			; 8-bit, CHS cyl lsb
		MOV	C,A
		MVI	A,IDE_CYL_LSB
		CALL	IDE_WR_8

		LDA	LBA			; 8-bit, CHS sector

;		and	3fh			; 1-63 if CFC/CHS
		MOV	C,A
		MVI	A,IDE_SECTOR
		CALL	IDE_WR_8

		MVI	C,1			; transfer one sector
		MVI	A,IDE_SEC_CNT
		CALL	IDE_WR_8
		XRA	A
		RET
;
;
;
;--------------------------------------------------------------------
;