📄 hdd.8
字号:
mov al,dl ;drive
shl al,4
or al,0a0h ;reserved bits
mov dx,hdc_drv
out dx,al ;set drive
;
; wait until HD ready, CX ticks
;
hd_rdy: add cx,[m_timer] ;start time + max number of ticks
mov dx,hdc_stat
hd_rdy1: in al,dx
test al,80h
jnz hd_rdy2 ;:busy
test al,40h
jnz hd_busy9 ;:ready, carry clear
hd_rdy2: cmp cx,[m_timer]
jns hd_rdy1 ;keep waiting
jmp hd_busy8
;
; issue command AL, wait for interrupt
;
hd_cmd: mov byte [m_hdflag],0 ;clear interrupt flag
mov dx,hdc_cmd
out dx,al
; wait for HD interrupt
hd_int: mov cx,18*4 ;4 seconds
add cx,[m_timer] ;start time + max number of ticks
hd_int1: cli ;test in critical section as some
;modern drives are "too fast" for
;slower embedded boards.
test byte m_hdflag,0ffh ;interrupt ?
jnz hd_int9 ;:yes, return NC
cmp cx,[m_timer] ;time-out ?
js hd_int8 ;:yes, return CY
sti ;end critical section, HLT follows
hlt ;power-saving wait for next interrupt
jmp hd_int1
hd_int8: stc ;time-out
mov al,80h ;status code
hd_int9: sti ;re-enable interrupts !
ret
;
; IRQ14 entry
;
irq14: push ax
push ds
xor ax,ax ;BIOS segment
mov ds,ax
mov byte [m_hdflag],0ffh ;set interrupt flag
mov al,eoi
out pic1,al
out pic0,al
pop ds
pop ax
iret
;
; set hard disk time-out
;
#if def HD_TIME
hd_timer:
call hd_sel ;select drive, wait for not busy
jb hd_tim9
mov dx,hdc_cnt
mov al,HD_TIME
out dx,al
mov al,0e3h
call hd_cmd ;issue command
jb hd_tim9
mov dl,low(hdc_err)
in al,dx
and al,7fh
jz hd_tim9
sub al,1 ;ok ?
jz hd_tim9
mov al,20h
stc
hd_tim9: jmp hd_exit0
#endif
;
; write CHS parameters to command file, including CHS translation
;
hd_chs: mov dx,hdc_cnt ;sector count
mov al,[bp._al]
out dx,al
inc dx
mov cl,[cs:di.dpt_shl] ;get shift count
#if def HDD_LBA
cmp cl,0ffh ;LBA mode ?
jz hd_chs2
#endif
mov bx,[bp._cx] ;sector number, cylinder
mov al,bl ;sector number
and ax,3fh ;(need AH = 0 for divide)
out dx,al ;hdc_sec
mov al,[bp._dh] ;head number
div byte [cs:di.dpt_phd] ;divide by physical heads
;-> AL = heads, AH = cylinders
inc dx
xchg bl,bh ;swap cylinder
shr bh,6 ;bit 7..6 become bits 9..8
shl bx,cl ;shift cylinder for CHS translation
or al,bl ;head
out dx,al ;hdc_cyl - cylinder low
inc dx
mov al,bh ;cylinder high
out dx,al ;hdc_cyh
inc dx
in al,dx ;hdc_drv
or al,ah ;heads
out dx,al
ret
;
; LBA translation
;
#if def HDD_LBA
hd_chs2: push eax ;save eax, ebx
push ebx
xor eax,eax
mov ax,[bp._cx] ;sector number, cylinder
xchg al,ah ;swap cylinder high, low
shr ah,6 ;fix cylinder high
mov ebx,eax
shl eax,8 ;cylinder * 255
sub eax,ebx
xor bx,bx
mov bl,[bp._dh] ;head number
add eax,ebx ;add head
mov ebx,eax
shl eax,6 ;cylinder * 255 + head * 63
sub eax,ebx
xor ebx,ebx
mov bl,[bp._cl] ;sector number
and bl,63
dec bx ;- 1
add eax,ebx
out dx,al ;hdc_sec sector = LBA 7..0
inc dx
shr ax,8
out dx,al ;hdc_cyl cylinder low = LBA 15..8
inc dx
shr eax,16
out dx,al ;hdc_cyh cylinder high = LBA 23..16
inc dx
in al,dx ;hdc_drv get drive
and al,0b0h ;keep reserved, drive select bits
or al,040h ;set LBA mode
or al,ah
out dx,al ;hdc_drv heads = LBA27..24
pop ebx
pop eax
ret
#endif
;
; HD detect / init
;
hd_init:
#if def HD_WAIT
;
; Some drives take a long time to become responsive to commands,
; because they only store very minimal firmware, and fetch the
; actual code from disk. Some of them are allergic to being touched
; before they are ready.
;
#if !def HD_WAITA
cmp word [m_rstflg],1234h ;Ctrl-Alt-Del ?
jz hd_wait9 ;:skip wait
#endif
xor bx,bx ;clear second counter
mov si,msg_wait
call v_msg
cmp bx,HD_ENA ;0 delay ?
jz hd_wait3 ;yes: bypass
hd_wait1: mov ax,18 ;about 1 second
add ax,[m_timer] ;start time + max number of ticks
hd_wait2: hlt ;low power wait, we'll be here for a
;while
cmp ax,[m_timer] ;time-out ?
jns hd_wait2 ;no: keep waiting
cmp bx,HD_ENA ;can we touch the drive now ?
jb hd_wait8 ;:no
hd_wait3: mov al,0ffh ;place FF on the IDE bus (or loopback)
mov dx,hdc_dat
out dx,al
mov dl,low(hdc_stat) ;does the status register read non-FF ?
in al,dx
cmp al,0ffh
jz hd_wait8a ;FF: no drive attached, bail
test al,80h ;busy ?
jnz hd_wait8 ;:don't touch
mov al,0a0h ;access master drive
mov dl,low(hdc_drv)
out dx,al
out iowait,ax
mov dl,low(hdc_stat) ;read status
in al,dx
test al,80h ;busy ?
jnz hd_wait8
test al,40h ;drive ready ?
jnz hd_wait8a ;:terminate the wait
hd_wait8: mov si,msg_dot ;display a dot each second
call v_msg
inc bx ;second counter
cmp bx,HD_WAIT
jb hd_wait1
hd_wait8a: mov si,msg_crlf ;go to next line
call v_msg
hd_wait9:
#endif
cli
mov ax,int13hd ;set interrupt vector
xchg [vec13],ax
mov [vec40],ax
mov ax,cs ;old INT13 becomes INT40
xchg [vec13+2],ax
mov [vec40+2],ax
mov word [vec41],hd_prm0 ;set vectors to disk parameters
mov [vec41+2],cs
mov word [vec46],hd_prm1
mov [vec46+2],cs
in al,pic1+1 ;enable HD interrupt
and al,0bfh
out iowait,ax
out pic1+1,al
in al,pic0+1 ;enable cascade interrupt
and al,0fbh
out iowait,ax
out pic0+1,al
sti
mov byte [m_hdcnt],2 ;2 drives to start
mov byte [m_hdstat],0 ;clear status
mov di,hd_prm0 ;setup first drive
mov dl,80h
mov al,0a0h
#if ! def HDD_PRES
call hd_pres ;check presence
jb hd_init1
#endif
call hd_set ;set parameters
jb hd_init1
; Unfortunately, it is not that easy to detect the slave drive,
; as the master drive will often drive the slave registers to
; "safe" values when the slave is not present.
;
; It is supposed to be possible to detect number of drives with
; the execute drive diagnostic command, but I don't see how.
;
; In the end, if the detection was incorrect, we will time out
; (about a second) when trying to identify the drive.
mov dl,81h ;setup second drive
#if ! def HDD_NOSLAVE
mov di,hd_prm1
mov al,0b0h
call hd_pres ;check presence
jb hd_init1
call hd_set ;set parameters
jb hd_init1 ;:error
inc dx ;increment drive count if no error
#endif
hd_init1: mov byte [cs:hd_top],dl ;store top hard disk number
and dl,7fh ;done - store number drives
mov byte [m_hdcnt],dl
ret
;
; check drive presence, AL = A0 or B0
;
hd_pres: push dx
mov dx,hdc_drv
out dx,al
out iowait,ax
out iowait,ax
out iowait,ax
mov dl,low(hdc_cnt) ;write test pattern
mov al,55h
out dx,al
mov dl,low(hdc_cyl) ;write negative pattern
mov al,0aah
out dx,al
mov dl,low(hdc_cnt) ;read test pattern
in al,dx
xor al,55h
jz hd_pres9 ;:ok
hd_pres8: stc
hd_pres9: pop dx
ret
;
; set up drive DL
;
hd_set: mov ah,25h ;get drive ID
mov bx,tmp_buf
int 13h
jb hd_set9
#if def cs_ide
push di ;save ^drive parameters
mov di,tmp_buf ;DS:DI points to identify buffer
push dx ;DL: drive
call cs_ide ;set drive timing parameters
pop dx
pop di
#endif
cmp word [tmp_buf+0],848ah ;CompactFlash ?
jz hd_set0 ;:yes
cmp word [tmp_buf+0],844ah ;CompactFlash ? (new SanDisk)
jz hd_set0 ;:yes
test byte [tmp_buf+1],80h ;ATAPI ?
jz hd_set0 ;:no
; note I/O base and drive ID of ATAPI CD-ROM
; this is assumed to be the first ATAPI device found
#if def CDBOOT
cmp byte [cs:d_cdbase],0 ;is this the first ATAPI drive ?
jnz hd_set9 ;:no
mov word [cs:d_cdbase],01f0h ;set address
test dl,1 ;master ?
jnz hd_set9 ;:slave, default
mov byte [cs:d_cddrv],0a0h ;master drive
#endif
hd_set9: stc ;error return
ret
hd_set0: mov al,[tmp_buf+12] ;sectors
mov [cs:di.dpt_sec],al
mov [cs:di.dpt_psec],al
mov al,[tmp_buf+94] ;multiple block size
mov [cs:di.dpt_mul],al
mov ax,[tmp_buf+2] ;cylinders
mov [cs:di.dpt_pcyl],ax
mov bl,[tmp_buf+6] ;heads
mov [cs:di.dpt_phd],bl
; CHS translation: shift cylinders right / heads left until
; cylinders < 1024
mov bh,0 ;shift count
#if def FORCE_LBA
cmp ax,8191 ;force LBA for high cylinder count
ja hd_lba
#endif
hd_set1: cmp ax,1024
jb hd_set2
shr ax,1 ;cylinders / 2
shl bl,1 ;heads * 2
#if def HDD_LBA
jb hd_lba ;:overflow - use LBA mode for this drive
#else
jb hd_set9 ;:overflow - cannot translate drive
#endif
inc bh ;count the shifts
jmp hd_set1
hd_set2: mov [cs:di.dpt_cyl],ax
mov [cs:di.dpt_head],bl
mov [cs:di.dpt_shl],bh
mov byte [cs:di.dpt_sig],0a0h ;signature
mov ah,9 ;set drive parameters
int 13h
jb hd_set9
mov ah,0dh ;reset drive
int 13h
hd_set2b:
push dx
mov ah,8 ;get max CHS
int 13h
mov al,dh ;heads
pop dx
jb hd_set9
mov ah,4 ;verify sectors
mov dh,al ;max head
mov al,cl ;max sector -> sector count
and al,3fh
and cl,0c0h ;start sector = 1
or cl,1
sub ch,1 ;cylinder - 1
jnb hd_set3
sub cl,40h
hd_set3: int 13h
jb hd_set9
#if def HD_TIME
mov ah,23h ;set drive time-out
mov al,HD_TIME
int 13h
#endif
ret ;normal return
#if def HDD_LBA
;
; determine LBA parameters (always 255 heads / 63 sectors)
;
hd_lba: test byte [tmp_buf+99],2 ;LBA mode supported ?
jz hd_set9 ;:no
push dx
mov ax,[tmp_buf+120] ;number of LBA sectors (low)
mov dx,[tmp_buf+122] ;(high)
mov bx,255*63 ; / heads / sectors
div bx
pop dx
; set drive parameters
mov [cs:di.dpt_cyl],ax
mov byte [cs:di.dpt_head],255 ;255 heads
mov byte [cs:di.dpt_shl],0ffh ;special shift -> LBA mode
mov byte [cs:di.dpt_sec],63 ;63 sectors
jmp hd_set2b ;note we don't set LBA parameters
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -