Приветствую! Кто знает ответ на такой вопрос (?): Программирую 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+ - нет. В чём проблема?
Привожу протокол обмена: активация диска, далее: 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
Попробуй сначала запускать команду рекалибровки, а потом выполнять ожидание раскрутки двигателя. Кстати, ты в CCR скорость прописываешь?
В 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 цилиндр
Я точно не знаю, но попробуй всё-таки после SEEK делать паузу. Судя по коду ошибки, головка стоит не на том цилиндре - возможно потому, что ещё не доехала. Ещё попробуй читать не целый цилиндр, а 1 сектор. Если прочитается, то постепенно увеличивай, чтобы найти максимальное число. Возможно, данный контроллер не умеет считывать цилиндр целиком.
Читал 1 сектор. та же ошибка. Но! Подал команду чтения дорожки (это код 42), он мне прочёл её, но не нулевую, а последнюю, которая была прочитана при загрузке ядра. Получается, что SEEK не отработал. А должен был (судя по результату команды).
SadKo Ошибка во временных задержках. вот посмотри давно писал регистры _ax и т. д. могут быть eax или ax Код (Text): _universal equ 1 ; закомментировать если не используется ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма инициализации │ ;│ Вход: cl - дисковод │ ;│ Выход: ZF=0 если инициализация успешна │ ;│ ZF=1 если таймаут или ошибка посылки комманды │ ;└───────────────────────────────────────────────────────────────────────────┘ InitFDD: mov dx,3F2h in al,dx and al,0FBh ; бит 2 - reset out dx,al or al,0Ch out dx,al ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма ожидания аппаратного прерывания IRQ6 │ ;│ Вход: │ ;│ Выход: ZF=0 если условие выполнилось пока счетчик не обнулился │ ;│ ZF=1 если обнулился счетчик │ ;└───────────────────────────────────────────────────────────────────────────┘ WaitForIRQ6: mov al,0Ah ; чтение IRR out 20h,al call WaitForFDDEvent in al,20h shl al,1 jns WFELabel2 ; бит 6 - IRQ6 mov al,8 pop _bx ; Выход из ожидания SCToFDDFromEAX: push edx push ecx push eax SCFDDLabel1: call SCToFDDFromStack SCFDDLabel2: pop eax pop ecx pop edx retn ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма посылки комманды │ ;│ Выход: ZF=0 если условие выполнилось пока счетчик не обнулился │ ;│ ZF=1 если обнулился счетчик │ ;└───────────────────────────────────────────────────────────────────────────┘ SendCommandFDD: push edx push ecx push eax mov cl,ah call IsDSKCHG jz SCFDDLabel2 call SCToFDDFromStack ; call WaitForIRQ6 pop eax pop ecx pop edx retn ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма рекалибровки │ ;│ Вход: cl - дисковод │ ;│ Выход: ZF=1 если успешна │ ;│ ZF=0 если ошибка таймаут при ожидании конца рекалибровки │ ;└───────────────────────────────────────────────────────────────────────────┘ Recalibrate: call RLabel1 ; дважды jz RLabel2 ; Поскольку 77 треков в RLabel1: mov ah,cl ; DRV mov al,7 ; RECALIBRATE FinishSeek: push _cx call SCToFDDFromEAX call WaitForIRQ6 pop _cx shl al,1 js RLabel2 RLabel3: sbb al,al RLabel2: retn ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма поиска трека │ ;│ Вход: cl - дисковод ch - трек │ ;│ Выход: ZF=1 если успешна │ ;│ ZF=0 если ошибка таймаут при ожидании конца поиска │ ;└───────────────────────────────────────────────────────────────────────────┘ Seek: mov _ax,_cx shl eax,8 mov al,0Fh jmp FinishSeek ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма проверки смены диска │ ;│ Вход: cl - дисковод │ ;│ Выход: Калибровка скорости │ ;└───────────────────────────────────────────────────────────────────────────┘ IsDSKCHG: mov dx,3F2h in al,dx MOLabel2: test cl,1 mov ah,10h jz MOLabel1 mov ah,21h MOLabel1: or al,ah out dx,al mov dl,0F7h in al,dx shl al,1 jnc FindSpeed call Recalibrate jnz FSLabel3 ; Ошибка рекалибровки push _cx mov ch,1 call Seek pop _cx jnz FSLabel3 ; Ошибка позиционирования mov dl,0F7h ; проверка наличия дискеты in al,dx ; вообще shl al,1 jc FSLabel3 FindSpeed: mov eax,0010AF03h ; 0011AF03h call SCToFDDFromEAX ; SPECIFY jz FSLabel2 mov eax,005F0013h ; !!! Если хотим использовать FIFO, то здесь 005F0013h, но тогда при записи ; на диск - будет требоваться на 16 байт (от 1 до 16 байт) больше послать ; информации, которая на диск писаться не будет, а будет служить лишь для ; заполнения буфера FIFO, поскольку сигнал запрос данных будет выдан FDC CPU ; как только освободиться FIFO, а на диск еще последний байт не запишется, ; то есть к примеру при чтении читается 512 байт, а при записи пишется ; 512, но дополнительно посылаются 16 байт чтобы запонить FIFO - итого 528. ; При использовании DMA этого не будет. При отключеном FIFO тоже. Этот ; момент происходит лишь при использовании универсальной программы FDD_IO. call SCToFDDFromEAX ; CONFIGURE jz FSLabel2 mov al,3 FSLabel1: mov dx,3F7h out dx,al ; jmp FSLabel2 push _ax push _cx ; _cx - меняется READ ID mov ah,cl mov al,4Ah call SCToFDDFromEAX test al,0C0h stc ; для сброса ZF pop _cx pop _ax jz RLabel3 ; выход со сбросом ZF dec al jns FSLabel1 FSLabel3: cmp al,al ; ZF = 1 ошибка FSLabel2: retn ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма ожидания события │ ;│ Вход: mov ecx, ... ; сетчик в единицах по 15 мксек │ ;│ call WaitForEvent │ ;│ ... ; код проверки условия │ ;│ retn ; ZF = 1 условие выполнено │ ;│ Выход: ZF=0 если условие выполнилось пока счетчик не обнулился │ ;│ ZF=1 если обнулился счетчик │ ;└───────────────────────────────────────────────────────────────────────────┘ Pause15mks: call WaitForEvent jmp short WFELabel2 WaitForHDDEvent: mov ecx,3333+3333 ; 0.100 секунд jmp short WaitForEvent WaitForFDDEvent: mov ecx,333333 ; 5 секунд WaitForEvent: pop _bx mov ah,10h WFELabel1: call _bx jz WFELabel2 ; дождались in al,61h and al,10h xor al,ah jz WFELabel1 xor ah,10h ifndef _32Bit db 67h endif loop WFELabel1 WFELabel2: or ecx,ecx retn ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма ввода-вывода комманды-результата с использованием стека │ ;│ Вход: В стеке комманда и достаточно места для результата ее исполнения │ ;│ Выход: ZF=0 если комманда выполнилось пока счетчик не обнулился │ ;│ ZF=1 если обнулился счетчик │ ;└───────────────────────────────────────────────────────────────────────────┘ SCToFDDFromStack: pop _ax ; +2 или +4 mov _si,_sp push _ax mov _di,_si ;┌───────────────────────────────────────────────────────────────────────────┐ ;│ Подпрограмма ввода-вывода комманды-результата │ ;│ Вход: esi-буфер ввода ─┐ если используется │ ;│ edi-буфер вывода ├─ режим _universal, то │ ;│ ebp-буфер для фазы исполнения если есть ─┘ адресация в ss: сегм. │ ;│ Выход: ZF=0 если комманда выполнилось пока счетчик не обнулился │ ;│ ZF=1 если обнулился счетчик │ ;└───────────────────────────────────────────────────────────────────────────┘ FDD_IO: call ReadyFDD FDD_IOLabel1: ifdef _universal db 36h ; ss: endif outsb ; послать комманду FDD_IOLabel3: call Pause15mks ReadyFDD: mov dx,3F4h call WaitForFDDEvent ; <────────────────────┐ in al,dx ; │ shl al,1 ; │ jnc short WFELabel2 ; бит RQM - готовность │ pop _cx ; выход из ожидания ───┘ mov ecx,10 ; должна быть задержка inc _dx ; edx = 3F5h test al,20h ; бит CB - Command Buzy jz WFELabel2 ; выход если CB = 0 shl al,1 ; бит CB - 1 jns NoExecute ; бит NonDMA=1 mov _si,_bp mov _di,_bp db 9Ch ; pushfd или pushf mov cl,0 ; нет задержки для inc _bp ; фазы исполнения db 9Dh ; popfd или popf NoExecute: jnc FDD_IOLabel1 ; бит CPU>FDD ifdef _universal db 36h ; ss: endif insb jmp FDD_IOLabel3
В каких именно временных задержках? Поменял код, не проходит рекалибровка. Вот, собсно, по сабжу: Код (Text): bool TFDCDirectory::OutByte(BYTE value) { wsize_t Timer = GetTimerMilliseconds()+FDC_WAIT_TIME; do { if ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&(FDC_MSR_RQM|FDC_MSR_DIO))==FDC_MSR_RQM) { outportb(FDC_REGISTER_BASE+FDC_REGISTER_FIFO, value); return true; } } while (GetTimerMilliseconds()<=Timer); return false; } bool TFDCDirectory::InputByte(BYTE &value) { wsize_t Timer = GetTimerMilliseconds()+FDC_WAIT_TIME; do { if ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&(FDC_MSR_RQM|FDC_MSR_DIO))==(FDC_MSR_RQM|FDC_MSR_DIO)) { value=inportb(FDC_REGISTER_BASE+FDC_REGISTER_FIFO); return true; } } while (GetTimerMilliseconds()<=Timer); return false; } bool TFDCDirectory::WriteCmd(const BYTE* array, size_t count) { FInterruptFlag=false; // Reset interrupt status // Wait controller while ((inportb(FDC_REGISTER_BASE+FDC_REGISTER_MSR)&FDC_MSR_CB)==FDC_MSR_CB) ; for (int i=0; i<count; i++) // Write command bytes if (!OutByte(*(array++))) return false; return WaitResponse(); // Wait till command is finished } bool TFDCDirectory::ReadCmdResult(BYTE* array, size_t count) { FInterruptFlag=false; // Reset interrupt status BYTE *p = array; for (int i=0; i<count; i++) // Read command result bytes if (!InputByte(*(array++))) return false; return true; // Return successful result } bool TFDCDirectory::WaitResponse(void)//bool sense) { wsize_t Timer=GetTimerMilliseconds()+FDC_SYNC_TIME; do { //Nothing } while ((GetTimerMilliseconds()<=Timer) && (!FInterruptFlag)); return FInterruptFlag; } bool TFDCDirectory::SenseInterrupt(BYTE *frame) { // SENCE INTERRUPT if (OutByte(FDC_CMDB1_SENSEINTERRUPT)) return ReadCmdResult(frame, 2); return false; } bool TFDCDirectory::RecalibrateDisk(int device) { // Initialize command frame BYTE frame[2]; frame[0] = FDC_CMDB1_RECALIBRATE; frame[1] = BYTE(device); // Recalibrate disk if (WriteCmd(frame, 2)) { BYTE result[2]; if (SenseInterrupt(result)) // Sense interrupt return (result[0]&(FDC_ST0_IC_ALARM|FDC_ST0_EC|FDC_ST0_NR))==0; } return false; } bool TFDCDirectory::ActivateDisk(int device) { // Setup data transfer mode outportb(FDC_REGISTER_BASE+FDC_REGISTER_CCR, FDC_CCR_DRATE_DFM_500); outportb(FDC_REGISTER_BASE+FDC_REGISTER_DSR, FDC_DSR_DRATE_DFM_500); // Setup parameters BYTE cmd[3] = { FDC_CMDB1_SPECIFY, 0xdf, 0x02 }; WriteCmd(cmd, 3); // Activate disk outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, 0); outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, BYTE(FCodes[device].Activate)); sleep(600); for (int i=0; i<3; i++) { if (RecalibrateDisk(device)) { if (SeekTrack(device, 0)) { FCurrDev = device; TTimer::SetTrigger(FDC_SYNC_TIME, false); return true; } } sleep(100); } outportb(FDC_REGISTER_BASE+FDC_REGISTER_DOR, 0); return false; } Теперь расписываю последовательность действий: Код (Text): Activate Disk(0) { CCR<<DRATE_DFM_500 DSR<<DRATE_DFM_500 FDC<<SPECIFY df 02 { while (MSR&CB!=0) /*wait */; OutByte SPECIFY { InterruptFlag = 0 set_timer(2 sec); if (MSR&(RQM|DIO) == RQM) { FIFO<<SPECIFY; return true; } if (timer_exceeds) return false; } OutByte df OutByte 02 return WaitResponse { set_timer(2 sec); if (InterruptFlag==1) /* Interupt flag is set by IRQ */ return true; if (timer_exceeds) return false; } } DOR<<0 DOR<<1c sleep 600 RecalibrateDisk 00 { FDC<<RECALIBRATE 00 FDC<<SENSE_INTERRUPT FDC>>frame[2] { InterruptFlag = 0 InputByte frame[0] { set_timer(2 sec) if (MSR&(RQM|DIO))==(RQM|DIO)) { FIFO>>frame[0] return true } if (timer_exceeds) return false } InputByte frame[1] } return (frame[0]&ALARM|EC|NR)==0 } ... } Собственно, дальше Recalibrate нет смысла смотреть, так как получаем протокол обмена: Код (Text): FDC<<03 df 02 FDC<<07 00 FDC<<08 FDC>>70 00
Господа! Вопрос снимаю! Это я креведко, не увидел, что повесил прерывание на IRQ 0 (таймер), а не на IRQ 6 (FDC). Сейчас всё корректно отрабатывает. А так - действительно каша получалась.
SadKo Электроника -- наука о контактах... Ну а программирование, по аналогии -- об указателях (в широком смысле)