;
 MACRO WAIT_HDD
	LD BC,IDE.Read.Status	
.loop:	IN A,(C)
	BIT IDE.ControlBit.Busy,A
	JR NZ,.loop
 ENDM
;______________________________________________________________________:
;

HD_BPB_PREP:
	LD	D,A
	IN	A,(SLOT3)
	EX	AF,AF'
	LD	A,SYS_PAGE
	OUT	(SLOT3),A
	LD	A,(SYS_PAGE.HD_IDF_ADR.sectors)
	LD	E,A
	EX	AF,AF'
	OUT	(SLOT3),A
	LD	A,D
	LD	D,#00
	LD	IX,#0000
	LD	B,#01
HD_PREPARE:
	PUSH	AF
	PUSH	HL
	CALL	HD_CALC_SECS
	JR	NC,.L1
	POP	HL
	POP	AF
	SCF 
	RET 

.L1:	LD A,B
	LD BC,IDE.Write.Counter						; Установить число секторов для записи
	OUT (C),A

 IF IDE_Optimization
 	INC C
	OUT (C),L
	INC C
	OUT (C),E
	INC C
	OUT (C),D
 ELSE
	LD BC,IDE.Write.Sector
	OUT (C),L							; СЕКТОР
	LD BC,IDE.Write.CylinderLow
	OUT (C),E							; дорожка low
	LD BC,IDE.Write.CylinderHigh
	OUT (C),D							; дорожка high
 ENDIF

	LD BC,IDE.Read.Control
	IN A,(C)
	AND #F0								; !!!!!  посмотреть
	OR H
	INC B								; IDE.Write.DeviceHead

	OUT (C),A

	POP HL								; BUFER & PAGE
	POP AF
	AND A
	RET


NEXT_ADD_SEC:
	PUSH AF

	LD A,B
	LD BC,IDE.Write.Counter						; Установить число секторов для записи
	OUT (C),A

 IF IDE_Optimization
	DEC B
	INC C
	IN A,(C)							; IDE.Read.Sector
	ADC A,E
	INC B
	OUT (C),A							; IDE.Write.Sector

	DEC B
	INC C
	IN A,(C)							; IDE.Read.CylinderLow
	ADC A,D
	INC B
	OUT (C),A							; IDE.Write.CylinderLow

	DEC B
	INC C
	IN A,(C)							; IDE.Read.CylinderHigh
	ADC A,0
	INC B
	OUT (C),A							; IDE.Write.CylinderHigh

	LD BC,IDE.Read.Control
	IN A,(C)
 ELSE
	LD BC,IDE.Read.Sector
	IN A,(C)
	ADC A,E
	INC B
	OUT (C),A							; IDE.Write.Sector

	LD BC,IDE.Read.CylinderLow
	IN A,(C)
	ADC A,D
	INC B
	OUT (C),A							; IDE.Write.CylinderLow

	LD BC,IDE.Read.CylinderHigh
	IN A,(C)
	ADC A,0
	INC B
	OUT (C),A							; IDE.Write.CylinderHigh

	LD BC,IDE.Read.Control
	IN A,(C)
 ENDIF

	LD D,A
	ADC A,0
	AND #0F
	LD E,A
	LD A,D
	AND #F0
	OR E
	INC B
	OUT (C),A							; IDE.Write.DeviceHead

	POP AF
	RET

; При исполнении производится вся подготовка к
; операциям чтения/записи вычисление
; цилиндров/головок/секторов и занесение их в регистры винчестера
; далее программа может сама только подать команду читать/писать и
; самостоятельно производить считывание/запись данных в винчестер.
; Команда удобна для работы программ в реальном времени, когда необходимо
; кроме чтения/записи данных производить какие либо иные действия.
FN_HDD_PREPARE:								; ПОДГОТОВКА К ВНЕШНИМ ОПЕРАЦИЯМ R/W
	AND A                                                           ; чтоб сбросить CF если он стоит и B=0
	INC B
	DEC B
	RET Z

	CALL HD_WAIT
	RET C

	CALL HD_PREPARE
	RET C
;!TEST
        EX      AF,AF'
         LD     A,#BF           ; check buffer address in SLOT3
         SUB    H
         JR     C,.SetCommand
        EX      AF,AF'

	SAFE_PORTY

	EXX
	LD C,SLOT3
	IN B,(C)
	EXX
	OUT (SLOT3),A
	EX AF,AF'

.SetCommand:
	LD BC,IDE.Write.Command
	LD A,IDE.ATA.ReadSectorsWithRetry
	; OUT (C),A               
	AND A
	RET

FN_HDD_READ_BPB:
	CALL	HD_WAIT
	RET	C
	CALL	HD_BPB_PREP
	RET	C

	JR	HD_RD_L1


; FOR LBA ONLY - NEXT_READ
; HL - bufer, A - page
; B - numer of sectors
; DE - add_par (next+DE)  (d.b. 1 for NEXT)
FN_HDD_READ_NEXT:
	AND A
	INC B
	DEC B
	RET Z								; ret if 0 sectors
	CALL HD_WAIT
	RET C
	CALL NEXT_ADD_SEC
	JR HD_RD_L1


; HL - BUFER, A - PAGE
FN_HDD_READ:
	AND A
	INC B
	DEC B
	RET Z
	CALL HD_WAIT
	RET C

	CALL HD_PREPARE
	RET C
HD_RD_L1:
	EXX
	LD C,SLOT3
	IN B,(C)
	EXX
	OUT (SLOT3),A
	EX AF,AF'

	SAFE_PORTY

	LD BC,IDE.Write.Command
	LD A,IDE.ATA.ReadSectorsWithRetry
	OUT (C),A
HD_RD_L2:

	WAIT_HDD

	BIT IDE.ControlBit.DataRequest,A
	JR NZ,HD_READ_CONT

	ZERO_PORTY
HD_RET:
	EXX
	OUT (C),B
	EXX
	LD BC,IDE.Read.Error
	IN A,(C)
	AND A
	SCF
	RET NZ

	EX AF,AF'
	AND A
	RET

HD_READ_CONT:
	LD BC,IDE.Read.Data

.loop_read1:
	DUP 16
		INI							; всего 16 раз INI - оптимально.
	EDUP
        JP NZ,.loop_read1
.loop_read2:
	DUP 16
		INI							; всего 16 раз INI - оптимально.
	EDUP
        JP NZ,.loop_read2

	LD A,H
	OR L
	JR NZ,HD_RD_L2

	LD A,SYS_PAGE
	OUT (SLOT3),A
	EX AF,AF'
	LD HL,SYS_PAGE.RAMD_FAT
	LD L,A
	LD A,(HL)
	OUT (SLOT3),A
	EX AF,AF'
	LD HL,#C000							; !!!!!
	JR HD_RD_L2

; HL - BUFER, A - PAGE
FN_HDD_WRITE:
	AND	A
	INC	B
	DEC	B
	RET	Z

	CALL	HD_WAIT
	RET	C
	CALL	HD_PREPARE
	RET	C

	EXX 
	LD	C,SLOT3
	IN	B,(C)
	EXX 
	OUT	(SLOT3),A
	EX	AF,AF'

	LD	BC,IDE.Write.Command
	LD	A,IDE.ATA.WriteSectorsWithRetry
	OUT	(C),A

HD_WR_L2:
	WAIT_HDD

	BIT IDE.ControlBit.DataRequest,A
	JP Z,HD_RET

        LD	BC,IDE.Write.Data
        LD	D,32
HD_WR_LOOP:
	DUP 16
		OUTI						        ; всего 16 раз OUTI - оптимально.
	EDUP

	DEC	D
	JP	NZ,HD_WR_LOOP

	LD	A,H
	OR	L
	JR	NZ,HD_WR_L2

	LD	A,SYS_PAGE
	OUT	(SLOT3),A
	EX	AF,AF'
	LD	HL,SYS_PAGE.RAMD_FAT
	LD	L,A
	LD	A,(HL)
	OUT	(SLOT3),A
	EX	AF,AF'
	LD	HL,#C000						;!!!!!
	JR	HD_WR_L2

FN_HDD_RECAL:
	LD	A,IDE.Drive.Master
	LD	BC,IDE.Write.DeviceHead
	OUT	(C),A
	LD	A,IDE.ATA.ExecuteDeviceDiagnostic
	CALL    HD_CMD_EXE
	AND	A
	BIT	IDE.ControlBit.Error,A
	RET	Z
	LD	BC,IDE.Read.Error
	IN	A,(C)
	CP	1							; !!!!! глянуть
	RET	Z
	SCF 
	RET 

        
;????? глянуть
;	RET C
;	LD A,#1F	; RECALIBRATE
;	CALL HD_CMD_EXE
;	RET


FN_HDD_TEST_IDE:
	LD	E,#00
	LD	BC,IDE.Write.DeviceHead
	LD	A,#A0							; !!!!! глянуть
	OUT	(C),A

	CALL	TEST_HDD_DRV

	JR	NZ,NO_HDD1
	SET	0,E
NO_HDD1:
	LD	BC,IDE.Write.DeviceHead
	LD	A,#B0							; !!!!! глянуть
	OUT	(C),A

	CALL	TEST_HDD_DRV

	JR	NZ,NO_HDD2
	SET	1,E
NO_HDD2:
	LD	A,E
	AND	A
	SCF 
	RET	Z							; HDD absent !
	AND	A
	RET 


;-----------------------------------[DIFFERENT]
; Не должна портить регистр E!!!
TEST_HDD_DRV:
; EXP_HDD.ASM variant
	LD HL,#01FE							; ?????
	LD BC,IDE.Write.Counter
	OUT (C),L
 IF IDE_Optimization
	INC C
	OUT (C),H							; IDE.Write.Sector

	DEC C
	INC B
	IN A,(C)							; ????? IDE.Write.Counter+#100
	CP L
	RET NZ
	INC C
 ELSE
	LD BC,IDE.Write.Sector
	OUT (C),H
	LD BC,IDE.Write.Counter+#100
	IN A,(C)
	CP L
	RET NZ
	LD BC,IDE.Write.Sector+#100
 ENDIF
	IN A,(C)							; ????? IDE.Write.Sector+#100
	CP H
	RET
/*
TEST_HDD_DRV:
; EXTENDED.ASM variant
	LD	HL,#00FF						;?????
	LD	BC,IDE.Write.CylinderLow
	OUT	(C),L
 IF IDE_Optimization
	INC C
	OUT	(C),H							; IDE.Write.CylinderHigh
					
	INC B
	DEC C
	IN	A,(C)							; ????? Тут регистр BC = #0254 - что за порт такой и что оттуда прочитается?
	CP	L
	RET	NZ
	INC C

 ELSE
	LD	BC,IDE.Write.CylinderHigh
	OUT	(C),H
	LD	BC,#0254						;???!!!!
	IN	A,(C)
	CP	L
	RET	NZ
	LD	BC,#0255						;???!!!!
 ENDIF

	IN	A,(C)							; ????? Тут регистр BC = #0255 - что за порт такой и что оттуда прочитается?
	CP	H
	RET
*/


FN_HDD_INIT:
	LD	BC,IDE.Write.DeviceHead
	LD	A,IDE.Drive.Master
	OUT	(C),A
	CALL    TEST_HDD_DRV
	JR	NZ,HD_ABSENT
HD_C0_L3:
	WAIT_HDD

	LD	BC,IDE.Write.Command
	LD	A,IDE.ATA.IdentifyDevice
	OUT	(C),A

	WAIT_HDD

	BIT	IDE.ControlBit.DataRequest,A
	JR	NZ,HD_C0_L2
	SCF 
	RET 
HD_ABSENT:
	LD	BC,IDE.Write.DeviceHead
	LD	A,#B0							; !HARDCODE
	OUT	(C),A
	CALL	TEST_HDD_DRV
	JR	Z,HD_C0_L3
	SCF 
	RET 

HD_C0_L2:
	LD	BC,IDE.Read.Data
	LD	HL,SYS_PAGE.HD_IDF_ADR
	IN	A,(SLOT3)
	LD	D,A
	LD	A,SYS_PAGE
	OUT	(SLOT3),A
	INIR 
	INIR 
	LD	A,(SYS_PAGE.HD_IDF_ADR.sectors)			        ; число секторов
	LD	C,A
	LD	HL,0
	LD	B,H
	LD	A,(SYS_PAGE.HD_IDF_ADR.heads)			        ; число головок
.loop:
	ADD	HL,BC
	DEC	A
	JR	NZ,.loop
	LD	(SYS_PAGE.HD_IDF_ADR.sec_cyl),HL

	WAIT_HDD

	LD	BC,IDE.Read.Control
	IN	A,(C)
	AND	#10
	LD	B,A
	LD	A,(SYS_PAGE.HD_IDF_ADR.heads)			        ; число головок

	DEC	A
	AND	#0F
	OR	#A0
	OR	B

	LD	H,A
	LD	A,(SYS_PAGE.HD_IDF_ADR+99)				; !HARDCODE через структуру!
	BIT	1,A
	JR	Z,HD_C0_NO_LBA
	SET	6,H

HD_C0_NO_LBA:
	LD	BC,IDE.Write.DeviceHead
	OUT	(C),H

	LD	A,(SYS_PAGE.HD_IDF_ADR.sectors)			        ; число секторов

	LD	BC,IDE.Write.Counter
	OUT	(C),A
	LD	A,D
	OUT	(SLOT3),A
	
	LD	A,IDE.ATA.InitializeDeviceParameters			; SET HDD PARAMETERS
	CALL	HD_CMD_EXE
	RET

HD_CMD_EXE:
	CALL	HD_WAIT
	RET	C
	LD	BC,IDE.Write.Command
	OUT	(C),A
HD_WAIT:
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	DE,0
HD_WAIT1:
	LD	BC,IDE.Read.Status
	IN	A,(C)
	BIT	IDE.ControlBit.Busy,A
	JR	Z,HD_W_EXIT
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,HD_WAIT1
	POP	AF
	POP	BC
	POP	DE
	SCF 
	RET 
HD_W_EXIT:
	POP AF
	POP BC
	POP DE
	AND A
	RET

/*
;EXTENDED.ASM Version

HDD_LBA:								;???!!!!
	POP	BC
	LD	L,E
	LD	E,D
	LD	D,XL
	XOR	A
	LD	H,A
	RET 

HD_CALC_SECS:
	LD	A,XH
	AND	A
	SCF 
	RET	NZ							; ошибка, слишком большой HDD

	PUSH	BC
	LD	BC,IDE.Write.DeviceHead
	DEC	B
	IN	A,(C)
	BIT	6,A
	JR	NZ,HDD_LBA
;	POP BC								;!!!!!  посмотреть
*/

;EXP_HDD.ASM Version
HDD_LBA:
	POP     BC
	LD      L,E
	LD      E,D
	LD	D,XL
	LD	A,XH
	AND     #0F
	LD      H,A
	RET

HD_CALC_SECS:
	PUSH    BC
	LD      BC,IDE.Read.Control
	IN      A,(C)
	BIT     6,A
	JR      NZ,HDD_LBA
	POP     BC							; снимаем значение для корректного ret

	LD      A,XH
	AND     A
	SCF
	RET     NZ							; ошибка, слишком большой HDD

;	IX,DE - абсолютный номер сектора
	PUSH    IX
	POP     HL

	IN      A,(SLOT3)
	LD      C,A
	LD      A,SYS_PAGE
	OUT     (SLOT3),A
	LD      A,C
	LD      (SYS_PAGE.COPY_SLOT3),A

; HL,DE - номер сектора
; BC - число секторов на цилиндре
	LD      BC,(SYS_PAGE.HD_IDF_ADR.sec_cyl)			; число секторов на цилиндре
	LD      A,16							; HL,DE разделить на BC
	SCF
DIV_LOOP:
	EX	DE,HL
	ADD     HL,HL
	EX	DE,HL
	ADC     HL,HL

	SBC     HL,BC							; сравнить HL и BC
	JR	NC,NO_ADD						; переноса не было - +1!
	ADD	HL,BC
	DEC	A
	JR	NZ,DIV_LOOP
	JR	DIV_END
NO_ADD:
	INC DE
	DEC A
	JR NZ,DIV_LOOP
DIV_END:
; DE - результат, HL - остаток
; DE - цилиндр
	LD A,(SYS_PAGE.HD_IDF_ADR+12)                                   ; !HARDCODE .sectors
; A - число секторов на дорожке
; HL - номер сектора в цилиндре
	LD B,0
	LD C,A
;	LD BC,(MS_BPB+S_P_T)                                            ; ЧИСЛО СЕКТОРОВ НА ДОРОЖКЕ
	XOR A
HD_CALC_LOOP2:
	SBC HL,BC
	INC A
	JR NC,HD_CALC_LOOP2

	DEC	A                                                       ; A - головка
	ADD	HL,BC							; L - сектор
	INC	L
	LD	H,A                                                     ; HL - HEAD,SEC

	LD	A,(SYS_PAGE.COPY_SLOT3)
	OUT	(SLOT3),A

	AND	A
	RET


FN_HDD_PART:
	BIT	0,A
	LD	A,IDE.Chanel.Primary
	JR	Z,.SET_CH
	LD	A,IDE.Chanel.Secondary
.SET_CH:
	OUT	(IDE.Chanel.Set),A
	RET

; ENDMODULE
;************************************************

; ?????
;	DB 'HDD_DRV_END'
; Вход:
; C - команда
;   0 - INIT - входных пар нет => A - число поддерживаемых дисков.
;   1 - RESET - выбор диска A - номер диска от 0 =>
;   2 - (STATUS) !!!!! TEST?
;   3 - MEDIA CHECK - A - номер диска => A = 0 - old. #FF - new (всегда #FF) !!!!! PREPARE???
;   4 - READ BPB - A - диск HL - адрес в текущей странице.
;   5 - READ - A - диск, IX:DE сектор, HL - адрес, B - число секторов
;   6 - WRITE - '' -
;   7 - PART
;   8 - READ_NEXT
;
; ошибки - CF - A - ошибка
;
; 0 - нет ошибки
; 1 - неверная команда
; 2 - неверный номер диска
; 3 - неверный формат (не MS-DOS)
; 4 - нет готовности
; 5 - ошибка позиционирования
; 6 - сектор не найден
; 7 - ошибка CRC
; 8 - защита записи
; 9 - ошибка чтения
; 10 - ошибка записи
; 11 - ГЛЮК
;