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

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

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

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

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

textual
Листинг программы
  1. ; tasm /m delay.asm
  2. ; tlink /t /x delay.obj
  3.  
  4. .MODEL Tiny
  5. .286
  6. .CODE
  7. ORG     100h
  8.  
  9. LOCALS
  10.  
  11. Start:
  12.  
  13.                 call    InitTimer      ; Выполняем один раз! (для PTDelay)
  14.                 call    InitPasDelay   ; Выполняем один раз! (для PasDelay)
  15.                 mov     al,'.'
  16.                 int     29h            ; Выводим точку
  17.  
  18.                 mov     ax,1000
  19.                 call    PTDelay        ; Ожидаем 1 секунду
  20.                 mov     al,'1'
  21.                 int     29h            ; Выводим '1'
  22.  
  23.                 mov     ax,1000
  24.                 call    PasDelay       ; Ожидаем 1 секунду
  25.                 mov     al,'2'
  26.                 int     29h            ; Выводим '2'
  27.  
  28.                 mov     ax,1000
  29.                 call    TickDelay      ; Ожидаем ~ 1 секунду
  30.                 mov     al,'3'
  31.                 int     29h            ; Выводим '3'
  32.  
  33.                 mov     ax,1000
  34.                 call    Int15Delay     ; Ожидаем 1 секунду
  35.                 mov     al,'4'
  36.                 int     29h            ; Выводим '4'
  37.  
  38.                 mov     al,'.'
  39.                 int     29h            ; Выводим точку
  40.                 xor     ah,ah
  41.                 int     16h            ; Ждём нажатия клавиши
  42.                 int     20h            ; Выходим из программы
  43.  
  44. ; -- Процедуры --------------------------------------------------------------;
  45.  
  46. ; Инициализировать таймер (установить режим и коэффициент пересчёта)
  47. InitTimer       proc
  48.                 cli                    ; На всякий случай запретим прерывания
  49.                 mov     al,34h         ; Режим 2 канала 0
  50.                 out     43h,al
  51.                 xor     al,al          ; 65536 циклов между IRQ
  52.                 out     40h,al
  53.                 out     40h,al
  54.                 sti
  55.                 ret
  56. InitTimer       endp
  57.  
  58. ; Получить значение таймера в DX:CX:AX (AX младшее слово)
  59. GetTimerValue   proc
  60.                 cld
  61.                 xor     ax,ax
  62.                 mov     es,ax
  63.                 mov     si,46Ch        ; ES:SI = 0000h:046Ch = системный счётчик BIOS
  64.                 mov     cx,es:[si+0]   ; CX = первое значение счётчика BIOS (младшее слово)
  65.                 out     43h,al         ; "Защёлкиваем" канал 0 таймера i8253/i8254
  66.                 cli
  67.                 mov     bx,es:[si+0]
  68.                 mov     dx,es:[si+2]   ; DX:BX = второе значение счётчика BIOS
  69.                 in      al,40h
  70.                 mov     ah,al
  71.                 in      al,40h
  72.                 xchg    ah,al          ; AX = значение таймера i8253/i8254
  73.                 sti
  74.                 cmp     cx,bx          ; Первое значение счётчика BIOS равно второму ?
  75.                 je      @@Ok           ; Да! Оставляем как есть (DX:CX), иначе...
  76.                 or      ax,ax          ; Счётчик изменился ПОСЛЕ "защёлкивания" (между OUT и CLI) ?
  77.                 js      @@Swap         ; Нет! Идём переносить BX в CX, иначе...
  78.                 or      bx,bx          ; При изменении таймера изменилось старшее слово ?
  79.                 jne     @@NoOverflow   ; Нет!
  80.                 dec     dx             ; Да, корректируем старшее слово (DX)
  81. @@NoOverflow:   cmp     ax,?           ; Здесь это аналогично jmp @@Ok (только короче и быстрее)
  82.                 ORG $-2
  83. @@Swap:         mov     cx,bx          ; DX:CX = DX:BX, если счётчик изменился между MOV CX и OUT
  84. @@Ok:           not     ax             ; Обратный отсчёт -> Прямой отсчёт
  85.                 ret
  86. GetTimerValue   endp
  87.  
  88. ; Осуществить задержку AX миллисекунд
  89. ; Использует GetTimerValue
  90. PTDelay         proc
  91.                 or      ax,ax
  92.                 jz      @@Exit         ; Если AX=0, то сразу выходим
  93.  
  94.                 mov     bp,sp          ; Сохраняем SP в BP
  95.                 push    ax             ; Сохраняем нужное нам число в стеке
  96.                 call    GetTimerValue  ; Получаем текущее значение таймера
  97.                 xchg    bx,ax
  98.                 mov     si,cx
  99.                 mov     di,dx          ; Сохраняем его в DI:SI:BX
  100.  
  101.                 finit                  ; Инициализируем сопроцессор
  102.                 fild    word ptr [bp-2]; Загружаем в стек кол-во миллисекунд
  103.                 push    012h
  104.                 push    034DDh         ; Заносим в стек число 1193181
  105.                 fild    dword ptr [bp-6]; Загружаем его в сопроцессор
  106.                 fmul                   ; Перемножаем эти числа
  107.                 push    1000
  108.                 fild    word ptr [bp-8]
  109.                 fdiv                   ; И делим на 1000
  110.                 fistp   dword ptr [bp-4]; А затем помещаем обратно в стек (округлив)
  111.                 mov     ax,[bp-4]
  112.                 mov     dx,[bp-2]      ; Читаем из стека результат в DX:AX
  113.                 mov     sp,bp          ; Восстанавливаем SP
  114.                 add     bx,ax
  115.                 adc     si,dx
  116.                 adc     di,0           ; Добавляем DX:AX к DI:SI:BX
  117. @@Repeat:
  118.                 push    bx si
  119.                 call    GetTimerValue  ; Получаем текущее значение таймера
  120.                 pop     si bx
  121.                 sub     ax,bx
  122.                 sbb     cx,si
  123.                 sbb     dx,di          ; Вычитаем DI:SI:BX из DX:CX:AX
  124.                 or      dx,dx
  125.                 js      @@Repeat       ; Повторяем, если получилось < 0
  126. @@Exit:         ret
  127. PTDelay         endp
  128.  
  129. PasDelayCnt     dw      0              ; Используется в процедуре PasDelay
  130.  
  131. ; Инициализировать переменную PasDelayCnt для работы процедуры PasDelay
  132. ; Использует PasDelayLoop
  133. InitPasDelay    proc
  134.                 xor     ax,ax
  135.                 mov     es,ax
  136.                 mov     di,46Ch
  137.                 mov     bl,es:[di]
  138. @@Wait:         cmp     bl,es:[di]
  139.                 je      @@Wait         ; Ждём следующий тик
  140.                 mov     bl,es:[di]
  141.                 mov     ax,-28
  142.                 cwd                    ; DX = 0FFFFh
  143.                 call    PasDelayLoop   ; Ждём следующий тик
  144.                 not     ax
  145.                 not     dx             ; Инвертируем DX:AX
  146.                 mov     cx,55
  147.                 xchg    bx,ax          ; Сохранить AX в BX
  148.                 xchg    ax,dx          ; Записать DX в AX
  149.                 cwd                    ; DX = 0
  150.                 div     cx             ; Делим DX:AX на CX (55)
  151.                 mov     PasDelayCnt[2],ax ; Сохраняем старшее слово
  152.                 xchg    ax,bx          ; Восстанавливаем AX из BX
  153.                 div     cx             ; Делим DX:AX на CX (55)
  154.                 mov     PasDelayCnt[0],ax ; Сохраняем младшее слово
  155.                 ret
  156. InitPasDelay    endp
  157.  
  158. ; Ожидать либо DX:AX циклов, либо изменения байта по адресу ES:[DI]
  159. ; зависимости от того, что произойдёт раньше)
  160. PasDelayLoop    proc
  161. @@Repeat:       sub     ax,1
  162.                 sbb     dx,0
  163.                 jc      @@Exit
  164.                 cmp     bl,es:[di]
  165.                 je      @@Repeat
  166. @@Exit:         ret
  167. PasDelayLoop    endp
  168.  
  169. ; Осуществить задержку AX миллисекунд (этот метод используется в языке Pascal)
  170. ; Использует PasDelayLoop
  171. PasDelay        proc
  172.                 xchg    cx,ax          ; Заносим AX в CX
  173.                 jcxz    @@Exit         ; Если CX=0, то сразу выходим
  174.  
  175.                 mov     ax,40h
  176.                 mov     es,ax
  177.                 xor     di,di
  178.                 mov     bl,es:[di]     ; BL = байт по адресу 0040h:0000h (не меняется)
  179. @@Repeat:       mov     ax,PasDelayCnt[0]
  180.                 mov     dx,PasDelayCnt[2]
  181.                 call    PasDelayLoop   ; Ждём DX:AX циклов
  182.                 loop    @@Repeat       ; CX раз
  183. @@Exit:         ret
  184. PasDelay        endp
  185.  
  186. ; Осуществить задержку AX миллисекунд точностью до 55 миллисекунд)
  187. ; Ничего не использует, весьма простой способ
  188. TickDelay       proc
  189.                 mov     cx,1193
  190.                 mul     cx             ; Делим AX на 55 и прибавляем 1
  191.                 inc     dx             ; Получаем в DX кол-во тиков таймера
  192.  
  193.                 xor     ax,ax
  194.                 mov     es,ax
  195.                 mov     si,46Ch
  196.                 mov     bx,es:[si+0]
  197.                 mov     cx,es:[si+2]   ; Читаем таймер BIOS в CX:BX
  198.                 add     bx,dx
  199.                 adc     cx,ax          ; Добавляем DX к CX:BX (AX=0)
  200. @@Repeat:
  201.                 mov     ax,es:[si+0]
  202.                 mov     dx,es:[si+2]   ; Читаем таймер BIOS в DX:AX
  203.                 sub     ax,bx
  204.                 sbb     dx,cx          ; Вычитаем CX:BX из DX:AX
  205.                 js      @@Repeat       ; Повторяем, если получилось <= 0
  206.                 ret
  207. TickDelay       endp
  208.  
  209. ; Осуществить задержку AX миллисекунд через int 15h
  210. ; Ничего не использует, самый простой, но не всегда надёжный способ
  211. ; Устанавливает флаг CF=CY=1, если функция занята
  212. Int15Delay      proc
  213.                 mov     cx,1000
  214.                 mul     cx             ; Умножаем AX на 1000
  215.                 mov     cx,dx
  216.                 mov     dx,ax          ; Переносим DX:AX в CX:DX
  217.                 mov     ah,86h
  218.                 int     15h            ; Вызываем int 15h
  219.                 ret
  220. Int15Delay      endp
  221.  
  222. END             Start

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


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

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

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

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут