Ata.inc
[ Return to Browse Source Page ]
; ATA Hard Disk Driver for PizziOS
; Started 17 Sep 1999
; Name changed hd.inc -> ata.inc 24 Aug 2000
; Ata_cmd:
; [esi] -> hd_dsc
; cl = Sector Count
; al = Sector #
; ah = Cylinder Low
; bl = Cylinder High
; bh = Device/Head
; ch = Command
; ds = os_data
ata_cmd:
push eax
push ecx
call ata_grab_drive
pop ecx
pop eax
;
or dl,011b
out dx,al ; al -> 03
dec dl
mov al,cl
out dx,al ; cl -> 02
or dl,0100b
mov al,bh
out dx,al ; bh -> 06
dec dl
mov al,bl
out dx,al ; bl -> 05
dec dl
shr ax,8
out dx,al ; ah -> 04
or dl,0111b
xor ebx,ebx
mov bl,[esi+1]
xor cl,cl
lea ebx,[ebx + offset hd_i0]
mov al,ch
mov [ebx],cl
out dx,al ; ch -> 07 (CMD)
ata_cmd_wait:
mov cl,[ebx]
test cl,cl
jz ata_cmd_wait ; yield()?
in al,dx ; acknowledge interrupt
test al,1
jz ata_cmd_success
stc
ata_cmd_success:
ret
ata_cmd_full:
push offset ata_release_drive ; call ata_cmd
jmp ata_cmd ; call ata_release_drive
; ret
read_sector:
; eax is device number
; ebx is sector number
; ds:[edi] is memory address
pushad
mov cx,os_data
push ds
mov ds,cx
read_sector0:
mov edx,ds:[hd_units]
cmp eax,edx
jnb read_sector_err
shl eax,5
lea esi,[eax + offset hd_dsc]
mov eax,[esi+12]
mov cl,[esi]
cmp ebx,eax
ja read_sector_err
cmp cl,1
jz ata_read_sector
; ... add ATAPI and other interfaces
; if (no recognized device)...
read_sector_err:
pop ds
popad
stc
ret
hd_getbsy:
push ebx
lea ecx,[esi+3]
mov bl,[ecx]
test bl,0F0h
jz hd_getbsy1
lea ecx,[ecx-32]
hd_getbsy1:
pop ebx
ret
ata_xlate_chs:
mov ebp,[esi+22]
xor edx,edx
lea ebx,[ebp]
shr ebx,16 ; s
and ebp,01Fh ; h
div ebx
lea ebx,[edx] ; mov bl,dl
xor edx,edx
div ebp
lea ebx,[ebx+1]
; eax = c, edx = h, ebx=s
mov ch,al
mov bh,dl ; h
mov al,bl ; s
mov bl,ah ; ch
mov ah,ch ; cl
or bh,cl
ret
ata_read_sector_chs:
call ata_xlate_chs
jmp ata_read_sector_chs_ret
ata_read_sector:
mov cl,[esi + 2]
mov eax,ebx ; mov al,bl / mov ah,bh
shr ebx,16
test cl,01000000b
jz ata_read_sector_chs
or bh,cl
ata_read_sector_chs_ret:
mov ecx,02001h ; cmd = 20h: read w/ retries
call ata_cmd
test al,08h
jz ata_read_error
and dl,0F8h
mov bx,ds
pop ds
mov ebp,(512/4)
ata_read_pio:
IFDEF ATA_32
in eax,dx
dec ebp
mov [edi],eax
lea edi,[edi+4]
jnz ata_read_pio
ELSE
in ax,dx
mov cx,ax
in ax,dx
and ecx,0ffffh
shl eax,16
dec ebp
lea eax,[ecx+eax]
mov [edi],eax
lea edi,[edi+4]
jnz ata_read_pio
ENDIF
mov dx,ds
mov ds,bx
call ata_release_drive
mov ds,dx
popad
; clc ; xor clears CF
ret
ata_read_error:
and dl,0feh
or dl,06h ; -> 6
in al,dx ; read error register, ack error
call ata_release_drive
pop ds
popad
stc
ret
ata_int_3:
push edx
mov dl,3
jmp ata_int_cnvrg
ata_int_2:
push edx
mov dl,2
jmp ata_int_cnvrg
ata_int_1:
push edx
mov dl,1
jmp ata_int_cnvrg
ata_int_0:
push edx
xor dl,dl
ata_int_cnvrg:
sti ; not important, BUT you might not want to switch tasks here?
and edx,03h
push eax
; push ebx
mov ax,os_data
; push ecx
push ds
mov ds,ax
; lea ebx,[edx*4 + offset hd_intprocs]
; xor eax,eax
; mov ecx,[ebx] ; get interrupt proc, if there is any
; mov [ebx],eax ; set interrupt proc to 0 to prevent stray interrupts from
; test ecx,ecx ; wandering into other procedures.
; jnz ecx ; jmp to interrupt proc
; hd_int_ret:
lea edx,[edx + offset hd_i0]
mov al,020h
mov ah,[edx]
out 0a0h,al
inc ah
out 020h,al
mov [edx],ah
pop ds
; pop ecx
; pop ebx
pop eax
pop edx
iretd
;hd_demo_int_proc:
; push edx
; push esi
; mov esi,[edx*4 + offset hd_intparams]
; mov dx,[esi+10]
; ;...
; pop esi
; pop edx
; jmp hd_int_ret
ata_readerr:
or dl,087h
in al,dx ; acknowledge error if there is any by reading from the status
and dl,011111001b
in al,dx ; and error registers.
pop ds
popad
stc
ret
ata_init:
mov esi,offset hd_init_1
call Print
call get_drives
mov ecx,ds:[hd_units]
mov edi,offset hd_dsc+12
mov esi,offset hd_init_2
mov edx,offset hd_init_num
test ecx,ecx
mov ebx,offset mb_string
jz hd_not_detected
hd_disploop:
call print ; Unit X:
mov eax,[edi]
shr eax,11
call print_num
xchg esi,ebx
call print
xchg ebx,esi
lea edi,[edi+32]
inc byte ptr [edx]
dec ecx
jnz hd_disploop
hd_not_detected:
;call do_crlf
mov dx,0A1h
in al,dx
and al,00110011b ; enable IRQ 14, 15, 10, 11
out dx,al
;mov ebx,offset hd_unexpected_int
;push ebx
;push ebx
;push ebx
;mov eax,020h+14
;call patch_int
;pop ebx
;mov eax,020h+15
;call patch_int
;pop ebx
;mov eax,020h+10
;call patch_int
;pop ebx
;mov eax,020h+11
;call patch_int
;
ret
;1F0, 170, 1E8, 168
get_drives:
xor ecx,ecx
xor ebp,ebp
get_drives_loop:
call scan_for_drive ; ecx = num of hd_dsc, ebp = drive select
sbb ecx,0
shl ebp,1
inc ecx
inc ebp
test ebp,08h
jz get_drives_loop
mov ds:[hd_units],ecx
ret
ata_poll: ;dx = "near" the base register. if it's pointing
; anywhere between hd[0] and hd[7] it'll work.
push eax
push edx
or dl,07h ; hd[7]:status
ata_poll_loop:
in al,dx
test al,080h ; BSY
jnz ata_poll_loop ; lock up point?
; xor al,8
; test al,09h ; if (DRQ && !ERR)
; jz hd_poll_success
;;
test al,08h ; DRQ
jnz ata_poll_success
;/;
stc
ata_poll_success:
; Note: If (success) CF = 0 since TEST always sets CF (and OF) to 0.
pop edx
pop eax
ret
scan_for_drive:
;In: Out: !CF = drive found
;ecx = hd_dsc # CF = no drive found
;ebp = Drive # Preserves: ecx, ebp
push ecx
push ebp
mov edi,ebp
shr edi,1
lea edi,[edi*2+offset hd_params]
mov dx,[edi]
or dl,07h
in al,dx
inc al
jz scan_for_drive_fail ; there's no HD controller here
call ata_poll ; wait for !BSY. returns error since no DRQ.
; push eax
; mov al,0b1h
; call do_crlf
; call print_hex
; pop eax
dec dl ; hd[6]: drive/head
mov ax,bp
and al,1
or al,0Ah
shl al,4
out dx,al
inc dl ; hd[7]: status/command
in al,dx
xor al,07Fh
test al,07Fh
jz scan_for_drive_fail ; drive not present.
; push eax
; mov al,0b2h
; call do_crlf
; call print_hex
; pop eax
shl ecx,5
mov al,11101100b
out dx,al
lea esi,[ecx + offset hd_dsc]
mov ecx,100
call common_delay
call ata_poll
jc scan_for_drive_fail
; mov al,0A1h
; call print_hex
and dl,0F8h ; hd[0]: data
xor cl,cl
mov edi,offset hd_buffer
scan_for_drive_io:
dec cl
in ax,dx
mov [edi],ax
lea edi,[edi+2]
jnz scan_for_drive_io
mov edi,offset hd_buffer
mov al,[edi]
xor al,01000000b
test al,11000000b
jnz scan_for_drive_fail ; if (!HD || REMOVABLE) fail
; push eax
; mov al,0A2h
; call print_hex
; pop eax
mov ebx,ebp
and bl,1
shl bl,4
or bl,010100000b ; bl=|1|0|1|DRV|:|0|0|0|0|
mov ax,[edi+49*2]
shr eax,8
mov [esi+2],bl ; during the renaming stalls, store bl
mov bh,al
shl al,2
and bh,2
and al,4
or bh,1 ; assume CHS works
or al,bh
; and al,11111101b ; FOR TESTING ONLY - disables LBA
mov [esi+18],al ; store canuse info
mov ebx,ebp
mov al,1
shr ebx,1
mov [esi],al ; 1 = ATA HD
mov [esi+1],bl ; [0,1,2,3] = IRQ codes (corresponding to irq's [14,15,10,11])
mov eax,edx
and al,0F8h
mov [esi+10],ax ; base register
; push eax
; mov al,0A3h
; call print_hex
; pop eax
mov ebx,[edi+60*2]
dec ebx
mov [esi+12],ebx ; assume LBA for size info for now
; begin chs
xor eax,eax
mov ax,[edi+1*2] ; c
lea ebx,[eax-1]
xor ecx,ecx
mov cx,[edi+6*2] ; s
mov [esi+4],bx
mul ecx
mov dx,[edi+3*2] ; h
mov [esi+8],cx
mov [esi+6],dx
and edx,01Fh
mul edx
lea ebx,[eax-1]
lea eax,[eax-1] ; avoids dependency
and ebx,0F0000000h
or edx,ebx
jnz scan_for_drive_nochs ; CHS overflow
; if data in word 49 isn't trustable, comment this out
mov cl,[esi+18]
test cl,010b
jz scan_for_drive_nolba
mov ebx,[esi+12]
call scan_for_drive_checklba
rol ebx,16 ; check for reversed word orders
call scan_for_drive_checklba
rol ebx,16
jmp scan_for_drive_nolba
; Note:
; this part is only necessary if bits 8 and 9 in word 49 are not to be
; trusted.
; mov ecx,[esi+6] ; huge drives use fake values for CHS (source: Linux)
; mov dx,[esi+4]
; or cl,01h ; there are two valid values for the 1 bit in the heads field
; cmp dx,16382 ; 16383 cyllendars
; jnz scan_for_drive_nolba
; cmp ecx,(62*65536 + 15) ; 63 sectors, 15 or 16 heads
; jnz scan_for_drive_nolba
; jmp scan_for_drive_nochs
scan_for_drive_uselba0:
mov [esi+12],ebx
scan_for_drive_uselba:
mov al,[esi+2]
or al,01000000b
mov [esi+2],al
jmp scan_for_drive_success
scan_for_drive_nolba:
mov bl,[esi+18]
mov [esi+12],eax
test bl,1
jz scan_for_drive_fail
;jmp scan_for_drive_usechs
scan_for_drive_usechs:
mov al,[esi+2]
and al,10110000b ; clear lba
mov [esi+2],al
mov bh,al
mov edx,[esi+4]
mov cl,[esi+8]
mov al,[esi+6]
mov [esi+20],edx
dec al
mov dx,[esi+8]
and al,00001111b
mov [esi+24],dx
or bh,al
mov ch,091h
xor edx,edx ; int proc = 0 (no proc)
call ata_cmd_full
; jc scan_for_drive_fail
;jmp scan_for_drive_success
scan_for_drive_success:
test ebp,1
jz scan_for_drive_not2nd
mov ax,[esi+10]
sub ax,[esi-22]
jnz scan_for_drive_not2nd
mov byte ptr [esi+3],0F0h
scan_for_drive_not2nd:
; push eax
; mov al,0AAh
; call print_hex
; pop eax
clc
pop ebp
pop ecx
ret
scan_for_drive_checklba: ; checks size reported by LBA against that reported
; by CHS.
lea edx,[ebx] ; eax=ecx = chs, edx=ebx = lba
lea ecx,[eax]
sub edx,eax
shr ecx,3 ; 1/8th discrepency tolerated
cmp edx,ecx ; cmp d(sizes),12.5%
pop ecx
jna scan_for_drive_uselba0
jmp ecx
scan_for_drive_nochs:
mov cl,[esi+18]
and cl,0FEh
mov [esi+18],cl
test cl,010b
jnz scan_for_drive_uselba
;jmp scan_for_drive_fail
scan_for_drive_fail:
; push eax
; mov al,0AFh
; call print_hex
; pop eax
stc
pop ebp
pop ecx
ret
ata_grab_drive:
ata_grab_drive_tryagain:
sti
mov dx,[esi+0Ah] ; hd_base
or dl,07h
call hd_getbsy
ata_grab_drive_waitbusy:
in al,dx
cmp al,01h ; ERR
jz ata_grab_drive_okgoahead
test al,080h ; BSY
jnz ata_grab_drive_waitbusy ; yield()? get_on_queue()?
test al,040h ; DRDY
jz ata_grab_drive_waitbusy
ata_grab_drive_okgoahead:
mov al,[ecx]
test al,al
jnz ata_grab_drive_waitbusy
; = 01??????
cli
mov al,[ecx]
test al,al
jnz ata_grab_drive_tryagain
or al,1
mov [ecx],al
and dl,0f8h
sti
ret
ata_release_drive:
xor al,al
call hd_getbsy
mov [ecx],al
ret
Download this file.
[ Return to Browse Source Page ]
Copyright 2000, Ed Pizzi