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

$Revision$


;**********************************************************
;  Непосредственная работа с устройством СD (ATAPI)
;**********************************************************
; Автор части исходного текста Кулаков Владимир Геннадьевич
; Адаптация, доработка и разработка Mario79,<Lrz>

; Максимальное количество повторений операции чтения
MaxRetr equ 10
; Предельное время ожидания готовности к приему команды
; (в тиках)
BSYWaitTime equ 1000  ;2
NoTickWaitTime equ 0xfffff
CDBlockSize equ 2048
;********************************************
;*        ЧТЕНИЕ СЕКТОРА С ПОВТОРАМИ        *
;* Многократное повторение чтения при сбоях *
;********************************************
ReadCDWRetr:
;-----------------------------------------------------------
; input  : eax = block to read
;          ebx = destination
;-----------------------------------------------------------
    pushad
        mov     eax, [CDSectorAddress]
        mov     ebx, [CDDataBuf_pointer]
    call  cd_calculate_cache
        xor     edi, edi
        add     esi, 8
    inc   edi
.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_CD_cache       ; ret in edi

    push  edi
    push  eax
    call  cd_calculate_cache_2
        shl     edi, 11
        add     edi, eax
        mov     [CDDataBuf_pointer], edi
    pop   eax
    pop   edi

    call  ReadCDWRetr_1
        cmp     [DevErrorCode], 0
    jne   .exit

        mov     [CDDataBuf_pointer], ebx
    call  cd_calculate_cache_1
        lea     esi, [edi*8+esi]
        mov     [esi], eax      ; sector number
;    mov   dword [esi+4],1       ; hd read - mark as same as in hd
.yeshdcache:
        mov     esi, edi
        shl     esi, 11;9
    push  eax
    call  cd_calculate_cache_2
        add     esi, eax
    pop   eax
        mov     edi, ebx;[CDDataBuf_pointer]
        mov     ecx, 512;/4
    cld
    rep   movsd                 ; move data
.exit:
    popad
    ret

ReadCDWRetr_1:
        pushad

; Цикл, пока команда не выполнена успешно или не
; исчерпано количество попыток
        mov     ECX, MaxRetr
@@NextRetr:
; Подать команду
;*************************************************
;*      ПОЛНОЕ ЧТЕНИЕ СЕКТОРА КОМПАКТ-ДИСКА      *
;* Считываются данные пользователя, информация   *
;* субканала и контрольная информация            *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале;           *
;* CDSectorAddress - адрес считываемого сектора. *
;* Данные считывается в массив CDDataBuf.        *
;*************************************************
;ReadCD:
	push	ecx
;        pusha
; Задать размер сектора
;        mov       [CDBlockSize],2048 ;2352
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Сформировать пакетную команду для считывания
; сектора данных
; Задать код команды Read CD
        mov     [PacketCommand], byte 0x28;0xBE
; Задать адрес сектора
        mov     AX, word [CDSectorAddress+2]
        xchg    AL, AH
        mov     word [PacketCommand+2], AX
        mov     AX, word [CDSectorAddress]
        xchg    AL, AH
        mov     word [PacketCommand+4], AX
;        mov   eax,[CDSectorAddress]
;        mov   [PacketCommand+2],eax
; Задать количество считываемых секторов
        mov     [PacketCommand+8], byte 1
; Задать считывание данных в полном объеме
;        mov     [PacketCommand+9],byte 0xF8
; Подать команду
        call  SendPacketDatCommand
        pop   ecx
;        ret

;        cmp       [DevErrorCode],0
        test    eax, eax
        jz    @@End_4

        or      ecx, ecx        ;{SPraid.simba} (for cd load)
        jz    @@End_4
        dec   ecx

        cmp     [timer_ticks_enable], 0
        jne   @f
        mov     eax, NoTickWaitTime
.wait:
        dec   eax
;        test  eax,eax
        jz    @@NextRetr
        jmp       .wait
@@:
; Задержка на 2,5 секунды
;       mov     EAX,[timer_ticks]
;       add     EAX,50  ;250
;@@Wait:
;       call    change_task
;       cmp     EAX,[timer_ticks]
;       ja      @@Wait
        loop  @@NextRetr
@@End_4:
        mov     dword [DevErrorCode], eax
        popad
        ret


; Универсальные процедуры, обеспечивающие выполнение
;             пакетных команд в режиме PIO

; Максимально допустимое время ожидания реакции
; устройства на пакетную команду (в тиках)

MaxCDWaitTime equ 1000 ;200 ;10 секунд
uglobal
; Область памяти для формирования пакетной команды
PacketCommand:
                 rb 12  ;DB 12 DUP (?)
; Область памяти для приема данных от дисковода
;CDDataBuf       DB 4096 DUP (0)
; Размер принимаемого блока данных в байтах
;CDBlockSize     DW ?
; Адрес считываемого сектора данных
CDSectorAddress:
                 DD ?
; Время начала очередной операции с диском
TickCounter_1 DD 0
; Время начала ожидания готовности устройства
WURStartTime DD 0
; указатель буфера для считывания
CDDataBuf_pointer dd 0
endg
;****************************************************
;*    ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ,    *
;* ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧУ ОДНОГО СЕКТОРА ДАННЫХ *
;*     РАЗМЕРОМ 2048 БАЙТ ОТ УСТРОЙСТВА К ХОСТУ     *
;* Входные параметры передаются через глобальные    *
;* перменные:                                       *
;* ChannelNumber - номер канала;                    *
;* DiskNumber - номер диска на канале;              *
;* PacketCommand - 12-байтный командный пакет;      *
;* CDBlockSize - размер принимаемого блока данных.  *
; return eax DevErrorCode
;****************************************************
SendPacketDatCommand:
        xor     eax, eax
;        mov	byte [DevErrorCode],al
; Задать режим CHS
        mov     byte [ATAAddressMode], al
; Послать ATA-команду передачи пакетной команды
        mov     byte [ATAFeatures], al
        mov     byte [ATASectorCount], al
        mov     byte [ATASectorNumber], al
        ; Загрузить размер передаваемого блока
        mov     [ATAHead], al
;        mov     AX,[CDBlockSize]
        mov     [ATACylinder], CDBlockSize
        mov     [ATACommand], 0A0h
        call    SendCommandToHDD_1
        test    eax, eax
;        cmp     [DevErrorCode],0 ;проверить код ошибки
        jnz     @@End_8    ;закончить, сохранив код ошибки

; Ожидание готовности дисковода к приему
; пакетной команды
        mov     DX, [ATABasePortAddr]
        add     DX, 7    ;порт 1х7h
        mov     ecx, NoTickWaitTime
@@WaitDevice0:
        cmp     [timer_ticks_enable], 0
        jne     @f
        dec     ecx
;        test    ecx,ecx
        jz      @@Err1_1
        jmp     .test
@@:
        call    change_task
        ; Проверить время выполнения команды
        mov     EAX, [timer_ticks]
        sub     EAX, [TickCounter_1]
        cmp     EAX, BSYWaitTime
        ja      @@Err1_1   ;ошибка тайм-аута
        ; Проверить готовность
.test:
        in      AL, DX
        test    AL, 80h  ;состояние сигнала BSY
        jnz     @@WaitDevice0
        test    AL, 1    ;состояние сигнала ERR
        jnz     @@Err6
        test    AL, 08h  ;состояние сигнала DRQ
        jz      @@WaitDevice0
; Послать пакетную команду
        cli
        mov     DX, [ATABasePortAddr]
        mov     AX, [PacketCommand]
        out     DX, AX
        mov     AX, [PacketCommand+2]
        out     DX, AX
        mov     AX, [PacketCommand+4]
        out     DX, AX
        mov     AX, [PacketCommand+6]
        out     DX, AX
        mov     AX, [PacketCommand+8]
        out     DX, AX
        mov     AX, [PacketCommand+10]
        out     DX, AX
        sti
; Ожидание готовности данных
        mov     DX, [ATABasePortAddr]
        add     DX, 7  ;порт 1х7h
        mov     ecx, NoTickWaitTime
@@WaitDevice1:
        cmp     [timer_ticks_enable], 0
        jne     @f
        dec     ecx
;        test    ecx,ecx
        jz      @@Err1_1
        jmp     .test_1
@@:
        call    change_task
        ; Проверить время выполнения команды
        mov     EAX, [timer_ticks]
        sub     EAX, [TickCounter_1]
        cmp     EAX, MaxCDWaitTime
        ja      @@Err1_1   ;ошибка тайм-аута
        ; Проверить готовность
.test_1:
        in      AL, DX
        test    AL, 80h  ;состояние сигнала BSY
        jnz     @@WaitDevice1
        test    AL, 1    ;состояние сигнала ERR
        jnz     @@Err6_temp
        test    AL, 08h  ;состояние сигнала DRQ
        jz      @@WaitDevice1
; Принять блок данных от контроллера
        mov     EDI, [CDDataBuf_pointer];0x7000  ;CDDataBuf
        ; Загрузить адрес регистра данных контроллера
        mov     DX, [ATABasePortAddr];порт 1x0h
        ; Загрузить в счетчик размер блока в байтах
        xor     ecx, ecx
        mov     CX, CDBlockSize
        ; Вычислить размер блока в 16-разрядных словах
        shr     CX, 1;разделить размер блока на 2
        ; Принять блок данных
        cli
        cld
        rep     insw
        sti
; Успешное завершение приема данных
@@End_8:
        xor     eax, eax
        ret

; Записать код ошибки
@@Err1_1:
        xor     eax, eax
	inc	eax
	ret
;        mov     [DevErrorCode],1
;	ret
@@Err6_temp:
        mov     eax, 7
	ret
;        mov     [DevErrorCode],7
;	ret
@@Err6:
        mov     eax, 6
	ret
;        mov     [DevErrorCode],6
;@@End_8:
;        ret



;***********************************************
;*  ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, *
;*     НЕ ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧИ ДАННЫХ    *
;* Входные параметры передаются через          *
;* глобальные перменные:                       *
;* ChannelNumber - номер канала;               *
;* DiskNumber - номер диска на канале;         *
;* PacketCommand - 12-байтный командный пакет. *
;***********************************************
SendPacketNoDatCommand:
        pushad
        xor     eax, eax
;        mov     byte [DevErrorCode],al
; Задать режим CHS
        mov     byte [ATAAddressMode], al
; Послать ATA-команду передачи пакетной команды
        mov     byte [ATAFeatures], al
        mov     byte [ATASectorCount], al
        mov     byte [ATASectorNumber], al
        mov     word [ATACylinder], ax
        mov     byte [ATAHead], al
        mov     [ATACommand], 0A0h
        call    SendCommandToHDD_1
;        cmp     [DevErrorCode],0 ;проверить код ошибки
        test    eax, eax
        jnz     @@End_9  ;закончить, сохранив код ошибки
; Ожидание готовности дисковода к приему
; пакетной команды
        mov     DX, [ATABasePortAddr]
        add     DX, 7  ;порт 1х7h
@@WaitDevice0_1:
        call    change_task
        ; Проверить время ожидания
        mov     EAX, [timer_ticks]
        sub     EAX, [TickCounter_1]
        cmp     EAX, BSYWaitTime
        ja      @@Err1_3   ;ошибка тайм-аута
        ; Проверить готовность
        in      AL, DX
        test    AL, 80h  ;состояние сигнала BSY
        jnz     @@WaitDevice0_1
        test    AL, 1    ;состояние сигнала ERR
        jnz     @@Err6_1
        test    AL, 08h  ;состояние сигнала DRQ
        jz      @@WaitDevice0_1
; Послать пакетную команду
;        cli
        mov     DX, [ATABasePortAddr]
        mov     AX, word [PacketCommand]
        out     DX, AX
        mov     AX, word [PacketCommand+2]
        out     DX, AX
        mov     AX, word [PacketCommand+4]
        out     DX, AX
        mov     AX, word [PacketCommand+6]
        out     DX, AX
        mov     AX, word [PacketCommand+8]
        out     DX, AX
        mov     AX, word [PacketCommand+10]
        out     DX, AX
;        sti
        cmp     [ignore_CD_eject_wait], 1
	je  @@clear_DEC
; Ожидание подтверждения приема команды
        mov     DX, [ATABasePortAddr]
        add     DX, 7  ;порт 1х7h
@@WaitDevice1_1:
        call    change_task
        ; Проверить время выполнения команды
        mov     EAX, [timer_ticks]
        sub     EAX, [TickCounter_1]
        cmp     EAX, MaxCDWaitTime
        ja      @@Err1_3   ;ошибка тайм-аута
        ; Ожидать освобождения устройства
        in      AL, DX
        test    AL, 80h  ;состояние сигнала BSY
        jnz     @@WaitDevice1_1
        test    AL, 1    ;состояние сигнала ERR
        jnz     @@Err6_1
        test    AL, 40h  ;состояние сигнала DRDY
        jz      @@WaitDevice1_1
@@clear_DEC:
        and     [DevErrorCode], 0
        popad
        ret
; Записать код ошибки
@@Err1_3:
        xor     eax, eax
	inc	eax
        jmp @@End_9
@@Err6_1:
        mov     eax, 6
@@End_9:
        mov     [DevErrorCode], eax
        popad
        ret

;****************************************************
;*          ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ         *
;* Входные параметры передаются через глобальные    *
;* переменные:                                      *
;* ChannelNumber - номер канала (1 или 2);          *
;* DiskNumber - номер диска (0 или 1);              *
;* ATAFeatures - "особенности";                     *
;* ATASectorCount - количество секторов;            *
;* ATASectorNumber - номер начального сектора;      *
;* ATACylinder - номер начального цилиндра;         *
;* ATAHead - номер начальной головки;               *
;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); *
;* ATACommand - код команды.                        *
;* После успешного выполнения функции:              *
;* в ATABasePortAddr - базовый адрес HDD;           *
;* в DevErrorCode - ноль.                           *
;* При возникновении ошибки в DevErrorCode будет    *
;* возвращен код ошибки в eax                       *
;****************************************************
SendCommandToHDD_1:
;        pushad
;        mov   	[DevErrorCode],0	not need 
; Проверить значение кода режима
        cmp     [ATAAddressMode], 1
        ja      @@Err2_4
; Проверить корректность номера канала
        mov     BX, [ChannelNumber]
        cmp     BX, 1
        jb      @@Err3_4
        cmp     BX, 2
        ja      @@Err3_4
; Установить базовый адрес
        dec     BX
        shl     BX, 1
        movzx   ebx, bx
        mov     AX, [ebx+StandardATABases]
        mov     [ATABasePortAddr], AX
; Ожидание готовности HDD к приему команды
        ; Выбрать нужный диск
        mov     DX, [ATABasePortAddr]
        add     DX, 6   ;адрес регистра головок
        mov     AL, [DiskNumber]
        cmp     AL, 1   ;проверить номера диска
        ja      @@Err4_4
        shl     AL, 4
        or      AL, 10100000b
        out     DX, AL
        ; Ожидать, пока диск не будет готов
        inc     DX
        mov     eax, [timer_ticks]
        mov     [TickCounter_1], eax
        mov     ecx, NoTickWaitTime
@@WaitHDReady_2:
        cmp     [timer_ticks_enable], 0
        jne    @f
        dec    ecx
;        test   ecx,ecx
        jz     @@Err1_4
        jmp    .test
@@:
        call    change_task
        ; Проверить время ожидания
        mov     eax, [timer_ticks]
        sub     eax, [TickCounter_1]
        cmp     eax, BSYWaitTime;300    ;ожидать 3 сек.
        ja      @@Err1_4   ;ошибка тайм-аута
        ; Прочитать регистр состояния
.test:
        in      AL, DX
        ; Проверить состояние сигнала BSY
        test    AL, 80h
        jnz     @@WaitHDReady_2
        ; Проверить состояние сигнала DRQ
        test    AL, 08h
        jnz     @@WaitHDReady_2

; Загрузить команду в регистры контроллера
        cli
        mov     DX, [ATABasePortAddr]
        inc     DX      ;регистр "особенностей"
        mov     AL, [ATAFeatures]
        out     DX, AL
        inc     DX      ;счетчик секторов
        mov     AL, [ATASectorCount]
        out     DX, AL
        inc     DX      ;регистр номера сектора
        mov     AL, [ATASectorNumber]
        out     DX, AL
        inc     DX      ;номер цилиндра (младший байт)
        mov     AX, [ATACylinder]
        out     DX, AL
        inc     DX      ;номер цилиндра (старший байт)
        mov     AL, AH
        out     DX, AL
        inc     DX      ;номер головки/номер диска
        mov     AL, [DiskNumber]
        shl     AL, 4
        cmp     [ATAHead], 0Fh;проверить номер головки
        ja      @@Err5_4
        or      AL, [ATAHead]
        or      AL, 10100000b
        mov     AH, [ATAAddressMode]
        shl     AH, 6
        or      AL, AH
        out     DX, AL
; Послать команду
        mov     AL, [ATACommand]
        inc     DX      ;регистр команд
        out     DX, AL
        sti
; Сбросить признак ошибки
;        mov     [DevErrorCode],0
@@End_10:
        xor     eax, eax
	ret
; Записать код ошибки
@@Err1_4:
        xor     eax, eax
	inc	eax
;        mov     [DevErrorCode],1
	ret
@@Err2_4:
        mov     eax, 2
;        mov     [DevErrorCode],2
	ret
@@Err3_4:
        mov     eax, 3
;        mov     [DevErrorCode],3
	ret
@@Err4_4:
        mov     eax, 4
;        mov     [DevErrorCode],4
	ret
@@Err5_4:
        mov     eax, 5
;        mov     [DevErrorCode],5
; Завершение работы программы
	ret
;        sti
;        popad

;*************************************************
;*    ОЖИДАНИЕ ГОТОВНОСТИ УСТРОЙСТВА К РАБОТЕ    *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
WaitUnitReady:
        pusha
; Запомнить время начала операции
        mov     EAX, [timer_ticks]
        mov     [WURStartTime], EAX
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Сформировать команду TEST UNIT READY
        mov     [PacketCommand], word 00h
; ЦИКЛ ОЖИДАНИЯ ГОТОВНОСТИ УСТРОЙСТВА
        mov     ecx, NoTickWaitTime
@@SendCommand:
        ; Подать команду проверки готовности
        call    SendPacketNoDatCommand
        cmp     [timer_ticks_enable], 0
        jne     @f
        cmp     [DevErrorCode], 0
        je      @@End_11
        dec     ecx
;        cmp     ecx,0
        jz      .Error
        jmp     @@SendCommand
@@:
        call    change_task
        ; Проверить код ошибки
        cmp     [DevErrorCode], 0
        je      @@End_11
        ; Проверить время ожидания готовности
        mov     EAX, [timer_ticks]
        sub     EAX, [WURStartTime]
        cmp     EAX, MaxCDWaitTime
        jb      @@SendCommand
.Error:
        ; Ошибка тайм-аута
        mov     [DevErrorCode], 1
@@End_11:
        popa
        ret

;*************************************************
;*            ЗАПРЕТИТЬ СМЕНУ ДИСКА              *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
prevent_medium_removal:
        pusha
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Задать код команды
        mov     [PacketCommand], byte 0x1E
; Задать код запрета
        mov     [PacketCommand+4], byte 11b
; Подать команду
        call SendPacketNoDatCommand
        mov     eax, ATAPI_IDE0_lock
        add     eax, [cdpos]
        dec  eax
        mov     [eax], byte 1
        popa
        ret

;*************************************************
;*            РАЗРЕШИТЬ СМЕНУ ДИСКА              *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
allow_medium_removal:
        pusha
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Задать код команды
        mov     [PacketCommand], byte 0x1E
; Задать код запрета
        mov     [PacketCommand+4], byte 00b
; Подать команду
        call SendPacketNoDatCommand
        mov     eax, ATAPI_IDE0_lock
        add     eax, [cdpos]
        dec  eax
        mov     [eax], byte 0
        popa
        ret

;*************************************************
;*         ЗАГРУЗИТЬ НОСИТЕЛЬ В ДИСКОВОД         *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
LoadMedium:
        pusha
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Сформировать команду START/STOP UNIT
        ; Задать код команды
        mov     [PacketCommand], word 1Bh
        ; Задать операцию загрузки носителя
        mov     [PacketCommand+4], word 00000011b
; Подать команду
        call    SendPacketNoDatCommand
        popa
        ret

;*************************************************
;*         ИЗВЛЕЧЬ НОСИТЕЛЬ ИЗ ДИСКОВОДА         *
;* Входные параметры передаются через глобальные *
;* перменные:                                    *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
EjectMedium:
        pusha
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Сформировать команду START/STOP UNIT
        ; Задать код команды
        mov     [PacketCommand], word 1Bh
        ; Задать операцию извлечения носителя
        mov     [PacketCommand+4], word 00000010b
; Подать команду
        call    SendPacketNoDatCommand
        popa
        ret

;*************************************************
;* Проверить событие нажатия кнопки извлечения   *
;*                     диска                     *
;* Входные параметры передаются через глобальные *
;* переменные:                                   *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
align 4
check_ATAPI_device_event:
        pusha
        mov     eax, [timer_ticks]
        sub     eax, [timer_ATAPI_check]
        cmp     eax, 100
    jb   .end_1
        mov     al, [DRIVE_DATA+1]
        and     al, 11b
        cmp     al, 10b
    jz  .ide3
.ide2_1:
        mov     al, [DRIVE_DATA+1]
        and     al, 1100b
        cmp     al, 1000b
    jz  .ide2
.ide1_1:
        mov     al, [DRIVE_DATA+1]
        and     al, 110000b
        cmp     al, 100000b
    jz  .ide1
.ide0_1:
        mov     al, [DRIVE_DATA+1]
        and     al, 11000000b
        cmp     al, 10000000b
    jz  .ide0
.end:

    sti
        mov     eax, [timer_ticks]
        mov     [timer_ATAPI_check], eax
.end_1:
        popa
        ret

.ide3:
    cli
        cmp     [ATAPI_IDE3_lock], 1
    jne  .ide2_1
        cmp     [IDE_Channel_2], 0
    jne  .ide1_1
        cmp     [cd_status], 0
    jne  .end
        mov     [IDE_Channel_2], 1
    call reserve_ok2
        mov     [ChannelNumber], 2
        mov     [DiskNumber], 1
        mov     [cdpos], 4
        call GetEvent_StatusNotification
        cmp     [CDDataBuf+4], byte 1
        je   .eject_ide3
        call syscall_cdaudio.free
    jmp  .ide2_1
.eject_ide3:
    call .eject
        call syscall_cdaudio.free
    jmp  .ide2_1

.ide2:
    cli
        cmp     [ATAPI_IDE2_lock], 1
    jne  .ide1_1
        cmp     [IDE_Channel_2], 0
    jne  .ide1_1
        cmp     [cd_status], 0
    jne  .end
        mov     [IDE_Channel_2], 1
    call  reserve_ok2
        mov     [ChannelNumber], 2
        mov     [DiskNumber], 0
        mov     [cdpos], 3
        call GetEvent_StatusNotification
        cmp     [CDDataBuf+4], byte 1
        je   .eject_ide2
        call syscall_cdaudio.free
    jmp  .ide1_1
.eject_ide2:
    call .eject
        call syscall_cdaudio.free
    jmp  .ide1_1

.ide1:
    cli
        cmp     [ATAPI_IDE1_lock], 1
    jne  .ide0_1
        cmp     [IDE_Channel_1], 0
    jne  .end
        cmp     [cd_status], 0
    jne  .end
        mov     [IDE_Channel_1], 1
    call reserve_ok2
        mov     [ChannelNumber], 1
        mov     [DiskNumber], 1
        mov     [cdpos], 2
        call GetEvent_StatusNotification
        cmp     [CDDataBuf+4], byte 1
        je   .eject_ide1
        call syscall_cdaudio.free
    jmp  .ide0_1
.eject_ide1:
    call .eject
        call syscall_cdaudio.free
    jmp  .ide0_1

.ide0:
    cli
        cmp     [ATAPI_IDE0_lock], 1
    jne  .end
        cmp     [IDE_Channel_1], 0
    jne  .end
        cmp     [cd_status], 0
    jne  .end
        mov     [IDE_Channel_1], 1
    call reserve_ok2
        mov     [ChannelNumber], 1
        mov     [DiskNumber], 0
        mov     [cdpos], 1
        call GetEvent_StatusNotification
        cmp     [CDDataBuf+4], byte 1
        je   .eject_ide0
        call syscall_cdaudio.free
    jmp  .end
.eject_ide0:
    call .eject
        call syscall_cdaudio.free
    jmp  .end

.eject:
        call clear_CD_cache
        call allow_medium_removal
        mov     [ignore_CD_eject_wait], 1
        call EjectMedium
        mov     [ignore_CD_eject_wait], 0
        ret
iglobal
timer_ATAPI_check dd 0
ATAPI_IDE0_lock db 0
ATAPI_IDE1_lock db 0
ATAPI_IDE2_lock db 0
ATAPI_IDE3_lock db 0
ignore_CD_eject_wait db 0
endg
;*************************************************
;* Получить сообщение о событии или состоянии    *
;*                  устройства                   *
;* Входные параметры передаются через глобальные *
;* переменные:                                   *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
GetEvent_StatusNotification:
        pusha
        mov     [CDDataBuf_pointer], CDDataBuf
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Задать код команды
        mov     [PacketCommand], byte 4Ah
        mov     [PacketCommand+1], byte 00000001b
; Задать запрос класса сообщений
        mov     [PacketCommand+4], byte 00010000b
; Размер выделенной области
        mov     [PacketCommand+7], byte 8h
        mov     [PacketCommand+8], byte 0h
; Подать команду
        call    SendPacketDatCommand
        popa
        ret

;*************************************************
; прочитать информацию из TOC
;* Входные параметры передаются через глобальные *
;* переменные:                                   *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
Read_TOC:
        pusha
        mov     [CDDataBuf_pointer], CDDataBuf
; Очистить буфер пакетной команды
        call  clear_packet_buffer
; Сформировать пакетную команду для считывания
; сектора данных
        mov     [PacketCommand], byte 0x43
        ; Задать формат
        mov     [PacketCommand+2], byte 1
; Размер выделенной области
        mov     [PacketCommand+7], byte 0xFF
        mov     [PacketCommand+8], byte 0h
; Подать команду
        call  SendPacketDatCommand
        popa
        ret

;*************************************************
;* ОПРЕДЕЛИТЬ ОБЩЕЕ КОЛИЧЕСТВО СЕКТОРОВ НА ДИСКЕ *
;* Входные параметры передаются через глобальные *
;* переменные:                                   *
;* ChannelNumber - номер канала;                 *
;* DiskNumber - номер диска на канале.           *
;*************************************************
;ReadCapacity:
;       pusha
;; Очистить буфер пакетной команды
;       call  clear_packet_buffer
;; Задать размер буфера в байтах
;       mov     [CDBlockSize],8
;; Сформировать команду READ CAPACITY
;       mov     [PacketCommand],word 25h
;; Подать команду
;       call    SendPacketDatCommand
;       popa
;       ret

clear_packet_buffer:
; Очистить буфер пакетной команды
        and     [PacketCommand], dword 0
        and     [PacketCommand+4], dword 0
        and     [PacketCommand+8], dword 0
        ret