379 lines
23 KiB
NASM
379 lines
23 KiB
NASM
; ========================================================================== ;
|
|
; IDEHDC.asm ;
|
|
; Direct disk I/O module for IDE disk controllers. ;
|
|
; Written by Alan Martin. ;
|
|
; Note - this code doesn't use interrupts! ;
|
|
; ========================================================================== ;
|
|
idehdc segment use16 'CODE' ; Direct disk I/O code segment.
|
|
; Initialize segment.
|
|
assume cs:idehdc ; Initial segment assumptions.
|
|
.386 ; Assume 386+ code is valid.
|
|
|
|
; Stack addressing equates.
|
|
_ax equ word ptr bp+001Ch ; Saved AX value.
|
|
_al equ byte ptr bp+001Ch ; Saved AL value.
|
|
_ah equ byte ptr bp+001Dh ; Saved AH value.
|
|
_bx equ word ptr bp+0010h ; Saved BX value.
|
|
_bl equ byte ptr bp+0010h ; Saved BL value.
|
|
_bh equ byte ptr bp+0011h ; Saved BH value.
|
|
_cx equ word ptr bp+0018h ; Saved CX value.
|
|
_cl equ byte ptr bp+0018h ; Saved CL value.
|
|
_ch equ byte ptr bp+0019h ; Saved CH value.
|
|
_dx equ word ptr bp+0014h ; Saved DX value.
|
|
_dl equ byte ptr bp+0014h ; Saved DL value.
|
|
_dh equ byte ptr bp+0015h ; Saved DH value.
|
|
|
|
delay macro x ; Long delay (for 400ns transition).
|
|
local @loop ; Local labels.
|
|
push cx ; Save CX.
|
|
mov cx,x ; Get repeat count.
|
|
@loop: loop @loop ; Loop X times.
|
|
pop cx ; Restore CX.
|
|
endm ; End of DELAY macro.
|
|
|
|
hdcwait macro ; Wait for HDC to finish commands.
|
|
local @loop,@ok,@err ; Local labels.
|
|
push ax ecx dx ; Save registers.
|
|
mov ecx,00040000h ; 1s delay on 486DX-50.
|
|
mov dx,01F7h ; HDC status register.
|
|
in al,dx ; Read status.
|
|
test al,80h ; Is the HDC busy?
|
|
jz @ok ; If not, end immediately.
|
|
@loop: in al,dx ; Read status.
|
|
delay 000Ah ; 50-100 clock delay.
|
|
test al,80h ; Is the HDC busy?
|
|
jz @ok ; If not, end loop.
|
|
loopd @loop ; Otherwise, continue.
|
|
pop dx ecx ax ; Restore registers.
|
|
stc ; Set CF.
|
|
jmp @err ; Exit with error.
|
|
@ok: pop dx ecx ax ; Restore registers.
|
|
clc ; Clear CF.
|
|
@err:
|
|
endm ; End of HDCWAIT macro.
|
|
|
|
; ------------------------------------------ ;
|
|
; IOREAD - Read sectors through direct I/O. ;
|
|
; Input.: AX=cylinder number ;
|
|
; BH=head number; BL=sector number ;
|
|
; CH=# of sectors; CL=drive (0 or 1) ;
|
|
; DS:DX->buffer for sectors ;
|
|
; Output: CF=0 (OK) or 1 (Error) ;
|
|
; AL=error code or 0 if no error. ;
|
|
; AH=device error code if applicable.;
|
|
; Errors: AL=0 - No error. ;
|
|
; 1 - Controller busy or absent. ;
|
|
; 2 - Drive not ready. ;
|
|
; 3 - Drive not ready for read. ;
|
|
; 4 - Device error: ;
|
|
; AH, bit 0: Address mark not found. ;
|
|
; 1: Track 0 not found. ;
|
|
; 2: Write fault. ;
|
|
; 4: Sector not found. ;
|
|
; 6: Error in data. ;
|
|
; 7: Sector marked bad. ;
|
|
; Note: There may be errors if a read is ;
|
|
; across a cylinder (or sometimes even ;
|
|
; a head) boundary. ;
|
|
; ------------------------------------------ ;
|
|
ioread proc far ; Read sectors through direct I/O.
|
|
pushad ; Save all registers.
|
|
mov bp,sp ; Address the stack.
|
|
push ds es ; Save segments.
|
|
in al,0A1h ; Get PIC 2 mask.
|
|
push ax ; Save it.
|
|
or al,40h ; Disable IRQ 14.
|
|
out 0A1h,al ; Set PIC 2 mask.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc ir_ok0 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp ir_err ; ...done.
|
|
ir_ok0:mov al,[_bh] ; Get head number.
|
|
mov ah,[_cl] ; Get drive number.
|
|
and ax,010Fh ; Mask out extra bits.
|
|
shl ah,04h ; Adjust AH.
|
|
or al,ah ; Combine data.
|
|
or al,0A0h ; Set 512 bytes + ECC.
|
|
mov dx,01F6h ; Write drive/head numbers...
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc ir_ok1 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp ir_err ; ...done.
|
|
ir_ok1:mov ecx,000C0000h ; 3s delay.
|
|
mov dx,01F7h ; HDC status register.
|
|
ir_l1: in al,dx ; Read status.
|
|
test al,40h ; Drive ready?
|
|
jnz ir_ok2 ; Continue if so.
|
|
loopd ir_l1 ; Loop for 3s.
|
|
mov al,02h ; Error 2: Drive not ready...
|
|
mov [_al],al ; .
|
|
jmp ir_err ; ...done.
|
|
ir_ok2:test al,10h ; Drive ready for read?
|
|
jnz ir_ok3 ; Continue if so.
|
|
loopd ir_l1 ; Loop for 3s.
|
|
mov al,03h ; Error 3: Cannot read data...
|
|
mov [_al],al ; .
|
|
jmp ir_err ; ...done.
|
|
ir_ok3:mov al,10h ; Set to >8 heads...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
mov dx,01F2h ; Write read parameters...
|
|
mov al,[_ch] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_bl] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_al] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_ah] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_bh] ; .
|
|
mov ah,[_cl] ; .
|
|
and ax,010Fh ; .
|
|
shl ah,04h ; .
|
|
or al,ah ; .
|
|
or al,0A0h ; .
|
|
out dx,al ; ...done.
|
|
mov dx,01F1h ; Write Precompensation = 0...
|
|
xor al,al ; .
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc ir_ok4 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp ir_err ; ...done.
|
|
ir_ok4:xor cx,cx ; Get sector count...
|
|
mov cl,[_ch] ; ...done.
|
|
push ds ; Put DS in ES...
|
|
pop es ; ...done.
|
|
mov di,[_dx] ; Get offset.
|
|
mov dx,01F7h ; Send read command...
|
|
mov al,20h ; .
|
|
out dx,al ; ...done.
|
|
ir_l2: mov dx,01F7h ; Get status port.
|
|
delay 000Ah ; Delay for >400ns.
|
|
in al,dx ; Get status.
|
|
test al,80h ; Busy?
|
|
jnz ir_l2 ; Loop if so.
|
|
test al,29h ; Loop if no change...
|
|
jz ir_l2 ; ...done.
|
|
test al,08h ; Ready for data?
|
|
jnz ir_rda ; If so, read it.
|
|
test al,21h ; Error in command?
|
|
jnz ir_dev ; If so, return device error.
|
|
jmp ir_l2 ; Continue loop.
|
|
ir_rda:push cx ; Save CX.
|
|
mov cx,0100h ; Repeat count.
|
|
mov dx,01F0h ; 16-bit transfer port.
|
|
rep insw ; Read data.
|
|
pop cx ; Restore CX.
|
|
loop ir_l2 ; Loop until done.
|
|
mov al,12h ; Deactivate controller...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
mov al,00h ; No error - return 0...
|
|
mov [_al],al ; ...done.
|
|
pop ax ; Reset PIC 2 mask...
|
|
out 0A1h,al ; ...done.
|
|
clc ; No error: CF=0.
|
|
pop es ds ; Restore segments.
|
|
popad ; Restore all registers.
|
|
ret ; Return (far).
|
|
ir_dev:mov al,04h ; Error 4: Device fault...
|
|
mov [_al],al ; ...done.
|
|
mov dx,01F1h ; Get error code...
|
|
in al,dx ; .
|
|
mov [_ah],al ; ...done.
|
|
mov dx,01F6h ; Recalibrate head...
|
|
mov al,[_bh] ; .
|
|
mov ah,[_cl] ; .
|
|
and ax,010Fh ; .
|
|
shl ah,04h ; .
|
|
or al,ah ; .
|
|
or al,0A0h ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,10h ; .
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
mov al,12h ; Deactivate controller...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
ir_err:pop ax ; Reset PIC 2 mask...
|
|
out 0A1h,al ; ...done.
|
|
stc ; Error: CF=1.
|
|
pop es ds ; Restore segments.
|
|
popad ; Restore all registers.
|
|
ret ; Return (far).
|
|
ioread endp ; End of IOREAD procedure.
|
|
|
|
; ------------------------------------------ ;
|
|
; IOWRITE - Write sectors through direct I/O.;
|
|
; Input.: AX=cylinder number ;
|
|
; BH=head number; BL=sector number ;
|
|
; CH=# of sectors; CL=drive (0 or 1) ;
|
|
; DS:DX->buffer for sectors ;
|
|
; Output: CF=0 (OK) or 1 (Error) ;
|
|
; AL=error code or 0 if no error. ;
|
|
; AH=device error code if applicable.;
|
|
; Errors: AL=0 - No error. ;
|
|
; 1 - Controller busy or absent. ;
|
|
; 2 - Drive not ready. ;
|
|
; 3 - Drive not ready for write. ;
|
|
; 4 - Device error: ;
|
|
; AH, bit 0: Address mark not found. ;
|
|
; 1: Track 0 not found. ;
|
|
; 2: Write fault. ;
|
|
; 4: Sector not found. ;
|
|
; 6: Error in data. ;
|
|
; 7: Sector marked bad. ;
|
|
; Note: There may be errors if a write is ;
|
|
; across a cylinder (or sometimes even ;
|
|
; a head) boundary. ;
|
|
; ------------------------------------------ ;
|
|
iowrite proc far ; Write sectors through direct I/O.
|
|
pushad ; Save all registers.
|
|
mov bp,sp ; Address the stack.
|
|
push ds es ; Save segments.
|
|
in al,0A1h ; Get PIC 2 mask.
|
|
push ax ; Save it.
|
|
or al,40h ; Disable IRQ 14.
|
|
out 0A1h,al ; Set PIC 2 mask.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc iw_ok0 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp iw_err ; ...done.
|
|
iw_ok0:mov al,[_bh] ; Get head number.
|
|
mov ah,[_cl] ; Get drive number.
|
|
and ax,010Fh ; Mask out extra bits.
|
|
shl ah,04h ; Adjust AH.
|
|
or al,ah ; Combine data.
|
|
or al,0A0h ; Set 512 bytes + ECC.
|
|
mov dx,01F6h ; Write drive/head numbers...
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc iw_ok1 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp iw_err ; ...done.
|
|
iw_ok1:mov ecx,000C0000h ; 3s delay.
|
|
mov dx,01F7h ; HDC status register.
|
|
iw_l1: in al,dx ; Read status.
|
|
test al,40h ; Drive ready?
|
|
jnz iw_ok2 ; Continue if so.
|
|
loopd iw_l1 ; Loop for 3s.
|
|
mov al,02h ; Error 2: Drive not ready...
|
|
mov [_al],al ; .
|
|
jmp iw_err ; ...done.
|
|
iw_ok2:test al,10h ; Drive ready for write?
|
|
jnz iw_ok3 ; Continue if so.
|
|
loopd iw_l1 ; Loop for 3s.
|
|
mov al,03h ; Error 3: Cannot write...
|
|
mov [_al],al ; .
|
|
jmp iw_err ; ...done.
|
|
iw_ok3:mov al,10h ; Set to >8 heads...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
mov dx,01F2h ; Write write parameters...
|
|
mov al,[_ch] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_bl] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_al] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_ah] ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,[_bh] ; .
|
|
mov ah,[_cl] ; .
|
|
and ax,010Fh ; .
|
|
shl ah,04h ; .
|
|
or al,ah ; .
|
|
or al,0A0h ; .
|
|
out dx,al ; ...done.
|
|
mov dx,01F1h ; Write Precompensation = 0...
|
|
xor al,al ; .
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
jnc iw_ok4 ; Continue if not busy.
|
|
mov al,01h ; Error 1: Controller busy...
|
|
mov [_al],al ; .
|
|
jmp iw_err ; ...done.
|
|
iw_ok4:xor cx,cx ; Get sector count...
|
|
mov cl,[_ch] ; ...done.
|
|
mov si,[_dx] ; Get offset.
|
|
mov dx,01F7h ; Send write command...
|
|
mov al,30h ; .
|
|
out dx,al ; ...done.
|
|
iw_l2: mov dx,01F7h ; Get status port.
|
|
delay 000Ah ; Delay for >400ns.
|
|
in al,dx ; Get status.
|
|
test al,80h ; Busy?
|
|
jnz iw_l2 ; Loop if so.
|
|
test al,29h ; Loop if no change...
|
|
jz iw_l2 ; ...done.
|
|
test al,08h ; Ready for data?
|
|
jnz iw_wda ; If so, write it.
|
|
test al,21h ; Error in command?
|
|
jnz iw_dev ; If so, return device error.
|
|
jmp iw_l2 ; Continue loop.
|
|
iw_wda:push cx ; Save CX.
|
|
mov cx,0100h ; Repeat count.
|
|
mov dx,01F0h ; 16-bit transfer port.
|
|
rep outsw ; Write data.
|
|
pop cx ; Restore CX.
|
|
loop iw_l2 ; Loop until done.
|
|
mov al,12h ; Deactivate controller...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
mov al,00h ; No error - return 0...
|
|
mov [_al],al ; ...done.
|
|
pop ax ; Reset PIC 2 mask...
|
|
out 0A1h,al ; ...done.
|
|
clc ; No error: CF=0.
|
|
pop es ds ; Restore segments.
|
|
popad ; Restore all registers.
|
|
ret ; Return (far).
|
|
iw_dev:mov al,04h ; Error 4: Device fault...
|
|
mov [_al],al ; ...done.
|
|
mov dx,01F1h ; Get error code...
|
|
in al,dx ; .
|
|
mov [_ah],al ; ...done.
|
|
mov dx,01F6h ; Recalibrate head...
|
|
mov al,[_bh] ; .
|
|
mov ah,[_cl] ; .
|
|
and ax,010Fh ; .
|
|
shl ah,04h ; .
|
|
or al,ah ; .
|
|
or al,0A0h ; .
|
|
out dx,al ; .
|
|
inc dx ; .
|
|
mov al,10h ; .
|
|
out dx,al ; ...done.
|
|
hdcwait ; Wait for HDC not busy.
|
|
mov al,12h ; Deactivate controller...
|
|
mov dx,03F6h ; .
|
|
out dx,ax ; ...done.
|
|
iw_err:pop ax ; Reset PIC 2 mask...
|
|
out 0A1h,al ; ...done.
|
|
stc ; Error: CF=1.
|
|
pop es ds ; Restore segments.
|
|
popad ; Restore all registers.
|
|
ret ; Return (far).
|
|
iowrite endp ; End of IOWRITE procedure.
|
|
|
|
; Clean up segment.
|
|
assume nothing ; Remove segment assumptions.
|
|
idehdc ends ; End of direct I/O segment.
|
|
; ========================================================================== ;
|