Как сделать задержку выполнения программы? - Assembler

Узнай цену своей работы

Формулировка задачи:

У Финогенова К.Г. в книге самоучителя приведет пример очень компактной задержки вот код
jmp $+2
в комментариях к коду написано что это небольшая задержка. Вопрос - что значит небольшая? На сколько она небольшая 1нс, 1мкс, 1мс, 1сек? (просто все в нашем мире относительно) И как она работает? jmp это же безусловный переход, куда уходит код после такой команды и каким образом он где то ненадолго задерживается?

Решение задачи: «Как сделать задержку выполнения программы?»

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

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

10   голосов , оценка 4.1 из 5