;----------------------------------------------------------;
; BOS kernel Christoffer Bubach, 2004-2007. ;
;----------------------------------------------------------;
; ;
; floppy disk driver. ;
; ;
;----------------------------------------------------------;
;---------------------------;
; variables and contants ;
;---------------------------;
fdd_int_done dw 0 ; is the IRQ done?
fdd_motor_on db 0 ; fdd motor on=1, off=0
fdd_drivehead db 0 ; ((head*4)|drive)
fdd_head db 0
fdd_track db 0
fdd_sector db 0
fdd_error db 0 ; should we recalibrate
; at next read/write?
fdd_errorcode db 0
result_ST0 db 0
result_ST1 db 0 ; or pcn
result_ST2 db 0
result_ST3 db 0
result_C db 0
result_H db 0
result_R db 0
result_N db 0
result_extra db 0
;------------------------------------------------------;
; initializes the floppy disk driver ;
; ;
;------------------------------------------------------;
fdc_init:
push eax
push ebx
push ecx
push edi
push esi
xor eax, eax ; get configuration
mov al, 0x10 ; from CMOS.
out 0x70, al
in al, 0x71
shr al, 4
cmp al, 4 ; a 1.44mb?
jnz .no_floppy
mov cl, 0x26 ; hook IRQ 6
mov dx, 0x8 ; CS = 8
mov edi, floppy_irq
call set_int
mov cl, 6 ; enable IRQ6
call enable_irq
call fdd_reset ; reset FDC
.no_floppy:
pop esi
pop edi
pop ecx
pop ebx
pop eax
ret
;------------------------------------------------------;
; floppy IRQ ;
; ;
;------------------------------------------------------;
floppy_irq:
push ds
push eax
push ebx
mov ax, 0x10
mov ds, ax
mov [fdd_int_done], 1
mov al, 0x20
out 0x20, al
pop ebx
pop eax
pop ds
iret
;------------------------------------------------------;
; wait for a floppy int ;
; out: cf = 1 if timeout ;
;------------------------------------------------------;
wait_int:
push eax
push ecx
mov ecx, 150 ; 50 = 0.5 seconds.
call active_delay ; timer.inc
.l1:
cmp dword [ecx], 0 ; timeup?
je .error
mov ax, [fdd_int_done] ; if not we check for int.
or ax, ax
jz .l1
clc
jmp .end
.error:
stc
.end:
pop ecx
pop eax
ret
;------------------------------------------------------;
; fdd motor off ;
; out: nothing ;
;------------------------------------------------------;
fdd_off:
cmp [fdd_motor_on], 0
je .end
push eax
push edx
mov dx, 0x3F2
mov al, 0x0c ; motor off
out dx, al
mov [fdd_motor_on], 0
pop edx
pop eax
.end:
ret
;------------------------------------------------------;
; fdd motor on ;
; out: nothing ;
;------------------------------------------------------;
fdd_on:
cmp [fdd_motor_on], 1
je .end
push eax
push edx
mov dx, 0x3F2 ; motor 0 on..
mov al, 0x1C
out dx, al
mov ecx, 20 ; 1/5 of a sec. to speed up
call delay ; in timer.inc
mov [fdd_motor_on], 1
pop edx
pop eax
.end:
ret
;------------------------------------------------------;
; send a data byte to the FDC ;
; ;
; in: al = data byte ;
;------------------------------------------------------;
fdc_sendbyte:
push edx
push ecx
push eax
mov ecx, 50 ; 50 = 0.5 seconds.
call active_delay ; timer.inc
.l1:
cmp dword [ecx], 0 ; timeup?
je .error
mov dx, 0x3f4 ; check status reg
in al, dx
and al, 0xC0
cmp al, 0x80 ; ok to write?
jnz .l1
pop eax
pop ecx
mov dx, 0x3F5 ; send byte
out dx, al
pop edx
clc
ret
.error:
pop eax
pop ecx
pop edx
stc
ret
;------------------------------------------------------;
; read a data byte from the FDC ;
; ;
; out: al = data byte ;
;------------------------------------------------------;
fdc_getbyte:
push edx
push ecx
push eax
mov ecx, 50 ; 50 = 0.5 seconds.
call active_delay ; timer.inc
.l1:
cmp dword [ecx], 0 ; timeup?
je .error
mov dx, 0x3f4 ; check status reg
in al, dx
and al, 0xD0
cmp al, 0xD0 ; ok to read?
jnz .l1
pop eax
pop ecx
mov dx, 0x3F5 ; get the byte
in al, dx
pop edx
clc
ret
.error:
pop eax
pop ecx
pop edx
stc
ret
;------------------------------------------------------;
; sense interrupt status command ;
; ;
;------------------------------------------------------;
sensei:
push eax
mov al, 0x08 ; fdc command
call fdc_sendbyte
call fdc_getbyte
mov ah, al ; save ST0 in ah
call fdc_getbyte ; read PCN
clc
test ah, 0x80 ; test for error:
jz .end ; "invalid command"
stc
.end:
pop eax
ret
;------------------------------------------------------;
; reset controller ;
; ;
;------------------------------------------------------;
fdd_reset:
push eax
push ecx
push edx
mov byte [fdd_motor_on], 0
mov dx, 0x3f2
mov al, 8 ; off with all motors,
out dx, al ; dma,irq etc..
mov ecx, 5
call delay ; in timer.inc
mov dx, 0x3f7
mov al, 0
out dx, al ; work at 500 kbit/s
mov dx, 0x3f2
mov al, 0x0c
out dx, al ; reenable interrupts
mov [fdd_int_done], 0
call wait_int ; wait for floppy int.
jc .error ; timeout?
mov cx, 0x04
.status: ; 4 dummy-reads.
call sensei
loop .status
mov al, 0x03 ; specify command
call fdc_sendbyte
mov al, 0xDF ; SRT, HUT
call fdc_sendbyte
mov al, 0x02 ; HLT, ND
call fdc_sendbyte
mov al, 1
call fdd_recal_seek
jc .error
call fdd_off
clc
jmp .end
.error:
call fdd_off
stc
.end:
pop edx
pop ecx
pop eax
ret
;------------------------------------------------------;
; fdd_recal_seek - fdd recalibrate/seek ;
;------------------------------------------------------;
; ;
; in: al = 0 on seek, 1 on recalibrate ;
; bl = (at seek) track ;
; bh = (at seek) ((head*4)|drive) ;
; ;
;------------------------------------------------------;
fdd_recal_seek:
push eax
call fdd_on ; turn motor on
cmp al, 0
jne .recalibrate
clc
cmp bl, [result_C] ; are we there yet? :D
je .ok
mov al, 0x0F ; seek command
call fdc_sendbyte
mov al, bh ; ((head*4)|drive)
call fdc_sendbyte
mov al, bl ; track
call fdc_sendbyte
mov [result_C], bl ; now on..?
jmp .get_int
.recalibrate:
mov al, 0x07 ; recalibrate command
call fdc_sendbyte
mov al, 0 ; drive number
call fdc_sendbyte
mov [result_C], 0 ; now on track 0
.get_int:
mov [fdd_int_done], 0
call wait_int
jc .error
call sensei ; sense interrupt status
jc .error
.ok:
clc
jmp .end
.error:
stc
.end:
pop eax
ret
;------------------------------------------------------;
; fdd_read_write - fdd read/write ;
;------------------------------------------------------;
; input: bl = 0 read, 1 write ;
; ch = track/cylinder ;
; cl = sector ;
; dh = head ;
; edi = address to store or read the data ;
; output: al = status ;
; cf = 0 if ok, 1 if error ;
;------------------------------------------------------;
fdd_read_write:
pushad
and dh, 1 ; head 0 or 1?
mov [fdd_head], dh ; store it.
shl dh, 2
or dh, 0 ; drive 0, fd0
mov [fdd_drivehead], dh ; dh = ((head*4)|drive)
mov [fdd_errorcode], 0x04 ; basic error code
cmp ch, 0x51 ; check for allowed
jae .error ; track number.
mov [fdd_track], ch
cmp cl, 0x13 ; check for allowed
jae .error ; sector number.
mov [fdd_sector], cl
cmp [fdd_error], 1
jne .no_previous_error
mov al, 1
call fdd_recal_seek
.no_previous_error:
call fdd_on
mov dx, 0x3F7
mov al, 0 ; 500Kb/sec mode
out dx, al
mov [fdd_errorcode], 0x80 ; error code
xor ecx, ecx
mov cx, 3 ; try seek 3 times
.l2:
mov al, 0
push ebx
mov bl, [fdd_track]
mov bh, [fdd_drivehead] ; ((head*4)|drive)
call fdd_recal_seek
pop ebx
jnc .l3 ; ok, continue.
loop .l2
jmp .error ; timeout.
.l3:
push ebx
cmp bl, 0
je .read_fdd
.write_fdd:
push edi
mov esi, edi
mov edi, 0x80000 ; copy the stuff we will
mov ecx, 128 ; write to the DMA buffer
rep movsd ; 128*4=512
pop edi
mov bl, 2 ; channel 2
mov esi, 512 ; bytes to write
mov ecx, 0x80000 ; page & offset
mov bh, 1 ; write floppy, read DMA
call dma_transfer
mov al, 0xC5 ; write sector command
call fdc_sendbyte
jmp .cont
.read_fdd:
mov bl, 2 ; channel 2
mov esi, 512 ; bytes to read
mov ecx, 0x80000 ; page & offset
mov bh, 0 ; read floppy, write DMA
call dma_transfer
mov al, 0xE6 ; read sector command
call fdc_sendbyte
.cont:
pop ebx
mov al, [fdd_drivehead] ; ((head*4)|drive)
call fdc_sendbyte
mov al, [fdd_track] ; track/cylinder
call fdc_sendbyte
mov al, [fdd_head] ; head/side 0 or 1
call fdc_sendbyte
mov al, [fdd_sector] ; sector number
call fdc_sendbyte
mov al, 0x02 ; sector size, 512 bytes
call fdc_sendbyte
mov al, 0x12 ; sectors to a track
call fdc_sendbyte
mov al, 0x1B ; gap length
call fdc_sendbyte
mov al, 0xFF ; data length
call fdc_sendbyte
mov [fdd_int_done], 0
call wait_int
jc .error
call fdc_getbyte
mov [result_ST0], al ; ST0
call fdc_getbyte
mov [result_ST1], al ; ST1
call fdc_getbyte
mov [result_ST2], al ; ST2
call fdc_getbyte
mov [result_C], al ; cylinder
call fdc_getbyte
mov [result_H], al ; head
call fdc_getbyte
mov [result_R], al ; sector number.
call fdc_getbyte
mov [result_N], al ; sector size
test [result_ST0], 0xc0 ; test if sr0 is 0xC0
jnz .error
cmp bl, 1 ; did we write the disk?
je .ok
mov ecx, 512 ; sector size in bytes
mov esi, 0x80000 ; copy to dest. in edi
rep movsb
.ok:
mov [fdd_errorcode], 0 ; no error.. :D
mov [fdd_error], 0 ; no recal. next time
clc
jmp .end
.error:
mov [fdd_error], 1 ; recalibrate next time
stc
.end:
call fdd_off ; floppy motor off
popad
mov al, [fdd_errorcode] ; error status
ret