;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision $


; Low-level driver for HDD access
; DMA support by Mario79
; Access through BIOS by diamond

align 4
hd_read:
;-----------------------------------------------------------
; input  : eax = block to read
;          ebx = destination
;-----------------------------------------------------------
        and     [hd_error], 0
        push    ecx esi edi     ; scan cache

;    mov   ecx,cache_max         ; entries in cache
;    mov   esi,HD_CACHE+8
        call    calculate_cache
        add     esi, 8

        mov     edi, 1

  hdreadcache:

        cmp     dword [esi+4], 0; empty
        je      nohdcache

        cmp     [esi], eax      ; correct sector
        je      yeshdcache

  nohdcache:

        add     esi, 8
        inc     edi
        dec     ecx
        jnz     hdreadcache

        call    find_empty_slot ; ret in edi
        cmp     [hd_error], 0
        jne     return_01
; Read through BIOS?
        cmp     [hdpos], 0x80
        jae     .bios
; hd_read_{dma,pio} use old ATA with 28 bit for sector number
        cmp     eax, 0x10000000
        jb      @f
        inc     [hd_error]
        jmp     return_01
@@:
; DMA read is permitted if [allow_dma_access]=1 or 2
        cmp     [allow_dma_access], 2
        ja      .nodma
        cmp     [dma_hdd], 1
        jnz     .nodma
        call    hd_read_dma
        jmp     @f
.nodma:
        call    hd_read_pio
        jmp     @f
.bios:
        call    bd_read
@@:
        cmp     [hd_error], 0
        jne     return_01
;    lea   esi,[edi*8+HD_CACHE]
;    push  eax
        call    calculate_cache_1
        lea     esi, [edi*8+esi]
;    pop   eax

        mov     [esi], eax      ; sector number
        mov     dword [esi+4], 1; hd read - mark as same as in hd

  yeshdcache:

        mov     esi, edi
        shl     esi, 9
;    add   esi,HD_CACHE+65536
        push    eax
        call    calculate_cache_2
        add     esi, eax
        pop     eax

        mov     edi, ebx
        mov     ecx, 512/4
        cld
        rep movsd               ; move data
 return_01:
        pop     edi esi ecx
        ret

align 4
hd_read_pio:
        push    eax edx

        call    wait_for_hd_idle
        cmp     [hd_error], 0
        jne     hd_read_error

        cli
        xor     eax, eax
        mov     edx, [hdbase]
        inc     edx
        out     dx, al; ATAFeatures ॣ���� "�ᮡ�����⥩"
        inc     edx
        inc     eax
        out     dx, al; ATASectorCount ����稪 ᥪ�஢
        inc     edx
        mov     eax, [esp+4]
        out     dx, al; ATASectorNumber ॣ���� ����� ᥪ��
        shr     eax, 8
        inc     edx
        out     dx, al; ATACylinder ����� 樫���� (����訩 ����)
        shr     eax, 8
        inc     edx
        out     dx, al; ����� 樫���� (���訩 ����)
        shr     eax, 8
        inc     edx
        and     al, 1+2+4+8
        add     al, byte [hdid]
        add     al, 128+64+32
        out     dx, al; ����� �������/����� ��᪠
        inc     edx
        mov     al, 20h
        out     dx, al; ATACommand ॣ���� ������
        sti

        call    wait_for_sector_buffer

        cmp     [hd_error], 0
        jne     hd_read_error

        cli
        push    edi
        shl     edi, 9
;    add   edi,HD_CACHE+65536
        push    eax
        call    calculate_cache_2
        add     edi, eax
        pop     eax

        mov     ecx, 256
        mov     edx, [hdbase]
        cld
        rep insw
        pop     edi
        sti

        pop     edx eax
        ret

disable_ide_int:
;        mov edx,[hdbase]
;        add edx,0x206
;        mov al,2
;        out dx,al
        cli
        ret

enable_ide_int:
;        mov edx,[hdbase]
;        add edx,0x206
;        mov al,0
;        out dx,al
        sti
        ret

align 4
hd_write:
;-----------------------------------------------------------
; input  : eax = block
;          ebx = pointer to memory
;-----------------------------------------------------------
        push    ecx esi edi

    ; check if the cache already has the sector and overwrite it

;    mov   ecx,cache_max
;    mov   esi,HD_CACHE+8
        call    calculate_cache
        add     esi, 8

        mov     edi, 1

  hdwritecache:

        cmp     dword [esi+4], 0; if cache slot is empty
        je      not_in_cache_write

        cmp     [esi], eax      ; if the slot has the sector
        je      yes_in_cache_write

  not_in_cache_write:

        add     esi, 8
        inc     edi
        dec     ecx
        jnz     hdwritecache

    ; sector not found in cache
    ; write the block to a new location

        call    find_empty_slot ; ret in edi
        cmp     [hd_error], 0
        jne     hd_write_access_denied

;    lea   esi,[edi*8+HD_CACHE]
;    push  eax
        call    calculate_cache_1
        lea     esi, [edi*8+esi]
;    pop   eax

        mov     [esi], eax      ; sector number

  yes_in_cache_write:

        mov     dword [esi+4], 2; write - differs from hd

        shl     edi, 9
;    add   edi,HD_CACHE+65536
        push    eax
        call    calculate_cache_2
        add     edi, eax
        pop     eax

        mov     esi, ebx
        mov     ecx, 512/4
        cld
        rep movsd               ; move data
 hd_write_access_denied:
        pop     edi esi ecx
        ret

align 4
cache_write_pio:
        cmp     dword[esi], 0x10000000
        jae     .bad
;    call  disable_ide_int

        call    wait_for_hd_idle
        cmp     [hd_error], 0
        jne     hd_write_error

        cli
        xor     eax, eax
        mov     edx, [hdbase]
        inc     edx
        out     dx, al
        inc     edx
        inc     eax
        out     dx, al
        inc     edx
        mov     eax, [esi]      ; eax = sector to write
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        and     al, 1+2+4+8
        add     al, byte [hdid]
        add     al, 128+64+32
        out     dx, al
        inc     edx
        mov     al, 30h
        out     dx, al
        sti

        call    wait_for_sector_buffer

        cmp     [hd_error], 0
        jne     hd_write_error

        push    ecx esi

        cli
        mov     esi, edi
        shl     esi, 9
;    add   esi,HD_CACHE+65536    ; esi = from memory position
        push    eax
        call    calculate_cache_2
        add     esi, eax
        pop     eax

        mov     ecx, 256
        mov     edx, [hdbase]
        cld
        rep outsw
        sti

;    call  enable_ide_int
        pop     esi ecx

        ret
.bad:
        inc     [hd_error]
        ret

save_hd_wait_timeout:

        push    eax
        mov     eax, [timer_ticks]
        add     eax, 300        ; 3 sec timeout
        mov     [hd_wait_timeout], eax
        pop     eax
        ret

align 4
check_hd_wait_timeout:

        push    eax
        mov     eax, [hd_wait_timeout]
        cmp     [timer_ticks], eax
        jg      hd_timeout_error
        pop     eax
        mov     [hd_error], 0
        ret

;iglobal
;  hd_timeout_str   db 'K : FS - HD timeout',0
;  hd_read_str      db 'K : FS - HD read error',0
;  hd_write_str     db 'K : FS - HD write error',0
;  hd_lba_str       db 'K : FS - HD LBA error',0
;endg

hd_timeout_error:

;    call  clear_hd_cache
;    call  clear_application_table_status
;    mov   esi,hd_timeout_str
;    call  sys_msg_board_str
    DEBUGF 1,"K : FS - HD timeout\n"

        mov     [hd_error], 1
        pop     eax
        ret

hd_read_error:

;    call  clear_hd_cache
;    call  clear_application_table_status
;    mov   esi,hd_read_str
;    call  sys_msg_board_str
    DEBUGF 1,"K : FS - HD read error\n"
        pop     edx eax
        ret

hd_write_error:

;    call  clear_hd_cache
;    call  clear_application_table_status
;     mov   esi,hd_write_str
;     call  sys_msg_board_str
    DEBUGF 1,"K : FS - HD write error\n"
        ret

hd_write_error_dma:
;        call    clear_hd_cache
;        call    clear_application_table_status
;        mov     esi, hd_write_str
;        call    sys_msg_board_str
        DEBUGF 1,"K : FS - HD read error\n"
        pop     esi
        ret

hd_lba_error:
;    call  clear_hd_cache
;    call  clear_application_table_status
;    mov   esi,hd_lba_str
;    call  sys_msg_board_str
     DEBUGF 1,"K : FS - HD LBA error\n"
        jmp     LBA_read_ret


align 4
wait_for_hd_idle:

        push    eax edx

        call    save_hd_wait_timeout

        mov     edx, [hdbase]
        add     edx, 0x7

  wfhil1:

        call    check_hd_wait_timeout
        cmp     [hd_error], 0
        jne     @f

        in      al, dx
        test    al, 128
        jnz     wfhil1

 @@:

        pop     edx eax
        ret


align 4
wait_for_sector_buffer:

        push    eax edx

        mov     edx, [hdbase]
        add     edx, 0x7

        call    save_hd_wait_timeout

  hdwait_sbuf:                  ; wait for sector buffer to be ready

        call    check_hd_wait_timeout
        cmp     [hd_error], 0
        jne     @f

        in      al, dx
        test    al, 8
        jz      hdwait_sbuf

        mov     [hd_error], 0

        cmp     [hd_setup], 1   ; do not mark error for setup request
        je      buf_wait_ok

        test    al, 1           ; previous command ended up with an error
        jz      buf_wait_ok
 @@:
        mov     [hd_error], 1

  buf_wait_ok:

        pop     edx eax
        ret

; \begin{Mario79}
align 4
wait_for_sector_dma_ide0:
        push    eax
        push    edx
        call    save_hd_wait_timeout
.wait:
        call    change_task
        cmp     [irq14_func], hdd_irq14
        jnz     .done
        call    check_hd_wait_timeout
        cmp     [hd_error], 0
        jz      .wait
        mov     [irq14_func], hdd_irq_null
        mov     dx, [IDEContrRegsBaseAddr]
        mov     al, 0
        out     dx, al
.done:
        pop     edx
        pop     eax
        ret

align 4
wait_for_sector_dma_ide1:
        push    eax
        push    edx
        call    save_hd_wait_timeout
.wait:
        call    change_task
        cmp     [irq15_func], hdd_irq15
        jnz     .done
        call    check_hd_wait_timeout
        cmp     [hd_error], 0
        jz      .wait
        mov     [irq15_func], hdd_irq_null
        mov     dx, [IDEContrRegsBaseAddr]
        add     dx, 8
        mov     al, 0
        out     dx, al
.done:
        pop     edx
        pop     eax
        ret

iglobal
align 4
; note that IDE descriptor table must be 4-byte aligned and do not cross 4K boundary
IDE_descriptor_table:
        dd      IDE_DMA
        dw      0x2000
        dw      0x8000

dma_cur_sector  dd      not 40h
dma_hdpos       dd      0
irq14_func      dd      hdd_irq_null
irq15_func      dd      hdd_irq_null
endg

uglobal
; all uglobals are zeroed at boot
dma_process     dd      0
dma_slot_ptr    dd      0
cache_chain_pos dd      0
cache_chain_ptr dd      0
cache_chain_size        db      0
cache_chain_started     db      0
dma_task_switched       db      0
dma_hdd         db      0
allow_dma_access db      0
endg

align 4
hdd_irq14:
        pushfd
        cli
        pushad
        mov     [irq14_func], hdd_irq_null
        mov     dx, [IDEContrRegsBaseAddr]
        mov     al, 0
        out     dx, al
;        call    update_counters
;        mov     ebx, [dma_process]
;        cmp     [CURRENT_TASK], ebx
;        jz      .noswitch
;        mov     [dma_task_switched], 1
;        mov     edi, [dma_slot_ptr]
;        mov     eax, [CURRENT_TASK]
;        mov     [dma_process], eax
;        mov     eax, [TASK_BASE]
;        mov     [dma_slot_ptr], eax
;        mov     [CURRENT_TASK], ebx
;        mov     [TASK_BASE], edi
;        mov     byte [DONT_SWITCH], 1
;        call    do_change_task
.noswitch:
        popad
        popfd
align 4
hdd_irq_null:
        ret

align 4
hdd_irq15:
        pushfd
        cli
        pushad
        mov     [irq15_func], hdd_irq_null
        mov     dx, [IDEContrRegsBaseAddr]
        add     dx, 8
        mov     al, 0
        out     dx, al
;        call    update_counters
;        mov     ebx, [dma_process]
;        cmp     [CURRENT_TASK], ebx
;        jz      .noswitch
;        mov     [dma_task_switched], 1
;        mov     edi, [dma_slot_ptr]
;        mov     eax, [CURRENT_TASK]
;        mov     [dma_process], eax
;        mov     eax, [TASK_BASE]
;        mov     [dma_slot_ptr], eax
;        mov     [CURRENT_TASK], ebx
;        mov     [TASK_BASE], edi
;        mov     byte [DONT_SWITCH], 1
;        call    do_change_task
.noswitch:
        popad
        popfd
        ret

align 4
hd_read_dma:
        push    eax
        push    edx
        mov     edx, [dma_hdpos]
        cmp     edx, [hdpos]
        jne     .notread
        mov     edx, [dma_cur_sector]
        cmp     eax, edx
        jb      .notread
        add     edx, 15
        cmp     [esp+4], edx
        ja      .notread
        mov     eax, [esp+4]
        sub     eax, [dma_cur_sector]
        shl     eax, 9
        add     eax, (OS_BASE+IDE_DMA)
        push    ecx esi edi
        mov     esi, eax
        shl     edi, 9
;        add     edi, HD_CACHE+0x10000
        push    eax
        call    calculate_cache_2
        add     edi, eax
        pop     eax

        mov     ecx, 512/4
        cld
        rep movsd
        pop     edi esi ecx
        pop     edx
        pop     eax
        ret
.notread:
        mov     eax, IDE_descriptor_table
        mov     dword [eax], IDE_DMA
        mov     word [eax+4], 0x2000
        sub     eax, OS_BASE
        mov     dx, [IDEContrRegsBaseAddr]
        cmp     [hdbase], 0x1F0
        jz      @f
        add     edx, 8
@@:
        push    edx
        add     edx, 4
        out     dx, eax
        pop     edx
        mov     al, 0
        out     dx, al
        add     edx, 2
        mov     al, 6
        out     dx, al
        call    wait_for_hd_idle
        cmp     [hd_error], 0
        jnz     hd_read_error
        call    disable_ide_int
        xor     eax, eax
        mov     edx, [hdbase]
        inc     edx
        out     dx, al
        inc     edx
        mov     eax, 10h
        out     dx, al
        inc     edx
        mov     eax, [esp+4]
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        and     al, 0xF
        add     al, byte [hdid]
        add     al, 11100000b
        out     dx, al
        inc     edx
        mov     al, 0xC8
        out     dx, al
        mov     dx, [IDEContrRegsBaseAddr]
        cmp     [hdbase], 0x1F0
        jz      @f
        add     dx, 8
@@:
        mov     al, 9
        out     dx, al
        mov     eax, [CURRENT_TASK]
        mov     [dma_process], eax
        mov     eax, [TASK_BASE]
        mov     [dma_slot_ptr], eax
        cmp     [hdbase], 0x1F0
        jnz     .ide1
        mov     [irq14_func], hdd_irq14
        jmp     @f
.ide1:
        mov     [irq15_func], hdd_irq15
@@:
        call    enable_ide_int
        cmp     [hdbase], 0x1F0
        jnz     .wait_ide1
        call    wait_for_sector_dma_ide0
        jmp     @f
.wait_ide1:
        call    wait_for_sector_dma_ide1
@@:
        cmp     [hd_error], 0
        jnz     hd_read_error
        mov     eax, [hdpos]
        mov     [dma_hdpos], eax
        pop     edx
        pop     eax
        mov     [dma_cur_sector], eax
        jmp     hd_read_dma

align 4
write_cache_sector:
        mov     [cache_chain_size], 1
        mov     [cache_chain_pos], edi
write_cache_chain:
        cmp     [hdpos], 0x80
        jae     bd_write_cache_chain
        mov     eax, [cache_chain_ptr]
        cmp     dword[eax], 0x10000000
        jae     .bad
        push    esi
        mov     eax, IDE_descriptor_table
        mov     edx, eax
        pusha
        mov     esi, [cache_chain_pos]
        shl     esi, 9
        call    calculate_cache_2
        add     esi, eax
        mov     edi, (OS_BASE+IDE_DMA)
        mov     dword [edx], IDE_DMA
        movzx   ecx, [cache_chain_size]
        shl     ecx, 9
        mov     word [edx+4], cx
        shr     ecx, 2
        cld
        rep movsd
        popa
        sub     eax, OS_BASE
        mov     dx, [IDEContrRegsBaseAddr]
        cmp     [hdbase], 0x1F0
        jz      @f
        add     edx, 8
@@:
        push    edx
        add     edx, 4
        out     dx, eax
        pop     edx
        mov     al, 0
        out     dx, al
        add     edx, 2
        mov     al, 6
        out     dx, al
        call    wait_for_hd_idle
        cmp     [hd_error], 0
        jnz     hd_write_error_dma
        call    disable_ide_int
        xor     eax, eax
        mov     edx, [hdbase]
        inc     edx
        out     dx, al
        inc     edx
        mov     al, [cache_chain_size]
        out     dx, al
        inc     edx
        mov     esi, [cache_chain_ptr]
        mov     eax, [esi]
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        out     dx, al
        shr     eax, 8
        inc     edx
        and     al, 0xF
        add     al, byte [hdid]
        add     al, 11100000b
        out     dx, al
        inc     edx
        mov     al, 0xCA
        out     dx, al
        mov     dx, [IDEContrRegsBaseAddr]
        cmp     [hdbase], 0x1F0
        jz      @f
        add     dx, 8
@@:
        mov     al, 1
        out     dx, al
        mov     eax, [CURRENT_TASK]
        mov     [dma_process], eax
        mov     eax, [TASK_BASE]
        mov     [dma_slot_ptr], eax
        cmp     [hdbase], 0x1F0
        jnz     .ide1
        mov     [irq14_func], hdd_irq14
        jmp     @f
.ide1:
        mov     [irq15_func], hdd_irq15
@@:
        call    enable_ide_int
        mov     [dma_cur_sector], not 0x40
        cmp     [hdbase], 0x1F0
        jnz     .wait_ide1
        call    wait_for_sector_dma_ide0
        jmp     @f
.wait_ide1:
        call    wait_for_sector_dma_ide1
@@:
        cmp     [hd_error], 0
        jnz     hd_write_error_dma
        pop     esi
        ret
.bad:
        inc     [hd_error]
        ret

uglobal
IDEContrRegsBaseAddr    dw      ?
endg
; \end{Mario79}

; \begin{diamond}
uglobal
bios_hdpos      dd      0       ; 0 is invalid value for [hdpos]
bios_cur_sector dd      ?
bios_read_len   dd      ?
endg
bd_read:
        push    eax
        push    edx
        mov     edx, [bios_hdpos]
        cmp     edx, [hdpos]
        jne     .notread
        mov     edx, [bios_cur_sector]
        cmp     eax, edx
        jb      .notread
        add     edx, [bios_read_len]
        dec     edx
        cmp     eax, edx
        ja      .notread
        sub     eax, [bios_cur_sector]
        shl     eax, 9
        add     eax, (OS_BASE+0x9A000)
        push    ecx esi edi
        mov     esi, eax
        shl     edi, 9
;        add     edi, HD_CACHE+0x10000
        push    eax
        call    calculate_cache_2
        add     edi, eax
        pop     eax

        mov     ecx, 512/4
        cld
        rep movsd
        pop     edi esi ecx
        pop     edx
        pop     eax
        ret
.notread:
        push    ecx
        mov     dl, 42h
        mov     ecx, 16
        call    int13_call
        pop     ecx
        test    eax, eax
        jnz     .v86err
        test    edx, edx
        jz      .readerr
        mov     [bios_read_len], edx
        mov     edx, [hdpos]
        mov     [bios_hdpos], edx
        pop     edx
        pop     eax
        mov     [bios_cur_sector], eax
        jmp     bd_read
.readerr:
.v86err:
        mov     [hd_error], 1
        jmp     hd_read_error

bd_write_cache_chain:
        pusha
        mov     esi, [cache_chain_pos]
        shl     esi, 9
        call    calculate_cache_2
        add     esi, eax
        mov     edi, OS_BASE + 0x9A000
        movzx   ecx, [cache_chain_size]
        push    ecx
        shl     ecx, 9-2
        rep movsd
        pop     ecx
        mov     dl, 43h
        mov     eax, [cache_chain_ptr]
        mov     eax, [eax]
        call    int13_call
        test    eax, eax
        jnz     .v86err
        cmp     edx, ecx
        jnz     .writeerr
        popa
        ret
.v86err:
.writeerr:
        popa
        mov     [hd_error], 1
        jmp     hd_write_error

uglobal
int13_regs_in   rb      sizeof.v86_regs
int13_regs_out  rb      sizeof.v86_regs
endg

int13_call:
; Because this code uses fixed addresses,
; it can not be run simultaniously by many threads.
; In current implementation it is protected by common mutex 'hd1_status'
        mov     word [OS_BASE + 510h], 10h             ; packet length
        mov     word [OS_BASE + 512h], cx              ; number of sectors
        mov     dword [OS_BASE + 514h], 9A000000h      ; buffer 9A00:0000
        mov     dword [OS_BASE + 518h], eax
        and     dword [OS_BASE + 51Ch], 0
        push    ebx ecx esi edi
        mov     ebx, int13_regs_in
        mov     edi, ebx
        mov     ecx, sizeof.v86_regs/4
        xor     eax, eax
        rep stosd
        mov     byte [ebx+v86_regs.eax+1], dl
        mov     eax, [hdpos]
        lea     eax, [BiosDisksData+(eax-80h)*4]
        mov     dl, [eax]
        mov     byte [ebx+v86_regs.edx], dl
        movzx   edx, byte [eax+1]
;        mov     dl, 5
        test    edx, edx
        jnz     .hasirq
        dec     edx
        jmp     @f
.hasirq:
        pushad
        stdcall enable_irq, edx
        popad
@@:
        mov     word [ebx+v86_regs.esi], 510h
        mov     word [ebx+v86_regs.ss], 9000h
        mov     word [ebx+v86_regs.esp], 0A000h
        mov     word [ebx+v86_regs.eip], 500h
        mov     [ebx+v86_regs.eflags], 20200h
        mov     esi, [sys_v86_machine]
        mov     ecx, 0x502
        push    fs
        call    v86_start
        pop     fs
        and     [bios_hdpos], 0
        pop     edi esi ecx ebx
        movzx   edx, byte [OS_BASE + 512h]
        test    byte [int13_regs_out+v86_regs.eflags], 1
        jnz     @f
        mov     edx, ecx
@@:
        ret
; \end{diamond}

reserve_hd1:

        cli
        cmp     [hd1_status], 0
        je      reserve_ok1

        sti
        call    change_task
        jmp     reserve_hd1

  reserve_ok1:

        push    eax
        mov     eax, [CURRENT_TASK]
        shl     eax, 5
        mov     eax, [eax+CURRENT_TASK+TASKDATA.pid]
        mov     [hd1_status], eax
        pop     eax
        sti
        ret
;********************************************

uglobal
hd_in_cache db ?
endg

reserve_hd_channel:
; BIOS disk accesses are protected with common mutex hd1_status
; This must be modified when hd1_status will not be valid!
        cmp     [hdpos], 0x80
        jae     .ret
        cmp     [hdbase], 0x1F0
        jne     .IDE_Channel_2
.IDE_Channel_1:
        cli
        cmp     [IDE_Channel_1], 0
        je      .reserve_ok_1
        sti
        call    change_task
        jmp     .IDE_Channel_1
.IDE_Channel_2:
        cli
        cmp     [IDE_Channel_2], 0
        je      .reserve_ok_2
        sti
        call    change_task
        jmp     .IDE_Channel_2
.reserve_ok_1:
        mov     [IDE_Channel_1], 1
        push    eax
        mov     al, 1
        jmp     @f
.reserve_ok_2:
        mov     [IDE_Channel_2], 1
        push    eax
        mov     al, 3
@@:
        cmp     [hdid], 1
        sbb     al, -1
        mov     [hd_in_cache], al
        pop     eax
        sti
.ret:
        ret

free_hd_channel:
; see comment at reserve_hd_channel
        cmp     [hdpos], 0x80
        jae     .ret
        cmp     [hdbase], 0x1F0
        jne     .IDE_Channel_2
.IDE_Channel_1:
        mov     [IDE_Channel_1], 0
.ret:
        ret
.IDE_Channel_2:
        mov     [IDE_Channel_2], 0
        ret
;********************************************