;
;==================================================================================================
;   HDSK DISK DRIVER
;==================================================================================================
;
; IO PORT ADDRESSES
;
HDSK_IO		.EQU	$FD
;
HDSK_CMDNONE	.EQU	0
HDSK_CMDRESET	.EQU	1
HDSK_CMDREAD	.EQU	2
HDSK_CMDWRITE	.EQU	3
HDSK_CMDPARAM	.EQU	4
;
; HDSK DEVICE CONFIGURATION
;
HDSK_DEVCNT	.EQU	2		; NUMBER OF HDSK DEVICES SUPPORTED
HDSK_CFGSIZ	.EQU	6		; SIZE OF CFG TBL ENTRIES
;
HDSK_DEV	.EQU	0		; OFFSET OF DEVICE NUMBER (BYTE)
HDSK_STAT	.EQU	1		; OFFSET OF STATUS (BYTE)
HDSK_LBA	.EQU	2		; OFFSET OF LBA (DWORD)
;
	DEVECHO	"HDSK: IO="
	DEVECHO	HDSK_IO
	DEVECHO	", DEVICE COUNT="
	DEVECHO	HDSK_DEVCNT
	DEVECHO	"\n"
;
HDSK_CFGTBL:
	; DEVICE 0
	.DB	0			; DRIVER DEVICE NUMBER
	.DB	0			; DEVICE STATUS
	.DW	0,0			; CURRENT LBA
#IF (HDSK_DEVCNT >= 2)
	; DEVICE 1
	.DB	1			; DEVICE NUMBER
	.DB	0			; DEVICE STATUS
	.DW	0,0			; CURRENT LBA
#ENDIF
;
#IF ($ - HDSK_CFGTBL) != (HDSK_DEVCNT * HDSK_CFGSIZ)
	.ECHO	"*** INVALID HDSK CONFIG TABLE ***\n"
#ENDIF
;
	.DB	$FF			; END MARKER
;
; STATUS
;
HDSK_STOK	.EQU	0		; OK
HDSK_STNOTRDY	.EQU	-1		; NOT READY
;
;
;
HDSK_INIT:
	CALL	NEWLINE			; FORMATTING
	PRTS("HDSK:$")
	PRTS(" DEVICES=$")
	LD	A,HDSK_DEVCNT
	CALL	PRTDECB
;
; SETUP THE DISPATCH TABLE ENTRIES
;
	XOR	A			; ZERO ACCUM
	LD	(HDSK_CURDEV),A		; INIT CURRENT DEVICE NUM
	LD	IY,HDSK_CFGTBL		; START OF DEV CFG TABLE
HDSK_INIT0:
	CALL	HDSK_PROBE		; HARDWARE PROBE
	JR	NZ,HDSK_INIT1		; SKIP DEVICE IF NOT PRESENT
	LD	BC,HDSK_FNTBL		; BC := DRIVER FUNC TABLE ADDRESS
	PUSH	IY			; CFG ENTRY POINTER
	POP	DE			; ... TO DE
	CALL	DIO_ADDENT		; ADD ENTRY TO GLOBAL DISK TABLE
	CALL	HDSK_INITDEV		; PERFORM DEVICE INITIALIZATION
HDSK_INIT1:
	LD	BC,HDSK_CFGSIZ		; SIZE OF CFG ENTRY
	ADD	IY,BC			; BUMP IY TO NEXT ENTRY
	LD	HL,HDSK_CURDEV		; POINT TO CURRENT DEVICE
	INC	(HL)			; AND INCREMENT IT
	LD	A,(IY)			; GET FIRST BYTE OF ENTRY
	INC	A			; TEST FOR END OF TABLE ($FF)
	JR	NZ,HDSK_INIT0		; IF NOT, LOOP
;
	XOR	A			; INIT SUCCEEDED
	RET				; RETURN
;
; PROBE FOR DEVICE EXISTENCE
;
HDSK_PROBE:
	XOR	A			; SIGNAL SUCCESS
	RET				; AND DONE
;
; INITIALIZE DEVICE
;
HDSK_INITDEV:
	LD	(IY+HDSK_STAT),HDSK_STNOTRDY	; STATUS := NOT READY
	XOR	A			; CLEAR ACCUM
	LD	(IY+HDSK_LBA+0),A	; ZERO LBA
	LD	(IY+HDSK_LBA+1),A	; ...
	LD	(IY+HDSK_LBA+2),A	; ...
	LD	(IY+HDSK_LBA+3),A	; ...
	XOR	A			; SIGNAL SUCCESS (REDUNDANT)
	RET				; AND DONE
;
; DRIVER FUNCTION TABLE
;
HDSK_FNTBL:
	.DW	HDSK_STATUS
	.DW	HDSK_RESET
	.DW	HDSK_SEEK
	.DW	HDSK_READ
	.DW	HDSK_WRITE
	.DW	HDSK_VERIFY
	.DW	HDSK_FORMAT
	.DW	HDSK_DEVICE
	.DW	HDSK_MEDIA
	.DW	HDSK_DEFMED
	.DW	HDSK_CAP
	.DW	HDSK_GEOM
#IF (($ - HDSK_FNTBL) != (DIO_FNCNT * 2))
	.ECHO	"*** INVALID HDSK FUNCTION TABLE ***\n"
#ENDIF
;
;
;
HDSK_VERIFY:
HDSK_FORMAT:
HDSK_DEFMED:
	SYSCHKERR(ERR_NOTIMPL)			; INVALID SUB-FUNCTION
	RET
;
;
;
HDSK_STATUS:
	LD	A,(IY+HDSK_STAT)	; LOAD STATUS
	OR	A			; SET FLAGS
	RET
;
;
;
HDSK_RESET:
	JP	HDSK_DSKRESET
;
; GET DISK CAPACITY
;   RETURN DE:HL=BLOCK COUNT, BC=BLOCK SIZE
;   ASSUME 1GB MEDIA SIZE, SO 1GB / 512B
;   IS $200000 SECTORS
;
HDSK_CAP:
	LD	DE,$20			; BLOCK COUNT MSW
	LD	HL,0			; BLOCK COUNT LSW
	LD	BC,512			; 512 BYTE SECTOR
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
HDSK_GEOM:
	; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS
	; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE
	CALL	HDSK_CAP		; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC
	LD	L,H			; DIVIDE BY 256 FOR # TRACKS
	LD	H,E			; ... HIGH BYTE DISCARDED, RESULT IN HL
	LD	D,$80 | 16		; HEADS / CYL = 16, SET LBA BIT
	LD	E,16			; SECTORS / TRACK = 16
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
HDSK_DEVICE:
	LD	D,DIODEV_HDSK		; D := DEVICE TYPE
	LD	E,(IY+HDSK_DEV)		; E := PHYSICAL DEVICE NUMBER
	LD	C,%00110000		; C := ATTRIBUTES, NON-REMOVABLE HARD DISK
	LD	H,0			; H := 0, DRIVER HAS NO MODES
	LD	L,HDSK_IO		; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
HDSK_MEDIA:
	LD	E,MID_HD		; HARD DISK MEDIA
	LD	D,0			; D:0=0 MEANS NO MEDIA CHANGE
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
HDSK_SEEK:
	BIT	7,D			; CHECK FOR LBA FLAG
	CALL	Z,HB_CHS2LBA		; CLEAR MEANS CHS, CONVERT TO LBA
	RES	7,D			; CLEAR FLAG REGARDLESS (DOES NO HARM IF ALREADY LBA)
	LD	(IY+HDSK_LBA+0),L	; SAVE NEW LBA
	LD	(IY+HDSK_LBA+1),H	; ...
	LD	(IY+HDSK_LBA+2),E	; ...
	LD	(IY+HDSK_LBA+3),D	; ...
	XOR	A			; SIGNAL SUCCESS
	RET				; AND RETURN
;
;
;
HDSK_READ:
	CALL	HB_DSKREAD		; HOOK HBIOS DISK READ SUPERVISOR
	LD	A,HDSK_CMDREAD
	JR	HDSK_RW
;
;
;
HDSK_WRITE:
	CALL	HB_DSKWRITE		; HOOK HBIOS DISK WRITE SUPERVISOR
	LD	A,HDSK_CMDWRITE
	JR	HDSK_RW
;
;
;
HDSK_RW:
	LD	(HDSK_CMD),A		; SET COMMAND BYTE
	LD	(HDSK_DMA),HL		; SAVE INITIAL DMA
	LD	A,E			; SECTOR COUNT TO A
	OR	A			; SET FLAGS
	RET	Z			; ZERO SECTOR I/O, RETURN W/ E=0 & A=0
	LD	B,A			; INIT SECTOR DOWNCOUNTER
	LD	C,0			; INIT SECTOR READ/WRITE COUNT
	LD	A,(IY+HDSK_DEV)		; GET DEVICE NUMBER
	LD	(HDSK_DRV),A		; ... AND SET FIELD IN HDSK PARM BLOCK

	; RESET HDSK INTERFACE IF NEEDED
	LD	A,(IY+HDSK_STAT)	; GET CURRENT STATUS
	OR	A			; SET FLAGS
	PUSH	BC			; SAVE COUNTERS
	CALL	NZ,HDSK_DSKRESET	; RESET IF NOT READY
	POP	BC			; RESTORE COUNTERS
	JR	NZ,HDSK_RW6		; ABORT ON FAILURE

HDSK_RW0:
	PUSH	BC			; SAVE COUNTERS
	XOR	A			; A = 0
	LD	(HDSK_RC),A		; CLEAR RETURN CODE
;
;;;#IF (DSKYENABLE)
;;;  #IF (DSKYDSKACT)
	LD	A,HDSK_LBA
	CALL	LDHLIYA
	CALL	HB_DSKACT		; SHOW ACTIVITY
;;;  #ENDIF
;;;#ENDIF
;
	; CONVERT LBA HHHH:LLLL (4 BYTES)
	; TO HDSK TRACK/SECTOR TTTT:SS (3 BYTES)
	; SAVING TO HDSK PARM BLOCK
	; (IY+HDSK_LBA+0) ==> (HDSK_SEC)
	LD	A,(IY+HDSK_LBA+0)
	LD	(HDSK_SEC),A
	; (IY+HDSK_LBA+1) ==> (HDSK_TRK+0)
	LD	A,(IY+HDSK_LBA+1)
	LD	(HDSK_TRK+0),A
	; (IY+HDSK_LBA+2) ==> (HDSK_TRK+1)
	LD	A,(IY+HDSK_LBA+2)
	LD	(HDSK_TRK+1),A

	; EXECUTE COMMAND
	LD	B,7			; SIZE OF PARAMETER BLOCK
	LD	HL,HDSK_PARMBLK		; ADDRESS OF PARAMETER BLOCK
	LD	C,$FD			; HDSK CMD PORT
	OTIR				; SEND IT

	; GET RESULT
	IN	A,(C)			; GET RESULT CODE
	LD	(HDSK_RC),A		; SAVE IT
	OR	A			; SET FLAGS

#IF (HDSKTRACE > 0)
	PUSH	AF			; SAVE RETURN CODE
#IF (HDSKTRACE == 1)
	CALL	NZ,HDSK_PRT		; DIAGNOSE ERRORS ONLY
#ENDIF
#IF (HDSKTRACE >= 2)
	CALL	HDSK_PRT		; DISPLAY ALL READ/WRITE RESULTS
#ENDIF
	POP	AF			; RESTORE RETURN CODE
#ENDIF

	JR	NZ,HDSK_RW5		; BAIL OUT ON ERROR

	; INCREMENT LBA
	LD	A,HDSK_LBA		; LBA OFFSET IN CFG ENTRY
	CALL	LDHLIYA			; HL := IY + A, REG A TRASHED
	CALL	INC32HL			; INCREMENT THE VALUE

	; INCREMENT DMA
	LD	HL,HDSK_DMA+1		; POINT TO MSB OF DMA
	INC	(HL)			; BUMP DMA BY
	INC	(HL)			; ... 512 BYTES

	XOR	A			; A := 0 SIGNALS SUCCESS

HDSK_RW5:
	POP	BC			; RECOVER COUNTERS
	JR	NZ,HDSK_RW6		; IF ERROR, GET OUT

	INC	C			; RECORD SECTOR COMPLETED
	DJNZ	HDSK_RW0		; LOOP AS NEEDED

HDSK_RW6:
	; RETURN WITH SECTORS READ IN E AND UPDATED DMA ADDRESS IN HL
	LD	E,C			; SECTOR READ COUNT TO E
	LD	HL,(HDSK_DMA)		; CURRENT DMA TO HL
	OR	A			; SET FLAGS BASED ON RETURN CODE
	RET	Z			; RETURN IF SUCCESS
	LD	A,ERR_IO		; SIGNAL IO ERROR
	OR	A			; SET FLAGS
	RET				; AND DONE
;
;
;
HDSK_DSKRESET:
;
#IF (HDSKTRACE >= 2)
	CALL	NEWLINE
	LD	DE,HDSKSTR_PREFIX
	CALL	WRITESTR
	CALL	PC_SPACE
	LD	DE,HDSKSTR_RESET
	CALL	WRITESTR
#ENDIF
;
	LD	B,32
	LD	A,HDSK_CMDRESET
HDSK_DSKRESET1:
	OUT	($FD),A
	DJNZ	HDSK_DSKRESET1

	XOR	A			; STATUS = OK
	LD	(IY+HDSK_STAT),A	; SAVE IT

	RET
;
;
;
HDSK_PRT:
	CALL	NEWLINE

	LD	DE,HDSKSTR_PREFIX
	CALL	WRITESTR

	CALL	PC_SPACE
	LD	DE,HDSKSTR_CMD
	CALL	WRITESTR
	LD	A,(HDSK_CMD)
	CALL	PRTHEXBYTE

	CALL	PC_SPACE
	CALL	PC_LBKT
	LD	A,(HDSK_CMD)
	LD	DE,HDSKSTR_NONE
	CP	HDSK_CMDNONE
	JP	Z,HDSK_PRTCMD
	LD	DE,HDSKSTR_RESET
	CP	HDSK_CMDRESET
	JP	Z,HDSK_PRTCMD
	LD	DE,HDSKSTR_READ
	CP	HDSK_CMDREAD
	JP	Z,HDSK_PRTCMD
	LD	DE,HDSKSTR_WRITE
	CP	HDSK_CMDWRITE
	JP	Z,HDSK_PRTCMD
	LD	DE,HDSKSTR_PARAM
	CP	HDSK_CMDPARAM
	JP	Z,HDSK_PRTCMD
	LD	DE,HDSKSTR_UNKCMD
HDSK_PRTCMD:
	CALL	WRITESTR
	CALL	PC_RBKT

	LD	A,(HDSK_CMD)
	CP	HDSK_CMDREAD
	JR	Z,HDSK_PRTRW
	CP	HDSK_CMDWRITE
	JR	Z,HDSK_PRTRW
	RET

HDSK_PRTRW:
	CALL	PC_SPACE
	LD	A,(HDSK_DRV)
	CALL	PRTHEXBYTE
	CALL	PC_SPACE
	LD	BC,(HDSK_TRK)
	CALL	PRTHEXWORD
	CALL	PC_SPACE
	LD	A,(HDSK_SEC)
	CALL	PRTHEXBYTE
	CALL	PC_SPACE
	LD	BC,(HDSK_DMA)
	CALL	PRTHEXWORD

	CALL	PC_SPACE
	LD	DE,HDSKSTR_ARROW
	CALL	WRITESTR

	CALL	PC_SPACE
	LD	DE,HDSKSTR_RC
	CALL	WRITESTR
	LD	A,(HDSK_RC)
	CALL	PRTHEXBYTE

	CALL	PC_SPACE
	CALL	PC_LBKT
	LD	A,(HDSK_RC)
	LD	DE,HDSKSTR_STOK
	CP	HDSK_STOK
	JP	Z,HDSK_PRTRC
	LD	DE,HDSKSTR_STUNK

HDSK_PRTRC:
	CALL	WRITESTR
	CALL	PC_RBKT

	RET
;
;
;
HDSKSTR_PREFIX	.TEXT	"HDSK:$"
HDSKSTR_CMD	.TEXT	"CMD=$"
HDSKSTR_RC	.TEXT	"RC=$"
HDSKSTR_ARROW	.TEXT	"-->$"
HDSKSTR_NONE	.TEXT	"NONE$"
HDSKSTR_RESET	.TEXT	"RESET$"
HDSKSTR_READ	.TEXT	"READ$"
HDSKSTR_WRITE	.TEXT	"WRITE$"
HDSKSTR_PARAM	.TEXT	"PARAM$"
HDSKSTR_UNKCMD	.TEXT	"UNKCMD$"
HDSKSTR_STOK	.TEXT	"OK$"
HDSKSTR_STUNK	.TEXT	"UNKNOWN ERROR$"
;
;==================================================================================================
;   HDSK DISK DRIVER - DATA
;==================================================================================================
;
HDSK_RC		.DB	0		; CURRENT RETURN CODE
HDSK_CURDEV	.DB	0		; CURRENT DEVICE NUMBER
;
HDSK_PARMBLK:
HDSK_CMD	.DB	0		; COMMAND (HDSK_READ, HDSK_WRITE, ...)
HDSK_DRV	.DB	0		; 0..7, HDSK DRIVE NUMBER
HDSK_SEC	.DB	0		; 0..255 SECTOR
HDSK_TRK	.DW	0		; 0..2047 TRACK
HDSK_DMA	.DW	0		; ADDRESS FOR SECTOR DATA EXCHANGE
