12

16 ビット MASM アセンブリ x86 で、500 ミリ秒ごとに画面に文字を出力するスリープ/遅延手順を作成しようとしています。私が行った調査から、これを達成するには3つの方法があるようです.CPUクロックティックを使用する方法を使用したいと思います.

Mac OS X Snow Leopard で VMWare Fusion を介して Windows XP を実行していることに注意してください。それが何かに影響するかどうかはわかりません。

誰かが私を正しい方向に向けるか、微調整できる実用的なコードを提供してもらえますか? ありがとうございました!

私が見つけたコードは、毎秒画面に「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
4

6 に答える 6

12

これは、純粋な MASM では実行できません。固定遅延を設定するためのすべての古いトリックは、マシンを完全に制御し、CPU で実行されている唯一のスレッドであることを前提として動作するため、5 億サイクル待機すると、正確に500,000,000/f数秒が経過します (CPU の場合)周波数f); 1 GHz プロセッサの場合は 500 ミリ秒になります。

最新のオペレーティング システムで実行しているため、CPU を他の多くのスレッドと共有しています (その中でもカーネル -- 何をしても、カーネルよりも優先することはできません!)。あなたのスレッドは、現実世界で 5 億回以上のサイクルが経過したことを意味します。この問題は、ユーザー空間のコードだけでは解決できません。カーネルの協力が必要になります。

これを解決する適切な方法は、どの Win32 API 関数が指定されたミリ秒の間スレッドを中断するかを調べてから、その関数を呼び出すことです。おそらくリンカーへの追加の引数を使用して、アセンブリから直接これを実行できるはずです。または、この機能を実行するための NT カーネル システム コールがあるかもしれません (私は NT システム コールの経験がほとんどなく、NT システム コール テーブルがどのようなものかまったくわかりませんが、スリープ機能はそのようなものです)。見られることを期待してください)。システム コールが利用可能な場合は、アセンブリから直接システム コールを発行するのがおそらく、目的の処理を実行する最も簡単な方法です。また、移植性が最も低くなります (ただし、アセンブリを作成しているのです!)。

編集: NT カーネル システム コール テーブルを見ると、 (元のコードで使用されているように) スリープや日付と時刻の取得に関連する呼び出しはないようですが、タイマーをセットアップしてクエリを実行するためのシステム コールがいくつかあります。タイマーが目的の遅延に達するのを待っている間に回転することは、洗練されていない場合でも効果的な解決策の 1 つです。

于 2009-12-07T08:44:11.297 に答える
6

INT 15h、関数 86h を使用:

Call With: AH = 86h CX:DX = 間隔 (μS)

于 2012-01-14T19:29:46.913 に答える
3

実際には、ROM BIOS割り込み1Ah機能00h、「現在のクロックカウントの読み取り」を使用できます。または、アドレス$ 40:$ 6Cでdwordを読み取ることもできますが、アトミック読み取りを確実にする必要があります。これは、MS-DOSによって約18.2Hzで増分されます。

詳細については、以下をお読みください:DOSクロック

于 2009-12-07T19:16:01.727 に答える
3

じゃあ。実行中の他のスレッドの速度を低下させる古いスタイルの非一定の電力消費遅延ループは、次のようになります。

       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

遅延は定数の 2 次です。しかし、この遅延ループを使用すると、世界のどこかで罪のない子猫が死んでしまいます。

于 2009-12-07T09:20:00.643 に答える
2

このコードはテストしませんでしたが、コンセプトは機能するはずです... es レジスタの保存/復元はオプションです! コードを注意深くチェックしてください!

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
于 2009-12-09T14:23:05.130 に答える
0

..上記のすべてのコード例の問題は、ノンブロッキング操作を使用していることです。比較的長い待機期間中に CPU 使用率を調べると、約 50% で実行されていることがわかります。私たちが望むのは、CPU 使用率が 0% 近くになるように実行をブロックする DOS または BIOS 機能を使用することです。

..さりげなく、BIOS INT 16h、AH=1 関数が思い浮かびます。その関数を呼び出し、時間切れになったときにキーストロークをキーボード バッファーに挿入するルーチンを考案できる場合があります。その考えには多くの問題があります ;) が、それは考える材料になるかもしれません。ある種の割り込みハンドラを作成する可能性があります。

..32 ビット Windows API には、「スリープ」機能があります。私はあなたがそれに感謝できると思います。

于 2012-01-14T17:35:39.500 に答える