The following uses PIO and should work for 16 or 32 bit:
Defs
ATA_REG_DATA equ 0x0000 ; RW Data
ATA_REG_COMMAND equ 0x0007 ; W Command
ATA_REG_STATUS equ 0x0007 ; R Status
ATA_COMMAND_IDENTIFY_DEVICE equ 0xEC
ATA_STATUS_BUSY equ 0x80
struct ATAPI_IDENTIFY
Config dw ?
Cylinders dw ? ; OBSOLETE
Reserved0 dw ?
Heads dw ? ; OBSOLETE
BytesPerTrack dw ?
BytesPerSector dw ?
SectorsPerTrack dw ? ; OBSOLETE
Vendor0 dw ?
Vendor1 dw ?
Vendor2 dw ?
Serial0 dw ?
Serial1 dw ?
Serial2 dw ?
Serial3 dw ?
Serial4 dw ?
Serial5 dw ?
Serial6 dw ?
Serial7 dw ?
Serial8 dw ?
Serial9 dw ?
BufferType dw ?
BufferSize dw ?
ECCBytes dw ?
Firmware0 dw ?
Firmware1 dw ?
Firmware2 dw ?
Firmware3 dw ?
ModelName0 dw ?
ModelName1 dw ?
ModelName2 dw ?
ModelName3 dw ?
ModelName4 dw ?
ModelName5 dw ?
ModelName6 dw ?
ModelName7 dw ?
ModelName8 dw ?
ModelName9 dw ?
MulSecPerInt dw ?
DWIO dw ?
LBADMA dw ?
Reserved1 dw ?
PIOMode dw ?
DMAMode dw ?
Reserved2 dw ?
ApCylinders dw ?
ApHeads dw ?
ApSectorsPerTrack dw ?
Capacity0 dw ?
Capacity1 dw ?
NumSecPerInt dw ?
LBASectors0 dw ?
LBASectors1 dw ?
SinDMAModes dw ?
MulDMAModes dw ? ;54
Reserved3 rw 64
Vendor3 rw 32
Reserved4 rw 96
Reserved5 rw 10
ends
Code
;mov bp,xxx ; The legacy ATA base is 0x0170 or 0x01F0 but this can also be acquired from PCI information (BAR registers)
; Write command
mov al,ATA_COMMAND_IDENTIFY_DEVICE
lea dx,[bp+ATA_REG_COMMAND]
out dx,al
; Wait idle
@@: ; wait
in al,dx ; dx is also ATA_REG_STATUS
test al,ATA_STATUS_BUSY
jnz @b ; wait
test al,ATA_STATUS_ERROR
jnz @f ; error
; Copy data
mov cx,(sizeof.ATAPI_IDENTIFY / 2) ; 2 bytes per read
lea dx,[bp+ATA_REG_DATA]
mov di,buffer
cld
rep insw ; 2 bytes per read
; You can now read buffer.BytesPerSector
;end
@@: ; error
;end
ATAPI_IDENTIFY buffer
Hope that helps