Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;********************************************
- ; ATA Hard Disk Routines
- ;
- ; Can read/write ATA/PATA disks on Primary or secondary bus
- ;********************************************
- %ifndef __ATA__
- %define __ATA__
- %include "pit.inc"
- ; register-offsets (Primary bus: 0x1F0- ; for secondary: 0x170- )
- %define ATA_PORT_DATA 0
- %define ATA_PORT_ERROR 1
- %define ATA_PORT_SECT_COUNT 2
- %define ATA_PORT_SECT_NUM 3
- %define ATA_PORT_CYL_LOW 4
- %define ATA_PORT_CYL_HIGH 5
- %define ATA_PORT_DRV_HEAD 6
- %define ATA_PORT_STATUS 7
- %define ATA_PORT_COMMAND 7
- ata_port_ctrl dw 0 ; 0x3F6 ; Device Control Register (secondary: 0x376)
- ata_port_base dw 0 ; can be 0x1F0 (primary bus) or 0x170 (secondary)
- ; status-reg's bits
- %define ATA_ST_ERR 0x01
- %define ATA_ST_IDX 0x02
- %define ATA_ST_DDRC 0x04
- %define ATA_ST_DRQ 0x08
- %define ATA_ST_SRV 0x10
- %define ATA_ST_DF 0x20
- %define ATA_ST_RDY 0x40
- %define ATA_ST_BSY 0x80
- ; drive-head-reg's bits (also used for selecting HD)
- ; bits 3-0: head number
- ; bit4: HD0(=0), HD1(=1)
- ; bits 7-5: 101b for master (0xA0) or 1011b (0xB0) for slave. ORed (0xE0 and 0xF0 for LBA28; 0x40 and 0x50 for LBA48)
- ; Device Control Register (0x3F6)
- ; bit 1: nIEN Disable IRQ sending
- ; bit 2: SRST Software Reset if 1 on all ATA drives on a bus
- ; bit 7: HOB Set this to read back the high-order-byte of the last LBA48 value sent to an I/O port.
- ; all other bits are reserved and should always be clear.
- %define ATA_DCR_NIEN 0x02
- %define ATA_DCR_SRST 0x04
- %define ATA_DCR_HOB 0x80
- ; HD ids
- %define ATA_CHS_MASTER 0xA0 ;(0xB0 is slave; i.e. 0xA0 | slave_bit)
- %define ATA_LBA28_MASTER 0xE0 ;(0xF0 is slave)
- %define ATA_LBA48_MASTER 0x40 ;(0x50 is slave)
- ata_slave_bit db 0 ; change this to 1 if not the master but the slave drive need to be used on the bus
- ; HD commands
- %define ATA_CMD_NOP 0x0
- %define ATA_CMD_READ 0x20
- %define ATA_CMD_READ_EXT 0x24 ; LBA48
- %define ATA_CMD_WRITE 0x30
- %define ATA_CMD_WRITE_EXT 0x34 ; LBA48
- %define ATA_CMD_READ_VRFY 0x40 ; sectors
- %define ATA_CMD_DIAG 0x90 ; execute device diagnostics
- %define ATA_CMD_STANDBY_IMM 0xE0
- %define ATA_CMD_IDLE_IMM 0xE1
- %define ATA_CMD_STANDBY 0xE2
- %define ATA_CMD_IDLE 0xE3
- %define ATA_CMD_CHK_PWR_MODE 0xE5
- %define ATA_CMD_SLEEP 0xE6
- %define ATA_CMD_FLUSH_CACHE 0xE7
- %define ATA_CMD_FLUSH_CACHE_EXT 0xEA
- %define ATA_CMD_IDENTIFY 0xEC
- ; there are many DMA-related cmds in the spec too!
- ata_id_arr dd 0x92000 ; Pointer to 512byte memory for IDENTIFY (just temporary memory)
- ; cmd results
- %define ATA_OK 0
- %define ATA_NO_DRIVE 1
- %define ATA_ERR 2
- %define ATA_TIMEOUT 3
- ; IDENTIFY data
- ; SerialNum word10-19 (20bytes). Words are big endian ("abcdefg" is stored as "badcfe g") (chars)
- ata_iden_sn times 20 db 0
- SerialNumtxt db "Serial number: ", 0
- ; Firmware version word23-26 (chars)
- ata_iden_fv times 8 db 0
- FirmwareVertxt db "Firmware version: ", 0
- ; ModelNumber word 27-46 (40bytes). (chars)
- ata_iden_mn times 40 db 0
- ModelNumtxt db "Model number: ", 0
- Supportedtxt db "Supported: ", 0
- ; ATA? word0, bit15 0: ATA
- ata_iden_ata db 0 ; 1 is ATA ; do we need this?
- ATAtxt db "ATA device", 0
- ; LBA? word49 bit9 (1 if LBA supported)
- ata_iden_lba db 0
- LBAtxt db "LBA ", 0
- ; LBA48 word83 bit10
- ata_iden_lba48 db 0
- LBA48txt db "LBA48 ", 0
- ; LBA28 word60-61 max 28-bit-LBA num (if nonzero: supports LBA28)
- ata_iden_maxlba28 dd 0
- MaxLBA28txt db "MaxLBA28: ", 0
- ; LBA48 word100-103 max 48-bit-LBA num
- ata_iden_maxlba48 times 8 db 0
- ata_iden_maxlba48tmp times 8 db 0
- MaxLBA48txt db "MaxLBA48: ", 0
- ; DMA? word49 bit8 (1 if DMA supported)
- ata_iden_dma db 0
- DMAtxt db "DMA ", 0
- ; MDMA word63 (supported MultiDMA modes) bit2-0
- ata_iden_mdma db 0
- ; UDMA word88 (supported UDMA modes) bit6-0
- ata_iden_udma db 0
- ; MaxLogicalSectorNumPerRead/WriteMultipleCmds word47 bit7-0
- ata_iden_maxlsn db 0
- MaxNumSectPerMultiCmdtxt db "Max number of logical sectors per r/w multiple cmds: ", 0
- ; Current number of logicals sectors/DRQblock on R/W Multiple cmds. word59 bits7-0
- ata_iden_currlsn db 0
- ; PhysicalSectorSize/LogicalSectorSize word106 see bits
- ; if bit14=1 and bit15=0 then info is valid in this word
- ; if bit13 is set then info in bits3-0 is valid (number of logsects/physsects)
- ; if bit12 is set, then this device has been formatted with logical sector size larger than 256 (see words117-118)
- ata_iden_logperphys db 0
- LogSectsPerPhysSecttxt db "Logical sectors per physical sector: ", 0
- ; LogicalSectorSize word117-118 (value is valid if bit12 of word106 is set) >256
- ata_iden_logsectsize dd 0
- LogicalSectorSizetxt db "Logical sectorsize: ", 0
- Capacitytxt db "Capacity: ", 0
- CapacityUnittxt db " Gb", 0
- ata_capacity dd 0 ; in Gb
- ata_identify_res db 0 ; result of IDENTIFY
- PrimaryBustxt db "Primary bus, ", 0
- SecondaryBustxt db "Secondary bus, ", 0
- Mastertxt db "Master", 0
- Slavetxt db "Slave", 0
- ata_mem_addr dd 0 ; for R/W
- ata_lba48 times 8 db 0 ; for R/W LBA48
- ; identifies HD
- ; OUT: AL
- ata_identify:
- xor eax, eax
- ;send 0xA0 to 0x1F6 (This is CHS-mode. LBA28 and 48 ?)
- mov al, ATA_CHS_MASTER
- mov bl, [ata_slave_bit]
- shl bl, 4
- or al, bl
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DRV_HEAD
- out dx, al
- ;set 0 to 0x1F2 to 0x1F5
- mov al, 0
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_COUNT
- out dx, al
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_NUM
- out dx, al
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_LOW
- out dx, al
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_HIGH
- out dx, al
- ;send 0xEC to 0x1F7
- mov al, ATA_CMD_IDENTIFY
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- ;read 0x1F7 (Status) if it is 0 then the drive doesn't exist
- in al, dx
- cmp al, ATA_NO_DRIVE
- jz .Back
- ;If not 0:
- ; poll 0x1F7 (STATUS) until bit7 clears (0x80) and bit3 sets: OK
- ; or
- ; bit0 sets or bit5 sets: ERR
- mov DWORD [pit_ticks], 0
- .Poll mov dx, [ata_port_base] ; Timeout should be added
- add dx, ATA_PORT_STATUS
- in al, dx
- mov bl, al
- and al, ATA_ST_BSY+ATA_ST_DRQ
- cmp al, ATA_ST_DRQ
- jz .Read
- and bl, ATA_ST_ERR+ATA_ST_DF
- cmp bl, 0
- jz .ChkTimOut
- mov al, ATA_ERR
- jmp .Back
- .ChkTimOut cmp DWORD [pit_ticks], 2000 ; 2sec timeout
- jna .Poll
- mov al, ATA_TIMEOUT
- jmp .Back
- ; Read 256 words from ATA_PORT_DATA
- .Read push edi
- mov edi, DWORD [ata_id_arr]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DATA
- mov ecx, 256
- rep insw
- push esi
- ; copy data
- mov ecx, 10
- mov esi, DWORD [ata_id_arr]
- add esi, 2*10
- mov edi, ata_iden_sn
- rep movsw
- mov ecx, 4
- mov esi, DWORD [ata_id_arr]
- add esi, 2*23
- mov edi, ata_iden_fv
- rep movsw
- mov ecx, 20
- mov esi, DWORD [ata_id_arr]
- add esi, 2*27
- mov edi, ata_iden_mn
- rep movsw
- mov esi, DWORD [ata_id_arr]
- test WORD [esi], 0x8000
- jz .SetATA
- mov BYTE [ata_iden_ata], 0
- jmp .ChkLBA
- .SetATA mov BYTE [ata_iden_ata], 1
- .ChkLBA mov esi, DWORD [ata_id_arr]
- add esi, 2*49
- test WORD [esi], 0x0200
- jnz .SetLBA
- mov BYTE [ata_iden_lba], 0
- ; here should cmd 0x91 executes and then read words54-56 for CHS
- ; also should calculate MAXLBA here from CHS
- jmp .ChkDMA
- .SetLBA mov BYTE [ata_iden_lba], 1
- .ChkLBA48 mov esi, DWORD [ata_id_arr]
- add esi, 2*83
- test WORD [esi], 0x0400
- jnz .SetLBA48
- mov BYTE [ata_iden_lba48], 0
- jmp .MaxLBA28
- .SetLBA48 mov BYTE [ata_iden_lba48], 1
- .MaxLBA28 mov ecx, 2
- mov esi, DWORD [ata_id_arr]
- add esi, 2*60
- mov edi, ata_iden_maxlba28
- rep movsw
- mov ecx, 4 ; data of LBA48 will be copied even if LBA48 is not set!
- mov esi, DWORD [ata_id_arr]
- add esi, 2*100
- mov edi, ata_iden_maxlba48
- rep movsw
- ; DMA
- .ChkDMA mov esi, DWORD [ata_id_arr]
- add esi, 2*49
- test WORD [esi], 0x0100
- jnz .SetDMA
- mov BYTE [ata_iden_dma], 0
- jmp .SetMDMA
- .SetDMA mov BYTE [ata_iden_dma], 1
- .SetMDMA mov esi, DWORD [ata_id_arr]
- add esi, 2*63
- mov cx, WORD [esi]
- and cx, 0x0007
- mov [ata_iden_mdma], cl
- mov esi, DWORD [ata_id_arr]
- add esi, 2*88
- mov cx, WORD [esi]
- and cx, 0x007F
- mov [ata_iden_udma], cl
- mov esi, DWORD [ata_id_arr]
- add esi, 2*47
- mov cx, WORD [esi]
- and cx, 0x007F
- mov [ata_iden_maxlsn], cl
- mov esi, DWORD [ata_id_arr]
- add esi, 2*59
- mov cx, WORD [esi]
- and cx, 0x007F
- mov [ata_iden_currlsn], cl
- mov esi, DWORD [ata_id_arr]
- add esi, 2*106
- mov cx, WORD [esi]
- mov ax, cx
- and ax, 0xC000
- cmp ax, 0x4000
- jz .Valid
- jmp .Sect512
- .Valid mov esi, DWORD [ata_id_arr]
- add esi, 2*106
- test WORD [esi], 0x2000
- jz .LogSectS
- and cl, 0x0F
- mov [ata_iden_logperphys], cl
- .LogSectS test WORD [esi], 0x1000
- jz .Sect512
- mov esi, DWORD [ata_id_arr]
- add esi, 2*117
- mov ecx, [esi]
- mov DWORD [ata_iden_logsectsize], ecx
- jmp .Capacity
- .Sect512 mov ecx, 512
- mov DWORD [ata_iden_logsectsize], ecx
- ; Capacity computation (Gb)
- .Capacity mov ecx, 21 ; if sector=512 byte then shift 2^11 to get Gb
- cmp DWORD [ata_iden_logsectsize], 1024
- jnz .Chk2048
- mov ecx, 20
- jmp .GetLBA
- .Chk2048 cmp DWORD [ata_iden_logsectsize], 2048
- jnz .Chk4096
- mov ecx, 19
- jmp .GetLBA
- .Chk4096 cmp DWORD [ata_iden_logsectsize], 4096
- jnz .GetLBA
- mov ecx, 18
- .GetLBA cmp BYTE [ata_iden_lba48], 1
- jz .GetLBA48
- mov edx, [ata_iden_maxlba28]
- shr edx, cl
- mov [ata_capacity], edx
- jmp .Ok
- .GetLBA48 push ecx
- mov ecx, 8
- mov esi, ata_iden_maxlba48
- mov edi, ata_iden_maxlba48tmp
- rep movsb
- pop ecx
- mov edx, DWORD [ata_iden_maxlba48tmp]
- shr edx, cl
- mov [ata_capacity], edx
- .Ok mov al, ATA_OK
- pop esi
- pop edi
- .Back mov [ata_identify_res], al
- ret
- ata_info:
- push esi
- ; serial number
- mov ebx, SerialNumtxt
- call stdio_puts
- mov ecx, 10
- mov esi, ata_iden_sn
- .NextSN mov ax, WORD [esi]
- mov bl, ah
- call stdio_put_ch
- mov bl, al
- call stdio_put_ch
- add esi, 2
- loop .NextSN
- call stdio_new_line
- ; firmware version
- mov ebx, FirmwareVertxt
- call stdio_puts
- mov ecx, 4
- mov esi, ata_iden_fv
- .NextFV mov ax, WORD [esi]
- mov bl, ah
- call stdio_put_ch
- mov bl, al
- call stdio_put_ch
- add esi, 2
- loop .NextFV
- call stdio_new_line
- ; model number
- mov ebx, ModelNumtxt
- call stdio_puts
- mov ecx, 20
- mov esi, ata_iden_mn
- .NextMN mov ax, WORD [esi]
- mov bl, ah
- call stdio_put_ch
- mov bl, al
- call stdio_put_ch
- add esi, 2
- loop .NextMN
- call stdio_new_line
- ; ATA?
- cmp BYTE [ata_iden_ata], 1
- jnz .PrLBA
- mov ebx, ATAtxt
- call stdio_puts
- call stdio_new_line
- mov ebx, Supportedtxt
- call stdio_puts
- ; LBA?
- .PrLBA cmp BYTE [ata_iden_lba], 1
- jnz .PrLBA48
- mov ebx, LBAtxt
- call stdio_puts
- ; LBA48?
- .PrLBA48 cmp BYTE [ata_iden_lba48], 1
- jnz .PrDMA
- mov ebx, LBA48txt
- call stdio_puts
- ; DMA?
- .PrDMA cmp BYTE [ata_iden_dma], 1
- jnz .PrMaxLBA28
- mov ebx, DMAtxt
- call stdio_puts
- ; Max sector-number of LBA28
- .PrMaxLBA28 call stdio_new_line
- cmp BYTE [ata_iden_lba], 1
- jnz .PrMaxSctPW
- mov ebx, MaxLBA28txt
- call stdio_puts
- mov eax, [ata_iden_maxlba28]
- call stdio_put_dec
- call stdio_new_line
- ; Max sector-number of LBA48
- cmp BYTE [ata_iden_lba48], 1
- jnz .PrMaxSctPW
- mov ebx, MaxLBA48txt
- call stdio_puts
- mov eax, DWORD [ata_iden_maxlba48] ;;;;;;
- mov edx, DWORD [ata_iden_maxlba48+4]
- call stdio_put_dec64
- call stdio_new_line
- ; Max number of Logical Sectors per Read/Write Multiple Commands
- .PrMaxSctPW mov ebx, MaxNumSectPerMultiCmdtxt
- call stdio_puts
- xor eax, eax
- mov al, BYTE [ata_iden_maxlsn]
- call stdio_put_dec
- call stdio_new_line
- cmp BYTE [ata_iden_logperphys], 0
- jz .PrLogSctSi
- mov ebx, LogSectsPerPhysSecttxt
- call stdio_puts
- xor eax, eax
- mov al, BYTE [ata_iden_logperphys]
- call stdio_put_dec
- call stdio_new_line
- .PrLogSctSi mov ebx, LogicalSectorSizetxt
- call stdio_puts
- mov eax, DWORD [ata_iden_logsectsize]
- call stdio_put_dec
- call stdio_new_line
- ; Capacity
- mov ebx, Capacitytxt
- call stdio_puts
- mov eax, [ata_capacity]
- call stdio_put_dec
- mov ebx, CapacityUnittxt
- call stdio_puts
- call stdio_new_line
- ; Primary or Secondary Bus, Master or Slave
- cmp WORD [ata_port_base], 0x1F0
- jnz .SecondaryB
- mov ebx, PrimaryBustxt
- call stdio_puts
- jmp .MasterSl
- .SecondaryB cmp WORD [ata_port_base], 0x170
- jnz .MasterSl
- mov ebx, SecondaryBustxt
- call stdio_puts
- .MasterSl cmp WORD [ata_slave_bit], 0
- jnz .Slave
- mov ebx, Mastertxt
- call stdio_puts
- call stdio_new_line
- jmp .End
- .Slave mov ebx, Slavetxt
- call stdio_puts
- call stdio_new_line
- .End pop esi
- ret
- ata_reset:
- mov dx, WORD [ata_port_ctrl] ; does a reset on the bus
- mov al, ATA_DCR_SRST
- out dx, al ; do a "software reset" on the bus
- xor eax, eax
- out dx, al ; reset the bus to normal operation
- in al, dx ; it might take 4 tries for status bits to reset
- in al, dx ; ie. do a 400ns delay
- in al, dx
- in al, dx ; do we really need these 4 in-s ?
- mov dx, [ata_port_base]
- add dx, ATA_PORT_STATUS
- .rdylp:
- in al, dx
- and al, ATA_ST_BSY+ATA_ST_RDY ; check BSY and RDY
- cmp al, ATA_ST_RDY ; want BSY clear and RDY set
- jne short .rdylp
- ret
- ; IN:
- ; ECX: lbaLo, EBP: lbaHi, EBX: sectorcnt, EAX: memaddr
- ; OUT: AL (0 indicates success)
- ata_rw:
- cmp BYTE [ata_iden_lba], 1
- jnz .Err ; if not LBA-drive then error
- mov [ata_mem_addr], eax
- cmp BYTE [ata_iden_lba48], 1
- jz .LBA48
- call ata_rw_lba28
- jmp .Back
- .LBA48 mov [ata_lba48], ecx
- mov [ata_lba48+4], ebp
- call ata_rw_lba48
- jmp .Back
- .Err mov al, ATA_ERR
- .Back ret
- ; IN: 28-bit LBA in ECX; sector-count in BL; memory-address in ata_mem_addr; R/W (read=0) in DL
- ; OUT: AL 0 is ok
- ; sector-count of 0 means 256 sectors (128Kb) !?
- ata_rw_lba28:
- ; send 0xE0 for the master or 0xF0 for the slave ORed with the highest 4 bits of the LBA to port DRV_HEAD
- push edx
- push ecx
- push ebx
- mov al, ATA_LBA28_MASTER
- mov bl, [ata_slave_bit]
- shl bl, 4
- or al, bl
- pop ebx
- shr ecx, 24
- and cl, 0x0F
- or al, cl
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DRV_HEAD
- out dx, al
- ; send a NULL byte to port ERROR, if you like (it's ignored)
- mov al, 0
- mov dx, [ata_port_base]
- add dx, ATA_PORT_ERROR
- out dx, al
- ; send sectorcount to port SECT_COUNT
- mov al, bl
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_COUNT
- out dx, al
- ; send the low 8 bits of the LBA to port SECT_NUM
- pop eax ; pop ecx in eax
- push eax
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_NUM
- out dx, al
- ; send the next low 8 bits of the LBA to port CYL_LOW
- pop eax
- push eax
- shr ax, 8
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_LOW
- out dx, al
- ; send the next low 8 bits of the LBA to port CYL_HIGH
- pop eax
- shr eax, 16
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_HIGH
- out dx, al
- ; check if read or write
- pop edx
- cmp dl, 1
- jz .Write
- ; Read
- ; send r/w cmd to port COMMAND
- mov al, ATA_CMD_READ
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- ; poll
- mov DWORD [pit_ticks], 0
- .Poll mov dx, [ata_port_base] ; Timeout should be added
- add dx, ATA_PORT_STATUS
- in al, dx
- mov bl, al
- and al, ATA_ST_BSY+ATA_ST_DRQ
- cmp al, ATA_ST_DRQ
- jz .Read
- and bl, ATA_ST_ERR+ATA_ST_DF
- cmp bl, 0
- jz .ChkTimOut
- xor eax, eax
- mov al, ATA_ERR
- jmp .Back
- .ChkTimOut cmp DWORD [pit_ticks], 2000 ; 2sec timeout
- jna .Poll
- xor eax, eax
- mov al, ATA_TIMEOUT
- jmp .Back
- ; sect_count times
- ; Read SectSize/2 words from PORT_DATA
- .Read mov eax, DWORD [ata_iden_logsectsize]
- shr eax, 1 ; to get word instead of byte (divide by 2)
- mul ebx
- mov ecx, eax ; EDX not important
- push edi
- mov edi, DWORD [ata_mem_addr]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DATA
- rep insw
- pop edi
- xor eax, eax
- mov al, ATA_OK
- jmp .Back
- ; Write
- ; send r/w cmd to port COMMAND
- .Write mov al, ATA_CMD_WRITE
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- ; poll
- mov DWORD [pit_ticks], 0
- .PollWr mov dx, [ata_port_base] ; Timeout should be added
- add dx, ATA_PORT_STATUS
- in al, dx
- mov bl, al
- and al, ATA_ST_BSY+ATA_ST_DRQ
- cmp al, ATA_ST_DRQ
- jz .Write2
- and bl, ATA_ST_ERR+ATA_ST_DF
- cmp bl, 0
- jz .ChkTimOutW
- xor eax, eax
- mov al, ATA_ERR
- jmp .Back
- .ChkTimOutW cmp DWORD [pit_ticks], 2000 ; 2sec timeout
- jna .PollWr
- xor eax, eax
- mov al, ATA_TIMEOUT
- jmp .Back
- ; sect_count times
- ; Write SectSize/2 words to PORT_DATA
- .Write2 mov eax, DWORD [ata_iden_logsectsize]
- shr eax, 1 ; to get word instead of byte (divide by 2)
- mul ebx
- mov ecx, eax ; EDX not important
- push esi
- mov esi, DWORD [ata_mem_addr]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DATA ; Don't use rep outsw, because it's too fast!
- .NextR outsw
- nop ; delay
- nop
- nop
- nop
- loop .NextR
- pop esi
- ; flush (0xE7) after each write (maybe in the loop after every cycle!?)
- mov al, ATA_CMD_FLUSH_CACHE
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- xor eax, eax
- mov al, ATA_OK
- .Back ret
- ; IN: 48-bit LBA in [ata_lba48]; sector-count in BX; memory-address in ata_mem_addr; R/W (read=0) in DL
- ; OUT: AL 0 is ok
- ; sector-count of 0 means 65536 sectors (32Mb) !?
- ; Since FOS is a 32-bit OS, 48-bit LBA cannot be entered in the command line!? (with LO and HI 32-bits on pstack it is possible).
- ata_rw_lba48:
- ; send 0x40 for the master or 0x50 for the slave
- push edx
- push ebx
- mov al, ATA_LBA48_MASTER
- mov bl, [ata_slave_bit]
- shl bl, 4
- or al, bl
- pop ebx
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DRV_HEAD
- out dx, al
- ; send sectorcount high-byte to port SECT_COUNT
- mov al, bh
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_COUNT
- out dx, al
- ; send LBA4 to port SECT_NUM
- mov al, [ata_lba48+3]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_NUM
- out dx, al
- ; send LBA5 to port CYL_LOW
- mov al, [ata_lba48+4]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_LOW
- out dx, al
- ; send LBA6 to port CYL_HI
- mov al, [ata_lba48+5]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_HIGH
- out dx, al
- ; send sectorcount low-byte to port SECT_COUNT
- mov al, bl
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_COUNT
- out dx, al
- ; send LBA1 to port SECT_NUM
- mov al, [ata_lba48]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_SECT_NUM
- out dx, al
- ; send LBA2 to port CYL_LOW
- mov al, [ata_lba48+1]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_LOW
- out dx, al
- ; send LBA3 to port CYL_HI
- mov al, [ata_lba48+2]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_CYL_HIGH
- out dx, al
- ; check if read or write
- pop edx
- cmp dl, 1
- jz .Write
- ; Read
- ; send r/w cmd to port COMMAND
- mov al, ATA_CMD_READ_EXT
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- ; poll
- mov DWORD [pit_ticks], 0
- .Poll mov dx, [ata_port_base] ; Timeout should be added
- add dx, ATA_PORT_STATUS
- in al, dx
- mov bl, al
- and al, ATA_ST_BSY+ATA_ST_DRQ
- cmp al, ATA_ST_DRQ
- jz .Read
- and bl, ATA_ST_ERR+ATA_ST_DF
- cmp bl, 0
- jz .ChkTimOut
- xor eax, eax
- mov al, ATA_ERR
- jmp .Back
- .ChkTimOut cmp DWORD [pit_ticks], 2000 ; 2sec timeout
- jna .Poll
- xor eax, eax
- mov al, ATA_TIMEOUT
- jmp .Back
- ; sect_count times
- ; Read SectSize/2 words from PORT_DATA
- .Read mov eax, DWORD [ata_iden_logsectsize]
- shr eax, 1 ; to get word instead of byte (divide by 2)
- mul ebx
- mov ecx, eax ; EDX not important
- push edi
- mov edi, DWORD [ata_mem_addr]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DATA
- rep insw
- pop edi
- xor eax, eax
- mov al, ATA_OK
- jmp .Back
- ; Write
- ; send r/w cmd to port COMMAND
- .Write mov al, ATA_CMD_WRITE_EXT
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- ; poll
- mov DWORD [pit_ticks], 0
- .PollWr mov dx, [ata_port_base] ; Timeout should be added
- add dx, ATA_PORT_STATUS
- in al, dx
- mov bl, al
- and al, ATA_ST_BSY+ATA_ST_DRQ
- cmp al, ATA_ST_DRQ
- jz .Write2
- and bl, ATA_ST_ERR+ATA_ST_DF
- cmp bl, 0
- jz .ChkTimOutW
- xor eax, eax
- mov al, ATA_ERR
- jmp .Back
- .ChkTimOutW cmp DWORD [pit_ticks], 2000 ; 2sec timeout
- jna .PollWr
- xor eax, eax
- mov al, ATA_TIMEOUT
- jmp .Back
- ; sect_count times
- ; Write SectSize/2 words to PORT_DATA
- .Write2 mov eax, DWORD [ata_iden_logsectsize]
- shr eax, 1 ; to get word instead of byte (divide by 2)
- mul ebx
- mov ecx, eax ; EDX not important
- push esi
- mov esi, DWORD [ata_mem_addr]
- mov dx, [ata_port_base]
- add dx, ATA_PORT_DATA ; Don't use rep outsw, because it's too fast!
- .NextR outsw
- nop ; delay
- nop
- nop
- nop
- loop .NextR
- pop esi
- ; flush (0xE7) after each write (maybe in the loop after every cycle!?)
- mov al, ATA_CMD_FLUSH_CACHE_EXT
- mov dx, [ata_port_base]
- add dx, ATA_PORT_COMMAND
- out dx, al
- xor eax, eax
- mov al, ATA_OK
- .Back ret
- %endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement