4

こんにちは、実際の時間/分/秒を表示しようとしています。これは私のコードサンプルです:

MOV AH, 2Ch
INT 21h

MOV AH, 0Eh

MOV AL, CH
INT 10h

MOV AL, 3Ah
INT 10h

MOV AL, CL
INT 10h

MOV AL, 3Ah
INT 10h

MOV AL, DH
INT 10h

ret

ここで、コンソールに表示されている内容を確認できます

ここに画像の説明を入力

4

3 に答える 3

4

命令セットのリファレンス マニュアルについては、タグのwikiを参照してください。参考資料やチュートリアルへの多くの優れたリンクもあります。


整数を関数に分解する必要がある ASCII 数字に分割するには、十分なコードが必要です。

これは、@hobbs の print2Digits 関数の最適化およびバグ修正されたバージョンです。(私も彼の答えでバージョンをバグ修正したので、それも正しいですが、これには最適化を残しました)。

print2Digits:
    ;; input in AL (0-99).  (Or preferably already zero-extended to AX so we can omit CBW)
    ;; clobbers AX and DX
    cbw             ; zero AH.  Sign-extending AL does the job because AL is only allowed to be 0-99.

    mov   dl, 10
    div   dl        ; quotient in AL(first (high) digit), remainder in AH(second (low) digit)

    add   ax, 0x3030  ; add '0' to al and ah at the same time.
    mov   dl, ah      ; save the 2nd digit

    mov   ah, 0x0E   ; BIOS call #: print single character
    int   0x10       ; print high digit first.  Doesn't clobber anything, so AH still holds 0x0E after
    mov   al, dl
    int   0x10       ; print the low digit 2nd
    ret

以前divは整数を base10 の 2 桁に分割していたのでah、ゼロにする必要があります。つまり、配当が AX にあり、AH にガベージがある可能性がある AL だけではありません。cbwor mov ah,0if the caller did movzx ax, chor something をゼロに保存できますah

(ただし、8086 には がないためmovzx、実際にはxor ax,ax/が必要ですmov al, ch。)

文字列全体を出力するための DOS システム コールがあるため、このAMD64 Linux FizzBu​​zzで行っているように、文字を小さなバッファーに格納して一度にすべて出力することができます。c ライブラリから printf を使用せずに、アセンブリ レベル プログラミングで整数を出力するにはどうすればよいですか?も参照してください。バッファ関数のより一般的な int->string 、またはx86 タグ wikiの他の複数桁の数値リンクについて


AL (AX の代わりに) を 10 で除算するために使用aamすることもでき、最初に AH をゼロにする必要がなくなります。div r8現在の Intel および AMD CPUよりもわずかに高速です。ただし、結果は とは逆のレジスタに格納されます。divつまり、 の後に余分な命令が追加されaamます。mov dl, 10これにより、との節約が相殺されcbwます。

print2Digits:
    ;; input in AL (0-99).  (Ignores AH because we use AAM instead of div)
    ;; clobbers AX and DX
    aam               ; like `div` by 10, but with the outputs reversed, and input from AL only
          ;; quotient in AH (high digit), remainder in AL(low digit).  (Opposite to div)

    add   ax, 0x3030  ; add '0' to al and ah at the same time.
    mov   dl, al      ; save the low digit
    mov   al, ah      ; print high digit first

    mov   ah, 0x0E    ; BIOS call #: print single character
    int   0x10        ; print first digit.  Doesn't clobber anything, so AH still holds 0x0E after
    mov   al, dl
    int   0x10        ; print second digit
    ret

文字列に保存したい (そして文字列出力関数またはシステム コールを 1 回呼び出す) 場合でも、AX をメモリに保存する前に al と ah を交換するxchg al,ah必要があります (たとえば、最新のハードウェアではより効率的ですが、 186: rol ax,8)。 divAX 内で正しい順序でそれらを生成します。


32ビットのアドレスサイズが利用可能な386の場合、1つの命令を節約できます。

lea   dx, [eax + 0x3030]   ; need a 32bit addressing mode to use eax as a source reg.  Adds '0' to both digits at once, with a different destination.
mov   al, dh               ; then get ready to print the high byte first

lea、アドレス サイズのプレフィックスと 2 バイトの mod/rm、および 32 ビットのディスプレースメントを必要とするため、コード サイズが大きく失われますが、1 つの命令を節約できます。

書き込み後leaの読み取りに使用すると、おそらく Sandybridge ファミリーの CPU でより高速になります。Haswell およびそれ以降のバージョンですが、Intel pre-SnB では、部分的なレジスタ ストールにより、純粋な 16 ビット バージョンを別の add 命令と mov 命令で使用する方が適切です。eaxdivax

もちろん、実際にパフォーマンスを気にするのであれば、実際に 10 で割る代わりに乗法逆数を使用するでしょう。 また、通常は、従来の BIOS 呼び出しを行う 16 ビット コードを作成することもありません!

于 2016-05-10T06:54:23.603 に答える