;__ATAPI-T_______________________________________________________________________________________________________________________ 
;
;	ATAPI UTILITY PROGRAM
;	AUTHOR: DAN WERNER -- 4/14/2009
;________________________________________________________________________________________________________________________________
;

; DATA CONSTANTS
;________________________________________________________________________________________________________________________________ 
;IDE REGISTER		IO PORT		; FUNCTION
IDELO		.EQU	$20		; DATA PORT (LOW BYTE)
IDEERR		.EQU	$21		; READ: ERROR REGISTER; WRITE: PRECOMP
IDESECTC	.EQU	$22		; SECTOR COUNT
IDESECTN	.EQU	$23		; SECTOR NUMBER
IDECYLLO	.EQU	$24		; CYLINDER LOW
IDECYLHI	.EQU	$25		; CYLINDER HIGH
IDEHEAD		.EQU	$26		; DRIVE/HEAD
IDESTTS		.EQU	$27		; READ: STATUS; WRITE: COMMAND
IDEHI		.EQU	$28		; DATA PORT (HIGH BYTE)
IDECTRL		.EQU	$2E		; READ: ALTERNATIVE STATUS; WRITE; DEVICE CONTROL
IDEADDR		.EQU	$2F		; DRIVE ADDRESS (READ ONLY)

BS:		.EQU	08H		; ASCII backspace character
CR		.EQU	$0D		; CARRIAGE RETURN CHARACTER
LF		.EQU	$0A		; LINE FEED CHARACTER
END		.EQU	'$'		; LINE TERMINATOR FOR CP/M STRINGS

sectors		.EQU	$20		; number of blocks reserved for directory

;________________________________________________________________________________________________________________________________ 
; MAIN PROGRAM BEGINS HERE
;________________________________________________________________________________________________________________________________ 
	.ORG	$0100

	
	LD	A,$00			; SET TO PRIMARY DEVICE
	LD	(IDEDEVICE),A		;

MAIN_LOOP:
	LD	A,(IDEDEVICE)		; CHECK DEVICE SETTING
	CP	00H			; IS PRIMARY?
	JR	Z,MAIN_PRIMARY		;
	LD	DE,MSG_SECONDARY	;
	JR	MAIN_CONTINUE		;
MAIN_PRIMARY:
	LD	DE,MSG_PRIMARY		;
MAIN_CONTINUE:
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	DE,MSG_START		;
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	C,01H			; CP/M console input call
	CALL	0005H			; GET K.B. DATA
	CP	'1'			; IS REQUEST SENSE
	JP	Z,MENU_REQ_SENSE	;
	CP	'2'			; IS GET IDE ID
	JP	Z,MENU_GET_IDE_ID	;
	CP	'3'			; IS GET ATAPI ID
	JP	Z,MENU_GET_ATAPI_ID	;
	CP	'4'			; IS EJECT DISK
	JP	Z,MENU_EJECT		;
	CP	'5'			; IS READ SECTOR
	JP	Z,MENU_READ_SECTOR	;
	CP	'6'			; IS GET FORMAT CAP.
	JP	Z,MENU_READ_FORMAT_CAPACITIES
	CP	'7'			; IS WRITE SECTOR
	JP	Z,MENU_WRITE_SECTOR	;
	CP	'8'			; IS FORMAT
	JP	Z,MENU_FORMAT		;
	CP	'9'			; IS FORMAT
	JP	Z,MENU_SYSGEN		;
	CP	'A'			; IS cpm format
	JP	Z,MENU_CFORMAT		;
	CP	'a'			; IS CPM FORMAT
	JP	Z,MENU_CFORMAT		;
	CP	'B'			; IS SELECT PRIMARY
	JP	Z,MENU_PRIMARY		;
	CP	'b'			; IS SELECT PRIMARY
	JP	Z,MENU_PRIMARY		;
	CP	'C'			; IS SELECT SECONDARY
	JP	Z,MENU_SECONDARY	;
	CP	'c'			; IS SELECT SECONDARY
	JP	Z,MENU_SECONDARY	;
	CP	'Q'			; IS QUIT
	JP	Z,MENU_QUIT		;
	CP	'q'			; IS QUIT
	JP	Z,MENU_QUIT		;
	JP	MAIN_LOOP		; LOOP TO MENU 

;________________________________________________________________________________________________________________________________ 
;
; MENU OPERATIONS
;________________________________________________________________________________________________________________________________ 
MENU_REQ_SENSE:
	LD	HL,ATAPI_REQUEST_SENSE	; SET COMMAND PACKET
	CALL	ATAPI_SEND_PACKET	; SEND COMMAND PACKET
	LD	IX,IDE_SECTOR_BUFFER	; SET SECTOR BUFFER
	CALL	ATAPI_READ_DATA		; READ DATA
	LD	HL,IDE_SECTOR_BUFFER	; SET ADDRESS TO DUMP
	LD      DE,IDE_SECTOR_BUFFER	; SET END ADDRESS
	INC	D			;
	CALL	DUMP_BUFFER		; DUMP BUFFER TO CONSOLE
	JP	MAIN_LOOP		; RETURN


MENU_GET_IDE_ID:
	CALL	IDE_GET_ID		; GET IDE HD IDENTIFICATION
	LD	HL,IDE_SECTOR_BUFFER	; SET ADDRESS TO DUMP
	LD      DE,IDE_SECTOR_BUFFER	; SET END ADDRESS
	INC	D			;
	INC	D			;
	CALL	DUMP_BUFFER		; DUMP BUFFER TO CONSOLE
	JP	MAIN_LOOP		; RETURN


MENU_GET_ATAPI_ID:
	LD	HL,ATAPI_INQUIRY	; SET COMMAND PACKET
	CALL	ATAPI_SEND_PACKET	; SEND COMMAND PACKET
	LD	IX,IDE_SECTOR_BUFFER	; SET SECTOR BUFFER
	CALL	ATAPI_READ_DATA		; READ ATAPI DATA
	LD	HL,IDE_SECTOR_BUFFER	; SET ADDRESS TO DUMP
	LD      DE,IDE_SECTOR_BUFFER	; SET END ADDRESS
	INC	D			; 
	CALL	DUMP_BUFFER		; DUMP BUFFER TO CONSOLE
	JP	MAIN_LOOP		; RETURN


MENU_EJECT:
	CALL	REQUEST_SENSE_LOOP	; CLEAR ATAPI ERRORS
	LD	HL,EJECT_DISK_PACKET	; SET COMMAND PACKET
	CALL	ATAPI_SEND_PACKET	; SEND COMMAND PACKET
	JP	MAIN_LOOP		; RETURN


MENU_READ_SECTOR:
	LD	DE,MSG_ENTER_ADDRESS	;
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET ADDRESS
	PUSH	HL			; STORE ADDRESS ON STACK
					;
	LD	DE,MSG_ENTER_BLOCK_NUMBER
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET HIGH WORD
	PUSH	HL			; STORE HIGH WORD TO STACK
	POP	DE			; RESTORE HIGH WORD TO DE
	LD	HL,KEYBUF+4		; INC BUFFER POINTER BY FOUR
	CALL	LDHL			; GET LOW WORD
					;
	LD	A,D			; STORE SECTOR ADDRESS TO PACKET
	LD	(READ_DISK_PACKET+2),A	;
	LD	A,E			;
	LD	(READ_DISK_PACKET+3),A	;
	LD	A,H			;
	LD	(READ_DISK_PACKET+4),A	;
	LD	A,L			;
	LD	(READ_DISK_PACKET+5),A	;
					;
	CALL	REQUEST_SENSE_LOOP	; GET ATAPI SENSE CODES TO CLEAR ERRORS
	LD	HL,READ_DISK_PACKET	; SET POINTER TO READ SECTOR PACKET
	CALL	ATAPI_SEND_PACKET	; SEND PACKET COMMAND
	POP	IX			; RESTORE ADDRESS
	PUSH	IX			; STORE IX
	CALL	ATAPI_READ_DATA		; 
	POP	HL			; RESTORE ADDRESS
	PUSH	HL			; STORE ADDRESS
	POP	DE			; RESTORE ADDRESS
	INC	H			;
	INC 	H			; 
	EX	DE,HL			; PLACE START AD IN HL AND END IN DE
	CALL	DUMP_BUFFER		;	
	JP	MAIN_LOOP		;


MENU_READ_FORMAT_CAPACITIES:
	LD	HL,ATAPI_READ_FORMAT_CAPACITIES
	CALL	ATAPI_SEND_PACKET	; SEND COMMAND PACKET
	LD	IX,IDE_SECTOR_BUFFER	; SET SECTOR BUFFER
	CALL	ATAPI_READ_DATA		; READ ATAPI DATA
	LD	HL,IDE_SECTOR_BUFFER	; SET ADDRESS TO DUMP
	LD      DE,IDE_SECTOR_BUFFER	; SET END ADDRESS
	INC	D			; 
	CALL	DUMP_BUFFER		; DUMP BUFFER TO CONSOLE
	JP	MAIN_LOOP		; RETURN


MENU_WRITE_SECTOR:
	LD	DE,MSG_ENTER_ADDRESS	;
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET ADDRESS
	PUSH	HL			; STORE ADDRESS ON STACK
					;
	LD	DE,MSG_ENTER_BLOCK_NUMBER
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET HIGH WORD
	PUSH	HL			; STORE HIGH WORD
	POP	DE			; RESTORE HIGH WORD INTO DE
	LD	HL,KEYBUF+4		; MOVE INPUT BUFFER POINTER
	CALL	LDHL			; GET LOW WORD
					;
	LD	A,D			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+2),A	;
	LD	A,E			;
	LD	(WRITE_DISK_PACKET+3),A	;
	LD	A,H			;
	LD	(WRITE_DISK_PACKET+4),A	;
	LD	A,L			;
	LD	(WRITE_DISK_PACKET+5),A	;
					;
	CALL	REQUEST_SENSE_LOOP	; GET SENSE DATA TO CLEAR ERROR CHANNEL
	LD	HL,WRITE_DISK_PACKET	; SET POINTER TO WRITE PACKET COMMAND
	CALL	ATAPI_SEND_PACKET	; SEND THE PACKET COMMAND
					;	
	POP	IX			; RESTORE ADDRESS INTO IX
	LD	B,0			; WRITE 256 WORDS
	CALL	ATAPI_WRITE_DATA	; WRITE DATA TO CHANNEL
	JP	MAIN_LOOP		; RETURN TO MAIN LOOP


MENU_SYSGEN:
	CALL 	CRLF			;
	LD	DE,MSG_ENTER_ADDRESS	;
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET ADDRESS
	PUSH	HL			; STORE ADDRESS ON STACK
	CALL 	CRLF			;					;
	LD	DE,MSG_PROMPT_NUMBER	;
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			; GET NUMBER OF BLOCKS
	LD	B,L			; STORE BLOCKS IN B
	LD	HL,0000H		; SET UP SECTOR TO WRITE
	LD	DE,0000H		;
	POP	IX			; RESTORE ADDRESS INTO IX
SYSGEN_LOOP:
	LD	A,D			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+2),A	;
	LD	A,E			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+3),A	;
	LD	A,H			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+4),A	;
	LD	A,L			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+5),A	;
	PUSH	HL			;
	PUSH	DE			;
	PUSH 	BC			; STORE BC
	PUSH	IX			;
	CALL	REQUEST_SENSE_LOOP	; GET SENSE DATA TO CLEAR ERROR CHANNEL
	LD	HL,WRITE_DISK_PACKET	; SET POINTER TO WRITE PACKET COMMAND
	CALL	ATAPI_SEND_PACKET	; SEND THE PACKET COMMAND
					;	
	POP	IX			; RESTORE ADDRESS INTO IX
	LD	B,0			; WRITE 256 WORDS
	CALL	ATAPI_WRITE_DATA	; WRITE DATA TO CHANNEL
	POP	BC			; RESTORE BC
	POP	DE			; RESTORE DE
	POP	HL			; RESTORE HL	
	INC	HL			; INC BLOCK
	DJNZ	SYSGEN_LOOP		;
	JP	MAIN_LOOP		; RETURN TO MAIN LOOP



MENU_CFORMAT:
	LD	DE,0000H		; STARTING SECTOR
	LD	HL,0040H		;
	LD	B,20H			; NUMBER OF SECTORS	

CFORMAT_LOOP:
	LD	A,D			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+2),A	;
	LD	A,E			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+3),A	;
	LD	A,H			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+4),A	;
	LD	A,L			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+5),A	;
	PUSH	HL			;
	PUSH	DE			;
	PUSH 	BC			; STORE BC
	CALL	REQUEST_SENSE_LOOP	; GET SENSE DATA TO CLEAR ERROR CHANNEL
	LD	HL,IDE_SECTOR_BUFFER	; INITIALIZE SECTOR BUFFER TO KNOWN VALUE $00
	LD	BC,$0200		;
	LD	A,$E5			;
	CALL	FILL_MEM		;
	LD	HL,WRITE_DISK_PACKET	; SET POINTER TO WRITE PACKET COMMAND
	CALL	ATAPI_SEND_PACKET	; SEND THE PACKET COMMAND
					;	
	LD	IX,IDE_SECTOR_BUFFER	; RESTORE ADDRESS INTO IX
	LD	B,0			; WRITE 256 WORDS
	CALL	ATAPI_WRITE_DATA	; WRITE DATA TO CHANNEL
	POP	BC			; RESTORE BC
	POP	DE			; RESTORE DE
	POP	HL			; RESTORE HL	
	INC	HL			; INC BLOCK
	DJNZ	CFORMAT_LOOP		;
	JP	MAIN_LOOP		; RETURN TO MAIN LOOP




MENU_FORMAT:
	LD	HL,ATAPI_READ_FORMAT_CAPACITIES
	CALL	ATAPI_SEND_PACKET	; SEND COMMAND PACKET
	LD	IX,IDE_SECTOR_BUFFER	; SET SECTOR BUFFER
	CALL	ATAPI_READ_DATA		; READ ATAPI DATA
					;
	LD	DE,MSG_RECOMMENDED_CAPACITY
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	DE,MSG_PROMPT_NUMBER	;	
	LD	C,09H			; CP/M WRITE STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	A,(IDE_SECTOR_BUFFER+4)	; 
	CALL	HXOUT			;
	LD	A,(IDE_SECTOR_BUFFER+5)	;
	CALL	HXOUT			;
	LD	A,(IDE_SECTOR_BUFFER+6)	;
	CALL	HXOUT			;
	LD	A,(IDE_SECTOR_BUFFER+7)	;
	CALL	HXOUT			;
	CALL    CRLF			;
	LD	DE,MSG_PROMPT_SIZE	;
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	A,(IDE_SECTOR_BUFFER+9)	;
	CALL	HXOUT			;
	LD	A,(IDE_SECTOR_BUFFER+10);
	CALL	HXOUT			;
	LD	A,(IDE_SECTOR_BUFFER+11);
	CALL	HXOUT			;
					;
	CALL    CRLF			;
	LD	DE,MSG_PROMPT_NUMBER	;
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			;
	LD	A,H			;
	LD	(FORMAT_DISK_PACKET2+4),A
	LD	A,L			;
	LD	(FORMAT_DISK_PACKET2+5),A
	LD	HL,KEYBUF+4		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			;
	LD	A,H			;
	LD	(FORMAT_DISK_PACKET2+6),A
	LD	A,L			;
	LD	(FORMAT_DISK_PACKET2+7),A
	CALL    CRLF			;
	LD	DE,MSG_PROMPT_SIZE	;
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H			;
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL 	GETLN			; GET A LINE OF INPUT FROM THE USER
	LD	HL,KEYBUF		; SET POINTER TO KEYBUF AREA
	CALL	LDHL			;
	LD	A,H			;
	LD	(FORMAT_DISK_PACKET2+9),A
	LD	A,L			;
	LD	(FORMAT_DISK_PACKET2+10),A
	LD	HL,KEYBUF+4		; SET POINTER TO KEYBUF AREA
	CALL	HEXIN			;
	LD	(FORMAT_DISK_PACKET2+11),A
	CALL    CRLF			;
					;
	LD	HL,FORMAT_DISK_PACKET	;
	CALL	ATAPI_SEND_PACKET	;
					;
	LD	IX,FORMAT_DISK_PACKET2	;
	LD	B,0			;
	CALL	ATAPI_WRITE_DATA	;
	JP	MAIN_LOOP		;

MENU_PRIMARY:
	LD	A,$00			; SET TO PRIMARY DEVICE
	LD	(IDEDEVICE),A		;
	JP	MAIN_LOOP		;

MENU_SECONDARY:
	LD	A,$10			; SET TO SECONDARY DEVICE
	LD	(IDEDEVICE),A		;
	JP	MAIN_LOOP		;


MENU_QUIT:
	LD	DE,MSG_END		;
	LD	C,09H			; CP/M WRITE END STRING TO CONSOLE CALL
	CALL	0005H			;
					;
	LD	C,00H			; CP/M SYSTEM RESET CALL
	CALL	0005H			; RETURN TO PROMPT

;________________________________________________________________________________________________________________________________ 
;
; MAIN PROGRAM ENDS HERE
;________________________________________________________________________________________________________________________________ 

;__ATAPI_SEND_PACKET_____________________________________________________________________________________________________________ 
;  SEND PACKET POINTED TO BY HL TO ATAPI DRIVE   
;
;________________________________________________________________________________________________________________________________ 
ATAPI_SEND_PACKET:

	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	CALL	IDE_WAIT_DRQ_ZERO	;
					;
	LD	A,0AH			;
	OUT	(IDECTRL),A		; Disable INT
	LD	A,00H			;
	OUT	(IDEERR),A		;
	LD	A,00H			;
	OUT	(IDESECTC),A		; 
	LD	A,00H			;
	OUT	(IDESECTN),A		;
	LD	A,00H			;
	OUT	(IDECYLLO),A		; 
	LD	A,60H			;
	OUT	(IDECYLHI),A		; 
	LD	A,(IDEDEVICE)		;
	OUT	(IDEHEAD),A		; Bit 4 selects device
	LD	A,0A0H			;
	OUT	(IDESTTS),A		;
					;
	CALL	IDE_WAIT_DRQ_READY	; MAKE SURE DRIVE IS READY TO PROCEED
					;
	LD	B,6			; SEND 12 BYTES (6 WORDS)
					;
ATAPI_SEND_PACKET_LOOP:
	LD	A,(HL)			; GET BYTE
	LD	D,A			; STORE LOW BYTE IN D
	INC	HL			; INC POINTER
	LD	A,(HL)			; GET HIGH BYTE
	OUT	(IDEHI),A		; STORE HIGH BYTE
	LD	A,D			; MOVE LOW BYTE INTO A
	OUT	(IDELO),A		; STORE LOW BYTE
	INC	HL			; INC POINTER
	IN	A,(IDECTRL)		; GET STATUS
	DJNZ	ATAPI_SEND_PACKET_LOOP	; LOOP
					;
	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	IN	A,(IDECTRL)		; READ STATUS (FOR DELAY)
					;
	RET				;




;__ATAPI_READ_DATA__________________________________________________________________________________________________________
;      READ DATA BLOCK INTO IDE_SECTOR_BUFFER
;
; IX = POINTER TO BUFFER
;
;___________________________________________________________________________________________________________________________

ATAPI_READ_DATA:
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	IN	A,(IDESTTS)		; READ  REG
	AND	%00001000		; MASK OFF BIT
	CP	08H			; IS DATA WAITING?
	JR	NZ,ATAPI_READ_DATA_EXIT	; NO, EXIT
ATAPI_READ_DATA_LOOP:
	IN	A,(IDELO)		;
	LD	(IX),A			;
	INC	IX			;
	IN	A,(IDEHI)		;
	LD	(IX),A			;
	INC	IX			;
	DJNZ	ATAPI_READ_DATA_LOOP	;
ATAPI_READ_DATA_EXIT:
	RET				;


;__ATAPI_WRITE_BUFFER_______________________________________________________________________________________________________
;
; B = BYTES TO TRANSFER
; IX = POINTER TO BUFFER
;___________________________________________________________________________________________________________________________

ATAPI_WRITE_DATA:
	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
ATAPI_WRITE_DATA_LOOP:
	IN	A,(IDESTTS)		; READ  REG
	LD	A,(IX)			;
	PUSH    AF			;
	INC	IX			;
	LD	A,(IX)			;
	OUT	(IDEHI),A		;
	POP	AF			;
	OUT	(IDELO),A		;
	INC	IX			;
	DJNZ	ATAPI_WRITE_DATA_LOOP	;
	RET				;


;__GET_IDE_ID_______________________________________________________________________________________________________________
;
;
;___________________________________________________________________________________________________________________________
IDE_GET_ID:
	CALL	IDE_WAIT_BUSY_READY	;
	RET	NC			;
	LD	A,(IDEDEVICE)		;
	OR	%10100000		;
	OUT	(IDEHEAD),A		; SELECT DEVICE
	CALL	IDE_WAIT_BUSY_READY	;
	RET	NC			;
	LD	A,$A1			;
	OUT	(IDESTTS),A		; $A1 = IDE 'ID DRIVE' COMMAND 
	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			;
	CALL	IDE_TEST_ERROR		; ENSURE NO ERROR WAS REPORTED
	RET	NC			;
	CALL	IDE_WAIT_BUFFER		; WAIT FOR FULL BUFFER SIGNAL FROM DRIVE
	RET	NC			;
	CALL	IDE_READ_BUFFER_LE	; GRAB THE 256 WORDS FROM THE BUFFER
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
	RET				;
		

;-----------------------------------------------------------------------------
IDE_SOFT_RESET:

	LD	A,%00001110		;NO INTERRUPTS, RESET DRIVE = 1
	OUT	(IDECTRL),A		;
	CALL	DELAY			;
	LD	A,%00001010		;NO INTERRUPTS, RESET DRIVE = 0
	OUT	(IDECTRL),A		;
	CALL	IDE_WAIT_BUSY_READY	;
	
	CALL	ATAPI_DEVICE_SELECTION	;
	CALL	DELAY			;
	CALL 	REQUEST_SENSE_LOOP	;
	RET



REQUEST_SENSE_LOOP:
	LD	HL,ATAPI_REQUEST_SENSE	;
	CALL	ATAPI_SEND_PACKET	;
	LD	IX,IDE_SECTOR_BUFFER	; SET SECTOR BUFFER
	CALL	ATAPI_READ_DATA		;	
	CALL	DELAY			;
	IN	A,(IDESTTS)		;READ ERROR REG
	AND	%00000001		;MASK OFF BIT
	JR	NZ,REQUEST_SENSE_LOOP	;
	RET

;--------------------------------------------------------------------------------
; IDE SELECT DEVICE 
;--------------------------------------------------------------------------------
ATAPI_DEVICE_SELECTION:

	CALL	IDE_WAIT_BUSY_READY	;	
	LD	A,(IDEDEVICE)		; Selects Device
	OR	0A0H			;
	OUT	(IDEHEAD),A		;	
	CALL	IDE_WAIT_BUSY_READY	;
	RET				;

;--------------------------------------------------------------------------------
; DELAY 
;--------------------------------------------------------------------------------
DELAY:


	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;	
	RET



;--------------------------------------------------------------------------------
; IDE INTERNAL SUBROUTINES 
;--------------------------------------------------------------------------------


IDE_WAIT_BUSY_READY:
	PUSH	DE			;
	PUSH 	BC			;
	LD	DE,0			;
IDE_WBSY:
	LD	B,5			;
IDE_DLP:
	DJNZ	IDE_DLP			;
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JR	Z,IDE_TO		;
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%10000000		; MASK OFF BUSY BIT
	JR	NZ,IDE_WBSY		; WE WANT BUSY(7) TO BE 0 
	SCF				; CARRY 1 = OK
	POP	BC			;
	POP	DE			;
	RET				;
IDE_TO:
	POP	BC			;
	POP	DE			;
	XOR	A			; CARRY 0 = TIMED OUT
	RET				;
	

;--------------------------------------------------------------------------------
; IDE INTERNAL SUBROUTINES 
;--------------------------------------------------------------------------------


IDE_WAIT_DRDY_READY:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%01000000		; MASK OFF RDY BIT
	JR	Z,IDE_WAIT_DRDY_READY	; WE WANT RDY(6) TO BE 1 
	RET


IDE_WAIT_DRQ_READY:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	Z,IDE_WAIT_DRQ_READY	; WE WANT DRQ(3) TO BE 1
	RET

IDE_WAIT_DRQ_ZERO:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	NZ,IDE_WAIT_DRQ_ZERO	; WE WANT DRQ(3) TO BE 0
	RET
	

;----------------------------------------------------------------------------

IDE_TEST_ERROR:
	SCF				;
	IN	A,(IDESTTS)		;
	BIT	0,A			; TEST ERROR BIT
	RET	Z			;
	BIT	5,A			;
	JR	NZ,IDE_ERR		; TEST WRITE ERROR BIT
	IN	A,(IDEERR)		; READ ERROR FLAGS
	LD	(IDEERROR),A		;
IDE_ERR:
	OR	A			; CARRY 0 = ERROR
	RET				; IF A = 0, IDE BUSY TIMED OUT

;-----------------------------------------------------------------------------
	
IDE_WAIT_BUFFER:	
	LD	DE,0			;
IDE_WDRQ:
	LD	B,5			;
IDE_BLP:
	DJNZ	IDE_BLP			;
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JR	Z,IDE_TO2		;
	IN	A,(IDESTTS)		; WAIT FOR DRIVE'S 512 BYTE READ BUFFER 
	BIT	3,A			; TO FILL (OR READY TO FILL)
	JR	Z,IDE_WDRQ		;
	SCF				; CARRY 1 = OK
	RET				;
IDE_TO2:
	XOR	A			; CARRY 0 = TIMED OUT
	RET				;

;------------------------------------------------------------------------------
IDE_READ_BUFFER_LE:

	PUSH	IX			;
	LD	IX,IDE_SECTOR_BUFFER	;
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
IDEBUFRD_LE:
	IN	A,(IDELO)		; LOW BYTE OF WORD FIRST	
	LD	(IX+1),A		; 'ID DRIVE' IDE RESPONSE IS LITTLE ENDIAN FORMAT
	IN	A,(IDEHI)		; THEN HIGH BYTE OF WORD
	LD	(IX),A			; 'ID DRIVE' IDE RESPONSE IS LITTLE ENDIAN FORMAT
	INC	IX			;
	INC	IX			;
	DJNZ	IDEBUFRD_LE		;
	POP	IX			;
	RET				;


;__DUMP__________________________________________________________________________________________________________________________ 
;
;	Print a Memory DumP (LOW)
;________________________________________________________________________________________________________________________________
;
DUMP_BUFFER:
	CALL	CRLF			;	
BLKRD:
	CALL	PHL			; PRINT START LOCATION
	LD	C,16			; SET FOR 16 LOCS
	PUSH	HL			; SAVE STARTING HL
NXTONE:
	LD 	A,(HL)			; GET BYTE
	CALL	HXOUT			; PRINT IT
	CALL	SPACE			;
UPDH:	
	INC	HL			; POINT NEXT
	DEC	C			; DEC. LOC COUNT
	JR	NZ,NXTONE		; IF LINE NOT DONE
					; NOW PRINT 'DECODED' DATA TO RIGHT OF DUMP
PCRLF:
	CALL	SPACE			; SPACE IT
	LD	C,16			; SET FOR 16 CHARS
	POP	HL			; GET BACK START
PCRLF0:
	LD	A,(HL)			; GET BYTE
	AND	060H			; SEE IF A 'DOT'
	LD	A,(HL)			; O.K. TO GET
	JR	NZ,PDOT			;
DOT:
	LD	A,2EH			; LOAD A DOT	
PDOT:
	CALL	COUT			; PRINT IT
	INC	HL			; 
	LD	A,D			;
	CP	H			;
	JR	NZ,UPDH1		;
	LD	A,E			;
	CP	L			;
	JP	Z,DUMP_END		;
;
;IF BLOCK NOT DUMPED, DO NEXT CHARACTER OR LINE
UPDH1:
	DEC	C			; DEC. CHAR COUNT
	JR	NZ,PCRLF0		; DO NEXT
CONTD:
	CALL	CRLF			;
	JP	BLKRD			;

DUMP_END:	
	RET				;
	

;__HXOUT_________________________________________________________________________________________________________________________ 
;
;	PRINT THE ACCUMULATOR CONTENTS AS HEX DATA ON THE SERIAL PORT
;________________________________________________________________________________________________________________________________
;
HXOUT:
	PUSH	BC			; SAVE BC
	LD	B,A			;
	RLC	A			; DO HIGH NIBBLE FIRST  
	RLC	A			;
	RLC	A			;
	RLC	A			;
	AND	0FH			; ONLY THIS NOW
	ADD	A,30H			; TRY A NUMBER
	CP	3AH			; TEST IT
	JR	C,OUT1			; IF CY SET PRINT 'NUMBER'
	ADD	A,07H			; MAKE IT AN ALPHA
OUT1:
	CALL	COUT			; SCREEN IT
	LD	A,B			; NEXT NIBBLE
	AND	0FH			; JUST THIS
	ADD	A,30H			; TRY A NUMBER
	CP	3AH			; TEST IT
	JR	C,OUT2			; PRINT 'NUMBER'
	ADD	A,07H			; MAKE IT ALPHA
OUT2:
	CALL	COUT			; SCREEN IT
	POP	BC			; RESTORE BC
	RET				;


;__SPACE_________________________________________________________________________________________________________________________ 
;
;	PRINT A SPACE CHARACTER ON THE SERIAL PORT
;________________________________________________________________________________________________________________________________
;
SPACE:
	PUSH	AF			; Store AF
	LD	A,20H			; LOAD A "SPACE"
	CALL	COUT			; SCREEN IT
	POP	AF			; RESTORE AF
	RET				; DONE

;__CRLF_________________________________________________________________________________________________________________________ 
;
;	PRINT A cr/lf
;________________________________________________________________________________________________________________________________
;
CRLF:
	PUSH	AF			; Store AF
	LD	A,0DH			; LOAD A "SPACE"
	CALL	COUT			; SCREEN IT
	LD	A,0AH			; LOAD A "SPACE"
	CALL	COUT			; SCREEN IT
	POP	AF			; RESTORE AF
	RET				; DONE

;__COUT_________________________________________________________________________________________________________________________ 
;
;	PRINT CONTENTS OF A 
;________________________________________________________________________________________________________________________________
;
COUT:
	PUSH	BC			;
	PUSH	AF			;
	PUSH	HL			;
	PUSH	DE			;
		
	LD	(COUT_BUFFER),A		;
	LD	DE,COUT_BUFFER		;
	LD	C,09H			; CP/M WRITE START STRING TO CONSOLE CALL
	CALL	0005H
	POP	DE			;
	POP	HL			;
	POP	AF			;
	POP	BC			;
	RET				; DONE




;__PHL_________________________________________________________________________________________________________________________ 
;
;	PRINT THE HL REG ON THE SERIAL PORT
;________________________________________________________________________________________________________________________________
;
PHL:
	LD	A,H			; GET HI BYTE
	CALL	HXOUT			; DO HEX OUT ROUTINE
	LD	A,L			; GET LOW BYTE
	CALL	HXOUT			; HEX IT
	CALL	SPACE			; 
	RET				; DONE  


;__GETLN_________________________________________________________________________________________________________________________ 
;
;	Read a line(80) of text from the Serial Port, handle <BS>, term on <CR>.
;       Exit if too many chars.   Store result in HL.  Char count in C.
;________________________________________________________________________________________________________________________________
;
GETLN:
	LD	C,00H			; ZERO CHAR COUNTER
	PUSH	DE			; STORE DE
GETLNLOP:
	CALL	KIN			; GET A KEY
	CP	CR			; IS <CR>?
	JR	Z,GETLNDONE		; YES, EXIT 
	CP	BS			; IS <BS>?
	JR	NZ,GETLNSTORE		; NO, STORE CHAR
	LD	A,C			; A=C
	CP	0			;
	JR	Z,GETLNLOP		; NOTHING TO BACKSPACE, IGNORE & GET NEXT KEY
	DEC	HL			; PERFORM BACKSPACE
	DEC	C			; LOWER CHAR COUNTER	
	LD	A,0			;
	LD	(HL),A			; STORE NULL IN BUFFER
	LD	A,20H			; BLANK OUT CHAR ON TERM
	CALL	COUT			;
	LD	A,BS			;
	CALL	COUT			;
	JR	GETLNLOP		; GET NEXT KEY
GETLNSTORE:
	LD	(HL),A			; STORE CHAR IN BUFFER
	INC	HL			; INC POINTER
	INC	C			; INC CHAR COUNTER	
	LD	A,C			; A=C
	CP	4DH			; OUT OF BUFFER SPACE?
	JR	NZ,GETLNLOP		; NOPE, GET NEXT CHAR
GETLNDONE:
	LD	(HL),00H		; STORE NULL IN BUFFER
	POP	DE			; RESTORE DE
	RET				;


;__KIN___________________________________________________________________________________________________________________________ 
;
;	Read from the Serial Port and Echo & convert input to UCASE
;________________________________________________________________________________________________________________________________
;
KIN:
	PUSH	BC			;
	PUSH	HL			;
	PUSH	DE			;
	LD	C,01H			; CP/M console input call
	CALL	0005H			; GET K.B. DATA
	POP	DE			;
	POP	HL			;
	POP	BC			;
	RET

;__LDHL__________________________________________________________________________________________________________________________ 
;
;	GET ONE WORD OF HEX DATA FROM BUFFER POINTED TO BY HL SERIAL PORT, RETURN IN HL
;________________________________________________________________________________________________________________________________
;
LDHL:
	PUSH	DE			; STORE DE
	CALL	HEXIN			; GET K.B. AND MAKE HEX
	LD	D,A			; THATS THE HI BYTE
	CALL	HEXIN			; DO HEX AGAIN
	LD	L,A			; THATS THE LOW BYTE
	LD	H,D			; MOVE TO HL
	POP	DE			; RESTORE BC
	RET				; GO BACK WITH ADDRESS  


;__HEXIN__________________________________________________________________________________________________________________________ 
;
;	GET ONE BYTE OF HEX DATA FROM BUFFER IN HL, RETURN IN A
;________________________________________________________________________________________________________________________________
;
HEXIN:
	PUSH	BC			; SAVE BC REGS.
	CALL	NIBL			; DO A NIBBLE
	RLC	A			; MOVE FIRST BYTE UPPER NIBBLE  
	RLC	A			; 
	RLC	A			; 
	RLC	A			; 
	LD	B,A			; SAVE ROTATED BYTE
	CALL	NIBL			; DO NEXT NIBBLE
	ADD	A,B			; COMBINE NIBBLES IN ACC.
	POP	BC			; RESTORE BC
	RET				; DONE  
NIBL:
	LD	A,(HL)			; GET K.B. DATA
	INC	HL			; INC KB POINTER
	CP	40H			; TEST FOR ALPHA
	JR	NC,ALPH			;
	AND	0FH			; GET THE BITS
	RET				;
ALPH:
	AND	0FH			; GET THE BITS
	ADD	A,09H			; MAKE IT HEX A-F
	RET				;

;__FILL_MEM______________________________________________________________________________________________________________________ 
;
;	Function	: fill memory with a value
;      	Input		: HL = start address block
;			: BC = length of block
;			: A = value to fill with
;	Uses		: DE, BC
;________________________________________________________________________________________________________________________________
;

FILL_MEM:

					; HL = start address of block
					; DE = HL + 1
	ld	e,l			;
	ld	d,h			;
	inc	de			;
					; initialise first byte of block
					; with data byte (&00)
					; with data byte in A
	ld	(hl),A			;
	ldir				;
	RET				; return to caller



;-----------------------------------------------------------------------------

; TEXT CONSTANTS

MSG_START:
	.TEXT	"ATAPI TEST PROGRAM"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN

	.TEXT	"1->Request Sense"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"2->Get IDE ID"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"3->Get ATAPI ID"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"4->Eject Disk"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"5->Read Sector"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"6->read Format capacities"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"7->Write Sector"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"8->Low Level Format"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"9->SYSGEN disk"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"A->CP/M Format disk"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"B->Select Primary Device"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"C->Select Secondary Device"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"Q->Quit"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	END			; LINE TERMINATOR


MSG_ENTER_BLOCK_NUMBER:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT   "ENTER BLOCK NUMBER (00000000):"
	.DB	END			; LINE TERMINATOR

MSG_ENTER_ADDRESS:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT   "ENTER ADDRESS (0000):"
	.DB	END	

MSG_RECOMMENDED_CAPACITY:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT   "CURRENT MEDIA RECOMMENDED SETTINGS:"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	END			; LINE TERMINATOR


MSG_PROMPT_NUMBER:
	.TEXT   "NUMBER OF BLOCKS:"
	.DB	END			; LINE TERMINATOR

MSG_PROMPT_SIZE:
	.TEXT   "BLOCK LENGTH:"
	.DB	END			; LINE TERMINATOR


MSG_END:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"END IDE TEST PROGRAM"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	END			; LINE TERMINATOR

MSG_PRIMARY:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"PRIMARY IDE"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	END			; LINE TERMINATOR
MSG_SECONDARY:
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.TEXT	"SECONDARY IDE"
	.DB	LF, CR			; LINE FEED AND CARRIAGE RETURN
	.DB	END			; LINE TERMINATOR

COUT_BUFFER:
	.DB	00
	.DB	"$"

KEYBUF:  	.TEXT "                                                                                "


EJECT_DISK_PACKET
	.DB	1BH,00H,00H,00H,02H,00H,00H,00H,00H,00H,00H,00H
READ_DISK_PACKET
	.DB	0A8H,00H,00H,00H,00H,01H,00H,00H,00H,01H,00H,00H
ATAPI_READ_FORMAT_CAPACITIES
	.DB	23H,00H,00H,00H,00H,00H,00H,00H,0FFH,00H,00H,00H
WRITE_DISK_PACKET
	.DB	2AH,00H,00H,00H,00H,11H,00H,00H,01H,00H,00H,00H
FORMAT_DISK_PACKET
	.DB	04H,17H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
FORMAT_DISK_PACKET2
	.DB	00H,00H,00H,08H,00H,00H,00H,00H,00H,00H,00H,00H
ATAPI_REQUEST_SENSE
	.DB	03H,00H,00H,00H,011H,00H,00H,00H,00H,00H,00H,00H
ATAPI_INQUIRY
	.DB	12H,00H,00H,00H,24H,00H,00H,00H,00H,00H,00H,00H

IDEDEVICE:		.DS	$01
IDEBYTESLO:		.DS	$01
IDEBYTESHI		.DS	$01
IDEERROR:		.DS	$01

	.ORG	$1500
IDE_SECTOR_BUFFER:	.DS	$0200

	.END


