[svn] / z80 / ide.s Repository:
ViewVC logotype

View of /z80/ide.s

Parent Directory Parent Directory | Revision Log Revision Log


Revision 55 - (download) (annotate)
Mon Jan 7 20:51:47 2008 UTC (2 years, 8 months ago) by steve
File size: 13313 byte(s)
Start of basic filesystem

;
; IDE interface driver
;
; Steve Maddison, 06/03/2007
;

; The IDE interface makes use of three I/O ports; two for the 16-bit IDE
; data bus and one for the remaining address and CS signals (referred to
; here as the "control" port).
ide_data_lsb:		equ	0x40
ide_data_msb:		equ	0x41
ide_control:		equ	0x42

ide_dev_name_master:	defm	"hd0\0"
ide_dev_name_slave:	defm	"hd1\0"

ide_driver:		defw	ide_sector_read
			defw	ide_sector_write
			defw	ide_get_size
			defw	ide_get_buffer

; IDE Regsiters (values for the control port)
; Bit 0-2 = Addr0-2, Bit 3 = /CS0, Bit 4 = /CS1
ide_reg_data:		equ	0x10	; 0: Data port
ide_reg_error:		equ	0x11	; 1: Error code is read-only
ide_reg_sectors:	equ	0x12	; 2: Sectors to transfer
; LBA sector address is a 28-bit value made up of the following 3 bytes
; and bits 0-3 of the fourth.
ide_reg_sector_0:	equ	0x13	; 3: low byte
ide_reg_sector_1:	equ	0x14	; 4: lower-middle byte
ide_reg_sector_2:	equ	0x15	; 5: upper-middle byte
ide_reg_sector_3:	equ	0x16	; 6: bits 0-3 high nibble
ide_reg_config:		equ	ide_reg_sector_3
; Following register has two purposes
ide_reg_status:		equ	0x17		; When read
ide_reg_command:	equ	ide_reg_status	; When written

; IDE error codes.
ide_error_none:		equ	0x00
ide_error_none_str:	defm	"No error (timeout?)\0"
ide_error_dam:		equ	0x01
ide_error_dam_str:	defm	"DAM not found\0"
ide_error_track:	equ	0x02
ide_error_track_str:	defm	"Track 000 not found\0"
ide_error_abort:	equ	0x04
ide_error_abort_str:	defm	"Command aborted\0"
ide_error_id:		equ	0x10
ide_error_id_str:	defm	"ID not found\0"
ide_error_ecc:		equ	0x40
ide_error_ecc_str:	defm	"Unrecoverable ECC error\0"
ide_error_block:	equ	0x80
ide_error_block_str:	defm	"Bad block detected\0"
ide_error_bogus_str:	defm	"Bogus error code\0"
; Table of error strings, indexed by bit set in error code.
ide_error_str_table:	defw	ide_error_none_str	; 0
			defw	ide_error_dam_str	; 1
			defw	ide_error_track_str	; 2
			defw	ide_error_bogus_str	; 3 (reserved)
			defw	ide_error_id_str	; 4
			defw	ide_error_bogus_str	; 5 (reserved)
			defw	ide_error_ecc_str	; 6
			defw	ide_error_ecc_str	; 7

; Masks for remaining bits (4-8) of register 6
ide_config_base:	equ	0xa0	; "Always on" bits
ide_config_master:	equ	0x00	; Access master device
ide_config_slave:	equ	0x10	; Access slave device
ide_config_lba:		equ	0x40	; Enable LBA

; Masks for bits in status register
ide_status_error:	equ	0x01	; Last command resulted in error
ide_status_drq:		equ	0x08	; Data Request Ready (sector buffer ready)
ide_status_df:		equ	0x20	; Write fault
ide_status_rdy:		equ	0x40	; Ready for command
ide_status_busy:	equ	0x80	; Executing command

; IDE commands
ide_cmd_read:		equ	0x20	; Read sectors with retry
ide_cmd_write:		equ	0x30	; Write sectors with retry
ide_cmd_id:		equ	0xec	; Identify device

; Offsets into IDE device identification info
ide_info_name:		equ	0x36
ide_info_name_len:	equ	40
ide_info_size:		equ	0x72	; Two 16-bit words, least significant first

; Initialisation messages
ide_init_header:	defm	"IDE devices\n\0";
ide_init_master:	defm	" Master: \0"
ide_init_slave:		defm	" Slave : \0"
ide_init_none:		defm	"none\0"

; Timeout values used when checking device is ready.
ide_timeout_count:	equ	0xffff	; Number of times to loop (16-bit)
ide_timeout_res:	equ	20	; Time to wait each loop (8-bit)


; Name:	ide_block_read
; Desc: Read one block (256 words) from IDE device.
; In:	HL = start of input buffer.
ide_block_read:
		ld	a,ide_reg_data
		out	(ide_control),a
		push	hl
		pop	ix
		ld	b,0	; loop 256 times 
ide_block_read_loop:
		in	a,(ide_data_lsb)
		ld	(ix+1),a
		in	a,(ide_data_msb)
		ld	(ix+0),a
		inc	ix
		inc	ix
		djnz	ide_block_read_loop
		ret

; Name: ide_block_write
; Desc:	Write one block (256 words) to IDE device.
; In:	HL = start of output buffer.
ide_block_write:
		ld	a,ide_reg_data
		out	(ide_control),a
		ld	b,0	; loop 256 times 
ide_block_write_loop:
		ld	a,(hl)
		out	(ide_data_msb),a
		inc	hl
		ld	a,(hl)
		out	(ide_data_lsb),a
		inc	hl
		djnz	ide_block_write_loop
		ret

; Name: ide_check_error
; Desc: Check for error condition.
; In:	none
; Out:	ZF = 1 on success, otherwise 1
; 	A  = IDE error code.
ide_check_error:
		; Fetch the status and check for error flag
		call	ide_get_status
		and	ide_status_error
		jp	z,ide_check_error_end
		; Fetch the error code
		ld	a,ide_reg_error
		out	(ide_control),a
		in	a,(ide_data_lsb)
ide_check_error_end:
		ret

; Name:	ide_config_check
; Desc:	Sanitise saved configuration parameters.
ide_config_check:
		ld	a,(ide_config)		; Load value for sanitising
		and	0xf0			; Clear lower nibble
		or	ide_config_base	| ide_config_lba
		ld	(ide_config),a		; Save result
		ret

; Name:	ide_error_str
; Desc:	Get error string for an IDE error code (if multiple bits are
;	set, the error string corresponding to the least significant
;	bit is returned).
; In:	A = IDE error code
; Out:	HL = Corresponding error string
ide_error_str:
		push	bc
		ld	hl,ide_error_str_table
		cp	0	; No error
		jp	z,ide_error_str_end
		ld	b,8	; There are 8 bits to check
ide_error_str_loop:
		inc	hl	; Point to next error string
		inc	hl
		cp	0x01	; LSB set?
		jp	z,ide_error_str_end
		rra
		djnz	ide_error_str_loop
ide_error_str_end:
		pop	bc
		ret

; Name: ide_get_buffer
; Desc: Return address of internal buffer
; Out:	HL = buffer address;
ide_get_buffer:
		ld	hl,ide_internal_buffer
		ret

; Name:	ide_get_id
; Desc:	Fetch device identifier.
; In:	HL = address of input buffer (0 for internal buffer)
; Out:	ZF = 1 on success,
;	CF = 1 if timed out,
; 	A  = IDE error code,
;	HL = address of input buffer
ide_get_id:
		call	ide_set_buffer
		push	hl			; Remember value
		call	ide_ready
		jp	nz,ide_get_id_end	; Timeout
		call	ide_config_check
		ld	a,ide_reg_config	; Set config
		out	(ide_control),a
		ld	a,(ide_config)
		out	(ide_data_lsb),a
		ld	a,ide_cmd_id		; Send command
		call	ide_send_command
		call	ide_ready_data		; Wait for DRQ
		jp	nz,ide_get_id_end	; Timeout
		call	ide_check_error		; Check for error
		jp	nz,ide_get_id_end	; Bail out - A = error code
		call	ide_block_read		; Read buffer
ide_get_id_end:
		pop	hl			; Restore value
		ret

; Name:	ide_get_name
; Desc:	Return a trimmed, null-terminated version of the device
;	name as found in the identifier info.
; In:	none
; Out:	ZF = 1 on success,
; 	A  = IDE error code,
; 	HL = address of string
ide_get_name:
		push	bc
		ld	hl,0	; Use internal buffer
		call	ide_get_id
		jp	nz,ide_get_name_end
		jp	c,ide_get_name_end
		; Write a 0 after the end of the field, just in case all
		; characters are in use.
		ld	hl,ide_info_name + ide_info_name_len + ide_internal_buffer
		ld	(hl),0

		ld	b,ide_info_name_len	; Keep count, in case the field
						; is nothing but spaces!
ide_get_name_trim:
		dec	hl
		ld	a,(hl)
		cp	0x20
		jp	nz,ide_get_name_ok
		ld	(hl),0
		djnz	ide_get_name_trim
ide_get_name_ok:
		xor	a	; set zero flag
		; Point HL to start of string
		ld	hl,ide_info_name + ide_internal_buffer
ide_get_name_end:
		pop	bc
		ret

; Name: ide_get_size
; Desc: Fetch size of device in blocks
; In:	A = IDE device ID
; Out:	BCDE = Number of blocks,
;	ZF   = 1 on success,
;	A    = IDE error code.
ide_get_size:
		ld	(ide_config),a
		ld	hl,0	; Use internal buffer
		call	ide_get_id
		jp	nz,ide_get_size_end
		jp	c,ide_get_size_end
		ld	a,(ide_info_size + ide_internal_buffer)
		ld	d,a
		ld	a,(ide_info_size + ide_internal_buffer + 1)
		ld	e,a
		ld	a,(ide_info_size + ide_internal_buffer + 2)
		ld	b,a
		ld	a,(ide_info_size + ide_internal_buffer + 3)
		ld	c,a
		xor	a
ide_get_size_end:
		ret
		
; Name: ide_get_status
; Desc:	Fetch contents of the status register
; In:	none
; Out:	A = contents of IDE status register
ide_get_status:	
		ld	a,ide_reg_status
		out	(ide_control),a
		in	a,(ide_data_lsb)
		ret

; Name:	ide_init
; Desc:	Initialise and list devices
ide_init:
		; Output header text
		ld	hl,ide_init_header
		call	console_outs
		; Info for master
		ld	hl,ide_init_master
		ld	a,ide_config_master
		call	ide_init_dev
		; Info for slave
		ld	hl,ide_init_slave
		ld	a,ide_config_slave
		call	ide_init_dev
		ret

; Name: ide_init_dev
; Desc: Helper for ide_init - scans for device, then initialises it and
;	prints an info line as necessary.
; In:	A  = IDE config parameters
;	HL = Address of string constant
;	IY = Device name
ide_init_dev:
		push	bc
		push	de
		call	console_outs
		ld	(ide_config),a
		call	ide_config_check
		ld	b,10			; Try ten times
ide_init_dev_get_name:
		call	ide_get_name
		jp	z,ide_init_dev_found
		djnz	ide_init_dev_get_name
		jp	nc,ide_init_dev_error
		ld	hl,ide_init_none
		jp	ide_init_dev_print
ide_init_dev_error:
		call	ide_error_str
		jp	ide_init_dev_print
ide_init_dev_found:
		push	hl
		; Add a new device
		ld	a,(ide_config)
		and	ide_config_slave
		jp	nz,ide_init_dev_slave
		ld	hl,ide_dev_name_master
		jp	ide_init_dev_add
ide_init_dev_slave:
		ld	hl,ide_dev_name_slave
ide_init_dev_add:
		ld	a,(ide_config)		; ID
		ld	b,dev_flag_block	; Flags
		ld	c,0x02			; Block size (512 >> 8)
		ld	de,ide_driver		; Driver
		call	dev_add
		pop	hl
ide_init_dev_print:
		call	console_outs
		ld	a,'\n'
		call	console_outb
		pop	de
		pop	bc
		ret
		
; Name: ide_ready
; Desc:	Wait until device is ready.
; Out:	ZF = 1 on success, 0 if timed out.
;	CF = 1 on timeout
ide_ready:
		push	bc
		; Check BUSY=0 and RDY=1
		ld	c,ide_status_busy & ~ide_status_rdy
		call	ide_wait
		pop	bc
		ret

; Name: ide_ready_data
; Desc:	Wait until device is ready to send/receive data.
; Out:	ZF = 1 on success, 0 if timed out.
;	CF = 1 on timeout
ide_ready_data:
		push	bc
		; Check BUSY=0 and DRQ=1
		ld	c,ide_status_busy & ~ide_status_drq
		call	ide_wait
		pop	bc
		ret

; Name: ide_sector_count
; Desc: Set IDE sector count register (currently always
;	to "1", although this could be parameterised).
ide_sector_count:
		ld	a,ide_reg_sectors
		out	(ide_control),a
		ld	a,1
		out	(ide_data_lsb),a
		ret

; Name:	ide_sector_read
; Desc:	Read one sector from device.
; In:	A    = IDE device ID
; 	BCDE = Sector index as per ide_sector_select,
; 	HL   = address of input buffer (0 for internal buffer)
; Out:	ZF = 1 on success,
;	CF = 1 if timed out,
; 	A  = IDE error code,
;	HL = address of input buffer
ide_sector_read:
		ld	(ide_config),a
		call	ide_set_buffer
		push	hl				; Remember values
		call	ide_ready			; Wait until device is ready
		jp	nz,ide_sector_read_error	; Timeout
		call	ide_sector_select		; Program sector index
		call	ide_sector_count		; Set sector count
		ld	a,ide_cmd_read			; Send read command
		call	ide_send_command
		call	ide_ready_data			; Wait until data is ready
		jp	nz,ide_sector_read_error	; Timeout
		call	ide_check_error
		jp	nz,ide_sector_read_error
		pop	hl
		push	hl
		call	ide_block_read			; Copy the data to our buffer
ide_sector_read_error:
		pop	hl				; Restore value
		ret

; Name: ide_sector_select
; Desc: Send LBA 28-bit sector address to the appropriate IDE registers.
; In:	BCDE = sector address, E being least significant. Only the least
; 	significant nibble of B is used.
ide_sector_select:
		ld	a,ide_reg_sector_0
		out	(ide_control),a
		ld	a,e
		out	(ide_data_lsb),a

		ld	a,ide_reg_sector_1
		out	(ide_control),a
		ld	a,d
		out	(ide_data_lsb),a

		ld	a,ide_reg_sector_2
		out	(ide_control),a
		ld	a,c
		out	(ide_data_lsb),a

		ld	a,ide_reg_sector_3
		out	(ide_control),a
		ld	a,b
		and	0x0f			; clear top nibble
		ld	b,a
		call	ide_config_check
		or	b
		out	(ide_data_lsb),a
		ret

; Name:	ide_sector_write
; Desc:	Write one sector to the device.
; In:	A    = IDE device ID
;	BCDE = Sector index as per ide_sector_select,
; 	HL   = address of output buffer.
; Out:	ZF = 1 on success,
;	CF = 1 if timed out,
; 	A  = IDE error code.
ide_sector_write:
		ld	(ide_config),a
		push	hl				; Remember values
		call	ide_ready			; Wait until device is ready
		jp	nz,ide_sector_write_error	; Timeout
		call	ide_sector_select		; Program sector index
		call	ide_sector_count		; Set sector count
		ld	a,ide_cmd_write			; Send write command
		call	ide_send_command
		call	ide_ready_data			; Wait until device is ready for data
		jp	nz,ide_sector_write_error	; Timeout
		pop	hl
		push	hl
		call	ide_block_write			; Copy our data to the device
		call	ide_ready			; Wait until device is ready
		jp	nz,ide_sector_write_error	; Timeout
		call	ide_check_error
ide_sector_write_error:
		pop	hl			; Restore value
		ret

; Name:	ide_send_command
; Desc: Send a command to the device
; In:	A = IDE command code
ide_send_command:
		push	af
		ld	a,ide_reg_command
		out	(ide_control),a
		pop	af
		out	(ide_data_lsb),a
		ret

; Name: ide_set_buffer
; Desc:	Check for magic buffer address (0)
; In:	HL = proposed buffer address
; Out:	HL = actual buffer address
ide_set_buffer:
		ld	a,h
		or	l
		jp	nz,ide_set_buffer_end
		ld	hl,ide_internal_buffer
ide_set_buffer_end:
		ret

; Name: ide_wait
; Desc:	Wait for given value of status register, with timeout.
; In:	C = mask to AND with register, such that a result of zero
;	indicates success.
; Out:	ZF = 1 on success, 0 if timed out,
;	CF = 1 if timed out
ide_wait:
		push	hl
		push	de
		and	a
		ld	de,ide_timeout_count
ide_wait_loop:
		call	ide_get_status
		ld	h,a
		and	c
		jp	z,ide_wait_end
		ld	b,ide_timeout_res
ide_wait_pause:
		djnz	ide_wait_pause
		dec	de
		ld	a,d
		or	e
		jp	nz,ide_wait_loop
		ld	a,h
		; Timeout - clear zero flag, set carry flag
		ld	a,1
		and	a
		scf	
ide_wait_end:
		pop	de
		pop	hl
		ret


Repository Admin
ViewVC Help
Powered by ViewVC 1.0.5