Вопрос по assembly, windows, sleep – Как создать функцию сна в 16-битной MASM Assembly x86?

10

Я пытаюсь создать процедуру сна / задержки в 16-битной MASM Assembly x86, которая, скажем, будет печатать символ на экране каждые 500 мс. Из проведенного мною исследования, кажется, есть три метода для достижения этой цели - я хотел бы использовать тот, который использует тактовые импульсы процессора.

Обратите внимание, что я работаю под управлением Windows XP через VMWare Fusion на Mac OS X Snow Leopard - я не уверен, влияет ли это на что-либо.

Может ли кто-нибудь указать мне правильное направление или предоставить рабочий фрагмент кода, который я могу настроить? Спасибо!

Код, который я нашел, должен напечатать 'A' на экране каждую секунду, но не работает (яв любом случае я хотел бы использовать миллисекунды).

TOP:
MOV AH,2C
INT 21
MOV BH,DH  ; DH has current second
GETSEC:      ; Loops until the current second is not equal to the last, in BH
MOV AH,2C
INT 21
CMP BH,DH  ; Here is the comparison to exit the loop and print 'A'
JNE PRINTA
JMP GETSEC
PRINTA:
MOV AH,02
MOV DL,41
INT 21
JMP TOP

РЕДАКТИРОВАТЬ: После GJ 'совет здесьЭто рабочая процедура. Просто позвони

DELAY PROC
 TIMER:
 MOV     AH, 00H
 INT     1AH
 CMP     DX,WAIT_TIME
 JB      TIMER
 ADD     DX,3         ;1-18, where smaller is faster and 18 is close to 1 second
 MOV     WAIT_TIME,DX
 RET
DELAY ENDP
Будьте осторожны в полночь, у вас могут возникнуть проблемы. Может быть, лучшая идея для чтения непосредственно в ячейке памяти в 0x0040: 0x0070. Читайте также:merlyn.demon.co.uk/pas-time.htm#RDT GJ.
Да. Я запускаю его на Windows через виртуальную машину, как указано в моем посте :) Yuval Karmi
Ваша процедура также терпит неудачу, когда первые 16 бит таймера сбрасываются до 0. GJ.
Кто-нибудь может опубликовать исправление этой процедуры? Я не совсем уверен, как это исправить. Yuval Karmi

Ваш Ответ

6   ответов
3

На самом деле вы можете использовать ROM ROM прерывание 1Ah с функцией 00h, 'Считать текущий счетчик часов, Или вы можете прочитать dword по адресу $ 40: $ 6C, но вы должны обеспечить атомарное чтение. Он увеличивается с помощью MS-DOS примерно на 18,2 Гц.

Для получения дополнительной информации читайте:Часы DOS

The DOS Clock ссылка ведет меня на пустую страницу. Frozen Flame
5

используйте INT 15h, функцию 86h:

Вызов с: AH = 86 ч CX: DX = интервал в США

Это лучший ответ. clearlight
2

Ну тогда. Старый стиль, непостоянный, потребляющий энергию цикл задержки, который замедлит работу других потоков, будет выглядеть так:

       delay equ 5000

top:   mov ax, delay
loopa: mov bx, delay
loopb: dec bx
       jnc loopb
       dec ax
       jnc loopa

       mov ah,2
       mov dl,'A'
       int 21
       jmp top

Задержка квадратична постоянной. Но если вы воспользуетесь этой петлей задержки, где-нибудь в мире молодой невинный котенок умрет.

0

... Проблема всех приведенных выше примеров кода заключается в том, что они используют неблокирующие операции. Если вы изучите использование процессора в течение относительно длительного периода ожидания, вы увидите, что он работает примерно на 50%. Мы хотим использовать некоторые функции DOS или BIOS, которые блокируют выполнение, так что загрузка процессора составляет около 0%.

..Offhand, на ум приходит BIOS INT 16h, AH = 1. Возможно, вы сможете разработать процедуру, которая вызывает эту функцию, а затем вставит нажатие клавиши в буфер клавиатуры, когда время истечет. Есть много проблем с этой идеей;), но это может быть пищей для размышлений. Вполне вероятно, что вы будете писать какой-то обработчик прерываний.

..В 32-битном Windows API естьСпать" функция. Я полагаю, вы могли бы подумать об этом.

2

Я не't проверить этот код, но концепция должна работать ... Сохранить / восстановить регистр необязательно! Проверьте код внимательно!

DelayProcedure:
    push  es                      //Save es and load new es
    mov   ax, 0040h
    mov   es, ax
//Pseudo atomic read of 32 bit DOS time tick variable
PseudoAtomicRead1:
    mov   ax, es:[006ch]
    mov   dx, es:[006eh]
    cmp   ax, es:[006ch]
    jne   PseudoAtomicRead1
//Add time delay to dx,ax where smaller is faster and 18 is close to 1 second
    add   ax, 3
    adc   dx, 0
//1800AFh is last DOS time tick value so check day overflow
    mov   cx, ax
    mov   bx, dx
//Do 32 bit subtract/compare
    sub   cx, 00AFh
    sbb   dx, 0018h
    jbe   DayOverflow
//Pseudo atomic read of 32 bit DOS time tick variable
PseudoAtomicRead2:
    mov   cx, es:[006ch]
    mov   bx, es:[006eh]
    cmp   cx, es:[006ch]
    jne   PseudoAtomicRead2
NotZero:
//At last do 32 bit compare
    sub   cx, ax
    sbb   bx, dx
    jae   Exit
//Check again day overflow because task scheduler can overjumps last time ticks
    inc   bx                //If no Day Overflow then bx = 0FFh
    jz    PseudoAtomicRead2
    jmp   Exit
DayOverflow:
//Pseudo atomic read of 32 bit DOS time tick variable
PseudoAtomicRead3:
    mov   ax, es:[006ch]
    mov   dx, es:[006eh]
    cmp   dx, es:[006ch]
    jne   PseudoAtomicRead3
//At last do 32 bit compare
    sub   ax, cx
    sbb   dx, bx
    jb    PseudoAtomicRead3
Exit:
    pop   es                      //Restore es
    ret
11

Это не может быть сделано в чистом MASM. Все старые приемы для установки фиксированной задержки основаны на предположении, что вы имеете полный контроль над машиной и являетесь единственным потоком, работающим на ЦП, так что если вы будете ждать 500 миллионов циклов, точно500,000,000/f пройдут секунды (для процессора на частотеf); тот'должно быть 500 мс для процессора с частотой 1 ГГц.

Поскольку вы работаете в современной операционной системе, вы разделяете ЦП со многими другими потоками (среди них ядро - независимо от того, что вы делаете, вы не можете иметь приоритет над ядром!), Так что ожидание только 500 миллионов цикловваша нить будет означать, что в реальном мире пройдет более 500 миллионов циклов. Эта проблема не может быть решена одним только кодом пользовательского пространства; вам понадобится сотрудничество с ядром.

Чтобы решить эту проблему, нужно посмотреть, какая функция Win32 API приостановит ваш поток на указанное количество миллисекунд, а затем просто вызвать эту функцию. Вы должны быть в состоянии сделать это непосредственно из сборки, возможно, с дополнительными аргументами для вашего компоновщика. Или для выполнения этой функции может быть системный вызов ядра NT (у меня очень мало опыта работы с системными вызовами NT, и я, честно говоря, понятия не имею, как выглядит таблица системных вызовов NT, но я мог бы использовать функцию сна ожидаю увидеть). Если доступен системный вызов, то выполнение прямого системного вызова из сборки, вероятно, является самым быстрым способом сделать то, что вы хотите; Это'Кроме того, наименее портативным (но тогда выпереписываю сборку!).

редактировать: Смотря натаблица системных вызовов ядра NTнетПохоже, это любые вызовы, связанные со сном или получением даты и времени (как использует ваш оригинальный код), но есть несколько системных вызовов для установки и запроса таймеров. Вращение, пока вы ждете таймера для достижения желаемой задержки, является одним из эффективных, хотя и не элегантных, решений.

во-первых, большое спасибо за тщательно продуманный ответ. Позвольте мне тогда расширить возможности. Как мне звонить каждый фиксированный промежуток времени, который может варьироваться от машины к машине (т.е. я нене забывайте, что он работает каждые 500 мс на одной машине и каждые 150 мс на другой). Yuval Karmi
В 16-битной подсистеме XP он выигралВ любом случае, с вызовами win32 далеко не получится. Gunther Piez
Я думаю, что вы имеете в видуциклы / ф " и не "ф / циклы ". Hannes Ovrén
kigurai: исправлено Вздох. Юваль: Честно говоря, яя недостаточно знаком с API-интерфейсами NT, чтобы понять, как вы можете сделать это из головы; Я могу только предложить то, что я всегда делаю в этой ситуации: посмотрите на таблицу системных вызовов и посмотрите, что вы можете построить с помощью имеющихся у вас инструментов! kquinn

Похожие вопросы