Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2461081
  • 博文数量: 392
  • 博客积分: 7040
  • 博客等级: 少将
  • 技术积分: 4138
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-17 13:03
个人简介

范德萨发而为

文章分类

全部博文(392)

文章存档

2017年(5)

2016年(19)

2015年(34)

2014年(14)

2013年(47)

2012年(40)

2011年(51)

2010年(137)

2009年(45)

分类:

2009-12-18 09:01:37

programming_ata_atapi  
An Introduction To Programming With ATA And ATAPI
If you're familiar with computer maintenance procedures, 
you'
re probably familiar with IDE (Integrated Drive Electronics),
the typical standard
for connecting hard drives and CD-ROM drives
to a PC
. ATA (AT Attachment) is essentially the same thing as IDE;
just a different name
.

Most motherboards today have two IDE controllers built-in,
designated
as the primary and the secondary IDE controller.
These two IDE controllers use the following standard I/O addresses:

Primary IDE controller: 1F0h to 1F7h and 3F6h to 3F7h
Secondary IDE controller: 170h to 177h and 376h to 377h

Each I/O address corresponds to a register on the IDE controller.
The following is a list of each I/O address used by ATA controllers
and the corresponding regiuster. (I/O addys given are for the primary
IDE controller
, obviously, but they correspond to the same secondary
IDE controller addresses
. Thus, for example, the secondary IDE
controller
's data register is at 170h, the secondary controller's
error
and features register is at 171h, and so on):

1F0 (Read and Write): Data Register
1F1 (Read): Error Register
1F1 (Write): Features Register
1F2 (Read and Write): Sector Count Register
1F3 (Read and Write): LBA Low Register
1F4 (Read and Write): LBA Mid Register
1F5 (Read and Write): LBA High Register
1F6 (Read and Write): Drive/Head Register
1F7 (Read): Status Register
1F7 (Write): Command Register
3F6 (Read): Alternate Status Register
3F6 (Write): Device Control Register

The status register is an 8-bit register which contains the following bits,
listed
in order from left to right:

BSY
(busy)
DRDY
(device ready)
DF
(Device Fault)
DSC
(seek complete)
DRQ
(Data Transfer Requested)
CORR
(data corrected)
IDX
(index mark)
ERR
(error)

The error register is also an 8-bit register, and contains the following bits,
again listed
in order from left to right:

BBK
(Bad Block)
UNC
(Uncorrectable data error)
MC
(Media Changed)
IDNF
(ID mark Not Found)
MCR
(Media Change Requested)
ABRT
(command aborted)
TK0NF
(Track 0 Not Found)
AMNF
(Address Mark Not Found)

ATA commands are issued
by writing the commands to the command register.
More specifically, ATA commands are issued using the following steps:

1. Poll the status register until it indicates the device is not busy
(BUSY will be set to 0)

2. Disable interrupts (assembler "cli" command)

3. Poll the status register until it indicates the device is ready
(DRDY will be set to 1)

4. Issue the command by outputting the command opcode to the command register

5. Re-enable interrupts (assembler "sti" command)

The following program is a relatively simple assembler program to run
the ATA
"IDENTIFY DRIVE" command, and print out the results of this
command to the screen
.

MOV DX
, 1F7h ;status register
LOOP1
:
IN AL
, DX ;sets AL to status register (which is 8 bits)

;If the first bit of the status register (BUSY) isn't 0, the device is busy,
;so keep looping until it isn'
t.

AND AL
, 10000000xB
JNE LOOP1

;----------------------------------------------------------------------------

;Clear interrupts so something doesn't interrupt the drive or controller
;while this program is working.
CLI

;----------------------------------------------------------------------------

MOV DX, 1F7h ;status register again
LOOP2:
IN AL, DX ;sets AL to status register again

;If the second bit of the status register (DRDY) isn'
t 1, the device isn't
;ready, so keep looping until it is.

AND AL, 01000000xB
JE LOOP2

;----------------------------------------------------------------------------

MOV DX, 1F6h ;device/head register
MOV AL, 0 ;0 selects device 0 (master). 10h would select device 1 (slave).
OUT DX, AL ;selects master device

MOV DX, 1F7h ;command register
MOV AL, 0ECh ;"IDENTIFY DRIVE" command
OUT DX, AL ;sends the command!

;----------------------------------------------------------------------------

MOV DX, 1F7h ;status register
LOOP3:
IN AL, DX

AND AL, 00001000xB ;if DRQ is not high, the device doesn'
t have data for us
JE LOOP3
;yet, so keep looking until it does!

;----------------------------------------------------------------------------

MOV DX
, 1F0h ;data register
MOV DI
, OFFSET buff ;points DI to the buffer we're using
MOV CX, 256 ;256 decimal. This controls the REP command.
CLD ;clear the direction flag so INSW increments DI (not decrements it)
REP INSW

;----------------------------------------------------------------------------

;We now have the string data in buff, so let'
s re-enable interrupts.

STI

;----------------------------------------------------------------------------

;...And now we can display the contents of buff!

MOV ES
, SEG buff
MOV BX
, OFFSET buff
MOV CX
, 256 ;256 decimal
MOV AH
, 2 ;"display output" option for INT 21
LOOP4
:
MOV DL
, [BX] ;moves the contents of the byte from "buff" into DL
INT
21h
INC BX
LOOPNZ LOOP4
;does this 256 times, because CX was set to 256

mov ah
,004C  ;terminate program
int 21h

buff db
256 DUP(?) ;buffer to hold the drive identification info

ATAPI
(ATA Packet Interface) is an extension to ATA which
essentially allows SCSI commands
(commands used to control SCSI devices)
to be sent to ATA devices
. ATAPI is used specifically for CD-ROM drives,
 which
, when they first started appearing for computers, were almost
universally SCSI
. Because SCSI controllers were expensive and clunky,
the SCSI command
set was eventually adopted for IDE, and typical CD-ROM
drives today
use ATAPI. ATAPI basically uses "packets" (similar to the
packet
concept of computer networking as it applies to TCP/IP, for example)
to send
and receive data and commands. Properly speaking, ATAPI is part
of the EIDE
(Enhanced IDE) standard.

A packet sent to an ATAPI device which contains a command
is called a
command packet
. These command packets are written to the data register
via the ATA
interface, and that's how ATAPI devices receive their commands.
The command packet has a 12-byte standard format, and the first byte of
the command packet contains the actual operation code. (The remaining 11
bytes supply parameter info for the command.) Note that although the
command packet is 12 bytes long, the packet is sent to the ATAPI device
through word writes, not byte writes. A "word" in PC assembly language
is 2 bytes, so you'
ll actually send the 12-byte command packet in only
6 write operations.

The "operation code" value you place in the ATAPI command packet is
actually a SCSI command code
. You do not use ATA commands with ATAPI
devices
; ATAPI devices use SCSI commands. For example, the SCSI
command to eject a CD
-ROM drive tray is the "START/STOP UNIT"
command
, which is SCSI command 1Bh. Similarly, to get an ATAPI
CD
-ROM drive to eject, you'd send it a command packet with 1Bh
for an operation code.

ATAPI contains several commands, but the most fundamental of these
is the PACKET command, which has an ATAPI opcode of A0h. The first
step in sending a command to an ATAPI device is to send it the
PACKET command over the regular ATA command register, just as the
program above sends the IDENTIFY DEVICE command. Once this PACKET
command is sent, the ATAPI interface goes into a condition called
HPD0: Check_Status_A State, which means that the ATA controller is
to wait for 400 nanoseconds, then poll the status register until
the BSY bit is zero. If the BSY bit is one, the host is supposed
to keep polling the status register until the BSY bit clears.

Once the BSY bit does clear (and DRQ is set to one), the ATAPI
interface changes to HPD1: Send_Packet State. In this state, the
host is supposed to send the command packet to the ATA controller'
s
data
register, one byte at a time. When all the bytes of the command
packet have been sent
, the host is to either transition to HPD2:
Check_Status_B State (if nIEN is set to one), or to HPD3: INTRQ_
Wait State (if nIEN is set to zero). Note that nIEN is the
second
-rightmost bit of the ATA Device Control Register.
You will note that this register is write-only, so you should
write to
this register to set nIEN before you begin sending the
ATAPI packet
.

If the host does transition to HPD3: INTRQ_Wait State, all it's
supposed to do is wait for INTRQ to be asserted. When INTRQ is
asserted, then the host shall transition to HPD2: Check_Status_B State.

The HPD2: Check_Status_B State is where things get a little hairy.
This is where you check the status register, but there are a lot of
condition bits you'
re supposed to check. First of all, the ATAPI spec
specifies that
"When entering this state from the HP1 ... state,
the host shall wait one PIO transfer cycle time before reading the
Status register. The wait may be accomplished by reading the
Alternate Status register and ignoring the result."


Once that's done, start checking the status register.
First of all, if BUSY is set to 1, the host is not to
leave the HPD2 state. The host is supposed to remain in HPD2
until BUSY clears to zero.

Once BUSY is zero, check DRQ. If DRQ is set to one, then the host
shall transition to yet another state, called the HPD4: Transfer_Data State.
However, the only time you'
d need to enter HPD4 is if DRQ is 1 during the
HPD2
State. If DRQ is zero now, you can skip HPD4 altogether.

If both BUSY and DRQ are zero, then, the command is probably complete.
Technically, there are a few other things you're supposed to check, but
we won'
t worry about those now.

The following is an ugly program in assembler to eject a CD-ROM drive.
I haven
't cleaned up the code as nicely as I should, but it does work
as long as you run it in real mode (what Win9x users would call "DOS mode").

MOV DX, 177h ;status register
LOOP1:
IN AL, DX ;sets AL to status register (which is 8 bits)

;If the first bit of the status register (BUSY) isn'
t 0, the device is busy,
;so keep looping until it isn't.

AND AL, 10000000xB
JNE LOOP1

;----------------------------------------------------------------------------

;Clear interrupts so something doesn'
t interrupt the drive or controller
;while this program is working.
CLI

;----------------------------------------------------------------------------

MOV DX
, 177h ;status register again
LOOP2
:
IN AL
, DX ;sets AL to status register again

;If the second bit of the status register (DRDY) isn't 1, the device isn't
;ready, so keep looping until it is.

AND AL
, 01000000xB
JE LOOP2

;----------------------------------------------------------------------------

MOV DX
, 176h ;device/head register
MOV AL
, 0 ;0 selects device 0 (master). 10h would select device 1 (slave).
OUT DX
, AL ;selects master device

;IMPORTANT: Set nIEN before you send the PACKET command!
;Let's set nIEN to 1 so we can skip the INTRQ_Wait state.

MOV DX, 3F6h ;Device Control register
MOV AL, 00001010xB ;nIEN is the second bit from the right here
OUT DX, AL ;nIEN is now one!

MOV DX, 177h ;command register
MOV AL, 0A0h ;PACKET command
OUT DX, AL ;sends the command!

;After sending the PACKET command, the host is to wait 400 nanoseconds before
;doing anything else.
MOV CX,0FFFFh
WAITLOOP:
LOOPNZ WAITLOOP

;----------------------------------------------------------------------------

MOV DX, 177h ;status register again
LOOP3:
IN AL, DX ;sets AL to status register again

;Poll until BUSY bit is clear.

AND AL, 10000000xB
JNE LOOP3

;Also, poll until DRQ is one.
MOV DX, 177h ;status register again
LOOP4:
IN AL, DX
AND AL, 00001000xB
JE LOOP4

;----------------------------------------------------------------------------
;NOW WE START SENDING THE COMMAND PACKET!!!

MOV CX, 6 ;do this 6 times because it'
s 6 word writes (a word is 2 bytes)
MOV DS
, SEG buff
MOV SI
, OFFSET buff
;DS:SI now points to the buffer which contains our ATAPI command packet
CLD
;clear direction flag so SI gets incremented, not decremented

COMPACKLOOP
: ;command packet sending loop
MOV DX
, 170h ;data register

;Because we're going to need to write a word (2 bytes), we can't just use an
;8-bit register like AL. For this operation, we'll need to use the full width
;of the 16-bit accumulator AX. We'
ll use the LODSW opcode, which loads AX
;with whatever DS:SI points to. Not only this, but if the direction flag is
;cleared (which we did a few lines above with the CLD instruction), LODSW
;also auto-increments SI.
LODSW
OUT DX
, AX ;send the current word of the command packet!!!

MOV DX
, 3F6h ;Alternate Status Register
IN AL
, DX ;wait one I/O cycle

LOOPNZ COMPACKLOOP

;----------------------------------------------------------------------------

;Once again, let's read the Alternate Status Register and ignore the result,
;since the spec says so.

MOV DX, 3F6h
IN AL, DX

;Okay... That'
s done.
;Time to poll the status register until BUSY is 0 again.

MOV DX
, 177h ;status register
LOOP5
:
IN AL
, DX

AND AL
, 10000000xB
JNE LOOP5

;BUSY is zero here.
;We're also supposed to check DRQ, but hey, screw it.

STI

;----------------------------------------------------------------------------

mov ah,004C  ;terminate program
int 21h

buff db 1Bh, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0
阅读(2322) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~