;
; cfdisc.s - Compact Flash routines
;
; Hacked from Mark Allcorns CFhardware,281
; http://www.primrosebank.net/computers/mtx/projects/cfx/cfx.htm
; http://www.gaby.de/gide/IDE-TCJ.pdf
; http://wiki.osdev.org/PCI_IDE_Controller#IDE_Interface
;
; @@@ TODO, use multiple block I/O rather than repeated single block I/O.
;
        .module cfdisc

;82c55 port mapping for the cfx ide interface hardware

ide8255_lsb	= 0x6c		; port a lower 8 data bits
ide8255_msb	= 0x6d		; port b upper 8 data bits
ide8255_ctl	= 0x6e		; port c control lines
ide8255_cfg	= 0x6f		; direction control

;control port settings
ide8255_read	= 0b10010010	;port c is output a and b inputs,using mode 0
ide8255_write	= 0b10000000	;all 3 ports are outputs
;ide address lines a0-a2 direct to interface
;ide control lines are inverted, so need to be set to trigger an active low pulse
;b3 = cs0, select main refisters
;b4 = cs1, select aux registers
;b5 = ide write line
;b6 = ide read line
;b7 = ide reset line
ide_wr_line	= 0b00100000
ide_rd_line	= 0b01000000
ide_reset	= 0b10000000

;ide command registers
ide_data	= 0b00001000	;cs0 register 0 16 bit wide data
ide_data_rlow	= 0b01001000	;cs0 register 0 16 bit wide data read line low
ide_data_wlow	= 0b00101000	;cs0 register 0 16 bit wide data write line low
ide_err		= 0b00001001	;cs0 register 1
ide_count	= 0b00001010	;cs0 register 2 sector count - always use 1 to transfer to the buffer
ide_lbalow	= 0b00001011	;cs0 register 3 low 8 bits of lba address
ide_lbamid	= 0b00001100	;cs0 register 4 mid 8 bits of lba address
ide_lbahigh	= 0b00001101	;cs0 register 5 upper 8 bits of lba address
ide_lbatop	= 0b00001110	;cs0 register 6 top 4 bits of lba address
ide_command	= 0b00001111	;cs0 register 7 write command
ide_status	= 0b00001111	;cs0 register 7 read status
;
ide_control	= 0b00010110	;cs1 register 6
ide_astatus	= 0b00010111	;cs1 register 7 read other status register

; ide status register:
; b7 = busy     1=busy, 0=not busy
; b6 = ready    1=ready for command, 0=not ready yet
; b5 = df       1=fault occured inside drive
; b4 = dsc      1=seek complete
; b3 = drq      1=data request ready, 0=not ready to xfer yet
; b2 = corr     1=correctable error occured
; b1 = idx      vendor specific
; b0 = err      1=error occured

;the actual ide commands used
cmd_recal	= 0x10
cmd_read	= 0x20
cmd_write	= 0x30
cmd_init	= 0x91
cmd_id		= 0xec
cmd_spinup	= 0xe0
cmd_spindn	= 0xe1

        .area   _CODE

; int cfcheck(void)
_cfcheck::
	ld	a,(0xffc1)
	cp	#'C'
	jr	nz,cfcheck2
	ld	a,(0xffc2)
	cp	#'F'
	jr	nz,cfcheck2
	ld	a,(0xffc3)
	cp	#'X'
	jr	nz,cfcheck2
	ld	hl,#1
	ret
cfcheck2:
	ld	hl,#0
	ret

; int cfinit(void)
_cfinit::
					;do a hard reset on the drive, by pulsing its reset pin.
	ld	a,#ide8255_read		;set the 8255 to read mode
	out	(ide8255_cfg),a
	ld	a,#ide_reset
	out	(ide8255_ctl),a		;pull the reset pin low
	ld	b,#8			;needs to stay low for 25usec according to the 1992 spec
reset_loop:				;100 cycles at 4mhz
	djnz	reset_loop
	xor	a
	out	(ide8255_ctl),a		;pull the reset pin back high
	ld	c,#ide_lbatop
	ld	e,#0b11100000		;select the master device. lba mode
	call	ide_wr_8
;
	;ld	b,#0			;for the timeout counter
	ld	bc,#ide_status		;implied ld b,0 in addition to setting the ide register
	jr	ide_init_pass1		;jump past the timeout checks on the first pass
ide_init_loop:
	dec	b			;exit if we've tried 256 times and drive still not ready
	jr	z,m_timeout
	call	cf_delay		;cf doesn't need to spin up, but allow some extra time
					;just in case. the 255 calls of the delay routine take
					;approx 2.5 sec at 4mhz
ide_init_pass1:
	call	ide_rd_8		;read status register to e
	rl	e			;bit 7 to carry
	jr	c, ide_init_loop	;wait for bsy to be clear
	rl	e			;bit 6 to carry
	jr	nc,ide_init_loop	;wait for rdy to be set
;
	ld	hl,#1
	ret
;
m_timeout:
	ld	hl,#0
	ret

;waste approx 3339 * 12 is approx 40,000 cycles delay 0.01 sec at 4mhz.
cf_delay:
	push	bc
	ld	bc,#12
cf_delay_loop:
	djnz	cf_delay_loop		;3323 cycles on inner loop
	dec	c			;4
	jr	nz,cf_delay_loop	;12
	pop	bc
	ret

; int cfread(unsigned long block, unsigned count, unsigned char *dest)
_cfread::
	ld	iy,#0
	add	iy,sp
cfreadloop:
	call	ide_wait_not_busy
	call	cfsetlba		;send lba number to the drive
	ld	e,#cmd_read
	ld	c,#ide_command
	call	ide_wr_8
	call	ide_wait_drq
	and	#0x01			;isolate bit 0
	jr	nz,cfgeterr		;error bit set, find out why
	ld	l,8(iy)
	ld	h,9(iy)
	call	cfread_data
	ld	8(iy),l
	ld	9(iy),h
	dec	6(iy)
	jr	z,cfreaddone
	call	inclba
	jr	cfreadloop
cfreaddone:
	ld	hl,#1
	ret
cfgeterr:
	ld	c,#ide_err
	call	ide_rd_8
	ld	l,a
	ld	h,#0xff
	ret

; int cfwrite(unsigned long block, unsigned count, const unsigned char *dest)
_cfwrite::
	ld	iy,#0
	add	iy,sp
cfwriteloop:
	call	ide_wait_not_busy
	call	cfsetlba		;send lba number to the drive
	ld	e,#cmd_write
	ld	c,#ide_command
	call	ide_wr_8
	call	ide_wait_drq
	and	#0x01			;isolate bit 0
	jr	nz,cfgeterr		;error bit set, find out why
	ld	l,8(iy)
	ld	h,9(iy)
	call	cfwrite_data
	ld	8(iy),l
	ld	9(iy),h
	call	ide_wait_not_busy
	and	#0x01
	jr	nz,cfgeterr
	dec	6(iy)
	jr	z,cfwritedone
	call	inclba
	jr	cfwriteloop
cfwritedone:
	ld	hl,#1
	ret

inclba:
	inc	2(iy)
	ret	nz
	inc	3(iy)
	ret	nz
	inc	4(iy)
	ret	nz
	inc	5(iy)
	ret

; expreimental CF read code PIO access re-jigged
cfread_data:
	;ld	b,#0			;256 2 byte transfers needed implied
	ld	bc,#ide8255_ctl		;preset c to pio port c
	ld	e,#ide_data		;all access through the 16 bit wide data register
	ld	d,#ide_data_rlow
read_data_loop:
	out	(c),e			;(12) set the register & chip select bits drive with read high                          ;
	out	(c),d			;(12) drive the read line low
	in	a,(ide8255_lsb)		;(11)
	ld	(hl),a			;( 7)
	inc	hl			;( 6)
	in	a,(ide8255_msb)		;(11)
	ld	(hl),a			;( 7)
	inc	hl			;( 6)
	djnz	read_data_loop		;(13)
	xor	a
	out	(c),a			;deselect everything also drives read high a final time
	ret

cfwrite_data:
	;ld	b,#0			;256 2 byte transfers needed implied
	ld	bc,#ide8255_ctl		;preset c to pio port c
	ld	e,#ide_data		;all access through the 16 bit wide data register
	ld	d,#ide_data_wlow
	ld	a,#ide8255_write	;set the 8255 to output mode
	out	(ide8255_cfg),a
write_data_loop:
	out	(c),e			;set the register & chip select bits write is high
	ld	a,(hl)
	inc	hl
	out	(ide8255_lsb),a		;put the low 8 bits onto the bus
	ld	a,(hl)
	inc	hl
	out	(ide8255_msb),a		;put the high 8 bits onto the bus
	out	(c),d			;drive the write line low
	djnz	write_data_loop
	xor	a
	out	(c),a			;deselect everything also drives write high
	ld	a,#ide8255_read
	out	(ide8255_cfg),a		;set the 8255 back to read mode
	ret

;
; Set CF LBA
;
cfsetlba:
	push	hl
	push	de
	push	bc
	push	af
	ld	a,5(iy)
	and	a,#0x0f
	or	#0xe0
	ld	e,a			;top 4 bits of address zero, 0xe is for lba master
	ld	c,#ide_lbatop
	call	ide_wr_8		;write lba mode to lba top byte register
	ld	e,4(iy)
	ld	c,#ide_lbahigh          
	call	ide_wr_8		;write to lba bits 16-23
	ld	e,3(iy)
	ld	c,#ide_lbamid          
	call	ide_wr_8		;write to lba bits 8-15
	ld	e,2(iy)
	ld	c,#ide_lbalow          
	call	ide_wr_8		;write to lba bits 0-7
	ld	e,#1
	ld	c,#ide_count
	call	ide_wr_8		;set the sector count to 1
	pop	af
	pop	bc
	pop	de
	pop	hl
	ret

ide_wait_not_busy:
	push	bc
	push	de
ide_wait_not_b_loop:
	ld	c,#ide_status
	call	ide_rd_8		;read status register to e
	ld	a,e			;should probably check for a timeout here; return status in a
	rl	e			;bit 7 to carry
	jr	c,ide_wait_not_b_loop	;wait for bsy to be clear
	pop	de
	pop	bc
	ret

ide_wait_ready:
	push	bc
	push	de
ide_wait_r_loop:
	ld	c,#ide_status     
	call	ide_rd_8		;read status register to e
	ld	a,e			;should probably check for a timeout here; return status in a
	rl	e			;bit 7 to carry
	jr	c,ide_wait_r_loop	;wait for bsy to be clear
	rl	e			;bit 6 to carry
	jr	nc,ide_wait_r_loop	;wait for rdy to be set
	pop	de
	pop	bc
	ret

;wait for the drive to be ready to transfer data.
;returns the drive's status in a
ide_wait_drq:
	push	bc
	push	de
ide_wait_d_loop:
	ld	c,#ide_status     
	call	ide_rd_8		;read status register to e
	ld	a,e			;should probably check for a timeout here, return status in a
	rl	e			;bit 7 to carry
	jr	c,ide_wait_d_loop	;wait for bsy to be clear
	rl	e			;bit 6 to carry
	rl	e			;bit 5 to carry
	rl	e			;bit 4 to carry
	rl	e			;bit 3 to carry
	jr	nc,ide_wait_d_loop	;wait for drq to be set
	pop	de
	pop	bc
	ret

;8 bit read of ide register
;data in e, (d unused in 8 bit transfer) 
;register number in c
;8255 in input mode by default
ide_rd_8:
	push	af
	ld	a,c
	out	(ide8255_ctl),a		;set the register & chip select bits
	or	#ide_rd_line
	out	(ide8255_ctl),a		;drive the read line low
	in	a,(ide8255_lsb)
	ld	e,a
	ld	a,c
	out	(ide8255_ctl),a		;drive the read line high again, completing the write cycle
	xor	a
	out	(ide8255_ctl),a		;deselect everything
	pop	af
	ret

;8 bit write to ide register
;data in e, (d unused in 8 bit transfer) 
;register number in c
ide_wr_8:
	push	af
	ld	a,#ide8255_write	;set the 8255 to output mode
	out	(ide8255_cfg),a
	ld	a,e
	out	(ide8255_lsb),a		;put the low 8 bits onto the bus
	ld	a,c
	out	(ide8255_ctl),a		;set the register & chip select bits
	or	#ide_wr_line
	out	(ide8255_ctl),a		;drive the write line low
	ld	a,c
	out	(ide8255_ctl),a		;drive the write line high again, completing the write cycle
	xor	a
	out	(ide8255_ctl),a		;deselect everything
	ld	a,#ide8255_read
	out	(ide8255_cfg),a		;set the 8255 back to read mode
	pop	af
	ret
