mirror of
https://github.com/stevenhowes/wbios.git
synced 2026-05-27 00:03:32 +01:00
d410ad0acb
Original source https://www.pcengines.ch/file/wbios111s.zip
1564 lines
36 KiB
Plaintext
1564 lines
36 KiB
Plaintext
;
|
|
; Hard Disk BIOS
|
|
;
|
|
; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved.
|
|
; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5.
|
|
;
|
|
; Limitations:
|
|
;
|
|
; - HDD must support command EC (identify device).
|
|
; - Read Long, Write Long (hardware-specific number of ECC bytes,
|
|
; rarely used) not supported
|
|
; - Format not supported (not available on IDE drives)
|
|
; - Only AMI / Intel style of CHS translation supported.
|
|
; - Extended disk address mode only works with drives that support
|
|
; LBA.
|
|
; - Because of the way the disk parameter table works (crunched
|
|
; down), new drives are only recognized on a cold start, not
|
|
; on Ctrl-Alt-Del restart.
|
|
;
|
|
; Notes:
|
|
;
|
|
; - Storage of disk parameters requires read/write shadow during POST
|
|
; (can be write protected later). This code will break if shadow
|
|
; is write protected during drive configuration !
|
|
;
|
|
; pd 050502 - add HDD_LOOSE option
|
|
; pd 040326 - change HDD_NOSLAVE policy, skip slave in any case if
|
|
; no master detected.
|
|
; pd 030720 - add 32 sector mode for small LBA drives
|
|
; pd 030304 - add option HD_INFO -> display config information
|
|
; - change dpt structure to add IDE I/O port and master /
|
|
; slave data
|
|
; - add four drive support, option HD_4DRV
|
|
; pd 001019 - add option HD_EDD -> packet interface
|
|
; pd 001019 - add functions 41 and 48 to support large drives.
|
|
; pd 001017 - fix power saving HLT to avoid race conditions.
|
|
; pd 001017 - don't limit cylinder number to 1023 in hd_lba
|
|
; (ensure correct result for function 15), do limit in
|
|
; function 08.
|
|
; pd 000211 - recognize new SanDisk ID
|
|
; pd 991020 - add hd_top variable, needed to support M-Systems
|
|
; DiskOnChip.
|
|
; pd 990501 - add CDBOOT hook
|
|
; pd 990427 - add ATAPI identify
|
|
; pd 990214 - add hook for IDE speed initialization (cs_ide)
|
|
; pd 990210 - add LBA mode support
|
|
; pd 981010 - fix read handshake
|
|
; pd 980710 - fix function 15: return 0 if drive not present
|
|
|
|
#if def DEBUG
|
|
HD_DEBUG: ;& comment out for production code
|
|
#endif
|
|
;
|
|
; drive parameter structure (stored in data module)
|
|
;
|
|
dpt_cyl equ 0 ;number of cylinders
|
|
dpt_head equ 2 ;number of heads
|
|
dpt_sig equ 3 ;signature, $A0
|
|
dpt_psec equ 4 ;physical sectors per track
|
|
dpt_mul equ 5 ;(precompensation) -> multiple count
|
|
dpt_shl equ 7 ;(reserved) -> shift count
|
|
dpt_ctl equ 8 ;drive control byte
|
|
dpt_pcyl equ 9 ;physical cylinders
|
|
dpt_phd equ 11 ;physical heads
|
|
dpt_port equ 12 ;IDE I/O port (legacy: landing zone)
|
|
dpt_sec equ 14 ;logical sectors per track
|
|
dpt_dev equ 15 ;master / slave (legacy: reserved)
|
|
dpt_len equ 16 ;length of structure
|
|
;
|
|
; disk address packet for extended read/write/verify/seek
|
|
;
|
|
#if def HD_EDD
|
|
drq_len equ 0 ;packet size in bytes
|
|
drq_res equ 1 ;reserved, must be 0
|
|
drq_blk equ 2 ;number of blocks, max. 127
|
|
drq_res2 equ 3 ;reserved, must be 0
|
|
drq_ofs equ 4 ;transfer buffer offset
|
|
drq_seg equ 6 ;transfer buffer segment
|
|
drq_lba equ 8 ;block number (8 bytes)
|
|
#endif
|
|
|
|
#if def FLASHDISK
|
|
int40: dec dl ;correct floppy drive number
|
|
int 40h ;execute floppy interrupt
|
|
inc dl ;restore drive number
|
|
retf 2 ;return, don't change status
|
|
intfld: jmp fldisk
|
|
;
|
|
; INT 13 entry
|
|
;
|
|
int13hd: sti
|
|
and dl,dl ;flash disk ?
|
|
jz intfld
|
|
jns int40 ;:floppy
|
|
and ah,ah ;reset drive ?
|
|
jnz int13hd1 ;:no
|
|
cmp dl,byte [cs:hd_top] ;above valid HDD ?
|
|
jae int40 ;-> floppy only
|
|
int 40h ;reset floppy
|
|
mov ah,0
|
|
#else
|
|
#if def CDBOOT
|
|
|
|
; redirect to floppy or CD emulation as needed
|
|
|
|
int40: test byte [cs:d_cdflag],1 ;emulation enabled ?
|
|
jz int40a ;:no
|
|
test dl,dl ;drive 0 ?
|
|
jnz int40b ;:no
|
|
jmp cddisk
|
|
|
|
int40a: int 40h ;execute floppy interrupt
|
|
retf 2 ;return, don't change status
|
|
|
|
int40b: dec dl ;correct floppy drive number
|
|
int 40h ;execute floppy interrupt
|
|
inc dl ;restore drive number
|
|
retf 2 ;return, don't change status
|
|
#else
|
|
; execute floppy interrupt
|
|
|
|
int40: int 40h ;execute floppy interrupt
|
|
retf 2 ;return, don't change status
|
|
#endif
|
|
#if def ROMDISK
|
|
int13fl: jmp fldisk
|
|
#endif
|
|
;
|
|
; INT 13 entry
|
|
;
|
|
int13hd: sti
|
|
and dl,dl ;HDD ?
|
|
jns int40 ;:floppy
|
|
cmp dl,byte [cs:hd_top] ;compare with max drive number
|
|
#if def ROMDISK
|
|
jz int13fl
|
|
#endif
|
|
jae int40 ;:floppy or DiskOnChip
|
|
and ah,ah ;reset drive ?
|
|
jnz int13hd1 ;:no
|
|
cmp dl,byte [cs:hd_top] ;above valid HDD ?
|
|
jae int40 ;-> floppy only
|
|
int 40h ;reset floppy
|
|
mov ah,0
|
|
#endif
|
|
;
|
|
; dispatch disk commands
|
|
;
|
|
int13hd1: push ds ;save registers
|
|
push es
|
|
pusha
|
|
mov bp,sp ;access to stack frame
|
|
xor di,di ;access BIOS segment
|
|
mov ds,di
|
|
#if def HD_DEBUG
|
|
; test byte [m_kbf],kb_fscrs ;scroll lock ?
|
|
; jnz int13dmp1 ;yes: don't display
|
|
call v_dump ;dump registers
|
|
int13dmp1:
|
|
#endif
|
|
mov di,ax ;command -> index
|
|
shr di,8
|
|
add di,di
|
|
and byte [bp+18h],0feh ;clear return carry
|
|
cmp di,hd_vec99-hd_vecs ;limit command vector
|
|
jae hd_badcmd ;:too high
|
|
jmp [cs:di.hd_vecs] ;jump to command
|
|
;
|
|
; Illegal command
|
|
;
|
|
hd_badcmd: mov byte [m_hdstat],1 ;illegal command
|
|
;
|
|
; AH=01: get status
|
|
;
|
|
hd_status: mov al,[m_hdstat] ;get old status
|
|
mov [bp._al],al ;return in AL
|
|
;
|
|
; return status
|
|
;
|
|
hd_exit0: mov [m_hdstat],al ;set error code
|
|
mov [bp._ah],al ;return in AH
|
|
hd_exit1: and al,al ;error ?
|
|
jz hd_exit2 ;:no
|
|
or byte [bp+18h],1 ;yes: set carry
|
|
#if def HD_DEBUG
|
|
stc
|
|
hd_exit2: pushf
|
|
test byte [m_kbf],kb_fscrs ;scroll lock ?
|
|
jnz int13dmp2 ;yes: don't display
|
|
call v_dump2 ;& dump registers
|
|
int13dmp2: popf
|
|
#else
|
|
hd_exit2:
|
|
#endif
|
|
popa ;restore registers
|
|
pop es
|
|
pop ds
|
|
iret ;return from interrupt
|
|
;
|
|
; IDE vector table
|
|
;
|
|
even
|
|
hd_vecs: dw hd_rst ;AH=00: recalibrate drive
|
|
dw hd_status ;AH=01: get status
|
|
dw hd_read ;AH=02: read
|
|
dw hd_write ;AH=03: write
|
|
dw hd_verify ;AH=04: verify
|
|
dw hd_badcmd ;AH=05: format track -> not supported
|
|
dw hd_badcmd ;AH=06: bad
|
|
dw hd_badcmd ;AH=07: bad
|
|
dw hd_getprm ;AH=08: read drive parameters
|
|
dw hd_setprm ;AH=09: set drive parameters
|
|
dw hd_badcmd ;AH=0A: read long -> not supported
|
|
dw hd_badcmd ;AH=0B: write long -> not supported
|
|
dw hd_seek ;AH=0C: seek
|
|
dw hd_rst2 ;AH=0D: alternate disk reset (HD only)
|
|
dw hd_badcmd ;AH=0E: bad
|
|
dw hd_badcmd ;AH=0F: bad
|
|
dw hd_trdy ;AH=10: test drive ready
|
|
dw hd_recal ;AH=11: recalibrate
|
|
dw hd_badcmd ;AH=12: bad
|
|
dw hd_badcmd ;AH=13: bad
|
|
dw hd_diag ;AH=14: controller diagnostics
|
|
dw hd_gettyp ;AH=15: get drive type
|
|
dw hd_badcmd ;AH=16: bad
|
|
dw hd_badcmd ;AH=17: bad
|
|
dw hd_badcmd ;AH=18: bad
|
|
dw hd_badcmd ;AH=19: bad
|
|
dw hd_badcmd ;AH=1A: bad
|
|
dw hd_badcmd ;AH=1B: bad
|
|
dw hd_badcmd ;AH=1C: bad
|
|
dw hd_badcmd ;AH=1D: bad
|
|
dw hd_badcmd ;AH=1E: bad
|
|
dw hd_badcmd ;AH=1F: bad
|
|
dw hd_badcmd ;AH=20: bad
|
|
dw hd_badcmd ;AH=21: bad
|
|
dw hd_badcmd ;AH=22: bad
|
|
#if def HD_TIME
|
|
dw hd_timer ;AH=23: set standby timer NON-STANDARD
|
|
#else
|
|
dw hd_badcmd
|
|
#endif
|
|
dw hd_setmul ;AH=24: set multiple mode
|
|
dw hd_id ;AH=25: identify drive
|
|
#if def HD_EDD
|
|
dw hd_badcmd ;AH=26: bad
|
|
dw hd_badcmd ;AH=27: bad
|
|
dw hd_badcmd ;AH=28: bad
|
|
dw hd_badcmd ;AH=29: bad
|
|
dw hd_badcmd ;AH=2A: bad
|
|
dw hd_badcmd ;AH=2B: bad
|
|
dw hd_badcmd ;AH=2C: bad
|
|
dw hd_badcmd ;AH=2D: bad
|
|
dw hd_badcmd ;AH=2E: bad
|
|
dw hd_badcmd ;AH=2F: bad
|
|
dw hd_badcmd ;AH=30: bad
|
|
dw hd_badcmd ;AH=31: bad
|
|
dw hd_badcmd ;AH=32: bad
|
|
dw hd_badcmd ;AH=33: bad
|
|
dw hd_badcmd ;AH=34: bad
|
|
dw hd_badcmd ;AH=35: bad
|
|
dw hd_badcmd ;AH=36: bad
|
|
dw hd_badcmd ;AH=37: bad
|
|
dw hd_badcmd ;AH=38: bad
|
|
dw hd_badcmd ;AH=39: bad
|
|
dw hd_badcmd ;AH=3a: bad
|
|
dw hd_badcmd ;AH=3b: bad
|
|
dw hd_badcmd ;AH=3c: bad
|
|
dw hd_badcmd ;AH=3d: bad
|
|
dw hd_badcmd ;AH=3e: bad
|
|
dw hd_badcmd ;AH=3f: bad
|
|
dw hd_badcmd ;AH=40: bad
|
|
dw hd_edd41 ;AH=41: detect extended interface
|
|
dw hd_xrd ;AH=42: extended read
|
|
dw hd_xwr ;AH=43: extended write
|
|
dw hd_xver ;AH=44: extended verify
|
|
dw hd_badcmd ;AH=45: bad (lock / unlock drive)
|
|
dw hd_badcmd ;AH=46: bad (eject removable media)
|
|
dw hd_xsk ;AH=47: extended seek
|
|
dw hd_edd48 ;AH=48: get extended parameters
|
|
#endif
|
|
hd_vec99: ;end of table
|
|
;
|
|
; AH=00: reset hard disk drives
|
|
; AH=0D: alternate reset (doesn't reset floppy)
|
|
;
|
|
hd_rst:
|
|
hd_rst2: call hd_inten ;enable HD interrupt
|
|
cmp dl,80h
|
|
jb hd_rst3
|
|
cmp dl,byte [cs:hd_top]
|
|
jb hd_rst4
|
|
hd_rst3: mov dl,80h ;hit primary channel
|
|
hd_rst4: call hd_parm ;get table index
|
|
|
|
mov dx,word [cs:di.dpt_port] ;port address
|
|
add dx,hdc_ctrl
|
|
mov al,4 ;soft reset
|
|
out dx,al
|
|
out iowait,ax ;wait a bit
|
|
out iowait,ax
|
|
out iowait,ax
|
|
out iowait,ax
|
|
out iowait,ax
|
|
mov al,0 ;end of reset, interrupt enable
|
|
out dx,al ;hdc_ctrl
|
|
sub dx,hdc_ctrl ;restore hdc base
|
|
call hd_busy18 ;wait while busy
|
|
jb hd_rst8 ;:error
|
|
and dl,0f0h
|
|
or dl,hdc_err ;check error status
|
|
in al,dx
|
|
and al,7fh
|
|
sub al,1
|
|
jnz hd_rst8 ;:bad status
|
|
mov al,0 ;ok status
|
|
jmp hd_exit0 ;return
|
|
|
|
hd_rst8: mov al,5 ;reset failed
|
|
hd_rst9: jmp hd_exit0
|
|
;
|
|
; AH=02: read sectors
|
|
;
|
|
hd_read: call hd_sel ;select drive
|
|
jb hd_read9
|
|
call hd_chs ;translate CHS
|
|
jb hd_read9
|
|
mov bl,[bp._al] ;get sector count
|
|
cld ;forward mode
|
|
mov di,[bp._bx] ;get destination address
|
|
mov byte [m_hdflag],0 ;clear interrupt flag
|
|
mov al,20h ;issue read command
|
|
or dl,hdc_cmd
|
|
out dx,al
|
|
|
|
hd_read1: call hd_int ;wait for interrupt
|
|
jb hd_read9
|
|
or dl,hdc_stat ;read status
|
|
in al,dx
|
|
mov byte [m_hdflag],0 ;clear interrupt flag for next
|
|
test al,1 ;ERR ?
|
|
jnz hd_read8
|
|
test al,8 ;DRQ ?
|
|
jz hd_read8 ;:no
|
|
and dl,0f0h
|
|
; or dl,hdc_dat ;read 512 bytes from drive
|
|
mov cx,256
|
|
rep insw
|
|
dec bl ;another sector ?
|
|
jnz hd_read1 ;:yes
|
|
hd_read8: sub byte [bp._al],bl ;adjust sector count to reality
|
|
call hd_stat ;get status
|
|
hd_read9: jmp hd_exit0
|
|
;
|
|
; AH=03: write sectors
|
|
;
|
|
hd_write: mov si,bx ;source address
|
|
call hd_sel ;select drive
|
|
jb hd_writ9
|
|
call hd_chs ;translate CHS
|
|
jb hd_writ9
|
|
mov bl,[bp._al] ;get sector count
|
|
cld ;forward mode
|
|
mov al,30h ;issue write command
|
|
or dl,hdc_cmd
|
|
out dx,al
|
|
|
|
hd_writ1: or dl,hdc_stat ;read status
|
|
xor cx,cx
|
|
mov byte [m_hdflag],0 ;clear interrupt flag for next
|
|
hd_writ2: in al,dx
|
|
test al,8 ;DRQ ?
|
|
jnz hd_writ3 ;:yes
|
|
test al,21h ;error ?
|
|
jnz hd_writ8
|
|
loop hd_writ2
|
|
mov al,80h ;time-out
|
|
jmp short hd_writ9
|
|
|
|
hd_writ3: and dl,0f0h
|
|
;or dl,hdc_dat ;write 512 bytes from drive
|
|
mov cx,256
|
|
es: rep outsw
|
|
call hd_int ;wait for interrupt
|
|
jb hd_writ9
|
|
dec bl ;another sector ?
|
|
jnz hd_writ1 ;:yes
|
|
|
|
hd_writ8: call hd_stat ;get status
|
|
hd_writ9: jmp hd_exit0
|
|
;
|
|
; AH=04: verify sectors
|
|
;
|
|
hd_verify: call hd_sel ;select drive
|
|
jb hd_ver9
|
|
call hd_chs ;translate CHS
|
|
mov al,40h
|
|
call hd_cmd ;read verify command
|
|
jb hd_ver9
|
|
call hd_stat ;get status
|
|
hd_ver9: jmp hd_exit0
|
|
;
|
|
; AH=08: get drive parameters
|
|
;
|
|
hd_getprm: cmp byte [m_hdcnt],0 ;no drives ?
|
|
jnz hd_getp1
|
|
jmp hd_badcmd ;:bad command
|
|
|
|
hd_getp1: call hd_parm ;get ^parameters
|
|
mov al,7 ;invalid drive number
|
|
mov cx,0 ;return 0 size
|
|
mov dx,0
|
|
jb hd_getp9 ;:not present
|
|
|
|
mov dh,[cs:di.dpt_head] ;DH = max head number
|
|
dec dh
|
|
mov dl,[m_hdcnt] ;DL = number of drives
|
|
mov cx,[cs:di.dpt_cyl] ;CX = max cylinders (swapped)
|
|
dec cx ;-1 for max cyl
|
|
#if def HDD_LBA
|
|
cmp byte [cs:di.dpt_shl],0fc ;LBA mode ?
|
|
jae hd_getp2
|
|
#endif
|
|
dec cx ;deduct one for diagnostic cylinder
|
|
hd_getp2: cmp cx,1023 ;limit cylinder to 1023
|
|
jbe hd_getp3
|
|
mov cx,1023
|
|
hd_getp3: xchg cl,ch
|
|
shl cl,6 ;CL high 6 bits = cylinders high
|
|
or cl,[cs:di.dpt_sec] ;CL = number of sectors
|
|
mov al,0 ;clear status
|
|
|
|
hd_getp9: mov [bp._cx],cx ;cylinder count
|
|
mov [bp._dx],dx ;number drives, heads
|
|
jmp hd_exit0
|
|
;
|
|
; AH=09: set drive parameters
|
|
;
|
|
hd_setprm: call hd_sel ;select drive
|
|
jb hd_setp9
|
|
mov ah,[cs:di.dpt_phd] ;number heads (physical)
|
|
dec ah
|
|
and dl,0f0h
|
|
or dl,hdc_drv ;set maximum heads
|
|
in al,dx
|
|
or al,ah
|
|
out dx,al
|
|
mov al,[cs:di.dpt_psec] ;(physical)
|
|
and dl,0f0h
|
|
or dl,hdc_cnt ;sector count
|
|
out dx,al
|
|
mov al,91h ;set drive parameters
|
|
call hd_cmd
|
|
jb hd_setp9 ;:error
|
|
call hd_stat ;check status
|
|
hd_setp9: jmp hd_exit0
|
|
;
|
|
; AH=0C: seek
|
|
;
|
|
hd_seek: call hd_sel ;select drive
|
|
jb hd_seek9
|
|
call hd_chs ;set CHS value
|
|
mov al,70h
|
|
call hd_cmd ;seek command
|
|
jb hd_seek9
|
|
call hd_stat ;check status
|
|
hd_seek9: cmp al,40h ;seek error ?
|
|
jnz hd_seek91
|
|
mov al,0 ;don't show it... (Core test will fail
|
|
hd_seek91: jmp hd_exit0 ;otherwise)
|
|
;
|
|
; AH=10: test drive ready
|
|
;
|
|
hd_trdy: mov cx,0ffffh ;no time-out
|
|
call hd_sel0 ;select drive, test ready
|
|
jb hd_trdy9
|
|
or dl,hdc_stat ;check status
|
|
in al,dx
|
|
mov [m_hdst],al
|
|
mov ah,0aah ;not ready
|
|
test al,40h
|
|
jz hd_trdy8
|
|
mov ah,40h ;seek error
|
|
test al,10h
|
|
jz hd_trdy8
|
|
mov ah,0cch ;write fault
|
|
test al,20h
|
|
jnz hd_trdy8
|
|
mov ah,0 ;ok status
|
|
hd_trdy8: mov al,ah
|
|
hd_trdy9: jmp hd_exit0
|
|
;
|
|
; AH=11: recalibrate
|
|
;
|
|
hd_recal: call hd_sel ;select drive
|
|
jb hd_rec9
|
|
mov al,10h
|
|
call hd_cmd ;recalibrate command
|
|
jb hd_rec9
|
|
call hd_stat ;get status
|
|
hd_rec9: jmp hd_exit0
|
|
;
|
|
; AH=14: controller diagnostics
|
|
;
|
|
hd_diag: mov dx,hdc ;primary I/O base
|
|
call hd_busy18 ;wait for not busy
|
|
mov al,20h ;bad controller
|
|
jb hd_diag9 ;:bad
|
|
mov al,90h ;diagnostic command
|
|
or dl,hdc_cmd
|
|
out dx,al
|
|
out iowait,al
|
|
mov cx,18*6 ;max. 6 seconds (!!!)
|
|
call hd_busy ;wait for not busy
|
|
mov al,80h ;time-out
|
|
jb hd_diag9
|
|
and dl,0f0h
|
|
or dl,hdc_err ;check error register
|
|
in al,dx
|
|
and al,7fh
|
|
sub al,1
|
|
jz hd_diag9 ;:ok
|
|
mov al,20h ;bad controller
|
|
hd_diag9: jmp hd_exit0
|
|
;
|
|
; AH=15: read DASD type
|
|
;
|
|
hd_gettyp: call hd_parm ;get pointer to parameter block
|
|
jb hd_gett8 ;:not present
|
|
mov al,[cs:di.dpt_head] ;number heads
|
|
mul byte [cs:di.dpt_sec] ;number sectors
|
|
mov dx,[cs:di.dpt_cyl] ;number cylinders
|
|
#if def HDD_LBA
|
|
cmp byte [cs:di.dpt_shl],0fc ;LBA mode ?
|
|
jae hd_gett2
|
|
#endif
|
|
dec dx ;minus one for diagnostics
|
|
hd_gett2: mul dx
|
|
mov cl,3 ;drive present
|
|
jmp short hd_gett9
|
|
|
|
hd_gett8: xor ax,ax ;0 = drive not present
|
|
xor cx,cx
|
|
xor dx,dx
|
|
hd_gett9: mov byte [bp._ah],cl ;0 = not present, 3 = present
|
|
mov [bp._cx],dx ;CX = MSB sector count
|
|
mov [bp._dx],ax ;DX = LSB sector count
|
|
mov al,0 ;ok status
|
|
mov [m_hdstat],al
|
|
jmp hd_exit1
|
|
;
|
|
; AH = 24: set multiple mode
|
|
;
|
|
hd_setmul: call hd_sel ;select drive
|
|
jb hd_setm9
|
|
and dl,0f0h
|
|
or dl,hdc_cnt
|
|
mov al,[bp._al] ;number of sectors
|
|
out dx,al
|
|
mov al,0c6h
|
|
call hd_cmd ;set multiple mode command
|
|
jb hd_setm9
|
|
call hd_stat ;get status
|
|
hd_setm9: jmp hd_exit0
|
|
;
|
|
; AH=25: identify drive
|
|
;
|
|
hd_id: call hd_selb ;select drive
|
|
;ignore time-out here, if drive not ready
|
|
;(ATAPI drive doesn't report ready
|
|
;until spoken to)
|
|
mov al,0ech ;identify drive
|
|
call hd_cmd ;issue command
|
|
jb hd_id9 ;:bad drive
|
|
in al,dx ;hdc_stat
|
|
test al,1 ;error ?
|
|
jz hd_id1 ;:no
|
|
|
|
hd_id0: mov al,0a1h ;ATAPI identify drive
|
|
call hd_cmd ;issue command
|
|
jb hd_id9 ;:time-out
|
|
|
|
hd_id1: xor cx,cx
|
|
hd_id2: in al,dx ;hdc_stat
|
|
test al,8 ;DRQ ?
|
|
jnz hd_id3 ;:yes
|
|
loop hd_id2
|
|
mov al,80h ;time-out
|
|
jmp short hd_id9
|
|
|
|
hd_id3: cld ;forward direction
|
|
and dl,0f0h
|
|
;or dl,hdc_dat
|
|
mov cx,256 ;512 bytes
|
|
mov di,bx ;destination
|
|
rep insw ;read data
|
|
call hd_stat ;get status
|
|
hd_id9: jmp hd_exit0 ;exit
|
|
|
|
#if def HD_EDD
|
|
;
|
|
; AH=41: detect EDD support
|
|
;
|
|
hd_edd41: cmp bx,55aah ;magic cookie ?
|
|
jnz hd_edd419 ;no: bad
|
|
mov word [bp._bx],0aa55h ;return cookie
|
|
mov word [bp._cx],1 ;support packet commands; no lock /
|
|
;eject
|
|
mov byte [bp._ah],1 ;major version
|
|
mov byte [m_hdstat],0
|
|
jmp hd_exit2 ;return carry clear
|
|
|
|
hd_edd419: jmp hd_badcmd ;return error
|
|
;
|
|
; AH=42: extended read
|
|
;
|
|
hd_xrd: call hd_sel ;select drive
|
|
jb hd_xrd9
|
|
call hd_xadr ;handle address
|
|
jb hd_xrd9
|
|
mov byte [m_hdflag],0 ;clear interrupt flag
|
|
mov al,20h ;issue read command
|
|
or dl,hdc_cmd
|
|
out dx,al
|
|
|
|
hd_xrd1: call hd_int ;wait for interrupt
|
|
jb hd_xrd9
|
|
or dl,hdc_stat ;read status
|
|
in al,dx
|
|
mov byte [m_hdflag],0 ;clear interrupt flag for next
|
|
test al,1 ;ERR ?
|
|
jnz hd_xrd8
|
|
test al,8 ;DRQ ?
|
|
jz hd_xrd8 ;:no
|
|
and dl,0f0h
|
|
;or dl,hdc_dat ;read 512 bytes from drive
|
|
mov cx,256
|
|
rep insw
|
|
dec bl ;another sector ?
|
|
jnz hd_xrd1 ;:yes
|
|
hd_xrd8: mov es,[bp._ds] ;access address packet
|
|
sub byte [es:si+drq_blk],bl ;adjust sector count to reality
|
|
call hd_stat ;get status
|
|
hd_xrd9: jmp hd_exit0
|
|
;
|
|
; AH=43: extended write
|
|
;
|
|
hd_xwr: call hd_sel ;select drive
|
|
jb hd_xwr9
|
|
call hd_xadr ;handle address
|
|
jb hd_xwr9
|
|
mov si,di ;buffer ^
|
|
mov al,30h ;issue write command
|
|
or dl,hdc_cmd
|
|
out dx,al
|
|
|
|
hd_xwr1: or dl,hdc_stat ;read status
|
|
xor cx,cx
|
|
mov byte [m_hdflag],0 ;clear interrupt flag for next
|
|
hd_xwr2: in al,dx
|
|
test al,8 ;DRQ ?
|
|
jnz hd_xwr3 ;:yes
|
|
test al,21h ;error ?
|
|
jnz hd_xwr8
|
|
loop hd_xwr2
|
|
mov al,80h ;time-out
|
|
jmp short hd_xwr9
|
|
|
|
hd_xwr3: and dl,0f0h
|
|
;or dl,hdc_dat ;write 512 bytes from drive
|
|
mov cx,256
|
|
es: rep outsw
|
|
call hd_int ;wait for interrupt
|
|
jb hd_xwr9
|
|
dec bl ;another sector ?
|
|
jnz hd_xwr1 ;:yes
|
|
|
|
hd_xwr8: call hd_stat ;get status
|
|
hd_xwr9: mov es,[bp._ds] ;access address packet
|
|
mov si,[bp._si]
|
|
sub byte [es:si+drq_blk],bl ;adjust sector count to reality
|
|
jmp hd_exit0
|
|
;
|
|
; AH=44: extended verify
|
|
;
|
|
hd_xver: call hd_sel ;select drive
|
|
jb hd_xver9
|
|
call hd_xadr ;handle address
|
|
jb hd_xver9
|
|
mov al,40h
|
|
call hd_cmd ;read verify command
|
|
jb hd_xver9
|
|
call hd_stat ;get status
|
|
hd_xver9: jmp hd_exit0
|
|
;
|
|
; AH=47: extended seek
|
|
;
|
|
hd_xsk: call hd_sel ;select drive
|
|
jb hd_xsk9
|
|
call hd_xadr ;handle address
|
|
jb hd_xsk9
|
|
mov al,70h
|
|
call hd_cmd ;seek command
|
|
jb hd_xsk9
|
|
call hd_stat ;check status
|
|
hd_xsk9: jmp hd_exit0
|
|
;
|
|
; AH=48: return drive parameters
|
|
;
|
|
; Note: Phoenix spec says we should return PHYSICAL geometry, but
|
|
; Award BIOS returns LOGICAL... Users of this function are most
|
|
; interested in the max sector count anyway.
|
|
;
|
|
hd_edd48: call hd_parm ;get ^parameter block -> DI
|
|
jb hd_edd489
|
|
mov si,di ;^parameter block
|
|
cld ;forward direction
|
|
mov es,[bp._ds] ;buffer segment
|
|
mov di,[bp._si] ;buffer offset
|
|
mov al,1 ;(error code)
|
|
cmp word [es:di],26 ;buffer at least 26 bytes long
|
|
jb hd_edd489 ;less -> error
|
|
mov ax,26 ;buffer length
|
|
stosw
|
|
mov ax,2 ;flags: valid geometry
|
|
stosw
|
|
xor eax,eax
|
|
mov ax,[cs:si.dpt_cyl] ;number of cylinders
|
|
stosd
|
|
mov al,[cs:si.dpt_head] ;number of heads
|
|
mov ah,0
|
|
stosd
|
|
mov al,[cs:si.dpt_sec] ;number of sectors
|
|
; mov ah,0
|
|
stosd
|
|
mov al,[cs:si.dpt_head] ;number heads
|
|
mul byte [cs:si.dpt_sec] ;number sectors
|
|
mov dx,[cs:si.dpt_cyl] ;number cylinders
|
|
mul dx
|
|
stosw ;-> physical sector count
|
|
xchg ax,dx
|
|
stosw
|
|
xor eax,eax
|
|
stosd
|
|
mov ax,512 ;bytes per sector
|
|
stosw
|
|
mov al,0 ;ok status
|
|
hd_edd489: jmp hd_exit0
|
|
;
|
|
; write LBA address to command file
|
|
;
|
|
; returns sector count in BL, transfer address in ES:DI
|
|
;
|
|
; this will break on old drives that don't support LBA
|
|
;
|
|
hd_xadr: mov es,[bp._ds] ;restore segment, SI still OK
|
|
cmp byte [es:si+drq_len],16 ;at least 16 bytes
|
|
jb hd_xadr9 ;:error
|
|
and dl,0f0h
|
|
or dl,hdc_cnt ;sector count
|
|
mov al,[es:si+drq_blk]
|
|
mov bl,al ;return in BL
|
|
out dx,al
|
|
inc dx
|
|
mov eax,[es:si+drq_lba] ;LBA sector number
|
|
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,40h ;set LBA mode
|
|
or al,ah
|
|
out dx,al ;hdc_drv heads = LBA27..24
|
|
mov di,[es:si+drq_ofs] ;get ^transfer buffer
|
|
mov es,[es:si+drq_seg]
|
|
cld ;forward mode
|
|
clc ;ok
|
|
ret
|
|
|
|
hd_xadr9: mov al,1 ;return error
|
|
stc
|
|
ret
|
|
#endif
|
|
;
|
|
; wait for not busy, check status
|
|
;
|
|
hd_stat0: call hd_busy18 ;wait until not busy
|
|
jb hd_stat9
|
|
|
|
; Enter here for faster service (assuming normally not busy)
|
|
; This is arranged to get fastest response when no error.
|
|
|
|
hd_stat: or dl,hdc_stat ;test whether busy
|
|
in al,dx
|
|
test al,80h
|
|
jnz hd_stat0 ;:busy
|
|
mov [m_hdst],al
|
|
mov ah,al ;save status
|
|
test al,24h ;write fault / ECC ?
|
|
jnz hd_stat1
|
|
and al,50h ;not ready, or seek error ?
|
|
cmp al,50h
|
|
jnz hd_stat2
|
|
test ah,1 ;other error ?
|
|
jnz hd_stat3
|
|
mov al,0 ;return ok status
|
|
ret
|
|
|
|
hd_stat1: mov al,11h ;ECC corrected data
|
|
test ah,4
|
|
jnz hd_stat9
|
|
mov al,0cch ;no - must be write fault
|
|
hd_stat9: stc
|
|
ret
|
|
|
|
hd_stat2: mov al,0aah ;not ready
|
|
test ah,40h
|
|
jz hd_stat9
|
|
mov al,40h ;no - must be seek error
|
|
stc
|
|
ret
|
|
|
|
hd_stat3: and dl,0f0h
|
|
or dl,hdc_err ;read error register
|
|
in al,dx
|
|
mov [m_hderr],al
|
|
mov si,hd_errtab
|
|
cmp al,0 ;nothing set -> undefined error
|
|
jz hd_stat5
|
|
hd_stat4: inc si
|
|
shl al,1
|
|
jnb hd_stat4
|
|
hd_stat5: mov al,[cs:si] ;get error code
|
|
stc
|
|
ret
|
|
;
|
|
; error register -> error code translation
|
|
;
|
|
hd_errtab: db 0e0h ;nothing set - status error
|
|
db 0ah ;80 - bad sector flag detected
|
|
db 10h ;40 - bad ECC
|
|
db 0bbh ;20 - undefined error
|
|
db 04h ;10 - record not found
|
|
db 01h ;08 - abort -> bad command
|
|
db 0bbh ;04 - undefined error
|
|
db 40h ;02 - seek error
|
|
db 02h ;01 - address mark not found
|
|
;
|
|
; get pointer to parameter block
|
|
;
|
|
; entry: DL = drive
|
|
; exit: CS:DI = parameter block
|
|
;
|
|
hd_parm: cmp dl,byte [cs:hd_top] ;valid drive number ?
|
|
jae hd_parm9 ;:bad
|
|
mov di,dx ;drive number
|
|
and di,7fh
|
|
shl di,4 ;* 16 dpt_len
|
|
add di,offset hd_prm0 ;+ table base
|
|
;clc
|
|
ret
|
|
|
|
hd_parm9: mov al,1 ;error code
|
|
stc
|
|
ret
|
|
;
|
|
; wait while HD busy, CX ticks, DX port base
|
|
;
|
|
hd_busy18: mov cx,18 ;18 ticks = 1 second
|
|
hd_busy: add cx,[m_timer] ;start time + max number of ticks
|
|
and dl,0f0h
|
|
or dl,hdc_stat
|
|
hd_busy1: in al,dx
|
|
test al,80h ;busy ?
|
|
jz hd_busy9 ;:no, carry clear
|
|
cmp cx,[m_timer]
|
|
jns hd_busy1 ;keep waiting
|
|
hd_busy8: stc ;time-out
|
|
mov al,80h ;status code
|
|
hd_busy9: ret
|
|
;
|
|
; select drive, wait for drive ready
|
|
;
|
|
; -> CS:DI = ^parameter block
|
|
;
|
|
|
|
; special entry: no drive number check
|
|
|
|
hd_selb: mov cx,18 ;1 s time-out
|
|
and dl,7fh ;mask drive number
|
|
jmp short hd_sel0a
|
|
|
|
; normal entry
|
|
|
|
hd_sel: mov cx,18 ;1 s time-out for ready wait
|
|
hd_sel0: and dl,7fh ;legal drive number ?
|
|
cmp dl,[m_hdcnt]
|
|
jae hd_parm9 ;:bad
|
|
hd_sel0a: mov di,dx
|
|
and di,007fh ;mask drive number
|
|
shl di,4
|
|
add di,offset hd_prm0
|
|
mov byte [m_hdflag],0 ;clear interrupt flag
|
|
mov al,byte [cs:di.dpt_dev]
|
|
or al,0a0h
|
|
mov dx,word [cs:di.dpt_port]
|
|
or dl,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
|
|
or dl,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
|
|
or dl,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
|
|
;
|
|
#if ! def irq14 ;(optional user override)
|
|
irq14:
|
|
irq15: 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
|
|
#endif
|
|
;
|
|
; set hard disk time-out
|
|
;
|
|
#if def HD_TIME
|
|
hd_timer:
|
|
call hd_sel ;select drive, wait for not busy
|
|
jb hd_tim9
|
|
and dl,0f0h
|
|
or dl,hdc_cnt
|
|
mov al,HD_TIME
|
|
out dx,al
|
|
mov al,0e3h
|
|
call hd_cmd ;issue command
|
|
jb hd_tim9
|
|
and dl,0f0h
|
|
or dl,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: and dl,0f0h
|
|
or dl,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,0fch ;LBA mode ?
|
|
jae 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
|
|
cmp cl,0ff ;FF = 255 heads
|
|
jnz hd_chs3
|
|
mov ebx,eax
|
|
shl eax,8 ;cylinder * 255
|
|
sub eax,ebx
|
|
jmp hd_chs4
|
|
|
|
hd_chs3: shl eax,5 ;* 32
|
|
cmp cl,0fc ;FC = 32 heads
|
|
jz hd_chs4
|
|
|
|
shl eax,1 ;* 64
|
|
cmp cl,0fd ;FD = 64 heads
|
|
jz hd_chs4
|
|
|
|
shl eax,1 ;* 128
|
|
|
|
hd_chs4: xor ebx,ebx
|
|
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
|
|
;
|
|
; enable HD interrupt
|
|
;
|
|
hd_inten: cli
|
|
in al,pic1+1 ;enable HD interrupt
|
|
#if def HD_4DRV
|
|
and al,03fh
|
|
#else
|
|
and al,0bfh
|
|
#endif
|
|
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
|
|
ret
|
|
;
|
|
; 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.
|
|
;
|
|
cmp word [m_rstflg],1234h ;Ctrl-Alt-Del ?
|
|
jz hd_wait9 ;:skip wait - drives should be ready
|
|
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+hdc_dat
|
|
out dx,al
|
|
or dl,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
|
|
and dl,0f0h
|
|
or dl,hdc_drv
|
|
out dx,al
|
|
out iowait,ax
|
|
or dl,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
|
|
|
|
call hd_inten ;enable HD interrupt
|
|
mov al,byte [cs:hd_top] ;set number of drives to start
|
|
and al,7fh
|
|
mov [m_hdcnt],al
|
|
mov byte [m_hdstat],0 ;clear status
|
|
|
|
; 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.
|
|
;
|
|
; To save time, skip slave detection if no master was seen.
|
|
|
|
mov di,hd_prm0-dpt_len ;setup first drive
|
|
mov dl,80h-1
|
|
|
|
hd_init1: inc dl ;next drive
|
|
add di,dpt_len
|
|
cmp dl,byte [cs:hd_top] ;done ?
|
|
jz hd_init2 ;:yes
|
|
#if def HDD_NOSLAVE ;time-saver: skip slave drives
|
|
test byte [cs:di.dpt_dev],10h ;slave ?
|
|
jnz hd_init1 ;yes: skip
|
|
#else
|
|
test byte [cs:di.dpt_dev],10h ;slave ?
|
|
jz hd_init1a ;:no
|
|
shr byte [cs:hd_good],1 ;was master good ?
|
|
jnc hd_init1 ;no: skip
|
|
hd_init1a:
|
|
#endif
|
|
|
|
#if ! def HDD_PRES
|
|
mov byte [cs:hd_good],0 ;clear good flag
|
|
call hd_pres ;check presence
|
|
jb hd_init1 ;:not present
|
|
inc byte [cs:hd_good] ;set good flag
|
|
#endif
|
|
call hd_set ;set parameters
|
|
;jb hd_init1 ;(if bad, we will find out later)
|
|
jmp hd_init1
|
|
|
|
hd_init2: mov di,hd_prm0 ;count drives, crunch descriptors
|
|
mov dl,0 ;clear drive count
|
|
mov dh,byte [cs:hd_top] ;current drive count
|
|
sub dh,07fh
|
|
push ds ;save DS, ES
|
|
push es
|
|
push cs ;DS = CS, ES = CS
|
|
pop ds
|
|
push cs
|
|
pop es
|
|
lea si,[di.-dpt_len]
|
|
hd_init3: add si,dpt_len ;go to next descriptor
|
|
hd_init4: dec dh ;another drive ?
|
|
jbe hd_init5 ;:no
|
|
test word [si.dpt_cyl],0ffffh ;0 cylinders ?
|
|
jz hd_init3 ;:drive doesn't exist
|
|
mov cx,dpt_len / 2 ;copy descriptor down
|
|
rep movsw
|
|
inc dl ;count good drives
|
|
jmp hd_init4
|
|
|
|
hd_init5: mov cx,offset hd_prm99 ;fill remaining descriptors
|
|
sub cx,di
|
|
jz hd_init6
|
|
shr cx,1
|
|
xor ax,ax
|
|
rep stosw
|
|
hd_init6: pop es
|
|
pop ds
|
|
mov byte [m_hdcnt],dl ;store number of drives
|
|
or dl,80h
|
|
mov byte [cs:hd_top],dl ;store top hard disk number
|
|
#if def HD_INFO
|
|
jmp hd_cr
|
|
#else
|
|
ret
|
|
#endif
|
|
;
|
|
; check drive presence, DI = ^device block
|
|
;
|
|
hd_pres: push dx
|
|
mov dx,word [cs:di.dpt_port]
|
|
or dl,hdc_drv
|
|
mov al,byte [cs:di.dpt_dev]
|
|
or al,0a0h
|
|
out dx,al
|
|
out iowait,ax
|
|
out iowait,ax
|
|
out iowait,ax
|
|
|
|
and dl,0f0h
|
|
or dl,hdc_cnt ;write test pattern
|
|
mov al,55h
|
|
out dx,al
|
|
and dl,0f0h
|
|
or dl,hdc_cyl ;write negative pattern
|
|
mov al,0aah
|
|
out dx,al
|
|
and dl,0f0h
|
|
or dl,hdc_cnt ;read test pattern
|
|
in al,dx
|
|
xor al,55h
|
|
jz hd_pres9 ;:ok
|
|
hd_pres8:
|
|
#if def HD_INFO
|
|
pusha
|
|
call hd_cr
|
|
mov ax,dx ;display port address
|
|
and al,0f0 ;(change into base)
|
|
call hex
|
|
mov si,offset hd_nf ;not found
|
|
call v_msg
|
|
popa
|
|
#endif
|
|
stc
|
|
hd_pres9: pop dx
|
|
ret
|
|
;
|
|
; set up drive DL, drive parameters DI
|
|
;
|
|
hd_set: mov ah,25h ;get drive ID
|
|
mov bx,tmp_buf
|
|
int 13h
|
|
jb hd_set9
|
|
|
|
#if def HD_INFO
|
|
call hd_inf1 ;display drive info, part 1
|
|
#endif
|
|
|
|
#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
|
|
|
|
#if def HDD_LOOSE
|
|
mov ax,[tmp_buf+0] ;device ID
|
|
and ax,084c0
|
|
cmp ax,08480 ;removable CF
|
|
jz hd_set0
|
|
cmp ax,08440 ;not removable CF
|
|
jz hd_set0
|
|
test ah,080 ;ATAPI ?
|
|
jz hd_set0
|
|
#else
|
|
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
|
|
#endif
|
|
|
|
; 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
|
|
|
|
#if def HD_INFO
|
|
call hd_inf2 ;say ATAPI
|
|
#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 cfg_lba
|
|
cmp word [cs:cfg_ofs+cfg_lba],0
|
|
jz hd_set1 ;0 -> stay in CHS mode
|
|
cmp ax,[cs:cfg_ofs+cfg_lba]
|
|
jae hd_lba ;select LBA if cylinder count >= config
|
|
#else
|
|
#if def FORCE_LBA
|
|
cmp ax,FORCE_LBA ;force LBA for high cylinder count
|
|
ja hd_lba
|
|
#endif
|
|
#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:
|
|
|
|
#if def HD_INFO
|
|
call hd_inf3 ;display physical / logical CHS
|
|
#endif
|
|
|
|
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
|
|
;
|
|
hd_lba: test byte [tmp_buf+99],2 ;LBA mode supported ?
|
|
jz hd_set9 ;:no
|
|
|
|
push dx
|
|
|
|
mov eax,dword [tmp_buf+120] ;number of LBA sectors (low)
|
|
mov dx,[tmp_buf+122] ;(high)
|
|
mov bx,32*63
|
|
mov cx,0fc00+32 ;32 heads
|
|
cmp eax,1023*32*63 ;max size ?
|
|
jbe hd_lba2
|
|
|
|
mov bx,64*63 ;64 heads
|
|
mov cx,0fd00+64
|
|
cmp eax,1023*64*63 ;max size ?
|
|
jbe hd_lba2
|
|
|
|
mov bx,128*63 ;128 heads
|
|
mov cx,0fe00+128
|
|
cmp eax,1023*128*63
|
|
jb hd_lba2
|
|
|
|
mov bx,255*63 ;255 heads
|
|
mov cx,0ff00+255
|
|
|
|
hd_lba2: div bx ;/ heads / sectors
|
|
|
|
; set drive parameters
|
|
|
|
mov [cs:di.dpt_cyl],ax
|
|
mov byte [cs:di.dpt_head],cl ;number of heads
|
|
mov byte [cs:di.dpt_shl],ch; special shift -> LBA mode
|
|
mov byte [cs:di.dpt_sec],63 ;63 sectors
|
|
pop dx
|
|
jmp hd_set2b ;note we don't set LBA parameters
|
|
#endif
|
|
;
|
|
; display drive information
|
|
;
|
|
#if def HD_INFO
|
|
;
|
|
; display drive info (DI = ^drive parms, tmp_buf = identify blk)
|
|
;
|
|
hd_inf1: pusha
|
|
call hd_cr ;display CR/LF
|
|
mov ax,[cs:di.dpt_port] ;display port
|
|
call hex
|
|
mov si,offset hd_mstr ;master
|
|
test byte [cs:di.dpt_dev],10h ;slave ?
|
|
jz hd_inf1a
|
|
mov si,offset hd_slve ;slave
|
|
hd_inf1a: call v_msg
|
|
mov ax,[tmp_buf+0] ;display config bits
|
|
call hex
|
|
mov si,offset tmp_buf+27*2
|
|
mov cx,20
|
|
hd_inf1b: lodsw ;display drive model number
|
|
push ax
|
|
mov al,ah ;big endian format !
|
|
call putc
|
|
pop ax
|
|
call putc
|
|
loop hd_inf1b
|
|
popa
|
|
ret
|
|
;
|
|
; say this is an ATAPI device
|
|
;
|
|
hd_inf2: pusha
|
|
mov si,offset hd_atapi
|
|
call v_msg
|
|
popa
|
|
ret
|
|
;
|
|
; display physical and logical information
|
|
;
|
|
hd_inf3: pusha
|
|
mov si,offset hd_phys
|
|
call v_msg
|
|
movzx eax,word [tmp_buf+2] ;"phys" cylinders
|
|
call hd_ints
|
|
movzx eax,word [tmp_buf+6] ;"phys" heads
|
|
call hd_ints
|
|
movzx eax,word [tmp_buf+12] ;"phys" sectors
|
|
call post_itoa
|
|
|
|
mov si,offset hd_log
|
|
call v_msg
|
|
movzx eax,word [cs:di.dpt_cyl] ;logical cylinders
|
|
call hd_ints
|
|
movzx eax,byte [cs:di.dpt_head] ;logical heads
|
|
call hd_ints
|
|
movzx eax,byte [cs:di.dpt_sec] ;logical sectors
|
|
call post_itoa
|
|
|
|
cmp byte [cs:di.dpt_shl],0ffh ;LBA mode ?
|
|
jnz hd_inf3a
|
|
mov si,offset hd_vlba ;say so
|
|
call v_msg
|
|
hd_inf3a: popa
|
|
ret
|
|
|
|
hd_cr: mov si,offset msg_crlf
|
|
jmp v_msg
|
|
|
|
hd_ints: call post_itoa ;display number
|
|
mov al,"/" ;display trailing /
|
|
jmp putc
|
|
|
|
hd_mstr: db "Master ",0
|
|
hd_slve: db "Slave ",0
|
|
hd_atapi: db "ATAPI",0
|
|
hd_phys: db 13,10,"Phys C/H/S ",0
|
|
hd_log: db " Log C/H/S ",0
|
|
hd_vlba: db " LBA",0
|
|
hd_nf: db "- no drive found ! ",0
|
|
|
|
#endif
|