Как сделать задержку выполнения программы? - Assembler
Формулировка задачи:
У Финогенова К.Г. в книге самоучителя приведет пример очень компактной задержки вот код
в комментариях к коду написано что это небольшая задержка. Вопрос - что значит небольшая? На сколько она небольшая 1нс, 1мкс, 1мс, 1сек? (просто все в нашем мире относительно) И как она работает? jmp это же безусловный переход, куда уходит код после такой команды и каким образом он где то ненадолго задерживается?
Листинг программы
- jmp $+2
Решение задачи: «Как сделать задержку выполнения программы?»
textual
Листинг программы
- ; 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
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д