Проблема с системным таймером под DOS

Тема в разделе "WASM.BEGINNERS", создана пользователем delafrog, 5 май 2008.

  1. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    У меня возникла следующая проблема.

    Разрабатывается небольшое DOS-приложение с использованием BORLANDC 3.1. Суть работы приложения - отслеживать время прихода сигнала от параллельного порта с точностью до единиц микросекунд. Для этого используются регисты системного таймера, которые инкрементируются примерно каждую микросекунду (даже чуть бустрее). При переполнении регистров таймера генерируется аппаратное прерывание INT 08h, которое обрабатывается BIOS. BIOS в конце обработки прерывания генерирует програмное прерывание INT 1Ch. В описываемой программе используется прерывание INT 1Ch для счёта количества переполнеий счётчика таймера с начала запуска программы. То есть, по вектру INT 1Ch кладётся обработчик прерывания, который инкрементирует глобальную переменную. Таким образом, считав значения регистов системного таймера и зная число переполнений счётчика системного таймера с начала программы можно получить полное чило тактов системного таймера.
    Алгоритм отслеживания импульсов о параллельного порта таков: считывается значение порта затем запускается цикл в каждом проходе которого также читается значение из параллельного порта, когда вноь пришедшее значение оказывается не равным предыдущему - считывается момент времени и помещается в массив, текущий индекс массива увеличивается. Цикл завершается по досижению индекса массива некоторого значения.
    Нормальный резудьтат работа такого алгоритма должна выглядеть как массив чисел упорядоченных по возрастанию.
    Реализация описанной схемы в среде BORLANDC 3.1 показала, что такой метод вполне хорошо работает, по крайней мере массивы выдаються упорядоченые по возрастанию. Но это только в режиме отладки программы, - если прогонять программу по Ctrl+F9 из среды BORLANDC 3.1. Если-же запускать уже сам .exe файл то начинается какая-то чехарда. Итоговые массивы оказываются неупорядоченными, как быдто импульсы которые пришли позже почему-то определяются как пришедшие раньше (если конечно цикл исполняется строго последовательно). И это составляет суть проблеммы. Как сделать .exe файл чтобы он работал хотя-бы также как программа работает при запуске из среды BORLANDC 3.1?
    Кто-нибудь знает в чём причина такого поведения программы? Чем отличается запуск программы из среды BORLANDC 3.1 и из командной строки?
     
  2. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    1. Надеюсь запускаешь в досе, а не в эмуляции дос?
    Необычно.
    Зачем запускать цикл? Ой чую двойное вхождение в таймер.

    Может стоит просто повесить на прерывание от ком порта? Тогда не надо будет делать цикл.
     
  3. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    Да, запускаю в досе. Хотя замечу, что сам таймер (в смысле обработчик прерывания INT 1Ch) работает и в эмуляции доса под виндой...


    Первоначально был обработчик прерывания от паралельного порта, но результат был аналогичный. Переделал в цикл - ничего не улучшилось

    А вот про двойное вхождение в таймер - это не понятно. Не можешь объяснить как оно может получиться. Я заметил ещё такую вещь. Если в цикле просто вызывать функцию считывания регистов таймера, по в режиме запуска программы из среды BORLANDC время испольнанения примерно в два раза меньше (~5 мкс против ~10 мкс) чем нежели запускать exe из командной строки.
     
  4. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    приведу фрагменты используемого кода.


    unsigned long ECntNum; // глобальная переменная - счётчик переполений таймера


    void interrupt (*OnTimer_old)(void); /* Старый обработчик прерывания */

    void interrupt OnTimer(void) // новый обработчик прервыания
    {
    ECntNum++;
    _asm{
    mov al,0x20
    out 0x20,al
    }
    }

    unsigned long counter() // функция определения количества тактов системного таймера
    {
    unsigned long nt,i1,i2;
    outp(0x43, 0x00); // запись управ. слова в регист таймера
    i1 = inp(0x40); // считывание младшего байта
    i2 = inp(0x40); // и старшего
    nt = (ECntNum<<16)+65536-((i2<<8)+i1); //
    return nt;
    }


    // простой вызовом функции counter в цикле - позволяет оценить время исполнения самой функции counter()
    int main()
    {
    double T[100];
    unsigned int n;


    OnTimer_old=getvect(0x08);
    setvect(0x08,OnTimer);

    ECntNum = 0;
    for(n=0;n<100;n++)
    {
    T[n] = counter();
    }
    setvect(0x08,OnTimer_old);


    return 0;
    }

    // пример с чтением из потра

    int main()
    {
    double T[100];
    unsigned int n,p1,p2;


    OnTimer_old=getvect(0x08);
    setvect(0x08,OnTimer);

    n = 0;
    ECntNum = 0;

    p1 = inp(0x379); // первоначальное чтение из параллельного порта
    do
    {
    p2 = inp(0x379); // чтение из параллельного порта в цикле
    if(p2!=p1)
    {
    T[n] = counter();
    n++;
    do{}while(inp(0x379)!=p1) // ждём пока не вернётся первоначальное значение в параллельном порту
    }
    }while(n<100)

    setvect(0x08,OnTimer_old);


    return 0;
    }
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Так это всё и решает, планирование ведь работает. Попробуй на чистом досе.
     
  6. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    пробовал в чистом досе - вообще вешал свой обработчик не на программное прерывание INT 1Ch, а на аппаратное прерывание INT 08h - ситуацию это не изменяет. Также - при запуске в отладчике borlandc работает хорошо, а exe-файл работает плохо. Как будто прерывания обрабатываются с каким-то запаздыванием... хотя у INT 08h вроде бы наивысший приоитет...
     
  7. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    Может-ли данная проблемы быть связана с тем что при запуске из под отладчика процессор находиться (по идее должен находится) в режиме отладке с флагом TF=1, а при запуске просто exe - файла флаг TF=0 ? Насколько мне известно при TF=1 процессор генерирует прерывание 01h которое должно обрабатываться программой отладчиком...
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Ну если пробовал в чистом досе да и ещё свой ISR вешал на таймер, тогда единственный возможный вариант - неправильно программируешь PIT.
     
  9. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    что значит PIT?
    как надо правильно? выше приведены основные фрагменты кода.
     
  10. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Программируемый интервальный таймер, перед использованием его надо программировать, тока учти, что у него низкое быстродействие, не помню точно, порядком 1-2MHz
     
  11. S_Alex

    S_Alex Alex

    Публикаций:
    0
    Регистрация:
    27 авг 2004
    Сообщения:
    561
    Адрес:
    Ukraine
    Похожая темка.
    http://www.wasm.ru/forum/viewtopic.php?id=26387

    А с какой частотой приходят сигналы?

    Таймер кажись 16-бит. Может он успевает переполниться? И вот тут выползают не последовательные значения.

    Clerk
    Да частота которая приходит на таймер 1 193 180 Гц.
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Тактовая чачтота таймера 1193180Гц
    Таймеры 8253 и 8254 состоят из трех независимых каналов, или счетчиков. Каждый канал содержит регистры:
    • состояния канала RS (8 разрядов)
    • управляющего слова RSW (8 разрядов)
    • буферный регистр OL (16 разрядов)
    • регистр счетчика CE (16 разрядов)
    • регистр констант пересчета CR (16 разрядов).

    Каналы таймера подключаются к внешним устройствам при помощи трех линий:
    • GATE - управляющий вход;
    • CLOCK - вход тактовой частоты;
    • OUT - выход таймера.
    Регистр счетчика CE работает в режиме вычитания. Его содержимое уменьшается по заднему фронту сигнала CLOCK при условии, что на вход GATE установлен уровень логической 1. В зависимости от режима работы таймера при достижении счетчиком CE нуля тем или иным образом изменяется выходной сигнал OUT.
    Буферный регистр OL предназначен для запоминания текущего содержимого регистра счетчика CE без остановки процесса счета. После запоминания буферный регистр доступен программе для чтения.
    Регистр констант пересчета CR может загружаться в регистр счетчика, если это требуется в текущем режиме работы таймера.

    Возможны шесть режимов работы таймера. Они разделяются на три типа:
    • Режимы 0, 4 - однократное выполнение функций.
    • Режимы 1, 5 - работа с перезапуском.
    • Режимы 2, 3 - работа с автозагрузкой.
    В режиме однократного выполнения функций перед началом счета содержимое регистра констант пересчета CR переписывается в регистр счетчика CE по сигналу CLOCK, если сигнал GATE установлен в 1. В дальнейшем содержимое регистра CE уменьшается по мере прихода импульсов CLOCK. Процесс счета можно приостановить, если подать на вход GATE уровень логического 0. Если затем на вход GATE подать 1, счет будет продолжен дальше. Для повторения выполнения функции необходима новая загрузка регистра CR, т.е. повторное программирование таймера.

    При работе с перезапуском не требуется повторного программирования таймера для выполнения той же функции. По фронту сигнала GATE значение константы из регистра CR вновь переписывается в регистр CE, даже если текущая операция не была завершена.
    В режиме автозагрузки регистр CR автоматически переписывается в регистр CE после завершения счета. Сигнал на выходе OUT появляется только при наличии на входе GATE уровня логической 1. Этот режим используется для создания программируемых импульсных генераторов и генераторов прямоугольных импульсов (меандра).
    Канал 0 используется в системных часах времени суток (не следует путать с часами реального времени, реализованными на другой микросхеме). Этот канал работает в режиме 3 и используется как генератор импульсов с частотой примерно 18.2 Гц. Именно эти импульсы вызывают аппаратное прерывание INT 8h.
    Канал 1 используется для регенерации содержимого динамической памяти компьютера. Выход канала OUT используется для запроса к каналу прямого доступа DMA, который и выполняет обновление содержимого памяти. Вам не следует перепрограммировать этот канал, так как это может привести к нарушениям в работе основной оперативной памяти компьютера.
    Канал 2 подключен к громкоговорителю компьютера. Канал использует режим 3 таймера.
    Таймеру соответствуют четыре порта ввода/вывода со следующими адресами:
    • 40h - канал 0;
    • 41h - канал 1;
    • 42h - канал 2;
    • 43h - управляющий регистр.

    Формат управляющего регистра:

    7 6 5 4 3 2 1 0
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ Формат константы пересчёта
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ 0 - двоичный счет;
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ 1 - двоично-десятичный счет.
    ¦ ¦ ¦ ¦ Режим работы таймера
    ¦ ¦ ¦ ¦ 0 0 0 - режим 0,прерывание от таймера;
    ¦ ¦ ¦ ¦ 0 0 1 - режим 1,программируемый ждущий мультивибратор;
    ¦ ¦ ¦ ¦ X 1 0 - режим 2,программируемый генератор импульсов;
    ¦ ¦ ¦ ¦ X 1 1 - режим 3,генератор меандра;
    ¦ ¦ ¦ ¦ 1 0 0 - режим 4,программно-запускаемый одновибратор;
    ¦ ¦ ¦ ¦ 1 0 1 - режим 5,аппаратно-запускаемый одновибратор.
    ¦ ¦ Способ загрузки констант через однобайтовый порт.
    ¦ ¦ 0 0 - код команды CLC (запомнить CE в буферном регистре OL, биты [0,3]=X)
    ¦ ¦ 0 1 - чтение/запись старшего байта;
    ¦ ¦ 1 0 - чтение/запись младшего байта;
    ¦ ¦ 1 1 - чтение/запись младшего, затем старшего байта.
    Номер канала, для которого предназначено управляющее слово или кодкоманды чтения состояния канала.
    0 0 - канал 0;
    0 1 - канал 1;
    1 0 - канал 2;
    1 1 - код команды RBC (чтение состояния канала).


    Формат команды RBC чтения слова состояния канала:
    7 6 5 4 3 2 1 0
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ 0 - Всегда равно нулю.
    ¦ ¦ ¦ ¦ ¦ ¦ 1 - выбор канала 0.
    ¦ ¦ ¦ ¦ ¦ 1 - выбор канала 1.
    ¦ ¦ ¦ ¦ 1 - выбор канала 2.
    ¦ ¦ ¦ 0 - читать состояние каналов;
    ¦ ¦ ¦ 1 - не читать состояние каналов.
    ¦ ¦ 0 - запомнить текущее содержимое CE;
    ¦ ¦ 1 - не запоминать содержимое CE.
    1 1 - код команды RBC.

    Формат слова состояния канала:
    7 6 5 4 3 2 1 0
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ Формат константы пересчёта
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ 0 - двоичный счет;
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ 1 - двоично-десятичный счет.
    ¦ ¦ ¦ ¦ Режим работы таймера
    ¦ ¦ ¦ ¦ 0 0 0 - режим 0,прерывание от таймера;
    ¦ ¦ ¦ ¦ 0 0 1 - режим 1,программируемый ждущий мультивибратор;
    ¦ ¦ ¦ ¦ X 1 0 - режим 2,программируемый генератор импульсов;
    ¦ ¦ ¦ ¦ X 1 1 - режим 3,генератор меандра;
    ¦ ¦ ¦ ¦ 1 0 0 - режим 4,программно-запускаемый одновибратор;
    ¦ ¦ ¦ ¦ 1 0 1 - режим 5,аппаратно-запускаемый одновибратор.
    ¦ ¦ Способ загрузки констант через однобайтовый порт.
    ¦ ¦ 0 0 - код команды CLC (запомнить CE в OL);
    ¦ ¦ 0 1 - чтение/запись старшего байта;
    ¦ ¦ 1 0 - чтение/запись младшего байта;
    ¦ ¦ 1 1 - чтение/запись младшего, затем старшего байта.
    ¦ Флаг перезагрузки констант;
    Состояние выхода OUT.

    Разряд 6 используется, в основном, в режимах 1 и 5 для определения, произошла ли загрузка константы из регистра CR в регистр счетчика CE.
    Разряд 7 позволяет определить состояние выходной линии канала OUT в момент выполнения команды RBC.
    Для программирования канала таймера необходимо выполнить следующую последовательность действий:
    • вывести в порт управляющего регистра с адресом 43h управляющее слово;
    • требуемое значение счетчика посылается в порт канала (адреса 40h...42h), причем вначале выводится младший, а затем старший байты значения счетчика.
    Сразу после этого канал таймера начнет выполнять требуемую функцию.
    Для чтения текущего содержимого счетчика CE необходимо выполнить следующее:
    • вывести в порт управляющего регистра код команды CLC (команда запоминания содержимого регистра CE);
    • вывести в порт управляющего регистра код команды запроса на чтение/запись в регистры канала (поле RW должно содержать 11);
    • двумя последовательными командами ввода из порта нужного канала ввести младший и старший байты текущего сосотояния счетчика CE.
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Код (Text):
    1. clock   dd 1193180  ;Тактовая частота таймера
    2.  
    3. Beeper proc Freq:dword
    4. Local durat:dword
    5.     in al,61h       ;системный порт
    6.     and al,11111100b
    7.     out 61h,al  ;останавливаем счёт в канале 2
    8.     mov al,10110110b    ;канал 2,запись младшего и старшего байт,генератор меандра,двоичный счёт
    9.     out 43h,al  ;управляющий регистр
    10.     finit       ;durat=clock/Freq
    11.     fldz
    12.     fild clock
    13.     fidiv Freq
    14.     fist durat 
    15.     mov eax,durat
    16.     out 42h,al  ;младший байт константы пересчёта
    17.     jmp short $+2
    18.     mov al,ah
    19.     out 42h,al  ;старшиё байт константы пересчёта
    20.     jmp short $+2
    21.     in al,61h
    22.     or al,00000011b
    23.     out 61h,al  ;подключаем speaker к каналу и разрешаем счёт   
    24.     ret
    25. Beeper endp
    Я когдато давно тащился от его программирования..
     
  14. gazlan

    gazlan Member

    Публикаций:
    0
    Регистрация:
    22 май 2005
    Сообщения:
    414
    Вот здесь делалось что-то похожее
     
  15. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    У меня почему-то не распаковывается файл. WinRar выдаёт ошибку. Можно выложить заново?
     
  16. Com[e]r

    Com[e]r Com[e]r

    Публикаций:
    0
    Регистрация:
    20 апр 2007
    Сообщения:
    2.624
    Адрес:
    ого..
    нормальный архив. юзай тотал коммандер ;)
     
  17. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    В тотал командере тоже не открывает. 98% распакавывает а потом говорит что неожиданый конец файла и ошибка контрольной суммы. Может кто-нибудь выложит уже сам текст, если у него распаковалось?
     
  18. delafrog

    delafrog New Member

    Публикаций:
    0
    Регистрация:
    5 май 2008
    Сообщения:
    10
    Обнаружил следующую особенность, связанную наверное с особенностью работы таймера.

    При запуске программы в режиме отладки прерывания и переполнения регистов таймера происходят синхронно: на каждое переполнения регистров таймера вызывается прерывание. При запуске из командной строки - прерывания приходят только на каждое второе переполнение счётчика таймера, при этом, по моему, значения регистров таймера происходит в два раза быстрее, то есть как раз так, что каждое второе переполнение будет происходить с частотой ~18.2 Гц.
     
  19. gazlan

    gazlan Member

    Публикаций:
    0
    Регистрация:
    22 май 2005
    Сообщения:
    414
    Some in ZIP
     
  20. valterg

    valterg Active Member

    Публикаций:
    0
    Регистрация:
    19 авг 2004
    Сообщения:
    2.105
    delafrog
    Ты читаешь счетчик побайтно. Надо прерывания закрыть, иначе между чтениями может быть другое прерывание и счетчик проскочит далеко, точнее ты считаешь мусор. В dos_fast именно так и сделано.