ATA read/write sectors: Difference between revisions
[unchecked revision] | [unchecked revision] |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
|||
(8 intermediate revisions by 5 users not shown) | |||
Line 4: | Line 4: | ||
==Read in CHS mode== |
==Read in CHS mode== |
||
Accessing disk using CHS (cylinder,head,sector) indexes seem to be kinda old type but is the base for LBA access. The following NASM subroutine reads CH sectors to memory address indicated by RDI register. |
Accessing disk using CHS (cylinder,head,sector) indexes seem to be kinda old type but is the base for LBA access. The following NASM long-mode subroutine reads CH sectors to memory address indicated by RDI register. |
||
< |
<syntaxhighlight lang="asm"> |
||
;============================================================================= |
;============================================================================= |
||
; ATA read sectors (CHS mode) |
; ATA read sectors (CHS mode) |
||
Line 77: | Line 77: | ||
popfq |
popfq |
||
ret |
ret |
||
</syntaxhighlight> |
|||
</source> |
|||
==Read in LBA mode== |
==Read in LBA mode== |
||
{{stub}} |
|||
<syntaxhighlight lang="asm"> |
|||
;============================================================================= |
|||
; ATA read sectors (LBA mode) |
|||
; |
|||
; @param EAX Logical Block Address of sector |
|||
; @param CL Number of sectors to read |
|||
; @param RDI The address of buffer to put data obtained from disk |
|||
; |
|||
; @return None |
|||
;============================================================================= |
|||
ata_lba_read: |
|||
pushfq |
|||
and rax, 0x0FFFFFFF |
|||
push rax |
|||
push rbx |
|||
push rcx |
|||
push rdx |
|||
push rdi |
|||
mov rbx, rax ; Save LBA in RBX |
|||
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA |
|||
shr eax, 24 ; Get bit 24 - 27 in al |
|||
or al, 11100000b ; Set bit 6 in al for LBA mode |
|||
out dx, al |
|||
mov edx, 0x01F2 ; Port to send number of sectors |
|||
mov al, cl ; Get number of sectors from CL |
|||
out dx, al |
|||
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
out dx, al |
|||
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
shr eax, 8 ; Get bit 8 - 15 in AL |
|||
out dx, al |
|||
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
shr eax, 16 ; Get bit 16 - 23 in AL |
|||
out dx, al |
|||
mov edx, 0x1F7 ; Command port |
|||
mov al, 0x20 ; Read with retry. |
|||
out dx, al |
|||
.still_going: in al, dx |
|||
test al, 8 ; the sector buffer requires servicing. |
|||
jz .still_going ; until the sector buffer is ready. |
|||
mov rax, 256 ; to read 256 words = 1 sector |
|||
xor bx, bx |
|||
mov bl, cl ; read CL sectors |
|||
mul bx |
|||
mov rcx, rax ; RCX is counter for INSW |
|||
mov rdx, 0x1F0 ; Data port, in and out |
|||
rep insw ; in to [RDI] |
|||
pop rdi |
|||
pop rdx |
|||
pop rcx |
|||
pop rbx |
|||
pop rax |
|||
popfq |
|||
ret |
|||
</syntaxhighlight> |
|||
=ATA write sectors= |
=ATA write sectors= |
||
A write is mostly equivalent to performing a read operation. The only changes needed are a change in command (0x30 for chs write), and the direction of the data, which is written (rep outsw from *SI) to the data port rather than read (rep insw to *DI). |
|||
==Write in CHS mode== |
|||
As noted by Dex (aka 'qark') in this [http://forum.osdev.org/viewtopic.php?f=1&t=12268 forum topic], the only difference to ata_chs_read is 30h is sent to command register instead of 20h, and using OUTSW instead of INSW, and of course from [RSI] instead of to [RDI]. So code for ata_chs_write can be obtained this way. |
|||
<syntaxhighlight lang="asm"> |
|||
==Write in LBA mode== |
|||
;============================================================================= |
|||
Similar to 'read in LBA mode'. |
|||
; ATA write sectors (LBA mode) |
|||
; |
|||
; @param EAX Logical Block Address of sector |
|||
; @param CL Number of sectors to write |
|||
; @param RDI The address of data to write to the disk |
|||
; |
|||
; @return None |
|||
;============================================================================= |
|||
ata_lba_write: |
|||
pushfq |
|||
and rax, 0x0FFFFFFF |
|||
push rax |
|||
push rbx |
|||
push rcx |
|||
push rdx |
|||
push rdi |
|||
mov rbx, rax ; Save LBA in RBX |
|||
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA |
|||
shr eax, 24 ; Get bit 24 - 27 in al |
|||
or al, 11100000b ; Set bit 6 in al for LBA mode |
|||
out dx, al |
|||
mov edx, 0x01F2 ; Port to send number of sectors |
|||
mov al, cl ; Get number of sectors from CL |
|||
out dx, al |
|||
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
out dx, al |
|||
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
shr eax, 8 ; Get bit 8 - 15 in AL |
|||
out dx, al |
|||
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA |
|||
mov eax, ebx ; Get LBA from EBX |
|||
shr eax, 16 ; Get bit 16 - 23 in AL |
|||
out dx, al |
|||
mov edx, 0x1F7 ; Command port |
|||
mov al, 0x30 ; Write with retry. |
|||
out dx, al |
|||
.still_going: in al, dx |
|||
test al, 8 ; the sector buffer requires servicing. |
|||
jz .still_going ; until the sector buffer is ready. |
|||
mov rax, 256 ; to read 256 words = 1 sector |
|||
xor bx, bx |
|||
mov bl, cl ; write CL sectors |
|||
mul bx |
|||
mov rcx, rax ; RCX is counter for OUTSW |
|||
mov rdx, 0x1F0 ; Data port, in and out |
|||
mov rsi, rdi |
|||
rep outsw ; out |
|||
pop rdi |
|||
pop rdx |
|||
pop rcx |
|||
pop rbx |
|||
pop rax |
|||
popfq |
|||
ret |
|||
</syntaxhighlight> |
|||
=See also= |
=See also= |
||
* [http://forum.osdev.org/viewtopic.php?t=12268 Read/write disk sectors by Dex] |
* [http://forum.osdev.org/viewtopic.php?t=12268 Read/write disk sectors by Dex] |
||
* [ |
* [[ATA_PIO_Mode|ATA PIO mode]] |
||
* [[ATA]] |
|||
* [http://wiki.osdev.org/ATA ATA/IDE] |
|||
[[Category:ATA]] |
Latest revision as of 04:22, 9 June 2024
Since interrupts (like INT 13h) can't be called easily in protected mode or long mode, direct disk access through ports might be the only solution. The below code is example of subroutines to read and write disk sectors directly from the first hard disk (80h) in long mode using CHS and LBA.
ATA read sectors
Read in CHS mode
Accessing disk using CHS (cylinder,head,sector) indexes seem to be kinda old type but is the base for LBA access. The following NASM long-mode subroutine reads CH sectors to memory address indicated by RDI register.
;=============================================================================
; ATA read sectors (CHS mode)
; Max head index is 15, giving 16 possible heads
; Max cylinder index can be a very large number (up to 65535)
; Sector is usually always 1-63, sector 0 reserved, max 255 sectors/track
; If using 63 sectors/track, max disk size = 31.5GB
; If using 255 sectors/track, max disk size = 127.5GB
; See OSDev forum links in bottom of [http://wiki.osdev.org/ATA]
;
; @param EBX The CHS values; 2 bytes, 1 byte (BH), 1 byte (BL) accordingly
; @param CH The number of sectors to read
; @param RDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
ata_chs_read: pushfq
push rax
push rbx
push rcx
push rdx
push rdi
mov rdx,1f6h ;port to send drive & head numbers
mov al,bh ;head index in BH
and al,00001111b ;head is only 4 bits long
or al,10100000b ;default 1010b in high nibble
out dx,al
mov rdx,1f2h ;Sector count port
mov al,ch ;Read CH sectors
out dx,al
mov rdx,1f3h ;Sector number port
mov al,bl ;BL is sector index
out dx,al
mov rdx,1f4h ;Cylinder low port
mov eax,ebx ;byte 2 in ebx, just above BH
mov cl,16
shr eax,cl ;shift down to AL
out dx,al
mov rdx,1f5h ;Cylinder high port
mov eax,ebx ;byte 3 in ebx, just above byte 2
mov cl,24
shr eax,cl ;shift down to AL
out dx,al
mov rdx,1f7h ;Command port
mov al,20h ;Read with retry.
out dx,al
.still_going: in al,dx
test al,8 ;the sector buffer requires servicing.
jz .still_going ;until the sector buffer is ready.
mov rax,512/2 ;to read 256 words = 1 sector
xor bx,bx
mov bl,ch ;read CH sectors
mul bx
mov rcx,rax ;RCX is counter for INSW
mov rdx,1f0h ;Data port, in and out
rep insw ;in to [RDI]
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret
Read in LBA mode
;=============================================================================
; ATA read sectors (LBA mode)
;
; @param EAX Logical Block Address of sector
; @param CL Number of sectors to read
; @param RDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
ata_lba_read:
pushfq
and rax, 0x0FFFFFFF
push rax
push rbx
push rcx
push rdx
push rdi
mov rbx, rax ; Save LBA in RBX
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
shr eax, 24 ; Get bit 24 - 27 in al
or al, 11100000b ; Set bit 6 in al for LBA mode
out dx, al
mov edx, 0x01F2 ; Port to send number of sectors
mov al, cl ; Get number of sectors from CL
out dx, al
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
mov eax, ebx ; Get LBA from EBX
out dx, al
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 8 ; Get bit 8 - 15 in AL
out dx, al
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 16 ; Get bit 16 - 23 in AL
out dx, al
mov edx, 0x1F7 ; Command port
mov al, 0x20 ; Read with retry.
out dx, al
.still_going: in al, dx
test al, 8 ; the sector buffer requires servicing.
jz .still_going ; until the sector buffer is ready.
mov rax, 256 ; to read 256 words = 1 sector
xor bx, bx
mov bl, cl ; read CL sectors
mul bx
mov rcx, rax ; RCX is counter for INSW
mov rdx, 0x1F0 ; Data port, in and out
rep insw ; in to [RDI]
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret
ATA write sectors
A write is mostly equivalent to performing a read operation. The only changes needed are a change in command (0x30 for chs write), and the direction of the data, which is written (rep outsw from *SI) to the data port rather than read (rep insw to *DI).
;=============================================================================
; ATA write sectors (LBA mode)
;
; @param EAX Logical Block Address of sector
; @param CL Number of sectors to write
; @param RDI The address of data to write to the disk
;
; @return None
;=============================================================================
ata_lba_write:
pushfq
and rax, 0x0FFFFFFF
push rax
push rbx
push rcx
push rdx
push rdi
mov rbx, rax ; Save LBA in RBX
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
shr eax, 24 ; Get bit 24 - 27 in al
or al, 11100000b ; Set bit 6 in al for LBA mode
out dx, al
mov edx, 0x01F2 ; Port to send number of sectors
mov al, cl ; Get number of sectors from CL
out dx, al
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
mov eax, ebx ; Get LBA from EBX
out dx, al
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 8 ; Get bit 8 - 15 in AL
out dx, al
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 16 ; Get bit 16 - 23 in AL
out dx, al
mov edx, 0x1F7 ; Command port
mov al, 0x30 ; Write with retry.
out dx, al
.still_going: in al, dx
test al, 8 ; the sector buffer requires servicing.
jz .still_going ; until the sector buffer is ready.
mov rax, 256 ; to read 256 words = 1 sector
xor bx, bx
mov bl, cl ; write CL sectors
mul bx
mov rcx, rax ; RCX is counter for OUTSW
mov rdx, 0x1F0 ; Data port, in and out
mov rsi, rdi
rep outsw ; out
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret