Чудеса FDC

Тема в разделе "WASM.OS.DEVEL", создана пользователем SadKo, 5 янв 2008.

  1. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Приветствую! Кто знает ответ на такой вопрос (?):
    Программирую FDC через порты.

    1. Активирую мотор дисковода.
    2. Засыпаю на 600 мсек.
    3. Провожу рекалибровку.
    4. Делаю seek на 0 дорожку.
    5. Инициализирую DMA на передачу данных двух дорожек.
    6. Посылаю команду чтения цилиндра (e6 00 00 00 01 02 12 1b ff).
    7. Получаю ответ с ошибкой (40 04 10 00 00 01 02).

    Вопрос: что не так, и почему это происходит? На виртуальных машинах qemu и bochs отрабатывает, при этом ответ содержит 00 00 00 01 00 01 02. Мало того, на 80486dx2 тоже отрабатывает. А вот на p4 2.0GHz и на AMD Athlon x2 3800+ - нет. В чём проблема?
     
  2. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Привожу протокол обмена:
    активация диска, далее:
    FDC<<07 00
    FDC<<08
    FDC>>70 00
    FDC<<0F 00 00
    FDC>>20 00
    FDC<<E6 00 00 00 01 02 12 1B FF
    FDC>>40 04 10 00 00 01 02
     
  3. Phantom_84

    Phantom_84 New Member

    Публикаций:
    0
    Регистрация:
    6 июн 2007
    Сообщения:
    820
    Попробуй сначала запускать команду рекалибровки, а потом выполнять ожидание раскрутки двигателя. Кстати, ты в CCR скорость прописываешь?
     
  4. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    В CCR и DSR пишу скорость 500, сейчас изменил протокол, добавил SPECIFY 0xdf 0x02.

    Самое интересное, если разобраться с ответом FDC:
    40 - ST0, выполнение команды завершилось аварийно
    04 - ST1, данные не найдены на диске
    10 - ST2, неверный цилиндр
    00 - цилиндр
    00 - головка
    01 - сектор
    02 - код размера сектора, 512 байт

    Подобный ответ вызывает у меня удивление, потому как на команду SEEK получаем успешный ответ:
    <<0F - SEEK
    <<00 - 0 головка 0 диска
    <<00 - NCN, номер цилиндра = 0
    <<SENSE INTERRUPT
    >>20 - Seek finished
    >>00 - PCN=0, 0 цилиндр
     
  5. Chizh

    Chizh New Member

    Публикаций:
    0
    Регистрация:
    10 дек 2007
    Сообщения:
    23
    Я точно не знаю, но попробуй всё-таки после SEEK делать паузу. Судя по коду ошибки, головка стоит не на том цилиндре - возможно потому, что ещё не доехала.
    Ещё попробуй читать не целый цилиндр, а 1 сектор. Если прочитается, то постепенно увеличивай, чтобы найти максимальное число. Возможно, данный контроллер не умеет считывать цилиндр целиком.
     
  6. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Читал 1 сектор. та же ошибка.
    Но! Подал команду чтения дорожки (это код 42), он мне прочёл её, но не нулевую, а последнюю, которая была прочитана при загрузке ядра. Получается, что SEEK не отработал. А должен был (судя по результату команды).
     
  7. Chizh

    Chizh New Member

    Публикаций:
    0
    Регистрация:
    10 дек 2007
    Сообщения:
    23
    Вроде бы рекалибровка сама устанавливает головку на 0 дорожку. Попробуй сделать после неё паузу.
     
  8. PROFi

    PROFi New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2003
    Сообщения:
    690
    SadKo

    Ошибка во временных задержках. вот посмотри давно писал регистры _ax и т. д. могут быть eax или ax
    Код (Text):
    1. _universal  equ 1       ; закомментировать если не используется
    2. ;┌───────────────────────────────────────────────────────────────────────────┐
    3. ;│ Подпрограмма инициализации                                                 │
    4. ;│ Вход: cl - дисковод                                     │
    5. ;│ Выход:      ZF=0 если инициализация успешна                    │
    6. ;│             ZF=1 если таймаут или ошибка посылки комманды            │
    7. ;└───────────────────────────────────────────────────────────────────────────┘
    8. InitFDD:        mov dx,3F2h
    9.             in al,dx
    10.             and al,0FBh     ; бит 2 - reset
    11.             out dx,al
    12.             or al,0Ch
    13.             out dx,al
    14. ;┌───────────────────────────────────────────────────────────────────────────┐
    15. ;│ Подпрограмма ожидания аппаратного прерывания IRQ6                         │
    16. ;│ Вход:                                   │
    17. ;│ Выход:      ZF=0 если условие выполнилось пока счетчик не обнулился        │
    18. ;│             ZF=1 если обнулился счетчик                     │
    19. ;└───────────────────────────────────────────────────────────────────────────┘
    20. WaitForIRQ6:        mov al,0Ah      ; чтение IRR
    21.             out 20h,al
    22.             call WaitForFDDEvent
    23.             in al,20h
    24.             shl al,1
    25.             jns WFELabel2       ; бит 6 - IRQ6
    26.             mov al,8       
    27.             pop _bx         ; Выход из ожидания
    28. SCToFDDFromEAX:     push edx
    29.             push ecx
    30.             push eax
    31. SCFDDLabel1:        call SCToFDDFromStack
    32. SCFDDLabel2:        pop eax
    33.             pop ecx
    34.             pop edx
    35.             retn
    36. ;┌───────────────────────────────────────────────────────────────────────────┐
    37. ;│ Подпрограмма посылки комманды                                             │
    38. ;│ Выход:      ZF=0 если условие выполнилось пока счетчик не обнулился        │
    39. ;│             ZF=1 если обнулился счетчик                     │
    40. ;└───────────────────────────────────────────────────────────────────────────┘
    41. SendCommandFDD:     push edx
    42.             push ecx
    43.             push eax
    44.             mov cl,ah
    45.             call IsDSKCHG
    46.             jz SCFDDLabel2
    47.             call SCToFDDFromStack
    48. ;           call WaitForIRQ6
    49.             pop eax
    50.             pop ecx
    51.             pop edx
    52.             retn
    53. ;┌───────────────────────────────────────────────────────────────────────────┐
    54. ;│ Подпрограмма рекалибровки                                               │
    55. ;│ Вход: cl - дисковод                                     │
    56. ;│ Выход:      ZF=1 если успешна                       │
    57. ;│             ZF=0 если ошибка таймаут при ожидании конца рекалибровки      │
    58. ;└───────────────────────────────────────────────────────────────────────────┘
    59. Recalibrate:        call RLabel1    ; дважды
    60.             jz RLabel2  ; Поскольку 77 треков в
    61. RLabel1:        mov ah,cl   ; DRV
    62.             mov al,7        ; RECALIBRATE
    63. FinishSeek:     push _cx
    64.             call SCToFDDFromEAX
    65.             call WaitForIRQ6
    66.             pop _cx
    67.             shl al,1
    68.             js RLabel2
    69. RLabel3:        sbb al,al
    70. RLabel2:        retn
    71. ;┌───────────────────────────────────────────────────────────────────────────┐
    72. ;│ Подпрограмма поиска трека                                                │
    73. ;│ Вход: cl - дисковод    ch - трек                            │
    74. ;│ Выход:      ZF=1 если успешна                       │
    75. ;│             ZF=0 если ошибка таймаут при ожидании конца поиска            │
    76. ;└───────────────────────────────────────────────────────────────────────────┘
    77. Seek:           mov _ax,_cx
    78.             shl eax,8
    79.             mov al,0Fh
    80.             jmp FinishSeek
    81. ;┌───────────────────────────────────────────────────────────────────────────┐
    82. ;│ Подпрограмма проверки смены диска                                     │
    83. ;│ Вход: cl - дисковод                                     │
    84. ;│ Выход: Калибровка скорости                           │
    85. ;└───────────────────────────────────────────────────────────────────────────┘
    86. IsDSKCHG:       mov dx,3F2h
    87.             in al,dx
    88. MOLabel2:       test cl,1
    89.             mov ah,10h
    90.             jz MOLabel1
    91.             mov ah,21h
    92. MOLabel1:       or al,ah
    93.             out dx,al
    94.             mov dl,0F7h
    95.             in al,dx
    96.             shl al,1
    97.             jnc FindSpeed
    98.             call Recalibrate
    99.             jnz FSLabel3        ; Ошибка рекалибровки
    100.             push _cx
    101.             mov ch,1
    102.             call Seek
    103.             pop _cx
    104.             jnz FSLabel3        ; Ошибка позиционирования
    105.             mov dl,0F7h     ; проверка наличия дискеты
    106.             in al,dx        ; вообще
    107.             shl al,1
    108.             jc FSLabel3
    109. FindSpeed:      mov eax,0010AF03h   ; 0011AF03h
    110.             call SCToFDDFromEAX ; SPECIFY
    111.             jz FSLabel2
    112.             mov eax,005F0013h  
    113. ; !!! Если хотим использовать FIFO, то здесь 005F0013h, но тогда при записи
    114. ; на диск - будет требоваться на 16 байт (от 1 до 16 байт) больше послать
    115. ; информации, которая на диск писаться не будет, а будет служить лишь для
    116. ; заполнения буфера FIFO, поскольку сигнал запрос данных будет выдан FDC CPU
    117. ; как только освободиться FIFO, а на диск еще последний байт не запишется,
    118. ; то есть к примеру при чтении читается 512 байт, а при записи пишется
    119. ; 512, но дополнительно посылаются 16 байт чтобы запонить FIFO - итого 528.
    120. ; При использовании DMA этого не будет. При отключеном FIFO тоже. Этот
    121. ; момент происходит лишь при использовании универсальной программы FDD_IO.
    122.             call SCToFDDFromEAX ; CONFIGURE
    123.             jz FSLabel2
    124.             mov al,3
    125. FSLabel1:       mov dx,3F7h
    126.             out dx,al
    127. ;           jmp FSLabel2
    128.             push _ax
    129.             push _cx            ; _cx - меняется READ ID
    130.             mov ah,cl
    131.             mov al,4Ah
    132.             call SCToFDDFromEAX
    133.             test al,0C0h
    134.             stc         ; для сброса ZF
    135.             pop _cx
    136.             pop _ax
    137.             jz RLabel3      ; выход со сбросом ZF
    138.             dec al
    139.             jns FSLabel1
    140. FSLabel3:       cmp al,al       ; ZF = 1 ошибка
    141. FSLabel2:       retn
    142. ;┌───────────────────────────────────────────────────────────────────────────┐
    143. ;│ Подпрограмма ожидания события                                             │
    144. ;│ Вход:       mov ecx, ...    ; сетчик в единицах по 15 мксек             │
    145. ;│           call WaitForEvent                                             │
    146. ;│             ...               ; код проверки условия                      │
    147. ;│             retn        ; ZF = 1 условие выполнено          │
    148. ;│ Выход:      ZF=0 если условие выполнилось пока счетчик не обнулился        │
    149. ;│             ZF=1 если обнулился счетчик                     │
    150. ;└───────────────────────────────────────────────────────────────────────────┘
    151. Pause15mks:     call WaitForEvent
    152.             jmp short WFELabel2
    153. WaitForHDDEvent:    mov ecx,3333+3333   ; 0.100 секунд
    154.             jmp short WaitForEvent
    155. WaitForFDDEvent:    mov ecx,333333      ; 5 секунд
    156. WaitForEvent:       pop _bx
    157.             mov ah,10h
    158. WFELabel1:      call _bx
    159.             jz WFELabel2        ; дождались
    160.             in al,61h
    161.             and al,10h
    162.             xor al,ah
    163.             jz WFELabel1
    164.             xor ah,10h
    165. ifndef  _32Bit
    166.             db 67h
    167. endif
    168.             loop WFELabel1
    169. WFELabel2:      or ecx,ecx
    170.             retn
    171. ;┌───────────────────────────────────────────────────────────────────────────┐
    172. ;│ Подпрограмма ввода-вывода комманды-результата с использованием стека      │
    173. ;│ Вход: В стеке комманда и достаточно места для результата ее исполнения    │
    174. ;│ Выход:      ZF=0 если комманда выполнилось пока счетчик не обнулился      │
    175. ;│             ZF=1 если обнулился счетчик                     │
    176. ;└───────────────────────────────────────────────────────────────────────────┘
    177. SCToFDDFromStack:   pop _ax     ; +2 или +4
    178.             mov _si,_sp
    179.             push _ax
    180.             mov _di,_si            
    181. ;┌───────────────────────────────────────────────────────────────────────────┐
    182. ;│ Подпрограмма ввода-вывода комманды-результата                             │
    183. ;│ Вход: esi-буфер ввода                          ─┐  если используется      │
    184. ;│       edi-буфер вывода                          ├─ режим _universal, то   │
    185. ;│       ebp-буфер для фазы исполнения если есть  ─┘  адресация в ss: сегм.  │
    186. ;│ Выход:      ZF=0 если комманда выполнилось пока счетчик не обнулился      │
    187. ;│             ZF=1 если обнулился счетчик                     │
    188. ;└───────────────────────────────────────────────────────────────────────────┘
    189. FDD_IO:         call ReadyFDD
    190. FDD_IOLabel1:
    191. ifdef _universal
    192.             db 36h          ; ss:
    193. endif
    194.             outsb               ; послать комманду
    195. FDD_IOLabel3:       call Pause15mks
    196. ReadyFDD:               mov dx,3F4h
    197.                         call WaitForFDDEvent    ; <────────────────────┐
    198.                         in al,dx                ;                      │
    199.                     shl al,1                ;                      │
    200.                         jnc short WFELabel2     ; бит RQM - готовность │
    201.                         pop _cx                 ; выход из ожидания ───┘
    202.                         mov ecx,10              ; должна быть задержка
    203.             inc _dx         ; edx = 3F5h
    204.             test al,20h     ; бит CB - Command Buzy
    205.             jz WFELabel2            ; выход если CB = 0
    206.             shl al,1            ; бит CB - 1
    207.             jns NoExecute       ; бит NonDMA=1
    208.             mov _si,_bp
    209.             mov _di,_bp
    210.             db 9Ch          ; pushfd или pushf
    211.             mov cl,0        ; нет задержки для
    212.             inc _bp         ; фазы исполнения
    213.             db 9Dh          ; popfd или popf
    214. NoExecute:      jnc FDD_IOLabel1        ; бит CPU>FDD
    215. ifdef   _universal
    216.             db 36h          ; ss:
    217. endif
    218.             insb
    219.             jmp FDD_IOLabel3
     
  9. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    В каких именно временных задержках?

    Поменял код, не проходит рекалибровка. Вот, собсно, по сабжу:

    Код (Text):
    1. bool TFDCDirectory::OutByte(BYTE value)
    2. {
    3.     wsize_t Timer = GetTimerMilliseconds()+FDC_WAIT_TIME;
    4.  
    5.     do
    6.     {
    7.         if ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&(FDC_MSR_RQM|FDC_MSR_DIO))==FDC_MSR_RQM)
    8.         {
    9.             outportb(FDC_REGISTER_BASE+FDC_REGISTER_FIFO, value);
    10.             return true;
    11.         }
    12.     } while (GetTimerMilliseconds()<=Timer);
    13.  
    14.     return false;
    15. }
    16.  
    17. bool TFDCDirectory::InputByte(BYTE &value)
    18. {
    19.     wsize_t Timer = GetTimerMilliseconds()+FDC_WAIT_TIME;
    20.  
    21.     do
    22.     {
    23.         if ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&(FDC_MSR_RQM|FDC_MSR_DIO))==(FDC_MSR_RQM|FDC_MSR_DIO))
    24.         {
    25.             value=inportb(FDC_REGISTER_BASE+FDC_REGISTER_FIFO);
    26.             return true;
    27.         }
    28.     } while (GetTimerMilliseconds()<=Timer);
    29.  
    30.     return false;
    31. }
    32.  
    33. bool TFDCDirectory::WriteCmd(const BYTE* array, size_t count)
    34. {
    35.     FInterruptFlag=false; // Reset interrupt status
    36.  
    37.     // Wait controller
    38.     while ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&FDC_MSR_CB)==FDC_MSR_CB) ;
    39.  
    40.     for (int i=0; i<count; i++) // Write command bytes
    41.         if (!OutByte(*(array++)))
    42.             return false;
    43.  
    44.     return WaitResponse(); // Wait till command is finished
    45. }
    46.  
    47. bool TFDCDirectory::ReadCmdResult(BYTE* array, size_t count)
    48. {
    49.     FInterruptFlag=false; // Reset interrupt status
    50.     BYTE *p = array;
    51.  
    52.     for (int i=0; i<count; i++) // Read command result bytes
    53.         if (!InputByte(*(array++)))
    54.             return false;
    55.  
    56.     return true; // Return successful result
    57. }
    58.  
    59. bool TFDCDirectory::WaitResponse(void)//bool sense)
    60. {
    61.     wsize_t Timer=GetTimerMilliseconds()+FDC_SYNC_TIME;
    62.     do
    63.     {
    64.         //Nothing
    65.     } while ((GetTimerMilliseconds()<=Timer) && (!FInterruptFlag));
    66.  
    67.     return FInterruptFlag;
    68. }
    69.  
    70. bool TFDCDirectory::SenseInterrupt(BYTE *frame)
    71. {
    72.     // SENCE INTERRUPT
    73.     if (OutByte(FDC_CMDB1_SENSEINTERRUPT))
    74.         return ReadCmdResult(frame, 2);
    75.  
    76.     return false;
    77. }
    78.  
    79. bool TFDCDirectory::RecalibrateDisk(int device)
    80. {
    81.     // Initialize command frame
    82.     BYTE frame[2];
    83.     frame[0] = FDC_CMDB1_RECALIBRATE;
    84.     frame[1] = BYTE(device);
    85.  
    86.     // Recalibrate disk
    87.     if (WriteCmd(frame, 2))
    88.     {
    89.         BYTE result[2];
    90.         if (SenseInterrupt(result)) // Sense interrupt
    91.             return (result[0]&(FDC_ST0_IC_ALARM|FDC_ST0_EC|FDC_ST0_NR))==0;
    92.     }
    93.     return false;
    94. }
    95.  
    96. bool TFDCDirectory::ActivateDisk(int device)
    97. {
    98.     // Setup data transfer mode
    99.     outportb(FDC_REGISTER_BASE+FDC_REGISTER_CCR, FDC_CCR_DRATE_DFM_500);
    100.     outportb(FDC_REGISTER_BASE+FDC_REGISTER_DSR, FDC_DSR_DRATE_DFM_500);
    101.    
    102.     // Setup parameters
    103.     BYTE cmd[3] = { FDC_CMDB1_SPECIFY, 0xdf, 0x02 };
    104.     WriteCmd(cmd, 3);
    105.    
    106.     // Activate disk
    107.     outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, 0);
    108.     outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, BYTE(FCodes[device].Activate));
    109.  
    110.     sleep(600);
    111.    
    112.     for (int i=0; i<3; i++)
    113.     {
    114.         if (RecalibrateDisk(device))
    115.         {
    116.             if (SeekTrack(device, 0))
    117.             {
    118.                 FCurrDev = device;
    119.                 TTimer::SetTrigger(FDC_SYNC_TIME, false);
    120.                 return true;
    121.             }
    122.         }
    123.         sleep(100);
    124.     }
    125.  
    126.     outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, 0);
    127.    
    128.     return false;
    129. }
    Теперь расписываю последовательность действий:
    Код (Text):
    1. Activate Disk(0) {
    2.     CCR<<DRATE_DFM_500
    3.     DSR<<DRATE_DFM_500
    4.     FDC<<SPECIFY df 02 {
    5.         while (MSR&CB!=0) /*wait */;
    6.         OutByte SPECIFY {
    7.             InterruptFlag = 0
    8.             set_timer(2 sec);
    9.             if (MSR&(RQM|DIO) == RQM) {
    10.                 FIFO<<SPECIFY;
    11.                 return true;
    12.             }
    13.             if (timer_exceeds)
    14.                 return false;
    15.         }
    16.         OutByte df
    17.         OutByte 02
    18.  
    19.         return WaitResponse {
    20.             set_timer(2 sec);
    21.             if (InterruptFlag==1) /* Interupt flag is set by IRQ */
    22.                 return true;
    23.             if (timer_exceeds)
    24.                 return false;
    25.         }
    26.     }
    27.  
    28.     DOR<<0
    29.     DOR<<1c
    30.     sleep 600
    31.    
    32.     RecalibrateDisk 00 {
    33.         FDC<<RECALIBRATE 00
    34.         FDC<<SENSE_INTERRUPT
    35.         FDC>>frame[2] {
    36.             InterruptFlag = 0
    37.             InputByte frame[0] {
    38.                 set_timer(2 sec)
    39.                 if (MSR&(RQM|DIO))==(RQM|DIO)) {
    40.                      FIFO>>frame[0]
    41.                      return true
    42.                 }
    43.                 if (timer_exceeds)
    44.                      return false
    45.             }
    46.             InputByte frame[1]
    47.         }
    48.         return (frame[0]&ALARM|EC|NR)==0
    49.     }
    50.     ...
    51. }
    Собственно, дальше Recalibrate нет смысла смотреть, так как получаем протокол обмена:
    Код (Text):
    1. FDC<<03 df 02
    2. FDC<<07 00
    3. FDC<<08
    4. FDC>>70 00
     
  10. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Господа! Вопрос снимаю!
    Это я креведко, не увидел, что повесил прерывание на IRQ 0 (таймер), а не на IRQ 6 (FDC). Сейчас всё корректно отрабатывает. А так - действительно каша получалась.
     
  11. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    SadKo
    Электроника -- наука о контактах... Ну а программирование, по аналогии -- об указателях (в широком смысле) :)