www.pudn.com > idefastsdfsad.rar > idefast.asm


; Somewhat faster communication with an IDE disk drive
; see http://www.pjrc.com/tech/8051/ide/ for more info

;  This code is an original work by Paul Stoffregen, written
;  in December 1999.  This code has been placed in the
;  public domain.  You may use it without any restrictions.
;  You may include it in your own projects, even commercial
;  (for profit) products.

;  This code is distributed in the hope that they will be useful,
;  but without any warranty; without even the implied warranty of
;  merchantability or fitness for a particular purpose.

; this code contains a modified copy of the code to read from an
; IDE hard drive, which is optimized for speed.  These routines
; were used in a homebrew MP3 player, which was able to sustain
; reads of 192 kbit/sec data while feeding it to a MAS3507D MP3
; decoder chip, using the UART in sync mode.  Reading 256 kbit/sec
; did not not work.  The 8051 was clocked with a 14.7456 MHz crystal.

; there is no documentation specific to this modified version,
; other than the comments within this code.

.equ	location, 0x2000	;where this program will exist
.equ	sect_buf, 0x2E00	;512 byte buffer

;------------------------------------------------------------------
; Hardware Configuration

;8255 chip.  Change these to specify where the 8255 is addressed,
;and which of the 8255's ports are connected to which ide signals.
;The first three control which 8255 ports have the control signals,
;upper and lower data bytes.  The last two are mode setting for the
;8255 to configure its ports, which must correspond to the way that
;the first three lines define which ports are connected.
.equ	ide_8255_lsb, 0x4000	;lower 8 bits
.equ	ide_8255_msb, 0x4001	;upper 8 bits
.equ	ide_8255_ctl, 0x4002	;control lines
.equ	cfg_8255, 0x4003
.equ	rd_ide_8255, 10010010b	;ide_8255_ctl out, ide_8255_lsb/msb input
.equ	wr_ide_8255, 10000000b	;all three ports output

;ide control lines for use with ide_8255_ctl.  Change these 8
;constants to reflect where each signal of the 8255 each of the
;ide control signals is connected.  All the control signals must
;be on the same port, but these 8 lines let you connect them to
;whichever pins on that port.
.equ	ide_a0_line, 0x01	;direct from 8255 to ide interface
.equ	ide_a1_line, 0x02	;direct from 8255 to ide interface
.equ	ide_a2_line, 0x04	;direct from 8255 to ide interface
.equ	ide_cs0_line, 0x08	;inverter between 8255 and ide interface
.equ	ide_cs1_line, 0x10	;inverter between 8255 and ide interface
.equ	ide_wr_line, 0x20	;inverter between 8255 and ide interface
.equ	ide_rd_line, 0x40	;inverter between 8255 and ide interface
.equ	ide_rst_line, 0x80	;inverter between 8255 and ide interface


;------------------------------------------------------------------
; More symbolic constants... these should not be changed, unless of
; course the IDE drive interface changes, perhaps when drives get
; to 128G and the PC industry will do yet another kludge.

;some symbolic constants for the ide registers, which makes the
;code more readable than always specifying the address pins
.equ	ide_data,	ide_cs0_line
.equ	ide_err,	ide_cs0_line + ide_a0_line
.equ	ide_sec_cnt,	ide_cs0_line + ide_a1_line
.equ	ide_sector,	ide_cs0_line + ide_a1_line + ide_a0_line
.equ	ide_cyl_lsb,	ide_cs0_line + ide_a2_line
.equ	ide_cyl_msb,	ide_cs0_line + ide_a2_line + ide_a0_line
.equ	ide_head,	ide_cs0_line + ide_a2_line + ide_a1_line
.equ	ide_command,	ide_cs0_line + ide_a2_line + ide_a1_line + ide_a0_line
.equ	ide_status,	ide_cs0_line + ide_a2_line + ide_a1_line + ide_a0_line
.equ	ide_control,	ide_cs1_line + ide_a2_line + ide_a1_line
.equ	ide_astatus,	ide_cs1_line + ide_a2_line + ide_a1_line + ide_a0_line

;IDE Command Constants.  These should never change.
.equ	ide_cmd_recal, 0x10
.equ	ide_cmd_read, 0x20
.equ	ide_cmd_write, 0x30
.equ	ide_cmd_init, 0x91
.equ	ide_cmd_id, 0xEC
.equ	ide_cmd_spindown, 0xE0
.equ	ide_cmd_spinup, 0xE1


;------------------------------------------------------------------
;internal ram usage

.equ	lba, 0x10		;4 bytes, 28 bit Logical Block Address
.equ	stack, 0x40



;------------------------------------------------------------------
; Routines that talk with the IDE drive, these should be called by
; the main program.


	;read a sector, specified by the 4 bytes in "lba"
	;Return, acc is zero on success, non-zero for an error
read_sector:
	acall	wr_lba
	mov	a, #ide_command
	mov	r2, #ide_cmd_read
	acall	ide_wr
	acall	ide_drq
	jb	acc.0, rs_err
	clr	a
	ret
rs_err: mov	a, #ide_err
	acall	ide_rd
	mov	a, r2
	jz	rs_err2
	ret
rs_err2:mov	a, #255
	ret



	;initialize the ide drive
ide_init:
	;acall	ide_hard_reset		;usually not necessary
	mov	a, #ide_head
	mov	r2, #10100000b
	acall	ide_wr			;select the master device
	mov	a, #ide_status
	acall	ide_rd
	mov	a, r2
	;should probably check for a timeout here
	jnb	acc.6, ide_init		;wait for RDY bit to be set
	jb	acc.7, ide_init		;wait for BSY bit to be clear
	mov	a, #ide_head
	mov	r2, #0xAF
	acall	ide_wr			;what should this config parm be?
	mov	a, #ide_sec_cnt
	mov	r2, #64
	acall	ide_wr			;what should this config parm be?
	mov	a, #ide_command
	mov	r2, #ide_cmd_init
	acall	ide_wr			;do init parameters command
	acall	ide_busy
	mov	a, #ide_command
	mov	r2, #ide_cmd_recal	;do recal command (is this necessary?)
	acall	ide_wr
	acall	ide_busy
	ret



;------------------------------------------------------------------
; Not quite as low, low level I/O.  These routines talk to the drive,
; using the low level I/O.  Normally a main program should not call
; directly to these.



	;This is the parts that's faster.  Notice that it doesn't
	;call to ide_rd and ide_wr, and it does less manipulation
	;with the 8255.

	;Read a block of 512 bytes (one sector) from the drive
	;and store it in memory @ DPTR
read_data:
	mov	r0, #0
	mov	r1, #2
	mov	r2, dph
	mov	p2, dph
	mov	dptr, #cfg_8255
	mov	a, #rd_ide_8255
	movx	@dptr, a		;config 8255 chip, read mode
rdataloop:
	mov	dptr, #ide_8255_ctl
	;mov	a, #ide_data
	;movx	@dptr, a		;drive address onto control lines
	mov	a, #ide_data | ide_rd_line	
	movx	@dptr, a		;assert read pin
	mov	dptr, #ide_8255_lsb
	clr	a
	movc	a, @a+dptr		;read the lower byte
	movx	@r0, a
	inc	r0
	mov	dptr, #ide_8255_msb
	clr	a
	movc	a, @a+dptr		;read the upper byte
	movx	@r0, a
	inc	r0
	mov	dptr, #ide_8255_ctl
	clr	a
	movx	@dptr, a		;deassert all control pins
	mov	a, r0
	jnz	rdataloop
	mov	a, r2
	inc	a
	mov	p2, a
	djnz	r1, rdataloop
	mov	p2, #255
	ret




	;write the logical block address to the drive's registers
wr_lba:
	mov	a, lba+3
	anl	a, #0x0F
	orl	a, #0xE0
	mov	r2, a
	mov	a, #ide_head
	acall	ide_wr
	mov	a, #ide_cyl_msb
	mov	r2, lba+2
	acall	ide_wr
	mov	a, #ide_cyl_lsb
	mov	r2, lba+1
	acall	ide_wr
	mov	a, #ide_sector
	mov	r2, lba+0
	acall	ide_wr
	;mov	a, #ide_sec_cnt
	;mov	r2, #1
	;acall	ide_wr
	ret




	;Wait for the ide drive to not be busy.
	;Returns the drive's status in Acc
ide_busy:
	mov	a, #ide_status		;wait for RDY bit to be set
	acall	ide_rd
	mov	a, r2
	;should probably check for a timeout here
	jb	acc.7, ide_busy
	ret


	;note that this function is changed to have a call to an
	;idle loop (commented out).

	;Wait for the drive to be ready to transfer data.
	;Returns the drive's status in Acc
ide_drq:
	mov	a, #ide_status		;wait for DRQ bit to be set
	acall	ide_rd
	;acall	idle
	mov	a, r2
	;should probably check for a timeout here
	jb	acc.7, ide_drq		;wait for BSY to be clear
	jnb	acc.3, ide_drq		;wait for DRQ to be set
	ret


;------------------------------------------------------------------
; Low Level I/O to the drive.  These are the routines that talk
; directly to the drive, via the 8255 chip.  Normally a main
; program would not call to these.

	;Do a read bus cycle to the drive, using the 8255.  This
	;is slow, because we have to manipulate the 8255 and use
	;the 8051's limited moxv and movx to do it (via dptr).
	;Note that the drive is read using MOVC, to run on a board
	;where the 8255 is read using PSEN.  If your board uses
	;RD instead of PSEN, chance the MOVC's to MOVX's.

	;input acc = ide regsiter address
	;output r2 = lower byte read from ide drive
	;output r3 = upper byte read from ide drive
	;dptr is changed
ide_rd:
	push	acc
	mov	dptr, #cfg_8255
	mov	a, #rd_ide_8255
	movx	@dptr, a		;config 8255 chip, read mode
	mov	dptr, #ide_8255_ctl
	pop	acc
	movx	@dptr, a		;drive address onto control lines
	orl	a, #ide_rd_line	
	movx	@dptr, a		;assert read pin
	mov	dptr, #ide_8255_msb
	;clr	a
	;movc	a, @a+dptr		;read the upper byte
	;mov	r3, a
	mov	dptr, #ide_8255_lsb
	clr	a
	movc	a, @a+dptr		;read the lower byte
	mov	r2, a
	mov	dptr, #ide_8255_ctl
	clr	a
	movx	@dptr, a		;deassert all control pins
	ret


	;Do a write bus cycle to the drive, via the 8255

	;input acc = ide register address
	;input r2 = lsb to write
	;input r3 = msb to write
	;dptr is changed
ide_wr:
	;push	acc
	mov	r4, a
	mov	dptr, #cfg_8255
	mov	a, #wr_ide_8255
	movx	@dptr, a		;config 8255 chip, write mode
	mov	dptr, #ide_8255_lsb
	mov	a, r2
	movx	@dptr, a		;drive lower lines with lsb (r2)
	;mov	dptr, #ide_8255_msb
	;mov	a, r3
	;movx	@dptr, a		;drive upper lines with msb (r3)
	mov	dptr, #ide_8255_ctl
	;pop	acc
	mov	a, r4
	movx	@dptr, a		;drive address onto control lines
	orl	a, #ide_wr_line	
	movx	@dptr, a		;assert write pin
	;nop
	clr	a
	movx	@dptr, a		;deassert all control pins
	;mov	dptr, #cfg_8255
	;mov	a, #rd_ide_8255
	;movx	@dptr, a		;config 8255 chip, read mode
	ret


	;do a hard reset on the drive, by pulsing its reset pin.
	;this should usually be followed with a call to "ide_init".
ide_hard_reset:
	mov	dptr, #cfg_8255
	mov	a, #wr_ide_8255
	movx	@dptr, a		;config 8255 chip, write mode
	mov	dptr, #ide_8255_ctl
	mov	a, #ide_rst_line
	movx	@dptr, a		;hard reset the disk drive
	mov	r2, #250
	djnz	r2, *			;delay > 25 us (reset pulse width)
	clr	a
	movx	@dptr, a		;no ide control lines asserted
	ret