Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.118.30.253] |
|
Сообщ.
#1
,
|
|
|
Как произвести задержку на несколько миллисекунд? Существует много способов осуществить задержку, рассмотрим некоторые из них. Теперь несколько слов о том, какой способ лучше всего использовать. Первый способ самый точный и надёжный, но и самый громоздкий. Хотя... не знаю почему, но под Windows счётчик BIOS изменяется не сразу, а только через несколько тиков системного программируемого таймера. Поэтому примерно один из 1300 (на моём компьютере) вызов GetTimerValue даёт сбой на 55 миллисекунд. В чистом DOS'е всё работает чётко! Второй способ достаточно точный на коротких задержках (в несколько десятков или сотен миллисекунд), но для больших задержек вместо него лучше использовать третий способ. Про четвёртый уже сказано достаточно. А вот, собственно, и долгожданный исходник: ; tasm /m delay.asm ; tlink /t /x delay.obj .MODEL Tiny .286 .CODE ORG 100h LOCALS Start: call InitTimer ; Выполняем один раз! (для PTDelay) call InitPasDelay ; Выполняем один раз! (для PasDelay) mov al,'.' int 29h ; Выводим точку mov ax,1000 call PTDelay ; Ожидаем 1 секунду mov al,'1' int 29h ; Выводим '1' mov ax,1000 call PasDelay ; Ожидаем 1 секунду mov al,'2' int 29h ; Выводим '2' mov ax,1000 call TickDelay ; Ожидаем ~ 1 секунду mov al,'3' int 29h ; Выводим '3' mov ax,1000 call Int15Delay ; Ожидаем 1 секунду mov al,'4' int 29h ; Выводим '4' mov al,'.' int 29h ; Выводим точку xor ah,ah int 16h ; Ждём нажатия клавиши int 20h ; Выходим из программы ; -- Процедуры --------------------------------------------------------------; ; Инициализировать таймер (установить режим и коэффициент пересчёта) InitTimer proc cli ; На всякий случай запретим прерывания mov al,34h ; Режим 2 канала 0 out 43h,al xor al,al ; 65536 циклов между IRQ out 40h,al out 40h,al sti ret InitTimer endp ; Получить значение таймера в DX:CX:AX (AX младшее слово) GetTimerValue proc cld xor ax,ax mov es,ax mov si,46Ch ; ES:SI = 0000h:046Ch = системный счётчик BIOS mov cx,es:[si+0] ; CX = первое значение счётчика BIOS (младшее слово) out 43h,al ; "Защёлкиваем" канал 0 таймера i8253/i8254 cli mov bx,es:[si+0] mov dx,es:[si+2] ; DX:BX = второе значение счётчика BIOS in al,40h mov ah,al in al,40h xchg ah,al ; AX = значение таймера i8253/i8254 sti cmp cx,bx ; Первое значение счётчика BIOS равно второму ? je @@Ok ; Да! Оставляем как есть (DX:CX), иначе... or ax,ax ; Счётчик изменился ПОСЛЕ "защёлкивания" (между OUT и CLI) ? js @@Swap ; Нет! Идём переносить BX в CX, иначе... or bx,bx ; При изменении таймера изменилось старшее слово ? jne @@NoOverflow ; Нет! dec dx ; Да, корректируем старшее слово (DX) @@NoOverflow: cmp ax,? ; Здесь это аналогично jmp @@Ok (только короче и быстрее) ORG $-2 @@Swap: mov cx,bx ; DX:CX = DX:BX, если счётчик изменился между MOV CX и OUT @@Ok: not ax ; Обратный отсчёт -> Прямой отсчёт ret GetTimerValue endp ; Осуществить задержку AX миллисекунд ; Использует GetTimerValue PTDelay proc or ax,ax jz @@Exit ; Если AX=0, то сразу выходим mov bp,sp ; Сохраняем SP в BP push ax ; Сохраняем нужное нам число в стеке call GetTimerValue ; Получаем текущее значение таймера xchg bx,ax mov si,cx mov di,dx ; Сохраняем его в DI:SI:BX finit ; Инициализируем сопроцессор fild word ptr [bp-2]; Загружаем в стек кол-во миллисекунд push 012h push 034DDh ; Заносим в стек число 1193181 fild dword ptr [bp-6]; Загружаем его в сопроцессор fmul ; Перемножаем эти числа push 1000 fild word ptr [bp-8] fdiv ; И делим на 1000 fistp dword ptr [bp-4]; А затем помещаем обратно в стек (округлив) mov ax,[bp-4] mov dx,[bp-2] ; Читаем из стека результат в DX:AX mov sp,bp ; Восстанавливаем SP add bx,ax adc si,dx adc di,0 ; Добавляем DX:AX к DI:SI:BX @@Repeat: push bx si call GetTimerValue ; Получаем текущее значение таймера pop si bx sub ax,bx sbb cx,si sbb dx,di ; Вычитаем DI:SI:BX из DX:CX:AX or dx,dx js @@Repeat ; Повторяем, если получилось < 0 @@Exit: ret PTDelay endp PasDelayCnt dw 0 ; Используется в процедуре PasDelay ; Инициализировать переменную PasDelayCnt для работы процедуры PasDelay ; Использует PasDelayLoop InitPasDelay proc xor ax,ax mov es,ax mov di,46Ch mov bl,es:[di] @@Wait: cmp bl,es:[di] je @@Wait ; Ждём следующий тик mov bl,es:[di] mov ax,-28 cwd ; DX = 0FFFFh call PasDelayLoop ; Ждём следующий тик not ax not dx ; Инвертируем DX:AX mov cx,55 xchg bx,ax ; Сохранить AX в BX xchg ax,dx ; Записать DX в AX cwd ; DX = 0 div cx ; Делим DX:AX на CX (55) mov PasDelayCnt[2],ax ; Сохраняем старшее слово xchg ax,bx ; Восстанавливаем AX из BX div cx ; Делим DX:AX на CX (55) mov PasDelayCnt[0],ax ; Сохраняем младшее слово ret InitPasDelay endp ; Ожидать либо DX:AX циклов, либо изменения байта по адресу ES:[DI] ; (в зависимости от того, что произойдёт раньше) PasDelayLoop proc @@Repeat: sub ax,1 sbb dx,0 jc @@Exit cmp bl,es:[di] je @@Repeat @@Exit: ret PasDelayLoop endp ; Осуществить задержку AX миллисекунд (этот метод используется в языке Pascal) ; Использует PasDelayLoop PasDelay proc xchg cx,ax ; Заносим AX в CX jcxz @@Exit ; Если CX=0, то сразу выходим mov ax,40h mov es,ax xor di,di mov bl,es:[di] ; BL = байт по адресу 0040h:0000h (не меняется) @@Repeat: mov ax,PasDelayCnt[0] mov dx,PasDelayCnt[2] call PasDelayLoop ; Ждём DX:AX циклов loop @@Repeat ; CX раз @@Exit: ret PasDelay endp ; Осуществить задержку AX миллисекунд (с точностью до 55 миллисекунд) ; Ничего не использует, весьма простой способ TickDelay proc mov cx,1193 mul cx ; Делим AX на 55 и прибавляем 1 inc dx ; Получаем в DX кол-во тиков таймера xor ax,ax mov es,ax mov si,46Ch mov bx,es:[si+0] mov cx,es:[si+2] ; Читаем таймер BIOS в CX:BX add bx,dx adc cx,ax ; Добавляем DX к CX:BX (AX=0) @@Repeat: mov ax,es:[si+0] mov dx,es:[si+2] ; Читаем таймер BIOS в DX:AX sub ax,bx sbb dx,cx ; Вычитаем CX:BX из DX:AX js @@Repeat ; Повторяем, если получилось <= 0 ret TickDelay endp ; Осуществить задержку AX миллисекунд через int 15h ; Ничего не использует, самый простой, но не всегда надёжный способ ; Устанавливает флаг CF=CY=1, если функция занята Int15Delay proc mov cx,1000 mul cx ; Умножаем AX на 1000 mov cx,dx mov dx,ax ; Переносим DX:AX в CX:DX mov ah,86h int 15h ; Вызываем int 15h ret Int15Delay endp END Start Прикреплённый файлdelay.zip (11.56 Кбайт, скачиваний: 312) |